]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/asm.rs
Auto merge of #44060 - taleks:issue-43205, r=arielb1
[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::ext::base;
17 use syntax::ext::base::*;
18 use syntax::feature_gate;
19 use syntax::parse::{self, token};
20 use syntax::ptr::P;
21 use syntax::symbol::Symbol;
22 use syntax::ast::AsmDialect;
23 use syntax_pos::Span;
24 use syntax::tokenstream;
25
26 enum State {
27     Asm,
28     Outputs,
29     Inputs,
30     Clobbers,
31     Options,
32     StateNone,
33 }
34
35 impl State {
36     fn next(&self) -> State {
37         match *self {
38             Asm => Outputs,
39             Outputs => Inputs,
40             Inputs => Clobbers,
41             Clobbers => Options,
42             Options => StateNone,
43             StateNone => StateNone,
44         }
45     }
46 }
47
48 const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
49
50 pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt,
51                        sp: Span,
52                        tts: &[tokenstream::TokenTree])
53                        -> Box<base::MacResult + 'cx> {
54     if !cx.ecfg.enable_asm() {
55         feature_gate::emit_feature_err(&cx.parse_sess,
56                                        "asm",
57                                        sp,
58                                        feature_gate::GateIssue::Language,
59                                        feature_gate::EXPLAIN_ASM);
60         return DummyResult::expr(sp);
61     }
62
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.
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());
74     let mut p = cx.new_parser_from_tts(&tts[first_colon..]);
75     let mut asm = Symbol::intern("");
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();
80     let mut volatile = false;
81     let mut alignstack = false;
82     let mut dialect = AsmDialect::Att;
83
84     let mut state = Asm;
85
86     'statement: loop {
87         match state {
88             Asm => {
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                 }
95                 // Nested parser, stop before the first colon (see above).
96                 let mut p2 = cx.new_parser_from_tts(&tts[..first_colon]);
97                 let (s, style) = match expr_to_string(cx,
98                                                       panictry!(p2.parse_expr()),
99                                                       "inline assembly must be a string literal") {
100                     Some((s, st)) => (s, st),
101                     // let compilation continue
102                     None => return DummyResult::expr(sp),
103                 };
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());
109                     p = parse::stream_to_parser(cx.parse_sess, extra_tts.into_iter().collect());
110                 }
111
112                 asm = s;
113                 asm_str_style = Some(style);
114             }
115             Outputs => {
116                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
117
118                     if !outputs.is_empty() {
119                         p.eat(&token::Comma);
120                     }
121
122                     let (constraint, _str_style) = panictry!(p.parse_str());
123
124                     let span = p.prev_span;
125
126                     panictry!(p.expect(&token::OpenDelim(token::Paren)));
127                     let out = panictry!(p.parse_expr());
128                     panictry!(p.expect(&token::CloseDelim(token::Paren)));
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.)
137                     let constraint_str = constraint.as_str();
138                     let mut ch = constraint_str.chars();
139                     let output = match ch.next() {
140                         Some('=') => None,
141                         Some('+') => {
142                             Some(Symbol::intern(&format!("={}", ch.as_str())))
143                         }
144                         _ => {
145                             cx.span_err(span, "output operand constraint lacks '=' or '+'");
146                             None
147                         }
148                     };
149
150                     let is_rw = output.is_some();
151                     let is_indirect = constraint_str.contains("*");
152                     outputs.push(ast::InlineAsmOutput {
153                         constraint: output.unwrap_or(constraint),
154                         expr: out,
155                         is_rw,
156                         is_indirect,
157                     });
158                 }
159             }
160             Inputs => {
161                 while p.token != token::Eof && p.token != token::Colon && p.token != token::ModSep {
162
163                     if !inputs.is_empty() {
164                         p.eat(&token::Comma);
165                     }
166
167                     let (constraint, _str_style) = panictry!(p.parse_str());
168
169                     if constraint.as_str().starts_with("=") {
170                         cx.span_err(p.prev_span, "input operand constraint contains '='");
171                     } else if constraint.as_str().starts_with("+") {
172                         cx.span_err(p.prev_span, "input operand constraint contains '+'");
173                     }
174
175                     panictry!(p.expect(&token::OpenDelim(token::Paren)));
176                     let input = panictry!(p.parse_expr());
177                     panictry!(p.expect(&token::CloseDelim(token::Paren)));
178
179                     inputs.push((constraint, input));
180                 }
181             }
182             Clobbers => {
183                 while p.token != token::Eof && p.token != token::Colon && 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.prev_span, "expected a clobber, found an option");
193                     } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") {
194                         cx.span_err(p.prev_span, "clobber should not be surrounded by braces");
195                     }
196
197                     clobs.push(s);
198                 }
199             }
200             Options => {
201                 let (option, _str_style) = panictry!(p.parse_str());
202
203                 if option == "volatile" {
204                     // Indicates that the inline assembly has side effects
205                     // and must not be optimized out along with its outputs.
206                     volatile = true;
207                 } else if option == "alignstack" {
208                     alignstack = true;
209                 } else if option == "intel" {
210                     dialect = AsmDialect::Intel;
211                 } else {
212                     cx.span_warn(p.prev_span, "unrecognized option");
213                 }
214
215                 if p.token == token::Comma {
216                     p.eat(&token::Comma);
217                 }
218             }
219             StateNone => (),
220         }
221
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()) {
226                 (&token::Colon, StateNone, _) |
227                 (&token::ModSep, _, StateNone) => {
228                     p.bump();
229                     break 'statement;
230                 }
231                 (&token::Colon, st, _) |
232                 (&token::ModSep, _, st) => {
233                     p.bump();
234                     state = st;
235                 }
236                 (&token::Eof, ..) => break 'statement,
237                 _ => break,
238             }
239         }
240     }
241
242     MacEager::expr(P(ast::Expr {
243         id: ast::DUMMY_NODE_ID,
244         node: ast::ExprKind::InlineAsm(P(ast::InlineAsm {
245             asm,
246             asm_str_style: asm_str_style.unwrap(),
247             outputs,
248             inputs,
249             clobbers: clobs,
250             volatile,
251             alignstack,
252             dialect,
253             ctxt: cx.backtrace(),
254         })),
255         span: sp,
256         attrs: ast::ThinVec::new(),
257     }))
258 }