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