case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
p = p->link;
}
}
return 0;
case ALOCALS: /* funny */
+ case ANPTRS:
+ case APTRS:
return 0;
}
}
case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
continue;
}
r = rega();
AUSEFIELD,
ALOCALS,
ATYPE,
+ ANPTRS,
+ APTRS,
ALAST,
};
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;
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();
s->text = p;
s->value = pc;
s->args = p->to.offset2;
+ s->nptrs = -1;
lastp = p;
p->pc = pc;
pc++;
case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
p = p->link;
}
}
case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
continue;
}
r = rega();
AUSEFIELD,
ALOCALS,
ATYPE,
+ ANPTRS,
+ APTRS,
ALAST
};
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
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) {
s->type = STEXT;
s->value = pc;
s->args = p->to.offset >> 32;
+ s->nptrs = -1;
lastp = p;
p->pc = pc++;
goto loop;
{ AUSEFIELD, ynop, Px, 0,0 },
{ ALOCALS },
{ ATYPE },
+ { ANPTRS },
+ { APTRS },
{ AEND },
0
case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
p = p->link;
}
}
case ASIGNAME:
case ALOCALS:
case ATYPE:
+ case ANPTRS:
+ case APTRS:
continue;
}
r = rega();
AUSEFIELD,
ALOCALS,
ATYPE,
+ ANPTRS,
+ APTRS,
ALAST
};
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
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) {
s->type = STEXT;
s->value = pc;
s->args = p->to.offset2;
+ s->nptrs = -1;
lastp = p;
p->pc = pc++;
goto loop;
{ AUSEFIELD, ynop, Px, 0,0 },
{ ALOCALS },
{ ATYPE },
+ { ANPTRS },
+ { APTRS },
0
};
--- /dev/null
+// 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;
+}
} u;
};
+typedef struct Bvec Bvec;
typedef struct Pkg Pkg;
typedef struct Sym Sym;
typedef struct Node Node;
EXTERN Bits zbits;
+struct Bvec
+{
+ int32 n; // number of bits
+ uint32 b[];
+};
+
typedef struct Var Var;
struct Var
{
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
*/
#include "opt.h"
static void allocauto(Prog* p);
+static void pointermap(Node* fn);
void
compile(Node *fn)
}
}
+ pointermap(fn);
+
genlist(curfn->enter);
retpc = nil;
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.
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.
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.
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
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);
}
return;
}
}
- if (ScanStackByFrames) {
+ if(ScanStackByFrames) {
USED(stk);
USED(guard);
doframe = false;
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
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
dofunc(Sym *sym)
{
Func *f;
+ uintgo cap;
switch(sym->symtype) {
case 't':
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;