// This file is part of GCC. // GCC is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free // Software Foundation; either version 3, or (at your option) any later // version. // GCC is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // You should have received a copy of the GNU General Public License // along with GCC; see the file COPYING3. If not see // . #include "rust-compile-intrinsic.h" #include "rust-compile-context.h" #include "rust-compile-type.h" #include "rust-compile-expr.h" #include "rust-compile-fnparam.h" #include "rust-builtins.h" #include "rust-diagnostics.h" #include "rust-location.h" #include "rust-constexpr.h" #include "rust-tree.h" #include "tree-core.h" #include "print-tree.h" #include "fold-const.h" #include "langhooks.h" #include "print-tree.h" namespace Rust { namespace Compile { static bool is_basic_integer_type (TyTy::BaseType *type) { switch (type->get_kind ()) { case TyTy::INT: case TyTy::UINT: case TyTy::USIZE: case TyTy::ISIZE: return true; default: return false; break; } } static bool check_for_basic_integer_type (const std::string &intrinsic_str, Location locus, TyTy::BaseType *type) { auto is_basic_integer = is_basic_integer_type (type); if (!is_basic_integer) { rust_error_at ( locus, "%s intrinsics can only be used with basic integer types (got %qs)", intrinsic_str.c_str (), type->get_name ().c_str ()); } return is_basic_integer; } static tree offset_handler (Context *ctx, TyTy::FnType *fntype); static tree sizeof_handler (Context *ctx, TyTy::FnType *fntype); static tree transmute_handler (Context *ctx, TyTy::FnType *fntype); static tree rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op); static tree wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op); static tree copy_nonoverlapping_handler (Context *ctx, TyTy::FnType *fntype); enum class Prefetch { Read, Write }; static tree prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind); static inline tree rotate_left_handler (Context *ctx, TyTy::FnType *fntype) { return rotate_handler (ctx, fntype, LROTATE_EXPR); } static inline tree rotate_right_handler (Context *ctx, TyTy::FnType *fntype) { return rotate_handler (ctx, fntype, RROTATE_EXPR); } const static std::function wrapping_op_handler (tree_code op) { return [op] (Context *ctx, TyTy::FnType *fntype) { return wrapping_op_handler_inner (ctx, fntype, op); }; } static inline tree prefetch_read_data (Context *ctx, TyTy::FnType *fntype) { return prefetch_data_handler (ctx, fntype, Prefetch::Read); } static inline tree prefetch_write_data (Context *ctx, TyTy::FnType *fntype) { return prefetch_data_handler (ctx, fntype, Prefetch::Write); } static tree atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering); static tree atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering); static inline std::function atomic_store_handler (int ordering) { return [ordering] (Context *ctx, TyTy::FnType *fntype) { return atomic_store_handler_inner (ctx, fntype, ordering); }; } static inline std::function atomic_load_handler (int ordering) { return [ordering] (Context *ctx, TyTy::FnType *fntype) { return atomic_load_handler_inner (ctx, fntype, ordering); }; } static inline tree unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op); const static std::function unchecked_op_handler (tree_code op) { return [op] (Context *ctx, TyTy::FnType *fntype) { return unchecked_op_inner (ctx, fntype, op); }; } inline tree sorry_handler (Context *ctx, TyTy::FnType *fntype) { rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented", fntype->get_identifier ().c_str ()); return error_mark_node; } static const std::map> generic_intrinsics = { {"offset", offset_handler}, {"size_of", sizeof_handler}, {"transmute", transmute_handler}, {"rotate_left", rotate_left_handler}, {"rotate_right", rotate_right_handler}, {"wrapping_add", wrapping_op_handler (PLUS_EXPR)}, {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)}, {"wrapping_mul", wrapping_op_handler (MULT_EXPR)}, {"copy_nonoverlapping", copy_nonoverlapping_handler}, {"prefetch_read_data", prefetch_read_data}, {"prefetch_write_data", prefetch_write_data}, {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)}, {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)}, {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)}, {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)}, {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)}, {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)}, {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)}, {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)}, {"unchecked_add", unchecked_op_handler (PLUS_EXPR)}, {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)}, {"unchecked_mul", unchecked_op_handler (MULT_EXPR)}, {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)}, {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)}, {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)}, {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)}, }; Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {} tree Intrinsics::compile (TyTy::FnType *fntype) { rust_assert (fntype->get_abi () == ABI::INTRINSIC); tree builtin = error_mark_node; BuiltinsContext &builtin_ctx = BuiltinsContext::get (); if (builtin_ctx.lookup_simple_builtin (fntype->get_identifier (), &builtin)) return builtin; // is it an generic builtin? auto it = generic_intrinsics.find (fntype->get_identifier ()); if (it != generic_intrinsics.end ()) return it->second (ctx, fntype); Location locus = ctx->get_mappings ()->lookup_location (fntype->get_ref ()); rust_error_at (locus, "unknown builtin intrinsic: %s", fntype->get_identifier ().c_str ()); return error_mark_node; } /** * Items can be forward compiled which means we may not need to invoke this * code. We might also have already compiled this generic function as well. */ static bool check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup) { if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup, fntype->get_id (), fntype)) { // Has this been added to the list? Then it must be finished if (ctx->function_completed (*lookup)) { tree dummy = NULL_TREE; if (!ctx->lookup_function_decl (fntype->get_ty_ref (), &dummy)) ctx->insert_function_decl (fntype, *lookup); return true; } } return false; } /** * Maybe override the Hir Lookups for the substituions in this context */ static void maybe_override_ctx (TyTy::FnType *fntype) { if (fntype->has_subsititions_defined ()) fntype->override_context (); } /** * Compile and setup a function's parameters */ static void compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl, std::vector *compiled_param_variables, std::vector *compiled_param_types = nullptr) { for (auto &parm : fntype->get_params ()) { auto &referenced_param = parm.first; auto ¶m_tyty = parm.second; auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty); Location param_locus = referenced_param->get_locus (); Bvariable *compiled_param_var = CompileFnParam::compile (ctx, fndecl, referenced_param, compiled_param_type, param_locus); compiled_param_variables->push_back (compiled_param_var); if (compiled_param_types) compiled_param_types->push_back (compiled_param_type); } } static tree compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype) { maybe_override_ctx (fntype); const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path; tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype); std::string ir_symbol_name = canonical_path.get () + fntype->subst_as_string (); std::string asm_name = ctx->mangle_item (fntype, canonical_path); unsigned int flags = 0; tree fndecl = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name, asm_name, flags, fntype->get_ident ().locus); TREE_PUBLIC (fndecl) = 0; TREE_READONLY (fndecl) = 1; DECL_ARTIFICIAL (fndecl) = 1; DECL_EXTERNAL (fndecl) = 0; DECL_DECLARED_INLINE_P (fndecl) = 1; return fndecl; } static void enter_intrinsic_block (Context *ctx, tree fndecl) { tree enclosing_scope = NULL_TREE; Location start_location = Location (); Location end_location = Location (); auto block = ctx->get_backend ()->block (fndecl, enclosing_scope, {}, start_location, end_location); ctx->push_block (block); } static void finalize_intrinsic_block (Context *ctx, tree fndecl) { tree bind_tree = ctx->pop_block (); gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); DECL_SAVED_TREE (fndecl) = bind_tree; ctx->push_function (fndecl); DECL_DECLARED_CONSTEXPR_P (fndecl) = 1; maybe_save_constexpr_fundef (fndecl); } static tree offset_handler (Context *ctx, TyTy::FnType *fntype) { // offset intrinsic has two params dst pointer and offset isize rust_assert (fntype->get_params ().size () == 2); auto fndecl = compile_intrinsic_function (ctx, fntype); std::vector param_vars; compile_fn_params (ctx, fntype, fndecl, ¶m_vars); auto &dst_param = param_vars.at (0); auto &size_param = param_vars.at (1); rust_assert (param_vars.size () == 2); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); // BUILTIN offset FN BODY BEGIN tree dst = ctx->get_backend ()->var_expression (dst_param, Location ()); tree size = ctx->get_backend ()->var_expression (size_param, Location ()); tree pointer_offset_expr = pointer_offset_expression (dst, size, BUILTINS_LOCATION); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {pointer_offset_expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN offset FN BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } static tree sizeof_handler (Context *ctx, TyTy::FnType *fntype) { // size_of has _zero_ parameters its parameter is the generic one rust_assert (fntype->get_params ().size () == 0); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // get the template parameter type tree fn size_of(); rust_assert (fntype->get_num_substitutions () == 1); auto ¶m_mapping = fntype->get_substs ().at (0); const TyTy::ParamType *param_tyty = param_mapping.get_param_ty (); TyTy::BaseType *resolved_tyty = param_tyty->resolve (); tree template_parameter_type = TyTyResolveCompile::compile (ctx, resolved_tyty); enter_intrinsic_block (ctx, fndecl); // BUILTIN size_of FN BODY BEGIN tree size_expr = TYPE_SIZE_UNIT (template_parameter_type); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {size_expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN size_of FN BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } static tree transmute_handler (Context *ctx, TyTy::FnType *fntype) { // transmute intrinsic has one parameter rust_assert (fntype->get_params ().size () == 1); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); std::vector param_vars; std::vector compiled_types; compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &compiled_types); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; // param to convert Bvariable *convert_me_param = param_vars.at (0); tree convert_me_expr = ctx->get_backend ()->var_expression (convert_me_param, Location ()); // check for transmute pre-conditions tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl)); tree source_type_expr = compiled_types.at (0); tree target_size_expr = TYPE_SIZE (target_type_expr); tree source_size_expr = TYPE_SIZE (source_type_expr); // for some reason, unit types and other zero-sized types return NULL for the // size expressions unsigned HOST_WIDE_INT target_size = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0; unsigned HOST_WIDE_INT source_size = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0; // size check for concrete types // TODO(liushuyu): check alignment for pointers; check for dependently-sized // types if (target_size != source_size) { rust_error_at (fntype->get_locus (), "cannot transmute between types of different sizes, or " "dependently-sized types"); rust_inform (fntype->get_ident ().locus, "source type: %qs (%lu bits)", fntype->get_params ().at (0).second->as_string ().c_str (), (unsigned long) source_size); rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)", fntype->get_return_type ()->as_string ().c_str (), (unsigned long) target_size); } enter_intrinsic_block (ctx, fndecl); // BUILTIN transmute FN BODY BEGIN // Return *((orig_type*)&decl) */ tree t = build_fold_addr_expr_loc (Location ().gcc_location (), convert_me_expr); t = fold_build1_loc (Location ().gcc_location (), NOP_EXPR, build_pointer_type (target_type_expr), t); tree result_expr = build_fold_indirect_ref_loc (Location ().gcc_location (), t); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {result_expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN transmute FN BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } static tree rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op) { // rotate intrinsic has two parameter rust_assert (fntype->get_params ().size () == 2); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // setup the params std::vector param_vars; compile_fn_params (ctx, fntype, fndecl, ¶m_vars); auto &x_param = param_vars.at (0); auto &y_param = param_vars.at (1); rust_assert (param_vars.size () == 2); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); // BUILTIN rotate FN BODY BEGIN tree x = ctx->get_backend ()->var_expression (x_param, Location ()); tree y = ctx->get_backend ()->var_expression (y_param, Location ()); tree rotate_expr = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {rotate_expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN rotate FN BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } /** * pub fn wrapping_{add, sub, mul}(lhs: T, rhs: T) -> T; */ static tree wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op) { // wrapping_ intrinsics have two parameter rust_assert (fntype->get_params ().size () == 2); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // setup the params std::vector param_vars; compile_fn_params (ctx, fntype, fndecl, ¶m_vars); auto &lhs_param = param_vars.at (0); auto &rhs_param = param_vars.at (1); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); // BUILTIN wrapping_ FN BODY BEGIN auto lhs = ctx->get_backend ()->var_expression (lhs_param, Location ()); auto rhs = ctx->get_backend ()->var_expression (rhs_param, Location ()); // Operations are always wrapping in Rust, as we have -fwrapv enabled by // default. The difference between a wrapping_{add, sub, mul} and a regular // arithmetic operation is that these intrinsics do not panic - they always // carry over. auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {wrap_expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN wrapping_ FN BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } /** * fn copy_nonoverlapping(src: *const T, dst: *mut T, count: usize); */ static tree copy_nonoverlapping_handler (Context *ctx, TyTy::FnType *fntype) { rust_assert (fntype->get_params ().size () == 3); rust_assert (fntype->get_num_substitutions () == 1); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // Most intrinsic functions are pure - not `copy_nonoverlapping` TREE_READONLY (fndecl) = 0; TREE_SIDE_EFFECTS (fndecl) = 1; // setup the params std::vector param_vars; compile_fn_params (ctx, fntype, fndecl, ¶m_vars); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); // BUILTIN copy_nonoverlapping BODY BEGIN auto src = ctx->get_backend ()->var_expression (param_vars[0], Location ()); auto dst = ctx->get_backend ()->var_expression (param_vars[1], Location ()); auto count = ctx->get_backend ()->var_expression (param_vars[2], Location ()); // We want to create the following statement // memcpy(dst, src, size_of::()); // so // memcpy(dst, src, size_expr); auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve (); auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty); tree size_expr = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count); tree memcpy_raw = nullptr; BuiltinsContext::get ().lookup_simple_builtin ("memcpy", &memcpy_raw); rust_assert (memcpy_raw); auto memcpy = build_fold_addr_expr_loc (Location ().gcc_location (), memcpy_raw); auto copy_call = ctx->get_backend ()->call_expression (memcpy, {dst, src, size_expr}, nullptr, Location ()); ctx->add_statement (copy_call); // BUILTIN copy_nonoverlapping BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } static tree make_unsigned_long_tree (Context *ctx, unsigned long value) { mpz_t mpz_value; mpz_init_set_ui (mpz_value, value); return ctx->get_backend ()->integer_constant_expression (integer_type_node, mpz_value); } static tree prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind) { rust_assert (fntype->get_params ().size () == 2); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // prefetching isn't pure and shouldn't be discarded after GIMPLE TREE_READONLY (fndecl) = 0; TREE_SIDE_EFFECTS (fndecl) = 1; std::vector args; compile_fn_params (ctx, fntype, fndecl, &args); if (!ctx->get_backend ()->function_set_parameters (fndecl, args)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); auto addr = ctx->get_backend ()->var_expression (args[0], Location ()); auto locality = ctx->get_backend ()->var_expression (args[1], Location ()); auto rw_flag = make_unsigned_long_tree (ctx, kind == Prefetch::Write ? 1 : 0); auto prefetch_raw = NULL_TREE; auto ok = BuiltinsContext::get ().lookup_simple_builtin ("prefetch", &prefetch_raw); rust_assert (ok); auto prefetch = build_fold_addr_expr_loc (Location ().gcc_location (), prefetch_raw); auto prefetch_call = ctx->get_backend ()->call_expression (prefetch, {addr, rw_flag, locality}, nullptr, Location ()); TREE_READONLY (prefetch_call) = 0; TREE_SIDE_EFFECTS (prefetch_call) = 1; ctx->add_statement (prefetch_call); finalize_intrinsic_block (ctx, fndecl); return fndecl; } static std::string build_atomic_builtin_name (const std::string &prefix, Location locus, TyTy::BaseType *operand_type) { static const std::map allowed_types = { {"i8", "1"}, {"i16", "2"}, {"i32", "4"}, {"i64", "8"}, {"i128", "16"}, {"isize", "8"}, {"u8", "1"}, {"u16", "2"}, {"u32", "4"}, {"u64", "8"}, {"u128", "16"}, {"usize", "8"}, }; // TODO: Can we maybe get the generic version (atomic_store_n) to work... This // would be so much better std::string result = prefix; auto type_name = operand_type->get_name (); if (type_name == "usize" || type_name == "isize") { rust_sorry_at ( locus, "atomics are not yet available for size types (usize, isize)"); return ""; } auto type_size_str = allowed_types.find (type_name); if (!check_for_basic_integer_type ("atomic", locus, operand_type)) return ""; result += type_size_str->second; return result; } static tree atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering) { rust_assert (fntype->get_params ().size () == 2); rust_assert (fntype->get_num_substitutions () == 1); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // Most intrinsic functions are pure but not the atomic ones TREE_READONLY (fndecl) = 0; TREE_SIDE_EFFECTS (fndecl) = 1; // setup the params std::vector param_vars; std::vector types; compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types); auto ok = ctx->get_backend ()->function_set_parameters (fndecl, param_vars); rust_assert (ok); enter_intrinsic_block (ctx, fndecl); auto dst = ctx->get_backend ()->var_expression (param_vars[0], Location ()); TREE_READONLY (dst) = 0; auto value = ctx->get_backend ()->var_expression (param_vars[1], Location ()); auto memorder = make_unsigned_long_tree (ctx, ordering); auto monomorphized_type = fntype->get_substs ()[0].get_param_ty ()->resolve (); auto builtin_name = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (), monomorphized_type); if (builtin_name.empty ()) return error_mark_node; tree atomic_store_raw = nullptr; BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &atomic_store_raw); rust_assert (atomic_store_raw); auto atomic_store = build_fold_addr_expr_loc (Location ().gcc_location (), atomic_store_raw); auto store_call = ctx->get_backend ()->call_expression (atomic_store, {dst, value, memorder}, nullptr, Location ()); TREE_READONLY (store_call) = 0; TREE_SIDE_EFFECTS (store_call) = 1; ctx->add_statement (store_call); finalize_intrinsic_block (ctx, fndecl); return fndecl; } static tree atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering) { rust_assert (fntype->get_params ().size () == 1); rust_assert (fntype->get_num_substitutions () == 1); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // Most intrinsic functions are pure but not the atomic ones // FIXME: Is atomic_load_* pure? Feels like it shouldn't so TREE_READONLY (fndecl) = 0; TREE_SIDE_EFFECTS (fndecl) = 1; // setup the params std::vector param_vars; std::vector types; compile_fn_params (ctx, fntype, fndecl, ¶m_vars, &types); auto ok = ctx->get_backend ()->function_set_parameters (fndecl, param_vars); rust_assert (ok); enter_intrinsic_block (ctx, fndecl); auto src = ctx->get_backend ()->var_expression (param_vars[0], Location ()); auto memorder = make_unsigned_long_tree (ctx, ordering); auto monomorphized_type = fntype->get_substs ()[0].get_param_ty ()->resolve (); auto builtin_name = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (), monomorphized_type); if (builtin_name.empty ()) return error_mark_node; tree atomic_load_raw = nullptr; BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &atomic_load_raw); rust_assert (atomic_load_raw); auto atomic_load = build_fold_addr_expr_loc (Location ().gcc_location (), atomic_load_raw); auto load_call = ctx->get_backend ()->call_expression (atomic_load, {src, memorder}, nullptr, Location ()); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {load_call}, Location ()); TREE_READONLY (load_call) = 0; TREE_SIDE_EFFECTS (load_call) = 1; ctx->add_statement (return_statement); finalize_intrinsic_block (ctx, fndecl); return fndecl; } static inline tree unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op) { rust_assert (fntype->get_params ().size () == 2); rust_assert (fntype->get_num_substitutions () == 1); tree lookup = NULL_TREE; if (check_for_cached_intrinsic (ctx, fntype, &lookup)) return lookup; auto fndecl = compile_intrinsic_function (ctx, fntype); // setup the params std::vector param_vars; compile_fn_params (ctx, fntype, fndecl, ¶m_vars); if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars)) return error_mark_node; enter_intrinsic_block (ctx, fndecl); // BUILTIN unchecked_ BODY BEGIN auto x = ctx->get_backend ()->var_expression (param_vars[0], Location ()); auto y = ctx->get_backend ()->var_expression (param_vars[1], Location ()); auto *monomorphized_type = fntype->get_substs ().at (0).get_param_ty ()->resolve (); check_for_basic_integer_type ("unchecked operation", fntype->get_locus (), monomorphized_type); auto expr = build2 (op, TREE_TYPE (x), x, y); auto return_statement = ctx->get_backend ()->return_statement (fndecl, {expr}, Location ()); ctx->add_statement (return_statement); // BUILTIN unchecked_ BODY END finalize_intrinsic_block (ctx, fndecl); return fndecl; } } // namespace Compile } // namespace Rust