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