https://gcc.gnu.org/g:b709aa449e06ffbdc12eb4ba6266ef71a3adef9f
commit r17-4128-gb709aa449e06ffbdc12eb4ba6266ef71a3adef9f Author: Owen Avery <[email protected]> Date: Fri Aug 14 21:33:23 2026 -0400 gccrs: Handle more llvm_asm usages 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]> Diff: --- 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 + gcc/rust/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 43f5802d3598..a30282000e72 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 2ac65bd6520b..73873e44049a 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 8dc2ed04cde4..77811f2b8dac 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 abd5e7d2dc5d..02c6f253810a 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 544c83db8b4d..cf4650d0031b 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 fd9d1f95399c..aff96db1fd8f 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 (); }
