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