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