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
merge done from the gofrontend repository.
......@@ -3623,6 +3623,8 @@ Unsafe_type_conversion_expression::do_get_backend(Translate_context* context)
|| et->map_type() != NULL
|| et->channel_type() != NULL
|| et->is_nil_type());
else if (t->function_type() != NULL)
go_assert(et->points_to() != NULL);
else
go_unreachable();
......@@ -6482,34 +6484,6 @@ Bound_method_expression::do_traverse(Traverse* 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
// 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,
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*
Bound_method_expression::do_get_backend(Translate_context* context)
Expression*
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->function_);
if (thunk->is_erroneous())
{
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
// lowering pass because at that point we don't know whether we need
// to create the thunk or not. If the expression is called, we
// don't need the thunk.
Location loc = this->location();
// Force the expression into a variable. This is only necessary if
// we are going to do nil checks below, but it's easy enough to
// always do it.
Expression* expr = this->expr_;
if (!expr->is_variable())
{
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
// dereference the pointer.
Named_object* fn = this->method_->named_object();
Function_type* fntype;
Function_type *fntype;
if (fn->is_function())
fntype = fn->func_value()->type();
else if (fn->is_function_declaration())
......@@ -6757,7 +6742,7 @@ Bound_method_expression::do_get_backend(Translate_context* context)
else
go_unreachable();
Expression* val = this->expr_;
Expression* val = expr;
if (fntype->receiver()->type()->points_to() == NULL
&& val->type()->points_to() != NULL)
val = Expression::make_unary(OPERATOR_MULT, val, loc);
......@@ -6781,17 +6766,28 @@ Bound_method_expression::do_get_backend(Translate_context* context)
vals->push_back(val);
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* expr = this->expr_;
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;
nil_check = bme_check_nil(this->method_->field_indexes(), loc, &ref);
expr = ref;
......@@ -6808,19 +6804,20 @@ Bound_method_expression::do_get_backend(Translate_context* context)
nil_check = Expression::make_binary(OPERATOR_OROR, nil_check, n, loc);
}
Bexpression* bme = ret->get_backend(context);
if (nil_check != NULL)
{
Gogo* gogo = context->gogo();
Bexpression* crash =
gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc)->get_backend(context);
Btype* btype = ret->type()->get_backend(gogo);
Bexpression* bcheck = nil_check->get_backend(context);
bme = gogo->backend()->conditional_expression(btype, bcheck, crash,
bme, loc);
}
return bme;
Expression* crash = gogo->runtime_error(RUNTIME_ERROR_NIL_DEREFERENCE,
loc);
// Fix the type of the conditional expression by pretending to
// evaluate to RET either way through the conditional.
crash = Expression::make_compound(crash, ret, loc);
ret = Expression::make_conditional(nil_check, crash, ret, loc);
}
// RET is a pointer to a struct, but we want a function type.
ret = Expression::make_unsafe_cast(this->type(), ret, loc);
return ret;
}
// Dump ast representation of a bound method expression.
......
......@@ -2888,7 +2888,7 @@ class Bound_method_expression : public Expression
do_traverse(Traverse*);
Expression*
do_lower(Gogo*, Named_object*, Statement_inserter*, int);
do_flatten(Gogo*, Named_object*, Statement_inserter*);
Type*
do_type();
......@@ -2907,7 +2907,8 @@ class Bound_method_expression : public Expression
}
Bexpression*
do_get_backend(Translate_context*);
do_get_backend(Translate_context*)
{ go_unreachable(); }
void
do_dump_expression(Ast_dump_context*) const;
......
......@@ -512,7 +512,6 @@ runtime_files = \
$(runtime_thread_files) \
runtime/yield.c \
$(rtems_task_variable_add_file) \
cpuprof.c \
go-iface.c \
lfstack.c \
malloc.c \
......
......@@ -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 \
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 \
$(am__objects_4) cpuprof.lo go-iface.lo lfstack.lo malloc.lo \
mprof.lo netpoll.lo rdebug.lo reflect.lo runtime1.lo \
sigqueue.lo time.lo $(am__objects_5)
$(am__objects_4) go-iface.lo lfstack.lo malloc.lo mprof.lo \
netpoll.lo rdebug.lo reflect.lo runtime1.lo sigqueue.lo \
time.lo $(am__objects_5)
am_libgo_llgo_la_OBJECTS = $(am__objects_6)
libgo_llgo_la_OBJECTS = $(am_libgo_llgo_la_OBJECTS)
libgo_llgo_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
......@@ -911,7 +911,6 @@ runtime_files = \
$(runtime_thread_files) \
runtime/yield.c \
$(rtems_task_variable_add_file) \
cpuprof.c \
go-iface.c \
lfstack.c \
malloc.c \
......@@ -1547,7 +1546,6 @@ mostlyclean-compile:
distclean-compile:
-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)/getncpu-bsd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getncpu-irix.Plo@am__quote@
......
......@@ -415,3 +415,16 @@ func startTheWorld() {
func getMstats() *mstats {
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,
#ifdef SIGPROF
if (sig == SIGPROF)
{
/* FIXME: Handle m == NULL by calling something like gc's
sigprofNonGo. */
if (m != NULL && gp != m->g0 && gp != m->gsignal)
runtime_sigprof ();
return;
......
......@@ -184,7 +184,8 @@ enum
// SysFault marks a (already SysAlloc'd) region to fault
// 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_SysUnused(void *v, uintptr nbytes);
void runtime_SysUsed(void *v, uintptr nbytes);
......
......@@ -2686,11 +2686,8 @@ runtime_mcount(void)
}
static struct {
Lock;
void (*fn)(uintptr*, int32);
uint32 lock;
int32 hz;
uintptr pcbuf[TracebackMaxFrames];
Location locbuf[TracebackMaxFrames];
} prof;
static void System(void) {}
......@@ -2703,8 +2700,11 @@ runtime_sigprof()
M *mp = g->m;
int32 n, i;
bool traceback;
uintptr pcbuf[TracebackMaxFrames];
Location locbuf[TracebackMaxFrames];
Slice stk;
if(prof.fn == nil || prof.hz == 0)
if(prof.hz == 0)
return;
if(mp == nil)
......@@ -2718,12 +2718,6 @@ runtime_sigprof()
if(mp->mcache == nil)
traceback = false;
runtime_lock(&prof);
if(prof.fn == nil) {
runtime_unlock(&prof);
mp->mallocing--;
return;
}
n = 0;
if(runtime_atomicload(&runtime_in_callers) > 0) {
......@@ -2735,34 +2729,44 @@ runtime_sigprof()
}
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++)
prof.pcbuf[i] = prof.locbuf[i].pc;
pcbuf[i] = locbuf[i].pc;
}
if(!traceback || n <= 0) {
n = 2;
prof.pcbuf[0] = (uintptr)runtime_getcallerpc(&n);
pcbuf[0] = (uintptr)runtime_getcallerpc(&n);
if(mp->gcing || mp->helpgc)
prof.pcbuf[1] = (uintptr)GC;
pcbuf[1] = (uintptr)GC;
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--;
}
// Arrange to call fn with a traceback hz times a second.
void
runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
runtime_setcpuprofilerate_m(int32 hz)
{
// Force sane arguments.
if(hz < 0)
hz = 0;
if(hz == 0)
fn = nil;
if(fn == nil)
hz = 0;
// Disable preemption, otherwise we can be rescheduled to another thread
// that has profiling enabled.
......@@ -2773,10 +2777,12 @@ runtime_setcpuprofilerate(void (*fn)(uintptr*, int32), int32 hz)
// it would deadlock.
runtime_resetcpuprofiler(0);
runtime_lock(&prof);
prof.fn = fn;
while (!runtime_cas(&prof.lock, 0, 1)) {
runtime_osyield();
}
prof.hz = hz;
runtime_unlock(&prof);
runtime_atomicstore(&prof.lock, 0);
runtime_lock(&runtime_sched);
runtime_sched.profilehz = hz;
runtime_unlock(&runtime_sched);
......
......@@ -417,7 +417,10 @@ void runtime_freezetheworld(void);
void runtime_unwindstack(G*, byte*);
void runtime_sigprof();
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)
__asm__ (GOSYM_PREFIX "runtime.usleep");
int64 runtime_cputicks(void)
......
......@@ -55,10 +55,6 @@ func getgoroot() (out String) {
out = runtime_getenv("GOROOT");
}
func runtime_pprof.runtime_cyclesPerSecond() (res int64) {
res = runtime_tickspersecond();
}
func sync.runtime_procPin() (p int) {
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