1 // Inline assembly support.
5 use rustc_data_structures::thin_vec::ThinVec;
7 use errors::DiagnosticBuilder;
10 use syntax::ext::base::{self, *};
11 use syntax::feature_gate;
12 use syntax::parse::{self, token};
14 use syntax::symbol::{Symbol, sym};
15 use syntax::ast::AsmDialect;
17 use syntax::tokenstream;
18 use syntax::{span_err, struct_span_err};
30 fn next(&self) -> State {
37 StateNone => StateNone,
42 const OPTIONS: &[Symbol] = &[sym::volatile, sym::alignstack, sym::intel];
44 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt<'_>,
46 tts: &[tokenstream::TokenTree])
47 -> Box<dyn base::MacResult + 'cx> {
48 if !cx.ecfg.enable_asm() {
49 feature_gate::emit_feature_err(&cx.parse_sess,
52 feature_gate::GateIssue::Language,
53 feature_gate::EXPLAIN_ASM);
56 let mut inline_asm = match parse_inline_asm(cx, sp, tts) {
57 Ok(Some(inline_asm)) => inline_asm,
58 Ok(None) => return DummyResult::expr(sp),
61 return DummyResult::expr(sp);
65 // If there are no outputs, the inline assembly is executed just for its side effects,
66 // so ensure that it is volatile
67 if inline_asm.outputs.is_empty() {
68 inline_asm.volatile = true;
71 MacEager::expr(P(ast::Expr {
72 id: ast::DUMMY_NODE_ID,
73 node: ast::ExprKind::InlineAsm(P(inline_asm)),
75 attrs: ThinVec::new(),
79 fn parse_inline_asm<'a>(
82 tts: &[tokenstream::TokenTree],
83 ) -> Result<Option<ast::InlineAsm>, DiagnosticBuilder<'a>> {
84 // Split the tts before the first colon, to avoid `asm!("x": y)` being
85 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
86 let first_colon = tts.iter()
89 tokenstream::TokenTree::Token(_, token::Colon) |
90 tokenstream::TokenTree::Token(_, token::ModSep) => true,
94 .unwrap_or(tts.len());
95 let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
96 let mut asm = Symbol::intern("");
97 let mut asm_str_style = None;
98 let mut outputs = Vec::new();
99 let mut inputs = Vec::new();
100 let mut clobs = Vec::new();
101 let mut volatile = false;
102 let mut alignstack = false;
103 let mut dialect = AsmDialect::Att;
110 if asm_str_style.is_some() {
111 // If we already have a string with instructions,
112 // ending up in Asm state again is an error.
113 return Err(struct_span_err!(
114 cx.parse_sess.span_diagnostic,
117 "malformed inline assembly"
120 // Nested parser, stop before the first colon (see above).
121 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
123 if p2.token == token::Eof {
125 cx.struct_span_err(sp, "macro requires a string literal as an argument");
126 err.span_label(sp, "string literal required");
130 let expr = p2.parse_expr()?;
132 match expr_to_string(cx, expr, "inline assembly must be a string literal") {
133 Some((s, st)) => (s, st),
134 None => return Ok(None),
137 // This is most likely malformed.
138 if p2.token != token::Eof {
139 let mut extra_tts = p2.parse_all_token_trees()?;
140 extra_tts.extend(tts[first_colon..].iter().cloned());
141 p = parse::stream_to_parser(cx.parse_sess, extra_tts.into_iter().collect());
145 asm_str_style = Some(style);
148 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
149 if !outputs.is_empty() {
150 p.eat(&token::Comma);
153 let (constraint, _) = p.parse_str()?;
155 let span = p.prev_span;
157 p.expect(&token::OpenDelim(token::Paren))?;
158 let expr = p.parse_expr()?;
159 p.expect(&token::CloseDelim(token::Paren))?;
161 // Expands a read+write operand into two operands.
163 // Use '+' modifier when you want the same expression
164 // to be both an input and an output at the same time.
165 // It's the opposite of '=&' which means that the memory
166 // cannot be shared with any other operand (usually when
167 // a register is clobbered early.)
168 let constraint_str = constraint.as_str();
169 let mut ch = constraint_str.chars();
170 let output = match ch.next() {
173 Some(Symbol::intern(&format!("={}", ch.as_str())))
176 span_err!(cx, span, E0661,
177 "output operand constraint lacks '=' or '+'");
182 let is_rw = output.is_some();
183 let is_indirect = constraint_str.contains("*");
184 outputs.push(ast::InlineAsmOutput {
185 constraint: output.unwrap_or(constraint),
193 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
194 if !inputs.is_empty() {
195 p.eat(&token::Comma);
198 let (constraint, _) = p.parse_str()?;
200 if constraint.as_str().starts_with("=") {
201 span_err!(cx, p.prev_span, E0662,
202 "input operand constraint contains '='");
203 } else if constraint.as_str().starts_with("+") {
204 span_err!(cx, p.prev_span, E0663,
205 "input operand constraint contains '+'");
208 p.expect(&token::OpenDelim(token::Paren))?;
209 let input = p.parse_expr()?;
210 p.expect(&token::CloseDelim(token::Paren))?;
212 inputs.push((constraint, input));
216 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
217 if !clobs.is_empty() {
218 p.eat(&token::Comma);
221 let (s, _) = p.parse_str()?;
223 if OPTIONS.iter().any(|&opt| s == opt) {
224 cx.span_warn(p.prev_span, "expected a clobber, found an option");
225 } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
226 span_err!(cx, p.prev_span, E0664,
227 "clobber should not be surrounded by braces");
234 let (option, _) = p.parse_str()?;
236 if option == sym::volatile {
237 // Indicates that the inline assembly has side effects
238 // and must not be optimized out along with its outputs.
240 } else if option == sym::alignstack {
242 } else if option == sym::intel {
243 dialect = AsmDialect::Intel;
245 cx.span_warn(p.prev_span, "unrecognized option");
248 if p.token == token::Comma {
249 p.eat(&token::Comma);
256 // MOD_SEP is a double colon '::' without space in between.
257 // When encountered, the state must be advanced twice.
258 match (&p.token, state.next(), state.next().next()) {
259 (&token::Colon, StateNone, _) |
260 (&token::ModSep, _, StateNone) => {
264 (&token::Colon, st, _) |
265 (&token::ModSep, _, st) => {
269 (&token::Eof, ..) => break 'statement,
275 Ok(Some(ast::InlineAsm {
277 asm_str_style: asm_str_style.unwrap(),
284 ctxt: cx.backtrace(),