mirror of https://github.com/golang/go.git
runtime: get rid of copyable check - all G frames are copyable.
Just go ahead and do it, if something is wrong we'll throw. Also rip out cc-generated arg ptr maps, they are useless now. LGTM=rsc R=rsc CC=golang-codereviews https://golang.org/cl/133690045
This commit is contained in:
parent
91baf5c65d
commit
00365b13b4
|
|
@ -1,45 +0,0 @@
|
|||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include <u.h>
|
||||
#include "cc.h"
|
||||
|
||||
enum {
|
||||
WORDSIZE = sizeof(uint32),
|
||||
WORDBITS = 32,
|
||||
};
|
||||
|
||||
uintptr
|
||||
bvsize(uintptr n)
|
||||
{
|
||||
return ((n + WORDBITS - 1) / WORDBITS) * WORDSIZE;
|
||||
}
|
||||
|
||||
Bvec*
|
||||
bvalloc(int32 n)
|
||||
{
|
||||
Bvec *bv;
|
||||
uintptr nbytes;
|
||||
|
||||
if(n < 0)
|
||||
fatal(Z, "bvalloc: initial size is negative\n");
|
||||
nbytes = sizeof(Bvec) + bvsize(n);
|
||||
bv = malloc(nbytes);
|
||||
if(bv == nil)
|
||||
fatal(Z, "bvalloc: malloc failed\n");
|
||||
memset(bv, 0, nbytes);
|
||||
bv->n = n;
|
||||
return bv;
|
||||
}
|
||||
|
||||
void
|
||||
bvset(Bvec *bv, int32 i)
|
||||
{
|
||||
uint32 mask;
|
||||
|
||||
if(i < 0 || i >= bv->n)
|
||||
fatal(Z, "bvset: index %d is out of bounds with length %d\n", i, bv->n);
|
||||
mask = 1UL << (i % WORDBITS);
|
||||
bv->b[i / WORDBITS] |= mask;
|
||||
}
|
||||
|
|
@ -761,12 +761,6 @@ Bits blsh(uint);
|
|||
int beq(Bits, Bits);
|
||||
int bset(Bits, uint);
|
||||
|
||||
/*
|
||||
* bv.c
|
||||
*/
|
||||
Bvec* bvalloc(int32 n);
|
||||
void bvset(Bvec *bv, int32 i);
|
||||
|
||||
/*
|
||||
* dpchk.c
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -31,30 +31,6 @@
|
|||
#include "gc.h"
|
||||
#include "../../runtime/funcdata.h"
|
||||
|
||||
enum { BitsPerPointer = 2 };
|
||||
|
||||
static void dumpgcargs(Type *fn, Sym *sym);
|
||||
|
||||
static Sym*
|
||||
makefuncdatasym(char *namefmt, int64 funcdatakind)
|
||||
{
|
||||
Node nod;
|
||||
Sym *sym;
|
||||
static int32 nsym;
|
||||
static char namebuf[40];
|
||||
|
||||
snprint(namebuf, sizeof(namebuf), namefmt, nsym++);
|
||||
sym = slookup(namebuf);
|
||||
sym->class = CSTATIC;
|
||||
memset(&nod, 0, sizeof nod);
|
||||
nod.op = ONAME;
|
||||
nod.sym = sym;
|
||||
nod.class = CSTATIC;
|
||||
gins(AFUNCDATA, nodconst(funcdatakind), &nod);
|
||||
linksym(sym)->type = SRODATA;
|
||||
return sym;
|
||||
}
|
||||
|
||||
int
|
||||
hasdotdotdot(Type *t)
|
||||
{
|
||||
|
|
@ -109,9 +85,6 @@ codgen(Node *n, Node *nn)
|
|||
{
|
||||
Prog *sp;
|
||||
Node *n1, nod, nod1;
|
||||
Sym *gcargs;
|
||||
Sym *gclocals;
|
||||
int isvarargs;
|
||||
|
||||
cursafe = 0;
|
||||
curarg = 0;
|
||||
|
|
@ -134,16 +107,6 @@ codgen(Node *n, Node *nn)
|
|||
p->from.sym->cfunc = 1;
|
||||
sp = p;
|
||||
|
||||
/*
|
||||
* generate funcdata symbol for this function.
|
||||
* data is filled in at the end of codgen().
|
||||
*/
|
||||
isvarargs = hasdotdotdot(thisfn);
|
||||
gcargs = nil;
|
||||
if(!isvarargs)
|
||||
gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps);
|
||||
gclocals = makefuncdatasym("gclocals·%d", FUNCDATA_LocalsPointerMaps);
|
||||
|
||||
/*
|
||||
* isolate first argument
|
||||
*/
|
||||
|
|
@ -178,22 +141,6 @@ codgen(Node *n, Node *nn)
|
|||
if(thechar=='6' || thechar=='7') /* [sic] */
|
||||
maxargsafe = xround(maxargsafe, 8);
|
||||
sp->to.offset += maxargsafe;
|
||||
|
||||
if(!isvarargs)
|
||||
dumpgcargs(thisfn, gcargs);
|
||||
|
||||
// TODO(rsc): "stkoff" is not right. It does not account for
|
||||
// the possibility of data stored in .safe variables.
|
||||
// Unfortunately those move up and down just like
|
||||
// the argument frame (and in fact dovetail with it)
|
||||
// so the number we need is not available or even
|
||||
// well-defined. Probably we need to make the safe
|
||||
// area its own section.
|
||||
// That said, we've been using stkoff for months
|
||||
// and nothing too terrible has happened.
|
||||
gextern(gclocals, nodconst(-stkoff), 0, 4); // locals
|
||||
gclocals->type = typ(0, T);
|
||||
gclocals->type->width = 4;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -673,113 +620,3 @@ bcomplex(Node *n, Node *c)
|
|||
boolgen(n, 1, Z);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Updates the bitvector with a set bit for each pointer containing
|
||||
// value in the type description starting at offset.
|
||||
static void
|
||||
walktype1(Type *t, int32 offset, Bvec *bv, int param)
|
||||
{
|
||||
Type *t1;
|
||||
int32 o;
|
||||
int32 widthptr;
|
||||
|
||||
widthptr = ewidth[TIND];
|
||||
switch(t->etype) {
|
||||
case TCHAR:
|
||||
case TUCHAR:
|
||||
case TSHORT:
|
||||
case TUSHORT:
|
||||
case TINT:
|
||||
case TUINT:
|
||||
case TLONG:
|
||||
case TULONG:
|
||||
case TVLONG:
|
||||
case TUVLONG:
|
||||
case TFLOAT:
|
||||
case TDOUBLE:
|
||||
// non-pointer types
|
||||
for(o = 0; o < t->width; o++)
|
||||
bvset(bv, ((offset + t->offset + o) / widthptr) * BitsPerPointer); // 1 = live scalar
|
||||
break;
|
||||
|
||||
case TIND:
|
||||
pointer:
|
||||
// pointer types
|
||||
if((offset + t->offset) % widthptr != 0)
|
||||
yyerror("unaligned pointer");
|
||||
bvset(bv, ((offset + t->offset) / widthptr)*BitsPerPointer + 1); // 2 = live ptr
|
||||
break;
|
||||
|
||||
case TARRAY:
|
||||
if(param) // unlike Go, C passes arrays by reference
|
||||
goto pointer;
|
||||
// array in struct or union is an actual array
|
||||
for(o = 0; o < t->width; o += t->link->width)
|
||||
walktype1(t->link, offset+o, bv, 0);
|
||||
break;
|
||||
|
||||
case TSTRUCT:
|
||||
// build map recursively
|
||||
for(t1 = t->link; t1 != T; t1 = t1->down)
|
||||
walktype1(t1, offset, bv, 0);
|
||||
break;
|
||||
|
||||
case TUNION:
|
||||
walktype1(t->link, offset, bv, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
yyerror("can't handle arg type %s\n", tnames[t->etype]);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute a bit vector to describe the pointer containing locations
|
||||
// in the argument list. Adds the data to gcsym and returns the offset
|
||||
// of end of the bit vector.
|
||||
static void
|
||||
dumpgcargs(Type *fn, Sym *sym)
|
||||
{
|
||||
Bvec *bv;
|
||||
Type *t;
|
||||
int32 i;
|
||||
int32 argbytes;
|
||||
int32 symoffset, argoffset;
|
||||
|
||||
// Dump the length of the bitmap array. This value is always one for
|
||||
// functions written in C.
|
||||
symoffset = 0;
|
||||
gextern(sym, nodconst(1), symoffset, 4);
|
||||
symoffset += 4;
|
||||
argbytes = (argsize(1) + ewidth[TIND] - 1);
|
||||
bv = bvalloc((argbytes / ewidth[TIND]) * BitsPerPointer);
|
||||
argoffset = 0;
|
||||
if(hasdotdotdot(thisfn))
|
||||
argoffset = align(0, fn->link, Aarg0, nil);
|
||||
if(argoffset > 0) {
|
||||
// The C calling convention returns structs by copying them to a
|
||||
// location pointed to by a hidden first argument. This first
|
||||
// argument is a pointer.
|
||||
if(argoffset != ewidth[TIND])
|
||||
yyerror("passbyptr arg not the right size");
|
||||
bvset(bv, 1); // 2 = live ptr
|
||||
}
|
||||
for(t = fn->down; t != T; t = t->down) {
|
||||
if(t->etype == TVOID)
|
||||
continue;
|
||||
argoffset = align(argoffset, t, Aarg1, nil);
|
||||
walktype1(t, argoffset, bv, 1);
|
||||
argoffset = align(argoffset, t, Aarg2, nil);
|
||||
}
|
||||
// Dump the length of the bitmap.
|
||||
gextern(sym, nodconst(bv->n), symoffset, 4);
|
||||
symoffset += 4;
|
||||
// Dump the words of the bitmap.
|
||||
for(i = 0; i < bv->n; i += 32) {
|
||||
gextern(sym, nodconst(bv->b[i/32]), symoffset, 4);
|
||||
symoffset += 4;
|
||||
}
|
||||
free(bv);
|
||||
// Finalize the gc symbol.
|
||||
sym->type = typ(0, T);
|
||||
sym->type->width = symoffset;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -331,145 +331,22 @@ mapnames[] = {
|
|||
// | args to callee |
|
||||
// +------------------+ <- frame->sp
|
||||
//
|
||||
// (arm: TODO)
|
||||
|
||||
typedef struct CopyableInfo CopyableInfo;
|
||||
struct CopyableInfo {
|
||||
Stack stk;
|
||||
int32 frames; // count of copyable frames (-1 = not copyable)
|
||||
};
|
||||
// (arm)
|
||||
// +------------------+
|
||||
// | args from caller |
|
||||
// +------------------+ <- frame->argp
|
||||
// | caller's retaddr |
|
||||
// +------------------+ <- frame->varp
|
||||
// | locals |
|
||||
// +------------------+
|
||||
// | args to callee |
|
||||
// +------------------+
|
||||
// | return address |
|
||||
// +------------------+ <- frame->sp
|
||||
|
||||
void runtime·main(void);
|
||||
void runtime·switchtoM(void(*)(void));
|
||||
|
||||
static bool
|
||||
checkframecopy(Stkframe *frame, void *arg)
|
||||
{
|
||||
CopyableInfo *cinfo;
|
||||
Func *f;
|
||||
StackMap *stackmap;
|
||||
|
||||
cinfo = arg;
|
||||
f = frame->fn;
|
||||
if(StackDebug >= 2)
|
||||
runtime·printf(" checking %s frame=[%p,%p] stk=[%p,%p]\n", runtime·funcname(f), frame->sp, frame->fp, cinfo->stk.lo, cinfo->stk.hi);
|
||||
// if we're not in the segment any more, return immediately.
|
||||
if(frame->varp < cinfo->stk.lo || frame->varp >= cinfo->stk.hi) {
|
||||
if(StackDebug >= 2)
|
||||
runtime·printf(" <next segment>\n");
|
||||
return false; // stop traceback
|
||||
}
|
||||
if(f->entry == (uintptr)runtime·switchtoM) {
|
||||
// A special routine at the bottom of stack of a goroutine that does onM call.
|
||||
// We will allow it to be copied even though we don't
|
||||
// have full GC info for it (because it is written in asm).
|
||||
cinfo->frames++;
|
||||
return true;
|
||||
}
|
||||
if((byte*)frame->varp != (byte*)frame->sp) { // not in prologue (and has at least one local or outarg)
|
||||
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
|
||||
if(stackmap == nil) {
|
||||
cinfo->frames = -1;
|
||||
runtime·printf("runtime: copystack: no locals info for %s\n", runtime·funcname(f));
|
||||
return false;
|
||||
}
|
||||
if(stackmap->n <= 0) {
|
||||
cinfo->frames = -1;
|
||||
runtime·printf("runtime: copystack: locals size info only for %s\n", runtime·funcname(f));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(frame->arglen != 0) {
|
||||
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
|
||||
if(stackmap == nil) {
|
||||
cinfo->frames = -1;
|
||||
runtime·printf("runtime: copystack: no arg info for %s\n", runtime·funcname(f));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
cinfo->frames++;
|
||||
return true; // this frame is ok; keep going
|
||||
}
|
||||
|
||||
// If the top segment of the stack contains an uncopyable
|
||||
// frame, return -1. Otherwise return the number of frames
|
||||
// in the top segment, all of which are copyable.
|
||||
static int32
|
||||
copyabletopsegment(G *gp)
|
||||
{
|
||||
CopyableInfo cinfo;
|
||||
Defer *d;
|
||||
Func *f;
|
||||
FuncVal *fn;
|
||||
StackMap *stackmap;
|
||||
bool (*cb)(Stkframe*, void*);
|
||||
|
||||
if(gp->stack.lo == 0)
|
||||
runtime·throw("missing stack in copyabletopsegment");
|
||||
cinfo.stk = gp->stack;
|
||||
cinfo.frames = 0;
|
||||
|
||||
// Check that each frame is copyable. As a side effect,
|
||||
// count the frames.
|
||||
cb = checkframecopy;
|
||||
runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, &cb, &cinfo, false);
|
||||
if(StackDebug >= 1 && cinfo.frames != -1)
|
||||
runtime·printf("copystack: %d copyable frames\n", cinfo.frames);
|
||||
|
||||
if(cinfo.frames == -1)
|
||||
return -1;
|
||||
|
||||
// Check to make sure all Defers are copyable
|
||||
for(d = gp->defer; d != nil; d = d->link) {
|
||||
if(cinfo.stk.lo <= (uintptr)d && (uintptr)d < cinfo.stk.hi) {
|
||||
// Defer is on the stack. Its copyableness has
|
||||
// been established during stack walking.
|
||||
// For now, this only happens with the Defer in runtime.main.
|
||||
continue;
|
||||
}
|
||||
if(d->argp < cinfo.stk.lo || cinfo.stk.hi <= d->argp)
|
||||
break; // a defer for the next segment
|
||||
fn = d->fn;
|
||||
if(fn == nil) // See issue 8047
|
||||
continue;
|
||||
f = runtime·findfunc((uintptr)fn->fn);
|
||||
if(f == nil) {
|
||||
runtime·printf("runtime: copystack: no func for deferred pc %p\n", fn->fn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Check to make sure we have an args pointer map for the defer's args.
|
||||
// We only need the args map, but we check
|
||||
// for the locals map also, because when the locals map
|
||||
// isn't provided it means the ptr map came from C and
|
||||
// C (particularly, cgo) lies to us. See issue 7695.
|
||||
stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
|
||||
if(stackmap == nil || stackmap->n <= 0) {
|
||||
runtime·printf("runtime: copystack: no arg info for deferred %s\n", runtime·funcname(f));
|
||||
return -1;
|
||||
}
|
||||
stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
|
||||
if(stackmap == nil || stackmap->n <= 0) {
|
||||
runtime·printf("runtime: copystack: no local info for deferred %s\n", runtime·funcname(f));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(cinfo.stk.lo <= (uintptr)fn && (uintptr)fn < cinfo.stk.hi) {
|
||||
// FuncVal is on the stack. Again, its copyableness
|
||||
// was established during stack walking.
|
||||
continue;
|
||||
}
|
||||
// The FuncVal may have pointers in it, but fortunately for us
|
||||
// the compiler won't put pointers into the stack in a
|
||||
// heap-allocated FuncVal.
|
||||
// One day if we do need to check this, we'll need maps of the
|
||||
// pointerness of the closure args. The only place we have that map
|
||||
// right now is in the gc program for the FuncVal. Ugh.
|
||||
}
|
||||
|
||||
return cinfo.frames;
|
||||
}
|
||||
|
||||
typedef struct AdjustInfo AdjustInfo;
|
||||
struct AdjustInfo {
|
||||
Stack old;
|
||||
|
|
@ -573,8 +450,12 @@ adjustframe(Stkframe *frame, void *arg)
|
|||
f = frame->fn;
|
||||
if(StackDebug >= 2)
|
||||
runtime·printf(" adjusting %s frame=[%p,%p] pc=%p continpc=%p\n", runtime·funcname(f), frame->sp, frame->fp, frame->pc, frame->continpc);
|
||||
if(f->entry == (uintptr)runtime·switchtoM)
|
||||
if(f->entry == (uintptr)runtime·switchtoM) {
|
||||
// A special routine at the bottom of stack of a goroutine that does an onM call.
|
||||
// We will allow it to be copied even though we don't
|
||||
// have full GC info for it (because it is written in asm).
|
||||
return true;
|
||||
}
|
||||
targetpc = frame->continpc;
|
||||
if(targetpc == 0) {
|
||||
// Frame is dead.
|
||||
|
|
@ -648,11 +529,10 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
|
|||
runtime·printf("runtime: adjustdefers argp=%p stk=%p %p\n", d->argp, adjinfo->old.lo, adjinfo->old.hi);
|
||||
runtime·throw("adjustdefers: unexpected argp");
|
||||
}
|
||||
d->argp += adjinfo->delta;
|
||||
fn = d->fn;
|
||||
if(fn == nil) {
|
||||
// Defer of nil function. It will panic when run, and there
|
||||
// aren't any args to adjust. See issue 8047.
|
||||
d->argp += adjinfo->delta;
|
||||
// Defer of nil function. It will panic when run. See issue 8047.
|
||||
continue;
|
||||
}
|
||||
f = runtime·findfunc((uintptr)fn->fn);
|
||||
|
|
@ -675,7 +555,11 @@ adjustdefers(G *gp, AdjustInfo *adjinfo)
|
|||
bv = runtime·stackmapdata(stackmap, 0);
|
||||
adjustpointers(d->args, &bv, adjinfo, f);
|
||||
}
|
||||
d->argp += adjinfo->delta;
|
||||
// The FuncVal may have pointers in it, but fortunately for us
|
||||
// the compiler won't put pointers into the stack in a
|
||||
// heap-allocated FuncVal.
|
||||
// One day if we do need to check this, we can use the gc bits in the
|
||||
// heap to do the right thing (although getting the size will be expensive).
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -707,10 +591,9 @@ adjustsudogs(G *gp, AdjustInfo *adjinfo)
|
|||
}
|
||||
}
|
||||
|
||||
// Copies the top stack segment of gp to a new stack segment of a
|
||||
// different size. The top segment must contain nframes frames.
|
||||
// Copies gp's stack to a new stack of a different size.
|
||||
static void
|
||||
copystack(G *gp, uintptr nframes, uintptr newsize)
|
||||
copystack(G *gp, uintptr newsize)
|
||||
{
|
||||
Stack old, new;
|
||||
uintptr used;
|
||||
|
|
@ -735,7 +618,7 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
|
|||
adjinfo.old = old;
|
||||
adjinfo.delta = new.hi - old.hi;
|
||||
cb = adjustframe;
|
||||
runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, nframes, &cb, &adjinfo, false);
|
||||
runtime·gentraceback(~(uintptr)0, ~(uintptr)0, 0, gp, 0, nil, 0x7fffffff, &cb, &adjinfo, false);
|
||||
|
||||
// adjust other miscellaneous things that have pointers into stacks.
|
||||
adjustctxt(gp, &adjinfo);
|
||||
|
|
@ -785,7 +668,7 @@ runtime·round2(int32 x)
|
|||
void
|
||||
runtime·newstack(void)
|
||||
{
|
||||
int32 oldsize, newsize, nframes;
|
||||
int32 oldsize, newsize;
|
||||
uintptr sp;
|
||||
G *gp;
|
||||
Gobuf morebuf;
|
||||
|
|
@ -867,10 +750,6 @@ runtime·newstack(void)
|
|||
}
|
||||
|
||||
// Allocate a bigger segment and move the stack.
|
||||
nframes = copyabletopsegment(gp);
|
||||
if(nframes == -1)
|
||||
runtime·throw("unable to grow stack");
|
||||
|
||||
oldsize = gp->stack.hi - gp->stack.lo;
|
||||
newsize = oldsize * 2;
|
||||
if(newsize > runtime·maxstacksize) {
|
||||
|
|
@ -880,7 +759,7 @@ runtime·newstack(void)
|
|||
|
||||
// Note that the concurrent GC might be scanning the stack as we try to replace it.
|
||||
// copystack takes care of the appropriate coordination with the stack scanner.
|
||||
copystack(gp, nframes, newsize);
|
||||
copystack(gp, newsize);
|
||||
if(StackDebug >= 1)
|
||||
runtime·printf("stack grow done\n");
|
||||
runtime·casgstatus(gp, Gwaiting, Grunning);
|
||||
|
|
@ -913,14 +792,13 @@ runtime·gostartcallfn(Gobuf *gobuf, FuncVal *fv)
|
|||
void
|
||||
runtime·shrinkstack(G *gp)
|
||||
{
|
||||
int32 nframes;
|
||||
uintptr used, oldsize, newsize;
|
||||
|
||||
if(runtime·readgstatus(gp) == Gdead)
|
||||
return;
|
||||
if(gp->stack.lo == 0)
|
||||
runtime·throw("missing stack in shrinkstack");
|
||||
//return; // TODO: why does this happen?
|
||||
|
||||
oldsize = gp->stack.hi - gp->stack.lo;
|
||||
newsize = oldsize / 2;
|
||||
if(newsize < FixedStack)
|
||||
|
|
@ -938,10 +816,7 @@ runtime·shrinkstack(G *gp)
|
|||
#endif
|
||||
if(StackDebug > 0)
|
||||
runtime·printf("shrinking stack %D->%D\n", (uint64)oldsize, (uint64)newsize);
|
||||
nframes = copyabletopsegment(gp);
|
||||
if(nframes == -1)
|
||||
return;
|
||||
copystack(gp, nframes, newsize);
|
||||
copystack(gp, newsize);
|
||||
}
|
||||
|
||||
static void badc(void);
|
||||
|
|
|
|||
Loading…
Reference in New Issue