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