Branch data Line data Source code
1 : : // Copyright (C) 2020-2025 Free Software Foundation, Inc.
2 : :
3 : : // This file is part of GCC.
4 : :
5 : : // GCC is free software; you can redistribute it and/or modify it under
6 : : // the terms of the GNU General Public License as published by the Free
7 : : // Software Foundation; either version 3, or (at your option) any later
8 : : // version.
9 : :
10 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : : // for more details.
14 : :
15 : : // You should have received a copy of the GNU General Public License
16 : : // along with GCC; see the file COPYING3. If not see
17 : : // <http://www.gnu.org/licenses/>.
18 : :
19 : : #include "expected.h"
20 : : #include "rust-macro-builtins-asm.h"
21 : : #include "rust-ast-fragment.h"
22 : : #include "rust-ast.h"
23 : : #include "rust-fmt.h"
24 : : #include "rust-stmt.h"
25 : : #include "rust-parse.h"
26 : :
27 : : namespace Rust {
28 : : std::set<std::string> potentially_nonpromoted_keywords
29 : : = {"in", "out", "lateout", "inout", "inlateout", "const", "sym", "label"};
30 : :
31 : : // Helper function strips the beginning and ending double quotes from a
32 : : // string.
33 : : std::string
34 : 110 : strip_double_quotes (const std::string &str)
35 : : {
36 : 110 : std::string result = str;
37 : :
38 : 110 : rust_assert (!str.empty ());
39 : :
40 : 110 : rust_assert (str.front () == '\"');
41 : 110 : rust_assert (str.back () == '\"');
42 : :
43 : : // we have to special case empty strings which just contain a set of quotes
44 : : // so, if the string is "\"\"", just return ""
45 : 110 : if (result.size () == 2)
46 : 3 : return "";
47 : :
48 : 107 : rust_assert (result.size () >= 3);
49 : :
50 : 107 : result.erase (0, 1);
51 : 107 : result.erase (result.size () - 1, 1);
52 : :
53 : 107 : return result;
54 : 110 : }
55 : :
56 : : tl::expected<InlineAsmContext, InlineAsmParseError>
57 : 3 : parse_clobber_abi (InlineAsmContext inline_asm_ctx)
58 : : {
59 : : // clobber_abi := "clobber_abi(" <abi> *("," <abi>) [","] ")"
60 : : // PARSE EVERYTHING COMMITTEDLY IN THIS FUNCTION, WE CONFIRMED VIA clobber_abi
61 : : // identifier keyword
62 : 3 : auto &parser = inline_asm_ctx.parser;
63 : 3 : auto last_token_id = inline_asm_ctx.last_token_id;
64 : 3 : auto &inline_asm = inline_asm_ctx.inline_asm;
65 : 3 : auto token = parser.peek_current_token ();
66 : 3 : if (!parser.skip_token (LEFT_PAREN))
67 : : {
68 : 2 : token = parser.peek_current_token ();
69 : :
70 : : // TODO: Error reporting shifted to the left 1 character, I'm not sure
71 : : // why.
72 : 2 : if (token->get_id () == last_token_id)
73 : : {
74 : 1 : rust_error_at (token->get_locus (),
75 : : "expected %<(%>, found end of macro arguments");
76 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
77 : : }
78 : : else
79 : : {
80 : 1 : rust_error_at (token->get_locus (), "expected %<(%>, found %qs",
81 : : token->get_token_description ());
82 : : }
83 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
84 : : }
85 : :
86 : 1 : if (parser.skip_token (RIGHT_PAREN))
87 : : {
88 : 1 : rust_error_at (
89 : 1 : parser.peek_current_token ()->get_locus (),
90 : : "at least one abi must be provided as an argument to %<clobber_abi%>");
91 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
92 : : }
93 : :
94 : 0 : std::vector<AST::TupleClobber> new_abis;
95 : :
96 : 0 : token = parser.peek_current_token ();
97 : :
98 : 0 : while (token->get_id () != last_token_id && token->get_id () != RIGHT_PAREN)
99 : : {
100 : : // Check if it is a string literal or not, codename: <ABI> in ABNF
101 : 0 : if (token->get_id () == STRING_LITERAL)
102 : : {
103 : : // TODO: Caring for span in here.
104 : 0 : new_abis.emplace_back (token->as_string (), token->get_locus ());
105 : : }
106 : : else
107 : : {
108 : : // TODO: We encountered something that is not string literal, which
109 : : // should be illegal, please emit the correct error
110 : : // https://github.com/rust-lang/rust/blob/b92758a9aef1cef7b79e2b72c3d8ba113e547f89/compiler/rustc_builtin_macros/src/asm.rs#L387
111 : 0 : rust_unreachable ();
112 : : }
113 : :
114 : 0 : if (parser.skip_token (RIGHT_PAREN))
115 : : {
116 : : break;
117 : : }
118 : :
119 : 0 : if (!parser.skip_token (COMMA))
120 : : {
121 : : // TODO: If the skip of comma is unsuccessful, which should be
122 : : // illegal, pleaes emit the correct error.
123 : 0 : rust_unreachable ();
124 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
125 : : }
126 : :
127 : 0 : token = parser.peek_current_token ();
128 : : }
129 : :
130 : : // Done processing the local clobber abis, push that to the main Args in
131 : : // argument
132 : :
133 : 0 : for (auto abi : new_abis)
134 : : {
135 : 0 : inline_asm.clobber_abi.push_back (abi);
136 : 0 : }
137 : :
138 : 0 : return inline_asm_ctx;
139 : 0 : }
140 : :
141 : : tl::optional<AST::InlineAsmRegOrRegClass>
142 : 32 : parse_reg (InlineAsmContext &inline_asm_ctx)
143 : : {
144 : 32 : using RegType = AST::InlineAsmRegOrRegClass::Type;
145 : 32 : auto &parser = inline_asm_ctx.parser;
146 : :
147 : 32 : if (!parser.skip_token (LEFT_PAREN))
148 : : {
149 : : // TODO: we expect a left parenthesis here, please return the correct
150 : : // error.
151 : 0 : rust_unreachable ();
152 : : return tl::nullopt;
153 : : }
154 : :
155 : : // after successful left parenthesis parsing, we should return ast of
156 : : // InlineAsmRegOrRegClass of reg or reg class
157 : 32 : auto token = parser.peek_current_token ();
158 : 32 : AST::InlineAsmRegOrRegClass reg_class;
159 : 32 : if (parser.skip_token (IDENTIFIER))
160 : : {
161 : : // construct a InlineAsmRegOrRegClass
162 : 31 : reg_class.type = RegType::RegClass;
163 : 31 : reg_class.reg_class.Symbol = token->as_string ();
164 : : }
165 : 1 : else if (parser.skip_token (STRING_LITERAL))
166 : : {
167 : : // TODO: there is STRING_LITERAL, and BYTE_STRING_LITERAL, should we check
168 : : // for both?
169 : :
170 : : // construct a InlineAsmRegOrRegClass
171 : : // parse_format_string
172 : 1 : reg_class.type = RegType::Reg;
173 : 1 : inline_asm_ctx.is_explicit = true;
174 : 1 : reg_class.reg_class.Symbol = token->as_string ();
175 : : }
176 : : else
177 : : {
178 : : // TODO: This should emit error
179 : : // return
180 : : // Err(p.dcx().create_err(errors::ExpectedRegisterClassOrExplicitRegister
181 : : // {
182 : : // span: p.token.span,
183 : : // }));
184 : 0 : rust_unreachable ();
185 : : }
186 : 32 : if (!parser.skip_token (RIGHT_PAREN))
187 : : {
188 : : // TODO: we expect a left parenthesis here, please return the correct
189 : : // error.
190 : 0 : rust_unreachable ();
191 : : return tl::nullopt;
192 : : }
193 : :
194 : 32 : return reg_class;
195 : 32 : }
196 : :
197 : : // From rustc
198 : : tl::expected<InlineAsmContext, InlineAsmParseError>
199 : 56 : parse_reg_operand (InlineAsmContext inline_asm_ctx)
200 : : {
201 : : // let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
202 : : // let (ident, _) = p.token.ident().unwrap();
203 : : // p.bump();
204 : : // p.expect(&token::Eq)?;
205 : : // allow_templates = false;
206 : : // Some(ident.name)
207 : : // } else {
208 : : // None
209 : : // };
210 : 56 : auto &parser = inline_asm_ctx.parser;
211 : 56 : auto token = parser.peek_current_token ();
212 : 56 : auto iden_token = parser.peek_current_token ();
213 : :
214 : 56 : tl::optional<std::string> name = tl::nullopt;
215 : 56 : if (check_identifier (parser, ""))
216 : : {
217 : 3 : auto equal_token = parser.peek_current_token ();
218 : :
219 : 3 : if (parser.skip_token (EQUAL))
220 : : {
221 : 3 : name = token->as_string ();
222 : : }
223 : : else
224 : : {
225 : 0 : rust_error_at (token->get_locus (),
226 : : "expected operand, %s, options, or "
227 : : "additional template string",
228 : : "clobber_abi");
229 : 0 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
230 : : }
231 : 3 : }
232 : :
233 : 56 : int slot = inline_asm_ctx.inline_asm.operands.size ();
234 : :
235 : : // Here is all parse_reg_operand functions we're using in a for loop
236 : 56 : auto parse_funcs = {parse_reg_operand_in, parse_reg_operand_out,
237 : : parse_reg_operand_lateout, parse_reg_operand_inout,
238 : : parse_reg_operand_const, parse_reg_operand_sym,
239 : 56 : parse_reg_operand_unexpected};
240 : :
241 : : // Loop over and execute the parsing functions, if the parser successfullly
242 : : // parses or if the parser fails to parse while it has committed to a token,
243 : : // we propogate the result.
244 : 56 : tl::expected<InlineAsmContext, InlineAsmParseError> parsing_operand (
245 : 56 : inline_asm_ctx);
246 : 226 : for (auto &parse_func : parse_funcs)
247 : : {
248 : 226 : auto result = parsing_operand.and_then (parse_func);
249 : :
250 : : // Per rust's asm.rs's structure
251 : : // After we've parse successfully, we break out and do a local validation
252 : : // of named, positional & explicit register operands
253 : :
254 : 226 : if (result.has_value ())
255 : : {
256 : 32 : inline_asm_ctx = *result;
257 : 32 : break;
258 : : }
259 : 194 : else if (result.error () == COMMITTED)
260 : : {
261 : 24 : if (parse_func == parse_reg_operand_unexpected)
262 : 24 : return inline_asm_ctx;
263 : : else
264 : 0 : return result;
265 : : }
266 : : }
267 : :
268 : 32 : auto &inline_asm = inline_asm_ctx.inline_asm;
269 : :
270 : 32 : token = inline_asm_ctx.parser.peek_current_token ();
271 : 32 : rust_debug_loc (token->get_locus (), "Here\n");
272 : 32 : if (inline_asm_ctx.is_explicit)
273 : : {
274 : 1 : if (name != tl::nullopt)
275 : : {
276 : 1 : rust_error_at (token->get_locus (),
277 : : "explicit register arguments cannot have names");
278 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
279 : : }
280 : 0 : inline_asm.reg_args.insert (slot);
281 : : }
282 : 31 : else if (name != tl::nullopt)
283 : : {
284 : 2 : if (inline_asm.named_args.find (name.value ())
285 : 2 : != inline_asm.named_args.end ())
286 : : {
287 : 1 : rust_error_at (token->get_locus (), "duplicate argument named %qs",
288 : 1 : name.value ().c_str ());
289 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
290 : : }
291 : 1 : inline_asm.named_args[name.value ()] = slot;
292 : : }
293 : : else
294 : : {
295 : 29 : if (!inline_asm.named_args.empty () || !inline_asm.reg_args.empty ())
296 : : {
297 : : // positional arguments cannot follow named arguments or explicit
298 : : // register arguments
299 : 0 : rust_error_at (token->get_locus (),
300 : : "positional arguments cannot follow named arguments "
301 : : "or explicit register arguments");
302 : 0 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
303 : : }
304 : : }
305 : :
306 : 30 : return inline_asm_ctx;
307 : 112 : }
308 : :
309 : : tl::expected<InlineAsmContext, InlineAsmParseError>
310 : 56 : parse_reg_operand_in (InlineAsmContext inline_asm_ctx)
311 : : {
312 : : // For the keyword IN, currently we count it as a seperate keyword called
313 : : // Rust::IN search for #define RS_TOKEN_LIST in code base.
314 : 56 : auto &parser = inline_asm_ctx.parser;
315 : 56 : location_t locus = parser.peek_current_token ()->get_locus ();
316 : 56 : if (!inline_asm_ctx.is_global_asm () && parser.skip_token (IN))
317 : : {
318 : 12 : auto reg = parse_reg (inline_asm_ctx);
319 : :
320 : 12 : if (parser.skip_token (UNDERSCORE))
321 : : {
322 : : // We are sure to be failing a test here, based on asm.rs
323 : : // https://github.com/rust-lang/rust/blob/a330e49593ee890f9197727a3a558b6e6b37f843/compiler/rustc_builtin_macros/src/asm.rs#L112
324 : 0 : rust_unreachable ();
325 : : // return tl::unexpected<InlineAsmParseError> (COMMITTED);
326 : : }
327 : :
328 : 12 : auto expr = parser.parse_expr ();
329 : :
330 : : // TODO: When we've succesfully parse an expr, remember to clone_expr()
331 : : // instead of nullptr
332 : 12 : AST::InlineAsmOperand::In in (reg, std::move (expr));
333 : 12 : inline_asm_ctx.inline_asm.operands.emplace_back (in, locus);
334 : 12 : return inline_asm_ctx;
335 : 24 : }
336 : 44 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
337 : : }
338 : :
339 : : tl::expected<InlineAsmContext, InlineAsmParseError>
340 : 44 : parse_reg_operand_out (InlineAsmContext inline_asm_ctx)
341 : : {
342 : 44 : auto &parser = inline_asm_ctx.parser;
343 : 44 : location_t locus = parser.peek_current_token ()->get_locus ();
344 : 88 : if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "out"))
345 : : {
346 : 17 : auto reg = parse_reg (inline_asm_ctx);
347 : 17 : std::unique_ptr<AST::Expr> expr = parser.parse_expr ();
348 : :
349 : 17 : rust_assert (expr != nullptr);
350 : :
351 : : /*auto expr_ptr =
352 : : std::make_unique<AST::Expr>(AST::LiteralExpr(Literal))*/
353 : :
354 : : // TODO: When we've succesfully parse an expr, remember to clone_expr()
355 : : // instead of nullptr
356 : 17 : AST::InlineAsmOperand::Out out (reg, false, std::move (expr));
357 : :
358 : 17 : inline_asm_ctx.inline_asm.operands.emplace_back (out, locus);
359 : :
360 : 17 : return inline_asm_ctx;
361 : 34 : }
362 : :
363 : 27 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
364 : : }
365 : :
366 : : tl::expected<InlineAsmContext, InlineAsmParseError>
367 : 27 : parse_reg_operand_lateout (InlineAsmContext inline_asm_ctx)
368 : : {
369 : 27 : auto &parser = inline_asm_ctx.parser;
370 : 27 : auto token = parser.peek_current_token ();
371 : 54 : if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "lateout"))
372 : : {
373 : 0 : rust_error_at (token->get_locus (),
374 : : "The lateout feature is not implemented");
375 : 0 : rust_unreachable ();
376 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
377 : : }
378 : :
379 : 27 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
380 : 27 : }
381 : :
382 : : tl::expected<InlineAsmContext, InlineAsmParseError>
383 : 27 : parse_reg_operand_inout (InlineAsmContext inline_asm_ctx)
384 : : {
385 : 27 : auto &parser = inline_asm_ctx.parser;
386 : 27 : auto token = parser.peek_current_token ();
387 : 27 : location_t locus = token->get_locus ();
388 : :
389 : 54 : if (!inline_asm_ctx.is_global_asm () && check_identifier (parser, "inout"))
390 : : {
391 : 3 : auto reg = parse_reg (inline_asm_ctx);
392 : :
393 : 3 : if (parser.skip_token (UNDERSCORE))
394 : : {
395 : : // We are sure to be failing a test here, based on asm.rs
396 : : // https://github.com/rust-lang/rust/blob/a330e49593ee890f9197727a3a558b6e6b37f843/compiler/rustc_builtin_macros/src/asm.rs#L112
397 : 0 : rust_error_at (token->get_locus (),
398 : : "The lateout feature is not implemented");
399 : 0 : rust_unreachable ();
400 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
401 : : }
402 : :
403 : : // TODO: Is error propogation our top priority, the ? in rust's asm.rs is
404 : : // doing a lot of work.
405 : 3 : std::unique_ptr<AST::Expr> in_expr = parser.parse_expr ();
406 : 3 : rust_assert (in_expr != nullptr);
407 : :
408 : 3 : std::unique_ptr<AST::Expr> out_expr;
409 : :
410 : 3 : if (parser.skip_token (MATCH_ARROW))
411 : : {
412 : 2 : if (!parser.skip_token (UNDERSCORE))
413 : : {
414 : : // auto result = parse_format_string (inline_asm_ctx);
415 : :
416 : 2 : out_expr = parser.parse_expr ();
417 : :
418 : 2 : AST::InlineAsmOperand::SplitInOut splitinout (
419 : 2 : reg, false, std::move (in_expr), std::move (out_expr));
420 : :
421 : 2 : inline_asm_ctx.inline_asm.operands.emplace_back (splitinout,
422 : : locus);
423 : :
424 : 2 : return inline_asm_ctx;
425 : 2 : }
426 : :
427 : 0 : rust_unreachable ();
428 : :
429 : : // TODO: Rembmer to pass in clone_expr() instead of nullptr
430 : : // https://github.com/rust-lang/rust/blob/a3167859f2fd8ff2241295469876a2b687280bdc/compiler/rustc_builtin_macros/src/asm.rs#L135
431 : : // RUST VERSION: ast::InlineAsmOperand::SplitInOut { reg, in_expr:
432 : : // expr, out_expr, late: false }
433 : : // struct AST::InlineAsmOperand::SplitInOut split_in_out (reg,
434 : : // false, nullptr,
435 : : // nullptr);
436 : : // inline_asm_ctx.inline_asm.operands.push_back (split_in_out);
437 : :
438 : : return inline_asm_ctx;
439 : : }
440 : : else
441 : : {
442 : 1 : AST::InlineAsmOperand::InOut inout (reg, false, std::move (in_expr));
443 : 1 : inline_asm_ctx.inline_asm.operands.emplace_back (inout, locus);
444 : : // https://github.com/rust-lang/rust/blob/a3167859f2fd8ff2241295469876a2b687280bdc/compiler/rustc_builtin_macros/src/asm.rs#L137
445 : : // RUST VERSION: ast::InlineAsmOperand::InOut { reg, expr, late: false
446 : : // }
447 : : // struct AST::InlineAsmOperand::InOut inout (reg, false,
448 : : // nullptr);
449 : : // inline_asm_ctx.inline_asm.operands.push_back (inout);
450 : 1 : return inline_asm_ctx;
451 : 1 : }
452 : 6 : }
453 : :
454 : 24 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
455 : 27 : }
456 : :
457 : : tl::expected<InlineAsmContext, InlineAsmParseError>
458 : 24 : parse_reg_operand_const (InlineAsmContext inline_asm_ctx)
459 : : {
460 : 24 : auto &parser = inline_asm_ctx.parser;
461 : 48 : if (parser.peek_current_token ()->get_id () == CONST)
462 : : {
463 : : // TODO: Please handle const with parse_expr instead.
464 : 0 : auto anon_const = parse_format_string (inline_asm_ctx);
465 : 0 : rust_unreachable ();
466 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
467 : : }
468 : :
469 : 24 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
470 : : }
471 : :
472 : : tl::expected<InlineAsmContext, InlineAsmParseError>
473 : 24 : parse_reg_operand_sym (InlineAsmContext inline_asm_ctx)
474 : : {
475 : 24 : auto &parser = inline_asm_ctx.parser;
476 : :
477 : 24 : if (check_identifier (parser, "sym"))
478 : : {
479 : : // TODO: Please handle sym, which needs ExprKind::Path in Rust's asm.rs
480 : 0 : rust_unreachable ();
481 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
482 : : }
483 : 24 : return tl::unexpected<InlineAsmParseError> (NONCOMMITED);
484 : : }
485 : :
486 : : tl::expected<InlineAsmContext, InlineAsmParseError>
487 : 24 : parse_reg_operand_unexpected (InlineAsmContext inline_asm_ctx)
488 : : {
489 : 24 : auto token = inline_asm_ctx.parser.peek_current_token ();
490 : : // TODO: It is weird that we can't seem to match any identifier,
491 : : // something must be wrong. consult compiler code in asm.rs or rust online
492 : : // compiler.
493 : : // rust_unreachable ();
494 : :
495 : : // rust_error_at (token->get_locus (), "ERROR RIGHT HERE");
496 : 24 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
497 : 24 : }
498 : :
499 : : void
500 : 9 : check_and_set (InlineAsmContext &inline_asm_ctx, AST::InlineAsm::Option option)
501 : : {
502 : 9 : auto &parser = inline_asm_ctx.parser;
503 : 9 : auto &inline_asm = inline_asm_ctx.inline_asm;
504 : 9 : if (inline_asm.options.count (option) != 0)
505 : : {
506 : : // TODO: report an error of duplication
507 : 4 : rust_error_at (parser.peek_current_token ()->get_locus (),
508 : : "the %qs option was already provided",
509 : 2 : AST::InlineAsm::option_to_string (option).c_str ());
510 : 2 : return;
511 : : }
512 : : else
513 : : {
514 : 7 : inline_asm.options.insert (option);
515 : : }
516 : : }
517 : : tl::expected<InlineAsmContext, InlineAsmParseError>
518 : 7 : parse_options (InlineAsmContext &inline_asm_ctx)
519 : : {
520 : 7 : auto &parser = inline_asm_ctx.parser;
521 : 7 : bool is_global_asm = inline_asm_ctx.inline_asm.is_global_asm;
522 : : // Parse everything commitedly
523 : 7 : if (!parser.skip_token (LEFT_PAREN))
524 : : {
525 : 1 : auto local_token = parser.peek_current_token ();
526 : 1 : rust_error_at (local_token->get_locus (), "expected %qs, found %qs", "(",
527 : 1 : local_token->as_string ().c_str ());
528 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
529 : 1 : }
530 : :
531 : 6 : auto token = parser.peek_current_token ();
532 : 11 : while (!parser.skip_token (RIGHT_PAREN))
533 : : {
534 : 22 : if (!is_global_asm && check_identifier (parser, "pure"))
535 : : {
536 : 0 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::PURE);
537 : : }
538 : 22 : else if (!is_global_asm && check_identifier (parser, "nomem"))
539 : : {
540 : 3 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::NOMEM);
541 : : }
542 : 16 : else if (!is_global_asm && check_identifier (parser, "readonly"))
543 : : {
544 : 0 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::READONLY);
545 : : }
546 : 16 : else if (!is_global_asm && check_identifier (parser, "preserves_flags"))
547 : : {
548 : 0 : check_and_set (inline_asm_ctx,
549 : : AST::InlineAsm::Option::PRESERVES_FLAGS);
550 : : }
551 : 16 : else if (!is_global_asm && check_identifier (parser, "noreturn"))
552 : : {
553 : 3 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::NORETURN);
554 : : }
555 : 10 : else if (!is_global_asm && check_identifier (parser, "nostack"))
556 : : {
557 : 1 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::NOSTACK);
558 : : }
559 : 8 : else if (!is_global_asm && check_identifier (parser, "may_unwind"))
560 : : {
561 : 0 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::MAY_UNWIND);
562 : : }
563 : 4 : else if (check_identifier (parser, "att_syntax"))
564 : : {
565 : 1 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::ATT_SYNTAX);
566 : : }
567 : 3 : else if (check_identifier (parser, "raw"))
568 : : {
569 : 1 : check_and_set (inline_asm_ctx, AST::InlineAsm::Option::RAW);
570 : : }
571 : : else
572 : : {
573 : 2 : rust_error_at (token->get_locus (),
574 : : "expected one of %qs, %qs, %qs, %qs, %qs, %qs, %qs, "
575 : : "%qs, %qs, or %qs, found %qs",
576 : : ")", "att_syntax", "may_unwind", "nomem", "noreturn",
577 : : "nostack", "preserves_flags", "pure", "raw",
578 : 2 : "readonly", token->as_string ().c_str ());
579 : 2 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
580 : : }
581 : 9 : if (parser.skip_token (RIGHT_PAREN))
582 : : {
583 : : break;
584 : : }
585 : :
586 : : // Parse comma as optional
587 : 5 : if (parser.skip_token (COMMA))
588 : 5 : continue;
589 : : else
590 : : {
591 : 0 : rust_unreachable ();
592 : : token = parser.peek_current_token ();
593 : : return tl::unexpected<InlineAsmParseError> (COMMITTED);
594 : : }
595 : : }
596 : :
597 : : // TODO: Per rust asm.rs regarding options_spans
598 : : // I'm guessing this has to do with some error reporting.
599 : : // let new_span = span_start.to(p.prev_token.span);
600 : : // args.options_spans.push(new_span);
601 : :
602 : 4 : return inline_asm_ctx;
603 : 6 : }
604 : :
605 : : bool
606 : 369 : check_identifier (Parser<MacroInvocLexer> &p, std::string ident)
607 : : {
608 : 369 : auto token = p.peek_current_token ();
609 : :
610 : 369 : if (token->get_id () == IDENTIFIER)
611 : : {
612 : 162 : auto str = token->as_string ();
613 : :
614 : : // For non-promoted keywords, we need to also check for them.
615 : :
616 : 162 : if (str == ident)
617 : : {
618 : 39 : p.skip_token ();
619 : 39 : return true;
620 : : }
621 : 123 : if (ident == "")
622 : : {
623 : 22 : if (potentially_nonpromoted_keywords.find (str)
624 : 22 : == potentially_nonpromoted_keywords.end ())
625 : : {
626 : 3 : p.skip_token ();
627 : 3 : return true;
628 : : }
629 : : return false;
630 : : }
631 : 162 : }
632 : :
633 : : return false;
634 : 369 : }
635 : :
636 : : tl::optional<std::string>
637 : 72 : parse_format_string (InlineAsmContext &inline_asm_ctx)
638 : : {
639 : 72 : auto &parser = inline_asm_ctx.parser;
640 : 72 : auto last_token_id = inline_asm_ctx.last_token_id;
641 : 72 : auto token = parser.peek_current_token ();
642 : :
643 : 72 : if (token->get_id () != last_token_id && token->get_id () == STRING_LITERAL)
644 : : {
645 : : // very nice, we got a supposedly formatted string.
646 : 38 : parser.skip_token ();
647 : 38 : return token->as_string ();
648 : : }
649 : : else
650 : : {
651 : 34 : return tl::nullopt;
652 : : }
653 : 72 : }
654 : :
655 : : tl::optional<AST::Fragment>
656 : 39 : MacroBuiltin::asm_handler (location_t invoc_locus, AST::MacroInvocData &invoc,
657 : : AST::InvocKind semicolon, AST::AsmKind is_global_asm)
658 : : {
659 : 39 : return parse_asm (invoc_locus, invoc, semicolon, is_global_asm);
660 : : }
661 : :
662 : : tl::optional<AST::Fragment>
663 : 15 : MacroBuiltin::llvm_asm_handler (location_t invoc_locus,
664 : : AST::MacroInvocData &invoc,
665 : : AST::InvocKind semicolon,
666 : : AST::AsmKind is_global_asm)
667 : : {
668 : 15 : return parse_llvm_asm (invoc_locus, invoc, semicolon, is_global_asm);
669 : : }
670 : :
671 : : tl::expected<InlineAsmContext, InlineAsmParseError>
672 : 37 : parse_asm_arg (InlineAsmContext inline_asm_ctx)
673 : : {
674 : 37 : auto &parser = inline_asm_ctx.parser;
675 : 37 : auto last_token_id = inline_asm_ctx.last_token_id;
676 : 37 : auto token = parser.peek_current_token ();
677 : 37 : tl::optional<std::string> fm_string;
678 : 95 : while (token->get_id () != last_token_id)
679 : : {
680 : 66 : token = parser.peek_current_token ();
681 : :
682 : 66 : if (token->get_id () == COLON || token->get_id () == SCOPE_RESOLUTION)
683 : : {
684 : 0 : rust_error_at (
685 : : token->get_locus (),
686 : : "the legacy LLVM-style %<asm!%> syntax is no longer supported");
687 : 8 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
688 : : }
689 : :
690 : : // We accept a comma token here.
691 : 66 : if (token->get_id () != COMMA
692 : 66 : && inline_asm_ctx.consumed_comma_without_formatted_string)
693 : : {
694 : : // if it is not a comma, but we consumed it previously, this is fine
695 : : // but we have to set it to false tho.
696 : 0 : inline_asm_ctx.consumed_comma_without_formatted_string = false;
697 : : }
698 : 66 : else if (token->get_id () == COMMA
699 : 66 : && !inline_asm_ctx.consumed_comma_without_formatted_string)
700 : : {
701 : 20 : inline_asm_ctx.consumed_comma_without_formatted_string = false;
702 : 20 : parser.skip_token ();
703 : : }
704 : 46 : else if (token->get_id () == COMMA
705 : 46 : && inline_asm_ctx.consumed_comma_without_formatted_string)
706 : : {
707 : : // We consumed comma, and there happens to also be a comma
708 : : // error should be: expected expression, found `,`
709 : 0 : rust_error_at (token->get_locus (), "expected expression, found %qs",
710 : : ",");
711 : 0 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
712 : 66 : break;
713 : : }
714 : :
715 : : // And if that token comma is also the trailing comma, we break
716 : 66 : token = parser.peek_current_token ();
717 : 66 : if (token->get_id () == COMMA && token->get_id () == last_token_id)
718 : : {
719 : 0 : parser.skip_token ();
720 : 0 : break;
721 : : }
722 : :
723 : : // Ok after the left paren is good, we better be parsing correctly
724 : : // everything in here, which is operand in ABNF
725 : :
726 : : // Parse clobber abi, eat the identifier named "clobber_abi" if true
727 : 66 : if (check_identifier (parser, "clobber_abi"))
728 : : {
729 : 3 : auto expected = parse_clobber_abi (inline_asm_ctx);
730 : 3 : if (expected.has_value ())
731 : 0 : continue;
732 : 3 : else if (expected.error () == COMMITTED)
733 : 3 : return expected;
734 : :
735 : : // The error type is definitely non-committed (we have checked above),
736 : : // we are allowed to keep on parsing
737 : : }
738 : :
739 : 63 : if (check_identifier (parser, "options"))
740 : : {
741 : 7 : auto expected = parse_options (inline_asm_ctx);
742 : 7 : if (expected.has_value ())
743 : 4 : continue;
744 : 3 : else if (expected.error () == COMMITTED)
745 : 3 : return expected;
746 : :
747 : : // The error type is definitely non-committed (we have checked above),
748 : : // we are allowed to keep on parsing
749 : : }
750 : :
751 : : // Ok after we have check that neither clobber_abi nor options works, the
752 : : // only other logical choice is reg_operand
753 : :
754 : 56 : auto expected = parse_reg_operand (inline_asm_ctx);
755 : 56 : if (expected.has_value ())
756 : 54 : continue;
757 : 2 : else if (expected.error () == COMMITTED)
758 : 2 : return expected;
759 : :
760 : : // Since parse_reg_operand is the last thing we've considered,
761 : : // The non-committed parse error type means that we have exhausted our
762 : : // search path
763 : :
764 : : // We then should return the error of COMMITTED, even though we have not
765 : : // committed to anything So that the error bubbles up and we recover from
766 : : // this error gracefully
767 : 0 : rust_error_at (token->get_locus (),
768 : : "expected operand, %s, options, or additional "
769 : : "template string",
770 : : "clobber_abi");
771 : 0 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
772 : : }
773 : 29 : return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
774 : 37 : }
775 : :
776 : : tl::expected<InlineAsmContext, InlineAsmParseError>
777 : 29 : expand_inline_asm_strings (InlineAsmContext inline_asm_ctx)
778 : : {
779 : 29 : auto &inline_asm = inline_asm_ctx.inline_asm;
780 : :
781 : 29 : auto str_vec = inline_asm.get_template_strs ();
782 : :
783 : 29 : decltype (str_vec) resulting_template_vec;
784 : 58 : for (auto &template_str : str_vec)
785 : : {
786 : : /*std::cout << template_str.symbol << std::endl;*/
787 : :
788 : 29 : auto pieces = Fmt::Pieces::collect (template_str.symbol, false,
789 : 29 : Fmt::ffi::ParseMode::InlineAsm);
790 : 29 : auto &pieces_vec = pieces.get_pieces ();
791 : :
792 : 29 : std::string transformed_template_str = "";
793 : 97 : for (size_t i = 0; i < pieces_vec.size (); i++)
794 : : {
795 : 68 : auto &piece = pieces_vec[i];
796 : 68 : if (piece.tag == Fmt::ffi::Piece::Tag::String)
797 : : {
798 : 80 : transformed_template_str += piece.string._0.to_string ();
799 : : }
800 : 28 : else if (piece.tag == Fmt::ffi::Piece::Tag::NextArgument)
801 : : {
802 : : /* std::cout << " " << i << ": "*/
803 : : /*<< piece.next_argument._0.to_string () << std::endl;*/
804 : :
805 : 28 : auto next_argument = piece.next_argument._0;
806 : 28 : switch (piece.next_argument._0.position.tag)
807 : : {
808 : 28 : case Fmt::ffi::Position::Tag::ArgumentImplicitlyIs:
809 : 28 : {
810 : 28 : auto idx = next_argument.position.argument_implicitly_is._0;
811 : : /*auto trait = next_argument.format;*/
812 : : /*auto arg = arguments.at (idx);*/
813 : :
814 : : /* // FIXME(Arthur): This API sucks*/
815 : : /* rust_assert (arg.get_kind ().kind*/
816 : : /*== AST::FormatArgumentKind::Kind::Normal);*/
817 : : /**/
818 : : /* args.push_back ({arg.get_expr ().clone_expr (),
819 : : * trait});*/
820 : :
821 : 56 : transformed_template_str += "%" + std::to_string (idx);
822 : : // std::cout << "argument implicitly is: " << idx <<
823 : : // std::endl; std::cout << "transformed template str is:"
824 : : // << transformed_template_str << std::endl;
825 : : /*std::cout << "trait: " << trait.to_string () <<
826 : : * std::endl;*/
827 : : /*std::cout << "arg: " << arg.to_string () << std::endl;*/
828 : : }
829 : 28 : break;
830 : 0 : case Fmt::ffi::Position::Tag::ArgumentIs:
831 : 0 : {
832 : 0 : auto idx = next_argument.position.argument_is._0;
833 : 0 : transformed_template_str += "%" + std::to_string (idx);
834 : 0 : break;
835 : : }
836 : 0 : case Fmt::ffi::Position::Tag::ArgumentNamed:
837 : 0 : rust_sorry_at (inline_asm.get_locus (),
838 : : "unhandled argument position specifier");
839 : 0 : break;
840 : : }
841 : 28 : }
842 : : }
843 : 29 : template_str.symbol = transformed_template_str;
844 : 58 : }
845 : :
846 : 29 : inline_asm.template_strs = str_vec;
847 : 29 : return inline_asm_ctx;
848 : 29 : }
849 : :
850 : : tl::optional<AST::Fragment>
851 : 39 : parse_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
852 : : AST::InvocKind semicolon, AST::AsmKind is_global_asm)
853 : : {
854 : : // From the rule of asm.
855 : : // We first parse all formatted strings. If we fail, then we return
856 : : // tl::nullopt
857 : :
858 : : // We then parse the asm arguments. If we fail, then we return
859 : : // tl::nullopt
860 : :
861 : : // We then validate. If we fail, then we return tl::nullopt
862 : :
863 : : // Done
864 : 39 : MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
865 : 39 : Parser<MacroInvocLexer> parser (lex);
866 : 39 : auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
867 : :
868 : 39 : AST::InlineAsm inline_asm (invoc_locus,
869 : 39 : is_global_asm == AST::AsmKind::Global);
870 : 39 : auto inline_asm_ctx = InlineAsmContext (inline_asm, parser, last_token_id);
871 : :
872 : 39 : auto resulting_context = parse_format_strings (inline_asm_ctx)
873 : 39 : .and_then (parse_asm_arg)
874 : 39 : .and_then (validate)
875 : 39 : .and_then (expand_inline_asm_strings);
876 : :
877 : : // TODO: I'm putting the validation here because the rust reference put
878 : : // it here Per Arthur's advice we would actually do the validation in a
879 : : // different stage. and visit on the InlineAsm AST instead of it's
880 : : // context.
881 : 39 : if (resulting_context)
882 : : {
883 : 29 : auto resulting_ctx = resulting_context.value ();
884 : 29 : auto node = resulting_ctx.inline_asm.clone_expr_without_block ();
885 : :
886 : 29 : std::vector<AST::SingleASTNode> single_vec = {};
887 : :
888 : : // If the macro invocation has a semicolon (`asm!("...");`), then we
889 : : // need to make it a statement. This way, it will be expanded
890 : : // properly.
891 : 29 : if (semicolon == AST::InvocKind::Semicoloned)
892 : 27 : single_vec.emplace_back (AST::SingleASTNode (
893 : 54 : std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
894 : : semicolon
895 : 54 : == AST::InvocKind::Semicoloned)));
896 : : else
897 : 2 : single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
898 : :
899 : 29 : AST::Fragment fragment_ast
900 : : = AST::Fragment (single_vec,
901 : 29 : std::vector<std::unique_ptr<AST::Token>> ());
902 : 29 : return fragment_ast;
903 : 29 : }
904 : : else
905 : : {
906 : 10 : return tl::nullopt;
907 : : }
908 : 78 : }
909 : :
910 : : tl::expected<InlineAsmContext, InlineAsmParseError>
911 : 39 : parse_format_strings (InlineAsmContext inline_asm_ctx)
912 : : {
913 : : // Parse the first ever formatted string, success or not, will skip 1
914 : : // token
915 : 39 : auto &parser = inline_asm_ctx.parser;
916 : 39 : auto last_token_id = inline_asm_ctx.last_token_id;
917 : 39 : auto fm_string = parse_format_string (inline_asm_ctx);
918 : :
919 : 39 : auto &inline_asm = inline_asm_ctx.inline_asm;
920 : 39 : auto token = parser.peek_current_token ();
921 : 39 : if (fm_string == tl::nullopt)
922 : : {
923 : 1 : rust_error_at (parser.peek_current_token ()->get_locus (),
924 : : "%s template must be a string literal", "asm");
925 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
926 : : }
927 : : else
928 : : {
929 : 38 : auto template_str
930 : : = AST::TupleTemplateStr (token->get_locus (),
931 : 76 : strip_double_quotes (fm_string.value ()));
932 : 38 : inline_asm.template_strs.push_back (template_str);
933 : 38 : }
934 : :
935 : : // formatted string stream
936 : :
937 : 76 : while (parser.peek_current_token ()->get_id () != last_token_id)
938 : : {
939 : 34 : if (!parser.skip_token (COMMA))
940 : : {
941 : 1 : rust_error_at (parser.peek_current_token ()->get_locus (),
942 : : "expected token %qs", ";");
943 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
944 : : }
945 : : // Ok after the comma is good, we better be parsing correctly
946 : : // everything in here, which is formatted string in ABNF
947 : 33 : inline_asm_ctx.consumed_comma_without_formatted_string = false;
948 : :
949 : 33 : token = parser.peek_current_token ();
950 : 33 : fm_string = parse_format_string (inline_asm_ctx);
951 : 33 : if (fm_string == tl::nullopt)
952 : : {
953 : 33 : inline_asm_ctx.consumed_comma_without_formatted_string = true;
954 : 33 : break;
955 : : }
956 : : else
957 : : {
958 : 0 : auto template_str
959 : : = AST::TupleTemplateStr (token->get_locus (),
960 : 0 : strip_double_quotes (fm_string.value ()));
961 : 0 : inline_asm.template_strs.push_back (template_str);
962 : 0 : }
963 : : }
964 : :
965 : 37 : return inline_asm_ctx;
966 : 39 : }
967 : :
968 : : // bool
969 : : // is_label (const std::string &potential_label)
970 : : // {
971 : :
972 : : // if (potential_label.empty () || potential_label.back () != ':')
973 : : // return false;
974 : :
975 : : // // Check if all characters before the last colon are digits
976 : : // for (size_t i = 0; i < potential_label.length () - 1; i++)
977 : : // {
978 : : // if (potential_label[i] < '0' || potential_label[i] > '9')
979 : : // return false;
980 : : // }
981 : :
982 : : // return true;
983 : : // }
984 : :
985 : : tl::expected<InlineAsmContext, InlineAsmParseError>
986 : 29 : validate (InlineAsmContext inline_asm_ctx)
987 : : {
988 : 29 : return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
989 : : }
990 : :
991 : : tl::optional<LlvmAsmContext>
992 : 15 : parse_llvm_templates (LlvmAsmContext ctx)
993 : : {
994 : 15 : auto &parser = ctx.parser;
995 : :
996 : 15 : auto token = parser.peek_current_token ();
997 : :
998 : 15 : if (token->get_id () == ctx.last_token_id
999 : 15 : || token->get_id () != STRING_LITERAL)
1000 : : {
1001 : 1 : return tl::nullopt;
1002 : : }
1003 : :
1004 : 28 : ctx.llvm_asm.get_templates ().emplace_back (token->get_locus (),
1005 : 28 : strip_double_quotes (
1006 : 14 : token->as_string ()));
1007 : 14 : ctx.parser.skip_token ();
1008 : :
1009 : 14 : token = parser.peek_current_token ();
1010 : 14 : if (token->get_id () != ctx.last_token_id && token->get_id () != COLON
1011 : 14 : && token->get_id () != SCOPE_RESOLUTION)
1012 : : {
1013 : : // We do not handle multiple template string, we provide minimal support
1014 : : // for the black_box intrinsics.
1015 : 0 : rust_unreachable ();
1016 : : }
1017 : :
1018 : 14 : return ctx;
1019 : 15 : }
1020 : :
1021 : : tl::optional<LlvmAsmContext>
1022 : 14 : parse_llvm_arguments (LlvmAsmContext ctx)
1023 : : {
1024 : 14 : auto &parser = ctx.parser;
1025 : 14 : enum State
1026 : : {
1027 : : Templates = 0,
1028 : : Output,
1029 : : Input,
1030 : : Clobbers,
1031 : : Options
1032 : 14 : } current_state
1033 : : = State::Templates;
1034 : :
1035 : 14 : while (parser.peek_current_token ()->get_id () != ctx.last_token_id
1036 : 222 : && parser.peek_current_token ()->get_id () != END_OF_FILE)
1037 : : {
1038 : 92 : if (parser.peek_current_token ()->get_id () == SCOPE_RESOLUTION)
1039 : : {
1040 : 0 : parser.skip_token (SCOPE_RESOLUTION);
1041 : 0 : current_state = static_cast<State> (current_state + 2);
1042 : : }
1043 : : else
1044 : : {
1045 : 46 : parser.skip_token (COLON);
1046 : 46 : current_state = static_cast<State> (current_state + 1);
1047 : : }
1048 : :
1049 : 46 : switch (current_state)
1050 : : {
1051 : 14 : case State::Output:
1052 : 14 : parse_llvm_outputs (ctx);
1053 : 14 : break;
1054 : 14 : case State::Input:
1055 : 14 : parse_llvm_inputs (ctx);
1056 : 14 : break;
1057 : 13 : case State::Clobbers:
1058 : 13 : parse_llvm_clobbers (ctx);
1059 : 13 : break;
1060 : 5 : case State::Options:
1061 : 5 : parse_llvm_options (ctx);
1062 : 5 : break;
1063 : 0 : case State::Templates:
1064 : 0 : default:
1065 : 0 : rust_unreachable ();
1066 : : }
1067 : : }
1068 : :
1069 : 14 : return ctx;
1070 : : }
1071 : :
1072 : : void
1073 : 28 : parse_llvm_operands (LlvmAsmContext &ctx, std::vector<AST::LlvmOperand> &result)
1074 : : {
1075 : 28 : auto &parser = ctx.parser;
1076 : 28 : auto token = parser.peek_current_token ();
1077 : 60 : while (token->get_id () != COLON && token->get_id () != SCOPE_RESOLUTION
1078 : 93 : && token->get_id () != ctx.last_token_id)
1079 : : {
1080 : 32 : std::string constraint;
1081 : 32 : if (token->get_id () == STRING_LITERAL)
1082 : : {
1083 : 32 : constraint = strip_double_quotes (token->as_string ());
1084 : : }
1085 : 32 : parser.skip_token (STRING_LITERAL);
1086 : 32 : parser.skip_token (LEFT_PAREN);
1087 : :
1088 : 32 : token = parser.peek_current_token ();
1089 : :
1090 : 32 : ParseRestrictions restrictions;
1091 : 32 : restrictions.expr_can_be_null = true;
1092 : 32 : auto expr = parser.parse_expr ();
1093 : :
1094 : 32 : parser.skip_token (RIGHT_PAREN);
1095 : :
1096 : 32 : result.emplace_back (constraint, std::move (expr));
1097 : :
1098 : 64 : if (parser.peek_current_token ()->get_id () == COMMA)
1099 : 9 : parser.skip_token (COMMA);
1100 : :
1101 : 32 : token = parser.peek_current_token ();
1102 : 32 : }
1103 : 28 : }
1104 : :
1105 : : void
1106 : 14 : parse_llvm_outputs (LlvmAsmContext &ctx)
1107 : : {
1108 : 14 : parse_llvm_operands (ctx, ctx.llvm_asm.get_outputs ());
1109 : 14 : }
1110 : :
1111 : : void
1112 : 14 : parse_llvm_inputs (LlvmAsmContext &ctx)
1113 : : {
1114 : 14 : parse_llvm_operands (ctx, ctx.llvm_asm.get_inputs ());
1115 : 14 : }
1116 : :
1117 : : void
1118 : 13 : parse_llvm_clobbers (LlvmAsmContext &ctx)
1119 : : {
1120 : 13 : auto &parser = ctx.parser;
1121 : 13 : auto token = parser.peek_current_token ();
1122 : 34 : while (token->get_id () != COLON && token->get_id () != ctx.last_token_id)
1123 : : {
1124 : 21 : if (token->get_id () == STRING_LITERAL)
1125 : : {
1126 : 63 : ctx.llvm_asm.get_clobbers ().push_back (
1127 : 42 : {strip_double_quotes (token->as_string ()), token->get_locus ()});
1128 : :
1129 : 21 : parser.skip_token (STRING_LITERAL);
1130 : : }
1131 : :
1132 : 21 : parser.maybe_skip_token (COMMA);
1133 : 21 : token = parser.peek_current_token ();
1134 : : }
1135 : 13 : }
1136 : :
1137 : : void
1138 : 5 : parse_llvm_options (LlvmAsmContext &ctx)
1139 : : {
1140 : 5 : auto &parser = ctx.parser;
1141 : 5 : auto token = parser.peek_current_token ();
1142 : :
1143 : 10 : while (token->get_id () != ctx.last_token_id)
1144 : : {
1145 : 5 : if (token->get_id () == STRING_LITERAL)
1146 : : {
1147 : 5 : auto token_str = strip_double_quotes (token->as_string ());
1148 : :
1149 : 5 : if (token_str == "volatile")
1150 : 5 : ctx.llvm_asm.set_volatile (true);
1151 : 0 : else if (token_str == "alignstack")
1152 : 0 : ctx.llvm_asm.set_align_stack (true);
1153 : 0 : else if (token_str == "intel")
1154 : 0 : ctx.llvm_asm.set_dialect (AST::LlvmInlineAsm::Dialect::Intel);
1155 : : else
1156 : 0 : rust_error_at (token->get_locus (),
1157 : : "Unknown llvm assembly option %qs",
1158 : : token_str.c_str ());
1159 : 5 : }
1160 : 5 : parser.skip_token (STRING_LITERAL);
1161 : :
1162 : 5 : token = parser.peek_current_token ();
1163 : :
1164 : 5 : if (token->get_id () == ctx.last_token_id)
1165 : 5 : continue;
1166 : 0 : parser.skip_token (COMMA);
1167 : : }
1168 : :
1169 : 5 : parser.skip_token ();
1170 : 5 : }
1171 : :
1172 : : tl::optional<AST::Fragment>
1173 : 15 : parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
1174 : : AST::InvocKind semicolon, AST::AsmKind is_global_asm)
1175 : : {
1176 : 15 : MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
1177 : 15 : Parser<MacroInvocLexer> parser (lex);
1178 : 15 : auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
1179 : :
1180 : 15 : AST::LlvmInlineAsm llvm_asm{invoc_locus};
1181 : :
1182 : 15 : auto asm_ctx = LlvmAsmContext (llvm_asm, parser, last_token_id);
1183 : :
1184 : 15 : tl::optional<LlvmAsmContext> resulting_context
1185 : 15 : = parse_llvm_templates (asm_ctx).and_then (parse_llvm_arguments);
1186 : :
1187 : 15 : if (resulting_context)
1188 : : {
1189 : 14 : auto resulting_ctx = resulting_context.value ();
1190 : 14 : auto node = resulting_ctx.llvm_asm.clone_expr_without_block ();
1191 : :
1192 : 14 : std::vector<AST::SingleASTNode> single_vec = {};
1193 : :
1194 : : // If the macro invocation has a semicolon (`asm!("...");`), then we
1195 : : // need to make it a statement. This way, it will be expanded
1196 : : // properly.
1197 : 14 : if (semicolon == AST::InvocKind::Semicoloned)
1198 : : {
1199 : 14 : single_vec.emplace_back (
1200 : 14 : std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
1201 : 28 : true /* has semicolon */));
1202 : : }
1203 : : else
1204 : 0 : single_vec.emplace_back (std::move (node));
1205 : :
1206 : 14 : AST::Fragment fragment_ast
1207 : : = AST::Fragment (single_vec,
1208 : 14 : std::vector<std::unique_ptr<AST::Token>> ());
1209 : 14 : return fragment_ast;
1210 : 14 : }
1211 : 1 : return tl::nullopt;
1212 : 30 : }
1213 : :
1214 : : } // namespace Rust
|