blob: cf20b5b98e02bf06376c079736ae38aeeeb4ecdb [file] [log] [blame]
// rust-gcc.cc -- Rust frontend to gcc IR.
// Copyright (C) 2011-2023 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 "rust-gcc.h"
#include "backend/rust-tree.h"
#include "backend/rust-builtins.h"
// 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 arithmetic_or_logical_expression_checked (ArithmeticOrLogicalOperator op,
tree left, tree right,
Location, Bvariable *receiver);
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;
}
tree
Gcc_backend::arithmetic_or_logical_expression (ArithmeticOrLogicalOperator op,
tree left, tree right,
Location location)
{
/* Check if either expression is an error, in which case we return an error
expression. */
if (left == error_mark_node || right == 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);
auto ret = NULL_TREE;
/* For arithmetic or logical operators, the resulting type should be the same
as the lhs operand. */
auto tree_type = TREE_TYPE (left);
auto original_type = tree_type;
auto loc = location.gcc_location ();
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 = convert (extended_type, left);
right = convert (extended_type, right);
tree_type = extended_type;
}
}
ret = fold_build2_loc (loc, tree_code, tree_type, left, right);
TREE_CONSTANT (ret) = TREE_CONSTANT (left) & TREE_CONSTANT (right);
// TODO: How do we handle floating point?
if (floating_point && extended_type != NULL_TREE)
ret = convert (original_type, ret);
return ret;
}
static bool
is_overflowing_expr (ArithmeticOrLogicalOperator op)
{
switch (op)
{
case ArithmeticOrLogicalOperator::ADD:
case ArithmeticOrLogicalOperator::SUBTRACT:
case ArithmeticOrLogicalOperator::MULTIPLY:
return true;
default:
return false;
}
}
static std::pair<tree, tree>
fetch_overflow_builtins (ArithmeticOrLogicalOperator op)
{
auto builtin_ctx = Rust::Compile::BuiltinsContext::get ();
auto builtin = NULL_TREE;
auto abort = NULL_TREE;
switch (op)
{
case ArithmeticOrLogicalOperator::ADD:
builtin_ctx.lookup_simple_builtin ("add_overflow", &builtin);
break;
case ArithmeticOrLogicalOperator::SUBTRACT:
builtin_ctx.lookup_simple_builtin ("sub_overflow", &builtin);
break;
case ArithmeticOrLogicalOperator::MULTIPLY:
builtin_ctx.lookup_simple_builtin ("mul_overflow", &builtin);
break;
default:
gcc_unreachable ();
break;
};
builtin_ctx.lookup_simple_builtin ("abort", &abort);
rust_assert (abort);
rust_assert (builtin);
// FIXME: ARTHUR: This is really ugly. The builtin context should take care of
// that
TREE_SIDE_EFFECTS (abort) = 1;
TREE_READONLY (abort) = 0;
// FIXME: ARTHUR: Same here. Remove these!
TREE_SIDE_EFFECTS (builtin) = 1;
TREE_READONLY (builtin) = 0;
return {abort, builtin};
}
// Return an expression for the arithmetic or logical operation LEFT OP RIGHT
// with overflow checking when possible
tree
Gcc_backend::arithmetic_or_logical_expression_checked (
ArithmeticOrLogicalOperator op, tree left, tree right, Location location,
Bvariable *receiver_var)
{
/* Check if either expression is an error, in which case we return an error
expression. */
if (left == error_mark_node || right == error_mark_node)
return error_mark_node;
auto loc = location.gcc_location ();
// FIXME: Add `if (!debug_mode)`
// No overflow checks for floating point operations or divisions. In that
// case, simply assign the result of the operation to the receiver variable
if (is_floating_point (left) || !is_overflowing_expr (op))
return assignment_statement (
receiver_var->get_tree (location),
arithmetic_or_logical_expression (op, left, right, location), location);
auto receiver = receiver_var->get_tree (location);
TREE_ADDRESSABLE (receiver) = 1;
auto result_ref = build_fold_addr_expr_loc (loc, receiver);
auto builtins = fetch_overflow_builtins (op);
auto abort = builtins.first;
auto builtin = builtins.second;
auto abort_call = build_call_expr_loc (loc, abort, 0);
// FIXME: ARTHUR: Is that needed?
TREE_SIDE_EFFECTS (abort_call) = 1;
TREE_READONLY (abort_call) = 0;
auto builtin_call
= build_call_expr_loc (loc, builtin, 3, left, right, result_ref);
auto overflow_check
= build2_loc (loc, EQ_EXPR, boolean_type_node, builtin_call,
boolean_constant_expression (true));
auto if_block = build3_loc (loc, COND_EXPR, void_type_node, overflow_check,
abort_call, NULL_TREE);
// FIXME: ARTHUR: Needed?
TREE_SIDE_EFFECTS (if_block) = 1;
TREE_READONLY (if_block) = 0;
return if_block;
}
// 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 ();
}