* go-gcc.cc (class Bblock): Define.
(Gcc_backend::if_statement): Change then_block and else_block to
Bblock*.
(Gcc_backend::block): New function.
(Gcc_backend::block_add_statements): New function.
(Gcc_backend::block_statement): New function.
(tree_to_block, block_to_tree): New functions.
From-SVN: r172731
+2011-04-19 Ian Lance Taylor <iant@google.com>
+
+ * go-gcc.cc (class Bblock): Define.
+ (Gcc_backend::if_statement): Change then_block and else_block to
+ Bblock*.
+ (Gcc_backend::block): New function.
+ (Gcc_backend::block_add_statements): New function.
+ (Gcc_backend::block_statement): New function.
+ (tree_to_block, block_to_tree): New functions.
+
2011-04-18 Ian Lance Taylor <iant@google.com>
* go-gcc.cc: Include "go-c.h".
{ }
};
+class Bblock : public Gcc_tree
+{
+ public:
+ Bblock(tree t)
+ : Gcc_tree(t)
+ { }
+};
+
class Bvariable : public Gcc_tree
{
public:
source_location);
Bstatement*
- if_statement(Bexpression* condition, Bstatement* then_block,
- Bstatement* else_block, source_location);
+ if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block,
+ source_location);
Bstatement*
switch_statement(Bexpression* value,
Bstatement*
statement_list(const std::vector<Bstatement*>&);
+ // Blocks.
+
+ Bblock*
+ block(Bfunction*, Bblock*, const std::vector<Bvariable*>&,
+ source_location, source_location);
+
+ void
+ block_add_statements(Bblock*, const std::vector<Bstatement*>&);
+
+ Bstatement*
+ block_statement(Bblock*);
+
// Variables.
Bvariable*
// If.
Bstatement*
-Gcc_backend::if_statement(Bexpression* condition, Bstatement* then_block,
- Bstatement* else_block, source_location location)
+Gcc_backend::if_statement(Bexpression* condition, Bblock* then_block,
+ Bblock* else_block, source_location location)
{
tree cond_tree = condition->get_tree();
tree then_tree = then_block->get_tree();
return this->make_statement(stmt_list);
}
+// Make a block. For some reason gcc uses a dual structure for
+// blocks: BLOCK tree nodes and BIND_EXPR tree nodes. Since the
+// BIND_EXPR node points to the BLOCK node, we store the BIND_EXPR in
+// the Bblock.
+
+Bblock*
+Gcc_backend::block(Bfunction* function, Bblock* enclosing,
+ const std::vector<Bvariable*>& vars,
+ source_location start_location,
+ source_location)
+{
+ 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();
+ gcc_assert(fndecl != NULL_TREE);
+
+ // We may have already created a block for local variables when
+ // we take the address of a parameter.
+ if (DECL_INITIAL(fndecl) == NULL_TREE)
+ {
+ BLOCK_SUPERCONTEXT(block_tree) = fndecl;
+ DECL_INITIAL(fndecl) = block_tree;
+ }
+ else
+ {
+ tree superblock_tree = DECL_INITIAL(fndecl);
+ BLOCK_SUPERCONTEXT(block_tree) = superblock_tree;
+ tree* pp;
+ for (pp = &BLOCK_SUBBLOCKS(superblock_tree);
+ *pp != NULL_TREE;
+ pp = &BLOCK_CHAIN(*pp))
+ ;
+ *pp = block_tree;
+ }
+ }
+ else
+ {
+ tree superbind_tree = enclosing->get_tree();
+ tree superblock_tree = BIND_EXPR_BLOCK(superbind_tree);
+ gcc_assert(TREE_CODE(superblock_tree) == BLOCK);
+
+ BLOCK_SUPERCONTEXT(block_tree) = superblock_tree;
+ tree* pp;
+ for (pp = &BLOCK_SUBBLOCKS(superblock_tree);
+ *pp != NULL_TREE;
+ pp = &BLOCK_CHAIN(*pp))
+ ;
+ *pp = block_tree;
+ }
+
+ tree* pp = &BLOCK_VARS(block_tree);
+ for (std::vector<Bvariable*>::const_iterator pv = vars.begin();
+ pv != vars.end();
+ ++pv)
+ {
+ *pp = (*pv)->get_tree();
+ if (*pp != error_mark_node)
+ pp = &DECL_CHAIN(*pp);
+ }
+ *pp = NULL_TREE;
+
+ TREE_USED(block_tree) = 1;
+
+ tree bind_tree = build3_loc(start_location, BIND_EXPR, void_type_node,
+ BLOCK_VARS(block_tree), NULL_TREE, block_tree);
+ TREE_SIDE_EFFECTS(bind_tree) = 1;
+
+ return new Bblock(bind_tree);
+}
+
+// Add statements to a block.
+
+void
+Gcc_backend::block_add_statements(Bblock* bblock,
+ const std::vector<Bstatement*>& statements)
+{
+ tree stmt_list = NULL_TREE;
+ for (std::vector<Bstatement*>::const_iterator p = statements.begin();
+ p != statements.end();
+ ++p)
+ {
+ tree s = (*p)->get_tree();
+ if (s != error_mark_node)
+ append_to_statement_list(s, &stmt_list);
+ }
+
+ tree bind_tree = bblock->get_tree();
+ gcc_assert(TREE_CODE(bind_tree) == BIND_EXPR);
+ BIND_EXPR_BODY(bind_tree) = stmt_list;
+}
+
+// Return a block as a statement.
+
+Bstatement*
+Gcc_backend::block_statement(Bblock* bblock)
+{
+ tree bind_tree = bblock->get_tree();
+ gcc_assert(TREE_CODE(bind_tree) == BIND_EXPR);
+ return this->make_statement(bind_tree);
+}
+
// Make a global variable.
Bvariable*
return new Bfunction(t);
}
+Bblock*
+tree_to_block(tree t)
+{
+ gcc_assert(TREE_CODE(t) == BIND_EXPR);
+ return new Bblock(t);
+}
+
tree
expr_to_tree(Bexpression* be)
{
return bs->get_tree();
}
+tree
+block_to_tree(Bblock* bb)
+{
+ return bb->get_tree();
+}
+
tree
var_to_tree(Bvariable* bv)
{
// The backend representation of a function definition.
class Bfunction;
+// The backend representation of a block.
+class Bblock;
+
// The backend representation of a variable.
class Bvariable;
// Create an if statement. ELSE_BLOCK may be NULL.
virtual Bstatement*
- if_statement(Bexpression* condition, Bstatement* then_block,
- Bstatement* else_block, source_location) = 0;
+ if_statement(Bexpression* condition, Bblock* then_block, Bblock* else_block,
+ source_location) = 0;
// Create a switch statement where the case values are constants.
// CASES and STATEMENTS must have the same number of entries. If
virtual Bstatement*
statement_list(const std::vector<Bstatement*>&) = 0;
+ // Blocks.
+
+ // Create a block. The frontend will call this function when it
+ // starts converting a block within a function. FUNCTION is the
+ // current function. ENCLOSING is the enclosing block; it will be
+ // NULL for the top-level block in a function. VARS is the list of
+ // local variables defined within this block; each entry will be
+ // created by the local_variable function. START_LOCATION is the
+ // location of the start of the block, more or less the location of
+ // the initial curly brace. END_LOCATION is the location of the end
+ // of the block, more or less the location of the final curly brace.
+ // The statements will be added after the block is created.
+ virtual Bblock*
+ block(Bfunction* function, Bblock* enclosing,
+ const std::vector<Bvariable*>& vars,
+ source_location start_location, source_location end_location) = 0;
+
+ // Add the statements to a block. The block is created first. Then
+ // the statements are created. Then the statements are added to the
+ // block. This will called exactly once per block. The vector may
+ // be empty if there are no statements.
+ virtual void
+ block_add_statements(Bblock*, const std::vector<Bstatement*>&) = 0;
+
+ // Return the block as a statement. This is used to include a block
+ // in a list of statements.
+ virtual Bstatement*
+ block_statement(Bblock*) = 0;
+
// Variables.
// Create an error variable. This is used for cases which should
extern Bexpression* tree_to_expr(tree);
extern Bstatement* tree_to_stat(tree);
extern Bfunction* tree_to_function(tree);
+extern Bblock* tree_to_block(tree);
extern tree expr_to_tree(Bexpression*);
extern tree stat_to_tree(Bstatement*);
+extern tree block_to_tree(Bblock*);
extern tree var_to_tree(Bvariable*);
#endif // !defined(GO_BACKEND_H)
case NAMED_OBJECT_CONST:
{
Named_constant* named_constant = this->u_.const_value;
- Translate_context subcontext(gogo, function, NULL, NULL_TREE);
+ Translate_context subcontext(gogo, function, NULL, NULL);
tree expr_tree = named_constant->expr()->get_tree(&subcontext);
if (expr_tree == error_mark_node)
decl = error_mark_node;
}
else
{
- Translate_context context(gogo, function, NULL, NULL_TREE);
+ Translate_context context(gogo, function, NULL, NULL);
tree rhs_tree = this->init_->get_tree(&context);
return Expression::convert_for_assignment(&context, this->type(),
this->init_->type(),
// TRY_CATCH_EXPR; if it does, we want to add to the end of the
// regular statements.
- Translate_context context(gogo, function, NULL, NULL_TREE);
- tree block_tree = this->preinit_->get_tree(&context);
+ Translate_context context(gogo, function, NULL, NULL);
+ Bblock* bblock = this->preinit_->get_backend(&context);
+ tree block_tree = block_to_tree(bblock);
if (block_tree == error_mark_node)
return error_mark_node;
gcc_assert(TREE_CODE(block_tree) == BIND_EXPR);
BLOCK_VARS(block) = declare_vars;
TREE_USED(block) = 1;
+ bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
+ NULL_TREE, block);
+ TREE_SIDE_EFFECTS(bind) = 1;
+
if (this->defer_stack_ != NULL)
{
Translate_context dcontext(gogo, named_function, this->block_,
- block);
+ tree_to_block(bind));
defer_init = this->defer_stack_->get_tree(&dcontext);
}
-
- bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block),
- NULL_TREE, block);
- TREE_SIDE_EFFECTS(bind) = 1;
}
// Build the trees for all the statements in the function.
- Translate_context context(gogo, named_function, NULL, NULL_TREE);
- tree code = this->block_->get_tree(&context);
+ Translate_context context(gogo, named_function, NULL, NULL);
+ Bblock* bblock = this->block_->get_backend(&context);
+ tree code = block_to_tree(bblock);
tree init = NULL_TREE;
tree except = NULL_TREE;
}
}
-// Get a tree for the statements in a block.
-
-tree
-Block::get_tree(Translate_context* context)
-{
- Gogo* gogo = context->gogo();
-
- tree block = make_node(BLOCK);
-
- // Put the new block into the block tree.
-
- if (context->block() == NULL)
- {
- tree fndecl;
- if (context->function() != NULL)
- fndecl = context->function()->func_value()->get_decl();
- else
- fndecl = current_function_decl;
- gcc_assert(fndecl != NULL_TREE);
-
- // We may have already created a block for the receiver.
- if (DECL_INITIAL(fndecl) == NULL_TREE)
- {
- BLOCK_SUPERCONTEXT(block) = fndecl;
- DECL_INITIAL(fndecl) = block;
- }
- else
- {
- tree superblock_tree = DECL_INITIAL(fndecl);
- BLOCK_SUPERCONTEXT(block) = superblock_tree;
- gcc_assert(BLOCK_CHAIN(block) == NULL_TREE);
- BLOCK_CHAIN(block) = block;
- }
- }
- else
- {
- tree superblock_tree = context->block_tree();
- BLOCK_SUPERCONTEXT(block) = superblock_tree;
- tree* pp;
- for (pp = &BLOCK_SUBBLOCKS(superblock_tree);
- *pp != NULL_TREE;
- pp = &BLOCK_CHAIN(*pp))
- ;
- *pp = block;
- }
-
- // Expand local variables in the block.
-
- tree* pp = &BLOCK_VARS(block);
- for (Bindings::const_definitions_iterator pv =
- this->bindings_->begin_definitions();
- pv != this->bindings_->end_definitions();
- ++pv)
- {
- if ((*pv)->is_variable() && !(*pv)->var_value()->is_parameter())
- {
- Bvariable* var = (*pv)->get_backend_variable(gogo,
- context->function());
- *pp = var_to_tree(var);
- if (*pp != error_mark_node)
- pp = &DECL_CHAIN(*pp);
- }
- }
- *pp = NULL_TREE;
-
- Translate_context subcontext(gogo, context->function(), this, block);
-
- tree statements = NULL_TREE;
-
- // Expand the statements.
-
- for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
- p != this->statements_.end();
- ++p)
- {
- tree statement = (*p)->get_tree(&subcontext);
- if (statement != error_mark_node)
- append_to_statement_list(statement, &statements);
- }
-
- TREE_USED(block) = 1;
-
- tree bind = build3(BIND_EXPR, void_type_node, BLOCK_VARS(block), statements,
- block);
- TREE_SIDE_EFFECTS(bind) = 1;
-
- return bind;
-}
-
// Return the integer type to use for a size.
GO_EXTERN_C
return this->statements_.back()->may_fall_through();
}
+// Convert a block to the backend representation.
+
+Bblock*
+Block::get_backend(Translate_context* context)
+{
+ Gogo* gogo = context->gogo();
+ Named_object* function = context->function();
+ std::vector<Bvariable*> vars;
+ vars.reserve(this->bindings_->size_definitions());
+ for (Bindings::const_definitions_iterator pv =
+ this->bindings_->begin_definitions();
+ pv != this->bindings_->end_definitions();
+ ++pv)
+ {
+ if ((*pv)->is_variable() && !(*pv)->var_value()->is_parameter())
+ vars.push_back((*pv)->get_backend_variable(gogo, function));
+ }
+
+ // FIXME: Permitting FUNCTION to be NULL here is a temporary measure
+ // until we have a proper representation of the init function.
+ Bfunction* bfunction;
+ if (function == NULL)
+ bfunction = NULL;
+ else
+ bfunction = tree_to_function(function->func_value()->get_decl());
+ Bblock* ret = context->backend()->block(bfunction, context->bblock(),
+ vars, this->start_location_,
+ this->end_location_);
+
+ Translate_context subcontext(gogo, function, this, ret);
+ std::vector<Bstatement*> bstatements;
+ bstatements.reserve(this->statements_.size());
+ for (std::vector<Statement*>::const_iterator p = this->statements_.begin();
+ p != this->statements_.end();
+ ++p)
+ bstatements.push_back(tree_to_stat((*p)->get_tree(&subcontext)));
+
+ context->backend()->block_add_statements(ret, bstatements);
+
+ return ret;
+}
+
// Class Variable.
Variable::Variable(Type* type, Expression* init, bool is_global,
class Import;
class Bexpression;
class Bstatement;
+class Bblock;
class Bvariable;
class Blabel;
bool
may_fall_through() const;
- // Return a tree of the code in this block.
- tree
- get_tree(Translate_context*);
+ // Convert the block to the backend representation.
+ Bblock*
+ get_backend(Translate_context*);
// Iterate over statements.
{
public:
Translate_context(Gogo* gogo, Named_object* function, Block* block,
- tree block_tree)
+ Bblock* bblock)
: gogo_(gogo), backend_(gogo->backend()), function_(function),
- block_(block), block_tree_(block_tree), is_const_(false)
+ block_(block), bblock_(bblock), is_const_(false)
{ }
// Accessors.
block()
{ return this->block_; }
- tree
- block_tree()
- { return this->block_tree_; }
+ Bblock*
+ bblock()
+ { return this->bblock_; }
bool
is_const()
Gogo* gogo_;
// The generator for the backend data structures.
Backend* backend_;
- // The function we are currently translating.
+ // The function we are currently translating. NULL if not in a
+ // function, e.g., the initializer of a global variable.
Named_object* function_;
- // The block we are currently translating.
+ // The block we are currently translating. NULL if not in a
+ // function.
Block *block_;
- // The BLOCK node for the current block.
- tree block_tree_;
+ // The backend representation of the current block. NULL if block_
+ // is NULL.
+ Bblock* bblock_;
// Whether this is being evaluated in a constant context. This is
// used for type descriptor initializers.
bool is_const_;
gcc_assert(current_function_decl != NULL_TREE);
DECL_CONTEXT(decl) = current_function_decl;
- // We have to add this variable to the block so that it winds up
- // in a BIND_EXPR.
- tree block_tree = context->block_tree();
- gcc_assert(block_tree != NULL_TREE);
+ // We have to add this variable to the BLOCK and the BIND_EXPR.
+ tree bind_tree = block_to_tree(context->bblock());
+ gcc_assert(bind_tree != NULL_TREE && TREE_CODE(bind_tree) == BIND_EXPR);
+ tree block_tree = BIND_EXPR_BLOCK(bind_tree);
+ gcc_assert(TREE_CODE(block_tree) == BLOCK);
DECL_CHAIN(decl) = BLOCK_VARS(block_tree);
BLOCK_VARS(block_tree) = decl;
+ BIND_EXPR_VARS(bind_tree) = BLOCK_VARS(block_tree);
this->decl_ = decl;
}
{ return this->block_->may_fall_through(); }
tree
- do_get_tree(Translate_context* context)
- { return this->block_->get_tree(context); }
+ do_get_tree(Translate_context* context);
private:
Block* block_;
};
+// Convert a block to the backend representation of a statement.
+
+tree
+Block_statement::do_get_tree(Translate_context* context)
+{
+ Bblock* bblock = this->block_->get_backend(context);
+ Bstatement* ret = context->backend()->block_statement(bblock);
+ return stat_to_tree(ret);
+}
+
// Make a block statement.
Statement*
gcc_assert(this->cond_->type()->is_boolean_type()
|| this->cond_->type()->is_error());
tree cond_tree = this->cond_->get_tree(context);
- tree then_tree = this->then_block_->get_tree(context);
- tree else_tree = (this->else_block_ == NULL
- ? NULL_TREE
- : this->else_block_->get_tree(context));
-
+ Bblock* then_block = this->then_block_->get_backend(context);
+ Bblock* else_block = (this->else_block_ == NULL
+ ? NULL
+ : this->else_block_->get_backend(context));
Bexpression* cond_expr = tree_to_expr(cond_tree);
- Bstatement* then_stat = tree_to_stat(then_tree);
- Bstatement* else_stat = (else_tree == NULL_TREE
- ? NULL
- : tree_to_stat(else_tree));
- Bstatement* ret = context->backend()->if_statement(cond_expr, then_stat,
- else_stat,
+ Bstatement* ret = context->backend()->if_statement(cond_expr, then_block,
+ else_block,
this->location());
return stat_to_tree(ret);
}
if (this->statements_ == NULL)
statements = NULL;
else
- statements = tree_to_stat(this->statements_->get_tree(context));
+ {
+ Bblock* bblock = this->statements_->get_backend(context);
+ statements = context->backend()->block_statement(bblock);
+ }
Bstatement* break_stat;
if (this->is_fallthrough_)
{
if (this->statements_ == NULL)
return NULL;
- return tree_to_stat(this->statements_->get_tree(context));
+ Bblock* bblock = this->statements_->get_backend(context);
+ return context->backend()->block_statement(bblock);
}
// Class Select_clauses.
// Make up a translation context for the array length
// expression. FIXME: This won't work in general.
- Translate_context context(gogo, NULL, NULL, NULL_TREE);
+ Translate_context context(gogo, NULL, NULL, NULL);
tree len = this->length_->get_tree(&context);
if (len != error_mark_node)
{