From: Pierre-Emmanuel Patry <[email protected]>

gcc/rust/ChangeLog:

        * Make-lang.in: Add rust-intrinsic-handlers.cc.
        * backend/rust-compile-intrinsic.cc (get_identifier): Move function
        to handlers namespace.
        (is_basic_integer_type): Likewise.
        (check_for_basic_integer_type): Likewise.
        (offset_handler): Likewise.
        (sizeof_handler): Likewise.
        (transmute_handler): Likewise.
        (rotate_handler): Likewise.
        (wrapping_op_handler_inner): Likewise.
        (op_with_overflow_inner): Likewise.
        (uninit_handler): Likewise.
        (move_val_init_handler): Likewise.
        (assume_handler): Likewise.
        (discriminant_value_handler): Likewise.
        (variant_count_handler): Likewise.
        (enum class): Likewise.
        (prefetch_data_handler): Likewise.
        (rotate_left_handler): Likewise.
        (rotate_right_handler): Likewise.
        (std::function<tree): Likewise.
        (wrapping_op_handler): Likewise.
        (op_with_overflow): Likewise.
        (prefetch_read_data): Likewise.
        (prefetch_write_data): Likewise.
        (atomic_store_handler_inner): Likewise.
        (atomic_load_handler_inner): Likewise.
        (atomic_store_handler): Likewise.
        (atomic_load_handler): Likewise.
        (unchecked_op_inner): Likewise.
        (unchecked_op_handler): Likewise.
        (copy_handler_inner): Likewise.
        (copy_handler): Likewise.
        (expect_handler_inner): Likewise.
        (expect_handler): Likewise.
        (try_handler_inner): Likewise.
        (try_handler): Likewise.
        (sorry_handler): Likewise.
        (check_for_cached_intrinsic): Likewise.
        (maybe_override_ctx): Likewise.
        (compile_fn_params): Likewise.
        (compile_intrinsic_function): Likewise.
        (enter_intrinsic_block): Likewise.
        (finalize_intrinsic_block): Likewise.
        (make_unsigned_long_tree): Likewise.
        (build_atomic_builtin_name): Likewise.
        * backend/rust-intrinsic-handlers.cc: New file.
        * backend/rust-intrinsic-handlers.h: New file.

Signed-off-by: Pierre-Emmanuel Patry <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.


Commit on github: 
https://github.com/Rust-GCC/gccrs/commit/52d97fd225ffeaa780c6fcfd3cb86b32cf67d39f

The commit has been mentioned in the following pull-request(s):
 - https://github.com/Rust-GCC/gccrs/pull/4459

 gcc/rust/Make-lang.in                       |    1 +
 gcc/rust/backend/rust-compile-intrinsic.cc  | 1463 +------------------
 gcc/rust/backend/rust-intrinsic-handlers.cc | 1375 +++++++++++++++++
 gcc/rust/backend/rust-intrinsic-handlers.h  |   94 ++
 4 files changed, 1511 insertions(+), 1422 deletions(-)
 create mode 100644 gcc/rust/backend/rust-intrinsic-handlers.cc
 create mode 100644 gcc/rust/backend/rust-intrinsic-handlers.h

diff --git a/gcc/rust/Make-lang.in b/gcc/rust/Make-lang.in
index 5a1551c09..bd5646f7c 100644
--- a/gcc/rust/Make-lang.in
+++ b/gcc/rust/Make-lang.in
@@ -206,6 +206,7 @@ GRS_OBJS = \
     rust/rust-hir-type-check-path.o \
     rust/rust-unsafe-checker.o \
     rust/rust-hir-pattern-analysis.o \
+    rust/rust-intrinsic-handlers.o \
     rust/rust-compile-intrinsic.o \
     rust/rust-compile-pattern.o \
     rust/rust-compile-fnparam.o \
diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc 
b/gcc/rust/backend/rust-compile-intrinsic.cc
index 450a86979..c733d926f 100644
--- a/gcc/rust/backend/rust-compile-intrinsic.cc
+++ b/gcc/rust/backend/rust-compile-intrinsic.cc
@@ -16,236 +16,54 @@
 
 #include "rust-compile-intrinsic.h"
 #include "rust-compile-context.h"
-#include "rust-compile-type.h"
-#include "rust-compile-fnparam.h"
 #include "rust-builtins.h"
 #include "rust-diagnostics.h"
-#include "rust-location.h"
-#include "rust-constexpr.h"
-#include "rust-session-manager.h"
-#include "rust-tree.h"
 #include "tree-core.h"
-#include "rust-gcc.h"
-#include "fold-const.h"
-#include "langhooks.h"
-#include "rust-constexpr.h"
-
-// declaration taken from "stringpool.h"
-// the get_identifier macro causes compilation issues
-extern tree get_identifier (const char *);
+#include "rust-intrinsic-handlers.h"
 
 namespace Rust {
 namespace Compile {
 
-static bool
-is_basic_integer_type (TyTy::BaseType *type)
-{
-  switch (type->get_kind ())
-    {
-    case TyTy::INT:
-    case TyTy::UINT:
-    case TyTy::USIZE:
-    case TyTy::ISIZE:
-      return true;
-    default:
-      return false;
-      break;
-    }
-}
-
-static bool
-check_for_basic_integer_type (const std::string &intrinsic_str,
-                             location_t locus, TyTy::BaseType *type)
-{
-  auto is_basic_integer = is_basic_integer_type (type);
-  if (!is_basic_integer)
-    {
-      rust_error_at (
-       locus,
-       "%s intrinsics can only be used with basic integer types (got %qs)",
-       intrinsic_str.c_str (), type->get_name ().c_str ());
-    }
-
-  return is_basic_integer;
-}
-
-static tree offset_handler (Context *ctx, TyTy::FnType *fntype);
-static tree sizeof_handler (Context *ctx, TyTy::FnType *fntype);
-static tree transmute_handler (Context *ctx, TyTy::FnType *fntype);
-static tree rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op);
-static tree wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                                      tree_code op);
-static tree op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype,
-                                   tree_code op);
-static tree uninit_handler (Context *ctx, TyTy::FnType *fntype);
-static tree move_val_init_handler (Context *ctx, TyTy::FnType *fntype);
-static tree assume_handler (Context *ctx, TyTy::FnType *fntype);
-static tree discriminant_value_handler (Context *ctx, TyTy::FnType *fntype);
-static tree variant_count_handler (Context *ctx, TyTy::FnType *fntype);
-
-enum class Prefetch
-{
-  Read,
-  Write
-};
-
-static tree prefetch_data_handler (Context *ctx, TyTy::FnType *fntype,
-                                  Prefetch kind);
-
-static inline tree
-rotate_left_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  return rotate_handler (ctx, fntype, LROTATE_EXPR);
-}
-static inline tree
-rotate_right_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  return rotate_handler (ctx, fntype, RROTATE_EXPR);
-}
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-wrapping_op_handler (tree_code op)
-{
-  return [op] (Context *ctx, TyTy::FnType *fntype) {
-    return wrapping_op_handler_inner (ctx, fntype, op);
-  };
-}
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-op_with_overflow (tree_code op)
-{
-  return [op] (Context *ctx, TyTy::FnType *fntype) {
-    return op_with_overflow_inner (ctx, fntype, op);
-  };
-}
-
-static inline tree
-prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
-{
-  return prefetch_data_handler (ctx, fntype, Prefetch::Read);
-}
-static inline tree
-prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
-{
-  return prefetch_data_handler (ctx, fntype, Prefetch::Write);
-}
-
-static tree atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                                       int ordering);
-static tree atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                                      int ordering);
-
-static inline std::function<tree (Context *, TyTy::FnType *)>
-atomic_store_handler (int ordering)
-{
-  return [ordering] (Context *ctx, TyTy::FnType *fntype) {
-    return atomic_store_handler_inner (ctx, fntype, ordering);
-  };
-}
-
-static inline std::function<tree (Context *, TyTy::FnType *)>
-atomic_load_handler (int ordering)
-{
-  return [ordering] (Context *ctx, TyTy::FnType *fntype) {
-    return atomic_load_handler_inner (ctx, fntype, ordering);
-  };
-}
-
-static inline tree unchecked_op_inner (Context *ctx, TyTy::FnType *fntype,
-                                      tree_code op);
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-unchecked_op_handler (tree_code op)
-{
-  return [op] (Context *ctx, TyTy::FnType *fntype) {
-    return unchecked_op_inner (ctx, fntype, op);
-  };
-}
-
-static inline tree copy_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                                      bool overlaps);
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-copy_handler (bool overlaps)
-{
-  return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
-    return copy_handler_inner (ctx, fntype, overlaps);
-  };
-}
-
-static inline tree expect_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                                        bool likely);
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-expect_handler (bool likely)
-{
-  return [likely] (Context *ctx, TyTy::FnType *fntype) {
-    return expect_handler_inner (ctx, fntype, likely);
-  };
-}
-
-static tree try_handler_inner (Context *ctx, TyTy::FnType *fntype,
-                              bool is_new_api);
-
-const static std::function<tree (Context *, TyTy::FnType *)>
-try_handler (bool is_new_api)
-{
-  return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
-    return try_handler_inner (ctx, fntype, is_new_api);
-  };
-}
-
-inline tree
-sorry_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
-                fntype->get_identifier ().c_str ());
-
-  return error_mark_node;
-}
-
-static const std::map<std::string,
-                     std::function<tree (Context *, TyTy::FnType *)>>
-  generic_intrinsics
-  = {{"offset", offset_handler},
-     {"size_of", sizeof_handler},
-     {"transmute", transmute_handler},
-     {"rotate_left", rotate_left_handler},
-     {"rotate_right", rotate_right_handler},
-     {"wrapping_add", wrapping_op_handler (PLUS_EXPR)},
-     {"wrapping_sub", wrapping_op_handler (MINUS_EXPR)},
-     {"wrapping_mul", wrapping_op_handler (MULT_EXPR)},
-     {"add_with_overflow", op_with_overflow (PLUS_EXPR)},
-     {"sub_with_overflow", op_with_overflow (MINUS_EXPR)},
-     {"mul_with_overflow", op_with_overflow (MULT_EXPR)},
-     {"copy", copy_handler (true)},
-     {"copy_nonoverlapping", copy_handler (false)},
-     {"prefetch_read_data", prefetch_read_data},
-     {"prefetch_write_data", prefetch_write_data},
-     {"atomic_store_seqcst", atomic_store_handler (__ATOMIC_SEQ_CST)},
-     {"atomic_store_release", atomic_store_handler (__ATOMIC_RELEASE)},
-     {"atomic_store_relaxed", atomic_store_handler (__ATOMIC_RELAXED)},
-     {"atomic_store_unordered", atomic_store_handler (__ATOMIC_RELAXED)},
-     {"atomic_load_seqcst", atomic_load_handler (__ATOMIC_SEQ_CST)},
-     {"atomic_load_acquire", atomic_load_handler (__ATOMIC_ACQUIRE)},
-     {"atomic_load_relaxed", atomic_load_handler (__ATOMIC_RELAXED)},
-     {"atomic_load_unordered", atomic_load_handler (__ATOMIC_RELAXED)},
-     {"unchecked_add", unchecked_op_handler (PLUS_EXPR)},
-     {"unchecked_sub", unchecked_op_handler (MINUS_EXPR)},
-     {"unchecked_mul", unchecked_op_handler (MULT_EXPR)},
-     {"unchecked_div", unchecked_op_handler (TRUNC_DIV_EXPR)},
-     {"unchecked_rem", unchecked_op_handler (TRUNC_MOD_EXPR)},
-     {"unchecked_shl", unchecked_op_handler (LSHIFT_EXPR)},
-     {"unchecked_shr", unchecked_op_handler (RSHIFT_EXPR)},
-     {"uninit", uninit_handler},
-     {"move_val_init", move_val_init_handler},
-     {"likely", expect_handler (true)},
-     {"unlikely", expect_handler (false)},
-     {"assume", assume_handler},
-     {"try", try_handler (false)},
-     {"catch_unwind", try_handler (true)},
-     {"discriminant_value", discriminant_value_handler},
-     {"variant_count", variant_count_handler}};
+static const std::map<std::string, handlers::HandlerBuilder> generic_intrinsics
+  = {{"offset", handlers::offset},
+     {"size_of", handlers::sizeof_handler},
+     {"transmute", handlers::transmute},
+     {"rotate_left", handlers::rotate_left},
+     {"rotate_right", handlers::rotate_right},
+     {"wrapping_add", handlers::wrapping_op (PLUS_EXPR)},
+     {"wrapping_sub", handlers::wrapping_op (MINUS_EXPR)},
+     {"wrapping_mul", handlers::wrapping_op (MULT_EXPR)},
+     {"add_with_overflow", handlers::op_with_overflow (PLUS_EXPR)},
+     {"sub_with_overflow", handlers::op_with_overflow (MINUS_EXPR)},
+     {"mul_with_overflow", handlers::op_with_overflow (MULT_EXPR)},
+     {"copy", handlers::copy (true)},
+     {"copy_nonoverlapping", handlers::copy (false)},
+     {"prefetch_read_data", handlers::prefetch_read_data},
+     {"prefetch_write_data", handlers::prefetch_write_data},
+     {"atomic_store_seqcst", handlers::atomic_store (__ATOMIC_SEQ_CST)},
+     {"atomic_store_release", handlers::atomic_store (__ATOMIC_RELEASE)},
+     {"atomic_store_relaxed", handlers::atomic_store (__ATOMIC_RELAXED)},
+     {"atomic_store_unordered", handlers::atomic_store (__ATOMIC_RELAXED)},
+     {"atomic_load_seqcst", handlers::atomic_load (__ATOMIC_SEQ_CST)},
+     {"atomic_load_acquire", handlers::atomic_load (__ATOMIC_ACQUIRE)},
+     {"atomic_load_relaxed", handlers::atomic_load (__ATOMIC_RELAXED)},
+     {"atomic_load_unordered", handlers::atomic_load (__ATOMIC_RELAXED)},
+     {"unchecked_add", handlers::unchecked_op (PLUS_EXPR)},
+     {"unchecked_sub", handlers::unchecked_op (MINUS_EXPR)},
+     {"unchecked_mul", handlers::unchecked_op (MULT_EXPR)},
+     {"unchecked_div", handlers::unchecked_op (TRUNC_DIV_EXPR)},
+     {"unchecked_rem", handlers::unchecked_op (TRUNC_MOD_EXPR)},
+     {"unchecked_shl", handlers::unchecked_op (LSHIFT_EXPR)},
+     {"unchecked_shr", handlers::unchecked_op (RSHIFT_EXPR)},
+     {"uninit", handlers::uninit},
+     {"move_val_init", handlers::move_val_init},
+     {"likely", handlers::expect (true)},
+     {"unlikely", handlers::expect (false)},
+     {"assume", handlers::assume},
+     {"try", handlers::try_handler (false)},
+     {"catch_unwind", handlers::try_handler (true)},
+     {"discriminant_value", handlers::discriminant_value},
+     {"variant_count", handlers::variant_count}};
 
 Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
 
@@ -281,1204 +99,5 @@ Intrinsics::compile (TyTy::FnType *fntype)
   return error_mark_node;
 }
 
-/**
- * Items can be forward compiled which means we may not need to invoke this
- * code. We might also have already compiled this generic function as well.
- */
-static bool
-check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
-{
-  const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
-  std::string asm_name = ctx->mangle_item (fntype, canonical_path);
-  if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
-                                fntype->get_id (), fntype, asm_name))
-    {
-      return true;
-    }
-
-  return false;
-}
-
-/**
- * Maybe override the Hir Lookups for the substituions in this context
- */
-static void
-maybe_override_ctx (TyTy::FnType *fntype)
-{
-  if (fntype->has_substitutions_defined ())
-    fntype->override_context ();
-}
-
-/**
- * Compile and setup a function's parameters
- */
-static void
-compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
-                  std::vector<Bvariable *> *compiled_param_variables,
-                  std::vector<tree_node *> *compiled_param_types = nullptr)
-{
-  for (auto &parm : fntype->get_params ())
-    {
-      auto &referenced_param = parm.get_pattern ();
-      auto param_tyty = parm.get_type ();
-      auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
-
-      location_t param_locus = referenced_param.get_locus ();
-      Bvariable *compiled_param_var
-       = CompileFnParam::compile (ctx, fndecl, referenced_param,
-                                  compiled_param_type, param_locus);
-
-      compiled_param_variables->push_back (compiled_param_var);
-      if (compiled_param_types)
-       compiled_param_types->push_back (compiled_param_type);
-    }
-}
-
-static tree
-compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
-{
-  maybe_override_ctx (fntype);
-
-  const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
-
-  tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
-  std::string ir_symbol_name
-    = canonical_path.get () + fntype->subst_as_string ();
-  std::string asm_name = ctx->mangle_item (fntype, canonical_path);
-
-  unsigned int flags = 0;
-  tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
-                                  flags, fntype->get_ident ().locus);
-
-  TREE_PUBLIC (fndecl) = 0;
-  TREE_READONLY (fndecl) = 1;
-  DECL_ARTIFICIAL (fndecl) = 1;
-  DECL_EXTERNAL (fndecl) = 0;
-  DECL_DECLARED_INLINE_P (fndecl) = 1;
-
-  return fndecl;
-}
-
-static void
-enter_intrinsic_block (Context *ctx, tree fndecl,
-                      const std::vector<Bvariable *> &vars = {})
-{
-  tree enclosing_scope = NULL_TREE;
-  location_t start_location = UNDEF_LOCATION;
-  location_t end_location = UNDEF_LOCATION;
-
-  auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
-                              end_location);
-
-  ctx->push_block (block);
-}
-
-static void
-finalize_intrinsic_block (Context *ctx, tree fndecl)
-{
-  tree bind_tree = ctx->pop_block ();
-
-  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
-
-  DECL_SAVED_TREE (fndecl) = bind_tree;
-
-  ctx->push_function (fndecl);
-
-  DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
-  maybe_save_constexpr_fundef (fndecl);
-}
-
-static tree
-offset_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  // offset intrinsic has two params dst pointer and offset isize
-  rust_assert (fntype->get_params ().size () == 2);
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  auto &dst_param = param_vars.at (0);
-  auto &size_param = param_vars.at (1);
-  rust_assert (param_vars.size () == 2);
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN offset FN BODY BEGIN
-  tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
-  tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
-  tree pointer_offset_expr
-    = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
-  auto return_statement
-    = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN offset FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-sizeof_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  // size_of has _zero_ parameters its parameter is the generic one
-  rust_assert (fntype->get_params ().size () == 0);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // get the template parameter type tree fn size_of<T>();
-  rust_assert (fntype->get_num_substitutions () == 1);
-  auto &param_mapping = fntype->get_substs ().at (0);
-  const auto param_tyty = param_mapping.get_param_ty ();
-  auto resolved_tyty = param_tyty->resolve ();
-  tree template_parameter_type
-    = TyTyResolveCompile::compile (ctx, resolved_tyty);
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN size_of FN BODY BEGIN
-  tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
-  auto return_statement
-    = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN size_of FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-transmute_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  // transmute intrinsic has one parameter
-  rust_assert (fntype->get_params ().size () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  std::vector<Bvariable *> param_vars;
-  std::vector<tree_node *> compiled_types;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars, &compiled_types);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  // param to convert
-  Bvariable *convert_me_param = param_vars.at (0);
-  tree convert_me_expr
-    = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
-
-  // check for transmute pre-conditions
-  tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
-  tree source_type_expr = compiled_types.at (0);
-  tree target_size_expr = TYPE_SIZE (target_type_expr);
-  tree source_size_expr = TYPE_SIZE (source_type_expr);
-  // for some reason, unit types and other zero-sized types return NULL for the
-  // size expressions
-  unsigned HOST_WIDE_INT target_size
-    = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
-  unsigned HOST_WIDE_INT source_size
-    = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
-
-  // size check for concrete types
-  // TODO(liushuyu): check alignment for pointers; check for dependently-sized
-  // types
-  if (target_size != source_size)
-    {
-      rust_error_at (fntype->get_locus (),
-                    "cannot transmute between types of different sizes, or "
-                    "dependently-sized types");
-      rust_inform (
-       fntype->get_ident ().locus, "source type: %qs (%lu bits)",
-       fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
-       (unsigned long) source_size);
-      rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
-                  fntype->get_return_type ()->as_string ().c_str (),
-                  (unsigned long) target_size);
-    }
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN transmute FN BODY BEGIN
-
-  // Return *((orig_type*)&decl)  */
-
-  tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
-  t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
-                      build_pointer_type (target_type_expr), t);
-  tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
-
-  auto return_statement
-    = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN transmute FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-rotate_handler (Context *ctx, TyTy::FnType *fntype, tree_code op)
-{
-  // rotate intrinsic has two parameter
-  rust_assert (fntype->get_params ().size () == 2);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  auto &x_param = param_vars.at (0);
-  auto &y_param = param_vars.at (1);
-  rust_assert (param_vars.size () == 2);
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN rotate FN BODY BEGIN
-  tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
-  tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
-  tree rotate_expr
-    = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
-  auto return_statement
-    = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN rotate FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-/**
- * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
- */
-static tree
-wrapping_op_handler_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
-{
-  // wrapping_<op> intrinsics have two parameter
-  rust_assert (fntype->get_params ().size () == 2);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  auto &lhs_param = param_vars.at (0);
-  auto &rhs_param = param_vars.at (1);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN wrapping_<op> FN BODY BEGIN
-  auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
-  auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
-
-  // Operations are always wrapping in Rust, as we have -fwrapv enabled by
-  // default. The difference between a wrapping_{add, sub, mul} and a regular
-  // arithmetic operation is that these intrinsics do not panic - they always
-  // carry over.
-  auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
-
-  auto return_statement
-    = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN wrapping_<op> FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-/**
- * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
- */
-static tree
-op_with_overflow_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
-{
-  // wrapping_<op> intrinsics have two parameter
-  rust_assert (fntype->get_params ().size () == 2);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  auto &x_param = param_vars.at (0);
-  auto &y_param = param_vars.at (1);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  rust_assert (fntype->get_num_substitutions () == 1);
-  auto &param_mapping = fntype->get_substs ().at (0);
-  const auto param_tyty = param_mapping.get_param_ty ();
-  auto resolved_tyty = param_tyty->resolve ();
-  tree template_parameter_type
-    = TyTyResolveCompile::compile (ctx, resolved_tyty);
-
-  // this should match y as well or we can take it from the TyTy structure
-  tree tmp_stmt = error_mark_node;
-  Bvariable *result_variable
-    = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
-                                  NULL_TREE, true /*address_is_taken*/,
-                                  UNDEF_LOCATION, &tmp_stmt);
-  Bvariable *bool_variable
-    = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
-                                  NULL_TREE, true /*address_is_taken*/,
-                                  UNDEF_LOCATION, &tmp_stmt);
-
-  enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
-
-  // BUILTIN op_with_overflow FN BODY BEGIN
-  auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
-  auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
-
-  tree overflow_builtin = error_mark_node;
-  switch (op)
-    {
-    case PLUS_EXPR:
-      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
-                                                    &overflow_builtin);
-      break;
-
-    case MINUS_EXPR:
-      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
-                                                    &overflow_builtin);
-      break;
-
-    case MULT_EXPR:
-      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
-                                                    &overflow_builtin);
-      break;
-
-    default:
-      rust_unreachable ();
-      break;
-    }
-  rust_assert (overflow_builtin != error_mark_node);
-
-  tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
-  tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
-  tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
-
-  tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
-                                          3, x, y, result_ref);
-
-  tree overflow_assignment
-    = Backend::assignment_statement (bool_decl, builtin_call,
-                                    BUILTINS_LOCATION);
-
-  ctx->add_statement (overflow_assignment);
-
-  std::vector<tree> vals = {result_decl, bool_decl};
-  tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
-  tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
-                                                     -1, UNDEF_LOCATION);
-
-  auto return_statement
-    = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-
-  // BUILTIN wrapping_<op> FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-/**
- * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
- * fn copy<T>(src: *const T, dst: *mut T, count: usize);
- */
-static tree
-copy_handler_inner (Context *ctx, TyTy::FnType *fntype, bool overlaps)
-{
-  rust_assert (fntype->get_params ().size () == 3);
-  rust_assert (fntype->get_num_substitutions () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN copy_nonoverlapping BODY BEGIN
-
-  auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
-  auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
-
-  // We want to create the following statement
-  // memcpy(dst, src, size_of::<T>());
-  // so
-  // memcpy(dst, src, size_expr);
-
-  auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
-  auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
-
-  tree size_expr
-    = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
-
-  tree memcpy_raw = nullptr;
-  BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
-                                                         : "__builtin_memcpy",
-                                                &memcpy_raw);
-  rust_assert (memcpy_raw);
-  auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
-
-  auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
-                                            nullptr, UNDEF_LOCATION);
-
-  ctx->add_statement (copy_call);
-
-  // BUILTIN copy_nonoverlapping BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-make_unsigned_long_tree (unsigned long value)
-{
-  return build_int_cst (integer_type_node, value);
-}
-
-static tree
-prefetch_data_handler (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
-{
-  rust_assert (fntype->get_params ().size () == 2);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // prefetching isn't pure and shouldn't be discarded after GIMPLE
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  std::vector<Bvariable *> args;
-  compile_fn_params (ctx, fntype, fndecl, &args);
-
-  if (!Backend::function_set_parameters (fndecl, args))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
-
-  // The core library technically allows you to pass any i32 value as a
-  // locality, but LLVM will then complain if the value cannot be constant
-  // evaluated. For now, we ignore the locality argument and instead always
-  // pass `3` (the most restrictive value). This allows us to still have
-  // prefetch behavior, just not as granular as expected. In future Rust
-  // versions, we hope that prefetch intrinsics will be split up according to
-  // locality, similarly to atomic intrinsics.
-  // The solution is to try and perform constant folding for the locality
-  // argument, or instead of creating a new function definition, modify the 
call
-  // site directly This has the bad side-effect of creating warnings about
-  // `unused name - locality`, which we hack away here:
-  // TODO: Take care of handling locality properly
-  Backend::var_expression (args[1], UNDEF_LOCATION);
-
-  auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
-
-  auto prefetch_raw = NULL_TREE;
-  auto ok = BuiltinsContext::get ().lookup_simple_builtin 
("__builtin_prefetch",
-                                                          &prefetch_raw);
-  rust_assert (ok);
-  auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
-
-  auto prefetch_call = Backend::call_expression (prefetch,
-                                                {addr, rw_flag,
-                                                 // locality arg
-                                                 make_unsigned_long_tree (3)},
-                                                nullptr, UNDEF_LOCATION);
-
-  TREE_READONLY (prefetch_call) = 0;
-  TREE_SIDE_EFFECTS (prefetch_call) = 1;
-
-  ctx->add_statement (prefetch_call);
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static std::string
-build_atomic_builtin_name (const std::string &prefix, location_t locus,
-                          TyTy::BaseType *operand_type)
-{
-  static const std::map<std::string, std::string> allowed_types = {
-    {"i8", "1"},    {"i16", "2"},   {"i32", "4"},   {"i64", "8"},
-    {"i128", "16"}, {"isize", "8"}, {"u8", "1"},    {"u16", "2"},
-    {"u32", "4"},   {"u64", "8"},   {"u128", "16"}, {"usize", "8"},
-  };
-
-  // TODO: Can we maybe get the generic version (atomic_store_n) to work... 
This
-  // would be so much better
-
-  std::string result = "__" + prefix; //  + "n";
-
-  auto type_name = operand_type->get_name ();
-  if (type_name == "usize" || type_name == "isize")
-    {
-      rust_sorry_at (
-       locus, "atomics are not yet available for size types (usize, isize)");
-      return "";
-    }
-
-  if (type_name.at (0) == 'i')
-    {
-      rust_sorry_at (locus, "atomics are not yet supported for signed "
-                           "integer types (i8, i16, i32, i64, i128)");
-      return "";
-    }
-
-  auto type_size_str = allowed_types.find (type_name);
-
-  if (!check_for_basic_integer_type ("atomic", locus, operand_type))
-    return "";
-
-  result += type_size_str->second;
-
-  return result;
-}
-
-static tree
-atomic_store_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
-{
-  rust_assert (fntype->get_params ().size () == 2);
-  rust_assert (fntype->get_num_substitutions () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // Most intrinsic functions are pure but not the atomic ones
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  std::vector<tree> types;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
-
-  auto ok = Backend::function_set_parameters (fndecl, param_vars);
-  rust_assert (ok);
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  TREE_READONLY (dst) = 0;
-
-  auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
-  auto memorder = make_unsigned_long_tree (ordering);
-
-  auto monomorphized_type
-    = fntype->get_substs ()[0].get_param_ty ()->resolve ();
-
-  auto builtin_name
-    = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
-                                monomorphized_type);
-  if (builtin_name.empty ())
-    return error_mark_node;
-
-  tree atomic_store_raw = nullptr;
-  BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
-                                                &atomic_store_raw);
-  rust_assert (atomic_store_raw);
-
-  auto atomic_store
-    = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
-
-  auto store_call
-    = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
-                               UNDEF_LOCATION);
-  TREE_READONLY (store_call) = 0;
-  TREE_SIDE_EFFECTS (store_call) = 1;
-
-  ctx->add_statement (store_call);
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-atomic_load_handler_inner (Context *ctx, TyTy::FnType *fntype, int ordering)
-{
-  rust_assert (fntype->get_params ().size () == 1);
-  rust_assert (fntype->get_num_substitutions () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // Most intrinsic functions are pure but not the atomic ones
-  // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  std::vector<tree> types;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
-
-  auto ok = Backend::function_set_parameters (fndecl, param_vars);
-  rust_assert (ok);
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  auto memorder = make_unsigned_long_tree (ordering);
-
-  auto monomorphized_type
-    = fntype->get_substs ()[0].get_param_ty ()->resolve ();
-
-  auto builtin_name
-    = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
-                                monomorphized_type);
-  if (builtin_name.empty ())
-    return error_mark_node;
-
-  tree atomic_load_raw = nullptr;
-  BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
-                                                &atomic_load_raw);
-  rust_assert (atomic_load_raw);
-
-  auto atomic_load
-    = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
-
-  auto load_call = Backend::call_expression (atomic_load, {src, memorder},
-                                            nullptr, UNDEF_LOCATION);
-  auto return_statement
-    = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
-
-  TREE_READONLY (load_call) = 0;
-  TREE_SIDE_EFFECTS (load_call) = 1;
-
-  ctx->add_statement (return_statement);
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static inline tree
-unchecked_op_inner (Context *ctx, TyTy::FnType *fntype, tree_code op)
-{
-  rust_assert (fntype->get_params ().size () == 2);
-  rust_assert (fntype->get_num_substitutions () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN unchecked_<op> BODY BEGIN
-
-  auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
-
-  auto *monomorphized_type
-    = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
-
-  check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
-                               monomorphized_type);
-
-  auto expr = build2 (op, TREE_TYPE (x), x, y);
-  auto return_statement
-    = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
-
-  ctx->add_statement (return_statement);
-
-  // BUILTIN unchecked_<op> BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-uninit_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  // uninit has _zero_ parameters its parameter is the generic one
-  rust_assert (fntype->get_params ().size () == 0);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // Most intrinsic functions are pure - not `uninit_handler`
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  // get the template parameter type tree fn uninit<T>();
-  rust_assert (fntype->get_num_substitutions () == 1);
-  auto &param_mapping = fntype->get_substs ().at (0);
-  const auto param_tyty = param_mapping.get_param_ty ();
-  auto resolved_tyty = param_tyty->resolve ();
-  tree template_parameter_type
-    = TyTyResolveCompile::compile (ctx, resolved_tyty);
-
-  // result temporary
-  tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
-  rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
-              == TYPE_SIZE_UNIT (dst_type));
-
-  tree tmp_stmt = error_mark_node;
-  Bvariable *bvar
-    = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
-                                  true /*address_is_taken*/, UNDEF_LOCATION,
-                                  &tmp_stmt);
-
-  enter_intrinsic_block (ctx, fndecl, {bvar});
-
-  // BUILTIN size_of FN BODY BEGIN
-
-  tree memset_builtin = error_mark_node;
-  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
-                                                &memset_builtin);
-  rust_assert (memset_builtin != error_mark_node);
-
-  // call memset with 0x01 and size of the thing see
-  // https://github.com/Rust-GCC/gccrs/issues/1899
-
-  tree dst = bvar->get_tree (BUILTINS_LOCATION);
-  tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
-  tree constant_byte = build_int_cst (integer_type_node, 0x01);
-  tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
-
-  tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
-                                         dst_addr, constant_byte, size_expr);
-  ctx->add_statement (memset_call);
-
-  auto return_statement
-    = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN size_of FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-move_val_init_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  rust_assert (fntype->get_params ().size () == 2);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // Most intrinsic functions are pure - not `move_val_init`
-  TREE_READONLY (fndecl) = 0;
-  TREE_SIDE_EFFECTS (fndecl) = 1;
-
-  // get the template parameter type tree fn size_of<T>();
-  rust_assert (fntype->get_num_substitutions () == 1);
-  auto &param_mapping = fntype->get_substs ().at (0);
-  auto param_tyty = param_mapping.get_param_ty ();
-  auto resolved_tyty = param_tyty->resolve ();
-  tree template_parameter_type
-    = TyTyResolveCompile::compile (ctx, resolved_tyty);
-
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN size_of FN BODY BEGIN
-
-  tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
-  tree size = TYPE_SIZE_UNIT (template_parameter_type);
-
-  tree memcpy_builtin = error_mark_node;
-  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
-                                                &memcpy_builtin);
-  rust_assert (memcpy_builtin != error_mark_node);
-
-  src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
-  tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
-                                         dst, src, size);
-
-  ctx->add_statement (memset_call);
-  // BUILTIN size_of FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static inline tree
-expect_handler_inner (Context *ctx, TyTy::FnType *fntype, bool likely)
-{
-  rust_assert (fntype->get_params ().size () == 1);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN expect_handler_inner FN BODY BEGIN
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-  tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  tree expect_fn_raw = nullptr;
-  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
-                                                &expect_fn_raw);
-  rust_assert (expect_fn_raw);
-  auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
-
-  // we need to convert the expression return type to long to match the 
expected
-  // parameter type of __builtin_expect
-  auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
-  auto expect_value
-    = make_unsigned_long_tree (static_cast<unsigned long> (likely));
-
-  auto expect_call
-    = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
-                               BUILTINS_LOCATION);
-  // the return value also needs to be casted (to bool)
-  auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, 
expect_call);
-  auto return_statement
-    = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
-  ctx->add_statement (return_statement);
-  // BUILTIN expect_handler_inner FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-assume_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  // TODO: make sure this is actually helping the compiler optimize
-
-  rust_assert (fntype->get_params ().size () == 1);
-  rust_assert (fntype->param_at (0).get_type ()->get_kind ()
-              == TyTy::TypeKind::BOOL);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  // TODO: make sure these are necessary
-  TREE_READONLY (fndecl) = 0;
-  DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
-  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
-                                       NULL_TREE, DECL_ATTRIBUTES (fndecl));
-
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN assume FN BODY BEGIN
-
-  tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-
-  tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
-                                                  void_type_node, 1, val);
-  TREE_SIDE_EFFECTS (assume_expr) = 1;
-
-  ctx->add_statement (assume_expr);
-  // BUILTIN assume FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-try_handler_inner (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
-{
-  rust_assert (fntype->get_params ().size () == 3);
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // The following tricks are needed to make sure the try-catch blocks are not
-  // optimized away
-  TREE_READONLY (fndecl) = 0;
-  DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
-  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
-                                       NULL_TREE, DECL_ATTRIBUTES (fndecl));
-
-  // BUILTIN try_handler FN BODY BEGIN
-  // setup the params
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-  tree enclosing_scope = NULL_TREE;
-
-  bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
-                       == CompileOptions::PanicStrategy::Abort;
-  tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-  tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
-  tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
-  tree normal_return_stmt = NULL_TREE;
-  tree error_return_stmt = NULL_TREE;
-  tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
-                                           BUILTINS_LOCATION);
-  tree catch_call = NULL_TREE;
-  tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
-                                  UNDEF_LOCATION);
-
-  if (is_new_api)
-    {
-      auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
-      auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
-                                                      UNDEF_LOCATION);
-      normal_return_stmt
-       = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
-      error_return_stmt
-       = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
-    }
-  else
-    {
-      normal_return_stmt = Backend::return_statement (fndecl, 
integer_zero_node,
-                                                     BUILTINS_LOCATION);
-      error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
-                                                    BUILTINS_LOCATION);
-    }
-  Backend::block_add_statements (try_block,
-                                std::vector<tree>{try_call,
-                                                  normal_return_stmt});
-  if (panic_is_abort)
-    {
-      // skip building the try-catch construct
-      ctx->add_statement (try_block);
-      finalize_intrinsic_block (ctx, fndecl);
-      return fndecl;
-    }
-
-  tree eh_pointer
-    = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
-                      integer_zero_node);
-  catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
-                                        NULL_TREE, BUILTINS_LOCATION);
-
-  tree catch_block = Backend::block (fndecl, enclosing_scope, {},
-                                    UNDEF_LOCATION, UNDEF_LOCATION);
-  Backend::block_add_statements (catch_block,
-                                std::vector<tree>{catch_call,
-                                                  error_return_stmt});
-  // emulate what cc1plus is doing for C++ try-catch
-  tree inner_eh_construct
-    = Backend::exception_handler_statement (catch_call, NULL_TREE,
-                                           error_return_stmt,
-                                           BUILTINS_LOCATION);
-  // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
-  auto eh_construct
-    = Backend::exception_handler_statement (try_block, inner_eh_construct,
-                                           NULL_TREE, BUILTINS_LOCATION);
-  ctx->add_statement (eh_construct);
-  // BUILTIN try_handler FN BODY END
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-discriminant_value_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  rust_assert (fntype->get_params ().size () == 1);
-  rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
-  rust_assert (fntype->has_substitutions ());
-  rust_assert (fntype->get_num_type_params () == 1);
-  auto &mapping = fntype->get_substs ().at (0);
-  auto param_ty = mapping.get_param_ty ();
-  rust_assert (param_ty->can_resolve ());
-  auto resolved = param_ty->resolve ();
-  auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
-
-  TyTy::BaseType *return_type = nullptr;
-  bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
-  rust_assert (ok);
-
-  bool is_adt = resolved->is<TyTy::ADTType> ();
-  bool is_enum = false;
-  if (is_adt)
-    {
-      const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
-      return_type = adt.get_repr_options ().repr;
-      rust_assert (return_type != nullptr);
-      is_enum = adt.is_enum ();
-    }
-
-  p->set_associated_type (return_type->get_ref ());
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN disriminant_value FN BODY BEGIN
-
-  tree result = integer_zero_node;
-  if (is_enum)
-    {
-      tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
-      tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
-      result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
-    }
-
-  auto return_statement
-    = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
-  ctx->add_statement (return_statement);
-
-  // BUILTIN disriminant_value FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
-static tree
-variant_count_handler (Context *ctx, TyTy::FnType *fntype)
-{
-  rust_assert (fntype->get_num_type_params () == 1);
-  auto &mapping = fntype->get_substs ().at (0);
-  auto param_ty = mapping.get_param_ty ();
-  rust_assert (param_ty->can_resolve ());
-  auto resolved = param_ty->resolve ();
-
-  size_t variant_count = 0;
-  bool is_adt = resolved->is<TyTy::ADTType> ();
-  if (is_adt)
-    {
-      const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
-      variant_count = adt.number_of_variants ();
-    }
-
-  tree lookup = NULL_TREE;
-  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
-    return lookup;
-
-  auto fndecl = compile_intrinsic_function (ctx, fntype);
-
-  std::vector<Bvariable *> param_vars;
-  compile_fn_params (ctx, fntype, fndecl, &param_vars);
-
-  if (!Backend::function_set_parameters (fndecl, param_vars))
-    return error_mark_node;
-
-  enter_intrinsic_block (ctx, fndecl);
-
-  // BUILTIN disriminant_value FN BODY BEGIN
-  tree result_decl = DECL_RESULT (fndecl);
-  tree type = TREE_TYPE (result_decl);
-
-  mpz_t ival;
-  mpz_init_set_ui (ival, variant_count);
-  tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
-  mpz_clear (ival);
-
-  auto return_statement
-    = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
-  ctx->add_statement (return_statement);
-
-  // BUILTIN disriminant_value FN BODY END
-
-  finalize_intrinsic_block (ctx, fndecl);
-
-  return fndecl;
-}
-
 } // namespace Compile
 } // namespace Rust
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.cc 
b/gcc/rust/backend/rust-intrinsic-handlers.cc
new file mode 100644
index 000000000..076ab06e0
--- /dev/null
+++ b/gcc/rust/backend/rust-intrinsic-handlers.cc
@@ -0,0 +1,1375 @@
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#include "rust-intrinsic-handlers.h"
+#include "rust-compile-context.h"
+#include "rust-compile-type.h"
+#include "rust-compile-fnparam.h"
+#include "rust-builtins.h"
+#include "rust-diagnostics.h"
+#include "rust-location.h"
+#include "rust-constexpr.h"
+#include "rust-session-manager.h"
+#include "rust-tree.h"
+#include "tree-core.h"
+#include "rust-gcc.h"
+#include "fold-const.h"
+#include "rust-constexpr.h"
+
+// declaration taken from "stringpool.h"
+// the get_identifier macro causes compilation issues
+extern tree get_identifier (const char *);
+
+namespace Rust {
+namespace Compile {
+namespace handlers {
+
+static tree
+make_unsigned_long_tree (unsigned long value)
+{
+  return build_int_cst (integer_type_node, value);
+}
+
+static bool
+is_basic_integer_type (TyTy::BaseType *type)
+{
+  switch (type->get_kind ())
+    {
+    case TyTy::INT:
+    case TyTy::UINT:
+    case TyTy::USIZE:
+    case TyTy::ISIZE:
+      return true;
+    default:
+      return false;
+      break;
+    }
+}
+
+/**
+ * Maybe override the Hir Lookups for the substituions in this context
+ */
+static void
+maybe_override_ctx (TyTy::FnType *fntype)
+{
+  if (fntype->has_substitutions_defined ())
+    fntype->override_context ();
+}
+
+static bool
+check_for_basic_integer_type (const std::string &intrinsic_str,
+                             location_t locus, TyTy::BaseType *type)
+{
+  auto is_basic_integer = is_basic_integer_type (type);
+  if (!is_basic_integer)
+    {
+      rust_error_at (
+       locus,
+       "%s intrinsics can only be used with basic integer types (got %qs)",
+       intrinsic_str.c_str (), type->get_name ().c_str ());
+    }
+
+  return is_basic_integer;
+}
+
+/**
+ * Items can be forward compiled which means we may not need to invoke this
+ * code. We might also have already compiled this generic function as well.
+ */
+static bool
+check_for_cached_intrinsic (Context *ctx, TyTy::FnType *fntype, tree *lookup)
+{
+  const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
+  std::string asm_name = ctx->mangle_item (fntype, canonical_path);
+  if (ctx->lookup_function_decl (fntype->get_ty_ref (), lookup,
+                                fntype->get_id (), fntype, asm_name))
+    {
+      return true;
+    }
+
+  return false;
+}
+
+static tree
+compile_intrinsic_function (Context *ctx, TyTy::FnType *fntype)
+{
+  maybe_override_ctx (fntype);
+
+  const Resolver::CanonicalPath &canonical_path = fntype->get_ident ().path;
+
+  tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
+  std::string ir_symbol_name
+    = canonical_path.get () + fntype->subst_as_string ();
+  std::string asm_name = ctx->mangle_item (fntype, canonical_path);
+
+  unsigned int flags = 0;
+  tree fndecl = Backend::function (compiled_fn_type, ir_symbol_name, asm_name,
+                                  flags, fntype->get_ident ().locus);
+
+  TREE_PUBLIC (fndecl) = 0;
+  TREE_READONLY (fndecl) = 1;
+  DECL_ARTIFICIAL (fndecl) = 1;
+  DECL_EXTERNAL (fndecl) = 0;
+  DECL_DECLARED_INLINE_P (fndecl) = 1;
+
+  return fndecl;
+}
+
+/**
+ * Compile and setup a function's parameters
+ */
+static void
+compile_fn_params (Context *ctx, TyTy::FnType *fntype, tree fndecl,
+                  std::vector<Bvariable *> *compiled_param_variables,
+                  std::vector<tree_node *> *compiled_param_types = nullptr)
+{
+  for (auto &parm : fntype->get_params ())
+    {
+      auto &referenced_param = parm.get_pattern ();
+      auto param_tyty = parm.get_type ();
+      auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
+
+      location_t param_locus = referenced_param.get_locus ();
+      Bvariable *compiled_param_var
+       = CompileFnParam::compile (ctx, fndecl, referenced_param,
+                                  compiled_param_type, param_locus);
+
+      compiled_param_variables->push_back (compiled_param_var);
+      if (compiled_param_types)
+       compiled_param_types->push_back (compiled_param_type);
+    }
+}
+
+static void
+enter_intrinsic_block (Context *ctx, tree fndecl,
+                      const std::vector<Bvariable *> &vars = {})
+{
+  tree enclosing_scope = NULL_TREE;
+  location_t start_location = UNDEF_LOCATION;
+  location_t end_location = UNDEF_LOCATION;
+
+  auto block = Backend::block (fndecl, enclosing_scope, vars, start_location,
+                              end_location);
+
+  ctx->push_block (block);
+}
+
+static void
+finalize_intrinsic_block (Context *ctx, tree fndecl)
+{
+  tree bind_tree = ctx->pop_block ();
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+
+  DECL_SAVED_TREE (fndecl) = bind_tree;
+
+  ctx->push_function (fndecl);
+
+  DECL_DECLARED_CONSTEXPR_P (fndecl) = 1;
+  maybe_save_constexpr_fundef (fndecl);
+}
+
+namespace inner {
+
+static std::string
+build_atomic_builtin_name (const std::string &prefix, location_t locus,
+                          TyTy::BaseType *operand_type)
+{
+  static const std::map<std::string, std::string> allowed_types = {
+    {"i8", "1"},    {"i16", "2"},   {"i32", "4"},   {"i64", "8"},
+    {"i128", "16"}, {"isize", "8"}, {"u8", "1"},    {"u16", "2"},
+    {"u32", "4"},   {"u64", "8"},   {"u128", "16"}, {"usize", "8"},
+  };
+
+  // TODO: Can we maybe get the generic version (atomic_store_n) to work... 
This
+  // would be so much better
+
+  std::string result = "__" + prefix; //  + "n";
+
+  auto type_name = operand_type->get_name ();
+  if (type_name == "usize" || type_name == "isize")
+    {
+      rust_sorry_at (
+       locus, "atomics are not yet available for size types (usize, isize)");
+      return "";
+    }
+
+  if (type_name.at (0) == 'i')
+    {
+      rust_sorry_at (locus, "atomics are not yet supported for signed "
+                           "integer types (i8, i16, i32, i64, i128)");
+      return "";
+    }
+
+  auto type_size_str = allowed_types.find (type_name);
+
+  if (!check_for_basic_integer_type ("atomic", locus, operand_type))
+    return "";
+
+  result += type_size_str->second;
+
+  return result;
+}
+
+inline tree
+unchecked_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  rust_assert (fntype->get_params ().size () == 2);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN unchecked_<op> BODY BEGIN
+
+  auto x = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  auto y = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+
+  auto *monomorphized_type
+    = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
+
+  check_for_basic_integer_type ("unchecked operation", fntype->get_locus (),
+                               monomorphized_type);
+
+  auto expr = build2 (op, TREE_TYPE (x), x, y);
+  auto return_statement
+    = Backend::return_statement (fndecl, expr, UNDEF_LOCATION);
+
+  ctx->add_statement (return_statement);
+
+  // BUILTIN unchecked_<op> BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+inline tree
+expect (Context *ctx, TyTy::FnType *fntype, bool likely)
+{
+  rust_assert (fntype->get_params ().size () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN expect_handler_inner FN BODY BEGIN
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+  tree expr = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  tree expect_fn_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_expect",
+                                                &expect_fn_raw);
+  rust_assert (expect_fn_raw);
+  auto expect_fn = build_fold_addr_expr_loc (BUILTINS_LOCATION, expect_fn_raw);
+
+  // we need to convert the expression return type to long to match the 
expected
+  // parameter type of __builtin_expect
+  auto expect_src = build1 (CONVERT_EXPR, long_integer_type_node, expr);
+  auto expect_value
+    = make_unsigned_long_tree (static_cast<unsigned long> (likely));
+
+  auto expect_call
+    = Backend::call_expression (expect_fn, {expect_src, expect_value}, nullptr,
+                               BUILTINS_LOCATION);
+  // the return value also needs to be casted (to bool)
+  auto expect_call_bool = build1 (CONVERT_EXPR, boolean_type_node, 
expect_call);
+  auto return_statement
+    = Backend::return_statement (fndecl, expect_call_bool, BUILTINS_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN expect_handler_inner FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
+{
+  rust_assert (fntype->get_params ().size () == 3);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // The following tricks are needed to make sure the try-catch blocks are not
+  // optimized away
+  TREE_READONLY (fndecl) = 0;
+  DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
+  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
+                                       NULL_TREE, DECL_ATTRIBUTES (fndecl));
+
+  // BUILTIN try_handler FN BODY BEGIN
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+  tree enclosing_scope = NULL_TREE;
+
+  bool panic_is_abort = Session::get_instance ().options.get_panic_strategy ()
+                       == CompileOptions::PanicStrategy::Abort;
+  tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+  tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
+  tree normal_return_stmt = NULL_TREE;
+  tree error_return_stmt = NULL_TREE;
+  tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr,
+                                           BUILTINS_LOCATION);
+  tree catch_call = NULL_TREE;
+  tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION,
+                                  UNDEF_LOCATION);
+
+  if (is_new_api)
+    {
+      auto ret_type = TyTyResolveCompile::get_unit_type (ctx);
+      auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1,
+                                                      UNDEF_LOCATION);
+      normal_return_stmt
+       = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
+      error_return_stmt
+       = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION);
+    }
+  else
+    {
+      normal_return_stmt = Backend::return_statement (fndecl, 
integer_zero_node,
+                                                     BUILTINS_LOCATION);
+      error_return_stmt = Backend::return_statement (fndecl, integer_one_node,
+                                                    BUILTINS_LOCATION);
+    }
+  Backend::block_add_statements (try_block,
+                                std::vector<tree>{try_call,
+                                                  normal_return_stmt});
+  if (panic_is_abort)
+    {
+      // skip building the try-catch construct
+      ctx->add_statement (try_block);
+      finalize_intrinsic_block (ctx, fndecl);
+      return fndecl;
+    }
+
+  tree eh_pointer
+    = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1,
+                      integer_zero_node);
+  catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer},
+                                        NULL_TREE, BUILTINS_LOCATION);
+
+  tree catch_block = Backend::block (fndecl, enclosing_scope, {},
+                                    UNDEF_LOCATION, UNDEF_LOCATION);
+  Backend::block_add_statements (catch_block,
+                                std::vector<tree>{catch_call,
+                                                  error_return_stmt});
+  // emulate what cc1plus is doing for C++ try-catch
+  tree inner_eh_construct
+    = Backend::exception_handler_statement (catch_call, NULL_TREE,
+                                           error_return_stmt,
+                                           BUILTINS_LOCATION);
+  // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing
+  auto eh_construct
+    = Backend::exception_handler_statement (try_block, inner_eh_construct,
+                                           NULL_TREE, BUILTINS_LOCATION);
+  ctx->add_statement (eh_construct);
+  // BUILTIN try_handler FN BODY END
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+/**
+ * pub fn wrapping_{add, sub, mul}<T>(lhs: T, rhs: T) -> T;
+ */
+tree
+wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  // wrapping_<op> intrinsics have two parameter
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &lhs_param = param_vars.at (0);
+  auto &rhs_param = param_vars.at (1);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN wrapping_<op> FN BODY BEGIN
+  auto lhs = Backend::var_expression (lhs_param, UNDEF_LOCATION);
+  auto rhs = Backend::var_expression (rhs_param, UNDEF_LOCATION);
+
+  // Operations are always wrapping in Rust, as we have -fwrapv enabled by
+  // default. The difference between a wrapping_{add, sub, mul} and a regular
+  // arithmetic operation is that these intrinsics do not panic - they always
+  // carry over.
+  auto wrap_expr = build2 (op, TREE_TYPE (lhs), lhs, rhs);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, wrap_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN wrapping_<op> FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+/**
+ * pub fn add_with_overflow<T>(x: T, y: T) -> (T, bool);
+ */
+tree
+op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  // wrapping_<op> intrinsics have two parameter
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &x_param = param_vars.at (0);
+  auto &y_param = param_vars.at (1);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  rust_assert (fntype->get_num_substitutions () == 1);
+  auto &param_mapping = fntype->get_substs ().at (0);
+  const auto param_tyty = param_mapping.get_param_ty ();
+  auto resolved_tyty = param_tyty->resolve ();
+  tree template_parameter_type
+    = TyTyResolveCompile::compile (ctx, resolved_tyty);
+
+  // this should match y as well or we can take it from the TyTy structure
+  tree tmp_stmt = error_mark_node;
+  Bvariable *result_variable
+    = Backend::temporary_variable (fndecl, NULL_TREE, template_parameter_type,
+                                  NULL_TREE, true /*address_is_taken*/,
+                                  UNDEF_LOCATION, &tmp_stmt);
+  Bvariable *bool_variable
+    = Backend::temporary_variable (fndecl, NULL_TREE, boolean_type_node,
+                                  NULL_TREE, true /*address_is_taken*/,
+                                  UNDEF_LOCATION, &tmp_stmt);
+
+  enter_intrinsic_block (ctx, fndecl, {result_variable, bool_variable});
+
+  // BUILTIN op_with_overflow FN BODY BEGIN
+  auto x = Backend::var_expression (x_param, UNDEF_LOCATION);
+  auto y = Backend::var_expression (y_param, UNDEF_LOCATION);
+
+  tree overflow_builtin = error_mark_node;
+  switch (op)
+    {
+    case PLUS_EXPR:
+      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_add_overflow",
+                                                    &overflow_builtin);
+      break;
+
+    case MINUS_EXPR:
+      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_sub_overflow",
+                                                    &overflow_builtin);
+      break;
+
+    case MULT_EXPR:
+      BuiltinsContext::get ().lookup_simple_builtin ("__builtin_mul_overflow",
+                                                    &overflow_builtin);
+      break;
+
+    default:
+      rust_unreachable ();
+      break;
+    }
+  rust_assert (overflow_builtin != error_mark_node);
+
+  tree bool_decl = bool_variable->get_tree (BUILTINS_LOCATION);
+  tree result_decl = result_variable->get_tree (BUILTINS_LOCATION);
+  tree result_ref = build_fold_addr_expr_loc (BUILTINS_LOCATION, result_decl);
+
+  tree builtin_call = build_call_expr_loc (BUILTINS_LOCATION, overflow_builtin,
+                                          3, x, y, result_ref);
+
+  tree overflow_assignment
+    = Backend::assignment_statement (bool_decl, builtin_call,
+                                    BUILTINS_LOCATION);
+
+  ctx->add_statement (overflow_assignment);
+
+  std::vector<tree> vals = {result_decl, bool_decl};
+  tree tuple_type = TREE_TYPE (DECL_RESULT (fndecl));
+  tree result_expr = Backend::constructor_expression (tuple_type, false, vals,
+                                                     -1, UNDEF_LOCATION);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+
+  // BUILTIN wrapping_<op> FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+/**
+ * fn copy_nonoverlapping<T>(src: *const T, dst: *mut T, count: usize);
+ * fn copy<T>(src: *const T, dst: *mut T, count: usize);
+ */
+tree
+copy (Context *ctx, TyTy::FnType *fntype, bool overlaps)
+{
+  rust_assert (fntype->get_params ().size () == 3);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure - not `copy_nonoverlapping` and `copy`
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN copy_nonoverlapping BODY BEGIN
+
+  auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  auto dst = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+  auto count = Backend::var_expression (param_vars[2], UNDEF_LOCATION);
+
+  // We want to create the following statement
+  // memcpy(dst, src, size_of::<T>());
+  // so
+  // memcpy(dst, src, size_expr);
+
+  auto *resolved_ty = fntype->get_substs ().at (0).get_param_ty ()->resolve ();
+  auto param_type = TyTyResolveCompile::compile (ctx, resolved_ty);
+
+  tree size_expr
+    = build2 (MULT_EXPR, size_type_node, TYPE_SIZE_UNIT (param_type), count);
+
+  tree memcpy_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin (overlaps ? "__builtin_memmove"
+                                                         : "__builtin_memcpy",
+                                                &memcpy_raw);
+  rust_assert (memcpy_raw);
+  auto memcpy = build_fold_addr_expr_loc (UNKNOWN_LOCATION, memcpy_raw);
+
+  auto copy_call = Backend::call_expression (memcpy, {dst, src, size_expr},
+                                            nullptr, UNDEF_LOCATION);
+
+  ctx->add_statement (copy_call);
+
+  // BUILTIN copy_nonoverlapping BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering)
+{
+  rust_assert (fntype->get_params ().size () == 2);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure but not the atomic ones
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  std::vector<tree> types;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
+
+  auto ok = Backend::function_set_parameters (fndecl, param_vars);
+  rust_assert (ok);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  auto dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  TREE_READONLY (dst) = 0;
+
+  auto value = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+  auto memorder = make_unsigned_long_tree (ordering);
+
+  auto monomorphized_type
+    = fntype->get_substs ()[0].get_param_ty ()->resolve ();
+
+  auto builtin_name
+    = build_atomic_builtin_name ("atomic_store_", fntype->get_locus (),
+                                monomorphized_type);
+  if (builtin_name.empty ())
+    return error_mark_node;
+
+  tree atomic_store_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
+                                                &atomic_store_raw);
+  rust_assert (atomic_store_raw);
+
+  auto atomic_store
+    = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_store_raw);
+
+  auto store_call
+    = Backend::call_expression (atomic_store, {dst, value, memorder}, nullptr,
+                               UNDEF_LOCATION);
+  TREE_READONLY (store_call) = 0;
+  TREE_SIDE_EFFECTS (store_call) = 1;
+
+  ctx->add_statement (store_call);
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering)
+{
+  rust_assert (fntype->get_params ().size () == 1);
+  rust_assert (fntype->get_num_substitutions () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure but not the atomic ones
+  // FIXME: Is atomic_load_* pure? Feels like it shouldn't so
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  std::vector<tree> types;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars, &types);
+
+  auto ok = Backend::function_set_parameters (fndecl, param_vars);
+  rust_assert (ok);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  auto src = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  auto memorder = make_unsigned_long_tree (ordering);
+
+  auto monomorphized_type
+    = fntype->get_substs ()[0].get_param_ty ()->resolve ();
+
+  auto builtin_name
+    = build_atomic_builtin_name ("atomic_load_", fntype->get_locus (),
+                                monomorphized_type);
+  if (builtin_name.empty ())
+    return error_mark_node;
+
+  tree atomic_load_raw = nullptr;
+  BuiltinsContext::get ().lookup_simple_builtin (builtin_name,
+                                                &atomic_load_raw);
+  rust_assert (atomic_load_raw);
+
+  auto atomic_load
+    = build_fold_addr_expr_loc (UNKNOWN_LOCATION, atomic_load_raw);
+
+  auto load_call = Backend::call_expression (atomic_load, {src, memorder},
+                                            nullptr, UNDEF_LOCATION);
+  auto return_statement
+    = Backend::return_statement (fndecl, load_call, UNDEF_LOCATION);
+
+  TREE_READONLY (load_call) = 0;
+  TREE_SIDE_EFFECTS (load_call) = 1;
+
+  ctx->add_statement (return_statement);
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+} // namespace inner
+
+const HandlerBuilder
+op_with_overflow (tree_code op)
+{
+  return [op] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::op_with_overflow (ctx, fntype, op);
+  };
+}
+
+tree
+rotate_left (Context *ctx, TyTy::FnType *fntype)
+{
+  return handlers::rotate (ctx, fntype, LROTATE_EXPR);
+}
+
+tree
+rotate_right (Context *ctx, TyTy::FnType *fntype)
+{
+  return handlers::rotate (ctx, fntype, RROTATE_EXPR);
+}
+
+const HandlerBuilder
+wrapping_op (tree_code op)
+{
+  return [op] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::wrapping_op (ctx, fntype, op);
+  };
+}
+
+HandlerBuilder
+atomic_store (int ordering)
+{
+  return [ordering] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::atomic_store (ctx, fntype, ordering);
+  };
+}
+
+HandlerBuilder
+atomic_load (int ordering)
+{
+  return [ordering] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::atomic_load (ctx, fntype, ordering);
+  };
+}
+
+const HandlerBuilder
+unchecked_op (tree_code op)
+{
+  return [op] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::unchecked_op (ctx, fntype, op);
+  };
+}
+
+const HandlerBuilder
+copy (bool overlaps)
+{
+  return [overlaps] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::copy (ctx, fntype, overlaps);
+  };
+}
+
+const HandlerBuilder
+expect (bool likely)
+{
+  return [likely] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::expect (ctx, fntype, likely);
+  };
+}
+
+const HandlerBuilder
+try_handler (bool is_new_api)
+{
+  return [is_new_api] (Context *ctx, TyTy::FnType *fntype) {
+    return inner::try_handler (ctx, fntype, is_new_api);
+  };
+}
+
+tree
+sorry (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_sorry_at (fntype->get_locus (), "intrinsic %qs is not yet implemented",
+                fntype->get_identifier ().c_str ());
+
+  return error_mark_node;
+}
+
+tree
+assume (Context *ctx, TyTy::FnType *fntype)
+{
+  // TODO: make sure this is actually helping the compiler optimize
+
+  rust_assert (fntype->get_params ().size () == 1);
+  rust_assert (fntype->param_at (0).get_type ()->get_kind ()
+              == TyTy::TypeKind::BOOL);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // TODO: make sure these are necessary
+  TREE_READONLY (fndecl) = 0;
+  DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1;
+  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
+                                       NULL_TREE, DECL_ATTRIBUTES (fndecl));
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN assume FN BODY BEGIN
+
+  tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+
+  tree assume_expr = build_call_expr_internal_loc (UNDEF_LOCATION, IFN_ASSUME,
+                                                  void_type_node, 1, val);
+  TREE_SIDE_EFFECTS (assume_expr) = 1;
+
+  ctx->add_statement (assume_expr);
+  // BUILTIN assume FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+discriminant_value (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_params ().size () == 1);
+  rust_assert (fntype->get_return_type ()->is<TyTy::PlaceholderType> ());
+  rust_assert (fntype->has_substitutions ());
+  rust_assert (fntype->get_num_type_params () == 1);
+  auto &mapping = fntype->get_substs ().at (0);
+  auto param_ty = mapping.get_param_ty ();
+  rust_assert (param_ty->can_resolve ());
+  auto resolved = param_ty->resolve ();
+  auto p = static_cast<TyTy::PlaceholderType *> (fntype->get_return_type ());
+
+  TyTy::BaseType *return_type = nullptr;
+  bool ok = ctx->get_tyctx ()->lookup_builtin ("isize", &return_type);
+  rust_assert (ok);
+
+  bool is_adt = resolved->is<TyTy::ADTType> ();
+  bool is_enum = false;
+  if (is_adt)
+    {
+      const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
+      return_type = adt.get_repr_options ().repr;
+      rust_assert (return_type != nullptr);
+      is_enum = adt.is_enum ();
+    }
+
+  p->set_associated_type (return_type->get_ref ());
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN disriminant_value FN BODY BEGIN
+
+  tree result = integer_zero_node;
+  if (is_enum)
+    {
+      tree val = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+      tree deref = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, val);
+      result = Backend::struct_field_expression (deref, 0, UNKNOWN_LOCATION);
+    }
+
+  auto return_statement
+    = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
+  ctx->add_statement (return_statement);
+
+  // BUILTIN disriminant_value FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+variant_count (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_num_type_params () == 1);
+  auto &mapping = fntype->get_substs ().at (0);
+  auto param_ty = mapping.get_param_ty ();
+  rust_assert (param_ty->can_resolve ());
+  auto resolved = param_ty->resolve ();
+
+  size_t variant_count = 0;
+  bool is_adt = resolved->is<TyTy::ADTType> ();
+  if (is_adt)
+    {
+      const auto &adt = *static_cast<TyTy::ADTType *> (resolved);
+      variant_count = adt.number_of_variants ();
+    }
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN disriminant_value FN BODY BEGIN
+  tree result_decl = DECL_RESULT (fndecl);
+  tree type = TREE_TYPE (result_decl);
+
+  mpz_t ival;
+  mpz_init_set_ui (ival, variant_count);
+  tree result = wide_int_to_tree (type, wi::from_mpz (type, ival, true));
+  mpz_clear (ival);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, result, BUILTINS_LOCATION);
+  ctx->add_statement (return_statement);
+
+  // BUILTIN disriminant_value FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+move_val_init (Context *ctx, TyTy::FnType *fntype)
+{
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure - not `move_val_init`
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // get the template parameter type tree fn size_of<T>();
+  rust_assert (fntype->get_num_substitutions () == 1);
+  auto &param_mapping = fntype->get_substs ().at (0);
+  auto param_tyty = param_mapping.get_param_ty ();
+  auto resolved_tyty = param_tyty->resolve ();
+  tree template_parameter_type
+    = TyTyResolveCompile::compile (ctx, resolved_tyty);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN size_of FN BODY BEGIN
+
+  tree dst = Backend::var_expression (param_vars[0], UNDEF_LOCATION);
+  tree src = Backend::var_expression (param_vars[1], UNDEF_LOCATION);
+  tree size = TYPE_SIZE_UNIT (template_parameter_type);
+
+  tree memcpy_builtin = error_mark_node;
+  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memcpy",
+                                                &memcpy_builtin);
+  rust_assert (memcpy_builtin != error_mark_node);
+
+  src = build_fold_addr_expr_loc (BUILTINS_LOCATION, src);
+  tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memcpy_builtin, 3,
+                                         dst, src, size);
+
+  ctx->add_statement (memset_call);
+  // BUILTIN size_of FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+uninit (Context *ctx, TyTy::FnType *fntype)
+{
+  // uninit has _zero_ parameters its parameter is the generic one
+  rust_assert (fntype->get_params ().size () == 0);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // Most intrinsic functions are pure - not `uninit_handler`
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  // get the template parameter type tree fn uninit<T>();
+  rust_assert (fntype->get_num_substitutions () == 1);
+  auto &param_mapping = fntype->get_substs ().at (0);
+  const auto param_tyty = param_mapping.get_param_ty ();
+  auto resolved_tyty = param_tyty->resolve ();
+  tree template_parameter_type
+    = TyTyResolveCompile::compile (ctx, resolved_tyty);
+
+  // result temporary
+  tree dst_type = TREE_TYPE (DECL_RESULT (fndecl));
+  rust_assert (TYPE_SIZE_UNIT (template_parameter_type)
+              == TYPE_SIZE_UNIT (dst_type));
+
+  tree tmp_stmt = error_mark_node;
+  Bvariable *bvar
+    = Backend::temporary_variable (fndecl, NULL_TREE, dst_type, NULL_TREE,
+                                  true /*address_is_taken*/, UNDEF_LOCATION,
+                                  &tmp_stmt);
+
+  enter_intrinsic_block (ctx, fndecl, {bvar});
+
+  // BUILTIN size_of FN BODY BEGIN
+
+  tree memset_builtin = error_mark_node;
+  BuiltinsContext::get ().lookup_simple_builtin ("__builtin_memset",
+                                                &memset_builtin);
+  rust_assert (memset_builtin != error_mark_node);
+
+  // call memset with 0x01 and size of the thing see
+  // https://github.com/Rust-GCC/gccrs/issues/1899
+
+  tree dst = bvar->get_tree (BUILTINS_LOCATION);
+  tree dst_addr = build_fold_addr_expr_loc (BUILTINS_LOCATION, dst);
+  tree constant_byte = build_int_cst (integer_type_node, 0x01);
+  tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
+
+  tree memset_call = build_call_expr_loc (BUILTINS_LOCATION, memset_builtin, 3,
+                                         dst_addr, constant_byte, size_expr);
+  ctx->add_statement (memset_call);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, dst, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN size_of FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+prefetch_read_data (Context *ctx, TyTy::FnType *fntype)
+{
+  return prefetch_data (ctx, fntype, Prefetch::Read);
+}
+tree
+prefetch_write_data (Context *ctx, TyTy::FnType *fntype)
+{
+  return prefetch_data (ctx, fntype, Prefetch::Write);
+}
+
+tree
+prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind)
+{
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // prefetching isn't pure and shouldn't be discarded after GIMPLE
+  TREE_READONLY (fndecl) = 0;
+  TREE_SIDE_EFFECTS (fndecl) = 1;
+
+  std::vector<Bvariable *> args;
+  compile_fn_params (ctx, fntype, fndecl, &args);
+
+  if (!Backend::function_set_parameters (fndecl, args))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  auto addr = Backend::var_expression (args[0], UNDEF_LOCATION);
+
+  // The core library technically allows you to pass any i32 value as a
+  // locality, but LLVM will then complain if the value cannot be constant
+  // evaluated. For now, we ignore the locality argument and instead always
+  // pass `3` (the most restrictive value). This allows us to still have
+  // prefetch behavior, just not as granular as expected. In future Rust
+  // versions, we hope that prefetch intrinsics will be split up according to
+  // locality, similarly to atomic intrinsics.
+  // The solution is to try and perform constant folding for the locality
+  // argument, or instead of creating a new function definition, modify the 
call
+  // site directly This has the bad side-effect of creating warnings about
+  // `unused name - locality`, which we hack away here:
+  // TODO: Take care of handling locality properly
+  Backend::var_expression (args[1], UNDEF_LOCATION);
+
+  auto rw_flag = make_unsigned_long_tree (kind == Prefetch::Write ? 1 : 0);
+
+  auto prefetch_raw = NULL_TREE;
+  auto ok = BuiltinsContext::get ().lookup_simple_builtin 
("__builtin_prefetch",
+                                                          &prefetch_raw);
+  rust_assert (ok);
+  auto prefetch = build_fold_addr_expr_loc (UNKNOWN_LOCATION, prefetch_raw);
+
+  auto prefetch_call = Backend::call_expression (prefetch,
+                                                {addr, rw_flag,
+                                                 // locality arg
+                                                 make_unsigned_long_tree (3)},
+                                                nullptr, UNDEF_LOCATION);
+
+  TREE_READONLY (prefetch_call) = 0;
+  TREE_SIDE_EFFECTS (prefetch_call) = 1;
+
+  ctx->add_statement (prefetch_call);
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+rotate (Context *ctx, TyTy::FnType *fntype, tree_code op)
+{
+  // rotate intrinsic has two parameter
+  rust_assert (fntype->get_params ().size () == 2);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // setup the params
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &x_param = param_vars.at (0);
+  auto &y_param = param_vars.at (1);
+  rust_assert (param_vars.size () == 2);
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN rotate FN BODY BEGIN
+  tree x = Backend::var_expression (x_param, UNDEF_LOCATION);
+  tree y = Backend::var_expression (y_param, UNDEF_LOCATION);
+  tree rotate_expr
+    = fold_build2_loc (BUILTINS_LOCATION, op, TREE_TYPE (x), x, y);
+  auto return_statement
+    = Backend::return_statement (fndecl, rotate_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN rotate FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+transmute (Context *ctx, TyTy::FnType *fntype)
+{
+  // transmute intrinsic has one parameter
+  rust_assert (fntype->get_params ().size () == 1);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  std::vector<tree_node *> compiled_types;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars, &compiled_types);
+
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  // param to convert
+  Bvariable *convert_me_param = param_vars.at (0);
+  tree convert_me_expr
+    = Backend::var_expression (convert_me_param, UNDEF_LOCATION);
+
+  // check for transmute pre-conditions
+  tree target_type_expr = TREE_TYPE (DECL_RESULT (fndecl));
+  tree source_type_expr = compiled_types.at (0);
+  tree target_size_expr = TYPE_SIZE (target_type_expr);
+  tree source_size_expr = TYPE_SIZE (source_type_expr);
+  // for some reason, unit types and other zero-sized types return NULL for the
+  // size expressions
+  unsigned HOST_WIDE_INT target_size
+    = target_size_expr ? TREE_INT_CST_LOW (target_size_expr) : 0;
+  unsigned HOST_WIDE_INT source_size
+    = source_size_expr ? TREE_INT_CST_LOW (source_size_expr) : 0;
+
+  // size check for concrete types
+  // TODO(liushuyu): check alignment for pointers; check for dependently-sized
+  // types
+  if (target_size != source_size)
+    {
+      rust_error_at (fntype->get_locus (),
+                    "cannot transmute between types of different sizes, or "
+                    "dependently-sized types");
+      rust_inform (
+       fntype->get_ident ().locus, "source type: %qs (%lu bits)",
+       fntype->get_params ().at (0).get_type ()->as_string ().c_str (),
+       (unsigned long) source_size);
+      rust_inform (fntype->get_ident ().locus, "target type: %qs (%lu bits)",
+                  fntype->get_return_type ()->as_string ().c_str (),
+                  (unsigned long) target_size);
+    }
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN transmute FN BODY BEGIN
+
+  // Return *((orig_type*)&decl)  */
+
+  tree t = build_fold_addr_expr_loc (UNKNOWN_LOCATION, convert_me_expr);
+  t = fold_build1_loc (UNKNOWN_LOCATION, NOP_EXPR,
+                      build_pointer_type (target_type_expr), t);
+  tree result_expr = build_fold_indirect_ref_loc (UNKNOWN_LOCATION, t);
+
+  auto return_statement
+    = Backend::return_statement (fndecl, result_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN transmute FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+sizeof_handler (Context *ctx, TyTy::FnType *fntype)
+{
+  // size_of has _zero_ parameters its parameter is the generic one
+  rust_assert (fntype->get_params ().size () == 0);
+
+  tree lookup = NULL_TREE;
+  if (check_for_cached_intrinsic (ctx, fntype, &lookup))
+    return lookup;
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  // get the template parameter type tree fn size_of<T>();
+  rust_assert (fntype->get_num_substitutions () == 1);
+  auto &param_mapping = fntype->get_substs ().at (0);
+  const auto param_tyty = param_mapping.get_param_ty ();
+  auto resolved_tyty = param_tyty->resolve ();
+  tree template_parameter_type
+    = TyTyResolveCompile::compile (ctx, resolved_tyty);
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN size_of FN BODY BEGIN
+  tree size_expr = TYPE_SIZE_UNIT (template_parameter_type);
+  auto return_statement
+    = Backend::return_statement (fndecl, size_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN size_of FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+tree
+offset (Context *ctx, TyTy::FnType *fntype)
+{
+  // offset intrinsic has two params dst pointer and offset isize
+  rust_assert (fntype->get_params ().size () == 2);
+
+  auto fndecl = compile_intrinsic_function (ctx, fntype);
+
+  std::vector<Bvariable *> param_vars;
+  compile_fn_params (ctx, fntype, fndecl, &param_vars);
+
+  auto &dst_param = param_vars.at (0);
+  auto &size_param = param_vars.at (1);
+  rust_assert (param_vars.size () == 2);
+  if (!Backend::function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  enter_intrinsic_block (ctx, fndecl);
+
+  // BUILTIN offset FN BODY BEGIN
+  tree dst = Backend::var_expression (dst_param, UNDEF_LOCATION);
+  tree size = Backend::var_expression (size_param, UNDEF_LOCATION);
+  tree pointer_offset_expr
+    = pointer_offset_expression (dst, size, BUILTINS_LOCATION);
+  auto return_statement
+    = Backend::return_statement (fndecl, pointer_offset_expr, UNDEF_LOCATION);
+  ctx->add_statement (return_statement);
+  // BUILTIN offset FN BODY END
+
+  finalize_intrinsic_block (ctx, fndecl);
+
+  return fndecl;
+}
+
+} // namespace handlers
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-intrinsic-handlers.h 
b/gcc/rust/backend/rust-intrinsic-handlers.h
new file mode 100644
index 000000000..1111f34f0
--- /dev/null
+++ b/gcc/rust/backend/rust-intrinsic-handlers.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2026 Free Software Foundation, Inc.
+//
+// This file is part of GCC.
+
+// GCC is free software; you can redistribute it and/or modify it under
+// the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3, or (at your option) any later
+// version.
+
+// GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+// WARRANTY; without even the implied warranty of MERCHANTABILITY or
+// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+// for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with GCC; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef RUST_INTRINSIC_HANDLERS_H
+#define RUST_INTRINSIC_HANDLERS_H
+
+#include "rust-compile-context.h"
+
+namespace Rust {
+namespace Compile {
+
+enum class Prefetch
+{
+  Read,
+  Write
+};
+
+namespace handlers {
+
+namespace inner {
+tree wrapping_op (Context *ctx, TyTy::FnType *fntype, tree_code op);
+
+tree atomic_store (Context *ctx, TyTy::FnType *fntype, int ordering);
+tree atomic_load (Context *ctx, TyTy::FnType *fntype, int ordering);
+inline tree copy (Context *ctx, TyTy::FnType *fntype, bool overlaps);
+inline tree expect (Context *ctx, TyTy::FnType *fntype, bool likely);
+tree try_handler (Context *ctx, TyTy::FnType *fntype, bool is_new_api);
+
+tree op_with_overflow (Context *ctx, TyTy::FnType *fntype, tree_code op);
+
+inline tree unchecked_op (Context *ctx, TyTy::FnType *fntype, tree_code op);
+
+} // namespace inner
+
+using HandlerBuilder = std::function<tree (Context *, TyTy::FnType *)>;
+
+const HandlerBuilder op_with_overflow (tree_code op);
+
+tree rotate_left (Context *ctx, TyTy::FnType *fntype);
+tree rotate_right (Context *ctx, TyTy::FnType *fntype);
+
+const HandlerBuilder wrapping_op (tree_code op);
+tree offset (Context *ctx, TyTy::FnType *fntype);
+tree sizeof_handler (Context *ctx, TyTy::FnType *fntype);
+tree transmute (Context *ctx, TyTy::FnType *fntype);
+tree rotate (Context *ctx, TyTy::FnType *fntype, tree_code op);
+tree uninit (Context *ctx, TyTy::FnType *fntype);
+tree move_val_init (Context *ctx, TyTy::FnType *fntype);
+tree assume (Context *ctx, TyTy::FnType *fntype);
+tree discriminant_value (Context *ctx, TyTy::FnType *fntype);
+tree variant_count (Context *ctx, TyTy::FnType *fntype);
+
+tree prefetch_data (Context *ctx, TyTy::FnType *fntype, Prefetch kind);
+
+const std::function<tree (Context *, TyTy::FnType *)>
+wrapping_op (tree_code op);
+
+HandlerBuilder atomic_store (int ordering);
+
+HandlerBuilder atomic_load (int ordering);
+
+const HandlerBuilder unchecked_op (tree_code op);
+
+const HandlerBuilder copy (bool overlaps);
+
+const HandlerBuilder expect (bool likely);
+
+const HandlerBuilder try_handler (bool is_new_api);
+
+tree prefetch_read_data (Context *ctx, TyTy::FnType *fntype);
+tree prefetch_write_data (Context *ctx, TyTy::FnType *fntype);
+tree sorry (Context *ctx, TyTy::FnType *fntype);
+
+} // namespace handlers
+
+} // namespace Compile
+} // namespace Rust
+
+#endif /* ! RUST_INTRINSIC_HANDLERS_H */

base-commit: 7b2691931b84449cd3221587ec07cec2d17fe956
-- 
2.53.0

Reply via email to