LCOV - code coverage report
Current view: top level - gcc/rust/backend - rust-intrinsic-handlers.cc (source / functions) Coverage Total Hit
Test: gcc.info Lines: 88.5 % 1170 1035
Test Date: 2026-09-19 16:22:48 Functions: 96.4 % 55 53
Legend: Lines:     hit not hit

            Line data    Source code
       1              : // Copyright (C) 2026 Free Software Foundation, Inc.
       2              : //
       3              : // This file is part of GCC.
       4              : 
       5              : // GCC is free software; you can redistribute it and/or modify it under
       6              : // the terms of the GNU General Public License as published by the Free
       7              : // Software Foundation; either version 3, or (at your option) any later
       8              : // version.
       9              : 
      10              : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
      11              : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
      12              : // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      13              : // for more details.
      14              : 
      15              : // You should have received a copy of the GNU General Public License
      16              : // along with GCC; see the file COPYING3.  If not see
      17              : // <http://www.gnu.org/licenses/>.
      18              : 
      19              : #include "rust-intrinsic-handlers.h"
      20              : #include "rust-compile-context.h"
      21              : #include "rust-compile-type.h"
      22              : #include "rust-compile-fnparam.h"
      23              : #include "rust-builtins.h"
      24              : #include "rust-diagnostics.h"
      25              : #include "rust-location.h"
      26              : #include "rust-constexpr.h"
      27              : #include "rust-session-manager.h"
      28              : #include "rust-tree.h"
      29              : #include "tree-core.h"
      30              : #include "rust-gcc.h"
      31              : #include "fold-const.h"
      32              : #include "rust-constexpr.h"
      33              : 
      34              : // declaration taken from "stringpool.h"
      35              : // the get_identifier macro causes compilation issues
      36              : extern tree get_identifier (const char *);
      37              : 
      38              : namespace Rust {
      39              : namespace Compile {
      40              : namespace handlers {
      41              : 
      42              : static tree
      43          126 : make_unsigned_long_tree (unsigned long value)
      44              : {
      45           14 :   return build_int_cst (integer_type_node, value);
      46              : }
      47              : 
      48              : static bool
      49          869 : is_basic_integer_type (TyTy::BaseType *type)
      50              : {
      51          869 :   switch (type->get_kind ())
      52              :     {
      53              :     case TyTy::INT:
      54              :     case TyTy::UINT:
      55              :     case TyTy::USIZE:
      56              :     case TyTy::ISIZE:
      57              :       return true;
      58           26 :     default:
      59           26 :       return false;
      60              :       break;
      61              :     }
      62              : }
      63              : 
      64              : /**
      65              :  * Maybe override the Hir Lookups for the substituions in this context
      66              :  */
      67              : static void
      68         3334 : maybe_override_ctx (TyTy::FnType *fntype)
      69              : {
      70         3334 :   if (fntype->has_substitutions_defined ())
      71         3331 :     fntype->override_context ();
      72         3334 : }
      73              : 
      74              : static bool
      75          869 : check_for_basic_integer_type (const std::string &intrinsic_str,
      76              :                               location_t locus, TyTy::BaseType *type)
      77              : {
      78          869 :   auto is_basic_integer = is_basic_integer_type (type);
      79          869 :   if (!is_basic_integer)
      80              :     {
      81           26 :       rust_error_at (
      82              :         locus,
      83              :         "%s intrinsic can only be used with basic integer types (got %qs)",
      84           52 :         intrinsic_str.c_str (), type->get_name ().c_str ());
      85              :     }
      86              : 
      87          869 :   return is_basic_integer;
      88              : }
      89              : 
      90              : /**
      91              :  * Items can be forward compiled which means we may not need to invoke this
      92              :  * code. We might also have already compiled this generic function as well.
      93              :  */
      94              : static bool
      95         3245 : check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
      96              : {
      97         3245 :   const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
      98         3245 :   std::string asm_name = ctx->mangle_item (fntype, canonical_path);
      99         3245 :   if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
     100              :                                  fntype->get_id (), fntype, asm_name))
     101              :     {
     102              :       return true;
     103              :     }
     104              : 
     105              :   return false;
     106         3245 : }
     107              : 
     108              : static tree
     109         3334 : compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
     110              : {
     111         3334 :   maybe_override_ctx (fntype);
     112              : 
     113         3334 :   const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
     114              : 
     115         3334 :   tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
     116         3334 :   std::string ir_symbol_name
     117         3334 :     = canonical_path.get () + fntype->subst_as_string ();
     118         3334 :   std::string asm_name = ctx->mangle_item (fntype, canonical_path);
     119              : 
     120         3334 :   unsigned int flags = 0;
     121         3334 :   tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
     122         3334 :                                    flags, fntype->get_ident ().locus);
     123              : 
     124         3334 :   TREE_PUBLIC (fndecl) = 0;
     125         3334 :   TREE_READONLY (fndecl) = 1;
     126         3334 :   DECL_ARTIFICIAL (fndecl) = 1;
     127         3334 :   DECL_EXTERNAL (fndecl) = 0;
     128         3334 :   DECL_DECLARED_INLINE_P (fndecl) = 1;
     129              : 
     130         3334 :   return fndecl;
     131         3334 : }
     132              : 
     133              : /**
     134              :  * Compile and setup a function's parameters
     135              :  */
     136              : static void
     137         2642 : compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
     138              :                    std::vector<Bvariable *> *compiled_param_variables,
     139              :                    std::vector<tree_node *> *compiled_param_types = nullptr)
     140              : {
     141         7124 :   for (auto &parm : fntype->get_params ())
     142              :     {
     143         4482 :       auto &referenced_param = parm.get_pattern ();
     144         4482 :       auto param_tyty = parm.get_type ();
     145         4482 :       auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
     146              : 
     147         4482 :       location_t param_locus = referenced_param.get_locus ();
     148         4482 :       Bvariable *compiled_param_var
     149         4482 :         = CompileFnParam::compile (ctx, fndecl, referenced_param,
     150         4482 :                                    compiled_param_type, param_locus);
     151              : 
     152         4482 :       compiled_param_variables->push_back (compiled_param_var);
     153         4482 :       if (compiled_param_types)
     154          396 :         compiled_param_types->push_back (compiled_param_type);
     155              :     }
     156         2642 : }
     157              : 
     158              : static void
     159         3330 : enter_intrinsic_block (Context *ctx, tree fndecl,
     160              :                        const std::vector<Bvariable *> &vars = {})
     161              : {
     162         3330 :   tree enclosing_scope = NULL_TREE;
     163         3330 :   location_t start_location = UNDEF_LOCATION;
     164         3330 :   location_t end_location = UNDEF_LOCATION;
     165              : 
     166         3330 :   auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
     167              :                                end_location);
     168              : 
     169         3330 :   ctx->push_block (block);
     170         3330 : }
     171              : 
     172              : static void
     173         3316 : finalize_intrinsic_block (Context *ctx, tree fndecl)
     174              : {
     175         3316 :   tree bind_tree = ctx->pop_block ();
     176              : 
     177         3316 :   gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
     178              : 
     179         3316 :   DECL_SAVED_TREE (fndecl) = bind_tree;
     180              : 
     181         3316 :   ctx->push_function (fndecl);
     182              : 
     183         3316 :   DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
     184         3316 :   maybe_save_constexpr_fundef (fndecl);
     185         3316 : }
     186              : 
     187              : static TyTy::BaseType *
     188           12 : get_inner_dst (TyTy::BaseType *type)
     189              : {
     190           12 :   TyTy::BaseType *curr = type;
     191           16 :   while (curr->get_kind () == TyTy::TypeKind::ADT)
     192              :     {
     193            4 :       auto variant = curr->as<TyTy::ADTType> ()->get_variants ().front ();
     194            4 :       curr = variant->get_field_at_index (variant->num_fields () - 1)
     195            4 :                ->get_field_type ();
     196              :     }
     197           12 :   return curr;
     198              : }
     199              : 
     200              : namespace inner {
     201              : 
     202              : static std::string
     203           98 : build_atomic_builtin_name (const std::string &prefix, location_t locus,
     204              :                            TyTy::BaseType *operand_type)
     205              : {
     206           98 :   static const std::map<std::string, std::string> allowed_types = {
     207              :     {"i8", "1"},    {"i16", "2"},   {"i32", "4"},   {"i64", "8"},
     208              :     {"i128", "16"}, {"isize", "8"}, {"u8", "1"},    {"u16", "2"},
     209              :     {"u32", "4"},   {"u64", "8"},   {"u128", "16"}, {"usize", "8"},
     210          462 :   };
     211              : 
     212              :   // TODO: Can we maybe get the generic version (atomic_store_n) to work... This
     213              :   // would be so much better
     214              : 
     215           98 :   std::string result = "__" + prefix; //  + "n";
     216              : 
     217           98 :   auto type_name = operand_type->get_name ();
     218           98 :   if (type_name == "usize" || type_name == "isize")
     219              :     {
     220            0 :       rust_sorry_at (
     221              :         locus, "atomics are not yet available for size types (usize, isize)");
     222            0 :       return "";
     223              :     }
     224              : 
     225           98 :   if (type_name.at (0) == 'i')
     226              :     {
     227            0 :       rust_sorry_at (locus, "atomics are not yet supported for signed "
     228              :                             "integer types (i8, i16, i32, i64, i128)");
     229            0 :       return "";
     230              :     }
     231              : 
     232           98 :   auto type_size_str = allowed_types.find (type_name);
     233              : 
     234           98 :   if (!check_for_basic_integer_type ("atomic operation", locus, operand_type))
     235           14 :     return "";
     236              : 
     237           84 :   result += type_size_str->second;
     238              : 
     239           84 :   return result;
     240           98 : }
     241              : 
     242              : inline tree
     243           58 : unchecked_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
     244              : {
     245           58 :   rust_assert (fntype->get_params ().size () == 2);
     246           58 :   rust_assert (fntype->get_num_substitutions () == 1);
     247              : 
     248           58 :   tree lookup = NULL_TREE;
     249           58 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     250            0 :     return lookup;
     251              : 
     252           58 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     253              : 
     254              :   // setup the params
     255           58 :   std::vector<Bvariable *> param_vars;
     256           58 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     257              : 
     258           58 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     259            0 :     return error_mark_node;
     260              : 
     261           58 :   enter_intrinsic_block (ctx, fndecl);
     262              : 
     263              :   // BUILTIN unchecked_<op> BODY BEGIN
     264              : 
     265           58 :   auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     266           58 :   auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
     267              : 
     268           58 :   auto *monomorphized_type
     269           58 :     = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
     270              : 
     271           58 :   auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
     272           58 :   check_for_basic_integer_type ("unchecked operation", call_locus,
     273              :                                 monomorphized_type);
     274              : 
     275           58 :   auto expr = build2 (op, TREE_TYPE (x), x, y);
     276           58 :   auto return_statement
     277           58 :     = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
     278              : 
     279           58 :   ctx->add_statement (return_statement);
     280              : 
     281              :   // BUILTIN unchecked_<op> BODY END
     282              : 
     283           58 :   finalize_intrinsic_block (ctx, fndecl);
     284              : 
     285           58 :   return fndecl;
     286           58 : }
     287              : 
     288              : inline tree
     289            0 : expect (Context *ctx, TyTy::FnType *fntype, bool likely)
     290              : {
     291            0 :   rust_assert (fntype->get_params ().size () == 1);
     292              : 
     293            0 :   tree lookup = NULL_TREE;
     294            0 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     295            0 :     return lookup;
     296              : 
     297            0 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     298              : 
     299            0 :   enter_intrinsic_block (ctx, fndecl);
     300              : 
     301              :   // BUILTIN expect_handler_inner FN BODY BEGIN
     302              :   // setup the params
     303            0 :   std::vector<Bvariable *> param_vars;
     304            0 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     305            0 :   tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     306            0 :   tree expect_fn_raw = nullptr;
     307            0 :   BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
     308              :                                                  &expect_fn_raw);
     309            0 :   rust_assert (expect_fn_raw);
     310            0 :   auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
     311              : 
     312              :   // we need to convert the expression return type to long to match the expected
     313              :   // parameter type of __builtin_expect
     314            0 :   auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
     315            0 :   auto expect_value
     316            0 :     = make_unsigned_long_tree (static_cast<unsigned long> (likely));
     317              : 
     318            0 :   auto expect_call
     319            0 :     = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
     320              :                                 BUILTINS_LOCATION);
     321              :   // the return value also needs to be casted (to bool)
     322            0 :   auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, expect_call);
     323            0 :   auto return_statement
     324            0 :     = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
     325            0 :   ctx->add_statement (return_statement);
     326              :   // BUILTIN expect_handler_inner FN BODY END
     327              : 
     328            0 :   finalize_intrinsic_block (ctx, fndecl);
     329              : 
     330            0 :   return fndecl;
     331            0 : }
     332              : 
     333              : tree
     334            2 : try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
     335              : {
     336            2 :   rust_assert (fntype->get_params ().size () == 3);
     337              : 
     338            2 :   tree lookup = NULL_TREE;
     339            2 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     340            0 :     return lookup;
     341            2 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     342              : 
     343            2 :   enter_intrinsic_block (ctx, fndecl);
     344              : 
     345              :   // The following tricks are needed to make sure the try-catch blocks are not
     346              :   // optimized away
     347            2 :   TREE_READONLY (fndecl) = 0;
     348            2 :   DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
     349            2 :   DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
     350            2 :                                         NULL_TREE, DECL_ATTRIBUTES (fndecl));
     351              : 
     352              :   // BUILTIN try_handler FN BODY BEGIN
     353              :   // setup the params
     354            2 :   std::vector<Bvariable *> param_vars;
     355            2 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     356            2 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     357            0 :     return error_mark_node;
     358            2 :   tree enclosing_scope = NULL_TREE;
     359              : 
     360            2 :   bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
     361            2 :                         == CompileOptions::PanicStrategy::Abort;
     362            2 :   tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     363            2 :   tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
     364            2 :   tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
     365            2 :   tree normal_return_stmt = NULL_TREE;
     366            2 :   tree error_return_stmt = NULL_TREE;
     367            2 :   tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
     368              :                                             BUILTINS_LOCATION);
     369            2 :   tree catch_call = NULL_TREE;
     370            2 :   tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
     371              :                                    UNDEF_LOCATION);
     372              : 
     373            2 :   if (is_new_api)
     374              :     {
     375            1 :       auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
     376            1 :       auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
     377              :                                                        UNDEF_LOCATION);
     378            1 :       normal_return_stmt
     379            1 :         = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
     380            1 :       error_return_stmt
     381            1 :         = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
     382              :     }
     383              :   else
     384              :     {
     385            1 :       normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node,
     386              :                                                       BUILTINS_LOCATION);
     387            1 :       error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
     388              :                                                      BUILTINS_LOCATION);
     389              :     }
     390            2 :   Backend::block_add_statements (try_block,
     391            2 :                                  std::vector<tree>{try_call,
     392            2 :                                                    normal_return_stmt});
     393            2 :   if (panic_is_abort)
     394              :     {
     395              :       // skip building the try-catch construct
     396            0 :       ctx->add_statement (try_block);
     397            0 :       finalize_intrinsic_block (ctx, fndecl);
     398            0 :       return fndecl;
     399              :     }
     400              : 
     401            2 :   tree eh_pointer
     402            2 :     = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
     403              :                        integer_zero_node);
     404            2 :   catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
     405              :                                          NULL_TREE, BUILTINS_LOCATION);
     406              : 
     407            2 :   tree catch_block = Backend::block (fndecl, enclosing_scope, {},
     408              :                                      UNDEF_LOCATION, UNDEF_LOCATION);
     409            2 :   Backend::block_add_statements (catch_block,
     410            2 :                                  std::vector<tree>{catch_call,
     411            2 :                                                    error_return_stmt});
     412              :   // emulate what cc1plus is doing for C++ try-catch
     413            2 :   tree inner_eh_construct
     414            2 :     = Backend::exception_handler_statement (catch_call, NULL_TREE,
     415              :                                             error_return_stmt,
     416              :                                             BUILTINS_LOCATION);
     417              :   // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
     418            2 :   auto eh_construct
     419            2 :     = Backend::exception_handler_statement (try_block, inner_eh_construct,
     420              :                                             NULL_TREE, BUILTINS_LOCATION);
     421            2 :   ctx->add_statement (eh_construct);
     422              :   // BUILTIN try_handler FN BODY END
     423            2 :   finalize_intrinsic_block (ctx, fndecl);
     424              : 
     425            2 :   return fndecl;
     426            2 : }
     427              : 
     428              : /**
     429              :  * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
     430              :  */
     431              : tree
     432          380 : wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
     433              : {
     434              :   // wrapping_<op> intrinsics have two parameter
     435          380 :   rust_assert (fntype->get_params ().size () == 2);
     436              : 
     437          380 :   tree lookup = NULL_TREE;
     438          380 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     439            0 :     return lookup;
     440              : 
     441          380 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     442              : 
     443              :   // setup the params
     444          380 :   std::vector<Bvariable *> param_vars;
     445          380 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     446              : 
     447          380 :   auto &lhs_param = param_vars.at (0);
     448          380 :   auto &rhs_param = param_vars.at (1);
     449              : 
     450          380 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     451            0 :     return error_mark_node;
     452              : 
     453          380 :   enter_intrinsic_block (ctx, fndecl);
     454              : 
     455              :   // BUILTIN wrapping_<op> FN BODY BEGIN
     456          380 :   auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
     457          380 :   auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
     458              : 
     459              :   // Operations are always wrapping in Rust, as we have -fwrapv enabled by
     460              :   // default. The difference between a wrapping_{add, sub, mul} and a regular
     461              :   // arithmetic operation is that these intrinsics do not panic - they always
     462              :   // carry over.
     463          380 :   auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
     464              : 
     465          380 :   auto return_statement
     466          380 :     = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
     467          380 :   ctx->add_statement (return_statement);
     468              :   // BUILTIN wrapping_<op> FN BODY END
     469              : 
     470          380 :   finalize_intrinsic_block (ctx, fndecl);
     471              : 
     472          380 :   return fndecl;
     473          380 : }
     474              : 
     475              : /**
     476              :  * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
     477              :  */
     478              : tree
     479          176 : op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op)
     480              : {
     481              :   // wrapping_<op> intrinsics have two parameter
     482          176 :   rust_assert (fntype->get_params ().size () == 2);
     483              : 
     484          176 :   tree lookup = NULL_TREE;
     485          176 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     486            0 :     return lookup;
     487              : 
     488          176 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     489              : 
     490              :   // setup the params
     491          176 :   std::vector<Bvariable *> param_vars;
     492          176 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     493              : 
     494          176 :   auto &x_param = param_vars.at (0);
     495          176 :   auto &y_param = param_vars.at (1);
     496              : 
     497          176 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     498            0 :     return error_mark_node;
     499              : 
     500          176 :   rust_assert (fntype->get_num_substitutions () == 1);
     501          176 :   auto &param_mapping = fntype->get_substs ().at (0);
     502          176 :   const auto param_tyty = param_mapping.get_param_ty ();
     503          176 :   auto resolved_tyty = param_tyty->resolve ();
     504          176 :   tree template_parameter_type
     505          176 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
     506              : 
     507              :   // this should match y as well or we can take it from the TyTy structure
     508          176 :   tree tmp_stmt = error_mark_node;
     509          176 :   Bvariable *result_variable
     510          176 :     = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
     511              :                                    NULL_TREE, true /*address_is_taken*/,
     512              :                                    UNDEF_LOCATION, &tmp_stmt);
     513          176 :   Bvariable *bool_variable
     514          176 :     = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
     515              :                                    NULL_TREE, true /*address_is_taken*/,
     516              :                                    UNDEF_LOCATION, &tmp_stmt);
     517              : 
     518          176 :   enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
     519              : 
     520              :   // BUILTIN op_with_overflow FN BODY BEGIN
     521          176 :   auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
     522          176 :   auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
     523              : 
     524          176 :   tree overflow_builtin = error_mark_node;
     525          176 :   switch (op)
     526              :     {
     527          162 :     case PLUS_EXPR:
     528          162 :       BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
     529              :                                                      &overflow_builtin);
     530          162 :       break;
     531              : 
     532            7 :     case MINUS_EXPR:
     533            7 :       BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
     534              :                                                      &overflow_builtin);
     535            7 :       break;
     536              : 
     537            7 :     case MULT_EXPR:
     538            7 :       BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
     539              :                                                      &overflow_builtin);
     540            7 :       break;
     541              : 
     542            0 :     default:
     543            0 :       rust_unreachable ();
     544          176 :       break;
     545              :     }
     546          176 :   rust_assert (overflow_builtin != error_mark_node);
     547              : 
     548          176 :   tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
     549          176 :   tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
     550          176 :   tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
     551              : 
     552          176 :   tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
     553              :                                            3, x, y, result_ref);
     554              : 
     555          176 :   tree overflow_assignment
     556          176 :     = Backend::assignment_statement (bool_decl, builtin_call,
     557              :                                      BUILTINS_LOCATION);
     558              : 
     559          176 :   ctx->add_statement (overflow_assignment);
     560              : 
     561          352 :   std::vector<tree> vals = {result_decl, bool_decl};
     562          176 :   tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
     563          176 :   tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
     564              :                                                       -1, UNDEF_LOCATION);
     565              : 
     566          176 :   auto return_statement
     567          176 :     = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
     568          176 :   ctx->add_statement (return_statement);
     569              : 
     570              :   // BUILTIN wrapping_<op> FN BODY END
     571              : 
     572          176 :   finalize_intrinsic_block (ctx, fndecl);
     573              : 
     574          176 :   return fndecl;
     575          176 : }
     576              : 
     577              : /**
     578              :  * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
     579              :  * fn copy<T>(src: *const T, dst: *mut T, count: usize);
     580              :  */
     581              : tree
     582          299 : copy (Context *ctx, TyTy::FnType *fntype, bool overlaps)
     583              : {
     584          299 :   rust_assert (fntype->get_params ().size () == 3);
     585          299 :   rust_assert (fntype->get_num_substitutions () == 1);
     586              : 
     587          299 :   tree lookup = NULL_TREE;
     588          299 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     589            0 :     return lookup;
     590              : 
     591          299 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     592              : 
     593              :   // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
     594          299 :   TREE_READONLY (fndecl) = 0;
     595          299 :   TREE_SIDE_EFFECTS (fndecl) = 1;
     596              : 
     597              :   // setup the params
     598          299 :   std::vector<Bvariable *> param_vars;
     599          299 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     600              : 
     601          299 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     602            0 :     return error_mark_node;
     603              : 
     604          299 :   enter_intrinsic_block (ctx, fndecl);
     605              : 
     606              :   // BUILTIN copy_nonoverlapping BODY BEGIN
     607              : 
     608          299 :   auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     609          299 :   auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
     610          299 :   auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
     611              : 
     612              :   // We want to create the following statement
     613              :   // memcpy(dst, src, size_of::<T>());
     614              :   // so
     615              :   // memcpy(dst, src, size_expr);
     616              : 
     617          299 :   auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
     618          299 :   auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
     619              : 
     620          299 :   tree size_expr
     621          299 :     = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
     622              : 
     623          299 :   tree memcpy_raw = nullptr;
     624          598 :   BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
     625              :                                                           : "__builtin_memcpy",
     626              :                                                  &memcpy_raw);
     627          299 :   rust_assert (memcpy_raw);
     628          299 :   auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
     629              : 
     630          299 :   auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
     631              :                                              nullptr, UNDEF_LOCATION);
     632              : 
     633          299 :   ctx->add_statement (copy_call);
     634              : 
     635              :   // BUILTIN copy_nonoverlapping BODY END
     636              : 
     637          299 :   finalize_intrinsic_block (ctx, fndecl);
     638              : 
     639          299 :   return fndecl;
     640          299 : }
     641              : 
     642              : tree
     643           70 : atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering)
     644              : {
     645           70 :   rust_assert (fntype->get_params ().size () == 2);
     646           70 :   rust_assert (fntype->get_num_substitutions () == 1);
     647              : 
     648           70 :   tree lookup = NULL_TREE;
     649           70 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     650            0 :     return lookup;
     651              : 
     652           70 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     653              : 
     654              :   // Most intrinsic functions are pure but not the atomic ones
     655           70 :   TREE_READONLY (fndecl) = 0;
     656           70 :   TREE_SIDE_EFFECTS (fndecl) = 1;
     657              : 
     658              :   // setup the params
     659           70 :   std::vector<Bvariable *> param_vars;
     660           70 :   std::vector<tree> types;
     661           70 :   compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
     662              : 
     663           70 :   auto ok = Backend::function_set_parameters (fndecl, param_vars);
     664           70 :   rust_assert (ok);
     665              : 
     666           70 :   enter_intrinsic_block (ctx, fndecl);
     667              : 
     668           70 :   auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     669           70 :   TREE_READONLY (dst) = 0;
     670              : 
     671           70 :   auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
     672           70 :   auto memorder = make_unsigned_long_tree (ordering);
     673              : 
     674           70 :   auto monomorphized_type
     675           70 :     = fntype->get_substs ()[0].get_param_ty ()->resolve ();
     676              : 
     677           70 :   auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
     678           70 :   auto builtin_name = build_atomic_builtin_name ("atomic_store_", call_locus,
     679           70 :                                                  monomorphized_type);
     680           70 :   if (builtin_name.empty ())
     681           14 :     return error_mark_node;
     682              : 
     683           56 :   tree atomic_store_raw = nullptr;
     684           56 :   BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
     685              :                                                  &atomic_store_raw);
     686           56 :   rust_assert (atomic_store_raw);
     687              : 
     688           56 :   auto atomic_store
     689           56 :     = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
     690              : 
     691           56 :   auto store_call
     692           56 :     = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
     693              :                                 UNDEF_LOCATION);
     694           56 :   TREE_READONLY (store_call) = 0;
     695           56 :   TREE_SIDE_EFFECTS (store_call) = 1;
     696              : 
     697           56 :   ctx->add_statement (store_call);
     698           56 :   finalize_intrinsic_block (ctx, fndecl);
     699              : 
     700           56 :   return fndecl;
     701           70 : }
     702              : 
     703              : tree
     704           28 : atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering)
     705              : {
     706           28 :   rust_assert (fntype->get_params ().size () == 1);
     707           28 :   rust_assert (fntype->get_num_substitutions () == 1);
     708              : 
     709           28 :   tree lookup = NULL_TREE;
     710           28 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     711            0 :     return lookup;
     712              : 
     713           28 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     714              : 
     715              :   // Most intrinsic functions are pure but not the atomic ones
     716              :   // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
     717           28 :   TREE_READONLY (fndecl) = 0;
     718           28 :   TREE_SIDE_EFFECTS (fndecl) = 1;
     719              : 
     720              :   // setup the params
     721           28 :   std::vector<Bvariable *> param_vars;
     722           28 :   std::vector<tree> types;
     723           28 :   compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
     724              : 
     725           28 :   auto ok = Backend::function_set_parameters (fndecl, param_vars);
     726           28 :   rust_assert (ok);
     727              : 
     728           28 :   enter_intrinsic_block (ctx, fndecl);
     729              : 
     730           28 :   auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
     731           28 :   auto memorder = make_unsigned_long_tree (ordering);
     732              : 
     733           28 :   auto monomorphized_type
     734           28 :     = fntype->get_substs ()[0].get_param_ty ()->resolve ();
     735              : 
     736           28 :   auto builtin_name
     737           28 :     = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
     738           56 :                                  monomorphized_type);
     739           28 :   if (builtin_name.empty ())
     740            0 :     return error_mark_node;
     741              : 
     742           28 :   tree atomic_load_raw = nullptr;
     743           28 :   BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
     744              :                                                  &atomic_load_raw);
     745           28 :   rust_assert (atomic_load_raw);
     746              : 
     747           28 :   auto atomic_load
     748           28 :     = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
     749              : 
     750           28 :   auto load_call = Backend::call_expression (atomic_load, {src, memorder},
     751              :                                              nullptr, UNDEF_LOCATION);
     752           28 :   auto return_statement
     753           28 :     = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
     754              : 
     755           28 :   TREE_READONLY (load_call) = 0;
     756           28 :   TREE_SIDE_EFFECTS (load_call) = 1;
     757              : 
     758           28 :   ctx->add_statement (return_statement);
     759              : 
     760           28 :   finalize_intrinsic_block (ctx, fndecl);
     761              : 
     762           28 :   return fndecl;
     763           28 : }
     764              : 
     765              : // Shared inner implementation for ctlz and ctlz_nonzero.
     766              : //
     767              : // nonzero=false → ctlz: ctlz(0) is well-defined in Rust and must return
     768              : //   bit_size, but __builtin_clz*(0) is undefined behaviour in C, so an
     769              : //   explicit arg==0 guard is emitted.
     770              : //
     771              : // nonzero=true → ctlz_nonzero: the caller guarantees arg != 0 (passing 0
     772              : //   is immediate UB in Rust), so the zero guard is omitted entirely.
     773              : static tree
     774          422 : ctlz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
     775              : {
     776          422 :   rust_assert (fntype->get_params ().size () == 1);
     777              : 
     778          422 :   tree lookup = NULL_TREE;
     779          422 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     780            0 :     return lookup;
     781              : 
     782          422 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     783              : 
     784          422 :   std::vector<Bvariable *> param_vars;
     785          422 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     786              : 
     787          422 :   auto arg_param = param_vars.at (0);
     788          422 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     789            0 :     return error_mark_node;
     790              : 
     791          422 :   rust_assert (fntype->get_num_substitutions () == 1);
     792          422 :   auto *monomorphized_type
     793          422 :     = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
     794          422 :   auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
     795          422 :   if (!check_for_basic_integer_type ("ctlz", call_locus, monomorphized_type))
     796            2 :     return error_mark_node;
     797              : 
     798          420 :   enter_intrinsic_block (ctx, fndecl);
     799              : 
     800              :   // BUILTIN ctlz FN BODY BEGIN
     801          420 :   auto locus = fntype->get_locus ();
     802          420 :   auto arg_expr = Backend::var_expression (arg_param, locus);
     803          420 :   tree arg_type = TREE_TYPE (arg_expr);
     804          420 :   unsigned bit_size = TYPE_PRECISION (arg_type);
     805              : 
     806              :   // Convert signed types to their same-width unsigned equivalent before
     807              :   // widening.  Without this, widening a signed type sign-extends it.
     808              :   // For example, i8(-1) = 0xFF widened to u32 gives 0xFFFFFFFF, so
     809              :   // __builtin_clz(0xFFFFFFFF) = 0, then 0 - diff(24) = -24.
     810              :   // Converting to u8 first gives 0xFF → 0x000000FF (zero-extended), so
     811              :   // __builtin_clz(0x000000FF) = 24, then 24 - 24 = 0.
     812          420 :   tree unsigned_type
     813          420 :     = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
     814          420 :   tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
     815              : 
     816              :   // Pick the narrowest GCC clz builtin whose operand type is wide enough to
     817              :   // hold bit_size bits.  diff records how many extra leading zeros the builtin
     818              :   // will count due to the width difference and is subtracted from the result.
     819              :   //
     820              :   // Example: ctlz(1u8) bit_size=8, int_prec=32, diff=24.
     821              :   //   __builtin_clz(1u) returns 31 (counts from bit 31 down to bit 0).
     822              :   //   31 - 24 = 7, which is the correct answer for an 8-bit value.
     823              :   //
     824              :   // TODO: 128-bit integers are not yet handled.
     825          420 :   unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
     826          420 :   unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
     827          420 :   unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
     828              : 
     829          420 :   const char *builtin_name = nullptr;
     830          420 :   tree cast_type = NULL_TREE;
     831          420 :   int diff = 0;
     832              : 
     833          420 :   if (bit_size <= int_prec)
     834              :     {
     835              :       // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
     836          315 :       builtin_name = "__builtin_clz";
     837          315 :       cast_type = unsigned_type_node;
     838          315 :       diff = static_cast<int> (int_prec - bit_size);
     839              :     }
     840          105 :   else if (bit_size <= long_prec)
     841              :     {
     842              :       // Fits in unsigned long but not unsigned int.
     843          105 :       builtin_name = "__builtin_clzl";
     844          105 :       cast_type = long_unsigned_type_node;
     845          105 :       diff = static_cast<int> (long_prec - bit_size);
     846              :     }
     847            0 :   else if (bit_size <= longlong_prec)
     848              :     {
     849              :       // Fits in unsigned long long but not unsigned long.
     850            0 :       builtin_name = "__builtin_clzll";
     851            0 :       cast_type = long_long_unsigned_type_node;
     852            0 :       diff = static_cast<int> (longlong_prec - bit_size);
     853              :     }
     854              :   else
     855              :     {
     856            0 :       rust_sorry_at (locus, "ctlz for %u-bit integers is not yet implemented",
     857              :                      bit_size);
     858            0 :       return error_mark_node;
     859              :     }
     860              : 
     861              :   // Widen the unsigned arg to the chosen builtin's operand type, call it,
     862              :   // then subtract the padding bits.  diff == 0 means the Rust type exactly
     863              :   // matches the builtin's operand width, so the subtraction is skipped.
     864          420 :   tree call_arg = fold_convert (cast_type, unsigned_arg);
     865              : 
     866          420 :   tree builtin_decl = error_mark_node;
     867          420 :   BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
     868          420 :   rust_assert (builtin_decl != error_mark_node);
     869              : 
     870          420 :   tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
     871          420 :   tree clz_expr
     872          420 :     = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
     873              : 
     874          420 :   if (diff > 0)
     875              :     {
     876          210 :       tree diff_cst = build_int_cst (integer_type_node, diff);
     877          210 :       clz_expr
     878          210 :         = fold_build2 (MINUS_EXPR, integer_type_node, clz_expr, diff_cst);
     879              :     }
     880              : 
     881          420 :   clz_expr = fold_convert (uint32_type_node, clz_expr);
     882              : 
     883          420 :   tree final_expr;
     884          420 :   if (!nonzero)
     885              :     {
     886              :       // ctlz(0) must return bit_size per the Rust reference.
     887              :       // We cannot pass 0 to __builtin_clz* (UB), so emit:
     888              :       //   arg == 0 ? bit_size : clz_expr
     889          252 :       tree zero = build_int_cst (arg_type, 0);
     890          252 :       tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
     891          252 :       tree width_cst = build_int_cst (uint32_type_node, bit_size);
     892          252 :       final_expr
     893          252 :         = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, clz_expr);
     894              :     }
     895              :   else
     896              :     {
     897              :       // ctlz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
     898              :       final_expr = clz_expr;
     899              :     }
     900              : 
     901          420 :   tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
     902          420 :   auto return_stmt = Backend::return_statement (fndecl, result, locus);
     903          420 :   ctx->add_statement (return_stmt);
     904              :   // BUILTIN ctlz FN BODY END
     905              : 
     906          420 :   finalize_intrinsic_block (ctx, fndecl);
     907          420 :   return fndecl;
     908          422 : }
     909              : 
     910              : // Shared inner implementation for cttz and cttz_nonzero.
     911              : //
     912              : // nonzero=false → cttz: cttz(0) is well-defined in Rust and must return
     913              : //   bit_size, but __builtin_ctz*(0) is undefined behaviour in C, so an
     914              : //   explicit arg==0 guard is emitted.
     915              : //
     916              : // nonzero=true → cttz_nonzero: the caller guarantees arg != 0 (passing 0
     917              : //   is immediate UB in Rust), so the zero guard is omitted entirely.
     918              : static tree
     919          282 : cttz_handler (Context *ctx, TyTy::FnType *fntype, bool nonzero)
     920              : {
     921          282 :   rust_assert (fntype->get_params ().size () == 1);
     922              : 
     923          282 :   tree lookup = NULL_TREE;
     924          282 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
     925            0 :     return lookup;
     926              : 
     927          282 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
     928              : 
     929          282 :   std::vector<Bvariable *> param_vars;
     930          282 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
     931              : 
     932          282 :   auto arg_param = param_vars.at (0);
     933          282 :   if (!Backend::function_set_parameters (fndecl, param_vars))
     934            0 :     return error_mark_node;
     935              : 
     936          282 :   rust_assert (fntype->get_num_substitutions () == 1);
     937          282 :   auto *monomorphized_type
     938          282 :     = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
     939          282 :   auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
     940          282 :   if (!check_for_basic_integer_type ("cttz", call_locus, monomorphized_type))
     941            2 :     return error_mark_node;
     942              : 
     943          280 :   enter_intrinsic_block (ctx, fndecl);
     944              : 
     945              :   // BUILTIN cttz FN BODY BEGIN
     946          280 :   auto locus = fntype->get_locus ();
     947          280 :   auto arg_expr = Backend::var_expression (arg_param, locus);
     948          280 :   tree arg_type = TREE_TYPE (arg_expr);
     949          280 :   unsigned bit_size = TYPE_PRECISION (arg_type);
     950              : 
     951              :   // Convert signed types to their same-width unsigned equivalent before
     952              :   // widening.  For cttz this is not strictly required for correctness (sign
     953              :   // extension fills high bits with 1s, which does not alter the trailing-zero
     954              :   // count at the low end), but it avoids relying on signed-integer
     955              :   // representations and keeps the approach consistent with ctlz.
     956          280 :   tree unsigned_type
     957          280 :     = !TYPE_UNSIGNED (arg_type) ? unsigned_type_for (arg_type) : arg_type;
     958          280 :   tree unsigned_arg = fold_convert (unsigned_type, arg_expr);
     959              : 
     960              :   // Pick the narrowest GCC ctz builtin whose operand type is wide enough to
     961              :   // hold bit_size bits.  Unlike ctlz, no diff adjustment is needed: widening
     962              :   // a value zero-extends it (fills the added high bits with 0s), which does
     963              :   // not introduce new trailing zeros at the low end.
     964              :   //
     965              :   // Example: cttz(0b00001000_u8) = 3
     966              :   //   Widened to u32: 0x00000008.  __builtin_ctz(0x00000008) = 3.
     967              :   //
     968              :   // TODO: 128-bit integers are not yet handled.
     969          280 :   unsigned int_prec = TYPE_PRECISION (unsigned_type_node);
     970          280 :   unsigned long_prec = TYPE_PRECISION (long_unsigned_type_node);
     971          280 :   unsigned longlong_prec = TYPE_PRECISION (long_long_unsigned_type_node);
     972              : 
     973          280 :   const char *builtin_name = nullptr;
     974          280 :   tree cast_type = NULL_TREE;
     975              : 
     976          280 :   if (bit_size <= int_prec)
     977              :     {
     978              :       // Fits in unsigned int: covers 8/16/32-bit integers on most targets.
     979              :       builtin_name = "__builtin_ctz";
     980              :       cast_type = unsigned_type_node;
     981              :     }
     982           70 :   else if (bit_size <= long_prec)
     983              :     {
     984              :       // Fits in unsigned long but not unsigned int.
     985              :       builtin_name = "__builtin_ctzl";
     986              :       cast_type = long_unsigned_type_node;
     987              :     }
     988            0 :   else if (bit_size <= longlong_prec)
     989              :     {
     990              :       // Fits in unsigned long long but not unsigned long.
     991              :       builtin_name = "__builtin_ctzll";
     992              :       cast_type = long_long_unsigned_type_node;
     993              :     }
     994              :   else
     995              :     {
     996            0 :       rust_sorry_at (locus, "cttz for %u-bit integers is not yet implemented",
     997              :                      bit_size);
     998            0 :       return error_mark_node;
     999              :     }
    1000              : 
    1001          280 :   tree call_arg = fold_convert (cast_type, unsigned_arg);
    1002              : 
    1003          280 :   tree builtin_decl = error_mark_node;
    1004          280 :   BuiltinsContext::get ().lookup_simple_builtin (builtin_name, &builtin_decl);
    1005          280 :   rust_assert (builtin_decl != error_mark_node);
    1006              : 
    1007          280 :   tree builtin_fn = build_fold_addr_expr_loc (locus, builtin_decl);
    1008          280 :   tree ctz_expr
    1009          280 :     = Backend::call_expression (builtin_fn, {call_arg}, nullptr, locus);
    1010              : 
    1011          280 :   ctz_expr = fold_convert (uint32_type_node, ctz_expr);
    1012              : 
    1013          280 :   tree final_expr;
    1014          280 :   if (!nonzero)
    1015              :     {
    1016              :       // cttz(0) must return bit_size per the Rust reference.
    1017              :       // We cannot pass 0 to __builtin_ctz* (UB), so emit:
    1018              :       //   arg == 0 ? bit_size : ctz_expr
    1019          168 :       tree zero = build_int_cst (arg_type, 0);
    1020          168 :       tree cmp = fold_build2 (EQ_EXPR, boolean_type_node, arg_expr, zero);
    1021          168 :       tree width_cst = build_int_cst (uint32_type_node, bit_size);
    1022          168 :       final_expr
    1023          168 :         = fold_build3 (COND_EXPR, uint32_type_node, cmp, width_cst, ctz_expr);
    1024              :     }
    1025              :   else
    1026              :     {
    1027              :       // cttz_nonzero: arg != 0 is guaranteed by the caller, no guard needed.
    1028              :       final_expr = ctz_expr;
    1029              :     }
    1030              : 
    1031          280 :   tree result = fold_convert (TREE_TYPE (DECL_RESULT (fndecl)), final_expr);
    1032          280 :   auto return_stmt = Backend::return_statement (fndecl, result, locus);
    1033          280 :   ctx->add_statement (return_stmt);
    1034              :   // BUILTIN cttz FN BODY END
    1035              : 
    1036          280 :   finalize_intrinsic_block (ctx, fndecl);
    1037          280 :   return fndecl;
    1038          282 : }
    1039              : 
    1040              : } // namespace inner
    1041              : 
    1042              : const HandlerBuilder
    1043        15354 : op_with_overflow (tree_code op)
    1044              : {
    1045        15530 :   return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1046          176 :     return inner::op_with_overflow (ctx, fntype, op);
    1047        15354 :   };
    1048              : }
    1049              : 
    1050              : tree
    1051          233 : rotate_left (Context *ctx, TyTy::FnType *fntype, location_t)
    1052              : {
    1053          233 :   return handlers::rotate (ctx, fntype, LROTATE_EXPR);
    1054              : }
    1055              : 
    1056              : tree
    1057          190 : rotate_right (Context *ctx, TyTy::FnType *fntype, location_t)
    1058              : {
    1059          190 :   return handlers::rotate (ctx, fntype, RROTATE_EXPR);
    1060              : }
    1061              : 
    1062              : const HandlerBuilder
    1063        15354 : wrapping_op (tree_code op)
    1064              : {
    1065        15734 :   return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1066          380 :     return inner::wrapping_op (ctx, fntype, op);
    1067        15354 :   };
    1068              : }
    1069              : 
    1070              : HandlerBuilder
    1071        20472 : atomic_store (int ordering)
    1072              : {
    1073        20542 :   return [ordering] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1074           70 :     return inner::atomic_store (ctx, fntype, ordering);
    1075        20472 :   };
    1076              : }
    1077              : 
    1078              : HandlerBuilder
    1079        20472 : atomic_load (int ordering)
    1080              : {
    1081        20500 :   return [ordering] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1082           28 :     return inner::atomic_load (ctx, fntype, ordering);
    1083        20472 :   };
    1084              : }
    1085              : 
    1086              : const HandlerBuilder
    1087        35826 : unchecked_op (tree_code op)
    1088              : {
    1089        35884 :   return [op] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1090           58 :     return inner::unchecked_op (ctx, fntype, op);
    1091        35826 :   };
    1092              : }
    1093              : 
    1094              : const HandlerBuilder
    1095        10236 : copy (bool overlaps)
    1096              : {
    1097        10535 :   return [overlaps] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1098          299 :     return inner::copy (ctx, fntype, overlaps);
    1099        10236 :   };
    1100              : }
    1101              : 
    1102              : const HandlerBuilder
    1103        10236 : expect (bool likely)
    1104              : {
    1105        10236 :   return [likely] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1106            0 :     return inner::expect (ctx, fntype, likely);
    1107        10236 :   };
    1108              : }
    1109              : 
    1110              : const HandlerBuilder
    1111        10236 : try_handler (bool is_new_api)
    1112              : {
    1113        10238 :   return [is_new_api] (Context *ctx, TyTy::FnType *fntype, location_t) {
    1114            2 :     return inner::try_handler (ctx, fntype, is_new_api);
    1115        10236 :   };
    1116              : }
    1117              : 
    1118              : tree
    1119            0 : sorry (Context *ctx, TyTy::FnType *fntype, location_t)
    1120              : {
    1121            0 :   rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
    1122              :                  fntype->get_identifier ().c_str ());
    1123              : 
    1124            0 :   return error_mark_node;
    1125              : }
    1126              : 
    1127              : tree
    1128            1 : assume (Context *ctx, TyTy::FnType *fntype, location_t)
    1129              : {
    1130              :   // TODO: make sure this is actually helping the compiler optimize
    1131              : 
    1132            1 :   rust_assert (fntype->get_params ().size () == 1);
    1133            1 :   rust_assert (fntype->param_at (0).get_type ()->get_kind ()
    1134              :                == TyTy::TypeKind::BOOL);
    1135              : 
    1136            1 :   tree lookup = NULL_TREE;
    1137            1 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1138            0 :     return lookup;
    1139              : 
    1140            1 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1141              : 
    1142              :   // TODO: make sure these are necessary
    1143            1 :   TREE_READONLY (fndecl) = 0;
    1144            1 :   DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
    1145            1 :   DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
    1146            1 :                                         NULL_TREE, DECL_ATTRIBUTES (fndecl));
    1147              : 
    1148            1 :   std::vector<Bvariable *> param_vars;
    1149            1 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1150              : 
    1151            1 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1152            0 :     return error_mark_node;
    1153              : 
    1154            1 :   enter_intrinsic_block (ctx, fndecl);
    1155              : 
    1156              :   // BUILTIN assume FN BODY BEGIN
    1157              : 
    1158            1 :   tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
    1159              : 
    1160            1 :   tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
    1161              :                                                    void_type_node, 1, val);
    1162            1 :   TREE_SIDE_EFFECTS (assume_expr) = 1;
    1163              : 
    1164            1 :   ctx->add_statement (assume_expr);
    1165              :   // BUILTIN assume FN BODY END
    1166              : 
    1167            1 :   finalize_intrinsic_block (ctx, fndecl);
    1168              : 
    1169            1 :   return fndecl;
    1170            1 : }
    1171              : 
    1172              : tree
    1173          102 : discriminant_value (Context *ctx, TyTy::FnType *fntype, location_t)
    1174              : {
    1175          102 :   rust_assert (fntype->get_params ().size () == 1);
    1176          102 :   rust_assert (fntype->has_substitutions ());
    1177          102 :   rust_assert (fntype->get_num_type_params () == 1);
    1178          102 :   auto &mapping = fntype->get_substs ().at (0);
    1179          102 :   auto param_ty = mapping.get_param_ty ();
    1180          102 :   rust_assert (param_ty->can_resolve ());
    1181          102 :   auto resolved = param_ty->resolve ();
    1182              : 
    1183          102 :   TyTy::BaseType *return_type = nullptr;
    1184          102 :   bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
    1185          102 :   rust_assert (ok);
    1186              : 
    1187          102 :   bool is_adt = resolved->is<TyTy::ADTType> ();
    1188          102 :   bool is_enum = false;
    1189          102 :   if (is_adt)
    1190              :     {
    1191          102 :       const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
    1192          102 :       auto *repr = adt.get_repr_options ().repr;
    1193          102 :       if (repr != nullptr)
    1194          102 :         return_type = repr;
    1195          102 :       is_enum = adt.is_enum ();
    1196              :     }
    1197              : 
    1198          102 :   tree lookup = NULL_TREE;
    1199          102 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1200            0 :     return lookup;
    1201              : 
    1202          102 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1203              : 
    1204          102 :   std::vector<Bvariable *> param_vars;
    1205          102 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1206              : 
    1207          102 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1208            0 :     return error_mark_node;
    1209              : 
    1210          102 :   enter_intrinsic_block (ctx, fndecl);
    1211              : 
    1212              :   // BUILTIN disriminant_value FN BODY BEGIN
    1213              : 
    1214          102 :   tree result = integer_zero_node;
    1215          102 :   if (is_enum)
    1216              :     {
    1217          102 :       tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
    1218          102 :       tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
    1219          102 :       result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
    1220              :     }
    1221              : 
    1222          102 :   auto return_statement
    1223          102 :     = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
    1224          102 :   ctx->add_statement (return_statement);
    1225              : 
    1226              :   // BUILTIN disriminant_value FN BODY END
    1227              : 
    1228          102 :   finalize_intrinsic_block (ctx, fndecl);
    1229              : 
    1230          102 :   return fndecl;
    1231          102 : }
    1232              : 
    1233              : tree
    1234            7 : variant_count (Context *ctx, TyTy::FnType *fntype, location_t)
    1235              : {
    1236            7 :   rust_assert (fntype->get_num_type_params () == 1);
    1237            7 :   auto &mapping = fntype->get_substs ().at (0);
    1238            7 :   auto param_ty = mapping.get_param_ty ();
    1239            7 :   rust_assert (param_ty->can_resolve ());
    1240            7 :   auto resolved = param_ty->resolve ();
    1241              : 
    1242            7 :   size_t variant_count = 0;
    1243            7 :   bool is_adt = resolved->is<TyTy::ADTType> ();
    1244            7 :   if (is_adt)
    1245              :     {
    1246            7 :       const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
    1247            7 :       variant_count = adt.number_of_variants ();
    1248              :     }
    1249              : 
    1250            7 :   tree lookup = NULL_TREE;
    1251            7 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1252            0 :     return lookup;
    1253              : 
    1254            7 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1255              : 
    1256            7 :   std::vector<Bvariable *> param_vars;
    1257            7 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1258              : 
    1259            7 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1260            0 :     return error_mark_node;
    1261              : 
    1262            7 :   enter_intrinsic_block (ctx, fndecl);
    1263              : 
    1264              :   // BUILTIN disriminant_value FN BODY BEGIN
    1265            7 :   tree result_decl = DECL_RESULT (fndecl);
    1266            7 :   tree type = TREE_TYPE (result_decl);
    1267              : 
    1268            7 :   mpz_t ival;
    1269            7 :   mpz_init_set_ui (ival, variant_count);
    1270            7 :   tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
    1271            7 :   mpz_clear (ival);
    1272              : 
    1273            7 :   auto return_statement
    1274            7 :     = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
    1275            7 :   ctx->add_statement (return_statement);
    1276              : 
    1277              :   // BUILTIN disriminant_value FN BODY END
    1278              : 
    1279            7 :   finalize_intrinsic_block (ctx, fndecl);
    1280              : 
    1281            7 :   return fndecl;
    1282            7 : }
    1283              : 
    1284              : tree
    1285           33 : move_val_init (Context *ctx, TyTy::FnType *fntype, location_t)
    1286              : {
    1287           33 :   rust_assert (fntype->get_params ().size () == 2);
    1288              : 
    1289           33 :   tree lookup = NULL_TREE;
    1290           33 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1291            0 :     return lookup;
    1292              : 
    1293           33 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1294              : 
    1295              :   // Most intrinsic functions are pure - not `move_val_init`
    1296           33 :   TREE_READONLY (fndecl) = 0;
    1297           33 :   TREE_SIDE_EFFECTS (fndecl) = 1;
    1298              : 
    1299              :   // get the template parameter type tree fn size_of<T>();
    1300           33 :   rust_assert (fntype->get_num_substitutions () == 1);
    1301           33 :   auto &param_mapping = fntype->get_substs ().at (0);
    1302           33 :   auto param_tyty = param_mapping.get_param_ty ();
    1303           33 :   auto resolved_tyty = param_tyty->resolve ();
    1304           33 :   tree template_parameter_type
    1305           33 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1306              : 
    1307           33 :   std::vector<Bvariable *> param_vars;
    1308           33 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1309              : 
    1310           33 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1311            0 :     return error_mark_node;
    1312              : 
    1313           33 :   enter_intrinsic_block (ctx, fndecl);
    1314              : 
    1315              :   // BUILTIN size_of FN BODY BEGIN
    1316              : 
    1317           33 :   tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
    1318           33 :   tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
    1319           33 :   tree size = TYPE_SIZE_UNIT (template_parameter_type);
    1320              : 
    1321           33 :   tree memcpy_builtin = error_mark_node;
    1322           33 :   BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
    1323              :                                                  &memcpy_builtin);
    1324           33 :   rust_assert (memcpy_builtin != error_mark_node);
    1325              : 
    1326           33 :   src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
    1327           33 :   tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
    1328              :                                           dst, src, size);
    1329              : 
    1330           33 :   ctx->add_statement (memset_call);
    1331              :   // BUILTIN size_of FN BODY END
    1332              : 
    1333           33 :   finalize_intrinsic_block (ctx, fndecl);
    1334              : 
    1335           33 :   return fndecl;
    1336           33 : }
    1337              : 
    1338              : tree
    1339          113 : uninit (Context *ctx, TyTy::FnType *fntype, location_t)
    1340              : {
    1341              :   // uninit has _zero_ parameters its parameter is the generic one
    1342          113 :   rust_assert (fntype->get_params ().size () == 0);
    1343              : 
    1344          113 :   tree lookup = NULL_TREE;
    1345          113 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1346            0 :     return lookup;
    1347              : 
    1348          113 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1349              : 
    1350              :   // Most intrinsic functions are pure - not `uninit_handler`
    1351          113 :   TREE_READONLY (fndecl) = 0;
    1352          113 :   TREE_SIDE_EFFECTS (fndecl) = 1;
    1353              : 
    1354              :   // get the template parameter type tree fn uninit<T>();
    1355          113 :   rust_assert (fntype->get_num_substitutions () == 1);
    1356          113 :   auto &param_mapping = fntype->get_substs ().at (0);
    1357          113 :   const auto param_tyty = param_mapping.get_param_ty ();
    1358          113 :   auto resolved_tyty = param_tyty->resolve ();
    1359          113 :   tree template_parameter_type
    1360          113 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1361              : 
    1362              :   // result temporary
    1363          113 :   tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
    1364          113 :   rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
    1365              :                == TYPE_SIZE_UNIT (dst_type));
    1366              : 
    1367          113 :   tree tmp_stmt = error_mark_node;
    1368          113 :   Bvariable *bvar
    1369          113 :     = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
    1370              :                                    true /*address_is_taken*/, UNDEF_LOCATION,
    1371              :                                    &tmp_stmt);
    1372              : 
    1373          113 :   enter_intrinsic_block (ctx, fndecl, {bvar});
    1374              : 
    1375              :   // BUILTIN size_of FN BODY BEGIN
    1376              : 
    1377          113 :   tree memset_builtin = error_mark_node;
    1378          113 :   BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
    1379              :                                                  &memset_builtin);
    1380          113 :   rust_assert (memset_builtin != error_mark_node);
    1381              : 
    1382              :   // call memset with 0x01 and size of the thing see
    1383              :   // https://github.com/Rust-GCC/gccrs/issues/1899
    1384              : 
    1385          113 :   tree dst = bvar->get_tree (BUILTINS_LOCATION);
    1386          113 :   tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
    1387          113 :   tree constant_byte = build_int_cst (integer_type_node, 0x01);
    1388          113 :   tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
    1389              : 
    1390          113 :   tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
    1391              :                                           dst_addr, constant_byte, size_expr);
    1392          113 :   ctx->add_statement (memset_call);
    1393              : 
    1394          113 :   auto return_statement
    1395          113 :     = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
    1396          113 :   ctx->add_statement (return_statement);
    1397              :   // BUILTIN size_of FN BODY END
    1398              : 
    1399          113 :   finalize_intrinsic_block (ctx, fndecl);
    1400              : 
    1401          113 :   return fndecl;
    1402              : }
    1403              : 
    1404              : tree
    1405            7 : prefetch_read_data (Context *ctx, TyTy::FnType *fntype, location_t)
    1406              : {
    1407            7 :   return prefetch_data (ctx, fntype, Prefetch::Read);
    1408              : }
    1409              : tree
    1410            7 : prefetch_write_data (Context *ctx, TyTy::FnType *fntype, location_t)
    1411              : {
    1412            7 :   return prefetch_data (ctx, fntype, Prefetch::Write);
    1413              : }
    1414              : 
    1415              : tree
    1416           14 : prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
    1417              : {
    1418           14 :   rust_assert (fntype->get_params ().size () == 2);
    1419              : 
    1420           14 :   tree lookup = NULL_TREE;
    1421           14 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1422            0 :     return lookup;
    1423              : 
    1424           14 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1425              : 
    1426              :   // prefetching isn't pure and shouldn't be discarded after GIMPLE
    1427           14 :   TREE_READONLY (fndecl) = 0;
    1428           14 :   TREE_SIDE_EFFECTS (fndecl) = 1;
    1429              : 
    1430           14 :   std::vector<Bvariable *> args;
    1431           14 :   compile_fn_params (ctx, fntype, fndecl, &args);
    1432              : 
    1433           14 :   if (!Backend::function_set_parameters (fndecl, args))
    1434            0 :     return error_mark_node;
    1435              : 
    1436           14 :   enter_intrinsic_block (ctx, fndecl);
    1437              : 
    1438           14 :   auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
    1439              : 
    1440              :   // The core library technically allows you to pass any i32 value as a
    1441              :   // locality, but LLVM will then complain if the value cannot be constant
    1442              :   // evaluated. For now, we ignore the locality argument and instead always
    1443              :   // pass `3` (the most restrictive value). This allows us to still have
    1444              :   // prefetch behavior, just not as granular as expected. In future Rust
    1445              :   // versions, we hope that prefetch intrinsics will be split up according to
    1446              :   // locality, similarly to atomic intrinsics.
    1447              :   // The solution is to try and perform constant folding for the locality
    1448              :   // argument, or instead of creating a new function definition, modify the call
    1449              :   // site directly This has the bad side-effect of creating warnings about
    1450              :   // `unused name - locality`, which we hack away here:
    1451              :   // TODO: Take care of handling locality properly
    1452           14 :   Backend::var_expression (args[1], UNDEF_LOCATION);
    1453              : 
    1454           14 :   auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
    1455              : 
    1456           14 :   auto prefetch_raw = NULL_TREE;
    1457           14 :   auto ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_prefetch",
    1458              :                                                            &prefetch_raw);
    1459           14 :   rust_assert (ok);
    1460           14 :   auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
    1461              : 
    1462           28 :   auto prefetch_call = Backend::call_expression (prefetch,
    1463              :                                                  {addr, rw_flag,
    1464              :                                                   // locality arg
    1465           14 :                                                   make_unsigned_long_tree (3)},
    1466              :                                                  nullptr, UNDEF_LOCATION);
    1467              : 
    1468           14 :   TREE_READONLY (prefetch_call) = 0;
    1469           14 :   TREE_SIDE_EFFECTS (prefetch_call) = 1;
    1470              : 
    1471           14 :   ctx->add_statement (prefetch_call);
    1472              : 
    1473           14 :   finalize_intrinsic_block (ctx, fndecl);
    1474              : 
    1475           14 :   return fndecl;
    1476           14 : }
    1477              : 
    1478              : tree
    1479          423 : rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
    1480              : {
    1481              :   // rotate intrinsic has two parameter
    1482          423 :   rust_assert (fntype->get_params ().size () == 2);
    1483              : 
    1484          423 :   tree lookup = NULL_TREE;
    1485          423 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1486            0 :     return lookup;
    1487              : 
    1488          423 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1489              : 
    1490              :   // setup the params
    1491          423 :   std::vector<Bvariable *> param_vars;
    1492          423 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1493              : 
    1494          423 :   auto &x_param = param_vars.at (0);
    1495          423 :   auto &y_param = param_vars.at (1);
    1496          423 :   rust_assert (param_vars.size () == 2);
    1497          423 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1498            0 :     return error_mark_node;
    1499              : 
    1500          423 :   enter_intrinsic_block (ctx, fndecl);
    1501              : 
    1502              :   // BUILTIN rotate FN BODY BEGIN
    1503          423 :   tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
    1504          423 :   tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
    1505          423 :   tree rotate_expr
    1506          423 :     = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
    1507          423 :   auto return_statement
    1508          423 :     = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
    1509          423 :   ctx->add_statement (return_statement);
    1510              :   // BUILTIN rotate FN BODY END
    1511              : 
    1512          423 :   finalize_intrinsic_block (ctx, fndecl);
    1513              : 
    1514          423 :   return fndecl;
    1515          423 : }
    1516              : 
    1517              : tree
    1518          228 : transmute (Context *ctx, TyTy::FnType *fntype, location_t)
    1519              : {
    1520              :   // transmute intrinsic has one parameter
    1521          228 :   rust_assert (fntype->get_params ().size () == 1);
    1522              : 
    1523          228 :   tree lookup = NULL_TREE;
    1524          228 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1525            0 :     return lookup;
    1526              : 
    1527          228 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1528              : 
    1529          228 :   std::vector<Bvariable *> param_vars;
    1530          228 :   std::vector<tree_node *> compiled_types;
    1531          228 :   compile_fn_params (ctx, fntype, fndecl, &param_vars, &compiled_types);
    1532              : 
    1533          228 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1534            0 :     return error_mark_node;
    1535              : 
    1536              :   // param to convert
    1537          228 :   Bvariable *convert_me_param = param_vars.at (0);
    1538          228 :   tree convert_me_expr
    1539          228 :     = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
    1540              : 
    1541              :   // check for transmute pre-conditions
    1542          228 :   tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
    1543          228 :   tree source_type_expr = compiled_types.at (0);
    1544          228 :   tree target_size_expr = TYPE_SIZE (target_type_expr);
    1545          228 :   tree source_size_expr = TYPE_SIZE (source_type_expr);
    1546              :   // for some reason, unit types and other zero-sized types return NULL for the
    1547              :   // size expressions
    1548          228 :   unsigned HOST_WIDE_INT target_size
    1549          228 :     = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
    1550          228 :   unsigned HOST_WIDE_INT source_size
    1551          228 :     = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
    1552              : 
    1553              :   // size check for concrete types
    1554              :   // TODO(liushuyu): check alignment for pointers; check for dependently-sized
    1555              :   // types
    1556          228 :   if (target_size != source_size)
    1557              :     {
    1558            7 :       rust_error_at (fntype->get_locus (),
    1559              :                      "cannot transmute between types of different sizes, or "
    1560              :                      "dependently-sized types");
    1561           14 :       rust_inform (
    1562            7 :         fntype->get_ident ().locus, "source type: %qs (%lu bits)",
    1563            7 :         fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
    1564              :         (unsigned long) source_size);
    1565            7 :       rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
    1566           14 :                    fntype->get_return_type ()->as_string ().c_str (),
    1567              :                    (unsigned long) target_size);
    1568              :     }
    1569              : 
    1570          228 :   enter_intrinsic_block (ctx, fndecl);
    1571              : 
    1572              :   // BUILTIN transmute FN BODY BEGIN
    1573              : 
    1574              :   // Return *((orig_type*)&decl)  */
    1575              : 
    1576          228 :   tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
    1577          228 :   t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
    1578              :                        build_pointer_type (target_type_expr), t);
    1579          228 :   tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
    1580              : 
    1581          228 :   auto return_statement
    1582          228 :     = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
    1583          228 :   ctx->add_statement (return_statement);
    1584              :   // BUILTIN transmute FN BODY END
    1585              : 
    1586          228 :   finalize_intrinsic_block (ctx, fndecl);
    1587              : 
    1588          228 :   return fndecl;
    1589          228 : }
    1590              : 
    1591              : tree
    1592          575 : sizeof_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    1593              : {
    1594              :   // size_of has _zero_ parameters its parameter is the generic one
    1595          575 :   rust_assert (fntype->get_params ().size () == 0);
    1596              : 
    1597          575 :   tree lookup = NULL_TREE;
    1598          575 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1599            0 :     return lookup;
    1600              : 
    1601          575 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1602              : 
    1603              :   // get the template parameter type tree fn size_of<T>();
    1604          575 :   rust_assert (fntype->get_num_substitutions () == 1);
    1605          575 :   auto &param_mapping = fntype->get_substs ().at (0);
    1606          575 :   const auto param_tyty = param_mapping.get_param_ty ();
    1607          575 :   auto resolved_tyty = param_tyty->resolve ();
    1608          575 :   tree template_parameter_type
    1609          575 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1610              : 
    1611          575 :   enter_intrinsic_block (ctx, fndecl);
    1612              : 
    1613              :   // BUILTIN size_of FN BODY BEGIN
    1614          575 :   tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
    1615          575 :   auto return_statement
    1616          575 :     = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
    1617          575 :   ctx->add_statement (return_statement);
    1618              :   // BUILTIN size_of FN BODY END
    1619              : 
    1620          575 :   finalize_intrinsic_block (ctx, fndecl);
    1621              : 
    1622          575 :   return fndecl;
    1623              : }
    1624              : 
    1625              : /**
    1626              :  * pub fn size_of_val<T: ?Sized>(_: *const T) -> usize;
    1627              :  */
    1628              : tree
    1629           11 : size_of_val_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    1630              : {
    1631           11 :   rust_assert (fntype->get_params ().size () == 1);
    1632              : 
    1633           11 :   tree lookup = NULL_TREE;
    1634           11 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1635            0 :     return lookup;
    1636              : 
    1637           11 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1638              : 
    1639           11 :   auto locus = fntype->get_locus ();
    1640              : 
    1641           11 :   std::vector<Bvariable *> param_vars;
    1642           11 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1643              : 
    1644           11 :   auto &__param = param_vars.at (0);
    1645           11 :   rust_assert (param_vars.size () == 1);
    1646           11 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1647            0 :     return error_mark_node;
    1648              : 
    1649           11 :   rust_assert (fntype->get_num_substitutions () == 1);
    1650           11 :   auto &param_mapping = fntype->get_substs ().at (0);
    1651           11 :   const auto param_tyty = param_mapping.get_param_ty ();
    1652           11 :   auto resolved_tyty = param_tyty->resolve ();
    1653           11 :   tree template_parameter_type
    1654           11 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1655              : 
    1656           11 :   enter_intrinsic_block (ctx, fndecl);
    1657              : 
    1658              :   // BUILTIN size_of FN BODY BEGIN
    1659              : 
    1660           11 :   tree size_expr = NULL_TREE;
    1661              : 
    1662           11 :   tree param = Backend::var_expression (__param, UNDEF_LOCATION);
    1663           11 :   tree param_ty = TREE_TYPE (param);
    1664           11 :   if (RS_DST_FLAG_P (param_ty))
    1665              :     {
    1666            8 :       tree data_field = TYPE_FIELDS (param_ty);
    1667            8 :       tree meta_field = DECL_CHAIN (data_field);
    1668            8 :       tree meta_field_expr
    1669            8 :         = build3_loc (locus, COMPONENT_REF, TREE_TYPE (meta_field), param,
    1670              :                       meta_field, NULL_TREE);
    1671              : 
    1672            8 :       TyTy::BaseType *inner_dst = get_inner_dst (resolved_tyty);
    1673            8 :       tree tail_size_expr = NULL_TREE;
    1674            8 :       if (inner_dst->get_kind () == TyTy::TypeKind::SLICE
    1675            8 :           || inner_dst->get_kind () == TyTy::TypeKind::STR)
    1676              :         {
    1677            5 :           tree elem_type = NULL_TREE;
    1678            5 :           if (inner_dst->get_kind () == TyTy::TypeKind::SLICE)
    1679              :             {
    1680            4 :               auto slice_tyty = static_cast<TyTy::SliceType *> (inner_dst);
    1681            4 :               elem_type
    1682            4 :                 = TyTyResolveCompile::compile (ctx,
    1683            4 :                                                slice_tyty->get_element_type ());
    1684              :             }
    1685              :           else
    1686            1 :             elem_type = char_type_node;
    1687              : 
    1688            5 :           tree elem_size = TYPE_SIZE_UNIT (elem_type);
    1689            5 :           tail_size_expr
    1690            5 :             = build2_loc (locus, MULT_EXPR, size_type_node,
    1691              :                           fold_convert_loc (locus, size_type_node,
    1692              :                                             meta_field_expr),
    1693              :                           fold_convert_loc (locus, size_type_node, elem_size));
    1694              :         }
    1695            3 :       else if (inner_dst->get_kind () == TyTy::TypeKind::DYNAMIC)
    1696              :         {
    1697            3 :           tree vtable_ptr_ty = TREE_TYPE (meta_field_expr);
    1698            3 :           tree vtable_ty = TREE_TYPE (vtable_ptr_ty);
    1699              : 
    1700            3 :           tree vtable_ref
    1701            3 :             = build1_loc (locus, INDIRECT_REF, vtable_ty, meta_field_expr);
    1702              : 
    1703            3 :           tree vtable_field_0 = TYPE_FIELDS (vtable_ty);
    1704            3 :           tree vtable_field_size = DECL_CHAIN (vtable_field_0);
    1705            3 :           rust_assert (vtable_field_size != NULL_TREE);
    1706              : 
    1707            3 :           tail_size_expr
    1708            3 :             = build3_loc (locus, COMPONENT_REF, TREE_TYPE (vtable_field_size),
    1709              :                           vtable_ref, vtable_field_size, NULL_TREE);
    1710              :         }
    1711              :       else
    1712              :         {
    1713            0 :           rust_unreachable ();
    1714              :         }
    1715            8 :       if (resolved_tyty->get_kind () == TyTy::TypeKind::ADT)
    1716            3 :         size_expr = build2_loc (locus, PLUS_EXPR, size_type_node,
    1717              :                                 fold_convert_loc (locus, size_type_node,
    1718            3 :                                                   TYPE_SIZE_UNIT (
    1719              :                                                     template_parameter_type)),
    1720              :                                 tail_size_expr);
    1721              :       else
    1722              :         size_expr = tail_size_expr;
    1723              :     }
    1724              :   else
    1725            3 :     size_expr = TYPE_SIZE_UNIT (template_parameter_type);
    1726              : 
    1727           11 :   auto return_statement
    1728           11 :     = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
    1729           11 :   ctx->add_statement (return_statement);
    1730              : 
    1731              :   // BUILTIN size_of FN BODY END
    1732              : 
    1733           11 :   finalize_intrinsic_block (ctx, fndecl);
    1734              : 
    1735           11 :   return fndecl;
    1736           11 : }
    1737              : 
    1738              : /**
    1739              :  * pub fn min_align_of<T>() -> usize;
    1740              :  */
    1741              : tree
    1742            2 : min_align_of_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    1743              : {
    1744              :   // min_align_of has _zero_ parameters its parameter is the generic one
    1745            2 :   rust_assert (fntype->get_params ().size () == 0);
    1746              : 
    1747            2 :   tree lookup = NULL_TREE;
    1748            2 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1749            0 :     return lookup;
    1750              : 
    1751            2 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1752              : 
    1753              :   // get the template parameter type tree fn min_align_of<T>();
    1754            2 :   rust_assert (fntype->get_num_substitutions () == 1);
    1755            2 :   auto &param_mapping = fntype->get_substs ().at (0);
    1756            2 :   const auto param_tyty = param_mapping.get_param_ty ();
    1757            2 :   auto resolved_tyty = param_tyty->resolve ();
    1758            2 :   tree template_parameter_type
    1759            2 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1760              : 
    1761            2 :   enter_intrinsic_block (ctx, fndecl);
    1762              : 
    1763              :   // BUILTIN min_align_of FN BODY BEGIN
    1764            2 :   tree align_expr
    1765            2 :     = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (template_parameter_type));
    1766              : 
    1767            2 :   auto return_statement
    1768            2 :     = Backend::return_statement (fndecl, align_expr, UNDEF_LOCATION);
    1769            2 :   ctx->add_statement (return_statement);
    1770              :   // BUILTIN min_align_of FN BODY END
    1771              : 
    1772            2 :   finalize_intrinsic_block (ctx, fndecl);
    1773              : 
    1774            2 :   return fndecl;
    1775              : }
    1776              : 
    1777              : /**
    1778              :  * pub fn min_align_of_val<T: ?Sized>(_: *const T) -> usize;
    1779              :  */
    1780              : tree
    1781            7 : min_align_of_val_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    1782              : {
    1783            7 :   rust_assert (fntype->get_params ().size () == 1);
    1784              : 
    1785            7 :   tree lookup = NULL_TREE;
    1786            7 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1787            0 :     return lookup;
    1788              : 
    1789            7 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1790              : 
    1791            7 :   auto locus = fntype->get_locus ();
    1792              : 
    1793            7 :   std::vector<Bvariable *> param_vars;
    1794            7 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1795              : 
    1796            7 :   auto &__param = param_vars.at (0);
    1797            7 :   rust_assert (param_vars.size () == 1);
    1798            7 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1799            0 :     return error_mark_node;
    1800              : 
    1801            7 :   rust_assert (fntype->get_num_substitutions () == 1);
    1802            7 :   auto &param_mapping = fntype->get_substs ().at (0);
    1803            7 :   const auto param_tyty = param_mapping.get_param_ty ();
    1804            7 :   auto resolved_tyty = param_tyty->resolve ();
    1805            7 :   tree template_parameter_type
    1806            7 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    1807              : 
    1808            7 :   enter_intrinsic_block (ctx, fndecl);
    1809              : 
    1810              :   // BUILTIN size_of FN BODY BEGIN
    1811              : 
    1812            7 :   tree align_expr = NULL_TREE;
    1813            7 :   tree param = Backend::var_expression (__param, UNDEF_LOCATION);
    1814            7 :   tree param_ty = TREE_TYPE (param);
    1815            7 :   if (RS_DST_FLAG_P (param_ty))
    1816              :     {
    1817            4 :       tree data_field = TYPE_FIELDS (param_ty);
    1818            4 :       tree meta_field = DECL_CHAIN (data_field);
    1819            4 :       tree meta_field_expr
    1820            4 :         = build3_loc (locus, COMPONENT_REF, TREE_TYPE (meta_field), param,
    1821              :                       meta_field, NULL_TREE);
    1822              : 
    1823            4 :       TyTy::BaseType *inner_dst = get_inner_dst (resolved_tyty);
    1824            4 :       tree tail_align_expr = NULL_TREE;
    1825              : 
    1826            4 :       if (inner_dst->get_kind () == TyTy::TypeKind::SLICE
    1827            4 :           || inner_dst->get_kind () == TyTy::TypeKind::STR)
    1828              :         {
    1829            2 :           tree elem_type = NULL_TREE;
    1830            2 :           if (inner_dst->get_kind () == TyTy::TypeKind::SLICE)
    1831              :             {
    1832            1 :               auto slice_tyty = static_cast<TyTy::SliceType *> (inner_dst);
    1833            1 :               elem_type
    1834            1 :                 = TyTyResolveCompile::compile (ctx,
    1835            1 :                                                slice_tyty->get_element_type ());
    1836              :             }
    1837              :           else
    1838            1 :             elem_type = char_type_node;
    1839              : 
    1840            2 :           tail_align_expr
    1841            2 :             = build_int_cst (size_type_node, TYPE_ALIGN_UNIT (elem_type));
    1842              :         }
    1843            2 :       else if (inner_dst->get_kind () == TyTy::TypeKind::DYNAMIC)
    1844              :         {
    1845            2 :           tree vtable_ptr_ty = TREE_TYPE (meta_field_expr);
    1846            2 :           tree vtable_ty = TREE_TYPE (vtable_ptr_ty);
    1847              : 
    1848            2 :           tree vtable_ref
    1849            2 :             = build1_loc (locus, INDIRECT_REF, vtable_ty, meta_field_expr);
    1850              : 
    1851            2 :           tree vtable_field_0 = TYPE_FIELDS (vtable_ty);
    1852            2 :           tree vtable_field_1 = DECL_CHAIN (vtable_field_0);
    1853            2 :           tree vtable_field_align = DECL_CHAIN (vtable_field_1);
    1854            2 :           rust_assert (vtable_field_align != NULL_TREE);
    1855              : 
    1856            2 :           tail_align_expr
    1857            2 :             = build3_loc (locus, COMPONENT_REF, TREE_TYPE (vtable_field_align),
    1858              :                           vtable_ref, vtable_field_align, NULL_TREE);
    1859              :         }
    1860              :       else
    1861              :         {
    1862            0 :           rust_unreachable ();
    1863              :         }
    1864              : 
    1865            4 :       if (resolved_tyty->get_kind () == TyTy::TypeKind::ADT)
    1866              :         {
    1867            0 :           tree base_align_expr
    1868            0 :             = build_int_cst (size_type_node,
    1869            0 :                              TYPE_ALIGN_UNIT (template_parameter_type));
    1870            0 :           align_expr = build2_loc (locus, MAX_EXPR, size_type_node,
    1871              :                                    base_align_expr, tail_align_expr);
    1872              :         }
    1873              :       else
    1874              :         align_expr = tail_align_expr;
    1875              :     }
    1876              :   else
    1877            3 :     align_expr = build_int_cst (size_type_node,
    1878            3 :                                 TYPE_ALIGN_UNIT (template_parameter_type));
    1879              : 
    1880            7 :   auto return_statement
    1881            7 :     = Backend::return_statement (fndecl, align_expr, UNDEF_LOCATION);
    1882            7 :   ctx->add_statement (return_statement);
    1883              : 
    1884              :   // BUILTIN size_of FN BODY END
    1885              : 
    1886            7 :   finalize_intrinsic_block (ctx, fndecl);
    1887              : 
    1888            7 :   return fndecl;
    1889            7 : }
    1890              : 
    1891              : tree
    1892           88 : offset (Context *ctx, TyTy::FnType *fntype, location_t expr_locus)
    1893              : {
    1894              :   // offset intrinsic has two params dst pointer and offset isize
    1895           88 :   rust_assert (fntype->get_params ().size () == 2);
    1896              : 
    1897           88 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1898              : 
    1899           88 :   std::vector<Bvariable *> param_vars;
    1900           88 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1901              : 
    1902           88 :   auto &dst_param = param_vars.at (0);
    1903           88 :   auto &size_param = param_vars.at (1);
    1904           88 :   rust_assert (param_vars.size () == 2);
    1905           88 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1906            0 :     return error_mark_node;
    1907              : 
    1908           88 :   enter_intrinsic_block (ctx, fndecl);
    1909              : 
    1910              :   // BUILTIN offset FN BODY BEGIN
    1911           88 :   tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
    1912           88 :   tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
    1913           88 :   tree pointer_offset_expr = pointer_offset_expression (dst, size, expr_locus);
    1914           88 :   auto return_statement
    1915           88 :     = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
    1916           88 :   ctx->add_statement (return_statement);
    1917              :   // BUILTIN offset FN BODY END
    1918              : 
    1919           88 :   finalize_intrinsic_block (ctx, fndecl);
    1920              : 
    1921           88 :   return fndecl;
    1922           88 : }
    1923              : 
    1924              : /**
    1925              :  * pub const fn bswap<T: Copy>(x: T) -> T;
    1926              :  */
    1927              : tree
    1928            9 : bswap_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    1929              : {
    1930            9 :   rust_assert (fntype->get_params ().size () == 1);
    1931              : 
    1932            9 :   tree lookup = NULL_TREE;
    1933            9 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    1934            0 :     return lookup;
    1935              : 
    1936            9 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    1937              : 
    1938            9 :   auto locus = fntype->get_locus ();
    1939              : 
    1940            9 :   std::vector<Bvariable *> param_vars;
    1941            9 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    1942              : 
    1943            9 :   auto &x_param = param_vars.at (0);
    1944            9 :   rust_assert (param_vars.size () == 1);
    1945            9 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    1946            0 :     return error_mark_node;
    1947              : 
    1948            9 :   auto *monomorphized_type
    1949            9 :     = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
    1950              : 
    1951            9 :   auto call_locus = ctx->get_mappings ().lookup_location (fntype->get_ref ());
    1952            9 :   check_for_basic_integer_type ("bswap", call_locus, monomorphized_type);
    1953              : 
    1954            9 :   tree template_parameter_type
    1955            9 :     = TyTyResolveCompile::compile (ctx, monomorphized_type);
    1956              : 
    1957            9 :   tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
    1958            9 :   unsigned HOST_WIDE_INT size = TREE_INT_CST_LOW (size_expr);
    1959              : 
    1960            9 :   enter_intrinsic_block (ctx, fndecl);
    1961              : 
    1962              :   // BUILTIN bswap FN BODY BEGIN
    1963              : 
    1964            9 :   auto expr_x = Backend::var_expression (x_param, locus);
    1965            9 :   tree result = NULL_TREE;
    1966              : 
    1967            9 :   if (size == 1)
    1968              :     {
    1969              :       result = expr_x;
    1970              :     }
    1971              :   else
    1972              :     {
    1973            8 :       tree target_type = NULL_TREE;
    1974            8 :       const char *builtin_name = nullptr;
    1975            8 :       switch (size)
    1976              :         {
    1977            2 :         case 2:
    1978            2 :           builtin_name = "__builtin_bswap16";
    1979            2 :           target_type = uint16_type_node;
    1980            2 :           break;
    1981            2 :         case 4:
    1982            2 :           builtin_name = "__builtin_bswap32";
    1983            2 :           target_type = uint32_type_node;
    1984            2 :           break;
    1985            2 :         case 8:
    1986            2 :           builtin_name = "__builtin_bswap64";
    1987            2 :           target_type = uint64_type_node;
    1988            2 :           break;
    1989            2 :         case 16:
    1990            2 :           builtin_name = "__builtin_bswap128";
    1991            2 :           target_type = uint128_type_node;
    1992            2 :           break;
    1993            0 :         default:
    1994            0 :           return error_mark_node;
    1995              :         }
    1996              : 
    1997            8 :       tree bswap_raw = nullptr;
    1998            8 :       auto ok = BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
    1999              :                                                                &bswap_raw);
    2000              : 
    2001            8 :       if (ok)
    2002              :         {
    2003            8 :           tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
    2004              : 
    2005            8 :           auto bswap_x = build1 (CONVERT_EXPR, target_type, expr_x);
    2006              : 
    2007            8 :           auto bswap_call
    2008            8 :             = Backend::call_expression (bswap_fn, {bswap_x}, NULL_TREE, locus);
    2009              : 
    2010            8 :           result = build1 (CONVERT_EXPR, template_parameter_type, bswap_call);
    2011              :         }
    2012              :       else
    2013              :         {
    2014            0 :           auto ok2 = BuiltinsContext::get ().lookup_simple_builtin (
    2015            0 :             "__builtin_bswap64", &bswap_raw);
    2016            0 :           rust_assert (ok2);
    2017              : 
    2018            0 :           tree bswap_fn = build_fold_addr_expr_loc (locus, bswap_raw);
    2019              : 
    2020            0 :           tree tmp_in_stmt = error_mark_node;
    2021            0 :           Bvariable *in_var
    2022            0 :             = Backend::temporary_variable (fndecl, NULL_TREE,
    2023              :                                            template_parameter_type, expr_x,
    2024              :                                            true, locus, &tmp_in_stmt);
    2025            0 :           ctx->add_statement (tmp_in_stmt);
    2026              : 
    2027            0 :           tree addr_x
    2028            0 :             = build_fold_addr_expr_loc (locus, in_var->get_tree (locus));
    2029            0 :           tree u64_ptr_type = build_pointer_type (uint64_type_node);
    2030              : 
    2031            0 :           tree low_ptr = fold_convert (u64_ptr_type, addr_x);
    2032            0 :           tree low = build_fold_indirect_ref_loc (locus, low_ptr);
    2033              : 
    2034            0 :           tree high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type, low_ptr,
    2035              :                                        size_int (8));
    2036            0 :           tree high = build_fold_indirect_ref_loc (locus, high_ptr);
    2037              : 
    2038            0 :           auto new_high
    2039            0 :             = Backend::call_expression (bswap_fn, {low}, NULL_TREE, locus);
    2040            0 :           auto new_low
    2041            0 :             = Backend::call_expression (bswap_fn, {high}, NULL_TREE, locus);
    2042              : 
    2043            0 :           tree tmp_stmt = error_mark_node;
    2044            0 :           Bvariable *result_var
    2045            0 :             = Backend::temporary_variable (fndecl, NULL_TREE,
    2046              :                                            template_parameter_type, NULL_TREE,
    2047              :                                            true, locus, &tmp_stmt);
    2048            0 :           ctx->add_statement (tmp_stmt);
    2049              : 
    2050            0 :           tree addr_res
    2051            0 :             = build_fold_addr_expr_loc (locus, result_var->get_tree (locus));
    2052            0 :           tree res_ptr = fold_convert (u64_ptr_type, addr_res);
    2053              : 
    2054            0 :           tree store_low
    2055            0 :             = build2 (MODIFY_EXPR, void_type_node,
    2056              :                       build_fold_indirect_ref_loc (locus, res_ptr), new_low);
    2057            0 :           ctx->add_statement (store_low);
    2058              : 
    2059            0 :           tree res_high_ptr = fold_build2 (POINTER_PLUS_EXPR, u64_ptr_type,
    2060              :                                            res_ptr, size_int (8));
    2061            0 :           tree store_high
    2062            0 :             = build2 (MODIFY_EXPR, void_type_node,
    2063              :                       build_fold_indirect_ref_loc (locus, res_high_ptr),
    2064              :                       new_high);
    2065            0 :           ctx->add_statement (store_high);
    2066              : 
    2067            0 :           result = result_var->get_tree (locus);
    2068              :         }
    2069              :     }
    2070              : 
    2071            9 :   auto return_statement = Backend::return_statement (fndecl, result, locus);
    2072              : 
    2073            9 :   TREE_READONLY (result) = 1;
    2074              : 
    2075            9 :   ctx->add_statement (return_statement);
    2076              : 
    2077              :   // BUILTIN bswap FN BODY END
    2078              : 
    2079            9 :   finalize_intrinsic_block (ctx, fndecl);
    2080              : 
    2081            9 :   return fndecl;
    2082            9 : }
    2083              : 
    2084              : tree
    2085          253 : ctlz_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2086              : {
    2087          253 :   return inner::ctlz_handler (ctx, fntype, false);
    2088              : }
    2089              : 
    2090              : tree
    2091          169 : ctlz_nonzero_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2092              : {
    2093          169 :   return inner::ctlz_handler (ctx, fntype, true);
    2094              : }
    2095              : 
    2096              : tree
    2097          169 : cttz_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2098              : {
    2099          169 :   return inner::cttz_handler (ctx, fntype, false);
    2100              : }
    2101              : 
    2102              : tree
    2103          113 : cttz_nonzero_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2104              : {
    2105          113 :   return inner::cttz_handler (ctx, fntype, true);
    2106              : }
    2107              : 
    2108              : /**
    2109              :  * pub unsafe fn write_bytes<T>(dst: *mut T, val: u8, count: usize);
    2110              :  */
    2111              : tree
    2112            1 : write_bytes_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2113              : {
    2114            1 :   rust_assert (fntype->get_params ().size () == 3);
    2115              : 
    2116            1 :   tree lookup = NULL_TREE;
    2117            1 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    2118            0 :     return lookup;
    2119              : 
    2120            1 :   tree fndecl = compile_intrinsic_function (ctx, fntype);
    2121              : 
    2122            1 :   auto locus = fntype->get_locus ();
    2123              : 
    2124            1 :   std::vector<Bvariable *> param_vars;
    2125            1 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    2126              : 
    2127            1 :   auto &dst_param = param_vars.at (0);
    2128            1 :   auto &val_param = param_vars.at (1);
    2129            1 :   auto &count_param = param_vars.at (2);
    2130            1 :   rust_assert (param_vars.size () == 3);
    2131            1 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    2132            0 :     return error_mark_node;
    2133              : 
    2134            1 :   auto *monomorphized_type
    2135            1 :     = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
    2136              : 
    2137            1 :   tree template_parameter_type
    2138            1 :     = TyTyResolveCompile::compile (ctx, monomorphized_type);
    2139            1 :   tree dst_size_expr = TYPE_SIZE_UNIT (template_parameter_type);
    2140            1 :   rust_assert (dst_size_expr != NULL_TREE);
    2141              : 
    2142            1 :   enter_intrinsic_block (ctx, fndecl);
    2143              : 
    2144              :   // BUILTIN WRITE_BYTES FN BODY START
    2145              : 
    2146            1 :   tree expr_dst = Backend::var_expression (dst_param, locus);
    2147            1 :   tree expr_val = Backend::var_expression (val_param, locus);
    2148            1 :   tree expr_count = Backend::var_expression (count_param, locus);
    2149              : 
    2150            1 :   tree expr_count_final
    2151            1 :     = fold_build2_loc (locus, MULT_EXPR, size_type_node,
    2152              :                        fold_convert_loc (locus, size_type_node, expr_count),
    2153              :                        fold_convert_loc (locus, size_type_node, dst_size_expr));
    2154              : 
    2155            1 :   tree write_bytes_raw = nullptr;
    2156              :   // void* memset( void* dest, int ch, size_t count );
    2157            1 :   bool ok = BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
    2158              :                                                            &write_bytes_raw);
    2159            1 :   rust_assert (ok);
    2160              : 
    2161            1 :   tree write_bytes_fn = build_fold_addr_expr_loc (locus, write_bytes_raw);
    2162            1 :   tree write_bytes_call
    2163            1 :     = Backend::call_expression (write_bytes_fn,
    2164              :                                 {expr_dst, expr_val, expr_count_final},
    2165              :                                 NULL_TREE, locus);
    2166              : 
    2167            1 :   ctx->add_statement (write_bytes_call);
    2168              : 
    2169              :   // BUILTIN WRITE_BYTES FN BODY END
    2170              : 
    2171            1 :   finalize_intrinsic_block (ctx, fndecl);
    2172              : 
    2173            1 :   TREE_READONLY (fndecl) = 0;
    2174              : 
    2175            1 :   return fndecl;
    2176            1 : }
    2177              : 
    2178              : /**
    2179              :  * pub fn arith_offset<T>(dst: *const T, offset: isize) -> *const T;
    2180              :  */
    2181              : tree
    2182            1 : arith_offset_handler (Context *ctx, TyTy::FnType *fntype, location_t expr_locus)
    2183              : {
    2184            1 :   rust_assert (fntype->get_params ().size () == 2);
    2185              : 
    2186            1 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    2187              : 
    2188            1 :   auto locus = fntype->get_locus ();
    2189              : 
    2190            1 :   std::vector<Bvariable *> param_vars;
    2191            1 :   compile_fn_params (ctx, fntype, fndecl, &param_vars);
    2192              : 
    2193            1 :   auto &dst_param = param_vars.at (0);
    2194            1 :   auto &size_param = param_vars.at (1);
    2195            1 :   rust_assert (param_vars.size () == 2);
    2196            1 :   if (!Backend::function_set_parameters (fndecl, param_vars))
    2197            0 :     return error_mark_node;
    2198              : 
    2199            1 :   enter_intrinsic_block (ctx, fndecl);
    2200              : 
    2201              :   // BUILTIN arith_offset FN BODY BEGIN
    2202              : 
    2203            1 :   tree dst = Backend::var_expression (dst_param, locus);
    2204            1 :   tree size = Backend::var_expression (size_param, locus);
    2205            1 :   tree pointer_offset_expr = pointer_offset_expression (dst, size, expr_locus);
    2206            1 :   auto return_statement
    2207            1 :     = Backend::return_statement (fndecl, pointer_offset_expr, locus);
    2208            1 :   ctx->add_statement (return_statement);
    2209              : 
    2210              :   // BUILTIN arith_offset FN BODY END
    2211              : 
    2212            1 :   finalize_intrinsic_block (ctx, fndecl);
    2213              : 
    2214            1 :   return fndecl;
    2215            1 : }
    2216              : 
    2217              : /**
    2218              :  * pub fn assert_zero_valid<T>();
    2219              :  *
    2220              :  * TODO: Since gccrs currently lacks comprehensive layout engine support for
    2221              :  * validity ranges (such as `rustc_layout_scalar_valid_range_start`), we cannot
    2222              :  * accurately determine if a type is safely zeroable. Therefore, this is
    2223              :  * implemented as a temporary no-op stub that always returns void (unit) and
    2224              :  * succeeds for all types.
    2225              :  */
    2226              : tree
    2227            2 : assert_zero_valid_handler (Context *ctx, TyTy::FnType *fntype, location_t)
    2228              : {
    2229            2 :   rust_assert (fntype->get_params ().size () == 0);
    2230              : 
    2231            2 :   tree lookup = NULL_TREE;
    2232            2 :   if (check_for_cached_intrinsic (ctx, fntype, &lookup))
    2233            0 :     return lookup;
    2234              : 
    2235            2 :   auto fndecl = compile_intrinsic_function (ctx, fntype);
    2236              : 
    2237            2 :   rust_assert (fntype->get_num_substitutions () == 1);
    2238            2 :   auto &param_mapping = fntype->get_substs ().at (0);
    2239            2 :   const auto param_tyty = param_mapping.get_param_ty ();
    2240            2 :   auto resolved_tyty = param_tyty->resolve ();
    2241              : 
    2242              :   // Safely resolve the template parameter type to ensure monomorphization
    2243              :   // works.
    2244              :   // Suppress unused-variable warning since layout verification is a FIXME.
    2245            2 :   [[gnu::unused]] tree template_parameter_type
    2246            2 :     = TyTyResolveCompile::compile (ctx, resolved_tyty);
    2247              : 
    2248            2 :   enter_intrinsic_block (ctx, fndecl);
    2249              : 
    2250              :   // BUILTIN assert_zero_valid FN BODY BEGIN
    2251              : 
    2252              :   // TODO: Implement layout verification via template_parameter_type once
    2253              :   // niche-filling and valid scalar ranges are supported in the layout engine.
    2254              :   // If invalid, this should emit a panic.
    2255              : 
    2256              :   // we always return unit for now.
    2257            2 :   auto return_statement
    2258            2 :     = Backend::return_statement (fndecl, void_node, UNDEF_LOCATION);
    2259            2 :   ctx->add_statement (return_statement);
    2260              : 
    2261              :   // BUILTIN assert_zero_valid FN BODY END
    2262              : 
    2263            2 :   finalize_intrinsic_block (ctx, fndecl);
    2264              : 
    2265            2 :   return fndecl;
    2266              : }
    2267              : 
    2268              : } // namespace handlers
    2269              : } // namespace Compile
    2270              : } // namespace Rust
        

Generated by: LCOV version 2.4-beta

LCOV profile is generated on x86_64 machine using following configure options: configure --disable-bootstrap --enable-coverage=opt --enable-languages=c,c++,fortran,go,jit,lto,rust,m2 --enable-host-shared. GCC test suite is run with the built compiler.