1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 // Inline assembly support.
15 use rustc_data_structures::thin_vec::ThinVec;
18 use syntax::ext::base;
19 use syntax::ext::base::*;
20 use syntax::feature_gate;
21 use syntax::parse::{self, token};
23 use syntax::symbol::Symbol;
24 use syntax::ast::AsmDialect;
26 use syntax::tokenstream;
38 fn next(&self) -> State {
45 StateNone => StateNone,
50 const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
52 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
54 tts: &[tokenstream::TokenTree])
55 -> Box<dyn base::MacResult + 'cx> {
56 if !cx.ecfg.enable_asm() {
57 feature_gate::emit_feature_err(&cx.parse_sess,
60 feature_gate::GateIssue::Language,
61 feature_gate::EXPLAIN_ASM);
62 return DummyResult::expr(sp);
65 // Split the tts before the first colon, to avoid `asm!("x": y)` being
66 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
67 let first_colon = tts.iter()
70 tokenstream::TokenTree::Token(_, token::Colon) |
71 tokenstream::TokenTree::Token(_, token::ModSep) => true,
75 .unwrap_or(tts.len());
76 let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
77 let mut asm = Symbol::intern("");
78 let mut asm_str_style = None;
79 let mut outputs = Vec::new();
80 let mut inputs = Vec::new();
81 let mut clobs = Vec::new();
82 let mut volatile = false;
83 let mut alignstack = false;
84 let mut dialect = AsmDialect::Att;
91 if asm_str_style.is_some() {
92 // If we already have a string with instructions,
93 // ending up in Asm state again is an error.
94 span_err!(cx, sp, E0660, "malformed inline assembly");
95 return DummyResult::expr(sp);
97 // Nested parser, stop before the first colon (see above).
98 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
99 let (s, style) = match expr_to_string(cx,
100 panictry!(p2.parse_expr()),
101 "inline assembly must be a string literal") {
102 Some((s, st)) => (s, st),
103 // let compilation continue
104 None => return DummyResult::expr(sp),
107 // This is most likely malformed.
108 if p2.token != token::Eof {
109 let mut extra_tts = panictry!(p2.parse_all_token_trees());
110 extra_tts.extend(tts[first_colon..].iter().cloned());
111 p = parse::stream_to_parser(cx.parse_sess, extra_tts.into_iter().collect());
115 asm_str_style = Some(style);
118 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
120 if !outputs.is_empty() {
121 p.eat(&token::Comma);
124 let (constraint, _str_style) = panictry!(p.parse_str());
126 let span = p.prev_span;
128 panictry!(p.expect(&token::OpenDelim(token::Paren)));
129 let out = panictry!(p.parse_expr());
130 panictry!(p.expect(&token::CloseDelim(token::Paren)));
132 // Expands a read+write operand into two operands.
134 // Use '+' modifier when you want the same expression
135 // to be both an input and an output at the same time.
136 // It's the opposite of '=&' which means that the memory
137 // cannot be shared with any other operand (usually when
138 // a register is clobbered early.)
139 let constraint_str = constraint.as_str();
140 let mut ch = constraint_str.chars();
141 let output = match ch.next() {
144 Some(Symbol::intern(&format!("={}", ch.as_str())))
147 span_err!(cx, span, E0661,
148 "output operand constraint lacks '=' or '+'");
153 let is_rw = output.is_some();
154 let is_indirect = constraint_str.contains("*");
155 outputs.push(ast::InlineAsmOutput {
156 constraint: output.unwrap_or(constraint),
164 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
166 if !inputs.is_empty() {
167 p.eat(&token::Comma);
170 let (constraint, _str_style) = panictry!(p.parse_str());
172 if constraint.as_str().starts_with("=") {
173 span_err!(cx, p.prev_span, E0662,
174 "input operand constraint contains '='");
175 } else if constraint.as_str().starts_with("+") {
176 span_err!(cx, p.prev_span, E0663,
177 "input operand constraint contains '+'");
180 panictry!(p.expect(&token::OpenDelim(token::Paren)));
181 let input = panictry!(p.parse_expr());
182 panictry!(p.expect(&token::CloseDelim(token::Paren)));
184 inputs.push((constraint, input));
188 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
190 if !clobs.is_empty() {
191 p.eat(&token::Comma);
194 let (s, _str_style) = panictry!(p.parse_str());
196 if OPTIONS.iter().any(|&opt| s == opt) {
197 cx.span_warn(p.prev_span, "expected a clobber, found an option");
198 } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
199 span_err!(cx, p.prev_span, E0664,
200 "clobber should not be surrounded by braces");
207 let (option, _str_style) = panictry!(p.parse_str());
209 if option == "volatile" {
210 // Indicates that the inline assembly has side effects
211 // and must not be optimized out along with its outputs.
213 } else if option == "alignstack" {
215 } else if option == "intel" {
216 dialect = AsmDialect::Intel;
218 cx.span_warn(p.prev_span, "unrecognized option");
221 if p.token == token::Comma {
222 p.eat(&token::Comma);
229 // MOD_SEP is a double colon '::' without space in between.
230 // When encountered, the state must be advanced twice.
231 match (&p.token, state.next(), state.next().next()) {
232 (&token::Colon, StateNone, _) |
233 (&token::ModSep, _, StateNone) => {
237 (&token::Colon, st, _) |
238 (&token::ModSep, _, st) => {
242 (&token::Eof, ..) => break 'statement,
248 // If there are no outputs, the inline assembly is executed just for its side effects,
249 // so ensure that it is volatile
250 if outputs.is_empty() {
254 MacEager::expr(P(ast::Expr {
255 id: ast::DUMMY_NODE_ID,
256 node: ast::ExprKind::InlineAsm(P(ast::InlineAsm {
258 asm_str_style: asm_str_style.unwrap(),
265 ctxt: cx.backtrace(),
268 attrs: ThinVec::new(),