diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in index e6a2099f043c..386fccbe83b5 100644 --- a/gcc/rust/Make-lang.in +++ b/gcc/rust/Make-lang.in @@ -64,6 +64,7 @@ gccrs$(exeext): $(GCCRS_D_OBJS) $(EXTRA_GCC_OBJS) libcommon-target.a $(LIBDEPS) # The compiler proper, not driver GRS_OBJS = \ rust/rust-lang.o \ + rust/rust-attribs.o \ rust/rust-object-export.o \ rust/rust-linemap.o \ rust/rust-diagnostics.o \ diff --git a/gcc/rust/backend/rust-builtins.cc b/gcc/rust/backend/rust-builtins.cc index ee3e42e6b534..626adf3bba1c 100644 --- a/gcc/rust/backend/rust-builtins.cc +++ b/gcc/rust/backend/rust-builtins.cc @@ -24,10 +24,6 @@ namespace Rust { namespace Compile { -static const int builtin_const = 1 << 0; -static const int builtin_noreturn = 1 << 1; -static const int builtin_novops = 1 << 2; - BuiltinsContext & BuiltinsContext::get () { @@ -74,6 +70,8 @@ BuiltinsContext::define_function_type (Type def_idx, Type ret_idx, auto return_type = builtin_types[ret_idx]; if (return_type == error_mark_node) { + // Mark the builtin as not available. + builtin_types[def_idx] = error_mark_node; va_end (list); return; } @@ -287,8 +285,46 @@ BuiltinsContext::register_rust_mappings () rust_intrinsic_to_gcc_builtin = { {"sinf32", "__builtin_sinf"}, {"sqrtf32", "__builtin_sqrtf"}, + {"sqrtf64", "__builtin_sqrt"}, {"unreachable", "__builtin_unreachable"}, {"abort", "__builtin_abort"}, + {"sinf64", "__builtin_sin"}, + {"cosf32", "__builtin_cosf"}, + {"cosf64", "__builtin_cos"}, + {"powf32", "__builtin_powf"}, + {"powf64", "__builtin_pow"}, + {"expf32", "__builtin_expf"}, + {"expf64", "__builtin_exp"}, + {"exp2f32", "__builtin_exp2f"}, + {"exp2f64", "__builtin_exp2"}, + {"logf32", "__builtin_logf"}, + {"logf64", "__builtin_log"}, + {"log10f32", "__builtin_log10f"}, + {"log10f64", "__builtin_log10"}, + {"log2f32", "__builtin_log2f"}, + {"log2f64", "__builtin_log2"}, + {"fmaf32", "__builtin_fmaf"}, + {"fmaf64", "__builtin_fma"}, + {"fabsf32", "__builtin_fabsf"}, + {"fabsf64", "__builtin_fabs"}, + {"minnumf32", "__builtin_fminf"}, + {"minnumf64", "__builtin_fmin"}, + {"maxnumf32", "__builtin_fmaxf"}, + {"maxnumf64", "__builtin_fmax"}, + {"copysignf32", "__builtin_copysignf"}, + {"copysignf64", "__builtin_copysign"}, + {"floorf32", "__builtin_floorf"}, + {"floorf64", "__builtin_floor"}, + {"ceilf32", "__builtin_ceilf"}, + {"ceilf64", "__builtin_ceil"}, + {"truncf32", "__builtin_truncf"}, + {"truncf64", "__builtin_trunc"}, + {"rintf32", "__builtin_rintf"}, + {"rintf64", "__builtin_rint"}, + {"nearbyintf32", "__builtin_nearbyintf"}, + {"nearbyintf64", "__builtin_nearbyint"}, + {"roundf32", "__builtin_roundf"}, + {"roundf64", "__builtin_round"}, }; } diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc index 1714d6bd2d2f..ce93a4d4ede6 100644 --- a/gcc/rust/backend/rust-compile-intrinsic.cc +++ b/gcc/rust/backend/rust-compile-intrinsic.cc @@ -245,6 +245,14 @@ static const std::mapget_backend ()->var_expression (args[0], Location ()); + auto addr = Backend::var_expression (args[0], UNDEF_LOCATION); // The core library technically allows you to pass any i32 value as a // locality, but LLVM will then complain if the value cannot be constant @@ -812,9 +820,9 @@ prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind) // site directly This has the bad side-effect of creating warnings about // `unused name - locality`, which we hack away here: // TODO: Take care of handling locality properly - ctx->get_backend ()->var_expression (args[1], Location ()); + Backend::var_expression (args[1], UNDEF_LOCATION); - auto rw_flag = make_unsigned_long_tree (ctx, kind == Prefetch::Write ? 1 : 0); + auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0); auto prefetch_raw = NULL_TREE; auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch", @@ -822,12 +830,11 @@ prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind) rust_assert (ok); auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw); - auto prefetch_call - = ctx->get_backend ()->call_expression (prefetch, - {addr, rw_flag, - // locality arg - make_unsigned_long_tree (ctx, 3)}, - nullptr, Location ()); + auto prefetch_call = Backend::call_expression (prefetch, + {addr, rw_flag, + // locality arg + make_unsigned_long_tree (3)}, + nullptr, UNDEF_LOCATION); TREE_READONLY (prefetch_call) = 0; TREE_SIDE_EFFECTS (prefetch_call) = 1; @@ -1087,7 +1094,8 @@ uninit_handler (Context *ctx, TyTy::FnType *fntype) // BUILTIN size_of FN BODY BEGIN tree memset_builtin = error_mark_node; - BuiltinsContext::get ().lookup_simple_builtin ("memset", &memset_builtin); + BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset", + &memset_builtin); rust_assert (memset_builtin != error_mark_node); // call memset with 0x01 and size of the thing see @@ -1150,7 +1158,8 @@ move_val_init_handler (Context *ctx, TyTy::FnType *fntype) tree size = TYPE_SIZE_UNIT (template_parameter_type); tree memcpy_builtin = error_mark_node; - BuiltinsContext::get ().lookup_simple_builtin ("memcpy", &memcpy_builtin); + BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy", + &memcpy_builtin); rust_assert (memcpy_builtin != error_mark_node); src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src); @@ -1184,7 +1193,8 @@ expect_handler_inner (Context *ctx, TyTy::FnType *fntype, bool likely) compile_fn_params (ctx, fntype, fndecl, ¶m_vars); tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION); tree expect_fn_raw = nullptr; - BuiltinsContext::get ().lookup_simple_builtin ("expect", &expect_fn_raw); + BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect", + &expect_fn_raw); rust_assert (expect_fn_raw); auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw); diff --git a/gcc/rust/rust-attribs.cc b/gcc/rust/rust-attribs.cc new file mode 100644 index 000000000000..173b6d98f316 --- /dev/null +++ b/gcc/rust/rust-attribs.cc @@ -0,0 +1,353 @@ +#include "config.h" +#include "system.h" +#include "coretypes.h" + +#include "tree.h" +#include "diagnostic.h" +#include "tm.h" +#include "cgraph.h" +#include "toplev.h" +#include "target.h" +#include "common/common-target.h" +#include "stringpool.h" +#include "attribs.h" +#include "varasm.h" +#include "fold-const.h" +#include "opts.h" + +/* Heavily based on the D frontend Only a subset of the attributes found in the + * D frontend have been pulled, the goal being to have the builtin function + * correctly setup. It's possible we may need to add extra attributes in the + * future. + */ + +extern const attribute_spec grs_langhook_common_attribute_table[]; + +/* Internal attribute handlers for built-in functions. */ +static tree +handle_noreturn_attribute (tree *, tree, tree, int, bool *); +static tree +handle_leaf_attribute (tree *, tree, tree, int, bool *); +static tree +handle_const_attribute (tree *, tree, tree, int, bool *); +static tree +handle_malloc_attribute (tree *, tree, tree, int, bool *); +static tree +handle_pure_attribute (tree *, tree, tree, int, bool *); +static tree +handle_novops_attribute (tree *, tree, tree, int, bool *); +static tree +handle_nonnull_attribute (tree *, tree, tree, int, bool *); +static tree +handle_nothrow_attribute (tree *, tree, tree, int, bool *); +static tree +handle_type_generic_attribute (tree *, tree, tree, int, bool *); +static tree +handle_transaction_pure_attribute (tree *, tree, tree, int, bool *); +static tree +handle_returns_twice_attribute (tree *, tree, tree, int, bool *); +static tree +handle_fnspec_attribute (tree *, tree, tree, int, bool *); +static tree +handle_omp_declare_simd_attribute (tree *, tree, tree, int, bool *); + +/* Helper to define attribute exclusions. */ +#define ATTR_EXCL(name, function, type, variable) \ + { \ + name, function, type, variable \ + } + +static const struct attribute_spec::exclusions attr_noreturn_exclusions[] = { + // ATTR_EXCL ("alloc_size", true, true, true), + ATTR_EXCL ("const", true, true, true), + // ATTR_EXCL ("malloc", true, true, true), + ATTR_EXCL ("pure", true, true, true), + ATTR_EXCL ("returns_twice", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +static const struct attribute_spec::exclusions attr_returns_twice_exclusions[] + = { + ATTR_EXCL ("noreturn", true, true, true), + ATTR_EXCL (NULL, false, false, false), +}; + +static const struct attribute_spec::exclusions attr_const_pure_exclusions[] = { + // ATTR_EXCL ("alloc_size", true, true, true), + ATTR_EXCL ("const", true, true, true), + ATTR_EXCL ("noreturn", true, true, true), + ATTR_EXCL ("pure", true, true, true), ATTR_EXCL (NULL, false, false, false)}; + +/* Helper to define an attribute. */ +#define ATTR_SPEC(name, min_len, max_len, decl_req, type_req, fn_type_req, \ + affects_type_identity, handler, exclude) \ + { \ + name, min_len, max_len, decl_req, type_req, fn_type_req, \ + affects_type_identity, handler, exclude \ + } + +/* Table of machine-independent attributes. + For internal use (marking of built-ins) only. */ +const attribute_spec grs_langhook_common_attribute_table[] = { + ATTR_SPEC ("noreturn", 0, 0, true, false, false, false, + handle_noreturn_attribute, attr_noreturn_exclusions), + ATTR_SPEC ("leaf", 0, 0, true, false, false, false, handle_leaf_attribute, + NULL), + ATTR_SPEC ("const", 0, 0, true, false, false, false, handle_const_attribute, + attr_const_pure_exclusions), + ATTR_SPEC ("malloc", 0, 0, true, false, false, false, handle_malloc_attribute, + NULL), + ATTR_SPEC ("returns_twice", 0, 0, true, false, false, false, + handle_returns_twice_attribute, attr_returns_twice_exclusions), + ATTR_SPEC ("pure", 0, 0, true, false, false, false, handle_pure_attribute, + attr_const_pure_exclusions), + ATTR_SPEC ("nonnull", 0, -1, false, true, true, false, + handle_nonnull_attribute, NULL), + ATTR_SPEC ("nothrow", 0, 0, true, false, false, false, + handle_nothrow_attribute, NULL), + ATTR_SPEC ("transaction_pure", 0, 0, false, true, true, false, + handle_transaction_pure_attribute, NULL), + ATTR_SPEC ("no vops", 0, 0, true, false, false, false, + handle_novops_attribute, NULL), + ATTR_SPEC ("type generic", 0, 0, false, true, true, false, + handle_type_generic_attribute, NULL), + ATTR_SPEC ("fn spec", 1, 1, false, true, true, false, handle_fnspec_attribute, + NULL), + ATTR_SPEC ("omp declare simd", 0, -1, true, false, false, false, + handle_omp_declare_simd_attribute, NULL), + ATTR_SPEC (NULL, 0, 0, false, false, false, false, NULL, NULL), +}; + +/* Built-in attribute handlers. + These functions take the arguments: + (tree *node, tree name, tree args, int flags, bool *no_add_attrs) */ + +/* Handle a "noreturn" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_noreturn_attribute (tree *node, tree, tree, int, bool *) +{ + tree type = TREE_TYPE (*node); + + if (TREE_CODE (*node) == FUNCTION_DECL) + TREE_THIS_VOLATILE (*node) = 1; + else if (TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) + TREE_TYPE (*node) = build_pointer_type ( + build_type_variant (TREE_TYPE (type), TYPE_READONLY (TREE_TYPE (type)), + 1)); + else + gcc_unreachable (); + + return NULL_TREE; +} + +/* Handle a "leaf" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_leaf_attribute (tree *node, tree name, tree, int, bool *no_add_attrs) +{ + if (TREE_CODE (*node) != FUNCTION_DECL) + { + warning (OPT_Wattributes, "%qE attribute ignored", name); + *no_add_attrs = true; + } + if (!TREE_PUBLIC (*node)) + { + warning (OPT_Wattributes, "%qE attribute has no effect", name); + *no_add_attrs = true; + } + + return NULL_TREE; +} + +/* Handle a "const" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_const_attribute (tree *node, tree, tree, int, bool *) +{ + tree type = TREE_TYPE (*node); + + if (TREE_CODE (*node) == FUNCTION_DECL) + TREE_READONLY (*node) = 1; + else if (TREE_CODE (type) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (type)) == FUNCTION_TYPE) + TREE_TYPE (*node) = build_pointer_type ( + build_type_variant (TREE_TYPE (type), 1, + TREE_THIS_VOLATILE (TREE_TYPE (type)))); + else + gcc_unreachable (); + + return NULL_TREE; +} + +/* Handle a "malloc" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_malloc_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL + && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (*node)))); + DECL_IS_MALLOC (*node) = 1; + return NULL_TREE; +} + +/* Handle a "pure" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_pure_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + DECL_PURE_P (*node) = 1; + return NULL_TREE; +} + +/* Handle a "no vops" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_novops_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + DECL_IS_NOVOPS (*node) = 1; + return NULL_TREE; +} + +/* Helper for nonnull attribute handling; fetch the operand number + from the attribute argument list. */ + +static bool +get_nonnull_operand (tree arg_num_expr, unsigned HOST_WIDE_INT *valp) +{ + /* Verify the arg number is a constant. */ + if (!tree_fits_uhwi_p (arg_num_expr)) + return false; + + *valp = TREE_INT_CST_LOW (arg_num_expr); + return true; +} + +/* Handle the "nonnull" attribute. */ + +static tree +handle_nonnull_attribute (tree *node, tree, tree args, int, bool *) +{ + tree type = *node; + + /* If no arguments are specified, all pointer arguments should be + non-null. Verify a full prototype is given so that the arguments + will have the correct types when we actually check them later. + Avoid diagnosing type-generic built-ins since those have no + prototype. */ + if (!args) + { + gcc_assert (prototype_p (type) || !TYPE_ATTRIBUTES (type) + || lookup_attribute ("type generic", TYPE_ATTRIBUTES (type))); + + return NULL_TREE; + } + + /* Argument list specified. Verify that each argument number references + a pointer argument. */ + for (; args; args = TREE_CHAIN (args)) + { + tree argument; + unsigned HOST_WIDE_INT arg_num = 0, ck_num; + + if (!get_nonnull_operand (TREE_VALUE (args), &arg_num)) + gcc_unreachable (); + + argument = TYPE_ARG_TYPES (type); + if (argument) + { + for (ck_num = 1;; ck_num++) + { + if (!argument || ck_num == arg_num) + break; + argument = TREE_CHAIN (argument); + } + + gcc_assert (argument + && TREE_CODE (TREE_VALUE (argument)) == POINTER_TYPE); + } + } + + return NULL_TREE; +} + +/* Handle a "nothrow" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_nothrow_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + TREE_NOTHROW (*node) = 1; + return NULL_TREE; +} + +/* Handle a "type generic" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_type_generic_attribute (tree *node, tree, tree, int, bool *) +{ + /* Ensure we have a function type. */ + gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); + + /* Ensure we have a variadic function. */ + gcc_assert (!prototype_p (*node) || stdarg_p (*node)); + + return NULL_TREE; +} + +/* Handle a "transaction_pure" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_transaction_pure_attribute (tree *node, tree, tree, int, bool *) +{ + /* Ensure we have a function type. */ + gcc_assert (TREE_CODE (*node) == FUNCTION_TYPE); + + return NULL_TREE; +} + +/* Handle a "returns_twice" attribute; arguments as in + struct attribute_spec.handler. */ + +static tree +handle_returns_twice_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + + DECL_IS_RETURNS_TWICE (*node) = 1; + + return NULL_TREE; +} + +/* Handle a "fn spec" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_fnspec_attribute (tree *, tree, tree args, int, bool *) +{ + gcc_assert (args && TREE_CODE (TREE_VALUE (args)) == STRING_CST + && !TREE_CHAIN (args)); + return NULL_TREE; +} + +/* Handle an "omp declare simd" attribute; arguments as in + struct attribute_spec.handler. */ + +tree +handle_omp_declare_simd_attribute (tree *node, tree, tree, int, bool *) +{ + gcc_assert (TREE_CODE (*node) == FUNCTION_DECL); + return NULL_TREE; +} diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc index d453eacfc9b8..839025633e42 100644 --- a/gcc/rust/rust-lang.cc +++ b/gcc/rust/rust-lang.cc @@ -383,6 +383,8 @@ rust_localize_identifier (const char *ident) return identifier_to_locale (ident); } +extern const attribute_spec grs_langhook_common_attribute_table[]; + /* The language hooks data structure. This is the main interface between the GCC * front-end and the GCC middle-end/back-end. A list of language hooks could be * found in /langhooks.h @@ -403,6 +405,8 @@ rust_localize_identifier (const char *ident) #undef LANG_HOOKS_GIMPLIFY_EXPR #undef LANG_HOOKS_EH_PERSONALITY +#undef LANG_HOOKS_COMMON_ATTRIBUTE_TABLE + #define LANG_HOOKS_NAME "GNU Rust" #define LANG_HOOKS_INIT grs_langhook_init #define LANG_HOOKS_OPTION_LANG_MASK grs_langhook_option_lang_mask @@ -423,6 +427,8 @@ rust_localize_identifier (const char *ident) #define LANG_HOOKS_GIMPLIFY_EXPR grs_langhook_gimplify_expr #define LANG_HOOKS_EH_PERSONALITY grs_langhook_eh_personality +#define LANG_HOOKS_COMMON_ATTRIBUTE_TABLE grs_langhook_common_attribute_table + #if CHECKING_P #undef LANG_HOOKS_RUN_LANG_SELFTESTS diff --git a/gcc/testsuite/rust/compile/torture/builtin_abort.rs b/gcc/testsuite/rust/compile/torture/builtin_abort.rs new file mode 100644 index 000000000000..a1f4a80fd31d --- /dev/null +++ b/gcc/testsuite/rust/compile/torture/builtin_abort.rs @@ -0,0 +1,14 @@ +// { dg-final { scan-assembler "\[^_\]abort" } } +#![feature(rustc_attrs)] +#![feature(intrinsics)] + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +pub fn main () -> i32 { + abort(); + 0 +} diff --git a/gcc/testsuite/rust/execute/torture/builtin_abort.rs b/gcc/testsuite/rust/execute/torture/builtin_abort.rs new file mode 100644 index 000000000000..9f2d8c2d9f38 --- /dev/null +++ b/gcc/testsuite/rust/execute/torture/builtin_abort.rs @@ -0,0 +1,14 @@ +// { dg-shouldfail "abort should stop the program" } +#![feature(rustc_attrs)] +#![feature(intrinsics)] + +mod intrinsics { + extern "rust-intrinsic" { + pub fn abort() -> !; + } +} + +pub fn main () -> i32 { + abort(); + 0 +}