]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
223e47cc LB |
11 | /* |
12 | * Inline assembly support. | |
13 | */ | |
1a4d82fc | 14 | use self::State::*; |
223e47cc | 15 | |
9cc50fc6 SL |
16 | use syntax::ast; |
17 | use syntax::codemap; | |
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}; | |
24 | use syntax::ptr::P; | |
b039eaaf | 25 | use syntax::ast::AsmDialect; |
970d7e83 | 26 | |
223e47cc LB |
27 | enum State { |
28 | Asm, | |
29 | Outputs, | |
30 | Inputs, | |
31 | Clobbers, | |
1a4d82fc JJ |
32 | Options, |
33 | StateNone | |
223e47cc LB |
34 | } |
35 | ||
1a4d82fc JJ |
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 | } | |
223e47cc LB |
46 | } |
47 | } | |
48 | ||
c34b1796 | 49 | const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; |
223e47cc | 50 | |
1a4d82fc JJ |
51 | pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree]) |
52 | -> Box<base::MacResult+'cx> { | |
85aaf69f SL |
53 | if !cx.ecfg.enable_asm() { |
54 | feature_gate::emit_feature_err( | |
e9174d1e SL |
55 | &cx.parse_sess.span_diagnostic, "asm", sp, |
56 | feature_gate::GateIssue::Language, | |
57 | feature_gate::EXPLAIN_ASM); | |
85aaf69f SL |
58 | return DummyResult::expr(sp); |
59 | } | |
60 | ||
9cc50fc6 SL |
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| { | |
64 | match *tt { | |
65 | ast::TokenTree::Token(_, token::Colon) | | |
66 | ast::TokenTree::Token(_, token::ModSep) => true, | |
67 | _ => false | |
68 | } | |
69 | }).unwrap_or(tts.len()); | |
70 | let mut p = cx.new_parser_from_tts(&tts[first_colon..]); | |
71 | let mut asm = token::InternedString::new(""); | |
1a4d82fc JJ |
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(); | |
223e47cc LB |
76 | let mut volatile = false; |
77 | let mut alignstack = false; | |
b039eaaf | 78 | let mut dialect = AsmDialect::Att; |
223e47cc LB |
79 | |
80 | let mut state = Asm; | |
970d7e83 | 81 | |
1a4d82fc | 82 | 'statement: loop { |
223e47cc LB |
83 | match state { |
84 | Asm => { | |
85aaf69f SL |
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); | |
90 | } | |
9cc50fc6 SL |
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()), | |
1a4d82fc JJ |
94 | "inline assembly must be a string literal") { |
95 | Some((s, st)) => (s, st), | |
96 | // let compilation continue | |
97 | None => return DummyResult::expr(sp), | |
98 | }; | |
9cc50fc6 SL |
99 | |
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()); | |
105 | } | |
106 | ||
1a4d82fc JJ |
107 | asm = s; |
108 | asm_str_style = Some(style); | |
223e47cc LB |
109 | } |
110 | Outputs => { | |
1a4d82fc JJ |
111 | while p.token != token::Eof && |
112 | p.token != token::Colon && | |
113 | p.token != token::ModSep { | |
223e47cc | 114 | |
9346a6ac | 115 | if !outputs.is_empty() { |
9cc50fc6 | 116 | p.eat(&token::Comma); |
223e47cc LB |
117 | } |
118 | ||
9346a6ac | 119 | let (constraint, _str_style) = panictry!(p.parse_str()); |
1a4d82fc JJ |
120 | |
121 | let span = p.last_span; | |
223e47cc | 122 | |
9346a6ac | 123 | panictry!(p.expect(&token::OpenDelim(token::Paren))); |
92a42be0 | 124 | let out = panictry!(p.parse_expr()); |
9346a6ac | 125 | panictry!(p.expect(&token::CloseDelim(token::Paren))); |
1a4d82fc JJ |
126 | |
127 | // Expands a read+write operand into two operands. | |
128 | // | |
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.) | |
54a0048b SL |
134 | let mut ch = constraint.chars(); |
135 | let output = match ch.next() { | |
136 | Some('=') => None, | |
137 | Some('+') => { | |
1a4d82fc | 138 | Some(token::intern_and_get_ident(&format!( |
54a0048b | 139 | "={}", ch.as_str()))) |
1a4d82fc JJ |
140 | } |
141 | _ => { | |
142 | cx.span_err(span, "output operand constraint lacks '=' or '+'"); | |
143 | None | |
144 | } | |
223e47cc LB |
145 | }; |
146 | ||
1a4d82fc | 147 | let is_rw = output.is_some(); |
9cc50fc6 SL |
148 | let is_indirect = constraint.contains("*"); |
149 | outputs.push(ast::InlineAsmOutput { | |
54a0048b | 150 | constraint: output.unwrap_or(constraint.clone()), |
9cc50fc6 SL |
151 | expr: out, |
152 | is_rw: is_rw, | |
153 | is_indirect: is_indirect, | |
154 | }); | |
223e47cc LB |
155 | } |
156 | } | |
157 | Inputs => { | |
1a4d82fc JJ |
158 | while p.token != token::Eof && |
159 | p.token != token::Colon && | |
160 | p.token != token::ModSep { | |
223e47cc | 161 | |
9346a6ac | 162 | if !inputs.is_empty() { |
9cc50fc6 | 163 | p.eat(&token::Comma); |
223e47cc LB |
164 | } |
165 | ||
9346a6ac | 166 | let (constraint, _str_style) = panictry!(p.parse_str()); |
1a4d82fc | 167 | |
9cc50fc6 | 168 | if constraint.starts_with("=") { |
1a4d82fc | 169 | cx.span_err(p.last_span, "input operand constraint contains '='"); |
9cc50fc6 | 170 | } else if constraint.starts_with("+") { |
1a4d82fc JJ |
171 | cx.span_err(p.last_span, "input operand constraint contains '+'"); |
172 | } | |
223e47cc | 173 | |
9346a6ac | 174 | panictry!(p.expect(&token::OpenDelim(token::Paren))); |
92a42be0 | 175 | let input = panictry!(p.parse_expr()); |
9346a6ac | 176 | panictry!(p.expect(&token::CloseDelim(token::Paren))); |
1a4d82fc JJ |
177 | |
178 | inputs.push((constraint, input)); | |
223e47cc LB |
179 | } |
180 | } | |
181 | Clobbers => { | |
1a4d82fc JJ |
182 | while p.token != token::Eof && |
183 | p.token != token::Colon && | |
184 | p.token != token::ModSep { | |
223e47cc | 185 | |
9346a6ac | 186 | if !clobs.is_empty() { |
9cc50fc6 | 187 | p.eat(&token::Comma); |
223e47cc LB |
188 | } |
189 | ||
9346a6ac | 190 | let (s, _str_style) = panictry!(p.parse_str()); |
223e47cc | 191 | |
1a4d82fc JJ |
192 | if OPTIONS.iter().any(|&opt| s == opt) { |
193 | cx.span_warn(p.last_span, "expected a clobber, found an option"); | |
194 | } | |
195 | clobs.push(s); | |
196 | } | |
223e47cc LB |
197 | } |
198 | Options => { | |
9346a6ac | 199 | let (option, _str_style) = panictry!(p.parse_str()); |
223e47cc | 200 | |
1a4d82fc JJ |
201 | if option == "volatile" { |
202 | // Indicates that the inline assembly has side effects | |
203 | // and must not be optimized out along with its outputs. | |
223e47cc | 204 | volatile = true; |
1a4d82fc | 205 | } else if option == "alignstack" { |
223e47cc | 206 | alignstack = true; |
1a4d82fc | 207 | } else if option == "intel" { |
b039eaaf | 208 | dialect = AsmDialect::Intel; |
1a4d82fc JJ |
209 | } else { |
210 | cx.span_warn(p.last_span, "unrecognized option"); | |
223e47cc LB |
211 | } |
212 | ||
1a4d82fc | 213 | if p.token == token::Comma { |
9cc50fc6 | 214 | p.eat(&token::Comma); |
223e47cc LB |
215 | } |
216 | } | |
1a4d82fc | 217 | StateNone => () |
223e47cc LB |
218 | } |
219 | ||
1a4d82fc JJ |
220 | loop { |
221 | // MOD_SEP is a double colon '::' without space in between. | |
222 | // When encountered, the state must be advanced twice. | |
223 | match (&p.token, state.next(), state.next().next()) { | |
224 | (&token::Colon, StateNone, _) | | |
225 | (&token::ModSep, _, StateNone) => { | |
9cc50fc6 | 226 | p.bump(); |
1a4d82fc | 227 | break 'statement; |
223e47cc | 228 | } |
1a4d82fc JJ |
229 | (&token::Colon, st, _) | |
230 | (&token::ModSep, _, st) => { | |
9cc50fc6 | 231 | p.bump(); |
1a4d82fc | 232 | state = st; |
223e47cc | 233 | } |
1a4d82fc JJ |
234 | (&token::Eof, _, _) => break 'statement, |
235 | _ => break | |
236 | } | |
223e47cc LB |
237 | } |
238 | } | |
239 | ||
1a4d82fc JJ |
240 | let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo { |
241 | call_site: sp, | |
242 | callee: codemap::NameAndSpan { | |
e9174d1e | 243 | format: codemap::MacroBang(intern("asm")), |
1a4d82fc | 244 | span: None, |
c34b1796 | 245 | allow_internal_unstable: false, |
1a4d82fc JJ |
246 | }, |
247 | }); | |
248 | ||
c34b1796 | 249 | MacEager::expr(P(ast::Expr { |
1a4d82fc | 250 | id: ast::DUMMY_NODE_ID, |
7453a54e | 251 | node: ast::ExprKind::InlineAsm(ast::InlineAsm { |
85aaf69f | 252 | asm: token::intern_and_get_ident(&asm), |
1a4d82fc | 253 | asm_str_style: asm_str_style.unwrap(), |
223e47cc | 254 | outputs: outputs, |
1a4d82fc JJ |
255 | inputs: inputs, |
256 | clobbers: clobs, | |
223e47cc LB |
257 | volatile: volatile, |
258 | alignstack: alignstack, | |
1a4d82fc JJ |
259 | dialect: dialect, |
260 | expn_id: expn_id, | |
223e47cc | 261 | }), |
92a42be0 SL |
262 | span: sp, |
263 | attrs: None, | |
1a4d82fc | 264 | })) |
223e47cc | 265 | } |