1 // Llvm-style inline assembly support.
5 use rustc_ast::ast::{self, LlvmAsmDialect};
7 use rustc_ast::token::{self, Token};
8 use rustc_ast::tokenstream::{self, TokenStream};
9 use rustc_errors::{struct_span_err, DiagnosticBuilder, PResult};
10 use rustc_expand::base::*;
11 use rustc_parse::parser::Parser;
12 use rustc_span::symbol::{kw, sym, Symbol};
25 fn next(&self) -> State {
32 StateNone => StateNone,
37 const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel];
39 pub fn expand_llvm_asm<'cx>(
40 cx: &'cx mut ExtCtxt<'_>,
43 ) -> Box<dyn MacResult + 'cx> {
44 let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
45 Ok(Some(inline_asm)) => inline_asm,
46 Ok(None) => return DummyResult::any(sp),
49 return DummyResult::any(sp);
53 // If there are no outputs, the inline assembly is executed just for its side effects,
54 // so ensure that it is volatile
55 if inline_asm.outputs.is_empty() {
56 inline_asm.volatile = true;
59 MacEager::expr(P(ast::Expr {
60 id: ast::DUMMY_NODE_ID,
61 kind: ast::ExprKind::LlvmInlineAsm(P(inline_asm)),
62 span: cx.with_def_site_ctxt(sp),
63 attrs: ast::AttrVec::new(),
68 fn parse_asm_str<'a>(p: &mut Parser<'a>) -> PResult<'a, Symbol> {
69 match p.parse_str_lit() {
70 Ok(str_lit) => Ok(str_lit.symbol_unescaped),
72 let span = opt_lit.map_or(p.token.span, |lit| lit.span);
73 let mut err = p.sess.span_diagnostic.struct_span_err(span, "expected string literal");
74 err.span_label(span, "not a string literal");
80 fn parse_inline_asm<'a>(
84 ) -> Result<Option<ast::LlvmInlineAsm>, DiagnosticBuilder<'a>> {
85 // Split the tts before the first colon, to avoid `llvm_asm!("x": y)` being
86 // parsed as `llvm_asm!(z)` with `z = "x": y` which is type ascription.
89 .position(|tt| match tt {
90 tokenstream::TokenTree::Token(Token { kind: token::Colon | token::ModSep, .. }) => true,
93 .unwrap_or(tts.len());
94 let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect());
95 let mut asm = kw::Invalid;
96 let mut asm_str_style = None;
97 let mut outputs = Vec::new();
98 let mut inputs = Vec::new();
99 let mut clobs = Vec::new();
100 let mut volatile = false;
101 let mut alignstack = false;
102 let mut dialect = LlvmAsmDialect::Att;
109 if asm_str_style.is_some() {
110 // If we already have a string with instructions,
111 // ending up in Asm state again is an error.
112 return Err(struct_span_err!(
113 cx.parse_sess.span_diagnostic,
116 "malformed inline assembly"
119 // Nested parser, stop before the first colon (see above).
120 let mut p2 = cx.new_parser_from_tts(tts.trees().take(first_colon).collect());
122 if p2.token == token::Eof {
124 cx.struct_span_err(sp, "macro requires a string literal as an argument");
125 err.span_label(sp, "string literal required");
129 let expr = p2.parse_expr()?;
131 match expr_to_string(cx, expr, "inline assembly must be a string literal") {
132 Some((s, st)) => (s, st),
133 None => return Ok(None),
136 // This is most likely malformed.
137 if p2.token != token::Eof {
138 let mut extra_tts = p2.parse_all_token_trees()?;
139 extra_tts.extend(tts.trees().skip(first_colon));
140 p = cx.new_parser_from_tts(extra_tts.into_iter().collect());
144 asm_str_style = Some(style);
147 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
148 if !outputs.is_empty() {
149 p.eat(&token::Comma);
152 let constraint = parse_asm_str(&mut p)?;
154 let span = p.prev_token.span;
156 p.expect(&token::OpenDelim(token::Paren))?;
157 let expr = p.parse_expr()?;
158 p.expect(&token::CloseDelim(token::Paren))?;
160 // Expands a read+write operand into two operands.
162 // Use '+' modifier when you want the same expression
163 // to be both an input and an output at the same time.
164 // It's the opposite of '=&' which means that the memory
165 // cannot be shared with any other operand (usually when
166 // a register is clobbered early.)
167 let constraint_str = constraint.as_str();
168 let mut ch = constraint_str.chars();
169 let output = match ch.next() {
171 Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
174 cx.parse_sess.span_diagnostic,
177 "output operand constraint lacks '=' or '+'"
184 let is_rw = output.is_some();
185 let is_indirect = constraint_str.contains('*');
186 outputs.push(ast::LlvmInlineAsmOutput {
187 constraint: output.unwrap_or(constraint),
195 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
196 if !inputs.is_empty() {
197 p.eat(&token::Comma);
200 let constraint = parse_asm_str(&mut p)?;
202 if constraint.as_str().starts_with('=') {
204 cx.parse_sess.span_diagnostic,
207 "input operand constraint contains '='"
210 } else if constraint.as_str().starts_with('+') {
212 cx.parse_sess.span_diagnostic,
215 "input operand constraint contains '+'"
220 p.expect(&token::OpenDelim(token::Paren))?;
221 let input = p.parse_expr()?;
222 p.expect(&token::CloseDelim(token::Paren))?;
224 inputs.push((constraint, input));
228 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
229 if !clobs.is_empty() {
230 p.eat(&token::Comma);
233 let s = parse_asm_str(&mut p)?;
235 if OPTIONS.iter().any(|&opt| s == opt) {
236 cx.span_warn(p.prev_token.span, "expected a clobber, found an option");
237 } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') {
239 cx.parse_sess.span_diagnostic,
242 "clobber should not be surrounded by braces"
251 let option = parse_asm_str(&mut p)?;
253 if option == sym::volatile {
254 // Indicates that the inline assembly has side effects
255 // and must not be optimized out along with its outputs.
257 } else if option == sym::alignstack {
259 } else if option == sym::intel {
260 dialect = LlvmAsmDialect::Intel;
262 cx.span_warn(p.prev_token.span, "unrecognized option");
265 if p.token == token::Comma {
266 p.eat(&token::Comma);
273 // MOD_SEP is a double colon '::' without space in between.
274 // When encountered, the state must be advanced twice.
275 match (&p.token.kind, state.next(), state.next().next()) {
276 (&token::Colon, StateNone, _) | (&token::ModSep, _, StateNone) => {
280 (&token::Colon, st, _) | (&token::ModSep, _, st) => {
284 (&token::Eof, ..) => break 'statement,
290 Ok(Some(ast::LlvmInlineAsm {
292 asm_str_style: asm_str_style.unwrap(),