From e5b56bd2b5596b99951955682ee7a097f454cc79 Mon Sep 17 00:00:00 2001 From: Yap Zhi Heng Date: Sun, 23 Aug 2026 15:34:47 +0800 Subject: [PATCH 1/4] gccrs: Make gcc targetm builtins available to the Rust front end This is part of making LLVM builtins work for gccrs. gcc/rust/ChangeLog: * backend/rust-builtins.h (BuiltinsContext::SetupState): New enum. * backend/rust-builtins.cc (BuiltinsContext::get): Ensure that BuiltinsContext is only initialized once. (BuiltinsContext::BuiltinsContext): Likewise. (BuiltinsContext::setup): Call targetm.init_builtins to make GCC builtins available to the frontend. * rust-gcc.cc (init): Eagerly initialize BuiltinsContext. Signed-Off-By: Yap Zhi Heng --- gcc/rust/backend/rust-builtins.cc | 13 ++++++++++++- gcc/rust/backend/rust-builtins.h | 9 +++++++++ gcc/rust/rust-gcc.cc | 3 +++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/gcc/rust/backend/rust-builtins.cc b/gcc/rust/backend/rust-builtins.cc index 881561fdde37..e685a4d1ea90 100644 --- a/gcc/rust/backend/rust-builtins.cc +++ b/gcc/rust/backend/rust-builtins.cc @@ -29,6 +29,10 @@ BuiltinsContext & BuiltinsContext::get () { static BuiltinsContext instance; + + if (instance.setup_state == SetupState::UNINITIALIZED) + instance.setup (); + return instance; } @@ -44,7 +48,7 @@ BuiltinsContext::lookup_simple_builtin (const std::string &name, tree *builtin) return lookup_gcc_builtin (*to_search, builtin); } -BuiltinsContext::BuiltinsContext () { setup (); } +BuiltinsContext::BuiltinsContext () : setup_state (SetupState::UNINITIALIZED) {} /** * Define a function type according to `builtin-types.def` @@ -358,11 +362,18 @@ BuiltinsContext::register_rust_mappings () void BuiltinsContext::setup () { + rust_assert (setup_state == SetupState::UNINITIALIZED); + setup_state = SetupState::INITIALIZING; + define_builtin_types (); define_builtin_attributes (); define_builtins (); + targetm.init_builtins (); + register_rust_mappings (); + + setup_state = SetupState::READY; } bool diff --git a/gcc/rust/backend/rust-builtins.h b/gcc/rust/backend/rust-builtins.h index 22250fa8905a..7819a7c03491 100644 --- a/gcc/rust/backend/rust-builtins.h +++ b/gcc/rust/backend/rust-builtins.h @@ -166,6 +166,15 @@ class BuiltinsContext ATTR_LAST, }; + enum class SetupState + { + UNINITIALIZED, + INITIALIZING, + READY, + }; + + SetupState setup_state; + /** * All builtin types, as defined in `builtin-types.def` * diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc index ada66a2c7c6b..c1f6002e8163 100644 --- a/gcc/rust/rust-gcc.cc +++ b/gcc/rust/rust-gcc.cc @@ -333,6 +333,9 @@ init () // this->define_builtin (BUILT_IN_ATOMIC_FETCH_OR_1, "__atomic_fetch_or_1", // NULL, // t, 0); + + // eagerly initialize BuiltinsContext + (void) Rust::Compile::BuiltinsContext::get (); } void From ef8e125b5781e2549abcef0a982b3f4af556a575 Mon Sep 17 00:00:00 2001 From: Yap Zhi Heng Date: Sun, 23 Aug 2026 15:46:45 +0800 Subject: [PATCH 2/4] gccrs: Initial implementation of LLVM builtin to GCC adapter Only works for rdseed, rdrand, addcarry and subborrow for now. Compiled 006t.original output from compiling llvm_builtins.rs: ``` struct (u16, i32) x86_rdrand16_step () { u16 RUSTTMP.0; i32 RUSTTMP.1; RUSTTMP.1 = __builtin_ia32_rdrand16_step (&RUSTTMP.0); return {.__0=RUSTTMP.0, .__1=RUSTTMP.1}; } struct (u32, i32) x86_rdrand32_step () { u32 RUSTTMP.2; i32 RUSTTMP.3; RUSTTMP.3 = __builtin_ia32_rdrand32_step (&RUSTTMP.2); return {.__0=RUSTTMP.2, .__1=RUSTTMP.3}; } struct (u64, i32) x86_rdrand64_step () { u64 RUSTTMP.4; i32 RUSTTMP.5; RUSTTMP.5 = __builtin_ia32_rdrand64_step ((unsigned long *) &RUSTTMP.4); return {.__0=RUSTTMP.4, .__1=RUSTTMP.5}; } struct (u16, i32) x86_rdseed16_step () { u16 RUSTTMP.6; i32 RUSTTMP.7; RUSTTMP.7 = __builtin_ia32_rdseed_hi_step (&RUSTTMP.6); return {.__0=RUSTTMP.6, .__1=RUSTTMP.7}; } struct (u32, i32) x86_rdseed32_step () { u32 RUSTTMP.8; i32 RUSTTMP.9; RUSTTMP.9 = __builtin_ia32_rdseed_si_step (&RUSTTMP.8); return {.__0=RUSTTMP.8, .__1=RUSTTMP.9}; } struct (u64, i32) x86_rdseed64_step () { u64 RUSTTMP.10; i32 RUSTTMP.11; RUSTTMP.11 = __builtin_ia32_rdseed_di_step ((unsigned long *) &RUSTTMP.10); return {.__0=RUSTTMP.10, .__1=RUSTTMP.11}; } struct (u8, u32) llvm_addcarry_u32 (const u8 a, const u32 b, const u32 c) { u32 RUSTTMP.12; u8 RUSTTMP.13; RUSTTMP.13 = (u8) __builtin_ia32_addcarryx_u32 ((unsigned char) a, (u32) b, (u32) c, &RUSTTMP.12); return {.__0=RUSTTMP.13, .__1=RUSTTMP.12}; } struct (u8, u32) llvm_subborrow_u32 (const u8 a, const u32 b, const u32 c) { u32 RUSTTMP.14; u8 RUSTTMP.15; RUSTTMP.15 = (u8) __builtin_ia32_sbb_u32 ((unsigned char) a, (u32) b, (u32) c, &RUSTTMP.14); return {.__0=RUSTTMP.15, .__1=RUSTTMP.14}; } struct (u8, u64) llvm_addcarry_u64 (const u8 a, const u64 b, const u64 c) { u64 RUSTTMP.16; u8 RUSTTMP.17; RUSTTMP.17 = (u8) __builtin_ia32_addcarryx_u64 ((unsigned char) a, (unsigned long) b, (unsigned long) c, (unsigned long *) &RUSTTMP.16); return {.__0=RUSTTMP.17, .__1=RUSTTMP.16}; } struct (u8, u64) llvm_subborrow_u64 (const u8 a, const u64 b, const u64 c) { u64 RUSTTMP.18; u8 RUSTTMP.19; RUSTTMP.19 = (u8) __builtin_ia32_sbb_u64 ((unsigned char) a, (unsigned long) b, (unsigned long) c, (unsigned long *) &RUSTTMP.18); return {.__0=RUSTTMP.19, .__1=RUSTTMP.18}; } ``` gcc/rust/ChangeLog: * backend/rust-builtins.h (LlvmBuiltinMappingResult): New enum. (LlvmBuiltinAdapter): New enum. (LlvmBuiltinMapping): New enum. (BuiltinsContext::map_llvm_to_gcc_builtin): New function for LLVM built-in mapping. (BuiltinsContext::register_builtin): New function for registering built-ins. (BuiltinsContext::register_llvm_to_gcc_builtin): New function. (BuiltinsContext::llvm_to_gcc_builtin): New map to translate function names. * backend/rust-builtins.cc (BuiltinsContext::map_llvm_to_gcc_builtin): Implementation. (BuiltinsContext::register_llvm_to_gcc_builtin): Implementation. (BuiltinsContext::register_builtin): Implementation. (BuiltinsContext::setup): Call register_llvm_to_gcc_builtin during setup. * rust-lang.cc (grs_langhook_builtin_function): Register GCC built-in functions. * backend/rust-compile-extern.h (CompileExternItem::OutputTupleOrder): New enum to denote the tuple field order of the LLVM built-in's return type. (CompileExternItem::compile_x86_output_pointer_adapter): New function to adapt LLVM's rdseed, rdrand, addcarry, subborrow function signatures to GCC's equivalents. (CompileExternItem::visit (HIR::ExternalFunctionItem)): Handle UNADJUSTED ABI case by properly resolving LLVM intrinsic function names. Signed-off-by: Yap Zhi Heng --- gcc/rust/backend/rust-builtins.cc | 71 +++++++ gcc/rust/backend/rust-builtins.h | 28 +++ gcc/rust/backend/rust-compile-extern.h | 213 ++++++++++++++++++++ gcc/rust/rust-lang.cc | 6 +- gcc/testsuite/rust/compile/llvm_builtins.rs | 50 +++++ 5 files changed, 366 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/rust/compile/llvm_builtins.rs diff --git a/gcc/rust/backend/rust-builtins.cc b/gcc/rust/backend/rust-builtins.cc index e685a4d1ea90..0d08cdf3d5fc 100644 --- a/gcc/rust/backend/rust-builtins.cc +++ b/gcc/rust/backend/rust-builtins.cc @@ -48,6 +48,25 @@ BuiltinsContext::lookup_simple_builtin (const std::string &name, tree *builtin) return lookup_gcc_builtin (*to_search, builtin); } +LlvmBuiltinMappingResult +BuiltinsContext::map_llvm_to_gcc_builtin (const std::string &name, + tree *resolved, + LlvmBuiltinAdapter *adapter) +{ + rust_assert (resolved != nullptr); + + auto mapping = llvm_to_gcc_builtin.find (name); + if (mapping == llvm_to_gcc_builtin.end ()) + return LlvmBuiltinMappingResult::NOT_MAPPED; + + *resolved = NULL_TREE; + if (!lookup_gcc_builtin (mapping->second.gcc_name, resolved)) + return LlvmBuiltinMappingResult::TARGET_UNAVAILABLE; + + *adapter = mapping->second.adapter; + return LlvmBuiltinMappingResult::RESOLVED; +} + BuiltinsContext::BuiltinsContext () : setup_state (SetupState::UNINITIALIZED) {} /** @@ -359,6 +378,57 @@ BuiltinsContext::register_rust_mappings () }; } +void +BuiltinsContext::register_llvm_to_gcc_builtin () +{ + llvm_to_gcc_builtin = { + {"llvm.x86.rdrand.16", + {"__builtin_ia32_rdrand16_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + {"llvm.x86.rdrand.32", + {"__builtin_ia32_rdrand32_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + {"llvm.x86.rdrand.64", + {"__builtin_ia32_rdrand64_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + {"llvm.x86.rdseed.16", + {"__builtin_ia32_rdseed_hi_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + {"llvm.x86.rdseed.32", + {"__builtin_ia32_rdseed_si_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + {"llvm.x86.rdseed.64", + {"__builtin_ia32_rdseed_di_step", + LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS}}, + + {"llvm.x86.addcarry.32", + {"__builtin_ia32_addcarryx_u32", + LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}, + {"llvm.x86.addcarryx.u32", + {"__builtin_ia32_addcarryx_u32", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + {"llvm.x86.subborrow.32", + {"__builtin_ia32_sbb_u32", + LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}, + {"llvm.x86.addcarry.64", + {"__builtin_ia32_addcarryx_u64", + LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}, + {"llvm.x86.addcarryx.u64", + {"__builtin_ia32_addcarryx_u64", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + {"llvm.x86.subborrow.64", + {"__builtin_ia32_sbb_u64", + LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}}; +} + +void +BuiltinsContext::register_builtin (tree decl) +{ + rust_assert (TREE_CODE (decl) == FUNCTION_DECL); + rust_assert (DECL_NAME (decl)); + + const char *name = IDENTIFIER_POINTER (DECL_NAME (decl)); + builtin_functions.insert ({std::string (name), decl}); +} + void BuiltinsContext::setup () { @@ -372,6 +442,7 @@ BuiltinsContext::setup () targetm.init_builtins (); register_rust_mappings (); + register_llvm_to_gcc_builtin (); setup_state = SetupState::READY; } diff --git a/gcc/rust/backend/rust-builtins.h b/gcc/rust/backend/rust-builtins.h index 7819a7c03491..e2c7f9816f9a 100644 --- a/gcc/rust/backend/rust-builtins.h +++ b/gcc/rust/backend/rust-builtins.h @@ -79,6 +79,26 @@ namespace Compile { // }; // Some(cx.get_intrinsic(&llvm_name)) +enum class LlvmBuiltinMappingResult +{ + NOT_MAPPED, + TARGET_UNAVAILABLE, + RESOLVED, +}; + +enum class LlvmBuiltinAdapter +{ + OUTPUT_POINTER_VALUE_STATUS, + OUTPUT_POINTER_STATUS_VALUE, + FORWARD_ARGUMENTS, +}; + +struct LlvmBuiltinMapping +{ + std::string gcc_name; + LlvmBuiltinAdapter adapter; +}; + class BuiltinsContext { public: @@ -86,6 +106,12 @@ class BuiltinsContext bool lookup_simple_builtin (const std::string &name, tree *builtin); + LlvmBuiltinMappingResult + map_llvm_to_gcc_builtin (const std::string &name, tree *resolved, + LlvmBuiltinAdapter *adapter); + + void register_builtin (tree decl); + private: enum Type { @@ -199,6 +225,7 @@ class BuiltinsContext void define_builtins (); void register_rust_mappings (); + void register_llvm_to_gcc_builtin (); BuiltinsContext (); @@ -213,6 +240,7 @@ class BuiltinsContext // A mapping of the GCC built-ins exposed to GCC Rust. std::map builtin_functions; std::map rust_intrinsic_to_gcc_builtin; + std::map llvm_to_gcc_builtin; }; } // namespace Compile diff --git a/gcc/rust/backend/rust-compile-extern.h b/gcc/rust/backend/rust-compile-extern.h index 4eabf0ddb853..f9aecef5b873 100644 --- a/gcc/rust/backend/rust-compile-extern.h +++ b/gcc/rust/backend/rust-compile-extern.h @@ -26,6 +26,9 @@ #include "rust-hir-full-decls.h" #include "rust-attributes.h" #include "rust-attribute-values.h" +#include "rust-builtins.h" +#include "rust-compile-fnparam.h" +#include "fold-const.h" namespace Rust { namespace Compile { @@ -127,6 +130,68 @@ class CompileExternItem : public HIRCompileBase, std::string ir_symbol_name = function.get_item_name ().as_string (); GGC::Ident asm_name = get_link_name (function); + if (fntype->get_abi () == ABI::UNADJUSTED) + { + tree resolved; + LlvmBuiltinAdapter adapter; + auto mapping_res = BuiltinsContext::get ().map_llvm_to_gcc_builtin ( + asm_name.as_string (), &resolved, &adapter); + + switch (mapping_res) + { + case LlvmBuiltinMappingResult::NOT_MAPPED: + rust_error_at (function.get_locus (), + "LLVM intrinsic %qs is not supported at the moment", + asm_name.c_str ()); + reference = error_mark_node; + return; + case LlvmBuiltinMappingResult::TARGET_UNAVAILABLE: + rust_error_at ( + function.get_locus (), + "LLVM intrinsic %qs is not available for this target", + asm_name.c_str ()); + reference = error_mark_node; + return; + case LlvmBuiltinMappingResult::RESOLVED: + break; + } + + tree adapter_tree = error_mark_node; + + switch (adapter) + { + case LlvmBuiltinAdapter::OUTPUT_POINTER_VALUE_STATUS: + adapter_tree = compile_x86_output_pointer_adapter ( + ctx, fntype, resolved, OutputTupleOrder::VALUE_STATUS, + function.get_locus ()); + break; + case LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE: + adapter_tree = compile_x86_output_pointer_adapter ( + ctx, fntype, resolved, OutputTupleOrder::STATUS_VALUE, + function.get_locus ()); + break; + case LlvmBuiltinAdapter::FORWARD_ARGUMENTS: + // TODO placeholder + adapter_tree = compile_x86_output_pointer_adapter ( + ctx, fntype, resolved, OutputTupleOrder::STATUS_VALUE, + function.get_locus ()); + break; + } + + if (adapter_tree == error_mark_node) + { + rust_error_at (function.get_locus (), + "Invalid signature for LLVM intrinsic %qs", + asm_name.c_str ()); + reference = error_mark_node; + return; + } + + ctx->insert_function_decl (fntype, adapter_tree); + reference = address_expression (adapter_tree, ref_locus); + return; + } + const unsigned int flags = Backend::function_is_declaration; tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name, flags, function.get_locus ()); @@ -144,6 +209,12 @@ class CompileExternItem : public HIRCompileBase, } private: + enum class OutputTupleOrder + { + VALUE_STATUS, + STATUS_VALUE, + }; + CompileExternItem (Context *ctx, TyTy::BaseType *concrete, location_t ref_locus) : HIRCompileBase (ctx), concrete (concrete), reference (error_mark_node), @@ -180,6 +251,148 @@ class CompileExternItem : public HIRCompileBase, return obj.get_item_name (); } + /** + * Compiles a wrapper that wraps around GCC built-ins for compatibility + * with LLVM built-ins function signatures returning a tuple with value + + * status. + * + * @param ctx + * @param fntype the LLVM built-in function type + * @param gcc_builtin the GCC built-in function + * @param order whether the LLVM built-in returns (value, status) or (status, + * value) + * @param locus + * @return tree + */ + static tree compile_x86_output_pointer_adapter (Context *ctx, + TyTy::FnType *fntype, + tree gcc_builtin, + OutputTupleOrder order, + location_t locus) + { + // expect to be a 2-field tuple return type + TyTy::TupleType *tuple + = fntype->get_return_type ()->try_as (); + + if (tuple == nullptr || tuple->num_fields () != 2) + // TODO probably add error here + return error_mark_node; + + tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype); + + const auto &path = fntype->get_ident ().path; + std::string ir_name = path.get () + fntype->subst_as_string (); + std::string asm_name = ctx->mangle_item (fntype, path); + + // start building the wrapper function + tree fndecl + = Backend::function (compiled_fn_type, ir_name, asm_name, 0, locus); + + TREE_PUBLIC (fndecl) = 0; + DECL_ARTIFICIAL (fndecl) = 1; + DECL_EXTERNAL (fndecl) = 0; + DECL_DECLARED_INLINE_P (fndecl) = 1; + + // compile params for the rust wrapper + std::vector param_vars; + param_vars.reserve (fntype->get_params ().size ()); + for (auto ¶m : fntype->get_params ()) + { + auto &pattern = param.get_pattern (); + tree type = TyTyResolveCompile::compile (ctx, param.get_type ()); + Bvariable *variable + = CompileFnParam::compile (ctx, fndecl, pattern, type, + pattern.get_locus ()); + param_vars.emplace_back (variable); + } + + if (!Backend::function_set_parameters (fndecl, param_vars)) + return error_mark_node; + + // forward the rust params, convert each into the corresponding gcc + // built-in param type + std::vector call_arguments; + // +1 here because gcc built-in expects output pointer as param + call_arguments.reserve (param_vars.size () + 1); + tree gcc_argument_types = TYPE_ARG_TYPES (TREE_TYPE (gcc_builtin)); + for (Bvariable *param : param_vars) + { + tree argument = param->get_tree (locus); + tree expected_type = TREE_VALUE (gcc_argument_types); + argument = Backend::convert_expression (expected_type, argument, locus); + call_arguments.emplace_back (argument); + gcc_argument_types = TREE_CHAIN (gcc_argument_types); + } + + // the order of llvm built-ins' tuple return types' fields is not + // consistent, some are (value, status) and others are (status, value), so + // we need to reorder it... + tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl)); + tree first_field = TYPE_FIELDS (tuple_type); + tree second_field = DECL_CHAIN (first_field); + + tree value_field + = order == OutputTupleOrder::VALUE_STATUS ? first_field : second_field; + tree status_field + = order == OutputTupleOrder::VALUE_STATUS ? second_field : first_field; + + tree value_type = TREE_TYPE (value_field); + tree status_type = TREE_TYPE (status_field); + + tree temporary_stmt = NULL_TREE; + Bvariable *value + = Backend::temporary_variable (fndecl, NULL_TREE, value_type, NULL_TREE, + true, locus, &temporary_stmt); + Bvariable *status + = Backend::temporary_variable (fndecl, NULL_TREE, status_type, NULL_TREE, + false, locus, &temporary_stmt); + + // compile the final gcc built-in param which is the output value pointer, + // which llvm returns as part of the tuple return type + tree gcc_pointer_type = TREE_VALUE (gcc_argument_types); + tree block + = Backend::block (fndecl, NULL_TREE, {value, status}, locus, locus); + ctx->push_block (block); + + tree value_decl = value->get_tree (locus); + tree status_decl = status->get_tree (locus); + + tree value_address = build_fold_addr_expr_loc (locus, value_decl); + value_address + = Backend::convert_expression (gcc_pointer_type, value_address, locus); + call_arguments.emplace_back (value_address); + + // call the gcc built-in with the params that were built and assign the + // returned val to the status temp var + tree builtin_call + = build_call_expr_loc_array (locus, gcc_builtin, + static_cast (call_arguments.size ()), + call_arguments.data ()); + builtin_call + = Backend::convert_expression (status_type, builtin_call, locus); + ctx->add_statement ( + Backend::assignment_statement (status_decl, builtin_call, locus)); + + // finally compile and add the return statement + std::vector tuple_values; + if (order == OutputTupleOrder::VALUE_STATUS) + tuple_values = {value_decl, status_decl}; + else + tuple_values = {status_decl, value_decl}; + + tree tuple_result + = Backend::constructor_expression (tuple_type, false, tuple_values, -1, + locus); + ctx->add_statement ( + Backend::return_statement (fndecl, tuple_result, locus)); + + tree body = ctx->pop_block (); + DECL_SAVED_TREE (fndecl) = body; + + ctx->push_function (fndecl); + return fndecl; + } + TyTy::BaseType *concrete; tree reference; location_t ref_locus; diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc index dc39bc053d0e..52f6e5a5b12a 100644 --- a/gcc/rust/rust-lang.cc +++ b/gcc/rust/rust-lang.cc @@ -38,6 +38,7 @@ #include "optional.h" #include "rust-unicode.h" #include "rust-punycode.h" +#include "rust-builtins.h" #include // note: header files must be in this order or else forward declarations don't @@ -226,10 +227,11 @@ grs_langhook_type_for_mode (machine_mode mode, int unsignedp) return NULL; } -// Record a builtin function. We just ignore builtin functions. +// Record a builtin function. static tree -grs_langhook_builtin_function (tree decl ATTRIBUTE_UNUSED) +grs_langhook_builtin_function (tree decl) { + Rust::Compile::BuiltinsContext::get ().register_builtin (decl); return decl; } diff --git a/gcc/testsuite/rust/compile/llvm_builtins.rs b/gcc/testsuite/rust/compile/llvm_builtins.rs new file mode 100644 index 000000000000..6503c0be2bde --- /dev/null +++ b/gcc/testsuite/rust/compile/llvm_builtins.rs @@ -0,0 +1,50 @@ +// { dg-options "-mrdrnd" } +#![feature(no_core, abi_unadjusted)] +#![no_core] + +#[allow(improper_ctypes)] +extern "unadjusted" { + #[link_name = "llvm.x86.rdrand.16"] + fn x86_rdrand16_step() -> (u16, i32); + #[link_name = "llvm.x86.rdrand.32"] + fn x86_rdrand32_step() -> (u32, i32); + #[link_name = "llvm.x86.rdrand.64"] + fn x86_rdrand64_step() -> (u64, i32); + #[link_name = "llvm.x86.rdseed.16"] + fn x86_rdseed16_step() -> (u16, i32); + #[link_name = "llvm.x86.rdseed.32"] + fn x86_rdseed32_step() -> (u32, i32); + #[link_name = "llvm.x86.rdseed.64"] + fn x86_rdseed64_step() -> (u64, i32); + + #[link_name = "llvm.x86.addcarry.32"] + fn llvm_addcarry_u32(a: u8, b: u32, c: u32) -> (u8, u32); + // #[link_name = "llvm.x86.addcarryx.u32"] + // fn llvm_addcarryx_u32(a: u8, b: u32, c: u32, d: *mut u8) -> u8; + #[link_name = "llvm.x86.subborrow.32"] + fn llvm_subborrow_u32(a: u8, b: u32, c: u32) -> (u8, u32); + #[link_name = "llvm.x86.addcarry.64"] + fn llvm_addcarry_u64(a: u8, b: u64, c: u64) -> (u8, u64); + // #[link_name = "llvm.x86.addcarryx.u64"] + // fn llvm_addcarryx_u64(a: u8, b: u64, c: u64, d: *mut u8) -> u8; + #[link_name = "llvm.x86.subborrow.64"] + fn llvm_subborrow_u64(a: u8, b: u64, c: u64) -> (u8, u64); + + // #[link_name = "llvm.x86.vcvtph2ps.128"] + // fn llvm_vcvtph2ps_128(a: i16x8) -> f32x4; + // #[link_name = "llvm.x86.vcvtph2ps.256"] + // fn llvm_vcvtph2ps_256(a: i16x8) -> f32x8; + // #[link_name = "llvm.x86.vcvtps2ph.128"] + // fn llvm_vcvtps2ph_128(a: f32x4, rounding: i32) -> i16x8; + // #[link_name = "llvm.x86.vcvtps2ph.256"] + // fn llvm_vcvtps2ph_256(a: f32x8, rounding: i32) -> i16x8; +} + +fn main() { + let (_status2, _v1) = unsafe { + llvm_addcarry_u32(0, 1, 2) + }; + let (_v2, _status2) = unsafe { + x86_rdrand16_step() + }; +} From 8b936192fff9c44d1ce909d9709be23c0b1b24f3 Mon Sep 17 00:00:00 2001 From: Yap Zhi Heng Date: Sat, 29 Aug 2026 16:11:37 +0800 Subject: [PATCH 3/4] gccrs: Implement 1-to-1 adapters for compiling LLVM built-ins signatures with GCC built-ins Similar to the previous commit, but for FORWARD_ARGUMENTS cases in CompileExternItem::visit (ExternalFunctionItem). Compiled 006t.original output from compiling llvm_builtins.rs for only the enabled FORWARD_ARGUMENT cases (): ``` u8 llvm_addcarryx_u32 (const u8 a, const u32 b, const u32 c, u8 * const {ref-all} d) { return (u8) __builtin_ia32_addcarryx_u32 ((unsigned char) a, (u32) b, (u32) c, (u32 *) d); } u8 llvm_addcarryx_u64 (const u8 a, const u64 b, const u64 c, u8 * const {ref-all} d) { return (u8) __builtin_ia32_addcarryx_u64 ((unsigned char) a, (unsigned long) b, (unsigned long) c, (unsigned long *) d); } ``` vcvtph2ps and vcvtps2ph functions are not supported yet as gccrs doesn't support SIMD types yet. gcc/rust/ChangeLog: * backend/rust-builtins.cc (BuiltinsContext::register_llvm_to_gcc_builtin): Add entries for vcvtph2ps & vcvtps2ph cases (currently unused due to the reason mentioned above). * backend/rust-compile-extern.h (CompileExternItem::visit(HIR::ExternalFunctionItem)): Remove placeholder function in the FORWARD_ARGUMENTS case under compilation of UNADJUSTED ABI. (CompileExternItem::compile_x86_forwarding_adapter): New function to adapt LLVM's addcarryx function signatures by simply forwarding the parameters and return var without changes. gcc/testsuite/ChangeLog: * rust/compile/llvm_builtins.rs: Enable addcarryx cases, add TODO comment for vcvtph2ps & vcvtps2ph cases. Signed-off-by: Yap Zhi Heng --- gcc/rust/backend/rust-builtins.cc | 12 ++- gcc/rust/backend/rust-compile-extern.h | 88 +++++++++++++++++++-- gcc/testsuite/rust/compile/llvm_builtins.rs | 10 ++- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/gcc/rust/backend/rust-builtins.cc b/gcc/rust/backend/rust-builtins.cc index 0d08cdf3d5fc..12ae27e20e11 100644 --- a/gcc/rust/backend/rust-builtins.cc +++ b/gcc/rust/backend/rust-builtins.cc @@ -416,7 +416,17 @@ BuiltinsContext::register_llvm_to_gcc_builtin () {"__builtin_ia32_addcarryx_u64", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, {"llvm.x86.subborrow.64", {"__builtin_ia32_sbb_u64", - LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}}; + LlvmBuiltinAdapter::OUTPUT_POINTER_STATUS_VALUE}}, + + {"llvm.x86.vcvtph2ps.128", + {"__builtin_ia32_vcvtph2ps", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + {"llvm.x86.vcvtph2ps.256", + {"__builtin_ia32_vcvtph2ps256", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + {"llvm.x86.vcvtps2ph.128", + {"__builtin_ia32_vcvtps2ph", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + {"llvm.x86.vcvtps2ph.256", + {"__builtin_ia32_vcvtps2ph256", LlvmBuiltinAdapter::FORWARD_ARGUMENTS}}, + }; } void diff --git a/gcc/rust/backend/rust-compile-extern.h b/gcc/rust/backend/rust-compile-extern.h index f9aecef5b873..b2237b77c246 100644 --- a/gcc/rust/backend/rust-compile-extern.h +++ b/gcc/rust/backend/rust-compile-extern.h @@ -171,10 +171,9 @@ class CompileExternItem : public HIRCompileBase, function.get_locus ()); break; case LlvmBuiltinAdapter::FORWARD_ARGUMENTS: - // TODO placeholder - adapter_tree = compile_x86_output_pointer_adapter ( - ctx, fntype, resolved, OutputTupleOrder::STATUS_VALUE, - function.get_locus ()); + adapter_tree + = compile_x86_forwarding_adapter (ctx, fntype, resolved, + function.get_locus ()); break; } @@ -262,7 +261,7 @@ class CompileExternItem : public HIRCompileBase, * @param order whether the LLVM built-in returns (value, status) or (status, * value) * @param locus - * @return tree + * @return tree the resultant wrapper function */ static tree compile_x86_output_pointer_adapter (Context *ctx, TyTy::FnType *fntype, @@ -393,6 +392,85 @@ class CompileExternItem : public HIRCompileBase, return fndecl; } + /** + * Compiles a 1-to-1 wrapper for LLVM built-ins that wraps around GCC + * built-ins. + * + * @param ctx + * @param fntype the LLVM built-in function type + * @param gcc_builtin the GCC built-in function + * @param locus + * @return tree the resultant wrapper function + */ + static tree compile_x86_forwarding_adapter (Context *ctx, + TyTy::FnType *fntype, + tree gcc_builtin, + location_t locus) + { + tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype); + + const auto &path = fntype->get_ident ().path; + std::string ir_name = path.get () + fntype->subst_as_string (); + std::string asm_name = ctx->mangle_item (fntype, path); + + // start building the wrapper function + tree fndecl + = Backend::function (compiled_fn_type, ir_name, asm_name, 0, locus); + + TREE_PUBLIC (fndecl) = 0; + DECL_ARTIFICIAL (fndecl) = 1; + DECL_EXTERNAL (fndecl) = 0; + DECL_DECLARED_INLINE_P (fndecl) = 1; + + // compile params for the rust wrapper + std::vector param_vars; + param_vars.reserve (fntype->get_params ().size ()); + for (auto ¶m : fntype->get_params ()) + { + auto &pattern = param.get_pattern (); + tree type = TyTyResolveCompile::compile (ctx, param.get_type ()); + Bvariable *variable + = CompileFnParam::compile (ctx, fndecl, pattern, type, + pattern.get_locus ()); + param_vars.emplace_back (variable); + } + + if (!Backend::function_set_parameters (fndecl, param_vars)) + return error_mark_node; + + // forward the rust params, convert each into the corresponding gcc + // built-in param type + std::vector call_arguments; + call_arguments.reserve (param_vars.size ()); + tree gcc_argument_types = TYPE_ARG_TYPES (TREE_TYPE (gcc_builtin)); + for (Bvariable *param : param_vars) + { + tree argument = param->get_tree (locus); + tree expected_type = TREE_VALUE (gcc_argument_types); + argument = Backend::convert_expression (expected_type, argument, locus); + call_arguments.emplace_back (argument); + gcc_argument_types = TREE_CHAIN (gcc_argument_types); + } + + tree builtin_call + = build_call_expr_loc_array (locus, gcc_builtin, + static_cast (call_arguments.size ()), + call_arguments.data ()); + tree wrapper_ret_type = TREE_TYPE (DECL_RESULT (fndecl)); + + builtin_call + = Backend::convert_expression (wrapper_ret_type, builtin_call, locus); + tree block = Backend::block (fndecl, NULL_TREE, {}, locus, locus); + ctx->push_block (block); + ctx->add_statement ( + Backend::return_statement (fndecl, builtin_call, locus)); + tree body = ctx->pop_block (); + DECL_SAVED_TREE (fndecl) = body; + + ctx->push_function (fndecl); + return fndecl; + } + TyTy::BaseType *concrete; tree reference; location_t ref_locus; diff --git a/gcc/testsuite/rust/compile/llvm_builtins.rs b/gcc/testsuite/rust/compile/llvm_builtins.rs index 6503c0be2bde..7f44eb02fa9d 100644 --- a/gcc/testsuite/rust/compile/llvm_builtins.rs +++ b/gcc/testsuite/rust/compile/llvm_builtins.rs @@ -19,17 +19,19 @@ extern "unadjusted" { #[link_name = "llvm.x86.addcarry.32"] fn llvm_addcarry_u32(a: u8, b: u32, c: u32) -> (u8, u32); - // #[link_name = "llvm.x86.addcarryx.u32"] - // fn llvm_addcarryx_u32(a: u8, b: u32, c: u32, d: *mut u8) -> u8; + #[link_name = "llvm.x86.addcarryx.u32"] + fn llvm_addcarryx_u32(a: u8, b: u32, c: u32, d: *mut u8) -> u8; #[link_name = "llvm.x86.subborrow.32"] fn llvm_subborrow_u32(a: u8, b: u32, c: u32) -> (u8, u32); #[link_name = "llvm.x86.addcarry.64"] fn llvm_addcarry_u64(a: u8, b: u64, c: u64) -> (u8, u64); - // #[link_name = "llvm.x86.addcarryx.u64"] - // fn llvm_addcarryx_u64(a: u8, b: u64, c: u64, d: *mut u8) -> u8; + #[link_name = "llvm.x86.addcarryx.u64"] + fn llvm_addcarryx_u64(a: u8, b: u64, c: u64, d: *mut u8) -> u8; #[link_name = "llvm.x86.subborrow.64"] fn llvm_subborrow_u64(a: u8, b: u64, c: u64) -> (u8, u64); + // TODO implement the SIMD types (i16x8, f32x4, f32x8) before + // enabling these tests // #[link_name = "llvm.x86.vcvtph2ps.128"] // fn llvm_vcvtph2ps_128(a: i16x8) -> f32x4; // #[link_name = "llvm.x86.vcvtph2ps.256"] From 52646989cd5dce29e9742c4048df2f6907dd3f9d Mon Sep 17 00:00:00 2001 From: Yap Zhi Heng Date: Sat, 29 Aug 2026 23:47:06 +0800 Subject: [PATCH 4/4] gccrs: Update llvm_builtins test cases gcc/testsuite/ChangeLog: * rust/compile/llvm_builtins.rs: Removed 64-bit functions, ensure main calls all llvm built-ins, add dg-final directives to check the generated tree dump, ensure the test only gets compiled on x86 targets. * rust/compile/llvm_builtins_64_bit.rs: New test derived from llvm_builtins.rs, but for 64-bit functions. Signed-off-by: Yap Zhi Heng --- gcc/testsuite/rust/compile/llvm_builtins.rs | 38 ++++++++++------- .../rust/compile/llvm_builtins_64_bit.rs | 42 +++++++++++++++++++ 2 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 gcc/testsuite/rust/compile/llvm_builtins_64_bit.rs diff --git a/gcc/testsuite/rust/compile/llvm_builtins.rs b/gcc/testsuite/rust/compile/llvm_builtins.rs index 7f44eb02fa9d..066c9d1a7181 100644 --- a/gcc/testsuite/rust/compile/llvm_builtins.rs +++ b/gcc/testsuite/rust/compile/llvm_builtins.rs @@ -1,4 +1,5 @@ -// { dg-options "-mrdrnd" } +// { dg-do compile { target { { i?86-*-* x86_64-*-* } && ia32 } } } +// { dg-options "-mrdrnd -mrdseed -fdump-tree-gimple" } #![feature(no_core, abi_unadjusted)] #![no_core] @@ -8,14 +9,10 @@ extern "unadjusted" { fn x86_rdrand16_step() -> (u16, i32); #[link_name = "llvm.x86.rdrand.32"] fn x86_rdrand32_step() -> (u32, i32); - #[link_name = "llvm.x86.rdrand.64"] - fn x86_rdrand64_step() -> (u64, i32); #[link_name = "llvm.x86.rdseed.16"] fn x86_rdseed16_step() -> (u16, i32); #[link_name = "llvm.x86.rdseed.32"] fn x86_rdseed32_step() -> (u32, i32); - #[link_name = "llvm.x86.rdseed.64"] - fn x86_rdseed64_step() -> (u64, i32); #[link_name = "llvm.x86.addcarry.32"] fn llvm_addcarry_u32(a: u8, b: u32, c: u32) -> (u8, u32); @@ -23,12 +20,6 @@ extern "unadjusted" { fn llvm_addcarryx_u32(a: u8, b: u32, c: u32, d: *mut u8) -> u8; #[link_name = "llvm.x86.subborrow.32"] fn llvm_subborrow_u32(a: u8, b: u32, c: u32) -> (u8, u32); - #[link_name = "llvm.x86.addcarry.64"] - fn llvm_addcarry_u64(a: u8, b: u64, c: u64) -> (u8, u64); - #[link_name = "llvm.x86.addcarryx.u64"] - fn llvm_addcarryx_u64(a: u8, b: u64, c: u64, d: *mut u8) -> u8; - #[link_name = "llvm.x86.subborrow.64"] - fn llvm_subborrow_u64(a: u8, b: u64, c: u64) -> (u8, u64); // TODO implement the SIMD types (i16x8, f32x4, f32x8) before // enabling these tests @@ -43,10 +34,29 @@ extern "unadjusted" { } fn main() { - let (_status2, _v1) = unsafe { + let (_v1, _s1) = unsafe { + x86_rdrand16_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdrand16_step} 1 gimple } } + }; + let (_v2, _s2) = unsafe { + x86_rdrand32_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdrand32_step} 1 gimple } } + }; + let (_v3, _s3) = unsafe { + x86_rdseed16_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdseed_hi_step} 1 gimple } } + }; + let (_v4, _s4) = unsafe { + x86_rdseed32_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdseed_si_step} 1 gimple } } + }; + + // { dg-final { scan-tree-dump-times {__builtin_ia32_addcarryx_u32} 2 gimple } } + let (_v5, _s5) = unsafe { llvm_addcarry_u32(0, 1, 2) }; - let (_v2, _status2) = unsafe { - x86_rdrand16_step() + let _s6 = unsafe { + let _temp_ptr: *mut u8; + llvm_addcarryx_u32(0, 1, 2, _temp_ptr) + }; + + let (_v7, _s7) = unsafe { + llvm_subborrow_u32(0, 1, 2) // { dg-final { scan-tree-dump-times {__builtin_ia32_sbb_u32} 1 gimple } } }; } diff --git a/gcc/testsuite/rust/compile/llvm_builtins_64_bit.rs b/gcc/testsuite/rust/compile/llvm_builtins_64_bit.rs new file mode 100644 index 000000000000..c3ba93b069c1 --- /dev/null +++ b/gcc/testsuite/rust/compile/llvm_builtins_64_bit.rs @@ -0,0 +1,42 @@ +// { dg-do compile { target { { i?86-*-* x86_64-*-* } && { ! ia32 } } } } +// { dg-options "-mrdrnd -m64 -mrdseed -fdump-tree-gimple" } +#![feature(no_core, abi_unadjusted)] +#![no_core] + +#[allow(improper_ctypes)] +extern "unadjusted" { + #[link_name = "llvm.x86.rdrand.64"] + fn x86_rdrand64_step() -> (u64, i32); + #[link_name = "llvm.x86.rdseed.64"] + fn x86_rdseed64_step() -> (u64, i32); + + #[link_name = "llvm.x86.addcarry.64"] + fn llvm_addcarry_u64(a: u8, b: u64, c: u64) -> (u8, u64); + #[link_name = "llvm.x86.addcarryx.u64"] + fn llvm_addcarryx_u64(a: u8, b: u64, c: u64, d: *mut u8) -> u8; + #[link_name = "llvm.x86.subborrow.64"] + fn llvm_subborrow_u64(a: u8, b: u64, c: u64) -> (u8, u64); +} + +fn main() { + let (_v1, _s1) = unsafe { + x86_rdrand64_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdrand64_step} 1 gimple } } + }; + let (_v2, _s2) = unsafe { + x86_rdseed64_step() // { dg-final { scan-tree-dump-times {__builtin_ia32_rdseed_di_step} 1 gimple } } + }; + + // { dg-final { scan-tree-dump-times {__builtin_ia32_addcarryx_u64} 2 gimple } } + let (_s3, _v3) = unsafe { + llvm_addcarry_u64(0, 1, 2) + }; + let _s4 = unsafe { + let _temp_ptr: *mut u8; + llvm_addcarryx_u64(0, 1, 2, _temp_ptr) + }; + + // { dg-final { scan-tree-dump-times {__builtin_ia32_sbb_u64} 1 gimple } } + let (_s5, _v5) = unsafe { + llvm_subborrow_u64(0, 1, 2) + }; +}