diff options
Diffstat (limited to 'gcc/go')
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/escape.cc | 79 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 41 | ||||
-rw-r--r-- | gcc/go/gofrontend/parse.cc | 15 | ||||
-rw-r--r-- | gcc/go/gofrontend/wb.cc | 65 |
5 files changed, 114 insertions, 88 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index fb7da45e..e7b959f 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -3ea7fc3b918210e7248dbc51d90af20639dc4167 +1072286ca9249bd6f75628aead325a66286bcf5b The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/escape.cc b/gcc/go/gofrontend/escape.cc index 01329e1..7bab3cb 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -873,13 +873,12 @@ escape_hash_match(std::string suffix, std::string name) 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 @@ -1473,6 +1472,35 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s) 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 @@ -1489,13 +1517,7 @@ Escape_analysis_assign::expression(Expression** pexpr) 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); @@ -2968,25 +2990,20 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src, 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); diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index d4a2071..99fb1b5 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -3700,7 +3700,7 @@ Expression::make_unsafe_cast(Type* type, Expression* expr, // 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; @@ -3714,13 +3714,6 @@ Unary_expression::check_operand_address_taken(Gogo* gogo) 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(); @@ -7028,26 +7021,14 @@ Bound_method_expression::do_flatten(Gogo* gogo, 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. @@ -9577,12 +9558,6 @@ Call_expression::lower_varargs(Gogo* gogo, Named_object* function, 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; diff --git a/gcc/go/gofrontend/parse.cc b/gcc/go/gofrontend/parse.cc index 98f3067..8162abf 100644 --- a/gcc/go/gofrontend/parse.cc +++ b/gcc/go/gofrontend/parse.cc @@ -3059,21 +3059,6 @@ Parse::create_closure(Named_object* function, Enclosing_vars* enclosing_vars, 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); } diff --git a/gcc/go/gofrontend/wb.cc b/gcc/go/gofrontend/wb.cc index 77a59ac..fb3ef4d 100644 --- a/gcc/go/gofrontend/wb.cc +++ b/gcc/go/gofrontend/wb.cc @@ -54,14 +54,10 @@ Mark_address_taken::expression(Expression** pexpr) // 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); } @@ -127,6 +123,53 @@ Mark_address_taken::expression(Expression** pexpr) 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 @@ -370,6 +413,12 @@ Gogo::add_write_barriers() 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); } |