Commit 4e0a51c2 authored by Carl Shapiro's avatar Carl Shapiro

cmd/5l, cmd/6l, cmd/8l, cmd/gc, runtime: generate and use bitmaps of argument pointer locations

With this change the compiler emits a bitmap for each function
covering its stack frame arguments area.  If an argument word
is known to contain a pointer, a bit is set.  The garbage
collector reads this information when scanning the stack by
frames and uses it to ignores locations known to not contain a
pointer.

R=golang-dev, bradfitz, daniel.morsing, dvyukov, khr, khr, iant, cshapiro
CC=golang-dev
https://golang.org/cl/9223046
parent 8bbb0853
......@@ -78,6 +78,8 @@ peep(void)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
p = p->link;
}
}
......@@ -1195,6 +1197,8 @@ copyu(Prog *p, Adr *v, Adr *s)
return 0;
case ALOCALS: /* funny */
case ANPTRS:
case APTRS:
return 0;
}
}
......
......@@ -250,6 +250,8 @@ regopt(Prog *firstp)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
continue;
}
r = rega();
......
......@@ -199,6 +199,8 @@ enum as
AUSEFIELD,
ALOCALS,
ATYPE,
ANPTRS,
APTRS,
ALAST,
};
......
......@@ -154,6 +154,8 @@ struct Sym
int32 elfsym;
int32 locals; // size of stack frame locals area
int32 args; // size of stack frame incoming arguments area
int32 nptrs; // number of bits in the pointer map
uint32* ptrs; // pointer map data
uchar special;
uchar fnptr; // used as fn ptr
uchar stkcheck;
......
......@@ -627,6 +627,38 @@ loop:
pc++;
goto loop;
case ANPTRS:
if(skip)
goto casedef;
if(cursym->nptrs != -1) {
diag("ldobj1: multiple pointer maps defined for %s", cursym->name);
errorexit();
}
if(p->to.offset > cursym->args/PtrSize) {
diag("ldobj1: pointer map definition for %s exceeds its argument size", cursym->name);
errorexit();
}
cursym->nptrs = p->to.offset;
if(cursym->nptrs != 0)
cursym->ptrs = mal((rnd(cursym->nptrs, 32) / 32) * sizeof(*cursym->ptrs));
pc++;
goto loop;
case APTRS:
if(skip)
goto casedef;
if(cursym->nptrs == -1 || cursym->ptrs == NULL) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
if(p->from.offset*32 >= rnd(cursym->nptrs, 32)) {
diag("ldobj1: excessive pointer map data provided for %s", cursym->name);
errorexit();
}
cursym->ptrs[p->from.offset] = p->to.offset;
pc++;
goto loop;
case ATEXT:
if(cursym != nil && cursym->text) {
histtoauto();
......@@ -670,6 +702,7 @@ loop:
s->text = p;
s->value = pc;
s->args = p->to.offset2;
s->nptrs = -1;
lastp = p;
p->pc = pc;
pc++;
......
......@@ -134,6 +134,8 @@ peep(void)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
p = p->link;
}
}
......
......@@ -225,6 +225,8 @@ regopt(Prog *firstp)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
continue;
}
r = rega();
......
......@@ -763,6 +763,8 @@ enum as
AUSEFIELD,
ALOCALS,
ATYPE,
ANPTRS,
APTRS,
ALAST
};
......
......@@ -161,6 +161,8 @@ struct Sym
int32 elfsym;
int32 locals; // size of stack frame locals area
int32 args; // size of stack frame incoming arguments area
int32 nptrs; // number of bits in the pointer map
uint32* ptrs; // pointer map data
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
Sym* next; // in text or data list
......
......@@ -616,6 +616,38 @@ loop:
pc++;
goto loop;
case ANPTRS:
if(skip)
goto casdef;
if(cursym->nptrs != -1) {
diag("ldobj1: multiple pointer maps defined for %s", cursym->name);
errorexit();
}
if(p->to.offset > cursym->args/PtrSize) {
diag("ldobj1: pointer map definition for %s exceeds its argument size", cursym->name);
errorexit();
}
cursym->nptrs = p->to.offset;
if(cursym->nptrs != 0)
cursym->ptrs = mal((rnd(cursym->nptrs, 32) / 32) * sizeof(*cursym->ptrs));
pc++;
goto loop;
case APTRS:
if(skip)
goto casdef;
if(cursym->nptrs == -1 || cursym->ptrs == NULL) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
if(p->from.offset*32 >= rnd(cursym->nptrs, 32)) {
diag("ldobj1: excessive pointer map data provided for %s", cursym->name);
errorexit();
}
cursym->ptrs[p->from.offset] = p->to.offset;
pc++;
goto loop;
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
......@@ -660,6 +692,7 @@ loop:
s->type = STEXT;
s->value = pc;
s->args = p->to.offset >> 32;
s->nptrs = -1;
lastp = p;
p->pc = pc++;
goto loop;
......
......@@ -1337,6 +1337,8 @@ Optab optab[] =
{ AUSEFIELD, ynop, Px, 0,0 },
{ ALOCALS },
{ ATYPE },
{ ANPTRS },
{ APTRS },
{ AEND },
0
......
......@@ -128,6 +128,8 @@ peep(void)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
p = p->link;
}
}
......
......@@ -197,6 +197,8 @@ regopt(Prog *firstp)
case ASIGNAME:
case ALOCALS:
case ATYPE:
case ANPTRS:
case APTRS:
continue;
}
r = rega();
......
......@@ -580,6 +580,8 @@ enum as
AUSEFIELD,
ALOCALS,
ATYPE,
ANPTRS,
APTRS,
ALAST
};
......
......@@ -145,6 +145,8 @@ struct Sym
int32 elfsym;
int32 locals; // size of stack frame locals area
int32 args; // size of stack frame incoming arguments area
int32 nptrs; // number of bits in the pointer map
uint32* ptrs; // pointer map data
Sym* hash; // in hash table
Sym* allsym; // in all symbol list
Sym* next; // in text or data list
......
......@@ -626,6 +626,38 @@ loop:
pc++;
goto loop;
case ANPTRS:
if(skip)
goto casdef;
if(cursym->nptrs != -1) {
diag("ldobj1: multiple pointer maps defined for %s", cursym->name);
errorexit();
}
if(p->to.offset > cursym->args/PtrSize) {
diag("ldobj1: pointer map definition for %s exceeds its argument size", cursym->name);
errorexit();
}
cursym->nptrs = p->to.offset;
if(cursym->nptrs != 0)
cursym->ptrs = mal((rnd(cursym->nptrs, 32) / 32) * sizeof(*cursym->ptrs));
pc++;
goto loop;
case APTRS:
if(skip)
goto casdef;
if(cursym->nptrs == -1 || cursym->ptrs == NULL) {
diag("ldobj1: pointer map data provided for %s without a definition", cursym->name);
errorexit();
}
if(p->from.offset*32 >= rnd(cursym->nptrs, 32)) {
diag("ldobj1: excessive pointer map data provided for %s", cursym->name);
errorexit();
}
cursym->ptrs[p->from.offset] = p->to.offset;
pc++;
goto loop;
case ATEXT:
s = p->from.sym;
if(s->text != nil) {
......@@ -665,6 +697,7 @@ loop:
s->type = STEXT;
s->value = pc;
s->args = p->to.offset2;
s->nptrs = -1;
lastp = p;
p->pc = pc++;
goto loop;
......
......@@ -1001,6 +1001,8 @@ Optab optab[] =
{ AUSEFIELD, ynop, Px, 0,0 },
{ ALOCALS },
{ ATYPE },
{ ANPTRS },
{ APTRS },
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 <libc.h>
#include "go.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("bvalloc: initial size is negative\n");
nbytes = sizeof(Bvec) + bvsize(n);
bv = malloc(nbytes);
if(bv == nil)
fatal("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("bvset: index %d is out of bounds with length %d\n", i, bv->n);
mask = 1 << (i % WORDBITS);
bv->b[i / WORDBITS] |= mask;
}
void
bvres(Bvec *bv, int32 i)
{
uint32 mask;
if(i < 0 || i >= bv->n)
fatal("bvres: index %d is out of bounds with length %d\n", i, bv->n);
mask = ~(1 << (i % WORDBITS));
bv->b[i / WORDBITS] &= mask;
}
int
bvget(Bvec *bv, int32 i)
{
uint32 mask, word;
if(i < 0 || i >= bv->n)
fatal("bvget: index %d is out of bounds with length %d\n", i, bv->n);
mask = 1 << (i % WORDBITS);
word = bv->b[i / WORDBITS] & mask;
return word ? 1 : 0;
}
int
bvisempty(Bvec *bv)
{
int32 i;
for(i = 0; i < bv->n; i += WORDBITS)
if(bv->b[i / WORDBITS] != 0)
return 0;
return 1;
}
int bvcmp(Bvec *bv1, Bvec *bv2)
{
int32 i;
if(bv1->n != bv2->n) {
fatal("bvcmp: size %d != %d\n", bv1->n, bv2->n);
}
for(i = 0; i < bv1->n; i += WORDBITS) {
if(bv1->b[i / WORDBITS] != bv2->b[i / WORDBITS]) {
fatal("bvcmp: element %x != %x @ %d\n", bv1->b[i/WORDBITS], bv2->b[i/WORDBITS], i/WORDBITS);
}
}
return 0;
}
......@@ -127,6 +127,7 @@ struct Val
} u;
};
typedef struct Bvec Bvec;
typedef struct Pkg Pkg;
typedef struct Sym Sym;
typedef struct Node Node;
......@@ -696,6 +697,12 @@ struct Bits
EXTERN Bits zbits;
struct Bvec
{
int32 n; // number of bits
uint32 b[];
};
typedef struct Var Var;
struct Var
{
......@@ -985,6 +992,16 @@ int bnum(Bits a);
Bits bor(Bits a, Bits b);
int bset(Bits a, uint n);
/*
* bv.c
*/
Bvec* bvalloc(int32 n);
void bvset(Bvec *bv, int32 i);
void bvres(Bvec *bv, int32 i);
int bvget(Bvec *bv, int32 i);
int bvisempty(Bvec *bv);
int bvcmp(Bvec *bv1, Bvec *bv2);
/*
* closure.c
*/
......
......@@ -8,6 +8,7 @@
#include "opt.h"
static void allocauto(Prog* p);
static void pointermap(Node* fn);
void
compile(Node *fn)
......@@ -108,6 +109,8 @@ compile(Node *fn)
}
}
pointermap(fn);
genlist(curfn->enter);
retpc = nil;
......@@ -168,6 +171,149 @@ ret:
lineno = lno;
}
static void
walktype1(Type *t, vlong *xoffset, Bvec *bv)
{
vlong fieldoffset, i, o;
Type *t1;
if(t->align > 0 && (*xoffset % t->align) != 0)
fatal("walktype1: invalid initial alignment, %T", t);
switch(t->etype) {
case TINT8:
case TUINT8:
case TINT16:
case TUINT16:
case TINT32:
case TUINT32:
case TINT64:
case TUINT64:
case TINT:
case TUINT:
case TUINTPTR:
case TBOOL:
case TFLOAT32:
case TFLOAT64:
case TCOMPLEX64:
case TCOMPLEX128:
*xoffset += t->width;
break;
case TPTR32:
case TPTR64:
case TUNSAFEPTR:
case TFUNC:
case TCHAN:
case TMAP:
if(*xoffset % widthptr != 0)
fatal("walktype1: invalid alignment, %T", t);
bvset(bv, *xoffset / widthptr);
*xoffset += t->width;
break;
case TSTRING:
// struct { byte *str; intgo len; }
if(*xoffset % widthptr != 0)
fatal("walktype1: invalid alignment, %T", t);
bvset(bv, *xoffset / widthptr);
*xoffset += t->width;
break;
case TINTER:
// struct { Itab* tab; union { void* ptr, uintptr val } data; }
// or, when isnilinter(t)==true:
// struct { Type* type; union { void* ptr, uintptr val } data; }
if(*xoffset % widthptr != 0)
fatal("walktype1: invalid alignment, %T", t);
bvset(bv, *xoffset / widthptr);
bvset(bv, (*xoffset + widthptr) / widthptr);
*xoffset += t->width;
break;
case TARRAY:
// The value of t->bound is -1 for slices types and >0 for
// for fixed array types. All other values are invalid.
if(t->bound < -1)
fatal("walktype1: invalid bound, %T", t);
if(isslice(t)) {
// struct { byte* array; uintgo len; uintgo cap; }
if(*xoffset % widthptr != 0)
fatal("walktype1: invalid TARRAY alignment, %T", t);
bvset(bv, *xoffset / widthptr);
*xoffset += t->width;
} else if(!haspointers(t->type))
*xoffset += t->width;
else
for(i = 0; i < t->bound; ++i)
walktype1(t->type, xoffset, bv);
break;
case TSTRUCT:
o = 0;
for(t1 = t->type; t1 != T; t1 = t1->down) {
fieldoffset = t1->width;
*xoffset += fieldoffset - o;
walktype1(t1->type, xoffset, bv);
o = fieldoffset + t1->type->width;
}
*xoffset += t->width - o;
break;
default:
fatal("walktype1: unexpected type, %T", t);
}
}
static void
walktype(Type *type, Bvec *bv)
{
vlong xoffset;
// Start the walk at offset 0. The correct offset will be
// filled in by the first type encountered during the walk.
xoffset = 0;
walktype1(type, &xoffset, bv);
}
// Compute a bit vector to describes the pointer containing locations
// in the argument list.
static void
pointermap(Node *fn)
{
Type *thistype, *inargtype, *outargtype;
Bvec *bv;
Prog *prog;
int32 i;
thistype = getthisx(fn->type);
inargtype = getinargx(fn->type);
outargtype = getoutargx(fn->type);
bv = bvalloc(fn->type->argwid / widthptr);
if(thistype != nil)
walktype(thistype, bv);
if(inargtype != nil)
walktype(inargtype, bv);
if(outargtype != nil)
walktype(outargtype, bv);
if(bvisempty(bv)) {
prog = gins(ANPTRS, N, N);
prog->to.type = D_CONST;
prog->to.offset = 0;
} else {
prog = gins(ANPTRS, N, N);
prog->to.type = D_CONST;
prog->to.offset = bv->n;
for(i = 0; i < bv->n; i += 32) {
prog = gins(APTRS, N, N);
prog->from.type = D_CONST;
prog->from.offset = i / 32;
prog->to.type = D_CONST;
prog->to.offset = bv->b[i / 32];
}
}
free(bv);
}
// Sort the list of stack variables. autos after anything else,
// within autos, unused after used, and within used on reverse alignment.
......
......@@ -1850,6 +1850,7 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
Auto *a;
Sym *s;
int32 off;
int32 i;
// These symbols won't show up in the first loop below because we
// skip STEXT symbols. Normal STEXT symbols are emitted by walking textp.
......@@ -1910,13 +1911,18 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*))
put(s, s->name, 'T', s->value, s->size, s->version, s->gotype);
/* frame, locals, args, auto and param after */
/* frame, locals, args, auto, param and pointers after */
put(nil, ".frame", 'm', (uint32)s->text->to.offset+PtrSize, 0, 0, 0);
put(nil, ".locals", 'm', s->locals, 0, 0, 0);
if(s->text->textflag & NOSPLIT)
put(nil, ".args", 'm', ArgsSizeUnknown, 0, 0, 0);
else
put(nil, ".args", 'm', s->args, 0, 0, 0);
if(s->nptrs >= 0) {
put(nil, ".nptrs", 'm', s->nptrs, 0, 0, 0);
for(i = 0; i < s->nptrs; i += 32)
put(nil, ".ptrs", 'm', s->ptrs[i / 32], 0, 0, 0);
}
for(a=s->autom; a; a=a->link) {
// Emit a or p according to actual offset, even if label is wrong.
......
......@@ -85,9 +85,10 @@ type Func struct { // Keep in sync with runtime.h:struct Func
entry uintptr // entry pc
pc0 uintptr // starting pc, ln for table
ln0 int32
frame int32 // stack frame size
args int32 // in/out args size
locals int32 // locals size
frame int32 // stack frame size
args int32 // in/out args size
locals int32 // locals size
ptrs []int32 // pointer map
}
// FuncForPC returns a *Func describing the function that contains the
......
......@@ -1391,25 +1391,53 @@ addroot(Obj obj)
work.nroot++;
}
// Scan a stack frame. The doframe parameter is a signal that the previously
// scanned activation has an unknown argument size. When *doframe is true the
// current activation must have its entire frame scanned. Otherwise, only the
// locals need to be scanned.
// Scan a stack frame. Normally, this scans the locals area,
// belonging to the current frame, and the arguments area, belonging
// to the calling frame. When the arguments area size is unknown, the
// arguments area scanning is delayed and the doframe parameter
// signals that the previously scanned activation has an unknown
// argument size. When *doframe is true, the possible arguments area
// for the callee, located between the stack pointer and the bottom of
// the locals area, is additionally scanned. Otherwise, this area is
// ignored, as it must have been scanned when the callee was scanned.
static void
addframeroots(Func *f, byte*, byte *sp, void *doframe)
{
byte *fp, *ap;
uintptr outs;
int32 i, j, rem;
uint32 w, b;
if(thechar == '5')
sp += sizeof(uintptr);
fp = sp + f->frame;
if(f->locals == 0 || *(bool*)doframe == true)
// Scan the entire stack frame.
addroot((Obj){sp, f->frame - sizeof(uintptr), 0});
else if(f->locals > 0) {
// Scan the locals area.
outs = f->frame - sizeof(uintptr) - f->locals;
addroot((Obj){sp + outs, f->locals, 0});
}
if(f->args > 0)
addroot((Obj){sp + f->frame, f->args, 0});
if(f->args > 0) {
// Scan the arguments area.
if(f->ptrs.array != nil) {
ap = fp;
rem = f->args / sizeof(uintptr);
for(i = 0; i < f->ptrs.len; i++) {
w = ((uint32*)f->ptrs.array)[i];
b = 1;
for((j = (rem < 32) ? rem : 32); j > 0; j--) {
if(w & b)
addroot((Obj){ap, sizeof(uintptr), 0});
b <<= 1;
ap += sizeof(uintptr);
}
rem -= 32;
}
} else
addroot((Obj){fp, f->args, 0});
}
*(bool*)doframe = (f->args == ArgsSizeUnknown);
}
......@@ -1469,7 +1497,7 @@ addstackroots(G *gp)
return;
}
}
if (ScanStackByFrames) {
if(ScanStackByFrames) {
USED(stk);
USED(guard);
doframe = false;
......
......@@ -412,6 +412,7 @@ struct Func
int32 frame; // stack frame size
int32 args; // in/out args size
int32 locals; // locals size
Slice ptrs; // pointer map
};
// layout of Itab known to compilers
......@@ -811,6 +812,7 @@ void runtime·netpollready(G**, PollDesc*, int32);
void runtime·crash(void);
#pragma varargck argpos runtime·printf 1
#pragma varargck type "c" int32
#pragma varargck type "d" int32
#pragma varargck type "d" uint32
#pragma varargck type "D" int64
......
......@@ -199,6 +199,7 @@ static void
dofunc(Sym *sym)
{
Func *f;
uintgo cap;
switch(sym->symtype) {
case 't':
......@@ -231,8 +232,24 @@ dofunc(Sym *sym)
func[nfunc-1].locals = sym->value;
else if(runtime·strcmp(sym->name, (byte*)".args") == 0)
func[nfunc-1].args = sym->value;
else {
runtime·printf("invalid 'm' symbol named '%s'\n", sym->name);
else if(runtime·strcmp(sym->name, (byte*)".nptrs") == 0) {
// TODO(cshapiro): use a dense representation for gc information
if(sym->value > func[nfunc-1].args/sizeof(uintptr)) {
runtime·printf("more pointer map entries than argument words\n");
runtime·throw("mangled symbol table");
}
cap = ROUND(sym->value, 32) / 32;
func[nfunc-1].ptrs.array = runtime·mallocgc(cap*sizeof(uint32), FlagNoPointers|FlagNoGC, 0, 1);
func[nfunc-1].ptrs.len = 0;
func[nfunc-1].ptrs.cap = cap;
} else if(runtime·strcmp(sym->name, (byte*)".ptrs") == 0) {
if(func[nfunc-1].ptrs.len >= func[nfunc-1].ptrs.cap) {
runtime·printf("more pointer map entries read than argument words\n");
runtime·throw("mangled symbol table");
}
((uint32*)func[nfunc-1].ptrs.array)[func[nfunc-1].ptrs.len++] = sym->value;
} else {
runtime·printf("invalid '%c' symbol named '%s'\n", (int8)sym->symtype, sym->name);
runtime·throw("mangled symbol table");
}
break;
......
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