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