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.
12 * Inline assembly support.
18 use syntax::codemap::Span;
19 use syntax::ext::base;
20 use syntax::ext::base::*;
21 use syntax::feature_gate;
22 use syntax::parse::token::intern;
23 use syntax::parse::{self, token};
25 use syntax::ast::AsmDialect;
37 fn next(&self) -> State {
44 StateNone => StateNone
49 const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
51 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
52 -> Box<base::MacResult+'cx> {
53 if !cx.ecfg.enable_asm() {
54 feature_gate::emit_feature_err(
55 &cx.parse_sess.span_diagnostic, "asm", sp,
56 feature_gate::GateIssue::Language,
57 feature_gate::EXPLAIN_ASM);
58 return DummyResult::expr(sp);
61 // Split the tts before the first colon, to avoid `asm!("x": y)` being
62 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
63 let first_colon = tts.iter().position(|tt| {
65 ast::TokenTree::Token(_, token::Colon) |
66 ast::TokenTree::Token(_, token::ModSep) => true,
69 }).unwrap_or(tts.len());
70 let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
71 let mut asm = token::InternedString::new("");
72 let mut asm_str_style = None;
73 let mut outputs = Vec::new();
74 let mut inputs = Vec::new();
75 let mut clobs = Vec::new();
76 let mut volatile = false;
77 let mut alignstack = false;
78 let mut dialect = AsmDialect::Att;
85 if asm_str_style.is_some() {
86 // If we already have a string with instructions,
87 // ending up in Asm state again is an error.
88 cx.span_err(sp, "malformed inline assembly");
89 return DummyResult::expr(sp);
91 // Nested parser, stop before the first colon (see above).
92 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
93 let (s, style) = match expr_to_string(cx, panictry!(p2.parse_expr()),
94 "inline assembly must be a string literal") {
95 Some((s, st)) => (s, st),
96 // let compilation continue
97 None => return DummyResult::expr(sp),
100 // This is most likely malformed.
101 if p2.token != token::Eof {
102 let mut extra_tts = panictry!(p2.parse_all_token_trees());
103 extra_tts.extend(tts[first_colon..].iter().cloned());
104 p = parse::tts_to_parser(cx.parse_sess, extra_tts, cx.cfg());
108 asm_str_style = Some(style);
111 while p.token != token::Eof &&
112 p.token != token::Colon &&
113 p.token != token::ModSep {
115 if !outputs.is_empty() {
116 p.eat(&token::Comma);
119 let (constraint, _str_style) = panictry!(p.parse_str());
121 let span = p.last_span;
123 panictry!(p.expect(&token::OpenDelim(token::Paren)));
124 let out = panictry!(p.parse_expr());
125 panictry!(p.expect(&token::CloseDelim(token::Paren)));
127 // Expands a read+write operand into two operands.
129 // Use '+' modifier when you want the same expression
130 // to be both an input and an output at the same time.
131 // It's the opposite of '=&' which means that the memory
132 // cannot be shared with any other operand (usually when
133 // a register is clobbered early.)
134 let output = match constraint.slice_shift_char() {
135 Some(('=', _)) => None,
136 Some(('+', operand)) => {
137 Some(token::intern_and_get_ident(&format!(
141 cx.span_err(span, "output operand constraint lacks '=' or '+'");
146 let is_rw = output.is_some();
147 let is_indirect = constraint.contains("*");
148 outputs.push(ast::InlineAsmOutput {
149 constraint: output.unwrap_or(constraint),
152 is_indirect: is_indirect,
157 while p.token != token::Eof &&
158 p.token != token::Colon &&
159 p.token != token::ModSep {
161 if !inputs.is_empty() {
162 p.eat(&token::Comma);
165 let (constraint, _str_style) = panictry!(p.parse_str());
167 if constraint.starts_with("=") {
168 cx.span_err(p.last_span, "input operand constraint contains '='");
169 } else if constraint.starts_with("+") {
170 cx.span_err(p.last_span, "input operand constraint contains '+'");
173 panictry!(p.expect(&token::OpenDelim(token::Paren)));
174 let input = panictry!(p.parse_expr());
175 panictry!(p.expect(&token::CloseDelim(token::Paren)));
177 inputs.push((constraint, input));
181 while p.token != token::Eof &&
182 p.token != token::Colon &&
183 p.token != token::ModSep {
185 if !clobs.is_empty() {
186 p.eat(&token::Comma);
189 let (s, _str_style) = panictry!(p.parse_str());
191 if OPTIONS.iter().any(|&opt| s == opt) {
192 cx.span_warn(p.last_span, "expected a clobber, found an option");
198 let (option, _str_style) = panictry!(p.parse_str());
200 if option == "volatile" {
201 // Indicates that the inline assembly has side effects
202 // and must not be optimized out along with its outputs.
204 } else if option == "alignstack" {
206 } else if option == "intel" {
207 dialect = AsmDialect::Intel;
209 cx.span_warn(p.last_span, "unrecognized option");
212 if p.token == token::Comma {
213 p.eat(&token::Comma);
220 // MOD_SEP is a double colon '::' without space in between.
221 // When encountered, the state must be advanced twice.
222 match (&p.token, state.next(), state.next().next()) {
223 (&token::Colon, StateNone, _) |
224 (&token::ModSep, _, StateNone) => {
228 (&token::Colon, st, _) |
229 (&token::ModSep, _, st) => {
233 (&token::Eof, _, _) => break 'statement,
239 let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
241 callee: codemap::NameAndSpan {
242 format: codemap::MacroBang(intern("asm")),
244 allow_internal_unstable: false,
248 MacEager::expr(P(ast::Expr {
249 id: ast::DUMMY_NODE_ID,
250 node: ast::ExprInlineAsm(ast::InlineAsm {
251 asm: token::intern_and_get_ident(&asm),
252 asm_str_style: asm_str_style.unwrap(),
257 alignstack: alignstack,