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