summaryrefslogtreecommitdiff
path: root/src/cmd/cc
diff options
context:
space:
mode:
authorKeith Randall <khr@golang.org>2013-07-24 09:41:06 -0700
committerKeith Randall <khr@golang.org>2013-07-24 09:41:06 -0700
commit879319251b98a0f6036afb1e3758c5f2daca1344 (patch)
tree9b16d3b2a761d898a20c01e7a1aa9202790deed5 /src/cmd/cc
parent4a4ef2f888d79ba33e7759548c788e0aaf2e0d64 (diff)
downloadgo-879319251b98a0f6036afb1e3758c5f2daca1344.tar.gz
cc: generate argument pointer maps for C functions.
R=golang-dev, rsc CC=golang-dev https://codereview.appspot.com/11683043
Diffstat (limited to 'src/cmd/cc')
-rw-r--r--src/cmd/cc/pgen.c135
1 files changed, 121 insertions, 14 deletions
diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c
index 27022d54e..65a6cbea3 100644
--- a/src/cmd/cc/pgen.c
+++ b/src/cmd/cc/pgen.c
@@ -31,6 +31,8 @@
#include "gc.h"
#include "../../pkg/runtime/funcdata.h"
+static int32 pointermap(Sym *gcsym, int32 offset);
+
int
hasdotdotdot(void)
{
@@ -101,7 +103,22 @@ codgen(Node *n, Node *nn)
p = gtext(n1->sym, stkoff);
sp = p;
-
+
+ /*
+ * generate funcdata symbol for this function.
+ * data is filled in at the end of codgen().
+ */
+ snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++);
+ gcsym = slookup(namebuf);
+ gcsym->class = CSTATIC;
+
+ memset(&nod, 0, sizeof nod);
+ nod.op = ONAME;
+ nod.sym = gcsym;
+ nod.class = CSTATIC;
+
+ gins(AFUNCDATA, nodconst(FUNCDATA_GC), &nod);
+
/*
* isolate first argument
*/
@@ -139,17 +156,6 @@ codgen(Node *n, Node *nn)
maxargsafe = xround(maxargsafe, 8);
sp->to.offset += maxargsafe;
- snprint(namebuf, sizeof namebuf, "gc·%d", ngcsym++);
- gcsym = slookup(namebuf);
- gcsym->class = CSTATIC;
-
- memset(&nod, 0, sizeof nod);
- nod.op = ONAME;
- nod.sym = gcsym;
- nod.class = CSTATIC;
-
- gins(AFUNCDATA, nodconst(FUNCDATA_GC), &nod);
-
// 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
@@ -162,8 +168,7 @@ codgen(Node *n, Node *nn)
off = 0;
gextern(gcsym, nodconst(stkoff), off, 4); // locals
off += 4;
- gextern(gcsym, nodconst(0), off, 4); // nptrs
- off += 4;
+ off = pointermap(gcsym, off); // nptrs and ptrs[...]
gcsym->type = typ(0, T);
gcsym->type->width = off;
}
@@ -633,3 +638,105 @@ bcomplex(Node *n, Node *c)
boolgen(n, 1, Z);
return 0;
}
+
+// Makes a bitmap marking the the pointers in t. t starts at the given byte
+// offset in the argument list. The returned bitmap should be for pointer
+// indexes (relative to offset 0) between baseidx and baseidx+32.
+static int32
+pointermap_type(Type *t, int32 offset, int32 baseidx)
+{
+ Type *t1;
+ int32 idx;
+ int32 m;
+
+ 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
+ return 0;
+ case TIND:
+ case TARRAY: // unlike Go, C passes arrays by reference
+ // pointer types
+ if((offset + t->offset) % ewidth[TIND] != 0)
+ yyerror("unaligned pointer");
+ idx = (offset + t->offset) / ewidth[TIND];
+ if(idx >= baseidx && idx < baseidx + 32)
+ return 1 << (idx - baseidx);
+ return 0;
+ case TSTRUCT:
+ // build map recursively
+ m = 0;
+ for(t1=t->link; t1; t1=t1->down)
+ m |= pointermap_type(t1, offset, baseidx);
+ return m;
+ case TUNION:
+ // We require that all elements of the union have the same pointer map.
+ m = pointermap_type(t->link, offset, baseidx);
+ for(t1=t->link->down; t1; t1=t1->down) {
+ if(pointermap_type(t1, offset, baseidx) != m)
+ yyerror("invalid union in argument list - pointer maps differ");
+ }
+ return m;
+ default:
+ yyerror("can't handle arg type %s\n", tnames[t->etype]);
+ return 0;
+ }
+}
+
+// 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 int32
+pointermap(Sym *gcsym, int32 off)
+{
+ int32 nptrs;
+ int32 i;
+ int32 s; // offset in argument list (in bytes)
+ int32 m; // current ptrs[i/32]
+ Type *t;
+
+ if(hasdotdotdot()) {
+ // give up for C vararg functions.
+ // TODO: maybe make a map just for the args we do know?
+ gextern(gcsym, nodconst(0), off, 4); // nptrs=0
+ return off + 4;
+ }
+ nptrs = (argsize() + ewidth[TIND] - 1) / ewidth[TIND];
+ gextern(gcsym, nodconst(nptrs), off, 4);
+ off += 4;
+
+ for(i = 0; i < nptrs; i += 32) {
+ // generate mask for ptrs at offsets i ... i+31
+ m = 0;
+ s = align(0, thisfn->link, Aarg0, nil);
+ if(s > 0 && i == 0) {
+ // 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(s != ewidth[TIND])
+ yyerror("passbyptr arg not the right size");
+ m = 1;
+ }
+ for(t=thisfn->down; t!=T; t=t->down) {
+ if(t->etype == TVOID)
+ continue;
+ s = align(s, t, Aarg1, nil);
+ m |= pointermap_type(t, s, i);
+ s = align(s, t, Aarg2, nil);
+ }
+ gextern(gcsym, nodconst(m), off, 4);
+ off += 4;
+ }
+ return off;
+ // TODO: needs a test for nptrs>32
+}