-3ea7fc3b918210e7248dbc51d90af20639dc4167
+1072286ca9249bd6f75628aead325a66286bcf5b
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
void
Gogo::analyze_escape()
{
- if (!optimize_allocation_flag.is_enabled() || saw_errors())
+ if (saw_errors())
return;
- // Currently runtime is hard-coded to non-escape in various places.
- // Don't run escape analysis for runtime.
- // TODO: remove this once it works for runtime.
- if (this->compiling_runtime() && this->package_name() == "runtime")
+ if (!optimize_allocation_flag.is_enabled()
+ && !this->compiling_runtime())
+ // We always run escape analysis when compiling runtime.
return;
// Discover strongly connected groups of functions to analyze for escape
return TRAVERSE_SKIP_COMPONENTS;
}
+// Helper function to emit moved-to-heap diagnostics.
+
+static void
+move_to_heap(Gogo* gogo, Expression *expr)
+{
+ Named_object* no;
+ if (expr->var_expression() != NULL)
+ no = expr->var_expression()->named_object();
+ else if (expr->enclosed_var_expression() != NULL)
+ no = expr->enclosed_var_expression()->variable();
+ else
+ return;
+
+ if ((no->is_variable()
+ && !no->var_value()->is_global())
+ || no->is_result_variable())
+ {
+ Node* n = Node::make_node(expr);
+ if (gogo->debug_escape_level() != 0)
+ go_inform(n->definition_location(),
+ "moved to heap: %s",
+ n->ast_format(gogo).c_str());
+ if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+ go_error_at(expr->location(),
+ "%s escapes to heap, not allowed in runtime",
+ n->ast_format(gogo).c_str());
+ }
+}
+
// Model expressions within a function as assignments and flows between nodes.
int
if (debug_level > 1)
go_inform((*pexpr)->location(), "%s too large for stack",
n->ast_format(gogo).c_str());
- if (debug_level != 0
- && ((*pexpr)->var_expression() != NULL
- || (*pexpr)->enclosed_var_expression() != NULL))
- go_inform(n->definition_location(),
- "moved to heap: %s",
- n->ast_format(gogo).c_str());
-
+ move_to_heap(gogo, *pexpr);
n->set_encoding(Node::ESCAPE_HEAP);
(*pexpr)->address_taken(true);
this->assign(this->context_->sink(), n);
if (src_leaks)
{
src->set_encoding(Node::ESCAPE_HEAP);
- if (debug_level != 0 && osrcesc != src->encoding())
- {
- if (underlying->var_expression() != NULL
- || underlying->enclosed_var_expression() != NULL)
- go_inform(underlying_node->definition_location(),
- "moved to heap: %s",
- underlying_node->ast_format(gogo).c_str());
-
- if (debug_level > 1)
- go_inform(src->location(),
- "%s escapes to heap, level={%d %d}, "
- "dst.eld=%d, src.eld=%d",
- src->ast_format(gogo).c_str(), level.value(),
- level.suffix_value(), dst_state->loop_depth,
- mod_loop_depth);
- else
- go_inform(src->location(), "%s escapes to heap",
- src->ast_format(gogo).c_str());
- }
+ if (osrcesc != src->encoding())
+ {
+ move_to_heap(gogo, underlying);
+ if (debug_level > 1)
+ go_inform(src->location(),
+ "%s escapes to heap, level={%d %d}, "
+ "dst.eld=%d, src.eld=%d",
+ src->ast_format(gogo).c_str(), level.value(),
+ level.suffix_value(), dst_state->loop_depth,
+ mod_loop_depth);
+ else if (debug_level > 0)
+ go_inform(src->location(), "%s escapes to heap",
+ src->ast_format(gogo).c_str());
+ }
this->flood(level.decrease(), dst,
underlying_node, mod_loop_depth);
// called after escape analysis but before inserting write barriers.
void
-Unary_expression::check_operand_address_taken(Gogo* gogo)
+Unary_expression::check_operand_address_taken(Gogo*)
{
if (this->op_ != OPERATOR_AND)
return;
if ((n->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
this->escapes_ = false;
- // When compiling the runtime, the address operator does not cause
- // local variables to escape. When escape analysis becomes the
- // default, this should be changed to make it an error if we have an
- // address operator that escapes.
- if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
- this->escapes_ = false;
-
Named_object* var = NULL;
if (this->expr_->var_expression() != NULL)
var = this->expr_->var_expression()->named_object();
vals->push_back(val);
Expression* ret = Expression::make_struct_composite_literal(st, vals, loc);
+ ret = Expression::make_heap_expression(ret, loc);
- if (!gogo->compiling_runtime() || gogo->package_name() != "runtime")
- {
- ret = Expression::make_heap_expression(ret, loc);
- Node* n = Node::make_node(this);
- if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
- ret->heap_expression()->set_allocate_on_stack();
- }
- else
- {
- // When compiling the runtime, method closures do not escape.
- // When escape analysis becomes the default, and applies to
- // method closures, this should be changed to make it an error
- // if a method closure escapes.
- Temporary_statement* ctemp = Statement::make_temporary(st, ret, loc);
- inserter->insert(ctemp);
- ret = Expression::make_temporary_reference(ctemp, loc);
- ret = Expression::make_unary(OPERATOR_AND, ret, loc);
- ret->unary_expression()->set_does_not_escape();
- }
+ Node* n = Node::make_node(this);
+ if ((n->encoding() & ESCAPE_MASK) == Node::ESCAPE_NONE)
+ ret->heap_expression()->set_allocate_on_stack();
+ else if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
+ go_error_at(loc, "%s escapes to heap, not allowed in runtime",
+ n->ast_format(gogo).c_str());
// If necessary, check whether the expression or any embedded
// pointers are nil.
Type* varargs_type, size_t param_count,
Slice_storage_escape_disp escape_disp)
{
- // When compiling the runtime, varargs slices do not escape. When
- // escape analysis becomes the default, this should be changed to
- // make it an error if we have a varargs slice that escapes.
- if (gogo->compiling_runtime() && gogo->package_name() == "runtime")
- escape_disp = SLICE_STORAGE_DOES_NOT_ESCAPE;
-
if (this->varargs_are_lowered_)
return;
Struct_type* st = closure_var->var_value()->type()->deref()->struct_type();
Expression* cv = Expression::make_struct_composite_literal(st, initializer,
location);
-
- // When compiling the runtime, closures do not escape. When escape
- // analysis becomes the default, and applies to closures, this
- // should be changed to make it an error if a closure escapes.
- if (this->gogo_->compiling_runtime()
- && this->gogo_->package_name() == "runtime")
- {
- Temporary_statement* ctemp = Statement::make_temporary(st, cv, location);
- this->gogo_->add_statement(ctemp);
- Expression* ref = Expression::make_temporary_reference(ctemp, location);
- Expression* addr = Expression::make_unary(OPERATOR_AND, ref, location);
- addr->unary_expression()->set_does_not_escape();
- return addr;
- }
-
return Expression::make_heap_expression(cv, location);
}
// Slice of an array. The escape analysis models this with
// a child Node representing the address of the array.
bool escapes = false;
- if (!this->gogo_->compiling_runtime()
- || this->gogo_->package_name() != "runtime")
- {
- Node* n = Node::make_node(expr);
- if (n->child() == NULL
- || (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
- escapes = true;
- }
+ Node* n = Node::make_node(expr);
+ if (n->child() == NULL
+ || (n->child()->encoding() & ESCAPE_MASK) != Node::ESCAPE_NONE)
+ escapes = true;
aie->array()->address_taken(escapes);
}
return TRAVERSE_CONTINUE;
}
+// Check variables and closures do not escape when compiling runtime.
+
+class Check_escape : public Traverse
+{
+ public:
+ Check_escape(Gogo* gogo)
+ : Traverse(traverse_expressions | traverse_variables),
+ gogo_(gogo)
+ { }
+
+ int
+ expression(Expression**);
+
+ int
+ variable(Named_object*);
+
+ private:
+ Gogo* gogo_;
+};
+
+int
+Check_escape::variable(Named_object* no)
+{
+ if ((no->is_variable() && no->var_value()->is_in_heap())
+ || (no->is_result_variable()
+ && no->result_var_value()->is_in_heap()))
+ go_error_at(no->location(),
+ "%s escapes to heap, not allowed in runtime",
+ no->name().c_str());
+ return TRAVERSE_CONTINUE;
+}
+
+int
+Check_escape::expression(Expression** pexpr)
+{
+ Expression* expr = *pexpr;
+ Func_expression* fe = expr->func_expression();
+ if (fe != NULL && fe->closure() != NULL)
+ {
+ Node* n = Node::make_node(expr);
+ if (n->encoding() == Node::ESCAPE_HEAP)
+ go_error_at(expr->location(),
+ "heap-allocated closure, not allowed in runtime");
+ }
+ return TRAVERSE_CONTINUE;
+}
+
// Add write barriers to the IR. This are required by the concurrent
// garbage collector. A write barrier is needed for any write of a
// pointer into memory controlled by the garbage collector. Write
Mark_address_taken mat(this);
this->traverse(&mat);
+ if (this->compiling_runtime() && this->package_name() == "runtime")
+ {
+ Check_escape chk(this);
+ this->traverse(&chk);
+ }
+
Write_barriers wb(this);
this->traverse(&wb);
}