escape: Add basic debugging.
Emit basic debug information when compiling with the flag -fgo-debug-escape#. Reviewed-on: https://go-review.googlesource.com/22376 2016-08-02 Chris Manghane <cmang@google.com> * lang.opt: Add -fgo-debug-escape option. * go-c.h (go_create_gogo): Add debug_escape_level parameter. * go-lang.c (go_langhook_init): Pass go_debug_escape_level to go_create_gogo. From-SVN: r239002
This commit is contained in:
parent
00803109af
commit
7295570dd4
10 changed files with 577 additions and 7 deletions
|
@ -1,3 +1,10 @@
|
||||||
|
2016-08-02 Chris Manghane <cmang@google.com>
|
||||||
|
|
||||||
|
* lang.opt: Add -fgo-debug-escape option.
|
||||||
|
* go-c.h (go_create_gogo): Add debug_escape_level parameter.
|
||||||
|
* go-lang.c (go_langhook_init): Pass go_debug_escape_level to
|
||||||
|
go_create_gogo.
|
||||||
|
|
||||||
2016-05-06 Chris Manghane <cmang@google.com>
|
2016-05-06 Chris Manghane <cmang@google.com>
|
||||||
|
|
||||||
* Make-lang.in (GO_OBJS): Add go/escape.o (based on an entirely
|
* Make-lang.in (GO_OBJS): Add go/escape.o (based on an entirely
|
||||||
|
|
|
@ -34,7 +34,8 @@ extern void go_add_search_path (const char*);
|
||||||
extern void go_create_gogo (int int_type_size, int pointer_size,
|
extern void go_create_gogo (int int_type_size, int pointer_size,
|
||||||
const char* pkgpath, const char *prefix,
|
const char* pkgpath, const char *prefix,
|
||||||
const char *relative_import_path,
|
const char *relative_import_path,
|
||||||
bool check_divide_zero, bool check_divide_overflow);
|
bool check_divide_zero, bool check_divide_overflow,
|
||||||
|
int debug_escape_level);
|
||||||
|
|
||||||
extern void go_parse_input_files (const char**, unsigned int,
|
extern void go_parse_input_files (const char**, unsigned int,
|
||||||
bool only_check_syntax,
|
bool only_check_syntax,
|
||||||
|
|
|
@ -101,7 +101,7 @@ go_langhook_init (void)
|
||||||
go_type_for_size). */
|
go_type_for_size). */
|
||||||
go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix,
|
go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix,
|
||||||
go_relative_import_path, go_check_divide_zero,
|
go_relative_import_path, go_check_divide_zero,
|
||||||
go_check_divide_overflow);
|
go_check_divide_overflow, go_debug_escape_level);
|
||||||
|
|
||||||
build_common_builtin_nodes ();
|
build_common_builtin_nodes ();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
d4b47fef149fc905ae6b418934f6be8cf6be433e
|
89a0b3a04f80df388242166b8835f12e82ceb194
|
||||||
|
|
||||||
The first line of this file holds the git revision number of the last
|
The first line of this file holds the git revision number of the last
|
||||||
merge done from the gofrontend repository.
|
merge done from the gofrontend repository.
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "gogo.h"
|
#include "gogo.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "expressions.h"
|
#include "expressions.h"
|
||||||
#include "statements.h"
|
#include "statements.h"
|
||||||
#include "escape.h"
|
#include "escape.h"
|
||||||
|
#include "ast-dump.h"
|
||||||
|
|
||||||
// class Node.
|
// class Node.
|
||||||
|
|
||||||
|
@ -47,6 +49,305 @@ Node::location() const
|
||||||
return Linemap::unknown_location();
|
return Linemap::unknown_location();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To match the cmd/gc debug output, strip away the packed prefixes on functions
|
||||||
|
// and variable/expressions.
|
||||||
|
|
||||||
|
std::string
|
||||||
|
strip_packed_prefix(Gogo* gogo, const std::string& s)
|
||||||
|
{
|
||||||
|
std::string packed_prefix = "." + gogo->pkgpath() + ".";
|
||||||
|
std::string fmt = s;
|
||||||
|
for (size_t pos = fmt.find(packed_prefix);
|
||||||
|
pos != std::string::npos;
|
||||||
|
pos = fmt.find(packed_prefix))
|
||||||
|
fmt.erase(pos, packed_prefix.length());
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper for debugging; return this node's AST formatted string.
|
||||||
|
// This is an implementation of gc's Nconv with obj.FmtShort.
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Node::ast_format(Gogo* gogo) const
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
if (this->is_sink())
|
||||||
|
ss << ".sink";
|
||||||
|
else if (this->object() != NULL)
|
||||||
|
{
|
||||||
|
Named_object* no = this->object();
|
||||||
|
if (no->is_function() && no->func_value()->enclosing() != NULL)
|
||||||
|
return "func literal";
|
||||||
|
ss << no->name();
|
||||||
|
}
|
||||||
|
else if (this->expr() != NULL)
|
||||||
|
{
|
||||||
|
Expression* e = this->expr();
|
||||||
|
bool is_call = e->call_expression() != NULL;
|
||||||
|
if (is_call)
|
||||||
|
e->call_expression()->fn();
|
||||||
|
Func_expression* fe = e->func_expression();;
|
||||||
|
|
||||||
|
bool is_closure = fe != NULL && fe->closure() != NULL;
|
||||||
|
if (is_closure)
|
||||||
|
{
|
||||||
|
if (is_call)
|
||||||
|
return "(func literal)()";
|
||||||
|
return "func literal";
|
||||||
|
}
|
||||||
|
Ast_dump_context::dump_to_stream(this->expr(), &ss);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Statement* s = this->statement();
|
||||||
|
Goto_unnamed_statement* unnamed = s->goto_unnamed_statement();
|
||||||
|
if (unnamed != NULL)
|
||||||
|
{
|
||||||
|
Statement* derived = unnamed->unnamed_label()->derived_from();
|
||||||
|
if (derived != NULL)
|
||||||
|
{
|
||||||
|
switch (derived->classification())
|
||||||
|
{
|
||||||
|
case Statement::STATEMENT_FOR:
|
||||||
|
case Statement::STATEMENT_FOR_RANGE:
|
||||||
|
return "for loop";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Statement::STATEMENT_SWITCH:
|
||||||
|
return "switch";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Statement::STATEMENT_TYPE_SWITCH:
|
||||||
|
return "type switch";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ast_dump_context::dump_to_stream(s, &ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strip_packed_prefix(gogo, ss.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper for debugging; return this node's detailed format string.
|
||||||
|
// This is an implementation of gc's Jconv with obj.FmtShort.
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Node::details() const
|
||||||
|
{
|
||||||
|
std::stringstream details;
|
||||||
|
|
||||||
|
if (!this->is_sink())
|
||||||
|
details << " l(" << LOCATION_LINE(this->location().gcc_location()) << ")";
|
||||||
|
|
||||||
|
bool is_varargs = false;
|
||||||
|
bool is_address_taken = false;
|
||||||
|
bool is_in_heap = false;
|
||||||
|
bool is_assigned = false;
|
||||||
|
std::string class_name;
|
||||||
|
|
||||||
|
Expression* e = this->expr();
|
||||||
|
Named_object* node_object = NULL;
|
||||||
|
if (this->object() != NULL)
|
||||||
|
node_object = this->object();
|
||||||
|
else if (e != NULL && e->var_expression() != NULL)
|
||||||
|
node_object = e->var_expression()->named_object();
|
||||||
|
|
||||||
|
if (node_object)
|
||||||
|
{
|
||||||
|
// TODO(cmang): For named variables and functions, we want to output
|
||||||
|
// the function depth.
|
||||||
|
if (node_object->is_variable())
|
||||||
|
{
|
||||||
|
Variable* var = node_object->var_value();
|
||||||
|
is_varargs = var->is_varargs_parameter();
|
||||||
|
is_address_taken = (var->is_address_taken()
|
||||||
|
|| var->is_non_escaping_address_taken());
|
||||||
|
is_in_heap = var->is_in_heap();
|
||||||
|
is_assigned = var->init() != NULL;
|
||||||
|
|
||||||
|
if (var->is_global())
|
||||||
|
class_name = "PEXTERN";
|
||||||
|
else if (var->is_parameter())
|
||||||
|
class_name = "PPARAM";
|
||||||
|
else if (var->is_closure())
|
||||||
|
class_name = "PPARAMREF";
|
||||||
|
else
|
||||||
|
class_name = "PAUTO";
|
||||||
|
}
|
||||||
|
else if (node_object->is_result_variable())
|
||||||
|
class_name = "PPARAMOUT";
|
||||||
|
else if (node_object->is_function()
|
||||||
|
|| node_object->is_function_declaration())
|
||||||
|
class_name = "PFUNC";
|
||||||
|
}
|
||||||
|
else if (e != NULL && e->enclosed_var_expression() != NULL)
|
||||||
|
{
|
||||||
|
Named_object* enclosed = e->enclosed_var_expression()->variable();
|
||||||
|
if (enclosed->is_variable())
|
||||||
|
{
|
||||||
|
Variable* var = enclosed->var_value();
|
||||||
|
is_address_taken = (var->is_address_taken()
|
||||||
|
|| var->is_non_escaping_address_taken());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Result_variable* var = enclosed->result_var_value();
|
||||||
|
is_address_taken = (var->is_address_taken()
|
||||||
|
|| var->is_non_escaping_address_taken());
|
||||||
|
}
|
||||||
|
class_name = "PPARAMREF";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!class_name.empty())
|
||||||
|
{
|
||||||
|
details << " class(" << class_name;
|
||||||
|
if (is_in_heap)
|
||||||
|
details << ",heap";
|
||||||
|
details << ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ((this->encoding() & ESCAPE_MASK))
|
||||||
|
{
|
||||||
|
case Node::ESCAPE_UNKNOWN:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Node::ESCAPE_HEAP:
|
||||||
|
details << " esc(h)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Node::ESCAPE_SCOPE:
|
||||||
|
details << " esc(s)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Node::ESCAPE_NONE:
|
||||||
|
details << " esc(no)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Node::ESCAPE_NEVER:
|
||||||
|
details << " esc(N)";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
details << " esc(" << this->encoding() << ")";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->state_ != NULL && this->state_->loop_depth != 0)
|
||||||
|
details << " ld(" << this->state_->loop_depth << ")";
|
||||||
|
|
||||||
|
if (is_varargs)
|
||||||
|
details << " isddd(1)";
|
||||||
|
if (is_address_taken)
|
||||||
|
details << " addrtaken";
|
||||||
|
if (is_assigned)
|
||||||
|
details << " assigned";
|
||||||
|
|
||||||
|
return details.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Node::op_format() const
|
||||||
|
{
|
||||||
|
std::stringstream op;
|
||||||
|
Ast_dump_context adc(&op, false);
|
||||||
|
if (this->expr() != NULL)
|
||||||
|
{
|
||||||
|
Expression* e = this->expr();
|
||||||
|
switch (e->classification())
|
||||||
|
{
|
||||||
|
case Expression::EXPRESSION_UNARY:
|
||||||
|
adc.dump_operator(e->unary_expression()->op());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Expression::EXPRESSION_BINARY:
|
||||||
|
adc.dump_operator(e->binary_expression()->op());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Expression::EXPRESSION_CALL:
|
||||||
|
op << "function call";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Expression::EXPRESSION_FUNC_REFERENCE:
|
||||||
|
if (e->func_expression()->is_runtime_function())
|
||||||
|
{
|
||||||
|
switch (e->func_expression()->runtime_code())
|
||||||
|
{
|
||||||
|
case Runtime::PANIC:
|
||||||
|
op << "panic";
|
||||||
|
|
||||||
|
case Runtime::APPEND:
|
||||||
|
op << "append";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Runtime::COPY:
|
||||||
|
op << "copy";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Runtime::MAKECHAN:
|
||||||
|
case Runtime::MAKECHANBIG:
|
||||||
|
case Runtime::MAKEMAP:
|
||||||
|
case Runtime::MAKEMAPBIG:
|
||||||
|
case Runtime::MAKESLICE1:
|
||||||
|
case Runtime::MAKESLICE2:
|
||||||
|
case Runtime::MAKESLICE1BIG:
|
||||||
|
case Runtime::MAKESLICE2BIG:
|
||||||
|
op << "make";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Runtime::DEFER:
|
||||||
|
op << "defer";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Runtime::RECOVER:
|
||||||
|
op << "recover";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Runtime::CLOSE:
|
||||||
|
op << "close";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Expression::EXPRESSION_ALLOCATION:
|
||||||
|
op << "new";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Expression::EXPRESSION_RECEIVE:
|
||||||
|
op << "<-";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this->statement() != NULL)
|
||||||
|
{
|
||||||
|
switch (this->statement()->classification())
|
||||||
|
{
|
||||||
|
case Statement::STATEMENT_DEFER:
|
||||||
|
op << "defer";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Statement::STATEMENT_RETURN:
|
||||||
|
op << "return";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op.str();
|
||||||
|
}
|
||||||
|
|
||||||
// Return this node's state, creating it if has not been initialized.
|
// Return this node's state, creating it if has not been initialized.
|
||||||
|
|
||||||
Node::Escape_state*
|
Node::Escape_state*
|
||||||
|
@ -255,6 +556,41 @@ Escape_context::Escape_context(Gogo* gogo, bool recursive)
|
||||||
state->loop_depth = -1;
|
state->loop_depth = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
debug_function_name(Named_object* fn)
|
||||||
|
{
|
||||||
|
if (fn == NULL)
|
||||||
|
return "<S>";
|
||||||
|
|
||||||
|
if (!fn->is_function()
|
||||||
|
|| fn->func_value()->enclosing() == NULL)
|
||||||
|
return Gogo::unpack_hidden_name(fn->name());
|
||||||
|
|
||||||
|
// Closures are named ".$nested#" where # starts from 0 to distinguish
|
||||||
|
// between closures. The cmd/gc closures are named in the format
|
||||||
|
// "enclosing.func#" where # starts from 1. If this is a closure, format
|
||||||
|
// its name to match cmd/gc.
|
||||||
|
Named_object* enclosing = fn->func_value()->enclosing();
|
||||||
|
|
||||||
|
// Extract #.
|
||||||
|
std::string name = Gogo::unpack_hidden_name(fn->name());
|
||||||
|
int closure_num = (int)strtol(name.substr(6).c_str(), NULL, 0);
|
||||||
|
closure_num++;
|
||||||
|
|
||||||
|
name = Gogo::unpack_hidden_name(enclosing->name());
|
||||||
|
char buf[200];
|
||||||
|
snprintf(buf, sizeof buf, "%s.func%d", name.c_str(), closure_num);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the name of the current function.
|
||||||
|
|
||||||
|
std::string
|
||||||
|
Escape_context::current_function_name() const
|
||||||
|
{
|
||||||
|
return debug_function_name(this->current_function_);
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the dummy return values for this Node N using the results
|
// Initialize the dummy return values for this Node N using the results
|
||||||
// in FNTYPE.
|
// in FNTYPE.
|
||||||
|
|
||||||
|
@ -381,6 +717,21 @@ Gogo::analyze_escape()
|
||||||
++fn)
|
++fn)
|
||||||
this->tag_function(context, *fn);
|
this->tag_function(context, *fn);
|
||||||
|
|
||||||
|
if (this->debug_escape_level() != 0)
|
||||||
|
{
|
||||||
|
std::vector<Node*> noesc = context->non_escaping_nodes();
|
||||||
|
for (std::vector<Node*>::const_iterator n = noesc.begin();
|
||||||
|
n != noesc.end();
|
||||||
|
++n)
|
||||||
|
{
|
||||||
|
Node::Escape_state* state = (*n)->state(context, NULL);
|
||||||
|
if (((*n)->encoding() & ESCAPE_MASK) == int(Node::ESCAPE_NONE))
|
||||||
|
inform((*n)->location(), "%s %s does not escape",
|
||||||
|
strip_packed_prefix(this, debug_function_name(state->fn)).c_str(),
|
||||||
|
(*n)->ast_format(this).c_str());
|
||||||
|
}
|
||||||
|
// TODO(cmang): Which objects in context->noesc actually don't escape.
|
||||||
|
}
|
||||||
delete context;
|
delete context;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -668,6 +1019,20 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s)
|
||||||
if (is_for_statement)
|
if (is_for_statement)
|
||||||
this->context_->decrease_loop_depth();
|
this->context_->decrease_loop_depth();
|
||||||
|
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
int debug_level = gogo->debug_escape_level();
|
||||||
|
if (debug_level > 1
|
||||||
|
&& s->unnamed_label_statement() == NULL
|
||||||
|
&& s->expression_statement() == NULL
|
||||||
|
&& !s->is_block_statement())
|
||||||
|
{
|
||||||
|
Node* n = Node::make_node(s);
|
||||||
|
std::string fn_name = this->context_->current_function_name();
|
||||||
|
inform(s->location(), "[%d] %s esc: %s",
|
||||||
|
this->context_->loop_depth(), fn_name.c_str(),
|
||||||
|
n->ast_format(gogo).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
switch (s->classification())
|
switch (s->classification())
|
||||||
{
|
{
|
||||||
case Statement::STATEMENT_VARIABLE_DECLARATION:
|
case Statement::STATEMENT_VARIABLE_DECLARATION:
|
||||||
|
@ -689,8 +1054,19 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s)
|
||||||
|
|
||||||
case Statement::STATEMENT_LABEL:
|
case Statement::STATEMENT_LABEL:
|
||||||
{
|
{
|
||||||
if (s->label_statement()->label()->looping())
|
Label_statement* label_stmt = s->label_statement();
|
||||||
|
if (label_stmt->label()->looping())
|
||||||
this->context_->increase_loop_depth();
|
this->context_->increase_loop_depth();
|
||||||
|
|
||||||
|
if (debug_level > 1)
|
||||||
|
{
|
||||||
|
std::string label_type = (label_stmt->label()->looping()
|
||||||
|
? "looping"
|
||||||
|
: "nonlooping");
|
||||||
|
inform(s->location(), "%s %s label",
|
||||||
|
label_stmt->label()->name().c_str(),
|
||||||
|
label_type.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -767,11 +1143,16 @@ Escape_analysis_assign::statement(Block*, size_t*, Statement* s)
|
||||||
int
|
int
|
||||||
Escape_analysis_assign::expression(Expression** pexpr)
|
Escape_analysis_assign::expression(Expression** pexpr)
|
||||||
{
|
{
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
int debug_level = gogo->debug_escape_level();
|
||||||
|
|
||||||
// Big stuff escapes unconditionally.
|
// Big stuff escapes unconditionally.
|
||||||
Node* n = Node::make_node(*pexpr);
|
Node* n = Node::make_node(*pexpr);
|
||||||
if ((n->encoding() & ESCAPE_MASK) != int(Node::ESCAPE_HEAP)
|
if ((n->encoding() & ESCAPE_MASK) != int(Node::ESCAPE_HEAP)
|
||||||
&& n->is_big(this->context_))
|
&& n->is_big(this->context_))
|
||||||
{
|
{
|
||||||
|
if (debug_level > 1)
|
||||||
|
inform((*pexpr)->location(), "too large for stack");
|
||||||
n->set_encoding(Node::ESCAPE_HEAP);
|
n->set_encoding(Node::ESCAPE_HEAP);
|
||||||
(*pexpr)->address_taken(true);
|
(*pexpr)->address_taken(true);
|
||||||
this->assign(this->context_->sink(), n);
|
this->assign(this->context_->sink(), n);
|
||||||
|
@ -780,6 +1161,15 @@ Escape_analysis_assign::expression(Expression** pexpr)
|
||||||
if ((*pexpr)->func_expression() == NULL)
|
if ((*pexpr)->func_expression() == NULL)
|
||||||
(*pexpr)->traverse_subexpressions(this);
|
(*pexpr)->traverse_subexpressions(this);
|
||||||
|
|
||||||
|
if (debug_level > 1)
|
||||||
|
{
|
||||||
|
Node* n = Node::make_node(*pexpr);
|
||||||
|
std::string fn_name = this->context_->current_function_name();
|
||||||
|
inform((*pexpr)->location(), "[%d] %s esc: %s",
|
||||||
|
this->context_->loop_depth(), fn_name.c_str(),
|
||||||
|
n->ast_format(gogo).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
switch ((*pexpr)->classification())
|
switch ((*pexpr)->classification())
|
||||||
{
|
{
|
||||||
case Expression::EXPRESSION_CALL:
|
case Expression::EXPRESSION_CALL:
|
||||||
|
@ -808,6 +1198,10 @@ Escape_analysis_assign::expression(Expression** pexpr)
|
||||||
Node* appended = Node::make_node(call->args()->back());
|
Node* appended = Node::make_node(call->args()->back());
|
||||||
this->assign_deref(this->context_->sink(), appended);
|
this->assign_deref(this->context_->sink(), appended);
|
||||||
|
|
||||||
|
if (debug_level > 2)
|
||||||
|
error_at((*pexpr)->location(),
|
||||||
|
"special treatment of append(slice1, slice2...)");
|
||||||
|
|
||||||
// The content of the original slice leaks as well.
|
// The content of the original slice leaks as well.
|
||||||
Node* appendee = Node::make_node(call->args()->back());
|
Node* appendee = Node::make_node(call->args()->back());
|
||||||
this->assign_deref(this->context_->sink(), appendee);
|
this->assign_deref(this->context_->sink(), appendee);
|
||||||
|
@ -1060,6 +1454,9 @@ Escape_analysis_assign::expression(Expression** pexpr)
|
||||||
void
|
void
|
||||||
Escape_analysis_assign::call(Call_expression* call)
|
Escape_analysis_assign::call(Call_expression* call)
|
||||||
{
|
{
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
int debug_level = gogo->debug_escape_level();
|
||||||
|
|
||||||
Func_expression* fn = call->fn()->func_expression();
|
Func_expression* fn = call->fn()->func_expression();
|
||||||
Function_type* fntype = call->get_function_type();
|
Function_type* fntype = call->get_function_type();
|
||||||
bool indirect = false;
|
bool indirect = false;
|
||||||
|
@ -1099,6 +1496,10 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
p != arg_nodes.end();
|
p != arg_nodes.end();
|
||||||
++p)
|
++p)
|
||||||
{
|
{
|
||||||
|
if (debug_level > 2)
|
||||||
|
inform(call->location(),
|
||||||
|
"esccall:: indirect call <- %s, untracked",
|
||||||
|
(*p)->ast_format(gogo).c_str());
|
||||||
this->assign(this->context_->sink(), *p);
|
this->assign(this->context_->sink(), *p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1111,6 +1512,10 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
&& fn->named_object()->is_function()
|
&& fn->named_object()->is_function()
|
||||||
&& !fntype->is_tagged())
|
&& !fntype->is_tagged())
|
||||||
{
|
{
|
||||||
|
if (debug_level > 2)
|
||||||
|
inform(call->location(), "esccall:: %s in recursive group",
|
||||||
|
call_node->ast_format(gogo).c_str());
|
||||||
|
|
||||||
Function* f = fn->named_object()->func_value();
|
Function* f = fn->named_object()->func_value();
|
||||||
const Bindings* callee_bindings = f->block()->bindings();
|
const Bindings* callee_bindings = f->block()->bindings();
|
||||||
|
|
||||||
|
@ -1176,6 +1581,9 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
|
|
||||||
for (; p != arg_nodes.end(); ++p)
|
for (; p != arg_nodes.end(); ++p)
|
||||||
{
|
{
|
||||||
|
if (debug_level > 2)
|
||||||
|
inform(call->location(), "esccall:: ... <- %s, untracked",
|
||||||
|
(*p)->ast_format(gogo).c_str());
|
||||||
this->assign(this->context_->sink(), *p);
|
this->assign(this->context_->sink(), *p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1183,7 +1591,13 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (debug_level > 2)
|
||||||
|
inform(call->location(), "esccall:: %s not recursive",
|
||||||
|
call_node->ast_format(gogo).c_str());
|
||||||
|
|
||||||
Node::Escape_state* call_state = call_node->state(this->context_, NULL);
|
Node::Escape_state* call_state = call_node->state(this->context_, NULL);
|
||||||
|
if (!call_state->retvals.empty())
|
||||||
|
error("esc already decorated call %s", call_node->ast_format(gogo).c_str());
|
||||||
this->context_->init_retvals(call_node, fntype);
|
this->context_->init_retvals(call_node, fntype);
|
||||||
|
|
||||||
// Receiver.
|
// Receiver.
|
||||||
|
@ -1251,6 +1665,9 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
|
|
||||||
for (; p != arg_nodes.end(); ++p)
|
for (; p != arg_nodes.end(); ++p)
|
||||||
{
|
{
|
||||||
|
if (debug_level > 2)
|
||||||
|
inform(call->location(), "esccall:: ... <- %s, untracked",
|
||||||
|
(*p)->ast_format(gogo).c_str());
|
||||||
this->assign(this->context_->sink(), *p);
|
this->assign(this->context_->sink(), *p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1265,6 +1682,17 @@ Escape_analysis_assign::call(Call_expression* call)
|
||||||
void
|
void
|
||||||
Escape_analysis_assign::assign(Node* dst, Node* src)
|
Escape_analysis_assign::assign(Node* dst, Node* src)
|
||||||
{
|
{
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
int debug_level = gogo->debug_escape_level();
|
||||||
|
if (debug_level > 1)
|
||||||
|
inform(dst->location(), "[%d] %s escassign: %s(%s)[%s] = %s(%s)[%s]",
|
||||||
|
this->context_->loop_depth(),
|
||||||
|
strip_packed_prefix(gogo, this->context_->current_function_name()).c_str(),
|
||||||
|
dst->ast_format(gogo).c_str(), dst->details().c_str(),
|
||||||
|
dst->op_format().c_str(),
|
||||||
|
src->ast_format(gogo).c_str(), src->details().c_str(),
|
||||||
|
src->op_format().c_str());
|
||||||
|
|
||||||
if (dst->expr() != NULL)
|
if (dst->expr() != NULL)
|
||||||
{
|
{
|
||||||
// Analyze the lhs of the assignment.
|
// Analyze the lhs of the assignment.
|
||||||
|
@ -1684,6 +2112,10 @@ Escape_analysis_assign::assign_from_note(std::string* note,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this->context_->gogo()->debug_escape_level() > 2)
|
||||||
|
inform(src->location(), "assignfromtag:: src= em=%s",
|
||||||
|
Escape_note::make_tag(enc).c_str());
|
||||||
|
|
||||||
if (enc == Node::ESCAPE_UNKNOWN)
|
if (enc == Node::ESCAPE_UNKNOWN)
|
||||||
{
|
{
|
||||||
// Lost track of the value.
|
// Lost track of the value.
|
||||||
|
@ -1750,6 +2182,11 @@ Escape_analysis_assign::flows(Node* dst, Node* src)
|
||||||
|| src_state->flows.find(dst) != src_state->flows.end())
|
|| src_state->flows.find(dst) != src_state->flows.end())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
if (gogo->debug_escape_level() > 2)
|
||||||
|
inform(Linemap::unknown_location(), "flows:: %s <- %s",
|
||||||
|
dst->ast_format(gogo).c_str(), src->ast_format(gogo).c_str());
|
||||||
|
|
||||||
if (dst_state->flows.empty())
|
if (dst_state->flows.empty())
|
||||||
this->context_->add_dst(dst);
|
this->context_->add_dst(dst);
|
||||||
|
|
||||||
|
@ -1905,6 +2342,22 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
src_state->level = level;
|
src_state->level = level;
|
||||||
int mod_loop_depth = std::max(extra_loop_depth, src_state->loop_depth);
|
int mod_loop_depth = std::max(extra_loop_depth, src_state->loop_depth);
|
||||||
|
|
||||||
|
Gogo* gogo = this->context_->gogo();
|
||||||
|
int debug_level = gogo->debug_escape_level();
|
||||||
|
if (debug_level > 1)
|
||||||
|
inform(Linemap::unknown_location(),
|
||||||
|
"escwalk: level:{%d %d} depth:%d "
|
||||||
|
"op=%s %s(%s) "
|
||||||
|
"scope:%s[%d] "
|
||||||
|
"extraloopdepth=%d",
|
||||||
|
level.value(), level.suffix_value(), this->context_->pdepth(),
|
||||||
|
src->op_format().c_str(),
|
||||||
|
src->ast_format(gogo).c_str(),
|
||||||
|
src->details().c_str(),
|
||||||
|
debug_function_name(src_state->fn).c_str(),
|
||||||
|
src_state->loop_depth,
|
||||||
|
extra_loop_depth);
|
||||||
|
|
||||||
this->context_->increase_pdepth();
|
this->context_->increase_pdepth();
|
||||||
|
|
||||||
// Input parameter flowing into output parameter?
|
// Input parameter flowing into output parameter?
|
||||||
|
@ -1934,6 +2387,20 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
// 2. return &in
|
// 2. return &in
|
||||||
// 3. tmp := in; return &tmp
|
// 3. tmp := in; return &tmp
|
||||||
// 4. return *in
|
// 4. return *in
|
||||||
|
if (debug_level != 0)
|
||||||
|
{
|
||||||
|
if (debug_level == 1)
|
||||||
|
inform(src->location(),
|
||||||
|
"leaking param: %s to result %s level=%d",
|
||||||
|
src->ast_format(gogo).c_str(), dst->ast_format(gogo).c_str(),
|
||||||
|
level.value());
|
||||||
|
else
|
||||||
|
inform(src->location(),
|
||||||
|
"leaking param: %s to result %s level={%d %d}",
|
||||||
|
src->ast_format(gogo).c_str(), dst->ast_format(gogo).c_str(),
|
||||||
|
level.value(), level.suffix_value());
|
||||||
|
}
|
||||||
|
|
||||||
if ((src->encoding() & ESCAPE_MASK) != Node::ESCAPE_RETURN)
|
if ((src->encoding() & ESCAPE_MASK) != Node::ESCAPE_RETURN)
|
||||||
{
|
{
|
||||||
int enc =
|
int enc =
|
||||||
|
@ -1968,6 +2435,9 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES),
|
Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES),
|
||||||
Node::ESCAPE_NONE);
|
Node::ESCAPE_NONE);
|
||||||
src->set_encoding(enc);
|
src->set_encoding(enc);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "mark escaped content: %s",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// A src object leaks if its value or address is assigned to a dst object
|
// A src object leaks if its value or address is assigned to a dst object
|
||||||
|
@ -1987,9 +2457,14 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES),
|
Node::max_encoding((src->encoding() | ESCAPE_CONTENT_ESCAPES),
|
||||||
Node::ESCAPE_NONE);
|
Node::ESCAPE_NONE);
|
||||||
src->set_encoding(enc);
|
src->set_encoding(enc);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "leaking param content: %s",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "leaking param");
|
||||||
src->set_encoding(Node::ESCAPE_SCOPE);
|
src->set_encoding(Node::ESCAPE_SCOPE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1998,6 +2473,10 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
Expression* e = src->expr();
|
Expression* e = src->expr();
|
||||||
if (e->enclosed_var_expression() != NULL)
|
if (e->enclosed_var_expression() != NULL)
|
||||||
{
|
{
|
||||||
|
if (src_leaks && debug_level != 0)
|
||||||
|
inform(src->location(), "leaking closure reference %s",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
|
|
||||||
Node* enclosed_node =
|
Node* enclosed_node =
|
||||||
Node::make_node(e->enclosed_var_expression()->variable());
|
Node::make_node(e->enclosed_var_expression()->variable());
|
||||||
this->flood(level, dst, enclosed_node, -1);
|
this->flood(level, dst, enclosed_node, -1);
|
||||||
|
@ -2020,6 +2499,23 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
if (src_leaks)
|
if (src_leaks)
|
||||||
{
|
{
|
||||||
src->set_encoding(Node::ESCAPE_HEAP);
|
src->set_encoding(Node::ESCAPE_HEAP);
|
||||||
|
if (debug_level != 0)
|
||||||
|
{
|
||||||
|
inform(underlying->location(), "moved to heap: %s",
|
||||||
|
underlying_node->ast_format(gogo).c_str());
|
||||||
|
|
||||||
|
if (debug_level > 1)
|
||||||
|
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
|
||||||
|
inform(src->location(), "%s escapes to heap",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
this->flood(level.decrease(), dst,
|
this->flood(level.decrease(), dst,
|
||||||
underlying_node, mod_loop_depth);
|
underlying_node, mod_loop_depth);
|
||||||
extra_loop_depth = mod_loop_depth;
|
extra_loop_depth = mod_loop_depth;
|
||||||
|
@ -2046,6 +2542,9 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
if (src_leaks)
|
if (src_leaks)
|
||||||
{
|
{
|
||||||
src->set_encoding(Node::ESCAPE_HEAP);
|
src->set_encoding(Node::ESCAPE_HEAP);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "%s escapes to heap",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
extra_loop_depth = mod_loop_depth;
|
extra_loop_depth = mod_loop_depth;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2089,6 +2588,9 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
if (src_leaks)
|
if (src_leaks)
|
||||||
{
|
{
|
||||||
src->set_encoding(Node::ESCAPE_HEAP);
|
src->set_encoding(Node::ESCAPE_HEAP);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "%s escapes to heap",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
extra_loop_depth = mod_loop_depth;
|
extra_loop_depth = mod_loop_depth;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2104,6 +2606,9 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
// A closure or bound method; we lost track of actual function
|
// A closure or bound method; we lost track of actual function
|
||||||
// so if this leaks, this call must be done on the heap.
|
// so if this leaks, this call must be done on the heap.
|
||||||
src->set_encoding(Node::ESCAPE_HEAP);
|
src->set_encoding(Node::ESCAPE_HEAP);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "%s escapes to heap",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2111,6 +2616,9 @@ Escape_analysis_flood::flood(Level level, Node* dst, Node* src,
|
||||||
{
|
{
|
||||||
// Calls to Runtime::NEW get lowered into an allocation expression.
|
// Calls to Runtime::NEW get lowered into an allocation expression.
|
||||||
src->set_encoding(Node::ESCAPE_HEAP);
|
src->set_encoding(Node::ESCAPE_HEAP);
|
||||||
|
if (debug_level != 0)
|
||||||
|
inform(src->location(), "%s escapes to heap",
|
||||||
|
src->ast_format(gogo).c_str());
|
||||||
}
|
}
|
||||||
else if ((e->field_reference_expression() != NULL
|
else if ((e->field_reference_expression() != NULL
|
||||||
&& e->field_reference_expression()->expr()->unary_expression() == NULL)
|
&& e->field_reference_expression()->expr()->unary_expression() == NULL)
|
||||||
|
@ -2184,6 +2692,13 @@ void
|
||||||
Gogo::propagate_escape(Escape_context* context, Node* dst)
|
Gogo::propagate_escape(Escape_context* context, Node* dst)
|
||||||
{
|
{
|
||||||
Node::Escape_state* state = dst->state(context, NULL);
|
Node::Escape_state* state = dst->state(context, NULL);
|
||||||
|
Gogo* gogo = context->gogo();
|
||||||
|
if (gogo->debug_escape_level() > 1)
|
||||||
|
inform(Linemap::unknown_location(), "escflood:%d: dst %s scope:%s[%d]",
|
||||||
|
context->flood_id(), dst->ast_format(gogo).c_str(),
|
||||||
|
debug_function_name(state->fn).c_str(),
|
||||||
|
state->loop_depth);
|
||||||
|
|
||||||
Escape_analysis_flood eaf(context);
|
Escape_analysis_flood eaf(context);
|
||||||
for (std::set<Node*>::const_iterator p = state->flows.begin();
|
for (std::set<Node*>::const_iterator p = state->flows.begin();
|
||||||
p != state->flows.end();
|
p != state->flows.end();
|
||||||
|
|
|
@ -193,6 +193,17 @@ class Node
|
||||||
Location
|
Location
|
||||||
location() const;
|
location() const;
|
||||||
|
|
||||||
|
// Return this node's AST formatted string.
|
||||||
|
std::string
|
||||||
|
ast_format(Gogo*) const;
|
||||||
|
|
||||||
|
// Return this node's detailed format string.
|
||||||
|
std::string
|
||||||
|
details() const;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
op_format() const;
|
||||||
|
|
||||||
// Return this node's escape state.
|
// Return this node's escape state.
|
||||||
Escape_state*
|
Escape_state*
|
||||||
state(Escape_context* context, Named_object* fn);
|
state(Escape_context* context, Named_object* fn);
|
||||||
|
@ -343,6 +354,10 @@ class Escape_context
|
||||||
set_current_function(Named_object* fn)
|
set_current_function(Named_object* fn)
|
||||||
{ this->current_function_ = fn; }
|
{ this->current_function_ = fn; }
|
||||||
|
|
||||||
|
// Return the name of the current function.
|
||||||
|
std::string
|
||||||
|
current_function_name() const;
|
||||||
|
|
||||||
// Return true if this is the context for a mutually recursive set of functions.
|
// Return true if this is the context for a mutually recursive set of functions.
|
||||||
bool
|
bool
|
||||||
recursive() const
|
recursive() const
|
||||||
|
|
|
@ -22,7 +22,8 @@ GO_EXTERN_C
|
||||||
void
|
void
|
||||||
go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath,
|
go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath,
|
||||||
const char *prefix, const char *relative_import_path,
|
const char *prefix, const char *relative_import_path,
|
||||||
bool check_divide_by_zero, bool check_divide_overflow)
|
bool check_divide_by_zero, bool check_divide_overflow,
|
||||||
|
int debug_escape_level)
|
||||||
{
|
{
|
||||||
go_assert(::gogo == NULL);
|
go_assert(::gogo == NULL);
|
||||||
Linemap* linemap = go_get_linemap();
|
Linemap* linemap = go_get_linemap();
|
||||||
|
@ -39,6 +40,7 @@ go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath,
|
||||||
::gogo->set_check_divide_by_zero(check_divide_by_zero);
|
::gogo->set_check_divide_by_zero(check_divide_by_zero);
|
||||||
if (check_divide_overflow)
|
if (check_divide_overflow)
|
||||||
::gogo->set_check_divide_overflow(check_divide_overflow);
|
::gogo->set_check_divide_overflow(check_divide_overflow);
|
||||||
|
::gogo->set_debug_escape_level(debug_escape_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the input files.
|
// Parse the input files.
|
||||||
|
|
|
@ -239,6 +239,16 @@ class Gogo
|
||||||
set_check_divide_overflow(bool b)
|
set_check_divide_overflow(bool b)
|
||||||
{ this->check_divide_overflow_ = b; }
|
{ this->check_divide_overflow_ = b; }
|
||||||
|
|
||||||
|
// Return the level of escape analysis debug information to emit.
|
||||||
|
int
|
||||||
|
debug_escape_level() const
|
||||||
|
{ return this->debug_escape_level_; }
|
||||||
|
|
||||||
|
// Set the level of escape analysis debugging from a command line option.
|
||||||
|
void
|
||||||
|
set_debug_escape_level(int level)
|
||||||
|
{ this->debug_escape_level_ = level; }
|
||||||
|
|
||||||
// Return the priority to use for the package we are compiling.
|
// Return the priority to use for the package we are compiling.
|
||||||
// This is two more than the largest priority of any package we
|
// This is two more than the largest priority of any package we
|
||||||
// import.
|
// import.
|
||||||
|
@ -786,6 +796,9 @@ class Gogo
|
||||||
// Whether or not to check for division overflow, from the
|
// Whether or not to check for division overflow, from the
|
||||||
// -fgo-check-divide-overflow option.
|
// -fgo-check-divide-overflow option.
|
||||||
bool check_divide_overflow_;
|
bool check_divide_overflow_;
|
||||||
|
// The level of escape analysis debug information to emit, from the
|
||||||
|
// -fgo-debug-escape option.
|
||||||
|
int debug_escape_level_;
|
||||||
// A list of types to verify.
|
// A list of types to verify.
|
||||||
std::vector<Type*> verify_types_;
|
std::vector<Type*> verify_types_;
|
||||||
// A list of interface types defined while parsing.
|
// A list of interface types defined while parsing.
|
||||||
|
@ -2715,7 +2728,7 @@ class Unnamed_label
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Unnamed_label(Location location)
|
Unnamed_label(Location location)
|
||||||
: location_(location), blabel_(NULL)
|
: location_(location), derived_from_(NULL), blabel_(NULL)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// Get the location where the label is defined.
|
// Get the location where the label is defined.
|
||||||
|
@ -2728,6 +2741,16 @@ class Unnamed_label
|
||||||
set_location(Location location)
|
set_location(Location location)
|
||||||
{ this->location_ = location; }
|
{ this->location_ = location; }
|
||||||
|
|
||||||
|
// Get the top level statement this unnamed label is derived from.
|
||||||
|
Statement*
|
||||||
|
derived_from() const
|
||||||
|
{ return this->derived_from_; }
|
||||||
|
|
||||||
|
// Set the top level statement this unnamed label is derived from.
|
||||||
|
void
|
||||||
|
set_derived_from(Statement* s)
|
||||||
|
{ this->derived_from_ = s; }
|
||||||
|
|
||||||
// Return a statement which defines this label.
|
// Return a statement which defines this label.
|
||||||
Bstatement*
|
Bstatement*
|
||||||
get_definition(Translate_context*);
|
get_definition(Translate_context*);
|
||||||
|
@ -2743,6 +2766,9 @@ class Unnamed_label
|
||||||
|
|
||||||
// The location where the label is defined.
|
// The location where the label is defined.
|
||||||
Location location_;
|
Location location_;
|
||||||
|
// The top-level statement this unnamed label was derived/lowered from.
|
||||||
|
// This is NULL is this label is not the top-level of a lowered statement.
|
||||||
|
Statement* derived_from_;
|
||||||
// The backend representation of this label.
|
// The backend representation of this label.
|
||||||
Blabel* blabel_;
|
Blabel* blabel_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -3058,7 +3058,6 @@ Unnamed_label_statement::do_get_backend(Translate_context* context)
|
||||||
return this->label_->get_definition(context);
|
return this->label_->get_definition(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Dump the AST representation for an unnamed label definition statement.
|
// Dump the AST representation for an unnamed label definition statement.
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -5091,6 +5090,7 @@ For_statement::do_lower(Gogo*, Named_object*, Block* enclosing,
|
||||||
}
|
}
|
||||||
|
|
||||||
Unnamed_label* top = new Unnamed_label(this->location());
|
Unnamed_label* top = new Unnamed_label(this->location());
|
||||||
|
top->set_derived_from(this);
|
||||||
b->add_statement(Statement::make_unnamed_label_statement(top));
|
b->add_statement(Statement::make_unnamed_label_statement(top));
|
||||||
|
|
||||||
s = Statement::make_block_statement(this->statements_,
|
s = Statement::make_block_statement(this->statements_,
|
||||||
|
|
|
@ -69,6 +69,10 @@ frequire-return-statement
|
||||||
Go Var(go_require_return_statement) Init(1) Warning
|
Go Var(go_require_return_statement) Init(1) Warning
|
||||||
Functions which return values must end with return statements.
|
Functions which return values must end with return statements.
|
||||||
|
|
||||||
|
fgo-debug-escape
|
||||||
|
Go Joined UInteger Var(go_debug_escape_level) Init(0)
|
||||||
|
Emit debugging information related to the escape analysis pass when run with -fgo-optimize-allocs.
|
||||||
|
|
||||||
o
|
o
|
||||||
Go Joined Separate
|
Go Joined Separate
|
||||||
; Documented in common.opt
|
; Documented in common.opt
|
||||||
|
|
Loading…
Add table
Reference in a new issue