From 886534d94a988ed2a7bd3ac4c536a4cb1c2bd7f5 Mon Sep 17 00:00:00 2001 From: ian Date: Thu, 3 May 2012 16:09:44 +0000 Subject: [PATCH] compiler: Fix order of initialization bug with global var a, b = f(). git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/branches/gcc-4_7-branch@187104 138bc75d-0d04-0410-961f-82ee72b054a4 --- gcc/go/gofrontend/gogo-tree.cc | 54 ++++++++++++++++++++++++++++++------------ gcc/go/gofrontend/gogo.cc | 5 ++++ gcc/go/gofrontend/gogo.h | 26 ++++++++++++++++++-- gcc/go/gofrontend/parse.cc | 18 +++++++++++++- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/gcc/go/gofrontend/gogo-tree.cc b/gcc/go/gofrontend/gogo-tree.cc index fa229320c96..b379b954c68 100644 --- a/gcc/go/gofrontend/gogo-tree.cc +++ b/gcc/go/gofrontend/gogo-tree.cc @@ -591,10 +591,11 @@ Find_var::expression(Expression** pexpr) return TRAVERSE_CONTINUE; } -// Return true if EXPR refers to VAR. +// Return true if EXPR, PREINIT, or DEP refers to VAR. static bool -expression_requires(Expression* expr, Block* preinit, Named_object* var) +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); @@ -602,7 +603,15 @@ expression_requires(Expression* expr, Block* preinit, Named_object* var) 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(); } @@ -659,7 +668,7 @@ typedef std::list Var_inits; // variable V2 then we initialize V1 after V2. static void -sort_var_inits(Var_inits* var_inits) +sort_var_inits(Gogo* gogo, Var_inits* var_inits) { Var_inits ready; while (!var_inits->empty()) @@ -668,6 +677,7 @@ sort_var_inits(Var_inits* var_inits) 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. We can skip P1->WAITING variables--that @@ -679,20 +689,22 @@ sort_var_inits(Var_inits* var_inits) for (; p2 != var_inits->end(); ++p2) { - if (expression_requires(init, preinit, p2->var())) + Named_object* p2var = p2->var(); + if (expression_requires(init, preinit, dep, p2var)) { // Check for cycles. - if (expression_requires(p2->var()->var_value()->init(), - p2->var()->var_value()->preinit(), + if (expression_requires(p2var->var_value()->init(), + p2var->var_value()->preinit(), + gogo->var_depends_on(p2var->var_value()), var)) { error_at(var->location(), ("initialization expressions for %qs and " "%qs depend upon each other"), var->message_name().c_str(), - p2->var()->message_name().c_str()); + p2var->message_name().c_str()); inform(p2->var()->location(), "%qs defined here", - p2->var()->message_name().c_str()); + p2var->message_name().c_str()); p2 = var_inits->end(); } else @@ -715,9 +727,11 @@ sort_var_inits(Var_inits* var_inits) // 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; when INIT is NULL, it means that - // PREINIT sets VAR, which we will interpret as a loop. - if (init != NULL && expression_requires(init, preinit, var)) + // 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()); @@ -784,7 +798,7 @@ Gogo::write_globals() } // There is nothing useful we can output for constants which - // have ideal or non-integeral type. + // have ideal or non-integral type. if (no->is_const()) { Type* type = no->const_value()->type(); @@ -835,7 +849,9 @@ Gogo::write_globals() ; else if (TREE_CONSTANT(init)) { - if (expression_requires(no->var_value()->init(), NULL, no)) + 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", @@ -880,6 +896,14 @@ Gogo::write_globals() else var_inits.push_back(Var_init(no, var_init_tree)); } + 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. + var_inits.push_back(Var_init(no, integer_zero_node)); + } if (!is_sink && no->var_value()->type()->has_pointer()) var_gc.push_back(no); @@ -897,7 +921,7 @@ Gogo::write_globals() // workable order. if (!var_inits.empty()) { - sort_var_inits(&var_inits); + sort_var_inits(this, &var_inits); for (Var_inits::const_iterator p = var_inits.begin(); p != var_inits.end(); ++p) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index 7bc0b557c9f..15c814bd12b 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int int_type_size, imported_unsafe_(false), packages_(), init_functions_(), + var_deps_(), need_init_fn_(false), init_fn_name_(), imported_init_fns_(), @@ -3820,6 +3821,10 @@ void Variable::lower_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()->lower_init_expression(gogo, function, inserter); + if (this->init_ != NULL && !this->init_is_lowered_) { if (this->seen_) diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 9c5f8cb4a6b..4990bf26dc7 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -384,6 +384,23 @@ class Gogo void clear_file_scope(); + // Record that VAR1 must be initialized after VAR2. This is used + // when VAR2 does not appear in VAR1's INIT or PREINIT. + void + record_var_depends_on(Variable* var1, Named_object* var2) + { + go_assert(this->var_deps_.find(var1) == this->var_deps_.end()); + this->var_deps_[var1] = var2; + } + + // Return the variable that VAR depends on, or NULL if none. + Named_object* + var_depends_on(Variable* var) const + { + Var_deps::const_iterator p = this->var_deps_.find(var); + return p != this->var_deps_.end() ? p->second : NULL; + } + // Queue up a type-specific function to be written out. This is // used when a type-specific function is needed when not at the top // level. @@ -639,8 +656,9 @@ class Gogo // Type used to map package names to packages. typedef std::map Packages; - // Type used to map special names in the sys package. - typedef std::map Sys_names; + // Type used to map variables to the function calls that set them. + // This is used for initialization dependency analysis. + typedef std::map Var_deps; // Type used to queue writing a type specific function. struct Specific_type_function @@ -683,6 +701,10 @@ class Gogo Packages packages_; // The functions named "init", if there are any. std::vector init_functions_; + // A mapping from variables to the function calls that initialize + // them, if it is not stored in the variable's init or preinit. + // This is used for dependency analysis. + Var_deps var_deps_; // Whether we need a magic initialization function. bool need_init_fn_; // The name of the magic initialization function. diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 7a567a1cd14..6567a42b36f 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -1667,6 +1667,7 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, // the right number of values, but it might. Declare the variables, // and then assign the results of the call to them. + Named_object* first_var = NULL; unsigned int index = 0; bool any_new = false; for (Typed_identifier_list::const_iterator pv = vars->begin(); @@ -1674,7 +1675,22 @@ Parse::init_vars_from_call(const Typed_identifier_list* vars, Type* type, ++pv, ++index) { Expression* init = Expression::make_call_result(call, index); - this->init_var(*pv, type, init, is_coloneq, false, &any_new); + Named_object* no = this->init_var(*pv, type, init, is_coloneq, false, + &any_new); + + if (this->gogo_->in_global_scope() && no->is_variable()) + { + if (first_var == NULL) + first_var = no; + else + { + // The subsequent vars have an implicit dependency on + // the first one, so that everything gets initialized in + // the right order and so that we detect cycles + // correctly. + this->gogo_->record_var_depends_on(no->var_value(), first_var); + } + } } if (is_coloneq && !any_new) -- 2.11.0