]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/asm.rs
Auto merge of #38992 - nagisa:i128-minvallit, 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 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::{self, token};
21 use syntax::ptr::P;
22 use syntax::symbol::Symbol;
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 = Symbol::intern("");
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);
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.prev_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 constraint_str = constraint.as_str();
139                     let mut ch = constraint_str.chars();
140                     let output = match ch.next() {
141                         Some('=') => None,
142                         Some('+') => {
143                             Some(Symbol::intern(&format!("={}", ch.as_str())))
144                         }
145                         _ => {
146                             cx.span_err(span, "output operand constraint lacks '=' or '+'");
147                             None
148                         }
149                     };
150
151                     let is_rw = output.is_some();
152                     let is_indirect = constraint_str.contains("*");
153                     outputs.push(ast::InlineAsmOutput {
154                         constraint: output.unwrap_or(constraint),
155                         expr: out,
156                         is_rw: is_rw,
157                         is_indirect: is_indirect,
158                     });
159                 }
160             }
161             Inputs => {
162                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
163
164                     if !inputs.is_empty() {
165                         p.eat(&token::Comma);
166                     }
167
168                     let (constraint, _str_style) = panictry!(p.parse_str());
169
170                     if constraint.as_str().starts_with("=") {
171                         cx.span_err(p.prev_span, "input operand constraint contains '='");
172                     } else if constraint.as_str().starts_with("+") {
173                         cx.span_err(p.prev_span, "input operand constraint contains '+'");
174                     }
175
176                     panictry!(p.expect(&token::OpenDelim(token::Paren)));
177                     let input = panictry!(p.parse_expr());
178                     panictry!(p.expect(&token::CloseDelim(token::Paren)));
179
180                     inputs.push((constraint, input));
181                 }
182             }
183             Clobbers => {
184                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
185
186                     if !clobs.is_empty() {
187                         p.eat(&token::Comma);
188                     }
189
190                     let (s, _str_style) = panictry!(p.parse_str());
191
192                     if OPTIONS.iter().any(|&opt| s == opt) {
193                         cx.span_warn(p.prev_span, "expected a clobber, found an option");
194                     } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
195                         cx.span_err(p.prev_span, "clobber should not be surrounded by braces");
196                     }
197
198                     clobs.push(s);
199                 }
200             }
201             Options => {
202                 let (option, _str_style) = panictry!(p.parse_str());
203
204                 if option == "volatile" {
205                     // Indicates that the inline assembly has side effects
206                     // and must not be optimized out along with its outputs.
207                     volatile = true;
208                 } else if option == "alignstack" {
209                     alignstack = true;
210                 } else if option == "intel" {
211                     dialect = AsmDialect::Intel;
212                 } else {
213                     cx.span_warn(p.prev_span, "unrecognized option");
214                 }
215
216                 if p.token == token::Comma {
217                     p.eat(&token::Comma);
218                 }
219             }
220             StateNone => (),
221         }
222
223         loop {
224             // MOD_SEP is a double colon '::' without space in between.
225             // When encountered, the state must be advanced twice.
226             match (&p.token, state.next(), state.next().next()) {
227                 (&token::Colon, StateNone, _) |
228                 (&token::ModSep, _, StateNone) => {
229                     p.bump();
230                     break 'statement;
231                 }
232                 (&token::Colon, st, _) |
233                 (&token::ModSep, _, st) => {
234                     p.bump();
235                     state = st;
236                 }
237                 (&token::Eof, ..) => break 'statement,
238                 _ => break,
239             }
240         }
241     }
242
243     let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
244         call_site: sp,
245         callee: codemap::NameAndSpan {
246             format: codemap::MacroBang(Symbol::intern("asm")),
247             span: None,
248             allow_internal_unstable: false,
249         },
250     });
251
252     MacEager::expr(P(ast::Expr {
253         id: ast::DUMMY_NODE_ID,
254         node: ast::ExprKind::InlineAsm(P(ast::InlineAsm {
255             asm: asm,
256             asm_str_style: asm_str_style.unwrap(),
257             outputs: outputs,
258             inputs: inputs,
259             clobbers: clobs,
260             volatile: volatile,
261             alignstack: alignstack,
262             dialect: dialect,
263             expn_id: expn_id,
264         })),
265         span: sp,
266         attrs: ast::ThinVec::new(),
267     }))
268 }