]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/asm.rs
Auto merge of #31077 - nagisa:mir-temp-promotion, r=dotdash
[rust.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 /*
12  * Inline assembly support.
13  */
14 use self::State::*;
15
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;
25 use syntax::ast::AsmDialect;
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, 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);
59     }
60
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("");
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;
79
80     let mut state = Asm;
81
82     'statement: loop {
83         match state {
84             Asm => {
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                 }
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),
98                 };
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
107                 asm = s;
108                 asm_str_style = Some(style);
109             }
110             Outputs => {
111                 while p.token != token::Eof &&
112                       p.token != token::Colon &&
113                       p.token != token::ModSep {
114
115                     if !outputs.is_empty() {
116                         p.eat(&token::Comma);
117                     }
118
119                     let (constraint, _str_style) = panictry!(p.parse_str());
120
121                     let span = p.last_span;
122
123                     panictry!(p.expect(&token::OpenDelim(token::Paren)));
124                     let out = panictry!(p.parse_expr());
125                     panictry!(p.expect(&token::CloseDelim(token::Paren)));
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.)
134                     let output = match constraint.slice_shift_char() {
135                         Some(('=', _)) => None,
136                         Some(('+', operand)) => {
137                             Some(token::intern_and_get_ident(&format!(
138                                         "={}", operand)))
139                         }
140                         _ => {
141                             cx.span_err(span, "output operand constraint lacks '=' or '+'");
142                             None
143                         }
144                     };
145
146                     let is_rw = output.is_some();
147                     let is_indirect = constraint.contains("*");
148                     outputs.push(ast::InlineAsmOutput {
149                         constraint: output.unwrap_or(constraint),
150                         expr: out,
151                         is_rw: is_rw,
152                         is_indirect: is_indirect,
153                     });
154                 }
155             }
156             Inputs => {
157                 while p.token != token::Eof &&
158                       p.token != token::Colon &&
159                       p.token != token::ModSep {
160
161                     if !inputs.is_empty() {
162                         p.eat(&token::Comma);
163                     }
164
165                     let (constraint, _str_style) = panictry!(p.parse_str());
166
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 '+'");
171                     }
172
173                     panictry!(p.expect(&token::OpenDelim(token::Paren)));
174                     let input = panictry!(p.parse_expr());
175                     panictry!(p.expect(&token::CloseDelim(token::Paren)));
176
177                     inputs.push((constraint, input));
178                 }
179             }
180             Clobbers => {
181                 while p.token != token::Eof &&
182                       p.token != token::Colon &&
183                       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                     }
194                     clobs.push(s);
195                 }
196             }
197             Options => {
198                 let (option, _str_style) = panictry!(p.parse_str());
199
200                 if option == "volatile" {
201                     // Indicates that the inline assembly has side effects
202                     // and must not be optimized out along with its outputs.
203                     volatile = true;
204                 } else if option == "alignstack" {
205                     alignstack = true;
206                 } else if option == "intel" {
207                     dialect = AsmDialect::Intel;
208                 } else {
209                     cx.span_warn(p.last_span, "unrecognized option");
210                 }
211
212                 if p.token == token::Comma {
213                     p.eat(&token::Comma);
214                 }
215             }
216             StateNone => ()
217         }
218
219         loop {
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) => {
225                     p.bump();
226                     break 'statement;
227                 }
228                 (&token::Colon, st, _)   |
229                 (&token::ModSep, _, st) => {
230                     p.bump();
231                     state = st;
232                 }
233                 (&token::Eof, _, _) => break 'statement,
234                 _ => break
235             }
236         }
237     }
238
239     let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
240         call_site: sp,
241         callee: codemap::NameAndSpan {
242             format: codemap::MacroBang(intern("asm")),
243             span: None,
244             allow_internal_unstable: false,
245         },
246     });
247
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(),
253             outputs: outputs,
254             inputs: inputs,
255             clobbers: clobs,
256             volatile: volatile,
257             alignstack: alignstack,
258             dialect: dialect,
259             expn_id: expn_id,
260         }),
261         span: sp,
262         attrs: None,
263     }))
264 }