diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc
new file mode 100644
index 00000000000..241a9b4dae3
--- /dev/null
+++ b/gcc/rust/rust-lang.cc
@@ -0,0 +1,456 @@
+// 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-system.h"
+#include "rust-diagnostics.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "diagnostic.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "gimplify.h"
+#include "stor-layout.h"
+#include "debug.h"
+#include "convert.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "selftest.h"
+#include "rust-cfg-parser.h"
+#include "rust-privacy-ctx.h"
+#include "rust-ast-resolve-item.h"
+#include "rust-optional.h"
+
+#include <mpfr.h>
+// note: header files must be in this order or else forward declarations don't
+// work properly. Kinda dumb system, but have to live with it. clang-format
+// seems to mess it up
+/* Order: config, system, coretypes, target, tree, gimple-expr, diagnostic,
+ * opts, fold-const, gimplify, stor-layout, debug, convert, langhooks,
+ * langhooks-def */
+
+// FIXME: test saving intellisense
+#include "options.h"
+
+// version check to stop compiling if c++ isn't c++11 or higher
+#if __cplusplus < 201103
+#error                                                                         \
+  "GCC Rust frontend requires C++11 or higher. You can compile the g++ frontend first and then compile the Rust frontend using that."
+#endif
+// TODO: is this best way to do it? Is it allowed? (should be)
+
+/* General TODOs:
+ *  - convert all copies of expensive-to-copy (deep copy) AST objects into
+ * moves, if possible. Don't remove clone functionality - it may be required for
+ * e.g. HIR conversion.
+ */
+
+#include "rust-session-manager.h"
+
+// Language-dependent contents of a type. GTY() mark used for garbage collector.
+struct GTY (()) lang_type
+{
+};
+
+// Language-dependent contents of a decl.
+struct GTY (()) lang_decl
+{
+};
+
+// Language-dependent contents of an identifier.  This must include a
+// tree_identifier.
+struct GTY (()) lang_identifier
+{
+  struct tree_identifier common;
+};
+
+// The resulting tree type.
+union GTY ((
+  desc ("TREE_CODE (&%h.generic) == IDENTIFIER_NODE"),
+  chain_next (
+    "CODE_CONTAINS_STRUCT (TREE_CODE (&%h.generic), "
+    "TS_COMMON) ? ((union lang_tree_node *) TREE_CHAIN (&%h.generic)) : NULL")))
+  lang_tree_node
+{
+  union tree_node GTY ((tag ("0"), desc ("tree_node_structure (&%h)"))) generic;
+  struct lang_identifier GTY ((tag ("1"))) identifier;
+};
+
+// We don't use language_function.
+struct GTY (()) language_function
+{
+};
+
+// has to be in same compilation unit as session, so here for now
+void
+rust_add_target_info (const char *key, const char *value)
+{
+  sorry ("TODO");
+
+  Rust::Session::get_instance ().options.target_data.insert_key_value_pair (
+    key, value);
+}
+
+/* Language hooks.  */
+
+/* Initial lang hook called (possibly), used for initialisation.
+ * Must call build_common_tree_nodes, set_sizetype, build_common_tree_nodes_2,
+ * and build_common_builtin_nodes, as well as set global variable
+ * void_list_node. Apparently called after option handling? */
+static bool
+grs_langhook_init (void)
+{
+  /* Something to do with this:
+   This allows the code in d-builtins.cc to not have to worry about
+   converting (C signed char *) to (D char *) for string arguments of
+   built-in functions. The parameter (signed_char = false) specifies
+   whether char is signed.  */
+  build_common_tree_nodes (false);
+
+  // Creates a new TREE_LIST node with purpose NULL_TREE and value
+  // void_type_node
+  void_list_node = build_tree_list (NULL_TREE, void_type_node);
+
+  // Builds built-ins for middle-end after all front-end built-ins are already
+  // instantiated
+  build_common_builtin_nodes ();
+
+  mpfr_set_default_prec (128);
+
+  using_eh_for_cleanups ();
+
+  // initialise compiler session
+  Rust::Session::get_instance ().init ();
+
+  return true;
+}
+
+/* The option mask (something to do with options for specific frontends or
+ * something). */
+static unsigned int
+grs_langhook_option_lang_mask (void)
+{
+  return CL_Rust;
+}
+
+/* Initialize the options structure. */
+static void
+grs_langhook_init_options_struct (struct gcc_options *opts)
+{
+  /* Operations are always wrapping in Rust, even on signed integer. This is
+   * useful for the low level wrapping_{add, sub, mul} intrinsics, not for
+   * regular arithmetic operations which are checked for overflow anyway using
+   * builtins */
+  opts->x_flag_wrapv = 1;
+
+  /* We need to warn on unused variables by default */
+  opts->x_warn_unused_variable = 1;
+  /* For const variables too */
+  opts->x_warn_unused_const_variable = 1;
+  /* And finally unused result for #[must_use] */
+  opts->x_warn_unused_result = 1;
+
+  // nothing yet - used by frontends to change specific options for the language
+  Rust::Session::get_instance ().init_options ();
+}
+
+/* Main entry point for front-end, apparently. Finds input file names in global
+ * vars in_fnames and num_in_fnames. From this, frontend can take over and do
+ * actual parsing and initial compilation. This function must create a complete
+ * parse tree in a global var, and then return.
+ *
+ * Some consider this the "start of compilation". */
+static void
+grs_langhook_parse_file (void)
+{
+  rust_debug ("Preparing to parse files. ");
+
+  Rust::Session::get_instance ().handle_input_files (num_in_fnames, in_fnames);
+}
+
+/* Seems to get the exact type for a specific type - e.g. for scalar float with
+ * 32-bit bitsize, it returns float, and for 64-bit bitsize, it returns double.
+ * Used to map RTL nodes to machine modes or something like that. */
+static tree
+grs_langhook_type_for_mode (machine_mode mode, int unsignedp)
+{
+  // TODO: change all this later to match rustc types
+  if (mode == TYPE_MODE (float_type_node))
+    return float_type_node;
+
+  if (mode == TYPE_MODE (double_type_node))
+    return double_type_node;
+
+  if (mode == TYPE_MODE (intQI_type_node)) // quarter integer mode - single byte
+					   // treated as integer
+    return unsignedp ? unsigned_intQI_type_node : intQI_type_node;
+  if (mode
+      == TYPE_MODE (intHI_type_node)) // half integer mode - two-byte integer
+    return unsignedp ? unsigned_intHI_type_node : intHI_type_node;
+  if (mode
+      == TYPE_MODE (intSI_type_node)) // single integer mode - four-byte integer
+    return unsignedp ? unsigned_intSI_type_node : intSI_type_node;
+  if (mode
+      == TYPE_MODE (
+	intDI_type_node)) // double integer mode - eight-byte integer
+    return unsignedp ? unsigned_intDI_type_node : intDI_type_node;
+  if (mode
+      == TYPE_MODE (intTI_type_node)) // tetra integer mode - 16-byte integer
+    return unsignedp ? unsigned_intTI_type_node : intTI_type_node;
+
+  if (mode == TYPE_MODE (integer_type_node))
+    return unsignedp ? unsigned_type_node : integer_type_node;
+
+  if (mode == TYPE_MODE (long_integer_type_node))
+    return unsignedp ? long_unsigned_type_node : long_integer_type_node;
+
+  if (mode == TYPE_MODE (long_long_integer_type_node))
+    return unsignedp ? long_long_unsigned_type_node
+		     : long_long_integer_type_node;
+
+  if (COMPLEX_MODE_P (mode))
+    {
+      if (mode == TYPE_MODE (complex_float_type_node))
+	return complex_float_type_node;
+      if (mode == TYPE_MODE (complex_double_type_node))
+	return complex_double_type_node;
+      if (mode == TYPE_MODE (complex_long_double_type_node))
+	return complex_long_double_type_node;
+      if (mode == TYPE_MODE (complex_integer_type_node) && !unsignedp)
+	return complex_integer_type_node;
+    }
+  /* gcc_unreachable */
+  return NULL;
+}
+
+// Record a builtin function. We just ignore builtin functions.
+static tree
+grs_langhook_builtin_function (tree decl ATTRIBUTE_UNUSED)
+{
+  return decl;
+}
+
+/* Return true if we are in the global binding level (which is never,
+ * apparently). */
+static bool
+grs_langhook_global_bindings_p (void)
+{
+  // return current_function_decl == NULL_TREE;
+  // gcc_unreachable();
+  // return true;
+  return false;
+}
+
+/* Push a declaration into the current binding level.  We can't
+   usefully implement this since we don't want to convert from tree
+   back to one of our internal data structures.  I think the only way
+   this is used is to record a decl which is to be returned by
+   getdecls, and we could implement it for that purpose if
+   necessary.  */
+static tree
+grs_langhook_pushdecl (tree decl ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+  return NULL;
+}
+
+/* This hook is used to get the current list of declarations as trees.
+   We don't support that; instead we use the write_globals hook.  This
+   can't simply crash because it is called by -gstabs.  */
+static tree
+grs_langhook_getdecls (void)
+{
+  // gcc_unreachable();
+  return NULL;
+}
+
+// Handle Rust-specific options. Return false if nothing happened.
+static bool
+grs_langhook_handle_option (
+  size_t scode, const char *arg, HOST_WIDE_INT value, int kind ATTRIBUTE_UNUSED,
+  location_t loc ATTRIBUTE_UNUSED,
+  const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+  // Convert integer code to lang.opt enum codes with names.
+  enum opt_code code = (enum opt_code) scode;
+
+  // Delegate to session manager
+  return Rust::Session::get_instance ().handle_option (code, arg, value, kind,
+						       loc, handlers);
+}
+
+/* Run after parsing options.  */
+static bool
+grs_langhook_post_options (const char **pfilename ATTRIBUTE_UNUSED)
+{
+  // can be used to override other options if required
+
+  // satisfies an assert in init_excess_precision in toplev.cc
+  if (flag_excess_precision /*_cmdline*/ == EXCESS_PRECISION_DEFAULT)
+    flag_excess_precision /*_cmdline*/ = EXCESS_PRECISION_STANDARD;
+
+  /* Returning false means that the backend should be used.  */
+  return false;
+}
+
+/* Rust-specific gimplification. May need to gimplify e.g.
+ * CALL_EXPR_STATIC_CHAIN */
+static int
+grs_langhook_gimplify_expr (tree *expr_p ATTRIBUTE_UNUSED,
+			    gimple_seq *pre_p ATTRIBUTE_UNUSED,
+			    gimple_seq *post_p ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (*expr_p) == CALL_EXPR
+      && CALL_EXPR_STATIC_CHAIN (*expr_p) != NULL_TREE)
+    gimplify_expr (&CALL_EXPR_STATIC_CHAIN (*expr_p), pre_p, post_p,
+		   is_gimple_val, fb_rvalue);
+  return GS_UNHANDLED;
+}
+
+static tree
+grs_langhook_eh_personality (void)
+{
+  static tree personality_decl;
+  if (personality_decl == NULL_TREE)
+    {
+      personality_decl = build_personality_function ("gccrs");
+      rust_preserve_from_gc (personality_decl);
+    }
+  return personality_decl;
+}
+
+tree
+convert (tree type, tree expr)
+{
+  if (type == error_mark_node || expr == error_mark_node
+      || TREE_TYPE (expr) == error_mark_node)
+    return error_mark_node;
+
+  if (type == TREE_TYPE (expr))
+    return expr;
+
+  if (TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (TREE_TYPE (expr)))
+    return fold_convert (type, expr);
+
+  switch (TREE_CODE (type))
+    {
+    case VOID_TYPE:
+    case BOOLEAN_TYPE:
+      return fold_convert (type, expr);
+    case INTEGER_TYPE:
+      return convert_to_integer (type, expr);
+    case POINTER_TYPE:
+      return convert_to_pointer (type, expr);
+    case REAL_TYPE:
+      return convert_to_real (type, expr);
+    case COMPLEX_TYPE:
+      return convert_to_complex (type, expr);
+    default:
+      break;
+    }
+
+  gcc_unreachable ();
+}
+
+/* FIXME: This is a hack to preserve trees that we create from the
+   garbage collector.  */
+
+static GTY (()) tree rust_gc_root;
+
+void
+rust_preserve_from_gc (tree t)
+{
+  rust_gc_root = tree_cons (NULL_TREE, t, rust_gc_root);
+}
+
+/* Convert an identifier for use in an error message.  */
+
+const char *
+rust_localize_identifier (const char *ident)
+{
+  return identifier_to_locale (ident);
+}
+
+/* The language hooks data structure. This is the main interface between the GCC
+ * front-end and the GCC middle-end/back-end. A list of language hooks could be
+ * found in <gcc>/langhooks.h
+ */
+#undef LANG_HOOKS_NAME
+#undef LANG_HOOKS_INIT
+#undef LANG_HOOKS_OPTION_LANG_MASK
+#undef LANG_HOOKS_INIT_OPTIONS_STRUCT
+#undef LANG_HOOKS_HANDLE_OPTION
+#undef LANG_HOOKS_POST_OPTIONS
+#undef LANG_HOOKS_PARSE_FILE
+#undef LANG_HOOKS_TYPE_FOR_MODE
+#undef LANG_HOOKS_BUILTIN_FUNCTION
+#undef LANG_HOOKS_GLOBAL_BINDINGS_P
+#undef LANG_HOOKS_PUSHDECL
+#undef LANG_HOOKS_GETDECLS
+#undef LANG_HOOKS_WRITE_GLOBALS
+#undef LANG_HOOKS_GIMPLIFY_EXPR
+#undef LANG_HOOKS_EH_PERSONALITY
+
+#define LANG_HOOKS_NAME "GNU Rust"
+#define LANG_HOOKS_INIT grs_langhook_init
+#define LANG_HOOKS_OPTION_LANG_MASK grs_langhook_option_lang_mask
+#define LANG_HOOKS_INIT_OPTIONS_STRUCT grs_langhook_init_options_struct
+#define LANG_HOOKS_HANDLE_OPTION grs_langhook_handle_option
+#define LANG_HOOKS_POST_OPTIONS grs_langhook_post_options
+/* Main lang-hook, apparently. Finds input file names in global vars in_fnames
+ * and num_in_fnames From this, frontend can take over and do actual parsing and
+ * initial compilation.
+ * This hook must create a complete parse tree in a global var, and then return.
+ */
+#define LANG_HOOKS_PARSE_FILE grs_langhook_parse_file
+#define LANG_HOOKS_TYPE_FOR_MODE grs_langhook_type_for_mode
+#define LANG_HOOKS_BUILTIN_FUNCTION grs_langhook_builtin_function
+#define LANG_HOOKS_GLOBAL_BINDINGS_P grs_langhook_global_bindings_p
+#define LANG_HOOKS_PUSHDECL grs_langhook_pushdecl
+#define LANG_HOOKS_GETDECLS grs_langhook_getdecls
+#define LANG_HOOKS_GIMPLIFY_EXPR grs_langhook_gimplify_expr
+#define LANG_HOOKS_EH_PERSONALITY grs_langhook_eh_personality
+
+#if CHECKING_P
+
+#undef LANG_HOOKS_RUN_LANG_SELFTESTS
+#define LANG_HOOKS_RUN_LANG_SELFTESTS selftest::run_rust_tests
+
+namespace selftest {
+
+void
+run_rust_tests ()
+{
+  // Call tests for the rust frontend here
+  rust_cfg_parser_test ();
+  rust_privacy_ctx_test ();
+  rust_crate_name_validation_test ();
+  rust_simple_path_resolve_test ();
+  rust_optional_test ();
+}
+} // namespace selftest
+
+#endif /* !CHECKING_P */
+
+// Expands all LANG_HOOKS_x of GCC
+struct lang_hooks lang_hooks = LANG_HOOKS_INITIALIZER;
+
+// These are for GCC's garbage collector to work properly or something
+#include "gt-rust-rust-lang.h"
+#include "gtype-rust.h"
diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc
new file mode 100644
index 00000000000..6d7f1a85f19
--- /dev/null
+++ b/gcc/rust/rust-session-manager.cc
@@ -0,0 +1,1189 @@
+// 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-session-manager.h"
+#include "rust-diagnostics.h"
+#include "rust-unsafe-checker.h"
+#include "rust-lex.h"
+#include "rust-parse.h"
+#include "rust-macro-expand.h"
+#include "rust-ast-resolve.h"
+#include "rust-ast-lower.h"
+#include "rust-hir-type-check.h"
+#include "rust-privacy-check.h"
+#include "rust-const-checker.h"
+#include "rust-tycheck-dump.h"
+#include "rust-compile.h"
+#include "rust-cfg-parser.h"
+#include "rust-lint-scan-deadcode.h"
+#include "rust-lint-unused-var.h"
+#include "rust-hir-dump.h"
+#include "rust-ast-dump.h"
+#include "rust-export-metadata.h"
+#include "rust-imports.h"
+#include "rust-extern-crate.h"
+#include "rust-attributes.h"
+
+#include "diagnostic.h"
+#include "input.h"
+#include "selftest.h"
+#include "target.h"
+
+extern bool
+saw_errors (void);
+
+extern Linemap *
+rust_get_linemap ();
+
+extern Backend *
+rust_get_backend ();
+
+namespace Rust {
+
+const char *kLexDumpFile = "gccrs.lex.dump";
+const char *kASTDumpFile = "gccrs.ast.dump";
+const char *kASTPrettyDumpFile = "gccrs.ast-pretty.dump";
+const char *kASTExpandedDumpFile = "gccrs.ast-expanded.dump";
+const char *kHIRDumpFile = "gccrs.hir.dump";
+const char *kHIRPrettyDumpFile = "gccrs.hir-pretty.dump";
+const char *kHIRTypeResolutionDumpFile = "gccrs.type-resolution.dump";
+const char *kTargetOptionsDumpFile = "gccrs.target-options.dump";
+
+const std::string kDefaultCrateName = "rust_out";
+const size_t kMaxNameLength = 64;
+
+Session &
+Session::get_instance ()
+{
+  static Session instance;
+  return instance;
+}
+
+static std::string
+infer_crate_name (const std::string &filename)
+{
+  if (filename == "-")
+    return kDefaultCrateName;
+
+  std::string crate = std::string (filename);
+  size_t path_sep = crate.find_last_of (file_separator);
+
+  // find the base filename
+  if (path_sep != std::string::npos)
+    crate.erase (0, path_sep + 1);
+
+  // find the file stem name (remove file extension)
+  size_t ext_position = crate.find_last_of ('.');
+  if (ext_position != std::string::npos)
+    crate.erase (ext_position);
+
+  // Replace all the '-' symbols with '_' per Rust rules
+  for (auto &c : crate)
+    {
+      if (c == '-')
+	c = '_';
+    }
+  return crate;
+}
+
+/* Validate the crate name using the ASCII rules
+   TODO: Support Unicode version of the rules */
+
+static bool
+validate_crate_name (const std::string &crate_name, Error &error)
+{
+  if (crate_name.empty ())
+    {
+      error = Error (Location (), "crate name cannot be empty");
+      return false;
+    }
+  if (crate_name.length () > kMaxNameLength)
+    {
+      error = Error (Location (), "crate name cannot exceed %lu characters",
+		     (unsigned long) kMaxNameLength);
+      return false;
+    }
+  for (auto &c : crate_name)
+    {
+      if (!(ISALNUM (c) || c == '_'))
+	{
+	  error = Error (Location (),
+			 "invalid character %<%c%> in crate name: %<%s%>", c,
+			 crate_name.c_str ());
+	  return false;
+	}
+    }
+  return true;
+}
+
+void
+Session::init ()
+{
+  options.target_data.insert_key_value_pair ("target_pointer_width",
+					     std::to_string (POINTER_SIZE));
+  options.target_data.insert_key_value_pair ("target_endian", BYTES_BIG_ENDIAN
+								? "big"
+								: "little");
+
+  // setup singleton linemap
+  linemap = rust_get_linemap ();
+
+  // setup backend to GCC GIMPLE
+  backend = rust_get_backend ();
+
+  // setup mappings class
+  mappings = Analysis::Mappings::get ();
+}
+
+/* Initialise default options. Actually called before handle_option, unlike init
+ * itself. */
+void
+Session::init_options ()
+{}
+
+// Handle option selection.
+bool
+Session::handle_option (
+  enum opt_code code, const char *arg, HOST_WIDE_INT value ATTRIBUTE_UNUSED,
+  int kind ATTRIBUTE_UNUSED, location_t loc ATTRIBUTE_UNUSED,
+  const struct cl_option_handlers *handlers ATTRIBUTE_UNUSED)
+{
+  // used to store whether results of various stuff are successful
+  bool ret = true;
+
+  // Handles options as listed in lang.opt.
+  switch (code)
+    {
+    case OPT_I:
+      case OPT_L: {
+	// TODO: add search path
+	const std::string p = std::string (arg);
+	add_search_path (p);
+      }
+      break;
+
+    case OPT_frust_crate_:
+      // set the crate name
+      if (arg != nullptr)
+	{
+	  auto error = Error (Location (), std::string ());
+	  if ((ret = validate_crate_name (arg, error)))
+	    {
+	      options.set_crate_name (arg);
+	      options.crate_name_set_manually = true;
+	    }
+	  else
+	    {
+	      rust_assert (!error.message.empty ());
+	      error.emit_error ();
+	    }
+	}
+      else
+	ret = false;
+      break;
+
+    case OPT_frust_dump_:
+      // enable dump and return whether this was successful
+      if (arg != nullptr)
+	{
+	  ret = enable_dump (std::string (arg));
+	}
+      else
+	{
+	  ret = false;
+	}
+      break;
+
+    case OPT_frust_mangling_:
+      Compile::Mangler::set_mangling (flag_rust_mangling);
+      break;
+
+      case OPT_frust_cfg_: {
+	auto string_arg = std::string (arg);
+	ret = handle_cfg_option (string_arg);
+	break;
+      }
+
+    case OPT_frust_edition_:
+      options.set_edition (flag_rust_edition);
+      break;
+
+    case OPT_frust_metadata_output_:
+      options.set_metadata_output (arg);
+      break;
+
+    default:
+      break;
+    }
+
+  return ret;
+}
+
+bool
+Session::handle_cfg_option (std::string &input)
+{
+  std::string key;
+  std::string value;
+
+  // Refactor this if needed
+  if (!parse_cfg_option (input, key, value))
+    {
+      rust_error_at (
+	Location (),
+	"invalid argument to %<-frust-cfg%>: Accepted formats are "
+	"%<-frust-cfg=key%> or %<-frust-cfg=key=\"value\"%> (quoted)");
+      return false;
+    }
+
+  if (value.empty ())
+    // rustc does not seem to error on dup key
+    options.target_data.insert_key (key);
+  else
+    options.target_data.insert_key_value_pair (key, value);
+
+  return true;
+}
+
+/* Enables a certain dump depending on the name passed in. Returns true if
+ * name is valid, false otherwise. */
+bool
+Session::enable_dump (std::string arg)
+{
+  if (arg.empty ())
+    {
+      rust_error_at (
+	Location (),
+	"dump option was not given a name. choose %<lex%>, %<parse%>, "
+	"%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+	" %<target_options%>, %<hir%>, or %<all%>");
+      return false;
+    }
+
+  if (arg == "all")
+    {
+      options.enable_all_dump_options ();
+    }
+  else if (arg == "lex")
+    {
+      options.enable_dump_option (CompileOptions::LEXER_DUMP);
+    }
+  else if (arg == "parse")
+    {
+      options.enable_dump_option (CompileOptions::PARSER_AST_DUMP);
+    }
+  else if (arg == "ast-pretty")
+    {
+      options.enable_dump_option (CompileOptions::AST_DUMP_PRETTY);
+    }
+  else if (arg == "register_plugins")
+    {
+      options.enable_dump_option (CompileOptions::REGISTER_PLUGINS_DUMP);
+    }
+  else if (arg == "injection")
+    {
+      options.enable_dump_option (CompileOptions::INJECTION_DUMP);
+    }
+  else if (arg == "expansion")
+    {
+      options.enable_dump_option (CompileOptions::EXPANSION_DUMP);
+    }
+  else if (arg == "resolution")
+    {
+      options.enable_dump_option (CompileOptions::RESOLUTION_DUMP);
+    }
+  else if (arg == "target_options")
+    {
+      options.enable_dump_option (CompileOptions::TARGET_OPTION_DUMP);
+    }
+  else if (arg == "hir")
+    {
+      options.enable_dump_option (CompileOptions::HIR_DUMP);
+    }
+  else if (arg == "hir-pretty")
+    {
+      options.enable_dump_option (CompileOptions::HIR_DUMP_PRETTY);
+    }
+  else
+    {
+      rust_error_at (
+	Location (),
+	"dump option %qs was unrecognised. choose %<lex%>, %<parse%>, "
+	"%<register_plugins%>, %<injection%>, %<expansion%>, %<resolution%>,"
+	" %<target_options%>, or %<hir%>",
+	arg.c_str ());
+      return false;
+    }
+  return true;
+}
+
+/* Actual main entry point for front-end. Called from langhook to parse files.
+ */
+void
+Session::handle_input_files (int num_files, const char **files)
+{
+  if (num_files != 1)
+    rust_fatal_error (Location (),
+		      "only one file may be specified on the command line");
+
+  const auto &file = files[0];
+
+  if (options.crate_name.empty ())
+    {
+      auto filename = "-";
+      if (num_files > 0)
+	filename = files[0];
+
+      auto crate_name = infer_crate_name (filename);
+      rust_debug ("inferred crate name: %s", crate_name.c_str ());
+      // set the preliminary crate name here
+      // we will figure out the real crate name in `handle_crate_name`
+      options.set_crate_name (crate_name);
+    }
+
+  CrateNum crate_num = mappings->get_next_crate_num (options.get_crate_name ());
+  mappings->set_current_crate (crate_num);
+
+  rust_debug ("Attempting to parse file: %s", file);
+  compile_crate (file);
+}
+
+void
+Session::handle_crate_name (const AST::Crate &parsed_crate)
+{
+  auto mappings = Analysis::Mappings::get ();
+  auto crate_name_changed = false;
+  auto error = Error (Location (), std::string ());
+
+  for (const auto &attr : parsed_crate.inner_attrs)
+    {
+      if (attr.get_path () != "crate_name")
+	continue;
+      if (!attr.has_attr_input ())
+	{
+	  rust_error_at (attr.get_locus (),
+			 "%<crate_name%> accepts one argument");
+	  continue;
+	}
+
+      auto &literal
+	= static_cast<AST::AttrInputLiteral &> (attr.get_attr_input ());
+      const auto &msg_str = literal.get_literal ().as_string ();
+      if (!validate_crate_name (msg_str, error))
+	{
+	  error.locus = attr.get_locus ();
+	  error.emit_error ();
+	  continue;
+	}
+
+      auto options = Session::get_instance ().options;
+      if (options.crate_name_set_manually && (options.crate_name != msg_str))
+	{
+	  rust_error_at (attr.get_locus (),
+			 "%<-frust-crate-name%> and %<#[crate_name]%> are "
+			 "required to match, but %qs does not match %qs",
+			 options.crate_name.c_str (), msg_str.c_str ());
+	}
+      crate_name_changed = true;
+      options.set_crate_name (msg_str);
+      mappings->set_crate_name (mappings->get_current_crate (), msg_str);
+    }
+
+  options.crate_name_set_manually |= crate_name_changed;
+  if (!options.crate_name_set_manually
+      && !validate_crate_name (options.crate_name, error))
+    {
+      error.emit_error ();
+      rust_inform (linemap->get_location (0),
+		   "crate name inferred from this file");
+    }
+}
+
+// Parses a single file with filename filename.
+void
+Session::compile_crate (const char *filename)
+{
+  RAIIFile file_wrap (filename);
+  if (!file_wrap.ok ())
+    {
+      rust_error_at (Location (), "cannot open filename %s: %m", filename);
+      return;
+    }
+
+  // parse file here
+  /* create lexer and parser - these are file-specific and so aren't instance
+   * variables */
+  Lexer lex (filename, std::move (file_wrap), linemap);
+  Parser<Lexer> parser (lex);
+
+  // generate crate from parser
+  std::unique_ptr<AST::Crate> ast_crate = parser.parse_crate ();
+
+  // handle crate name
+  handle_crate_name (*ast_crate.get ());
+
+  // dump options
+  if (options.dump_option_enabled (CompileOptions::LEXER_DUMP))
+    {
+      dump_lex (parser);
+    }
+  if (options.dump_option_enabled (CompileOptions::PARSER_AST_DUMP))
+    {
+      dump_ast (parser, *ast_crate.get ());
+    }
+  if (options.dump_option_enabled (CompileOptions::AST_DUMP_PRETTY))
+    {
+      dump_ast_pretty (*ast_crate.get ());
+    }
+  if (options.dump_option_enabled (CompileOptions::TARGET_OPTION_DUMP))
+    {
+      options.target_data.dump_target_options ();
+    }
+
+  if (saw_errors ())
+    return;
+
+  // setup the mappings for this AST
+  CrateNum current_crate = mappings->get_current_crate ();
+  AST::Crate &parsed_crate
+    = mappings->insert_ast_crate (std::move (ast_crate), current_crate);
+
+  /* basic pipeline:
+   *  - lex
+   *  - parse
+   *  - register plugins (dummy stage for now) - attribute injection? what is
+   * this? (attribute injection is injecting attributes specified in command
+   * line into crate root)
+   *  - injection (some lint checks or dummy, register builtin macros, crate
+   * injection)
+   *  - expansion (expands all macros, maybe build test harness, AST
+   * validation, maybe macro crate)
+   *  - resolution (name resolution, type resolution, maybe feature checking,
+   * maybe buffered lints)
+   *  TODO not done */
+
+  rust_debug ("\033[0;31mSUCCESSFULLY PARSED CRATE \033[0m");
+
+  // If -fsyntax-only was passed, we can just skip the remaining passes.
+  // Parsing errors are already emitted in `parse_crate()`
+  if (flag_syntax_only)
+    return;
+
+  // register plugins pipeline stage
+  register_plugins (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY REGISTERED PLUGINS \033[0m");
+  if (options.dump_option_enabled (CompileOptions::REGISTER_PLUGINS_DUMP))
+    {
+      // TODO: what do I dump here?
+    }
+
+  // injection pipeline stage
+  injection (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY FINISHED INJECTION \033[0m");
+  if (options.dump_option_enabled (CompileOptions::INJECTION_DUMP))
+    {
+      // TODO: what do I dump here? injected crate names?
+    }
+
+  Analysis::AttributeChecker ().go (parsed_crate);
+
+  // expansion pipeline stage
+  expansion (parsed_crate);
+  rust_debug ("\033[0;31mSUCCESSFULLY FINISHED EXPANSION \033[0m");
+  if (options.dump_option_enabled (CompileOptions::EXPANSION_DUMP))
+    {
+      // dump AST with expanded stuff
+      rust_debug ("BEGIN POST-EXPANSION AST DUMP");
+      dump_ast_expanded (parser, parsed_crate);
+      rust_debug ("END POST-EXPANSION AST DUMP");
+    }
+
+  // resolution pipeline stage
+  Resolver::NameResolution::Resolve (parsed_crate);
+  if (options.dump_option_enabled (CompileOptions::RESOLUTION_DUMP))
+    {
+      // TODO: what do I dump here? resolved names? AST with resolved names?
+    }
+
+  if (saw_errors ())
+    return;
+
+  // lower AST to HIR
+  std::unique_ptr<HIR::Crate> lowered
+    = HIR::ASTLowering::Resolve (parsed_crate);
+  if (saw_errors ())
+    return;
+
+  // add the mappings to it
+  HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+  if (options.dump_option_enabled (CompileOptions::HIR_DUMP))
+    {
+      dump_hir (hir);
+    }
+  if (options.dump_option_enabled (CompileOptions::HIR_DUMP_PRETTY))
+    {
+      dump_hir_pretty (hir);
+    }
+
+  // type resolve
+  Resolver::TypeResolution::Resolve (hir);
+  if (options.dump_option_enabled (CompileOptions::TYPE_RESOLUTION_DUMP))
+    {
+      dump_type_resolution (hir);
+    }
+
+  if (saw_errors ())
+    return;
+
+  // Various HIR error passes. The privacy pass happens before the unsafe checks
+  Privacy::Resolver::resolve (hir);
+  if (saw_errors ())
+    return;
+
+  HIR::UnsafeChecker ().go (hir);
+  HIR::ConstChecker ().go (hir);
+
+  if (saw_errors ())
+    return;
+
+  // do compile to gcc generic
+  Compile::Context ctx (backend);
+  Compile::CompileCrate::Compile (hir, &ctx);
+
+  // we can't do static analysis if there are errors to worry about
+  if (!saw_errors ())
+    {
+      // lints
+      Analysis::ScanDeadcode::Scan (hir);
+      Analysis::UnusedVariables::Lint (ctx);
+
+      // metadata
+      bool specified_emit_metadata
+	= flag_rust_embed_metadata || options.metadata_output_path_set ();
+      if (!specified_emit_metadata)
+	{
+	  Metadata::PublicInterface::ExportTo (
+	    hir, Metadata::PublicInterface::expected_metadata_filename ());
+	}
+      else
+	{
+	  if (flag_rust_embed_metadata)
+	    Metadata::PublicInterface::Export (hir);
+	  if (options.metadata_output_path_set ())
+	    Metadata::PublicInterface::ExportTo (
+	      hir, options.get_metadata_output ());
+	}
+    }
+
+  // pass to GCC middle-end
+  ctx.write_to_backend ();
+}
+
+void
+Session::register_plugins (AST::Crate &crate ATTRIBUTE_UNUSED)
+{
+  rust_debug ("ran register_plugins (with no body)");
+}
+
+// TODO: move somewhere else
+bool
+contains_name (const AST::AttrVec &attrs, std::string name)
+{
+  for (const auto &attr : attrs)
+    {
+      if (attr.get_path () == name)
+	return true;
+    }
+
+  return false;
+}
+
+void
+Session::injection (AST::Crate &crate)
+{
+  rust_debug ("started injection");
+
+  // lint checks in future maybe?
+
+  // register builtin macros
+  /* In rustc, builtin macros are divided into 3 categories depending on use -
+   * "bang" macros, "attr" macros, and "derive" macros. I think the meanings
+   * of these categories should be fairly obvious to anyone who has used rust.
+   * Builtin macro list by category: Bang
+   *      - asm
+   *      - assert
+   *      - cfg
+   *      - column
+   *      - compile_error
+   *      - concat_idents
+   *      - concat
+   *      - env
+   *      - file
+   *      - format_args_nl
+   *      - format_args
+   *      - global_asm
+   *      - include_bytes
+   *      - include_str
+   *      - include
+   *      - line
+   *      - log_syntax
+   *      - module_path
+   *      - option_env
+   *      - stringify
+   *      - trace_macros
+   *  Attr
+   *      - bench
+   *      - global_allocator
+   *      - test
+   *      - test_case
+   *  Derive
+   *      - Clone
+   *      - Copy
+   *      - Debug
+   *      - Default
+   *      - Eq
+   *      - Hash
+   *      - Ord
+   *      - PartialEq
+   *      - PartialOrd
+   *      - RustcDecodable
+   *      - RustcEncodable
+   * rustc also has a "quote" macro that is defined differently and is
+   * supposedly not stable so eh. */
+  /* TODO: actually implement injection of these macros. In particular, derive
+   * macros, cfg, and test should be prioritised since they seem to be used
+   * the most. */
+
+  // crate injection
+  std::vector<std::string> names;
+  if (contains_name (crate.inner_attrs, "no_core"))
+    {
+      // no prelude
+      injected_crate_name = "";
+    }
+  else if (contains_name (crate.inner_attrs, "no_std"))
+    {
+      names.push_back ("core");
+
+      if (!contains_name (crate.inner_attrs, "compiler_builtins"))
+	{
+	  names.push_back ("compiler_builtins");
+	}
+
+      injected_crate_name = "core";
+    }
+  else
+    {
+      names.push_back ("std");
+
+      injected_crate_name = "std";
+    }
+
+  // reverse iterate through names to insert crate items in "forward" order at
+  // beginning of crate
+  for (auto it = names.rbegin (); it != names.rend (); ++it)
+    {
+      // create "macro use" attribute for use on extern crate item to enable
+      // loading macros from it
+      AST::Attribute attr (AST::SimplePath::from_str ("macro_use", Location ()),
+			   nullptr);
+
+      // create "extern crate" item with the name
+      std::unique_ptr<AST::ExternCrate> extern_crate (
+	new AST::ExternCrate (*it, AST::Visibility::create_error (),
+			      {std::move (attr)},
+			      Linemap::unknown_location ()));
+
+      // insert at beginning
+      // crate.items.insert (crate.items.begin (), std::move (extern_crate));
+    }
+
+  // create use tree path
+  // prelude is injected_crate_name
+  // FIXME: Once we do want to include the standard library, add the prelude
+  // use item
+  // std::vector<AST::SimplePathSegment> segments
+  //   = {AST::SimplePathSegment (injected_crate_name, Location ()),
+  //      AST::SimplePathSegment ("prelude", Location ()),
+  //      AST::SimplePathSegment ("v1", Location ())};
+  // // create use tree and decl
+  // std::unique_ptr<AST::UseTreeGlob> use_tree (
+  //   new AST::UseTreeGlob (AST::UseTreeGlob::PATH_PREFIXED,
+  //     		  AST::SimplePath (std::move (segments)), Location ()));
+  // AST::Attribute prelude_attr (AST::SimplePath::from_str ("prelude_import",
+  //     						  Location ()),
+  //     		       nullptr);
+  // std::unique_ptr<AST::UseDeclaration> use_decl (
+  //   new AST::UseDeclaration (std::move (use_tree),
+  //     		     AST::Visibility::create_error (),
+  //     		     {std::move (prelude_attr)}, Location ()));
+
+  // crate.items.insert (crate.items.begin (), std::move (use_decl));
+
+  /* TODO: potentially add checking attribute crate type? I can't figure out
+   * what this does currently comment says "Unconditionally collect crate
+   * types from attributes to make them used", which presumably refers to
+   * checking the linkage info by "crate_type". It also seems to ensure that
+   * an invalid crate type is not specified, so maybe just do that. Valid
+   * crate types: bin lib dylib staticlib cdylib rlib proc-macro */
+
+  // this crate type will have options affecting the metadata ouput
+
+  rust_debug ("finished injection");
+}
+
+void
+Session::expansion (AST::Crate &crate)
+{
+  rust_debug ("started expansion");
+
+  /* rustc has a modification to windows PATH temporarily here, which may end
+   * up being required */
+
+  // create macro expansion config?
+  // if not, would at least have to configure recursion_limit
+  ExpansionCfg cfg;
+
+  // create extctxt? from parse session, cfg, and resolver?
+  /* expand by calling cxtctxt object's monotonic_expander's expand_crate
+   * method. */
+  MacroExpander expander (crate, cfg, *this);
+  expander.expand_crate ();
+
+  // error reporting - check unused macros, get missing fragment specifiers
+
+  // build test harness
+
+  // ast validation (also with proc macro decls)
+
+  // maybe create macro crate if not rustdoc
+
+  rust_debug ("finished expansion");
+}
+
+void
+Session::dump_lex (Parser<Lexer> &parser) const
+{
+  std::ofstream out;
+  out.open (kLexDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kLexDumpFile);
+      return;
+    }
+
+  // TODO: rewrite lexer dump or something so that it allows for the crate
+  // to already be parsed
+  parser.debug_dump_lex_output (out);
+  out.close ();
+}
+
+void
+Session::dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTDumpFile);
+      return;
+    }
+
+  parser.debug_dump_ast_output (crate, out);
+  out.close ();
+}
+
+void
+Session::dump_ast_pretty (AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTPrettyDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTDumpFile);
+      return;
+    }
+
+  AST::Dump (out).go (crate);
+
+  out.close ();
+}
+
+void
+Session::dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kASTExpandedDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kASTExpandedDumpFile);
+      return;
+    }
+
+  parser.debug_dump_ast_output (crate, out);
+  out.close ();
+}
+
+void
+Session::dump_hir (HIR::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kHIRDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRDumpFile);
+      return;
+    }
+
+  out << crate.as_string ();
+  out.close ();
+}
+
+void
+Session::dump_hir_pretty (HIR::Crate &crate) const
+{
+  std::ofstream out;
+  out.open (kHIRPrettyDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRPrettyDumpFile);
+      return;
+    }
+
+  HIR::Dump (out).go (crate);
+  out.close ();
+}
+
+void
+Session::dump_type_resolution (HIR::Crate &hir) const
+{
+  std::ofstream out;
+  out.open (kHIRTypeResolutionDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kHIRTypeResolutionDumpFile);
+      return;
+    }
+
+  Resolver::TypeResolverDump::go (hir, out);
+  out.close ();
+}
+
+// imports
+
+NodeId
+Session::load_extern_crate (const std::string &crate_name, Location locus)
+{
+  // has it already been loaded?
+  CrateNum found_crate_num = UNKNOWN_CREATENUM;
+  bool found = mappings->lookup_crate_name (crate_name, found_crate_num);
+  if (found)
+    {
+      NodeId resolved_node_id = UNKNOWN_NODEID;
+      bool resolved
+	= mappings->crate_num_to_nodeid (found_crate_num, resolved_node_id);
+      rust_assert (resolved);
+
+      return resolved_node_id;
+    }
+
+  std::string relative_import_path = "";
+  Import::Stream *s
+    = Import::open_package (crate_name, locus, relative_import_path);
+  if (s == NULL)
+    {
+      rust_error_at (locus, "failed to locate crate %<%s%>",
+		     crate_name.c_str ());
+      return UNKNOWN_NODEID;
+    }
+
+  Imports::ExternCrate extern_crate (*s);
+  bool ok = extern_crate.load (locus);
+  if (!ok)
+    {
+      rust_error_at (locus, "failed to load crate metadata");
+      return UNKNOWN_NODEID;
+    }
+
+  // ensure the current vs this crate name don't collide
+  const std::string current_crate_name = mappings->get_current_crate_name ();
+  if (current_crate_name.compare (extern_crate.get_crate_name ()) == 0)
+    {
+      rust_error_at (locus, "current crate name %<%s%> collides with this",
+		     current_crate_name.c_str ());
+      return UNKNOWN_NODEID;
+    }
+
+  // setup mappings
+  CrateNum saved_crate_num = mappings->get_current_crate ();
+  CrateNum crate_num
+    = mappings->get_next_crate_num (extern_crate.get_crate_name ());
+  mappings->set_current_crate (crate_num);
+
+  // then lets parse this as a 2nd crate
+  Lexer lex (extern_crate.get_metadata ());
+  Parser<Lexer> parser (lex);
+  std::unique_ptr<AST::Crate> metadata_crate = parser.parse_crate ();
+  AST::Crate &parsed_crate
+    = mappings->insert_ast_crate (std::move (metadata_crate), crate_num);
+
+  // name resolve it
+  Resolver::NameResolution::Resolve (parsed_crate);
+
+  // perform hir lowering
+  std::unique_ptr<HIR::Crate> lowered
+    = HIR::ASTLowering::Resolve (parsed_crate);
+  HIR::Crate &hir = mappings->insert_hir_crate (std::move (lowered));
+
+  // perform type resolution
+  Resolver::TypeResolution::Resolve (hir);
+
+  // always restore the crate_num
+  mappings->set_current_crate (saved_crate_num);
+
+  return parsed_crate.get_node_id ();
+}
+//
+
+void
+TargetOptions::dump_target_options () const
+{
+  std::ofstream out;
+  out.open (kTargetOptionsDumpFile);
+  if (out.fail ())
+    {
+      rust_error_at (Linemap::unknown_location (), "cannot open %s:%m; ignored",
+		     kTargetOptionsDumpFile);
+      return;
+    }
+
+  if (features.empty ())
+    {
+      out << "No target options available!\n";
+    }
+
+  for (const auto &pairs : features)
+    {
+      for (const auto &value : pairs.second)
+	out << pairs.first + ": \"" + value + "\"\n";
+
+      if (pairs.second.empty ())
+	out << pairs.first + "\n";
+    }
+
+  out.close ();
+}
+
+void
+TargetOptions::init_derived_values ()
+{
+  // enable derived values based on target families
+  if (has_key_value_pair ("target_family", "unix"))
+    insert_key ("unix");
+  if (has_key_value_pair ("target_family", "windows"))
+    insert_key ("windows");
+
+  // implicitly enable features - this should not be required in general
+  if (has_key_value_pair ("target_feature", "aes"))
+    enable_implicit_feature_reqs ("aes");
+  if (has_key_value_pair ("target_feature", "avx"))
+    enable_implicit_feature_reqs ("sse4.2");
+  if (has_key_value_pair ("target_feature", "avx2"))
+    enable_implicit_feature_reqs ("avx");
+  if (has_key_value_pair ("target_feature", "pclmulqdq"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sha"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sse2"))
+    enable_implicit_feature_reqs ("sse");
+  if (has_key_value_pair ("target_feature", "sse3"))
+    enable_implicit_feature_reqs ("sse2");
+  if (has_key_value_pair ("target_feature", "sse4.1"))
+    enable_implicit_feature_reqs ("sse3");
+  if (has_key_value_pair ("target_feature", "sse4.2"))
+    enable_implicit_feature_reqs ("sse4.1");
+  if (has_key_value_pair ("target_feature", "ssse3"))
+    enable_implicit_feature_reqs ("sse3");
+}
+
+void
+TargetOptions::enable_implicit_feature_reqs (std::string feature)
+{
+  if (feature == "aes")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "avx")
+    enable_implicit_feature_reqs ("sse4.2");
+  else if (feature == "avx2")
+    enable_implicit_feature_reqs ("avx");
+  else if (feature == "fma")
+    enable_implicit_feature_reqs ("avx");
+  else if (feature == "pclmulqdq")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sha")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sse2")
+    enable_implicit_feature_reqs ("sse");
+  else if (feature == "sse3")
+    enable_implicit_feature_reqs ("sse2");
+  else if (feature == "sse4.1")
+    enable_implicit_feature_reqs ("sse3");
+  else if (feature == "sse4.2")
+    enable_implicit_feature_reqs ("sse4.1");
+  else if (feature == "ssse3")
+    enable_implicit_feature_reqs ("sse3");
+
+  if (!has_key_value_pair ("target_feature", feature))
+    {
+      insert_key_value_pair ("target_feature", feature);
+
+      rust_debug ("had to implicitly enable feature '%s'!", feature.c_str ());
+    }
+}
+
+// NOTEs:
+/* mrustc compile pipeline:
+ *  - target load (pass target spec to parser?)
+ *  - parse (convert source to AST)
+ *  - load crates (load any explicitly mentioned extern crates [not all of
+ * them])
+ *  - expand (AST transformations from attributes and macros, loads remaining
+ * extern crates [std/core and any triggered by macro expansion])
+ *  - implicit crates (test harness, allocator crate, panic crate)
+ *  - resolve use (annotate every 'use' item with source [supposedly handles
+ * nasty recursion])
+ *  - resolve index (generate index of visible items for every module [avoids
+ * recursion in next pass])
+ *  - resolve absolute (resolve all paths into either variable names
+ * [types/values] or absolute paths)
+ *  - HIR lower (convert modified AST to simpler HIR [both expressions and
+ * module tree])
+ *  - resolve type aliases (replace any usages of type aliases with actual
+ * type [except associated types])
+ *  - resolve bind (iterate HIR tree and set binding annotations on all
+ * concrete types [avoids path lookups later])
+ *  - resolve HIR markings (generate "markings" [e.g. for Copy/Send/Sync/...]
+ * for all types
+ *  - sort impls (small pass - sort impls into groups)
+ *  - resolve UFCS outer (determine source trait for all top-level <T>::Type
+ * [qualified] paths)
+ *  - resolve UFCS paths (do the same, but include for exprs this time. also
+ * normalises results of previous pass [expanding known associated types])
+ *  - constant evaluate (evaluate all constants)
+ *  - typecheck outer (checks impls are sane)
+ *  - typecheck expressions (resolve and check types for all exprs)
+ *  - expand HIR annotate (annotate how exprs are used - used for closure
+ * extractions and reborrows)
+ *  - expand HIR closures (extract closures into structs implementing Fn*
+ * traits)
+ *  - expand HIR vtables (generate vtables for types with dyn dispatch)
+ *  - expand HIR calls (converts method and callable calls into explicit
+ * function calls)
+ *  - expand HIR reborrows (apply reborrow rules [taking '&mut *v' instead of
+ * 'v'])
+ *  - expand HIR erasedtype (replace all erased types 'impl Trait' with the
+ * true type)
+ *  - typecheck expressions (validate - double check that previous passes
+ * haven't broke type system rules)
+ *  - lower MIR (convert HIR exprs into a control-flow graph [MIR])
+ *  - MIR validate (check that the generated MIR is consistent)
+ *  - MIR cleanup (perform various transformations on MIR - replace reads of
+ * const items with the item itself; convert casts to unsized types into
+ * 'MakeDst' operations)
+ *  - MIR optimise (perform various simple optimisations on the MIR - constant
+ * propagation, dead code elimination, borrow elimination, some inlining)
+ *  - MIR validate PO (re-validate the MIR)
+ *  - MIR validate full (optionally: perform expensive state-tracking
+ * validation on MIR)
+ *  - trans enumerate (enumerate all items needed for code generation,
+ * primarily types used for generics)
+ *  - trans auto impls (create magic trait impls as enumerated in previous
+ * pass)
+ *  - trans monomorph (generate monomorphised copies of all functions [with
+ * generics replaced with real types])
+ *  - MIR optimise inline (run optimisation again, this time with full type
+ * info [primarily for inlining])
+ *  - HIR serialise (write out HIR dump [module tree and generic/inline MIR])
+ *  - trans codegen (generate final output file: emit C source file and call C
+ * compiler) */
+
+/* rustc compile pipeline (basic, in way less detail):
+ *  - parse input (parse .rs to AST)
+ *  - name resolution, macro expansion, and configuration (process AST
+ * recursively, resolving paths, expanding macros, processing #[cfg] nodes
+ * [i.e. maybe stripping stuff from AST])
+ *  - lower to HIR
+ *  - type check and other analyses (e.g. privacy checking)
+ *  - lower to MIR and post-processing (and do stuff like borrow checking)
+ *  - translation to LLVM IR and LLVM optimisations (produce the .o files)
+ *  - linking (link together .o files) */
+
+/* Pierced-together rustc compile pipeline (from source):
+ *  - parse input (parse file to crate)
+ *  - register plugins (attributes injection, set various options, register
+ * lints, load plugins)
+ *  - expansion/configure and expand (initial 'cfg' processing, 'loading
+ * compiler plugins', syntax expansion, secondary 'cfg' expansion, synthesis
+ * of a test harness if required, injection of any std lib dependency and
+ * prelude, and name resolution) - actually documented inline
+ *      - seeming pierced-together order: pre-AST expansion lint checks,
+ * registering builtin macros, crate injection, then expand all macros, then
+ * maybe build test harness, AST validation, maybe create a macro crate (if
+ * not rustdoc), name resolution, complete gated feature checking, add all
+ * buffered lints
+ *  - create global context (lower to HIR)
+ *  - analysis on global context (HIR optimisations? create MIR?)
+ *  - code generation
+ *  - link */
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+void
+rust_crate_name_validation_test (void)
+{
+  auto error = Rust::Error (Location (), std::string ());
+  ASSERT_TRUE (Rust::validate_crate_name ("example", error));
+  ASSERT_TRUE (Rust::validate_crate_name ("abcdefg_1234", error));
+  ASSERT_TRUE (Rust::validate_crate_name ("1", error));
+  // FIXME: The next test does not pass as of current implementation
+  // ASSERT_TRUE (Rust::CompileOptions::validate_crate_name ("惊吓"));
+  // NOTE: - is not allowed in the crate name ...
+
+  ASSERT_FALSE (Rust::validate_crate_name ("abcdefg-1234", error));
+  ASSERT_FALSE (Rust::validate_crate_name ("a+b", error));
+  ASSERT_FALSE (Rust::validate_crate_name ("/a+b/", error));
+
+  /* Tests for crate name inference */
+  ASSERT_EQ (Rust::infer_crate_name ("c.rs"), "c");
+  // NOTE: ... but - is allowed when in the filename
+  ASSERT_EQ (Rust::infer_crate_name ("a-b.rs"), "a_b");
+  ASSERT_EQ (Rust::infer_crate_name ("book.rs.txt"), "book.rs");
+#if defined(HAVE_DOS_BASED_FILE_SYSTEM)
+  ASSERT_EQ (Rust::infer_crate_name ("a\\c\\a-b.rs"), "a_b");
+#else
+  ASSERT_EQ (Rust::infer_crate_name ("a/c/a-b.rs"), "a_b");
+#endif
+}
+} // namespace selftest
+#endif // CHECKING_P
diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h
new file mode 100644
index 00000000000..99dd107239b
--- /dev/null
+++ b/gcc/rust/rust-session-manager.h
@@ -0,0 +1,358 @@
+// 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-session-manager.h"
+
+#ifndef RUST_SESSION_MANAGER_H
+#define RUST_SESSION_MANAGER_H
+
+#include "rust-linemap.h"
+#include "rust-backend.h"
+#include "rust-hir-map.h"
+#include "safe-ctype.h"
+
+#include "config.h"
+#include "rust-system.h"
+#include "coretypes.h"
+#include "options.h"
+
+namespace Rust {
+// parser forward decl
+template <typename ManagedTokenSource> class Parser;
+class Lexer;
+// crate forward decl
+namespace AST {
+struct Crate;
+}
+// crate forward decl
+namespace HIR {
+struct Crate;
+}
+
+/* Data related to target, most useful for conditional compilation and
+ * whatever. */
+struct TargetOptions
+{
+  /* TODO: maybe make private and access through helpers to allow changes to
+   * impl */
+  std::unordered_map<std::string, std::unordered_set<std::string> > features;
+
+public:
+  // Returns whether a key is defined in the feature set.
+  bool has_key (std::string key) const
+  {
+    return features.find (key) != features.end ();
+  }
+
+  // Returns whether a key exists with the given value in the feature set.
+  bool has_key_value_pair (std::string key, std::string value) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      {
+	auto set = it->second;
+	auto it2 = set.find (value);
+	if (it2 != set.end ())
+	  return true;
+      }
+    return false;
+  }
+
+  /* Returns the singular value from the key, or if the key has multiple, an
+   * empty string. */
+  std::string get_singular_value (std::string key) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      {
+	auto set = it->second;
+	if (set.size () == 1)
+	  return *set.begin ();
+      }
+    return "";
+  }
+
+  /* Returns all values associated with a key (including none), or an empty
+   * set if no key is found. */
+  std::unordered_set<std::string> get_values_for_key (std::string key) const
+  {
+    auto it = features.find (key);
+    if (it != features.end ())
+      return it->second;
+    return {};
+  }
+
+  /* Inserts a key (no value) into the feature set. This will do nothing if
+   * the key already exists. This returns whether the insertion was successful
+   * (i.e. whether key already existed). */
+  bool insert_key (std::string key)
+  {
+    return features
+      .insert (std::make_pair (key, std::unordered_set<std::string> ()))
+      .second;
+  }
+
+  // Inserts a key-value pair into the feature set.
+  void insert_key_value_pair (std::string key, std::string value)
+  {
+    auto existing_set = get_values_for_key (key);
+    existing_set.insert (std::move (value));
+    features[std::move (key)] = std::move (existing_set);
+  }
+
+  // Dump all target options to stderr.
+  void dump_target_options () const;
+
+  /* Creates derived values and implicit enables after all target info is
+   * added (e.g. "unix"). */
+  void init_derived_values ();
+
+  /* Enables all requirements for the feature given, and will enable feature
+   * itself if not enabled. */
+  void enable_implicit_feature_reqs (std::string feature);
+
+  /* According to reference, Rust uses either multi-map key-values or just
+   * values (although values may be aliases for a key-value value). This seems
+   * like overkill. Thus, depending on whether the attributes used in cfg are
+   * fixed or not, I think I'll either put each non-multimap "key-value" as a
+   * separate field and have the multimap "key-values" in a regular map for
+   * that one key, or actually use a multimap.
+   *
+   * rustc itself uses a set of key-value tuples where the second tuple
+   * element is optional. This gets rid of the requirement to make a
+   * multi-map, I guess, but seems like it might make search slow (unless all
+   * "is defined"-only ones have empty string as second element). */
+  /* cfg attributes:
+   * - target_arch: single value
+   * - target_feature: multiple values possible
+   * - target_os: single value
+   * - target_family: single value (or no value?)
+   * - unix: set when target_family = "unix"
+   * - windows: set when target_family = "windows"
+   *  - if these are just syntactic sugar, then maybe have a separate set or
+   * map for this kind of stuff
+   * - target_env: set when needed for disambiguation about ABI - usually
+   * empty string for GNU, complicated
+   *  - seems to be a single value (if any)
+   * - target_endian: single value; "little" or "big"
+   * - target_pointer_width: single value, "32" for 32-bit pointers, etc.
+   * - target_vendor, single value
+   * - test: set when testing is being done
+   *  - again, seems similar to a "is defined" rather than "is equal to" like
+   * unix
+   * - debug_assertions: seems to "is defined"
+   * - proc_macro: no idea, bad docs. seems to be boolean, so maybe "is
+   * defined"
+   */
+};
+
+// Defines compiler options (e.g. dump, etc.).
+struct CompileOptions
+{
+  enum DumpOption
+  {
+    LEXER_DUMP,
+    PARSER_AST_DUMP,
+    AST_DUMP_PRETTY,
+    REGISTER_PLUGINS_DUMP,
+    INJECTION_DUMP,
+    EXPANSION_DUMP,
+    RESOLUTION_DUMP,
+    TARGET_OPTION_DUMP,
+    HIR_DUMP,
+    HIR_DUMP_PRETTY,
+    TYPE_RESOLUTION_DUMP,
+  };
+
+  std::set<DumpOption> dump_options;
+
+  /* configuration options - actually useful for conditional compilation and
+   * whatever data related to target arch, features, os, family, env, endian,
+   * pointer width, vendor */
+  TargetOptions target_data;
+  std::string crate_name;
+  bool crate_name_set_manually = false;
+  bool enable_test = false;
+  bool debug_assertions = false;
+  bool proc_macro = false;
+  std::string metadata_output_path;
+
+  enum class Edition
+  {
+    E2015 = 0,
+    E2018,
+    E2021,
+  } edition
+    = Edition::E2015;
+
+  bool dump_option_enabled (DumpOption option) const
+  {
+    return dump_options.find (option) != dump_options.end ();
+  }
+
+  void enable_dump_option (DumpOption option) { dump_options.insert (option); }
+
+  void enable_all_dump_options ()
+  {
+    enable_dump_option (DumpOption::LEXER_DUMP);
+    enable_dump_option (DumpOption::PARSER_AST_DUMP);
+    enable_dump_option (DumpOption::AST_DUMP_PRETTY);
+    enable_dump_option (DumpOption::REGISTER_PLUGINS_DUMP);
+    enable_dump_option (DumpOption::INJECTION_DUMP);
+    enable_dump_option (DumpOption::EXPANSION_DUMP);
+    enable_dump_option (DumpOption::RESOLUTION_DUMP);
+    enable_dump_option (DumpOption::TARGET_OPTION_DUMP);
+    enable_dump_option (DumpOption::HIR_DUMP);
+    enable_dump_option (DumpOption::HIR_DUMP_PRETTY);
+    enable_dump_option (DumpOption::TYPE_RESOLUTION_DUMP);
+  }
+
+  void set_crate_name (std::string name)
+  {
+    rust_assert (!name.empty ());
+
+    crate_name = std::move (name);
+  }
+
+  const std::string &get_crate_name () const
+  {
+    rust_assert (!crate_name.empty ());
+    return crate_name;
+  }
+
+  void set_edition (int raw_edition)
+  {
+    edition = static_cast<Edition> (raw_edition);
+  }
+
+  const Edition &get_edition () { return edition; }
+
+  void set_metadata_output (const std::string &path)
+  {
+    metadata_output_path = path;
+  }
+
+  const std::string &get_metadata_output () const
+  {
+    return metadata_output_path;
+  }
+
+  bool metadata_output_path_set () const
+  {
+    return !metadata_output_path.empty ();
+  }
+};
+
+/* Defines a compiler session. This is for a single compiler invocation, so
+ * potentially includes parsing multiple crates. */
+struct Session
+{
+  CompileOptions options;
+  /* This should really be in a per-crate storage area but it is wiped with
+   * every file so eh. */
+  std::string injected_crate_name;
+
+  /* extra files get included during late stages of compilation (e.g. macro
+   * expansion) */
+  std::vector<std::string> extra_files;
+
+  // backend wrapper to GCC GENERIC
+  Backend *backend;
+
+  // backend linemap
+  Linemap *linemap;
+
+  // mappings
+  Analysis::Mappings *mappings;
+
+public:
+  /* Get a reference to the static session instance */
+  static Session &get_instance ();
+
+  Session () = default;
+  ~Session () = default;
+
+  /* This initializes the compiler session. Corresponds to langhook
+   * grs_langhook_init(). Note that this is called after option handling. */
+  void init ();
+
+  // delete those constructors so we don't access the singleton in any
+  // other way than via `get_instance()`
+  Session (Session const &) = delete;
+  void operator= (Session const &) = delete;
+
+  bool handle_option (enum opt_code code, const char *arg, HOST_WIDE_INT value,
+		      int kind, location_t loc,
+		      const struct cl_option_handlers *handlers);
+  void handle_input_files (int num_files, const char **files);
+  void init_options ();
+  void handle_crate_name (const AST::Crate &parsed_crate);
+
+  /* This function saves the filename data into the session manager using the
+   * `move` semantics, and returns a C-style string referencing the input
+   * std::string */
+  inline const char *include_extra_file (std::string filename)
+  {
+    extra_files.push_back (std::move (filename));
+    return extra_files.back ().c_str ();
+  }
+
+  NodeId load_extern_crate (const std::string &crate_name, Location locus);
+
+private:
+  void compile_crate (const char *filename);
+  bool enable_dump (std::string arg);
+
+  void dump_lex (Parser<Lexer> &parser) const;
+  void dump_ast (Parser<Lexer> &parser, AST::Crate &crate) const;
+  void dump_ast_pretty (AST::Crate &crate) const;
+  void dump_ast_expanded (Parser<Lexer> &parser, AST::Crate &crate) const;
+  void dump_hir (HIR::Crate &crate) const;
+  void dump_hir_pretty (HIR::Crate &crate) const;
+  void dump_type_resolution (HIR::Crate &crate) const;
+
+  // pipeline stages - TODO maybe move?
+  /* Register plugins pipeline stage. TODO maybe move to another object?
+   * Currently dummy stage. In future will handle attribute injection
+   * (top-level inner attribute creation from command line arguments), setting
+   * options maybe, registering lints maybe, loading plugins maybe. */
+  void register_plugins (AST::Crate &crate);
+
+  /* Injection pipeline stage. TODO maybe move to another object? Maybe have
+   * some lint checks (in future, obviously), register builtin macros, crate
+   * injection. */
+  void injection (AST::Crate &crate);
+
+  /* Expansion pipeline stage. TODO maybe move to another object? Expands all
+   * macros, maybe build test harness in future, AST validation, maybe create
+   * macro crate (if not rustdoc).*/
+  void expansion (AST::Crate &crate);
+
+  // handle cfg_option
+  bool handle_cfg_option (std::string &data);
+};
+
+} // namespace Rust
+
+#if CHECKING_P
+namespace selftest {
+extern void
+rust_crate_name_validation_test (void);
+}
+#endif // CHECKING_P
+
+#endif
