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.
20 use parse::token::InternedString;
35 fn next(&self) -> State {
42 StateNone => StateNone
47 static OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
49 pub fn expand_asm(cx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
50 -> Box<base::MacResult> {
51 let mut p = parse::new_parser_from_tts(cx.parse_sess(),
54 .map(|x| (*x).clone())
57 let mut asm = InternedString::new("");
58 let mut asm_str_style = None;
59 let mut outputs = Vec::new();
60 let mut inputs = Vec::new();
61 let mut cons = "".to_string();
62 let mut volatile = false;
63 let mut alignstack = false;
64 let mut dialect = ast::AsmAtt;
68 let mut read_write_operands = Vec::new();
73 let (s, style) = match expr_to_str(cx, p.parse_expr(),
74 "inline assembly must be a string literal.") {
75 Some((s, st)) => (s, st),
76 // let compilation continue
77 None => return DummyResult::expr(sp),
80 asm_str_style = Some(style);
83 while p.token != token::EOF &&
84 p.token != token::COLON &&
85 p.token != token::MOD_SEP {
87 if outputs.len() != 0 {
91 let (constraint, _str_style) = p.parse_str();
93 let span = p.last_span;
95 p.expect(&token::LPAREN);
96 let out = p.parse_expr();
97 p.expect(&token::RPAREN);
99 // Expands a read+write operand into two operands.
101 // Use '+' modifier when you want the same expression
102 // to be both an input and an output at the same time.
103 // It's the opposite of '=&' which means that the memory
104 // cannot be shared with any other operand (usually when
105 // a register is clobbered early.)
106 let output = match constraint.get().slice_shift_char() {
107 (Some('='), _) => None,
108 (Some('+'), operand) => {
109 // Save a reference to the output
110 read_write_operands.push((outputs.len(), out));
111 Some(token::intern_and_get_ident(format!(
113 operand).as_slice()))
116 cx.span_err(span, "output operand constraint lacks '=' or '+'");
121 outputs.push((output.unwrap_or(constraint), out));
125 while p.token != token::EOF &&
126 p.token != token::COLON &&
127 p.token != token::MOD_SEP {
129 if inputs.len() != 0 {
130 p.eat(&token::COMMA);
133 let (constraint, _str_style) = p.parse_str();
135 if constraint.get().starts_with("=") {
136 cx.span_err(p.last_span, "input operand constraint contains '='");
137 } else if constraint.get().starts_with("+") {
138 cx.span_err(p.last_span, "input operand constraint contains '+'");
141 p.expect(&token::LPAREN);
142 let input = p.parse_expr();
143 p.expect(&token::RPAREN);
145 inputs.push((constraint, input));
150 let mut clobs = Vec::new();
151 while p.token != token::EOF &&
152 p.token != token::COLON &&
153 p.token != token::MOD_SEP {
155 if clobs.len() != 0 {
156 p.eat(&token::COMMA);
159 let (s, _str_style) = p.parse_str();
160 let clob = format!("~\\{{}\\}", s);
163 if OPTIONS.iter().any(|opt| s.equiv(opt)) {
164 cx.span_warn(p.last_span, "expected a clobber, but found an option");
168 cons = clobs.connect(",");
172 let mut clobs = Vec::new();
173 while p.token != token::EOF &&
174 p.token != token::COLON &&
175 p.token != token::MOD_SEP {
177 if clobs.len() != 0 {
178 p.eat(&token::COMMA);
181 let (s, _str_style) = p.parse_str();
182 let clob = format!("~{{{}}}", s);
185 if OPTIONS.iter().any(|opt| s.equiv(opt)) {
186 cx.span_warn(p.last_span, "expected a clobber, but found an option");
190 cons = clobs.connect(",");
193 let (option, _str_style) = p.parse_str();
195 if option.equiv(&("volatile")) {
196 // Indicates that the inline assembly has side effects
197 // and must not be optimized out along with its outputs.
199 } else if option.equiv(&("alignstack")) {
201 } else if option.equiv(&("intel")) {
202 dialect = ast::AsmIntel;
204 cx.span_warn(p.last_span, "unrecognized option");
207 if p.token == token::COMMA {
208 p.eat(&token::COMMA);
215 // MOD_SEP is a double colon '::' without space in between.
216 // When encountered, the state must be advanced twice.
217 match (&p.token, state.next(), state.next().next()) {
218 (&token::COLON, StateNone, _) |
219 (&token::MOD_SEP, _, StateNone) => {
223 (&token::COLON, st, _) |
224 (&token::MOD_SEP, _, st) => {
228 (&token::EOF, _, _) => break 'statement,
234 // Append an input operand, with the form of ("0", expr)
235 // that links to an output operand.
236 for &(i, out) in read_write_operands.iter() {
237 inputs.push((token::intern_and_get_ident(i.to_str().as_slice()),
241 MacExpr::new(box(GC) ast::Expr {
242 id: ast::DUMMY_NODE_ID,
243 node: ast::ExprInlineAsm(ast::InlineAsm {
244 asm: token::intern_and_get_ident(asm.get()),
245 asm_str_style: asm_str_style.unwrap(),
246 clobbers: token::intern_and_get_ident(cons.as_slice()),
250 alignstack: alignstack,