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 : 46 : strip_double_quotes (const std::string &str)
35 : : {
36 : 46 : std::string result = str;
37 : :
38 : 46 : rust_assert (!str.empty ());
39 : :
40 : 46 : rust_assert (str.front () == '\"');
41 : 46 : 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 : 46 : if (result.size () == 2)
46 : 2 : return "";
47 : :
48 : 44 : rust_assert (result.size () >= 3);
49 : :
50 : 44 : result.erase (0, 1);
51 : 44 : result.erase (result.size () - 1, 1);
52 : :
53 : 44 : return result;
54 : 46 : }
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.push_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 : 2 : MacroBuiltin::llvm_asm_handler (location_t invoc_locus,
664 : : AST::MacroInvocData &invoc,
665 : : AST::InvocKind semicolon,
666 : : AST::AsmKind is_global_asm)
667 : : {
668 : 2 : 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 : : }
842 : : }
843 : 29 : template_str.symbol = transformed_template_str;
844 : 29 : }
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 node = (*resulting_context).inline_asm.clone_expr_without_block ();
884 : :
885 : 29 : std::vector<AST::SingleASTNode> single_vec = {};
886 : :
887 : : // If the macro invocation has a semicolon (`asm!("...");`), then we
888 : : // need to make it a statement. This way, it will be expanded
889 : : // properly.
890 : 29 : if (semicolon == AST::InvocKind::Semicoloned)
891 : 27 : single_vec.emplace_back (AST::SingleASTNode (
892 : 54 : std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
893 : : semicolon
894 : 54 : == AST::InvocKind::Semicoloned)));
895 : : else
896 : 2 : single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
897 : :
898 : 29 : AST::Fragment fragment_ast
899 : : = AST::Fragment (single_vec,
900 : 29 : std::vector<std::unique_ptr<AST::Token>> ());
901 : 29 : return fragment_ast;
902 : 29 : }
903 : : else
904 : : {
905 : 10 : return tl::nullopt;
906 : : }
907 : 78 : }
908 : :
909 : : tl::expected<InlineAsmContext, InlineAsmParseError>
910 : 39 : parse_format_strings (InlineAsmContext inline_asm_ctx)
911 : : {
912 : : // Parse the first ever formatted string, success or not, will skip 1
913 : : // token
914 : 39 : auto &parser = inline_asm_ctx.parser;
915 : 39 : auto last_token_id = inline_asm_ctx.last_token_id;
916 : 39 : auto fm_string = parse_format_string (inline_asm_ctx);
917 : :
918 : 39 : auto &inline_asm = inline_asm_ctx.inline_asm;
919 : 39 : auto token = parser.peek_current_token ();
920 : 39 : if (fm_string == tl::nullopt)
921 : : {
922 : 1 : rust_error_at (parser.peek_current_token ()->get_locus (),
923 : : "%s template must be a string literal", "asm");
924 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
925 : : }
926 : : else
927 : : {
928 : 38 : auto template_str
929 : : = AST::TupleTemplateStr (token->get_locus (),
930 : 76 : strip_double_quotes (fm_string.value ()));
931 : 38 : inline_asm.template_strs.push_back (template_str);
932 : 38 : }
933 : :
934 : : // formatted string stream
935 : :
936 : 76 : while (parser.peek_current_token ()->get_id () != last_token_id)
937 : : {
938 : 34 : if (!parser.skip_token (COMMA))
939 : : {
940 : 1 : rust_error_at (parser.peek_current_token ()->get_locus (),
941 : : "expected token %qs", ";");
942 : 1 : return tl::unexpected<InlineAsmParseError> (COMMITTED);
943 : : }
944 : : // Ok after the comma is good, we better be parsing correctly
945 : : // everything in here, which is formatted string in ABNF
946 : 33 : inline_asm_ctx.consumed_comma_without_formatted_string = false;
947 : :
948 : 33 : token = parser.peek_current_token ();
949 : 33 : fm_string = parse_format_string (inline_asm_ctx);
950 : 33 : if (fm_string == tl::nullopt)
951 : : {
952 : 33 : inline_asm_ctx.consumed_comma_without_formatted_string = true;
953 : 33 : break;
954 : : }
955 : : else
956 : : {
957 : 0 : auto template_str
958 : : = AST::TupleTemplateStr (token->get_locus (),
959 : 0 : strip_double_quotes (fm_string.value ()));
960 : 0 : inline_asm.template_strs.push_back (template_str);
961 : 0 : }
962 : : }
963 : :
964 : 37 : return inline_asm_ctx;
965 : 39 : }
966 : :
967 : : // bool
968 : : // is_label (const std::string &potential_label)
969 : : // {
970 : :
971 : : // if (potential_label.empty () || potential_label.back () != ':')
972 : : // return false;
973 : :
974 : : // // Check if all characters before the last colon are digits
975 : : // for (size_t i = 0; i < potential_label.length () - 1; i++)
976 : : // {
977 : : // if (potential_label[i] < '0' || potential_label[i] > '9')
978 : : // return false;
979 : : // }
980 : :
981 : : // return true;
982 : : // }
983 : :
984 : : tl::expected<InlineAsmContext, InlineAsmParseError>
985 : 29 : validate (InlineAsmContext inline_asm_ctx)
986 : : {
987 : 29 : return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
988 : : }
989 : :
990 : : tl::optional<LlvmAsmContext>
991 : 2 : parse_llvm_templates (LlvmAsmContext ctx)
992 : : {
993 : 2 : auto &parser = ctx.parser;
994 : :
995 : 2 : auto token = parser.peek_current_token ();
996 : :
997 : 2 : if (token->get_id () == ctx.last_token_id
998 : 2 : || token->get_id () != STRING_LITERAL)
999 : : {
1000 : 0 : return tl::nullopt;
1001 : : }
1002 : :
1003 : 4 : ctx.llvm_asm.get_templates ().emplace_back (token->get_locus (),
1004 : 4 : strip_double_quotes (
1005 : 2 : token->as_string ()));
1006 : 2 : ctx.parser.skip_token ();
1007 : :
1008 : 2 : token = parser.peek_current_token ();
1009 : 2 : if (token->get_id () != ctx.last_token_id && token->get_id () != COLON
1010 : 2 : && token->get_id () != SCOPE_RESOLUTION)
1011 : : {
1012 : : // We do not handle multiple template string, we provide minimal support
1013 : : // for the black_box intrinsics.
1014 : 0 : rust_unreachable ();
1015 : : }
1016 : :
1017 : 2 : return ctx;
1018 : 2 : }
1019 : :
1020 : : tl::optional<LlvmAsmContext>
1021 : 2 : parse_llvm_arguments (LlvmAsmContext ctx)
1022 : : {
1023 : 2 : auto &parser = ctx.parser;
1024 : 2 : enum State
1025 : : {
1026 : : Templates = 0,
1027 : : Output,
1028 : : Input,
1029 : : Clobbers,
1030 : : Options
1031 : 2 : } current_state
1032 : : = State::Templates;
1033 : :
1034 : 2 : while (parser.peek_current_token ()->get_id () != ctx.last_token_id
1035 : 40 : && parser.peek_current_token ()->get_id () != END_OF_FILE)
1036 : : {
1037 : 16 : if (parser.peek_current_token ()->get_id () == SCOPE_RESOLUTION)
1038 : : {
1039 : 0 : parser.skip_token (SCOPE_RESOLUTION);
1040 : 0 : current_state = static_cast<State> (current_state + 2);
1041 : : }
1042 : : else
1043 : : {
1044 : 8 : parser.skip_token (COLON);
1045 : 8 : current_state = static_cast<State> (current_state + 1);
1046 : : }
1047 : :
1048 : 8 : switch (current_state)
1049 : : {
1050 : 2 : case State::Output:
1051 : 2 : parse_llvm_outputs (ctx);
1052 : 2 : break;
1053 : 2 : case State::Input:
1054 : 2 : parse_llvm_inputs (ctx);
1055 : 2 : break;
1056 : 2 : case State::Clobbers:
1057 : 2 : parse_llvm_clobbers (ctx);
1058 : 2 : break;
1059 : 2 : case State::Options:
1060 : 2 : parse_llvm_options (ctx);
1061 : 2 : break;
1062 : 0 : case State::Templates:
1063 : 0 : default:
1064 : 0 : rust_unreachable ();
1065 : : }
1066 : : }
1067 : :
1068 : 2 : return ctx;
1069 : : }
1070 : :
1071 : : void
1072 : 4 : parse_llvm_operands (LlvmAsmContext &ctx, std::vector<AST::LlvmOperand> &result)
1073 : : {
1074 : 4 : auto &parser = ctx.parser;
1075 : 4 : auto token = parser.peek_current_token ();
1076 : 6 : while (token->get_id () != COLON && token->get_id () != SCOPE_RESOLUTION
1077 : 8 : && token->get_id () != ctx.last_token_id)
1078 : : {
1079 : 2 : std::string constraint;
1080 : 2 : if (token->get_id () == STRING_LITERAL)
1081 : : {
1082 : 2 : constraint = strip_double_quotes (token->as_string ());
1083 : : }
1084 : 2 : parser.skip_token (STRING_LITERAL);
1085 : 2 : parser.skip_token (LEFT_PAREN);
1086 : :
1087 : 2 : token = parser.peek_current_token ();
1088 : :
1089 : 2 : ParseRestrictions restrictions;
1090 : 2 : restrictions.expr_can_be_null = true;
1091 : 2 : auto expr = parser.parse_expr ();
1092 : :
1093 : 2 : parser.skip_token (RIGHT_PAREN);
1094 : :
1095 : 2 : result.emplace_back (constraint, std::move (expr));
1096 : :
1097 : 4 : if (parser.peek_current_token ()->get_id () == COMMA)
1098 : 0 : parser.skip_token (COMMA);
1099 : :
1100 : 2 : token = parser.peek_current_token ();
1101 : 2 : }
1102 : 4 : }
1103 : :
1104 : : void
1105 : 2 : parse_llvm_outputs (LlvmAsmContext &ctx)
1106 : : {
1107 : 2 : parse_llvm_operands (ctx, ctx.llvm_asm.get_outputs ());
1108 : 2 : }
1109 : :
1110 : : void
1111 : 2 : parse_llvm_inputs (LlvmAsmContext &ctx)
1112 : : {
1113 : 2 : parse_llvm_operands (ctx, ctx.llvm_asm.get_inputs ());
1114 : 2 : }
1115 : :
1116 : : void
1117 : 2 : parse_llvm_clobbers (LlvmAsmContext &ctx)
1118 : : {
1119 : 2 : auto &parser = ctx.parser;
1120 : 2 : auto token = parser.peek_current_token ();
1121 : 4 : while (token->get_id () != COLON && token->get_id () != ctx.last_token_id)
1122 : : {
1123 : 2 : if (token->get_id () == STRING_LITERAL)
1124 : : {
1125 : 2 : ctx.llvm_asm.get_clobbers ().push_back (
1126 : 6 : {strip_double_quotes (token->as_string ()), token->get_locus ()});
1127 : : }
1128 : 2 : parser.skip_token (STRING_LITERAL);
1129 : 2 : token = parser.peek_current_token ();
1130 : : }
1131 : 2 : }
1132 : :
1133 : : void
1134 : 2 : parse_llvm_options (LlvmAsmContext &ctx)
1135 : : {
1136 : 2 : auto &parser = ctx.parser;
1137 : 2 : auto token = parser.peek_current_token ();
1138 : :
1139 : 4 : while (token->get_id () != ctx.last_token_id)
1140 : : {
1141 : 2 : if (token->get_id () == STRING_LITERAL)
1142 : : {
1143 : 2 : auto token_str = strip_double_quotes (token->as_string ());
1144 : :
1145 : 2 : if (token_str == "volatile")
1146 : 2 : ctx.llvm_asm.set_volatile (true);
1147 : 0 : else if (token_str == "alignstack")
1148 : 0 : ctx.llvm_asm.set_align_stack (true);
1149 : 0 : else if (token_str == "intel")
1150 : 0 : ctx.llvm_asm.set_dialect (AST::LlvmInlineAsm::Dialect::Intel);
1151 : : else
1152 : 0 : rust_error_at (token->get_locus (),
1153 : : "Unknown llvm assembly option %qs",
1154 : : token_str.c_str ());
1155 : 2 : }
1156 : 2 : parser.skip_token (STRING_LITERAL);
1157 : :
1158 : 2 : token = parser.peek_current_token ();
1159 : :
1160 : 2 : if (token->get_id () == ctx.last_token_id)
1161 : 2 : continue;
1162 : 0 : parser.skip_token (COMMA);
1163 : : }
1164 : :
1165 : 2 : parser.skip_token ();
1166 : 2 : }
1167 : :
1168 : : tl::optional<AST::Fragment>
1169 : 2 : parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
1170 : : AST::InvocKind semicolon, AST::AsmKind is_global_asm)
1171 : : {
1172 : 2 : MacroInvocLexer lex (invoc.get_delim_tok_tree ().to_token_stream ());
1173 : 2 : Parser<MacroInvocLexer> parser (lex);
1174 : 2 : auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
1175 : :
1176 : 2 : AST::LlvmInlineAsm llvm_asm{invoc_locus};
1177 : :
1178 : 2 : auto asm_ctx = LlvmAsmContext (llvm_asm, parser, last_token_id);
1179 : :
1180 : 2 : auto resulting_context
1181 : 2 : = parse_llvm_templates (asm_ctx).and_then (parse_llvm_arguments);
1182 : :
1183 : 2 : if (resulting_context)
1184 : : {
1185 : 2 : auto node = (*resulting_context).llvm_asm.clone_expr_without_block ();
1186 : :
1187 : 2 : std::vector<AST::SingleASTNode> single_vec = {};
1188 : :
1189 : : // If the macro invocation has a semicolon (`asm!("...");`), then we
1190 : : // need to make it a statement. This way, it will be expanded
1191 : : // properly.
1192 : 2 : if (semicolon == AST::InvocKind::Semicoloned)
1193 : 2 : single_vec.emplace_back (AST::SingleASTNode (
1194 : 4 : std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
1195 : : semicolon
1196 : 4 : == AST::InvocKind::Semicoloned)));
1197 : : else
1198 : 0 : single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
1199 : :
1200 : 2 : AST::Fragment fragment_ast
1201 : : = AST::Fragment (single_vec,
1202 : 2 : std::vector<std::unique_ptr<AST::Token>> ());
1203 : 2 : return fragment_ast;
1204 : 2 : }
1205 : 0 : return tl::nullopt;
1206 : 4 : }
1207 : :
1208 : : } // namespace Rust
|