https://gcc.gnu.org/g:4b9b1bfe119945c7855e51c047d36615a77fa995
commit r17-1890-g4b9b1bfe119945c7855e51c047d36615a77fa995 Author: Enes Cevik <[email protected]> Date: Mon May 25 19:00:08 2026 +0300 gccrs: attr: Add attributes rustc_allocator and rustc_allocator_nounwind This patch introduces the `rustc_allocator` and `rustc_allocator_nounwind` attributes. These attributes instruct the GCC backend to apply `malloc` and `nothrow`. gcc/rust/ChangeLog: * backend/rust-compile-base.cc (HIRCompileBase::setup_fndecl): Dispatch the new attributes. (HIRCompileBase::handle_rustc_allocator_on_fndecl): New function. (HIRCompileBase::handle_rustc_allocator_nounwind_on_fndecl): New function. * backend/rust-compile-base.h (handle_rustc_allocator_on_fndecl): New declaration. (handle_rustc_allocator_nounwind_on_fndecl): Likewise. * util/rust-attribute-values.h (Attributes): Add RUSTC_ALLOCATOR and RUSTC_ALLOCATOR_NOUNWIND constexprs. * util/rust-attributes.cc (__definitions): Register rustc_allocator and rustc_allocator_nounwind in the BuiltinAttributes list. * checks/errors/rust-builtin-attribute-checker.cc (rustc_allocator): New function. (rustc_allocator_nounwind): New function. (attribute_checking_handlers): Add rustc_allocator and rustc_allocator_nounwind functions. gcc/testsuite/ChangeLog: * rust/compile/attr-allocator1.rs: New test. * rust/compile/attr-allocator2.rs: New test. * rust/compile/attr-allocator3.rs: New test. Signed-off-by: Enes Cevik <[email protected]> Diff: --- gcc/rust/backend/rust-compile-base.cc | 60 ++++++++++++++++++++++ gcc/rust/backend/rust-compile-base.h | 7 +++ .../errors/rust-builtin-attribute-checker.cc | 26 ++++++++++ gcc/rust/util/rust-attribute-values.h | 3 ++ gcc/rust/util/rust-attributes.cc | 4 +- gcc/testsuite/rust/compile/attr-allocator1.rs | 12 +++++ gcc/testsuite/rust/compile/attr-allocator2.rs | 10 ++++ gcc/testsuite/rust/compile/attr-allocator3.rs | 6 +++ 8 files changed, 127 insertions(+), 1 deletion(-) diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc index 1318b876e650..54b531173e94 100644 --- a/gcc/rust/backend/rust-compile-base.cc +++ b/gcc/rust/backend/rust-compile-base.cc @@ -96,6 +96,9 @@ HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point, = attr_str == Values::Attributes::PROC_MACRO_ATTRIBUTE; bool is_proc_macro_derive = attr_str == Values::Attributes::PROC_MACRO_DERIVE; + bool is_allocator = attr_str == Values::Attributes::RUSTC_ALLOCATOR; + bool is_allocator_nounwind + = attr_str == Values::Attributes::RUSTC_ALLOCATOR_NOUNWIND; if (is_inline) { @@ -137,6 +140,14 @@ HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point, { handle_derive_proc_macro_attribute_on_fndecl (fndecl, attr); } + else if (is_allocator) + { + handle_rustc_allocator_on_fndecl (fndecl, attr); + } + else if (is_allocator_nounwind) + { + handle_rustc_allocator_nounwind_on_fndecl (fndecl, attr); + } } } @@ -298,6 +309,55 @@ HIRCompileBase::handle_rustc_std_internal_symbol_attribute_on_fndecl ( NULL_TREE, DECL_ATTRIBUTES (fndecl)); } +void +HIRCompileBase::handle_rustc_allocator_on_fndecl (tree fndecl, + const AST::Attribute &attr) +{ + tree return_type = TREE_TYPE (TREE_TYPE (fndecl)); + if (!POINTER_TYPE_P (return_type)) + { + rust_error_at (attr.get_locus (), + "%<rustc_allocator%> attribute must be applied to a " + "function returning a pointer"); + return; + } + + // https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs#L49 + // `#[rustc_allocator]`: a hint to LLVM that the pointer returned from this + // function is never null. + // + // https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_typeck/src/collect.rs#L2482 + // This attribute sets CodegenFnAttrFlags::ALLOCATOR. + // + // https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_codegen_llvm/src/attributes.rs#L302 + // This flag activates LLVM::NoAlias attribute. + // + // In GCC, setting DECL_IS_MALLOC on a FUNCTION_DECL means this function + // should be treated as if it were a malloc, meaning it returns a pointer that + // is not an alias. + DECL_IS_MALLOC (fndecl) = 1; + DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("malloc"), NULL_TREE, + DECL_ATTRIBUTES (fndecl)); +} + +void +HIRCompileBase::handle_rustc_allocator_nounwind_on_fndecl ( + tree fndecl, const AST::Attribute &attr) +{ + // https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_middle/src/middle/codegen_fn_attrs.rs#L55 + // `#[rustc_allocator_nounwind]`: an indicator that an imported FFI + // function will never unwind. + // + // https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/compiler/rustc_typeck/src/collect.rs#L2484 + // This attribute sets CodegenFnAttrFlags::NOUNWIND. + // + // In GCC, setting TREE_NOTHROW on a FUNCTION_DECL means a call to the + // function cannot throw an exception, which exactly matches this behavior. + TREE_NOTHROW (fndecl) = 1; + DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("nothrow"), NULL_TREE, + DECL_ATTRIBUTES (fndecl)); +} + void HIRCompileBase::handle_deprecated_attribute_on_fndecl ( tree fndecl, const AST::Attribute &attr) diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h index 551a02e4d82b..d93cc3ba3dfe 100644 --- a/gcc/rust/backend/rust-compile-base.h +++ b/gcc/rust/backend/rust-compile-base.h @@ -155,6 +155,13 @@ protected: static void handle_rustc_std_internal_symbol_attribute_on_fndecl ( tree fndecl, const AST::Attribute &attr); + static void handle_rustc_allocator_on_fndecl (tree fndecl, + const AST::Attribute &attr); + + static void + handle_rustc_allocator_nounwind_on_fndecl (tree fndecl, + const AST::Attribute &attr); + static void setup_abi_options (tree fndecl, ABI abi); static tree indirect_expression (tree expr, location_t locus); diff --git a/gcc/rust/checks/errors/rust-builtin-attribute-checker.cc b/gcc/rust/checks/errors/rust-builtin-attribute-checker.cc index 46b96f23aced..ba1bf6be1153 100644 --- a/gcc/rust/checks/errors/rust-builtin-attribute-checker.cc +++ b/gcc/rust/checks/errors/rust-builtin-attribute-checker.cc @@ -370,6 +370,30 @@ rustc_std_internal_symbol (const AST::Attribute &attribute) } } +void +rustc_allocator (const AST::Attribute &attribute) +{ + if (attribute.has_attr_input ()) + { + rust_error_at (attribute.get_locus (), + "malformed %<rustc_allocator%> attribute input"); + rust_inform (attribute.get_locus (), + "must be of the form: %<#[rustc_allocator]%>"); + } +} + +void +rustc_allocator_nounwind (const AST::Attribute &attribute) +{ + if (attribute.has_attr_input ()) + { + rust_error_at (attribute.get_locus (), + "malformed %<rustc_allocator_nounwind%> attribute input"); + rust_inform (attribute.get_locus (), + "must be of the form: %<#[rustc_allocator_nounwind]%>"); + } +} + } // namespace handlers const std::unordered_map<std::string, std::function<void (AST::Attribute &)>> @@ -389,6 +413,8 @@ const std::unordered_map<std::string, std::function<void (AST::Attribute &)>> {Attrs::PROC_MACRO_ATTRIBUTE, handlers::proc_macro}, {Attrs::TARGET_FEATURE, handlers::target_feature}, {Attrs::RUSTC_STD_INTERNAL_SYMBOL, handlers::rustc_std_internal_symbol}, + {Attrs::RUSTC_ALLOCATOR, handlers::rustc_allocator}, + {Attrs::RUSTC_ALLOCATOR_NOUNWIND, handlers::rustc_allocator_nounwind}, }; tl::optional<std::function<void (AST::Attribute &)>> diff --git a/gcc/rust/util/rust-attribute-values.h b/gcc/rust/util/rust-attribute-values.h index 02d915f1acbd..9c14df39b1cd 100644 --- a/gcc/rust/util/rust-attribute-values.h +++ b/gcc/rust/util/rust-attribute-values.h @@ -107,6 +107,9 @@ public: static constexpr auto &RUSTC_ARGS_REQUIRED_CONST = "rustc_args_required_const"; + + static constexpr auto &RUSTC_ALLOCATOR = "rustc_allocator"; + static constexpr auto &RUSTC_ALLOCATOR_NOUNWIND = "rustc_allocator_nounwind"; }; } // namespace Values } // namespace Rust diff --git a/gcc/rust/util/rust-attributes.cc b/gcc/rust/util/rust-attributes.cc index 7042f8bd10ed..08115d4653c5 100644 --- a/gcc/rust/util/rust-attributes.cc +++ b/gcc/rust/util/rust-attributes.cc @@ -94,7 +94,9 @@ static const BuiltinAttrDefinition __definitions[] {Attrs::FUNDAMENTAL, TYPE_CHECK}, {Attrs::NON_EXHAUSTIVE, TYPE_CHECK}, {Attrs::RUSTFMT, EXTERNAL}, - {Attrs::TEST, CODE_GENERATION}}; + {Attrs::TEST, CODE_GENERATION}, + {Attrs::RUSTC_ALLOCATOR, CODE_GENERATION}, + {Attrs::RUSTC_ALLOCATOR_NOUNWIND, CODE_GENERATION}}; static const std::set<std::string> __outer_attributes = {Attrs::INLINE, diff --git a/gcc/testsuite/rust/compile/attr-allocator1.rs b/gcc/testsuite/rust/compile/attr-allocator1.rs new file mode 100644 index 000000000000..282109580642 --- /dev/null +++ b/gcc/testsuite/rust/compile/attr-allocator1.rs @@ -0,0 +1,12 @@ +// { dg-additional-options "-fdump-tree-gimple" } +#![feature(no_core, rustc_attrs)] +#![no_core] + +#[rustc_allocator] +#[rustc_allocator_nounwind] +fn _foo() -> *mut u8 { + 0 as *mut u8 +} + +// { dg-final { scan-tree-dump "malloc" "gimple" } } +// { dg-final { scan-tree-dump "nothrow" "gimple" } } diff --git a/gcc/testsuite/rust/compile/attr-allocator2.rs b/gcc/testsuite/rust/compile/attr-allocator2.rs new file mode 100644 index 000000000000..215204391d71 --- /dev/null +++ b/gcc/testsuite/rust/compile/attr-allocator2.rs @@ -0,0 +1,10 @@ +#![feature(no_core, rustc_attrs)] +#![no_core] + +#[rustc_allocator(invalid)] // { dg-error "malformed .rustc_allocator. attribute input" } +pub fn _foo() -> *mut u8 { + 0 as *mut u8 +} + +#[rustc_allocator_nounwind(invalid)] // { dg-error "malformed .rustc_allocator_nounwind. attribute input" } +pub fn _bar() {} diff --git a/gcc/testsuite/rust/compile/attr-allocator3.rs b/gcc/testsuite/rust/compile/attr-allocator3.rs new file mode 100644 index 000000000000..9eb70452bef9 --- /dev/null +++ b/gcc/testsuite/rust/compile/attr-allocator3.rs @@ -0,0 +1,6 @@ +#![feature(no_core, rustc_attrs)] +#![no_core] + +#[rustc_allocator] // { dg-error ".rustc_allocator. attribute must be applied to a function returning a pointer" } +pub fn _foo() {} +
