Skip to content

Commit 06b6c63

Browse files
author
Ian Lance Taylor
committed
compiler: enable escape analysis for runtime
The runtime package was hard-coded non-escape, and the escape analysis was not run for the runtime package. This CL removes the hard-code, and lets the escape analysis decide. It is not allowed for local variables and closures in the runtime to be heap allocated. This CL adds the check that make sure that they indeed do not escape. The escape analysis is always run when compiling the runtime now. Fixes golang/go#17431 Reviewed-on: https://go-review.googlesource.com/86246 From-SVN: r256820
1 parent 78a5fce commit 06b6c63

File tree

5 files changed

+114
-88
lines changed

5 files changed

+114
-88
lines changed

gcc/go/gofrontend/MERGE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
3ea7fc3b918210e7248dbc51d90af20639dc4167
1+
1072286ca9249bd6f75628aead325a66286bcf5b
22

33
The first line of this file holds the git revision number of the last
44
merge done from the gofrontend repository.

gcc/go/gofrontend/escape.cc

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -873,13 +873,12 @@ escape_hash_match(std::string suffix, std::string name)
873873
void
874874
Gogo::analyze_escape()
875875
{
876-
if (!optimize_allocation_flag.is_enabled() || saw_errors())
876+
if (saw_errors())
877877
return;
878878

879-
// Currently runtime is hard-coded to non-escape in various places.
880-
// Don't run escape analysis for runtime.
881-
// TODO: remove this once it works for runtime.
882-
if (this->compiling_runtime() && this->package_name() == "runtime")
879+
if (!optimize_allocation_flag.is_enabled()
880+
&& !this->compiling_runtime())
881+
// We always run escape analysis when compiling runtime.
883882
return;
884883

885884
// Discover strongly connected groups of functions to analyze for escape
@@ -1473,6 +1472,35 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s)
14731472
return TRAVERSE_SKIP_COMPONENTS;
14741473
}
14751474

1475+
// Helper function to emit moved-to-heap diagnostics.
1476+
1477+
static void
1478+
move_to_heap(Gogo* gogo, Expression *expr)
1479+
{
1480+
Named_object* no;
1481+
if (expr->var_expression() != NULL)
1482+
no = expr->var_expression()->named_object();
1483+
else if (expr->enclosed_var_expression() != NULL)
1484+
no = expr->enclosed_var_expression()->variable();
1485+
else
1486+
return;
1487+
1488+
if ((no->is_variable()
1489+
&& !no->var_value()->is_global())
1490+
|| no->is_result_variable())
1491+
{
1492+
Node* n = Node::make_node(expr);
1493+
if (gogo->debug_escape_level() != 0)
1494+
go_inform(n->definition_location(),
1495+
"moved to heap: %s",
1496+
n->ast_format(gogo).c_str());
1497+
if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
1498+
go_error_at(expr->location(),
1499+
"%s escapes to heap, not allowed in runtime",
1500+
n->ast_format(gogo).c_str());
1501+
}
1502+
}
1503+
14761504
// Model expressions within a function as assignments and flows between nodes.
14771505

14781506
int
@@ -1489,13 +1517,7 @@ Escape_analysis_assign::expression(Expression** pexpr)
14891517
if (debug_level > 1)
14901518
go_inform((*pexpr)->location(), "%s too large for stack",
14911519
n->ast_format(gogo).c_str());
1492-
if (debug_level != 0
1493-
&& ((*pexpr)->var_expression() != NULL
1494-
|| (*pexpr)->enclosed_var_expression() != NULL))
1495-
go_inform(n->definition_location(),
1496-
"moved to heap: %s",
1497-
n->ast_format(gogo).c_str());
1498-
1520+
move_to_heap(gogo, *pexpr);
14991521
n->set_encoding(Node::ESCAPE_HEAP);
15001522
(*pexpr)->address_taken(true);
15011523
this->assign(this->context_->sink(), n);
@@ -2968,25 +2990,20 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
29682990
if (src_leaks)
29692991
{
29702992
src->set_encoding(Node::ESCAPE_HEAP);
2971-
if (debug_level != 0 && osrcesc != src->encoding())
2972-
{
2973-
if (underlying->var_expression() != NULL
2974-
|| underlying->enclosed_var_expression() != NULL)
2975-
go_inform(underlying_node->definition_location(),
2976-
"moved to heap: %s",
2977-
underlying_node->ast_format(gogo).c_str());
2978-
2979-
if (debug_level > 1)
2980-
go_inform(src->location(),
2981-
"%s escapes to heap, level={%d %d}, "
2982-
"dst.eld=%d, src.eld=%d",
2983-
src->ast_format(gogo).c_str(), level.value(),
2984-
level.suffix_value(), dst_state->loop_depth,
2985-
mod_loop_depth);
2986-
else
2987-
go_inform(src->location(), "%s escapes to heap",
2988-
src->ast_format(gogo).c_str());
2989-
}
2993+
if (osrcesc != src->encoding())
2994+
{
2995+
move_to_heap(gogo, underlying);
2996+
if (debug_level > 1)
2997+
go_inform(src->location(),
2998+
"%s escapes to heap, level={%d %d}, "
2999+
"dst.eld=%d, src.eld=%d",
3000+
src->ast_format(gogo).c_str(), level.value(),
3001+
level.suffix_value(), dst_state->loop_depth,
3002+
mod_loop_depth);
3003+
else if (debug_level > 0)
3004+
go_inform(src->location(), "%s escapes to heap",
3005+
src->ast_format(gogo).c_str());
3006+
}
29903007

29913008
this->flood(level.decrease(), dst,
29923009
underlying_node, mod_loop_depth);

gcc/go/gofrontend/expressions.cc

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3700,7 +3700,7 @@ Expression::make_unsafe_cast(Type* type, Expression* expr,
37003700
// called after escape analysis but before inserting write barriers.
37013701

37023702
void
3703-
Unary_expression::check_operand_address_taken(Gogo* gogo)
3703+
Unary_expression::check_operand_address_taken(Gogo*)
37043704
{
37053705
if (this->op_ != OPERATOR_AND)
37063706
return;
@@ -3714,13 +3714,6 @@ Unary_expression::check_operand_address_taken(Gogo* gogo)
37143714
if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
37153715
this->escapes_ = false;
37163716

3717-
// When compiling the runtime, the address operator does not cause
3718-
// local variables to escape. When escape analysis becomes the
3719-
// default, this should be changed to make it an error if we have an
3720-
// address operator that escapes.
3721-
if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
3722-
this->escapes_ = false;
3723-
37243717
Named_object* var = NULL;
37253718
if (this->expr_->var_expression() != NULL)
37263719
var = this->expr_->var_expression()->named_object();
@@ -7028,26 +7021,14 @@ Bound_method_expression::do_flatten(Gogo* gogo, Named_object*,
70287021
vals->push_back(val);
70297022

70307023
Expression* ret = Expression::make_struct_composite_literal(st, vals, loc);
7024+
ret = Expression::make_heap_expression(ret, loc);
70317025

7032-
if (!gogo->compiling_runtime() || gogo->package_name() != "runtime")
7033-
{
7034-
ret = Expression::make_heap_expression(ret, loc);
7035-
Node* n = Node::make_node(this);
7036-
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
7037-
ret->heap_expression()->set_allocate_on_stack();
7038-
}
7039-
else
7040-
{
7041-
// When compiling the runtime, method closures do not escape.
7042-
// When escape analysis becomes the default, and applies to
7043-
// method closures, this should be changed to make it an error
7044-
// if a method closure escapes.
7045-
Temporary_statement* ctemp = Statement::make_temporary(st, ret, loc);
7046-
inserter->insert(ctemp);
7047-
ret = Expression::make_temporary_reference(ctemp, loc);
7048-
ret = Expression::make_unary(OPERATOR_AND, ret, loc);
7049-
ret->unary_expression()->set_does_not_escape();
7050-
}
7026+
Node* n = Node::make_node(this);
7027+
if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
7028+
ret->heap_expression()->set_allocate_on_stack();
7029+
else if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
7030+
go_error_at(loc, "%s escapes to heap, not allowed in runtime",
7031+
n->ast_format(gogo).c_str());
70517032

70527033
// If necessary, check whether the expression or any embedded
70537034
// pointers are nil.
@@ -9577,12 +9558,6 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function,
95779558
Type* varargs_type, size_t param_count,
95789559
Slice_storage_escape_disp escape_disp)
95799560
{
9580-
// When compiling the runtime, varargs slices do not escape. When
9581-
// escape analysis becomes the default, this should be changed to
9582-
// make it an error if we have a varargs slice that escapes.
9583-
if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
9584-
escape_disp = SLICE_STORAGE_DOES_NOT_ESCAPE;
9585-
95869561
if (this->varargs_are_lowered_)
95879562
return;
95889563

gcc/go/gofrontend/parse.cc

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3059,21 +3059,6 @@ Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars,
30593059
Struct_type* st = closure_var->var_value()->type()->deref()->struct_type();
30603060
Expression* cv = Expression::make_struct_composite_literal(st, initializer,
30613061
location);
3062-
3063-
// When compiling the runtime, closures do not escape. When escape
3064-
// analysis becomes the default, and applies to closures, this
3065-
// should be changed to make it an error if a closure escapes.
3066-
if (this->gogo_->compiling_runtime()
3067-
&& this->gogo_->package_name() == "runtime")
3068-
{
3069-
Temporary_statement* ctemp = Statement::make_temporary(st, cv, location);
3070-
this->gogo_->add_statement(ctemp);
3071-
Expression* ref = Expression::make_temporary_reference(ctemp, location);
3072-
Expression* addr = Expression::make_unary(OPERATOR_AND, ref, location);
3073-
addr->unary_expression()->set_does_not_escape();
3074-
return addr;
3075-
}
3076-
30773062
return Expression::make_heap_expression(cv, location);
30783063
}
30793064

gcc/go/gofrontend/wb.cc

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,10 @@ Mark_address_taken::expression(Expression** pexpr)
5454
// Slice of an array. The escape analysis models this with
5555
// a child Node representing the address of the array.
5656
bool escapes = false;
57-
if (!this->gogo_->compiling_runtime()
58-
|| this->gogo_->package_name() != "runtime")
59-
{
60-
Node* n = Node::make_node(expr);
61-
if (n->child() == NULL
62-
|| (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
63-
escapes = true;
64-
}
57+
Node* n = Node::make_node(expr);
58+
if (n->child() == NULL
59+
|| (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
60+
escapes = true;
6561
aie->array()->address_taken(escapes);
6662
}
6763

@@ -127,6 +123,53 @@ Mark_address_taken::expression(Expression** pexpr)
127123
return TRAVERSE_CONTINUE;
128124
}
129125

126+
// Check variables and closures do not escape when compiling runtime.
127+
128+
class Check_escape : public Traverse
129+
{
130+
public:
131+
Check_escape(Gogo* gogo)
132+
: Traverse(traverse_expressions | traverse_variables),
133+
gogo_(gogo)
134+
{ }
135+
136+
int
137+
expression(Expression**);
138+
139+
int
140+
variable(Named_object*);
141+
142+
private:
143+
Gogo* gogo_;
144+
};
145+
146+
int
147+
Check_escape::variable(Named_object* no)
148+
{
149+
if ((no->is_variable() && no->var_value()->is_in_heap())
150+
|| (no->is_result_variable()
151+
&& no->result_var_value()->is_in_heap()))
152+
go_error_at(no->location(),
153+
"%s escapes to heap, not allowed in runtime",
154+
no->name().c_str());
155+
return TRAVERSE_CONTINUE;
156+
}
157+
158+
int
159+
Check_escape::expression(Expression** pexpr)
160+
{
161+
Expression* expr = *pexpr;
162+
Func_expression* fe = expr->func_expression();
163+
if (fe != NULL && fe->closure() != NULL)
164+
{
165+
Node* n = Node::make_node(expr);
166+
if (n->encoding() == Node::ESCAPE_HEAP)
167+
go_error_at(expr->location(),
168+
"heap-allocated closure, not allowed in runtime");
169+
}
170+
return TRAVERSE_CONTINUE;
171+
}
172+
130173
// Add write barriers to the IR. This are required by the concurrent
131174
// garbage collector. A write barrier is needed for any write of a
132175
// pointer into memory controlled by the garbage collector. Write
@@ -370,6 +413,12 @@ Gogo::add_write_barriers()
370413
Mark_address_taken mat(this);
371414
this->traverse(&mat);
372415

416+
if (this->compiling_runtime() && this->package_name() == "runtime")
417+
{
418+
Check_escape chk(this);
419+
this->traverse(&chk);
420+
}
421+
373422
Write_barriers wb(this);
374423
this->traverse(&wb);
375424
}

0 commit comments

Comments
 (0)