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