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