Commit 4bc44ceb by Ian Lance Taylor

compiler: Add flattening pass

From-SVN: r206502
parent abd47137
......@@ -575,6 +575,18 @@ class Expression
int iota_value)
{ return this->do_lower(gogo, function, inserter, iota_value); }
// Flatten an expression. This is called after order_evaluation.
// FUNCTION is the function we are in; it will be NULL for an
// expression initializing a global variable. INSERTER may be used
// to insert statements before the statement or initializer
// containing this expression; it is normally used to create
// temporary variables. This function must resolve expressions
// which could not be fully parsed into their final form. It
// returns the same Expression or a new one.
Expression*
flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter)
{ return this->do_flatten(gogo, function, inserter); }
// Determine the real type of an expression with abstract integer,
// floating point, or complex type. TYPE_CONTEXT describes the
// expected type.
......@@ -698,6 +710,12 @@ class Expression
do_lower(Gogo*, Named_object*, Statement_inserter*, int)
{ return this; }
// Return a flattened expression.
virtual Expression*
do_flatten(Gogo*, Named_object*, Statement_inserter*)
{ return this; }
// Return whether this is a constant expression.
virtual bool
do_is_constant() const
......
......@@ -119,12 +119,15 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
// Use temporary variables to force order of evaluation.
::gogo->order_evaluations();
// Flatten the parse tree.
::gogo->flatten();
// Build thunks for functions which call recover.
::gogo->build_recover_thunks();
// Convert complicated go and defer statements into simpler ones.
::gogo->simplify_thunk_statements();
// Dump ast, use filename[0] as the base name
::gogo->dump_ast(filenames[0]);
}
......
......@@ -2703,6 +2703,169 @@ Gogo::order_evaluations()
this->traverse(&order_eval);
}
// Traversal to flatten parse tree after order of evaluation rules are applied.
class Flatten : public Traverse
{
public:
Flatten(Gogo* gogo, Named_object* function)
: Traverse(traverse_variables
| traverse_functions
| traverse_statements
| traverse_expressions),
gogo_(gogo), function_(function), inserter_()
{ }
void
set_inserter(const Statement_inserter* inserter)
{ this->inserter_ = *inserter; }
int
variable(Named_object*);
int
function(Named_object*);
int
statement(Block*, size_t* pindex, Statement*);
int
expression(Expression**);
private:
// General IR.
Gogo* gogo_;
// The function we are traversing.
Named_object* function_;
// Current statement inserter for use by expressions.
Statement_inserter inserter_;
};
// Flatten variables.
int
Flatten::variable(Named_object* no)
{
if (!no->is_variable())
return TRAVERSE_CONTINUE;
if (no->is_variable() && no->var_value()->is_global())
{
// Global variables can have loops in their initialization
// expressions. This is handled in flatten_init_expression.
no->var_value()->flatten_init_expression(this->gogo_, this->function_,
&this->inserter_);
return TRAVERSE_CONTINUE;
}
go_assert(!no->var_value()->has_pre_init());
return TRAVERSE_SKIP_COMPONENTS;
}
// Flatten the body of a function. Record the function while flattening it,
// so that we can pass it down when flattening an expression.
int
Flatten::function(Named_object* no)
{
go_assert(this->function_ == NULL);
this->function_ = no;
int t = no->func_value()->traverse(this);
this->function_ = NULL;
if (t == TRAVERSE_EXIT)
return t;
return TRAVERSE_SKIP_COMPONENTS;
}
// Flatten statement parse trees.
int
Flatten::statement(Block* block, size_t* pindex, Statement* sorig)
{
// Because we explicitly traverse the statement's contents
// ourselves, we want to skip block statements here. There is
// nothing to flatten in a block statement.
if (sorig->is_block_statement())
return TRAVERSE_CONTINUE;
Statement_inserter hold_inserter(this->inserter_);
this->inserter_ = Statement_inserter(block, pindex);
// Flatten the expressions first.
int t = sorig->traverse_contents(this);
if (t == TRAVERSE_EXIT)
{
this->inserter_ = hold_inserter;
return t;
}
// Keep flattening until nothing changes.
Statement* s = sorig;
while (true)
{
Statement* snew = s->flatten(this->gogo_, this->function_, block,
&this->inserter_);
if (snew == s)
break;
s = snew;
t = s->traverse_contents(this);
if (t == TRAVERSE_EXIT)
{
this->inserter_ = hold_inserter;
return t;
}
}
if (s != sorig)
block->replace_statement(*pindex, s);
this->inserter_ = hold_inserter;
return TRAVERSE_SKIP_COMPONENTS;
}
// Flatten expression parse trees.
int
Flatten::expression(Expression** pexpr)
{
// Keep flattening until nothing changes.
while (true)
{
Expression* e = *pexpr;
if (e->traverse_subexpressions(this) == TRAVERSE_EXIT)
return TRAVERSE_EXIT;
Expression* enew = e->flatten(this->gogo_, this->function_,
&this->inserter_);
if (enew == e)
break;
*pexpr = enew;
}
return TRAVERSE_SKIP_COMPONENTS;
}
// Flatten an expression. INSERTER may be NULL, in which case the
// expression had better not need to create any temporaries.
void
Gogo::flatten_expression(Named_object* function, Statement_inserter* inserter,
Expression** pexpr)
{
Flatten flatten(this, function);
if (inserter != NULL)
flatten.set_inserter(inserter);
flatten.expression(pexpr);
}
void
Gogo::flatten()
{
Flatten flatten(this, NULL);
this->traverse(&flatten);
}
// Traversal to convert calls to the predeclared recover function to
// pass in an argument indicating whether it can recover from a panic
// or not.
......@@ -4286,10 +4449,11 @@ Variable::Variable(Type* type, Expression* init, bool is_global,
backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false),
is_address_taken_(false), is_non_escaping_address_taken_(false),
seen_(false), init_is_lowered_(false), type_from_init_tuple_(false),
type_from_range_index_(false), type_from_range_value_(false),
type_from_chan_element_(false), is_type_switch_var_(false),
determined_type_(false), in_unique_section_(false)
seen_(false), init_is_lowered_(false), init_is_flattened_(false),
type_from_init_tuple_(false), type_from_range_index_(false),
type_from_range_value_(false), type_from_chan_element_(false),
is_type_switch_var_(false), determined_type_(false),
in_unique_section_(false)
{
go_assert(type != NULL || init != NULL);
go_assert(!is_parameter || init == NULL);
......@@ -4351,6 +4515,40 @@ Variable::lower_init_expression(Gogo* gogo, Named_object* function,
}
}
// Flatten the initialization expression after ordering evaluations.
void
Variable::flatten_init_expression(Gogo* gogo, Named_object* function,
Statement_inserter* inserter)
{
Named_object* dep = gogo->var_depends_on(this);
if (dep != NULL && dep->is_variable())
dep->var_value()->flatten_init_expression(gogo, function, inserter);
if (this->init_ != NULL && !this->init_is_flattened_)
{
if (this->seen_)
{
// We will give an error elsewhere, this is just to prevent
// an infinite loop.
return;
}
this->seen_ = true;
Statement_inserter global_inserter;
if (this->is_global_)
{
global_inserter = Statement_inserter(gogo, this);
inserter = &global_inserter;
}
gogo->flatten_expression(function, inserter, &this->init_);
this->seen_ = false;
this->init_is_flattened_ = true;
}
}
// Get the preinit block.
Block*
......
......@@ -487,6 +487,10 @@ class Gogo
void
lower_constant(Named_object*);
// Flatten an expression.
void
flatten_expression(Named_object* function, Statement_inserter*, Expression**);
// Create all necessary function descriptors.
void
create_function_descriptors();
......@@ -531,6 +535,10 @@ class Gogo
void
order_evaluations();
// Flatten parse tree.
void
flatten();
// Build thunks for functions which call recover.
void
build_recover_thunks();
......@@ -1447,6 +1455,10 @@ class Variable
void
lower_init_expression(Gogo*, Named_object*, Statement_inserter*);
// Flatten the initialization expression after ordering evaluations.
void
flatten_init_expression(Gogo*, Named_object*, Statement_inserter*);
// A special case: the init value is used only to determine the
// type. This is used if the variable is defined using := with the
// comma-ok form of a map index or a receive expression. The init
......@@ -1580,6 +1592,8 @@ class Variable
bool seen_ : 1;
// True if we have lowered the initialization expression.
bool init_is_lowered_ : 1;
// True if we have flattened the initialization expression.
bool init_is_flattened_ : 1;
// True if init is a tuple used to set the type.
bool type_from_init_tuple_ : 1;
// True if init is a range clause and the type is the index type.
......
......@@ -246,6 +246,16 @@ Variable_declaration_statement::do_lower(Gogo* gogo, Named_object* function,
return this;
}
// Flatten the variable's initialization expression.
Statement*
Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function,
Block*, Statement_inserter* inserter)
{
this->var_->var_value()->flatten_init_expression(gogo, function, inserter);
return this;
}
// Convert a variable declaration to the backend representation.
Bstatement*
......
......@@ -306,6 +306,16 @@ class Statement
Statement_inserter* inserter)
{ return this->do_lower(gogo, function, block, inserter); }
// Flatten a statement. This is called immediately after the order of
// evaluation rules are applied to statements. It returns the same
// Statement or a new one. FUNCTION is the function containing this
// statement. BLOCK is the block containing this statement.
// INSERTER can be used to insert new statements before this one.
Statement*
flatten(Gogo* gogo, Named_object* function, Block* block,
Statement_inserter* inserter)
{ return this->do_flatten(gogo, function, block, inserter); }
// Set type information for unnamed constants.
void
determine_types();
......@@ -412,6 +422,12 @@ class Statement
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
{ return this; }
// Implemented by the child class: lower this statement to a simpler
// one.
virtual Statement*
do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*)
{ return this; }
// Implemented by child class: set type information for unnamed
// constants. Any statement which includes an expression needs to
// implement this.
......@@ -583,6 +599,9 @@ class Variable_declaration_statement : public Statement
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
Statement*
do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
Bstatement*
do_get_backend(Translate_context*);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment