aboutsummaryrefslogtreecommitdiff
path: root/gcc/go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2014-01-09 23:28:10 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2014-01-09 23:28:10 +0000
commit4bc44ceb91c0f7ad3250b8b01bdebe4e8b003f86 (patch)
treec528403ee91166ddae1e31a8413eab3bacb5f12d /gcc/go
parentabd471378cbe20d51cc6a9c8b4f20907ef0f6825 (diff)
downloadgcc-4bc44ceb91c0f7ad3250b8b01bdebe4e8b003f86.zip
gcc-4bc44ceb91c0f7ad3250b8b01bdebe4e8b003f86.tar.gz
gcc-4bc44ceb91c0f7ad3250b8b01bdebe4e8b003f86.tar.bz2
compiler: Add flattening pass
From-SVN: r206502
Diffstat (limited to 'gcc/go')
-rw-r--r--gcc/go/gofrontend/expressions.h18
-rw-r--r--gcc/go/gofrontend/go.cc5
-rw-r--r--gcc/go/gofrontend/gogo.cc206
-rw-r--r--gcc/go/gofrontend/gogo.h14
-rw-r--r--gcc/go/gofrontend/statements.cc10
-rw-r--r--gcc/go/gofrontend/statements.h19
6 files changed, 267 insertions, 5 deletions
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index e447418..66fbac3 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -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
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index 55b4dca..26e83a1 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -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]);
}
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index e46bf9c..9e822e6 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -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*
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index a9a5681..285e060 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -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.
diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc
index 3a0bc3b..a28aa3d 100644
--- a/gcc/go/gofrontend/statements.cc
+++ b/gcc/go/gofrontend/statements.cc
@@ -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*
diff --git a/gcc/go/gofrontend/statements.h b/gcc/go/gofrontend/statements.h
index b128fa0..7d9bcfd 100644
--- a/gcc/go/gofrontend/statements.h
+++ b/gcc/go/gofrontend/statements.h
@@ -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*);