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