compiler: Use backend interface for defining global declarations.
* go-gcc.cc: Include "cgraph.h" and "gimplify.h". (Gcc_backend::return_statement): Push and pop function. (Gcc_backend::label): Likewise. (Gcc_backend::function_defer_statement): Likewise. (Gcc_backend::switch_statement): Add function parameter. (Gcc_backend::block): Don't permit function to be NULL. (Gcc_backend::temporary_variable): Change go_assert to gcc_assert. (Gcc_backend::gc_root_variable): New function. (Gcc_backend::write_global_definitions): New function. From-SVN: r209819
This commit is contained in:
parent
10695c6a1d
commit
036165d811
@ -1,3 +1,16 @@
|
||||
2014-04-25 Chris Manghane <cmang@google.com>
|
||||
|
||||
* go-gcc.cc: Include "cgraph.h" and "gimplify.h".
|
||||
(Gcc_backend::return_statement): Push and pop function.
|
||||
(Gcc_backend::label): Likewise.
|
||||
(Gcc_backend::function_defer_statement): Likewise.
|
||||
(Gcc_backend::switch_statement): Add function parameter.
|
||||
(Gcc_backend::block): Don't permit function to be NULL.
|
||||
(Gcc_backend::temporary_variable): Change go_assert to
|
||||
gcc_assert.
|
||||
(Gcc_backend::gc_root_variable): New function.
|
||||
(Gcc_backend::write_global_definitions): New function.
|
||||
|
||||
2014-04-22 Chris Manghane <cmang@google.com>
|
||||
|
||||
* go-gcc.cc (Gcc_backend::temporary_variable): Push cfun around
|
||||
|
168
gcc/go/go-gcc.cc
168
gcc/go/go-gcc.cc
@ -29,9 +29,11 @@
|
||||
#include "stor-layout.h"
|
||||
#include "varasm.h"
|
||||
#include "tree-iterator.h"
|
||||
#include "cgraph.h"
|
||||
#include "convert.h"
|
||||
#include "basic-block.h"
|
||||
#include "gimple-expr.h"
|
||||
#include "gimplify.h"
|
||||
#include "toplev.h"
|
||||
#include "output.h"
|
||||
#include "real.h"
|
||||
@ -317,7 +319,7 @@ class Gcc_backend : public Backend
|
||||
Location);
|
||||
|
||||
Bstatement*
|
||||
switch_statement(Bexpression* value,
|
||||
switch_statement(Bfunction* function, Bexpression* value,
|
||||
const std::vector<std::vector<Bexpression*> >& cases,
|
||||
const std::vector<Bstatement*>& statements,
|
||||
Location);
|
||||
@ -375,6 +377,9 @@ class Gcc_backend : public Backend
|
||||
temporary_variable(Bfunction*, Bblock*, Btype*, Bexpression*, bool,
|
||||
Location, Bstatement**);
|
||||
|
||||
Bvariable*
|
||||
gc_root_variable(Btype*, Bexpression*);
|
||||
|
||||
Bvariable*
|
||||
immutable_struct(const std::string&, bool, bool, Btype*, Location);
|
||||
|
||||
@ -420,6 +425,12 @@ class Gcc_backend : public Backend
|
||||
bool
|
||||
function_set_body(Bfunction* function, Bstatement* code_stmt);
|
||||
|
||||
void
|
||||
write_global_definitions(const std::vector<Btype*>&,
|
||||
const std::vector<Bexpression*>&,
|
||||
const std::vector<Bfunction*>&,
|
||||
const std::vector<Bvariable*>&);
|
||||
|
||||
private:
|
||||
// Make a Bexpression from a tree.
|
||||
Bexpression*
|
||||
@ -1709,6 +1720,7 @@ Gcc_backend::return_statement(Bfunction* bfunction,
|
||||
tree result = DECL_RESULT(fntree);
|
||||
if (result == error_mark_node)
|
||||
return this->error_statement();
|
||||
|
||||
tree ret;
|
||||
if (vals.empty())
|
||||
ret = fold_build1_loc(location.gcc_location(), RETURN_EXPR, void_type_node,
|
||||
@ -1732,7 +1744,14 @@ Gcc_backend::return_statement(Bfunction* bfunction,
|
||||
// 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<Bexpression*>::const_iterator p = vals.begin();
|
||||
p != vals.end();
|
||||
@ -1818,6 +1837,7 @@ Gcc_backend::if_statement(Bexpression* condition, Bblock* then_block,
|
||||
|
||||
Bstatement*
|
||||
Gcc_backend::switch_statement(
|
||||
Bfunction* function,
|
||||
Bexpression* value,
|
||||
const std::vector<std::vector<Bexpression*> >& cases,
|
||||
const std::vector<Bstatement*>& statements,
|
||||
@ -1825,6 +1845,12 @@ Gcc_backend::switch_statement(
|
||||
{
|
||||
gcc_assert(cases.size() == statements.size());
|
||||
|
||||
tree decl = function->get_tree();
|
||||
if (DECL_STRUCT_FUNCTION(decl) == NULL)
|
||||
push_struct_function(decl);
|
||||
else
|
||||
push_cfun(DECL_STRUCT_FUNCTION(decl));
|
||||
|
||||
tree stmt_list = NULL_TREE;
|
||||
std::vector<std::vector<Bexpression*> >::const_iterator pc = cases.begin();
|
||||
for (std::vector<Bstatement*>::const_iterator ps = statements.begin();
|
||||
@ -1864,6 +1890,7 @@ Gcc_backend::switch_statement(
|
||||
append_to_statement_list(t, &stmt_list);
|
||||
}
|
||||
}
|
||||
pop_cfun();
|
||||
|
||||
tree tv = value->get_tree();
|
||||
if (tv == error_mark_node)
|
||||
@ -1922,13 +1949,7 @@ Gcc_backend::block(Bfunction* function, Bblock* enclosing,
|
||||
tree block_tree = make_node(BLOCK);
|
||||
if (enclosing == NULL)
|
||||
{
|
||||
// FIXME: Permitting FUNCTION to be NULL is a temporary measure
|
||||
// until we have a proper representation of the init function.
|
||||
tree fndecl;
|
||||
if (function == NULL)
|
||||
fndecl = current_function_decl;
|
||||
else
|
||||
fndecl = function->get_tree();
|
||||
tree fndecl = function->get_tree();
|
||||
gcc_assert(fndecl != NULL_TREE);
|
||||
|
||||
// We may have already created a block for local variables when
|
||||
@ -1982,7 +2003,6 @@ Gcc_backend::block(Bfunction* function, Bblock* enclosing,
|
||||
void_type_node, BLOCK_VARS(block_tree),
|
||||
NULL_TREE, block_tree);
|
||||
TREE_SIDE_EFFECTS(bind_tree) = 1;
|
||||
|
||||
return new Bblock(bind_tree);
|
||||
}
|
||||
|
||||
@ -2214,7 +2234,7 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
|
||||
return this->error_variable();
|
||||
}
|
||||
|
||||
go_assert(function != NULL);
|
||||
gcc_assert(function != NULL);
|
||||
tree decl = function->get_tree();
|
||||
|
||||
tree var;
|
||||
@ -2263,6 +2283,28 @@ Gcc_backend::temporary_variable(Bfunction* function, Bblock* bblock,
|
||||
return new Bvariable(var);
|
||||
}
|
||||
|
||||
// Make a GC root variable.
|
||||
|
||||
Bvariable*
|
||||
Gcc_backend::gc_root_variable(Btype* type, Bexpression* init)
|
||||
{
|
||||
tree type_tree = type->get_tree();
|
||||
tree init_tree = init->get_tree();
|
||||
if (type_tree == error_mark_node || init_tree == error_mark_node)
|
||||
return this->error_variable();
|
||||
|
||||
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
|
||||
create_tmp_var_name("gc"), type_tree);
|
||||
DECL_EXTERNAL(decl) = 0;
|
||||
TREE_PUBLIC(decl) = 0;
|
||||
TREE_STATIC(decl) = 1;
|
||||
DECL_ARTIFICIAL(decl) = 1;
|
||||
DECL_INITIAL(decl) = init_tree;
|
||||
rest_of_decl_compilation(decl, 1, 0);
|
||||
|
||||
return new Bvariable(decl);
|
||||
}
|
||||
|
||||
// Create a named immutable initialized data structure.
|
||||
|
||||
Bvariable*
|
||||
@ -2277,9 +2319,9 @@ Gcc_backend::immutable_struct(const std::string& name, bool is_hidden,
|
||||
get_identifier_from_string(name),
|
||||
build_qualified_type(type_tree, TYPE_QUAL_CONST));
|
||||
TREE_STATIC(decl) = 1;
|
||||
TREE_USED(decl) = 1;
|
||||
TREE_READONLY(decl) = 1;
|
||||
TREE_CONSTANT(decl) = 1;
|
||||
TREE_USED(decl) = 1;
|
||||
DECL_ARTIFICIAL(decl) = 1;
|
||||
if (!is_hidden)
|
||||
TREE_PUBLIC(decl) = 1;
|
||||
@ -2369,7 +2411,17 @@ Gcc_backend::label(Bfunction* function, const std::string& name,
|
||||
{
|
||||
tree decl;
|
||||
if (name.empty())
|
||||
decl = create_artificial_label(location.gcc_location());
|
||||
{
|
||||
tree func_tree = function->get_tree();
|
||||
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);
|
||||
@ -2477,11 +2529,18 @@ Gcc_backend::function_defer_statement(Bfunction* function, Bexpression* undefer,
|
||||
{
|
||||
tree undefer_tree = undefer->get_tree();
|
||||
tree defer_tree = defer->get_tree();
|
||||
tree fntree = function->get_tree();
|
||||
|
||||
if (undefer_tree == error_mark_node
|
||||
|| defer_tree == error_mark_node)
|
||||
|| defer_tree == error_mark_node
|
||||
|| fntree == error_mark_node)
|
||||
return this->error_statement();
|
||||
|
||||
if (DECL_STRUCT_FUNCTION(fntree) == NULL)
|
||||
push_struct_function(fntree);
|
||||
else
|
||||
push_cfun(DECL_STRUCT_FUNCTION(fntree));
|
||||
|
||||
tree stmt_list = NULL;
|
||||
Blabel* blabel = this->label(function, "", location);
|
||||
Bstatement* label_def = this->label_definition_statement(blabel);
|
||||
@ -2494,6 +2553,7 @@ Gcc_backend::function_defer_statement(Bfunction* function, Bexpression* undefer,
|
||||
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 this->make_statement(stmt_list);
|
||||
}
|
||||
@ -2538,6 +2598,88 @@ Gcc_backend::function_set_body(Bfunction* function, Bstatement* code_stmt)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
|
||||
// FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
|
||||
|
||||
void
|
||||
Gcc_backend::write_global_definitions(
|
||||
const std::vector<Btype*>& type_decls,
|
||||
const std::vector<Bexpression*>& constant_decls,
|
||||
const std::vector<Bfunction*>& 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)
|
||||
{
|
||||
if ((*p)->get_tree() != error_mark_node)
|
||||
{
|
||||
defs[i] = (*p)->get_tree();
|
||||
go_preserve_from_gc(defs[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
for (std::vector<Btype*>::const_iterator p = type_decls.begin();
|
||||
p != type_decls.end();
|
||||
++p)
|
||||
{
|
||||
tree type_tree = (*p)->get_tree();
|
||||
if (type_tree != error_mark_node
|
||||
&& IS_TYPE_OR_DECL_P(type_tree))
|
||||
{
|
||||
defs[i] = TYPE_NAME(type_tree);
|
||||
gcc_assert(defs[i] != NULL);
|
||||
go_preserve_from_gc(defs[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for (std::vector<Bexpression*>::const_iterator p = constant_decls.begin();
|
||||
p != constant_decls.end();
|
||||
++p)
|
||||
{
|
||||
if ((*p)->get_tree() != error_mark_node)
|
||||
{
|
||||
defs[i] = (*p)->get_tree();
|
||||
go_preserve_from_gc(defs[i]);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
for (std::vector<Bfunction*>::const_iterator p = function_decls.begin();
|
||||
p != function_decls.end();
|
||||
++p)
|
||||
{
|
||||
tree decl = (*p)->get_tree();
|
||||
if (decl != error_mark_node)
|
||||
{
|
||||
go_preserve_from_gc(decl);
|
||||
gimplify_function_tree(decl);
|
||||
cgraph_finalize_function(decl, true);
|
||||
|
||||
defs[i] = decl;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
// Pass everything back to the middle-end.
|
||||
|
||||
wrapup_global_declarations(defs, i);
|
||||
|
||||
finalize_compilation_unit();
|
||||
|
||||
check_global_declarations(defs, i);
|
||||
emit_debug_global_declarations(defs, i);
|
||||
|
||||
delete[] defs;
|
||||
}
|
||||
|
||||
// The single backend.
|
||||
|
||||
static Gcc_backend gcc_backend;
|
||||
|
@ -406,9 +406,9 @@ class Backend
|
||||
// integers, then STATEMENTS[i] is executed. STATEMENTS[i] will
|
||||
// either end with a goto statement or will fall through into
|
||||
// STATEMENTS[i + 1]. CASES[i] is empty for the default clause,
|
||||
// which need not be last.
|
||||
// which need not be last. FUNCTION is the current function.
|
||||
virtual Bstatement*
|
||||
switch_statement(Bexpression* value,
|
||||
switch_statement(Bfunction* function, Bexpression* value,
|
||||
const std::vector<std::vector<Bexpression*> >& cases,
|
||||
const std::vector<Bstatement*>& statements,
|
||||
Location) = 0;
|
||||
@ -534,6 +534,12 @@ class Backend
|
||||
bool address_is_taken, Location location,
|
||||
Bstatement** pstatement) = 0;
|
||||
|
||||
// Create a GC root variable. TYPE is the __go_gc_root_list struct described
|
||||
// in Gogo::register_gc_vars. INIT is the composite literal consisting of a
|
||||
// pointer to the next GC root and the global variables registered.
|
||||
virtual Bvariable*
|
||||
gc_root_variable(Btype* type, Bexpression* init) = 0;
|
||||
|
||||
// Create a named immutable initialized data structure. This is
|
||||
// used for type descriptors, map descriptors, and function
|
||||
// descriptors. This returns a Bvariable because it corresponds to
|
||||
@ -653,6 +659,16 @@ class Backend
|
||||
// true on success, false on failure.
|
||||
virtual bool
|
||||
function_set_body(Bfunction* function, Bstatement* code_stmt) = 0;
|
||||
|
||||
// Utility.
|
||||
|
||||
// Write the definitions for all TYPE_DECLS, CONSTANT_DECLS,
|
||||
// FUNCTION_DECLS, and VARIABLE_DECLS declared globally.
|
||||
virtual void
|
||||
write_global_definitions(const std::vector<Btype*>& type_decls,
|
||||
const std::vector<Bexpression*>& constant_decls,
|
||||
const std::vector<Bfunction*>& function_decls,
|
||||
const std::vector<Bvariable*>& variable_decls) = 0;
|
||||
};
|
||||
|
||||
// The backend interface has to define this function.
|
||||
|
@ -3578,127 +3578,7 @@ Expression::make_unsafe_cast(Type* type, Expression* expr,
|
||||
return new Unsafe_type_conversion_expression(type, expr, location);
|
||||
}
|
||||
|
||||
// Unary expressions.
|
||||
|
||||
class Unary_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Unary_expression(Operator op, Expression* expr, Location location)
|
||||
: Expression(EXPRESSION_UNARY, location),
|
||||
op_(op), escapes_(true), create_temp_(false), expr_(expr),
|
||||
issue_nil_check_(false)
|
||||
{ }
|
||||
|
||||
// Return the operator.
|
||||
Operator
|
||||
op() const
|
||||
{ return this->op_; }
|
||||
|
||||
// Return the operand.
|
||||
Expression*
|
||||
operand() const
|
||||
{ return this->expr_; }
|
||||
|
||||
// Record that an address expression does not escape.
|
||||
void
|
||||
set_does_not_escape()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->escapes_ = false;
|
||||
}
|
||||
|
||||
// Record that this is an address expression which should create a
|
||||
// temporary variable if necessary. This is used for method calls.
|
||||
void
|
||||
set_create_temp()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->create_temp_ = true;
|
||||
}
|
||||
|
||||
// Apply unary opcode OP to UNC, setting NC. Return true if this
|
||||
// could be done, false if not. Issue errors for overflow.
|
||||
static bool
|
||||
eval_constant(Operator op, const Numeric_constant* unc,
|
||||
Location, Numeric_constant* nc);
|
||||
|
||||
static Expression*
|
||||
do_import(Import*);
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse* traverse)
|
||||
{ return Expression::traverse(&this->expr_, traverse); }
|
||||
|
||||
Expression*
|
||||
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
|
||||
|
||||
Expression*
|
||||
do_flatten(Gogo*, Named_object*, Statement_inserter*);
|
||||
|
||||
bool
|
||||
do_is_constant() const;
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{ return this->expr_->is_immutable()
|
||||
|| (this->op_ == OPERATOR_AND && this->expr_->is_variable()); }
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant*) const;
|
||||
|
||||
Type*
|
||||
do_type();
|
||||
|
||||
void
|
||||
do_determine_type(const Type_context*);
|
||||
|
||||
void
|
||||
do_check_types(Gogo*);
|
||||
|
||||
Expression*
|
||||
do_copy()
|
||||
{
|
||||
return Expression::make_unary(this->op_, this->expr_->copy(),
|
||||
this->location());
|
||||
}
|
||||
|
||||
bool
|
||||
do_must_eval_subexpressions_in_order(int*) const
|
||||
{ return this->op_ == OPERATOR_MULT; }
|
||||
|
||||
bool
|
||||
do_is_addressable() const
|
||||
{ return this->op_ == OPERATOR_MULT; }
|
||||
|
||||
tree
|
||||
do_get_tree(Translate_context*);
|
||||
|
||||
void
|
||||
do_export(Export*) const;
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
void
|
||||
do_issue_nil_check()
|
||||
{ this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); }
|
||||
|
||||
private:
|
||||
// The unary operator to apply.
|
||||
Operator op_;
|
||||
// Normally true. False if this is an address expression which does
|
||||
// not escape the current function.
|
||||
bool escapes_;
|
||||
// True if this is an address expression which should create a
|
||||
// temporary variable if necessary.
|
||||
bool create_temp_;
|
||||
// The operand.
|
||||
Expression* expr_;
|
||||
// Whether or not to issue a nil check for this expression if its address
|
||||
// is being taken.
|
||||
bool issue_nil_check_;
|
||||
};
|
||||
// Class Unary_expression.
|
||||
|
||||
// If we are taking the address of a composite literal, and the
|
||||
// contents are not constant, then we want to make a heap expression
|
||||
@ -4214,11 +4094,18 @@ Unary_expression::do_get_tree(Translate_context* context)
|
||||
}
|
||||
}
|
||||
|
||||
// Build a decl for a constant constructor.
|
||||
if ((this->expr_->is_composite_literal()
|
||||
if (this->is_gc_root_)
|
||||
{
|
||||
// Build a decl for a GC root variable. GC roots are mutable, so they
|
||||
// cannot be represented as an immutable_struct in the backend.
|
||||
Bvariable* gc_root = gogo->backend()->gc_root_variable(btype, bexpr);
|
||||
bexpr = gogo->backend()->var_expression(gc_root, loc);
|
||||
}
|
||||
else if ((this->expr_->is_composite_literal()
|
||||
|| this->expr_->string_expression() != NULL)
|
||||
&& this->expr_->is_immutable())
|
||||
{
|
||||
// Build a decl for a constant constructor.
|
||||
static unsigned int counter;
|
||||
char buf[100];
|
||||
snprintf(buf, sizeof buf, "C%u", counter);
|
||||
@ -12508,6 +12395,14 @@ Fixed_array_construction_expression::do_get_tree(Translate_context* context)
|
||||
return expr_to_tree(this->get_constructor(context, btype));
|
||||
}
|
||||
|
||||
Expression*
|
||||
Expression::make_array_composite_literal(Type* type, Expression_list* vals,
|
||||
Location location)
|
||||
{
|
||||
go_assert(type->array_type() != NULL && !type->is_slice_type());
|
||||
return new Fixed_array_construction_expression(type, NULL, vals, location);
|
||||
}
|
||||
|
||||
// Construct a slice.
|
||||
|
||||
class Slice_construction_expression : public Array_construction_expression
|
||||
|
@ -30,6 +30,7 @@ class Var_expression;
|
||||
class Temporary_reference_expression;
|
||||
class Set_and_use_temporary_expression;
|
||||
class String_expression;
|
||||
class Unary_expression;
|
||||
class Binary_expression;
|
||||
class Call_expression;
|
||||
class Func_expression;
|
||||
@ -327,6 +328,10 @@ class Expression
|
||||
static Expression*
|
||||
make_struct_composite_literal(Type*, Expression_list*, Location);
|
||||
|
||||
// Make an array composite literal.
|
||||
static Expression*
|
||||
make_array_composite_literal(Type*, Expression_list*, Location);
|
||||
|
||||
// Make a slice composite literal.
|
||||
static Expression*
|
||||
make_slice_composite_literal(Type*, Expression_list*, Location);
|
||||
@ -533,6 +538,12 @@ class Expression
|
||||
Expression*
|
||||
deref();
|
||||
|
||||
// If this is a unary expression, return the Unary_expression
|
||||
// structure. Otherwise return NULL.
|
||||
Unary_expression*
|
||||
unary_expression()
|
||||
{ return this->convert<Unary_expression, EXPRESSION_UNARY>(); }
|
||||
|
||||
// If this is a binary expression, return the Binary_expression
|
||||
// structure. Otherwise return NULL.
|
||||
Binary_expression*
|
||||
@ -1286,6 +1297,143 @@ class String_expression : public Expression
|
||||
Type* type_;
|
||||
};
|
||||
|
||||
// A Unary expression.
|
||||
|
||||
class Unary_expression : public Expression
|
||||
{
|
||||
public:
|
||||
Unary_expression(Operator op, Expression* expr, Location location)
|
||||
: Expression(EXPRESSION_UNARY, location),
|
||||
op_(op), escapes_(true), create_temp_(false), is_gc_root_(false),
|
||||
expr_(expr), issue_nil_check_(false)
|
||||
{ }
|
||||
|
||||
// Return the operator.
|
||||
Operator
|
||||
op() const
|
||||
{ return this->op_; }
|
||||
|
||||
// Return the operand.
|
||||
Expression*
|
||||
operand() const
|
||||
{ return this->expr_; }
|
||||
|
||||
// Record that an address expression does not escape.
|
||||
void
|
||||
set_does_not_escape()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->escapes_ = false;
|
||||
}
|
||||
|
||||
// Record that this is an address expression which should create a
|
||||
// temporary variable if necessary. This is used for method calls.
|
||||
void
|
||||
set_create_temp()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->create_temp_ = true;
|
||||
}
|
||||
|
||||
// Record that this is an address expression of a GC root, which is a
|
||||
// mutable composite literal. This used for registering GC variables.
|
||||
void
|
||||
set_is_gc_root()
|
||||
{
|
||||
go_assert(this->op_ == OPERATOR_AND);
|
||||
this->is_gc_root_ = true;
|
||||
}
|
||||
|
||||
// Apply unary opcode OP to UNC, setting NC. Return true if this
|
||||
// could be done, false if not. Issue errors for overflow.
|
||||
static bool
|
||||
eval_constant(Operator op, const Numeric_constant* unc,
|
||||
Location, Numeric_constant* nc);
|
||||
|
||||
static Expression*
|
||||
do_import(Import*);
|
||||
|
||||
protected:
|
||||
int
|
||||
do_traverse(Traverse* traverse)
|
||||
{ return Expression::traverse(&this->expr_, traverse); }
|
||||
|
||||
Expression*
|
||||
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
|
||||
|
||||
Expression*
|
||||
do_flatten(Gogo*, Named_object*, Statement_inserter*);
|
||||
|
||||
bool
|
||||
do_is_constant() const;
|
||||
|
||||
bool
|
||||
do_is_immutable() const
|
||||
{
|
||||
return (this->expr_->is_immutable()
|
||||
|| (this->op_ == OPERATOR_AND && this->expr_->is_variable()));
|
||||
}
|
||||
|
||||
bool
|
||||
do_numeric_constant_value(Numeric_constant*) const;
|
||||
|
||||
Type*
|
||||
do_type();
|
||||
|
||||
void
|
||||
do_determine_type(const Type_context*);
|
||||
|
||||
void
|
||||
do_check_types(Gogo*);
|
||||
|
||||
Expression*
|
||||
do_copy()
|
||||
{
|
||||
return Expression::make_unary(this->op_, this->expr_->copy(),
|
||||
this->location());
|
||||
}
|
||||
|
||||
bool
|
||||
do_must_eval_subexpressions_in_order(int*) const
|
||||
{ return this->op_ == OPERATOR_MULT; }
|
||||
|
||||
bool
|
||||
do_is_addressable() const
|
||||
{ return this->op_ == OPERATOR_MULT; }
|
||||
|
||||
tree
|
||||
do_get_tree(Translate_context*);
|
||||
|
||||
void
|
||||
do_export(Export*) const;
|
||||
|
||||
void
|
||||
do_dump_expression(Ast_dump_context*) const;
|
||||
|
||||
void
|
||||
do_issue_nil_check()
|
||||
{ this->issue_nil_check_ = (this->op_ == OPERATOR_MULT); }
|
||||
|
||||
private:
|
||||
// The unary operator to apply.
|
||||
Operator op_;
|
||||
// Normally true. False if this is an address expression which does
|
||||
// not escape the current function.
|
||||
bool escapes_;
|
||||
// True if this is an address expression which should create a
|
||||
// temporary variable if necessary.
|
||||
bool create_temp_;
|
||||
// True if this is an address expression for a GC root. A GC root is a
|
||||
// special struct composite literal that is mutable when addressed, meaning
|
||||
// it cannot be represented as an immutable_struct in the backend.
|
||||
bool is_gc_root_;
|
||||
// The operand.
|
||||
Expression* expr_;
|
||||
// Whether or not to issue a nil check for this expression if its address
|
||||
// is being taken.
|
||||
bool issue_nil_check_;
|
||||
};
|
||||
|
||||
// A binary expression.
|
||||
|
||||
class Binary_expression : public Expression
|
||||
|
@ -236,830 +236,6 @@ Gogo::define_builtin_function_trees()
|
||||
false);
|
||||
}
|
||||
|
||||
// Add statements to INIT_STMT_LIST which run the initialization
|
||||
// functions for imported packages. This is only used for the "main"
|
||||
// package.
|
||||
|
||||
void
|
||||
Gogo::init_imports(tree* init_stmt_list)
|
||||
{
|
||||
go_assert(this->is_main_package());
|
||||
|
||||
if (this->imported_init_fns_.empty())
|
||||
return;
|
||||
|
||||
tree fntype = build_function_type(void_type_node, void_list_node);
|
||||
|
||||
// We must call them in increasing priority order.
|
||||
std::vector<Import_init> v;
|
||||
for (std::set<Import_init>::const_iterator p =
|
||||
this->imported_init_fns_.begin();
|
||||
p != this->imported_init_fns_.end();
|
||||
++p)
|
||||
v.push_back(*p);
|
||||
std::sort(v.begin(), v.end());
|
||||
|
||||
for (std::vector<Import_init>::const_iterator p = v.begin();
|
||||
p != v.end();
|
||||
++p)
|
||||
{
|
||||
std::string user_name = p->package_name() + ".init";
|
||||
tree decl = build_decl(UNKNOWN_LOCATION, FUNCTION_DECL,
|
||||
get_identifier_from_string(user_name),
|
||||
fntype);
|
||||
const std::string& init_name(p->init_name());
|
||||
SET_DECL_ASSEMBLER_NAME(decl, get_identifier_from_string(init_name));
|
||||
TREE_PUBLIC(decl) = 1;
|
||||
DECL_EXTERNAL(decl) = 1;
|
||||
append_to_statement_list(build_call_expr(decl, 0), init_stmt_list);
|
||||
}
|
||||
}
|
||||
|
||||
// Register global variables with the garbage collector. We need to
|
||||
// register all variables which can hold a pointer value. They become
|
||||
// roots during the mark phase. We build a struct that is easy to
|
||||
// hook into a list of roots.
|
||||
|
||||
// struct __go_gc_root_list
|
||||
// {
|
||||
// struct __go_gc_root_list* __next;
|
||||
// struct __go_gc_root
|
||||
// {
|
||||
// void* __decl;
|
||||
// size_t __size;
|
||||
// } __roots[];
|
||||
// };
|
||||
|
||||
// The last entry in the roots array has a NULL decl field.
|
||||
|
||||
void
|
||||
Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
|
||||
tree* init_stmt_list)
|
||||
{
|
||||
if (var_gc.empty())
|
||||
return;
|
||||
|
||||
size_t count = var_gc.size();
|
||||
|
||||
tree root_type = Gogo::builtin_struct(NULL, "__go_gc_root", NULL_TREE, 2,
|
||||
"__next",
|
||||
ptr_type_node,
|
||||
"__size",
|
||||
sizetype);
|
||||
|
||||
tree index_type = build_index_type(size_int(count));
|
||||
tree array_type = build_array_type(root_type, index_type);
|
||||
|
||||
tree root_list_type = make_node(RECORD_TYPE);
|
||||
root_list_type = Gogo::builtin_struct(NULL, "__go_gc_root_list",
|
||||
root_list_type, 2,
|
||||
"__next",
|
||||
build_pointer_type(root_list_type),
|
||||
"__roots",
|
||||
array_type);
|
||||
|
||||
// Build an initialier for the __roots array.
|
||||
|
||||
vec<constructor_elt, va_gc> *roots_init;
|
||||
vec_alloc(roots_init, count + 1);
|
||||
|
||||
size_t i = 0;
|
||||
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
|
||||
p != var_gc.end();
|
||||
++p, ++i)
|
||||
{
|
||||
vec<constructor_elt, va_gc> *init;
|
||||
vec_alloc(init, 2);
|
||||
|
||||
constructor_elt empty = {NULL, NULL};
|
||||
constructor_elt* elt = init->quick_push(empty);
|
||||
tree field = TYPE_FIELDS(root_type);
|
||||
elt->index = field;
|
||||
Bvariable* bvar = (*p)->get_backend_variable(this, NULL);
|
||||
tree decl = var_to_tree(bvar);
|
||||
go_assert(TREE_CODE(decl) == VAR_DECL);
|
||||
elt->value = build_fold_addr_expr(decl);
|
||||
|
||||
elt = init->quick_push(empty);
|
||||
field = DECL_CHAIN(field);
|
||||
elt->index = field;
|
||||
elt->value = DECL_SIZE_UNIT(decl);
|
||||
|
||||
elt = roots_init->quick_push(empty);
|
||||
elt->index = size_int(i);
|
||||
elt->value = build_constructor(root_type, init);
|
||||
}
|
||||
|
||||
// The list ends with a NULL entry.
|
||||
|
||||
vec<constructor_elt, va_gc> *init;
|
||||
vec_alloc(init, 2);
|
||||
|
||||
constructor_elt empty = {NULL, NULL};
|
||||
constructor_elt* elt = init->quick_push(empty);
|
||||
tree field = TYPE_FIELDS(root_type);
|
||||
elt->index = field;
|
||||
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
|
||||
|
||||
elt = init->quick_push(empty);
|
||||
field = DECL_CHAIN(field);
|
||||
elt->index = field;
|
||||
elt->value = size_zero_node;
|
||||
|
||||
elt = roots_init->quick_push(empty);
|
||||
elt->index = size_int(i);
|
||||
elt->value = build_constructor(root_type, init);
|
||||
|
||||
// Build a constructor for the struct.
|
||||
|
||||
vec<constructor_elt, va_gc> *root_list_init;
|
||||
vec_alloc(root_list_init, 2);
|
||||
|
||||
elt = root_list_init->quick_push(empty);
|
||||
field = TYPE_FIELDS(root_list_type);
|
||||
elt->index = field;
|
||||
elt->value = fold_convert(TREE_TYPE(field), null_pointer_node);
|
||||
|
||||
elt = root_list_init->quick_push(empty);
|
||||
field = DECL_CHAIN(field);
|
||||
elt->index = field;
|
||||
elt->value = build_constructor(array_type, roots_init);
|
||||
|
||||
// Build a decl to register.
|
||||
|
||||
tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
|
||||
create_tmp_var_name("gc"), root_list_type);
|
||||
DECL_EXTERNAL(decl) = 0;
|
||||
TREE_PUBLIC(decl) = 0;
|
||||
TREE_STATIC(decl) = 1;
|
||||
DECL_ARTIFICIAL(decl) = 1;
|
||||
DECL_INITIAL(decl) = build_constructor(root_list_type, root_list_init);
|
||||
rest_of_decl_compilation(decl, 1, 0);
|
||||
|
||||
static tree register_gc_fndecl;
|
||||
tree call = Gogo::call_builtin(®ister_gc_fndecl,
|
||||
Linemap::predeclared_location(),
|
||||
"__go_register_gc_roots",
|
||||
1,
|
||||
void_type_node,
|
||||
build_pointer_type(root_list_type),
|
||||
build_fold_addr_expr(decl));
|
||||
if (call != error_mark_node)
|
||||
append_to_statement_list(call, init_stmt_list);
|
||||
}
|
||||
|
||||
// Create the magic initialization function. INIT_STMT_LIST is the
|
||||
// code that it needs to run.
|
||||
|
||||
void
|
||||
Gogo::write_initialization_function(Named_object* initfn, tree init_stmt_list)
|
||||
{
|
||||
// Make sure that we thought we needed an initialization function,
|
||||
// as otherwise we will not have reported it in the export data.
|
||||
go_assert(this->is_main_package() || this->need_init_fn_);
|
||||
|
||||
if (initfn == NULL)
|
||||
initfn = this->initialization_function_decl();
|
||||
|
||||
Bfunction* fndecl = initfn->func_value()->get_or_make_decl(this, initfn);
|
||||
Location loc = this->package_->location();
|
||||
std::vector<Bvariable*> vars;
|
||||
this->backend()->block(fndecl, NULL, vars, loc, loc);
|
||||
|
||||
if (!this->backend()->function_set_body(fndecl, tree_to_stat(init_stmt_list)))
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
return;
|
||||
}
|
||||
gimplify_function_tree(function_to_tree(fndecl));
|
||||
cgraph_add_new_function(function_to_tree(fndecl), false);
|
||||
}
|
||||
|
||||
// Search for references to VAR in any statements or called functions.
|
||||
|
||||
class Find_var : public Traverse
|
||||
{
|
||||
public:
|
||||
// A hash table we use to avoid looping. The index is the name of a
|
||||
// named object. We only look through objects defined in this
|
||||
// package.
|
||||
typedef Unordered_set(const void*) Seen_objects;
|
||||
|
||||
Find_var(Named_object* var, Seen_objects* seen_objects)
|
||||
: Traverse(traverse_expressions),
|
||||
var_(var), seen_objects_(seen_objects), found_(false)
|
||||
{ }
|
||||
|
||||
// Whether the variable was found.
|
||||
bool
|
||||
found() const
|
||||
{ return this->found_; }
|
||||
|
||||
int
|
||||
expression(Expression**);
|
||||
|
||||
private:
|
||||
// The variable we are looking for.
|
||||
Named_object* var_;
|
||||
// Names of objects we have already seen.
|
||||
Seen_objects* seen_objects_;
|
||||
// True if the variable was found.
|
||||
bool found_;
|
||||
};
|
||||
|
||||
// See if EXPR refers to VAR, looking through function calls and
|
||||
// variable initializations.
|
||||
|
||||
int
|
||||
Find_var::expression(Expression** pexpr)
|
||||
{
|
||||
Expression* e = *pexpr;
|
||||
|
||||
Var_expression* ve = e->var_expression();
|
||||
if (ve != NULL)
|
||||
{
|
||||
Named_object* v = ve->named_object();
|
||||
if (v == this->var_)
|
||||
{
|
||||
this->found_ = true;
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
|
||||
if (v->is_variable() && v->package() == NULL)
|
||||
{
|
||||
Expression* init = v->var_value()->init();
|
||||
if (init != NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(v);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this name.
|
||||
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We traverse the code of any function we see. Note that this
|
||||
// means that we will traverse the code of a function whose address
|
||||
// is taken even if it is not called.
|
||||
Func_expression* fe = e->func_expression();
|
||||
if (fe != NULL)
|
||||
{
|
||||
const Named_object* f = fe->named_object();
|
||||
if (f->is_function() && f->package() == NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(f);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this name.
|
||||
if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Temporary_reference_expression* tre = e->temporary_reference_expression();
|
||||
if (tre != NULL)
|
||||
{
|
||||
Temporary_statement* ts = tre->statement();
|
||||
Expression* init = ts->init();
|
||||
if (init != NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(ts);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this temporary
|
||||
// statement.
|
||||
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// Return true if EXPR, PREINIT, or DEP refers to VAR.
|
||||
|
||||
static bool
|
||||
expression_requires(Expression* expr, Block* preinit, Named_object* dep,
|
||||
Named_object* var)
|
||||
{
|
||||
Find_var::Seen_objects seen_objects;
|
||||
Find_var find_var(var, &seen_objects);
|
||||
if (expr != NULL)
|
||||
Expression::traverse(&expr, &find_var);
|
||||
if (preinit != NULL)
|
||||
preinit->traverse(&find_var);
|
||||
if (dep != NULL)
|
||||
{
|
||||
Expression* init = dep->var_value()->init();
|
||||
if (init != NULL)
|
||||
Expression::traverse(&init, &find_var);
|
||||
if (dep->var_value()->has_pre_init())
|
||||
dep->var_value()->preinit()->traverse(&find_var);
|
||||
}
|
||||
|
||||
return find_var.found();
|
||||
}
|
||||
|
||||
// Sort variable initializations. If the initialization expression
|
||||
// for variable A refers directly or indirectly to the initialization
|
||||
// expression for variable B, then we must initialize B before A.
|
||||
|
||||
class Var_init
|
||||
{
|
||||
public:
|
||||
Var_init()
|
||||
: var_(NULL), init_(NULL)
|
||||
{ }
|
||||
|
||||
Var_init(Named_object* var, Bstatement* init)
|
||||
: var_(var), init_(init)
|
||||
{ }
|
||||
|
||||
// Return the variable.
|
||||
Named_object*
|
||||
var() const
|
||||
{ return this->var_; }
|
||||
|
||||
// Return the initialization expression.
|
||||
Bstatement*
|
||||
init() const
|
||||
{ return this->init_; }
|
||||
|
||||
private:
|
||||
// The variable being initialized.
|
||||
Named_object* var_;
|
||||
// The initialization statement.
|
||||
Bstatement* init_;
|
||||
};
|
||||
|
||||
typedef std::list<Var_init> Var_inits;
|
||||
|
||||
// Sort the variable initializations. The rule we follow is that we
|
||||
// emit them in the order they appear in the array, except that if the
|
||||
// initialization expression for a variable V1 depends upon another
|
||||
// variable V2 then we initialize V1 after V2.
|
||||
|
||||
static void
|
||||
sort_var_inits(Gogo* gogo, Var_inits* var_inits)
|
||||
{
|
||||
typedef std::pair<Named_object*, Named_object*> No_no;
|
||||
typedef std::map<No_no, bool> Cache;
|
||||
Cache cache;
|
||||
|
||||
Var_inits ready;
|
||||
while (!var_inits->empty())
|
||||
{
|
||||
Var_inits::iterator p1 = var_inits->begin();
|
||||
Named_object* var = p1->var();
|
||||
Expression* init = var->var_value()->init();
|
||||
Block* preinit = var->var_value()->preinit();
|
||||
Named_object* dep = gogo->var_depends_on(var->var_value());
|
||||
|
||||
// Start walking through the list to see which variables VAR
|
||||
// needs to wait for.
|
||||
Var_inits::iterator p2 = p1;
|
||||
++p2;
|
||||
|
||||
for (; p2 != var_inits->end(); ++p2)
|
||||
{
|
||||
Named_object* p2var = p2->var();
|
||||
No_no key(var, p2var);
|
||||
std::pair<Cache::iterator, bool> ins =
|
||||
cache.insert(std::make_pair(key, false));
|
||||
if (ins.second)
|
||||
ins.first->second = expression_requires(init, preinit, dep, p2var);
|
||||
if (ins.first->second)
|
||||
{
|
||||
// Check for cycles.
|
||||
key = std::make_pair(p2var, var);
|
||||
ins = cache.insert(std::make_pair(key, false));
|
||||
if (ins.second)
|
||||
ins.first->second =
|
||||
expression_requires(p2var->var_value()->init(),
|
||||
p2var->var_value()->preinit(),
|
||||
gogo->var_depends_on(p2var->var_value()),
|
||||
var);
|
||||
if (ins.first->second)
|
||||
{
|
||||
error_at(var->location(),
|
||||
("initialization expressions for %qs and "
|
||||
"%qs depend upon each other"),
|
||||
var->message_name().c_str(),
|
||||
p2var->message_name().c_str());
|
||||
inform(p2->var()->location(), "%qs defined here",
|
||||
p2var->message_name().c_str());
|
||||
p2 = var_inits->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't emit P1 until P2 is emitted. Move P1.
|
||||
Var_inits::iterator p3 = p2;
|
||||
++p3;
|
||||
var_inits->splice(p3, *var_inits, p1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p2 == var_inits->end())
|
||||
{
|
||||
// VAR does not depends upon any other initialization expressions.
|
||||
|
||||
// Check for a loop of VAR on itself. We only do this if
|
||||
// INIT is not NULL and there is no dependency; when INIT is
|
||||
// NULL, it means that PREINIT sets VAR, which we will
|
||||
// interpret as a loop.
|
||||
if (init != NULL && dep == NULL
|
||||
&& expression_requires(init, preinit, NULL, var))
|
||||
error_at(var->location(),
|
||||
"initialization expression for %qs depends upon itself",
|
||||
var->message_name().c_str());
|
||||
ready.splice(ready.end(), *var_inits, p1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now READY is the list in the desired initialization order.
|
||||
var_inits->swap(ready);
|
||||
}
|
||||
|
||||
// Write out the global definitions.
|
||||
|
||||
void
|
||||
Gogo::write_globals()
|
||||
{
|
||||
this->build_interface_method_tables();
|
||||
|
||||
Bindings* bindings = this->current_bindings();
|
||||
|
||||
for (Bindings::const_declarations_iterator p = bindings->begin_declarations();
|
||||
p != bindings->end_declarations();
|
||||
++p)
|
||||
{
|
||||
// If any function declarations needed a descriptor, make sure
|
||||
// we build it.
|
||||
Named_object* no = p->second;
|
||||
if (no->is_function_declaration())
|
||||
no->func_declaration_value()->build_backend_descriptor(this);
|
||||
}
|
||||
|
||||
size_t count_definitions = bindings->size_definitions();
|
||||
size_t count = count_definitions;
|
||||
|
||||
tree* vec = new tree[count];
|
||||
|
||||
Named_object* init_fndecl = NULL;
|
||||
tree init_stmt_list = NULL_TREE;
|
||||
|
||||
if (this->is_main_package())
|
||||
this->init_imports(&init_stmt_list);
|
||||
|
||||
// A list of variable initializations.
|
||||
Var_inits var_inits;
|
||||
|
||||
// A list of variables which need to be registered with the garbage
|
||||
// collector.
|
||||
std::vector<Named_object*> var_gc;
|
||||
var_gc.reserve(count);
|
||||
|
||||
tree var_init_stmt_list = NULL_TREE;
|
||||
size_t i = 0;
|
||||
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
|
||||
p != bindings->end_definitions();
|
||||
++p, ++i)
|
||||
{
|
||||
Named_object* no = *p;
|
||||
|
||||
go_assert(i < count);
|
||||
|
||||
go_assert(!no->is_type_declaration() && !no->is_function_declaration());
|
||||
// There is nothing to do for a package.
|
||||
if (no->is_package())
|
||||
{
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There is nothing to do for an object which was imported from
|
||||
// a different package into the global scope.
|
||||
if (no->package() != NULL)
|
||||
{
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip blank named functions and constants.
|
||||
if ((no->is_function() && no->func_value()->is_sink())
|
||||
|| (no->is_const() && no->const_value()->is_sink()))
|
||||
{
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
|
||||
// There is nothing useful we can output for constants which
|
||||
// have ideal or non-integral type.
|
||||
if (no->is_const())
|
||||
{
|
||||
Type* type = no->const_value()->type();
|
||||
if (type == NULL)
|
||||
type = no->const_value()->expr()->type();
|
||||
if (type->is_abstract() || type->integer_type() == NULL)
|
||||
{
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!no->is_variable())
|
||||
{
|
||||
vec[i] = no->get_tree(this, NULL);
|
||||
if (vec[i] == error_mark_node)
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Bvariable* var = no->get_backend_variable(this, NULL);
|
||||
vec[i] = var_to_tree(var);
|
||||
if (vec[i] == error_mark_node)
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
--i;
|
||||
--count;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for a sink variable, which may be used to run an
|
||||
// initializer purely for its side effects.
|
||||
bool is_sink = no->name()[0] == '_' && no->name()[1] == '.';
|
||||
|
||||
Bstatement* var_init_stmt = NULL;
|
||||
if (!no->var_value()->has_pre_init())
|
||||
{
|
||||
Bexpression* var_binit = no->var_value()->get_init(this, NULL);
|
||||
if (var_binit == NULL)
|
||||
;
|
||||
else if (TREE_CONSTANT(expr_to_tree(var_binit)))
|
||||
{
|
||||
if (expression_requires(no->var_value()->init(), NULL,
|
||||
this->var_depends_on(no->var_value()),
|
||||
no))
|
||||
error_at(no->location(),
|
||||
"initialization expression for %qs depends "
|
||||
"upon itself",
|
||||
no->message_name().c_str());
|
||||
this->backend()->global_variable_set_init(var, var_binit);
|
||||
}
|
||||
else if (is_sink)
|
||||
var_init_stmt =
|
||||
this->backend()->expression_statement(var_binit);
|
||||
else
|
||||
{
|
||||
Location loc = no->var_value()->location();
|
||||
Bexpression* var_expr =
|
||||
this->backend()->var_expression(var, loc);
|
||||
var_init_stmt =
|
||||
this->backend()->assignment_statement(var_expr, var_binit,
|
||||
loc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are going to create temporary variables which
|
||||
// means that we need an fndecl.
|
||||
if (init_fndecl == NULL)
|
||||
init_fndecl = this->initialization_function_decl();
|
||||
|
||||
Bvariable* var_decl = is_sink ? NULL : var;
|
||||
var_init_stmt =
|
||||
no->var_value()->get_init_block(this, init_fndecl, var_decl);
|
||||
}
|
||||
|
||||
if (var_init_stmt != NULL)
|
||||
{
|
||||
if (no->var_value()->init() == NULL
|
||||
&& !no->var_value()->has_pre_init())
|
||||
append_to_statement_list(stat_to_tree(var_init_stmt),
|
||||
&var_init_stmt_list);
|
||||
else
|
||||
var_inits.push_back(Var_init(no, var_init_stmt));
|
||||
}
|
||||
else if (this->var_depends_on(no->var_value()) != NULL)
|
||||
{
|
||||
// This variable is initialized from something that is
|
||||
// not in its init or preinit. This variable needs to
|
||||
// participate in dependency analysis sorting, in case
|
||||
// some other variable depends on this one.
|
||||
Btype* int_btype =
|
||||
Type::lookup_integer_type("int")->get_backend(this);
|
||||
Bexpression* zero = this->backend()->zero_expression(int_btype);
|
||||
Bstatement* zero_stmt =
|
||||
this->backend()->expression_statement(zero);
|
||||
var_inits.push_back(Var_init(no, zero_stmt));
|
||||
}
|
||||
|
||||
if (!is_sink && no->var_value()->type()->has_pointer())
|
||||
var_gc.push_back(no);
|
||||
}
|
||||
}
|
||||
|
||||
// Register global variables with the garbage collector.
|
||||
this->register_gc_vars(var_gc, &init_stmt_list);
|
||||
|
||||
// Simple variable initializations, after all variables are
|
||||
// registered.
|
||||
append_to_statement_list(var_init_stmt_list, &init_stmt_list);
|
||||
|
||||
// Complex variable initializations, first sorting them into a
|
||||
// workable order.
|
||||
if (!var_inits.empty())
|
||||
{
|
||||
sort_var_inits(this, &var_inits);
|
||||
for (Var_inits::const_iterator p = var_inits.begin();
|
||||
p != var_inits.end();
|
||||
++p)
|
||||
append_to_statement_list(stat_to_tree(p->init()), &init_stmt_list);
|
||||
}
|
||||
|
||||
// After all the variables are initialized, call the "init"
|
||||
// functions if there are any.
|
||||
for (std::vector<Named_object*>::const_iterator p =
|
||||
this->init_functions_.begin();
|
||||
p != this->init_functions_.end();
|
||||
++p)
|
||||
{
|
||||
tree decl = (*p)->get_tree(this, NULL);
|
||||
tree call = build_call_expr(decl, 0);
|
||||
append_to_statement_list(call, &init_stmt_list);
|
||||
}
|
||||
|
||||
// Set up a magic function to do all the initialization actions.
|
||||
// This will be called if this package is imported.
|
||||
if (init_stmt_list != NULL
|
||||
|| this->need_init_fn_
|
||||
|| this->is_main_package())
|
||||
this->write_initialization_function(init_fndecl, init_stmt_list);
|
||||
|
||||
// We should not have seen any new bindings created during the
|
||||
// conversion.
|
||||
go_assert(count_definitions == this->current_bindings()->size_definitions());
|
||||
|
||||
// Pass everything back to the middle-end.
|
||||
|
||||
wrapup_global_declarations(vec, count);
|
||||
|
||||
finalize_compilation_unit();
|
||||
|
||||
check_global_declarations(vec, count);
|
||||
emit_debug_global_declarations(vec, count);
|
||||
|
||||
delete[] vec;
|
||||
}
|
||||
|
||||
// Get a tree for a named object.
|
||||
|
||||
tree
|
||||
Named_object::get_tree(Gogo* gogo, Named_object* function)
|
||||
{
|
||||
if (this->tree_ != NULL_TREE)
|
||||
return this->tree_;
|
||||
|
||||
if (Gogo::is_erroneous_name(this->name_))
|
||||
{
|
||||
this->tree_ = error_mark_node;
|
||||
return error_mark_node;
|
||||
}
|
||||
|
||||
tree decl;
|
||||
switch (this->classification_)
|
||||
{
|
||||
case NAMED_OBJECT_CONST:
|
||||
{
|
||||
Translate_context subcontext(gogo, function, NULL, NULL);
|
||||
Type* type = this->u_.const_value->type();
|
||||
Location loc = this->location();
|
||||
|
||||
Expression* const_ref = Expression::make_const_reference(this, loc);
|
||||
Bexpression* const_decl =
|
||||
tree_to_expr(const_ref->get_tree(&subcontext));
|
||||
if (type != NULL && type->is_numeric_type())
|
||||
{
|
||||
Btype* btype = type->get_backend(gogo);
|
||||
std::string name = this->get_id(gogo);
|
||||
const_decl =
|
||||
gogo->backend()->named_constant_expression(btype, name,
|
||||
const_decl, loc);
|
||||
}
|
||||
decl = expr_to_tree(const_decl);
|
||||
}
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_TYPE:
|
||||
{
|
||||
Named_type* named_type = this->u_.type_value;
|
||||
tree type_tree = type_to_tree(named_type->get_backend(gogo));
|
||||
if (type_tree == error_mark_node)
|
||||
decl = error_mark_node;
|
||||
else
|
||||
{
|
||||
decl = TYPE_NAME(type_tree);
|
||||
go_assert(decl != NULL_TREE);
|
||||
|
||||
// We need to produce a type descriptor for every named
|
||||
// type, and for a pointer to every named type, since
|
||||
// other files or packages might refer to them. We need
|
||||
// to do this even for hidden types, because they might
|
||||
// still be returned by some function. Simply calling the
|
||||
// type_descriptor method is enough to create the type
|
||||
// descriptor, even though we don't do anything with it.
|
||||
if (this->package_ == NULL)
|
||||
{
|
||||
named_type->
|
||||
type_descriptor_pointer(gogo,
|
||||
Linemap::predeclared_location());
|
||||
Type* pn = Type::make_pointer_type(named_type);
|
||||
pn->type_descriptor_pointer(gogo,
|
||||
Linemap::predeclared_location());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_TYPE_DECLARATION:
|
||||
error("reference to undefined type %qs",
|
||||
this->message_name().c_str());
|
||||
return error_mark_node;
|
||||
|
||||
case NAMED_OBJECT_VAR:
|
||||
case NAMED_OBJECT_RESULT_VAR:
|
||||
case NAMED_OBJECT_SINK:
|
||||
go_unreachable();
|
||||
|
||||
case NAMED_OBJECT_FUNC:
|
||||
{
|
||||
Function* func = this->u_.func_value;
|
||||
decl = function_to_tree(func->get_or_make_decl(gogo, this));
|
||||
if (decl != error_mark_node)
|
||||
{
|
||||
if (func->block() != NULL)
|
||||
{
|
||||
if (DECL_STRUCT_FUNCTION(decl) == NULL)
|
||||
push_struct_function(decl);
|
||||
else
|
||||
push_cfun(DECL_STRUCT_FUNCTION(decl));
|
||||
|
||||
cfun->function_start_locus = func->location().gcc_location();
|
||||
cfun->function_end_locus =
|
||||
func->block()->end_location().gcc_location();
|
||||
|
||||
func->build(gogo, this);
|
||||
|
||||
gimplify_function_tree(decl);
|
||||
|
||||
cgraph_finalize_function(decl, true);
|
||||
|
||||
pop_cfun();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_ERRONEOUS:
|
||||
decl = error_mark_node;
|
||||
break;
|
||||
|
||||
default:
|
||||
go_unreachable();
|
||||
}
|
||||
|
||||
if (TREE_TYPE(decl) == error_mark_node)
|
||||
decl = error_mark_node;
|
||||
|
||||
tree ret = decl;
|
||||
|
||||
this->tree_ = ret;
|
||||
|
||||
if (ret != error_mark_node)
|
||||
go_preserve_from_gc(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Get the backend representation.
|
||||
|
||||
Bfunction*
|
||||
@ -1106,15 +282,6 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no)
|
||||
return this->fndecl_;
|
||||
}
|
||||
|
||||
// Return the function's decl after it has been built.
|
||||
|
||||
tree
|
||||
Function::get_decl() const
|
||||
{
|
||||
go_assert(this->fndecl_ != NULL);
|
||||
return function_to_tree(this->fndecl_);
|
||||
}
|
||||
|
||||
// Build the descriptor for a function declaration. This won't
|
||||
// necessarily happen if the package has just a declaration for the
|
||||
// function and no other reference to it, but we may still need the
|
||||
@ -1214,55 +381,6 @@ go_type_for_mode(enum machine_mode mode, int unsignedp)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
// Build a builtin struct with a list of fields. The name is
|
||||
// STRUCT_NAME. STRUCT_TYPE is NULL_TREE or an empty RECORD_TYPE
|
||||
// node; this exists so that the struct can have fields which point to
|
||||
// itself. If PTYPE is not NULL, store the result in *PTYPE. There
|
||||
// are NFIELDS fields. Each field is a name (a const char*) followed
|
||||
// by a type (a tree).
|
||||
|
||||
tree
|
||||
Gogo::builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
|
||||
int nfields, ...)
|
||||
{
|
||||
if (ptype != NULL && *ptype != NULL_TREE)
|
||||
return *ptype;
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, nfields);
|
||||
|
||||
tree fields = NULL_TREE;
|
||||
for (int i = 0; i < nfields; ++i)
|
||||
{
|
||||
const char* field_name = va_arg(ap, const char*);
|
||||
tree type = va_arg(ap, tree);
|
||||
if (type == error_mark_node)
|
||||
{
|
||||
if (ptype != NULL)
|
||||
*ptype = error_mark_node;
|
||||
return error_mark_node;
|
||||
}
|
||||
tree field = build_decl(BUILTINS_LOCATION, FIELD_DECL,
|
||||
get_identifier(field_name), type);
|
||||
DECL_CHAIN(field) = fields;
|
||||
fields = field;
|
||||
}
|
||||
|
||||
va_end(ap);
|
||||
|
||||
if (struct_type == NULL_TREE)
|
||||
struct_type = make_node(RECORD_TYPE);
|
||||
finish_builtin_struct(struct_type, struct_name, fields, NULL_TREE);
|
||||
|
||||
if (ptype != NULL)
|
||||
{
|
||||
go_preserve_from_gc(struct_type);
|
||||
*ptype = struct_type;
|
||||
}
|
||||
|
||||
return struct_type;
|
||||
}
|
||||
|
||||
// Build a constructor for a slice. SLICE_TYPE_TREE is the type of
|
||||
// the slice. VALUES is the value pointer and COUNT is the number of
|
||||
// entries. If CAPACITY is not NULL, it is the capacity; otherwise
|
||||
|
@ -575,6 +575,164 @@ Gogo::current_bindings() const
|
||||
return this->globals_;
|
||||
}
|
||||
|
||||
// Add statements to INIT_STMTS which run the initialization
|
||||
// functions for imported packages. This is only used for the "main"
|
||||
// package.
|
||||
|
||||
void
|
||||
Gogo::init_imports(std::vector<Bstatement*>& init_stmts)
|
||||
{
|
||||
go_assert(this->is_main_package());
|
||||
|
||||
if (this->imported_init_fns_.empty())
|
||||
return;
|
||||
|
||||
Location unknown_loc = Linemap::unknown_location();
|
||||
Function_type* func_type =
|
||||
Type::make_function_type(NULL, NULL, NULL, unknown_loc);
|
||||
Btype* fntype = func_type->get_backend_fntype(this);
|
||||
|
||||
// We must call them in increasing priority order.
|
||||
std::vector<Import_init> v;
|
||||
for (std::set<Import_init>::const_iterator p =
|
||||
this->imported_init_fns_.begin();
|
||||
p != this->imported_init_fns_.end();
|
||||
++p)
|
||||
v.push_back(*p);
|
||||
std::sort(v.begin(), v.end());
|
||||
|
||||
// We build calls to the init functions, which take no arguments.
|
||||
std::vector<Bexpression*> empty_args;
|
||||
for (std::vector<Import_init>::const_iterator p = v.begin();
|
||||
p != v.end();
|
||||
++p)
|
||||
{
|
||||
std::string user_name = p->package_name() + ".init";
|
||||
const std::string& init_name(p->init_name());
|
||||
|
||||
Bfunction* pfunc = this->backend()->function(fntype, user_name, init_name,
|
||||
true, true, true, false,
|
||||
false, unknown_loc);
|
||||
Bexpression* pfunc_code =
|
||||
this->backend()->function_code_expression(pfunc, unknown_loc);
|
||||
Bexpression* pfunc_call =
|
||||
this->backend()->call_expression(pfunc_code, empty_args, unknown_loc);
|
||||
init_stmts.push_back(this->backend()->expression_statement(pfunc_call));
|
||||
}
|
||||
}
|
||||
|
||||
// Register global variables with the garbage collector. We need to
|
||||
// register all variables which can hold a pointer value. They become
|
||||
// roots during the mark phase. We build a struct that is easy to
|
||||
// hook into a list of roots.
|
||||
|
||||
// struct __go_gc_root_list
|
||||
// {
|
||||
// struct __go_gc_root_list* __next;
|
||||
// struct __go_gc_root
|
||||
// {
|
||||
// void* __decl;
|
||||
// size_t __size;
|
||||
// } __roots[];
|
||||
// };
|
||||
|
||||
// The last entry in the roots array has a NULL decl field.
|
||||
|
||||
void
|
||||
Gogo::register_gc_vars(const std::vector<Named_object*>& var_gc,
|
||||
std::vector<Bstatement*>& init_stmts)
|
||||
{
|
||||
if (var_gc.empty())
|
||||
return;
|
||||
|
||||
Type* pvt = Type::make_pointer_type(Type::make_void_type());
|
||||
Type* uint_type = Type::lookup_integer_type("uint");
|
||||
Struct_type* root_type = Type::make_builtin_struct_type(2,
|
||||
"__decl", pvt,
|
||||
"__size", uint_type);
|
||||
|
||||
Location builtin_loc = Linemap::predeclared_location();
|
||||
size_t count = var_gc.size();
|
||||
mpz_t lenval;
|
||||
mpz_init_set_ui(lenval, count);
|
||||
Expression* length = Expression::make_integer(&lenval, NULL, builtin_loc);
|
||||
mpz_clear(lenval);
|
||||
|
||||
Array_type* root_array_type = Type::make_array_type(root_type, length);
|
||||
Type* ptdt = Type::make_type_descriptor_ptr_type();
|
||||
Struct_type* root_list_type =
|
||||
Type::make_builtin_struct_type(2,
|
||||
"__next", ptdt,
|
||||
"__roots", root_array_type);
|
||||
|
||||
// Build an initializer for the __roots array.
|
||||
|
||||
Expression_list* roots_init = new Expression_list();
|
||||
|
||||
size_t i = 0;
|
||||
for (std::vector<Named_object*>::const_iterator p = var_gc.begin();
|
||||
p != var_gc.end();
|
||||
++p, ++i)
|
||||
{
|
||||
Expression_list* init = new Expression_list();
|
||||
|
||||
Location no_loc = (*p)->location();
|
||||
Expression* decl = Expression::make_var_reference(*p, no_loc);
|
||||
Expression* decl_addr =
|
||||
Expression::make_unary(OPERATOR_AND, decl, no_loc);
|
||||
init->push_back(decl_addr);
|
||||
|
||||
Expression* decl_size =
|
||||
Expression::make_type_info(decl->type(), Expression::TYPE_INFO_SIZE);
|
||||
init->push_back(decl_size);
|
||||
|
||||
Expression* root_ctor =
|
||||
Expression::make_struct_composite_literal(root_type, init, no_loc);
|
||||
roots_init->push_back(root_ctor);
|
||||
}
|
||||
|
||||
// The list ends with a NULL entry.
|
||||
|
||||
Expression_list* null_init = new Expression_list();
|
||||
Expression* nil = Expression::make_nil(builtin_loc);
|
||||
null_init->push_back(nil);
|
||||
|
||||
mpz_t zval;
|
||||
mpz_init_set_ui(zval, 0UL);
|
||||
Expression* zero = Expression::make_integer(&zval, NULL, builtin_loc);
|
||||
mpz_clear(zval);
|
||||
null_init->push_back(zero);
|
||||
|
||||
Expression* null_root_ctor =
|
||||
Expression::make_struct_composite_literal(root_type, null_init,
|
||||
builtin_loc);
|
||||
roots_init->push_back(null_root_ctor);
|
||||
|
||||
// Build a constructor for the struct.
|
||||
|
||||
Expression_list* root_list_init = new Expression_list();
|
||||
root_list_init->push_back(nil);
|
||||
|
||||
Expression* roots_ctor =
|
||||
Expression::make_array_composite_literal(root_array_type, roots_init,
|
||||
builtin_loc);
|
||||
root_list_init->push_back(roots_ctor);
|
||||
|
||||
Expression* root_list_ctor =
|
||||
Expression::make_struct_composite_literal(root_list_type, root_list_init,
|
||||
builtin_loc);
|
||||
|
||||
Expression* root_addr = Expression::make_unary(OPERATOR_AND, root_list_ctor,
|
||||
builtin_loc);
|
||||
root_addr->unary_expression()->set_is_gc_root();
|
||||
Expression* register_roots = Runtime::make_call(Runtime::REGISTER_GC_ROOTS,
|
||||
builtin_loc, 1, root_addr);
|
||||
|
||||
Translate_context context(this, NULL, NULL, NULL);
|
||||
Bexpression* bcall = tree_to_expr(register_roots->get_tree(&context));
|
||||
init_stmts.push_back(this->backend()->expression_statement(bcall));
|
||||
}
|
||||
|
||||
// Get the name to use for the import control function. If there is a
|
||||
// global function or variable, then we know that that name must be
|
||||
// unique in the link, and we use it as the basis for our name.
|
||||
@ -614,6 +772,521 @@ Gogo::initialization_function_decl()
|
||||
return Named_object::make_function(name, NULL, initfn);
|
||||
}
|
||||
|
||||
// Create the magic initialization function. CODE_STMT is the
|
||||
// code that it needs to run.
|
||||
|
||||
Named_object*
|
||||
Gogo::create_initialization_function(Named_object* initfn,
|
||||
Bstatement* code_stmt)
|
||||
{
|
||||
// Make sure that we thought we needed an initialization function,
|
||||
// as otherwise we will not have reported it in the export data.
|
||||
go_assert(this->is_main_package() || this->need_init_fn_);
|
||||
|
||||
if (initfn == NULL)
|
||||
initfn = this->initialization_function_decl();
|
||||
|
||||
// Bind the initialization function code to a block.
|
||||
Bfunction* fndecl = initfn->func_value()->get_or_make_decl(this, initfn);
|
||||
Location pkg_loc = this->package_->location();
|
||||
std::vector<Bvariable*> vars;
|
||||
this->backend()->block(fndecl, NULL, vars, pkg_loc, pkg_loc);
|
||||
|
||||
if (!this->backend()->function_set_body(fndecl, code_stmt))
|
||||
{
|
||||
go_assert(saw_errors());
|
||||
return NULL;
|
||||
}
|
||||
return initfn;
|
||||
}
|
||||
|
||||
// Search for references to VAR in any statements or called functions.
|
||||
|
||||
class Find_var : public Traverse
|
||||
{
|
||||
public:
|
||||
// A hash table we use to avoid looping. The index is the name of a
|
||||
// named object. We only look through objects defined in this
|
||||
// package.
|
||||
typedef Unordered_set(const void*) Seen_objects;
|
||||
|
||||
Find_var(Named_object* var, Seen_objects* seen_objects)
|
||||
: Traverse(traverse_expressions),
|
||||
var_(var), seen_objects_(seen_objects), found_(false)
|
||||
{ }
|
||||
|
||||
// Whether the variable was found.
|
||||
bool
|
||||
found() const
|
||||
{ return this->found_; }
|
||||
|
||||
int
|
||||
expression(Expression**);
|
||||
|
||||
private:
|
||||
// The variable we are looking for.
|
||||
Named_object* var_;
|
||||
// Names of objects we have already seen.
|
||||
Seen_objects* seen_objects_;
|
||||
// True if the variable was found.
|
||||
bool found_;
|
||||
};
|
||||
|
||||
// See if EXPR refers to VAR, looking through function calls and
|
||||
// variable initializations.
|
||||
|
||||
int
|
||||
Find_var::expression(Expression** pexpr)
|
||||
{
|
||||
Expression* e = *pexpr;
|
||||
|
||||
Var_expression* ve = e->var_expression();
|
||||
if (ve != NULL)
|
||||
{
|
||||
Named_object* v = ve->named_object();
|
||||
if (v == this->var_)
|
||||
{
|
||||
this->found_ = true;
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
|
||||
if (v->is_variable() && v->package() == NULL)
|
||||
{
|
||||
Expression* init = v->var_value()->init();
|
||||
if (init != NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(v);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this name.
|
||||
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We traverse the code of any function we see. Note that this
|
||||
// means that we will traverse the code of a function whose address
|
||||
// is taken even if it is not called.
|
||||
Func_expression* fe = e->func_expression();
|
||||
if (fe != NULL)
|
||||
{
|
||||
const Named_object* f = fe->named_object();
|
||||
if (f->is_function() && f->package() == NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(f);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this name.
|
||||
if (f->func_value()->block()->traverse(this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Temporary_reference_expression* tre = e->temporary_reference_expression();
|
||||
if (tre != NULL)
|
||||
{
|
||||
Temporary_statement* ts = tre->statement();
|
||||
Expression* init = ts->init();
|
||||
if (init != NULL)
|
||||
{
|
||||
std::pair<Seen_objects::iterator, bool> ins =
|
||||
this->seen_objects_->insert(ts);
|
||||
if (ins.second)
|
||||
{
|
||||
// This is the first time we have seen this temporary
|
||||
// statement.
|
||||
if (Expression::traverse(&init, this) == TRAVERSE_EXIT)
|
||||
return TRAVERSE_EXIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRAVERSE_CONTINUE;
|
||||
}
|
||||
|
||||
// Return true if EXPR, PREINIT, or DEP refers to VAR.
|
||||
|
||||
static bool
|
||||
expression_requires(Expression* expr, Block* preinit, Named_object* dep,
|
||||
Named_object* var)
|
||||
{
|
||||
Find_var::Seen_objects seen_objects;
|
||||
Find_var find_var(var, &seen_objects);
|
||||
if (expr != NULL)
|
||||
Expression::traverse(&expr, &find_var);
|
||||
if (preinit != NULL)
|
||||
preinit->traverse(&find_var);
|
||||
if (dep != NULL)
|
||||
{
|
||||
Expression* init = dep->var_value()->init();
|
||||
if (init != NULL)
|
||||
Expression::traverse(&init, &find_var);
|
||||
if (dep->var_value()->has_pre_init())
|
||||
dep->var_value()->preinit()->traverse(&find_var);
|
||||
}
|
||||
|
||||
return find_var.found();
|
||||
}
|
||||
|
||||
// Sort variable initializations. If the initialization expression
|
||||
// for variable A refers directly or indirectly to the initialization
|
||||
// expression for variable B, then we must initialize B before A.
|
||||
|
||||
class Var_init
|
||||
{
|
||||
public:
|
||||
Var_init()
|
||||
: var_(NULL), init_(NULL)
|
||||
{ }
|
||||
|
||||
Var_init(Named_object* var, Bstatement* init)
|
||||
: var_(var), init_(init)
|
||||
{ }
|
||||
|
||||
// Return the variable.
|
||||
Named_object*
|
||||
var() const
|
||||
{ return this->var_; }
|
||||
|
||||
// Return the initialization expression.
|
||||
Bstatement*
|
||||
init() const
|
||||
{ return this->init_; }
|
||||
|
||||
private:
|
||||
// The variable being initialized.
|
||||
Named_object* var_;
|
||||
// The initialization statement.
|
||||
Bstatement* init_;
|
||||
};
|
||||
|
||||
typedef std::list<Var_init> Var_inits;
|
||||
|
||||
// Sort the variable initializations. The rule we follow is that we
|
||||
// emit them in the order they appear in the array, except that if the
|
||||
// initialization expression for a variable V1 depends upon another
|
||||
// variable V2 then we initialize V1 after V2.
|
||||
|
||||
static void
|
||||
sort_var_inits(Gogo* gogo, Var_inits* var_inits)
|
||||
{
|
||||
typedef std::pair<Named_object*, Named_object*> No_no;
|
||||
typedef std::map<No_no, bool> Cache;
|
||||
Cache cache;
|
||||
|
||||
Var_inits ready;
|
||||
while (!var_inits->empty())
|
||||
{
|
||||
Var_inits::iterator p1 = var_inits->begin();
|
||||
Named_object* var = p1->var();
|
||||
Expression* init = var->var_value()->init();
|
||||
Block* preinit = var->var_value()->preinit();
|
||||
Named_object* dep = gogo->var_depends_on(var->var_value());
|
||||
|
||||
// Start walking through the list to see which variables VAR
|
||||
// needs to wait for.
|
||||
Var_inits::iterator p2 = p1;
|
||||
++p2;
|
||||
|
||||
for (; p2 != var_inits->end(); ++p2)
|
||||
{
|
||||
Named_object* p2var = p2->var();
|
||||
No_no key(var, p2var);
|
||||
std::pair<Cache::iterator, bool> ins =
|
||||
cache.insert(std::make_pair(key, false));
|
||||
if (ins.second)
|
||||
ins.first->second = expression_requires(init, preinit, dep, p2var);
|
||||
if (ins.first->second)
|
||||
{
|
||||
// Check for cycles.
|
||||
key = std::make_pair(p2var, var);
|
||||
ins = cache.insert(std::make_pair(key, false));
|
||||
if (ins.second)
|
||||
ins.first->second =
|
||||
expression_requires(p2var->var_value()->init(),
|
||||
p2var->var_value()->preinit(),
|
||||
gogo->var_depends_on(p2var->var_value()),
|
||||
var);
|
||||
if (ins.first->second)
|
||||
{
|
||||
error_at(var->location(),
|
||||
("initialization expressions for %qs and "
|
||||
"%qs depend upon each other"),
|
||||
var->message_name().c_str(),
|
||||
p2var->message_name().c_str());
|
||||
inform(p2->var()->location(), "%qs defined here",
|
||||
p2var->message_name().c_str());
|
||||
p2 = var_inits->end();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We can't emit P1 until P2 is emitted. Move P1.
|
||||
Var_inits::iterator p3 = p2;
|
||||
++p3;
|
||||
var_inits->splice(p3, *var_inits, p1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (p2 == var_inits->end())
|
||||
{
|
||||
// VAR does not depends upon any other initialization expressions.
|
||||
|
||||
// Check for a loop of VAR on itself. We only do this if
|
||||
// INIT is not NULL and there is no dependency; when INIT is
|
||||
// NULL, it means that PREINIT sets VAR, which we will
|
||||
// interpret as a loop.
|
||||
if (init != NULL && dep == NULL
|
||||
&& expression_requires(init, preinit, NULL, var))
|
||||
error_at(var->location(),
|
||||
"initialization expression for %qs depends upon itself",
|
||||
var->message_name().c_str());
|
||||
ready.splice(ready.end(), *var_inits, p1);
|
||||
}
|
||||
}
|
||||
|
||||
// Now READY is the list in the desired initialization order.
|
||||
var_inits->swap(ready);
|
||||
}
|
||||
|
||||
// Write out the global definitions.
|
||||
|
||||
void
|
||||
Gogo::write_globals()
|
||||
{
|
||||
this->build_interface_method_tables();
|
||||
|
||||
Bindings* bindings = this->current_bindings();
|
||||
|
||||
for (Bindings::const_declarations_iterator p = bindings->begin_declarations();
|
||||
p != bindings->end_declarations();
|
||||
++p)
|
||||
{
|
||||
// If any function declarations needed a descriptor, make sure
|
||||
// we build it.
|
||||
Named_object* no = p->second;
|
||||
if (no->is_function_declaration())
|
||||
no->func_declaration_value()->build_backend_descriptor(this);
|
||||
}
|
||||
|
||||
// Lists of globally declared types, variables, constants, and functions
|
||||
// that must be defined.
|
||||
std::vector<Btype*> type_decls;
|
||||
std::vector<Bvariable*> var_decls;
|
||||
std::vector<Bexpression*> const_decls;
|
||||
std::vector<Bfunction*> func_decls;
|
||||
|
||||
// The init function declaration, if necessary.
|
||||
Named_object* init_fndecl = NULL;
|
||||
|
||||
std::vector<Bstatement*> init_stmts;
|
||||
std::vector<Bstatement*> var_init_stmts;
|
||||
|
||||
if (this->is_main_package())
|
||||
this->init_imports(init_stmts);
|
||||
|
||||
// A list of variable initializations.
|
||||
Var_inits var_inits;
|
||||
|
||||
// A list of variables which need to be registered with the garbage
|
||||
// collector.
|
||||
size_t count_definitions = bindings->size_definitions();
|
||||
std::vector<Named_object*> var_gc;
|
||||
var_gc.reserve(count_definitions);
|
||||
|
||||
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
|
||||
p != bindings->end_definitions();
|
||||
++p)
|
||||
{
|
||||
Named_object* no = *p;
|
||||
go_assert(!no->is_type_declaration() && !no->is_function_declaration());
|
||||
|
||||
// There is nothing to do for a package.
|
||||
if (no->is_package())
|
||||
continue;
|
||||
|
||||
// There is nothing to do for an object which was imported from
|
||||
// a different package into the global scope.
|
||||
if (no->package() != NULL)
|
||||
continue;
|
||||
|
||||
// Skip blank named functions and constants.
|
||||
if ((no->is_function() && no->func_value()->is_sink())
|
||||
|| (no->is_const() && no->const_value()->is_sink()))
|
||||
continue;
|
||||
|
||||
// There is nothing useful we can output for constants which
|
||||
// have ideal or non-integral type.
|
||||
if (no->is_const())
|
||||
{
|
||||
Type* type = no->const_value()->type();
|
||||
if (type == NULL)
|
||||
type = no->const_value()->expr()->type();
|
||||
if (type->is_abstract() || !type->is_numeric_type())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!no->is_variable())
|
||||
no->get_backend(this, const_decls, type_decls, func_decls);
|
||||
else
|
||||
{
|
||||
Variable* var = no->var_value();
|
||||
Bvariable* bvar = no->get_backend_variable(this, NULL);
|
||||
var_decls.push_back(bvar);
|
||||
|
||||
// Check for a sink variable, which may be used to run an
|
||||
// initializer purely for its side effects.
|
||||
bool is_sink = no->name()[0] == '_' && no->name()[1] == '.';
|
||||
|
||||
Bstatement* var_init_stmt = NULL;
|
||||
if (!var->has_pre_init())
|
||||
{
|
||||
Bexpression* var_binit = var->get_init(this, NULL);
|
||||
|
||||
// If the backend representation of the variable initializer is
|
||||
// constant, we can just set the initial value using
|
||||
// global_var_set_init instead of during the init() function.
|
||||
// The initializer is constant if it is the zero-value of the
|
||||
// variable's type or if the initial value is an immutable value
|
||||
// that is not copied to the heap.
|
||||
bool is_constant_initializer = false;
|
||||
if (var->init() == NULL)
|
||||
is_constant_initializer = true;
|
||||
else
|
||||
{
|
||||
Type* var_type = var->type();
|
||||
Expression* init = var->init();
|
||||
Expression* init_cast =
|
||||
Expression::make_cast(var_type, init, var->location());
|
||||
is_constant_initializer =
|
||||
init_cast->is_immutable() && !var_type->has_pointer();
|
||||
}
|
||||
|
||||
if (var_binit == NULL)
|
||||
;
|
||||
else if (is_constant_initializer)
|
||||
{
|
||||
if (expression_requires(var->init(), NULL,
|
||||
this->var_depends_on(var), no))
|
||||
error_at(no->location(),
|
||||
"initialization expression for %qs depends "
|
||||
"upon itself",
|
||||
no->message_name().c_str());
|
||||
this->backend()->global_variable_set_init(bvar, var_binit);
|
||||
}
|
||||
else if (is_sink)
|
||||
var_init_stmt =
|
||||
this->backend()->expression_statement(var_binit);
|
||||
else
|
||||
{
|
||||
Location loc = var->location();
|
||||
Bexpression* var_expr =
|
||||
this->backend()->var_expression(bvar, loc);
|
||||
var_init_stmt =
|
||||
this->backend()->assignment_statement(var_expr, var_binit,
|
||||
loc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are going to create temporary variables which
|
||||
// means that we need an fndecl.
|
||||
if (init_fndecl == NULL)
|
||||
init_fndecl = this->initialization_function_decl();
|
||||
|
||||
Bvariable* var_decl = is_sink ? NULL : bvar;
|
||||
var_init_stmt = var->get_init_block(this, init_fndecl, var_decl);
|
||||
}
|
||||
|
||||
if (var_init_stmt != NULL)
|
||||
{
|
||||
if (var->init() == NULL && !var->has_pre_init())
|
||||
var_init_stmts.push_back(var_init_stmt);
|
||||
else
|
||||
var_inits.push_back(Var_init(no, var_init_stmt));
|
||||
}
|
||||
else if (this->var_depends_on(var) != NULL)
|
||||
{
|
||||
// This variable is initialized from something that is
|
||||
// not in its init or preinit. This variable needs to
|
||||
// participate in dependency analysis sorting, in case
|
||||
// some other variable depends on this one.
|
||||
Btype* btype = no->var_value()->type()->get_backend(this);
|
||||
Bexpression* zero = this->backend()->zero_expression(btype);
|
||||
Bstatement* zero_stmt =
|
||||
this->backend()->expression_statement(zero);
|
||||
var_inits.push_back(Var_init(no, zero_stmt));
|
||||
}
|
||||
|
||||
if (!is_sink && var->type()->has_pointer())
|
||||
var_gc.push_back(no);
|
||||
}
|
||||
}
|
||||
|
||||
// Register global variables with the garbage collector.
|
||||
this->register_gc_vars(var_gc, init_stmts);
|
||||
|
||||
// Simple variable initializations, after all variables are
|
||||
// registered.
|
||||
init_stmts.push_back(this->backend()->statement_list(var_init_stmts));
|
||||
|
||||
// Complete variable initializations, first sorting them into a
|
||||
// workable order.
|
||||
if (!var_inits.empty())
|
||||
{
|
||||
sort_var_inits(this, &var_inits);
|
||||
for (Var_inits::const_iterator p = var_inits.begin();
|
||||
p != var_inits.end();
|
||||
++p)
|
||||
init_stmts.push_back(p->init());
|
||||
}
|
||||
|
||||
// After all the variables are initialized, call the init
|
||||
// functions if there are any. Init functions take no arguments, so
|
||||
// we pass in EMPTY_ARGS to call them.
|
||||
std::vector<Bexpression*> empty_args;
|
||||
for (std::vector<Named_object*>::const_iterator p =
|
||||
this->init_functions_.begin();
|
||||
p != this->init_functions_.end();
|
||||
++p)
|
||||
{
|
||||
Location func_loc = (*p)->location();
|
||||
Function* func = (*p)->func_value();
|
||||
Bfunction* initfn = func->get_or_make_decl(this, *p);
|
||||
Bexpression* func_code =
|
||||
this->backend()->function_code_expression(initfn, func_loc);
|
||||
Bexpression* call = this->backend()->call_expression(func_code,
|
||||
empty_args,
|
||||
func_loc);
|
||||
init_stmts.push_back(this->backend()->expression_statement(call));
|
||||
}
|
||||
|
||||
// Set up a magic function to do all the initialization actions.
|
||||
// This will be called if this package is imported.
|
||||
Bstatement* init_fncode = this->backend()->statement_list(init_stmts);
|
||||
if (this->need_init_fn_ || this->is_main_package())
|
||||
{
|
||||
init_fndecl =
|
||||
this->create_initialization_function(init_fndecl, init_fncode);
|
||||
if (init_fndecl != NULL)
|
||||
func_decls.push_back(init_fndecl->func_value()->get_decl());
|
||||
}
|
||||
|
||||
// We should not have seen any new bindings created during the conversion.
|
||||
go_assert(count_definitions == this->current_bindings()->size_definitions());
|
||||
|
||||
// Define all globally declared values.
|
||||
if (!saw_errors())
|
||||
this->backend()->write_global_definitions(type_decls, const_decls,
|
||||
func_decls, var_decls);
|
||||
}
|
||||
|
||||
// Return the current block.
|
||||
|
||||
Block*
|
||||
@ -4182,6 +4855,15 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
|
||||
return this->fndecl_;
|
||||
}
|
||||
|
||||
// Return the function's decl after it has been built.
|
||||
|
||||
Bfunction*
|
||||
Function::get_decl() const
|
||||
{
|
||||
go_assert(this->fndecl_ != NULL);
|
||||
return this->fndecl_;
|
||||
}
|
||||
|
||||
// Build the backend representation for the function code.
|
||||
|
||||
void
|
||||
@ -5266,8 +5948,7 @@ Variable::get_init_block(Gogo* gogo, Named_object* function,
|
||||
{
|
||||
Location loc = this->location();
|
||||
Expression* val_expr =
|
||||
Expression::convert_for_assignment(gogo, this->type(),
|
||||
this->init_, this->location());
|
||||
Expression::make_cast(this->type(), this->init_, loc);
|
||||
Bexpression* val = tree_to_expr(val_expr->get_tree(&context));
|
||||
Bexpression* var_ref = gogo->backend()->var_expression(var_decl, loc);
|
||||
decl_init = gogo->backend()->assignment_statement(var_ref, val, loc);
|
||||
@ -5353,8 +6034,7 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function,
|
||||
}
|
||||
else
|
||||
{
|
||||
tree fndecl = function->func_value()->get_decl();
|
||||
Bfunction* bfunction = tree_to_function(fndecl);
|
||||
Bfunction* bfunction = function->func_value()->get_decl();
|
||||
bool is_address_taken = (this->is_non_escaping_address_taken_
|
||||
&& !this->is_in_heap());
|
||||
if (is_parameter)
|
||||
@ -5391,8 +6071,7 @@ Result_variable::get_backend_variable(Gogo* gogo, Named_object* function,
|
||||
if (this->is_in_heap())
|
||||
type = Type::make_pointer_type(type);
|
||||
Btype* btype = type->get_backend(gogo);
|
||||
tree fndecl = function->func_value()->get_decl();
|
||||
Bfunction* bfunction = tree_to_function(fndecl);
|
||||
Bfunction* bfunction = function->func_value()->get_decl();
|
||||
std::string n = Gogo::unpack_hidden_name(name);
|
||||
bool is_address_taken = (this->is_non_escaping_address_taken_
|
||||
&& !this->is_in_heap());
|
||||
@ -5482,6 +6161,33 @@ Named_constant::import_const(Import* imp, std::string* pname, Type** ptype,
|
||||
imp->require_c_string(";\n");
|
||||
}
|
||||
|
||||
// Get the backend representation.
|
||||
|
||||
Bexpression*
|
||||
Named_constant::get_backend(Gogo* gogo, Named_object* const_no)
|
||||
{
|
||||
if (this->bconst_ == NULL)
|
||||
{
|
||||
Translate_context subcontext(gogo, NULL, NULL, NULL);
|
||||
Type* type = this->type();
|
||||
Location loc = this->location();
|
||||
|
||||
Expression* const_ref = Expression::make_const_reference(const_no, loc);
|
||||
Bexpression* const_decl =
|
||||
tree_to_expr(const_ref->get_tree(&subcontext));
|
||||
if (type != NULL && type->is_numeric_type())
|
||||
{
|
||||
Btype* btype = type->get_backend(gogo);
|
||||
std::string name = const_no->get_id(gogo);
|
||||
const_decl =
|
||||
gogo->backend()->named_constant_expression(btype, name,
|
||||
const_decl, loc);
|
||||
}
|
||||
this->bconst_ = const_decl;
|
||||
}
|
||||
return this->bconst_;
|
||||
}
|
||||
|
||||
// Add a method.
|
||||
|
||||
Named_object*
|
||||
@ -5552,8 +6258,7 @@ Unknown_name::set_real_named_object(Named_object* no)
|
||||
Named_object::Named_object(const std::string& name,
|
||||
const Package* package,
|
||||
Classification classification)
|
||||
: name_(name), package_(package), classification_(classification),
|
||||
tree_(NULL)
|
||||
: name_(name), package_(package), classification_(classification)
|
||||
{
|
||||
if (Gogo::is_sink_name(name))
|
||||
go_assert(classification == NAMED_OBJECT_SINK);
|
||||
@ -5928,6 +6633,72 @@ Named_object::get_id(Gogo* gogo)
|
||||
return decl_name;
|
||||
}
|
||||
|
||||
// Get the backend representation for this named object.
|
||||
|
||||
void
|
||||
Named_object::get_backend(Gogo* gogo, std::vector<Bexpression*>& const_decls,
|
||||
std::vector<Btype*>& type_decls,
|
||||
std::vector<Bfunction*>& func_decls)
|
||||
{
|
||||
switch (this->classification_)
|
||||
{
|
||||
case NAMED_OBJECT_CONST:
|
||||
if (!Gogo::is_erroneous_name(this->name_))
|
||||
const_decls.push_back(this->u_.const_value->get_backend(gogo, this));
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_TYPE:
|
||||
{
|
||||
Named_type* named_type = this->u_.type_value;
|
||||
if (!Gogo::is_erroneous_name(this->name_))
|
||||
type_decls.push_back(named_type->get_backend(gogo));
|
||||
|
||||
// We need to produce a type descriptor for every named
|
||||
// type, and for a pointer to every named type, since
|
||||
// other files or packages might refer to them. We need
|
||||
// to do this even for hidden types, because they might
|
||||
// still be returned by some function. Simply calling the
|
||||
// type_descriptor method is enough to create the type
|
||||
// descriptor, even though we don't do anything with it.
|
||||
if (this->package_ == NULL)
|
||||
{
|
||||
named_type->
|
||||
type_descriptor_pointer(gogo, Linemap::predeclared_location());
|
||||
Type* pn = Type::make_pointer_type(named_type);
|
||||
pn->type_descriptor_pointer(gogo, Linemap::predeclared_location());
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_TYPE_DECLARATION:
|
||||
error("reference to undefined type %qs",
|
||||
this->message_name().c_str());
|
||||
return;
|
||||
|
||||
case NAMED_OBJECT_VAR:
|
||||
case NAMED_OBJECT_RESULT_VAR:
|
||||
case NAMED_OBJECT_SINK:
|
||||
go_unreachable();
|
||||
|
||||
case NAMED_OBJECT_FUNC:
|
||||
{
|
||||
Function* func = this->u_.func_value;
|
||||
if (!Gogo::is_erroneous_name(this->name_))
|
||||
func_decls.push_back(func->get_or_make_decl(gogo, this));
|
||||
|
||||
if (func->block() != NULL)
|
||||
func->build(gogo, this);
|
||||
}
|
||||
break;
|
||||
|
||||
case NAMED_OBJECT_ERRONEOUS:
|
||||
break;
|
||||
|
||||
default:
|
||||
go_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
// Class Bindings.
|
||||
|
||||
Bindings::Bindings(Bindings* enclosing)
|
||||
@ -6400,8 +7171,7 @@ Label::get_backend_label(Translate_context* context)
|
||||
if (this->blabel_ == NULL)
|
||||
{
|
||||
Function* function = context->function()->func_value();
|
||||
tree fndecl = function->get_decl();
|
||||
Bfunction* bfunction = tree_to_function(fndecl);
|
||||
Bfunction* bfunction = function->get_decl();
|
||||
this->blabel_ = context->backend()->label(bfunction, this->name_,
|
||||
this->location_);
|
||||
}
|
||||
@ -6427,8 +7197,7 @@ Unnamed_label::get_blabel(Translate_context* context)
|
||||
if (this->blabel_ == NULL)
|
||||
{
|
||||
Function* function = context->function()->func_value();
|
||||
tree fndecl = function->get_decl();
|
||||
Bfunction* bfunction = tree_to_function(fndecl);
|
||||
Bfunction* bfunction = function->get_decl();
|
||||
this->blabel_ = context->backend()->label(bfunction, "",
|
||||
this->location_);
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class Backend;
|
||||
class Export;
|
||||
class Import;
|
||||
class Bexpression;
|
||||
class Btype;
|
||||
class Bstatement;
|
||||
class Bblock;
|
||||
class Bvariable;
|
||||
@ -591,11 +592,6 @@ class Gogo
|
||||
Expression*
|
||||
runtime_error(int code, Location);
|
||||
|
||||
// Build a builtin struct with a list of fields.
|
||||
static tree
|
||||
builtin_struct(tree* ptype, const char* struct_name, tree struct_type,
|
||||
int nfields, ...);
|
||||
|
||||
// Mark a function declaration as a builtin library function.
|
||||
static void
|
||||
mark_fndecl_as_builtin_library(tree fndecl);
|
||||
@ -650,17 +646,18 @@ class Gogo
|
||||
Named_object*
|
||||
initialization_function_decl();
|
||||
|
||||
// Write the magic initialization function.
|
||||
void
|
||||
write_initialization_function(Named_object* fndecl, tree init_stmt_list);
|
||||
// Create the magic initialization function.
|
||||
Named_object*
|
||||
create_initialization_function(Named_object* fndecl, Bstatement* code_stmt);
|
||||
|
||||
// Initialize imported packages.
|
||||
void
|
||||
init_imports(tree*);
|
||||
init_imports(std::vector<Bstatement*>&);
|
||||
|
||||
// Register variables with the garbage collector.
|
||||
void
|
||||
register_gc_vars(const std::vector<Named_object*>&, tree*);
|
||||
register_gc_vars(const std::vector<Named_object*>&,
|
||||
std::vector<Bstatement*>&);
|
||||
|
||||
// Type used to map import names to packages.
|
||||
typedef std::map<std::string, Package*> Imports;
|
||||
@ -1086,7 +1083,7 @@ class Function
|
||||
get_or_make_decl(Gogo*, Named_object*);
|
||||
|
||||
// Return the function's decl after it has been built.
|
||||
tree
|
||||
Bfunction*
|
||||
get_decl() const;
|
||||
|
||||
// Set the function decl to hold a backend representation of the function
|
||||
@ -1675,7 +1672,7 @@ class Named_constant
|
||||
Named_constant(Type* type, Expression* expr, int iota_value,
|
||||
Location location)
|
||||
: type_(type), expr_(expr), iota_value_(iota_value), location_(location),
|
||||
lowering_(false), is_sink_(false)
|
||||
lowering_(false), is_sink_(false), bconst_(NULL)
|
||||
{ }
|
||||
|
||||
Type*
|
||||
@ -1737,6 +1734,10 @@ class Named_constant
|
||||
static void
|
||||
import_const(Import*, std::string*, Type**, Expression**);
|
||||
|
||||
// Get the backend representation of the constant value.
|
||||
Bexpression*
|
||||
get_backend(Gogo*, Named_object*);
|
||||
|
||||
private:
|
||||
// The type of the constant.
|
||||
Type* type_;
|
||||
@ -1754,6 +1755,8 @@ class Named_constant
|
||||
bool lowering_;
|
||||
// Whether this constant is blank named and needs only type checking.
|
||||
bool is_sink_;
|
||||
// The backend representation of the constant value.
|
||||
Bexpression* bconst_;
|
||||
};
|
||||
|
||||
// A type declaration.
|
||||
@ -2176,9 +2179,10 @@ class Named_object
|
||||
std::string
|
||||
get_id(Gogo*);
|
||||
|
||||
// Return a tree representing this object.
|
||||
tree
|
||||
get_tree(Gogo*, Named_object* function);
|
||||
// Get the backend representation of this object.
|
||||
void
|
||||
get_backend(Gogo*, std::vector<Bexpression*>&, std::vector<Btype*>&,
|
||||
std::vector<Bfunction*>&);
|
||||
|
||||
// Define a type declaration.
|
||||
void
|
||||
@ -2219,8 +2223,6 @@ class Named_object
|
||||
Function_declaration* func_declaration_value;
|
||||
Package* package_value;
|
||||
} u_;
|
||||
// The DECL tree for this object if we have already converted it.
|
||||
tree tree_;
|
||||
};
|
||||
|
||||
// A binding contour. This binds names to objects.
|
||||
|
@ -434,15 +434,9 @@ Temporary_statement::do_get_backend(Translate_context* context)
|
||||
{
|
||||
go_assert(this->bvariable_ == NULL);
|
||||
|
||||
// FIXME: Permitting FUNCTION to be NULL here is a temporary measure
|
||||
// until we have a better representation of the init function.
|
||||
Named_object* function = context->function();
|
||||
Bfunction* bfunction;
|
||||
if (function == NULL)
|
||||
bfunction = NULL;
|
||||
else
|
||||
bfunction = tree_to_function(function->func_value()->get_decl());
|
||||
|
||||
go_assert(function != NULL);
|
||||
Bfunction* bfunction = function->func_value()->get_decl();
|
||||
Btype* btype = this->type()->get_backend(context->gogo());
|
||||
|
||||
Bexpression* binit;
|
||||
@ -2781,8 +2775,6 @@ Return_statement::do_get_backend(Translate_context* context)
|
||||
Location loc = this->location();
|
||||
|
||||
Function* function = context->function()->func_value();
|
||||
tree fndecl = function->get_decl();
|
||||
|
||||
Function::Results* results = function->result_variables();
|
||||
std::vector<Bexpression*> retvals;
|
||||
if (results != NULL && !results->empty())
|
||||
@ -2797,7 +2789,7 @@ Return_statement::do_get_backend(Translate_context* context)
|
||||
}
|
||||
}
|
||||
|
||||
return context->backend()->return_statement(tree_to_function(fndecl),
|
||||
return context->backend()->return_statement(function->get_decl(),
|
||||
retvals, loc);
|
||||
}
|
||||
|
||||
@ -3803,8 +3795,10 @@ Constant_switch_statement::do_get_backend(Translate_context* context)
|
||||
this->clauses_->get_backend(context, break_label, &all_cases,
|
||||
&all_statements);
|
||||
|
||||
Bfunction* bfunction = context->function()->func_value()->get_decl();
|
||||
Bstatement* switch_statement;
|
||||
switch_statement = context->backend()->switch_statement(switch_val_expr,
|
||||
switch_statement = context->backend()->switch_statement(bfunction,
|
||||
switch_val_expr,
|
||||
all_cases,
|
||||
all_statements,
|
||||
this->location());
|
||||
@ -4980,7 +4974,9 @@ Select_clauses::get_backend(Translate_context* context,
|
||||
std::vector<Bstatement*> statements;
|
||||
statements.reserve(2);
|
||||
|
||||
Bstatement* switch_stmt = context->backend()->switch_statement(bcall,
|
||||
Bfunction* bfunction = context->function()->func_value()->get_decl();
|
||||
Bstatement* switch_stmt = context->backend()->switch_statement(bfunction,
|
||||
bcall,
|
||||
cases,
|
||||
clauses,
|
||||
location);
|
||||
|
Loading…
Reference in New Issue
Block a user