diff --git a/gcc/rust/backend/rust-builtins.h b/gcc/rust/backend/rust-builtins.h
new file mode 100644
index 00000000000..2bfa6c6cdf7
--- /dev/null
+++ b/gcc/rust/backend/rust-builtins.h
@@ -0,0 +1,189 @@
+// 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_BUILTINS_H
+#define RUST_BUILTINS_H
+
+#include "rust-system.h"
+#include "tree.h"
+#include "langhooks.h"
+
+namespace Rust {
+namespace Compile {
+
+// https://github.com/rust-lang/rust/blob/master/library/core/src/intrinsics.rs
+// https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/intrinsic.rs
+// https://github.com/Rust-GCC/gccrs/issues/658
+//
+//   let llvm_name = match name {
+//     sym::sqrtf32 => "llvm.sqrt.f32",
+//     sym::sqrtf64 => "llvm.sqrt.f64",
+//     sym::powif32 => "llvm.powi.f32",
+//     sym::powif64 => "llvm.powi.f64",
+//     sym::sinf32 => "llvm.sin.f32",
+//     sym::sinf64 => "llvm.sin.f64",
+//     sym::cosf32 => "llvm.cos.f32",
+//     sym::cosf64 => "llvm.cos.f64",
+//     sym::powf32 => "llvm.pow.f32",
+//     sym::powf64 => "llvm.pow.f64",
+//     sym::expf32 => "llvm.exp.f32",
+//     sym::expf64 => "llvm.exp.f64",
+//     sym::exp2f32 => "llvm.exp2.f32",
+//     sym::exp2f64 => "llvm.exp2.f64",
+//     sym::logf32 => "llvm.log.f32",
+//     sym::logf64 => "llvm.log.f64",
+//     sym::log10f32 => "llvm.log10.f32",
+//     sym::log10f64 => "llvm.log10.f64",
+//     sym::log2f32 => "llvm.log2.f32",
+//     sym::log2f64 => "llvm.log2.f64",
+//     sym::fmaf32 => "llvm.fma.f32",
+//     sym::fmaf64 => "llvm.fma.f64",
+//     sym::fabsf32 => "llvm.fabs.f32",
+//     sym::fabsf64 => "llvm.fabs.f64",
+//     sym::minnumf32 => "llvm.minnum.f32",
+//     sym::minnumf64 => "llvm.minnum.f64",
+//     sym::maxnumf32 => "llvm.maxnum.f32",
+//     sym::maxnumf64 => "llvm.maxnum.f64",
+//     sym::copysignf32 => "llvm.copysign.f32",
+//     sym::copysignf64 => "llvm.copysign.f64",
+//     sym::floorf32 => "llvm.floor.f32",
+//     sym::floorf64 => "llvm.floor.f64",
+//     sym::ceilf32 => "llvm.ceil.f32",
+//     sym::ceilf64 => "llvm.ceil.f64",
+//     sym::truncf32 => "llvm.trunc.f32",
+//     sym::truncf64 => "llvm.trunc.f64",
+//     sym::rintf32 => "llvm.rint.f32",
+//     sym::rintf64 => "llvm.rint.f64",
+//     sym::nearbyintf32 => "llvm.nearbyint.f32",
+//     sym::nearbyintf64 => "llvm.nearbyint.f64",
+//     sym::roundf32 => "llvm.round.f32",
+//     sym::roundf64 => "llvm.round.f64",
+//     _ => return None,
+// };
+// Some(cx.get_intrinsic(&llvm_name))
+class BuiltinsContext
+{
+public:
+  static BuiltinsContext &get ()
+  {
+    static BuiltinsContext instance;
+    return instance;
+  }
+
+  bool lookup_simple_builtin (const std::string &name, tree *builtin)
+  {
+    auto it = rust_intrinsic_to_gcc_builtin.find (name);
+    if (it == rust_intrinsic_to_gcc_builtin.end ())
+      return false;
+
+    return lookup_gcc_builtin (it->second, builtin);
+  }
+
+private:
+  static const int builtin_const = 1 << 0;
+  static const int builtin_noreturn = 1 << 1;
+  static const int builtin_novops = 1 << 2;
+
+  BuiltinsContext () { setup (); }
+
+  void setup ()
+  {
+    tree math_function_type_f32
+      = build_function_type_list (float_type_node, float_type_node, NULL_TREE);
+
+    define_builtin ("sinf32", BUILT_IN_SINF, "__builtin_sinf", "sinf",
+		    math_function_type_f32, builtin_const);
+
+    define_builtin ("sqrtf32", BUILT_IN_SQRTF, "__builtin_sqrtf", "sqrtf",
+		    math_function_type_f32, builtin_const);
+
+    define_builtin ("unreachable", BUILT_IN_UNREACHABLE,
+		    "__builtin_unreachable", NULL,
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin ("abort", BUILT_IN_ABORT, "__builtin_abort", "abort",
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin ("breakpoint", BUILT_IN_TRAP, "__builtin_trap", "breakpoint",
+		    build_function_type (void_type_node, void_list_node),
+		    builtin_const | builtin_noreturn);
+
+    define_builtin (
+      "memcpy", BUILT_IN_MEMCPY, "__builtin_memcpy", "memcpy",
+      build_function_type_list (build_pointer_type (void_type_node),
+				build_pointer_type (void_type_node),
+				build_pointer_type (void_type_node),
+				size_type_node, NULL_TREE),
+      0);
+  }
+
+  // Define a builtin function.  BCODE is the builtin function code
+  // defined by builtins.def.  NAME is the name of the builtin function.
+  // LIBNAME is the name of the corresponding library function, and is
+  // NULL if there isn't one.  FNTYPE is the type of the function.
+  // CONST_P is true if the function has the const attribute.
+  // NORETURN_P is true if the function has the noreturn attribute.
+  void define_builtin (const std::string rust_name, built_in_function bcode,
+		       const char *name, const char *libname, tree fntype,
+		       int flags)
+  {
+    tree decl = add_builtin_function (name, fntype, bcode, BUILT_IN_NORMAL,
+				      libname, NULL_TREE);
+    if ((flags & builtin_const) != 0)
+      TREE_READONLY (decl) = 1;
+    if ((flags & builtin_noreturn) != 0)
+      TREE_THIS_VOLATILE (decl) = 1;
+    if ((flags & builtin_novops) != 0)
+      DECL_IS_NOVOPS (decl) = 1;
+    set_builtin_decl (bcode, decl, true);
+    this->builtin_functions_[name] = decl;
+    if (libname != NULL)
+      {
+	decl = add_builtin_function (libname, fntype, bcode, BUILT_IN_NORMAL,
+				     NULL, NULL_TREE);
+	if ((flags & builtin_const) != 0)
+	  TREE_READONLY (decl) = 1;
+	if ((flags & builtin_noreturn) != 0)
+	  TREE_THIS_VOLATILE (decl) = 1;
+	if ((flags & builtin_novops) != 0)
+	  DECL_IS_NOVOPS (decl) = 1;
+	this->builtin_functions_[libname] = decl;
+      }
+
+    rust_intrinsic_to_gcc_builtin[rust_name] = name;
+  }
+
+  bool lookup_gcc_builtin (const std::string &name, tree *builtin)
+  {
+    auto it = builtin_functions_.find (name);
+    if (it == builtin_functions_.end ())
+      return false;
+
+    *builtin = it->second;
+    return true;
+  }
+
+  // A mapping of the GCC built-ins exposed to GCC Rust.
+  std::map<std::string, tree> builtin_functions_;
+  std::map<std::string, std::string> rust_intrinsic_to_gcc_builtin;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_BUILTINS_H
diff --git a/gcc/rust/backend/rust-compile-base.cc b/gcc/rust/backend/rust-compile-base.cc
new file mode 100644
index 00000000000..2b5c850872f
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-base.cc
@@ -0,0 +1,730 @@
+// Copyright (C) 2020-2022 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-compile-base.h"
+#include "rust-abi.h"
+#include "rust-compile-item.h"
+#include "rust-compile-stmt.h"
+#include "rust-compile-expr.h"
+#include "rust-compile-fnparam.h"
+#include "rust-compile-var-decl.h"
+#include "rust-constexpr.h"
+#include "rust-diagnostics.h"
+#include "rust-expr.h"	// for AST::AttrInputLiteral
+#include "rust-macro.h" // for AST::MetaNameValueStr
+
+#include "fold-const.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "tree.h"
+
+namespace Rust {
+namespace Compile {
+
+bool inline should_mangle_item (const tree fndecl)
+{
+  return lookup_attribute ("no_mangle", DECL_ATTRIBUTES (fndecl)) == NULL_TREE;
+}
+
+void
+HIRCompileBase::setup_fndecl (tree fndecl, bool is_main_entry_point,
+			      bool is_generic_fn, HIR::Visibility &visibility,
+			      const HIR::FunctionQualifiers &qualifiers,
+			      const AST::AttrVec &attrs)
+{
+  // if its the main fn or pub visibility mark its as DECL_PUBLIC
+  // please see https://github.com/Rust-GCC/gccrs/pull/137
+  bool is_pub = visibility.get_vis_type () == HIR::Visibility::VisType::PUBLIC;
+  if (is_main_entry_point || (is_pub && !is_generic_fn))
+    {
+      TREE_PUBLIC (fndecl) = 1;
+    }
+
+  // is it a const fn
+  if (qualifiers.is_const ())
+    {
+      TREE_READONLY (fndecl) = 1;
+    }
+
+  // is it inline?
+  for (const auto &attr : attrs)
+    {
+      bool is_inline = attr.get_path ().as_string ().compare ("inline") == 0;
+      bool is_must_use
+	= attr.get_path ().as_string ().compare ("must_use") == 0;
+      bool is_cold = attr.get_path ().as_string ().compare ("cold") == 0;
+      bool is_link_section
+	= attr.get_path ().as_string ().compare ("link_section") == 0;
+      bool no_mangle = attr.get_path ().as_string ().compare ("no_mangle") == 0;
+      bool is_deprecated
+	= attr.get_path ().as_string ().compare ("deprecated") == 0;
+
+      if (is_inline)
+	{
+	  handle_inline_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_must_use)
+	{
+	  handle_must_use_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_cold)
+	{
+	  handle_cold_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_link_section)
+	{
+	  handle_link_section_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (is_deprecated)
+	{
+	  handle_deprecated_attribute_on_fndecl (fndecl, attr);
+	}
+      else if (no_mangle)
+	{
+	  handle_no_mangle_attribute_on_fndecl (fndecl, attr);
+	}
+    }
+}
+
+void
+HIRCompileBase::handle_cold_attribute_on_fndecl (tree fndecl,
+						 const AST::Attribute &attr)
+{
+  // simple #[cold]
+  if (!attr.has_attr_input ())
+    {
+      tree cold = get_identifier ("cold");
+      // this will get handled by the GCC backend later
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (cold, NULL_TREE, DECL_ATTRIBUTES (fndecl));
+      return;
+    }
+
+  rust_error_at (attr.get_locus (),
+		 "attribute %<cold%> does not accept any arguments");
+}
+
+void
+HIRCompileBase::handle_link_section_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  if (!attr.has_attr_input ())
+    {
+      rust_error_at (attr.get_locus (),
+		     "%<link_section%> expects exactly one argment");
+      return;
+    }
+
+  rust_assert (attr.get_attr_input ().get_attr_input_type ()
+	       == AST::AttrInput::AttrInputType::LITERAL);
+
+  auto &literal = static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+  const auto &msg_str = literal.get_literal ().as_string ();
+
+  if (decl_section_name (fndecl))
+    {
+      rust_warning_at (attr.get_locus (), 0, "section name redefined");
+    }
+
+  set_decl_section_name (fndecl, msg_str.c_str ());
+}
+
+void
+HIRCompileBase::handle_no_mangle_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  if (attr.has_attr_input ())
+    {
+      rust_error_at (attr.get_locus (),
+		     "attribute %<no_mangle%> does not accept any arguments");
+      return;
+    }
+
+  DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("no_mangle"), NULL_TREE,
+					DECL_ATTRIBUTES (fndecl));
+}
+
+void
+HIRCompileBase::handle_deprecated_attribute_on_fndecl (
+  tree fndecl, const AST::Attribute &attr)
+{
+  tree value = NULL_TREE;
+  TREE_DEPRECATED (fndecl) = 1;
+
+  // simple #[deprecated]
+  if (!attr.has_attr_input ())
+    return;
+
+  const AST::AttrInput &input = attr.get_attr_input ();
+  auto input_type = input.get_attr_input_type ();
+
+  if (input_type == AST::AttrInput::AttrInputType::LITERAL)
+    {
+      // handle #[deprecated = "message"]
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      value = build_string (msg_str.size (), msg_str.c_str ());
+    }
+  else if (input_type == AST::AttrInput::AttrInputType::TOKEN_TREE)
+    {
+      // handle #[deprecated(since = "...", note = "...")]
+      const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+      AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
+      for (const auto &item : meta_item->get_items ())
+	{
+	  auto converted_item = item->to_meta_name_value_str ();
+	  if (!converted_item)
+	    continue;
+	  auto key_value = converted_item->get_name_value_pair ();
+	  if (key_value.first.compare ("since") == 0)
+	    {
+	      // valid, but this is handled by Cargo and some third-party audit
+	      // tools
+	      continue;
+	    }
+	  else if (key_value.first.compare ("note") == 0)
+	    {
+	      const auto &msg_str = key_value.second;
+	      if (value)
+		rust_error_at (attr.get_locus (), "multiple %<note%> items");
+	      value = build_string (msg_str.size (), msg_str.c_str ());
+	    }
+	  else
+	    {
+	      rust_error_at (attr.get_locus (), "unknown meta item %qs",
+			     key_value.first.c_str ());
+	    }
+	}
+    }
+
+  if (value)
+    {
+      tree attr_list = build_tree_list (NULL_TREE, value);
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("deprecated"), attr_list,
+		     DECL_ATTRIBUTES (fndecl));
+    }
+}
+
+void
+HIRCompileBase::handle_inline_attribute_on_fndecl (tree fndecl,
+						   const AST::Attribute &attr)
+{
+  // simple #[inline]
+  if (!attr.has_attr_input ())
+    {
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      return;
+    }
+
+  const AST::AttrInput &input = attr.get_attr_input ();
+  bool is_token_tree
+    = input.get_attr_input_type () == AST::AttrInput::AttrInputType::TOKEN_TREE;
+  rust_assert (is_token_tree);
+  const auto &option = static_cast<const AST::DelimTokenTree &> (input);
+  AST::AttrInputMetaItemContainer *meta_item = option.parse_to_meta_item ();
+  if (meta_item->get_items ().size () != 1)
+    {
+      rust_error_at (attr.get_locus (), "invalid number of arguments");
+      return;
+    }
+
+  const std::string inline_option
+    = meta_item->get_items ().at (0)->as_string ();
+
+  // we only care about NEVER and ALWAYS else its an error
+  bool is_always = inline_option.compare ("always") == 0;
+  bool is_never = inline_option.compare ("never") == 0;
+
+  // #[inline(never)]
+  if (is_never)
+    {
+      DECL_UNINLINABLE (fndecl) = 1;
+    }
+  // #[inline(always)]
+  else if (is_always)
+    {
+      DECL_DECLARED_INLINE_P (fndecl) = 1;
+      DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"),
+					    NULL, DECL_ATTRIBUTES (fndecl));
+    }
+  else
+    {
+      rust_error_at (attr.get_locus (), "unknown inline option");
+    }
+}
+
+void
+HIRCompileBase::handle_must_use_attribute_on_fndecl (tree fndecl,
+						     const AST::Attribute &attr)
+{
+  tree nodiscard = get_identifier ("nodiscard");
+  tree value = NULL_TREE;
+
+  if (attr.has_attr_input ())
+    {
+      rust_assert (attr.get_attr_input ().get_attr_input_type ()
+		   == AST::AttrInput::AttrInputType::LITERAL);
+
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      tree message = build_string (msg_str.size (), msg_str.c_str ());
+
+      value = tree_cons (nodiscard, message, NULL_TREE);
+    }
+
+  DECL_ATTRIBUTES (fndecl)
+    = tree_cons (nodiscard, value, DECL_ATTRIBUTES (fndecl));
+}
+
+void
+HIRCompileBase::setup_abi_options (tree fndecl, ABI abi)
+{
+  tree abi_tree = NULL_TREE;
+
+  switch (abi)
+    {
+    case Rust::ABI::RUST:
+    case Rust::ABI::INTRINSIC:
+    case Rust::ABI::C:
+    case Rust::ABI::CDECL:
+      // `decl_attributes` function (not the macro) has the side-effect of
+      // actually switching the codegen backend to use the ABI we annotated.
+      // However, since `cdecl` is the default ABI GCC will be using, explicitly
+      // specifying that ABI will cause GCC to emit a warning saying the
+      // attribute is useless (which is confusing to the user as the attribute
+      // is added by us).
+      DECL_ATTRIBUTES (fndecl)
+	= tree_cons (get_identifier ("cdecl"), NULL, DECL_ATTRIBUTES (fndecl));
+
+      return;
+
+    case Rust::ABI::STDCALL:
+      abi_tree = get_identifier ("stdcall");
+
+      break;
+
+    case Rust::ABI::FASTCALL:
+      abi_tree = get_identifier ("fastcall");
+
+      break;
+
+    case Rust::ABI::SYSV64:
+      abi_tree = get_identifier ("sysv_abi");
+
+      break;
+
+    case Rust::ABI::WIN64:
+      abi_tree = get_identifier ("ms_abi");
+
+      break;
+
+    default:
+      break;
+    }
+
+  decl_attributes (&fndecl, build_tree_list (abi_tree, NULL_TREE), 0);
+}
+
+// ported from gcc/c/c-typecheck.c
+//
+// Mark EXP saying that we need to be able to take the
+// address of it; it should not be allocated in a register.
+// Returns true if successful.  ARRAY_REF_P is true if this
+// is for ARRAY_REF construction - in that case we don't want
+// to look through VIEW_CONVERT_EXPR from VECTOR_TYPE to ARRAY_TYPE,
+// it is fine to use ARRAY_REFs for vector subscripts on vector
+// register variables.
+bool
+HIRCompileBase::mark_addressable (tree exp, Location locus)
+{
+  tree x = exp;
+
+  while (1)
+    switch (TREE_CODE (x))
+      {
+      case VIEW_CONVERT_EXPR:
+	if (TREE_CODE (TREE_TYPE (x)) == ARRAY_TYPE
+	    && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (x, 0))))
+	  return true;
+	x = TREE_OPERAND (x, 0);
+	break;
+
+      case COMPONENT_REF:
+	// TODO
+	// if (DECL_C_BIT_FIELD (TREE_OPERAND (x, 1)))
+	//   {
+	//     error ("cannot take address of bit-field %qD", TREE_OPERAND (x,
+	//     1)); return false;
+	//   }
+
+	/* FALLTHRU */
+      case ADDR_EXPR:
+      case ARRAY_REF:
+      case REALPART_EXPR:
+      case IMAGPART_EXPR:
+	x = TREE_OPERAND (x, 0);
+	break;
+
+      case COMPOUND_LITERAL_EXPR:
+	TREE_ADDRESSABLE (x) = 1;
+	TREE_ADDRESSABLE (COMPOUND_LITERAL_EXPR_DECL (x)) = 1;
+	return true;
+
+      case CONSTRUCTOR:
+	TREE_ADDRESSABLE (x) = 1;
+	return true;
+
+      case VAR_DECL:
+      case CONST_DECL:
+      case PARM_DECL:
+      case RESULT_DECL:
+	// (we don't have a concept of a "register" declaration)
+	// fallthrough */
+
+	/* FALLTHRU */
+      case FUNCTION_DECL:
+	TREE_ADDRESSABLE (x) = 1;
+
+	/* FALLTHRU */
+      default:
+	return true;
+      }
+
+  return false;
+}
+
+tree
+HIRCompileBase::address_expression (tree expr, Location location)
+{
+  if (expr == error_mark_node)
+    return error_mark_node;
+
+  if (!mark_addressable (expr, location))
+    return error_mark_node;
+
+  return build_fold_addr_expr_loc (location.gcc_location (), expr);
+}
+
+tree
+HIRCompileBase::indirect_expression (tree expr, Location locus)
+{
+  if (expr == error_mark_node)
+    return error_mark_node;
+
+  return build_fold_indirect_ref_loc (locus.gcc_location (), expr);
+}
+
+std::vector<Bvariable *>
+HIRCompileBase::compile_locals_for_block (Context *ctx, Resolver::Rib &rib,
+					  tree fndecl)
+{
+  std::vector<Bvariable *> locals;
+  for (auto it : rib.get_declarations ())
+    {
+      NodeId node_id = it.first;
+      HirId ref = UNKNOWN_HIRID;
+      if (!ctx->get_mappings ()->lookup_node_to_hir (node_id, &ref))
+	continue;
+
+      // we only care about local patterns
+      HIR::Pattern *pattern = ctx->get_mappings ()->lookup_hir_pattern (ref);
+      if (pattern == nullptr)
+	continue;
+
+      // lookup the type
+      TyTy::BaseType *tyty = nullptr;
+      if (!ctx->get_tyctx ()->lookup_type (ref, &tyty))
+	continue;
+
+      // compile the local
+      tree type = TyTyResolveCompile::compile (ctx, tyty);
+      Bvariable *compiled
+	= CompileVarDecl::compile (fndecl, type, pattern, ctx);
+      locals.push_back (compiled);
+    }
+  return locals;
+}
+
+void
+HIRCompileBase::compile_function_body (Context *ctx, tree fndecl,
+				       HIR::BlockExpr &function_body,
+				       bool has_return_type)
+{
+  for (auto &s : function_body.get_statements ())
+    {
+      auto compiled_expr = CompileStmt::Compile (s.get (), ctx);
+      if (compiled_expr != nullptr)
+	{
+	  tree s = convert_to_void (compiled_expr, ICV_STATEMENT);
+	  ctx->add_statement (s);
+	}
+    }
+
+  if (function_body.has_expr ())
+    {
+      // the previous passes will ensure this is a valid return
+      // or a valid trailing expression
+      tree compiled_expr
+	= CompileExpr::Compile (function_body.expr.get (), ctx);
+
+      if (compiled_expr != nullptr)
+	{
+	  if (has_return_type)
+	    {
+	      std::vector<tree> retstmts;
+	      retstmts.push_back (compiled_expr);
+
+	      auto ret = ctx->get_backend ()->return_statement (
+		fndecl, retstmts,
+		function_body.get_final_expr ()->get_locus ());
+	      ctx->add_statement (ret);
+	    }
+	  else
+	    {
+	      // FIXME can this actually happen?
+	      ctx->add_statement (compiled_expr);
+	    }
+	}
+    }
+}
+
+tree
+HIRCompileBase::compile_function (
+  Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
+  std::vector<HIR::FunctionParam> &function_params,
+  const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
+  AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
+  const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
+  bool function_has_return)
+{
+  tree compiled_fn_type = TyTyResolveCompile::compile (ctx, fntype);
+  std::string ir_symbol_name
+    = canonical_path->get () + fntype->subst_as_string ();
+
+  // we don't mangle the main fn since we haven't implemented the main shim
+  bool is_main_fn = fn_name.compare ("main") == 0;
+  std::string asm_name = fn_name;
+
+  unsigned int flags = 0;
+  tree fndecl = ctx->get_backend ()->function (compiled_fn_type, ir_symbol_name,
+					       "" /* asm_name */, flags, locus);
+
+  setup_fndecl (fndecl, is_main_fn, fntype->has_subsititions_defined (),
+		visibility, qualifiers, outer_attrs);
+  setup_abi_options (fndecl, qualifiers.get_abi ());
+
+  // conditionally mangle the function name
+  bool should_mangle = should_mangle_item (fndecl);
+  if (!is_main_fn && should_mangle)
+    asm_name = ctx->mangle_item (fntype, *canonical_path);
+  SET_DECL_ASSEMBLER_NAME (fndecl,
+			   get_identifier_with_length (asm_name.data (),
+						       asm_name.length ()));
+
+  // insert into the context
+  ctx->insert_function_decl (fntype, fndecl);
+
+  // setup the params
+  TyTy::BaseType *tyret = fntype->get_return_type ();
+  std::vector<Bvariable *> param_vars;
+  if (!self_param.is_error ())
+    {
+      rust_assert (fntype->is_method ());
+      TyTy::BaseType *self_tyty_lookup = fntype->get_self_type ();
+
+      tree self_type = TyTyResolveCompile::compile (ctx, self_tyty_lookup);
+      Bvariable *compiled_self_param
+	= CompileSelfParam::compile (ctx, fndecl, self_param, self_type,
+				     self_param.get_locus ());
+
+      param_vars.push_back (compiled_self_param);
+      ctx->insert_var_decl (self_param.get_mappings ().get_hirid (),
+			    compiled_self_param);
+    }
+
+  // offset from + 1 for the TyTy::FnType being used when this is a method to
+  // skip over Self on the FnType
+  bool is_method = !self_param.is_error ();
+  size_t i = is_method ? 1 : 0;
+  for (auto &referenced_param : function_params)
+    {
+      auto tyty_param = fntype->param_at (i++);
+      auto param_tyty = tyty_param.second;
+      auto compiled_param_type = TyTyResolveCompile::compile (ctx, param_tyty);
+
+      Location param_locus = referenced_param.get_locus ();
+      Bvariable *compiled_param_var
+	= CompileFnParam::compile (ctx, fndecl, &referenced_param,
+				   compiled_param_type, param_locus);
+
+      param_vars.push_back (compiled_param_var);
+
+      const HIR::Pattern &param_pattern = *referenced_param.get_param_name ();
+      ctx->insert_var_decl (param_pattern.get_pattern_mappings ().get_hirid (),
+			    compiled_param_var);
+    }
+
+  if (!ctx->get_backend ()->function_set_parameters (fndecl, param_vars))
+    return error_mark_node;
+
+  // lookup locals
+  auto body_mappings = function_body->get_mappings ();
+  Resolver::Rib *rib = nullptr;
+  bool ok
+    = ctx->get_resolver ()->find_name_rib (body_mappings.get_nodeid (), &rib);
+  rust_assert (ok);
+
+  std::vector<Bvariable *> locals
+    = compile_locals_for_block (ctx, *rib, fndecl);
+
+  tree enclosing_scope = NULL_TREE;
+  Location start_location = function_body->get_locus ();
+  Location end_location = function_body->get_end_locus ();
+
+  tree code_block = ctx->get_backend ()->block (fndecl, enclosing_scope, locals,
+						start_location, end_location);
+  ctx->push_block (code_block);
+
+  Bvariable *return_address = nullptr;
+  if (function_has_return)
+    {
+      tree return_type = TyTyResolveCompile::compile (ctx, tyret);
+
+      bool address_is_taken = false;
+      tree ret_var_stmt = NULL_TREE;
+
+      return_address
+	= ctx->get_backend ()->temporary_variable (fndecl, code_block,
+						   return_type, NULL,
+						   address_is_taken, locus,
+						   &ret_var_stmt);
+
+      ctx->add_statement (ret_var_stmt);
+    }
+
+  ctx->push_fn (fndecl, return_address);
+  compile_function_body (ctx, fndecl, *function_body, function_has_return);
+  tree bind_tree = ctx->pop_block ();
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+  DECL_SAVED_TREE (fndecl) = bind_tree;
+
+  ctx->pop_fn ();
+  ctx->push_function (fndecl);
+
+  return fndecl;
+}
+
+tree
+HIRCompileBase::compile_constant_item (
+  Context *ctx, TyTy::BaseType *resolved_type,
+  const Resolver::CanonicalPath *canonical_path, HIR::Expr *const_value_expr,
+  Location locus)
+{
+  const std::string &ident = canonical_path->get ();
+  tree type = TyTyResolveCompile::compile (ctx, resolved_type);
+  tree const_type = build_qualified_type (type, TYPE_QUAL_CONST);
+
+  bool is_block_expr
+    = const_value_expr->get_expression_type () == HIR::Expr::ExprType::Block;
+
+  // compile the expression
+  tree folded_expr = error_mark_node;
+  if (!is_block_expr)
+    {
+      tree value = CompileExpr::Compile (const_value_expr, ctx);
+      folded_expr = fold_expr (value);
+    }
+  else
+    {
+      // in order to compile a block expr we want to reuse as much existing
+      // machineary that we already have. This means the best approach is to
+      // make a _fake_ function with a block so it can hold onto temps then
+      // use our constexpr code to fold it completely or error_mark_node
+      Backend::typed_identifier receiver;
+      tree compiled_fn_type = ctx->get_backend ()->function_type (
+	receiver, {}, {Backend::typed_identifier ("_", const_type, locus)},
+	NULL, locus);
+
+      tree fndecl
+	= ctx->get_backend ()->function (compiled_fn_type, ident, "", 0, locus);
+      TREE_READONLY (fndecl) = 1;
+
+      tree enclosing_scope = NULL_TREE;
+      HIR::BlockExpr *function_body
+	= static_cast<HIR::BlockExpr *> (const_value_expr);
+      Location start_location = function_body->get_locus ();
+      Location end_location = function_body->get_end_locus ();
+
+      tree code_block
+	= ctx->get_backend ()->block (fndecl, enclosing_scope, {},
+				      start_location, end_location);
+      ctx->push_block (code_block);
+
+      bool address_is_taken = false;
+      tree ret_var_stmt = NULL_TREE;
+      Bvariable *return_address
+	= ctx->get_backend ()->temporary_variable (fndecl, code_block,
+						   const_type, NULL,
+						   address_is_taken, locus,
+						   &ret_var_stmt);
+
+      ctx->add_statement (ret_var_stmt);
+      ctx->push_fn (fndecl, return_address);
+
+      compile_function_body (ctx, fndecl, *function_body, true);
+      tree bind_tree = ctx->pop_block ();
+
+      gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+      DECL_SAVED_TREE (fndecl) = bind_tree;
+
+      ctx->pop_fn ();
+
+      // lets fold it into a call expr
+      tree call = build_call_array_loc (locus.gcc_location (), const_type,
+					fndecl, 0, NULL);
+      folded_expr = fold_expr (call);
+    }
+
+  return named_constant_expression (const_type, ident, folded_expr, locus);
+}
+
+tree
+HIRCompileBase::named_constant_expression (tree type_tree,
+					   const std::string &name,
+					   tree const_val, Location location)
+{
+  if (type_tree == error_mark_node || const_val == error_mark_node)
+    return error_mark_node;
+
+  tree name_tree = get_identifier_with_length (name.data (), name.length ());
+  tree decl
+    = build_decl (location.gcc_location (), CONST_DECL, name_tree, type_tree);
+  DECL_INITIAL (decl) = const_val;
+  TREE_CONSTANT (decl) = 1;
+  TREE_READONLY (decl) = 1;
+
+  rust_preserve_from_gc (decl);
+  return decl;
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-compile-base.h b/gcc/rust/backend/rust-compile-base.h
new file mode 100644
index 00000000000..4c20933cafc
--- /dev/null
+++ b/gcc/rust/backend/rust-compile-base.h
@@ -0,0 +1,146 @@
+// Copyright (C) 2020-2022 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_COMPILE_BASE
+#define RUST_COMPILE_BASE
+
+#include "rust-compile-context.h"
+#include "rust-compile-type.h"
+#include "rust-hir-visitor.h"
+#include "rust-hir-full.h"
+
+namespace Rust {
+namespace Compile {
+
+class HIRCompileBase
+{
+public:
+  virtual ~HIRCompileBase () {}
+
+protected:
+  HIRCompileBase (Context *ctx) : ctx (ctx) {}
+
+  Context *ctx;
+
+protected:
+  Context *get_context () { return ctx; }
+
+  tree coercion_site (HirId id, tree rvalue, const TyTy::BaseType *actual,
+		      const TyTy::BaseType *expected, Location lvalue_locus,
+		      Location rvalue_locus);
+  tree coercion_site1 (tree rvalue, const TyTy::BaseType *actual,
+		       const TyTy::BaseType *expected, Location lvalue_locus,
+		       Location rvalue_locus);
+
+  tree coerce_to_dyn_object (tree compiled_ref, const TyTy::BaseType *actual,
+			     const TyTy::DynamicObjectType *ty, Location locus);
+
+  tree compute_address_for_trait_item (
+    const Resolver::TraitItemReference *ref,
+    const TyTy::TypeBoundPredicate *predicate,
+    std::vector<std::pair<Resolver::TraitReference *, HIR::ImplBlock *>>
+      &receiver_bounds,
+    const TyTy::BaseType *receiver, const TyTy::BaseType *root, Location locus);
+
+  bool verify_array_capacities (tree ltype, tree rtype, Location ltype_locus,
+				Location rtype_locus);
+
+  tree query_compile (HirId ref, TyTy::BaseType *lookup,
+		      const HIR::PathIdentSegment &final_segment,
+		      const Analysis::NodeMapping &mappings,
+		      Location expr_locus, bool is_qualified_path);
+
+  tree resolve_adjustements (std::vector<Resolver::Adjustment> &adjustments,
+			     tree expression, Location locus);
+
+  tree resolve_deref_adjustment (Resolver::Adjustment &adjustment,
+				 tree expression, Location locus);
+
+  tree resolve_indirection_adjustment (Resolver::Adjustment &adjustment,
+				       tree expression, Location locus);
+
+  tree resolve_unsized_adjustment (Resolver::Adjustment &adjustment,
+				   tree expression, Location locus);
+
+  tree resolve_unsized_slice_adjustment (Resolver::Adjustment &adjustment,
+					 tree expression, Location locus);
+
+  tree resolve_unsized_dyn_adjustment (Resolver::Adjustment &adjustment,
+				       tree expression, Location locus);
+
+  static void setup_fndecl (tree fndecl, bool is_main_entry_point,
+			    bool is_generic_fn, HIR::Visibility &visibility,
+			    const HIR::FunctionQualifiers &qualifiers,
+			    const AST::AttrVec &attrs);
+
+  static void handle_inline_attribute_on_fndecl (tree fndecl,
+						 const AST::Attribute &attr);
+
+  static void handle_cold_attribute_on_fndecl (tree fndecl,
+					       const AST::Attribute &attr);
+
+  static void handle_must_use_attribute_on_fndecl (tree fndecl,
+						   const AST::Attribute &attr);
+
+  static void
+  handle_link_section_attribute_on_fndecl (tree fndecl,
+					   const AST::Attribute &attr);
+  static void
+  handle_deprecated_attribute_on_fndecl (tree fndecl,
+					 const AST::Attribute &attr);
+
+  static void handle_no_mangle_attribute_on_fndecl (tree fndecl,
+						    const AST::Attribute &attr);
+
+  static void setup_abi_options (tree fndecl, ABI abi);
+
+  static tree address_expression (tree expr, Location locus);
+
+  static tree indirect_expression (tree expr, Location locus);
+
+  static bool mark_addressable (tree, Location);
+
+  static std::vector<Bvariable *>
+  compile_locals_for_block (Context *ctx, Resolver::Rib &rib, tree fndecl);
+
+  static void compile_function_body (Context *ctx, tree fndecl,
+				     HIR::BlockExpr &function_body,
+				     bool has_return_type);
+
+  static tree compile_function (
+    Context *ctx, const std::string &fn_name, HIR::SelfParam &self_param,
+    std::vector<HIR::FunctionParam> &function_params,
+    const HIR::FunctionQualifiers &qualifiers, HIR::Visibility &visibility,
+    AST::AttrVec &outer_attrs, Location locus, HIR::BlockExpr *function_body,
+    const Resolver::CanonicalPath *canonical_path, TyTy::FnType *fntype,
+    bool function_has_return);
+
+  static tree
+  compile_constant_item (Context *ctx, TyTy::BaseType *resolved_type,
+			 const Resolver::CanonicalPath *canonical_path,
+			 HIR::Expr *const_value_expr, Location locus);
+
+  static tree named_constant_expression (tree type_tree,
+					 const std::string &name,
+					 tree const_val, Location location);
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_COMPILE_BASE
diff --git a/gcc/rust/backend/rust-mangle.cc b/gcc/rust/backend/rust-mangle.cc
new file mode 100644
index 00000000000..4d202078a70
--- /dev/null
+++ b/gcc/rust/backend/rust-mangle.cc
@@ -0,0 +1,307 @@
+#include "rust-mangle.h"
+#include "fnv-hash.h"
+#include "rust-base62.h"
+
+// FIXME: Rename those to legacy_*
+static const std::string kMangledSymbolPrefix = "_ZN";
+static const std::string kMangledSymbolDelim = "E";
+static const std::string kMangledGenericDelim = "$C$";
+static const std::string kMangledSubstBegin = "$LT$";
+static const std::string kMangledSubstEnd = "$GT$";
+static const std::string kMangledSpace = "$u20$";
+static const std::string kMangledRef = "$RF$";
+static const std::string kMangledPtr = "$BP$";
+static const std::string kMangledLeftSqParen = "$u5b$";	 // [
+static const std::string kMangledRightSqParen = "$u5d$"; // ]
+static const std::string kQualPathBegin = "_" + kMangledSubstBegin;
+static const std::string kMangledComma = "$C$";
+
+namespace Rust {
+namespace Compile {
+
+Mangler::MangleVersion Mangler::version = MangleVersion::LEGACY;
+
+static std::string
+legacy_mangle_name (const std::string &name)
+{
+  // example
+  //  <&T as core::fmt::Debug>::fmt:
+  //  _ZN42_$LT$$RF$T$u20$as$u20$core..fmt..Debug$GT$3fmt17h6dac924c0051eef7E
+  // replace all white space with $ and & with RF
+  //
+  // <example::Bar as example::A>::fooA:
+  // _ZN43_$LT$example..Bar$u20$as$u20$example..A$GT$4fooA17hfc615fa76c7db7a0E:
+  //
+  // core::ptr::const_ptr::<impl *const T>::cast:
+  // _ZN4core3ptr9const_ptr33_$LT$impl$u20$$BP$const$u20$T$GT$4cast17hb79f4617226f1d55E:
+  //
+  // core::ptr::const_ptr::<impl *const [T]>::as_ptr:
+  // _ZN4core3ptr9const_ptr43_$LT$impl$u20$$BP$const$u20$$u5b$T$u5d$$GT$6as_ptr17he16e0dcd9473b04fE:
+  //
+  // example::Foo<T>::new:
+  // _ZN7example12Foo$LT$T$GT$3new17h9a2aacb7fd783515E:
+  //
+  // <example::Identity as example::FnLike<&T,&T>>::call
+  // _ZN74_$LT$example..Identity$u20$as$u20$example..FnLike$LT$$RF$T$C$$RF$T$GT$$GT$4call17ha9ee58935895acb3E
+
+  std::string buffer;
+  for (size_t i = 0; i < name.size (); i++)
+    {
+      std::string m;
+      char c = name.at (i);
+
+      if (c == ' ')
+	m = kMangledSpace;
+      else if (c == '&')
+	m = kMangledRef;
+      else if (i == 0 && c == '<')
+	m = kQualPathBegin;
+      else if (c == '<')
+	m = kMangledSubstBegin;
+      else if (c == '>')
+	m = kMangledSubstEnd;
+      else if (c == '*')
+	m = kMangledPtr;
+      else if (c == '[')
+	m = kMangledLeftSqParen;
+      else if (c == ']')
+	m = kMangledRightSqParen;
+      else if (c == ',')
+	m = kMangledComma;
+      else if (c == ':')
+	{
+	  rust_assert (i + 1 < name.size ());
+	  rust_assert (name.at (i + 1) == ':');
+	  i++;
+	  m = "..";
+	}
+      else
+	m.push_back (c);
+
+      buffer += m;
+    }
+
+  return std::to_string (buffer.size ()) + buffer;
+}
+
+static std::string
+legacy_mangle_canonical_path (const Resolver::CanonicalPath &path)
+{
+  std::string buffer;
+  for (size_t i = 0; i < path.size (); i++)
+    {
+      auto &seg = path.get_seg_at (i);
+      buffer += legacy_mangle_name (seg.second);
+    }
+  return buffer;
+}
+
+// rustc uses a sip128 hash for legacy mangling, but an fnv 128 was quicker to
+// implement for now
+static std::string
+legacy_hash (const std::string &fingerprint)
+{
+  Hash::FNV128 hasher;
+  hasher.write ((const unsigned char *) fingerprint.c_str (),
+		fingerprint.size ());
+
+  uint64_t hi, lo;
+  hasher.sum (&hi, &lo);
+
+  char hex[16 + 1];
+  memset (hex, 0, sizeof hex);
+  snprintf (hex, sizeof hex, "%08" PRIx64 "%08" PRIx64, lo, hi);
+
+  return "h" + std::string (hex, sizeof (hex) - 1);
+}
+
+static std::string
+v0_tuple_prefix (const TyTy::BaseType *ty)
+{
+  if (ty->is_unit ())
+    return "u";
+
+  // FIXME: ARTHUR: Add rest of algorithm
+  return "";
+}
+
+static std::string
+v0_numeric_prefix (const TyTy::BaseType *ty)
+{
+  static const std::map<std::string, std::string> num_prefixes = {
+    {"[i8]", "a"},    {"[u8]", "h"},	{"[i16]", "s"}, {"[u16]", "t"},
+    {"[i32]", "l"},   {"[u32]", "m"},	{"[i64]", "x"}, {"[u64]", "y"},
+    {"[isize]", "i"}, {"[usize]", "j"}, {"[f32]", "f"}, {"[f64]", "d"},
+  };
+
+  auto ty_kind = ty->get_kind ();
+  auto ty_str = ty->as_string ();
+  auto numeric_iter = num_prefixes.end ();
+
+  // Special numeric types
+  if (ty_kind == TyTy::TypeKind::ISIZE)
+    return "i";
+  else if (ty_kind == TyTy::TypeKind::USIZE)
+    return "j";
+
+  numeric_iter = num_prefixes.find (ty_str);
+  if (numeric_iter != num_prefixes.end ())
+    return numeric_iter->second;
+
+  return "";
+}
+
+static std::string
+v0_simple_type_prefix (const TyTy::BaseType *ty)
+{
+  switch (ty->get_kind ())
+    {
+    case TyTy::TypeKind::BOOL:
+      return "b";
+    case TyTy::TypeKind::CHAR:
+      return "c";
+    case TyTy::TypeKind::STR:
+      return "e";
+    case TyTy::TypeKind::NEVER:
+      return "z";
+
+      // Placeholder types
+    case TyTy::TypeKind::ERROR:	      // Fallthrough
+    case TyTy::TypeKind::INFER:	      // Fallthrough
+    case TyTy::TypeKind::PLACEHOLDER: // Fallthrough
+    case TyTy::TypeKind::PARAM:
+      // FIXME: TyTy::TypeKind::BOUND is also a valid variant in rustc
+      return "p";
+
+    case TyTy::TypeKind::TUPLE:
+      return v0_tuple_prefix (ty);
+
+    case TyTy::TypeKind::UINT:	// Fallthrough
+    case TyTy::TypeKind::INT:	// Fallthrough
+    case TyTy::TypeKind::FLOAT: // Fallthrough
+    case TyTy::TypeKind::ISIZE: // Fallthrough
+    case TyTy::TypeKind::USIZE: // Fallthrough
+      return v0_numeric_prefix (ty);
+
+    default:
+      return "";
+    }
+
+  gcc_unreachable ();
+}
+
+// Add an underscore-terminated base62 integer to the mangling string.
+// This corresponds to the `<base-62-number>` grammar in the v0 mangling RFC:
+//  - 0 is encoded as "_"
+//  - any other value is encoded as itself minus one in base 62, followed by
+//  "_"
+static void
+v0_add_integer_62 (std::string &mangled, uint64_t x)
+{
+  if (x > 0)
+    mangled.append (base62_integer (x - 1));
+
+  mangled.append ("_");
+}
+
+// Add a tag-prefixed base62 integer to the mangling string when the
+// integer is greater than 0:
+//  - 0 is encoded as "" (nothing)
+//  - any other value is encoded as <tag> + v0_add_integer_62(itself), that is
+//  <tag> + base62(itself - 1) + '_'
+static void
+v0_add_opt_integer_62 (std::string &mangled, std::string tag, uint64_t x)
+{
+  if (x > 0)
+    {
+      mangled.append (tag);
+      v0_add_integer_62 (mangled, x);
+    }
+}
+
+static void
+v0_add_disambiguator (std::string &mangled, uint64_t dis)
+{
+  v0_add_opt_integer_62 (mangled, "s", dis);
+}
+
+// Add an identifier to the mangled string. This corresponds to the
+// `<identifier>` grammar in the v0 mangling RFC.
+static void
+v0_add_identifier (std::string &mangled, const std::string &identifier)
+{
+  // FIXME: gccrs cannot handle unicode identifiers yet, so we never have to
+  // create mangling for unicode values for now. However, this is handled
+  // by the v0 mangling scheme. The grammar for unicode identifier is
+  // contained in <undisambiguated-identifier>, right under the <identifier>
+  // one. If the identifier contains unicode values, then an extra "u" needs
+  // to be added to the mangling string and `punycode` must be used to encode
+  // the characters.
+
+  mangled += std::to_string (identifier.size ());
+
+  // If the first character of the identifier is a digit or an underscore, we
+  // add an extra underscore
+  if (identifier[0] == '_')
+    mangled.append ("_");
+
+  mangled.append (identifier);
+}
+
+static std::string
+v0_type_prefix (const TyTy::BaseType *ty)
+{
+  auto ty_prefix = v0_simple_type_prefix (ty);
+  if (!ty_prefix.empty ())
+    return ty_prefix;
+
+  // FIXME: We need to fetch more type prefixes
+  gcc_unreachable ();
+}
+
+static std::string
+legacy_mangle_item (const TyTy::BaseType *ty,
+		    const Resolver::CanonicalPath &path)
+{
+  const std::string hash = legacy_hash (ty->as_string ());
+  const std::string hash_sig = legacy_mangle_name (hash);
+
+  return kMangledSymbolPrefix + legacy_mangle_canonical_path (path) + hash_sig
+	 + kMangledSymbolDelim;
+}
+
+static std::string
+v0_mangle_item (const TyTy::BaseType *ty, const Resolver::CanonicalPath &path)
+{
+  // we can get this from the canonical_path
+  auto mappings = Analysis::Mappings::get ();
+  std::string crate_name;
+  bool ok = mappings->get_crate_name (path.get_crate_num (), crate_name);
+  rust_assert (ok);
+
+  std::string mangled;
+  // FIXME: Add real algorithm once all pieces are implemented
+  auto ty_prefix = v0_type_prefix (ty);
+  v0_add_identifier (mangled, crate_name);
+  v0_add_disambiguator (mangled, 62);
+
+  gcc_unreachable ();
+}
+
+std::string
+Mangler::mangle_item (const TyTy::BaseType *ty,
+		      const Resolver::CanonicalPath &path) const
+{
+  switch (version)
+    {
+    case Mangler::MangleVersion::LEGACY:
+      return legacy_mangle_item (ty, path);
+    case Mangler::MangleVersion::V0:
+      return v0_mangle_item (ty, path);
+    default:
+      gcc_unreachable ();
+    }
+}
+
+} // namespace Compile
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-mangle.h b/gcc/rust/backend/rust-mangle.h
new file mode 100644
index 00000000000..6d5a64f8bce
--- /dev/null
+++ b/gcc/rust/backend/rust-mangle.h
@@ -0,0 +1,52 @@
+// 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_MANGLE_H
+#define RUST_MANGLE_H
+
+#include "rust-system.h"
+#include "rust-tyty.h"
+
+namespace Rust {
+namespace Compile {
+
+class Mangler
+{
+public:
+  enum MangleVersion
+  {
+    // Values defined in rust/lang.opt
+    LEGACY = 0,
+    V0 = 1,
+  };
+
+  // this needs to support Legacy and V0 see github #429 or #305
+  std::string mangle_item (const TyTy::BaseType *ty,
+			   const Resolver::CanonicalPath &path) const;
+
+  static void set_mangling (int frust_mangling_value)
+  {
+    version = static_cast<MangleVersion> (frust_mangling_value);
+  }
+
+private:
+  static enum MangleVersion version;
+};
+
+} // namespace Compile
+} // namespace Rust
+
+#endif // RUST_MANGLE_H
diff --git a/gcc/rust/backend/rust-tree.cc b/gcc/rust/backend/rust-tree.cc
new file mode 100644
index 00000000000..3d71e19fe82
--- /dev/null
+++ b/gcc/rust/backend/rust-tree.cc
@@ -0,0 +1,958 @@
+// Copyright (C) 2020-2022 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-tree.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "escaped_string.h"
+
+namespace Rust {
+
+void
+mark_exp_read (tree exp)
+{
+  if (exp == NULL)
+    return;
+
+  switch (TREE_CODE (exp))
+    {
+    case VAR_DECL:
+      gcc_fallthrough ();
+    case PARM_DECL:
+      DECL_READ_P (exp) = 1;
+      break;
+    case ARRAY_REF:
+    case COMPONENT_REF:
+    case MODIFY_EXPR:
+    case REALPART_EXPR:
+    case IMAGPART_EXPR:
+    CASE_CONVERT:
+    case ADDR_EXPR:
+    case INDIRECT_REF:
+    case FLOAT_EXPR:
+    case NON_DEPENDENT_EXPR:
+    case VIEW_CONVERT_EXPR:
+      mark_exp_read (TREE_OPERAND (exp, 0));
+      break;
+    case COMPOUND_EXPR:
+      mark_exp_read (TREE_OPERAND (exp, 1));
+      break;
+    case COND_EXPR:
+      if (TREE_OPERAND (exp, 1))
+	mark_exp_read (TREE_OPERAND (exp, 1));
+      if (TREE_OPERAND (exp, 2))
+	mark_exp_read (TREE_OPERAND (exp, 2));
+      break;
+    default:
+      break;
+    }
+}
+
+tree
+convert_from_reference (tree val)
+{
+  if (TREE_TYPE (val) && TYPE_REF_P (TREE_TYPE (val)))
+    {
+      tree t = TREE_TYPE (TREE_TYPE (val));
+      tree ref = build1 (INDIRECT_REF, t, val);
+
+      mark_exp_read (val);
+
+      TREE_SIDE_EFFECTS (ref)
+	= (TREE_THIS_VOLATILE (ref) || TREE_SIDE_EFFECTS (val));
+      val = ref;
+    }
+
+  return val;
+}
+
+tree
+mark_use (tree expr, bool rvalue_p, bool read_p,
+	  location_t loc /* = UNKNOWN_LOCATION */,
+	  bool reject_builtin /* = true */)
+{
+#define RECUR(t) mark_use ((t), rvalue_p, read_p, loc, reject_builtin)
+
+  if (expr == NULL_TREE || error_operand_p (expr))
+    return expr;
+
+  if (reject_builtin)
+    return error_mark_node;
+
+  if (read_p)
+    mark_exp_read (expr);
+
+  bool recurse_op[3] = {false, false, false};
+  switch (TREE_CODE (expr))
+    {
+    case COMPONENT_REF:
+    case NON_DEPENDENT_EXPR:
+      recurse_op[0] = true;
+      break;
+    case COMPOUND_EXPR:
+      recurse_op[1] = true;
+      break;
+    case COND_EXPR:
+      recurse_op[2] = true;
+      if (TREE_OPERAND (expr, 1))
+	recurse_op[1] = true;
+      break;
+    case INDIRECT_REF:
+      if (REFERENCE_REF_P (expr))
+	{
+	  /* Try to look through the reference.  */
+	  tree ref = TREE_OPERAND (expr, 0);
+	  tree r = mark_rvalue_use (ref, loc, reject_builtin);
+	  if (r != ref)
+	    expr = convert_from_reference (r);
+	}
+      break;
+
+    case VIEW_CONVERT_EXPR:
+      if (location_wrapper_p (expr))
+	{
+	  loc = EXPR_LOCATION (expr);
+	  tree op = TREE_OPERAND (expr, 0);
+	  tree nop = RECUR (op);
+	  if (nop == error_mark_node)
+	    return error_mark_node;
+	  else if (op == nop)
+	    /* No change.  */;
+	  else if (DECL_P (nop) || CONSTANT_CLASS_P (nop))
+	    {
+	      /* Reuse the location wrapper.  */
+	      TREE_OPERAND (expr, 0) = nop;
+	      /* If we're replacing a DECL with a constant, we also need to
+		 change the TREE_CODE of the location wrapper.  */
+	      if (rvalue_p)
+		TREE_SET_CODE (expr, NON_LVALUE_EXPR);
+	    }
+	  else
+	    {
+	      /* Drop the location wrapper.  */
+	      expr = nop;
+	      protected_set_expr_location (expr, loc);
+	    }
+	  return expr;
+	}
+      gcc_fallthrough ();
+    CASE_CONVERT:
+      recurse_op[0] = true;
+      break;
+
+    default:
+      break;
+    }
+
+  for (int i = 0; i < 3; ++i)
+    if (recurse_op[i])
+      {
+	tree op = TREE_OPERAND (expr, i);
+	op = RECUR (op);
+	if (op == error_mark_node)
+	  return error_mark_node;
+	TREE_OPERAND (expr, i) = op;
+      }
+
+  return expr;
+#undef RECUR
+}
+
+tree
+mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
+		 bool reject_builtin /* = true */)
+{
+  return mark_use (e, true, true, loc, reject_builtin);
+}
+
+tree
+mark_lvalue_use (tree expr)
+{
+  return mark_use (expr, false, true, input_location, false);
+}
+
+tree
+mark_lvalue_use_nonread (tree expr)
+{
+  return mark_use (expr, false, false, input_location, false);
+}
+
+tree
+mark_discarded_use (tree expr)
+{
+  if (expr == NULL_TREE)
+    return expr;
+
+  STRIP_ANY_LOCATION_WRAPPER (expr);
+
+  switch (TREE_CODE (expr))
+    {
+    case COND_EXPR:
+      TREE_OPERAND (expr, 2) = mark_discarded_use (TREE_OPERAND (expr, 2));
+      gcc_fallthrough ();
+    case COMPOUND_EXPR:
+      TREE_OPERAND (expr, 1) = mark_discarded_use (TREE_OPERAND (expr, 1));
+      return expr;
+
+    case COMPONENT_REF:
+    case ARRAY_REF:
+    case INDIRECT_REF:
+    case MEMBER_REF:
+      break;
+    default:
+      if (DECL_P (expr))
+	break;
+      else
+	return expr;
+    }
+
+  return mark_use (expr, true, true, input_location, false);
+}
+
+tree
+convert_to_void (tree expr, impl_conv_void implicit)
+{
+  location_t loc = expr_loc_or_input_loc (expr);
+  if (expr == error_mark_node || TREE_TYPE (expr) == error_mark_node)
+    return error_mark_node;
+
+  expr = mark_discarded_use (expr);
+  if (implicit == ICV_CAST)
+    /* An explicit cast to void avoids all -Wunused-but-set* warnings.  */
+    mark_exp_read (expr);
+
+  if (!TREE_TYPE (expr))
+    return expr;
+
+  if (VOID_TYPE_P (TREE_TYPE (expr)))
+    return expr;
+  switch (TREE_CODE (expr))
+    {
+      case COND_EXPR: {
+	/* The two parts of a cond expr might be separate lvalues.  */
+	tree op1 = TREE_OPERAND (expr, 1);
+	tree op2 = TREE_OPERAND (expr, 2);
+	bool side_effects
+	  = ((op1 && TREE_SIDE_EFFECTS (op1)) || TREE_SIDE_EFFECTS (op2));
+	tree new_op1, new_op2;
+	new_op1 = NULL_TREE;
+	if (implicit != ICV_CAST && !side_effects)
+	  {
+	    if (op1)
+	      new_op1 = convert_to_void (op1, ICV_SECOND_OF_COND);
+	    new_op2 = convert_to_void (op2, ICV_THIRD_OF_COND);
+	  }
+	else
+	  {
+	    if (op1)
+	      new_op1 = convert_to_void (op1, ICV_CAST);
+	    new_op2 = convert_to_void (op2, ICV_CAST);
+	  }
+
+	expr = build3_loc (loc, COND_EXPR, TREE_TYPE (new_op2),
+			   TREE_OPERAND (expr, 0), new_op1, new_op2);
+	break;
+      }
+
+      case COMPOUND_EXPR: {
+	/* The second part of a compound expr contains the value.  */
+	tree op1 = TREE_OPERAND (expr, 1);
+	tree new_op1;
+	if (implicit != ICV_CAST
+	    && !warning_suppressed_p (expr /* What warning? */))
+	  new_op1 = convert_to_void (op1, ICV_RIGHT_OF_COMMA);
+	else
+	  new_op1 = convert_to_void (op1, ICV_CAST);
+
+	if (new_op1 != op1)
+	  {
+	    tree t = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (new_op1),
+				 TREE_OPERAND (expr, 0), new_op1);
+	    expr = t;
+	  }
+
+	break;
+      }
+
+    case NON_LVALUE_EXPR:
+    case NOP_EXPR:
+      /* These have already decayed to rvalue.  */
+      break;
+
+    case CALL_EXPR:
+      maybe_warn_nodiscard (expr, implicit);
+      break;
+
+      case INDIRECT_REF: {
+	tree type = TREE_TYPE (expr);
+	int is_reference = TYPE_REF_P (TREE_TYPE (TREE_OPERAND (expr, 0)));
+	int is_volatile = TYPE_VOLATILE (type);
+	int is_complete = COMPLETE_TYPE_P (type);
+
+	/* Can't load the value if we don't know the type.  */
+	if (is_volatile && !is_complete)
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of incomplete type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in second operand "
+			    "of conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in third operand "
+			    "of conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in right operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in left operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "incomplete type %qT in for increment "
+			    "expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	/* Don't load the value if this is an implicit dereference, or if
+	   the type needs to be handled by ctors/dtors.  */
+	else if (is_volatile && is_reference)
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in second operand of "
+			    "conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in third operand of "
+			    "conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in right operand of "
+			    "comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in left operand of comma "
+			    "operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "implicit dereference will not access "
+			    "object of type %qT in for increment expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	else if (is_volatile && TREE_ADDRESSABLE (type))
+	  {
+	    switch (implicit)
+	      {
+	      case ICV_CAST:
+		warning_at (loc, 0,
+			    "conversion to void will not access "
+			    "object of non-trivially-copyable type %qT",
+			    type);
+		break;
+	      case ICV_SECOND_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in second "
+			    "operand of conditional expression",
+			    type);
+		break;
+	      case ICV_THIRD_OF_COND:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in third "
+			    "operand of conditional expression",
+			    type);
+		break;
+	      case ICV_RIGHT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in right "
+			    "operand of comma operator",
+			    type);
+		break;
+	      case ICV_LEFT_OF_COMMA:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in left "
+			    "operand of comma operator",
+			    type);
+		break;
+	      case ICV_STATEMENT:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in statement",
+			    type);
+		break;
+	      case ICV_THIRD_IN_FOR:
+		warning_at (loc, 0,
+			    "indirection will not access object of "
+			    "non-trivially-copyable type %qT in for "
+			    "increment expression",
+			    type);
+		break;
+	      default:
+		gcc_unreachable ();
+	      }
+	  }
+	if (is_reference || !is_volatile || !is_complete
+	    || TREE_ADDRESSABLE (type))
+	  {
+	    /* Emit a warning (if enabled) when the "effect-less" INDIRECT_REF
+	       operation is stripped off. Note that we don't warn about
+	       - an expression with TREE_NO_WARNING set. (For an example of
+		 such expressions, see build_over_call in call.cc.)
+	       - automatic dereferencing of references, since the user cannot
+		 control it. (See also warn_if_unused_value() in c-common.cc.)
+	     */
+	    if (warn_unused_value && implicit != ICV_CAST
+		&& !warning_suppressed_p (expr, OPT_Wunused_value)
+		&& !is_reference)
+	      warning_at (loc, OPT_Wunused_value, "value computed is not used");
+	    expr = TREE_OPERAND (expr, 0);
+	    if (TREE_CODE (expr) == CALL_EXPR)
+	      maybe_warn_nodiscard (expr, implicit);
+	  }
+
+	break;
+      }
+
+      case VAR_DECL: {
+	/* External variables might be incomplete.  */
+	tree type = TREE_TYPE (expr);
+	int is_complete = COMPLETE_TYPE_P (type);
+
+	if (TYPE_VOLATILE (type) && !is_complete)
+	  switch (implicit)
+	    {
+	    case ICV_CAST:
+	      warning_at (loc, 0,
+			  "conversion to void will not access "
+			  "object %qE of incomplete type %qT",
+			  expr, type);
+	      break;
+	    case ICV_SECOND_OF_COND:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in second operand of "
+			  "conditional expression",
+			  expr, type);
+	      break;
+	    case ICV_THIRD_OF_COND:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in third operand of "
+			  "conditional expression",
+			  expr, type);
+	      break;
+	    case ICV_RIGHT_OF_COMMA:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in right operand of comma operator",
+			  expr, type);
+	      break;
+	    case ICV_LEFT_OF_COMMA:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in left operand of comma operator",
+			  expr, type);
+	      break;
+	    case ICV_STATEMENT:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in statement",
+			  expr, type);
+	      break;
+	    case ICV_THIRD_IN_FOR:
+	      warning_at (loc, 0,
+			  "variable %qE of incomplete type %qT will "
+			  "not be accessed in for increment expression",
+			  expr, type);
+	      break;
+	    default:
+	      gcc_unreachable ();
+	    }
+
+	break;
+      }
+
+    default:;
+    }
+
+  if (!TREE_SIDE_EFFECTS (expr))
+    expr = void_node;
+
+  return expr;
+}
+
+void
+maybe_warn_nodiscard (tree expr, impl_conv_void implicit)
+{
+  tree call = expr;
+  if (TREE_CODE (expr) == TARGET_EXPR)
+    call = TARGET_EXPR_INITIAL (expr);
+
+  location_t loc = expr_loc_or_input_loc (call);
+  tree callee = CALL_EXPR_FN (call);
+  if (!callee)
+    return;
+
+  tree type = TREE_TYPE (callee);
+  if (INDIRECT_TYPE_P (type))
+    type = TREE_TYPE (type);
+
+  tree rettype = TREE_TYPE (type);
+  tree fn = get_fndecl_from_callee (callee);
+  tree attr;
+  if (implicit != ICV_CAST && fn
+      && (attr = lookup_attribute ("nodiscard", DECL_ATTRIBUTES (fn))))
+    {
+      escaped_string msg;
+      tree args = TREE_VALUE (attr);
+      if (args)
+	msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
+      const char *format
+	= (msg ? G_ ("ignoring return value of %qD, that must be used: %<%s%>")
+	       : G_ ("ignoring return value of %qD, that must be used"));
+      const char *raw_msg = msg ? (const char *) msg : "";
+      auto_diagnostic_group d;
+      if (warning_at (loc, OPT_Wunused_result, format, fn, raw_msg))
+	inform (DECL_SOURCE_LOCATION (fn), "declared here");
+    }
+  else if (implicit != ICV_CAST
+	   && (attr
+	       = lookup_attribute ("nodiscard", TYPE_ATTRIBUTES (rettype))))
+    {
+      escaped_string msg;
+      tree args = TREE_VALUE (attr);
+      if (args)
+	msg.escape (TREE_STRING_POINTER (TREE_VALUE (args)));
+      const char *format
+	= (msg ? G_ (
+	     "ignoring returned value of type %qT, that must be used: %<%s%>")
+	       : G_ ("ignoring returned value of type %qT, that must be used"));
+      const char *raw_msg = msg ? (const char *) msg : "";
+      auto_diagnostic_group d;
+      if (warning_at (loc, OPT_Wunused_result, format, rettype, raw_msg))
+	{
+	  if (fn)
+	    inform (DECL_SOURCE_LOCATION (fn), "in call to %qD, declared here",
+		    fn);
+	  inform (DECL_SOURCE_LOCATION (TYPE_NAME (rettype)),
+		  "%qT declared here", rettype);
+	}
+    }
+}
+
+location_t
+expr_loc_or_loc (const_tree t, location_t or_loc)
+{
+  location_t loc = EXPR_LOCATION (t);
+  if (loc == UNKNOWN_LOCATION)
+    loc = or_loc;
+  return loc;
+}
+
+location_t
+expr_loc_or_input_loc (const_tree t)
+{
+  return expr_loc_or_loc (t, input_location);
+}
+
+// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
+// if we can.
+tree
+get_fndecl_from_callee (tree fn)
+{
+  if (fn == NULL_TREE)
+    return fn;
+  if (TREE_CODE (fn) == FUNCTION_DECL)
+    return fn;
+  tree type = TREE_TYPE (fn);
+  if (type == NULL_TREE || !INDIRECT_TYPE_P (type))
+    return NULL_TREE;
+
+  STRIP_NOPS (fn);
+  if (TREE_CODE (fn) == ADDR_EXPR || TREE_CODE (fn) == FDESC_EXPR)
+    fn = TREE_OPERAND (fn, 0);
+  if (TREE_CODE (fn) == FUNCTION_DECL)
+    return fn;
+  return NULL_TREE;
+}
+
+tree
+pointer_offset_expression (tree base_tree, tree index_tree, location_t location)
+{
+  tree element_type_tree = TREE_TYPE (TREE_TYPE (base_tree));
+  if (base_tree == error_mark_node || TREE_TYPE (base_tree) == error_mark_node
+      || index_tree == error_mark_node || element_type_tree == error_mark_node)
+    return error_mark_node;
+
+  tree element_size = TYPE_SIZE_UNIT (element_type_tree);
+  index_tree = fold_convert_loc (location, sizetype, index_tree);
+  tree offset
+    = fold_build2_loc (location, MULT_EXPR, sizetype, index_tree, element_size);
+
+  return fold_build2_loc (location, POINTER_PLUS_EXPR, TREE_TYPE (base_tree),
+			  base_tree, offset);
+}
+
+// forked from gcc/cp/tree.cc cp_walk_subtrees
+/* Apply FUNC to all language-specific sub-trees of TP in a pre-order
+   traversal.  Called from walk_tree.  */
+
+tree
+rs_walk_subtrees (tree *tp, int *walk_subtrees_p, walk_tree_fn func, void *data,
+		  hash_set<tree> *pset)
+{
+  enum tree_code code = TREE_CODE (*tp);
+  tree result;
+
+#define WALK_SUBTREE(NODE)                                                     \
+  do                                                                           \
+    {                                                                          \
+      result = rs_walk_tree (&(NODE), func, data, pset);                       \
+      if (result)                                                              \
+	goto out;                                                              \
+    }                                                                          \
+  while (0)
+
+  if (TYPE_P (*tp))
+    {
+      /* If *WALK_SUBTREES_P is 1, we're interested in the syntactic form of
+	 the argument, so don't look through typedefs, but do walk into
+	 template arguments for alias templates (and non-typedefed classes).
+
+	 If *WALK_SUBTREES_P > 1, we're interested in type identity or
+	 equivalence, so look through typedefs, ignoring template arguments for
+	 alias templates, and walk into template args of classes.
+
+	 See find_abi_tags_r for an example of setting *WALK_SUBTREES_P to 2
+	 when that's the behavior the walk_tree_fn wants.  */
+      if (*walk_subtrees_p == 1 && typedef_variant_p (*tp))
+	{
+	  *walk_subtrees_p = 0;
+	  return NULL_TREE;
+	}
+    }
+
+  /* Not one of the easy cases.  We must explicitly go through the
+     children.  */
+  result = NULL_TREE;
+  switch (code)
+    {
+    case TREE_LIST:
+      WALK_SUBTREE (TREE_PURPOSE (*tp));
+      break;
+
+    case RECORD_TYPE:
+      if (TYPE_PTRMEMFUNC_P (*tp))
+	WALK_SUBTREE (TYPE_PTRMEMFUNC_FN_TYPE_RAW (*tp));
+      break;
+
+    case CONSTRUCTOR:
+      if (COMPOUND_LITERAL_P (*tp))
+	WALK_SUBTREE (TREE_TYPE (*tp));
+      break;
+
+    case DECL_EXPR:
+      /* User variables should be mentioned in BIND_EXPR_VARS
+	 and their initializers and sizes walked when walking
+	 the containing BIND_EXPR.  Compiler temporaries are
+	 handled here.  And also normal variables in templates,
+	 since do_poplevel doesn't build a BIND_EXPR then.  */
+      if (VAR_P (TREE_OPERAND (*tp, 0))
+	  && (DECL_ARTIFICIAL (TREE_OPERAND (*tp, 0))
+	      && !TREE_STATIC (TREE_OPERAND (*tp, 0))))
+	{
+	  tree decl = TREE_OPERAND (*tp, 0);
+	  WALK_SUBTREE (DECL_INITIAL (decl));
+	  WALK_SUBTREE (DECL_SIZE (decl));
+	  WALK_SUBTREE (DECL_SIZE_UNIT (decl));
+	}
+      break;
+
+    default:
+      return NULL_TREE;
+    }
+
+  /* We didn't find what we were looking for.  */
+out:
+  return result;
+
+#undef WALK_SUBTREE
+}
+
+// forked from gcc/cp/tree.cc cp_expr_location
+
+/* Like EXPR_LOCATION, but also handle some tcc_exceptional that have
+   locations.  */
+
+location_t
+rs_expr_location (const_tree t_)
+{
+  tree t = CONST_CAST_TREE (t_);
+  if (t == NULL_TREE)
+    return UNKNOWN_LOCATION;
+
+  return EXPR_LOCATION (t);
+}
+
+// forked from gcc/cp/class.cc is_really_empty_class
+
+/* Returns true if TYPE contains no actual data, just various
+   possible combinations of empty classes.  If IGNORE_VPTR is true,
+   a vptr doesn't prevent the class from being considered empty.  Typically
+   we want to ignore the vptr on assignment, and not on initialization.  */
+
+bool
+is_really_empty_class (tree type, bool ignore_vptr)
+{
+  if (CLASS_TYPE_P (type))
+    {
+      tree field;
+      tree binfo;
+      tree base_binfo;
+      int i;
+
+      /* CLASSTYPE_EMPTY_P isn't set properly until the class is actually laid
+	 out, but we'd like to be able to check this before then.  */
+      if (COMPLETE_TYPE_P (type) && is_empty_class (type))
+	return true;
+
+      if (!ignore_vptr && TYPE_CONTAINS_VPTR_P (type))
+	return false;
+
+      for (binfo = TYPE_BINFO (type), i = 0;
+	   BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
+	if (!is_really_empty_class (BINFO_TYPE (base_binfo), ignore_vptr))
+	  return false;
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+	if (TREE_CODE (field) == FIELD_DECL
+	    && !DECL_ARTIFICIAL (field)
+	    /* An unnamed bit-field is not a data member.  */
+	    && !DECL_UNNAMED_BIT_FIELD (field)
+	    && !is_really_empty_class (TREE_TYPE (field), ignore_vptr))
+	  return false;
+      return true;
+    }
+  else if (TREE_CODE (type) == ARRAY_TYPE)
+    return (integer_zerop (array_type_nelts_top (type))
+	    || is_really_empty_class (TREE_TYPE (type), ignore_vptr));
+  return false;
+}
+
+// forked from gcc/cp/class.cc is_empty_class
+
+/* Returns 1 if TYPE contains only padding bytes.  */
+
+int
+is_empty_class (tree type)
+{
+  if (type == error_mark_node)
+    return 0;
+
+  if (!CLASS_TYPE_P (type))
+    return 0;
+
+  return CLASSTYPE_EMPTY_P (type);
+}
+
+// forked from gcc/cp/tree.cc array_type_nelts_top
+
+/* Return, as an INTEGER_CST node, the number of elements for TYPE
+   (which is an ARRAY_TYPE).  This counts only elements of the top
+   array.  */
+
+tree
+array_type_nelts_top (tree type)
+{
+  return fold_build2_loc (input_location, PLUS_EXPR, sizetype,
+			  array_type_nelts (type), size_one_node);
+}
+
+// forked from gcc/cp/tree.cc builtin_valid_in_constant_expr_p
+
+/* Test whether DECL is a builtin that may appear in a
+   constant-expression. */
+
+bool
+builtin_valid_in_constant_expr_p (const_tree decl)
+{
+  STRIP_ANY_LOCATION_WRAPPER (decl);
+  if (TREE_CODE (decl) != FUNCTION_DECL)
+    /* Not a function.  */
+    return false;
+  if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL)
+    {
+      if (fndecl_built_in_p (decl, BUILT_IN_FRONTEND))
+	switch (DECL_FE_FUNCTION_CODE (decl))
+	  {
+	  case RS_BUILT_IN_IS_CONSTANT_EVALUATED:
+	  case RS_BUILT_IN_SOURCE_LOCATION:
+	  case RS_BUILT_IN_IS_CORRESPONDING_MEMBER:
+	  case RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS:
+	    return true;
+	  default:
+	    break;
+	  }
+      /* Not a built-in.  */
+      return false;
+    }
+  switch (DECL_FUNCTION_CODE (decl))
+    {
+      /* These always have constant results like the corresponding
+	 macros/symbol.  */
+    case BUILT_IN_FILE:
+    case BUILT_IN_FUNCTION:
+    case BUILT_IN_LINE:
+
+      /* The following built-ins are valid in constant expressions
+	 when their arguments are.  */
+    case BUILT_IN_ADD_OVERFLOW_P:
+    case BUILT_IN_SUB_OVERFLOW_P:
+    case BUILT_IN_MUL_OVERFLOW_P:
+
+      /* These have constant results even if their operands are
+	 non-constant.  */
+    case BUILT_IN_CONSTANT_P:
+    case BUILT_IN_ATOMIC_ALWAYS_LOCK_FREE:
+      return true;
+    default:
+      return false;
+    }
+}
+
+// forked from gcc/cp/decl2.cc decl_maybe_constant_var_p
+
+/* Returns true if DECL could be a symbolic constant variable, depending on
+   its initializer.  */
+
+bool
+decl_maybe_constant_var_p (tree decl)
+{
+  tree type = TREE_TYPE (decl);
+  if (!VAR_P (decl))
+    return false;
+  if (DECL_DECLARED_CONSTEXPR_P (decl))
+    return true;
+  if (DECL_HAS_VALUE_EXPR_P (decl))
+    /* A proxy isn't constant.  */
+    return false;
+  if (TYPE_REF_P (type))
+    /* References can be constant.  */;
+  else if (RS_TYPE_CONST_NON_VOLATILE_P (type)
+	   && INTEGRAL_OR_ENUMERATION_TYPE_P (type))
+    /* And const integers.  */;
+  else
+    return false;
+
+  if (DECL_INITIAL (decl) && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl))
+    /* We know the initializer, and it isn't constant.  */
+    return false;
+  else
+    return true;
+}
+
+// forked from gcc/cp/typeck.cc cp_type_quals
+
+/* Returns the type qualifiers for this type, including the qualifiers on the
+   elements for an array type.  */
+
+int
+rs_type_quals (const_tree type)
+{
+  int quals;
+  /* This CONST_CAST is okay because strip_array_types returns its
+     argument unmodified and we assign it to a const_tree.  */
+  type = strip_array_types (CONST_CAST_TREE (type));
+  if (type == error_mark_node
+      /* Quals on a FUNCTION_TYPE are memfn quals.  */
+      || TREE_CODE (type) == FUNCTION_TYPE)
+    return TYPE_UNQUALIFIED;
+  quals = TYPE_QUALS (type);
+  /* METHOD and REFERENCE_TYPEs should never have quals.  */
+  gcc_assert (
+    (TREE_CODE (type) != METHOD_TYPE && !TYPE_REF_P (type))
+    || ((quals & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE)) == TYPE_UNQUALIFIED));
+  return quals;
+}
+
+} // namespace Rust
diff --git a/gcc/rust/backend/rust-tree.h b/gcc/rust/backend/rust-tree.h
new file mode 100644
index 00000000000..a667cbfc8ad
--- /dev/null
+++ b/gcc/rust/backend/rust-tree.h
@@ -0,0 +1,508 @@
+// Copyright (C) 2020-2022 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_TREE
+#define RUST_TREE
+
+#include "rust-system.h"
+#include "coretypes.h"
+#include "tree.h"
+
+/* Returns true if NODE is a pointer.  */
+#define TYPE_PTR_P(NODE) (TREE_CODE (NODE) == POINTER_TYPE)
+
+/* Returns true if NODE is a reference.  */
+#define TYPE_REF_P(NODE) (TREE_CODE (NODE) == REFERENCE_TYPE)
+
+/* Returns true if NODE is a pointer or a reference.  */
+#define INDIRECT_TYPE_P(NODE) (TYPE_PTR_P (NODE) || TYPE_REF_P (NODE))
+
+/* [basic.fundamental]
+
+   Types  bool, char, wchar_t, and the signed and unsigned integer types
+   are collectively called integral types.
+
+   Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
+   types as well, which is incorrect in C++.  Keep these checks in
+   ascending code order.  */
+#define RS_INTEGRAL_TYPE_P(TYPE)                                               \
+  (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
+
+/* [basic.fundamental]
+
+   Integral and floating types are collectively called arithmetic
+   types.
+
+   As a GNU extension, we also accept complex types.
+
+   Keep these checks in ascending code order.  */
+#define ARITHMETIC_TYPE_P(TYPE)                                                \
+  (RS_INTEGRAL_TYPE_P (TYPE) || TREE_CODE (TYPE) == REAL_TYPE                  \
+   || TREE_CODE (TYPE) == COMPLEX_TYPE)
+
+/* True iff TYPE is cv decltype(nullptr).  */
+#define NULLPTR_TYPE_P(TYPE) (TREE_CODE (TYPE) == NULLPTR_TYPE)
+
+/* [basic.types]
+
+   Arithmetic types, enumeration types, pointer types,
+   pointer-to-member types, and std::nullptr_t are collectively called
+   scalar types.
+
+   Keep these checks in ascending code order.  */
+#define SCALAR_TYPE_P(TYPE)                                                    \
+  (TREE_CODE (TYPE) == ENUMERAL_TYPE || ARITHMETIC_TYPE_P (TYPE)               \
+   || TYPE_PTR_P (TYPE) || NULLPTR_TYPE_P (TYPE))
+
+/* True if NODE is an implicit INDIRECT_REF from convert_from_reference.  */
+#define REFERENCE_REF_P(NODE)                                                  \
+  (INDIRECT_REF_P (NODE) && TREE_TYPE (TREE_OPERAND (NODE, 0))                 \
+   && TYPE_REF_P (TREE_TYPE (TREE_OPERAND ((NODE), 0))))
+
+// this is a helper to differentiate RECORD types between actual records and
+// slices
+#define SLICE_FLAG TREE_LANG_FLAG_0
+#define SLICE_TYPE_P(TYPE)                                                     \
+  (TREE_CODE (TYPE) == RECORD_TYPE && TREE_LANG_FLAG_0 (TYPE))
+
+/* Returns true if NODE is a pointer to member function type.  */
+#define TYPE_PTRMEMFUNC_P(NODE)                                                \
+  (TREE_CODE (NODE) == RECORD_TYPE && TYPE_PTRMEMFUNC_FLAG (NODE))
+
+#define TYPE_PTRMEMFUNC_FLAG(NODE) (TYPE_LANG_FLAG_2 (RECORD_TYPE_CHECK (NODE)))
+
+#define TYPE_PTRMEMFUNC_FN_TYPE_RAW(NODE) (TREE_TYPE (TYPE_FIELDS (NODE)))
+
+/* True if NODE is a compound-literal, i.e., a brace-enclosed
+   initializer cast to a particular type.  This is mostly only set during
+   template parsing; once the initializer has been digested into an actual
+   value of the type, the expression is represented by a TARGET_EXPR.  */
+#define COMPOUND_LITERAL_P(NODE)                                               \
+  (TREE_CODE (NODE) == CONSTRUCTOR && TREE_HAS_CONSTRUCTOR (NODE))
+
+/* When appearing in an INDIRECT_REF, it means that the tree structure
+   underneath is actually a call to a constructor.  This is needed
+   when the constructor must initialize local storage (which can
+   be automatically destroyed), rather than allowing it to allocate
+   space from the heap.
+
+   When appearing in a SAVE_EXPR, it means that underneath
+   is a call to a constructor.
+
+   When appearing in a CONSTRUCTOR, the expression is an unconverted
+   compound literal.
+
+   When appearing in a FIELD_DECL, it means that this field
+   has been duly initialized in its constructor.  */
+#define TREE_HAS_CONSTRUCTOR(NODE) (TREE_LANG_FLAG_4 (NODE))
+
+/* Nonzero if T is a class type.  Zero for template type parameters,
+   typename types, and so forth.  */
+#define CLASS_TYPE_P(T)                                                        \
+  (RECORD_OR_UNION_CODE_P (TREE_CODE (T)) && TYPE_LANG_FLAG_5 (T))
+
+/* [class.virtual]
+
+   A class that declares or inherits a virtual function is called a
+   polymorphic class.  */
+#define TYPE_POLYMORPHIC_P(NODE) (TREE_LANG_FLAG_2 (NODE))
+
+/* Nonzero if this class has a virtual function table pointer.  */
+#define TYPE_CONTAINS_VPTR_P(NODE)                                             \
+  (TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE))
+
+/* A vector of BINFOs for the direct and indirect virtual base classes
+   that this type uses in a post-order depth-first left-to-right
+   order.  (In other words, these bases appear in the order that they
+   should be initialized.)  */
+#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
+
+/* A vector of BINFOs for the direct and indirect virtual base classes
+   that this type uses in a post-order depth-first left-to-right
+   order.  (In other words, these bases appear in the order that they
+   should be initialized.)  */
+#define CLASSTYPE_VBASECLASSES(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->vbases)
+
+/* We used to have a variant type for lang_type.  Keep the name of the
+   checking accessor for the sole survivor.  */
+#define LANG_TYPE_CLASS_CHECK(NODE) (TYPE_LANG_SPECIFIC (NODE))
+
+/* Keep these checks in ascending code order.  */
+#define RECORD_OR_UNION_CODE_P(T) ((T) == RECORD_TYPE || (T) == UNION_TYPE)
+#define OVERLOAD_TYPE_P(T) (CLASS_TYPE_P (T) || TREE_CODE (T) == ENUMERAL_TYPE)
+
+/* Nonzero if this class is "empty" in the sense of the C++ ABI.  */
+#define CLASSTYPE_EMPTY_P(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->empty_p)
+
+/* True if DECL is declared 'constexpr'.  */
+#define DECL_DECLARED_CONSTEXPR_P(DECL)                                        \
+  DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (DECL))
+
+#define VAR_OR_FUNCTION_DECL_CHECK(NODE)                                       \
+  TREE_CHECK2 (NODE, VAR_DECL, FUNCTION_DECL)
+
+// Below macros are copied from gcc/c-family/c-common.h
+
+/* In a FIELD_DECL, nonzero if the decl was originally a bitfield.  */
+#define DECL_C_BIT_FIELD(NODE) (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) == 1)
+#define SET_DECL_C_BIT_FIELD(NODE)                                             \
+  (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 1)
+#define CLEAR_DECL_C_BIT_FIELD(NODE)                                           \
+  (DECL_LANG_FLAG_4 (FIELD_DECL_CHECK (NODE)) = 0)
+
+/* True if the decl was an unnamed bitfield.  */
+#define DECL_UNNAMED_BIT_FIELD(NODE)                                           \
+  (DECL_C_BIT_FIELD (NODE) && !DECL_NAME (NODE))
+
+/* 1 iff NODE is function-local.  */
+#define DECL_FUNCTION_SCOPE_P(NODE)                                            \
+  (DECL_CONTEXT (NODE) && TREE_CODE (DECL_CONTEXT (NODE)) == FUNCTION_DECL)
+
+/* Nonzero if this type is const-qualified, but not
+   volatile-qualified.  Other qualifiers are ignored.  This macro is
+   used to test whether or not it is OK to bind an rvalue to a
+   reference.  */
+#define RS_TYPE_CONST_NON_VOLATILE_P(NODE)                                     \
+  ((rs_type_quals (NODE) & (TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE))             \
+   == TYPE_QUAL_CONST)
+
+/* [basic.fundamental]
+
+   Types  bool, char, wchar_t, and the signed and unsigned integer types
+   are collectively called integral types.
+
+   Note that INTEGRAL_TYPE_P, as defined in tree.h, allows enumeration
+   types as well, which is incorrect in C++.  Keep these checks in
+   ascending code order.  */
+#define RS_INTEGRAL_TYPE_P(TYPE)                                               \
+  (TREE_CODE (TYPE) == BOOLEAN_TYPE || TREE_CODE (TYPE) == INTEGER_TYPE)
+
+/* Returns true if TYPE is an integral or enumeration name.  Keep
+   these checks in ascending code order.  */
+#define INTEGRAL_OR_ENUMERATION_TYPE_P(TYPE)                                   \
+  (TREE_CODE (TYPE) == ENUMERAL_TYPE || RS_INTEGRAL_TYPE_P (TYPE))
+
+/* Nonzero for a VAR_DECL that was initialized with a
+   constant-expression.  */
+#define DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P(NODE)                        \
+  (TREE_LANG_FLAG_2 (VAR_DECL_CHECK (NODE)))
+
+// Above macros are copied from gcc/c-family/c-common.h
+
+// forked from gcc/cp/cp-tree.h treee_pair_s
+
+struct GTY (()) tree_pair_s
+{
+  tree purpose;
+  tree value;
+};
+
+// forked from gcc/cp/cp-tree.h tree_pair_p
+
+typedef tree_pair_s *tree_pair_p;
+
+// forked from gcc/cp/cp-tree.h lang_type
+
+/* This structure provides additional information above and beyond
+   what is provide in the ordinary tree_type.  In the past, we used it
+   for the types of class types, template parameters types, typename
+   types, and so forth.  However, there can be many (tens to hundreds
+   of thousands) of template parameter types in a compilation, and
+   there's no need for this additional information in that case.
+   Therefore, we now use this data structure only for class types.
+
+   In the past, it was thought that there would be relatively few
+   class types.  However, in the presence of heavy use of templates,
+   many (i.e., thousands) of classes can easily be generated.
+   Therefore, we should endeavor to keep the size of this structure to
+   a minimum.  */
+struct GTY (()) lang_type
+{
+  unsigned char align;
+
+  unsigned has_type_conversion : 1;
+  unsigned has_copy_ctor : 1;
+  unsigned has_default_ctor : 1;
+  unsigned const_needs_init : 1;
+  unsigned ref_needs_init : 1;
+  unsigned has_const_copy_assign : 1;
+  unsigned use_template : 2;
+
+  unsigned has_mutable : 1;
+  unsigned com_interface : 1;
+  unsigned non_pod_class : 1;
+  unsigned nearly_empty_p : 1;
+  unsigned user_align : 1;
+  unsigned has_copy_assign : 1;
+  unsigned has_new : 1;
+  unsigned has_array_new : 1;
+
+  unsigned gets_delete : 2;
+  unsigned interface_only : 1;
+  unsigned interface_unknown : 1;
+  unsigned contains_empty_class_p : 1;
+  unsigned anon_aggr : 1;
+  unsigned non_zero_init : 1;
+  unsigned empty_p : 1;
+  /* 32 bits allocated.  */
+
+  unsigned vec_new_uses_cookie : 1;
+  unsigned declared_class : 1;
+  unsigned diamond_shaped : 1;
+  unsigned repeated_base : 1;
+  unsigned being_defined : 1;
+  unsigned debug_requested : 1;
+  unsigned fields_readonly : 1;
+  unsigned ptrmemfunc_flag : 1;
+
+  unsigned lazy_default_ctor : 1;
+  unsigned lazy_copy_ctor : 1;
+  unsigned lazy_copy_assign : 1;
+  unsigned lazy_destructor : 1;
+  unsigned has_const_copy_ctor : 1;
+  unsigned has_complex_copy_ctor : 1;
+  unsigned has_complex_copy_assign : 1;
+  unsigned non_aggregate : 1;
+
+  unsigned has_complex_dflt : 1;
+  unsigned has_list_ctor : 1;
+  unsigned non_std_layout : 1;
+  unsigned is_literal : 1;
+  unsigned lazy_move_ctor : 1;
+  unsigned lazy_move_assign : 1;
+  unsigned has_complex_move_ctor : 1;
+  unsigned has_complex_move_assign : 1;
+
+  unsigned has_constexpr_ctor : 1;
+  unsigned unique_obj_representations : 1;
+  unsigned unique_obj_representations_set : 1;
+  bool erroneous : 1;
+  bool non_pod_aggregate : 1;
+
+  /* When adding a flag here, consider whether or not it ought to
+     apply to a template instance if it applies to the template.  If
+     so, make sure to copy it in instantiate_class_template!  */
+
+  /* There are some bits left to fill out a 32-bit word.  Keep track
+     of this by updating the size of this bitfield whenever you add or
+     remove a flag.  */
+  unsigned dummy : 3;
+
+  tree primary_base;
+  vec<tree_pair_s, va_gc> *vcall_indices;
+  tree vtables;
+  tree typeinfo_var;
+  vec<tree, va_gc> *vbases;
+  tree as_base;
+  vec<tree, va_gc> *pure_virtuals;
+  tree friend_classes;
+  vec<tree, va_gc> *GTY ((reorder ("resort_type_member_vec"))) members;
+  tree key_method;
+  tree decl_list;
+  tree befriending_classes;
+  /* In a RECORD_TYPE, information specific to Objective-C++, such
+     as a list of adopted protocols or a pointer to a corresponding
+     @interface.  See objc/objc-act.h for details.  */
+  tree objc_info;
+  /* FIXME reuse another field?  */
+  tree lambda_expr;
+};
+
+namespace Rust {
+
+// forked from gcc/cp/cp-tree.h tsubst_flags_t
+
+/* This type is used for parameters and variables which hold
+   combinations of the flags in enum tsubst_flags.  */
+typedef int tsubst_flags_t;
+
+// forked from gcc/cp/cvt.cc convert_to_void
+//
+// When an expression is used in a void context, its value is discarded and
+// no lvalue-rvalue and similar conversions happen [expr.static.cast/4,
+// stmt.expr/1, expr.comma/1].  This permits dereferencing an incomplete type
+// in a void context. The C++ standard does not define what an `access' to an
+// object is, but there is reason to believe that it is the lvalue to rvalue
+// conversion -- if it were not, `*&*p = 1' would violate [expr]/4 in that it
+// accesses `*p' not to calculate the value to be stored. But, dcl.type.cv/8
+// indicates that volatile semantics should be the same between C and C++
+// where ever possible. C leaves it implementation defined as to what
+// constitutes an access to a volatile. So, we interpret `*vp' as a read of
+// the volatile object `vp' points to, unless that is an incomplete type. For
+// volatile references we do not do this interpretation, because that would
+// make it impossible to ignore the reference return value from functions. We
+// issue warnings in the confusing cases.
+//
+// The IMPLICIT is ICV_CAST when the user is explicitly converting an
+// expression to void via a cast. If an expression is being implicitly
+// converted, IMPLICIT indicates the context of the implicit conversion.
+
+/* Possible cases of implicit or explicit bad conversions to void. */
+enum impl_conv_void
+{
+  ICV_CAST,	      /* (explicit) conversion to void */
+  ICV_SECOND_OF_COND, /* second operand of conditional expression */
+  ICV_THIRD_OF_COND,  /* third operand of conditional expression */
+  ICV_RIGHT_OF_COMMA, /* right operand of comma operator */
+  ICV_LEFT_OF_COMMA,  /* left operand of comma operator */
+  ICV_STATEMENT,      /* statement */
+  ICV_THIRD_IN_FOR    /* for increment expression */
+};
+
+/* BUILT_IN_FRONTEND function codes.  */
+enum rs_built_in_function
+{
+  RS_BUILT_IN_IS_CONSTANT_EVALUATED,
+  RS_BUILT_IN_INTEGER_PACK,
+  RS_BUILT_IN_IS_CORRESPONDING_MEMBER,
+  RS_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS,
+  RS_BUILT_IN_SOURCE_LOCATION,
+  RS_BUILT_IN_LAST
+};
+
+extern tree
+convert_to_void (tree expr, impl_conv_void implicit);
+
+// The lvalue-to-rvalue conversion (7.1) is applied if and only if the
+// expression is a glvalue of volatile-qualified type and it is one of the
+// following:
+// * ( expression ), where expression is one of these expressions,
+// * id-expression (8.1.4),
+// * subscripting (8.2.1),
+// * class member access (8.2.5),
+// * indirection (8.3.1),
+// * pointer-to-member operation (8.5),
+// * conditional expression (8.16) where both the second and the third
+//   operands are one of these expressions, or
+// * comma expression (8.19) where the right operand is one of these
+//   expressions.
+extern tree
+mark_discarded_use (tree expr);
+
+// Mark EXP as read, not just set, for set but not used -Wunused warning
+// purposes.
+extern void
+mark_exp_read (tree exp);
+
+// We've seen an actual use of EXPR.  Possibly replace an outer variable
+// reference inside with its constant value or a lambda capture.
+extern tree
+mark_use (tree expr, bool rvalue_p, bool read_p, location_t loc,
+	  bool reject_builtin);
+
+// Called whenever the expression EXPR is used in an rvalue context.
+// When REJECT_BUILTIN is true the expression is checked to make sure
+// it doesn't make it possible to obtain the address of a GCC built-in
+// function with no library fallback (or any of its bits, such as in
+// a conversion to bool).
+extern tree
+mark_rvalue_use (tree e, location_t loc /* = UNKNOWN_LOCATION */,
+		 bool reject_builtin /* = true */);
+
+// Called whenever an expression is used in an lvalue context.
+extern tree
+mark_lvalue_use (tree expr);
+
+// As above, but don't consider this use a read.
+extern tree
+mark_lvalue_use_nonread (tree expr);
+
+// We are using a reference VAL for its value. Bash that reference all the way
+// down to its lowest form.
+extern tree
+convert_from_reference (tree val);
+
+// Subroutine of convert_to_void.  Warn if we're discarding something with
+// attribute [[nodiscard]].
+extern void
+maybe_warn_nodiscard (tree expr, impl_conv_void implicit);
+
+extern location_t
+expr_loc_or_loc (const_tree t, location_t or_loc);
+
+extern location_t
+expr_loc_or_input_loc (const_tree t);
+
+// FN is the callee of a CALL_EXPR or AGGR_INIT_EXPR; return the FUNCTION_DECL
+// if we can.
+extern tree
+get_fndecl_from_callee (tree fn);
+
+// FIXME some helpers from HIRCompileBase could probably be moved here over time
+
+// Return an expression for the address of BASE[INDEX], used in offset intrinsic
+extern tree
+pointer_offset_expression (tree base_tree, tree index_tree, location_t locus);
+
+/* A tree node, together with a location, so that we can track locations
+   (and ranges) during parsing.
+
+   The location is redundant for node kinds that have locations,
+   but not all node kinds do (e.g. constants, and references to
+   params, locals, etc), so we stash a copy here.  */
+
+extern location_t rs_expr_location (const_tree);
+
+extern int
+is_empty_class (tree type);
+
+extern tree array_type_nelts_top (tree);
+
+extern bool
+is_really_empty_class (tree, bool);
+
+extern bool builtin_valid_in_constant_expr_p (const_tree);
+
+extern bool maybe_constexpr_fn (tree);
+
+extern bool var_in_maybe_constexpr_fn (tree);
+
+extern int
+rs_type_quals (const_tree type);
+
+extern bool decl_maybe_constant_var_p (tree);
+
+extern tree
+rs_walk_subtrees (tree *, int *, walk_tree_fn, void *, hash_set<tree> *);
+#define rs_walk_tree(tp, func, data, pset)                                     \
+  walk_tree_1 (tp, func, data, pset, rs_walk_subtrees)
+#define rs_walk_tree_without_duplicates(tp, func, data)                        \
+  walk_tree_without_duplicates_1 (tp, func, data, rs_walk_subtrees)
+
+// forked from gcc/cp/cp-tree.h cp_expr_loc_or_loc
+
+inline location_t
+rs_expr_loc_or_loc (const_tree t, location_t or_loc)
+{
+  location_t loc = rs_expr_location (t);
+  if (loc == UNKNOWN_LOCATION)
+    loc = or_loc;
+  return loc;
+}
+
+// forked from gcc/cp/cp-tree.h cp_expr_loc_or_input_loc
+
+inline location_t
+rs_expr_loc_or_input_loc (const_tree t)
+{
+  return rs_expr_loc_or_loc (t, input_location);
+}
+
+} // namespace Rust
+
+#endif // RUST_TREE
diff --git a/gcc/rust/rust-backend.h b/gcc/rust/rust-backend.h
new file mode 100644
index 00000000000..126283c1a54
--- /dev/null
+++ b/gcc/rust/rust-backend.h
@@ -0,0 +1,506 @@
+// Copyright (C) 2020-2022 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_BACKEND_H
+#define RUST_BACKEND_H
+
+#include <gmp.h>
+#include <mpfr.h>
+#include <mpc.h>
+
+#include "rust-location.h"
+#include "rust-linemap.h"
+#include "rust-diagnostics.h"
+#include "operator.h"
+#include "tree.h"
+
+// Pointers to these types are created by the backend, passed to the
+// frontend, and passed back to the backend.  The types must be
+// defined by the backend using these names.
+
+// The backend representation of a variable.
+class Bvariable;
+
+// The backend interface.  This is a pure abstract class that a
+// specific backend will implement.
+
+class Backend
+{
+public:
+  virtual ~Backend () {}
+
+  // Name/type/location.  Used for function parameters, struct fields,
+  // interface methods.
+  struct typed_identifier
+  {
+    std::string name;
+    tree type;
+    Location location;
+
+    typed_identifier ()
+      : name (), type (NULL_TREE), location (Linemap::unknown_location ())
+    {}
+
+    typed_identifier (const std::string &a_name, tree a_type,
+		      Location a_location)
+      : name (a_name), type (a_type), location (a_location)
+    {}
+  };
+
+  // debug
+  virtual void debug (tree) = 0;
+  virtual void debug (Bvariable *) = 0;
+
+  virtual tree get_identifier_node (const std::string &str) = 0;
+
+  // Types.
+
+  // get unit-type
+  virtual tree unit_type () = 0;
+
+  // Get the unnamed boolean type.
+  virtual tree bool_type () = 0;
+
+  // Get the char type
+  virtual tree char_type () = 0;
+
+  // Get the wchar type
+  virtual tree wchar_type () = 0;
+
+  // Get the Host pointer size in bits
+  virtual int get_pointer_size () = 0;
+
+  // Get the raw str type const char*
+  virtual tree raw_str_type () = 0;
+
+  // Get an unnamed integer type with the given signedness and number
+  // of bits.
+  virtual tree integer_type (bool is_unsigned, int bits) = 0;
+
+  // Get an unnamed floating point type with the given number of bits
+  // (32 or 64).
+  virtual tree float_type (int bits) = 0;
+
+  // Get an unnamed complex type with the given number of bits (64 or 128).
+  virtual tree complex_type (int bits) = 0;
+
+  // Get a pointer type.
+  virtual tree pointer_type (tree to_type) = 0;
+
+  // Get a reference type.
+  virtual tree reference_type (tree to_type) = 0;
+
+  // make type immutable
+  virtual tree immutable_type (tree base) = 0;
+
+  // Get a function type.  The receiver, parameter, and results are
+  // generated from the types in the Function_type.  The Function_type
+  // is provided so that the names are available.  This should return
+  // not the type of a Go function (which is a pointer to a struct)
+  // but the type of a C function pointer (which will be used as the
+  // type of the first field of the struct).  If there is more than
+  // one result, RESULT_STRUCT is a struct type to hold the results,
+  // and RESULTS may be ignored; if there are zero or one results,
+  // RESULT_STRUCT is NULL.
+  virtual tree function_type (const typed_identifier &receiver,
+			      const std::vector<typed_identifier> &parameters,
+			      const std::vector<typed_identifier> &results,
+			      tree result_struct, Location location)
+    = 0;
+
+  virtual tree
+  function_type_varadic (const typed_identifier &receiver,
+			 const std::vector<typed_identifier> &parameters,
+			 const std::vector<typed_identifier> &results,
+			 tree result_struct, Location location)
+    = 0;
+
+  virtual tree function_ptr_type (tree result,
+				  const std::vector<tree> &praameters,
+				  Location location)
+    = 0;
+
+  // Get a struct type.
+  virtual tree struct_type (const std::vector<typed_identifier> &fields) = 0;
+
+  // Get a union type.
+  virtual tree union_type (const std::vector<typed_identifier> &fields) = 0;
+
+  // Get an array type.
+  virtual tree array_type (tree element_type, tree length) = 0;
+
+  // Return a named version of a type.  The location is the location
+  // of the type definition.  This will not be called for a type
+  // created via placeholder_pointer_type, placeholder_struct_type, or
+  // placeholder_array_type..  (It may be called for a pointer,
+  // struct, or array type in a case like "type P *byte; type Q P".)
+  virtual tree named_type (const std::string &name, tree, Location) = 0;
+
+  // Return the size of a type.
+  virtual int64_t type_size (tree) = 0;
+
+  // Return the alignment of a type.
+  virtual int64_t type_alignment (tree) = 0;
+
+  // Return the alignment of a struct field of this type.  This is
+  // normally the same as type_alignment, but not always.
+  virtual int64_t type_field_alignment (tree) = 0;
+
+  // Return the offset of field INDEX in a struct type.  INDEX is the
+  // entry in the FIELDS std::vector parameter of struct_type or
+  // set_placeholder_struct_type.
+  virtual int64_t type_field_offset (tree, size_t index) = 0;
+
+  // Expressions.
+
+  // Return an expression for a zero value of the given type.  This is
+  // used for cases such as local variable initialization and
+  // converting nil to other types.
+  virtual tree zero_expression (tree) = 0;
+
+  virtual tree unit_expression () = 0;
+
+  // Create a reference to a variable.
+  virtual tree var_expression (Bvariable *var, Location) = 0;
+
+  // Return an expression for the multi-precision integer VAL in BTYPE.
+  virtual tree integer_constant_expression (tree btype, mpz_t val) = 0;
+
+  // Return an expression for the floating point value VAL in BTYPE.
+  virtual tree float_constant_expression (tree btype, mpfr_t val) = 0;
+
+  // Return an expression for the complex value VAL in BTYPE.
+  virtual tree complex_constant_expression (tree btype, mpc_t val) = 0;
+
+  // Return an expression for the string value VAL.
+  virtual tree string_constant_expression (const std::string &val) = 0;
+
+  // Get a char literal
+  virtual tree char_constant_expression (char c) = 0;
+
+  // Get a char literal
+  virtual tree wchar_constant_expression (wchar_t c) = 0;
+
+  // Return an expression for the boolean value VAL.
+  virtual tree boolean_constant_expression (bool val) = 0;
+
+  // Return an expression for the real part of BCOMPLEX.
+  virtual tree real_part_expression (tree bcomplex, Location) = 0;
+
+  // Return an expression for the imaginary part of BCOMPLEX.
+  virtual tree imag_part_expression (tree bcomplex, Location) = 0;
+
+  // Return an expression for the complex number (BREAL, BIMAG).
+  virtual tree complex_expression (tree breal, tree bimag, Location) = 0;
+
+  // Return an expression that converts EXPR to TYPE.
+  virtual tree convert_expression (tree type, tree expr, Location) = 0;
+
+  // Return an expression for the field at INDEX in BSTRUCT.
+  virtual tree struct_field_expression (tree bstruct, size_t index, Location)
+    = 0;
+
+  // Create an expression that executes BSTAT before BEXPR.
+  virtual tree compound_expression (tree bstat, tree bexpr, Location) = 0;
+
+  // Return an expression that executes THEN_EXPR if CONDITION is true, or
+  // ELSE_EXPR otherwise and returns the result as type BTYPE, within the
+  // specified function FUNCTION.  ELSE_EXPR may be NULL.  BTYPE may be NULL.
+  virtual tree conditional_expression (tree function, tree btype,
+				       tree condition, tree then_expr,
+				       tree else_expr, Location)
+    = 0;
+
+  // Return an expression for the negation operation OP EXPR.
+  // Supported values of OP are enumerated in NegationOperator.
+  virtual tree negation_expression (NegationOperator op, tree expr, Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in ArithmeticOrLogicalOperator.
+  virtual tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+						 tree left, tree right,
+						 Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in ComparisonOperator.
+  virtual tree comparison_expression (ComparisonOperator op, tree left,
+				      tree right, Location)
+    = 0;
+
+  // Return an expression for the operation LEFT OP RIGHT.
+  // Supported values of OP are enumerated in LazyBooleanOperator.
+  virtual tree lazy_boolean_expression (LazyBooleanOperator op, tree left,
+					tree right, Location)
+    = 0;
+
+  // Return an expression that constructs BTYPE with VALS.  BTYPE must be the
+  // backend representation a of struct.  VALS must be in the same order as the
+  // corresponding fields in BTYPE.
+  virtual tree constructor_expression (tree btype, bool is_variant,
+				       const std::vector<tree> &vals, int,
+				       Location)
+    = 0;
+
+  // Return an expression that constructs an array of BTYPE with INDEXES and
+  // VALS.  INDEXES and VALS must have the same amount of elements. Each index
+  // in INDEXES must be in the same order as the corresponding value in VALS.
+  virtual tree
+  array_constructor_expression (tree btype,
+				const std::vector<unsigned long> &indexes,
+				const std::vector<tree> &vals, Location)
+    = 0;
+
+  virtual tree array_initializer (tree, tree, tree, tree, tree, tree *,
+				  Location)
+    = 0;
+
+  // Return an expression for ARRAY[INDEX] as an l-value.  ARRAY is a valid
+  // fixed-length array, not a slice.
+  virtual tree array_index_expression (tree array, tree index, Location) = 0;
+
+  // Create an expression for a call to FN with ARGS, taking place within
+  // caller CALLER.
+  virtual tree call_expression (tree fn, const std::vector<tree> &args,
+				tree static_chain, Location)
+    = 0;
+
+  // Statements.
+
+  // Create a variable initialization statement in the specified
+  // function.  This initializes a local variable at the point in the
+  // program flow where it is declared.
+  virtual tree init_statement (tree, Bvariable *var, tree init) = 0;
+
+  // Create an assignment statement within the specified function.
+  virtual tree assignment_statement (tree lhs, tree rhs, Location) = 0;
+
+  // Create a return statement, passing the representation of the
+  // function and the list of values to return.
+  virtual tree return_statement (tree, const std::vector<tree> &, Location) = 0;
+
+  // Create an if statement within a function.  ELSE_BLOCK may be NULL.
+  virtual tree if_statement (tree, tree condition, tree then_block,
+			     tree else_block, Location)
+    = 0;
+
+  // infinite loop expressions
+  virtual tree loop_expression (tree body, Location) = 0;
+
+  // exit expressions
+  virtual tree exit_expression (tree condition, Location) = 0;
+
+  // Create a single statement from two statements.
+  virtual tree compound_statement (tree, tree) = 0;
+
+  // Create a single statement from a list of statements.
+  virtual tree statement_list (const std::vector<tree> &) = 0;
+
+  // Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if
+  // an exception occurs. EXCEPT_STMT may be NULL.  FINALLY_STMT may be NULL and
+  // if not NULL, it will always be executed.  This is used for handling defers
+  // in Go functions.  In C++, the resulting code is of this form:
+  //   try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
+  virtual tree exception_handler_statement (tree bstat, tree except_stmt,
+					    tree finally_stmt, Location)
+    = 0;
+
+  // Blocks.
+
+  // Create a block.  The frontend will call this function when it
+  // starts converting a block within a function.  FUNCTION is the
+  // current function.  ENCLOSING is the enclosing block; it will be
+  // NULL for the top-level block in a function.  VARS is the list of
+  // local variables defined within this block; each entry will be
+  // created by the local_variable function.  START_LOCATION is the
+  // location of the start of the block, more or less the location of
+  // the initial curly brace.  END_LOCATION is the location of the end
+  // of the block, more or less the location of the final curly brace.
+  // The statements will be added after the block is created.
+  virtual tree block (tree function, tree enclosing,
+		      const std::vector<Bvariable *> &vars,
+		      Location start_location, Location end_location)
+    = 0;
+
+  // Add the statements to a block.  The block is created first.  Then
+  // the statements are created.  Then the statements are added to the
+  // block.  This will called exactly once per block.  The vector may
+  // be empty if there are no statements.
+  virtual void block_add_statements (tree, const std::vector<tree> &) = 0;
+
+  // Variables.
+
+  // Create an error variable.  This is used for cases which should
+  // not occur in a correct program, in order to keep the compilation
+  // going without crashing.
+  virtual Bvariable *error_variable () = 0;
+
+  // Create a global variable. NAME is the package-qualified name of
+  // the variable.  ASM_NAME is the encoded identifier for the
+  // variable, incorporating the package, and made safe for the
+  // assembler.  BTYPE is the type of the variable.  IS_EXTERNAL is
+  // true if the variable is defined in some other package.  IS_HIDDEN
+  // is true if the variable is not exported (name begins with a lower
+  // case letter).  IN_UNIQUE_SECTION is true if the variable should
+  // be put into a unique section if possible; this is intended to
+  // permit the linker to garbage collect the variable if it is not
+  // referenced.  LOCATION is where the variable was defined.
+  virtual Bvariable *global_variable (const std::string &name,
+				      const std::string &asm_name, tree btype,
+				      bool is_external, bool is_hidden,
+				      bool in_unique_section, Location location)
+    = 0;
+
+  // A global variable will 1) be initialized to zero, or 2) be
+  // initialized to a constant value, or 3) be initialized in the init
+  // function.  In case 2, the frontend will call
+  // global_variable_set_init to set the initial value.  If this is
+  // not called, the backend should initialize a global variable to 0.
+  // The init function may then assign a value to it.
+  virtual void global_variable_set_init (Bvariable *, tree) = 0;
+
+  // Create a local variable.  The frontend will create the local
+  // variables first, and then create the block which contains them.
+  // FUNCTION is the function in which the variable is defined.  NAME
+  // is the name of the variable.  TYPE is the type.  DECL_VAR, if not
+  // null, gives the location at which the value of this variable may
+  // be found, typically used to create an inner-scope reference to an
+  // outer-scope variable, to extend the lifetime of the variable beyond
+  // the inner scope.  IS_ADDRESS_TAKEN is true if the address of this
+  // variable is taken (this implies that the address does not escape
+  // the function, as otherwise the variable would be on the heap).
+  // LOCATION is where the variable is defined.  For each local variable
+  // the frontend will call init_statement to set the initial value.
+  virtual Bvariable *local_variable (tree function, const std::string &name,
+				     tree type, Bvariable *decl_var,
+				     Location location)
+    = 0;
+
+  // Create a function parameter.  This is an incoming parameter, not
+  // a result parameter (result parameters are treated as local
+  // variables).  The arguments are as for local_variable.
+  virtual Bvariable *parameter_variable (tree function, const std::string &name,
+					 tree type, Location location)
+    = 0;
+
+  // Create a static chain parameter.  This is the closure parameter.
+  virtual Bvariable *static_chain_variable (tree function,
+					    const std::string &name, tree type,
+					    Location location)
+    = 0;
+
+  // Create a temporary variable.  A temporary variable has no name,
+  // just a type.  We pass in FUNCTION and BLOCK in case they are
+  // needed.  If INIT is not NULL, the variable should be initialized
+  // to that value.  Otherwise the initial value is irrelevant--the
+  // backend does not have to explicitly initialize it to zero.
+  // ADDRESS_IS_TAKEN is true if the programs needs to take the
+  // address of this temporary variable.  LOCATION is the location of
+  // the statement or expression which requires creating the temporary
+  // variable, and may not be very useful.  This function should
+  // return a variable which can be referenced later and should set
+  // *PSTATEMENT to a statement which initializes the variable.
+  virtual Bvariable *temporary_variable (tree, tree, tree, tree init,
+					 bool address_is_taken,
+					 Location location, tree *pstatement)
+    = 0;
+
+  // Labels.
+
+  // Create a new label.  NAME will be empty if this is a label
+  // created by the frontend for a loop construct.  The location is
+  // where the label is defined.
+  virtual tree label (tree, const std::string &name, Location) = 0;
+
+  // Create a statement which defines a label.  This statement will be
+  // put into the codestream at the point where the label should be
+  // defined.
+  virtual tree label_definition_statement (tree) = 0;
+
+  // Create a goto statement to a label.
+  virtual tree goto_statement (tree, Location) = 0;
+
+  // Create an expression for the address of a label.  This is used to
+  // get the return address of a deferred function which may call
+  // recover.
+  virtual tree label_address (tree, Location) = 0;
+
+  // Functions.
+
+  // Bit flags to pass to the function method.
+
+  // Set if this is a function declaration rather than a definition;
+  // the definition will be in another compilation unit.
+  static const unsigned int function_is_declaration = 1 << 0;
+
+  // Set if the function should never be inlined because they call
+  // recover and must be visible for correct panic recovery.
+  static const unsigned int function_is_uninlinable = 1 << 1;
+
+  // Set if the function does not return.  This is set for the
+  // implementation of panic.
+  static const unsigned int function_does_not_return = 1 << 2;
+
+  // Set if the function should be put in a unique section if
+  // possible.  This is used for field tracking.
+  static const unsigned int function_in_unique_section = 1 << 3;
+
+  // Declare or define a function of FNTYPE.
+  // NAME is the Go name of the function.  ASM_NAME, if not the empty
+  // string, is the name that should be used in the symbol table; this
+  // will be non-empty if a magic extern comment is used.  FLAGS is
+  // bit flags described above.
+  virtual tree function (tree fntype, const std::string &name,
+			 const std::string &asm_name, unsigned int flags,
+			 Location)
+    = 0;
+
+  // Create a statement that runs all deferred calls for FUNCTION.  This should
+  // be a statement that looks like this in C++:
+  //   finish:
+  //     try { DEFER_RETURN; } catch { CHECK_DEFER; goto finish; }
+  virtual tree function_defer_statement (tree function, tree undefer,
+					 tree check_defer, Location)
+    = 0;
+
+  // Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
+  // This will only be called for a function definition.  Returns true on
+  // success, false on failure.
+  virtual bool
+  function_set_parameters (tree function,
+			   const std::vector<Bvariable *> &param_vars)
+    = 0;
+
+  // Utility.
+
+  // Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
+  // FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
+  virtual void
+  write_global_definitions (const std::vector<tree> &type_decls,
+			    const std::vector<tree> &constant_decls,
+			    const std::vector<tree> &function_decls,
+			    const std::vector<Bvariable *> &variable_decls)
+    = 0;
+
+  // Write SIZE bytes of export data from BYTES to the proper
+  // section in the output object file.
+  virtual void write_export_data (const char *bytes, unsigned int size) = 0;
+};
+
+#endif // RUST_BACKEND_H
diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc
new file mode 100644
index 00000000000..3a682fc0127
--- /dev/null
+++ b/gcc/rust/rust-gcc.cc
@@ -0,0 +1,2718 @@
+// rust-gcc.cc -- Rust frontend to gcc IR.
+// Copyright (C) 2011-2022 Free Software Foundation, Inc.
+// Contributed by Ian Lance Taylor, Google.
+// forked from gccgo
+
+// 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-system.h"
+
+// This has to be included outside of extern "C", so we have to
+// include it here before tree.h includes it later.
+#include <gmp.h>
+
+#include "tree.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "stringpool.h"
+#include "stor-layout.h"
+#include "varasm.h"
+#include "tree-iterator.h"
+#include "tm.h"
+#include "function.h"
+#include "cgraph.h"
+#include "convert.h"
+#include "gimple-expr.h"
+#include "gimplify.h"
+#include "langhooks.h"
+#include "toplev.h"
+#include "output.h"
+#include "realmpfr.h"
+#include "builtins.h"
+#include "print-tree.h"
+#include "attribs.h"
+
+#include "rust-location.h"
+#include "rust-linemap.h"
+#include "rust-backend.h"
+#include "rust-object-export.h"
+
+#include "backend/rust-tree.h"
+
+// TODO: this will have to be significantly modified to work with Rust
+
+// Bvariable is a bit more complicated, because of zero-sized types.
+// The GNU linker does not permit dynamic variables with zero size.
+// When we see such a variable, we generate a version of the type with
+// non-zero size.  However, when referring to the global variable, we
+// want an expression of zero size; otherwise, if, say, the global
+// variable is passed to a function, we will be passing a
+// non-zero-sized value to a zero-sized value, which can lead to a
+// miscompilation.
+
+class Bvariable
+{
+public:
+  Bvariable (tree t) : t_ (t), orig_type_ (NULL) {}
+
+  Bvariable (tree t, tree orig_type) : t_ (t), orig_type_ (orig_type) {}
+
+  // Get the tree for use as an expression.
+  tree get_tree (Location) const;
+
+  // Get the actual decl;
+  tree get_decl () const { return this->t_; }
+
+private:
+  tree t_;
+  tree orig_type_;
+};
+
+// Get the tree of a variable for use as an expression.  If this is a
+// zero-sized global, create an expression that refers to the decl but
+// has zero size.
+tree
+Bvariable::get_tree (Location location) const
+{
+  if (this->t_ == error_mark_node)
+    return error_mark_node;
+
+  TREE_USED (this->t_) = 1;
+  if (this->orig_type_ == NULL || TREE_TYPE (this->t_) == this->orig_type_)
+    {
+      return this->t_;
+    }
+
+  // Return *(orig_type*)&decl.  */
+  tree t = build_fold_addr_expr_loc (location.gcc_location (), this->t_);
+  t = fold_build1_loc (location.gcc_location (), NOP_EXPR,
+		       build_pointer_type (this->orig_type_), t);
+  return build_fold_indirect_ref_loc (location.gcc_location (), t);
+}
+
+// This file implements the interface between the Rust frontend proper
+// and the gcc IR.  This implements specific instantiations of
+// abstract classes defined by the Rust frontend proper.  The Rust
+// frontend proper class methods of these classes to generate the
+// backend representation.
+
+class Gcc_backend : public Backend
+{
+public:
+  Gcc_backend ();
+
+  void debug (tree t) { debug_tree (t); };
+  void debug (Bvariable *t) { debug_tree (t->get_decl ()); };
+
+  tree get_identifier_node (const std::string &str)
+  {
+    return get_identifier_with_length (str.data (), str.length ());
+  }
+
+  // Types.
+
+  tree unit_type ()
+  {
+    static tree unit_type;
+    if (unit_type == nullptr)
+      {
+	auto unit_type_node = struct_type ({});
+	unit_type = named_type ("()", unit_type_node,
+				::Linemap::predeclared_location ());
+      }
+
+    return unit_type;
+  }
+
+  tree bool_type () { return boolean_type_node; }
+
+  tree char_type () { return char_type_node; }
+
+  tree wchar_type ()
+  {
+    tree wchar = make_unsigned_type (32);
+    TYPE_STRING_FLAG (wchar) = 1;
+    return wchar;
+  }
+
+  int get_pointer_size ();
+
+  tree raw_str_type ();
+
+  tree integer_type (bool, int);
+
+  tree float_type (int);
+
+  tree complex_type (int);
+
+  tree pointer_type (tree);
+
+  tree reference_type (tree);
+
+  tree immutable_type (tree);
+
+  tree function_type (const typed_identifier &,
+		      const std::vector<typed_identifier> &,
+		      const std::vector<typed_identifier> &, tree,
+		      const Location);
+
+  tree function_type_varadic (const typed_identifier &,
+			      const std::vector<typed_identifier> &,
+			      const std::vector<typed_identifier> &, tree,
+			      const Location);
+
+  tree function_ptr_type (tree, const std::vector<tree> &, Location);
+
+  tree struct_type (const std::vector<typed_identifier> &);
+
+  tree union_type (const std::vector<typed_identifier> &);
+
+  tree array_type (tree, tree);
+
+  tree named_type (const std::string &, tree, Location);
+
+  int64_t type_size (tree);
+
+  int64_t type_alignment (tree);
+
+  int64_t type_field_alignment (tree);
+
+  int64_t type_field_offset (tree, size_t index);
+
+  // Expressions.
+
+  tree zero_expression (tree);
+
+  tree unit_expression () { return integer_zero_node; }
+
+  tree var_expression (Bvariable *var, Location);
+
+  tree integer_constant_expression (tree type, mpz_t val);
+
+  tree float_constant_expression (tree type, mpfr_t val);
+
+  tree complex_constant_expression (tree type, mpc_t val);
+
+  tree string_constant_expression (const std::string &val);
+
+  tree wchar_constant_expression (wchar_t c);
+
+  tree char_constant_expression (char c);
+
+  tree boolean_constant_expression (bool val);
+
+  tree real_part_expression (tree bcomplex, Location);
+
+  tree imag_part_expression (tree bcomplex, Location);
+
+  tree complex_expression (tree breal, tree bimag, Location);
+
+  tree convert_expression (tree type, tree expr, Location);
+
+  tree struct_field_expression (tree, size_t, Location);
+
+  tree compound_expression (tree, tree, Location);
+
+  tree conditional_expression (tree, tree, tree, tree, tree, Location);
+
+  tree negation_expression (NegationOperator op, tree expr, Location);
+
+  tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+					 tree left, tree right, Location);
+
+  tree comparison_expression (ComparisonOperator op, tree left, tree right,
+			      Location);
+
+  tree lazy_boolean_expression (LazyBooleanOperator op, tree left, tree right,
+				Location);
+
+  tree constructor_expression (tree, bool, const std::vector<tree> &, int,
+			       Location);
+
+  tree array_constructor_expression (tree, const std::vector<unsigned long> &,
+				     const std::vector<tree> &, Location);
+
+  tree array_initializer (tree, tree, tree, tree, tree, tree *, Location);
+
+  tree array_index_expression (tree array, tree index, Location);
+
+  tree call_expression (tree fn, const std::vector<tree> &args,
+			tree static_chain, Location);
+
+  // Statements.
+
+  tree init_statement (tree, Bvariable *var, tree init);
+
+  tree assignment_statement (tree lhs, tree rhs, Location);
+
+  tree return_statement (tree, const std::vector<tree> &, Location);
+
+  tree if_statement (tree, tree condition, tree then_block, tree else_block,
+		     Location);
+
+  tree compound_statement (tree, tree);
+
+  tree statement_list (const std::vector<tree> &);
+
+  tree exception_handler_statement (tree bstat, tree except_stmt,
+				    tree finally_stmt, Location);
+
+  tree loop_expression (tree body, Location);
+
+  tree exit_expression (tree condition, Location);
+
+  // Blocks.
+
+  tree block (tree, tree, const std::vector<Bvariable *> &, Location, Location);
+
+  void block_add_statements (tree, const std::vector<tree> &);
+
+  // Variables.
+
+  Bvariable *error_variable () { return new Bvariable (error_mark_node); }
+
+  Bvariable *global_variable (const std::string &var_name,
+			      const std::string &asm_name, tree type,
+			      bool is_external, bool is_hidden,
+			      bool in_unique_section, Location location);
+
+  void global_variable_set_init (Bvariable *, tree);
+
+  Bvariable *local_variable (tree, const std::string &, tree, Bvariable *,
+			     Location);
+
+  Bvariable *parameter_variable (tree, const std::string &, tree, Location);
+
+  Bvariable *static_chain_variable (tree, const std::string &, tree, Location);
+
+  Bvariable *temporary_variable (tree, tree, tree, tree, bool, Location,
+				 tree *);
+
+  // Labels.
+
+  tree label (tree, const std::string &name, Location);
+
+  tree label_definition_statement (tree);
+
+  tree goto_statement (tree, Location);
+
+  tree label_address (tree, Location);
+
+  // Functions.
+
+  tree function (tree fntype, const std::string &name,
+		 const std::string &asm_name, unsigned int flags, Location);
+
+  tree function_defer_statement (tree function, tree undefer, tree defer,
+				 Location);
+
+  bool function_set_parameters (tree function,
+				const std::vector<Bvariable *> &);
+
+  void write_global_definitions (const std::vector<tree> &,
+				 const std::vector<tree> &,
+				 const std::vector<tree> &,
+				 const std::vector<Bvariable *> &);
+
+  void write_export_data (const char *bytes, unsigned int size);
+
+private:
+  tree fill_in_fields (tree, const std::vector<typed_identifier> &);
+
+  tree fill_in_array (tree, tree, tree);
+
+  tree non_zero_size_type (tree);
+
+  tree convert_tree (tree, tree, Location);
+};
+
+// A helper function to create a GCC identifier from a C++ string.
+
+static inline tree
+get_identifier_from_string (const std::string &str)
+{
+  return get_identifier_with_length (str.data (), str.length ());
+}
+
+// Define the built-in functions that are exposed to GCCRust.
+
+Gcc_backend::Gcc_backend ()
+{
+  /* We need to define the fetch_and_add functions, since we use them
+     for ++ and --.  */
+  // tree t = this->integer_type (true, BITS_PER_UNIT)->get_tree ();
+  // tree p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_1,
+  // "__sync_fetch_and_add_1",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 2)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_2,
+  // "__sync_fetch_and_add_2",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 4)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_4,
+  // "__sync_fetch_and_add_4",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // t = this->integer_type (true, BITS_PER_UNIT * 8)->get_tree ();
+  // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE));
+  // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_8,
+  // "__sync_fetch_and_add_8",
+  //       		NULL, build_function_type_list (t, p, t, NULL_TREE), 0);
+
+  // // We use __builtin_expect for magic import functions.
+  // this->define_builtin (BUILT_IN_EXPECT, "__builtin_expect", NULL,
+  //       		build_function_type_list (long_integer_type_node,
+  //       					  long_integer_type_node,
+  //       					  long_integer_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+
+  // // We use __builtin_memcmp for struct comparisons.
+  // this->define_builtin (BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp",
+  //       		build_function_type_list (integer_type_node,
+  //       					  const_ptr_type_node,
+  //       					  const_ptr_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // We use __builtin_memmove for copying data.
+  // this->define_builtin (BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove",
+  //       		build_function_type_list (void_type_node, ptr_type_node,
+  //       					  const_ptr_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // We use __builtin_memset for zeroing data.
+  // this->define_builtin (BUILT_IN_MEMSET, "__builtin_memset", "memset",
+  //       		build_function_type_list (void_type_node, ptr_type_node,
+  //       					  integer_type_node,
+  //       					  size_type_node, NULL_TREE),
+  //       		0);
+
+  // // Used by runtime/internal/sys and math/bits.
+  // this->define_builtin (BUILT_IN_CTZ, "__builtin_ctz", "ctz",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CTZLL, "__builtin_ctzll", "ctzll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CLZ, "__builtin_clz", "clz",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_CLZLL, "__builtin_clzll", "clzll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_POPCOUNT, "__builtin_popcount", "popcount",
+  //       		build_function_type_list (integer_type_node,
+  //       					  unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_POPCOUNTLL, "__builtin_popcountll",
+  //       		"popcountll",
+  //       		build_function_type_list (integer_type_node,
+  //       					  long_long_unsigned_type_node,
+  //       					  NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP16, "__builtin_bswap16", "bswap16",
+  //       		build_function_type_list (uint16_type_node,
+  //       					  uint16_type_node, NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32",
+  //       		build_function_type_list (uint32_type_node,
+  //       					  uint32_type_node, NULL_TREE),
+  //       		builtin_const);
+  // this->define_builtin (BUILT_IN_BSWAP64, "__builtin_bswap64", "bswap64",
+  //       		build_function_type_list (uint64_type_node,
+  //       					  uint64_type_node, NULL_TREE),
+  //       		builtin_const);
+
+  // We provide some functions for the math library.
+
+  // We use __builtin_return_address in the thunk we build for
+  // functions which call recover, and for runtime.getcallerpc.
+  // t = build_function_type_list (ptr_type_node, unsigned_type_node,
+  // NULL_TREE); this->define_builtin (BUILT_IN_RETURN_ADDRESS,
+  // "__builtin_return_address",
+  //       		NULL, t, 0);
+
+  // The runtime calls __builtin_dwarf_cfa for runtime.getcallersp.
+  // t = build_function_type_list (ptr_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_DWARF_CFA, "__builtin_dwarf_cfa", NULL, t,
+  // 0);
+
+  // The runtime calls __builtin_extract_return_addr when recording
+  // the address to which a function returns.
+  // this->define_builtin (
+  //   BUILT_IN_EXTRACT_RETURN_ADDR, "__builtin_extract_return_addr", NULL,
+  //   build_function_type_list (ptr_type_node, ptr_type_node, NULL_TREE), 0);
+
+  // The compiler uses __builtin_trap for some exception handling
+  // cases.
+  // this->define_builtin (BUILT_IN_TRAP, "__builtin_trap", NULL,
+  //       		build_function_type (void_type_node, void_list_node),
+  //       		builtin_noreturn);
+
+  // The runtime uses __builtin_prefetch.
+  // this->define_builtin (BUILT_IN_PREFETCH, "__builtin_prefetch", NULL,
+  //       		build_varargs_function_type_list (void_type_node,
+  //       						  const_ptr_type_node,
+  //       						  NULL_TREE),
+  //       		builtin_novops);
+
+  // The compiler uses __builtin_unreachable for cases that cannot
+  // occur.
+  // this->define_builtin (BUILT_IN_UNREACHABLE, "__builtin_unreachable", NULL,
+  //       		build_function_type (void_type_node, void_list_node),
+  //       		builtin_const | builtin_noreturn);
+
+  // We provide some atomic functions.
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_LOAD_4, "__atomic_load_4", NULL, t,
+  // 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_LOAD_8, "__atomic_load_8", NULL, t,
+  // 0);
+
+  // t = build_function_type_list (void_type_node, ptr_type_node,
+  // uint32_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_STORE_4, "__atomic_store_4", NULL, t,
+  //       		0);
+
+  // t = build_function_type_list (void_type_node, ptr_type_node,
+  // uint64_type_node,
+  //       			integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_STORE_8, "__atomic_store_8", NULL, t,
+  //       		0);
+
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			uint32_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_4, "__atomic_exchange_4",
+  // NULL,
+  //       		t, 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			uint64_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_8, "__atomic_exchange_8",
+  // NULL,
+  //       		t, 0);
+
+  // t = build_function_type_list (boolean_type_node, ptr_type_node,
+  // ptr_type_node,
+  //       			uint32_type_node, boolean_type_node,
+  //       			integer_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4,
+  //       		"__atomic_compare_exchange_4", NULL, t, 0);
+
+  // t = build_function_type_list (boolean_type_node, ptr_type_node,
+  // ptr_type_node,
+  //       			uint64_type_node, boolean_type_node,
+  //       			integer_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8,
+  //       		"__atomic_compare_exchange_8", NULL, t, 0);
+
+  // t = build_function_type_list (uint32_type_node, ptr_type_node,
+  //       			uint32_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (uint64_type_node, ptr_type_node,
+  //       			uint64_type_node, integer_type_node, NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (unsigned_char_type_node, ptr_type_node,
+  //       			unsigned_char_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1",
+  //       		NULL, t, 0);
+  // this->define_builtin (BUILT_IN_ATOMIC_FETCH_AND_1, "__atomic_fetch_and_1",
+  //       		NULL, t, 0);
+
+  // t = build_function_type_list (unsigned_char_type_node, ptr_type_node,
+  //       			unsigned_char_type_node, integer_type_node,
+  //       			NULL_TREE);
+  // this->define_builtin (BUILT_IN_ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1",
+  // NULL,
+  //       		t, 0);
+  // this->define_builtin (BUILT_IN_ATOMIC_FETCH_OR_1, "__atomic_fetch_or_1",
+  // NULL,
+  //       		t, 0);
+}
+
+// Get an unnamed integer type.
+
+int
+Gcc_backend::get_pointer_size ()
+{
+  return POINTER_SIZE;
+}
+
+tree
+Gcc_backend::raw_str_type ()
+{
+  tree char_ptr = build_pointer_type (char_type_node);
+  tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST);
+  return const_char_type;
+}
+
+tree
+Gcc_backend::integer_type (bool is_unsigned, int bits)
+{
+  tree type;
+  if (is_unsigned)
+    {
+      if (bits == INT_TYPE_SIZE)
+	type = unsigned_type_node;
+      else if (bits == SHORT_TYPE_SIZE)
+	type = short_unsigned_type_node;
+      else if (bits == LONG_TYPE_SIZE)
+	type = long_unsigned_type_node;
+      else if (bits == LONG_LONG_TYPE_SIZE)
+	type = long_long_unsigned_type_node;
+      else
+	type = make_unsigned_type (bits);
+    }
+  else
+    {
+      if (bits == INT_TYPE_SIZE)
+	type = integer_type_node;
+      else if (bits == SHORT_TYPE_SIZE)
+	type = short_integer_type_node;
+      else if (bits == LONG_TYPE_SIZE)
+	type = long_integer_type_node;
+      else if (bits == LONG_LONG_TYPE_SIZE)
+	type = long_long_integer_type_node;
+      else
+	type = make_signed_type (bits);
+    }
+  return type;
+}
+
+// Get an unnamed float type.
+
+tree
+Gcc_backend::float_type (int bits)
+{
+  tree type;
+  if (bits == FLOAT_TYPE_SIZE)
+    type = float_type_node;
+  else if (bits == DOUBLE_TYPE_SIZE)
+    type = double_type_node;
+  else if (bits == LONG_DOUBLE_TYPE_SIZE)
+    type = long_double_type_node;
+  else
+    {
+      type = make_node (REAL_TYPE);
+      TYPE_PRECISION (type) = bits;
+      layout_type (type);
+    }
+  return type;
+}
+
+// Get an unnamed complex type.
+
+tree
+Gcc_backend::complex_type (int bits)
+{
+  tree type;
+  if (bits == FLOAT_TYPE_SIZE * 2)
+    type = complex_float_type_node;
+  else if (bits == DOUBLE_TYPE_SIZE * 2)
+    type = complex_double_type_node;
+  else if (bits == LONG_DOUBLE_TYPE_SIZE * 2)
+    type = complex_long_double_type_node;
+  else
+    {
+      type = make_node (REAL_TYPE);
+      TYPE_PRECISION (type) = bits / 2;
+      layout_type (type);
+      type = build_complex_type (type);
+    }
+  return type;
+}
+
+// Get a pointer type.
+
+tree
+Gcc_backend::pointer_type (tree to_type)
+{
+  if (to_type == error_mark_node)
+    return error_mark_node;
+  tree type = build_pointer_type (to_type);
+  return type;
+}
+
+// Get a reference type.
+
+tree
+Gcc_backend::reference_type (tree to_type)
+{
+  if (to_type == error_mark_node)
+    return error_mark_node;
+  tree type = build_reference_type (to_type);
+  return type;
+}
+
+// Get immutable type
+
+tree
+Gcc_backend::immutable_type (tree base)
+{
+  if (base == error_mark_node)
+    return error_mark_node;
+  tree constified = build_qualified_type (base, TYPE_QUAL_CONST);
+  return constified;
+}
+
+// Make a function type.
+
+tree
+Gcc_backend::function_type (const typed_identifier &receiver,
+			    const std::vector<typed_identifier> &parameters,
+			    const std::vector<typed_identifier> &results,
+			    tree result_struct, Location)
+{
+  tree args = NULL_TREE;
+  tree *pp = &args;
+  if (receiver.type != NULL_TREE)
+    {
+      tree t = receiver.type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+       p != parameters.end (); ++p)
+    {
+      tree t = p->type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  // Varargs is handled entirely at the Rust level.  When converted to
+  // GENERIC functions are not varargs.
+  *pp = void_list_node;
+
+  tree result;
+  if (results.empty ())
+    result = void_type_node;
+  else if (results.size () == 1)
+    result = results.front ().type;
+  else
+    {
+      gcc_assert (result_struct != NULL);
+      result = result_struct;
+    }
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // The libffi library cannot represent a zero-sized object.  To
+  // avoid causing confusion on 32-bit SPARC, we treat a function that
+  // returns a zero-sized value as returning void.  That should do no
+  // harm since there is no actual value to be returned.  See
+  // https://gcc.gnu.org/PR72814 for details.
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_function_type (result, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_type_varadic (
+  const typed_identifier &receiver,
+  const std::vector<typed_identifier> &parameters,
+  const std::vector<typed_identifier> &results, tree result_struct, Location)
+{
+  size_t n = parameters.size () + (receiver.type != NULL_TREE ? 1 : 0);
+  tree *args = XALLOCAVEC (tree, n);
+  size_t offs = 0;
+
+  if (receiver.type != NULL_TREE)
+    {
+      tree t = receiver.type;
+      if (t == error_mark_node)
+	return error_mark_node;
+
+      args[offs++] = t;
+    }
+
+  for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+       p != parameters.end (); ++p)
+    {
+      tree t = p->type;
+      if (t == error_mark_node)
+	return error_mark_node;
+      args[offs++] = t;
+    }
+
+  tree result;
+  if (results.empty ())
+    result = void_type_node;
+  else if (results.size () == 1)
+    result = results.front ().type;
+  else
+    {
+      gcc_assert (result_struct != NULL_TREE);
+      result = result_struct;
+    }
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // The libffi library cannot represent a zero-sized object.  To
+  // avoid causing confusion on 32-bit SPARC, we treat a function that
+  // returns a zero-sized value as returning void.  That should do no
+  // harm since there is no actual value to be returned.  See
+  // https://gcc.gnu.org/PR72814 for details.
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_varargs_function_type_array (result, n, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_ptr_type (tree result_type,
+				const std::vector<tree> &parameters,
+				Location /* locus */)
+{
+  tree args = NULL_TREE;
+  tree *pp = &args;
+
+  for (auto &param : parameters)
+    {
+      if (param == error_mark_node)
+	return error_mark_node;
+
+      *pp = tree_cons (NULL_TREE, param, NULL_TREE);
+      pp = &TREE_CHAIN (*pp);
+    }
+
+  *pp = void_list_node;
+
+  tree result = result_type;
+  if (result != void_type_node && int_size_in_bytes (result) == 0)
+    result = void_type_node;
+
+  tree fntype = build_function_type (result, args);
+  if (fntype == error_mark_node)
+    return error_mark_node;
+
+  return build_pointer_type (fntype);
+}
+
+// Make a struct type.
+
+tree
+Gcc_backend::struct_type (const std::vector<typed_identifier> &fields)
+{
+  return this->fill_in_fields (make_node (RECORD_TYPE), fields);
+}
+
+// Make a union type.
+
+tree
+Gcc_backend::union_type (const std::vector<typed_identifier> &fields)
+{
+  return this->fill_in_fields (make_node (UNION_TYPE), fields);
+}
+
+// Fill in the fields of a struct or union type.
+
+tree
+Gcc_backend::fill_in_fields (tree fill,
+			     const std::vector<typed_identifier> &fields)
+{
+  tree field_trees = NULL_TREE;
+  tree *pp = &field_trees;
+  for (std::vector<typed_identifier>::const_iterator p = fields.begin ();
+       p != fields.end (); ++p)
+    {
+      tree name_tree = get_identifier_from_string (p->name);
+      tree type_tree = p->type;
+      if (type_tree == error_mark_node)
+	return error_mark_node;
+      tree field = build_decl (p->location.gcc_location (), FIELD_DECL,
+			       name_tree, type_tree);
+      DECL_CONTEXT (field) = fill;
+      *pp = field;
+      pp = &DECL_CHAIN (field);
+    }
+  TYPE_FIELDS (fill) = field_trees;
+  layout_type (fill);
+
+  // Because Rust permits converting between named struct types and
+  // equivalent struct types, for which we use VIEW_CONVERT_EXPR, and
+  // because we don't try to maintain TYPE_CANONICAL for struct types,
+  // we need to tell the middle-end to use structural equality.
+  SET_TYPE_STRUCTURAL_EQUALITY (fill);
+
+  return fill;
+}
+
+// Make an array type.
+
+tree
+Gcc_backend::array_type (tree element_type, tree length)
+{
+  return this->fill_in_array (make_node (ARRAY_TYPE), element_type, length);
+}
+
+// Fill in an array type.
+
+tree
+Gcc_backend::fill_in_array (tree fill, tree element_type, tree length_tree)
+{
+  if (element_type == error_mark_node || length_tree == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (TYPE_SIZE (element_type) != NULL_TREE);
+
+  length_tree = fold_convert (sizetype, length_tree);
+
+  // build_index_type takes the maximum index, which is one less than
+  // the length.
+  tree index_type_tree = build_index_type (
+    fold_build2 (MINUS_EXPR, sizetype, length_tree, size_one_node));
+
+  TREE_TYPE (fill) = element_type;
+  TYPE_DOMAIN (fill) = index_type_tree;
+  TYPE_ADDR_SPACE (fill) = TYPE_ADDR_SPACE (element_type);
+  layout_type (fill);
+
+  if (TYPE_STRUCTURAL_EQUALITY_P (element_type))
+    SET_TYPE_STRUCTURAL_EQUALITY (fill);
+  else if (TYPE_CANONICAL (element_type) != element_type
+	   || TYPE_CANONICAL (index_type_tree) != index_type_tree)
+    TYPE_CANONICAL (fill) = build_array_type (TYPE_CANONICAL (element_type),
+					      TYPE_CANONICAL (index_type_tree));
+
+  return fill;
+}
+
+// Return a named version of a type.
+
+tree
+Gcc_backend::named_type (const std::string &name, tree type, Location location)
+{
+  if (type == error_mark_node)
+    return error_mark_node;
+
+  // The middle-end expects a basic type to have a name.  In Rust every
+  // basic type will have a name.  The first time we see a basic type,
+  // give it whatever Rust name we have at this point.
+  if (TYPE_NAME (type) == NULL_TREE
+      && location.gcc_location () == BUILTINS_LOCATION
+      && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == REAL_TYPE
+	  || TREE_CODE (type) == COMPLEX_TYPE
+	  || TREE_CODE (type) == BOOLEAN_TYPE))
+    {
+      tree decl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
+			      get_identifier_from_string (name), type);
+      TYPE_NAME (type) = decl;
+      return type;
+    }
+
+  tree copy = build_variant_type_copy (type);
+  tree decl = build_decl (location.gcc_location (), TYPE_DECL,
+			  get_identifier_from_string (name), copy);
+  DECL_ORIGINAL_TYPE (decl) = type;
+  TYPE_NAME (copy) = decl;
+  return copy;
+}
+
+// Return the size of a type.
+
+int64_t
+Gcc_backend::type_size (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  if (t == void_type_node)
+    return 0;
+  t = TYPE_SIZE_UNIT (t);
+  gcc_assert (tree_fits_uhwi_p (t));
+  unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW (t);
+  int64_t ret = static_cast<int64_t> (val_wide);
+  if (ret < 0 || static_cast<unsigned HOST_WIDE_INT> (ret) != val_wide)
+    return -1;
+  return ret;
+}
+
+// Return the alignment of a type.
+
+int64_t
+Gcc_backend::type_alignment (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  return TYPE_ALIGN_UNIT (t);
+}
+
+// Return the alignment of a struct field of type BTYPE.
+
+int64_t
+Gcc_backend::type_field_alignment (tree t)
+{
+  if (t == error_mark_node)
+    return 1;
+  return rust_field_alignment (t);
+}
+
+// Return the offset of a field in a struct.
+
+int64_t
+Gcc_backend::type_field_offset (tree struct_tree, size_t index)
+{
+  if (struct_tree == error_mark_node)
+    return 0;
+  gcc_assert (TREE_CODE (struct_tree) == RECORD_TYPE);
+  tree field = TYPE_FIELDS (struct_tree);
+  for (; index > 0; --index)
+    {
+      field = DECL_CHAIN (field);
+      gcc_assert (field != NULL_TREE);
+    }
+  HOST_WIDE_INT offset_wide = int_byte_position (field);
+  int64_t ret = static_cast<int64_t> (offset_wide);
+  gcc_assert (ret == offset_wide);
+  return ret;
+}
+
+// Return the zero value for a type.
+
+tree
+Gcc_backend::zero_expression (tree t)
+{
+  tree ret;
+  if (t == error_mark_node)
+    ret = error_mark_node;
+  else
+    ret = build_zero_cst (t);
+  return ret;
+}
+
+// An expression that references a variable.
+
+tree
+Gcc_backend::var_expression (Bvariable *var, Location location)
+{
+  return var->get_tree (location);
+}
+
+// Return a typed value as a constant integer.
+// This function does not release the memory of @val
+
+tree
+Gcc_backend::integer_constant_expression (tree t, mpz_t val)
+{
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  tree ret = wide_int_to_tree (t, wi::from_mpz (t, val, true));
+  return ret;
+}
+
+// Return a typed value as a constant floating-point number.
+
+tree
+Gcc_backend::float_constant_expression (tree t, mpfr_t val)
+{
+  tree ret;
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  REAL_VALUE_TYPE r1;
+  real_from_mpfr (&r1, val, t, GMP_RNDN);
+  REAL_VALUE_TYPE r2;
+  real_convert (&r2, TYPE_MODE (t), &r1);
+  ret = build_real (t, r2);
+  return ret;
+}
+
+// Return a typed real and imaginary value as a constant complex number.
+
+tree
+Gcc_backend::complex_constant_expression (tree t, mpc_t val)
+{
+  tree ret;
+  if (t == error_mark_node)
+    return error_mark_node;
+
+  REAL_VALUE_TYPE r1;
+  real_from_mpfr (&r1, mpc_realref (val), TREE_TYPE (t), GMP_RNDN);
+  REAL_VALUE_TYPE r2;
+  real_convert (&r2, TYPE_MODE (TREE_TYPE (t)), &r1);
+
+  REAL_VALUE_TYPE r3;
+  real_from_mpfr (&r3, mpc_imagref (val), TREE_TYPE (t), GMP_RNDN);
+  REAL_VALUE_TYPE r4;
+  real_convert (&r4, TYPE_MODE (TREE_TYPE (t)), &r3);
+
+  ret = build_complex (t, build_real (TREE_TYPE (t), r2),
+		       build_real (TREE_TYPE (t), r4));
+  return ret;
+}
+
+// Make a constant string expression.
+
+tree
+Gcc_backend::string_constant_expression (const std::string &val)
+{
+  tree index_type = build_index_type (size_int (val.length ()));
+  tree const_char_type = build_qualified_type (char_type_node, TYPE_QUAL_CONST);
+  tree string_type = build_array_type (const_char_type, index_type);
+  TYPE_STRING_FLAG (string_type) = 1;
+  tree string_val = build_string (val.length (), val.data ());
+  TREE_TYPE (string_val) = string_type;
+
+  return string_val;
+}
+
+tree
+Gcc_backend::wchar_constant_expression (wchar_t c)
+{
+  return build_int_cst (this->wchar_type (), c);
+}
+
+tree
+Gcc_backend::char_constant_expression (char c)
+{
+  return build_int_cst (this->char_type (), c);
+}
+
+// Make a constant boolean expression.
+
+tree
+Gcc_backend::boolean_constant_expression (bool val)
+{
+  return val ? boolean_true_node : boolean_false_node;
+}
+
+// Return the real part of a complex expression.
+
+tree
+Gcc_backend::real_part_expression (tree complex_tree, Location location)
+{
+  if (complex_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+  tree ret
+    = fold_build1_loc (location.gcc_location (), REALPART_EXPR,
+		       TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+  return ret;
+}
+
+// Return the imaginary part of a complex expression.
+
+tree
+Gcc_backend::imag_part_expression (tree complex_tree, Location location)
+{
+  if (complex_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+  tree ret
+    = fold_build1_loc (location.gcc_location (), IMAGPART_EXPR,
+		       TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+  return ret;
+}
+
+// Make a complex expression given its real and imaginary parts.
+
+tree
+Gcc_backend::complex_expression (tree real_tree, tree imag_tree,
+				 Location location)
+{
+  if (real_tree == error_mark_node || imag_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real_tree))
+	      == TYPE_MAIN_VARIANT (TREE_TYPE (imag_tree)));
+  gcc_assert (SCALAR_FLOAT_TYPE_P (TREE_TYPE (real_tree)));
+  tree ret = fold_build2_loc (location.gcc_location (), COMPLEX_EXPR,
+			      build_complex_type (TREE_TYPE (real_tree)),
+			      real_tree, imag_tree);
+  return ret;
+}
+
+// An expression that converts an expression to a different type.
+
+tree
+Gcc_backend::convert_expression (tree type_tree, tree expr_tree,
+				 Location location)
+{
+  if (type_tree == error_mark_node || expr_tree == error_mark_node
+      || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  tree ret;
+  if (this->type_size (type_tree) == 0
+      || TREE_TYPE (expr_tree) == void_type_node)
+    {
+      // Do not convert zero-sized types.
+      ret = expr_tree;
+    }
+  else if (TREE_CODE (type_tree) == INTEGER_TYPE)
+    ret = convert_to_integer (type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == REAL_TYPE)
+    ret = convert_to_real (type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == COMPLEX_TYPE)
+    ret = convert_to_complex (type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == POINTER_TYPE
+	   && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE)
+    ret = convert_to_pointer (type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == RECORD_TYPE
+	   || TREE_CODE (type_tree) == ARRAY_TYPE)
+    ret = fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+			   type_tree, expr_tree);
+  else
+    ret = fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+
+  return ret;
+}
+
+// Return an expression for the field at INDEX in BSTRUCT.
+
+tree
+Gcc_backend::struct_field_expression (tree struct_tree, size_t index,
+				      Location location)
+{
+  if (struct_tree == error_mark_node
+      || TREE_TYPE (struct_tree) == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TREE_CODE (TREE_TYPE (struct_tree)) == RECORD_TYPE
+	      || TREE_CODE (TREE_TYPE (struct_tree)) == UNION_TYPE);
+  tree field = TYPE_FIELDS (TREE_TYPE (struct_tree));
+  if (field == NULL_TREE)
+    {
+      // This can happen for a type which refers to itself indirectly
+      // and then turns out to be erroneous.
+      return error_mark_node;
+    }
+  for (unsigned int i = index; i > 0; --i)
+    {
+      field = DECL_CHAIN (field);
+      gcc_assert (field != NULL_TREE);
+    }
+  if (TREE_TYPE (field) == error_mark_node)
+    return error_mark_node;
+  tree ret = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+			      TREE_TYPE (field), struct_tree, field, NULL_TREE);
+  if (TREE_CONSTANT (struct_tree))
+    TREE_CONSTANT (ret) = 1;
+  return ret;
+}
+
+// Return an expression that executes BSTAT before BEXPR.
+
+tree
+Gcc_backend::compound_expression (tree stat, tree expr, Location location)
+{
+  if (stat == error_mark_node || expr == error_mark_node)
+    return error_mark_node;
+  tree ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+			      TREE_TYPE (expr), stat, expr);
+  return ret;
+}
+
+// Return an expression that executes THEN_EXPR if CONDITION is true, or
+// ELSE_EXPR otherwise.
+
+tree
+Gcc_backend::conditional_expression (tree, tree type_tree, tree cond_expr,
+				     tree then_expr, tree else_expr,
+				     Location location)
+{
+  if (type_tree == error_mark_node || cond_expr == error_mark_node
+      || then_expr == error_mark_node || else_expr == error_mark_node)
+    return error_mark_node;
+  tree ret = build3_loc (location.gcc_location (), COND_EXPR, type_tree,
+			 cond_expr, then_expr, else_expr);
+  return ret;
+}
+
+/* Helper function that converts rust operators to equivalent GCC tree_code.
+   Note that CompoundAssignmentOperator don't get their corresponding tree_code,
+   because they get compiled away when we lower AST to HIR. */
+static enum tree_code
+operator_to_tree_code (NegationOperator op)
+{
+  switch (op)
+    {
+    case NegationOperator::NEGATE:
+      return NEGATE_EXPR;
+    case NegationOperator::NOT:
+      return TRUTH_NOT_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Note that GCC tree code distinguishes floating point division and integer
+   division. These two types of division are represented as the same rust
+   operator, and can only be distinguished via context(i.e. the TREE_TYPE of the
+   operands). */
+static enum tree_code
+operator_to_tree_code (ArithmeticOrLogicalOperator op, bool floating_point)
+{
+  switch (op)
+    {
+    case ArithmeticOrLogicalOperator::ADD:
+      return PLUS_EXPR;
+    case ArithmeticOrLogicalOperator::SUBTRACT:
+      return MINUS_EXPR;
+    case ArithmeticOrLogicalOperator::MULTIPLY:
+      return MULT_EXPR;
+    case ArithmeticOrLogicalOperator::DIVIDE:
+      if (floating_point)
+	return RDIV_EXPR;
+      else
+	return TRUNC_DIV_EXPR;
+    case ArithmeticOrLogicalOperator::MODULUS:
+      return TRUNC_MOD_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_AND:
+      return BIT_AND_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_OR:
+      return BIT_IOR_EXPR;
+    case ArithmeticOrLogicalOperator::BITWISE_XOR:
+      return BIT_XOR_EXPR;
+    case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+      return LSHIFT_EXPR;
+    case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+      return RSHIFT_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static enum tree_code
+operator_to_tree_code (ComparisonOperator op)
+{
+  switch (op)
+    {
+    case ComparisonOperator::EQUAL:
+      return EQ_EXPR;
+    case ComparisonOperator::NOT_EQUAL:
+      return NE_EXPR;
+    case ComparisonOperator::GREATER_THAN:
+      return GT_EXPR;
+    case ComparisonOperator::LESS_THAN:
+      return LT_EXPR;
+    case ComparisonOperator::GREATER_OR_EQUAL:
+      return GE_EXPR;
+    case ComparisonOperator::LESS_OR_EQUAL:
+      return LE_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+static enum tree_code
+operator_to_tree_code (LazyBooleanOperator op)
+{
+  switch (op)
+    {
+    case LazyBooleanOperator::LOGICAL_OR:
+      return TRUTH_ORIF_EXPR;
+    case LazyBooleanOperator::LOGICAL_AND:
+      return TRUTH_ANDIF_EXPR;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Helper function for deciding if a tree is a floating point node. */
+bool
+is_floating_point (tree t)
+{
+  auto tree_type = TREE_CODE (TREE_TYPE (t));
+  return tree_type == REAL_TYPE || tree_type == COMPLEX_TYPE;
+}
+
+// Return an expression for the negation operation OP EXPR.
+tree
+Gcc_backend::negation_expression (NegationOperator op, tree expr_tree,
+				  Location location)
+{
+  /* Check if the expression is an error, in which case we return an error
+     expression. */
+  if (expr_tree == error_mark_node || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  /* For negation operators, the resulting type should be the same as its
+     operand. */
+  auto tree_type = TREE_TYPE (expr_tree);
+  auto original_type = tree_type;
+  auto tree_code = operator_to_tree_code (op);
+
+  /* For floating point operations we may need to extend the precision of type.
+     For example, a 64-bit machine may not support operations on float32. */
+  bool floating_point = is_floating_point (expr_tree);
+  auto extended_type = NULL_TREE;
+  if (floating_point)
+    {
+      extended_type = excess_precision_type (tree_type);
+      if (extended_type != NULL_TREE)
+	{
+	  expr_tree = convert (extended_type, expr_tree);
+	  tree_type = extended_type;
+	}
+    }
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build1_loc (location.gcc_location (), tree_code,
+				   tree_type, expr_tree);
+  if (floating_point && extended_type != NULL_TREE)
+    new_tree = convert (original_type, expr_tree);
+  return new_tree;
+}
+
+// Return an expression for the arithmetic or logical operation LEFT OP RIGHT.
+tree
+Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+					       tree left_tree, tree right_tree,
+					       Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* We need to determine if we're doing floating point arithmetics of integer
+     arithmetics. */
+  bool floating_point = is_floating_point (left_tree);
+
+  /* For arithmetic or logical operators, the resulting type should be the same
+     as the lhs operand. */
+  auto tree_type = TREE_TYPE (left_tree);
+  auto original_type = tree_type;
+  auto tree_code = operator_to_tree_code (op, floating_point);
+
+  /* For floating point operations we may need to extend the precision of type.
+     For example, a 64-bit machine may not support operations on float32. */
+  auto extended_type = NULL_TREE;
+  if (floating_point)
+    {
+      extended_type = excess_precision_type (tree_type);
+      if (extended_type != NULL_TREE)
+	{
+	  left_tree = convert (extended_type, left_tree);
+	  right_tree = convert (extended_type, right_tree);
+	  tree_type = extended_type;
+	}
+    }
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  TREE_CONSTANT (new_tree)
+    = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree);
+
+  if (floating_point && extended_type != NULL_TREE)
+    new_tree = convert (original_type, new_tree);
+  return new_tree;
+}
+
+// Return an expression for the comparison operation LEFT OP RIGHT.
+tree
+Gcc_backend::comparison_expression (ComparisonOperator op, tree left_tree,
+				    tree right_tree, Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* For comparison operators, the resulting type should be boolean. */
+  auto tree_type = boolean_type_node;
+  auto tree_code = operator_to_tree_code (op);
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  return new_tree;
+}
+
+// Return an expression for the lazy boolean operation LEFT OP RIGHT.
+tree
+Gcc_backend::lazy_boolean_expression (LazyBooleanOperator op, tree left_tree,
+				      tree right_tree, Location location)
+{
+  /* Check if either expression is an error, in which case we return an error
+     expression. */
+  if (left_tree == error_mark_node || right_tree == error_mark_node)
+    return error_mark_node;
+
+  /* For lazy boolean operators, the resulting type should be the same as the
+     rhs operand. */
+  auto tree_type = TREE_TYPE (right_tree);
+  auto tree_code = operator_to_tree_code (op);
+
+  /* Construct a new tree and build an expression from it. */
+  auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+				   tree_type, left_tree, right_tree);
+  return new_tree;
+}
+
+// Return an expression that constructs BTYPE with VALS.
+
+tree
+Gcc_backend::constructor_expression (tree type_tree, bool is_variant,
+				     const std::vector<tree> &vals,
+				     int union_index, Location location)
+{
+  if (type_tree == error_mark_node)
+    return error_mark_node;
+
+  vec<constructor_elt, va_gc> *init;
+  vec_alloc (init, vals.size ());
+
+  tree sink = NULL_TREE;
+  bool is_constant = true;
+  tree field = TYPE_FIELDS (type_tree);
+
+  if (is_variant)
+    {
+      gcc_assert (union_index != -1);
+      gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+
+      for (int i = 0; i < union_index; i++)
+	{
+	  gcc_assert (field != NULL_TREE);
+	  field = DECL_CHAIN (field);
+	}
+
+      tree nested_ctor
+	= constructor_expression (TREE_TYPE (field), false, vals, -1, location);
+
+      constructor_elt empty = {NULL, NULL};
+      constructor_elt *elt = init->quick_push (empty);
+      elt->index = field;
+      elt->value
+	= this->convert_tree (TREE_TYPE (field), nested_ctor, location);
+      if (!TREE_CONSTANT (elt->value))
+	is_constant = false;
+    }
+  else
+    {
+      if (union_index != -1)
+	{
+	  gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+	  tree val = vals.front ();
+	  for (int i = 0; i < union_index; i++)
+	    {
+	      gcc_assert (field != NULL_TREE);
+	      field = DECL_CHAIN (field);
+	    }
+	  if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+	      || TREE_TYPE (val) == error_mark_node)
+	    return error_mark_node;
+
+	  if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+	    {
+	      // GIMPLE cannot represent indices of zero-sized types so
+	      // trying to construct a map with zero-sized keys might lead
+	      // to errors.  Instead, we evaluate each expression that
+	      // would have been added as a map element for its
+	      // side-effects and construct an empty map.
+	      append_to_statement_list (val, &sink);
+	    }
+	  else
+	    {
+	      constructor_elt empty = {NULL, NULL};
+	      constructor_elt *elt = init->quick_push (empty);
+	      elt->index = field;
+	      elt->value
+		= this->convert_tree (TREE_TYPE (field), val, location);
+	      if (!TREE_CONSTANT (elt->value))
+		is_constant = false;
+	    }
+	}
+      else
+	{
+	  gcc_assert (TREE_CODE (type_tree) == RECORD_TYPE);
+	  for (std::vector<tree>::const_iterator p = vals.begin ();
+	       p != vals.end (); ++p, field = DECL_CHAIN (field))
+	    {
+	      gcc_assert (field != NULL_TREE);
+	      tree val = (*p);
+	      if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+		  || TREE_TYPE (val) == error_mark_node)
+		return error_mark_node;
+
+	      if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+		{
+		  // GIMPLE cannot represent indices of zero-sized types so
+		  // trying to construct a map with zero-sized keys might lead
+		  // to errors.  Instead, we evaluate each expression that
+		  // would have been added as a map element for its
+		  // side-effects and construct an empty map.
+		  append_to_statement_list (val, &sink);
+		  continue;
+		}
+
+	      constructor_elt empty = {NULL, NULL};
+	      constructor_elt *elt = init->quick_push (empty);
+	      elt->index = field;
+	      elt->value
+		= this->convert_tree (TREE_TYPE (field), val, location);
+	      if (!TREE_CONSTANT (elt->value))
+		is_constant = false;
+	    }
+	  gcc_assert (field == NULL_TREE);
+	}
+    }
+
+  tree ret = build_constructor (type_tree, init);
+  if (is_constant)
+    TREE_CONSTANT (ret) = 1;
+  if (sink != NULL_TREE)
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+			   sink, ret);
+  return ret;
+}
+
+tree
+Gcc_backend::array_constructor_expression (
+  tree type_tree, const std::vector<unsigned long> &indexes,
+  const std::vector<tree> &vals, Location location)
+{
+  if (type_tree == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (indexes.size () == vals.size ());
+
+  tree element_type = TREE_TYPE (type_tree);
+  HOST_WIDE_INT element_size = int_size_in_bytes (element_type);
+  vec<constructor_elt, va_gc> *init;
+  vec_alloc (init, element_size == 0 ? 0 : vals.size ());
+
+  tree sink = NULL_TREE;
+  bool is_constant = true;
+  for (size_t i = 0; i < vals.size (); ++i)
+    {
+      tree index = size_int (indexes[i]);
+      tree val = vals[i];
+
+      if (index == error_mark_node || val == error_mark_node)
+	return error_mark_node;
+
+      if (element_size == 0)
+	{
+	  // GIMPLE cannot represent arrays of zero-sized types so trying
+	  // to construct an array of zero-sized values might lead to errors.
+	  // Instead, we evaluate each expression that would have been added as
+	  // an array value for its side-effects and construct an empty array.
+	  append_to_statement_list (val, &sink);
+	  continue;
+	}
+
+      if (!TREE_CONSTANT (val))
+	is_constant = false;
+
+      constructor_elt empty = {NULL, NULL};
+      constructor_elt *elt = init->quick_push (empty);
+      elt->index = index;
+      elt->value = val;
+    }
+
+  tree ret = build_constructor (type_tree, init);
+  if (is_constant)
+    TREE_CONSTANT (ret) = 1;
+  if (sink != NULL_TREE)
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+			   sink, ret);
+  return ret;
+}
+
+// Build insns to create an array, initialize all elements of the array to
+// value, and return it
+tree
+Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type,
+				tree length, tree value, tree *tmp,
+				Location locus)
+{
+  std::vector<tree> stmts;
+
+  // Temporary array we initialize with the desired value.
+  tree t = NULL_TREE;
+  Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type,
+						   NULL_TREE, true, locus, &t);
+  tree arr = tmp_array->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary for the array length used for initialization loop guard.
+  Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node,
+						 length, true, locus, &t);
+  tree len = tmp_len->get_tree (locus);
+  stmts.push_back (t);
+
+  // Temporary variable for pointer used to initialize elements.
+  tree ptr_type = this->pointer_type (TREE_TYPE (array_type));
+  tree ptr_init
+    = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type,
+		  this->array_index_expression (arr, integer_zero_node, locus));
+  Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type,
+						 ptr_init, false, locus, &t);
+  tree ptr = tmp_ptr->get_tree (locus);
+  stmts.push_back (t);
+
+  // push statement list for the loop
+  std::vector<tree> loop_stmts;
+
+  // Loop exit condition:
+  //   if (length == 0) break;
+  t = this->comparison_expression (ComparisonOperator::EQUAL, len,
+				   this->zero_expression (TREE_TYPE (len)),
+				   locus);
+
+  t = this->exit_expression (t, locus);
+  loop_stmts.push_back (t);
+
+  // Assign value to the current pointer position
+  //   *ptr = value;
+  t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus);
+  loop_stmts.push_back (t);
+
+  // Move pointer to next element
+  //   ptr++;
+  tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type));
+  t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size));
+  loop_stmts.push_back (t);
+
+  // Decrement loop counter.
+  //   length--;
+  t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len,
+	      convert (TREE_TYPE (len), integer_one_node));
+  loop_stmts.push_back (t);
+
+  // pop statments and finish loop
+  tree loop_body = this->statement_list (loop_stmts);
+  stmts.push_back (this->loop_expression (loop_body, locus));
+
+  // Return the temporary in the provided pointer and the statement list which
+  // initializes it.
+  *tmp = tmp_array->get_tree (locus);
+  return this->statement_list (stmts);
+}
+
+// Return an expression representing ARRAY[INDEX]
+
+tree
+Gcc_backend::array_index_expression (tree array_tree, tree index_tree,
+				     Location location)
+{
+  if (array_tree == error_mark_node || TREE_TYPE (array_tree) == error_mark_node
+      || index_tree == error_mark_node)
+    return error_mark_node;
+
+  // A function call that returns a zero sized object will have been
+  // changed to return void.  If we see void here, assume we are
+  // dealing with a zero sized type and just evaluate the operands.
+  tree ret;
+  if (TREE_TYPE (array_tree) != void_type_node)
+    ret = build4_loc (location.gcc_location (), ARRAY_REF,
+		      TREE_TYPE (TREE_TYPE (array_tree)), array_tree,
+		      index_tree, NULL_TREE, NULL_TREE);
+  else
+    ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+			   void_type_node, array_tree, index_tree);
+
+  return ret;
+}
+
+// Create an expression for a call to FN_EXPR with FN_ARGS.
+tree
+Gcc_backend::call_expression (tree fn, const std::vector<tree> &fn_args,
+			      tree chain_expr, Location location)
+{
+  if (fn == error_mark_node || TREE_TYPE (fn) == error_mark_node)
+    return error_mark_node;
+
+  gcc_assert (FUNCTION_POINTER_TYPE_P (TREE_TYPE (fn)));
+  tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn)));
+
+  size_t nargs = fn_args.size ();
+  tree *args = nargs == 0 ? NULL : new tree[nargs];
+  for (size_t i = 0; i < nargs; ++i)
+    {
+      args[i] = fn_args.at (i);
+    }
+
+  tree fndecl = fn;
+  if (TREE_CODE (fndecl) == ADDR_EXPR)
+    fndecl = TREE_OPERAND (fndecl, 0);
+
+  // This is to support builtin math functions when using 80387 math.
+  tree excess_type = NULL_TREE;
+  if (optimize && TREE_CODE (fndecl) == FUNCTION_DECL
+      && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
+      && DECL_IS_UNDECLARED_BUILTIN (fndecl) && nargs > 0
+      && ((SCALAR_FLOAT_TYPE_P (rettype)
+	   && SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[0])))
+	  || (COMPLEX_FLOAT_TYPE_P (rettype)
+	      && COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[0])))))
+    {
+      excess_type = excess_precision_type (TREE_TYPE (args[0]));
+      if (excess_type != NULL_TREE)
+	{
+	  tree excess_fndecl
+	    = mathfn_built_in (excess_type, DECL_FUNCTION_CODE (fndecl));
+	  if (excess_fndecl == NULL_TREE)
+	    excess_type = NULL_TREE;
+	  else
+	    {
+	      fn = build_fold_addr_expr_loc (location.gcc_location (),
+					     excess_fndecl);
+	      for (size_t i = 0; i < nargs; ++i)
+		{
+		  if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[i]))
+		      || COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[i])))
+		    args[i] = ::convert (excess_type, args[i]);
+		}
+	    }
+	}
+    }
+
+  tree ret
+    = build_call_array_loc (location.gcc_location (),
+			    excess_type != NULL_TREE ? excess_type : rettype,
+			    fn, nargs, args);
+
+  // check for deprecated function usage
+  if (fndecl && TREE_DEPRECATED (fndecl))
+    {
+      // set up the call-site information for `warn_deprecated_use`
+      input_location = location.gcc_location ();
+      warn_deprecated_use (fndecl, NULL_TREE);
+    }
+
+  if (chain_expr)
+    CALL_EXPR_STATIC_CHAIN (ret) = chain_expr;
+
+  if (excess_type != NULL_TREE)
+    {
+      // Calling convert here can undo our excess precision change.
+      // That may or may not be a bug in convert_to_real.
+      ret = build1_loc (location.gcc_location (), NOP_EXPR, rettype, ret);
+    }
+
+  delete[] args;
+  return ret;
+}
+
+// Variable initialization.
+
+tree
+Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree)
+{
+  tree var_tree = var->get_decl ();
+  if (var_tree == error_mark_node || init_tree == error_mark_node)
+    return error_mark_node;
+  gcc_assert (TREE_CODE (var_tree) == VAR_DECL);
+
+  // To avoid problems with GNU ld, we don't make zero-sized
+  // externally visible variables.  That might lead us to doing an
+  // initialization of a zero-sized expression to a non-zero sized
+  // variable, or vice-versa.  Avoid crashes by omitting the
+  // initializer.  Such initializations don't mean anything anyhow.
+  if (int_size_in_bytes (TREE_TYPE (var_tree)) != 0 && init_tree != NULL_TREE
+      && TREE_TYPE (init_tree) != void_type_node
+      && int_size_in_bytes (TREE_TYPE (init_tree)) != 0)
+    {
+      DECL_INITIAL (var_tree) = init_tree;
+      init_tree = NULL_TREE;
+    }
+
+  tree ret = build1_loc (DECL_SOURCE_LOCATION (var_tree), DECL_EXPR,
+			 void_type_node, var_tree);
+  if (init_tree != NULL_TREE)
+    ret = build2_loc (DECL_SOURCE_LOCATION (var_tree), COMPOUND_EXPR,
+		      void_type_node, init_tree, ret);
+
+  return ret;
+}
+
+// Assignment.
+
+tree
+Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location)
+{
+  if (lhs == error_mark_node || rhs == error_mark_node)
+    return error_mark_node;
+
+  // To avoid problems with GNU ld, we don't make zero-sized
+  // externally visible variables.  That might lead us to doing an
+  // assignment of a zero-sized expression to a non-zero sized
+  // expression; avoid crashes here by avoiding assignments of
+  // zero-sized expressions.  Such assignments don't really mean
+  // anything anyhow.
+  if (TREE_TYPE (lhs) == void_type_node
+      || int_size_in_bytes (TREE_TYPE (lhs)) == 0
+      || TREE_TYPE (rhs) == void_type_node
+      || int_size_in_bytes (TREE_TYPE (rhs)) == 0)
+    return this->compound_statement (lhs, rhs);
+
+  rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location);
+
+  return fold_build2_loc (location.gcc_location (), MODIFY_EXPR, void_type_node,
+			  lhs, rhs);
+}
+
+// Return.
+
+tree
+Gcc_backend::return_statement (tree fntree, const std::vector<tree> &vals,
+			       Location location)
+{
+  if (fntree == error_mark_node)
+    return error_mark_node;
+  tree result = DECL_RESULT (fntree);
+  if (result == error_mark_node)
+    return error_mark_node;
+
+  // If the result size is zero bytes, we have set the function type
+  // to have a result type of void, so don't return anything.
+  // See the function_type method.
+  tree res_type = TREE_TYPE (result);
+  if (res_type == void_type_node || int_size_in_bytes (res_type) == 0)
+    {
+      tree stmt_list = NULL_TREE;
+      for (std::vector<tree>::const_iterator p = vals.begin ();
+	   p != vals.end (); p++)
+	{
+	  tree val = (*p);
+	  if (val == error_mark_node)
+	    return error_mark_node;
+	  append_to_statement_list (val, &stmt_list);
+	}
+      tree ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+				  void_type_node, NULL_TREE);
+      append_to_statement_list (ret, &stmt_list);
+      return stmt_list;
+    }
+
+  tree ret;
+  if (vals.empty ())
+    ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+			   void_type_node, NULL_TREE);
+  else if (vals.size () == 1)
+    {
+      tree val = vals.front ();
+      if (val == error_mark_node)
+	return error_mark_node;
+      tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				  void_type_node, result, vals.front ());
+      ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+			     void_type_node, set);
+    }
+  else
+    {
+      // To return multiple values, copy the values into a temporary
+      // variable of the right structure type, and then assign the
+      // temporary variable to the DECL_RESULT in the return
+      // statement.
+      tree stmt_list = NULL_TREE;
+      tree rettype = TREE_TYPE (result);
+
+      if (DECL_STRUCT_FUNCTION (fntree) == NULL)
+	push_struct_function (fntree);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (fntree));
+      tree rettmp = create_tmp_var (rettype, "RESULT");
+      pop_cfun ();
+
+      tree field = TYPE_FIELDS (rettype);
+      for (std::vector<tree>::const_iterator p = vals.begin ();
+	   p != vals.end (); p++, field = DECL_CHAIN (field))
+	{
+	  gcc_assert (field != NULL_TREE);
+	  tree ref
+	    = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+			       TREE_TYPE (field), rettmp, field, NULL_TREE);
+	  tree val = (*p);
+	  if (val == error_mark_node)
+	    return error_mark_node;
+	  tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				      void_type_node, ref, (*p));
+	  append_to_statement_list (set, &stmt_list);
+	}
+      gcc_assert (field == NULL_TREE);
+      tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+				  void_type_node, result, rettmp);
+      tree ret_expr = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+				       void_type_node, set);
+      append_to_statement_list (ret_expr, &stmt_list);
+      ret = stmt_list;
+    }
+  return ret;
+}
+
+// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an
+// error occurs.  EXCEPT_STMT may be NULL.  FINALLY_STMT may be NULL and if not
+// NULL, it will always be executed.  This is used for handling defers in Rust
+// functions.  In C++, the resulting code is of this form:
+//   try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
+
+tree
+Gcc_backend::exception_handler_statement (tree try_stmt, tree except_stmt,
+					  tree finally_stmt, Location location)
+{
+  if (try_stmt == error_mark_node || except_stmt == error_mark_node
+      || finally_stmt == error_mark_node)
+    return error_mark_node;
+
+  if (except_stmt != NULL_TREE)
+    try_stmt = build2_loc (location.gcc_location (), TRY_CATCH_EXPR,
+			   void_type_node, try_stmt,
+			   build2_loc (location.gcc_location (), CATCH_EXPR,
+				       void_type_node, NULL, except_stmt));
+  if (finally_stmt != NULL_TREE)
+    try_stmt = build2_loc (location.gcc_location (), TRY_FINALLY_EXPR,
+			   void_type_node, try_stmt, finally_stmt);
+  return try_stmt;
+}
+
+// If.
+
+tree
+Gcc_backend::if_statement (tree, tree cond_tree, tree then_tree, tree else_tree,
+			   Location location)
+{
+  if (cond_tree == error_mark_node || then_tree == error_mark_node
+      || else_tree == error_mark_node)
+    return error_mark_node;
+  tree ret = build3_loc (location.gcc_location (), COND_EXPR, void_type_node,
+			 cond_tree, then_tree, else_tree);
+  return ret;
+}
+
+// Loops
+
+tree
+Gcc_backend::loop_expression (tree body, Location locus)
+{
+  return fold_build1_loc (locus.gcc_location (), LOOP_EXPR, void_type_node,
+			  body);
+}
+
+tree
+Gcc_backend::exit_expression (tree cond_tree, Location locus)
+{
+  return fold_build1_loc (locus.gcc_location (), EXIT_EXPR, void_type_node,
+			  cond_tree);
+}
+
+// Pair of statements.
+
+tree
+Gcc_backend::compound_statement (tree s1, tree s2)
+{
+  tree stmt_list = NULL_TREE;
+  tree t = s1;
+  if (t == error_mark_node)
+    return error_mark_node;
+  append_to_statement_list (t, &stmt_list);
+  t = s2;
+  if (t == error_mark_node)
+    return error_mark_node;
+  append_to_statement_list (t, &stmt_list);
+
+  // If neither statement has any side effects, stmt_list can be NULL
+  // at this point.
+  if (stmt_list == NULL_TREE)
+    stmt_list = integer_zero_node;
+
+  return stmt_list;
+}
+
+// List of statements.
+
+tree
+Gcc_backend::statement_list (const std::vector<tree> &statements)
+{
+  tree stmt_list = NULL_TREE;
+  for (std::vector<tree>::const_iterator p = statements.begin ();
+       p != statements.end (); ++p)
+    {
+      tree t = (*p);
+      if (t == error_mark_node)
+	return error_mark_node;
+      append_to_statement_list (t, &stmt_list);
+    }
+  return stmt_list;
+}
+
+// Make a block.  For some reason gcc uses a dual structure for
+// blocks: BLOCK tree nodes and BIND_EXPR tree nodes.  Since the
+// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in
+// the Bblock.
+
+tree
+Gcc_backend::block (tree fndecl, tree enclosing,
+		    const std::vector<Bvariable *> &vars,
+		    Location start_location, Location)
+{
+  tree block_tree = make_node (BLOCK);
+  if (enclosing == NULL)
+    {
+      gcc_assert (fndecl != NULL_TREE);
+
+      // We may have already created a block for local variables when
+      // we take the address of a parameter.
+      if (DECL_INITIAL (fndecl) == NULL_TREE)
+	{
+	  BLOCK_SUPERCONTEXT (block_tree) = fndecl;
+	  DECL_INITIAL (fndecl) = block_tree;
+	}
+      else
+	{
+	  tree superblock_tree = DECL_INITIAL (fndecl);
+	  BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+	  tree *pp;
+	  for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+	       pp = &BLOCK_CHAIN (*pp))
+	    ;
+	  *pp = block_tree;
+	}
+    }
+  else
+    {
+      tree superblock_tree = BIND_EXPR_BLOCK (enclosing);
+      gcc_assert (TREE_CODE (superblock_tree) == BLOCK);
+
+      BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+      tree *pp;
+      for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+	   pp = &BLOCK_CHAIN (*pp))
+	;
+      *pp = block_tree;
+    }
+
+  tree *pp = &BLOCK_VARS (block_tree);
+  for (std::vector<Bvariable *>::const_iterator pv = vars.begin ();
+       pv != vars.end (); ++pv)
+    {
+      *pp = (*pv)->get_decl ();
+      if (*pp != error_mark_node)
+	pp = &DECL_CHAIN (*pp);
+    }
+  *pp = NULL_TREE;
+
+  TREE_USED (block_tree) = 1;
+
+  tree bind_tree
+    = build3_loc (start_location.gcc_location (), BIND_EXPR, void_type_node,
+		  BLOCK_VARS (block_tree), NULL_TREE, block_tree);
+  TREE_SIDE_EFFECTS (bind_tree) = 1;
+  return bind_tree;
+}
+
+// Add statements to a block.
+
+void
+Gcc_backend::block_add_statements (tree bind_tree,
+				   const std::vector<tree> &statements)
+{
+  tree stmt_list = NULL_TREE;
+  for (std::vector<tree>::const_iterator p = statements.begin ();
+       p != statements.end (); ++p)
+    {
+      tree s = (*p);
+      if (s != error_mark_node)
+	append_to_statement_list (s, &stmt_list);
+    }
+
+  gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+  BIND_EXPR_BODY (bind_tree) = stmt_list;
+}
+
+// This is not static because we declare it with GTY(()) in rust-c.h.
+tree rust_non_zero_struct;
+
+// Return a type corresponding to TYPE with non-zero size.
+
+tree
+Gcc_backend::non_zero_size_type (tree type)
+{
+  if (int_size_in_bytes (type) != 0)
+    return type;
+
+  switch (TREE_CODE (type))
+    {
+    case RECORD_TYPE:
+      if (TYPE_FIELDS (type) != NULL_TREE)
+	{
+	  tree ns = make_node (RECORD_TYPE);
+	  tree field_trees = NULL_TREE;
+	  tree *pp = &field_trees;
+	  for (tree field = TYPE_FIELDS (type); field != NULL_TREE;
+	       field = DECL_CHAIN (field))
+	    {
+	      tree ft = TREE_TYPE (field);
+	      if (field == TYPE_FIELDS (type))
+		ft = non_zero_size_type (ft);
+	      tree f = build_decl (DECL_SOURCE_LOCATION (field), FIELD_DECL,
+				   DECL_NAME (field), ft);
+	      DECL_CONTEXT (f) = ns;
+	      *pp = f;
+	      pp = &DECL_CHAIN (f);
+	    }
+	  TYPE_FIELDS (ns) = field_trees;
+	  layout_type (ns);
+	  return ns;
+	}
+
+      if (rust_non_zero_struct == NULL_TREE)
+	{
+	  type = make_node (RECORD_TYPE);
+	  tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+				   get_identifier ("dummy"), boolean_type_node);
+	  DECL_CONTEXT (field) = type;
+	  TYPE_FIELDS (type) = field;
+	  layout_type (type);
+	  rust_non_zero_struct = type;
+	}
+      return rust_non_zero_struct;
+
+      case ARRAY_TYPE: {
+	tree element_type = non_zero_size_type (TREE_TYPE (type));
+	return build_array_type_nelts (element_type, 1);
+      }
+
+    default:
+      gcc_unreachable ();
+    }
+
+  gcc_unreachable ();
+}
+
+// Convert EXPR_TREE to TYPE_TREE.  Sometimes the same unnamed Rust type
+// can be created multiple times and thus have multiple tree
+// representations.  Make sure this does not confuse the middle-end.
+
+tree
+Gcc_backend::convert_tree (tree type_tree, tree expr_tree, Location location)
+{
+  if (type_tree == TREE_TYPE (expr_tree))
+    return expr_tree;
+
+  if (type_tree == error_mark_node || expr_tree == error_mark_node
+      || TREE_TYPE (expr_tree) == error_mark_node)
+    return error_mark_node;
+
+  if (POINTER_TYPE_P (type_tree) || INTEGRAL_TYPE_P (type_tree)
+      || SCALAR_FLOAT_TYPE_P (type_tree) || COMPLEX_FLOAT_TYPE_P (type_tree))
+    return fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+  else if (TREE_CODE (type_tree) == RECORD_TYPE
+	   || TREE_CODE (type_tree) == UNION_TYPE
+	   || TREE_CODE (type_tree) == ARRAY_TYPE)
+    {
+      gcc_assert (int_size_in_bytes (type_tree)
+		  == int_size_in_bytes (TREE_TYPE (expr_tree)));
+      if (TYPE_MAIN_VARIANT (type_tree)
+	  == TYPE_MAIN_VARIANT (TREE_TYPE (expr_tree)))
+	return fold_build1_loc (location.gcc_location (), NOP_EXPR, type_tree,
+				expr_tree);
+      return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+			      type_tree, expr_tree);
+    }
+
+  gcc_unreachable ();
+}
+
+// Make a global variable.
+
+Bvariable *
+Gcc_backend::global_variable (const std::string &var_name,
+			      const std::string &asm_name, tree type_tree,
+			      bool is_external, bool is_hidden,
+			      bool in_unique_section, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+
+  // The GNU linker does not like dynamic variables with zero size.
+  tree orig_type_tree = type_tree;
+  if ((is_external || !is_hidden) && int_size_in_bytes (type_tree) == 0)
+    type_tree = this->non_zero_size_type (type_tree);
+
+  tree decl = build_decl (location.gcc_location (), VAR_DECL,
+			  get_identifier_from_string (var_name), type_tree);
+  if (is_external)
+    DECL_EXTERNAL (decl) = 1;
+  else
+    TREE_STATIC (decl) = 1;
+  if (!is_hidden)
+    {
+      TREE_PUBLIC (decl) = 1;
+      SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+    }
+  else
+    {
+      SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+    }
+
+  TREE_USED (decl) = 1;
+
+  if (in_unique_section)
+    resolve_unique_section (decl, 0, 1);
+
+  rust_preserve_from_gc (decl);
+
+  return new Bvariable (decl, orig_type_tree);
+}
+
+// Set the initial value of a global variable.
+
+void
+Gcc_backend::global_variable_set_init (Bvariable *var, tree expr_tree)
+{
+  if (expr_tree == error_mark_node)
+    return;
+  gcc_assert (TREE_CONSTANT (expr_tree));
+  tree var_decl = var->get_decl ();
+  if (var_decl == error_mark_node)
+    return;
+  DECL_INITIAL (var_decl) = expr_tree;
+
+  // If this variable goes in a unique section, it may need to go into
+  // a different one now that DECL_INITIAL is set.
+  if (symtab_node::get (var_decl)
+      && symtab_node::get (var_decl)->implicit_section)
+    {
+      set_decl_section_name (var_decl, (const char *) NULL);
+      resolve_unique_section (var_decl, compute_reloc_for_constant (expr_tree),
+			      1);
+    }
+}
+
+// Make a local variable.
+
+Bvariable *
+Gcc_backend::local_variable (tree function, const std::string &name,
+			     tree type_tree, Bvariable *decl_var,
+			     Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), VAR_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = function;
+
+  if (decl_var != NULL)
+    {
+      DECL_HAS_VALUE_EXPR_P (decl) = 1;
+      SET_DECL_VALUE_EXPR (decl, decl_var->get_decl ());
+    }
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a function parameter variable.
+
+Bvariable *
+Gcc_backend::parameter_variable (tree function, const std::string &name,
+				 tree type_tree, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), PARM_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = function;
+  DECL_ARG_TYPE (decl) = type_tree;
+
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a static chain variable.
+
+Bvariable *
+Gcc_backend::static_chain_variable (tree fndecl, const std::string &name,
+				    tree type_tree, Location location)
+{
+  if (type_tree == error_mark_node)
+    return this->error_variable ();
+  tree decl = build_decl (location.gcc_location (), PARM_DECL,
+			  get_identifier_from_string (name), type_tree);
+  DECL_CONTEXT (decl) = fndecl;
+  DECL_ARG_TYPE (decl) = type_tree;
+  TREE_USED (decl) = 1;
+  DECL_ARTIFICIAL (decl) = 1;
+  DECL_IGNORED_P (decl) = 1;
+  TREE_READONLY (decl) = 1;
+
+  struct function *f = DECL_STRUCT_FUNCTION (fndecl);
+  if (f == NULL)
+    {
+      push_struct_function (fndecl);
+      pop_cfun ();
+      f = DECL_STRUCT_FUNCTION (fndecl);
+    }
+  gcc_assert (f->static_chain_decl == NULL);
+  f->static_chain_decl = decl;
+  DECL_STATIC_CHAIN (fndecl) = 1;
+
+  rust_preserve_from_gc (decl);
+  return new Bvariable (decl);
+}
+
+// Make a temporary variable.
+
+Bvariable *
+Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree,
+				 tree init_tree, bool is_address_taken,
+				 Location location, tree *pstatement)
+{
+  gcc_assert (fndecl != NULL_TREE);
+  if (type_tree == error_mark_node || init_tree == error_mark_node
+      || fndecl == error_mark_node)
+    {
+      *pstatement = error_mark_node;
+      return this->error_variable ();
+    }
+
+  tree var;
+  // We can only use create_tmp_var if the type is not addressable.
+  if (!TREE_ADDRESSABLE (type_tree))
+    {
+      if (DECL_STRUCT_FUNCTION (fndecl) == NULL)
+	push_struct_function (fndecl);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+
+      var = create_tmp_var (type_tree, "RUSTTMP");
+      pop_cfun ();
+    }
+  else
+    {
+      gcc_assert (bind_tree != NULL_TREE);
+      var = build_decl (location.gcc_location (), VAR_DECL,
+			create_tmp_var_name ("RUSTTMP"), type_tree);
+      DECL_ARTIFICIAL (var) = 1;
+      DECL_IGNORED_P (var) = 1;
+      TREE_USED (var) = 1;
+      DECL_CONTEXT (var) = fndecl;
+
+      // We have to add this variable to the BLOCK and the BIND_EXPR.
+      gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+      tree block_tree = BIND_EXPR_BLOCK (bind_tree);
+      gcc_assert (TREE_CODE (block_tree) == BLOCK);
+      DECL_CHAIN (var) = BLOCK_VARS (block_tree);
+      BLOCK_VARS (block_tree) = var;
+      BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree);
+    }
+
+  if (this->type_size (type_tree) != 0 && init_tree != NULL_TREE
+      && TREE_TYPE (init_tree) != void_type_node)
+    DECL_INITIAL (var) = this->convert_tree (type_tree, init_tree, location);
+
+  if (is_address_taken)
+    TREE_ADDRESSABLE (var) = 1;
+
+  *pstatement
+    = build1_loc (location.gcc_location (), DECL_EXPR, void_type_node, var);
+
+  // For a zero sized type, don't initialize VAR with BINIT, but still
+  // evaluate BINIT for its side effects.
+  if (init_tree != NULL_TREE
+      && (this->type_size (type_tree) == 0
+	  || TREE_TYPE (init_tree) == void_type_node))
+    *pstatement = this->compound_statement (init_tree, *pstatement);
+
+  return new Bvariable (var);
+}
+
+// Make a label.
+
+tree
+Gcc_backend::label (tree func_tree, const std::string &name, Location location)
+{
+  tree decl;
+  if (name.empty ())
+    {
+      if (DECL_STRUCT_FUNCTION (func_tree) == NULL)
+	push_struct_function (func_tree);
+      else
+	push_cfun (DECL_STRUCT_FUNCTION (func_tree));
+
+      decl = create_artificial_label (location.gcc_location ());
+
+      pop_cfun ();
+    }
+  else
+    {
+      tree id = get_identifier_from_string (name);
+      decl
+	= build_decl (location.gcc_location (), LABEL_DECL, id, void_type_node);
+      DECL_CONTEXT (decl) = func_tree;
+    }
+  return decl;
+}
+
+// Make a statement which defines a label.
+
+tree
+Gcc_backend::label_definition_statement (tree label)
+{
+  return fold_build1_loc (DECL_SOURCE_LOCATION (label), LABEL_EXPR,
+			  void_type_node, label);
+}
+
+// Make a goto statement.
+
+tree
+Gcc_backend::goto_statement (tree label, Location location)
+{
+  return fold_build1_loc (location.gcc_location (), GOTO_EXPR, void_type_node,
+			  label);
+}
+
+// Get the address of a label.
+
+tree
+Gcc_backend::label_address (tree label, Location location)
+{
+  TREE_USED (label) = 1;
+  TREE_ADDRESSABLE (label) = 1;
+  tree ret
+    = fold_convert_loc (location.gcc_location (), ptr_type_node,
+			build_fold_addr_expr_loc (location.gcc_location (),
+						  label));
+  return ret;
+}
+
+// Declare or define a new function.
+
+tree
+Gcc_backend::function (tree functype, const std::string &name,
+		       const std::string &asm_name, unsigned int flags,
+		       Location location)
+{
+  if (functype != error_mark_node)
+    {
+      gcc_assert (FUNCTION_POINTER_TYPE_P (functype));
+      functype = TREE_TYPE (functype);
+    }
+  tree id = get_identifier_from_string (name);
+  if (functype == error_mark_node || id == error_mark_node)
+    return error_mark_node;
+
+  tree decl
+    = build_decl (location.gcc_location (), FUNCTION_DECL, id, functype);
+  if (!asm_name.empty ())
+    SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+
+  if ((flags & function_is_declaration) != 0)
+    DECL_EXTERNAL (decl) = 1;
+  else
+    {
+      tree restype = TREE_TYPE (functype);
+      tree resdecl = build_decl (location.gcc_location (), RESULT_DECL,
+				 NULL_TREE, restype);
+      DECL_ARTIFICIAL (resdecl) = 1;
+      DECL_IGNORED_P (resdecl) = 1;
+      DECL_CONTEXT (resdecl) = decl;
+      DECL_RESULT (decl) = resdecl;
+    }
+  if ((flags & function_is_uninlinable) != 0)
+    DECL_UNINLINABLE (decl) = 1;
+  if ((flags & function_does_not_return) != 0)
+    TREE_THIS_VOLATILE (decl) = 1;
+  if ((flags & function_in_unique_section) != 0)
+    resolve_unique_section (decl, 0, 1);
+
+  rust_preserve_from_gc (decl);
+  return decl;
+}
+
+// Create a statement that runs all deferred calls for FUNCTION.  This should
+// be a statement that looks like this in C++:
+//   finish:
+//     try { UNDEFER; } catch { CHECK_DEFER; goto finish; }
+
+tree
+Gcc_backend::function_defer_statement (tree function, tree undefer_tree,
+				       tree defer_tree, Location location)
+{
+  if (undefer_tree == error_mark_node || defer_tree == error_mark_node
+      || function == error_mark_node)
+    return error_mark_node;
+
+  if (DECL_STRUCT_FUNCTION (function) == NULL)
+    push_struct_function (function);
+  else
+    push_cfun (DECL_STRUCT_FUNCTION (function));
+
+  tree stmt_list = NULL;
+  tree label = this->label (function, "", location);
+  tree label_def = this->label_definition_statement (label);
+  append_to_statement_list (label_def, &stmt_list);
+
+  tree jump_stmt = this->goto_statement (label, location);
+  tree catch_body
+    = build2 (COMPOUND_EXPR, void_type_node, defer_tree, jump_stmt);
+  catch_body = build2 (CATCH_EXPR, void_type_node, NULL, catch_body);
+  tree try_catch
+    = build2 (TRY_CATCH_EXPR, void_type_node, undefer_tree, catch_body);
+  append_to_statement_list (try_catch, &stmt_list);
+  pop_cfun ();
+
+  return stmt_list;
+}
+
+// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
+// This will only be called for a function definition.
+
+bool
+Gcc_backend::function_set_parameters (
+  tree function, const std::vector<Bvariable *> &param_vars)
+{
+  if (function == error_mark_node)
+    return false;
+
+  tree params = NULL_TREE;
+  tree *pp = &params;
+  for (std::vector<Bvariable *>::const_iterator pv = param_vars.begin ();
+       pv != param_vars.end (); ++pv)
+    {
+      *pp = (*pv)->get_decl ();
+      gcc_assert (*pp != error_mark_node);
+      pp = &DECL_CHAIN (*pp);
+    }
+  *pp = NULL_TREE;
+  DECL_ARGUMENTS (function) = params;
+  return true;
+}
+
+// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
+// FUNCTION_DECLS, and VARIABLE_DECLS declared globally, as well as
+// emit early debugging information.
+
+void
+Gcc_backend::write_global_definitions (
+  const std::vector<tree> &type_decls, const std::vector<tree> &constant_decls,
+  const std::vector<tree> &function_decls,
+  const std::vector<Bvariable *> &variable_decls)
+{
+  size_t count_definitions = type_decls.size () + constant_decls.size ()
+			     + function_decls.size () + variable_decls.size ();
+
+  tree *defs = new tree[count_definitions];
+
+  // Convert all non-erroneous declarations into Gimple form.
+  size_t i = 0;
+  for (std::vector<Bvariable *>::const_iterator p = variable_decls.begin ();
+       p != variable_decls.end (); ++p)
+    {
+      tree v = (*p)->get_decl ();
+      if (v != error_mark_node)
+	{
+	  defs[i] = v;
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+
+  for (std::vector<tree>::const_iterator p = type_decls.begin ();
+       p != type_decls.end (); ++p)
+    {
+      tree type_tree = (*p);
+      if (type_tree != error_mark_node && IS_TYPE_OR_DECL_P (type_tree))
+	{
+	  defs[i] = TYPE_NAME (type_tree);
+	  gcc_assert (defs[i] != NULL);
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+  for (std::vector<tree>::const_iterator p = constant_decls.begin ();
+       p != constant_decls.end (); ++p)
+    {
+      if ((*p) != error_mark_node)
+	{
+	  defs[i] = (*p);
+	  rust_preserve_from_gc (defs[i]);
+	  ++i;
+	}
+    }
+  for (std::vector<tree>::const_iterator p = function_decls.begin ();
+       p != function_decls.end (); ++p)
+    {
+      tree decl = (*p);
+      if (decl != error_mark_node)
+	{
+	  rust_preserve_from_gc (decl);
+	  if (DECL_STRUCT_FUNCTION (decl) == NULL)
+	    allocate_struct_function (decl, false);
+	  dump_function (TDI_original, decl);
+	  cgraph_node::finalize_function (decl, true);
+
+	  defs[i] = decl;
+	  ++i;
+	}
+    }
+
+  // Pass everything back to the middle-end.
+
+  wrapup_global_declarations (defs, i);
+
+  delete[] defs;
+}
+
+void
+Gcc_backend::write_export_data (const char *bytes, unsigned int size)
+{
+  rust_write_export_data (bytes, size);
+}
+
+// Return the backend generator.
+
+Backend *
+rust_get_backend ()
+{
+  return new Gcc_backend ();
+}
