Commit 238fc344 by Ian Lance Taylor

runtime: copy cpuprof code from Go 1.7 runtime

    
    This replaces runtime/cpuprof.goc with go/runtime/cpuprof.go and adjusts
    the supporting code in runtime/proc.c.
    
    This adds another case where the compiler needs to avoid heap allocation
    in the runtime package: when evaluating a method expression into a
    closure.  Implementing this required moving the relevant code from
    do_get_backend to do_flatten, so that I could easily add a temporary
    variable.  Doing that let me get rid of Bound_method_expression::do_lower.
    
    Reviewed-on: https://go-review.googlesource.com/31050

From-SVN: r241163
parent 6d59425d
e3913d96fb024b916c87a4dc01f413523467ead9 5f043fc2bf0f92a84a1f7da57acd79a61c9d2592
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.
...@@ -3623,6 +3623,8 @@ Unsafe_type_conversion_expression::do_get_backend(Translate_context* context) ...@@ -3623,6 +3623,8 @@ Unsafe_type_conversion_expression::do_get_backend(Translate_context* context)
|| et->map_type() != NULL || et->map_type() != NULL
|| et->channel_type() != NULL || et->channel_type() != NULL
|| et->is_nil_type()); || et->is_nil_type());
else if (t->function_type() != NULL)
go_assert(et->points_to() != NULL);
else else
go_unreachable(); go_unreachable();
...@@ -6482,34 +6484,6 @@ Bound_method_expression::do_traverse(Traverse* traverse) ...@@ -6482,34 +6484,6 @@ Bound_method_expression::do_traverse(Traverse* traverse)
return Expression::traverse(&this->expr_, traverse); return Expression::traverse(&this->expr_, traverse);
} }
// Lower the expression. If this is a method value rather than being
// called, and the method is accessed via a pointer, we may need to
// add nil checks. Introduce a temporary variable so that those nil
// checks do not cause multiple evaluation.
Expression*
Bound_method_expression::do_lower(Gogo*, Named_object*,
Statement_inserter* inserter, int)
{
// For simplicity we use a temporary for every call to an embedded
// method, even though some of them might be pure value methods and
// not require a temporary.
if (this->expr_->var_expression() == NULL
&& this->expr_->temporary_reference_expression() == NULL
&& this->expr_->set_and_use_temporary_expression() == NULL
&& (this->method_->field_indexes() != NULL
|| (this->method_->is_value_method()
&& this->expr_->type()->points_to() != NULL)))
{
Temporary_statement* temp =
Statement::make_temporary(this->expr_->type(), NULL, this->location());
inserter->insert(temp);
this->expr_ = Expression::make_set_and_use_temporary(temp, this->expr_,
this->location());
}
return this;
}
// Return the type of a bound method expression. The type of this // Return the type of a bound method expression. The type of this
// object is simply the type of the method with no receiver. // object is simply the type of the method with no receiver.
...@@ -6724,32 +6698,43 @@ bme_check_nil(const Method::Field_indexes* field_indexes, Location loc, ...@@ -6724,32 +6698,43 @@ bme_check_nil(const Method::Field_indexes* field_indexes, Location loc,
return cond; return cond;
} }
// Get the backend representation for a method value. // Flatten a method value into a struct with nil checks. We can't do
// this in the lowering phase, because if the method value is called
// directly we don't need a thunk. That case will have been handled
// by Call_expression::do_lower, so if we get here then we do need a
// thunk.
Bexpression* Expression*
Bound_method_expression::do_get_backend(Translate_context* context) Bound_method_expression::do_flatten(Gogo* gogo, Named_object*,
Statement_inserter* inserter)
{ {
Named_object* thunk = Bound_method_expression::create_thunk(context->gogo(), Location loc = this->location();
Named_object* thunk = Bound_method_expression::create_thunk(gogo,
this->method_, this->method_,
this->function_); this->function_);
if (thunk->is_erroneous()) if (thunk->is_erroneous())
{ {
go_assert(saw_errors()); go_assert(saw_errors());
return context->backend()->error_expression(); return Expression::make_error(loc);
} }
// FIXME: We should lower this earlier, but we can't lower it in the // Force the expression into a variable. This is only necessary if
// lowering pass because at that point we don't know whether we need // we are going to do nil checks below, but it's easy enough to
// to create the thunk or not. If the expression is called, we // always do it.
// don't need the thunk. Expression* expr = this->expr_;
if (!expr->is_variable())
Location loc = this->location(); {
Temporary_statement* etemp = Statement::make_temporary(NULL, expr, loc);
inserter->insert(etemp);
expr = Expression::make_temporary_reference(etemp, loc);
}
// If the method expects a value, and we have a pointer, we need to // If the method expects a value, and we have a pointer, we need to
// dereference the pointer. // dereference the pointer.
Named_object* fn = this->method_->named_object(); Named_object* fn = this->method_->named_object();
Function_type* fntype; Function_type *fntype;
if (fn->is_function()) if (fn->is_function())
fntype = fn->func_value()->type(); fntype = fn->func_value()->type();
else if (fn->is_function_declaration()) else if (fn->is_function_declaration())
...@@ -6757,7 +6742,7 @@ Bound_method_expression::do_get_backend(Translate_context* context) ...@@ -6757,7 +6742,7 @@ Bound_method_expression::do_get_backend(Translate_context* context)
else else
go_unreachable(); go_unreachable();
Expression* val = this->expr_; Expression* val = expr;
if (fntype->receiver()->type()->points_to() == NULL if (fntype->receiver()->type()->points_to() == NULL
&& val->type()->points_to() != NULL) && val->type()->points_to() != NULL)
val = Expression::make_unary(OPERATOR_MULT, val, loc); val = Expression::make_unary(OPERATOR_MULT, val, loc);
...@@ -6781,17 +6766,28 @@ Bound_method_expression::do_get_backend(Translate_context* context) ...@@ -6781,17 +6766,28 @@ Bound_method_expression::do_get_backend(Translate_context* context)
vals->push_back(val); vals->push_back(val);
Expression* ret = Expression::make_struct_composite_literal(st, vals, loc); Expression* ret = Expression::make_struct_composite_literal(st, vals, loc);
ret = Expression::make_heap_expression(ret, loc);
// See whether the expression or any embedded pointers are nil. if (!gogo->compiling_runtime() || gogo->package_name() != "runtime")
ret = Expression::make_heap_expression(ret, loc);
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();
}
// If necessary, check whether the expression or any embedded
// pointers are nil.
Expression* nil_check = NULL; Expression* nil_check = NULL;
Expression* expr = this->expr_;
if (this->method_->field_indexes() != NULL) if (this->method_->field_indexes() != NULL)
{ {
// Note that we are evaluating this->expr_ twice, but that is OK
// because in the lowering pass we forced it into a temporary
// variable.
Expression* ref = expr; Expression* ref = expr;
nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref); nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref);
expr = ref; expr = ref;
...@@ -6808,19 +6804,20 @@ Bound_method_expression::do_get_backend(Translate_context* context) ...@@ -6808,19 +6804,20 @@ Bound_method_expression::do_get_backend(Translate_context* context)
nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc); nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc);
} }
Bexpression* bme = ret->get_backend(context);
if (nil_check != NULL) if (nil_check != NULL)
{ {
Gogo* gogo = context->gogo(); Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
Bexpression* crash = loc);
gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE, // Fix the type of the conditional expression by pretending to
loc)->get_backend(context); // evaluate to RET either way through the conditional.
Btype* btype = ret->type()->get_backend(gogo); crash = Expression::make_compound(crash, ret, loc);
Bexpression* bcheck = nil_check->get_backend(context); ret = Expression::make_conditional(nil_check, crash, ret, loc);
bme = gogo->backend()->conditional_expression(btype, bcheck, crash, }
bme, loc);
} // RET is a pointer to a struct, but we want a function type.
return bme; ret = Expression::make_unsafe_cast(this->type(), ret, loc);
return ret;
} }
// Dump ast representation of a bound method expression. // Dump ast representation of a bound method expression.
......
...@@ -2888,7 +2888,7 @@ class Bound_method_expression : public Expression ...@@ -2888,7 +2888,7 @@ class Bound_method_expression : public Expression
do_traverse(Traverse*); do_traverse(Traverse*);
Expression* Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int); do_flatten(Gogo*, Named_object*, Statement_inserter*);
Type* Type*
do_type(); do_type();
...@@ -2907,7 +2907,8 @@ class Bound_method_expression : public Expression ...@@ -2907,7 +2907,8 @@ class Bound_method_expression : public Expression
} }
Bexpression* Bexpression*
do_get_backend(Translate_context*); do_get_backend(Translate_context*)
{ go_unreachable(); }
void void
do_dump_expression(Ast_dump_context*) const; do_dump_expression(Ast_dump_context*) const;
......
...@@ -512,7 +512,6 @@ runtime_files = \ ...@@ -512,7 +512,6 @@ runtime_files = \
$(runtime_thread_files) \ $(runtime_thread_files) \
runtime/yield.c \ runtime/yield.c \
$(rtems_task_variable_add_file) \ $(rtems_task_variable_add_file) \
cpuprof.c \
go-iface.c \ go-iface.c \
lfstack.c \ lfstack.c \
malloc.c \ malloc.c \
......
...@@ -261,9 +261,9 @@ am__objects_6 = go-append.lo go-assert.lo go-assert-interface.lo \ ...@@ -261,9 +261,9 @@ am__objects_6 = go-append.lo go-assert.lo go-assert-interface.lo \
mcentral.lo $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo \ mcentral.lo $(am__objects_1) mfixalloc.lo mgc0.lo mheap.lo \
msize.lo $(am__objects_2) panic.lo parfor.lo print.lo proc.lo \ msize.lo $(am__objects_2) panic.lo parfor.lo print.lo proc.lo \
runtime.lo signal_unix.lo thread.lo $(am__objects_3) yield.lo \ runtime.lo signal_unix.lo thread.lo $(am__objects_3) yield.lo \
$(am__objects_4) cpuprof.lo go-iface.lo lfstack.lo malloc.lo \ $(am__objects_4) go-iface.lo lfstack.lo malloc.lo mprof.lo \
mprof.lo netpoll.lo rdebug.lo reflect.lo runtime1.lo \ netpoll.lo rdebug.lo reflect.lo runtime1.lo sigqueue.lo \
sigqueue.lo time.lo $(am__objects_5) time.lo $(am__objects_5)
am_libgo_llgo_la_OBJECTS = $(am__objects_6) am_libgo_llgo_la_OBJECTS = $(am__objects_6)
libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS) libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS)
libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
...@@ -911,7 +911,6 @@ runtime_files = \ ...@@ -911,7 +911,6 @@ runtime_files = \
$(runtime_thread_files) \ $(runtime_thread_files) \
runtime/yield.c \ runtime/yield.c \
$(rtems_task_variable_add_file) \ $(rtems_task_variable_add_file) \
cpuprof.c \
go-iface.c \ go-iface.c \
lfstack.c \ lfstack.c \
malloc.c \ malloc.c \
...@@ -1547,7 +1546,6 @@ mostlyclean-compile: ...@@ -1547,7 +1546,6 @@ mostlyclean-compile:
distclean-compile: distclean-compile:
-rm -f *.tab.c -rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cpuprof.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env_posix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env_posix.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-bsd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-bsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-irix.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-irix.Plo@am__quote@
......
...@@ -415,3 +415,16 @@ func startTheWorld() { ...@@ -415,3 +415,16 @@ func startTheWorld() {
func getMstats() *mstats { func getMstats() *mstats {
return &memstats return &memstats
} }
// Temporary for gccgo until we port proc.go.
func setcpuprofilerate_m(hz int32)
// Temporary for gccgo until we port mem_GOOS.go.
func sysAlloc(n uintptr, sysStat *uint64) unsafe.Pointer
// Temporary for gccgo until we port proc.go, so that the C signal
// handler can call into cpuprof.
//go:linkname cpuprofAdd runtime.cpuprofAdd
func cpuprofAdd(stk []uintptr) {
cpuprof.add(stk)
}
...@@ -156,6 +156,8 @@ runtime_sighandler (int sig, Siginfo *info, ...@@ -156,6 +156,8 @@ runtime_sighandler (int sig, Siginfo *info,
#ifdef SIGPROF #ifdef SIGPROF
if (sig == SIGPROF) if (sig == SIGPROF)
{ {
/* FIXME: Handle m == NULL by calling something like gc's
sigprofNonGo. */
if (m != NULL && gp != m->g0 && gp != m->gsignal) if (m != NULL && gp != m->g0 && gp != m->gsignal)
runtime_sigprof (); runtime_sigprof ();
return; return;
......
...@@ -184,7 +184,8 @@ enum ...@@ -184,7 +184,8 @@ enum
// SysFault marks a (already SysAlloc'd) region to fault // SysFault marks a (already SysAlloc'd) region to fault
// if accessed. Used only for debugging the runtime. // if accessed. Used only for debugging the runtime.
void* runtime_SysAlloc(uintptr nbytes, uint64 *stat); void* runtime_SysAlloc(uintptr nbytes, uint64 *stat)
__asm__ (GOSYM_PREFIX "runtime.sysAlloc");
void runtime_SysFree(void *v, uintptr nbytes, uint64 *stat); void runtime_SysFree(void *v, uintptr nbytes, uint64 *stat);
void runtime_SysUnused(void *v, uintptr nbytes); void runtime_SysUnused(void *v, uintptr nbytes);
void runtime_SysUsed(void *v, uintptr nbytes); void runtime_SysUsed(void *v, uintptr nbytes);
......
...@@ -2686,11 +2686,8 @@ runtime_mcount(void) ...@@ -2686,11 +2686,8 @@ runtime_mcount(void)
} }
static struct { static struct {
Lock; uint32 lock;
void (*fn)(uintptr*, int32);
int32 hz; int32 hz;
uintptr pcbuf[TracebackMaxFrames];
Location locbuf[TracebackMaxFrames];
} prof; } prof;
static void System(void) {} static void System(void) {}
...@@ -2703,8 +2700,11 @@ runtime_sigprof() ...@@ -2703,8 +2700,11 @@ runtime_sigprof()
M *mp = g->m; M *mp = g->m;
int32 n, i; int32 n, i;
bool traceback; bool traceback;
uintptr pcbuf[TracebackMaxFrames];
Location locbuf[TracebackMaxFrames];
Slice stk;
if(prof.fn == nil || prof.hz == 0) if(prof.hz == 0)
return; return;
if(mp == nil) if(mp == nil)
...@@ -2718,12 +2718,6 @@ runtime_sigprof() ...@@ -2718,12 +2718,6 @@ runtime_sigprof()
if(mp->mcache == nil) if(mp->mcache == nil)
traceback = false; traceback = false;
runtime_lock(&prof);
if(prof.fn == nil) {
runtime_unlock(&prof);
mp->mallocing--;
return;
}
n = 0; n = 0;
if(runtime_atomicload(&runtime_in_callers) > 0) { if(runtime_atomicload(&runtime_in_callers) > 0) {
...@@ -2735,34 +2729,44 @@ runtime_sigprof() ...@@ -2735,34 +2729,44 @@ runtime_sigprof()
} }
if(traceback) { if(traceback) {
n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf), false); n = runtime_callers(0, locbuf, nelem(locbuf), false);
for(i = 0; i < n; i++) for(i = 0; i < n; i++)
prof.pcbuf[i] = prof.locbuf[i].pc; pcbuf[i] = locbuf[i].pc;
} }
if(!traceback || n <= 0) { if(!traceback || n <= 0) {
n = 2; n = 2;
prof.pcbuf[0] = (uintptr)runtime_getcallerpc(&n); pcbuf[0] = (uintptr)runtime_getcallerpc(&n);
if(mp->gcing || mp->helpgc) if(mp->gcing || mp->helpgc)
prof.pcbuf[1] = (uintptr)GC; pcbuf[1] = (uintptr)GC;
else else
prof.pcbuf[1] = (uintptr)System; pcbuf[1] = (uintptr)System;
}
if (prof.hz != 0) {
stk.__values = &pcbuf[0];
stk.__count = n;
stk.__capacity = n;
// Simple cas-lock to coordinate with setcpuprofilerate.
while (!runtime_cas(&prof.lock, 0, 1)) {
runtime_osyield();
}
if (prof.hz != 0) {
runtime_cpuprofAdd(stk);
}
runtime_atomicstore(&prof.lock, 0);
} }
prof.fn(prof.pcbuf, n);
runtime_unlock(&prof);
mp->mallocing--; mp->mallocing--;
} }
// Arrange to call fn with a traceback hz times a second. // Arrange to call fn with a traceback hz times a second.
void void
runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) runtime_setcpuprofilerate_m(int32 hz)
{ {
// Force sane arguments. // Force sane arguments.
if(hz < 0) if(hz < 0)
hz = 0; hz = 0;
if(hz == 0)
fn = nil;
if(fn == nil)
hz = 0;
// Disable preemption, otherwise we can be rescheduled to another thread // Disable preemption, otherwise we can be rescheduled to another thread
// that has profiling enabled. // that has profiling enabled.
...@@ -2773,10 +2777,12 @@ runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz) ...@@ -2773,10 +2777,12 @@ runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
// it would deadlock. // it would deadlock.
runtime_resetcpuprofiler(0); runtime_resetcpuprofiler(0);
runtime_lock(&prof); while (!runtime_cas(&prof.lock, 0, 1)) {
prof.fn = fn; runtime_osyield();
}
prof.hz = hz; prof.hz = hz;
runtime_unlock(&prof); runtime_atomicstore(&prof.lock, 0);
runtime_lock(&runtime_sched); runtime_lock(&runtime_sched);
runtime_sched.profilehz = hz; runtime_sched.profilehz = hz;
runtime_unlock(&runtime_sched); runtime_unlock(&runtime_sched);
......
...@@ -417,7 +417,10 @@ void runtime_freezetheworld(void); ...@@ -417,7 +417,10 @@ void runtime_freezetheworld(void);
void runtime_unwindstack(G*, byte*); void runtime_unwindstack(G*, byte*);
void runtime_sigprof(); void runtime_sigprof();
void runtime_resetcpuprofiler(int32); void runtime_resetcpuprofiler(int32);
void runtime_setcpuprofilerate(void(*)(uintptr*, int32), int32); void runtime_setcpuprofilerate_m(int32)
__asm__ (GOSYM_PREFIX "runtime.setcpuprofilerate_m");
void runtime_cpuprofAdd(Slice)
__asm__ (GOSYM_PREFIX "runtime.cpuprofAdd");
void runtime_usleep(uint32) void runtime_usleep(uint32)
__asm__ (GOSYM_PREFIX "runtime.usleep"); __asm__ (GOSYM_PREFIX "runtime.usleep");
int64 runtime_cputicks(void) int64 runtime_cputicks(void)
......
...@@ -55,10 +55,6 @@ func getgoroot() (out String) { ...@@ -55,10 +55,6 @@ func getgoroot() (out String) {
out = runtime_getenv("GOROOT"); out = runtime_getenv("GOROOT");
} }
func runtime_pprof.runtime_cyclesPerSecond() (res int64) {
res = runtime_tickspersecond();
}
func sync.runtime_procPin() (p int) { func sync.runtime_procPin() (p int) {
M *mp; M *mp;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment