diff options
author | Philip Herron <philip.herron@embecosm.com> | 2022-10-21 14:01:04 +0200 |
---|---|---|
committer | Arthur Cohen <arthur.cohen@embecosm.com> | 2022-12-13 14:00:07 +0100 |
commit | 15f04af347e3b65f436808077cbac4fa566019f9 (patch) | |
tree | 32ec5a4c2ca65044848cb37137c1074ed63f5401 /gcc/rust/rust-gcc.cc | |
parent | 509e4c32c6a80ede6c6dda0f4cfc96f94d24c4d6 (diff) | |
download | gcc-15f04af347e3b65f436808077cbac4fa566019f9.zip gcc-15f04af347e3b65f436808077cbac4fa566019f9.tar.gz gcc-15f04af347e3b65f436808077cbac4fa566019f9.tar.bz2 |
gccrs: Add base for HIR to GCC GENERIC lowering
This pass walks the HIR crate and turns them into GCC `tree`s. We do not have
any Rust specific tree's. We are slowly removing the backend abstraction
which was ported over from gccgo in favour of using `tree`s directly.
gcc/rust/
* backend/rust-builtins.h: New.
* backend/rust-compile-base.cc: New.
* backend/rust-compile-base.h: New.
* backend/rust-mangle.cc: New.
* backend/rust-mangle.h: New.
* backend/rust-tree.cc: New.
* backend/rust-tree.h: New.
* rust-backend.h: New.
* rust-gcc.cc: New.
Co-authored-by: David Faust <david.faust@oracle.com>
Diffstat (limited to 'gcc/rust/rust-gcc.cc')
-rw-r--r-- | gcc/rust/rust-gcc.cc | 2718 |
1 files changed, 2718 insertions, 0 deletions
diff --git a/gcc/rust/rust-gcc.cc b/gcc/rust/rust-gcc.cc new file mode 100644 index 0000000..3a682fc --- /dev/null +++ b/gcc/rust/rust-gcc.cc @@ -0,0 +1,2718 @@ +// rust-gcc.cc -- Rust frontend to gcc IR. +// Copyright (C) 2011-2022 Free Software Foundation, Inc. +// Contributed by Ian Lance Taylor, Google. +// forked from gccgo + +// This file is part of GCC. + +// GCC is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 3, or (at your option) any later +// version. + +// GCC is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. + +// You should have received a copy of the GNU General Public License +// along with GCC; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +#include "rust-system.h" + +// This has to be included outside of extern "C", so we have to +// include it here before tree.h includes it later. +#include <gmp.h> + +#include "tree.h" +#include "opts.h" +#include "fold-const.h" +#include "stringpool.h" +#include "stor-layout.h" +#include "varasm.h" +#include "tree-iterator.h" +#include "tm.h" +#include "function.h" +#include "cgraph.h" +#include "convert.h" +#include "gimple-expr.h" +#include "gimplify.h" +#include "langhooks.h" +#include "toplev.h" +#include "output.h" +#include "realmpfr.h" +#include "builtins.h" +#include "print-tree.h" +#include "attribs.h" + +#include "rust-location.h" +#include "rust-linemap.h" +#include "rust-backend.h" +#include "rust-object-export.h" + +#include "backend/rust-tree.h" + +// TODO: this will have to be significantly modified to work with Rust + +// Bvariable is a bit more complicated, because of zero-sized types. +// The GNU linker does not permit dynamic variables with zero size. +// When we see such a variable, we generate a version of the type with +// non-zero size. However, when referring to the global variable, we +// want an expression of zero size; otherwise, if, say, the global +// variable is passed to a function, we will be passing a +// non-zero-sized value to a zero-sized value, which can lead to a +// miscompilation. + +class Bvariable +{ +public: + Bvariable (tree t) : t_ (t), orig_type_ (NULL) {} + + Bvariable (tree t, tree orig_type) : t_ (t), orig_type_ (orig_type) {} + + // Get the tree for use as an expression. + tree get_tree (Location) const; + + // Get the actual decl; + tree get_decl () const { return this->t_; } + +private: + tree t_; + tree orig_type_; +}; + +// Get the tree of a variable for use as an expression. If this is a +// zero-sized global, create an expression that refers to the decl but +// has zero size. +tree +Bvariable::get_tree (Location location) const +{ + if (this->t_ == error_mark_node) + return error_mark_node; + + TREE_USED (this->t_) = 1; + if (this->orig_type_ == NULL || TREE_TYPE (this->t_) == this->orig_type_) + { + return this->t_; + } + + // Return *(orig_type*)&decl. */ + tree t = build_fold_addr_expr_loc (location.gcc_location (), this->t_); + t = fold_build1_loc (location.gcc_location (), NOP_EXPR, + build_pointer_type (this->orig_type_), t); + return build_fold_indirect_ref_loc (location.gcc_location (), t); +} + +// This file implements the interface between the Rust frontend proper +// and the gcc IR. This implements specific instantiations of +// abstract classes defined by the Rust frontend proper. The Rust +// frontend proper class methods of these classes to generate the +// backend representation. + +class Gcc_backend : public Backend +{ +public: + Gcc_backend (); + + void debug (tree t) { debug_tree (t); }; + void debug (Bvariable *t) { debug_tree (t->get_decl ()); }; + + tree get_identifier_node (const std::string &str) + { + return get_identifier_with_length (str.data (), str.length ()); + } + + // Types. + + tree unit_type () + { + static tree unit_type; + if (unit_type == nullptr) + { + auto unit_type_node = struct_type ({}); + unit_type = named_type ("()", unit_type_node, + ::Linemap::predeclared_location ()); + } + + return unit_type; + } + + tree bool_type () { return boolean_type_node; } + + tree char_type () { return char_type_node; } + + tree wchar_type () + { + tree wchar = make_unsigned_type (32); + TYPE_STRING_FLAG (wchar) = 1; + return wchar; + } + + int get_pointer_size (); + + tree raw_str_type (); + + tree integer_type (bool, int); + + tree float_type (int); + + tree complex_type (int); + + tree pointer_type (tree); + + tree reference_type (tree); + + tree immutable_type (tree); + + tree function_type (const typed_identifier &, + const std::vector<typed_identifier> &, + const std::vector<typed_identifier> &, tree, + const Location); + + tree function_type_varadic (const typed_identifier &, + const std::vector<typed_identifier> &, + const std::vector<typed_identifier> &, tree, + const Location); + + tree function_ptr_type (tree, const std::vector<tree> &, Location); + + tree struct_type (const std::vector<typed_identifier> &); + + tree union_type (const std::vector<typed_identifier> &); + + tree array_type (tree, tree); + + tree named_type (const std::string &, tree, Location); + + int64_t type_size (tree); + + int64_t type_alignment (tree); + + int64_t type_field_alignment (tree); + + int64_t type_field_offset (tree, size_t index); + + // Expressions. + + tree zero_expression (tree); + + tree unit_expression () { return integer_zero_node; } + + tree var_expression (Bvariable *var, Location); + + tree integer_constant_expression (tree type, mpz_t val); + + tree float_constant_expression (tree type, mpfr_t val); + + tree complex_constant_expression (tree type, mpc_t val); + + tree string_constant_expression (const std::string &val); + + tree wchar_constant_expression (wchar_t c); + + tree char_constant_expression (char c); + + tree boolean_constant_expression (bool val); + + tree real_part_expression (tree bcomplex, Location); + + tree imag_part_expression (tree bcomplex, Location); + + tree complex_expression (tree breal, tree bimag, Location); + + tree convert_expression (tree type, tree expr, Location); + + tree struct_field_expression (tree, size_t, Location); + + tree compound_expression (tree, tree, Location); + + tree conditional_expression (tree, tree, tree, tree, tree, Location); + + tree negation_expression (NegationOperator op, tree expr, Location); + + tree arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op, + tree left, tree right, Location); + + tree comparison_expression (ComparisonOperator op, tree left, tree right, + Location); + + tree lazy_boolean_expression (LazyBooleanOperator op, tree left, tree right, + Location); + + tree constructor_expression (tree, bool, const std::vector<tree> &, int, + Location); + + tree array_constructor_expression (tree, const std::vector<unsigned long> &, + const std::vector<tree> &, Location); + + tree array_initializer (tree, tree, tree, tree, tree, tree *, Location); + + tree array_index_expression (tree array, tree index, Location); + + tree call_expression (tree fn, const std::vector<tree> &args, + tree static_chain, Location); + + // Statements. + + tree init_statement (tree, Bvariable *var, tree init); + + tree assignment_statement (tree lhs, tree rhs, Location); + + tree return_statement (tree, const std::vector<tree> &, Location); + + tree if_statement (tree, tree condition, tree then_block, tree else_block, + Location); + + tree compound_statement (tree, tree); + + tree statement_list (const std::vector<tree> &); + + tree exception_handler_statement (tree bstat, tree except_stmt, + tree finally_stmt, Location); + + tree loop_expression (tree body, Location); + + tree exit_expression (tree condition, Location); + + // Blocks. + + tree block (tree, tree, const std::vector<Bvariable *> &, Location, Location); + + void block_add_statements (tree, const std::vector<tree> &); + + // Variables. + + Bvariable *error_variable () { return new Bvariable (error_mark_node); } + + Bvariable *global_variable (const std::string &var_name, + const std::string &asm_name, tree type, + bool is_external, bool is_hidden, + bool in_unique_section, Location location); + + void global_variable_set_init (Bvariable *, tree); + + Bvariable *local_variable (tree, const std::string &, tree, Bvariable *, + Location); + + Bvariable *parameter_variable (tree, const std::string &, tree, Location); + + Bvariable *static_chain_variable (tree, const std::string &, tree, Location); + + Bvariable *temporary_variable (tree, tree, tree, tree, bool, Location, + tree *); + + // Labels. + + tree label (tree, const std::string &name, Location); + + tree label_definition_statement (tree); + + tree goto_statement (tree, Location); + + tree label_address (tree, Location); + + // Functions. + + tree function (tree fntype, const std::string &name, + const std::string &asm_name, unsigned int flags, Location); + + tree function_defer_statement (tree function, tree undefer, tree defer, + Location); + + bool function_set_parameters (tree function, + const std::vector<Bvariable *> &); + + void write_global_definitions (const std::vector<tree> &, + const std::vector<tree> &, + const std::vector<tree> &, + const std::vector<Bvariable *> &); + + void write_export_data (const char *bytes, unsigned int size); + +private: + tree fill_in_fields (tree, const std::vector<typed_identifier> &); + + tree fill_in_array (tree, tree, tree); + + tree non_zero_size_type (tree); + + tree convert_tree (tree, tree, Location); +}; + +// A helper function to create a GCC identifier from a C++ string. + +static inline tree +get_identifier_from_string (const std::string &str) +{ + return get_identifier_with_length (str.data (), str.length ()); +} + +// Define the built-in functions that are exposed to GCCRust. + +Gcc_backend::Gcc_backend () +{ + /* We need to define the fetch_and_add functions, since we use them + for ++ and --. */ + // tree t = this->integer_type (true, BITS_PER_UNIT)->get_tree (); + // tree p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_1, + // "__sync_fetch_and_add_1", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 2)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_2, + // "__sync_fetch_and_add_2", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 4)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_4, + // "__sync_fetch_and_add_4", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // t = this->integer_type (true, BITS_PER_UNIT * 8)->get_tree (); + // p = build_pointer_type (build_qualified_type (t, TYPE_QUAL_VOLATILE)); + // this->define_builtin (BUILT_IN_SYNC_ADD_AND_FETCH_8, + // "__sync_fetch_and_add_8", + // NULL, build_function_type_list (t, p, t, NULL_TREE), 0); + + // // We use __builtin_expect for magic import functions. + // this->define_builtin (BUILT_IN_EXPECT, "__builtin_expect", NULL, + // build_function_type_list (long_integer_type_node, + // long_integer_type_node, + // long_integer_type_node, + // NULL_TREE), + // builtin_const); + + // // We use __builtin_memcmp for struct comparisons. + // this->define_builtin (BUILT_IN_MEMCMP, "__builtin_memcmp", "memcmp", + // build_function_type_list (integer_type_node, + // const_ptr_type_node, + // const_ptr_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // We use __builtin_memmove for copying data. + // this->define_builtin (BUILT_IN_MEMMOVE, "__builtin_memmove", "memmove", + // build_function_type_list (void_type_node, ptr_type_node, + // const_ptr_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // We use __builtin_memset for zeroing data. + // this->define_builtin (BUILT_IN_MEMSET, "__builtin_memset", "memset", + // build_function_type_list (void_type_node, ptr_type_node, + // integer_type_node, + // size_type_node, NULL_TREE), + // 0); + + // // Used by runtime/internal/sys and math/bits. + // this->define_builtin (BUILT_IN_CTZ, "__builtin_ctz", "ctz", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CTZLL, "__builtin_ctzll", "ctzll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CLZ, "__builtin_clz", "clz", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_CLZLL, "__builtin_clzll", "clzll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_POPCOUNT, "__builtin_popcount", "popcount", + // build_function_type_list (integer_type_node, + // unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_POPCOUNTLL, "__builtin_popcountll", + // "popcountll", + // build_function_type_list (integer_type_node, + // long_long_unsigned_type_node, + // NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP16, "__builtin_bswap16", "bswap16", + // build_function_type_list (uint16_type_node, + // uint16_type_node, NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP32, "__builtin_bswap32", "bswap32", + // build_function_type_list (uint32_type_node, + // uint32_type_node, NULL_TREE), + // builtin_const); + // this->define_builtin (BUILT_IN_BSWAP64, "__builtin_bswap64", "bswap64", + // build_function_type_list (uint64_type_node, + // uint64_type_node, NULL_TREE), + // builtin_const); + + // We provide some functions for the math library. + + // We use __builtin_return_address in the thunk we build for + // functions which call recover, and for runtime.getcallerpc. + // t = build_function_type_list (ptr_type_node, unsigned_type_node, + // NULL_TREE); this->define_builtin (BUILT_IN_RETURN_ADDRESS, + // "__builtin_return_address", + // NULL, t, 0); + + // The runtime calls __builtin_dwarf_cfa for runtime.getcallersp. + // t = build_function_type_list (ptr_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_DWARF_CFA, "__builtin_dwarf_cfa", NULL, t, + // 0); + + // The runtime calls __builtin_extract_return_addr when recording + // the address to which a function returns. + // this->define_builtin ( + // BUILT_IN_EXTRACT_RETURN_ADDR, "__builtin_extract_return_addr", NULL, + // build_function_type_list (ptr_type_node, ptr_type_node, NULL_TREE), 0); + + // The compiler uses __builtin_trap for some exception handling + // cases. + // this->define_builtin (BUILT_IN_TRAP, "__builtin_trap", NULL, + // build_function_type (void_type_node, void_list_node), + // builtin_noreturn); + + // The runtime uses __builtin_prefetch. + // this->define_builtin (BUILT_IN_PREFETCH, "__builtin_prefetch", NULL, + // build_varargs_function_type_list (void_type_node, + // const_ptr_type_node, + // NULL_TREE), + // builtin_novops); + + // The compiler uses __builtin_unreachable for cases that cannot + // occur. + // this->define_builtin (BUILT_IN_UNREACHABLE, "__builtin_unreachable", NULL, + // build_function_type (void_type_node, void_list_node), + // builtin_const | builtin_noreturn); + + // We provide some atomic functions. + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_LOAD_4, "__atomic_load_4", NULL, t, + // 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_LOAD_8, "__atomic_load_8", NULL, t, + // 0); + + // t = build_function_type_list (void_type_node, ptr_type_node, + // uint32_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_STORE_4, "__atomic_store_4", NULL, t, + // 0); + + // t = build_function_type_list (void_type_node, ptr_type_node, + // uint64_type_node, + // integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_STORE_8, "__atomic_store_8", NULL, t, + // 0); + + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // uint32_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_4, "__atomic_exchange_4", + // NULL, + // t, 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // uint64_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_EXCHANGE_8, "__atomic_exchange_8", + // NULL, + // t, 0); + + // t = build_function_type_list (boolean_type_node, ptr_type_node, + // ptr_type_node, + // uint32_type_node, boolean_type_node, + // integer_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_4, + // "__atomic_compare_exchange_4", NULL, t, 0); + + // t = build_function_type_list (boolean_type_node, ptr_type_node, + // ptr_type_node, + // uint64_type_node, boolean_type_node, + // integer_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_COMPARE_EXCHANGE_8, + // "__atomic_compare_exchange_8", NULL, t, 0); + + // t = build_function_type_list (uint32_type_node, ptr_type_node, + // uint32_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_4, "__atomic_add_fetch_4", + // NULL, t, 0); + + // t = build_function_type_list (uint64_type_node, ptr_type_node, + // uint64_type_node, integer_type_node, NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_ADD_FETCH_8, "__atomic_add_fetch_8", + // NULL, t, 0); + + // t = build_function_type_list (unsigned_char_type_node, ptr_type_node, + // unsigned_char_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_AND_FETCH_1, "__atomic_and_fetch_1", + // NULL, t, 0); + // this->define_builtin (BUILT_IN_ATOMIC_FETCH_AND_1, "__atomic_fetch_and_1", + // NULL, t, 0); + + // t = build_function_type_list (unsigned_char_type_node, ptr_type_node, + // unsigned_char_type_node, integer_type_node, + // NULL_TREE); + // this->define_builtin (BUILT_IN_ATOMIC_OR_FETCH_1, "__atomic_or_fetch_1", + // NULL, + // t, 0); + // this->define_builtin (BUILT_IN_ATOMIC_FETCH_OR_1, "__atomic_fetch_or_1", + // NULL, + // t, 0); +} + +// Get an unnamed integer type. + +int +Gcc_backend::get_pointer_size () +{ + return POINTER_SIZE; +} + +tree +Gcc_backend::raw_str_type () +{ + tree char_ptr = build_pointer_type (char_type_node); + tree const_char_type = build_qualified_type (char_ptr, TYPE_QUAL_CONST); + return const_char_type; +} + +tree +Gcc_backend::integer_type (bool is_unsigned, int bits) +{ + tree type; + if (is_unsigned) + { + if (bits == INT_TYPE_SIZE) + type = unsigned_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_unsigned_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_unsigned_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_unsigned_type_node; + else + type = make_unsigned_type (bits); + } + else + { + if (bits == INT_TYPE_SIZE) + type = integer_type_node; + else if (bits == SHORT_TYPE_SIZE) + type = short_integer_type_node; + else if (bits == LONG_TYPE_SIZE) + type = long_integer_type_node; + else if (bits == LONG_LONG_TYPE_SIZE) + type = long_long_integer_type_node; + else + type = make_signed_type (bits); + } + return type; +} + +// Get an unnamed float type. + +tree +Gcc_backend::float_type (int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE) + type = float_type_node; + else if (bits == DOUBLE_TYPE_SIZE) + type = double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE) + type = long_double_type_node; + else + { + type = make_node (REAL_TYPE); + TYPE_PRECISION (type) = bits; + layout_type (type); + } + return type; +} + +// Get an unnamed complex type. + +tree +Gcc_backend::complex_type (int bits) +{ + tree type; + if (bits == FLOAT_TYPE_SIZE * 2) + type = complex_float_type_node; + else if (bits == DOUBLE_TYPE_SIZE * 2) + type = complex_double_type_node; + else if (bits == LONG_DOUBLE_TYPE_SIZE * 2) + type = complex_long_double_type_node; + else + { + type = make_node (REAL_TYPE); + TYPE_PRECISION (type) = bits / 2; + layout_type (type); + type = build_complex_type (type); + } + return type; +} + +// Get a pointer type. + +tree +Gcc_backend::pointer_type (tree to_type) +{ + if (to_type == error_mark_node) + return error_mark_node; + tree type = build_pointer_type (to_type); + return type; +} + +// Get a reference type. + +tree +Gcc_backend::reference_type (tree to_type) +{ + if (to_type == error_mark_node) + return error_mark_node; + tree type = build_reference_type (to_type); + return type; +} + +// Get immutable type + +tree +Gcc_backend::immutable_type (tree base) +{ + if (base == error_mark_node) + return error_mark_node; + tree constified = build_qualified_type (base, TYPE_QUAL_CONST); + return constified; +} + +// Make a function type. + +tree +Gcc_backend::function_type (const typed_identifier &receiver, + const std::vector<typed_identifier> ¶meters, + const std::vector<typed_identifier> &results, + tree result_struct, Location) +{ + tree args = NULL_TREE; + tree *pp = &args; + if (receiver.type != NULL_TREE) + { + tree t = receiver.type; + if (t == error_mark_node) + return error_mark_node; + *pp = tree_cons (NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + for (std::vector<typed_identifier>::const_iterator p = parameters.begin (); + p != parameters.end (); ++p) + { + tree t = p->type; + if (t == error_mark_node) + return error_mark_node; + *pp = tree_cons (NULL_TREE, t, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + // Varargs is handled entirely at the Rust level. When converted to + // GENERIC functions are not varargs. + *pp = void_list_node; + + tree result; + if (results.empty ()) + result = void_type_node; + else if (results.size () == 1) + result = results.front ().type; + else + { + gcc_assert (result_struct != NULL); + result = result_struct; + } + if (result == error_mark_node) + return error_mark_node; + + // The libffi library cannot represent a zero-sized object. To + // avoid causing confusion on 32-bit SPARC, we treat a function that + // returns a zero-sized value as returning void. That should do no + // harm since there is no actual value to be returned. See + // https://gcc.gnu.org/PR72814 for details. + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_function_type (result, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +tree +Gcc_backend::function_type_varadic ( + const typed_identifier &receiver, + const std::vector<typed_identifier> ¶meters, + const std::vector<typed_identifier> &results, tree result_struct, Location) +{ + size_t n = parameters.size () + (receiver.type != NULL_TREE ? 1 : 0); + tree *args = XALLOCAVEC (tree, n); + size_t offs = 0; + + if (receiver.type != NULL_TREE) + { + tree t = receiver.type; + if (t == error_mark_node) + return error_mark_node; + + args[offs++] = t; + } + + for (std::vector<typed_identifier>::const_iterator p = parameters.begin (); + p != parameters.end (); ++p) + { + tree t = p->type; + if (t == error_mark_node) + return error_mark_node; + args[offs++] = t; + } + + tree result; + if (results.empty ()) + result = void_type_node; + else if (results.size () == 1) + result = results.front ().type; + else + { + gcc_assert (result_struct != NULL_TREE); + result = result_struct; + } + if (result == error_mark_node) + return error_mark_node; + + // The libffi library cannot represent a zero-sized object. To + // avoid causing confusion on 32-bit SPARC, we treat a function that + // returns a zero-sized value as returning void. That should do no + // harm since there is no actual value to be returned. See + // https://gcc.gnu.org/PR72814 for details. + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_varargs_function_type_array (result, n, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +tree +Gcc_backend::function_ptr_type (tree result_type, + const std::vector<tree> ¶meters, + Location /* locus */) +{ + tree args = NULL_TREE; + tree *pp = &args; + + for (auto ¶m : parameters) + { + if (param == error_mark_node) + return error_mark_node; + + *pp = tree_cons (NULL_TREE, param, NULL_TREE); + pp = &TREE_CHAIN (*pp); + } + + *pp = void_list_node; + + tree result = result_type; + if (result != void_type_node && int_size_in_bytes (result) == 0) + result = void_type_node; + + tree fntype = build_function_type (result, args); + if (fntype == error_mark_node) + return error_mark_node; + + return build_pointer_type (fntype); +} + +// Make a struct type. + +tree +Gcc_backend::struct_type (const std::vector<typed_identifier> &fields) +{ + return this->fill_in_fields (make_node (RECORD_TYPE), fields); +} + +// Make a union type. + +tree +Gcc_backend::union_type (const std::vector<typed_identifier> &fields) +{ + return this->fill_in_fields (make_node (UNION_TYPE), fields); +} + +// Fill in the fields of a struct or union type. + +tree +Gcc_backend::fill_in_fields (tree fill, + const std::vector<typed_identifier> &fields) +{ + tree field_trees = NULL_TREE; + tree *pp = &field_trees; + for (std::vector<typed_identifier>::const_iterator p = fields.begin (); + p != fields.end (); ++p) + { + tree name_tree = get_identifier_from_string (p->name); + tree type_tree = p->type; + if (type_tree == error_mark_node) + return error_mark_node; + tree field = build_decl (p->location.gcc_location (), FIELD_DECL, + name_tree, type_tree); + DECL_CONTEXT (field) = fill; + *pp = field; + pp = &DECL_CHAIN (field); + } + TYPE_FIELDS (fill) = field_trees; + layout_type (fill); + + // Because Rust permits converting between named struct types and + // equivalent struct types, for which we use VIEW_CONVERT_EXPR, and + // because we don't try to maintain TYPE_CANONICAL for struct types, + // we need to tell the middle-end to use structural equality. + SET_TYPE_STRUCTURAL_EQUALITY (fill); + + return fill; +} + +// Make an array type. + +tree +Gcc_backend::array_type (tree element_type, tree length) +{ + return this->fill_in_array (make_node (ARRAY_TYPE), element_type, length); +} + +// Fill in an array type. + +tree +Gcc_backend::fill_in_array (tree fill, tree element_type, tree length_tree) +{ + if (element_type == error_mark_node || length_tree == error_mark_node) + return error_mark_node; + + gcc_assert (TYPE_SIZE (element_type) != NULL_TREE); + + length_tree = fold_convert (sizetype, length_tree); + + // build_index_type takes the maximum index, which is one less than + // the length. + tree index_type_tree = build_index_type ( + fold_build2 (MINUS_EXPR, sizetype, length_tree, size_one_node)); + + TREE_TYPE (fill) = element_type; + TYPE_DOMAIN (fill) = index_type_tree; + TYPE_ADDR_SPACE (fill) = TYPE_ADDR_SPACE (element_type); + layout_type (fill); + + if (TYPE_STRUCTURAL_EQUALITY_P (element_type)) + SET_TYPE_STRUCTURAL_EQUALITY (fill); + else if (TYPE_CANONICAL (element_type) != element_type + || TYPE_CANONICAL (index_type_tree) != index_type_tree) + TYPE_CANONICAL (fill) = build_array_type (TYPE_CANONICAL (element_type), + TYPE_CANONICAL (index_type_tree)); + + return fill; +} + +// Return a named version of a type. + +tree +Gcc_backend::named_type (const std::string &name, tree type, Location location) +{ + if (type == error_mark_node) + return error_mark_node; + + // The middle-end expects a basic type to have a name. In Rust every + // basic type will have a name. The first time we see a basic type, + // give it whatever Rust name we have at this point. + if (TYPE_NAME (type) == NULL_TREE + && location.gcc_location () == BUILTINS_LOCATION + && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == REAL_TYPE + || TREE_CODE (type) == COMPLEX_TYPE + || TREE_CODE (type) == BOOLEAN_TYPE)) + { + tree decl = build_decl (BUILTINS_LOCATION, TYPE_DECL, + get_identifier_from_string (name), type); + TYPE_NAME (type) = decl; + return type; + } + + tree copy = build_variant_type_copy (type); + tree decl = build_decl (location.gcc_location (), TYPE_DECL, + get_identifier_from_string (name), copy); + DECL_ORIGINAL_TYPE (decl) = type; + TYPE_NAME (copy) = decl; + return copy; +} + +// Return the size of a type. + +int64_t +Gcc_backend::type_size (tree t) +{ + if (t == error_mark_node) + return 1; + if (t == void_type_node) + return 0; + t = TYPE_SIZE_UNIT (t); + gcc_assert (tree_fits_uhwi_p (t)); + unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW (t); + int64_t ret = static_cast<int64_t> (val_wide); + if (ret < 0 || static_cast<unsigned HOST_WIDE_INT> (ret) != val_wide) + return -1; + return ret; +} + +// Return the alignment of a type. + +int64_t +Gcc_backend::type_alignment (tree t) +{ + if (t == error_mark_node) + return 1; + return TYPE_ALIGN_UNIT (t); +} + +// Return the alignment of a struct field of type BTYPE. + +int64_t +Gcc_backend::type_field_alignment (tree t) +{ + if (t == error_mark_node) + return 1; + return rust_field_alignment (t); +} + +// Return the offset of a field in a struct. + +int64_t +Gcc_backend::type_field_offset (tree struct_tree, size_t index) +{ + if (struct_tree == error_mark_node) + return 0; + gcc_assert (TREE_CODE (struct_tree) == RECORD_TYPE); + tree field = TYPE_FIELDS (struct_tree); + for (; index > 0; --index) + { + field = DECL_CHAIN (field); + gcc_assert (field != NULL_TREE); + } + HOST_WIDE_INT offset_wide = int_byte_position (field); + int64_t ret = static_cast<int64_t> (offset_wide); + gcc_assert (ret == offset_wide); + return ret; +} + +// Return the zero value for a type. + +tree +Gcc_backend::zero_expression (tree t) +{ + tree ret; + if (t == error_mark_node) + ret = error_mark_node; + else + ret = build_zero_cst (t); + return ret; +} + +// An expression that references a variable. + +tree +Gcc_backend::var_expression (Bvariable *var, Location location) +{ + return var->get_tree (location); +} + +// Return a typed value as a constant integer. +// This function does not release the memory of @val + +tree +Gcc_backend::integer_constant_expression (tree t, mpz_t val) +{ + if (t == error_mark_node) + return error_mark_node; + + tree ret = wide_int_to_tree (t, wi::from_mpz (t, val, true)); + return ret; +} + +// Return a typed value as a constant floating-point number. + +tree +Gcc_backend::float_constant_expression (tree t, mpfr_t val) +{ + tree ret; + if (t == error_mark_node) + return error_mark_node; + + REAL_VALUE_TYPE r1; + real_from_mpfr (&r1, val, t, GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert (&r2, TYPE_MODE (t), &r1); + ret = build_real (t, r2); + return ret; +} + +// Return a typed real and imaginary value as a constant complex number. + +tree +Gcc_backend::complex_constant_expression (tree t, mpc_t val) +{ + tree ret; + if (t == error_mark_node) + return error_mark_node; + + REAL_VALUE_TYPE r1; + real_from_mpfr (&r1, mpc_realref (val), TREE_TYPE (t), GMP_RNDN); + REAL_VALUE_TYPE r2; + real_convert (&r2, TYPE_MODE (TREE_TYPE (t)), &r1); + + REAL_VALUE_TYPE r3; + real_from_mpfr (&r3, mpc_imagref (val), TREE_TYPE (t), GMP_RNDN); + REAL_VALUE_TYPE r4; + real_convert (&r4, TYPE_MODE (TREE_TYPE (t)), &r3); + + ret = build_complex (t, build_real (TREE_TYPE (t), r2), + build_real (TREE_TYPE (t), r4)); + return ret; +} + +// Make a constant string expression. + +tree +Gcc_backend::string_constant_expression (const std::string &val) +{ + tree index_type = build_index_type (size_int (val.length ())); + tree const_char_type = build_qualified_type (char_type_node, TYPE_QUAL_CONST); + tree string_type = build_array_type (const_char_type, index_type); + TYPE_STRING_FLAG (string_type) = 1; + tree string_val = build_string (val.length (), val.data ()); + TREE_TYPE (string_val) = string_type; + + return string_val; +} + +tree +Gcc_backend::wchar_constant_expression (wchar_t c) +{ + return build_int_cst (this->wchar_type (), c); +} + +tree +Gcc_backend::char_constant_expression (char c) +{ + return build_int_cst (this->char_type (), c); +} + +// Make a constant boolean expression. + +tree +Gcc_backend::boolean_constant_expression (bool val) +{ + return val ? boolean_true_node : boolean_false_node; +} + +// Return the real part of a complex expression. + +tree +Gcc_backend::real_part_expression (tree complex_tree, Location location) +{ + if (complex_tree == error_mark_node) + return error_mark_node; + gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree))); + tree ret + = fold_build1_loc (location.gcc_location (), REALPART_EXPR, + TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree); + return ret; +} + +// Return the imaginary part of a complex expression. + +tree +Gcc_backend::imag_part_expression (tree complex_tree, Location location) +{ + if (complex_tree == error_mark_node) + return error_mark_node; + gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree))); + tree ret + = fold_build1_loc (location.gcc_location (), IMAGPART_EXPR, + TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree); + return ret; +} + +// Make a complex expression given its real and imaginary parts. + +tree +Gcc_backend::complex_expression (tree real_tree, tree imag_tree, + Location location) +{ + if (real_tree == error_mark_node || imag_tree == error_mark_node) + return error_mark_node; + gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real_tree)) + == TYPE_MAIN_VARIANT (TREE_TYPE (imag_tree))); + gcc_assert (SCALAR_FLOAT_TYPE_P (TREE_TYPE (real_tree))); + tree ret = fold_build2_loc (location.gcc_location (), COMPLEX_EXPR, + build_complex_type (TREE_TYPE (real_tree)), + real_tree, imag_tree); + return ret; +} + +// An expression that converts an expression to a different type. + +tree +Gcc_backend::convert_expression (tree type_tree, tree expr_tree, + Location location) +{ + if (type_tree == error_mark_node || expr_tree == error_mark_node + || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + tree ret; + if (this->type_size (type_tree) == 0 + || TREE_TYPE (expr_tree) == void_type_node) + { + // Do not convert zero-sized types. + ret = expr_tree; + } + else if (TREE_CODE (type_tree) == INTEGER_TYPE) + ret = convert_to_integer (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == REAL_TYPE) + ret = convert_to_real (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == COMPLEX_TYPE) + ret = convert_to_complex (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == POINTER_TYPE + && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE) + ret = convert_to_pointer (type_tree, expr_tree); + else if (TREE_CODE (type_tree) == RECORD_TYPE + || TREE_CODE (type_tree) == ARRAY_TYPE) + ret = fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR, + type_tree, expr_tree); + else + ret = fold_convert_loc (location.gcc_location (), type_tree, expr_tree); + + return ret; +} + +// Return an expression for the field at INDEX in BSTRUCT. + +tree +Gcc_backend::struct_field_expression (tree struct_tree, size_t index, + Location location) +{ + if (struct_tree == error_mark_node + || TREE_TYPE (struct_tree) == error_mark_node) + return error_mark_node; + gcc_assert (TREE_CODE (TREE_TYPE (struct_tree)) == RECORD_TYPE + || TREE_CODE (TREE_TYPE (struct_tree)) == UNION_TYPE); + tree field = TYPE_FIELDS (TREE_TYPE (struct_tree)); + if (field == NULL_TREE) + { + // This can happen for a type which refers to itself indirectly + // and then turns out to be erroneous. + return error_mark_node; + } + for (unsigned int i = index; i > 0; --i) + { + field = DECL_CHAIN (field); + gcc_assert (field != NULL_TREE); + } + if (TREE_TYPE (field) == error_mark_node) + return error_mark_node; + tree ret = fold_build3_loc (location.gcc_location (), COMPONENT_REF, + TREE_TYPE (field), struct_tree, field, NULL_TREE); + if (TREE_CONSTANT (struct_tree)) + TREE_CONSTANT (ret) = 1; + return ret; +} + +// Return an expression that executes BSTAT before BEXPR. + +tree +Gcc_backend::compound_expression (tree stat, tree expr, Location location) +{ + if (stat == error_mark_node || expr == error_mark_node) + return error_mark_node; + tree ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, + TREE_TYPE (expr), stat, expr); + return ret; +} + +// Return an expression that executes THEN_EXPR if CONDITION is true, or +// ELSE_EXPR otherwise. + +tree +Gcc_backend::conditional_expression (tree, tree type_tree, tree cond_expr, + tree then_expr, tree else_expr, + Location location) +{ + if (type_tree == error_mark_node || cond_expr == error_mark_node + || then_expr == error_mark_node || else_expr == error_mark_node) + return error_mark_node; + tree ret = build3_loc (location.gcc_location (), COND_EXPR, type_tree, + cond_expr, then_expr, else_expr); + return ret; +} + +/* Helper function that converts rust operators to equivalent GCC tree_code. + Note that CompoundAssignmentOperator don't get their corresponding tree_code, + because they get compiled away when we lower AST to HIR. */ +static enum tree_code +operator_to_tree_code (NegationOperator op) +{ + switch (op) + { + case NegationOperator::NEGATE: + return NEGATE_EXPR; + case NegationOperator::NOT: + return TRUTH_NOT_EXPR; + default: + gcc_unreachable (); + } +} + +/* Note that GCC tree code distinguishes floating point division and integer + division. These two types of division are represented as the same rust + operator, and can only be distinguished via context(i.e. the TREE_TYPE of the + operands). */ +static enum tree_code +operator_to_tree_code (ArithmeticOrLogicalOperator op, bool floating_point) +{ + switch (op) + { + case ArithmeticOrLogicalOperator::ADD: + return PLUS_EXPR; + case ArithmeticOrLogicalOperator::SUBTRACT: + return MINUS_EXPR; + case ArithmeticOrLogicalOperator::MULTIPLY: + return MULT_EXPR; + case ArithmeticOrLogicalOperator::DIVIDE: + if (floating_point) + return RDIV_EXPR; + else + return TRUNC_DIV_EXPR; + case ArithmeticOrLogicalOperator::MODULUS: + return TRUNC_MOD_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_AND: + return BIT_AND_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_OR: + return BIT_IOR_EXPR; + case ArithmeticOrLogicalOperator::BITWISE_XOR: + return BIT_XOR_EXPR; + case ArithmeticOrLogicalOperator::LEFT_SHIFT: + return LSHIFT_EXPR; + case ArithmeticOrLogicalOperator::RIGHT_SHIFT: + return RSHIFT_EXPR; + default: + gcc_unreachable (); + } +} + +static enum tree_code +operator_to_tree_code (ComparisonOperator op) +{ + switch (op) + { + case ComparisonOperator::EQUAL: + return EQ_EXPR; + case ComparisonOperator::NOT_EQUAL: + return NE_EXPR; + case ComparisonOperator::GREATER_THAN: + return GT_EXPR; + case ComparisonOperator::LESS_THAN: + return LT_EXPR; + case ComparisonOperator::GREATER_OR_EQUAL: + return GE_EXPR; + case ComparisonOperator::LESS_OR_EQUAL: + return LE_EXPR; + default: + gcc_unreachable (); + } +} + +static enum tree_code +operator_to_tree_code (LazyBooleanOperator op) +{ + switch (op) + { + case LazyBooleanOperator::LOGICAL_OR: + return TRUTH_ORIF_EXPR; + case LazyBooleanOperator::LOGICAL_AND: + return TRUTH_ANDIF_EXPR; + default: + gcc_unreachable (); + } +} + +/* Helper function for deciding if a tree is a floating point node. */ +bool +is_floating_point (tree t) +{ + auto tree_type = TREE_CODE (TREE_TYPE (t)); + return tree_type == REAL_TYPE || tree_type == COMPLEX_TYPE; +} + +// Return an expression for the negation operation OP EXPR. +tree +Gcc_backend::negation_expression (NegationOperator op, tree expr_tree, + Location location) +{ + /* Check if the expression is an error, in which case we return an error + expression. */ + if (expr_tree == error_mark_node || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + /* For negation operators, the resulting type should be the same as its + operand. */ + auto tree_type = TREE_TYPE (expr_tree); + auto original_type = tree_type; + auto tree_code = operator_to_tree_code (op); + + /* For floating point operations we may need to extend the precision of type. + For example, a 64-bit machine may not support operations on float32. */ + bool floating_point = is_floating_point (expr_tree); + auto extended_type = NULL_TREE; + if (floating_point) + { + extended_type = excess_precision_type (tree_type); + if (extended_type != NULL_TREE) + { + expr_tree = convert (extended_type, expr_tree); + tree_type = extended_type; + } + } + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build1_loc (location.gcc_location (), tree_code, + tree_type, expr_tree); + if (floating_point && extended_type != NULL_TREE) + new_tree = convert (original_type, expr_tree); + return new_tree; +} + +// Return an expression for the arithmetic or logical operation LEFT OP RIGHT. +tree +Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op, + tree left_tree, tree right_tree, + Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* We need to determine if we're doing floating point arithmetics of integer + arithmetics. */ + bool floating_point = is_floating_point (left_tree); + + /* For arithmetic or logical operators, the resulting type should be the same + as the lhs operand. */ + auto tree_type = TREE_TYPE (left_tree); + auto original_type = tree_type; + auto tree_code = operator_to_tree_code (op, floating_point); + + /* For floating point operations we may need to extend the precision of type. + For example, a 64-bit machine may not support operations on float32. */ + auto extended_type = NULL_TREE; + if (floating_point) + { + extended_type = excess_precision_type (tree_type); + if (extended_type != NULL_TREE) + { + left_tree = convert (extended_type, left_tree); + right_tree = convert (extended_type, right_tree); + tree_type = extended_type; + } + } + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + TREE_CONSTANT (new_tree) + = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree); + + if (floating_point && extended_type != NULL_TREE) + new_tree = convert (original_type, new_tree); + return new_tree; +} + +// Return an expression for the comparison operation LEFT OP RIGHT. +tree +Gcc_backend::comparison_expression (ComparisonOperator op, tree left_tree, + tree right_tree, Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* For comparison operators, the resulting type should be boolean. */ + auto tree_type = boolean_type_node; + auto tree_code = operator_to_tree_code (op); + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + return new_tree; +} + +// Return an expression for the lazy boolean operation LEFT OP RIGHT. +tree +Gcc_backend::lazy_boolean_expression (LazyBooleanOperator op, tree left_tree, + tree right_tree, Location location) +{ + /* Check if either expression is an error, in which case we return an error + expression. */ + if (left_tree == error_mark_node || right_tree == error_mark_node) + return error_mark_node; + + /* For lazy boolean operators, the resulting type should be the same as the + rhs operand. */ + auto tree_type = TREE_TYPE (right_tree); + auto tree_code = operator_to_tree_code (op); + + /* Construct a new tree and build an expression from it. */ + auto new_tree = fold_build2_loc (location.gcc_location (), tree_code, + tree_type, left_tree, right_tree); + return new_tree; +} + +// Return an expression that constructs BTYPE with VALS. + +tree +Gcc_backend::constructor_expression (tree type_tree, bool is_variant, + const std::vector<tree> &vals, + int union_index, Location location) +{ + if (type_tree == error_mark_node) + return error_mark_node; + + vec<constructor_elt, va_gc> *init; + vec_alloc (init, vals.size ()); + + tree sink = NULL_TREE; + bool is_constant = true; + tree field = TYPE_FIELDS (type_tree); + + if (is_variant) + { + gcc_assert (union_index != -1); + gcc_assert (TREE_CODE (type_tree) == UNION_TYPE); + + for (int i = 0; i < union_index; i++) + { + gcc_assert (field != NULL_TREE); + field = DECL_CHAIN (field); + } + + tree nested_ctor + = constructor_expression (TREE_TYPE (field), false, vals, -1, location); + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), nested_ctor, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + else + { + if (union_index != -1) + { + gcc_assert (TREE_CODE (type_tree) == UNION_TYPE); + tree val = vals.front (); + for (int i = 0; i < union_index; i++) + { + gcc_assert (field != NULL_TREE); + field = DECL_CHAIN (field); + } + if (TREE_TYPE (field) == error_mark_node || val == error_mark_node + || TREE_TYPE (val) == error_mark_node) + return error_mark_node; + + if (int_size_in_bytes (TREE_TYPE (field)) == 0) + { + // GIMPLE cannot represent indices of zero-sized types so + // trying to construct a map with zero-sized keys might lead + // to errors. Instead, we evaluate each expression that + // would have been added as a map element for its + // side-effects and construct an empty map. + append_to_statement_list (val, &sink); + } + else + { + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), val, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + } + else + { + gcc_assert (TREE_CODE (type_tree) == RECORD_TYPE); + for (std::vector<tree>::const_iterator p = vals.begin (); + p != vals.end (); ++p, field = DECL_CHAIN (field)) + { + gcc_assert (field != NULL_TREE); + tree val = (*p); + if (TREE_TYPE (field) == error_mark_node || val == error_mark_node + || TREE_TYPE (val) == error_mark_node) + return error_mark_node; + + if (int_size_in_bytes (TREE_TYPE (field)) == 0) + { + // GIMPLE cannot represent indices of zero-sized types so + // trying to construct a map with zero-sized keys might lead + // to errors. Instead, we evaluate each expression that + // would have been added as a map element for its + // side-effects and construct an empty map. + append_to_statement_list (val, &sink); + continue; + } + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = field; + elt->value + = this->convert_tree (TREE_TYPE (field), val, location); + if (!TREE_CONSTANT (elt->value)) + is_constant = false; + } + gcc_assert (field == NULL_TREE); + } + } + + tree ret = build_constructor (type_tree, init); + if (is_constant) + TREE_CONSTANT (ret) = 1; + if (sink != NULL_TREE) + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree, + sink, ret); + return ret; +} + +tree +Gcc_backend::array_constructor_expression ( + tree type_tree, const std::vector<unsigned long> &indexes, + const std::vector<tree> &vals, Location location) +{ + if (type_tree == error_mark_node) + return error_mark_node; + + gcc_assert (indexes.size () == vals.size ()); + + tree element_type = TREE_TYPE (type_tree); + HOST_WIDE_INT element_size = int_size_in_bytes (element_type); + vec<constructor_elt, va_gc> *init; + vec_alloc (init, element_size == 0 ? 0 : vals.size ()); + + tree sink = NULL_TREE; + bool is_constant = true; + for (size_t i = 0; i < vals.size (); ++i) + { + tree index = size_int (indexes[i]); + tree val = vals[i]; + + if (index == error_mark_node || val == error_mark_node) + return error_mark_node; + + if (element_size == 0) + { + // GIMPLE cannot represent arrays of zero-sized types so trying + // to construct an array of zero-sized values might lead to errors. + // Instead, we evaluate each expression that would have been added as + // an array value for its side-effects and construct an empty array. + append_to_statement_list (val, &sink); + continue; + } + + if (!TREE_CONSTANT (val)) + is_constant = false; + + constructor_elt empty = {NULL, NULL}; + constructor_elt *elt = init->quick_push (empty); + elt->index = index; + elt->value = val; + } + + tree ret = build_constructor (type_tree, init); + if (is_constant) + TREE_CONSTANT (ret) = 1; + if (sink != NULL_TREE) + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree, + sink, ret); + return ret; +} + +// Build insns to create an array, initialize all elements of the array to +// value, and return it +tree +Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type, + tree length, tree value, tree *tmp, + Location locus) +{ + std::vector<tree> stmts; + + // Temporary array we initialize with the desired value. + tree t = NULL_TREE; + Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type, + NULL_TREE, true, locus, &t); + tree arr = tmp_array->get_tree (locus); + stmts.push_back (t); + + // Temporary for the array length used for initialization loop guard. + Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node, + length, true, locus, &t); + tree len = tmp_len->get_tree (locus); + stmts.push_back (t); + + // Temporary variable for pointer used to initialize elements. + tree ptr_type = this->pointer_type (TREE_TYPE (array_type)); + tree ptr_init + = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type, + this->array_index_expression (arr, integer_zero_node, locus)); + Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type, + ptr_init, false, locus, &t); + tree ptr = tmp_ptr->get_tree (locus); + stmts.push_back (t); + + // push statement list for the loop + std::vector<tree> loop_stmts; + + // Loop exit condition: + // if (length == 0) break; + t = this->comparison_expression (ComparisonOperator::EQUAL, len, + this->zero_expression (TREE_TYPE (len)), + locus); + + t = this->exit_expression (t, locus); + loop_stmts.push_back (t); + + // Assign value to the current pointer position + // *ptr = value; + t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus); + loop_stmts.push_back (t); + + // Move pointer to next element + // ptr++; + tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type)); + t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size)); + loop_stmts.push_back (t); + + // Decrement loop counter. + // length--; + t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len, + convert (TREE_TYPE (len), integer_one_node)); + loop_stmts.push_back (t); + + // pop statments and finish loop + tree loop_body = this->statement_list (loop_stmts); + stmts.push_back (this->loop_expression (loop_body, locus)); + + // Return the temporary in the provided pointer and the statement list which + // initializes it. + *tmp = tmp_array->get_tree (locus); + return this->statement_list (stmts); +} + +// Return an expression representing ARRAY[INDEX] + +tree +Gcc_backend::array_index_expression (tree array_tree, tree index_tree, + Location location) +{ + if (array_tree == error_mark_node || TREE_TYPE (array_tree) == error_mark_node + || index_tree == error_mark_node) + return error_mark_node; + + // A function call that returns a zero sized object will have been + // changed to return void. If we see void here, assume we are + // dealing with a zero sized type and just evaluate the operands. + tree ret; + if (TREE_TYPE (array_tree) != void_type_node) + ret = build4_loc (location.gcc_location (), ARRAY_REF, + TREE_TYPE (TREE_TYPE (array_tree)), array_tree, + index_tree, NULL_TREE, NULL_TREE); + else + ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, + void_type_node, array_tree, index_tree); + + return ret; +} + +// Create an expression for a call to FN_EXPR with FN_ARGS. +tree +Gcc_backend::call_expression (tree fn, const std::vector<tree> &fn_args, + tree chain_expr, Location location) +{ + if (fn == error_mark_node || TREE_TYPE (fn) == error_mark_node) + return error_mark_node; + + gcc_assert (FUNCTION_POINTER_TYPE_P (TREE_TYPE (fn))); + tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn))); + + size_t nargs = fn_args.size (); + tree *args = nargs == 0 ? NULL : new tree[nargs]; + for (size_t i = 0; i < nargs; ++i) + { + args[i] = fn_args.at (i); + } + + tree fndecl = fn; + if (TREE_CODE (fndecl) == ADDR_EXPR) + fndecl = TREE_OPERAND (fndecl, 0); + + // This is to support builtin math functions when using 80387 math. + tree excess_type = NULL_TREE; + if (optimize && TREE_CODE (fndecl) == FUNCTION_DECL + && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL) + && DECL_IS_UNDECLARED_BUILTIN (fndecl) && nargs > 0 + && ((SCALAR_FLOAT_TYPE_P (rettype) + && SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[0]))) + || (COMPLEX_FLOAT_TYPE_P (rettype) + && COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[0]))))) + { + excess_type = excess_precision_type (TREE_TYPE (args[0])); + if (excess_type != NULL_TREE) + { + tree excess_fndecl + = mathfn_built_in (excess_type, DECL_FUNCTION_CODE (fndecl)); + if (excess_fndecl == NULL_TREE) + excess_type = NULL_TREE; + else + { + fn = build_fold_addr_expr_loc (location.gcc_location (), + excess_fndecl); + for (size_t i = 0; i < nargs; ++i) + { + if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[i])) + || COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[i]))) + args[i] = ::convert (excess_type, args[i]); + } + } + } + } + + tree ret + = build_call_array_loc (location.gcc_location (), + excess_type != NULL_TREE ? excess_type : rettype, + fn, nargs, args); + + // check for deprecated function usage + if (fndecl && TREE_DEPRECATED (fndecl)) + { + // set up the call-site information for `warn_deprecated_use` + input_location = location.gcc_location (); + warn_deprecated_use (fndecl, NULL_TREE); + } + + if (chain_expr) + CALL_EXPR_STATIC_CHAIN (ret) = chain_expr; + + if (excess_type != NULL_TREE) + { + // Calling convert here can undo our excess precision change. + // That may or may not be a bug in convert_to_real. + ret = build1_loc (location.gcc_location (), NOP_EXPR, rettype, ret); + } + + delete[] args; + return ret; +} + +// Variable initialization. + +tree +Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree) +{ + tree var_tree = var->get_decl (); + if (var_tree == error_mark_node || init_tree == error_mark_node) + return error_mark_node; + gcc_assert (TREE_CODE (var_tree) == VAR_DECL); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // initialization of a zero-sized expression to a non-zero sized + // variable, or vice-versa. Avoid crashes by omitting the + // initializer. Such initializations don't mean anything anyhow. + if (int_size_in_bytes (TREE_TYPE (var_tree)) != 0 && init_tree != NULL_TREE + && TREE_TYPE (init_tree) != void_type_node + && int_size_in_bytes (TREE_TYPE (init_tree)) != 0) + { + DECL_INITIAL (var_tree) = init_tree; + init_tree = NULL_TREE; + } + + tree ret = build1_loc (DECL_SOURCE_LOCATION (var_tree), DECL_EXPR, + void_type_node, var_tree); + if (init_tree != NULL_TREE) + ret = build2_loc (DECL_SOURCE_LOCATION (var_tree), COMPOUND_EXPR, + void_type_node, init_tree, ret); + + return ret; +} + +// Assignment. + +tree +Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location) +{ + if (lhs == error_mark_node || rhs == error_mark_node) + return error_mark_node; + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // assignment of a zero-sized expression to a non-zero sized + // expression; avoid crashes here by avoiding assignments of + // zero-sized expressions. Such assignments don't really mean + // anything anyhow. + if (TREE_TYPE (lhs) == void_type_node + || int_size_in_bytes (TREE_TYPE (lhs)) == 0 + || TREE_TYPE (rhs) == void_type_node + || int_size_in_bytes (TREE_TYPE (rhs)) == 0) + return this->compound_statement (lhs, rhs); + + rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location); + + return fold_build2_loc (location.gcc_location (), MODIFY_EXPR, void_type_node, + lhs, rhs); +} + +// Return. + +tree +Gcc_backend::return_statement (tree fntree, const std::vector<tree> &vals, + Location location) +{ + if (fntree == error_mark_node) + return error_mark_node; + tree result = DECL_RESULT (fntree); + if (result == error_mark_node) + return error_mark_node; + + // If the result size is zero bytes, we have set the function type + // to have a result type of void, so don't return anything. + // See the function_type method. + tree res_type = TREE_TYPE (result); + if (res_type == void_type_node || int_size_in_bytes (res_type) == 0) + { + tree stmt_list = NULL_TREE; + for (std::vector<tree>::const_iterator p = vals.begin (); + p != vals.end (); p++) + { + tree val = (*p); + if (val == error_mark_node) + return error_mark_node; + append_to_statement_list (val, &stmt_list); + } + tree ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, NULL_TREE); + append_to_statement_list (ret, &stmt_list); + return stmt_list; + } + + tree ret; + if (vals.empty ()) + ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, NULL_TREE); + else if (vals.size () == 1) + { + tree val = vals.front (); + if (val == error_mark_node) + return error_mark_node; + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, result, vals.front ()); + ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, set); + } + else + { + // To return multiple values, copy the values into a temporary + // variable of the right structure type, and then assign the + // temporary variable to the DECL_RESULT in the return + // statement. + tree stmt_list = NULL_TREE; + tree rettype = TREE_TYPE (result); + + if (DECL_STRUCT_FUNCTION (fntree) == NULL) + push_struct_function (fntree); + else + push_cfun (DECL_STRUCT_FUNCTION (fntree)); + tree rettmp = create_tmp_var (rettype, "RESULT"); + pop_cfun (); + + tree field = TYPE_FIELDS (rettype); + for (std::vector<tree>::const_iterator p = vals.begin (); + p != vals.end (); p++, field = DECL_CHAIN (field)) + { + gcc_assert (field != NULL_TREE); + tree ref + = fold_build3_loc (location.gcc_location (), COMPONENT_REF, + TREE_TYPE (field), rettmp, field, NULL_TREE); + tree val = (*p); + if (val == error_mark_node) + return error_mark_node; + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, ref, (*p)); + append_to_statement_list (set, &stmt_list); + } + gcc_assert (field == NULL_TREE); + tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR, + void_type_node, result, rettmp); + tree ret_expr = fold_build1_loc (location.gcc_location (), RETURN_EXPR, + void_type_node, set); + append_to_statement_list (ret_expr, &stmt_list); + ret = stmt_list; + } + return ret; +} + +// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an +// error occurs. EXCEPT_STMT may be NULL. FINALLY_STMT may be NULL and if not +// NULL, it will always be executed. This is used for handling defers in Rust +// functions. In C++, the resulting code is of this form: +// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; } + +tree +Gcc_backend::exception_handler_statement (tree try_stmt, tree except_stmt, + tree finally_stmt, Location location) +{ + if (try_stmt == error_mark_node || except_stmt == error_mark_node + || finally_stmt == error_mark_node) + return error_mark_node; + + if (except_stmt != NULL_TREE) + try_stmt = build2_loc (location.gcc_location (), TRY_CATCH_EXPR, + void_type_node, try_stmt, + build2_loc (location.gcc_location (), CATCH_EXPR, + void_type_node, NULL, except_stmt)); + if (finally_stmt != NULL_TREE) + try_stmt = build2_loc (location.gcc_location (), TRY_FINALLY_EXPR, + void_type_node, try_stmt, finally_stmt); + return try_stmt; +} + +// If. + +tree +Gcc_backend::if_statement (tree, tree cond_tree, tree then_tree, tree else_tree, + Location location) +{ + if (cond_tree == error_mark_node || then_tree == error_mark_node + || else_tree == error_mark_node) + return error_mark_node; + tree ret = build3_loc (location.gcc_location (), COND_EXPR, void_type_node, + cond_tree, then_tree, else_tree); + return ret; +} + +// Loops + +tree +Gcc_backend::loop_expression (tree body, Location locus) +{ + return fold_build1_loc (locus.gcc_location (), LOOP_EXPR, void_type_node, + body); +} + +tree +Gcc_backend::exit_expression (tree cond_tree, Location locus) +{ + return fold_build1_loc (locus.gcc_location (), EXIT_EXPR, void_type_node, + cond_tree); +} + +// Pair of statements. + +tree +Gcc_backend::compound_statement (tree s1, tree s2) +{ + tree stmt_list = NULL_TREE; + tree t = s1; + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + t = s2; + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + + // If neither statement has any side effects, stmt_list can be NULL + // at this point. + if (stmt_list == NULL_TREE) + stmt_list = integer_zero_node; + + return stmt_list; +} + +// List of statements. + +tree +Gcc_backend::statement_list (const std::vector<tree> &statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector<tree>::const_iterator p = statements.begin (); + p != statements.end (); ++p) + { + tree t = (*p); + if (t == error_mark_node) + return error_mark_node; + append_to_statement_list (t, &stmt_list); + } + return stmt_list; +} + +// Make a block. For some reason gcc uses a dual structure for +// blocks: BLOCK tree nodes and BIND_EXPR tree nodes. Since the +// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in +// the Bblock. + +tree +Gcc_backend::block (tree fndecl, tree enclosing, + const std::vector<Bvariable *> &vars, + Location start_location, Location) +{ + tree block_tree = make_node (BLOCK); + if (enclosing == NULL) + { + gcc_assert (fndecl != NULL_TREE); + + // We may have already created a block for local variables when + // we take the address of a parameter. + if (DECL_INITIAL (fndecl) == NULL_TREE) + { + BLOCK_SUPERCONTEXT (block_tree) = fndecl; + DECL_INITIAL (fndecl) = block_tree; + } + else + { + tree superblock_tree = DECL_INITIAL (fndecl); + BLOCK_SUPERCONTEXT (block_tree) = superblock_tree; + tree *pp; + for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE; + pp = &BLOCK_CHAIN (*pp)) + ; + *pp = block_tree; + } + } + else + { + tree superblock_tree = BIND_EXPR_BLOCK (enclosing); + gcc_assert (TREE_CODE (superblock_tree) == BLOCK); + + BLOCK_SUPERCONTEXT (block_tree) = superblock_tree; + tree *pp; + for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE; + pp = &BLOCK_CHAIN (*pp)) + ; + *pp = block_tree; + } + + tree *pp = &BLOCK_VARS (block_tree); + for (std::vector<Bvariable *>::const_iterator pv = vars.begin (); + pv != vars.end (); ++pv) + { + *pp = (*pv)->get_decl (); + if (*pp != error_mark_node) + pp = &DECL_CHAIN (*pp); + } + *pp = NULL_TREE; + + TREE_USED (block_tree) = 1; + + tree bind_tree + = build3_loc (start_location.gcc_location (), BIND_EXPR, void_type_node, + BLOCK_VARS (block_tree), NULL_TREE, block_tree); + TREE_SIDE_EFFECTS (bind_tree) = 1; + return bind_tree; +} + +// Add statements to a block. + +void +Gcc_backend::block_add_statements (tree bind_tree, + const std::vector<tree> &statements) +{ + tree stmt_list = NULL_TREE; + for (std::vector<tree>::const_iterator p = statements.begin (); + p != statements.end (); ++p) + { + tree s = (*p); + if (s != error_mark_node) + append_to_statement_list (s, &stmt_list); + } + + gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); + BIND_EXPR_BODY (bind_tree) = stmt_list; +} + +// This is not static because we declare it with GTY(()) in rust-c.h. +tree rust_non_zero_struct; + +// Return a type corresponding to TYPE with non-zero size. + +tree +Gcc_backend::non_zero_size_type (tree type) +{ + if (int_size_in_bytes (type) != 0) + return type; + + switch (TREE_CODE (type)) + { + case RECORD_TYPE: + if (TYPE_FIELDS (type) != NULL_TREE) + { + tree ns = make_node (RECORD_TYPE); + tree field_trees = NULL_TREE; + tree *pp = &field_trees; + for (tree field = TYPE_FIELDS (type); field != NULL_TREE; + field = DECL_CHAIN (field)) + { + tree ft = TREE_TYPE (field); + if (field == TYPE_FIELDS (type)) + ft = non_zero_size_type (ft); + tree f = build_decl (DECL_SOURCE_LOCATION (field), FIELD_DECL, + DECL_NAME (field), ft); + DECL_CONTEXT (f) = ns; + *pp = f; + pp = &DECL_CHAIN (f); + } + TYPE_FIELDS (ns) = field_trees; + layout_type (ns); + return ns; + } + + if (rust_non_zero_struct == NULL_TREE) + { + type = make_node (RECORD_TYPE); + tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL, + get_identifier ("dummy"), boolean_type_node); + DECL_CONTEXT (field) = type; + TYPE_FIELDS (type) = field; + layout_type (type); + rust_non_zero_struct = type; + } + return rust_non_zero_struct; + + case ARRAY_TYPE: { + tree element_type = non_zero_size_type (TREE_TYPE (type)); + return build_array_type_nelts (element_type, 1); + } + + default: + gcc_unreachable (); + } + + gcc_unreachable (); +} + +// Convert EXPR_TREE to TYPE_TREE. Sometimes the same unnamed Rust type +// can be created multiple times and thus have multiple tree +// representations. Make sure this does not confuse the middle-end. + +tree +Gcc_backend::convert_tree (tree type_tree, tree expr_tree, Location location) +{ + if (type_tree == TREE_TYPE (expr_tree)) + return expr_tree; + + if (type_tree == error_mark_node || expr_tree == error_mark_node + || TREE_TYPE (expr_tree) == error_mark_node) + return error_mark_node; + + if (POINTER_TYPE_P (type_tree) || INTEGRAL_TYPE_P (type_tree) + || SCALAR_FLOAT_TYPE_P (type_tree) || COMPLEX_FLOAT_TYPE_P (type_tree)) + return fold_convert_loc (location.gcc_location (), type_tree, expr_tree); + else if (TREE_CODE (type_tree) == RECORD_TYPE + || TREE_CODE (type_tree) == UNION_TYPE + || TREE_CODE (type_tree) == ARRAY_TYPE) + { + gcc_assert (int_size_in_bytes (type_tree) + == int_size_in_bytes (TREE_TYPE (expr_tree))); + if (TYPE_MAIN_VARIANT (type_tree) + == TYPE_MAIN_VARIANT (TREE_TYPE (expr_tree))) + return fold_build1_loc (location.gcc_location (), NOP_EXPR, type_tree, + expr_tree); + return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR, + type_tree, expr_tree); + } + + gcc_unreachable (); +} + +// Make a global variable. + +Bvariable * +Gcc_backend::global_variable (const std::string &var_name, + const std::string &asm_name, tree type_tree, + bool is_external, bool is_hidden, + bool in_unique_section, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + + // The GNU linker does not like dynamic variables with zero size. + tree orig_type_tree = type_tree; + if ((is_external || !is_hidden) && int_size_in_bytes (type_tree) == 0) + type_tree = this->non_zero_size_type (type_tree); + + tree decl = build_decl (location.gcc_location (), VAR_DECL, + get_identifier_from_string (var_name), type_tree); + if (is_external) + DECL_EXTERNAL (decl) = 1; + else + TREE_STATIC (decl) = 1; + if (!is_hidden) + { + TREE_PUBLIC (decl) = 1; + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + } + else + { + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + } + + TREE_USED (decl) = 1; + + if (in_unique_section) + resolve_unique_section (decl, 0, 1); + + rust_preserve_from_gc (decl); + + return new Bvariable (decl, orig_type_tree); +} + +// Set the initial value of a global variable. + +void +Gcc_backend::global_variable_set_init (Bvariable *var, tree expr_tree) +{ + if (expr_tree == error_mark_node) + return; + gcc_assert (TREE_CONSTANT (expr_tree)); + tree var_decl = var->get_decl (); + if (var_decl == error_mark_node) + return; + DECL_INITIAL (var_decl) = expr_tree; + + // If this variable goes in a unique section, it may need to go into + // a different one now that DECL_INITIAL is set. + if (symtab_node::get (var_decl) + && symtab_node::get (var_decl)->implicit_section) + { + set_decl_section_name (var_decl, (const char *) NULL); + resolve_unique_section (var_decl, compute_reloc_for_constant (expr_tree), + 1); + } +} + +// Make a local variable. + +Bvariable * +Gcc_backend::local_variable (tree function, const std::string &name, + tree type_tree, Bvariable *decl_var, + Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), VAR_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = function; + + if (decl_var != NULL) + { + DECL_HAS_VALUE_EXPR_P (decl) = 1; + SET_DECL_VALUE_EXPR (decl, decl_var->get_decl ()); + } + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a function parameter variable. + +Bvariable * +Gcc_backend::parameter_variable (tree function, const std::string &name, + tree type_tree, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), PARM_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = function; + DECL_ARG_TYPE (decl) = type_tree; + + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a static chain variable. + +Bvariable * +Gcc_backend::static_chain_variable (tree fndecl, const std::string &name, + tree type_tree, Location location) +{ + if (type_tree == error_mark_node) + return this->error_variable (); + tree decl = build_decl (location.gcc_location (), PARM_DECL, + get_identifier_from_string (name), type_tree); + DECL_CONTEXT (decl) = fndecl; + DECL_ARG_TYPE (decl) = type_tree; + TREE_USED (decl) = 1; + DECL_ARTIFICIAL (decl) = 1; + DECL_IGNORED_P (decl) = 1; + TREE_READONLY (decl) = 1; + + struct function *f = DECL_STRUCT_FUNCTION (fndecl); + if (f == NULL) + { + push_struct_function (fndecl); + pop_cfun (); + f = DECL_STRUCT_FUNCTION (fndecl); + } + gcc_assert (f->static_chain_decl == NULL); + f->static_chain_decl = decl; + DECL_STATIC_CHAIN (fndecl) = 1; + + rust_preserve_from_gc (decl); + return new Bvariable (decl); +} + +// Make a temporary variable. + +Bvariable * +Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree, + tree init_tree, bool is_address_taken, + Location location, tree *pstatement) +{ + gcc_assert (fndecl != NULL_TREE); + if (type_tree == error_mark_node || init_tree == error_mark_node + || fndecl == error_mark_node) + { + *pstatement = error_mark_node; + return this->error_variable (); + } + + tree var; + // We can only use create_tmp_var if the type is not addressable. + if (!TREE_ADDRESSABLE (type_tree)) + { + if (DECL_STRUCT_FUNCTION (fndecl) == NULL) + push_struct_function (fndecl); + else + push_cfun (DECL_STRUCT_FUNCTION (fndecl)); + + var = create_tmp_var (type_tree, "RUSTTMP"); + pop_cfun (); + } + else + { + gcc_assert (bind_tree != NULL_TREE); + var = build_decl (location.gcc_location (), VAR_DECL, + create_tmp_var_name ("RUSTTMP"), type_tree); + DECL_ARTIFICIAL (var) = 1; + DECL_IGNORED_P (var) = 1; + TREE_USED (var) = 1; + DECL_CONTEXT (var) = fndecl; + + // We have to add this variable to the BLOCK and the BIND_EXPR. + gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR); + tree block_tree = BIND_EXPR_BLOCK (bind_tree); + gcc_assert (TREE_CODE (block_tree) == BLOCK); + DECL_CHAIN (var) = BLOCK_VARS (block_tree); + BLOCK_VARS (block_tree) = var; + BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree); + } + + if (this->type_size (type_tree) != 0 && init_tree != NULL_TREE + && TREE_TYPE (init_tree) != void_type_node) + DECL_INITIAL (var) = this->convert_tree (type_tree, init_tree, location); + + if (is_address_taken) + TREE_ADDRESSABLE (var) = 1; + + *pstatement + = build1_loc (location.gcc_location (), DECL_EXPR, void_type_node, var); + + // For a zero sized type, don't initialize VAR with BINIT, but still + // evaluate BINIT for its side effects. + if (init_tree != NULL_TREE + && (this->type_size (type_tree) == 0 + || TREE_TYPE (init_tree) == void_type_node)) + *pstatement = this->compound_statement (init_tree, *pstatement); + + return new Bvariable (var); +} + +// Make a label. + +tree +Gcc_backend::label (tree func_tree, const std::string &name, Location location) +{ + tree decl; + if (name.empty ()) + { + if (DECL_STRUCT_FUNCTION (func_tree) == NULL) + push_struct_function (func_tree); + else + push_cfun (DECL_STRUCT_FUNCTION (func_tree)); + + decl = create_artificial_label (location.gcc_location ()); + + pop_cfun (); + } + else + { + tree id = get_identifier_from_string (name); + decl + = build_decl (location.gcc_location (), LABEL_DECL, id, void_type_node); + DECL_CONTEXT (decl) = func_tree; + } + return decl; +} + +// Make a statement which defines a label. + +tree +Gcc_backend::label_definition_statement (tree label) +{ + return fold_build1_loc (DECL_SOURCE_LOCATION (label), LABEL_EXPR, + void_type_node, label); +} + +// Make a goto statement. + +tree +Gcc_backend::goto_statement (tree label, Location location) +{ + return fold_build1_loc (location.gcc_location (), GOTO_EXPR, void_type_node, + label); +} + +// Get the address of a label. + +tree +Gcc_backend::label_address (tree label, Location location) +{ + TREE_USED (label) = 1; + TREE_ADDRESSABLE (label) = 1; + tree ret + = fold_convert_loc (location.gcc_location (), ptr_type_node, + build_fold_addr_expr_loc (location.gcc_location (), + label)); + return ret; +} + +// Declare or define a new function. + +tree +Gcc_backend::function (tree functype, const std::string &name, + const std::string &asm_name, unsigned int flags, + Location location) +{ + if (functype != error_mark_node) + { + gcc_assert (FUNCTION_POINTER_TYPE_P (functype)); + functype = TREE_TYPE (functype); + } + tree id = get_identifier_from_string (name); + if (functype == error_mark_node || id == error_mark_node) + return error_mark_node; + + tree decl + = build_decl (location.gcc_location (), FUNCTION_DECL, id, functype); + if (!asm_name.empty ()) + SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name)); + + if ((flags & function_is_declaration) != 0) + DECL_EXTERNAL (decl) = 1; + else + { + tree restype = TREE_TYPE (functype); + tree resdecl = build_decl (location.gcc_location (), RESULT_DECL, + NULL_TREE, restype); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_CONTEXT (resdecl) = decl; + DECL_RESULT (decl) = resdecl; + } + if ((flags & function_is_uninlinable) != 0) + DECL_UNINLINABLE (decl) = 1; + if ((flags & function_does_not_return) != 0) + TREE_THIS_VOLATILE (decl) = 1; + if ((flags & function_in_unique_section) != 0) + resolve_unique_section (decl, 0, 1); + + rust_preserve_from_gc (decl); + return decl; +} + +// Create a statement that runs all deferred calls for FUNCTION. This should +// be a statement that looks like this in C++: +// finish: +// try { UNDEFER; } catch { CHECK_DEFER; goto finish; } + +tree +Gcc_backend::function_defer_statement (tree function, tree undefer_tree, + tree defer_tree, Location location) +{ + if (undefer_tree == error_mark_node || defer_tree == error_mark_node + || function == error_mark_node) + return error_mark_node; + + if (DECL_STRUCT_FUNCTION (function) == NULL) + push_struct_function (function); + else + push_cfun (DECL_STRUCT_FUNCTION (function)); + + tree stmt_list = NULL; + tree label = this->label (function, "", location); + tree label_def = this->label_definition_statement (label); + append_to_statement_list (label_def, &stmt_list); + + tree jump_stmt = this->goto_statement (label, location); + tree catch_body + = build2 (COMPOUND_EXPR, void_type_node, defer_tree, jump_stmt); + catch_body = build2 (CATCH_EXPR, void_type_node, NULL, catch_body); + tree try_catch + = build2 (TRY_CATCH_EXPR, void_type_node, undefer_tree, catch_body); + append_to_statement_list (try_catch, &stmt_list); + pop_cfun (); + + return stmt_list; +} + +// Record PARAM_VARS as the variables to use for the parameters of FUNCTION. +// This will only be called for a function definition. + +bool +Gcc_backend::function_set_parameters ( + tree function, const std::vector<Bvariable *> ¶m_vars) +{ + if (function == error_mark_node) + return false; + + tree params = NULL_TREE; + tree *pp = ¶ms; + for (std::vector<Bvariable *>::const_iterator pv = param_vars.begin (); + pv != param_vars.end (); ++pv) + { + *pp = (*pv)->get_decl (); + gcc_assert (*pp != error_mark_node); + pp = &DECL_CHAIN (*pp); + } + *pp = NULL_TREE; + DECL_ARGUMENTS (function) = params; + return true; +} + +// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS, +// FUNCTION_DECLS, and VARIABLE_DECLS declared globally, as well as +// emit early debugging information. + +void +Gcc_backend::write_global_definitions ( + const std::vector<tree> &type_decls, const std::vector<tree> &constant_decls, + const std::vector<tree> &function_decls, + const std::vector<Bvariable *> &variable_decls) +{ + size_t count_definitions = type_decls.size () + constant_decls.size () + + function_decls.size () + variable_decls.size (); + + tree *defs = new tree[count_definitions]; + + // Convert all non-erroneous declarations into Gimple form. + size_t i = 0; + for (std::vector<Bvariable *>::const_iterator p = variable_decls.begin (); + p != variable_decls.end (); ++p) + { + tree v = (*p)->get_decl (); + if (v != error_mark_node) + { + defs[i] = v; + rust_preserve_from_gc (defs[i]); + ++i; + } + } + + for (std::vector<tree>::const_iterator p = type_decls.begin (); + p != type_decls.end (); ++p) + { + tree type_tree = (*p); + if (type_tree != error_mark_node && IS_TYPE_OR_DECL_P (type_tree)) + { + defs[i] = TYPE_NAME (type_tree); + gcc_assert (defs[i] != NULL); + rust_preserve_from_gc (defs[i]); + ++i; + } + } + for (std::vector<tree>::const_iterator p = constant_decls.begin (); + p != constant_decls.end (); ++p) + { + if ((*p) != error_mark_node) + { + defs[i] = (*p); + rust_preserve_from_gc (defs[i]); + ++i; + } + } + for (std::vector<tree>::const_iterator p = function_decls.begin (); + p != function_decls.end (); ++p) + { + tree decl = (*p); + if (decl != error_mark_node) + { + rust_preserve_from_gc (decl); + if (DECL_STRUCT_FUNCTION (decl) == NULL) + allocate_struct_function (decl, false); + dump_function (TDI_original, decl); + cgraph_node::finalize_function (decl, true); + + defs[i] = decl; + ++i; + } + } + + // Pass everything back to the middle-end. + + wrapup_global_declarations (defs, i); + + delete[] defs; +} + +void +Gcc_backend::write_export_data (const char *bytes, unsigned int size) +{ + rust_write_export_data (bytes, size); +} + +// Return the backend generator. + +Backend * +rust_get_backend () +{ + return new Gcc_backend (); +} |