1 // Inline assembly support.
5 use rustc_data_structures::thin_vec::ThinVec;
7 use errors::DiagnosticBuilder;
10 use syntax_expand::base::{self, *};
11 use syntax::token::{self, Token};
13 use syntax::symbol::{kw, sym, Symbol};
14 use syntax::ast::AsmDialect;
16 use syntax::tokenstream::{self, TokenStream};
17 use syntax::{span_err, struct_span_err};
19 use rustc_error_codes::*;
31 fn next(&self) -> State {
38 StateNone => StateNone,
43 const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel];
45 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt<'_>,
48 -> Box<dyn base::MacResult + 'cx> {
49 let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
50 Ok(Some(inline_asm)) => inline_asm,
51 Ok(None) => return DummyResult::any(sp),
54 return DummyResult::any(sp);
58 // If there are no outputs, the inline assembly is executed just for its side effects,
59 // so ensure that it is volatile
60 if inline_asm.outputs.is_empty() {
61 inline_asm.volatile = true;
64 MacEager::expr(P(ast::Expr {
65 id: ast::DUMMY_NODE_ID,
66 kind: ast::ExprKind::InlineAsm(P(inline_asm)),
67 span: cx.with_def_site_ctxt(sp),
68 attrs: ThinVec::new(),
72 fn parse_inline_asm<'a>(
76 ) -> Result<Option<ast::InlineAsm>, DiagnosticBuilder<'a>> {
77 // Split the tts before the first colon, to avoid `asm!("x": y)` being
78 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
79 let first_colon = tts.trees()
82 tokenstream::TokenTree::Token(Token { kind: token::Colon, .. }) |
83 tokenstream::TokenTree::Token(Token { kind: token::ModSep, .. }) => true,
87 .unwrap_or(tts.len());
88 let mut p = cx.new_parser_from_tts(tts.trees().skip(first_colon).collect());
89 let mut asm = kw::Invalid;
90 let mut asm_str_style = None;
91 let mut outputs = Vec::new();
92 let mut inputs = Vec::new();
93 let mut clobs = Vec::new();
94 let mut volatile = false;
95 let mut alignstack = false;
96 let mut dialect = AsmDialect::Att;
103 if asm_str_style.is_some() {
104 // If we already have a string with instructions,
105 // ending up in Asm state again is an error.
106 return Err(struct_span_err!(
107 cx.parse_sess.span_diagnostic,
110 "malformed inline assembly"
113 // Nested parser, stop before the first colon (see above).
115 cx.new_parser_from_tts(tts.trees().take(first_colon).collect());
117 if p2.token == token::Eof {
119 cx.struct_span_err(sp, "macro requires a string literal as an argument");
120 err.span_label(sp, "string literal required");
124 let expr = p2.parse_expr()?;
126 match expr_to_string(cx, expr, "inline assembly must be a string literal") {
127 Some((s, st)) => (s, st),
128 None => return Ok(None),
131 // This is most likely malformed.
132 if p2.token != token::Eof {
133 let mut extra_tts = p2.parse_all_token_trees()?;
134 extra_tts.extend(tts.trees().skip(first_colon));
135 p = cx.new_parser_from_tts(extra_tts.into_iter().collect());
139 asm_str_style = Some(style);
142 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
143 if !outputs.is_empty() {
144 p.eat(&token::Comma);
147 let (constraint, _) = p.parse_str()?;
149 let span = p.prev_span;
151 p.expect(&token::OpenDelim(token::Paren))?;
152 let expr = p.parse_expr()?;
153 p.expect(&token::CloseDelim(token::Paren))?;
155 // Expands a read+write operand into two operands.
157 // Use '+' modifier when you want the same expression
158 // to be both an input and an output at the same time.
159 // It's the opposite of '=&' which means that the memory
160 // cannot be shared with any other operand (usually when
161 // a register is clobbered early.)
162 let constraint_str = constraint.as_str();
163 let mut ch = constraint_str.chars();
164 let output = match ch.next() {
167 Some(Symbol::intern(&format!("={}", ch.as_str())))
170 span_err!(cx, span, E0661,
171 "output operand constraint lacks '=' or '+'");
176 let is_rw = output.is_some();
177 let is_indirect = constraint_str.contains("*");
178 outputs.push(ast::InlineAsmOutput {
179 constraint: output.unwrap_or(constraint),
187 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
188 if !inputs.is_empty() {
189 p.eat(&token::Comma);
192 let (constraint, _) = p.parse_str()?;
194 if constraint.as_str().starts_with("=") {
195 span_err!(cx, p.prev_span, E0662,
196 "input operand constraint contains '='");
197 } else if constraint.as_str().starts_with("+") {
198 span_err!(cx, p.prev_span, E0663,
199 "input operand constraint contains '+'");
202 p.expect(&token::OpenDelim(token::Paren))?;
203 let input = p.parse_expr()?;
204 p.expect(&token::CloseDelim(token::Paren))?;
206 inputs.push((constraint, input));
210 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
211 if !clobs.is_empty() {
212 p.eat(&token::Comma);
215 let (s, _) = p.parse_str()?;
217 if OPTIONS.iter().any(|&opt| s == opt) {
218 cx.span_warn(p.prev_span, "expected a clobber, found an option");
219 } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
220 span_err!(cx, p.prev_span, E0664,
221 "clobber should not be surrounded by braces");
228 let (option, _) = p.parse_str()?;
230 if option == sym::volatile {
231 // Indicates that the inline assembly has side effects
232 // and must not be optimized out along with its outputs.
234 } else if option == sym::alignstack {
236 } else if option == sym::intel {
237 dialect = AsmDialect::Intel;
239 cx.span_warn(p.prev_span, "unrecognized option");
242 if p.token == token::Comma {
243 p.eat(&token::Comma);
250 // MOD_SEP is a double colon '::' without space in between.
251 // When encountered, the state must be advanced twice.
252 match (&p.token.kind, state.next(), state.next().next()) {
253 (&token::Colon, StateNone, _) |
254 (&token::ModSep, _, StateNone) => {
258 (&token::Colon, st, _) |
259 (&token::ModSep, _, st) => {
263 (&token::Eof, ..) => break 'statement,
269 Ok(Some(ast::InlineAsm {
271 asm_str_style: asm_str_style.unwrap(),