Skip to content

Commit 19ed722

Browse files
thanmianlancetaylor
authored andcommitted
compiler: fix bug in handling of unordered set during exporting
In CL 183850 a change was made to combine tracking/discovery of exported types and imported packages during export data generation. As a result of this refactoring a bug was introduced: the new code can potentially insert items into the exports set (an unordered_set) while iterating through the same set, which is illegal according to the spec for std::unordered_set. This patch fixes the problem by changing the type discovery phase to iterate through a separate list of sorted exports, as opposed to iterating through the main unordered set. Also included is a change to fix the code that looks for variables that are referenced from inlined routine bodies (this code wasn't scanning all of the function that it needed to scan). New test case for this problem in CL 186697. Updates golang/go#33020. Change-Id: Id9ae5af462bc0819e8c9a8dd038ce9746297ad5d Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/185977 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 0e51b7e commit 19ed722

File tree

3 files changed

+62
-42
lines changed

3 files changed

+62
-42
lines changed

go/export.cc

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ class Collect_export_references : public Traverse
111111
: Traverse(traverse_expressions
112112
| traverse_types),
113113
exp_(exp), exports_(exports), imports_(imports),
114-
inline_fcn_worklist_(NULL)
114+
inline_fcn_worklist_(NULL), exports_finalized_(false)
115115
{ }
116116

117117
// Initial entry point; performs a walk to expand the exports set.
@@ -121,7 +121,7 @@ class Collect_export_references : public Traverse
121121
// Second entry point (called after the method above), to find
122122
// all types referenced by exports.
123123
void
124-
prepare_types();
124+
prepare_types(const std::vector<Named_object*>& sorted_exports);
125125

126126
protected:
127127
// Override of parent class method.
@@ -141,6 +141,13 @@ class Collect_export_references : public Traverse
141141
traverse_named_type(Named_type*);
142142

143143
private:
144+
145+
// Add a named object to the exports set (during expand_exports()).
146+
// Returns TRUE if a new object was added to the exports set,
147+
// FALSE otherwise.
148+
bool
149+
add_to_exports(Named_object*);
150+
144151
// The exporter.
145152
Export* exp_;
146153
// The set of named objects to export.
@@ -152,6 +159,8 @@ class Collect_export_references : public Traverse
152159
// Worklist of functions we are exporting with inline bodies that need
153160
// to be checked.
154161
std::vector<Named_object*>* inline_fcn_worklist_;
162+
// Set to true if expand_exports() has been called and is complete.
163+
bool exports_finalized_;
155164
};
156165

157166
void
@@ -172,6 +181,18 @@ Collect_export_references::expand_exports(std::vector<Named_object*>* fcns)
172181
}
173182
}
174183
this->inline_fcn_worklist_ = NULL;
184+
this->exports_finalized_ = true;
185+
}
186+
187+
bool
188+
Collect_export_references::add_to_exports(Named_object* no)
189+
{
190+
std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
191+
this->exports_->insert(no);
192+
// If the export list has been finalized, then we should not be
193+
// adding anything new to the exports set.
194+
go_assert(!this->exports_finalized_ || !ins.second);
195+
return ins.second;
175196
}
176197

177198
int
@@ -189,7 +210,7 @@ Collect_export_references::expression(Expression** pexpr)
189210
if (var_package != NULL)
190211
this->imports_->insert(var_package);
191212

192-
this->exports_->insert(no);
213+
this->add_to_exports(no);
193214
no->var_value()->set_is_referenced_by_inline();
194215
}
195216
return TRAVERSE_CONTINUE;
@@ -210,17 +231,16 @@ Collect_export_references::expression(Expression** pexpr)
210231

211232
if (this->inline_fcn_worklist_ != NULL)
212233
{
213-
std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
214-
this->exports_->insert(no);
234+
bool added = this->add_to_exports(no);
215235

216236
if (no->is_function())
217237
no->func_value()->set_is_referenced_by_inline();
218238

219-
// If ins.second is false then this object was already in
239+
// If 'added' is false then this object was already in
220240
// exports_, in which case it was already added to
221241
// check_inline_refs_ the first time we added it to exports_, so
222242
// we don't need to add it again.
223-
if (ins.second
243+
if (added
224244
&& no->is_function()
225245
&& no->func_value()->export_for_inlining())
226246
this->inline_fcn_worklist_->push_back(no);
@@ -238,11 +258,11 @@ Collect_export_references::expression(Expression** pexpr)
238258
// exported inline function from another package).
239259

240260
void
241-
Collect_export_references::prepare_types()
261+
Collect_export_references::prepare_types(const std::vector<Named_object*>& sorted_exports)
242262
{
243263
// Iterate through the exported objects and traverse any types encountered.
244-
for (Unordered_set(Named_object*)::iterator p = this->exports_->begin();
245-
p != this->exports_->end();
264+
for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin();
265+
p != sorted_exports.end();
246266
++p)
247267
{
248268
Named_object* no = *p;
@@ -506,7 +526,8 @@ Export::export_globals(const std::string& package_name,
506526
const std::map<std::string, Package*>& imports,
507527
const std::string& import_init_fn,
508528
const Import_init_set& imported_init_fns,
509-
const Bindings* bindings)
529+
const Bindings* bindings,
530+
Unordered_set(Named_object*)* functions_marked_inline)
510531
{
511532
// If there have been any errors so far, don't try to export
512533
// anything. That way the export code doesn't have to worry about
@@ -520,35 +541,21 @@ Export::export_globals(const std::string& package_name,
520541
// CHECK_INLINE_REFS is also on EXPORTS.
521542
Unordered_set(Named_object*) exports;
522543
std::vector<Named_object*> check_inline_refs;
544+
check_inline_refs.reserve(functions_marked_inline->size());
545+
546+
// Add all functions/methods from the "marked inlined" set to the
547+
// CHECK_INLINE_REFS worklist.
548+
for (Unordered_set(Named_object*)::const_iterator p = functions_marked_inline->begin();
549+
p != functions_marked_inline->end();
550+
++p)
551+
check_inline_refs.push_back(*p);
523552

524553
for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
525554
p != bindings->end_definitions();
526555
++p)
527556
{
528557
if (should_export(*p))
529-
{
530-
exports.insert(*p);
531-
532-
if ((*p)->is_function()
533-
&& (*p)->func_value()->export_for_inlining())
534-
check_inline_refs.push_back(*p);
535-
else if ((*p)->is_type())
536-
{
537-
const Bindings* methods = (*p)->type_value()->local_methods();
538-
if (methods != NULL)
539-
{
540-
for (Bindings::const_definitions_iterator pm =
541-
methods->begin_definitions();
542-
pm != methods->end_definitions();
543-
++pm)
544-
{
545-
Function* fn = (*pm)->func_value();
546-
if (fn->export_for_inlining())
547-
check_inline_refs.push_back(*pm);
548-
}
549-
}
550-
}
551-
}
558+
exports.insert(*p);
552559
}
553560

554561
for (Bindings::const_declarations_iterator p =
@@ -593,7 +600,7 @@ Export::export_globals(const std::string& package_name,
593600

594601
// Collect up the set of types mentioned in things we're exporting,
595602
// and any packages that may be referred to indirectly.
596-
collect.prepare_types();
603+
collect.prepare_types(sorted_exports);
597604

598605
// Assign indexes to all exported types and types referenced by
599606
// things we're exporting. Return value is index of first non-exported

go/export.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ class Export : public String_dump
158158
const std::map<std::string, Package*>& imports,
159159
const std::string& import_init_fn,
160160
const Import_init_set& imported_init_fns,
161-
const Bindings* bindings);
161+
const Bindings* bindings,
162+
Unordered_set(Named_object*)* marked_inline_functions);
162163

163164
// Record a type that is mentioned in export data. Return value is
164165
// TRUE for newly visited types, FALSE for types that have been seen

go/gogo.cc

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5078,9 +5078,10 @@ Inline_within_budget::expression(Expression** pexpr)
50785078
class Mark_inline_candidates : public Traverse
50795079
{
50805080
public:
5081-
Mark_inline_candidates()
5081+
Mark_inline_candidates(Unordered_set(Named_object*)* marked)
50825082
: Traverse(traverse_functions
5083-
| traverse_types)
5083+
| traverse_types),
5084+
marked_functions_(marked)
50845085
{ }
50855086

50865087
int
@@ -5097,6 +5098,9 @@ class Mark_inline_candidates : public Traverse
50975098
// budget is a heuristic. In the usual GCC spirit, we could
50985099
// consider setting this via a command line option.
50995100
const int budget_heuristic = 80;
5101+
5102+
// Set of named objects that are marked as inline candidates.
5103+
Unordered_set(Named_object*)* marked_functions_;
51005104
};
51015105

51025106
// Mark a function if it is an inline candidate.
@@ -5109,7 +5113,10 @@ Mark_inline_candidates::function(Named_object* no)
51095113
Inline_within_budget iwb(&budget);
51105114
func->block()->traverse(&iwb);
51115115
if (budget >= 0)
5112-
func->set_export_for_inlining();
5116+
{
5117+
func->set_export_for_inlining();
5118+
this->marked_functions_->insert(no);
5119+
}
51135120
return TRAVERSE_CONTINUE;
51145121
}
51155122

@@ -5135,7 +5142,10 @@ Mark_inline_candidates::type(Type* t)
51355142
Inline_within_budget iwb(&budget);
51365143
func->block()->traverse(&iwb);
51375144
if (budget >= 0)
5138-
func->set_export_for_inlining();
5145+
{
5146+
func->set_export_for_inlining();
5147+
this->marked_functions_->insert(no);
5148+
}
51395149
}
51405150
return TRAVERSE_CONTINUE;
51415151
}
@@ -5150,7 +5160,8 @@ Gogo::do_exports()
51505160

51515161
// Mark any functions whose body should be exported for inlining by
51525162
// other packages.
5153-
Mark_inline_candidates mic;
5163+
Unordered_set(Named_object*) marked_functions;
5164+
Mark_inline_candidates mic(&marked_functions);
51545165
this->traverse(&mic);
51555166

51565167
// For now we always stream to a section. Later we may want to
@@ -5187,7 +5198,8 @@ Gogo::do_exports()
51875198
this->imports_,
51885199
init_fn_name,
51895200
this->imported_init_fns_,
5190-
this->package_->bindings());
5201+
this->package_->bindings(),
5202+
&marked_functions);
51915203

51925204
if (!this->c_header_.empty() && !saw_errors())
51935205
this->write_c_header();

0 commit comments

Comments
 (0)