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:
Chris Manghane 2014-04-26 03:38:34 +00:00 committed by Ian Lance Taylor
parent 10695c6a1d
commit 036165d811
9 changed files with 1161 additions and 1062 deletions

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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(&register_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

View File

@ -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_);
}

View File

@ -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.

View File

@ -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);