aboutsummaryrefslogtreecommitdiff
path: root/gcc/rust/rust-gcc.cc
diff options
context:
space:
mode:
authorPhilip Herron <philip.herron@embecosm.com>2022-10-21 14:01:04 +0200
committerArthur Cohen <arthur.cohen@embecosm.com>2022-12-13 14:00:07 +0100
commit15f04af347e3b65f436808077cbac4fa566019f9 (patch)
tree32ec5a4c2ca65044848cb37137c1074ed63f5401 /gcc/rust/rust-gcc.cc
parent509e4c32c6a80ede6c6dda0f4cfc96f94d24c4d6 (diff)
downloadgcc-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.cc2718
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> &parameters,
+ const std::vector<typed_identifier> &results,
+ tree result_struct, Location)
+{
+ tree args = NULL_TREE;
+ tree *pp = &args;
+ if (receiver.type != NULL_TREE)
+ {
+ tree t = receiver.type;
+ if (t == error_mark_node)
+ return error_mark_node;
+ *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+ pp = &TREE_CHAIN (*pp);
+ }
+
+ for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+ p != parameters.end (); ++p)
+ {
+ tree t = p->type;
+ if (t == error_mark_node)
+ return error_mark_node;
+ *pp = tree_cons (NULL_TREE, t, NULL_TREE);
+ pp = &TREE_CHAIN (*pp);
+ }
+
+ // Varargs is handled entirely at the Rust level. When converted to
+ // GENERIC functions are not varargs.
+ *pp = void_list_node;
+
+ tree result;
+ if (results.empty ())
+ result = void_type_node;
+ else if (results.size () == 1)
+ result = results.front ().type;
+ else
+ {
+ gcc_assert (result_struct != NULL);
+ result = result_struct;
+ }
+ if (result == error_mark_node)
+ return error_mark_node;
+
+ // The libffi library cannot represent a zero-sized object. To
+ // avoid causing confusion on 32-bit SPARC, we treat a function that
+ // returns a zero-sized value as returning void. That should do no
+ // harm since there is no actual value to be returned. See
+ // https://gcc.gnu.org/PR72814 for details.
+ if (result != void_type_node && int_size_in_bytes (result) == 0)
+ result = void_type_node;
+
+ tree fntype = build_function_type (result, args);
+ if (fntype == error_mark_node)
+ return error_mark_node;
+
+ return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_type_varadic (
+ const typed_identifier &receiver,
+ const std::vector<typed_identifier> &parameters,
+ const std::vector<typed_identifier> &results, tree result_struct, Location)
+{
+ size_t n = parameters.size () + (receiver.type != NULL_TREE ? 1 : 0);
+ tree *args = XALLOCAVEC (tree, n);
+ size_t offs = 0;
+
+ if (receiver.type != NULL_TREE)
+ {
+ tree t = receiver.type;
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ args[offs++] = t;
+ }
+
+ for (std::vector<typed_identifier>::const_iterator p = parameters.begin ();
+ p != parameters.end (); ++p)
+ {
+ tree t = p->type;
+ if (t == error_mark_node)
+ return error_mark_node;
+ args[offs++] = t;
+ }
+
+ tree result;
+ if (results.empty ())
+ result = void_type_node;
+ else if (results.size () == 1)
+ result = results.front ().type;
+ else
+ {
+ gcc_assert (result_struct != NULL_TREE);
+ result = result_struct;
+ }
+ if (result == error_mark_node)
+ return error_mark_node;
+
+ // The libffi library cannot represent a zero-sized object. To
+ // avoid causing confusion on 32-bit SPARC, we treat a function that
+ // returns a zero-sized value as returning void. That should do no
+ // harm since there is no actual value to be returned. See
+ // https://gcc.gnu.org/PR72814 for details.
+ if (result != void_type_node && int_size_in_bytes (result) == 0)
+ result = void_type_node;
+
+ tree fntype = build_varargs_function_type_array (result, n, args);
+ if (fntype == error_mark_node)
+ return error_mark_node;
+
+ return build_pointer_type (fntype);
+}
+
+tree
+Gcc_backend::function_ptr_type (tree result_type,
+ const std::vector<tree> &parameters,
+ Location /* locus */)
+{
+ tree args = NULL_TREE;
+ tree *pp = &args;
+
+ for (auto &param : parameters)
+ {
+ if (param == error_mark_node)
+ return error_mark_node;
+
+ *pp = tree_cons (NULL_TREE, param, NULL_TREE);
+ pp = &TREE_CHAIN (*pp);
+ }
+
+ *pp = void_list_node;
+
+ tree result = result_type;
+ if (result != void_type_node && int_size_in_bytes (result) == 0)
+ result = void_type_node;
+
+ tree fntype = build_function_type (result, args);
+ if (fntype == error_mark_node)
+ return error_mark_node;
+
+ return build_pointer_type (fntype);
+}
+
+// Make a struct type.
+
+tree
+Gcc_backend::struct_type (const std::vector<typed_identifier> &fields)
+{
+ return this->fill_in_fields (make_node (RECORD_TYPE), fields);
+}
+
+// Make a union type.
+
+tree
+Gcc_backend::union_type (const std::vector<typed_identifier> &fields)
+{
+ return this->fill_in_fields (make_node (UNION_TYPE), fields);
+}
+
+// Fill in the fields of a struct or union type.
+
+tree
+Gcc_backend::fill_in_fields (tree fill,
+ const std::vector<typed_identifier> &fields)
+{
+ tree field_trees = NULL_TREE;
+ tree *pp = &field_trees;
+ for (std::vector<typed_identifier>::const_iterator p = fields.begin ();
+ p != fields.end (); ++p)
+ {
+ tree name_tree = get_identifier_from_string (p->name);
+ tree type_tree = p->type;
+ if (type_tree == error_mark_node)
+ return error_mark_node;
+ tree field = build_decl (p->location.gcc_location (), FIELD_DECL,
+ name_tree, type_tree);
+ DECL_CONTEXT (field) = fill;
+ *pp = field;
+ pp = &DECL_CHAIN (field);
+ }
+ TYPE_FIELDS (fill) = field_trees;
+ layout_type (fill);
+
+ // Because Rust permits converting between named struct types and
+ // equivalent struct types, for which we use VIEW_CONVERT_EXPR, and
+ // because we don't try to maintain TYPE_CANONICAL for struct types,
+ // we need to tell the middle-end to use structural equality.
+ SET_TYPE_STRUCTURAL_EQUALITY (fill);
+
+ return fill;
+}
+
+// Make an array type.
+
+tree
+Gcc_backend::array_type (tree element_type, tree length)
+{
+ return this->fill_in_array (make_node (ARRAY_TYPE), element_type, length);
+}
+
+// Fill in an array type.
+
+tree
+Gcc_backend::fill_in_array (tree fill, tree element_type, tree length_tree)
+{
+ if (element_type == error_mark_node || length_tree == error_mark_node)
+ return error_mark_node;
+
+ gcc_assert (TYPE_SIZE (element_type) != NULL_TREE);
+
+ length_tree = fold_convert (sizetype, length_tree);
+
+ // build_index_type takes the maximum index, which is one less than
+ // the length.
+ tree index_type_tree = build_index_type (
+ fold_build2 (MINUS_EXPR, sizetype, length_tree, size_one_node));
+
+ TREE_TYPE (fill) = element_type;
+ TYPE_DOMAIN (fill) = index_type_tree;
+ TYPE_ADDR_SPACE (fill) = TYPE_ADDR_SPACE (element_type);
+ layout_type (fill);
+
+ if (TYPE_STRUCTURAL_EQUALITY_P (element_type))
+ SET_TYPE_STRUCTURAL_EQUALITY (fill);
+ else if (TYPE_CANONICAL (element_type) != element_type
+ || TYPE_CANONICAL (index_type_tree) != index_type_tree)
+ TYPE_CANONICAL (fill) = build_array_type (TYPE_CANONICAL (element_type),
+ TYPE_CANONICAL (index_type_tree));
+
+ return fill;
+}
+
+// Return a named version of a type.
+
+tree
+Gcc_backend::named_type (const std::string &name, tree type, Location location)
+{
+ if (type == error_mark_node)
+ return error_mark_node;
+
+ // The middle-end expects a basic type to have a name. In Rust every
+ // basic type will have a name. The first time we see a basic type,
+ // give it whatever Rust name we have at this point.
+ if (TYPE_NAME (type) == NULL_TREE
+ && location.gcc_location () == BUILTINS_LOCATION
+ && (TREE_CODE (type) == INTEGER_TYPE || TREE_CODE (type) == REAL_TYPE
+ || TREE_CODE (type) == COMPLEX_TYPE
+ || TREE_CODE (type) == BOOLEAN_TYPE))
+ {
+ tree decl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
+ get_identifier_from_string (name), type);
+ TYPE_NAME (type) = decl;
+ return type;
+ }
+
+ tree copy = build_variant_type_copy (type);
+ tree decl = build_decl (location.gcc_location (), TYPE_DECL,
+ get_identifier_from_string (name), copy);
+ DECL_ORIGINAL_TYPE (decl) = type;
+ TYPE_NAME (copy) = decl;
+ return copy;
+}
+
+// Return the size of a type.
+
+int64_t
+Gcc_backend::type_size (tree t)
+{
+ if (t == error_mark_node)
+ return 1;
+ if (t == void_type_node)
+ return 0;
+ t = TYPE_SIZE_UNIT (t);
+ gcc_assert (tree_fits_uhwi_p (t));
+ unsigned HOST_WIDE_INT val_wide = TREE_INT_CST_LOW (t);
+ int64_t ret = static_cast<int64_t> (val_wide);
+ if (ret < 0 || static_cast<unsigned HOST_WIDE_INT> (ret) != val_wide)
+ return -1;
+ return ret;
+}
+
+// Return the alignment of a type.
+
+int64_t
+Gcc_backend::type_alignment (tree t)
+{
+ if (t == error_mark_node)
+ return 1;
+ return TYPE_ALIGN_UNIT (t);
+}
+
+// Return the alignment of a struct field of type BTYPE.
+
+int64_t
+Gcc_backend::type_field_alignment (tree t)
+{
+ if (t == error_mark_node)
+ return 1;
+ return rust_field_alignment (t);
+}
+
+// Return the offset of a field in a struct.
+
+int64_t
+Gcc_backend::type_field_offset (tree struct_tree, size_t index)
+{
+ if (struct_tree == error_mark_node)
+ return 0;
+ gcc_assert (TREE_CODE (struct_tree) == RECORD_TYPE);
+ tree field = TYPE_FIELDS (struct_tree);
+ for (; index > 0; --index)
+ {
+ field = DECL_CHAIN (field);
+ gcc_assert (field != NULL_TREE);
+ }
+ HOST_WIDE_INT offset_wide = int_byte_position (field);
+ int64_t ret = static_cast<int64_t> (offset_wide);
+ gcc_assert (ret == offset_wide);
+ return ret;
+}
+
+// Return the zero value for a type.
+
+tree
+Gcc_backend::zero_expression (tree t)
+{
+ tree ret;
+ if (t == error_mark_node)
+ ret = error_mark_node;
+ else
+ ret = build_zero_cst (t);
+ return ret;
+}
+
+// An expression that references a variable.
+
+tree
+Gcc_backend::var_expression (Bvariable *var, Location location)
+{
+ return var->get_tree (location);
+}
+
+// Return a typed value as a constant integer.
+// This function does not release the memory of @val
+
+tree
+Gcc_backend::integer_constant_expression (tree t, mpz_t val)
+{
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ tree ret = wide_int_to_tree (t, wi::from_mpz (t, val, true));
+ return ret;
+}
+
+// Return a typed value as a constant floating-point number.
+
+tree
+Gcc_backend::float_constant_expression (tree t, mpfr_t val)
+{
+ tree ret;
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ REAL_VALUE_TYPE r1;
+ real_from_mpfr (&r1, val, t, GMP_RNDN);
+ REAL_VALUE_TYPE r2;
+ real_convert (&r2, TYPE_MODE (t), &r1);
+ ret = build_real (t, r2);
+ return ret;
+}
+
+// Return a typed real and imaginary value as a constant complex number.
+
+tree
+Gcc_backend::complex_constant_expression (tree t, mpc_t val)
+{
+ tree ret;
+ if (t == error_mark_node)
+ return error_mark_node;
+
+ REAL_VALUE_TYPE r1;
+ real_from_mpfr (&r1, mpc_realref (val), TREE_TYPE (t), GMP_RNDN);
+ REAL_VALUE_TYPE r2;
+ real_convert (&r2, TYPE_MODE (TREE_TYPE (t)), &r1);
+
+ REAL_VALUE_TYPE r3;
+ real_from_mpfr (&r3, mpc_imagref (val), TREE_TYPE (t), GMP_RNDN);
+ REAL_VALUE_TYPE r4;
+ real_convert (&r4, TYPE_MODE (TREE_TYPE (t)), &r3);
+
+ ret = build_complex (t, build_real (TREE_TYPE (t), r2),
+ build_real (TREE_TYPE (t), r4));
+ return ret;
+}
+
+// Make a constant string expression.
+
+tree
+Gcc_backend::string_constant_expression (const std::string &val)
+{
+ tree index_type = build_index_type (size_int (val.length ()));
+ tree const_char_type = build_qualified_type (char_type_node, TYPE_QUAL_CONST);
+ tree string_type = build_array_type (const_char_type, index_type);
+ TYPE_STRING_FLAG (string_type) = 1;
+ tree string_val = build_string (val.length (), val.data ());
+ TREE_TYPE (string_val) = string_type;
+
+ return string_val;
+}
+
+tree
+Gcc_backend::wchar_constant_expression (wchar_t c)
+{
+ return build_int_cst (this->wchar_type (), c);
+}
+
+tree
+Gcc_backend::char_constant_expression (char c)
+{
+ return build_int_cst (this->char_type (), c);
+}
+
+// Make a constant boolean expression.
+
+tree
+Gcc_backend::boolean_constant_expression (bool val)
+{
+ return val ? boolean_true_node : boolean_false_node;
+}
+
+// Return the real part of a complex expression.
+
+tree
+Gcc_backend::real_part_expression (tree complex_tree, Location location)
+{
+ if (complex_tree == error_mark_node)
+ return error_mark_node;
+ gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+ tree ret
+ = fold_build1_loc (location.gcc_location (), REALPART_EXPR,
+ TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+ return ret;
+}
+
+// Return the imaginary part of a complex expression.
+
+tree
+Gcc_backend::imag_part_expression (tree complex_tree, Location location)
+{
+ if (complex_tree == error_mark_node)
+ return error_mark_node;
+ gcc_assert (COMPLEX_FLOAT_TYPE_P (TREE_TYPE (complex_tree)));
+ tree ret
+ = fold_build1_loc (location.gcc_location (), IMAGPART_EXPR,
+ TREE_TYPE (TREE_TYPE (complex_tree)), complex_tree);
+ return ret;
+}
+
+// Make a complex expression given its real and imaginary parts.
+
+tree
+Gcc_backend::complex_expression (tree real_tree, tree imag_tree,
+ Location location)
+{
+ if (real_tree == error_mark_node || imag_tree == error_mark_node)
+ return error_mark_node;
+ gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (real_tree))
+ == TYPE_MAIN_VARIANT (TREE_TYPE (imag_tree)));
+ gcc_assert (SCALAR_FLOAT_TYPE_P (TREE_TYPE (real_tree)));
+ tree ret = fold_build2_loc (location.gcc_location (), COMPLEX_EXPR,
+ build_complex_type (TREE_TYPE (real_tree)),
+ real_tree, imag_tree);
+ return ret;
+}
+
+// An expression that converts an expression to a different type.
+
+tree
+Gcc_backend::convert_expression (tree type_tree, tree expr_tree,
+ Location location)
+{
+ if (type_tree == error_mark_node || expr_tree == error_mark_node
+ || TREE_TYPE (expr_tree) == error_mark_node)
+ return error_mark_node;
+
+ tree ret;
+ if (this->type_size (type_tree) == 0
+ || TREE_TYPE (expr_tree) == void_type_node)
+ {
+ // Do not convert zero-sized types.
+ ret = expr_tree;
+ }
+ else if (TREE_CODE (type_tree) == INTEGER_TYPE)
+ ret = convert_to_integer (type_tree, expr_tree);
+ else if (TREE_CODE (type_tree) == REAL_TYPE)
+ ret = convert_to_real (type_tree, expr_tree);
+ else if (TREE_CODE (type_tree) == COMPLEX_TYPE)
+ ret = convert_to_complex (type_tree, expr_tree);
+ else if (TREE_CODE (type_tree) == POINTER_TYPE
+ && TREE_CODE (TREE_TYPE (expr_tree)) == INTEGER_TYPE)
+ ret = convert_to_pointer (type_tree, expr_tree);
+ else if (TREE_CODE (type_tree) == RECORD_TYPE
+ || TREE_CODE (type_tree) == ARRAY_TYPE)
+ ret = fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+ type_tree, expr_tree);
+ else
+ ret = fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+
+ return ret;
+}
+
+// Return an expression for the field at INDEX in BSTRUCT.
+
+tree
+Gcc_backend::struct_field_expression (tree struct_tree, size_t index,
+ Location location)
+{
+ if (struct_tree == error_mark_node
+ || TREE_TYPE (struct_tree) == error_mark_node)
+ return error_mark_node;
+ gcc_assert (TREE_CODE (TREE_TYPE (struct_tree)) == RECORD_TYPE
+ || TREE_CODE (TREE_TYPE (struct_tree)) == UNION_TYPE);
+ tree field = TYPE_FIELDS (TREE_TYPE (struct_tree));
+ if (field == NULL_TREE)
+ {
+ // This can happen for a type which refers to itself indirectly
+ // and then turns out to be erroneous.
+ return error_mark_node;
+ }
+ for (unsigned int i = index; i > 0; --i)
+ {
+ field = DECL_CHAIN (field);
+ gcc_assert (field != NULL_TREE);
+ }
+ if (TREE_TYPE (field) == error_mark_node)
+ return error_mark_node;
+ tree ret = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+ TREE_TYPE (field), struct_tree, field, NULL_TREE);
+ if (TREE_CONSTANT (struct_tree))
+ TREE_CONSTANT (ret) = 1;
+ return ret;
+}
+
+// Return an expression that executes BSTAT before BEXPR.
+
+tree
+Gcc_backend::compound_expression (tree stat, tree expr, Location location)
+{
+ if (stat == error_mark_node || expr == error_mark_node)
+ return error_mark_node;
+ tree ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+ TREE_TYPE (expr), stat, expr);
+ return ret;
+}
+
+// Return an expression that executes THEN_EXPR if CONDITION is true, or
+// ELSE_EXPR otherwise.
+
+tree
+Gcc_backend::conditional_expression (tree, tree type_tree, tree cond_expr,
+ tree then_expr, tree else_expr,
+ Location location)
+{
+ if (type_tree == error_mark_node || cond_expr == error_mark_node
+ || then_expr == error_mark_node || else_expr == error_mark_node)
+ return error_mark_node;
+ tree ret = build3_loc (location.gcc_location (), COND_EXPR, type_tree,
+ cond_expr, then_expr, else_expr);
+ return ret;
+}
+
+/* Helper function that converts rust operators to equivalent GCC tree_code.
+ Note that CompoundAssignmentOperator don't get their corresponding tree_code,
+ because they get compiled away when we lower AST to HIR. */
+static enum tree_code
+operator_to_tree_code (NegationOperator op)
+{
+ switch (op)
+ {
+ case NegationOperator::NEGATE:
+ return NEGATE_EXPR;
+ case NegationOperator::NOT:
+ return TRUTH_NOT_EXPR;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Note that GCC tree code distinguishes floating point division and integer
+ division. These two types of division are represented as the same rust
+ operator, and can only be distinguished via context(i.e. the TREE_TYPE of the
+ operands). */
+static enum tree_code
+operator_to_tree_code (ArithmeticOrLogicalOperator op, bool floating_point)
+{
+ switch (op)
+ {
+ case ArithmeticOrLogicalOperator::ADD:
+ return PLUS_EXPR;
+ case ArithmeticOrLogicalOperator::SUBTRACT:
+ return MINUS_EXPR;
+ case ArithmeticOrLogicalOperator::MULTIPLY:
+ return MULT_EXPR;
+ case ArithmeticOrLogicalOperator::DIVIDE:
+ if (floating_point)
+ return RDIV_EXPR;
+ else
+ return TRUNC_DIV_EXPR;
+ case ArithmeticOrLogicalOperator::MODULUS:
+ return TRUNC_MOD_EXPR;
+ case ArithmeticOrLogicalOperator::BITWISE_AND:
+ return BIT_AND_EXPR;
+ case ArithmeticOrLogicalOperator::BITWISE_OR:
+ return BIT_IOR_EXPR;
+ case ArithmeticOrLogicalOperator::BITWISE_XOR:
+ return BIT_XOR_EXPR;
+ case ArithmeticOrLogicalOperator::LEFT_SHIFT:
+ return LSHIFT_EXPR;
+ case ArithmeticOrLogicalOperator::RIGHT_SHIFT:
+ return RSHIFT_EXPR;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+static enum tree_code
+operator_to_tree_code (ComparisonOperator op)
+{
+ switch (op)
+ {
+ case ComparisonOperator::EQUAL:
+ return EQ_EXPR;
+ case ComparisonOperator::NOT_EQUAL:
+ return NE_EXPR;
+ case ComparisonOperator::GREATER_THAN:
+ return GT_EXPR;
+ case ComparisonOperator::LESS_THAN:
+ return LT_EXPR;
+ case ComparisonOperator::GREATER_OR_EQUAL:
+ return GE_EXPR;
+ case ComparisonOperator::LESS_OR_EQUAL:
+ return LE_EXPR;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+static enum tree_code
+operator_to_tree_code (LazyBooleanOperator op)
+{
+ switch (op)
+ {
+ case LazyBooleanOperator::LOGICAL_OR:
+ return TRUTH_ORIF_EXPR;
+ case LazyBooleanOperator::LOGICAL_AND:
+ return TRUTH_ANDIF_EXPR;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Helper function for deciding if a tree is a floating point node. */
+bool
+is_floating_point (tree t)
+{
+ auto tree_type = TREE_CODE (TREE_TYPE (t));
+ return tree_type == REAL_TYPE || tree_type == COMPLEX_TYPE;
+}
+
+// Return an expression for the negation operation OP EXPR.
+tree
+Gcc_backend::negation_expression (NegationOperator op, tree expr_tree,
+ Location location)
+{
+ /* Check if the expression is an error, in which case we return an error
+ expression. */
+ if (expr_tree == error_mark_node || TREE_TYPE (expr_tree) == error_mark_node)
+ return error_mark_node;
+
+ /* For negation operators, the resulting type should be the same as its
+ operand. */
+ auto tree_type = TREE_TYPE (expr_tree);
+ auto original_type = tree_type;
+ auto tree_code = operator_to_tree_code (op);
+
+ /* For floating point operations we may need to extend the precision of type.
+ For example, a 64-bit machine may not support operations on float32. */
+ bool floating_point = is_floating_point (expr_tree);
+ auto extended_type = NULL_TREE;
+ if (floating_point)
+ {
+ extended_type = excess_precision_type (tree_type);
+ if (extended_type != NULL_TREE)
+ {
+ expr_tree = convert (extended_type, expr_tree);
+ tree_type = extended_type;
+ }
+ }
+
+ /* Construct a new tree and build an expression from it. */
+ auto new_tree = fold_build1_loc (location.gcc_location (), tree_code,
+ tree_type, expr_tree);
+ if (floating_point && extended_type != NULL_TREE)
+ new_tree = convert (original_type, expr_tree);
+ return new_tree;
+}
+
+// Return an expression for the arithmetic or logical operation LEFT OP RIGHT.
+tree
+Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
+ tree left_tree, tree right_tree,
+ Location location)
+{
+ /* Check if either expression is an error, in which case we return an error
+ expression. */
+ if (left_tree == error_mark_node || right_tree == error_mark_node)
+ return error_mark_node;
+
+ /* We need to determine if we're doing floating point arithmetics of integer
+ arithmetics. */
+ bool floating_point = is_floating_point (left_tree);
+
+ /* For arithmetic or logical operators, the resulting type should be the same
+ as the lhs operand. */
+ auto tree_type = TREE_TYPE (left_tree);
+ auto original_type = tree_type;
+ auto tree_code = operator_to_tree_code (op, floating_point);
+
+ /* For floating point operations we may need to extend the precision of type.
+ For example, a 64-bit machine may not support operations on float32. */
+ auto extended_type = NULL_TREE;
+ if (floating_point)
+ {
+ extended_type = excess_precision_type (tree_type);
+ if (extended_type != NULL_TREE)
+ {
+ left_tree = convert (extended_type, left_tree);
+ right_tree = convert (extended_type, right_tree);
+ tree_type = extended_type;
+ }
+ }
+
+ /* Construct a new tree and build an expression from it. */
+ auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+ tree_type, left_tree, right_tree);
+ TREE_CONSTANT (new_tree)
+ = TREE_CONSTANT (left_tree) && TREE_CONSTANT (right_tree);
+
+ if (floating_point && extended_type != NULL_TREE)
+ new_tree = convert (original_type, new_tree);
+ return new_tree;
+}
+
+// Return an expression for the comparison operation LEFT OP RIGHT.
+tree
+Gcc_backend::comparison_expression (ComparisonOperator op, tree left_tree,
+ tree right_tree, Location location)
+{
+ /* Check if either expression is an error, in which case we return an error
+ expression. */
+ if (left_tree == error_mark_node || right_tree == error_mark_node)
+ return error_mark_node;
+
+ /* For comparison operators, the resulting type should be boolean. */
+ auto tree_type = boolean_type_node;
+ auto tree_code = operator_to_tree_code (op);
+
+ /* Construct a new tree and build an expression from it. */
+ auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+ tree_type, left_tree, right_tree);
+ return new_tree;
+}
+
+// Return an expression for the lazy boolean operation LEFT OP RIGHT.
+tree
+Gcc_backend::lazy_boolean_expression (LazyBooleanOperator op, tree left_tree,
+ tree right_tree, Location location)
+{
+ /* Check if either expression is an error, in which case we return an error
+ expression. */
+ if (left_tree == error_mark_node || right_tree == error_mark_node)
+ return error_mark_node;
+
+ /* For lazy boolean operators, the resulting type should be the same as the
+ rhs operand. */
+ auto tree_type = TREE_TYPE (right_tree);
+ auto tree_code = operator_to_tree_code (op);
+
+ /* Construct a new tree and build an expression from it. */
+ auto new_tree = fold_build2_loc (location.gcc_location (), tree_code,
+ tree_type, left_tree, right_tree);
+ return new_tree;
+}
+
+// Return an expression that constructs BTYPE with VALS.
+
+tree
+Gcc_backend::constructor_expression (tree type_tree, bool is_variant,
+ const std::vector<tree> &vals,
+ int union_index, Location location)
+{
+ if (type_tree == error_mark_node)
+ return error_mark_node;
+
+ vec<constructor_elt, va_gc> *init;
+ vec_alloc (init, vals.size ());
+
+ tree sink = NULL_TREE;
+ bool is_constant = true;
+ tree field = TYPE_FIELDS (type_tree);
+
+ if (is_variant)
+ {
+ gcc_assert (union_index != -1);
+ gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+
+ for (int i = 0; i < union_index; i++)
+ {
+ gcc_assert (field != NULL_TREE);
+ field = DECL_CHAIN (field);
+ }
+
+ tree nested_ctor
+ = constructor_expression (TREE_TYPE (field), false, vals, -1, location);
+
+ constructor_elt empty = {NULL, NULL};
+ constructor_elt *elt = init->quick_push (empty);
+ elt->index = field;
+ elt->value
+ = this->convert_tree (TREE_TYPE (field), nested_ctor, location);
+ if (!TREE_CONSTANT (elt->value))
+ is_constant = false;
+ }
+ else
+ {
+ if (union_index != -1)
+ {
+ gcc_assert (TREE_CODE (type_tree) == UNION_TYPE);
+ tree val = vals.front ();
+ for (int i = 0; i < union_index; i++)
+ {
+ gcc_assert (field != NULL_TREE);
+ field = DECL_CHAIN (field);
+ }
+ if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+ || TREE_TYPE (val) == error_mark_node)
+ return error_mark_node;
+
+ if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+ {
+ // GIMPLE cannot represent indices of zero-sized types so
+ // trying to construct a map with zero-sized keys might lead
+ // to errors. Instead, we evaluate each expression that
+ // would have been added as a map element for its
+ // side-effects and construct an empty map.
+ append_to_statement_list (val, &sink);
+ }
+ else
+ {
+ constructor_elt empty = {NULL, NULL};
+ constructor_elt *elt = init->quick_push (empty);
+ elt->index = field;
+ elt->value
+ = this->convert_tree (TREE_TYPE (field), val, location);
+ if (!TREE_CONSTANT (elt->value))
+ is_constant = false;
+ }
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (type_tree) == RECORD_TYPE);
+ for (std::vector<tree>::const_iterator p = vals.begin ();
+ p != vals.end (); ++p, field = DECL_CHAIN (field))
+ {
+ gcc_assert (field != NULL_TREE);
+ tree val = (*p);
+ if (TREE_TYPE (field) == error_mark_node || val == error_mark_node
+ || TREE_TYPE (val) == error_mark_node)
+ return error_mark_node;
+
+ if (int_size_in_bytes (TREE_TYPE (field)) == 0)
+ {
+ // GIMPLE cannot represent indices of zero-sized types so
+ // trying to construct a map with zero-sized keys might lead
+ // to errors. Instead, we evaluate each expression that
+ // would have been added as a map element for its
+ // side-effects and construct an empty map.
+ append_to_statement_list (val, &sink);
+ continue;
+ }
+
+ constructor_elt empty = {NULL, NULL};
+ constructor_elt *elt = init->quick_push (empty);
+ elt->index = field;
+ elt->value
+ = this->convert_tree (TREE_TYPE (field), val, location);
+ if (!TREE_CONSTANT (elt->value))
+ is_constant = false;
+ }
+ gcc_assert (field == NULL_TREE);
+ }
+ }
+
+ tree ret = build_constructor (type_tree, init);
+ if (is_constant)
+ TREE_CONSTANT (ret) = 1;
+ if (sink != NULL_TREE)
+ ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+ sink, ret);
+ return ret;
+}
+
+tree
+Gcc_backend::array_constructor_expression (
+ tree type_tree, const std::vector<unsigned long> &indexes,
+ const std::vector<tree> &vals, Location location)
+{
+ if (type_tree == error_mark_node)
+ return error_mark_node;
+
+ gcc_assert (indexes.size () == vals.size ());
+
+ tree element_type = TREE_TYPE (type_tree);
+ HOST_WIDE_INT element_size = int_size_in_bytes (element_type);
+ vec<constructor_elt, va_gc> *init;
+ vec_alloc (init, element_size == 0 ? 0 : vals.size ());
+
+ tree sink = NULL_TREE;
+ bool is_constant = true;
+ for (size_t i = 0; i < vals.size (); ++i)
+ {
+ tree index = size_int (indexes[i]);
+ tree val = vals[i];
+
+ if (index == error_mark_node || val == error_mark_node)
+ return error_mark_node;
+
+ if (element_size == 0)
+ {
+ // GIMPLE cannot represent arrays of zero-sized types so trying
+ // to construct an array of zero-sized values might lead to errors.
+ // Instead, we evaluate each expression that would have been added as
+ // an array value for its side-effects and construct an empty array.
+ append_to_statement_list (val, &sink);
+ continue;
+ }
+
+ if (!TREE_CONSTANT (val))
+ is_constant = false;
+
+ constructor_elt empty = {NULL, NULL};
+ constructor_elt *elt = init->quick_push (empty);
+ elt->index = index;
+ elt->value = val;
+ }
+
+ tree ret = build_constructor (type_tree, init);
+ if (is_constant)
+ TREE_CONSTANT (ret) = 1;
+ if (sink != NULL_TREE)
+ ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR, type_tree,
+ sink, ret);
+ return ret;
+}
+
+// Build insns to create an array, initialize all elements of the array to
+// value, and return it
+tree
+Gcc_backend::array_initializer (tree fndecl, tree block, tree array_type,
+ tree length, tree value, tree *tmp,
+ Location locus)
+{
+ std::vector<tree> stmts;
+
+ // Temporary array we initialize with the desired value.
+ tree t = NULL_TREE;
+ Bvariable *tmp_array = this->temporary_variable (fndecl, block, array_type,
+ NULL_TREE, true, locus, &t);
+ tree arr = tmp_array->get_tree (locus);
+ stmts.push_back (t);
+
+ // Temporary for the array length used for initialization loop guard.
+ Bvariable *tmp_len = this->temporary_variable (fndecl, block, size_type_node,
+ length, true, locus, &t);
+ tree len = tmp_len->get_tree (locus);
+ stmts.push_back (t);
+
+ // Temporary variable for pointer used to initialize elements.
+ tree ptr_type = this->pointer_type (TREE_TYPE (array_type));
+ tree ptr_init
+ = build1_loc (locus.gcc_location (), ADDR_EXPR, ptr_type,
+ this->array_index_expression (arr, integer_zero_node, locus));
+ Bvariable *tmp_ptr = this->temporary_variable (fndecl, block, ptr_type,
+ ptr_init, false, locus, &t);
+ tree ptr = tmp_ptr->get_tree (locus);
+ stmts.push_back (t);
+
+ // push statement list for the loop
+ std::vector<tree> loop_stmts;
+
+ // Loop exit condition:
+ // if (length == 0) break;
+ t = this->comparison_expression (ComparisonOperator::EQUAL, len,
+ this->zero_expression (TREE_TYPE (len)),
+ locus);
+
+ t = this->exit_expression (t, locus);
+ loop_stmts.push_back (t);
+
+ // Assign value to the current pointer position
+ // *ptr = value;
+ t = this->assignment_statement (build_fold_indirect_ref (ptr), value, locus);
+ loop_stmts.push_back (t);
+
+ // Move pointer to next element
+ // ptr++;
+ tree size = TYPE_SIZE_UNIT (TREE_TYPE (ptr_type));
+ t = build2 (POSTINCREMENT_EXPR, ptr_type, ptr, convert (ptr_type, size));
+ loop_stmts.push_back (t);
+
+ // Decrement loop counter.
+ // length--;
+ t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (len), len,
+ convert (TREE_TYPE (len), integer_one_node));
+ loop_stmts.push_back (t);
+
+ // pop statments and finish loop
+ tree loop_body = this->statement_list (loop_stmts);
+ stmts.push_back (this->loop_expression (loop_body, locus));
+
+ // Return the temporary in the provided pointer and the statement list which
+ // initializes it.
+ *tmp = tmp_array->get_tree (locus);
+ return this->statement_list (stmts);
+}
+
+// Return an expression representing ARRAY[INDEX]
+
+tree
+Gcc_backend::array_index_expression (tree array_tree, tree index_tree,
+ Location location)
+{
+ if (array_tree == error_mark_node || TREE_TYPE (array_tree) == error_mark_node
+ || index_tree == error_mark_node)
+ return error_mark_node;
+
+ // A function call that returns a zero sized object will have been
+ // changed to return void. If we see void here, assume we are
+ // dealing with a zero sized type and just evaluate the operands.
+ tree ret;
+ if (TREE_TYPE (array_tree) != void_type_node)
+ ret = build4_loc (location.gcc_location (), ARRAY_REF,
+ TREE_TYPE (TREE_TYPE (array_tree)), array_tree,
+ index_tree, NULL_TREE, NULL_TREE);
+ else
+ ret = fold_build2_loc (location.gcc_location (), COMPOUND_EXPR,
+ void_type_node, array_tree, index_tree);
+
+ return ret;
+}
+
+// Create an expression for a call to FN_EXPR with FN_ARGS.
+tree
+Gcc_backend::call_expression (tree fn, const std::vector<tree> &fn_args,
+ tree chain_expr, Location location)
+{
+ if (fn == error_mark_node || TREE_TYPE (fn) == error_mark_node)
+ return error_mark_node;
+
+ gcc_assert (FUNCTION_POINTER_TYPE_P (TREE_TYPE (fn)));
+ tree rettype = TREE_TYPE (TREE_TYPE (TREE_TYPE (fn)));
+
+ size_t nargs = fn_args.size ();
+ tree *args = nargs == 0 ? NULL : new tree[nargs];
+ for (size_t i = 0; i < nargs; ++i)
+ {
+ args[i] = fn_args.at (i);
+ }
+
+ tree fndecl = fn;
+ if (TREE_CODE (fndecl) == ADDR_EXPR)
+ fndecl = TREE_OPERAND (fndecl, 0);
+
+ // This is to support builtin math functions when using 80387 math.
+ tree excess_type = NULL_TREE;
+ if (optimize && TREE_CODE (fndecl) == FUNCTION_DECL
+ && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL)
+ && DECL_IS_UNDECLARED_BUILTIN (fndecl) && nargs > 0
+ && ((SCALAR_FLOAT_TYPE_P (rettype)
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[0])))
+ || (COMPLEX_FLOAT_TYPE_P (rettype)
+ && COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[0])))))
+ {
+ excess_type = excess_precision_type (TREE_TYPE (args[0]));
+ if (excess_type != NULL_TREE)
+ {
+ tree excess_fndecl
+ = mathfn_built_in (excess_type, DECL_FUNCTION_CODE (fndecl));
+ if (excess_fndecl == NULL_TREE)
+ excess_type = NULL_TREE;
+ else
+ {
+ fn = build_fold_addr_expr_loc (location.gcc_location (),
+ excess_fndecl);
+ for (size_t i = 0; i < nargs; ++i)
+ {
+ if (SCALAR_FLOAT_TYPE_P (TREE_TYPE (args[i]))
+ || COMPLEX_FLOAT_TYPE_P (TREE_TYPE (args[i])))
+ args[i] = ::convert (excess_type, args[i]);
+ }
+ }
+ }
+ }
+
+ tree ret
+ = build_call_array_loc (location.gcc_location (),
+ excess_type != NULL_TREE ? excess_type : rettype,
+ fn, nargs, args);
+
+ // check for deprecated function usage
+ if (fndecl && TREE_DEPRECATED (fndecl))
+ {
+ // set up the call-site information for `warn_deprecated_use`
+ input_location = location.gcc_location ();
+ warn_deprecated_use (fndecl, NULL_TREE);
+ }
+
+ if (chain_expr)
+ CALL_EXPR_STATIC_CHAIN (ret) = chain_expr;
+
+ if (excess_type != NULL_TREE)
+ {
+ // Calling convert here can undo our excess precision change.
+ // That may or may not be a bug in convert_to_real.
+ ret = build1_loc (location.gcc_location (), NOP_EXPR, rettype, ret);
+ }
+
+ delete[] args;
+ return ret;
+}
+
+// Variable initialization.
+
+tree
+Gcc_backend::init_statement (tree, Bvariable *var, tree init_tree)
+{
+ tree var_tree = var->get_decl ();
+ if (var_tree == error_mark_node || init_tree == error_mark_node)
+ return error_mark_node;
+ gcc_assert (TREE_CODE (var_tree) == VAR_DECL);
+
+ // To avoid problems with GNU ld, we don't make zero-sized
+ // externally visible variables. That might lead us to doing an
+ // initialization of a zero-sized expression to a non-zero sized
+ // variable, or vice-versa. Avoid crashes by omitting the
+ // initializer. Such initializations don't mean anything anyhow.
+ if (int_size_in_bytes (TREE_TYPE (var_tree)) != 0 && init_tree != NULL_TREE
+ && TREE_TYPE (init_tree) != void_type_node
+ && int_size_in_bytes (TREE_TYPE (init_tree)) != 0)
+ {
+ DECL_INITIAL (var_tree) = init_tree;
+ init_tree = NULL_TREE;
+ }
+
+ tree ret = build1_loc (DECL_SOURCE_LOCATION (var_tree), DECL_EXPR,
+ void_type_node, var_tree);
+ if (init_tree != NULL_TREE)
+ ret = build2_loc (DECL_SOURCE_LOCATION (var_tree), COMPOUND_EXPR,
+ void_type_node, init_tree, ret);
+
+ return ret;
+}
+
+// Assignment.
+
+tree
+Gcc_backend::assignment_statement (tree lhs, tree rhs, Location location)
+{
+ if (lhs == error_mark_node || rhs == error_mark_node)
+ return error_mark_node;
+
+ // To avoid problems with GNU ld, we don't make zero-sized
+ // externally visible variables. That might lead us to doing an
+ // assignment of a zero-sized expression to a non-zero sized
+ // expression; avoid crashes here by avoiding assignments of
+ // zero-sized expressions. Such assignments don't really mean
+ // anything anyhow.
+ if (TREE_TYPE (lhs) == void_type_node
+ || int_size_in_bytes (TREE_TYPE (lhs)) == 0
+ || TREE_TYPE (rhs) == void_type_node
+ || int_size_in_bytes (TREE_TYPE (rhs)) == 0)
+ return this->compound_statement (lhs, rhs);
+
+ rhs = this->convert_tree (TREE_TYPE (lhs), rhs, location);
+
+ return fold_build2_loc (location.gcc_location (), MODIFY_EXPR, void_type_node,
+ lhs, rhs);
+}
+
+// Return.
+
+tree
+Gcc_backend::return_statement (tree fntree, const std::vector<tree> &vals,
+ Location location)
+{
+ if (fntree == error_mark_node)
+ return error_mark_node;
+ tree result = DECL_RESULT (fntree);
+ if (result == error_mark_node)
+ return error_mark_node;
+
+ // If the result size is zero bytes, we have set the function type
+ // to have a result type of void, so don't return anything.
+ // See the function_type method.
+ tree res_type = TREE_TYPE (result);
+ if (res_type == void_type_node || int_size_in_bytes (res_type) == 0)
+ {
+ tree stmt_list = NULL_TREE;
+ for (std::vector<tree>::const_iterator p = vals.begin ();
+ p != vals.end (); p++)
+ {
+ tree val = (*p);
+ if (val == error_mark_node)
+ return error_mark_node;
+ append_to_statement_list (val, &stmt_list);
+ }
+ tree ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+ void_type_node, NULL_TREE);
+ append_to_statement_list (ret, &stmt_list);
+ return stmt_list;
+ }
+
+ tree ret;
+ if (vals.empty ())
+ ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+ void_type_node, NULL_TREE);
+ else if (vals.size () == 1)
+ {
+ tree val = vals.front ();
+ if (val == error_mark_node)
+ return error_mark_node;
+ tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+ void_type_node, result, vals.front ());
+ ret = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+ void_type_node, set);
+ }
+ else
+ {
+ // To return multiple values, copy the values into a temporary
+ // variable of the right structure type, and then assign the
+ // temporary variable to the DECL_RESULT in the return
+ // statement.
+ tree stmt_list = NULL_TREE;
+ tree rettype = TREE_TYPE (result);
+
+ if (DECL_STRUCT_FUNCTION (fntree) == NULL)
+ push_struct_function (fntree);
+ else
+ push_cfun (DECL_STRUCT_FUNCTION (fntree));
+ tree rettmp = create_tmp_var (rettype, "RESULT");
+ pop_cfun ();
+
+ tree field = TYPE_FIELDS (rettype);
+ for (std::vector<tree>::const_iterator p = vals.begin ();
+ p != vals.end (); p++, field = DECL_CHAIN (field))
+ {
+ gcc_assert (field != NULL_TREE);
+ tree ref
+ = fold_build3_loc (location.gcc_location (), COMPONENT_REF,
+ TREE_TYPE (field), rettmp, field, NULL_TREE);
+ tree val = (*p);
+ if (val == error_mark_node)
+ return error_mark_node;
+ tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+ void_type_node, ref, (*p));
+ append_to_statement_list (set, &stmt_list);
+ }
+ gcc_assert (field == NULL_TREE);
+ tree set = fold_build2_loc (location.gcc_location (), MODIFY_EXPR,
+ void_type_node, result, rettmp);
+ tree ret_expr = fold_build1_loc (location.gcc_location (), RETURN_EXPR,
+ void_type_node, set);
+ append_to_statement_list (ret_expr, &stmt_list);
+ ret = stmt_list;
+ }
+ return ret;
+}
+
+// Create a statement that attempts to execute BSTAT and calls EXCEPT_STMT if an
+// error occurs. EXCEPT_STMT may be NULL. FINALLY_STMT may be NULL and if not
+// NULL, it will always be executed. This is used for handling defers in Rust
+// functions. In C++, the resulting code is of this form:
+// try { BSTAT; } catch { EXCEPT_STMT; } finally { FINALLY_STMT; }
+
+tree
+Gcc_backend::exception_handler_statement (tree try_stmt, tree except_stmt,
+ tree finally_stmt, Location location)
+{
+ if (try_stmt == error_mark_node || except_stmt == error_mark_node
+ || finally_stmt == error_mark_node)
+ return error_mark_node;
+
+ if (except_stmt != NULL_TREE)
+ try_stmt = build2_loc (location.gcc_location (), TRY_CATCH_EXPR,
+ void_type_node, try_stmt,
+ build2_loc (location.gcc_location (), CATCH_EXPR,
+ void_type_node, NULL, except_stmt));
+ if (finally_stmt != NULL_TREE)
+ try_stmt = build2_loc (location.gcc_location (), TRY_FINALLY_EXPR,
+ void_type_node, try_stmt, finally_stmt);
+ return try_stmt;
+}
+
+// If.
+
+tree
+Gcc_backend::if_statement (tree, tree cond_tree, tree then_tree, tree else_tree,
+ Location location)
+{
+ if (cond_tree == error_mark_node || then_tree == error_mark_node
+ || else_tree == error_mark_node)
+ return error_mark_node;
+ tree ret = build3_loc (location.gcc_location (), COND_EXPR, void_type_node,
+ cond_tree, then_tree, else_tree);
+ return ret;
+}
+
+// Loops
+
+tree
+Gcc_backend::loop_expression (tree body, Location locus)
+{
+ return fold_build1_loc (locus.gcc_location (), LOOP_EXPR, void_type_node,
+ body);
+}
+
+tree
+Gcc_backend::exit_expression (tree cond_tree, Location locus)
+{
+ return fold_build1_loc (locus.gcc_location (), EXIT_EXPR, void_type_node,
+ cond_tree);
+}
+
+// Pair of statements.
+
+tree
+Gcc_backend::compound_statement (tree s1, tree s2)
+{
+ tree stmt_list = NULL_TREE;
+ tree t = s1;
+ if (t == error_mark_node)
+ return error_mark_node;
+ append_to_statement_list (t, &stmt_list);
+ t = s2;
+ if (t == error_mark_node)
+ return error_mark_node;
+ append_to_statement_list (t, &stmt_list);
+
+ // If neither statement has any side effects, stmt_list can be NULL
+ // at this point.
+ if (stmt_list == NULL_TREE)
+ stmt_list = integer_zero_node;
+
+ return stmt_list;
+}
+
+// List of statements.
+
+tree
+Gcc_backend::statement_list (const std::vector<tree> &statements)
+{
+ tree stmt_list = NULL_TREE;
+ for (std::vector<tree>::const_iterator p = statements.begin ();
+ p != statements.end (); ++p)
+ {
+ tree t = (*p);
+ if (t == error_mark_node)
+ return error_mark_node;
+ append_to_statement_list (t, &stmt_list);
+ }
+ return stmt_list;
+}
+
+// Make a block. For some reason gcc uses a dual structure for
+// blocks: BLOCK tree nodes and BIND_EXPR tree nodes. Since the
+// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in
+// the Bblock.
+
+tree
+Gcc_backend::block (tree fndecl, tree enclosing,
+ const std::vector<Bvariable *> &vars,
+ Location start_location, Location)
+{
+ tree block_tree = make_node (BLOCK);
+ if (enclosing == NULL)
+ {
+ gcc_assert (fndecl != NULL_TREE);
+
+ // We may have already created a block for local variables when
+ // we take the address of a parameter.
+ if (DECL_INITIAL (fndecl) == NULL_TREE)
+ {
+ BLOCK_SUPERCONTEXT (block_tree) = fndecl;
+ DECL_INITIAL (fndecl) = block_tree;
+ }
+ else
+ {
+ tree superblock_tree = DECL_INITIAL (fndecl);
+ BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+ tree *pp;
+ for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+ pp = &BLOCK_CHAIN (*pp))
+ ;
+ *pp = block_tree;
+ }
+ }
+ else
+ {
+ tree superblock_tree = BIND_EXPR_BLOCK (enclosing);
+ gcc_assert (TREE_CODE (superblock_tree) == BLOCK);
+
+ BLOCK_SUPERCONTEXT (block_tree) = superblock_tree;
+ tree *pp;
+ for (pp = &BLOCK_SUBBLOCKS (superblock_tree); *pp != NULL_TREE;
+ pp = &BLOCK_CHAIN (*pp))
+ ;
+ *pp = block_tree;
+ }
+
+ tree *pp = &BLOCK_VARS (block_tree);
+ for (std::vector<Bvariable *>::const_iterator pv = vars.begin ();
+ pv != vars.end (); ++pv)
+ {
+ *pp = (*pv)->get_decl ();
+ if (*pp != error_mark_node)
+ pp = &DECL_CHAIN (*pp);
+ }
+ *pp = NULL_TREE;
+
+ TREE_USED (block_tree) = 1;
+
+ tree bind_tree
+ = build3_loc (start_location.gcc_location (), BIND_EXPR, void_type_node,
+ BLOCK_VARS (block_tree), NULL_TREE, block_tree);
+ TREE_SIDE_EFFECTS (bind_tree) = 1;
+ return bind_tree;
+}
+
+// Add statements to a block.
+
+void
+Gcc_backend::block_add_statements (tree bind_tree,
+ const std::vector<tree> &statements)
+{
+ tree stmt_list = NULL_TREE;
+ for (std::vector<tree>::const_iterator p = statements.begin ();
+ p != statements.end (); ++p)
+ {
+ tree s = (*p);
+ if (s != error_mark_node)
+ append_to_statement_list (s, &stmt_list);
+ }
+
+ gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+ BIND_EXPR_BODY (bind_tree) = stmt_list;
+}
+
+// This is not static because we declare it with GTY(()) in rust-c.h.
+tree rust_non_zero_struct;
+
+// Return a type corresponding to TYPE with non-zero size.
+
+tree
+Gcc_backend::non_zero_size_type (tree type)
+{
+ if (int_size_in_bytes (type) != 0)
+ return type;
+
+ switch (TREE_CODE (type))
+ {
+ case RECORD_TYPE:
+ if (TYPE_FIELDS (type) != NULL_TREE)
+ {
+ tree ns = make_node (RECORD_TYPE);
+ tree field_trees = NULL_TREE;
+ tree *pp = &field_trees;
+ for (tree field = TYPE_FIELDS (type); field != NULL_TREE;
+ field = DECL_CHAIN (field))
+ {
+ tree ft = TREE_TYPE (field);
+ if (field == TYPE_FIELDS (type))
+ ft = non_zero_size_type (ft);
+ tree f = build_decl (DECL_SOURCE_LOCATION (field), FIELD_DECL,
+ DECL_NAME (field), ft);
+ DECL_CONTEXT (f) = ns;
+ *pp = f;
+ pp = &DECL_CHAIN (f);
+ }
+ TYPE_FIELDS (ns) = field_trees;
+ layout_type (ns);
+ return ns;
+ }
+
+ if (rust_non_zero_struct == NULL_TREE)
+ {
+ type = make_node (RECORD_TYPE);
+ tree field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+ get_identifier ("dummy"), boolean_type_node);
+ DECL_CONTEXT (field) = type;
+ TYPE_FIELDS (type) = field;
+ layout_type (type);
+ rust_non_zero_struct = type;
+ }
+ return rust_non_zero_struct;
+
+ case ARRAY_TYPE: {
+ tree element_type = non_zero_size_type (TREE_TYPE (type));
+ return build_array_type_nelts (element_type, 1);
+ }
+
+ default:
+ gcc_unreachable ();
+ }
+
+ gcc_unreachable ();
+}
+
+// Convert EXPR_TREE to TYPE_TREE. Sometimes the same unnamed Rust type
+// can be created multiple times and thus have multiple tree
+// representations. Make sure this does not confuse the middle-end.
+
+tree
+Gcc_backend::convert_tree (tree type_tree, tree expr_tree, Location location)
+{
+ if (type_tree == TREE_TYPE (expr_tree))
+ return expr_tree;
+
+ if (type_tree == error_mark_node || expr_tree == error_mark_node
+ || TREE_TYPE (expr_tree) == error_mark_node)
+ return error_mark_node;
+
+ if (POINTER_TYPE_P (type_tree) || INTEGRAL_TYPE_P (type_tree)
+ || SCALAR_FLOAT_TYPE_P (type_tree) || COMPLEX_FLOAT_TYPE_P (type_tree))
+ return fold_convert_loc (location.gcc_location (), type_tree, expr_tree);
+ else if (TREE_CODE (type_tree) == RECORD_TYPE
+ || TREE_CODE (type_tree) == UNION_TYPE
+ || TREE_CODE (type_tree) == ARRAY_TYPE)
+ {
+ gcc_assert (int_size_in_bytes (type_tree)
+ == int_size_in_bytes (TREE_TYPE (expr_tree)));
+ if (TYPE_MAIN_VARIANT (type_tree)
+ == TYPE_MAIN_VARIANT (TREE_TYPE (expr_tree)))
+ return fold_build1_loc (location.gcc_location (), NOP_EXPR, type_tree,
+ expr_tree);
+ return fold_build1_loc (location.gcc_location (), VIEW_CONVERT_EXPR,
+ type_tree, expr_tree);
+ }
+
+ gcc_unreachable ();
+}
+
+// Make a global variable.
+
+Bvariable *
+Gcc_backend::global_variable (const std::string &var_name,
+ const std::string &asm_name, tree type_tree,
+ bool is_external, bool is_hidden,
+ bool in_unique_section, Location location)
+{
+ if (type_tree == error_mark_node)
+ return this->error_variable ();
+
+ // The GNU linker does not like dynamic variables with zero size.
+ tree orig_type_tree = type_tree;
+ if ((is_external || !is_hidden) && int_size_in_bytes (type_tree) == 0)
+ type_tree = this->non_zero_size_type (type_tree);
+
+ tree decl = build_decl (location.gcc_location (), VAR_DECL,
+ get_identifier_from_string (var_name), type_tree);
+ if (is_external)
+ DECL_EXTERNAL (decl) = 1;
+ else
+ TREE_STATIC (decl) = 1;
+ if (!is_hidden)
+ {
+ TREE_PUBLIC (decl) = 1;
+ SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+ }
+ else
+ {
+ SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+ }
+
+ TREE_USED (decl) = 1;
+
+ if (in_unique_section)
+ resolve_unique_section (decl, 0, 1);
+
+ rust_preserve_from_gc (decl);
+
+ return new Bvariable (decl, orig_type_tree);
+}
+
+// Set the initial value of a global variable.
+
+void
+Gcc_backend::global_variable_set_init (Bvariable *var, tree expr_tree)
+{
+ if (expr_tree == error_mark_node)
+ return;
+ gcc_assert (TREE_CONSTANT (expr_tree));
+ tree var_decl = var->get_decl ();
+ if (var_decl == error_mark_node)
+ return;
+ DECL_INITIAL (var_decl) = expr_tree;
+
+ // If this variable goes in a unique section, it may need to go into
+ // a different one now that DECL_INITIAL is set.
+ if (symtab_node::get (var_decl)
+ && symtab_node::get (var_decl)->implicit_section)
+ {
+ set_decl_section_name (var_decl, (const char *) NULL);
+ resolve_unique_section (var_decl, compute_reloc_for_constant (expr_tree),
+ 1);
+ }
+}
+
+// Make a local variable.
+
+Bvariable *
+Gcc_backend::local_variable (tree function, const std::string &name,
+ tree type_tree, Bvariable *decl_var,
+ Location location)
+{
+ if (type_tree == error_mark_node)
+ return this->error_variable ();
+ tree decl = build_decl (location.gcc_location (), VAR_DECL,
+ get_identifier_from_string (name), type_tree);
+ DECL_CONTEXT (decl) = function;
+
+ if (decl_var != NULL)
+ {
+ DECL_HAS_VALUE_EXPR_P (decl) = 1;
+ SET_DECL_VALUE_EXPR (decl, decl_var->get_decl ());
+ }
+ rust_preserve_from_gc (decl);
+ return new Bvariable (decl);
+}
+
+// Make a function parameter variable.
+
+Bvariable *
+Gcc_backend::parameter_variable (tree function, const std::string &name,
+ tree type_tree, Location location)
+{
+ if (type_tree == error_mark_node)
+ return this->error_variable ();
+ tree decl = build_decl (location.gcc_location (), PARM_DECL,
+ get_identifier_from_string (name), type_tree);
+ DECL_CONTEXT (decl) = function;
+ DECL_ARG_TYPE (decl) = type_tree;
+
+ rust_preserve_from_gc (decl);
+ return new Bvariable (decl);
+}
+
+// Make a static chain variable.
+
+Bvariable *
+Gcc_backend::static_chain_variable (tree fndecl, const std::string &name,
+ tree type_tree, Location location)
+{
+ if (type_tree == error_mark_node)
+ return this->error_variable ();
+ tree decl = build_decl (location.gcc_location (), PARM_DECL,
+ get_identifier_from_string (name), type_tree);
+ DECL_CONTEXT (decl) = fndecl;
+ DECL_ARG_TYPE (decl) = type_tree;
+ TREE_USED (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+ TREE_READONLY (decl) = 1;
+
+ struct function *f = DECL_STRUCT_FUNCTION (fndecl);
+ if (f == NULL)
+ {
+ push_struct_function (fndecl);
+ pop_cfun ();
+ f = DECL_STRUCT_FUNCTION (fndecl);
+ }
+ gcc_assert (f->static_chain_decl == NULL);
+ f->static_chain_decl = decl;
+ DECL_STATIC_CHAIN (fndecl) = 1;
+
+ rust_preserve_from_gc (decl);
+ return new Bvariable (decl);
+}
+
+// Make a temporary variable.
+
+Bvariable *
+Gcc_backend::temporary_variable (tree fndecl, tree bind_tree, tree type_tree,
+ tree init_tree, bool is_address_taken,
+ Location location, tree *pstatement)
+{
+ gcc_assert (fndecl != NULL_TREE);
+ if (type_tree == error_mark_node || init_tree == error_mark_node
+ || fndecl == error_mark_node)
+ {
+ *pstatement = error_mark_node;
+ return this->error_variable ();
+ }
+
+ tree var;
+ // We can only use create_tmp_var if the type is not addressable.
+ if (!TREE_ADDRESSABLE (type_tree))
+ {
+ if (DECL_STRUCT_FUNCTION (fndecl) == NULL)
+ push_struct_function (fndecl);
+ else
+ push_cfun (DECL_STRUCT_FUNCTION (fndecl));
+
+ var = create_tmp_var (type_tree, "RUSTTMP");
+ pop_cfun ();
+ }
+ else
+ {
+ gcc_assert (bind_tree != NULL_TREE);
+ var = build_decl (location.gcc_location (), VAR_DECL,
+ create_tmp_var_name ("RUSTTMP"), type_tree);
+ DECL_ARTIFICIAL (var) = 1;
+ DECL_IGNORED_P (var) = 1;
+ TREE_USED (var) = 1;
+ DECL_CONTEXT (var) = fndecl;
+
+ // We have to add this variable to the BLOCK and the BIND_EXPR.
+ gcc_assert (TREE_CODE (bind_tree) == BIND_EXPR);
+ tree block_tree = BIND_EXPR_BLOCK (bind_tree);
+ gcc_assert (TREE_CODE (block_tree) == BLOCK);
+ DECL_CHAIN (var) = BLOCK_VARS (block_tree);
+ BLOCK_VARS (block_tree) = var;
+ BIND_EXPR_VARS (bind_tree) = BLOCK_VARS (block_tree);
+ }
+
+ if (this->type_size (type_tree) != 0 && init_tree != NULL_TREE
+ && TREE_TYPE (init_tree) != void_type_node)
+ DECL_INITIAL (var) = this->convert_tree (type_tree, init_tree, location);
+
+ if (is_address_taken)
+ TREE_ADDRESSABLE (var) = 1;
+
+ *pstatement
+ = build1_loc (location.gcc_location (), DECL_EXPR, void_type_node, var);
+
+ // For a zero sized type, don't initialize VAR with BINIT, but still
+ // evaluate BINIT for its side effects.
+ if (init_tree != NULL_TREE
+ && (this->type_size (type_tree) == 0
+ || TREE_TYPE (init_tree) == void_type_node))
+ *pstatement = this->compound_statement (init_tree, *pstatement);
+
+ return new Bvariable (var);
+}
+
+// Make a label.
+
+tree
+Gcc_backend::label (tree func_tree, const std::string &name, Location location)
+{
+ tree decl;
+ if (name.empty ())
+ {
+ if (DECL_STRUCT_FUNCTION (func_tree) == NULL)
+ push_struct_function (func_tree);
+ else
+ push_cfun (DECL_STRUCT_FUNCTION (func_tree));
+
+ decl = create_artificial_label (location.gcc_location ());
+
+ pop_cfun ();
+ }
+ else
+ {
+ tree id = get_identifier_from_string (name);
+ decl
+ = build_decl (location.gcc_location (), LABEL_DECL, id, void_type_node);
+ DECL_CONTEXT (decl) = func_tree;
+ }
+ return decl;
+}
+
+// Make a statement which defines a label.
+
+tree
+Gcc_backend::label_definition_statement (tree label)
+{
+ return fold_build1_loc (DECL_SOURCE_LOCATION (label), LABEL_EXPR,
+ void_type_node, label);
+}
+
+// Make a goto statement.
+
+tree
+Gcc_backend::goto_statement (tree label, Location location)
+{
+ return fold_build1_loc (location.gcc_location (), GOTO_EXPR, void_type_node,
+ label);
+}
+
+// Get the address of a label.
+
+tree
+Gcc_backend::label_address (tree label, Location location)
+{
+ TREE_USED (label) = 1;
+ TREE_ADDRESSABLE (label) = 1;
+ tree ret
+ = fold_convert_loc (location.gcc_location (), ptr_type_node,
+ build_fold_addr_expr_loc (location.gcc_location (),
+ label));
+ return ret;
+}
+
+// Declare or define a new function.
+
+tree
+Gcc_backend::function (tree functype, const std::string &name,
+ const std::string &asm_name, unsigned int flags,
+ Location location)
+{
+ if (functype != error_mark_node)
+ {
+ gcc_assert (FUNCTION_POINTER_TYPE_P (functype));
+ functype = TREE_TYPE (functype);
+ }
+ tree id = get_identifier_from_string (name);
+ if (functype == error_mark_node || id == error_mark_node)
+ return error_mark_node;
+
+ tree decl
+ = build_decl (location.gcc_location (), FUNCTION_DECL, id, functype);
+ if (!asm_name.empty ())
+ SET_DECL_ASSEMBLER_NAME (decl, get_identifier_from_string (asm_name));
+
+ if ((flags & function_is_declaration) != 0)
+ DECL_EXTERNAL (decl) = 1;
+ else
+ {
+ tree restype = TREE_TYPE (functype);
+ tree resdecl = build_decl (location.gcc_location (), RESULT_DECL,
+ NULL_TREE, restype);
+ DECL_ARTIFICIAL (resdecl) = 1;
+ DECL_IGNORED_P (resdecl) = 1;
+ DECL_CONTEXT (resdecl) = decl;
+ DECL_RESULT (decl) = resdecl;
+ }
+ if ((flags & function_is_uninlinable) != 0)
+ DECL_UNINLINABLE (decl) = 1;
+ if ((flags & function_does_not_return) != 0)
+ TREE_THIS_VOLATILE (decl) = 1;
+ if ((flags & function_in_unique_section) != 0)
+ resolve_unique_section (decl, 0, 1);
+
+ rust_preserve_from_gc (decl);
+ return decl;
+}
+
+// Create a statement that runs all deferred calls for FUNCTION. This should
+// be a statement that looks like this in C++:
+// finish:
+// try { UNDEFER; } catch { CHECK_DEFER; goto finish; }
+
+tree
+Gcc_backend::function_defer_statement (tree function, tree undefer_tree,
+ tree defer_tree, Location location)
+{
+ if (undefer_tree == error_mark_node || defer_tree == error_mark_node
+ || function == error_mark_node)
+ return error_mark_node;
+
+ if (DECL_STRUCT_FUNCTION (function) == NULL)
+ push_struct_function (function);
+ else
+ push_cfun (DECL_STRUCT_FUNCTION (function));
+
+ tree stmt_list = NULL;
+ tree label = this->label (function, "", location);
+ tree label_def = this->label_definition_statement (label);
+ append_to_statement_list (label_def, &stmt_list);
+
+ tree jump_stmt = this->goto_statement (label, location);
+ tree catch_body
+ = build2 (COMPOUND_EXPR, void_type_node, defer_tree, jump_stmt);
+ catch_body = build2 (CATCH_EXPR, void_type_node, NULL, catch_body);
+ tree try_catch
+ = build2 (TRY_CATCH_EXPR, void_type_node, undefer_tree, catch_body);
+ append_to_statement_list (try_catch, &stmt_list);
+ pop_cfun ();
+
+ return stmt_list;
+}
+
+// Record PARAM_VARS as the variables to use for the parameters of FUNCTION.
+// This will only be called for a function definition.
+
+bool
+Gcc_backend::function_set_parameters (
+ tree function, const std::vector<Bvariable *> &param_vars)
+{
+ if (function == error_mark_node)
+ return false;
+
+ tree params = NULL_TREE;
+ tree *pp = &params;
+ for (std::vector<Bvariable *>::const_iterator pv = param_vars.begin ();
+ pv != param_vars.end (); ++pv)
+ {
+ *pp = (*pv)->get_decl ();
+ gcc_assert (*pp != error_mark_node);
+ pp = &DECL_CHAIN (*pp);
+ }
+ *pp = NULL_TREE;
+ DECL_ARGUMENTS (function) = params;
+ return true;
+}
+
+// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
+// FUNCTION_DECLS, and VARIABLE_DECLS declared globally, as well as
+// emit early debugging information.
+
+void
+Gcc_backend::write_global_definitions (
+ const std::vector<tree> &type_decls, const std::vector<tree> &constant_decls,
+ const std::vector<tree> &function_decls,
+ const std::vector<Bvariable *> &variable_decls)
+{
+ size_t count_definitions = type_decls.size () + constant_decls.size ()
+ + function_decls.size () + variable_decls.size ();
+
+ tree *defs = new tree[count_definitions];
+
+ // Convert all non-erroneous declarations into Gimple form.
+ size_t i = 0;
+ for (std::vector<Bvariable *>::const_iterator p = variable_decls.begin ();
+ p != variable_decls.end (); ++p)
+ {
+ tree v = (*p)->get_decl ();
+ if (v != error_mark_node)
+ {
+ defs[i] = v;
+ rust_preserve_from_gc (defs[i]);
+ ++i;
+ }
+ }
+
+ for (std::vector<tree>::const_iterator p = type_decls.begin ();
+ p != type_decls.end (); ++p)
+ {
+ tree type_tree = (*p);
+ if (type_tree != error_mark_node && IS_TYPE_OR_DECL_P (type_tree))
+ {
+ defs[i] = TYPE_NAME (type_tree);
+ gcc_assert (defs[i] != NULL);
+ rust_preserve_from_gc (defs[i]);
+ ++i;
+ }
+ }
+ for (std::vector<tree>::const_iterator p = constant_decls.begin ();
+ p != constant_decls.end (); ++p)
+ {
+ if ((*p) != error_mark_node)
+ {
+ defs[i] = (*p);
+ rust_preserve_from_gc (defs[i]);
+ ++i;
+ }
+ }
+ for (std::vector<tree>::const_iterator p = function_decls.begin ();
+ p != function_decls.end (); ++p)
+ {
+ tree decl = (*p);
+ if (decl != error_mark_node)
+ {
+ rust_preserve_from_gc (decl);
+ if (DECL_STRUCT_FUNCTION (decl) == NULL)
+ allocate_struct_function (decl, false);
+ dump_function (TDI_original, decl);
+ cgraph_node::finalize_function (decl, true);
+
+ defs[i] = decl;
+ ++i;
+ }
+ }
+
+ // Pass everything back to the middle-end.
+
+ wrapup_global_declarations (defs, i);
+
+ delete[] defs;
+}
+
+void
+Gcc_backend::write_export_data (const char *bytes, unsigned int size)
+{
+ rust_write_export_data (bytes, size);
+}
+
+// Return the backend generator.
+
+Backend *
+rust_get_backend ()
+{
+ return new Gcc_backend ();
+}