aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/rust-lang.cc
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2023-03-29 09:01:23 -0700
committerIan Lance Taylor <iant@golang.org>2023-03-29 09:01:23 -0700
commit6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced (patch)
tree1deecdcfbf185c7044bc861d0ace51285c96cb62 /gcc/rust/rust-lang.cc
parent795cffe109e28b248a54b8ee583cbae48368c2a7 (diff)
parentaa8f4242efc99f24de73c59d53996f28db28c13f (diff)
downloadgcc-6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced.zip
gcc-6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced.tar.gz
gcc-6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced.tar.bz2
Merge from trunk revision aa8f4242efc99f24de73c59d53996f28db28c13f.
Diffstat (limited to 'gcc/rust/rust-lang.cc')
-rw-r--r--gcc/rust/rust-lang.cc467
1 files changed, 467 insertions, 0 deletions
diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc
new file mode 100644
index 0000000..1fb1c25
--- /dev/null
+++ b/gcc/rust/rust-lang.cc
@@ -0,0 +1,467 @@
+// Copyright (C) 2020-2023 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);
+
+ // 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;
+ }
+
+ /* See (a) <https://github.com/Rust-GCC/gccrs/issues/1713>
+ "Test failure on msp430-elfbare target", and
+ (b) <https://gcc.gnu.org/PR46805>
+ "ICE: SIGSEGV in optab_for_tree_code (optabs.c:407) with -O -fno-tree-scev-cprop -ftree-vectorize"
+ -- we have to support "random" modes/types here.
+ TODO Clean all this up (either locally, or preferably per PR46805:
+ "Ideally we'd never use lang_hooks.types.type_for_mode (or _for_size) in the
+ middle-end but had a pure middle-end based implementation". */
+ for (size_t i = 0; i < NUM_INT_N_ENTS; i ++)
+ if (int_n_enabled_p[i]
+ && mode == int_n_data[i].m)
+ return (unsignedp ? int_n_trees[i].unsigned_type
+ : int_n_trees[i].signed_type);
+
+ /* 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"