This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Go patch committed: Add flattening pass
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org, gofrontend-dev at googlegroups dot com
- Date: Thu, 09 Jan 2014 15:28:39 -0800
- Subject: Go patch committed: Add flattening pass
- Authentication-results: sourceware.org; auth=none
This patch from Chris Manghane adds a flattening pass to the Go
frontend. This is a step toward moving more types of expressions into
the backend interface. Bootstrapped and ran Go testsuite on
x86_64-unknown-linux-gnu. Committed to mainline.
Ian
diff -r 447683c37ddf go/expressions.h
--- a/go/expressions.h Thu Jan 09 15:16:28 2014 -0800
+++ b/go/expressions.h Thu Jan 09 15:18:09 2014 -0800
@@ -575,6 +575,18 @@
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 @@
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 -r 447683c37ddf go/go.cc
--- a/go/go.cc Thu Jan 09 15:16:28 2014 -0800
+++ b/go/go.cc Thu Jan 09 15:18:09 2014 -0800
@@ -119,12 +119,15 @@
// 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 -r 447683c37ddf go/gogo.cc
--- a/go/gogo.cc Thu Jan 09 15:16:28 2014 -0800
+++ b/go/gogo.cc Thu Jan 09 15:18:09 2014 -0800
@@ -2703,6 +2703,169 @@
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 @@
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 @@
}
}
+// 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 -r 447683c37ddf go/gogo.h
--- a/go/gogo.h Thu Jan 09 15:16:28 2014 -0800
+++ b/go/gogo.h Thu Jan 09 15:18:09 2014 -0800
@@ -487,6 +487,10 @@
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 @@
void
order_evaluations();
+ // Flatten parse tree.
+ void
+ flatten();
+
// Build thunks for functions which call recover.
void
build_recover_thunks();
@@ -1447,6 +1455,10 @@
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 @@
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 -r 447683c37ddf go/statements.cc
--- a/go/statements.cc Thu Jan 09 15:16:28 2014 -0800
+++ b/go/statements.cc Thu Jan 09 15:18:09 2014 -0800
@@ -246,6 +246,16 @@
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 -r 447683c37ddf go/statements.h
--- a/go/statements.h Thu Jan 09 15:16:28 2014 -0800
+++ b/go/statements.h Thu Jan 09 15:18:09 2014 -0800
@@ -306,6 +306,16 @@
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 @@
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 @@
Statement*
do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
+ Statement*
+ do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
Bstatement*
do_get_backend(Translate_context*);