diff options
author | Ian Lance Taylor <iant@golang.org> | 2023-03-29 09:01:23 -0700 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2023-03-29 09:01:23 -0700 |
commit | 6612f4f8cb9b0d5af18ec69ad04e56debc3e6ced (patch) | |
tree | 1deecdcfbf185c7044bc861d0ace51285c96cb62 /gcc/rust/rust-lang.cc | |
parent | 795cffe109e28b248a54b8ee583cbae48368c2a7 (diff) | |
parent | aa8f4242efc99f24de73c59d53996f28db28c13f (diff) | |
download | gcc-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.cc | 467 |
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" |