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