From: Owen Avery <[email protected]>

More subtle differences between GCC and LLVM inline assembly might be
an issue in the future, but this should handle or safely reject most
usages.

gcc/rust/ChangeLog:

        * ast/rust-expr.h (LlvmInlineAsm::LlvmInlineAsm): Initialize
        more member variables.
        (LlvmInlineAsm::get_dialect): Make function const qualified.
        (LlvmInlineAsm::is_stack_aligned): Likewise.
        (LlvmInlineAsm::is_volatile): Likewise.
        * backend/rust-compile-asm.cc
        (CompileLlvmAsm::construct_operands): Handle llvm-specific "=*m"
        constraint.
        (CompileLlvmAsm::tree_codegen_asm): Handle change from multiple
        template strings to a single template string.
        * hir/rust-ast-lower-expr.cc (convert_template_str): New
        function.
        (check_llvm_asm_support): Remove unnecessary parameters and
        expand the set of allowed llvm_asm usages.
        (ASTLoweringExpr::visit (LlvmInlineAsm)): Adjust lowering.
        * hir/tree/rust-hir-expr.h (LlvmInlineAsm::templates): Remove
        member variable and replace with...
        (LlvmInlineAsm::template_str): ...new member variable.
        (LlvmInlineAsm::LlvmInlineAsm): Handle member variable changes.
        (LlvmInlineAsm::get_templates): Remove member function and
        replace with...
        (LlvmInlineAsm::get_template): ...new member function.
        * hir/tree/rust-hir-visitor.cc
        (DefaultHIRVisitor::walk (LlvmInlineAsm)): Visit outer
        attributes.
        * typecheck/rust-hir-type-check-expr.cc
        (TypeCheckExpr::visit (LlvmInlineAsm)): Adjust comments.

Signed-off-by: Owen Avery <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/0029e0e44559501663f95997a892a2a701d95840

The commit has NOT been mentioned in any issue.

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4786

 gcc/rust/ast/rust-expr.h                      |   9 +-
 gcc/rust/backend/rust-compile-asm.cc          |  27 ++--
 gcc/rust/hir/rust-ast-lower-expr.cc           | 145 +++++++++++++++---
 gcc/rust/hir/tree/rust-hir-expr.h             |   8 +-
 gcc/rust/hir/tree/rust-hir-visitor.cc         |   1 +
 .../typecheck/rust-hir-type-check-expr.cc     |   4 +-
 6 files changed, 157 insertions(+), 37 deletions(-)

diff --git a/gcc/rust/ast/rust-expr.h b/gcc/rust/ast/rust-expr.h
index 43f5802d3..a30282000 100644
--- a/gcc/rust/ast/rust-expr.h
+++ b/gcc/rust/ast/rust-expr.h
@@ -5852,10 +5852,11 @@ private:
 
 public:
   LlvmInlineAsm (location_t locus)
-    : locus (locus), template_str (UNKNOWN_LOCATION, "")
+    : locus (locus), template_str (UNKNOWN_LOCATION, ""), volatility (false),
+      align_stack (false), dialect (Dialect::Att)
   {}
 
-  Dialect get_dialect () { return dialect; }
+  Dialect get_dialect () const { return dialect; }
 
   location_t get_locus () const override { return locus; }
 
@@ -5890,10 +5891,10 @@ public:
   }
 
   void set_align_stack (bool align_stack) { this->align_stack = align_stack; }
-  bool is_stack_aligned () { return align_stack; }
+  bool is_stack_aligned () const { return align_stack; }
 
   void set_volatile (bool volatility) { this->volatility = volatility; }
-  bool is_volatile () { return volatility; }
+  bool is_volatile () const { return volatility; }
 
   void set_dialect (Dialect dialect) { this->dialect = dialect; }
 
diff --git a/gcc/rust/backend/rust-compile-asm.cc 
b/gcc/rust/backend/rust-compile-asm.cc
index 2ac65bd65..73873e440 100644
--- a/gcc/rust/backend/rust-compile-asm.cc
+++ b/gcc/rust/backend/rust-compile-asm.cc
@@ -187,8 +187,22 @@ CompileLlvmAsm::construct_operands 
(std::vector<HIR::LlvmOperand> operands)
   for (auto &operand : operands)
     {
       tree t = CompileExpr::Compile (*operand.expr, this->ctx);
-      auto name = build_string (operand.constraint.size () + 1,
-                               operand.constraint.c_str ());
+
+      // handle indirect memory operand
+      std::string *constraint;
+      std::string constraint_copy;
+      if (operand.constraint == "=*m")
+       {
+         constraint = &constraint_copy;
+         constraint_copy = "=m";
+         t = indirect_expression (t, operand.expr->get_locus ());
+       }
+      else
+       {
+         constraint = &operand.constraint;
+       }
+
+      auto name = build_string (constraint->size () + 1, constraint->c_str ());
       ls.push_back (build_tree_list (build_tree_list (NULL_TREE, name), t));
     }
   return ls.get_head ();
@@ -215,13 +229,8 @@ CompileLlvmAsm::tree_codegen_asm (HIR::LlvmInlineAsm &expr)
   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_STRING (ret)
+    = Backend::string_constant_expression (expr.template_str.symbol);
   ASM_INPUTS (ret) = construct_operands (expr.inputs);
   ASM_OUTPUTS (ret) = construct_operands (expr.outputs);
   ASM_CLOBBERS (ret) = construct_clobbers (expr.get_clobbers ());
diff --git a/gcc/rust/hir/rust-ast-lower-expr.cc 
b/gcc/rust/hir/rust-ast-lower-expr.cc
index 8dc2ed04c..77811f2b8 100644
--- a/gcc/rust/hir/rust-ast-lower-expr.cc
+++ b/gcc/rust/hir/rust-ast-lower-expr.cc
@@ -1014,17 +1014,115 @@ ASTLoweringExpr::visit (AST::InlineAsm &expr)
 }
 
 namespace {
-// We're not really supporting llvm_asm, only the bare minimum for libcore's
-// blackbox
-// llvm_asm!("" : : "r"(&mut dummy) : "memory" : "volatile");
+
+tl::optional<std::string>
+convert_template_str (const std::string &in_template)
+{
+  std::string out_template;
+  auto it = in_template.cbegin ();
+
+  while (it != in_template.cend ())
+    {
+      if (*it == '$')
+       {
+         it++;
+         if (it == in_template.cend ())
+           {
+             return tl::nullopt;
+           }
+         else if (*it >= '0' && *it <= '9')
+           {
+             out_template.push_back ('%');
+             out_template.push_back (*it);
+             it++;
+           }
+         else if (*it == '$')
+           {
+             out_template.push_back ('$');
+             it++;
+           }
+         else if (*it == '{')
+           {
+             it++;
+             // converting
+             //     v
+             //   ${123:abc}
+             // to
+             //   %abc123
+             auto num_it = it;
+             while (true)
+               {
+                 if (it == in_template.cend ())
+                   return tl::nullopt;
+                 if (*it == ':')
+                   break;
+                 it++;
+               }
+             auto colon_it = it;
+             while (true)
+               {
+                 if (it == in_template.cend ())
+                   return tl::nullopt;
+                 if (*it == '}')
+                   break;
+                 it++;
+               }
+             // output
+             out_template.push_back ('%');
+             out_template.append (colon_it + 1, it);
+             out_template.append (num_it, colon_it);
+             // increment past '}'
+             it++;
+           }
+         else
+           {
+             return tl::nullopt;
+           }
+       }
+      else if (*it == '%' || *it == '{' || *it == '|' || *it == '}')
+       {
+         out_template.push_back ('%');
+         out_template.push_back (*it);
+         it++;
+       }
+      else
+       {
+         out_template.push_back (*it);
+         it++;
+       }
+    }
+
+  return out_template;
+}
+
+// We're not really supporting llvm_asm, only the bare minimum for libcore
+// ex: llvm_asm!("" : : "r"(&mut dummy) : "memory" : "volatile");
 bool
-check_llvm_asm_support (const std::vector<LlvmOperand> &inputs,
-                       const std::vector<LlvmOperand> &outputs,
-                       const AST::LlvmInlineAsm &expr)
+check_llvm_asm_support (const AST::LlvmInlineAsm &expr)
 {
-  return outputs.size () == 0 && inputs.size () <= 1
-        && expr.get_clobbers ().size () <= 1
-        && expr.get_template ().symbol == "";
+  // TODO: more checks/constraint rewriting?
+
+  if (!convert_template_str (expr.get_template ().symbol).has_value ())
+    return false;
+
+  // TODO: check output constraints?
+
+  // prohibit commas
+  // GCC uses them to list multiple options for constraints (?)
+  // while LLVM uses them for inout args (?)
+  for (auto &input : expr.get_inputs ())
+    if (input.constraint.find (',') != std::string::npos)
+      return false;
+
+  // TODO: check clobbers?
+
+  // no alignstack or intel support
+  if (expr.is_stack_aligned ())
+    return false;
+  if (expr.get_dialect () == AST::LlvmInlineAsm::Dialect::Intel)
+    return false;
+
+  return true;
 }
 
 } // namespace
@@ -1032,6 +1130,16 @@ check_llvm_asm_support (const std::vector<LlvmOperand> 
&inputs,
 void
 ASTLoweringExpr::visit (AST::LlvmInlineAsm &expr)
 {
+  if (!check_llvm_asm_support (expr))
+    {
+      rust_error_at (expr.get_locus (), "unsupported %qs construct",
+                    "llvm_asm");
+      rust_inform (
+       expr.get_locus (),
+       "%<llvm_asm%> has been replaced with %<asm%>, gccrs only supports a "
+       "subset of %<llvm_asm%> to compile libcore");
+    }
+
   auto crate_num = mappings.get_current_crate ();
   Analysis::NodeMapping mapping (crate_num, expr.get_node_id (),
                                 mappings.get_next_hir_id (crate_num),
@@ -1061,19 +1169,18 @@ ASTLoweringExpr::visit (AST::LlvmInlineAsm &expr)
                                      expr.is_stack_aligned (),
                                      expr.get_dialect ()};
 
-  if (!check_llvm_asm_support (inputs, outputs, expr))
-    {
-      rust_error_at (expr.get_locus (), "unsupported %qs construct",
-                    "llvm_asm");
-      rust_inform (
-       expr.get_locus (),
-       "%<llvm_asm%> has been replaced with %<asm%>, gccrs only supports a "
-       "subset of %<llvm_asm%> to compile libcore");
-    }
+  auto new_template = expr.get_template ();
+  new_template.symbol
+    = convert_template_str (new_template.symbol).value_or (std::string ());
+
+  rust_debug_fmt_at (expr.get_locus (),
+                    "converting %<llvm_asm%> template %qs to %qs",
+                    expr.get_template ().symbol.c_str (),
+                    new_template.symbol.c_str ());
 
   translated
     = new HIR::LlvmInlineAsm (expr.get_locus (), inputs, outputs,
-                             {expr.get_template ()}, expr.get_clobbers (),
+                             std::move (new_template), expr.get_clobbers (),
                              options, expr.get_outer_attrs (), mapping);
 }
 
diff --git a/gcc/rust/hir/tree/rust-hir-expr.h 
b/gcc/rust/hir/tree/rust-hir-expr.h
index abd5e7d2d..02c6f2538 100644
--- a/gcc/rust/hir/tree/rust-hir-expr.h
+++ b/gcc/rust/hir/tree/rust-hir-expr.h
@@ -3297,18 +3297,18 @@ public:
   AST::AttrVec outer_attrs;
   std::vector<LlvmOperand> inputs;
   std::vector<LlvmOperand> outputs;
-  std::vector<AST::TupleTemplateStr> templates;
+  AST::TupleTemplateStr template_str;
   std::vector<AST::TupleClobber> clobbers;
   Options options;
 
   LlvmInlineAsm (location_t locus, std::vector<LlvmOperand> inputs,
                 std::vector<LlvmOperand> outputs,
-                std::vector<AST::TupleTemplateStr> templates,
+                AST::TupleTemplateStr template_str,
                 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)),
+      template_str (std::move (template_str)), clobbers (std::move (clobbers)),
       options (options)
   {}
 
@@ -3326,7 +3326,7 @@ public:
     return new LlvmInlineAsm (*this);
   }
 
-  std::vector<AST::TupleTemplateStr> &get_templates () { return templates; }
+  AST::TupleTemplateStr &get_template () { return template_str; }
 
   Expr::ExprType get_expression_type () const override
   {
diff --git a/gcc/rust/hir/tree/rust-hir-visitor.cc 
b/gcc/rust/hir/tree/rust-hir-visitor.cc
index 544c83db8..cf4650d00 100644
--- a/gcc/rust/hir/tree/rust-hir-visitor.cc
+++ b/gcc/rust/hir/tree/rust-hir-visitor.cc
@@ -595,6 +595,7 @@ DefaultHIRVisitor::walk (InlineAsm &expr)
 void
 DefaultHIRVisitor::walk (LlvmInlineAsm &expr)
 {
+  visit_outer_attrs (expr);
   for (auto &output : expr.outputs)
     output.expr->accept_vis (*this);
   for (auto &input : expr.inputs)
diff --git a/gcc/rust/typecheck/rust-hir-type-check-expr.cc 
b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
index fd9d1f953..aff96db1f 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-expr.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-expr.cc
@@ -1021,13 +1021,15 @@ TypeCheckExpr::visit (HIR::InlineAsm &expr)
 void
 TypeCheckExpr::visit (HIR::LlvmInlineAsm &expr)
 {
+  // TODO: verify input/output types?
+
   for (auto &i : expr.inputs)
     TypeCheckExpr::Resolve (*i.expr);
 
   for (auto &o : expr.outputs)
     TypeCheckExpr::Resolve (*o.expr);
 
-  // Black box hint is unit type
+  // always unit type
   infered = TyTy::TupleType::get_unit_type ();
 }
 

base-commit: eeec65b7b0154ac5f3337857aca391e3b2a66339
-- 
2.54.0

Reply via email to