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