]> git.proxmox.com Git - rustc.git/blob - src/libsyntax_ext/asm.rs
New upstream version 1.13.0+dfsg1
[rustc.git] / src / libsyntax_ext / asm.rs
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.
4 //
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.
10
11 // Inline assembly support.
12 //
13 use self::State::*;
14
15 use syntax::ast;
16 use syntax::codemap;
17 use syntax::ext::base;
18 use syntax::ext::base::*;
19 use syntax::feature_gate;
20 use syntax::parse::token::intern;
21 use syntax::parse::{self, token};
22 use syntax::ptr::P;
23 use syntax::ast::AsmDialect;
24 use syntax_pos::Span;
25 use syntax::tokenstream;
26
27 enum State {
28 Asm,
29 Outputs,
30 Inputs,
31 Clobbers,
32 Options,
33 StateNone,
34 }
35
36 impl State {
37 fn next(&self) -> State {
38 match *self {
39 Asm => Outputs,
40 Outputs => Inputs,
41 Inputs => Clobbers,
42 Clobbers => Options,
43 Options => StateNone,
44 StateNone => StateNone,
45 }
46 }
47 }
48
49 const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
50
51 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
52 sp: Span,
53 tts: &[tokenstream::TokenTree])
54 -> Box<base::MacResult + 'cx> {
55 if !cx.ecfg.enable_asm() {
56 feature_gate::emit_feature_err(&cx.parse_sess,
57 "asm",
58 sp,
59 feature_gate::GateIssue::Language,
60 feature_gate::EXPLAIN_ASM);
61 return DummyResult::expr(sp);
62 }
63
64 // Split the tts before the first colon, to avoid `asm!("x": y)` being
65 // parsed as `asm!(z)` with `z = "x": y` which is type ascription.
66 let first_colon = tts.iter()
67 .position(|tt| {
68 match *tt {
69 tokenstream::TokenTree::Token(_, token::Colon) |
70 tokenstream::TokenTree::Token(_, token::ModSep) => true,
71 _ => false,
72 }
73 })
74 .unwrap_or(tts.len());
75 let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
76 let mut asm = token::InternedString::new("");
77 let mut asm_str_style = None;
78 let mut outputs = Vec::new();
79 let mut inputs = Vec::new();
80 let mut clobs = Vec::new();
81 let mut volatile = false;
82 let mut alignstack = false;
83 let mut dialect = AsmDialect::Att;
84
85 let mut state = Asm;
86
87 'statement: loop {
88 match state {
89 Asm => {
90 if asm_str_style.is_some() {
91 // If we already have a string with instructions,
92 // ending up in Asm state again is an error.
93 cx.span_err(sp, "malformed inline assembly");
94 return DummyResult::expr(sp);
95 }
96 // Nested parser, stop before the first colon (see above).
97 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
98 let (s, style) = match expr_to_string(cx,
99 panictry!(p2.parse_expr()),
100 "inline assembly must be a string literal") {
101 Some((s, st)) => (s, st),
102 // let compilation continue
103 None => return DummyResult::expr(sp),
104 };
105
106 // This is most likely malformed.
107 if p2.token != token::Eof {
108 let mut extra_tts = panictry!(p2.parse_all_token_trees());
109 extra_tts.extend(tts[first_colon..].iter().cloned());
110 p = parse::tts_to_parser(cx.parse_sess, extra_tts, cx.cfg());
111 }
112
113 asm = s;
114 asm_str_style = Some(style);
115 }
116 Outputs => {
117 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
118
119 if !outputs.is_empty() {
120 p.eat(&token::Comma);
121 }
122
123 let (constraint, _str_style) = panictry!(p.parse_str());
124
125 let span = p.last_span;
126
127 panictry!(p.expect(&token::OpenDelim(token::Paren)));
128 let out = panictry!(p.parse_expr());
129 panictry!(p.expect(&token::CloseDelim(token::Paren)));
130
131 // Expands a read+write operand into two operands.
132 //
133 // Use '+' modifier when you want the same expression
134 // to be both an input and an output at the same time.
135 // It's the opposite of '=&' which means that the memory
136 // cannot be shared with any other operand (usually when
137 // a register is clobbered early.)
138 let mut ch = constraint.chars();
139 let output = match ch.next() {
140 Some('=') => None,
141 Some('+') => {
142 Some(token::intern_and_get_ident(&format!("={}", ch.as_str())))
143 }
144 _ => {
145 cx.span_err(span, "output operand constraint lacks '=' or '+'");
146 None
147 }
148 };
149
150 let is_rw = output.is_some();
151 let is_indirect = constraint.contains("*");
152 outputs.push(ast::InlineAsmOutput {
153 constraint: output.unwrap_or(constraint.clone()),
154 expr: out,
155 is_rw: is_rw,
156 is_indirect: is_indirect,
157 });
158 }
159 }
160 Inputs => {
161 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
162
163 if !inputs.is_empty() {
164 p.eat(&token::Comma);
165 }
166
167 let (constraint, _str_style) = panictry!(p.parse_str());
168
169 if constraint.starts_with("=") {
170 cx.span_err(p.last_span, "input operand constraint contains '='");
171 } else if constraint.starts_with("+") {
172 cx.span_err(p.last_span, "input operand constraint contains '+'");
173 }
174
175 panictry!(p.expect(&token::OpenDelim(token::Paren)));
176 let input = panictry!(p.parse_expr());
177 panictry!(p.expect(&token::CloseDelim(token::Paren)));
178
179 inputs.push((constraint, input));
180 }
181 }
182 Clobbers => {
183 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
184
185 if !clobs.is_empty() {
186 p.eat(&token::Comma);
187 }
188
189 let (s, _str_style) = panictry!(p.parse_str());
190
191 if OPTIONS.iter().any(|&opt| s == opt) {
192 cx.span_warn(p.last_span, "expected a clobber, found an option");
193 } else if s.starts_with("{") || s.ends_with("}") {
194 cx.span_err(p.last_span, "clobber should not be surrounded by braces");
195 }
196
197 clobs.push(s);
198 }
199 }
200 Options => {
201 let (option, _str_style) = panictry!(p.parse_str());
202
203 if option == "volatile" {
204 // Indicates that the inline assembly has side effects
205 // and must not be optimized out along with its outputs.
206 volatile = true;
207 } else if option == "alignstack" {
208 alignstack = true;
209 } else if option == "intel" {
210 dialect = AsmDialect::Intel;
211 } else {
212 cx.span_warn(p.last_span, "unrecognized option");
213 }
214
215 if p.token == token::Comma {
216 p.eat(&token::Comma);
217 }
218 }
219 StateNone => (),
220 }
221
222 loop {
223 // MOD_SEP is a double colon '::' without space in between.
224 // When encountered, the state must be advanced twice.
225 match (&p.token, state.next(), state.next().next()) {
226 (&token::Colon, StateNone, _) |
227 (&token::ModSep, _, StateNone) => {
228 p.bump();
229 break 'statement;
230 }
231 (&token::Colon, st, _) |
232 (&token::ModSep, _, st) => {
233 p.bump();
234 state = st;
235 }
236 (&token::Eof, ..) => break 'statement,
237 _ => break,
238 }
239 }
240 }
241
242 let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
243 call_site: sp,
244 callee: codemap::NameAndSpan {
245 format: codemap::MacroBang(intern("asm")),
246 span: None,
247 allow_internal_unstable: false,
248 },
249 });
250
251 MacEager::expr(P(ast::Expr {
252 id: ast::DUMMY_NODE_ID,
253 node: ast::ExprKind::InlineAsm(ast::InlineAsm {
254 asm: token::intern_and_get_ident(&asm),
255 asm_str_style: asm_str_style.unwrap(),
256 outputs: outputs,
257 inputs: inputs,
258 clobbers: clobs,
259 volatile: volatile,
260 alignstack: alignstack,
261 dialect: dialect,
262 expn_id: expn_id,
263 }),
264 span: sp,
265 attrs: ast::ThinVec::new(),
266 }))
267 }