]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Parse and lower llvm asm node
authorPierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
Tue, 15 Apr 2025 09:38:29 +0000 (11:38 +0200)
committerP-E-P <32375388+P-E-P@users.noreply.github.com>
Thu, 17 Apr 2025 18:05:17 +0000 (18:05 +0000)
Add a new HIR LlvmInlineAsm HIR node as well as some structures to
represent it's options and operands. Lower AST::LlvmInlineAsm node to it
and then create a tree from that node.

gcc/rust/ChangeLog:

* ast/rust-ast-collector.cc (TokenCollector::visit): Remove unreachable
code.
* ast/rust-expr.h (struct LlvmOperand): Add LlvmOperand struct to
represent input and outputs.
(class LlvmInlineAsm): Add input, output and clobber operands.
(struct TupleTemplateStr): Add locus getter.
* backend/rust-compile-block.h: Add visit for LlvmInlineAsm.
* backend/rust-compile-expr.cc (CompileExpr::visit): Add llvm inline
asm stmt compilation.
* backend/rust-compile-expr.h: Add function prototype.
* backend/rust-compile-asm.h (class CompileLlvmAsm): Add llvm asm hir
not to gimple.
* backend/rust-compile-asm.cc (CompileLlvmAsm::CompileLlvmAsm): Add
constructor.
(CompileLlvmAsm::construct_operands): Add function to construct operand
tree.
(CompileLlvmAsm::construct_clobbers): Add function to construct clobber
tree.
(CompileLlvmAsm::tree_codegen_asm): Generate the whole tree for a given
llvm inline assembly node.
* checks/errors/borrowck/rust-bir-builder-expr-stmt.cc (ExprStmtBuilder::visit):
Add visit function.
* checks/errors/borrowck/rust-bir-builder-expr-stmt.h: Add function
prototype.
* checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h: Add visit
function.
* checks/errors/borrowck/rust-bir-builder-struct.h: Likewise.
* checks/errors/borrowck/rust-function-collector.h: Likewise.
* checks/errors/privacy/rust-privacy-reporter.cc (PrivacyReporter::visit):
Likewise.
* checks/errors/privacy/rust-privacy-reporter.h: Add visit function
prototype.
* checks/errors/rust-const-checker.cc (ConstChecker::visit): Add visit
function.
* checks/errors/rust-const-checker.h: Add visit function prototype.
* checks/errors/rust-hir-pattern-analysis.cc (PatternChecker::visit):
Add visit function.
* checks/errors/rust-hir-pattern-analysis.h: Add visit function
prototype.
* checks/errors/rust-unsafe-checker.cc (UnsafeChecker::visit): Add
visit function.
* checks/errors/rust-unsafe-checker.h: Add function prototype.
* expand/rust-macro-builtins-asm.cc (parse_llvm_templates): Parse
templates.
(parse_llvm_arguments): Add function to parse non template tokens.
(parse_llvm_operands): Add function to parse operands, either input or
output.
(parse_llvm_outputs): Add function to parse and collect llvm asm
outputs.
(parse_llvm_inputs): Likewise with inputs.
(parse_llvm_clobbers): Add function to parse llvm asm clobbers.
(parse_llvm_options): Add function to parse llvm asm options.
(parse_llvm_asm): Add function to parse llvm asm.
* expand/rust-macro-builtins-asm.h (class LlvmAsmContext): Add context
for llvm asm parser.
(parse_llvm_outputs): Add function prototype.
(parse_llvm_inputs): Likewise.
(parse_llvm_clobbers): Likewise.
(parse_llvm_options): Likewise.
* hir/rust-ast-lower-expr.cc (ASTLoweringExpr::visit): Lower AST llvm
asm node to HIR.
* hir/rust-ast-lower-expr.h: Add function prototype.
* hir/rust-hir-dump.cc (Dump::visit): Add visit function.
* hir/rust-hir-dump.h: Add function prototype.
* hir/tree/rust-hir-expr-abstract.h: Add HIR llvm asm node kind.
* hir/tree/rust-hir-expr.h (struct LlvmOperand): Add LlvmOperand type
to represent input and outputs.
(class LlvmInlineAsm): Add LlvmInlineAsm hir node.
* hir/tree/rust-hir-full-decls.h (class LlvmInlineAsm): Add
LlvmInlineAsm hir node forward declaration.
* hir/tree/rust-hir-visitor.h: Add visit functions for LlvmInlineAsm
hir node.
* hir/tree/rust-hir.cc (LlvmInlineAsm::accept_vis): Add hir node
visitor related functions.
* typecheck/rust-hir-type-check-expr.cc (TypeCheckExpr::visit):
Type check input and output operands.
* typecheck/rust-hir-type-check-expr.h: Add function prototype.
* ast/rust-ast-visitor.cc (DefaultASTVisitor::visit): Visit input and
output operand expressions.
* resolve/rust-ast-resolve-expr.cc (ResolveExpr::visit): Resolve input
and output expressions.
* resolve/rust-ast-resolve-expr.h: Add function prototypes.

Signed-off-by: Pierre-Emmanuel Patry <pierre-emmanuel.patry@embecosm.com>
36 files changed:
gcc/rust/ast/rust-ast-collector.cc
gcc/rust/ast/rust-ast-visitor.cc
gcc/rust/ast/rust-expr.h
gcc/rust/backend/rust-compile-asm.cc
gcc/rust/backend/rust-compile-asm.h
gcc/rust/backend/rust-compile-block.h
gcc/rust/backend/rust-compile-expr.cc
gcc/rust/backend/rust-compile-expr.h
gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.cc
gcc/rust/checks/errors/borrowck/rust-bir-builder-expr-stmt.h
gcc/rust/checks/errors/borrowck/rust-bir-builder-lazyboolexpr.h
gcc/rust/checks/errors/borrowck/rust-bir-builder-struct.h
gcc/rust/checks/errors/borrowck/rust-function-collector.h
gcc/rust/checks/errors/privacy/rust-privacy-reporter.cc
gcc/rust/checks/errors/privacy/rust-privacy-reporter.h
gcc/rust/checks/errors/rust-const-checker.cc
gcc/rust/checks/errors/rust-const-checker.h
gcc/rust/checks/errors/rust-hir-pattern-analysis.cc
gcc/rust/checks/errors/rust-hir-pattern-analysis.h
gcc/rust/checks/errors/rust-unsafe-checker.cc
gcc/rust/checks/errors/rust-unsafe-checker.h
gcc/rust/expand/rust-macro-builtins-asm.cc
gcc/rust/expand/rust-macro-builtins-asm.h
gcc/rust/hir/rust-ast-lower-expr.cc
gcc/rust/hir/rust-ast-lower-expr.h
gcc/rust/hir/rust-hir-dump.cc
gcc/rust/hir/rust-hir-dump.h
gcc/rust/hir/tree/rust-hir-expr-abstract.h
gcc/rust/hir/tree/rust-hir-expr.h
gcc/rust/hir/tree/rust-hir-full-decls.h
gcc/rust/hir/tree/rust-hir-visitor.h
gcc/rust/hir/tree/rust-hir.cc
gcc/rust/resolve/rust-ast-resolve-expr.cc
gcc/rust/resolve/rust-ast-resolve-expr.h
gcc/rust/typecheck/rust-hir-type-check-expr.cc
gcc/rust/typecheck/rust-hir-type-check-expr.h

index 289f8601813c802bd93701fcf2d5e9db3a8945a3..ca096682354fb6adaabbea1a77e0b5b5ac054ceb 100644 (file)
@@ -1520,15 +1520,11 @@ TokenCollector::visit (AsyncBlockExpr &expr)
 
 void
 TokenCollector::visit (InlineAsm &expr)
-{
-  rust_unreachable ();
-}
+{}
 
 void
 TokenCollector::visit (LlvmInlineAsm &expr)
-{
-  rust_unreachable ();
-}
+{}
 
 // rust-item.h
 
index 32e1d1f39a7b5e2de9394a7e9cfacfa18fddafbf..57741021540536e78a90ba9e8ba0359446a685ee 100644 (file)
@@ -715,7 +715,13 @@ DefaultASTVisitor::visit (AST::InlineAsm &expr)
 
 void
 DefaultASTVisitor::visit (AST::LlvmInlineAsm &expr)
-{}
+{
+  for (auto &output : expr.get_outputs ())
+    visit (output.expr);
+
+  for (auto &input : expr.get_inputs ())
+    visit (input.expr);
+}
 
 void
 DefaultASTVisitor::visit (AST::TypeParam &param)
index 0dfb4e5fd607af14d772c4f23418790d64cba22b..fdb6360eb3b4b399cfe413c2ac506cc4e82895ab 100644 (file)
@@ -4885,6 +4885,27 @@ struct InlineAsmRegOrRegClass
   location_t locus;
 };
 
+struct LlvmOperand
+{
+  std::string constraint;
+  std::unique_ptr<Expr> expr;
+
+  LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
+    : constraint (constraint), expr (std::move (expr))
+  {}
+
+  LlvmOperand (const LlvmOperand &other)
+    : constraint (other.constraint), expr (other.expr->clone_expr ())
+  {}
+  LlvmOperand &operator= (const LlvmOperand &other)
+  {
+    constraint = other.constraint;
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+};
+
 class InlineAsmOperand
 {
 public:
@@ -5258,6 +5279,7 @@ struct TupleTemplateStr
   location_t loc;
   std::string symbol;
 
+  location_t get_locus () { return loc; }
   TupleTemplateStr (location_t loc, const std::string &symbol)
     : loc (loc), symbol (symbol)
   {}
@@ -5335,18 +5357,23 @@ class LlvmInlineAsm : public ExprWithoutBlock
   // llvm_asm!("" :         : "r"(&mut dummy) : "memory" : "volatile");
   //           Asm, Outputs, Inputs,            Clobbers, Options,
 
-private:
-  location_t locus;
-  std::vector<Attribute> outer_attrs;
-
-  std::vector<TupleClobber> clobber_abi;
-  bool is_volatile;
-  bool align_stack;
+public:
   enum class Dialect
   {
     Att,
     Intel,
-  } dialect;
+  };
+
+private:
+  location_t locus;
+  std::vector<Attribute> outer_attrs;
+  std::vector<LlvmOperand> inputs;
+  std::vector<LlvmOperand> outputs;
+  std::vector<TupleTemplateStr> templates;
+  std::vector<TupleClobber> clobbers;
+  bool volatility;
+  bool align_stack;
+  Dialect dialect;
 
 public:
   LlvmInlineAsm (location_t locus) : locus (locus) {}
@@ -5372,10 +5399,28 @@ public:
     return new LlvmInlineAsm (*this);
   }
 
+  std::vector<TupleTemplateStr> &get_templates () { return templates; }
+
   Expr::Kind get_expr_kind () const override
   {
     return Expr::Kind::LlvmInlineAsm;
   }
+
+  void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
+  bool is_stack_aligned () { return align_stack; }
+
+  void set_volatile (bool volatility) { this->volatility = volatility; }
+  bool is_volatile () { return volatility; }
+
+  void set_dialect (Dialect dialect) { this->dialect = dialect; }
+
+  void set_inputs (std::vector<LlvmOperand> operands) { inputs = operands; }
+  void set_outputs (std::vector<LlvmOperand> operands) { outputs = operands; }
+
+  std::vector<LlvmOperand> &get_inputs () { return inputs; }
+  std::vector<LlvmOperand> &get_outputs () { return outputs; }
+
+  std::vector<TupleClobber> &get_clobbers () { return clobbers; }
 };
 
 } // namespace AST
index 3a22658d2a25728b3bc3e06fe28933beea5695f6..f04a03ed060dfe6d8eba4dc115ca61d17b067c15 100644 (file)
@@ -1,5 +1,7 @@
 #include "rust-compile-asm.h"
 #include "rust-compile-expr.h"
+#include "rust-system.h"
+
 namespace Rust {
 namespace Compile {
 
@@ -141,5 +143,57 @@ CompileAsm::asm_construct_label_tree (HIR::InlineAsm &expr)
   return NULL_TREE;
 }
 
+CompileLlvmAsm::CompileLlvmAsm (Context *ctx) : HIRCompileBase (ctx) {}
+
+tree
+CompileLlvmAsm::construct_operands (std::vector<HIR::LlvmOperand> operands)
+{
+  tree head = NULL_TREE;
+  for (auto &operand : operands)
+    {
+      tree t = CompileExpr::Compile (*operand.expr, this->ctx);
+      auto name = build_string (operand.constraint.size () + 1,
+                               operand.constraint.c_str ());
+      head = chainon (head,
+                     build_tree_list (build_tree_list (NULL_TREE, name), t));
+    }
+  return head;
+}
+
+tree
+CompileLlvmAsm::construct_clobbers (std::vector<AST::TupleClobber> clobbers)
+{
+  tree head = NULL_TREE;
+  for (auto &clobber : clobbers)
+    {
+      auto name
+       = build_string (clobber.symbol.size () + 1, clobber.symbol.c_str ());
+      head = chainon (head, build_tree_list (NULL_TREE, name));
+    }
+  return head;
+}
+
+tree
+CompileLlvmAsm::tree_codegen_asm (HIR::LlvmInlineAsm &expr)
+{
+  tree ret = make_node (ASM_EXPR);
+  TREE_TYPE (ret) = void_type_node;
+  SET_EXPR_LOCATION (ret, expr.get_locus ());
+  ASM_VOLATILE_P (ret) = expr.options.is_volatile;
+
+  std::stringstream ss;
+  for (const auto &template_str : expr.templates)
+    {
+      ss << template_str.symbol << "\n";
+    }
+
+  ASM_STRING (ret) = Backend::string_constant_expression (ss.str ());
+  ASM_INPUTS (ret) = construct_operands (expr.inputs);
+  ASM_OUTPUTS (ret) = construct_operands (expr.outputs);
+  ASM_CLOBBERS (ret) = construct_clobbers (expr.get_clobbers ());
+
+  return ret;
+}
+
 } // namespace Compile
 } // namespace Rust
index 4abd24eded4acf8b5f593258eab3621f3c5eae8b..22be94a88334db27720289e0257aa7b63ef16716 100644 (file)
@@ -56,6 +56,20 @@ public:
 
   tree tree_codegen_asm (HIR::InlineAsm &);
 };
+
+class CompileLlvmAsm : private HIRCompileBase
+{
+private:
+  tree construct_operands (std::vector<HIR::LlvmOperand> operands);
+
+  tree construct_clobbers (std::vector<AST::TupleClobber>);
+
+public:
+  CompileLlvmAsm (Context *ctx);
+
+  tree tree_codegen_asm (HIR::LlvmInlineAsm &);
+};
+
 } // namespace Compile
 } // namespace Rust
 #endif // RUST_COMPILE_ASM
index 642ce2cfd15f612f10cc4cd58f1b8c0a68356748..896ff7584421f881e95ccd532b1b54e84ba2ab8d 100644 (file)
@@ -100,6 +100,7 @@ public:
   void visit (HIR::AwaitExpr &) override {}
   void visit (HIR::AsyncBlockExpr &) override {}
   void visit (HIR::InlineAsm &) override {}
+  void visit (HIR::LlvmInlineAsm &) override {}
 
 private:
   CompileConditionalBlocks (Context *ctx, Bvariable *result)
@@ -182,6 +183,7 @@ public:
   void visit (HIR::AwaitExpr &) override {}
   void visit (HIR::AsyncBlockExpr &) override {}
   void visit (HIR::InlineAsm &) override {}
+  void visit (HIR::LlvmInlineAsm &) override {}
 
 private:
   CompileExprWithBlock (Context *ctx, Bvariable *result)
index 9dcec64f5b6a520749e67812379ad20241fa1b6b..1e09c6c155ae1f380025ee478f8941b1039a1b69 100644 (file)
@@ -367,6 +367,13 @@ CompileExpr::visit (HIR::InlineAsm &expr)
   // CompileAsm::asm_build_expr (expr);
 }
 
+void
+CompileExpr::visit (HIR::LlvmInlineAsm &expr)
+{
+  CompileLlvmAsm asm_codegen (ctx);
+  ctx->add_statement (asm_codegen.tree_codegen_asm (expr));
+}
+
 void
 CompileExpr::visit (HIR::IfExprConseqElse &expr)
 {
index 45e0d3c350d0827f14e3ce42e5a87a38238d3c6f..a9c59ab71398449ba19b4742105daedb3b155484 100644 (file)
@@ -69,6 +69,7 @@ public:
   void visit (HIR::RangeFromToInclExpr &expr) override;
   void visit (HIR::ClosureExpr &expr) override;
   void visit (HIR::InlineAsm &expr) override;
+  void visit (HIR::LlvmInlineAsm &expr) override;
 
   // TODO
   void visit (HIR::ErrorPropagationExpr &) override {}
index d8e00e38519e559947f57b94d288debf18d09e3c..70ad7ed21a5bdeaadce6550c92295973f359278e 100644 (file)
@@ -324,6 +324,10 @@ void
 ExprStmtBuilder::visit (HIR::InlineAsm &expr)
 {}
 
+void
+ExprStmtBuilder::visit (HIR::LlvmInlineAsm &expr)
+{}
+
 void
 ExprStmtBuilder::visit (HIR::MethodCallExpr &expr)
 {}
index f538a1209a8f6d7cedeeacfe1910d0b5f1b2c057..48b81991e07d0f6085cfb016b79f4151bdbf44d4 100644 (file)
@@ -100,6 +100,7 @@ protected: // Expr
   void visit (HIR::IfExpr &expr) override;
   void visit (HIR::IfExprConseqElse &expr) override;
   void visit (HIR::InlineAsm &expr) override;
+  void visit (HIR::LlvmInlineAsm &expr) override;
 
   void visit (HIR::MatchExpr &expr) override;
   void visit (HIR::AwaitExpr &expr) override;
index 71cf710a41111a5367e04bdcaa046c6d6c6985f3..dbf365a34789c927b50bd426d8c7a29b52db4a00 100644 (file)
@@ -207,6 +207,7 @@ public:
   }
 
   void visit (HIR::InlineAsm &expr) override {}
+  void visit (HIR::LlvmInlineAsm &expr) override {}
 
 protected: // Illegal at this position.
   void visit (HIR::StructExprFieldIdentifier &field) override
index 2f0d67c50488b5e74c46b1d7c8fa89ffc0479fd7..fdec3291acd330b0b36003f699f31ac110f0b359 100644 (file)
@@ -153,6 +153,7 @@ protected:
   void visit (HIR::AwaitExpr &expr) override { rust_unreachable (); }
   void visit (HIR::AsyncBlockExpr &expr) override { rust_unreachable (); }
   void visit (HIR::InlineAsm &expr) override { rust_unreachable (); }
+  void visit (HIR::LlvmInlineAsm &expr) override { rust_unreachable (); }
   void visit (HIR::TypeParam &param) override { rust_unreachable (); }
   void visit (HIR::ConstGenericParam &param) override { rust_unreachable (); }
   void visit (HIR::LifetimeWhereClauseItem &item) override
index 6c1ad44452839985a2516a493c5d1db4f56369b2..74b5f4365c1c5d78ed7b47f98ad5b9f76b416fbb 100644 (file)
@@ -123,6 +123,7 @@ public:
   void visit (HIR::AwaitExpr &expr) override {}
   void visit (HIR::AsyncBlockExpr &expr) override {}
   void visit (HIR::InlineAsm &expr) override {}
+  void visit (HIR::LlvmInlineAsm &expr) override {}
   void visit (HIR::TypeParam &param) override {}
   void visit (HIR::ConstGenericParam &param) override {}
   void visit (HIR::LifetimeWhereClauseItem &item) override {}
index fd3de13fd4d1d7595ad946cab422f4e773af90d3..c78a2f5dcc6d9be6be1b5e174fbd3ca3bb2eab56 100644 (file)
@@ -305,6 +305,10 @@ void
 PrivacyReporter::visit (HIR::InlineAsm &)
 {}
 
+void
+PrivacyReporter::visit (HIR::LlvmInlineAsm &)
+{}
+
 void
 PrivacyReporter::visit (HIR::TypePath &path)
 {
index 505c8673c571c357228e549f7afc6e9be1876140..c3d9f3011f904acffe16fd3189cb5723da76c44e 100644 (file)
@@ -125,6 +125,7 @@ types
   virtual void visit (HIR::AwaitExpr &expr);
   virtual void visit (HIR::AsyncBlockExpr &expr);
   virtual void visit (HIR::InlineAsm &expr);
+  virtual void visit (HIR::LlvmInlineAsm &expr);
 
   virtual void visit (HIR::EnumItemTuple &);
   virtual void visit (HIR::EnumItemStruct &);
index 81594c00beb490858c16b961c22107b63f58ef54..7423b82779a3ca1cd44f55d38ca1d4bdf8d8b371 100644 (file)
@@ -536,6 +536,10 @@ void
 ConstChecker::visit (InlineAsm &)
 {}
 
+void
+ConstChecker::visit (LlvmInlineAsm &)
+{}
+
 void
 ConstChecker::visit (TypeParam &)
 {}
index 92a5c49bb51c27b8eda4ddace3d14d5c1891dacd..33f956ac0e5d432075e3310d6d1f1b90c3fa7f66 100644 (file)
@@ -132,6 +132,7 @@ private:
   virtual void visit (AwaitExpr &expr) override;
   virtual void visit (AsyncBlockExpr &expr) override;
   virtual void visit (InlineAsm &expr) override;
+  virtual void visit (LlvmInlineAsm &expr) override;
 
   virtual void visit (TypeParam &param) override;
   virtual void visit (ConstGenericParam &param) override;
index 257f4cd0c08cbdac30861c4e47afd5209634569a..648bc07762db7c4e034442b4bc76b9131c237b7b 100644 (file)
@@ -422,6 +422,10 @@ void
 PatternChecker::visit (InlineAsm &expr)
 {}
 
+void
+PatternChecker::visit (LlvmInlineAsm &expr)
+{}
+
 void
 PatternChecker::visit (TypeParam &)
 {}
index 2171340d4ad7dabd22d4f202889bf1c5420b05d3..6d60ceda538456fcd958a5d900ce9ea8f5842ed4 100644 (file)
@@ -106,6 +106,7 @@ private:
   virtual void visit (AwaitExpr &expr) override;
   virtual void visit (AsyncBlockExpr &expr) override;
   virtual void visit (InlineAsm &expr) override;
+  virtual void visit (LlvmInlineAsm &expr) override;
   virtual void visit (TypeParam &param) override;
   virtual void visit (ConstGenericParam &param) override;
   virtual void visit (LifetimeWhereClauseItem &item) override;
index 41e851e2c1901772d945e377b89f0389bf91d6e5..7097c50003520090d35950e09527d3ec58fc29c1 100644 (file)
@@ -670,6 +670,17 @@ UnsafeChecker::visit (InlineAsm &expr)
     "use of inline assembly is unsafe and requires unsafe function or block");
 }
 
+void
+UnsafeChecker::visit (LlvmInlineAsm &expr)
+{
+  if (unsafe_context.is_in_context ())
+    return;
+
+  rust_error_at (
+    expr.get_locus (), ErrorCode::E0133,
+    "use of inline assembly is unsafe and requires unsafe function or block");
+}
+
 void
 UnsafeChecker::visit (TypeParam &)
 {}
index 9f1d8c7f8711e50d0544e8295ed5dee5eec90c5f..7f5388c8e18de75b586e2797545704ae6bad7336 100644 (file)
@@ -114,6 +114,7 @@ private:
   virtual void visit (AwaitExpr &expr) override;
   virtual void visit (AsyncBlockExpr &expr) override;
   virtual void visit (InlineAsm &expr) override;
+  virtual void visit (LlvmInlineAsm &expr) override;
   virtual void visit (TypeParam &param) override;
   virtual void visit (ConstGenericParam &param) override;
   virtual void visit (LifetimeWhereClauseItem &item) override;
index d905ae938c9c689c57cda8015927c5e33adb9de3..633fe24f4c9537c89a919d605a1b7f178cfc38f3 100644 (file)
@@ -22,6 +22,7 @@
 #include "rust-ast.h"
 #include "rust-fmt.h"
 #include "rust-stmt.h"
+#include "rust-parse.h"
 
 namespace Rust {
 std::map<AST::InlineAsmOption, std::string> InlineAsmOptionMap{
@@ -980,6 +981,184 @@ validate (InlineAsmContext inline_asm_ctx)
   return tl::expected<InlineAsmContext, InlineAsmParseError> (inline_asm_ctx);
 }
 
+tl::optional<LlvmAsmContext>
+parse_llvm_templates (LlvmAsmContext ctx)
+{
+  auto &parser = ctx.parser;
+
+  auto token = parser.peek_current_token ();
+
+  if (token->get_id () == ctx.last_token_id
+      || token->get_id () != STRING_LITERAL)
+    {
+      return tl::nullopt;
+    }
+
+  ctx.llvm_asm.get_templates ().emplace_back (token->get_locus (),
+                                             strip_double_quotes (
+                                               token->as_string ()));
+  ctx.parser.skip_token ();
+
+  token = parser.peek_current_token ();
+  if (token->get_id () != ctx.last_token_id && token->get_id () != COLON
+      && token->get_id () != SCOPE_RESOLUTION)
+    {
+      // We do not handle multiple template string, we provide minimal support
+      // for the black_box intrinsics.
+      rust_unreachable ();
+    }
+
+  return ctx;
+}
+
+tl::optional<LlvmAsmContext>
+parse_llvm_arguments (LlvmAsmContext ctx)
+{
+  auto &parser = ctx.parser;
+  enum State
+  {
+    Templates = 0,
+    Output,
+    Input,
+    Clobbers,
+    Options
+  } current_state
+    = State::Templates;
+
+  while (parser.peek_current_token ()->get_id () != ctx.last_token_id
+        && parser.peek_current_token ()->get_id () != END_OF_FILE)
+    {
+      if (parser.peek_current_token ()->get_id () == SCOPE_RESOLUTION)
+       {
+         parser.skip_token (SCOPE_RESOLUTION);
+         current_state = static_cast<State> (current_state + 2);
+       }
+      else
+       {
+         parser.skip_token (COLON);
+         current_state = static_cast<State> (current_state + 1);
+       }
+
+      switch (current_state)
+       {
+       case State::Output:
+         parse_llvm_outputs (ctx);
+         break;
+       case State::Input:
+         parse_llvm_inputs (ctx);
+         break;
+       case State::Clobbers:
+         parse_llvm_clobbers (ctx);
+         break;
+       case State::Options:
+         parse_llvm_options (ctx);
+         break;
+       case State::Templates:
+       default:
+         rust_unreachable ();
+       }
+    }
+
+  return ctx;
+}
+
+void
+parse_llvm_operands (LlvmAsmContext &ctx, std::vector<AST::LlvmOperand> &result)
+{
+  auto &parser = ctx.parser;
+  auto token = parser.peek_current_token ();
+  while (token->get_id () != COLON && token->get_id () != SCOPE_RESOLUTION
+        && token->get_id () != ctx.last_token_id)
+    {
+      std::string constraint;
+      if (token->get_id () == STRING_LITERAL)
+       {
+         constraint = strip_double_quotes (token->as_string ());
+       }
+      parser.skip_token (STRING_LITERAL);
+      parser.skip_token (LEFT_PAREN);
+
+      token = parser.peek_current_token ();
+
+      ParseRestrictions restrictions;
+      restrictions.expr_can_be_null = true;
+      auto expr = parser.parse_expr ();
+
+      parser.skip_token (RIGHT_PAREN);
+
+      result.emplace_back (constraint, std::move (expr));
+
+      if (parser.peek_current_token ()->get_id () == COMMA)
+       parser.skip_token (COMMA);
+
+      token = parser.peek_current_token ();
+    }
+}
+
+void
+parse_llvm_outputs (LlvmAsmContext &ctx)
+{
+  parse_llvm_operands (ctx, ctx.llvm_asm.get_outputs ());
+}
+
+void
+parse_llvm_inputs (LlvmAsmContext &ctx)
+{
+  parse_llvm_operands (ctx, ctx.llvm_asm.get_inputs ());
+}
+
+void
+parse_llvm_clobbers (LlvmAsmContext &ctx)
+{
+  auto &parser = ctx.parser;
+  auto token = parser.peek_current_token ();
+  while (token->get_id () != COLON && token->get_id () != ctx.last_token_id)
+    {
+      if (token->get_id () == STRING_LITERAL)
+       {
+         ctx.llvm_asm.get_clobbers ().push_back (
+           {strip_double_quotes (token->as_string ()), token->get_locus ()});
+       }
+      parser.skip_token (STRING_LITERAL);
+      token = parser.peek_current_token ();
+    }
+}
+
+void
+parse_llvm_options (LlvmAsmContext &ctx)
+{
+  auto &parser = ctx.parser;
+  auto token = parser.peek_current_token ();
+
+  while (token->get_id () != ctx.last_token_id)
+    {
+      if (token->get_id () == STRING_LITERAL)
+       {
+         auto token_str = strip_double_quotes (token->as_string ());
+
+         if (token_str == "volatile")
+           ctx.llvm_asm.set_volatile (true);
+         else if (token_str == "alignstack")
+           ctx.llvm_asm.set_align_stack (true);
+         else if (token_str == "intel")
+           ctx.llvm_asm.set_dialect (AST::LlvmInlineAsm::Dialect::Intel);
+         else
+           rust_error_at (token->get_locus (),
+                          "Unknown llvm assembly option %qs",
+                          token_str.c_str ());
+       }
+      parser.skip_token (STRING_LITERAL);
+
+      token = parser.peek_current_token ();
+
+      if (token->get_id () == ctx.last_token_id)
+       continue;
+      parser.skip_token (COMMA);
+    }
+
+  parser.skip_token ();
+}
+
 tl::optional<AST::Fragment>
 parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
                AST::InvocKind semicolon, AST::AsmKind is_global_asm)
@@ -989,6 +1168,35 @@ parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
   auto last_token_id = macro_end_token (invoc.get_delim_tok_tree (), parser);
 
   AST::LlvmInlineAsm llvm_asm{invoc_locus};
+
+  auto asm_ctx = LlvmAsmContext (llvm_asm, parser, last_token_id);
+
+  auto resulting_context
+    = parse_llvm_templates (asm_ctx).and_then (parse_llvm_arguments);
+
+  if (resulting_context)
+    {
+      auto node = (*resulting_context).llvm_asm.clone_expr_without_block ();
+
+      std::vector<AST::SingleASTNode> single_vec = {};
+
+      // If the macro invocation has a semicolon (`asm!("...");`), then we
+      // need to make it a statement. This way, it will be expanded
+      // properly.
+      if (semicolon == AST::InvocKind::Semicoloned)
+       single_vec.emplace_back (AST::SingleASTNode (
+         std::make_unique<AST::ExprStmt> (std::move (node), invoc_locus,
+                                          semicolon
+                                            == AST::InvocKind::Semicoloned)));
+      else
+       single_vec.emplace_back (AST::SingleASTNode (std::move (node)));
+
+      AST::Fragment fragment_ast
+       = AST::Fragment (single_vec,
+                        std::vector<std::unique_ptr<AST::Token>> ());
+      return fragment_ast;
+    }
+  return tl::nullopt;
 }
 
 } // namespace Rust
index e13ee25a9357a947839cb2551a252e5ce2ea4bf2..bd64a7f4dd628a259e4f627f004fd0f2e8090bfb 100644 (file)
@@ -174,8 +174,33 @@ parse_label (Parser<MacroInvocLexer> &parser, TokenId last_token_id,
 
 // LLVM ASM bits
 
-WARN_UNUSED_RESULT
-tl::optional<AST::Fragment>
+class LlvmAsmContext
+{
+public:
+  AST::LlvmInlineAsm &llvm_asm;
+  Parser<MacroInvocLexer> &parser;
+  int last_token_id;
+
+public:
+  LlvmAsmContext (AST::LlvmInlineAsm &llvm_asm, Parser<MacroInvocLexer> &parser,
+                 int last_token_id)
+    : llvm_asm (llvm_asm), parser (parser), last_token_id (last_token_id)
+  {}
+};
+
+void
+parse_llvm_outputs (LlvmAsmContext &ctx);
+
+void
+parse_llvm_inputs (LlvmAsmContext &ctx);
+
+void
+parse_llvm_clobbers (LlvmAsmContext &ctx);
+
+void
+parse_llvm_options (LlvmAsmContext &ctx);
+
+WARN_UNUSED_RESULT tl::optional<AST::Fragment>
 parse_llvm_asm (location_t invoc_locus, AST::MacroInvocData &invoc,
                AST::InvocKind semicolon, AST::AsmKind is_global_asm);
 
index 3f040d47261856fc19f8aaa344fa8736e0925014..e9e3b4e2e04ddb436a85eaeebe2f4198a832e605 100644 (file)
@@ -955,6 +955,50 @@ ASTLoweringExpr::visit (AST::InlineAsm &expr)
                          hir_operands, expr.get_clobber_abi (),
                          expr.get_options (), mapping);
 }
+
+void
+ASTLoweringExpr::visit (AST::LlvmInlineAsm &expr)
+{
+  auto crate_num = mappings.get_current_crate ();
+  Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
+                                mappings.get_next_hir_id (crate_num),
+                                mappings.get_next_localdef_id (crate_num));
+
+  std::vector<LlvmOperand> inputs;
+  std::vector<LlvmOperand> outputs;
+
+  for (auto i : expr.get_inputs ())
+    {
+      std::unique_ptr<Expr> inner_expr
+       = std::unique_ptr<Expr> (translate (*i.expr.get ()));
+      inputs.emplace_back (i.constraint, std::move (inner_expr));
+    }
+
+  for (auto o : expr.get_outputs ())
+    {
+      std::unique_ptr<Expr> inner_expr
+       = std::unique_ptr<Expr> (translate (*o.expr.get ()));
+      outputs.emplace_back (o.constraint, std::move (inner_expr));
+    }
+
+  HIR::LlvmInlineAsm::Options options{expr.is_volatile (),
+                                     expr.is_stack_aligned (),
+                                     expr.get_dialect ()};
+
+  // We're not really supporting llvm_asm, only the bare minimum
+  // we're quite conservative here as the current code support more usecase.
+  rust_assert (outputs.size () == 0);
+  rust_assert (inputs.size () <= 1);
+  rust_assert (expr.get_clobbers ().size () <= 1);
+  rust_assert (expr.get_templates ().size () == 1);
+  rust_assert (expr.get_templates ()[0].symbol == "");
+
+  translated
+    = new HIR::LlvmInlineAsm (expr.get_locus (), inputs, outputs,
+                             expr.get_templates (), expr.get_clobbers (),
+                             options, expr.get_outer_attrs (), mapping);
+}
+
 void
 ASTLoweringExpr::visit (AST::FormatArgs &fmt)
 {
index dd903a0c6e2bc341f9978c0ed3b438c5a5fe63d7..b8681fb07cadabc02da907ee4f0e32fa37299019 100644 (file)
@@ -122,6 +122,7 @@ public:
   void visit (AST::ClosureExprInner &expr) override;
   void visit (AST::ClosureExprInnerTyped &expr) override;
   void visit (AST::InlineAsm &expr) override;
+  void visit (AST::LlvmInlineAsm &expr) override;
 
   // Extra visitor for FormatArgs nodes
   void visit (AST::FormatArgs &fmt) override;
index c4523e646b2c28c10abcb4101058d00a731f3833..156bd8139075c1cd1d3b8806d98f9143a882a0ad 100644 (file)
@@ -1509,6 +1509,10 @@ void
 Dump::visit (InlineAsm &e)
 {}
 
+void
+Dump::visit (LlvmInlineAsm &e)
+{}
+
 void
 Dump::visit (TypeParam &e)
 {
index 49ac4051541d81e57df4b64c6d48567a36400f05..b3015c639cf2d5bb04060eaf0edc5b1b24db33cd 100644 (file)
@@ -166,6 +166,7 @@ private:
   virtual void visit (AwaitExpr &) override;
   virtual void visit (AsyncBlockExpr &) override;
   virtual void visit (InlineAsm &) override;
+  virtual void visit (LlvmInlineAsm &) override;
 
   virtual void visit (TypeParam &) override;
   virtual void visit (ConstGenericParam &) override;
index ecf9bd1af5d1bc9a69b8eebc3d040b7fff5e939e..5bc5d890309918369b48cbd8dac125f44bd43ddc 100644 (file)
@@ -71,6 +71,7 @@ public:
     AsyncBlock,
     Path,
     InlineAsm,
+    LlvmInlineAsm,
   };
 
   BaseKind get_hir_kind () override final { return Node::BaseKind::EXPR; }
index 20287b830f9cafd2825500f593c0a7d657172f13..6400cb9aacf84fb4c40c1ec64145b6902d19f470 100644 (file)
@@ -3118,6 +3118,80 @@ public:
             AST::AttrVec outer_attribs = AST::AttrVec ());
 };
 
+struct LlvmOperand
+{
+  std::string constraint;
+  std::unique_ptr<Expr> expr;
+
+  LlvmOperand (std::string constraint, std::unique_ptr<Expr> &&expr)
+    : constraint (constraint), expr (std::move (expr))
+  {}
+
+  LlvmOperand (const LlvmOperand &other)
+    : constraint (other.constraint), expr (other.expr->clone_expr ())
+  {}
+  LlvmOperand &operator= (const LlvmOperand &other)
+  {
+    constraint = other.constraint;
+    expr = other.expr->clone_expr ();
+
+    return *this;
+  }
+};
+
+class LlvmInlineAsm : public ExprWithoutBlock
+{
+public:
+  struct Options
+  {
+    bool is_volatile;
+    bool align_stack;
+    AST::LlvmInlineAsm::Dialect dialect;
+  };
+
+  location_t locus;
+  AST::AttrVec outer_attrs;
+  std::vector<LlvmOperand> inputs;
+  std::vector<LlvmOperand> outputs;
+  std::vector<AST::TupleTemplateStr> templates;
+  std::vector<AST::TupleClobber> clobbers;
+  Options options;
+
+  LlvmInlineAsm (location_t locus, std::vector<LlvmOperand> inputs,
+                std::vector<LlvmOperand> outputs,
+                std::vector<AST::TupleTemplateStr> templates,
+                std::vector<AST::TupleClobber> clobbers, Options options,
+                AST::AttrVec outer_attrs, Analysis::NodeMapping mappings)
+    : ExprWithoutBlock (mappings, std::move (outer_attrs)), locus (locus),
+      inputs (std::move (inputs)), outputs (std::move (outputs)),
+      templates (std::move (templates)), clobbers (std::move (clobbers)),
+      options (options)
+  {}
+
+  AST::LlvmInlineAsm::Dialect get_dialect () { return options.dialect; }
+
+  location_t get_locus () const override { return locus; }
+
+  std::vector<AST::Attribute> &get_outer_attrs () { return outer_attrs; }
+
+  void accept_vis (HIRFullVisitor &vis) override;
+  void accept_vis (HIRExpressionVisitor &vis) override;
+
+  LlvmInlineAsm *clone_expr_without_block_impl () const override
+  {
+    return new LlvmInlineAsm (*this);
+  }
+
+  std::vector<AST::TupleTemplateStr> &get_templates () { return templates; }
+
+  Expr::ExprType get_expression_type () const override
+  {
+    return Expr::ExprType::LlvmInlineAsm;
+  }
+
+  std::vector<AST::TupleClobber> get_clobbers () { return clobbers; }
+};
+
 } // namespace HIR
 } // namespace Rust
 
index 6eae99d58f6e5faaa7e0378637886142bf1b1850..a1722b068218f34976b9a15d80a48b5c8bfb7a57 100644 (file)
@@ -126,6 +126,7 @@ class InlineAsmRegClass;
 struct AnonConst;
 class InlineAsmOperand;
 class InlineAsm;
+class LlvmInlineAsm;
 
 // rust-stmt.h
 class EmptyStmt;
index b14547bc7128cd08a2e117f9a9f9edb7d240058b..c9344846798e96cac91eb82ff94e95035de426c0 100644 (file)
@@ -84,6 +84,7 @@ public:
   virtual void visit (AwaitExpr &expr) = 0;
   virtual void visit (AsyncBlockExpr &expr) = 0;
   virtual void visit (InlineAsm &expr) = 0;
+  virtual void visit (LlvmInlineAsm &expr) = 0;
   virtual void visit (TypeParam &param) = 0;
   virtual void visit (ConstGenericParam &param) = 0;
   virtual void visit (LifetimeWhereClauseItem &item) = 0;
@@ -220,6 +221,7 @@ public:
   virtual void visit (AwaitExpr &) override {}
   virtual void visit (AsyncBlockExpr &) override {}
   virtual void visit (InlineAsm &) override {}
+  virtual void visit (LlvmInlineAsm &) override {}
 
   virtual void visit (TypeParam &) override {}
   virtual void visit (ConstGenericParam &) override {}
@@ -441,6 +443,7 @@ public:
   virtual void visit (IfExpr &expr) = 0;
   virtual void visit (IfExprConseqElse &expr) = 0;
   virtual void visit (InlineAsm &expr) = 0;
+  virtual void visit (LlvmInlineAsm &expr) = 0;
   virtual void visit (MatchExpr &expr) = 0;
   virtual void visit (AwaitExpr &expr) = 0;
   virtual void visit (AsyncBlockExpr &expr) = 0;
index 4fc6b1ae18f030b284a149a02e9bc1cc6d4999d9..c94335734de0a29345b4224a4dba7463b7afb5f9 100644 (file)
@@ -3821,6 +3821,17 @@ InlineAsm::accept_vis (HIRFullVisitor &vis)
   vis.visit (*this);
 }
 
+void
+LlvmInlineAsm::accept_vis (HIRFullVisitor &vis)
+{
+  vis.visit (*this);
+}
+void
+LlvmInlineAsm::accept_vis (HIRExpressionVisitor &vis)
+{
+  vis.visit (*this);
+}
+
 void
 BorrowExpr::accept_vis (HIRExpressionVisitor &vis)
 {
index 2aebaaf999df42160bc54d3d3cd7c0e29dbbb124..bc972fe74eb36e3486bb128f953eaa7b428cc13c 100644 (file)
@@ -368,6 +368,17 @@ ResolveExpr::visit (AST::InlineAsm &expr)
 {
   translate_operand (expr, prefix, canonical_prefix);
 }
+
+void
+ResolveExpr::visit (AST::LlvmInlineAsm &expr)
+{
+  for (auto &output : expr.get_outputs ())
+    ResolveExpr::go (*output.expr, prefix, canonical_prefix);
+
+  for (auto &input : expr.get_inputs ())
+    ResolveExpr::go (*input.expr, prefix, canonical_prefix);
+}
+
 void
 ResolveExpr::visit (AST::UnsafeBlockExpr &expr)
 {
index 374ffb06fd5a20f703b5ea344177fd9bdc436fff..d43318f5d9244b03f2bc90cf7bf404b6f74f9457 100644 (file)
@@ -57,6 +57,7 @@ public:
   void visit (AST::IfLetExprConseqElse &expr) override;
   void visit (AST::BlockExpr &expr) override;
   void visit (AST::InlineAsm &expr) override;
+  void visit (AST::LlvmInlineAsm &expr) override;
   void visit (AST::UnsafeBlockExpr &expr) override;
   void visit (AST::ArrayElemsValues &elems) override;
   void visit (AST::ArrayExpr &expr) override;
index d01e4f8b60d617c127d10c17cc3f570758577897..b960e73abd920c8061239a4b33a85ca6a2a5160e 100644 (file)
@@ -843,6 +843,19 @@ TypeCheckExpr::visit (HIR::InlineAsm &expr)
     infered = TyTy::TupleType::get_unit_type ();
 }
 
+void
+TypeCheckExpr::visit (HIR::LlvmInlineAsm &expr)
+{
+  for (auto &i : expr.inputs)
+    TypeCheckExpr::Resolve (*i.expr);
+
+  for (auto &o : expr.outputs)
+    TypeCheckExpr::Resolve (*o.expr);
+
+  // Black box hint is unit type
+  infered = TyTy::TupleType::get_unit_type ();
+}
+
 void
 TypeCheckExpr::visit (HIR::RangeFullExpr &expr)
 {
index 3ceef7a521e70455a1816c212297cdd2ba4f4b09..0b859f76f61592e6a7312dd42193897706f3e157 100644 (file)
@@ -70,6 +70,7 @@ public:
   void visit (HIR::WhileLoopExpr &expr) override;
   void visit (HIR::ClosureExpr &expr) override;
   void visit (HIR::InlineAsm &expr) override;
+  void visit (HIR::LlvmInlineAsm &expr) override;
 
   // TODO
   void visit (HIR::ErrorPropagationExpr &) override {}