diff options
author | Austin Clements <austin@google.com> | 2014-10-22 11:21:16 -0400 |
---|---|---|
committer | Austin Clements <austin@google.com> | 2014-10-22 11:21:16 -0400 |
commit | 2c586384223e980fd3303625ea6b02ed5d9fb9c0 (patch) | |
tree | 54aa72cd9d5eb1566f682d3b0770141a11b54fc2 /src/cmd | |
parent | ebe8a09fb603d9510cb982a6c11a3e1638f7f8fb (diff) | |
parent | 45fcda1dc8d9b4d4a9b642faf8e78941873f508d (diff) | |
download | go-2c586384223e980fd3303625ea6b02ed5d9fb9c0.tar.gz |
[dev.power64] build: merge default into dev.power64
LGTM=rsc
R=rsc
CC=golang-codereviews
https://codereview.appspot.com/160200044
Diffstat (limited to 'src/cmd')
193 files changed, 2765 insertions, 2132 deletions
diff --git a/src/cmd/5c/cgen.c b/src/cmd/5c/cgen.c index 08ed36055..5ea8eea07 100644 --- a/src/cmd/5c/cgen.c +++ b/src/cmd/5c/cgen.c @@ -46,7 +46,7 @@ _cgen(Node *n, Node *nn, int inrel) } if(n == Z || n->type == T) return; - if(typesuv[n->type->etype]) { + if(typesuv[n->type->etype] && (n->op != OFUNC || nn != Z)) { sugen(n, nn, n->type->width); return; } @@ -75,7 +75,7 @@ _cgen(Node *n, Node *nn, int inrel) if(r != Z && r->complex >= FNX) switch(o) { default: - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); @@ -107,7 +107,7 @@ _cgen(Node *n, Node *nn, int inrel) if(l->addable >= INDEXED && l->complex < FNX) { if(nn != Z || r->addable < INDEXED) { if(r->complex >= FNX && nn == Z) - regret(&nod, r); + regret(&nod, r, 0, 0); else regalloc(&nod, r, nn); cgen(r, &nod); @@ -348,7 +348,7 @@ _cgen(Node *n, Node *nn, int inrel) if(l->op != OIND) diag(n, "bad function call"); - regret(&nod, l->left); + regret(&nod, l->left, 0, 0); cgen(l->left, &nod); regsalloc(&nod1, l->left); gopcode(OAS, &nod, Z, &nod1); @@ -377,11 +377,11 @@ _cgen(Node *n, Node *nn, int inrel) if(REGARG >= 0) if(o != reg[REGARG]) reg[REGARG]--; - if(nn != Z) { - regret(&nod, n); - gopcode(OAS, &nod, Z, nn); + regret(&nod, n, l->type, 1); + if(nn != Z) + gmove(&nod, nn); + if(nod.op == OREGISTER) regfree(&nod); - } break; case OIND: @@ -823,7 +823,7 @@ boolgen(Node *n, int true, Node *nn) if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { - regret(&nod, r); + regret(&nod, r, 0, 0); cgenrel(r, &nod); regsalloc(&nod1, r); gopcode(OAS, &nod, Z, &nod1); @@ -957,7 +957,7 @@ sugen(Node *n, Node *nn, int32 w) if(nn != Z && side(nn)) { nod1 = *n; nod1.type = typ(TIND, n->type); - regret(&nod2, &nod1); + regret(&nod2, &nod1, 0, 0); lcgen(nn, &nod2); regsalloc(&nod0, &nod1); gopcode(OAS, &nod2, Z, &nod0); @@ -1036,6 +1036,20 @@ sugen(Node *n, Node *nn, int32 w) break; case OFUNC: + if(!hasdotdotdot(n->left->type)) { + cgen(n, Z); + if(nn != Z) { + curarg -= n->type->width; + regret(&nod1, n, n->left->type, 1); + if(nn->complex >= FNX) { + regsalloc(&nod2, n); + cgen(&nod1, &nod2); + nod1 = nod2; + } + cgen(&nod1, nn); + } + break; + } if(nn == Z) { sugen(n, nodrat, w); break; diff --git a/src/cmd/5c/gc.h b/src/cmd/5c/gc.h index 166900c3a..7417b7dbe 100644 --- a/src/cmd/5c/gc.h +++ b/src/cmd/5c/gc.h @@ -210,7 +210,7 @@ void usedset(Node*, int); void xcom(Node*); int bcomplex(Node*, Node*); Prog* gtext(Sym*, int32); -vlong argsize(void); +vlong argsize(int); /* * cgen.c @@ -236,7 +236,7 @@ Node* nodconst(int32); Node* nod32const(vlong); Node* nodfconst(double); void nodreg(Node*, Node*, int); -void regret(Node*, Node*); +void regret(Node*, Node*, Type*, int); int tmpreg(void); void regalloc(Node*, Node*, Node*); void regfree(Node*); diff --git a/src/cmd/5c/sgen.c b/src/cmd/5c/sgen.c index efcc0437b..a36612caa 100644 --- a/src/cmd/5c/sgen.c +++ b/src/cmd/5c/sgen.c @@ -36,7 +36,7 @@ gtext(Sym *s, int32 stkoff) { int32 a; - a = argsize(); + a = argsize(1); if((textflag & NOSPLIT) != 0 && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); diff --git a/src/cmd/5c/swt.c b/src/cmd/5c/swt.c index d24a5df9b..f39963b8f 100644 --- a/src/cmd/5c/swt.c +++ b/src/cmd/5c/swt.c @@ -374,10 +374,11 @@ align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; - int w; + int w, packw; o = i; w = 1; + packw = 0; switch(op) { default: diag(Z, "unknown align opcode %d", op); @@ -388,7 +389,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1) w = 1; if(packflg) - w = packflg; + packw = packflg; break; case Ael1: /* initial align of struct element */ @@ -404,7 +405,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1 || w > SZ_LONG) fatal(Z, "align"); if(packflg) - w = packflg; + packw = packflg; break; case Ael2: /* width of a struct element */ @@ -440,6 +441,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) w = SZ_LONG; /* because of a pun in cc/dcl.c:contig() */ break; } + if(packw != 0 && xround(o, w) != xround(o, packw)) + diag(Z, "#pragma pack changes offset of %T", t); o = xround(o, w); if(maxalign != nil && *maxalign < w) *maxalign = w; diff --git a/src/cmd/5c/txt.c b/src/cmd/5c/txt.c index a753510ca..af40220cc 100644 --- a/src/cmd/5c/txt.c +++ b/src/cmd/5c/txt.c @@ -274,15 +274,43 @@ nodreg(Node *n, Node *nn, int reg) } void -regret(Node *n, Node *nn) +regret(Node *n, Node *nn, Type *t, int mode) { int r; - r = REGRET; - if(typefd[nn->type->etype]) - r = FREGRET+NREG; - nodreg(n, nn, r); - reg[r]++; + if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { + r = REGRET; + if(typefd[nn->type->etype]) + r = FREGRET+NREG; + nodreg(n, nn, r); + reg[r]++; + return; + } + + if(mode == 1) { + // fetch returned value after call. + // already called gargs, so curarg is set. + curarg = (curarg+3) & ~3; + regaalloc(n, nn); + return; + } + + if(mode == 2) { + // store value to be returned. + // must compute arg offset. + if(t->etype != TFUNC) + fatal(Z, "bad regret func %T", t); + *n = *nn; + n->op = ONAME; + n->class = CPARAM; + n->sym = slookup(".ret"); + n->complex = nodret->complex; + n->xoffset = argsize(0); + n->addable = 20; + return; + } + + fatal(Z, "bad regret"); } int diff --git a/src/cmd/5g/cgen.c b/src/cmd/5g/cgen.c index a42e67baf..5fae7a564 100644 --- a/src/cmd/5g/cgen.c +++ b/src/cmd/5g/cgen.c @@ -254,7 +254,6 @@ cgen(Node *n, Node *res) case OOR: case OXOR: case OADD: - case OADDPTR: case OMUL: a = optoas(n->op, nl->type); goto sbop; @@ -1635,7 +1634,10 @@ int componentgen(Node *nr, Node *nl) { Node nodl, nodr, tmp; + Type *t; int freel, freer; + vlong fldcount; + vlong loffset, roffset; freel = 0; freer = 0; @@ -1645,8 +1647,33 @@ componentgen(Node *nr, Node *nl) goto no; case TARRAY: - if(!isslice(nl->type)) + t = nl->type; + + // Slices are ok. + if(isslice(t)) + break; + // Small arrays are ok. + if(t->bound > 0 && t->bound <= 3 && !isfat(t->type)) + break; + + goto no; + + case TSTRUCT: + // Small structs with non-fat types are ok. + // Zero-sized structs are treated separately elsewhere. + fldcount = 0; + for(t=nl->type->type; t; t=t->down) { + if(isfat(t->type)) + goto no; + if(t->etype != TFIELD) + fatal("componentgen: not a TFIELD: %lT", t); + fldcount++; + } + if(fldcount == 0 || fldcount > 4) goto no; + + break; + case TSTRING: case TINTER: break; @@ -1674,6 +1701,7 @@ componentgen(Node *nr, Node *nl) freer = 1; } + // nl and nr are 'cadable' which basically means they are names (variables) now. // If they are the same variable, don't generate any code, because the // VARDEF we generate will mark the old value as dead incorrectly. @@ -1683,8 +1711,25 @@ componentgen(Node *nr, Node *nl) switch(nl->type->etype) { case TARRAY: + // componentgen for arrays. if(nl->op == ONAME) gvardef(nl); + t = nl->type; + if(!isslice(t)) { + nodl.type = t->type; + nodr.type = nodl.type; + for(fldcount=0; fldcount < t->bound; fldcount++) { + if(nr == N) + clearslim(&nodl); + else + gmove(&nodr, &nodl); + nodl.xoffset += t->type->width; + nodr.xoffset += t->type->width; + } + goto yes; + } + + // componentgen for slices. nodl.xoffset += Array_array; nodl.type = ptrto(nl->type->type); @@ -1759,6 +1804,31 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); goto yes; + + case TSTRUCT: + if(nl->op == ONAME) + gvardef(nl); + loffset = nodl.xoffset; + roffset = nodr.xoffset; + // funarg structs may not begin at offset zero. + if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type) + loffset -= nl->type->type->width; + if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type) + roffset -= nr->type->type->width; + + for(t=nl->type->type; t; t=t->down) { + nodl.xoffset = loffset + t->width; + nodl.type = t->type; + + if(nr == N) + clearslim(&nodl); + else { + nodr.xoffset = roffset + t->width; + nodr.type = nodl.type; + gmove(&nodr, &nodl); + } + } + goto yes; } no: diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 1241a23ea..5a70fcdda 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -1599,7 +1599,6 @@ optoas(int op, Type *t) case CASE(OADD, TINT32): case CASE(OADD, TUINT32): case CASE(OADD, TPTR32): - case CASE(OADDPTR, TPTR32): a = AADD; break; diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index d259a232e..8e49a2d9c 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -1315,6 +1315,7 @@ void addreg(Adr *a, int rn) { a->sym = nil; + a->node = nil; a->name = D_NONE; a->type = D_REG; a->reg = rn; diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 02b4c78f1..9c1c04e2d 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -599,10 +599,10 @@ asmb(void) if(iself) goto ElfSym; case Hplan9: - symo = HEADR+segtext.len+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; break; ElfSym: - symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, INITRND); break; } diff --git a/src/cmd/6a/lex.c b/src/cmd/6a/lex.c index 167e6b6c1..b50e1622e 100644 --- a/src/cmd/6a/lex.c +++ b/src/cmd/6a/lex.c @@ -435,49 +435,49 @@ struct "IRETQ", LTYPE0, AIRETQ, "IRETW", LTYPE0, AIRETW, - "JOS", LTYPER, AJOS, + "JOS", LTYPER, AJOS, /* overflow set (OF = 1) */ "JO", LTYPER, AJOS, /* alternate */ - "JOC", LTYPER, AJOC, + "JOC", LTYPER, AJOC, /* overflow clear (OF = 0) */ "JNO", LTYPER, AJOC, /* alternate */ - "JCS", LTYPER, AJCS, + "JCS", LTYPER, AJCS, /* carry set (CF = 1) */ "JB", LTYPER, AJCS, /* alternate */ "JC", LTYPER, AJCS, /* alternate */ "JNAE", LTYPER, AJCS, /* alternate */ "JLO", LTYPER, AJCS, /* alternate */ - "JCC", LTYPER, AJCC, + "JCC", LTYPER, AJCC, /* carry clear (CF = 0) */ "JAE", LTYPER, AJCC, /* alternate */ "JNB", LTYPER, AJCC, /* alternate */ "JNC", LTYPER, AJCC, /* alternate */ "JHS", LTYPER, AJCC, /* alternate */ - "JEQ", LTYPER, AJEQ, + "JEQ", LTYPER, AJEQ, /* equal (ZF = 1) */ "JE", LTYPER, AJEQ, /* alternate */ "JZ", LTYPER, AJEQ, /* alternate */ - "JNE", LTYPER, AJNE, + "JNE", LTYPER, AJNE, /* not equal (ZF = 0) */ "JNZ", LTYPER, AJNE, /* alternate */ - "JLS", LTYPER, AJLS, + "JLS", LTYPER, AJLS, /* lower or same (unsigned) (CF = 1 || ZF = 1) */ "JBE", LTYPER, AJLS, /* alternate */ "JNA", LTYPER, AJLS, /* alternate */ - "JHI", LTYPER, AJHI, + "JHI", LTYPER, AJHI, /* higher (unsigned) (CF = 0 && ZF = 0) */ "JA", LTYPER, AJHI, /* alternate */ "JNBE", LTYPER, AJHI, /* alternate */ - "JMI", LTYPER, AJMI, + "JMI", LTYPER, AJMI, /* negative (minus) (SF = 1) */ "JS", LTYPER, AJMI, /* alternate */ - "JPL", LTYPER, AJPL, + "JPL", LTYPER, AJPL, /* non-negative (plus) (SF = 0) */ "JNS", LTYPER, AJPL, /* alternate */ - "JPS", LTYPER, AJPS, + "JPS", LTYPER, AJPS, /* parity set (PF = 1) */ "JP", LTYPER, AJPS, /* alternate */ "JPE", LTYPER, AJPS, /* alternate */ - "JPC", LTYPER, AJPC, + "JPC", LTYPER, AJPC, /* parity clear (PF = 0) */ "JNP", LTYPER, AJPC, /* alternate */ "JPO", LTYPER, AJPC, /* alternate */ - "JLT", LTYPER, AJLT, + "JLT", LTYPER, AJLT, /* less than (signed) (SF != OF) */ "JL", LTYPER, AJLT, /* alternate */ "JNGE", LTYPER, AJLT, /* alternate */ - "JGE", LTYPER, AJGE, + "JGE", LTYPER, AJGE, /* greater than or equal (signed) (SF = OF) */ "JNL", LTYPER, AJGE, /* alternate */ - "JLE", LTYPER, AJLE, + "JLE", LTYPER, AJLE, /* less than or equal (signed) (ZF = 1 || SF != OF) */ "JNG", LTYPER, AJLE, /* alternate */ - "JGT", LTYPER, AJGT, + "JGT", LTYPER, AJGT, /* greater than (signed) (ZF = 0 && SF = OF) */ "JG", LTYPER, AJGT, /* alternate */ "JNLE", LTYPER, AJGT, /* alternate */ "JCXZL", LTYPER, AJCXZL, @@ -612,7 +612,7 @@ struct "SCASL", LTYPE0, ASCASL, "SCASQ", LTYPE0, ASCASQ, "SCASW", LTYPE0, ASCASW, - "SETCC", LTYPE1, ASETCC, + "SETCC", LTYPE1, ASETCC, /* see JCC etc above for condition codes */ "SETCS", LTYPE1, ASETCS, "SETEQ", LTYPE1, ASETEQ, "SETGE", LTYPE1, ASETGE, diff --git a/src/cmd/6c/cgen.c b/src/cmd/6c/cgen.c index bdef76ff0..b66c6add3 100644 --- a/src/cmd/6c/cgen.c +++ b/src/cmd/6c/cgen.c @@ -51,7 +51,7 @@ cgen(Node *n, Node *nn) } if(n == Z || n->type == T) return; - if(typesu[n->type->etype]) { + if(typesu[n->type->etype] && (n->op != OFUNC || nn != Z)) { sugen(n, nn, n->type->width); return; } @@ -88,7 +88,7 @@ cgen(Node *n, Node *nn) if(cond(o) && typesu[l->type->etype]) break; - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); @@ -135,7 +135,7 @@ cgen(Node *n, Node *nn) if(!hardleft) { if(nn != Z || r->addable < INDEXED || hardconst(r)) { if(r->complex >= FNX && nn == Z) - regret(&nod, r); + regret(&nod, r, 0, 0); else regalloc(&nod, r, nn); cgen(r, &nod); @@ -929,7 +929,7 @@ cgen(Node *n, Node *nn) if(l->op != OIND) diag(n, "bad function call"); - regret(&nod, l->left); + regret(&nod, l->left, 0, 0); cgen(l->left, &nod); regsalloc(&nod1, l->left); gmove(&nod, &nod1); @@ -956,11 +956,13 @@ cgen(Node *n, Node *nn) gpcdata(PCDATA_ArgSize, -1); if(REGARG >= 0 && reg[REGARG]) reg[REGARG]--; - if(nn != Z) { - regret(&nod, n); + regret(&nod, n, l->type, 1); // update maxarg if nothing else + gpcdata(PCDATA_ArgSize, curarg); + gpcdata(PCDATA_ArgSize, -1); + if(nn != Z) gmove(&nod, nn); + if(nod.op == OREGISTER) regfree(&nod); - } break; case OIND: @@ -1382,7 +1384,7 @@ boolgen(Node *n, int true, Node *nn) if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); gmove(&nod, &nod1); @@ -1535,7 +1537,7 @@ sugen(Node *n, Node *nn, int32 w) if(nn != Z && side(nn)) { nod1 = *n; nod1.type = typ(TIND, n->type); - regret(&nod2, &nod1); + regret(&nod2, &nod1, 0, 0); lcgen(nn, &nod2); regsalloc(&nod0, &nod1); cgen(&nod2, &nod0); @@ -1617,6 +1619,20 @@ sugen(Node *n, Node *nn, int32 w) break; case OFUNC: + if(!hasdotdotdot(n->left->type)) { + cgen(n, Z); + if(nn != Z) { + curarg -= n->type->width; + regret(&nod1, n, n->left->type, 1); + if(nn->complex >= FNX) { + regsalloc(&nod2, n); + cgen(&nod1, &nod2); + nod1 = nod2; + } + cgen(&nod1, nn); + } + break; + } if(nn == Z) { sugen(n, nodrat, w); break; diff --git a/src/cmd/6c/gc.h b/src/cmd/6c/gc.h index bc4e36ccd..aa9d95d21 100644 --- a/src/cmd/6c/gc.h +++ b/src/cmd/6c/gc.h @@ -210,7 +210,7 @@ void xcom(Node*); void indx(Node*); int bcomplex(Node*, Node*); Prog* gtext(Sym*, int32); -vlong argsize(void); +vlong argsize(int); /* * cgen.c @@ -239,7 +239,7 @@ Node* nodfconst(double); Node* nodgconst(vlong, Type*); int nodreg(Node*, Node*, int); int isreg(Node*, int); -void regret(Node*, Node*); +void regret(Node*, Node*, Type*, int); void regalloc(Node*, Node*, Node*); void regfree(Node*); void regialloc(Node*, Node*, Node*); diff --git a/src/cmd/6c/sgen.c b/src/cmd/6c/sgen.c index ba1c1f652..d99510185 100644 --- a/src/cmd/6c/sgen.c +++ b/src/cmd/6c/sgen.c @@ -36,7 +36,7 @@ gtext(Sym *s, int32 stkoff) { vlong v; - v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff); + v = ((uvlong)argsize(1) << 32) | (stkoff & 0xffffffff); if((textflag & NOSPLIT) && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); @@ -124,10 +124,7 @@ xcom(Node *n) break; case ONAME: - if(flag_largemodel) - n->addable = 9; - else - n->addable = 10; + n->addable = 9; if(n->class == CPARAM || n->class == CAUTO) n->addable = 11; break; diff --git a/src/cmd/6c/swt.c b/src/cmd/6c/swt.c index d7713648d..6e918eb10 100644 --- a/src/cmd/6c/swt.c +++ b/src/cmd/6c/swt.c @@ -250,10 +250,11 @@ align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; - int w; + int w, packw; o = i; w = 1; + packw = 0; switch(op) { default: diag(Z, "unknown align opcode %d", op); @@ -264,7 +265,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1) w = 1; if(packflg) - w = packflg; + packw = packflg; break; case Ael1: /* initial align of struct element */ @@ -277,7 +278,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1 || w > SZ_VLONG) fatal(Z, "align"); if(packflg) - w = packflg; + packw = packflg; break; case Ael2: /* width of a struct element */ @@ -331,6 +332,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) o = align(o, t, Ael2, nil); break; } + if(packw != 0 && xround(o, w) != xround(o, packw)) + diag(Z, "#pragma pack changes offset of %T", t); o = xround(o, w); if(maxalign && *maxalign < w) *maxalign = w; diff --git a/src/cmd/6c/txt.c b/src/cmd/6c/txt.c index 4d07436c3..3bdbf410e 100644 --- a/src/cmd/6c/txt.c +++ b/src/cmd/6c/txt.c @@ -351,15 +351,43 @@ nodreg(Node *n, Node *nn, int r) } void -regret(Node *n, Node *nn) +regret(Node *n, Node *nn, Type *t, int mode) { int r; + + if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { + r = REGRET; + if(typefd[nn->type->etype]) + r = FREGRET; + nodreg(n, nn, r); + reg[r]++; + return; + } + + if(mode == 1) { + // fetch returned value after call. + // already called gargs, so curarg is set. + curarg = (curarg+7) & ~7; + regaalloc(n, nn); + return; + } - r = REGRET; - if(typefd[nn->type->etype]) - r = FREGRET; - nodreg(n, nn, r); - reg[r]++; + if(mode == 2) { + // store value to be returned. + // must compute arg offset. + if(t->etype != TFUNC) + fatal(Z, "bad regret func %T", t); + *n = *nn; + n->op = ONAME; + n->class = CPARAM; + n->sym = slookup(".ret"); + n->complex = nodret->complex; + n->addable = 20; + n->xoffset = argsize(0); + return; + } + + fatal(Z, "bad regret"); } void diff --git a/src/cmd/6g/cgen.c b/src/cmd/6g/cgen.c index 4dd505b08..592e81542 100644 --- a/src/cmd/6g/cgen.c +++ b/src/cmd/6g/cgen.c @@ -247,7 +247,6 @@ cgen(Node *n, Node *res) case OOR: case OXOR: case OADD: - case OADDPTR: case OMUL: a = optoas(n->op, nl->type); if(a == AIMULB) { @@ -752,12 +751,7 @@ agenr(Node *n, Node *a, Node *res) regalloc(&n3, types[tptr], res); p1 = gins(ALEAQ, N, &n3); datastring(nl->val.u.sval->s, nl->val.u.sval->len, &p1->from); - if(flag_largemodel) { - gins(AADDQ, &n2, &n3); - } else { - p1->from.scale = 1; - p1->from.index = n2.val.u.reg; - } + gins(AADDQ, &n2, &n3); goto indexdone; } diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index a5da17d61..27f6c01fe 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -51,7 +51,6 @@ void allocparams(void); void checklabels(void); void ginscall(Node*, int); int gen_as_init(Node*); -void clearslim(Node*); /* * cgen.c diff --git a/src/cmd/6g/ggen.c b/src/cmd/6g/ggen.c index c385798f2..9665d831b 100644 --- a/src/cmd/6g/ggen.c +++ b/src/cmd/6g/ggen.c @@ -943,7 +943,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res) if(t->width == 1) { // byte multiply behaves differently. nodreg(&ax, t, D_AH); - nodreg(&dx, t, D_DL); + nodreg(&dx, t, D_DX); gmove(&ax, &dx); } nodreg(&dx, t, D_DX); diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index f3464b7e1..a451d7d62 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -598,11 +598,8 @@ ismem(Node *n) case ONAME: case OPARAM: case OCLOSUREVAR: - return 1; case OADDR: - if(flag_largemodel) - return 1; - break; + return 1; } return 0; } @@ -1540,14 +1537,12 @@ optoas(int op, Type *t) case CASE(OADD, TINT32): case CASE(OADD, TUINT32): case CASE(OADD, TPTR32): - case CASE(OADDPTR, TPTR32): a = AADDL; break; case CASE(OADD, TINT64): case CASE(OADD, TUINT64): case CASE(OADD, TPTR64): - case CASE(OADDPTR, TPTR64): a = AADDQ; break; diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index 0f2720443..24617836f 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -838,6 +838,11 @@ copyu(Prog *p, Adr *v, Adr *s) static int copyas(Adr *a, Adr *v) { + if(D_AL <= a->type && a->type <= D_R15B) + fatal("use of byte register"); + if(D_AL <= v->type && v->type <= D_R15B) + fatal("use of byte register"); + if(a->type != v->type) return 0; if(regtyp(v)) diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index 02945fb40..1f757e197 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -844,7 +844,7 @@ prop(Reg *r, Bits ref, Bits cal) if(v == v1 || ((cal.b[j/32]>>(j&31))&1) == 0) { for(; v1 != nil; v1 = v1->nextinnode) { j = v1 - var; - cal.b[j/32] |= 1<<(j&31); + cal.b[j/32] |= 1UL<<(j&31); } } } @@ -1191,6 +1191,7 @@ void addreg(Adr *a, int rn) { a->sym = nil; + a->node = nil; a->offset = 0; a->type = rn; diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index e251e32ca..18b5aa311 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -290,7 +290,6 @@ elfreloc1(Reloc *r, vlong sectoff) break; case R_CALL: - case R_PCREL: if(r->siz == 4) { if(r->xsym->type == SDYNIMPORT) VPUT(R_X86_64_GOTPCREL | (uint64)elfsym<<32); @@ -299,7 +298,14 @@ elfreloc1(Reloc *r, vlong sectoff) } else return -1; break; - + + case R_PCREL: + if(r->siz == 4) { + VPUT(R_X86_64_PC32 | (uint64)elfsym<<32); + } else + return -1; + break; + case R_TLS: if(r->siz == 4) { if(flag_shared) @@ -323,7 +329,7 @@ machoreloc1(Reloc *r, vlong sectoff) rs = r->xsym; - if(rs->type == SHOSTOBJ) { + if(rs->type == SHOSTOBJ || r->type == R_PCREL) { if(rs->dynid < 0) { diag("reloc %d to non-macho symbol %s type=%d", r->type, rs->name, rs->type); return -1; @@ -345,10 +351,13 @@ machoreloc1(Reloc *r, vlong sectoff) v |= MACHO_X86_64_RELOC_UNSIGNED<<28; break; case R_CALL: - case R_PCREL: v |= 1<<24; // pc-relative bit v |= MACHO_X86_64_RELOC_BRANCH<<28; break; + case R_PCREL: + // NOTE: Only works with 'external' relocation. Forced above. + v |= 1<<24; // pc-relative bit + v |= MACHO_X86_64_RELOC_SIGNED<<28; } switch(r->siz) { @@ -689,10 +698,10 @@ asmb(void) case Hplan9: case Helf: debug['s'] = 1; - symo = HEADR+segtext.len+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; break; case Hdarwin: - symo = rnd(HEADR+segtext.len, INITRND)+rnd(segdata.filelen, INITRND)+machlink; + symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink; break; case Hlinux: case Hfreebsd: @@ -701,11 +710,11 @@ asmb(void) case Hdragonfly: case Hsolaris: case Hnacl: - symo = rnd(HEADR+segtext.len, INITRND)+rnd(segrodata.len, INITRND)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, INITRND); break; case Hwindows: - symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, PEFILEALIGN); break; } diff --git a/src/cmd/8a/lex.c b/src/cmd/8a/lex.c index 32c099b75..807e48cb5 100644 --- a/src/cmd/8a/lex.c +++ b/src/cmd/8a/lex.c @@ -352,49 +352,49 @@ struct "IRETL", LTYPE0, AIRETL, "IRETW", LTYPE0, AIRETW, - "JOS", LTYPER, AJOS, + "JOS", LTYPER, AJOS, /* overflow set (OF = 1) */ "JO", LTYPER, AJOS, /* alternate */ - "JOC", LTYPER, AJOC, + "JOC", LTYPER, AJOC, /* overflow clear (OF = 0) */ "JNO", LTYPER, AJOC, /* alternate */ - "JCS", LTYPER, AJCS, + "JCS", LTYPER, AJCS, /* carry set (CF = 1) */ "JB", LTYPER, AJCS, /* alternate */ "JC", LTYPER, AJCS, /* alternate */ "JNAE", LTYPER, AJCS, /* alternate */ "JLO", LTYPER, AJCS, /* alternate */ - "JCC", LTYPER, AJCC, + "JCC", LTYPER, AJCC, /* carry clear (CF = 0) */ "JAE", LTYPER, AJCC, /* alternate */ "JNB", LTYPER, AJCC, /* alternate */ "JNC", LTYPER, AJCC, /* alternate */ "JHS", LTYPER, AJCC, /* alternate */ - "JEQ", LTYPER, AJEQ, + "JEQ", LTYPER, AJEQ, /* equal (ZF = 1) */ "JE", LTYPER, AJEQ, /* alternate */ "JZ", LTYPER, AJEQ, /* alternate */ - "JNE", LTYPER, AJNE, + "JNE", LTYPER, AJNE, /* not equal (ZF = 0) */ "JNZ", LTYPER, AJNE, /* alternate */ - "JLS", LTYPER, AJLS, + "JLS", LTYPER, AJLS, /* lower or same (unsigned) (CF = 1 || ZF = 1) */ "JBE", LTYPER, AJLS, /* alternate */ "JNA", LTYPER, AJLS, /* alternate */ - "JHI", LTYPER, AJHI, + "JHI", LTYPER, AJHI, /* higher (unsigned) (CF = 0 && ZF = 0) */ "JA", LTYPER, AJHI, /* alternate */ "JNBE", LTYPER, AJHI, /* alternate */ - "JMI", LTYPER, AJMI, + "JMI", LTYPER, AJMI, /* negative (minus) (SF = 1) */ "JS", LTYPER, AJMI, /* alternate */ - "JPL", LTYPER, AJPL, + "JPL", LTYPER, AJPL, /* non-negative (plus) (SF = 0) */ "JNS", LTYPER, AJPL, /* alternate */ - "JPS", LTYPER, AJPS, + "JPS", LTYPER, AJPS, /* parity set (PF = 1) */ "JP", LTYPER, AJPS, /* alternate */ "JPE", LTYPER, AJPS, /* alternate */ - "JPC", LTYPER, AJPC, + "JPC", LTYPER, AJPC, /* parity clear (PF = 0) */ "JNP", LTYPER, AJPC, /* alternate */ "JPO", LTYPER, AJPC, /* alternate */ - "JLT", LTYPER, AJLT, + "JLT", LTYPER, AJLT, /* less than (signed) (SF != OF) */ "JL", LTYPER, AJLT, /* alternate */ "JNGE", LTYPER, AJLT, /* alternate */ - "JGE", LTYPER, AJGE, + "JGE", LTYPER, AJGE, /* greater than or equal (signed) (SF = OF) */ "JNL", LTYPER, AJGE, /* alternate */ - "JLE", LTYPER, AJLE, + "JLE", LTYPER, AJLE, /* less than or equal (signed) (ZF = 1 || SF != OF) */ "JNG", LTYPER, AJLE, /* alternate */ - "JGT", LTYPER, AJGT, + "JGT", LTYPER, AJGT, /* greater than (signed) (ZF = 0 && SF = OF) */ "JG", LTYPER, AJGT, /* alternate */ "JNLE", LTYPER, AJGT, /* alternate */ @@ -493,7 +493,7 @@ struct "SCASB", LTYPE0, ASCASB, "SCASL", LTYPE0, ASCASL, "SCASW", LTYPE0, ASCASW, - "SETCC", LTYPE1, ASETCC, + "SETCC", LTYPE1, ASETCC, /* see JCC etc above for condition codes */ "SETCS", LTYPE1, ASETCS, "SETEQ", LTYPE1, ASETEQ, "SETGE", LTYPE1, ASETGE, diff --git a/src/cmd/8c/cgen.c b/src/cmd/8c/cgen.c index f54102245..8ac8e3600 100644 --- a/src/cmd/8c/cgen.c +++ b/src/cmd/8c/cgen.c @@ -49,7 +49,7 @@ cgen(Node *n, Node *nn) } if(n == Z || n->type == T) return; - if(typesuv[n->type->etype]) { + if(typesuv[n->type->etype] && (n->op != OFUNC || nn != Z)) { sugen(n, nn, n->type->width); return; } @@ -86,7 +86,7 @@ cgen(Node *n, Node *nn) if(cond(o) && typesuv[l->type->etype]) break; - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); @@ -147,7 +147,7 @@ cgen(Node *n, Node *nn) if(!hardleft) { if(nn != Z || r->addable < INDEXED) { if(r->complex >= FNX && nn == Z) - regret(&nod, r); + regret(&nod, r, 0, 0); else regalloc(&nod, r, nn); cgen(r, &nod); @@ -922,7 +922,7 @@ cgen(Node *n, Node *nn) if(l->op != OIND) diag(n, "bad function call"); - regret(&nod, l->left); + regret(&nod, l->left, 0, 0); cgen(l->left, &nod); regsalloc(&nod1, l->left); gmove(&nod, &nod1); @@ -949,12 +949,12 @@ cgen(Node *n, Node *nn) gpcdata(PCDATA_ArgSize, -1); if(REGARG >= 0 && reg[REGARG]) reg[REGARG]--; - if(nn != Z) { - regret(&nod, n); + regret(&nod, n, l->type, 1); // update maxarg if nothing else + if(nn != Z) gmove(&nod, nn); + if(nod.op == OREGISTER) regfree(&nod); - } else - if(typefd[n->type->etype]) + if(nn == Z && hasdotdotdot(l->type) && typefd[n->type->etype]) gins(AFMOVDP, &fregnode0, &fregnode0); break; @@ -1374,7 +1374,7 @@ boolgen(Node *n, int true, Node *nn) if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); gmove(&nod, &nod1); @@ -1567,7 +1567,7 @@ sugen(Node *n, Node *nn, int32 w) if(nn != Z && side(nn)) { nod1 = *n; nod1.type = typ(TIND, n->type); - regret(&nod2, &nod1); + regret(&nod2, &nod1, 0, 0); lcgen(nn, &nod2); regsalloc(&nod0, &nod1); cgen(&nod2, &nod0); @@ -1649,6 +1649,20 @@ sugen(Node *n, Node *nn, int32 w) break; case OFUNC: + if(!hasdotdotdot(n->left->type)) { + cgen(n, Z); + if(nn != Z) { + curarg -= n->type->width; + regret(&nod1, n, n->left->type, 1); + if(nn->complex >= FNX) { + regsalloc(&nod2, n); + cgen(&nod1, &nod2); + nod1 = nod2; + } + cgen(&nod1, nn); + } + break; + } if(nn == Z) { sugen(n, nodrat, w); break; diff --git a/src/cmd/8c/gc.h b/src/cmd/8c/gc.h index 9c4613f56..aa3888d73 100644 --- a/src/cmd/8c/gc.h +++ b/src/cmd/8c/gc.h @@ -210,7 +210,7 @@ void xcom(Node*); void indx(Node*); int bcomplex(Node*, Node*); Prog* gtext(Sym*, int32); -vlong argsize(void); +vlong argsize(int); /* * cgen.c @@ -244,7 +244,7 @@ Node* nodconst(int32); Node* nodfconst(double); int nodreg(Node*, Node*, int); int isreg(Node*, int); -void regret(Node*, Node*); +void regret(Node*, Node*, Type*, int); void regalloc(Node*, Node*, Node*); void regfree(Node*); void regialloc(Node*, Node*, Node*); diff --git a/src/cmd/8c/sgen.c b/src/cmd/8c/sgen.c index 069bbc1fc..d647010ef 100644 --- a/src/cmd/8c/sgen.c +++ b/src/cmd/8c/sgen.c @@ -35,7 +35,7 @@ gtext(Sym *s, int32 stkoff) { int32 a; - a = argsize(); + a = argsize(1); if((textflag & NOSPLIT) != 0 && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); diff --git a/src/cmd/8c/swt.c b/src/cmd/8c/swt.c index ae36f84ea..d960519e3 100644 --- a/src/cmd/8c/swt.c +++ b/src/cmd/8c/swt.c @@ -255,10 +255,11 @@ align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; - int w; + int w, packw; o = i; w = 1; + packw = 0; switch(op) { default: diag(Z, "unknown align opcode %d", op); @@ -269,7 +270,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1) w = 1; if(packflg) - w = packflg; + packw = packflg; break; case Ael1: /* initial align of struct element */ @@ -285,7 +286,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1 || w > SZ_LONG) fatal(Z, "align"); if(packflg) - w = packflg; + packw = packflg; break; case Ael2: /* width of a struct element */ @@ -320,6 +321,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) o = align(o, t, Ael2, nil); break; } + if(packw != 0 && xround(o, w) != xround(o, packw)) + diag(Z, "#pragma pack changes offset of %T", t); o = xround(o, w); if(maxalign && *maxalign < w) *maxalign = w; diff --git a/src/cmd/8c/txt.c b/src/cmd/8c/txt.c index 25082de05..7f87a0a0d 100644 --- a/src/cmd/8c/txt.c +++ b/src/cmd/8c/txt.c @@ -311,15 +311,43 @@ nodreg(Node *n, Node *nn, int r) } void -regret(Node *n, Node *nn) +regret(Node *n, Node *nn, Type *t, int mode) { int r; - r = REGRET; - if(typefd[nn->type->etype]) - r = FREGRET; - nodreg(n, nn, r); - reg[r]++; + if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { + r = REGRET; + if(typefd[nn->type->etype]) + r = FREGRET; + nodreg(n, nn, r); + reg[r]++; + return; + } + + if(mode == 1) { + // fetch returned value after call. + // already called gargs, so curarg is set. + curarg = (curarg+3) & ~3; + regaalloc(n, nn); + return; + } + + if(mode == 2) { + // store value to be returned. + // must compute arg offset. + if(t->etype != TFUNC) + fatal(Z, "bad regret func %T", t); + *n = *nn; + n->op = ONAME; + n->class = CPARAM; + n->sym = slookup(".retx"); + n->complex = 0; + n->addable = 20; + n->xoffset = argsize(0); + return; + } + + fatal(Z, "bad regret"); } void diff --git a/src/cmd/8g/cgen.c b/src/cmd/8g/cgen.c index 5988a4328..f3093bc26 100644 --- a/src/cmd/8g/cgen.c +++ b/src/cmd/8g/cgen.c @@ -242,7 +242,6 @@ cgen(Node *n, Node *res) case OOR: case OXOR: case OADD: - case OADDPTR: case OMUL: a = optoas(n->op, nl->type); if(a == AIMULB) { @@ -1367,7 +1366,10 @@ int componentgen(Node *nr, Node *nl) { Node nodl, nodr; + Type *t; int freel, freer; + vlong fldcount; + vlong loffset, roffset; freel = 0; freer = 0; @@ -1377,8 +1379,33 @@ componentgen(Node *nr, Node *nl) goto no; case TARRAY: - if(!isslice(nl->type)) + t = nl->type; + + // Slices are ok. + if(isslice(t)) + break; + // Small arrays are ok. + if(t->bound > 0 && t->bound <= 3 && !isfat(t->type)) + break; + + goto no; + + case TSTRUCT: + // Small structs with non-fat types are ok. + // Zero-sized structs are treated separately elsewhere. + fldcount = 0; + for(t=nl->type->type; t; t=t->down) { + if(isfat(t->type)) + goto no; + if(t->etype != TFIELD) + fatal("componentgen: not a TFIELD: %lT", t); + fldcount++; + } + if(fldcount == 0 || fldcount > 4) goto no; + + break; + case TSTRING: case TINTER: break; @@ -1399,7 +1426,7 @@ componentgen(Node *nr, Node *nl) freer = 1; } } - + // nl and nr are 'cadable' which basically means they are names (variables) now. // If they are the same variable, don't generate any code, because the // VARDEF we generate will mark the old value as dead incorrectly. @@ -1409,8 +1436,25 @@ componentgen(Node *nr, Node *nl) switch(nl->type->etype) { case TARRAY: + // componentgen for arrays. if(nl->op == ONAME) gvardef(nl); + t = nl->type; + if(!isslice(t)) { + nodl.type = t->type; + nodr.type = nodl.type; + for(fldcount=0; fldcount < t->bound; fldcount++) { + if(nr == N) + clearslim(&nodl); + else + gmove(&nodr, &nodl); + nodl.xoffset += t->type->width; + nodr.xoffset += t->type->width; + } + goto yes; + } + + // componentgen for slices. nodl.xoffset += Array_array; nodl.type = ptrto(nl->type->type); @@ -1422,7 +1466,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1432,7 +1476,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_cap-Array_nel; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_cap-Array_nel; @@ -1457,7 +1501,7 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); nodl.xoffset += Array_nel-Array_array; - nodl.type = types[TUINT32]; + nodl.type = types[simtype[TUINT]]; if(nr != N) { nodr.xoffset += Array_nel-Array_array; @@ -1492,6 +1536,31 @@ componentgen(Node *nr, Node *nl) gmove(&nodr, &nodl); goto yes; + + case TSTRUCT: + if(nl->op == ONAME) + gvardef(nl); + loffset = nodl.xoffset; + roffset = nodr.xoffset; + // funarg structs may not begin at offset zero. + if(nl->type->etype == TSTRUCT && nl->type->funarg && nl->type->type) + loffset -= nl->type->type->width; + if(nr != N && nr->type->etype == TSTRUCT && nr->type->funarg && nr->type->type) + roffset -= nr->type->type->width; + + for(t=nl->type->type; t; t=t->down) { + nodl.xoffset = loffset + t->width; + nodl.type = t->type; + + if(nr == N) + clearslim(&nodl); + else { + nodr.xoffset = roffset + t->width; + nodr.type = nodl.type; + gmove(&nodr, &nodl); + } + } + goto yes; } no: diff --git a/src/cmd/8g/ggen.c b/src/cmd/8g/ggen.c index 2285a04e6..5e3140480 100644 --- a/src/cmd/8g/ggen.c +++ b/src/cmd/8g/ggen.c @@ -991,7 +991,7 @@ cgen_hmul(Node *nl, Node *nr, Node *res) if(t->width == 1) { // byte multiply behaves differently. nodreg(&ax, t, D_AH); - nodreg(&dx, t, D_DL); + nodreg(&dx, t, D_DX); gmove(&ax, &dx); } nodreg(&dx, t, D_DX); diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 66d5b8d69..9ee418cb7 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -430,7 +430,6 @@ optoas(int op, Type *t) case CASE(OADD, TINT32): case CASE(OADD, TUINT32): case CASE(OADD, TPTR32): - case CASE(OADDPTR, TPTR32): a = AADDL; break; diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index d88987f95..91a91d20d 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -636,6 +636,11 @@ copyu(Prog *p, Adr *v, Adr *s) static int copyas(Adr *a, Adr *v) { + if(D_AL <= a->type && a->type <= D_BL) + fatal("use of byte register"); + if(D_AL <= v->type && v->type <= D_BL) + fatal("use of byte register"); + if(a->type != v->type) return 0; if(regtyp(v)) diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index a69d124db..302b273a1 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -1168,6 +1168,7 @@ void addreg(Adr *a, int rn) { a->sym = nil; + a->node = nil; a->offset = 0; a->type = rn; diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index 114a3eb5d..c135dce70 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -619,17 +619,17 @@ asmb(void) if(iself) goto Elfsym; case Hplan9: - symo = HEADR+segtext.filelen+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; break; case Hdarwin: - symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(segdata.filelen, INITRND)+machlink; + symo = segdata.fileoff+rnd(segdata.filelen, INITRND)+machlink; break; Elfsym: - symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, INITRND); break; case Hwindows: - symo = rnd(HEADR+segtext.filelen, PEFILEALIGN)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, PEFILEALIGN); break; } diff --git a/src/cmd/9c/cgen.c b/src/cmd/9c/cgen.c index 0d7a40294..aeedc60c0 100644 --- a/src/cmd/9c/cgen.c +++ b/src/cmd/9c/cgen.c @@ -45,7 +45,7 @@ cgen(Node *n, Node *nn) } if(n == Z || n->type == T) return; - if(typesu[n->type->etype]) { + if(typesu[n->type->etype] && (n->op != OFUNC || nn != Z)) { sugen(n, nn, n->type->width); return; } @@ -74,7 +74,7 @@ cgen(Node *n, Node *nn) if(r != Z && r->complex >= FNX) switch(o) { default: - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); @@ -324,7 +324,7 @@ cgen(Node *n, Node *nn) if(l->op != OIND) diag(n, "bad function call"); - regret(&nod, l->left); + regret(&nod, l->left, 0, 0); cgen(l->left, &nod); regsalloc(&nod1, l->left); gopcode(OAS, &nod, Z, &nod1); @@ -353,11 +353,13 @@ cgen(Node *n, Node *nn) if(REGARG>=0) if(o != reg[REGARG]) reg[REGARG]--; - if(nn != Z) { - regret(&nod, n); + regret(&nod, n, l->type, 1); // update maxarg if nothing else + gpcdata(PCDATA_ArgSize, curarg); + gpcdata(PCDATA_ArgSize, -1); + if(nn != Z) gopcode(OAS, &nod, Z, nn); + if(nod.op == OREGISTER) regfree(&nod); - } break; case OIND: @@ -759,7 +761,7 @@ boolgen(Node *n, int true, Node *nn) if(true) o = comrel[relindex(o)]; if(l->complex >= FNX && r->complex >= FNX) { - regret(&nod, r); + regret(&nod, r, 0, 0); cgen(r, &nod); regsalloc(&nod1, r); gopcode(OAS, &nod, Z, &nod1); @@ -966,6 +968,20 @@ sugen(Node *n, Node *nn, int32 w) break; case OFUNC: + if(!hasdotdotdot(n->left->type)) { + cgen(n, Z); + if(nn != Z) { + curarg -= n->type->width; + regret(&nod1, n, n->left->type, 1); + if(nn->complex >= FNX) { + regsalloc(&nod2, n); + cgen(&nod1, &nod2); + nod1 = nod2; + } + cgen(&nod1, nn); + } + break; + } if(nn == Z) { sugen(n, nodrat, w); break; diff --git a/src/cmd/9c/gc.h b/src/cmd/9c/gc.h index 82204902b..fbe5099fe 100644 --- a/src/cmd/9c/gc.h +++ b/src/cmd/9c/gc.h @@ -141,7 +141,6 @@ extern int hintabsize; EXTERN int32 maxargsafe; EXTERN Multab multab[20]; EXTERN int mnstring; -EXTERN int retok; EXTERN Node* nodrat; EXTERN Node* nodret; EXTERN Node* nodsafe; @@ -212,7 +211,7 @@ void noretval(int); void xcom(Node*); int bcomplex(Node*, Node*); Prog* gtext(Sym*, int32); -vlong argsize(void); +vlong argsize(int); /* * cgen.c @@ -238,7 +237,7 @@ Node* nod32const(vlong); Node* nodfconst(double); Node* nodgconst(vlong v, Type *t); void nodreg(Node*, Node*, int); -void regret(Node*, Node*); +void regret(Node*, Node*, Type*, int); void regalloc(Node*, Node*, Node*); void regfree(Node*); void regialloc(Node*, Node*, Node*); diff --git a/src/cmd/9c/sgen.c b/src/cmd/9c/sgen.c index 25f0438e5..b03c17267 100644 --- a/src/cmd/9c/sgen.c +++ b/src/cmd/9c/sgen.c @@ -34,7 +34,7 @@ gtext(Sym *s, int32 stkoff) { vlong v; - v = ((uvlong)argsize() << 32) | (stkoff & 0xffffffff); + v = ((uvlong)argsize(1) << 32) | (stkoff & 0xffffffff); if((textflag & NOSPLIT) && stkoff >= 128) yyerror("stack frame too large for NOSPLIT function"); diff --git a/src/cmd/9c/swt.c b/src/cmd/9c/swt.c index 83058e7ab..a63db60b2 100644 --- a/src/cmd/9c/swt.c +++ b/src/cmd/9c/swt.c @@ -324,10 +324,11 @@ align(int32 i, Type *t, int op, int32 *maxalign) { int32 o; Type *v; - int w; + int w, packw; o = i; w = 1; + packw = 0; switch(op) { default: diag(Z, "unknown align opcode %d", op); @@ -338,7 +339,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1) w = 1; if(packflg) - w = packflg; + packw = packflg; break; case Ael1: /* initial allign of struct element */ @@ -351,7 +352,7 @@ align(int32 i, Type *t, int op, int32 *maxalign) if(w < 1 || w > SZ_VLONG) fatal(Z, "align"); if(packflg) - w = packflg; + packw = packflg; break; case Ael2: /* width of a struct element */ @@ -386,6 +387,8 @@ align(int32 i, Type *t, int op, int32 *maxalign) o = align(o, t, Ael2, nil); break; } + if(packw != 0 && xround(o, w) != xround(o, packw)) + diag(Z, "#pragma pack changes offset of %T", t); o = xround(o, w); if(maxalign && *maxalign < w) *maxalign = w; diff --git a/src/cmd/9c/txt.c b/src/cmd/9c/txt.c index 84aeb4d8a..e46aba84e 100644 --- a/src/cmd/9c/txt.c +++ b/src/cmd/9c/txt.c @@ -319,15 +319,43 @@ nodreg(Node *n, Node *nn, int reg) } void -regret(Node *n, Node *nn) +regret(Node *n, Node *nn, Type *t, int mode) { int r; + + if(mode == 0 || hasdotdotdot(t) || nn->type->width == 0) { + r = REGRET; + if(typefd[nn->type->etype]) + r = FREGRET+NREG; + nodreg(n, nn, r); + reg[r]++; + return; + } + + if(mode == 1) { + // fetch returned value after call. + // already called gargs, so curarg is set. + curarg = (curarg+7) & ~7; + regaalloc(n, nn); + return; + } - r = REGRET; - if(typefd[nn->type->etype]) - r = FREGRET+NREG; - nodreg(n, nn, r); - reg[r]++; + if(mode == 2) { + // store value to be returned. + // must compute arg offset. + if(t->etype != TFUNC) + fatal(Z, "bad regret func %T", t); + *n = *nn; + n->op = ONAME; + n->class = CPARAM; + n->sym = slookup(".ret"); + n->complex = nodret->complex; + n->addable = 20; + n->xoffset = argsize(0); + return; + } + + fatal(Z, "bad regret"); } void diff --git a/src/cmd/9g/cgen.c b/src/cmd/9g/cgen.c index 86a56975a..e38936001 100644 --- a/src/cmd/9g/cgen.c +++ b/src/cmd/9g/cgen.c @@ -260,7 +260,6 @@ cgen(Node *n, Node *res) case OOR: case OXOR: case OADD: - case OADDPTR: case OMUL: a = optoas(n->op, nl->type); goto sbop; diff --git a/src/cmd/9g/gg.h b/src/cmd/9g/gg.h index 6aa00117f..2eb516b40 100644 --- a/src/cmd/9g/gg.h +++ b/src/cmd/9g/gg.h @@ -49,7 +49,6 @@ void allocparams(void); void checklabels(void); void ginscall(Node*, int); int gen_as_init(Node*); -void clearslim(Node*); /* * cgen.c diff --git a/src/cmd/9g/gsubr.c b/src/cmd/9g/gsubr.c index dfdff0587..b194cfd9e 100644 --- a/src/cmd/9g/gsubr.c +++ b/src/cmd/9g/gsubr.c @@ -614,11 +614,8 @@ ismem(Node *n) case ONAME: case OPARAM: case OCLOSUREVAR: - return 1; case OADDR: - //if(flag_largemodel) - return 1; - break; + return 1; } return 0; } @@ -1474,11 +1471,9 @@ optoas(int op, Type *t) case CASE(OADD, TINT32): case CASE(OADD, TUINT32): case CASE(OADD, TPTR32): - case CASE(OADDPTR, TPTR32): case CASE(OADD, TINT64): case CASE(OADD, TUINT64): case CASE(OADD, TPTR64): - case CASE(OADDPTR, TPTR64): a = AADD; break; diff --git a/src/cmd/9l/asm.c b/src/cmd/9l/asm.c index 5aeea1b6b..b8ca777c3 100644 --- a/src/cmd/9l/asm.c +++ b/src/cmd/9l/asm.c @@ -245,10 +245,10 @@ asmb(void) if(iself) goto ElfSym; case Hplan9: - symo = HEADR+segtext.len+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; break; ElfSym: - symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; + symo = segdata.fileoff+segdata.filelen; symo = rnd(symo, INITRND); break; } diff --git a/src/cmd/addr2line/main.go b/src/cmd/addr2line/main.go index 3802f764f..267f4170a 100644 --- a/src/cmd/addr2line/main.go +++ b/src/cmd/addr2line/main.go @@ -19,17 +19,14 @@ package main import ( "bufio" - "debug/elf" - "debug/gosym" - "debug/macho" - "debug/pe" - "debug/plan9obj" "flag" "fmt" "log" "os" "strconv" "strings" + + "cmd/internal/objfile" ) func printUsage(w *os.File) { @@ -60,18 +57,12 @@ func main() { usage() } - f, err := os.Open(flag.Arg(0)) + f, err := objfile.Open(flag.Arg(0)) if err != nil { log.Fatal(err) } - textStart, symtab, pclntab, err := loadTables(f) - if err != nil { - log.Fatalf("reading %s: %v", flag.Arg(0), err) - } - - pcln := gosym.NewLineTable(pclntab, textStart) - tab, err := gosym.NewTable(symtab, pcln) + tab, err := f.PCLineTable() if err != nil { log.Fatalf("reading %s: %v", flag.Arg(0), err) } @@ -102,145 +93,3 @@ func main() { } stdout.Flush() } - -func loadTables(f *os.File) (textStart uint64, symtab, pclntab []byte, err error) { - if obj, err := elf.NewFile(f); err == nil { - if sect := obj.Section(".text"); sect != nil { - textStart = sect.Addr - } - if sect := obj.Section(".gosymtab"); sect != nil { - if symtab, err = sect.Data(); err != nil { - return 0, nil, nil, err - } - } - if sect := obj.Section(".gopclntab"); sect != nil { - if pclntab, err = sect.Data(); err != nil { - return 0, nil, nil, err - } - } - return textStart, symtab, pclntab, nil - } - - if obj, err := macho.NewFile(f); err == nil { - if sect := obj.Section("__text"); sect != nil { - textStart = sect.Addr - } - if sect := obj.Section("__gosymtab"); sect != nil { - if symtab, err = sect.Data(); err != nil { - return 0, nil, nil, err - } - } - if sect := obj.Section("__gopclntab"); sect != nil { - if pclntab, err = sect.Data(); err != nil { - return 0, nil, nil, err - } - } - return textStart, symtab, pclntab, nil - } - - if obj, err := pe.NewFile(f); err == nil { - var imageBase uint64 - switch oh := obj.OptionalHeader.(type) { - case *pe.OptionalHeader32: - imageBase = uint64(oh.ImageBase) - case *pe.OptionalHeader64: - imageBase = oh.ImageBase - default: - return 0, nil, nil, fmt.Errorf("pe file format not recognized") - } - if sect := obj.Section(".text"); sect != nil { - textStart = imageBase + uint64(sect.VirtualAddress) - } - if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil { - return 0, nil, nil, err - } - if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil { - return 0, nil, nil, err - } - return textStart, symtab, pclntab, nil - } - - if obj, err := plan9obj.NewFile(f); err == nil { - textStart = obj.LoadAddress + obj.HdrSize - if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil { - return 0, nil, nil, err - } - if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil { - return 0, nil, nil, err - } - return textStart, symtab, pclntab, nil - } - - return 0, nil, nil, fmt.Errorf("unrecognized binary format") -} - -func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) { - for _, s := range f.Symbols { - if s.Name != name { - continue - } - if s.SectionNumber <= 0 { - return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) - } - if len(f.Sections) < int(s.SectionNumber) { - return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) - } - return s, nil - } - return nil, fmt.Errorf("no %s symbol found", name) -} - -func loadPETable(f *pe.File, sname, ename string) ([]byte, error) { - ssym, err := findPESymbol(f, sname) - if err != nil { - return nil, err - } - esym, err := findPESymbol(f, ename) - if err != nil { - return nil, err - } - if ssym.SectionNumber != esym.SectionNumber { - return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) - } - sect := f.Sections[ssym.SectionNumber-1] - data, err := sect.Data() - if err != nil { - return nil, err - } - return data[ssym.Value:esym.Value], nil -} - -func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) { - syms, err := f.Symbols() - if err != nil { - return nil, err - } - for _, s := range syms { - if s.Name != name { - continue - } - return &s, nil - } - return nil, fmt.Errorf("no %s symbol found", name) -} - -func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) { - ssym, err := findPlan9Symbol(f, sname) - if err != nil { - return nil, err - } - esym, err := findPlan9Symbol(f, ename) - if err != nil { - return nil, err - } - sect := f.Section("text") - if sect == nil { - return nil, err - } - data, err := sect.Data() - if err != nil { - return nil, err - } - textStart := f.LoadAddress + f.HdrSize - return data[ssym.Value-textStart : esym.Value-textStart], nil -} diff --git a/src/cmd/api/goapi.go b/src/cmd/api/goapi.go index 932b5520f..7e8f85848 100644 --- a/src/cmd/api/goapi.go +++ b/src/cmd/api/goapi.go @@ -378,7 +378,51 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) { } if w.context != nil && file == fmt.Sprintf("zruntime_defs_%s_%s.go", w.context.GOOS, w.context.GOARCH) { // Just enough to keep the api checker happy. - src := "package runtime; type maptype struct{}; type _type struct{}; type alg struct{}; type mspan struct{}; type m struct{}; type lock struct{}; type slicetype struct{};" + src := "package runtime; type (" + + " _defer struct{};" + + " _func struct{};" + + " _panic struct{};" + + " _select struct{}; " + + " _type struct{};" + + " alg struct{};" + + " chantype struct{};" + + " context struct{};" + // windows + " eface struct{};" + + " funcval struct{};" + + " g struct{};" + + " gobuf struct{};" + + " hchan struct{};" + + " iface struct{};" + + " interfacetype struct{};" + + " itab struct{};" + + " m struct{};" + + " maptype struct{};" + + " mcache struct{};" + + " mspan struct{};" + + " mutex struct{};" + + " note struct{};" + + " slicetype struct{};" + + " stkframe struct{};" + + " sudog struct{};" + + " waitq struct{};" + + " wincallbackcontext struct{};" + + " keventt struct{};" + + " timespec struct{};" + + " epollevent struct{};" + + "); " + + "const (" + + " cb_max = 2000;" + + " _CacheLineSize = 64;" + + " _Gidle = 1;" + + " _Grunnable = 2;" + + " _Grunning = 3;" + + " _Gsyscall = 4;" + + " _Gwaiting = 5;" + + " _Gdead = 6;" + + " _Genqueue = 7;" + + " _Gcopystack = 8;" + + " _NSIG = 32;" + + ")" f, err = parser.ParseFile(fset, filename, src, 0) if err != nil { log.Fatalf("incorrect generated file: %s", err) diff --git a/src/cmd/api/run.go b/src/cmd/api/run.go index 896b2b4a1..c323deb60 100644 --- a/src/cmd/api/run.go +++ b/src/cmd/api/run.go @@ -98,11 +98,9 @@ func prepGoPath() string { if err == nil { username = u.Username } else { - // Only need to handle Unix here, as Windows's os/user uses - // native syscall and should work fine without cgo. username = os.Getenv("USER") if username == "" { - log.Fatalf("Error getting current user: %v", err) + username = "nobody" } } diff --git a/src/cmd/cc/cc.h b/src/cmd/cc/cc.h index c8aac1253..1dae5acd9 100644 --- a/src/cmd/cc/cc.h +++ b/src/cmd/cc/cc.h @@ -794,7 +794,7 @@ void xcom(Node*); int32 exreg(Type*); int32 align(int32, Type*, int, int32*); int32 maxround(int32, int32); -int hasdotdotdot(void); +int hasdotdotdot(Type*); void linkarchinit(void); extern schar ewidth[]; diff --git a/src/cmd/cc/cc.y b/src/cmd/cc/cc.y index 11ee444b7..8d7cb1472 100644 --- a/src/cmd/cc/cc.y +++ b/src/cmd/cc/cc.y @@ -1043,6 +1043,7 @@ complex: } | LSTRUCT sbody { + diag(Z, "struct must have tag"); taggen++; sprint(symb, "_%d_", taggen); $$ = dotag(lookup(), TSTRUCT, autobn); diff --git a/src/cmd/cc/dcl.c b/src/cmd/cc/dcl.c index a7a942686..292717d68 100644 --- a/src/cmd/cc/dcl.c +++ b/src/cmd/cc/dcl.c @@ -697,7 +697,8 @@ argmark(Node *n, int pass) { Type *t; - autoffset = align(0, thisfn->link, Aarg0, nil); + if(hasdotdotdot(thisfn->link)) + autoffset = align(0, thisfn->link, Aarg0, nil); stkoff = 0; for(; n->left != Z; n = n->left) { if(n->op != OFUNC || n->left->op != ONAME) @@ -1401,6 +1402,10 @@ xdecl(int c, Type *t, Sym *s) } tmerge(t, s); s->type = t; + if(c == CTYPEDEF && (typechlv[t->etype] || typefd[t->etype])) { + s->type = copytyp(t); + s->type->tag = s; + } s->class = c; s->block = 0; s->offset = o; @@ -1481,12 +1486,9 @@ edecl(int c, Type *t, Sym *s) { Type *t1; - if(s == S) { - if(!typesu[t->etype]) - diag(Z, "unnamed structure element must be struct/union"); - if(c != CXXX) - diag(Z, "unnamed structure element cannot have class"); - } else + if(s == S) + diag(Z, "unnamed structure elements not supported"); + else if(c != CXXX) diag(Z, "structure element cannot have class: %s", s->name); t1 = t; diff --git a/src/cmd/cc/godefs.c b/src/cmd/cc/godefs.c index 3755a8fc0..d3ab52fde 100644 --- a/src/cmd/cc/godefs.c +++ b/src/cmd/cc/godefs.c @@ -154,7 +154,6 @@ static void printtypename(Type *t) { Sym *s; - Type *t1; int w; char *n; @@ -188,60 +187,27 @@ printtypename(Type *t) switch(t->etype) { case TINT: - Bprint(&outbuf, "int32"); - break; case TUINT: - Bprint(&outbuf, "uint32"); - break; case TCHAR: - Bprint(&outbuf, "int8"); - break; case TUCHAR: - Bprint(&outbuf, "uint8"); - break; case TSHORT: - Bprint(&outbuf, "int16"); - break; case TUSHORT: - Bprint(&outbuf, "uint16"); - break; case TLONG: - // The 32/64-bit ambiguous types (int,uint,uintptr) - // are assigned a TLONG/TULONG to distinguish them - // from always 32-bit types which get a TINT/TUINT. - // (See int_x/uint_x in pkg/runtime/runtime.h.) - // For LONG and VLONG types, we generate the - // unqualified Go type when appropriate. - // This makes it easier to write Go code that - // modifies objects with autogenerated-from-C types. - if(ewidth[TIND] == 4) - Bprint(&outbuf, "int"); - else - Bprint(&outbuf, "int32"); - break; case TULONG: - if(ewidth[TIND] == 4) - Bprint(&outbuf, "uint"); - else - Bprint(&outbuf, "uint32"); - break; case TVLONG: - if(ewidth[TIND] == 8) - Bprint(&outbuf, "int"); - else - Bprint(&outbuf, "int64"); - break; case TUVLONG: - if(ewidth[TIND] == 8) - Bprint(&outbuf, "uint"); - else - Bprint(&outbuf, "uint64"); - break; case TFLOAT: - Bprint(&outbuf, "float32"); - break; case TDOUBLE: - Bprint(&outbuf, "float64"); + // All names used in the runtime code should be typedefs. + if(t->tag != nil) { + if(strcmp(t->tag->name, "intgo") == 0) + Bprint(&outbuf, "int"); + else if(strcmp(t->tag->name, "uintgo") == 0) + Bprint(&outbuf, "uint"); + else + Bprint(&outbuf, "%s", t->tag->name); + } else + Bprint(&outbuf, "C.%T", t); break; case TUNION: case TSTRUCT: @@ -251,27 +217,18 @@ printtypename(Type *t) n = s->name; else if(t->tag) n = t->tag->name; - if(strcmp(n, "String") == 0){ + if(strcmp(n, "String") == 0) Bprint(&outbuf, "string"); - } else if(strcmp(n, "Slice") == 0){ + else if(strcmp(n, "Slice") == 0) Bprint(&outbuf, "[]byte"); - } else + else if(strcmp(n, "Eface") == 0) + Bprint(&outbuf, "interface{}"); + else Bprint(&outbuf, "%U", n); break; case TFUNC: - Bprint(&outbuf, "func("); - for(t1 = t->down; t1 != T; t1 = t1->down) { - if(t1->etype == TVOID) - break; - if(t1 != t->down) - Bprint(&outbuf, ", "); - printtypename(t1); - } - Bprint(&outbuf, ")"); - if(t->link && t->link->etype != TVOID) { - Bprint(&outbuf, " "); - printtypename(t->link); - } + // There's no equivalent to a C function in the Go world. + Bprint(&outbuf, "unsafe.Pointer"); break; case TDOT: Bprint(&outbuf, "...interface{}"); @@ -360,9 +317,9 @@ godefvar(Sym *s) switch(t->etype) { case TENUM: if(!typefd[t->etype]) - Bprint(&outbuf, "const %U = %lld\n", s->name, s->vconst); + Bprint(&outbuf, "const %s = %lld\n", s->name, s->vconst); else - Bprint(&outbuf, "const %U = %f\n;", s->name, s->fconst); + Bprint(&outbuf, "const %s = %f\n;", s->name, s->fconst); break; case TFUNC: diff --git a/src/cmd/cc/pgen.c b/src/cmd/cc/pgen.c index c2cf0e101..4b4684a52 100644 --- a/src/cmd/cc/pgen.c +++ b/src/cmd/cc/pgen.c @@ -56,24 +56,24 @@ makefuncdatasym(char *namefmt, int64 funcdatakind) } int -hasdotdotdot(void) +hasdotdotdot(Type *t) { - Type *t; - - for(t=thisfn->down; t!=T; t=t->down) + for(t=t->down; t!=T; t=t->down) if(t->etype == TDOT) return 1; return 0; } vlong -argsize(void) +argsize(int doret) { Type *t; int32 s; //print("t=%T\n", thisfn); - s = align(0, thisfn->link, Aarg0, nil); + s = 0; + if(hasdotdotdot(thisfn)) + s = align(s, thisfn->link, Aarg0, nil); for(t=thisfn->down; t!=T; t=t->down) { switch(t->etype) { case TVOID: @@ -93,6 +93,14 @@ argsize(void) s = (s+7) & ~7; else s = (s+3) & ~3; + if(doret && thisfn->link->etype != TVOID) { + s = align(s, thisfn->link, Aarg1, nil); + s = align(s, thisfn->link, Aarg2, nil); + if(thechar == '6') + s = (s+7) & ~7; + else + s = (s+3) & ~3; + } return s; } @@ -123,13 +131,14 @@ codgen(Node *n, Node *nn) nearln = nn->lineno; p = gtext(n1->sym, stkoff); + p->from.sym->cfunc = 1; sp = p; /* * generate funcdata symbol for this function. * data is filled in at the end of codgen(). */ - isvarargs = hasdotdotdot(); + isvarargs = hasdotdotdot(thisfn); gcargs = nil; if(!isvarargs) gcargs = makefuncdatasym("gcargs·%d", FUNCDATA_ArgsPointerMaps); @@ -212,7 +221,7 @@ supgen(Node *n) void gen(Node *n) { - Node *l, nod; + Node *l, nod, nod1; Prog *sp, *spc, *spb; Case *cn; long sbc, scc; @@ -273,14 +282,26 @@ loop: gbranch(ORETURN); break; } + if(typecmplx[n->type->etype] && !hasdotdotdot(thisfn)) { + regret(&nod, n, thisfn, 2); + sugen(l, &nod, n->type->width); + noretval(3); + gbranch(ORETURN); + break; + } if(typecmplx[n->type->etype]) { sugen(l, nodret, n->type->width); noretval(3); gbranch(ORETURN); break; } - regret(&nod, n); + regret(&nod1, n, thisfn, 2); + nod = nod1; + if(nod.op != OREGISTER) + regalloc(&nod, n, Z); cgen(l, &nod); + if(nod1.op != OREGISTER) + gmove(&nod, &nod1); regfree(&nod); if(typefd[n->type->etype]) noretval(1); @@ -729,9 +750,11 @@ dumpgcargs(Type *fn, Sym *sym) symoffset = 0; gextern(sym, nodconst(1), symoffset, 4); symoffset += 4; - argbytes = (argsize() + ewidth[TIND] - 1); + argbytes = (argsize(1) + ewidth[TIND] - 1); bv = bvalloc((argbytes / ewidth[TIND]) * BitsPerPointer); - argoffset = align(0, fn->link, Aarg0, nil); + 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 diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index 841c84833..d77d56c22 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -229,7 +229,8 @@ func (p *Package) guessKinds(f *File) []*Name { // Determine kinds for names we already know about, // like #defines or 'struct foo', before bothering with gcc. var names, needType []*Name - for _, n := range f.Name { + for _, key := range nameKeys(f.Name) { + n := f.Name[key] // If we've already found this name as a #define // and we can translate it as a constant value, do so. if n.Define != "" { @@ -331,6 +332,7 @@ func (p *Package) guessKinds(f *File) []*Name { const ( notType = 1 << iota notConst + notDeclared ) for _, line := range strings.Split(stderr, "\n") { if !strings.Contains(line, ": error:") { @@ -365,7 +367,7 @@ func (p *Package) guessKinds(f *File) []*Name { completed = true case "not-declared": - error_(token.NoPos, "%s", strings.TrimSpace(line[c2+1:])) + sniff[i] |= notDeclared case "not-type": sniff[i] |= notType case "not-const": @@ -374,12 +376,12 @@ func (p *Package) guessKinds(f *File) []*Name { } if !completed { - fatalf("%s did not produce error at completed:1\non input:\n%s", p.gccBaseCmd()[0], b.Bytes()) + fatalf("%s did not produce error at completed:1\non input:\n%s\nfull error output:\n%s", p.gccBaseCmd()[0], b.Bytes(), stderr) } for i, n := range names { switch sniff[i] { - case 0: + default: error_(token.NoPos, "could not determine kind of name for C.%s", fixGo(n.Go)) case notType: n.Kind = "const" @@ -390,6 +392,14 @@ func (p *Package) guessKinds(f *File) []*Name { } } if nerrors > 0 { + // Check if compiling the preamble by itself causes any errors, + // because the messages we've printed out so far aren't helpful + // to users debugging preamble mistakes. See issue 8442. + preambleErrors := p.gccErrors([]byte(f.Preamble)) + if len(preambleErrors) > 0 { + error_(token.NoPos, "\n%s errors for preamble:\n%s", p.gccBaseCmd()[0], preambleErrors) + } + fatalf("unresolved names") } @@ -649,7 +659,13 @@ func (p *Package) rewriteRef(f *File) { f.Name[fpName] = name } r.Name = name - expr = ast.NewIdent(name.Mangle) + // Rewrite into call to _Cgo_ptr to prevent assignments. The _Cgo_ptr + // function is defined in out.go and simply returns its argument. See + // issue 7757. + expr = &ast.CallExpr{ + Fun: &ast.Ident{NamePos: (*r.Expr).Pos(), Name: "_Cgo_ptr"}, + Args: []ast.Expr{ast.NewIdent(name.Mangle)}, + } } else if r.Name.Kind == "type" { // Okay - might be new(T) expr = r.Name.Type.Go @@ -1068,12 +1084,6 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { return t } - // clang won't generate DW_AT_byte_size for pointer types, - // so we have to fix it here. - if dt, ok := base(dtype).(*dwarf.PtrType); ok && dt.ByteSize == -1 { - dt.ByteSize = c.ptrSize - } - t := new(Type) t.Size = dtype.Size() // note: wrong for array of pointers, corrected below t.Align = -1 @@ -1097,12 +1107,20 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { t.Go = c.Opaque(t.Size) break } + count := dt.Count + if count == -1 { + // Indicates flexible array member, which Go doesn't support. + // Translate to zero-length array instead. + count = 0 + } sub := c.Type(dt.Type, pos) t.Align = sub.Align t.Go = &ast.ArrayType{ - Len: c.intExpr(dt.Count), + Len: c.intExpr(count), Elt: sub.Go, } + // Recalculate t.Size now that we know sub.Size. + t.Size = count * sub.Size t.C.Set("__typeof__(%s[%d])", sub.C, dt.Count) case *dwarf.BoolType: @@ -1203,6 +1221,11 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } case *dwarf.PtrType: + // Clang doesn't emit DW_AT_byte_size for pointer types. + if t.Size != c.ptrSize && t.Size != -1 { + fatalf("%s: unexpected: %d-byte pointer type - %s", lineno(pos), t.Size, dtype) + } + t.Size = c.ptrSize t.Align = c.ptrSize if _, ok := base(dt.Type).(*dwarf.VoidType); ok { @@ -1374,34 +1397,24 @@ func (c *typeConv) Type(dtype dwarf.Type, pos token.Pos) *Type { } } - if t.Size <= 0 { - // Clang does not record the size of a pointer in its DWARF entry, - // so if dtype is an array, the call to dtype.Size at the top of the function - // computed the size as the array length * 0 = 0. - // The type switch called Type (this function) recursively on the pointer - // entry, and the code near the top of the function updated the size to - // be correct, so calling dtype.Size again will produce the correct value. - t.Size = dtype.Size() - if t.Size < 0 { - // Unsized types are [0]byte, unless they're typedefs of other types - // or structs with tags. - // if so, use the name we've already defined. - t.Size = 0 - switch dt := dtype.(type) { - case *dwarf.TypedefType: - // ok - case *dwarf.StructType: - if dt.StructName != "" { - break - } - t.Go = c.Opaque(0) - default: - t.Go = c.Opaque(0) - } - if t.C.Empty() { - t.C.Set("void") + if t.Size < 0 { + // Unsized types are [0]byte, unless they're typedefs of other types + // or structs with tags. + // if so, use the name we've already defined. + t.Size = 0 + switch dt := dtype.(type) { + case *dwarf.TypedefType: + // ok + case *dwarf.StructType: + if dt.StructName != "" { + break } - return t + t.Go = c.Opaque(0) + default: + t.Go = c.Opaque(0) + } + if t.C.Empty() { + t.C.Set("void") } } @@ -1533,6 +1546,9 @@ func (c *typeConv) pad(fld []*ast.Field, size int64) []*ast.Field { // Struct conversion: return Go and (6g) C syntax for type. func (c *typeConv) Struct(dt *dwarf.StructType, pos token.Pos) (expr *ast.StructType, csyntax string, align int64) { + // Minimum alignment for a struct is 1 byte. + align = 1 + var buf bytes.Buffer buf.WriteString("struct {") fld := make([]*ast.Field, 0, 2*len(dt.Field)+1) // enough for padding around every field diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 1ef78b757..6586531ad 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -58,16 +58,14 @@ func (p *Package) writeDefs() { fmt.Fprintf(fgo2, "// Created by cgo - DO NOT EDIT\n\n") fmt.Fprintf(fgo2, "package %s\n\n", p.PackageName) fmt.Fprintf(fgo2, "import \"unsafe\"\n\n") - if *importSyscall { - fmt.Fprintf(fgo2, "import \"syscall\"\n\n") - } if !*gccgo && *importRuntimeCgo { fmt.Fprintf(fgo2, "import _ \"runtime/cgo\"\n\n") } - fmt.Fprintf(fgo2, "type _ unsafe.Pointer\n\n") if *importSyscall { - fmt.Fprintf(fgo2, "func _Cerrno(dst *error, x int32) { *dst = syscall.Errno(x) }\n") + fmt.Fprintf(fgo2, "import \"syscall\"\n\n") + fmt.Fprintf(fgo2, "var _ syscall.Errno\n") } + fmt.Fprintf(fgo2, "func _Cgo_ptr(ptr unsafe.Pointer) unsafe.Pointer { return ptr }\n\n") typedefNames := make([]string, 0, len(typedef)) for name := range typedef { @@ -87,9 +85,10 @@ func (p *Package) writeDefs() { } if *gccgo { - fmt.Fprintf(fc, p.cPrologGccgo()) + fmt.Fprint(fc, p.cPrologGccgo()) } else { - fmt.Fprintf(fc, cProlog) + fmt.Fprint(fc, cProlog) + fmt.Fprint(fgo2, goProlog) } gccgoSymbolPrefix := p.gccgoSymbolPrefix() @@ -296,10 +295,6 @@ func (p *Package) structType(n *Name) (string, int64) { fmt.Fprintf(&buf, "\t\tchar __pad%d[%d];\n", off, pad) off += pad } - if n.AddError { - fmt.Fprint(&buf, "\t\tint e[2*sizeof(void *)/sizeof(int)]; /* error */\n") - off += 2 * p.PtrSize - } if off == 0 { fmt.Fprintf(&buf, "\t\tchar unused;\n") // avoid empty struct } @@ -334,19 +329,18 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { } // Builtins defined in the C prolog. - inProlog := name == "CString" || name == "GoString" || name == "GoStringN" || name == "GoBytes" || name == "_CMalloc" + inProlog := builtinDefs[name] != "" + cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) + paramnames := []string(nil) + for i, param := range d.Type.Params.List { + paramName := fmt.Sprintf("p%d", i) + param.Names = []*ast.Ident{ast.NewIdent(paramName)} + paramnames = append(paramnames, paramName) + } if *gccgo { // Gccgo style hooks. fmt.Fprint(fgo2, "\n") - cname := fmt.Sprintf("_cgo%s%s", cPrefix, n.Mangle) - paramnames := []string(nil) - for i, param := range d.Type.Params.List { - paramName := fmt.Sprintf("p%d", i) - param.Names = []*ast.Ident{ast.NewIdent(paramName)} - paramnames = append(paramnames, paramName) - } - conf.Fprint(fgo2, fset, d) fmt.Fprint(fgo2, " {\n") if !inProlog { @@ -383,7 +377,7 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { fmt.Fprint(fgo2, "}\n") // declare the C function. - fmt.Fprintf(fgo2, "//extern _cgo%s%s\n", cPrefix, n.Mangle) + fmt.Fprintf(fgo2, "//extern %s\n", cname) d.Name = ast.NewIdent(cname) if n.AddError { l := d.Type.Results.List @@ -394,61 +388,49 @@ func (p *Package) writeDefsFunc(fc, fgo2 *os.File, n *Name) { return } - conf.Fprint(fgo2, fset, d) - fmt.Fprint(fgo2, "\n") if inProlog { + fmt.Fprint(fgo2, builtinDefs[name]) return } - var argSize int64 - _, argSize = p.structType(n) - // C wrapper calls into gcc, passing a pointer to the argument frame. - fmt.Fprintf(fc, "#pragma cgo_import_static _cgo%s%s\n", cPrefix, n.Mangle) - fmt.Fprintf(fc, "void _cgo%s%s(void*);\n", cPrefix, n.Mangle) - fmt.Fprintf(fc, "\n") - fmt.Fprintf(fc, "void\n") - if argSize == 0 { - argSize++ + fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", cname) + fmt.Fprintf(fc, "void %s(void*);\n", cname) + fmt.Fprintf(fc, "void *·%s = %s;\n", cname, cname) + + nret := 0 + if !void { + d.Type.Results.List[0].Names = []*ast.Ident{ast.NewIdent("r1")} + nret = 1 } - // TODO(rsc): The struct here should declare pointers only where - // there are pointers in the actual argument frame. - // This is a workaround for golang.org/issue/6397. - fmt.Fprintf(fc, "·%s(struct{", n.Mangle) - if n := argSize / p.PtrSize; n > 0 { - fmt.Fprintf(fc, "void *y[%d];", n) + if n.AddError { + d.Type.Results.List[nret].Names = []*ast.Ident{ast.NewIdent("r2")} } - if n := argSize % p.PtrSize; n > 0 { - fmt.Fprintf(fc, "uint8 x[%d];", n) + + fmt.Fprint(fgo2, "\n") + fmt.Fprintf(fgo2, "var %s unsafe.Pointer\n", cname) + conf.Fprint(fgo2, fset, d) + fmt.Fprint(fgo2, " {\n") + + // NOTE: Using uintptr to hide from escape analysis. + arg := "0" + if len(paramnames) > 0 { + arg = "uintptr(unsafe.Pointer(&p0))" + } else if !void { + arg = "uintptr(unsafe.Pointer(&r1))" } - fmt.Fprintf(fc, "}p)\n") - fmt.Fprintf(fc, "{\n") - fmt.Fprintf(fc, "\truntime·cgocall(_cgo%s%s, &p);\n", cPrefix, n.Mangle) + + prefix := "" if n.AddError { - // gcc leaves errno in first word of interface at end of p. - // check whether it is zero; if so, turn interface into nil. - // if not, turn interface into errno. - // Go init function initializes ·_Cerrno with an os.Errno - // for us to copy. - fmt.Fprintln(fc, ` { - int32 e; - void **v; - v = (void**)(&p+1) - 2; /* v = final two void* of p */ - e = *(int32*)v; - v[0] = (void*)0xdeadbeef; - v[1] = (void*)0xdeadbeef; - if(e == 0) { - /* nil interface */ - v[0] = 0; - v[1] = 0; - } else { - ·_Cerrno(v, e); /* fill in v as error for errno e */ - } - }`) + prefix = "errno := " } - fmt.Fprintf(fc, "}\n") - fmt.Fprintf(fc, "\n") + fmt.Fprintf(fgo2, "\t%s_cgo_runtime_cgocall_errno(%s, %s)\n", prefix, cname, arg) + if n.AddError { + fmt.Fprintf(fgo2, "\tif errno != 0 { r2 = syscall.Errno(errno) }\n") + } + fmt.Fprintf(fgo2, "\treturn\n") + fmt.Fprintf(fgo2, "}\n") } // writeOutput creates stubs for a specific source file to be compiled by 6g @@ -521,7 +503,11 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { // Gcc wrapper unpacks the C argument struct // and calls the actual C function. - fmt.Fprintf(fgcc, "void\n") + if n.AddError { + fmt.Fprintf(fgcc, "int\n") + } else { + fmt.Fprintf(fgcc, "void\n") + } fmt.Fprintf(fgcc, "_cgo%s%s(void *v)\n", cPrefix, n.Mangle) fmt.Fprintf(fgcc, "{\n") if n.AddError { @@ -557,7 +543,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) { } fmt.Fprintf(fgcc, ");\n") if n.AddError { - fmt.Fprintf(fgcc, "\t*(int*)(a->e) = errno;\n") + fmt.Fprintf(fgcc, "\treturn errno;\n") } fmt.Fprintf(fgcc, "}\n") fmt.Fprintf(fgcc, "\n") @@ -1166,46 +1152,74 @@ const cProlog = ` #include "runtime.h" #include "cgocall.h" +static void *cgocall_errno = runtime·cgocall_errno; +void *·_cgo_runtime_cgocall_errno = &cgocall_errno; + +static void *runtime_gostring = runtime·gostring; +void *·_cgo_runtime_gostring = &runtime_gostring; + +static void *runtime_gostringn = runtime·gostringn; +void *·_cgo_runtime_gostringn = &runtime_gostringn; + +static void *runtime_gobytes = runtime·gobytes; +void *·_cgo_runtime_gobytes = &runtime_gobytes; + +static void *runtime_cmalloc = runtime·cmalloc; +void *·_cgo_runtime_cmalloc = &runtime_cmalloc; + void ·_Cerrno(void*, int32); +` -void -·_Cfunc_GoString(int8 *p, String s) -{ - s = runtime·gostring((byte*)p); - FLUSH(&s); +const goProlog = ` +var _cgo_runtime_cgocall_errno func(unsafe.Pointer, uintptr) int32 +var _cgo_runtime_cmalloc func(uintptr) unsafe.Pointer +` + +const goStringDef = ` +var _cgo_runtime_gostring func(*_Ctype_char) string +func _Cfunc_GoString(p *_Ctype_char) string { + return _cgo_runtime_gostring(p) } +` -void -·_Cfunc_GoStringN(int8 *p, int32 l, String s) -{ - s = runtime·gostringn((byte*)p, l); - FLUSH(&s); +const goStringNDef = ` +var _cgo_runtime_gostringn func(*_Ctype_char, int) string +func _Cfunc_GoStringN(p *_Ctype_char, l _Ctype_int) string { + return _cgo_runtime_gostringn(p, int(l)) } +` -void -·_Cfunc_GoBytes(int8 *p, int32 l, Slice s) -{ - s = runtime·gobytes((byte*)p, l); - FLUSH(&s); +const goBytesDef = ` +var _cgo_runtime_gobytes func(unsafe.Pointer, int) []byte +func _Cfunc_GoBytes(p unsafe.Pointer, l _Ctype_int) []byte { + return _cgo_runtime_gobytes(p, int(l)) } +` -void -·_Cfunc_CString(String s, int8 *p) -{ - p = runtime·cmalloc(s.len+1); - runtime·memmove((byte*)p, s.str, s.len); - p[s.len] = 0; - FLUSH(&p); +const cStringDef = ` +func _Cfunc_CString(s string) *_Ctype_char { + p := _cgo_runtime_cmalloc(uintptr(len(s)+1)) + pp := (*[1<<30]byte)(p) + copy(pp[:], s) + pp[len(s)] = 0 + return (*_Ctype_char)(p) } +` -void -·_Cfunc__CMalloc(uintptr n, int8 *p) -{ - p = runtime·cmalloc(n); - FLUSH(&p); +const cMallocDef = ` +func _Cfunc__CMalloc(n _Ctype_size_t) unsafe.Pointer { + return _cgo_runtime_cmalloc(uintptr(n)) } ` +var builtinDefs = map[string]string{ + "GoString": goStringDef, + "GoStringN": goStringNDef, + "GoBytes": goBytesDef, + "CString": cStringDef, + "_CMalloc": cMallocDef, +} + func (p *Package) cPrologGccgo() string { return strings.Replace(cPrologGccgo, "PREFIX", cPrefix, -1) } diff --git a/src/cmd/dist/a.h b/src/cmd/dist/a.h index 6222e5060..288063b94 100644 --- a/src/cmd/dist/a.h +++ b/src/cmd/dist/a.h @@ -108,9 +108,6 @@ void mkzexperiment(char*, char*); // buildgo.c void mkzdefaultcc(char*, char*); -// goc2c.c -void goc2c(char*, char*); - // main.c extern int vflag; extern int sflag; @@ -129,6 +126,7 @@ bool isfile(char *p); char* lastelem(char*); Time mtime(char*); void readfile(Buf*, char*); +void copyfile(char*, char*, int); void run(Buf *b, char *dir, int mode, char *cmd, ...); void runv(Buf *b, char *dir, int mode, Vec *argv); void bgrunv(char *dir, int mode, Vec *argv); diff --git a/src/cmd/dist/buf.c b/src/cmd/dist/buf.c index 45fb1954d..2ddc6be75 100644 --- a/src/cmd/dist/buf.c +++ b/src/cmd/dist/buf.c @@ -202,7 +202,7 @@ vadd(Vec *v, char *p) } // vaddn adds a string consisting of the n bytes at p to the vector. -void +static void vaddn(Vec *v, char *p, int n) { char *q; diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c index 1feeccf41..6fab14a11 100644 --- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -35,7 +35,6 @@ bool rebuildall; bool defaultclang; static bool shouldbuild(char*, char*); -static void copy(char*, char*, int); static void dopack(char*, char*, char**, int); static char *findgoversion(void); @@ -628,7 +627,6 @@ char *depsuffix[] = { ".h", ".s", ".go", - ".goc", }; // gentab records how to generate some trivial files. @@ -661,7 +659,7 @@ install(char *dir) { char *name, *p, *elem, *prefix, *exe; bool islib, ispkg, isgo, stale, ispackcmd; - Buf b, b1, path; + Buf b, b1, path, final_path, final_name; Vec compile, files, link, go, missing, clean, lib, extra; Time ttarg, t; int i, j, k, n, doclean, targ; @@ -676,6 +674,8 @@ install(char *dir) binit(&b); binit(&b1); binit(&path); + binit(&final_path); + binit(&final_name); vinit(&compile); vinit(&files); vinit(&link); @@ -688,11 +688,12 @@ install(char *dir) // path = full path to dir. bpathf(&path, "%s/src/%s", goroot, dir); + bpathf(&final_path, "%s/src/%s", goroot_final, dir); name = lastelem(dir); // For misc/prof, copy into the tool directory and we're done. if(hasprefix(dir, "misc/")) { - copy(bpathf(&b, "%s/%s", tooldir, name), + copyfile(bpathf(&b, "%s/%s", tooldir, name), bpathf(&b1, "%s/misc/%s", goroot, name), 1); goto out; } @@ -904,17 +905,19 @@ install(char *dir) // For package runtime, copy some files into the work space. if(streq(dir, "pkg/runtime")) { - copy(bpathf(&b, "%s/arch_GOARCH.h", workdir), + copyfile(bpathf(&b, "%s/arch_GOARCH.h", workdir), bpathf(&b1, "%s/arch_%s.h", bstr(&path), goarch), 0); - copy(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir), + copyfile(bpathf(&b, "%s/defs_GOOS_GOARCH.h", workdir), bpathf(&b1, "%s/defs_%s_%s.h", bstr(&path), goos, goarch), 0); p = bpathf(&b1, "%s/signal_%s_%s.h", bstr(&path), goos, goarch); if(isfile(p)) - copy(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0); - copy(bpathf(&b, "%s/os_GOOS.h", workdir), + copyfile(bpathf(&b, "%s/signal_GOOS_GOARCH.h", workdir), p, 0); + copyfile(bpathf(&b, "%s/os_GOOS.h", workdir), bpathf(&b1, "%s/os_%s.h", bstr(&path), goos), 0); - copy(bpathf(&b, "%s/signals_GOOS.h", workdir), + copyfile(bpathf(&b, "%s/signals_GOOS.h", workdir), bpathf(&b1, "%s/signals_%s.h", bstr(&path), goos), 0); + copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch), + bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0); } // Generate any missing files; regenerate existing ones. @@ -948,26 +951,10 @@ install(char *dir) // The last batch was required for the generators. // This one is generated. if(streq(dir, "pkg/runtime")) { - copy(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), + copyfile(bpathf(&b, "%s/zasm_GOOS_GOARCH.h", workdir), bpathf(&b1, "%s/zasm_%s_%s.h", bstr(&path), goos, goarch), 0); } - // Generate .c files from .goc files. - if(streq(dir, "pkg/runtime")) { - for(i=0; i<files.len; i++) { - p = files.p[i]; - if(!hassuffix(p, ".goc")) - continue; - // b = path/zp but with _goos_goarch.c instead of .goc - bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p)); - b.len -= 4; - bwritef(&b, "_%s_%s.c", goos, goarch); - goc2c(p, bstr(&b)); - vadd(&files, bstr(&b)); - } - vuniq(&files); - } - if((!streq(goos, gohostos) || !streq(goarch, gohostarch)) && isgo) { // We've generated the right files; the go command can do the build. if(vflag > 1) @@ -1138,9 +1125,9 @@ nobuild: // In package runtime, we install runtime.h and cgocall.h too, // for use by cgo compilation. if(streq(dir, "pkg/runtime")) { - copy(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch), + copyfile(bpathf(&b, "%s/pkg/%s_%s/cgocall.h", goroot, goos, goarch), bpathf(&b1, "%s/src/pkg/runtime/cgocall.h", goroot), 0); - copy(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch), + copyfile(bpathf(&b, "%s/pkg/%s_%s/runtime.h", goroot, goos, goarch), bpathf(&b1, "%s/src/pkg/runtime/runtime.h", goroot), 0); } @@ -1277,8 +1264,8 @@ out: } // copy copies the file src to dst, via memory (so only good for small files). -static void -copy(char *dst, char *src, int exec) +void +copyfile(char *dst, char *src, int exec) { Buf b; diff --git a/src/cmd/dist/buildruntime.c b/src/cmd/dist/buildruntime.c index 9b40a5968..7af38456d 100644 --- a/src/cmd/dist/buildruntime.c +++ b/src/cmd/dist/buildruntime.c @@ -112,7 +112,7 @@ mkzgoos(char *dir, char *file) bwritestr(&out, "// auto generated by go tool dist\n\n"); - if (streq(goos, "linux")) { + if(streq(goos, "linux")) { bwritestr(&out, "// +build !android\n\n"); } @@ -169,11 +169,12 @@ mkzasm(char *dir, char *file) { int i, n; char *aggr, *p; - Buf in, b, out, exp; + Buf in, b, b1, out, exp; Vec argv, lines, fields; binit(&in); binit(&b); + binit(&b1); binit(&out); binit(&exp); vinit(&argv); @@ -181,6 +182,10 @@ mkzasm(char *dir, char *file) vinit(&fields); bwritestr(&out, "// auto generated by go tool dist\n\n"); + if(streq(goos, "linux")) { + bwritestr(&out, "// +build !android\n\n"); + } + for(i=0; i<nelem(zasmhdr); i++) { if(hasprefix(goarch, zasmhdr[i].goarch) && hasprefix(goos, zasmhdr[i].goos)) { bwritestr(&out, zasmhdr[i].hdr); @@ -190,6 +195,9 @@ mkzasm(char *dir, char *file) fatal("unknown $GOOS/$GOARCH in mkzasm"); ok: + copyfile(bpathf(&b, "%s/pkg/%s_%s/textflag.h", goroot, goos, goarch), + bpathf(&b1, "%s/src/cmd/ld/textflag.h", goroot), 0); + // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c // to get acid [sic] output. Run once without the -a -o workdir/proc.acid in order to // report compilation failures (the -o redirects all messages, unfortunately). @@ -201,6 +209,8 @@ ok: vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); vadd(&argv, "-I"); vadd(&argv, bprintf(&b, "%s", workdir)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch)); vadd(&argv, "-n"); vadd(&argv, "-a"); vadd(&argv, "-o"); @@ -241,6 +251,8 @@ ok: aggr = "seh"; else if(streq(fields.p[1], "Alg")) aggr = "alg"; + else if(streq(fields.p[1], "Panic")) + aggr = "panic"; } if(hasprefix(lines.p[i], "}")) aggr = nil; @@ -273,6 +285,7 @@ ok: bfree(&in); bfree(&b); + bfree(&b1); bfree(&out); bfree(&exp); vfree(&argv); @@ -294,6 +307,10 @@ mkzsys(char *dir, char *file) binit(&out); bwritestr(&out, "// auto generated by go tool dist\n\n"); + if(streq(goos, "linux")) { + bwritestr(&out, "// +build !android\n\n"); + } + if(streq(goos, "windows")) { bwritef(&out, "// runtime·callbackasm is called by external code to\n" @@ -346,13 +363,24 @@ mkzruntimedefs(char *dir, char *file) vinit(&seen); bwritestr(&out, "// auto generated by go tool dist\n" - "\n" + "\n"); + + if(streq(goos, "linux")) { + bwritestr(&out, "// +build !android\n\n"); + } + + bwritestr(&out, "package runtime\n" "import \"unsafe\"\n" "var _ unsafe.Pointer\n" "\n" ); + // Do not emit definitions for these. + vadd(&seen, "true"); + vadd(&seen, "false"); + vadd(&seen, "raceenabled"); + vadd(&seen, "allgs"); // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs // on each of the runtimedefs C files. @@ -363,6 +391,8 @@ mkzruntimedefs(char *dir, char *file) vadd(&argv, bprintf(&b, "GOARCH_%s", goarch)); vadd(&argv, "-I"); vadd(&argv, bprintf(&b, "%s", workdir)); + vadd(&argv, "-I"); + vadd(&argv, bprintf(&b, "%s/pkg/%s_%s", goroot, goos, goarch)); vadd(&argv, "-q"); vadd(&argv, "-n"); vadd(&argv, "-o"); @@ -382,15 +412,15 @@ mkzruntimedefs(char *dir, char *file) splitlines(&lines, bstr(&in)); for(i=0; i<lines.len; i++) { p = lines.p[i]; - // Drop comment, func, and const lines. - if(hasprefix(p, "//") || hasprefix(p, "const") || hasprefix(p, "func")) + // Drop comment and func lines. + if(hasprefix(p, "//") || hasprefix(p, "func")) continue; // Note beginning of type or var decl, which can be multiline. // Remove duplicates. The linear check of seen here makes the // whole processing quadratic in aggregate, but there are only // about 100 declarations, so this is okay (and simple). - if(hasprefix(p, "type ") || hasprefix(p, "var ")) { + if(hasprefix(p, "type ") || hasprefix(p, "var ") || hasprefix(p, "const ")) { splitfields(&fields, p); if(fields.len < 2) continue; @@ -401,6 +431,17 @@ mkzruntimedefs(char *dir, char *file) } vadd(&seen, fields.p[1]); } + + // Const lines are printed in original case (usually upper). Add a leading _ as needed. + if(hasprefix(p, "const ")) { + if('A' <= p[6] && p[6] <= 'Z') + bwritestr(&out, "const _"); + else + bwritestr(&out, "const "); + bwritestr(&out, p+6); + continue; + } + if(skip) { if(hasprefix(p, "}")) skip = 0; @@ -409,6 +450,11 @@ mkzruntimedefs(char *dir, char *file) bwritestr(&out, p); } + + // Some windows specific const. + if(streq(goos, "windows")) { + bwritestr(&out, bprintf(&b, "const cb_max = %d\n", MAXWINCB)); + } writefile(&out, file, 0); diff --git a/src/cmd/dist/goc2c.c b/src/cmd/dist/goc2c.c deleted file mode 100644 index 72cbc1be6..000000000 --- a/src/cmd/dist/goc2c.c +++ /dev/null @@ -1,833 +0,0 @@ -// Copyright 2009 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 "a.h" - -/* - * Translate a .goc file into a .c file. A .goc file is a combination - * of a limited form of Go with C. - */ - -/* - package PACKAGENAME - {# line} - func NAME([NAME TYPE { , NAME TYPE }]) [(NAME TYPE { , NAME TYPE })] \{ - C code with proper brace nesting - \} -*/ - -/* - * We generate C code which implements the function such that it can - * be called from Go and executes the C code. - */ - -static char *input; -static Buf *output; -#define EOF -1 - -enum -{ - use64bitint = 1, -}; - -static int -xgetchar(void) -{ - int c; - - c = *input; - if(c == 0) - return EOF; - input++; - return c; -} - -static void -xungetc(void) -{ - input--; -} - -static void -xputchar(char c) -{ - bwrite(output, &c, 1); -} - -static int -xisspace(int c) -{ - return c == ' ' || c == '\t' || c == '\r' || c == '\n'; -} - -/* Whether we're emitting for gcc */ -static int gcc; - -/* File and line number */ -static const char *file; -static unsigned int lineno; - -/* List of names and types. */ -struct params { - struct params *next; - char *name; - char *type; -}; - -/* index into type_table */ -enum { - Bool, - Float, - Int, - Uint, - Uintptr, - String, - Slice, - Eface, - Complex128, - Float32, - Float64, -}; - -static struct { - char *name; - int size; - int rnd; // alignment -} type_table[] = { - /* - * variable sized first, for easy replacement. - * order matches enum above. - * default is 32-bit architecture sizes. - * spelling as in package runtime, so intgo/uintgo not int/uint. - */ - {"bool", 1}, - {"float", 4}, - {"intgo", 4}, - {"uintgo", 4}, - {"uintptr", 4}, - {"String", 8}, - {"Slice", 12}, - {"Eface", 8}, - {"Complex128", 16}, - - /* fixed size */ - {"float32", 4}, - {"float64", 8}, - {"byte", 1}, - {"int8", 1}, - {"uint8", 1}, - {"int16", 2}, - {"uint16", 2}, - {"int32", 4}, - {"rune", 4}, - {"uint32", 4}, - {"int64", 8}, - {"uint64", 8}, - - {nil, 0}, -}; - -/* Fixed structure alignment (non-gcc only) */ -int structround = 4; - -/* Unexpected EOF. */ -static void -bad_eof(void) -{ - fatal("%s:%d: unexpected EOF\n", file, lineno); -} - -/* Free a list of parameters. */ -static void -free_params(struct params *p) -{ - while (p != nil) { - struct params *next; - - next = p->next; - xfree(p->name); - xfree(p->type); - xfree(p); - p = next; - } -} - -/* Read a character, tracking lineno. */ -static int -getchar_update_lineno(void) -{ - int c; - - c = xgetchar(); - if (c == '\n') - ++lineno; - return c; -} - -/* Read a character, giving an error on EOF, tracking lineno. */ -static int -getchar_no_eof(void) -{ - int c; - - c = getchar_update_lineno(); - if (c == EOF) - bad_eof(); - return c; -} - -/* Read a character, skipping comments. */ -static int -getchar_skipping_comments(void) -{ - int c; - - while (1) { - c = getchar_update_lineno(); - if (c != '/') - return c; - - c = xgetchar(); - if (c == '/') { - do { - c = getchar_update_lineno(); - } while (c != EOF && c != '\n'); - return c; - } else if (c == '*') { - while (1) { - c = getchar_update_lineno(); - if (c == EOF) - return EOF; - if (c == '*') { - do { - c = getchar_update_lineno(); - } while (c == '*'); - if (c == '/') - break; - } - } - } else { - xungetc(); - return '/'; - } - } -} - -/* - * Read and return a token. Tokens are string or character literals - * or else delimited by whitespace or by [(),{}]. - * The latter are all returned as single characters. - */ -static char * -read_token(void) -{ - int c, q; - char *buf; - unsigned int alc, off; - char* delims = "(),{}"; - - while (1) { - c = getchar_skipping_comments(); - if (c == EOF) - return nil; - if (!xisspace(c)) - break; - } - alc = 16; - buf = xmalloc(alc + 1); - off = 0; - if(c == '"' || c == '\'') { - q = c; - buf[off] = c; - ++off; - while (1) { - if (off+2 >= alc) { // room for c and maybe next char - alc *= 2; - buf = xrealloc(buf, alc + 1); - } - c = getchar_no_eof(); - buf[off] = c; - ++off; - if(c == q) - break; - if(c == '\\') { - buf[off] = getchar_no_eof(); - ++off; - } - } - } else if (xstrrchr(delims, c) != nil) { - buf[off] = c; - ++off; - } else { - while (1) { - if (off >= alc) { - alc *= 2; - buf = xrealloc(buf, alc + 1); - } - buf[off] = c; - ++off; - c = getchar_skipping_comments(); - if (c == EOF) - break; - if (xisspace(c) || xstrrchr(delims, c) != nil) { - if (c == '\n') - lineno--; - xungetc(); - break; - } - } - } - buf[off] = '\0'; - return buf; -} - -/* Read a token, giving an error on EOF. */ -static char * -read_token_no_eof(void) -{ - char *token = read_token(); - if (token == nil) - bad_eof(); - return token; -} - -/* Read the package clause, and return the package name. */ -static char * -read_package(void) -{ - char *token; - - token = read_token_no_eof(); - if (token == nil) - fatal("%s:%d: no token\n", file, lineno); - if (!streq(token, "package")) { - fatal("%s:%d: expected \"package\", got \"%s\"\n", - file, lineno, token); - } - return read_token_no_eof(); -} - -/* Read and copy preprocessor lines. */ -static void -read_preprocessor_lines(void) -{ - int first; - - first = 1; - while (1) { - int c; - - do { - c = getchar_skipping_comments(); - } while (xisspace(c)); - if (c != '#') { - xungetc(); - break; - } - if(first) { - first = 0; - xputchar('\n'); - } - xputchar(c); - do { - c = getchar_update_lineno(); - xputchar(c); - } while (c != '\n'); - } -} - -/* - * Read a type in Go syntax and return a type in C syntax. We only - * permit basic types and pointers. - */ -static char * -read_type(void) -{ - char *p, *op, *q; - int pointer_count; - unsigned int len; - - p = read_token_no_eof(); - if (*p != '*' && !streq(p, "int") && !streq(p, "uint")) - return p; - op = p; - pointer_count = 0; - while (*p == '*') { - ++pointer_count; - ++p; - } - len = xstrlen(p); - q = xmalloc(len + 2 + pointer_count + 1); - xmemmove(q, p, len); - - // Turn int/uint into intgo/uintgo. - if((len == 3 && xmemcmp(q, "int", 3) == 0) || (len == 4 && xmemcmp(q, "uint", 4) == 0)) { - q[len++] = 'g'; - q[len++] = 'o'; - } - - while (pointer_count-- > 0) - q[len++] = '*'; - - q[len] = '\0'; - xfree(op); - return q; -} - -/* Return the size of the given type. */ -static int -type_size(char *p, int *rnd) -{ - int i; - - if(p[xstrlen(p)-1] == '*') { - *rnd = type_table[Uintptr].rnd; - return type_table[Uintptr].size; - } - - if(streq(p, "Iface")) - p = "Eface"; - - for(i=0; type_table[i].name; i++) - if(streq(type_table[i].name, p)) { - *rnd = type_table[i].rnd; - return type_table[i].size; - } - fatal("%s:%d: unknown type %s\n", file, lineno, p); - return 0; -} - -/* - * Read a list of parameters. Each parameter is a name and a type. - * The list ends with a ')'. We have already read the '('. - */ -static struct params * -read_params(int *poffset) -{ - char *token; - struct params *ret, **pp, *p; - int offset, size, rnd; - - ret = nil; - pp = &ret; - token = read_token_no_eof(); - offset = 0; - if (!streq(token, ")")) { - while (1) { - p = xmalloc(sizeof(struct params)); - p->name = token; - p->next = nil; - *pp = p; - pp = &p->next; - - if(streq(token, "...")) { - p->type = xstrdup(""); - } else { - p->type = read_type(); - rnd = 0; - size = type_size(p->type, &rnd); - if(rnd > structround) - rnd = structround; - if(offset%rnd) - offset += rnd - offset%rnd; - offset += size; - } - - token = read_token_no_eof(); - if (!streq(token, ",")) - break; - token = read_token_no_eof(); - } - } - if (!streq(token, ")")) { - fatal("%s:%d: expected '('\n", - file, lineno); - } - if (poffset != nil) - *poffset = offset; - return ret; -} - -/* - * Read a function header. This reads up to and including the initial - * '{' character. Returns 1 if it read a header, 0 at EOF. - */ -static int -read_func_header(char **name, struct params **params, int *paramwid, struct params **rets) -{ - int lastline; - char *token; - - lastline = -1; - while (1) { - read_preprocessor_lines(); - token = read_token(); - if (token == nil) - return 0; - if (streq(token, "func")) { - if(lastline != -1) - bwritef(output, "\n"); - break; - } - if (lastline != lineno) { - if (lastline == lineno-1) - bwritef(output, "\n"); - else - bwritef(output, "\n#line %d \"%s\"\n", lineno, file); - lastline = lineno; - } - bwritef(output, "%s ", token); - } - - *name = read_token_no_eof(); - - token = read_token(); - if (token == nil || !streq(token, "(")) { - fatal("%s:%d: expected \"(\"\n", - file, lineno); - } - *params = read_params(paramwid); - - token = read_token(); - if (token == nil || !streq(token, "(")) - *rets = nil; - else { - *rets = read_params(nil); - token = read_token(); - } - if (token == nil || !streq(token, "{")) { - fatal("%s:%d: expected \"{\"\n", - file, lineno); - } - return 1; -} - -/* Write out parameters. */ -static void -write_params(struct params *params, int *first) -{ - struct params *p; - - for (p = params; p != nil; p = p->next) { - if (*first) - *first = 0; - else - bwritef(output, ", "); - bwritef(output, "%s %s", p->type, p->name); - } -} - -/* Write a 6g function header. */ -static void -write_6g_func_header(char *package, char *name, struct params *params, - int paramwid, struct params *rets) -{ - int first, n; - struct params *p; - - bwritef(output, "void\n"); - if(!contains(name, "·")) - bwritef(output, "%s·", package); - bwritef(output, "%s(", name); - - first = 1; - write_params(params, &first); - - /* insert padding to align output struct */ - if(rets != nil && paramwid%structround != 0) { - n = structround - paramwid%structround; - if(n & 1) - bwritef(output, ", uint8"); - if(n & 2) - bwritef(output, ", uint16"); - if(n & 4) - bwritef(output, ", uint32"); - } - - write_params(rets, &first); - bwritef(output, ")\n{\n"); - - for (p = rets; p != nil; p = p->next) { - if(streq(p->name, "...")) - continue; - if(streq(p->type, "Slice")) - bwritef(output, "\t%s.array = 0;\n\t%s.len = 0;\n\t%s.cap = 0;\n", p->name, p->name, p->name); - else if(streq(p->type, "String")) - bwritef(output, "\t%s.str = 0;\n\t%s.len = 0;\n", p->name, p->name); - else if(streq(p->type, "Eface")) - bwritef(output, "\t%s.type = 0;\n\t%s.data = 0;\n", p->name, p->name); - else if(streq(p->type, "Iface")) - bwritef(output, "\t%s.tab = 0;\n\t%s.data = 0;\n", p->name, p->name); - else if(streq(p->type, "Complex128")) - bwritef(output, "\t%s.real = 0;\n\t%s.imag = 0;\n", p->name, p->name); - else - bwritef(output, "\t%s = 0;\n", p->name); - bwritef(output, "\tFLUSH(&%s);\n", p->name); - } -} - -/* Write a 6g function trailer. */ -static void -write_6g_func_trailer(struct params *rets) -{ - struct params *p; - - for (p = rets; p != nil; p = p->next) - if(!streq(p->name, "...")) - bwritef(output, "\tFLUSH(&%s);\n", p->name); - bwritef(output, "}\n"); -} - -/* Define the gcc function return type if necessary. */ -static void -define_gcc_return_type(char *package, char *name, struct params *rets) -{ - struct params *p; - - if (rets == nil || rets->next == nil) - return; - bwritef(output, "struct %s_%s_ret {\n", package, name); - for (p = rets; p != nil; p = p->next) - bwritef(output, " %s %s;\n", p->type, p->name); - bwritef(output, "};\n"); -} - -/* Write out the gcc function return type. */ -static void -write_gcc_return_type(char *package, char *name, struct params *rets) -{ - if (rets == nil) - bwritef(output, "void"); - else if (rets->next == nil) - bwritef(output, "%s", rets->type); - else - bwritef(output, "struct %s_%s_ret", package, name); -} - -/* Write out a gcc function header. */ -static void -write_gcc_func_header(char *package, char *name, struct params *params, - struct params *rets) -{ - int first; - struct params *p; - - define_gcc_return_type(package, name, rets); - write_gcc_return_type(package, name, rets); - bwritef(output, " %s_%s(", package, name); - first = 1; - write_params(params, &first); - bwritef(output, ") asm (\"%s.%s\");\n", package, name); - write_gcc_return_type(package, name, rets); - bwritef(output, " %s_%s(", package, name); - first = 1; - write_params(params, &first); - bwritef(output, ")\n{\n"); - for (p = rets; p != nil; p = p->next) - bwritef(output, " %s %s;\n", p->type, p->name); -} - -/* Write out a gcc function trailer. */ -static void -write_gcc_func_trailer(char *package, char *name, struct params *rets) -{ - if (rets == nil) { - // nothing to do - } - else if (rets->next == nil) - bwritef(output, "return %s;\n", rets->name); - else { - struct params *p; - - bwritef(output, " {\n struct %s_%s_ret __ret;\n", package, name); - for (p = rets; p != nil; p = p->next) - bwritef(output, " __ret.%s = %s;\n", p->name, p->name); - bwritef(output, " return __ret;\n }\n"); - } - bwritef(output, "}\n"); -} - -/* Write out a function header. */ -static void -write_func_header(char *package, char *name, - struct params *params, int paramwid, - struct params *rets) -{ - if (gcc) - write_gcc_func_header(package, name, params, rets); - else - write_6g_func_header(package, name, params, paramwid, rets); - bwritef(output, "#line %d \"%s\"\n", lineno, file); -} - -/* Write out a function trailer. */ -static void -write_func_trailer(char *package, char *name, - struct params *rets) -{ - if (gcc) - write_gcc_func_trailer(package, name, rets); - else - write_6g_func_trailer(rets); -} - -/* - * Read and write the body of the function, ending in an unnested } - * (which is read but not written). - */ -static void -copy_body(void) -{ - int nesting = 0; - while (1) { - int c; - - c = getchar_no_eof(); - if (c == '}' && nesting == 0) - return; - xputchar(c); - switch (c) { - default: - break; - case '{': - ++nesting; - break; - case '}': - --nesting; - break; - case '/': - c = getchar_update_lineno(); - xputchar(c); - if (c == '/') { - do { - c = getchar_no_eof(); - xputchar(c); - } while (c != '\n'); - } else if (c == '*') { - while (1) { - c = getchar_no_eof(); - xputchar(c); - if (c == '*') { - do { - c = getchar_no_eof(); - xputchar(c); - } while (c == '*'); - if (c == '/') - break; - } - } - } - break; - case '"': - case '\'': - { - int delim = c; - do { - c = getchar_no_eof(); - xputchar(c); - if (c == '\\') { - c = getchar_no_eof(); - xputchar(c); - c = '\0'; - } - } while (c != delim); - } - break; - } - } -} - -/* Process the entire file. */ -static void -process_file(void) -{ - char *package, *name, *p, *n; - struct params *params, *rets; - int paramwid; - - package = read_package(); - read_preprocessor_lines(); - while (read_func_header(&name, ¶ms, ¶mwid, &rets)) { - // name may have a package override already - n = xstrstr(name, "·"); - if(n != nil) { - p = xmalloc(n - name + 1); - xmemmove(p, name, n - name); - p[n - name] = 0; - n += xstrlen("·"); - } else { - p = package; - n = name; - } - write_func_header(p, n, params, paramwid, rets); - copy_body(); - write_func_trailer(p, n, rets); - xfree(name); - if(p != package) xfree(p); - free_params(params); - free_params(rets); - } - xfree(package); -} - -void -goc2c(char *goc, char *c) -{ - int i; - Buf in, out; - - binit(&in); - binit(&out); - - file = goc; - readfile(&in, goc); - - // TODO: set gcc=1 when using gcc - - if(!gcc) { - if(contains(goarch, "64p32")) { - type_table[Uintptr].size = 4; - type_table[Int].size = 4; - structround = 8; - } else if(contains(goarch, "64")) { - type_table[Uintptr].size = 8; - if(use64bitint) { - type_table[Int].size = 8; - } else { - type_table[Int].size = 4; - } - structround = 8; - } else { - // NOTE: These are set in the initializer, - // but they might have been changed by a - // previous invocation of goc2c, so we have - // to restore them. - type_table[Uintptr].size = 4; - type_table[Int].size = 4; - structround = 4; - } - - type_table[Uint].size = type_table[Int].size; - type_table[Slice].size = type_table[Uintptr].size+2*type_table[Int].size; - type_table[Eface].size = 2*type_table[Uintptr].size; - type_table[String].size = 2*type_table[Uintptr].size; - - for(i=0; i<nelem(type_table); i++) - type_table[i].rnd = type_table[i].size; - - type_table[String].rnd = type_table[Uintptr].rnd; - type_table[Slice].rnd = type_table[Uintptr].rnd; - type_table[Eface].rnd = type_table[Uintptr].rnd; - type_table[Complex128].rnd = type_table[Float64].rnd; - } - - bprintf(&out, "// auto generated by go tool dist\n// goos=%s goarch=%s\n\n", goos, goarch); - input = bstr(&in); - output = &out; - - lineno = 1; - process_file(); - - writefile(&out, c, 0); -} diff --git a/src/cmd/dist/plan9.c b/src/cmd/dist/plan9.c index 8d492ebc6..e4bf25147 100644 --- a/src/cmd/dist/plan9.c +++ b/src/cmd/dist/plan9.c @@ -23,7 +23,7 @@ bprintf(Buf *b, char *fmt, ...) { va_list arg; char buf[4096]; - + breset(b); va_start(arg, fmt); vsnprintf(buf, sizeof buf, fmt, arg); @@ -572,10 +572,10 @@ bool hassuffix(char *p, char *suffix) { int np, ns; - + np = strlen(p); ns = strlen(suffix); - return np >= ns && strcmp(p+np-ns, suffix) == 0; + return np >= ns && streq(p+np-ns, suffix); } // hasprefix reports whether p begins with prefix. diff --git a/src/cmd/dist/unix.c b/src/cmd/dist/unix.c index 668b189ca..893ed5cdd 100644 --- a/src/cmd/dist/unix.c +++ b/src/cmd/dist/unix.c @@ -431,7 +431,7 @@ xremoveall(char *p) } bfree(&b); - vfree(&dir); + vfree(&dir); } // xreaddir replaces dst with a list of the names of the files in dir. @@ -441,13 +441,13 @@ xreaddir(Vec *dst, char *dir) { DIR *d; struct dirent *dp; - + vreset(dst); d = opendir(dir); if(d == nil) fatal("opendir %s: %s", dir, strerror(errno)); while((dp = readdir(d)) != nil) { - if(strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + if(streq(dp->d_name, ".") || streq(dp->d_name, "..")) continue; vadd(dst, dp->d_name); } @@ -461,7 +461,7 @@ xworkdir(void) { Buf b; char *p; - + binit(&b); xgetenv(&b, "TMPDIR"); @@ -546,10 +546,10 @@ bool hassuffix(char *p, char *suffix) { int np, ns; - + np = strlen(p); ns = strlen(suffix); - return np >= ns && strcmp(p+np-ns, suffix) == 0; + return np >= ns && streq(p+np-ns, suffix); } // hasprefix reports whether p begins with prefix. @@ -716,7 +716,7 @@ main(int argc, char **argv) fatal("unknown architecture: %s", u.machine); } - if(strcmp(gohostarch, "arm") == 0) + if(streq(gohostarch, "arm")) maxnbg = 1; // The OS X 10.6 linker does not support external linking mode. @@ -728,7 +728,7 @@ main(int argc, char **argv) // // Roughly, OS X 10.N shows up as uname release (N+4), // so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12. - if(strcmp(gohostos, "darwin") == 0) { + if(streq(gohostos, "darwin")) { if(uname(&u) < 0) fatal("uname: %s", strerror(errno)); osx = atoi(u.release) - 4; diff --git a/src/cmd/dist/windows.c b/src/cmd/dist/windows.c index 2839c4bc5..1102adff5 100644 --- a/src/cmd/dist/windows.c +++ b/src/cmd/dist/windows.c @@ -770,10 +770,10 @@ bool hassuffix(char *p, char *suffix) { int np, ns; - + np = strlen(p); ns = strlen(suffix); - return np >= ns && strcmp(p+np-ns, suffix) == 0; + return np >= ns && streq(p+np-ns, suffix); } bool diff --git a/src/cmd/fix/doc.go b/src/cmd/fix/doc.go index 5de3e08c5..057016957 100644 --- a/src/cmd/fix/doc.go +++ b/src/cmd/fix/doc.go @@ -27,7 +27,7 @@ rewrites are idempotent, so that it is safe to apply fix to updated or partially updated code even without using the -r flag. Fix prints the full list of fixes it can apply in its help output; -to see them, run go tool fix -?. +to see them, run go tool fix -help. Fix does not make backup copies of the files that it edits. Instead, use a version control system's ``diff'' functionality to inspect diff --git a/src/cmd/gc/builtin.c b/src/cmd/gc/builtin.c index 4808269b7..60b7c2f97 100644 --- a/src/cmd/gc/builtin.c +++ b/src/cmd/gc/builtin.c @@ -9,11 +9,12 @@ char *runtimeimport = "func @\"\".throwreturn ()\n" "func @\"\".throwinit ()\n" "func @\"\".panicwrap (? string, ? string, ? string)\n" - "func @\"\".panic (? interface {})\n" - "func @\"\".recover (? *int32) (? interface {})\n" + "func @\"\".gopanic (? interface {})\n" + "func @\"\".gorecover (? *int32) (? interface {})\n" "func @\"\".printbool (? bool)\n" "func @\"\".printfloat (? float64)\n" "func @\"\".printint (? int64)\n" + "func @\"\".printhex (? uint64)\n" "func @\"\".printuint (? uint64)\n" "func @\"\".printcomplex (? complex128)\n" "func @\"\".printstring (? string)\n" @@ -64,7 +65,6 @@ char *runtimeimport = "func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" "func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" "func @\"\".efacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" - "func @\"\".equal (@\"\".typ·2 *byte, @\"\".x1·3 any, @\"\".x2·4 any) (@\"\".ret·1 bool)\n" "func @\"\".makemap (@\"\".mapType·2 *byte, @\"\".hint·3 int64) (@\"\".hmap·1 map[any]any)\n" "func @\"\".mapaccess1 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 *any) (@\"\".val·1 *any)\n" "func @\"\".mapaccess1_fast32 (@\"\".mapType·2 *byte, @\"\".hmap·3 map[any]any, @\"\".key·4 any) (@\"\".val·1 *any)\n" @@ -96,12 +96,12 @@ char *runtimeimport = "func @\"\".makeslice (@\"\".typ·2 *byte, @\"\".nel·3 int64, @\"\".cap·4 int64) (@\"\".ary·1 []any)\n" "func @\"\".growslice (@\"\".typ·2 *byte, @\"\".old·3 []any, @\"\".n·4 int64) (@\"\".ary·1 []any)\n" "func @\"\".memmove (@\"\".to·1 *any, @\"\".frm·2 *any, @\"\".length·3 uintptr)\n" - "func @\"\".memequal (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" - "func @\"\".memequal8 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" - "func @\"\".memequal16 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" - "func @\"\".memequal32 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" - "func @\"\".memequal64 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" - "func @\"\".memequal128 (@\"\".eq·1 *bool, @\"\".size·2 uintptr, @\"\".x·3 *any, @\"\".y·4 *any)\n" + "func @\"\".memequal (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + "func @\"\".memequal8 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + "func @\"\".memequal16 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + "func @\"\".memequal32 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + "func @\"\".memequal64 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" + "func @\"\".memequal128 (@\"\".x·2 *any, @\"\".y·3 *any, @\"\".size·4 uintptr) (? bool)\n" "func @\"\".int64div (? int64, ? int64) (? int64)\n" "func @\"\".uint64div (? uint64, ? uint64) (? uint64)\n" "func @\"\".int64mod (? int64, ? int64) (? int64)\n" diff --git a/src/cmd/gc/bv.c b/src/cmd/gc/bv.c index 0e8f8d473..cfd1cd281 100644 --- a/src/cmd/gc/bv.c +++ b/src/cmd/gc/bv.c @@ -108,6 +108,9 @@ bvnext(Bvec *bv, int32 i) { uint32 w; + if(i >= bv->n) + return -1; + // Jump i ahead to next word with bits. if((bv->b[i>>WORDSHIFT]>>(i&WORDMASK)) == 0) { i &= ~WORDMASK; @@ -117,7 +120,7 @@ bvnext(Bvec *bv, int32 i) } if(i >= bv->n) return -1; - + // Find 1 bit. w = bv->b[i>>WORDSHIFT]>>(i&WORDMASK); while((w&1) == 0) { diff --git a/src/cmd/gc/const.c b/src/cmd/gc/const.c index c01784a81..e418b9c56 100644 --- a/src/cmd/gc/const.c +++ b/src/cmd/gc/const.c @@ -1566,7 +1566,6 @@ isgoconst(Node *n) case ORSH: case OSUB: case OXOR: - case OCONV: case OIOTA: case OCOMPLEX: case OREAL: @@ -1574,7 +1573,12 @@ isgoconst(Node *n) if(isgoconst(n->left) && (n->right == N || isgoconst(n->right))) return 1; break; - + + case OCONV: + if(okforconst[n->type->etype] && isgoconst(n->left)) + return 1; + break; + case OLEN: case OCAP: l = n->left; diff --git a/src/cmd/gc/fmt.c b/src/cmd/gc/fmt.c index 951170aef..98556a658 100644 --- a/src/cmd/gc/fmt.c +++ b/src/cmd/gc/fmt.c @@ -1153,7 +1153,7 @@ exprfmt(Fmt *f, Node *n, int prec) case Csend: return fmtprint(f, "chan<- %N", n->left); default: - if(n->left != N && n->left->op == TCHAN && n->left->sym == S && n->left->etype == Crecv) + if(n->left != N && n->left->op == OTCHAN && n->left->sym == S && n->left->etype == Crecv) return fmtprint(f, "chan (%N)", n->left); else return fmtprint(f, "chan %N", n->left); diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 908a5e53d..86acd8825 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -806,7 +806,8 @@ cgen_eface(Node *n, Node *res) void cgen_slice(Node *n, Node *res) { - Node src, dst, *cap, *len, *offs, *add, *base; + Node src, dst, *cap, *len, *offs, *add, *base, *tmpcap, *tmplen, *cmp, con; + Prog *p1, *p2; cap = n->list->n; len = n->list->next->n; @@ -823,6 +824,11 @@ cgen_slice(Node *n, Node *res) // garbage collector can see. base = temp(types[TUINTPTR]); + tmplen = temp(types[TINT]); + if(n->op != OSLICESTR) + tmpcap = temp(types[TINT]); + else + tmpcap = tmplen; if(isnil(n->left)) { tempname(&src, n->left->type); @@ -837,43 +843,62 @@ cgen_slice(Node *n, Node *res) fatal("slicearr is supposed to work on pointer: %+N\n", n); cgen(&src, base); cgen_checknil(base); - if(offs != N) { - add = nod(OADD, base, offs); - typecheck(&add, Erv); - cgen(add, base); - } - } else if(offs == N) { - src.type = types[tptr]; - cgen(&src, base); } else { src.type = types[tptr]; - add = nod(OADDPTR, &src, offs); - typecheck(&add, Erv); - cgen(add, base); + cgen(&src, base); } // committed to the update gvardef(res); + // compute len and cap. + // len = n-i, cap = m-i, and offs = i*width. + // computing offs last lets the multiply overwrite i. + cgen(len, tmplen); + if(n->op != OSLICESTR) + cgen(cap, tmpcap); + + // if new cap != 0 { base += add } + // This avoids advancing base past the end of the underlying array/string, + // so that it cannot point at the next object in memory. + // If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero. + // In essence we are replacing x[i:j:k] where i == j == k + // or x[i:j] where i == j == cap(x) with x[0:0:0]. + if(offs != N) { + p1 = gjmp(P); + p2 = gjmp(P); + patch(p1, pc); + + nodconst(&con, tmpcap->type, 0); + cmp = nod(OEQ, tmpcap, &con); + typecheck(&cmp, Erv); + bgen(cmp, 1, -1, p2); + + add = nod(OADD, base, offs); + typecheck(&add, Erv); + cgen(add, base); + + patch(p2, pc); + } + // dst.array = src.array [ + lo *width ] dst = *res; dst.xoffset += Array_array; dst.type = types[tptr]; - cgen(base, &dst); // dst.len = hi [ - lo ] dst = *res; dst.xoffset += Array_nel; dst.type = types[simtype[TUINT]]; - cgen(len, &dst); + cgen(tmplen, &dst); if(n->op != OSLICESTR) { // dst.cap = cap [ - lo ] dst = *res; dst.xoffset += Array_cap; dst.type = types[simtype[TUINT]]; - cgen(cap, &dst); + cgen(tmpcap, &dst); } } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index c3da5f636..12c1e9853 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -447,7 +447,6 @@ enum OSUB, // x - y OOR, // x | y OXOR, // x ^ y - OADDPTR, // ptr + uintptr, inserted by compiler only, used to avoid unsafe type changes during codegen OADDSTR, // s + "foo" OADDR, // &x OANDAND, // b0 && b1 @@ -1169,6 +1168,7 @@ void cgen_callmeth(Node *n, int proc); void cgen_eface(Node* n, Node* res); void cgen_slice(Node* n, Node* res); void clearlabels(void); +void clearslim(Node*); void checklabels(void); int dotoffset(Node *n, int64 *oary, Node **nn); void gen(Node *n); @@ -1363,6 +1363,7 @@ int is64(Type *t); int isbadimport(Strlit *s); int isblank(Node *n); int isblanksym(Sym *s); +int isdirectiface(Type*); int isfixedarray(Type *t); int isideal(Type *t); int isinter(Type *t); diff --git a/src/cmd/gc/mparith1.c b/src/cmd/gc/mparith1.c index 1519caec7..d33a81e09 100644 --- a/src/cmd/gc/mparith1.c +++ b/src/cmd/gc/mparith1.c @@ -591,7 +591,7 @@ Fconv(Fmt *fp) d = mpgetflt(fvp); if(d >= 0 && (fp->flags & FmtSign)) fmtprint(fp, "+"); - return fmtprint(fp, "%g", d, exp, fvp); + return fmtprint(fp, "%g", d); } // very out of range. compute decimal approximation by hand. diff --git a/src/cmd/gc/mparith3.c b/src/cmd/gc/mparith3.c index 95618f1c6..6afd75c02 100644 --- a/src/cmd/gc/mparith3.c +++ b/src/cmd/gc/mparith3.c @@ -251,8 +251,8 @@ mpgetfltN(Mpflt *a, int prec, int bias) s = minexp - e; if(s > prec+1) s = prec+1; - if((v & ((1<<s)-1)) != 0) - v |= 1<<s; + if((v & ((1ULL<<s)-1)) != 0) + v |= 1ULL<<s; v >>= s; e = minexp; } diff --git a/src/cmd/gc/order.c b/src/cmd/gc/order.c index 30dbc7dac..d11e9828c 100644 --- a/src/cmd/gc/order.c +++ b/src/cmd/gc/order.c @@ -593,7 +593,10 @@ orderstmt(Node *n, Order *order) orderexpr(&n->rlist->n->left, order); // arg to recv ch = n->rlist->n->left->type; tmp1 = ordertemp(ch->type, order, haspointers(ch->type)); - tmp2 = ordertemp(types[TBOOL], order, 0); + if(!isblank(n->list->next->n)) + tmp2 = ordertemp(n->list->next->n->type, order, 0); + else + tmp2 = ordertemp(types[TBOOL], order, 0); order->out = list(order->out, n); r = nod(OAS, n->list->n, tmp1); typecheck(&r, Etop); @@ -768,6 +771,12 @@ orderstmt(Node *n, Order *order) // Special: clean case temporaries in each block entry. // Select must enter one of its blocks, so there is no // need for a cleaning at the end. + // Doubly special: evaluation order for select is stricter + // than ordinary expressions. Even something like p.c + // has to be hoisted into a temporary, so that it cannot be + // reordered after the channel evaluation for a different + // case (if p were nil, then the timing of the fault would + // give this away). t = marktemp(order); for(l=n->list; l; l=l->next) { if(l->n->op != OXCASE) @@ -810,6 +819,8 @@ orderstmt(Node *n, Order *order) // r->left == N means 'case <-c'. // c is always evaluated; x and ok are only evaluated when assigned. orderexpr(&r->right->left, order); + if(r->right->left->op != ONAME) + r->right->left = ordercopyexpr(r->right->left, r->right->left->type, order, 0); // Introduce temporary for receive and move actual copy into case body. // avoids problems with target being addressed, as usual. @@ -1055,6 +1066,19 @@ orderexpr(Node **np, Order *order) orderexpr(&n->left, order); n = ordercopyexpr(n, n->type, order, 1); break; + + case OEQ: + case ONE: + orderexpr(&n->left, order); + orderexpr(&n->right, order); + t = n->left->type; + if(t->etype == TSTRUCT || isfixedarray(t)) { + // for complex comparisons, we need both args to be + // addressable so we can pass them to the runtime. + orderaddrtemp(&n->left, order); + orderaddrtemp(&n->right, order); + } + break; } lineno = lno; diff --git a/src/cmd/gc/plive.c b/src/cmd/gc/plive.c index 716cdd108..dc4edad63 100644 --- a/src/cmd/gc/plive.c +++ b/src/cmd/gc/plive.c @@ -1113,8 +1113,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) // struct { byte *str; intgo len; } if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 3:0 = multiword:string + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot *xoffset += t->width; break; @@ -1145,9 +1144,7 @@ twobitwalktype1(Type *t, vlong *xoffset, Bvec *bv) // struct { byte *array; uintgo len; uintgo cap; } if((*xoffset & (widthptr-1)) != 0) fatal("twobitwalktype1: invalid TARRAY alignment, %T", t); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 0); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); - bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 2); // 3:1 = multiword/slice + bvset(bv, (*xoffset / widthptr) * BitsPerPointer + 1); // 2 = live ptr in first slot *xoffset += t->width; } else for(i = 0; i < t->bound; i++) diff --git a/src/cmd/gc/popt.c b/src/cmd/gc/popt.c index 4c75e6c26..6e6db88ef 100644 --- a/src/cmd/gc/popt.c +++ b/src/cmd/gc/popt.c @@ -49,7 +49,7 @@ noreturn(Prog *p) symlist[0] = pkglookup("panicindex", runtimepkg); symlist[1] = pkglookup("panicslice", runtimepkg); symlist[2] = pkglookup("throwinit", runtimepkg); - symlist[3] = pkglookup("panic", runtimepkg); + symlist[3] = pkglookup("gopanic", runtimepkg); symlist[4] = pkglookup("panicwrap", runtimepkg); symlist[5] = pkglookup("throwreturn", runtimepkg); symlist[6] = pkglookup("selectgo", runtimepkg); diff --git a/src/cmd/gc/racewalk.c b/src/cmd/gc/racewalk.c index 285bd78a2..27581702c 100644 --- a/src/cmd/gc/racewalk.c +++ b/src/cmd/gc/racewalk.c @@ -419,8 +419,10 @@ racewalknode(Node **np, NodeList **init, int wr, int skip) ret: if(n->op != OBLOCK) // OBLOCK is handled above in a special way. racewalklist(n->list, init); - racewalknode(&n->ntest, &n->ntest->ninit, 0, 0); - racewalknode(&n->nincr, &n->nincr->ninit, 0, 0); + if(n->ntest != N) + racewalknode(&n->ntest, &n->ntest->ninit, 0, 0); + if(n->nincr != N) + racewalknode(&n->nincr, &n->nincr->ninit, 0, 0); racewalklist(n->nbody, nil); racewalklist(n->nelse, nil); racewalklist(n->rlist, nil); diff --git a/src/cmd/gc/reflect.c b/src/cmd/gc/reflect.c index 8170c15b6..f227054ca 100644 --- a/src/cmd/gc/reflect.c +++ b/src/cmd/gc/reflect.c @@ -378,7 +378,7 @@ methods(Type *t) // type stored in interface word it = t; - if(it->width > widthptr) + if(!isdirectiface(it)) it = ptrto(t); // make list of methods for t, @@ -724,7 +724,7 @@ dcommontype(Sym *s, int ot, Type *t) if(ot != 0) fatal("dcommontype %d", ot); - sizeofAlg = 4*widthptr; + sizeofAlg = 2*widthptr; if(algarray == nil) algarray = pkglookup("algarray", runtimepkg); alg = algtype(t); @@ -785,6 +785,8 @@ dcommontype(Sym *s, int ot, Type *t) i = KindSlice; if(!haspointers(t)) i |= KindNoPointers; + if(isdirectiface(t)) + i |= KindDirectIface; if(gcprog) i |= KindGCProg; ot = duint8(s, ot, i); // kind @@ -1239,8 +1241,7 @@ static Sym* dalgsym(Type *t) { int ot; - Sym *s, *hash, *hashfunc, *eq; - char buf[100]; + Sym *s, *hash, *hashfunc, *eq, *eqfunc; // dalgsym is only called for a type that needs an algorithm table, // which implies that the type is comparable (or else it would use ANOEQ). @@ -1251,29 +1252,18 @@ dalgsym(Type *t) eq = typesymprefix(".eq", t); geneq(eq, t); - // make Go func (a closure) for calling the hash function from Go + // make Go funcs (closures) for calling hash and equal from Go hashfunc = typesymprefix(".hashfunc", t); dsymptr(hashfunc, 0, hash, 0); ggloblsym(hashfunc, widthptr, DUPOK|RODATA); + eqfunc = typesymprefix(".eqfunc", t); + dsymptr(eqfunc, 0, eq, 0); + ggloblsym(eqfunc, widthptr, DUPOK|RODATA); - // ../../pkg/runtime/runtime.h:/Alg + // ../../pkg/runtime/alg.go:/typeAlg ot = 0; ot = dsymptr(s, ot, hashfunc, 0); - ot = dsymptr(s, ot, eq, 0); - ot = dsymptr(s, ot, pkglookup("memprint", runtimepkg), 0); - switch(t->width) { - default: - ot = dsymptr(s, ot, pkglookup("memcopy", runtimepkg), 0); - break; - case 1: - case 2: - case 4: - case 8: - case 16: - snprint(buf, sizeof buf, "memcopy%d", (int)t->width*8); - ot = dsymptr(s, ot, pkglookup(buf, runtimepkg), 0); - break; - } + ot = dsymptr(s, ot, eqfunc, 0); ggloblsym(s, ot, DUPOK|RODATA); return s; @@ -1511,8 +1501,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset) *xoffset += t->width; break; case TSTRING: - proggendata(g, BitsMultiWord); - proggendata(g, BitsString); + proggendata(g, BitsPointer); + proggendata(g, BitsScalar); *xoffset += t->width; break; case TINTER: @@ -1525,8 +1515,8 @@ gengcprog1(ProgGen *g, Type *t, vlong *xoffset) break; case TARRAY: if(isslice(t)) { - proggendata(g, BitsMultiWord); - proggendata(g, BitsSlice); + proggendata(g, BitsPointer); + proggendata(g, BitsScalar); proggendata(g, BitsScalar); } else { t1 = t->type; diff --git a/src/cmd/gc/runtime.go b/src/cmd/gc/runtime.go index 0257c3c7d..128fd1a31 100644 --- a/src/cmd/gc/runtime.go +++ b/src/cmd/gc/runtime.go @@ -20,12 +20,13 @@ func throwreturn() func throwinit() func panicwrap(string, string, string) -func panic(interface{}) -func recover(*int32) interface{} +func gopanic(interface{}) +func gorecover(*int32) interface{} func printbool(bool) func printfloat(float64) func printint(int64) +func printhex(uint64) func printuint(uint64) func printcomplex(complex128) func printstring(string) @@ -84,8 +85,6 @@ func efaceeq(i1 any, i2 any) (ret bool) func ifacethash(i1 any) (ret uint32) func efacethash(i1 any) (ret uint32) -func equal(typ *byte, x1, x2 any) (ret bool) - // *byte is really *runtime.Type func makemap(mapType *byte, hint int64) (hmap map[any]any) func mapaccess1(mapType *byte, hmap map[any]any, key *any) (val *any) @@ -124,12 +123,12 @@ func makeslice(typ *byte, nel int64, cap int64) (ary []any) func growslice(typ *byte, old []any, n int64) (ary []any) func memmove(to *any, frm *any, length uintptr) -func memequal(eq *bool, size uintptr, x, y *any) -func memequal8(eq *bool, size uintptr, x, y *any) -func memequal16(eq *bool, size uintptr, x, y *any) -func memequal32(eq *bool, size uintptr, x, y *any) -func memequal64(eq *bool, size uintptr, x, y *any) -func memequal128(eq *bool, size uintptr, x, y *any) +func memequal(x, y *any, size uintptr) bool +func memequal8(x, y *any, size uintptr) bool +func memequal16(x, y *any, size uintptr) bool +func memequal32(x, y *any, size uintptr) bool +func memequal64(x, y *any, size uintptr) bool +func memequal128(x, y *any, size uintptr) bool // only used on 32-bit func int64div(int64, int64) int64 diff --git a/src/cmd/gc/select.c b/src/cmd/gc/select.c index 7168e6b80..ed23e4318 100644 --- a/src/cmd/gc/select.c +++ b/src/cmd/gc/select.c @@ -337,19 +337,23 @@ selecttype(int32 size) sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("g")), typenod(ptrto(types[TUINT8])))); sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("selectdone")), typenod(ptrto(types[TUINT8])))); sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("link")), typenod(ptrto(types[TUINT8])))); + sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("prev")), typenod(ptrto(types[TUINT8])))); sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8])))); sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64]))); + sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("nrelease")), typenod(types[TINT32]))); + sudog->list = list(sudog->list, nod(ODCLFIELD, newname(lookup("waitlink")), typenod(ptrto(types[TUINT8])))); typecheck(&sudog, Etype); sudog->type->noalg = 1; sudog->type->local = 1; scase = nod(OTSTRUCT, N, N); - scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("sg")), sudog)); + scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("elem")), typenod(ptrto(types[TUINT8])))); scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("chan")), typenod(ptrto(types[TUINT8])))); scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("pc")), typenod(types[TUINTPTR]))); scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("kind")), typenod(types[TUINT16]))); scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("so")), typenod(types[TUINT16]))); scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("receivedp")), typenod(ptrto(types[TUINT8])))); + scase->list = list(scase->list, nod(ODCLFIELD, newname(lookup("releasetime")), typenod(types[TUINT64]))); typecheck(&scase, Etype); scase->type->noalg = 1; scase->type->local = 1; diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 0195f3d62..d62d55e77 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -656,11 +656,15 @@ maptype(Type *key, Type *val) { Type *t; Type *bad; - int atype; + int atype, mtype; if(key != nil) { atype = algtype1(key, &bad); - switch(bad == T ? key->etype : bad->etype) { + if(bad == T) + mtype = key->etype; + else + mtype = bad->etype; + switch(mtype) { default: if(atype == ANOEQ) yyerror("invalid map key type %T", key); @@ -2856,18 +2860,19 @@ genhash(Sym *sym, Type *t) } // Return node for -// if p.field != q.field { *eq = false; return } +// if p.field != q.field { return false } static Node* -eqfield(Node *p, Node *q, Node *field, Node *eq) +eqfield(Node *p, Node *q, Node *field) { - Node *nif, *nx, *ny; + Node *nif, *nx, *ny, *r; nx = nod(OXDOT, p, field); ny = nod(OXDOT, q, field); nif = nod(OIF, N, N); nif->ntest = nod(ONE, nx, ny); - nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, eq, N), nodbool(0))); - nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + r = nod(ORETURN, N, N); + r->list = list(r->list, nodbool(0)); + nif->nbody = list(nif->nbody, r); return nif; } @@ -2896,11 +2901,11 @@ eqmemfunc(vlong size, Type *type) } // Return node for -// if memequal(size, &p.field, &q.field, eq); !*eq { return } +// if !memequal(&p.field, &q.field, size) { return false } static Node* -eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq) +eqmem(Node *p, Node *q, Node *field, vlong size) { - Node *nif, *nx, *ny, *call; + Node *nif, *nx, *ny, *call, *r; nx = nod(OADDR, nod(OXDOT, p, field), N); nx->etype = 1; // does not escape @@ -2910,15 +2915,16 @@ eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq) typecheck(&ny, Erv); call = nod(OCALL, eqmemfunc(size, nx->type->type), N); - call->list = list(call->list, eq); - call->list = list(call->list, nodintconst(size)); call->list = list(call->list, nx); call->list = list(call->list, ny); + call->list = list(call->list, nodintconst(size)); nif = nod(OIF, N, N); nif->ninit = list(nif->ninit, call); - nif->ntest = nod(ONOT, nod(OIND, eq, N), N); - nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + nif->ntest = nod(ONOT, call, N); + r = nod(ORETURN, N, N); + r->list = list(r->list, nodbool(0)); + nif->nbody = list(nif->nbody, r); return nif; } @@ -2928,7 +2934,7 @@ eqmem(Node *p, Node *q, Node *field, vlong size, Node *eq) void geneq(Sym *sym, Type *t) { - Node *n, *fn, *np, *neq, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange; + Node *n, *fn, *np, *nq, *tfn, *nif, *ni, *nx, *ny, *nrange, *r; Type *t1, *first; int old_safemode; int64 size; @@ -2941,24 +2947,23 @@ geneq(Sym *sym, Type *t) dclcontext = PEXTERN; markdcl(); - // func sym(eq *bool, s uintptr, p, q *T) + // func sym(p, q *T, s uintptr) bool fn = nod(ODCLFUNC, N, N); fn->nname = newname(sym); fn->nname->class = PFUNC; tfn = nod(OTFUNC, N, N); fn->nname->ntype = tfn; - n = nod(ODCLFIELD, newname(lookup("eq")), typenod(ptrto(types[TBOOL]))); - tfn->list = list(tfn->list, n); - neq = n->left; - n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR])); - tfn->list = list(tfn->list, n); n = nod(ODCLFIELD, newname(lookup("p")), typenod(ptrto(t))); tfn->list = list(tfn->list, n); np = n->left; n = nod(ODCLFIELD, newname(lookup("q")), typenod(ptrto(t))); tfn->list = list(tfn->list, n); nq = n->left; + n = nod(ODCLFIELD, newname(lookup("s")), typenod(types[TUINTPTR])); + tfn->list = list(tfn->list, n); + n = nod(ODCLFIELD, N, typenod(types[TBOOL])); + tfn->rlist = list(tfn->rlist, n); funchdr(fn); @@ -2984,7 +2989,7 @@ geneq(Sym *sym, Type *t) colasdefn(nrange->list, nrange); ni = nrange->list->n; - // if p[i] != q[i] { *eq = false; return } + // if p[i] != q[i] { return false } nx = nod(OINDEX, np, ni); nx->bounded = 1; ny = nod(OINDEX, nq, ni); @@ -2992,13 +2997,11 @@ geneq(Sym *sym, Type *t) nif = nod(OIF, N, N); nif->ntest = nod(ONE, nx, ny); - nif->nbody = list(nif->nbody, nod(OAS, nod(OIND, neq, N), nodbool(0))); - nif->nbody = list(nif->nbody, nod(ORETURN, N, N)); + r = nod(ORETURN, N, N); + r->list = list(r->list, nodbool(0)); + nif->nbody = list(nif->nbody, r); nrange->nbody = list(nrange->nbody, nif); fn->nbody = list(fn->nbody, nrange); - - // *eq = true; - fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1))); break; case TSTRUCT: @@ -3023,16 +3026,16 @@ geneq(Sym *sym, Type *t) // cross-package unexported fields. if(first != T) { if(first->down == t1) { - fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym))); } else if(first->down->down == t1) { - fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym))); first = first->down; if(!isblanksym(first->sym)) - fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym), neq)); + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(first->sym))); } else { // More than two fields: use memequal. size = offend - first->width; // first->width is offset - fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size, neq)); + fn->nbody = list(fn->nbody, eqmem(np, nq, newname(first->sym), size)); } first = T; } @@ -3042,14 +3045,17 @@ geneq(Sym *sym, Type *t) continue; // Check this field, which is not just memory. - fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym), neq)); + fn->nbody = list(fn->nbody, eqfield(np, nq, newname(t1->sym))); } - // *eq = true; - fn->nbody = list(fn->nbody, nod(OAS, nod(OIND, neq, N), nodbool(1))); break; } + // return true + r = nod(ORETURN, N, N); + r->list = list(r->list, nodbool(1)); + fn->nbody = list(fn->nbody, r); + if(debug['r']) dumplist("geneq body", fn->nbody); @@ -3792,3 +3798,42 @@ checknil(Node *x, NodeList **init) n->typecheck = 1; *init = list(*init, n); } + +/* + * Can this type be stored directly in an interface word? + */ +int +isdirectiface(Type *t) +{ + // Setting IfacePointerOnly = 1 changes the + // interface representation so that the data word + // in an interface value must always be a pointer. + // Setting it to 0 uses the original representation, + // where the data word can hold a pointer or any + // non-pointer value no bigger than a pointer. + enum { + IfacePointerOnly = 1, + }; + + if(IfacePointerOnly) { + switch(t->etype) { + case TPTR32: + case TPTR64: + case TCHAN: + case TMAP: + case TFUNC: + case TUNSAFEPTR: + return 1; + case TARRAY: + // Array of 1 direct iface type can be direct. + return t->bound == 1 && isdirectiface(t->type); + case TSTRUCT: + // Struct with 1 field of direct iface type can be direct. + return t->type != T && t->type->down == T && isdirectiface(t->type->type); + } + return 0; + } + + dowidth(t); + return t->width <= widthptr; +} diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 92e9ad521..746feb4d1 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -525,19 +525,6 @@ reswitch: op = n->etype; goto arith; - case OADDPTR: - ok |= Erv; - l = typecheck(&n->left, Erv); - r = typecheck(&n->right, Erv); - if(l->type == T || r->type == T) - goto error; - if(l->type->etype != tptr) - fatal("bad OADDPTR left type %E for %N", l->type->etype, n->left); - if(r->type->etype != TUINTPTR) - fatal("bad OADDPTR right type %E for %N", r->type->etype, n->right); - n->type = types[tptr]; - goto ret; - case OADD: case OAND: case OANDAND: @@ -2965,7 +2952,7 @@ typecheckas2(Node *n) if(l->defn == n) l->type = r->type; l = n->list->next->n; - if(l->type != T) + if(l->type != T && l->type->etype != TBOOL) checkassignto(types[TBOOL], l); if(l->defn == n && l->ntype == N) l->type = types[TBOOL]; diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index cf25a3eac..365ece267 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -543,11 +543,11 @@ walkexpr(Node **np, NodeList **init) goto ret; case OPANIC: - n = mkcall("panic", T, init, n->left); + n = mkcall("gopanic", T, init, n->left); goto ret; case ORECOVER: - n = mkcall("recover", n->type, init, nod(OADDR, nodfp, N)); + n = mkcall("gorecover", n->type, init, nod(OADDR, nodfp, N)); goto ret; case OLITERAL: @@ -673,7 +673,7 @@ walkexpr(Node **np, NodeList **init) n1 = nod(OADDR, n->list->n, N); n1->etype = 1; // addr does not escape fn = chanfn("chanrecv2", 2, r->left->type); - r = mkcall1(fn, types[TBOOL], init, typename(r->left->type), r->left, n1); + r = mkcall1(fn, n->list->next->n->type, init, typename(r->left->type), r->left, n1); n = nod(OAS, n->list->next->n, r); typecheck(&n, Etop); goto ret; @@ -723,6 +723,12 @@ walkexpr(Node **np, NodeList **init) var->typecheck = 1; fn = mapfn(p, t); r = mkcall1(fn, getoutargx(fn->type), init, typename(t), r->left, key); + + // mapaccess2* returns a typed bool, but due to spec changes, + // the boolean result of i.(T) is now untyped so we make it the + // same type as the variable on the lhs. + if(!isblank(n->list->next->n)) + r->type->type->down->type = n->list->next->n->type; n->rlist = list1(r); n->op = OAS2FUNC; n->list->n = var; @@ -770,6 +776,12 @@ walkexpr(Node **np, NodeList **init) *p = '\0'; fn = syslook(buf, 1); + + // runtime.assert(E|I)2TOK returns a typed bool, but due + // to spec changes, the boolean result of i.(T) is now untyped + // so we make it the same type as the variable on the lhs. + if(!isblank(n->list->next->n)) + fn->type->type->down->type->type = n->list->next->n->type; ll = list1(typename(r->type)); ll = list(ll, r->left); argtype(fn, r->left->type); @@ -822,9 +834,7 @@ walkexpr(Node **np, NodeList **init) walkexpr(&n->left, init); // Optimize convT2E as a two-word copy when T is uintptr-shaped. - if(!isinter(n->left->type) && isnilinter(n->type) && - (n->left->type->width == widthptr) && - isint[simsimtype(n->left->type)]) { + if(isnilinter(n->type) && isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) { l = nod(OEFACE, typename(n->left->type), n->left); l->type = n->type; l->typecheck = n->typecheck; @@ -872,8 +882,7 @@ walkexpr(Node **np, NodeList **init) l->addable = 1; ll = list(ll, l); - if(n->left->type->width == widthptr && - isint[simsimtype(n->left->type)]) { + if(isdirectiface(n->left->type) && n->left->type->width == widthptr && isint[simsimtype(n->left->type)]) { /* For pointer types, we can make a special form of optimization * * These statements are put onto the expression init list: @@ -1828,9 +1837,12 @@ walkprint(Node *nn, NodeList **init, int defer) t = types[TINT64]; } } else { - if(et == TUINT64) - on = syslook("printuint", 0); - else + if(et == TUINT64) { + if((t->sym->pkg == runtimepkg || compiling_runtime) && strcmp(t->sym->name, "hex") == 0) + on = syslook("printhex", 0); + else + on = syslook("printuint", 0); + } else on = syslook("printint", 0); } } else if(isfloat[et]) { @@ -2866,14 +2878,14 @@ sliceany(Node* n, NodeList **init) lb = N; } - // dynamic checks convert all bounds to unsigned to save us the bound < 0 comparison - // generate - // if hb > bound || lb > hb { panicslice() } + // Checking src[lb:hb:cb] or src[lb:hb]. + // if chk0 || chk1 || chk2 { panicslice() } chk = N; - chk0 = N; - chk1 = N; - chk2 = N; + chk0 = N; // cap(src) < cb + chk1 = N; // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb] + chk2 = N; // hb < lb + // All comparisons are unsigned to avoid testing < 0. bt = types[simtype[TUINT]]; if(cb != N && cb->type->width > 4) bt = types[TUINT64]; @@ -3013,10 +3025,10 @@ eqfor(Type *t) n = newname(sym); n->class = PFUNC; ntype = nod(OTFUNC, N, N); - ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(types[TBOOL])))); - ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(ptrto(t)))); + ntype->list = list(ntype->list, nod(ODCLFIELD, N, typenod(types[TUINTPTR]))); + ntype->rlist = list(ntype->rlist, nod(ODCLFIELD, N, typenod(types[TBOOL]))); typecheck(&ntype, Etype); n->type = ntype->type; return n; @@ -3037,10 +3049,9 @@ countfield(Type *t) static void walkcompare(Node **np, NodeList **init) { - Node *n, *l, *r, *fn, *call, *a, *li, *ri, *expr; + Node *n, *l, *r, *call, *a, *li, *ri, *expr; int andor, i; Type *t, *t1; - static Node *tempbool; n = *np; @@ -3058,8 +3069,9 @@ walkcompare(Node **np, NodeList **init) break; } - if(!islvalue(n->left) || !islvalue(n->right)) - goto hard; + if(!islvalue(n->left) || !islvalue(n->right)) { + fatal("arguments of comparison must be lvalues"); + } l = temp(ptrto(t)); a = nod(OAS, l, nod(OADDR, n->left, N)); @@ -3118,57 +3130,16 @@ walkcompare(Node **np, NodeList **init) goto ret; } - // Chose not to inline, but still have addresses. - // Call equality function directly. - // The equality function requires a bool pointer for - // storing its address, because it has to be callable - // from C, and C can't access an ordinary Go return value. - // To avoid creating many temporaries, cache one per function. - if(tempbool == N || tempbool->curfn != curfn) - tempbool = temp(types[TBOOL]); - + // Chose not to inline. Call equality function directly. call = nod(OCALL, eqfor(t), N); - a = nod(OADDR, tempbool, N); - a->etype = 1; // does not escape - call->list = list(call->list, a); - call->list = list(call->list, nodintconst(t->width)); call->list = list(call->list, l); call->list = list(call->list, r); - typecheck(&call, Etop); - walkstmt(&call); - *init = list(*init, call); - - // tempbool cannot be used directly as multiple comparison - // expressions may exist in the same statement. Create another - // temporary to hold the value (its address is not taken so it can - // be optimized away). - r = temp(types[TBOOL]); - a = nod(OAS, r, tempbool); - typecheck(&a, Etop); - walkstmt(&a); - *init = list(*init, a); - + call->list = list(call->list, nodintconst(t->width)); + r = call; if(n->op != OEQ) r = nod(ONOT, r, N); goto ret; -hard: - // Cannot take address of one or both of the operands. - // Instead, pass directly to runtime helper function. - // Easier on the stack than passing the address - // of temporary variables, because we are better at reusing - // the argument space than temporary variable space. - fn = syslook("equal", 1); - l = n->left; - r = n->right; - argtype(fn, n->left->type); - argtype(fn, n->left->type); - r = mkcall1(fn, n->type, init, typename(n->left->type), l, r); - if(n->op == ONE) { - r = nod(ONOT, r, N); - } - goto ret; - ret: typecheck(&r, Erv); walkexpr(&r, init); diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go index fa9262c0f..fd92ca278 100644 --- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -885,7 +885,7 @@ func (b *builder) build(a *action) (err error) { } if len(gofiles) == 0 { - return &build.NoGoError{a.p.Dir} + return &build.NoGoError{Dir: a.p.Dir} } // If we're doing coverage, preprocess the .go files and put them in the work directory @@ -1613,8 +1613,10 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, importArgs [] } func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error { + // Add -I pkg/GOOS_GOARCH so #include "textflag.h" works in .s files. + inc := filepath.Join(goroot, "pkg", fmt.Sprintf("%s_%s", goos, goarch)) sfile = mkAbs(p.Dir, sfile) - return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) + return b.run(p.Dir, p.ImportPath, nil, tool(archChar+"a"), "-trimpath", b.work, "-I", obj, "-I", inc, "-o", ofile, "-D", "GOOS_"+goos, "-D", "GOARCH_"+goarch, sfile) } func (gcToolchain) pkgpath(basedir string, p *Package) string { @@ -2312,7 +2314,23 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles, gxxfiles, mfiles nonGccObjs = append(nonGccObjs, f) } } - if err := b.gccld(p, ofile, stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs), gccObjs); err != nil { + ldflags := stringList(bareLDFLAGS, "-Wl,-r", "-nostdlib", staticLibs) + + // Some systems, such as Ubuntu, always add --build-id to + // every link, but we don't want a build ID since we are + // producing an object file. On some of those system a plain + // -r (not -Wl,-r) will turn off --build-id, but clang 3.0 + // doesn't support a plain -r. I don't know how to turn off + // --build-id when using clang other than passing a trailing + // --build-id=none. So that is what we do, but only on + // systems likely to support it, which is to say, systems that + // normally use gold or the GNU linker. + switch goos { + case "android", "dragonfly", "linux", "netbsd": + ldflags = append(ldflags, "-Wl,--build-id=none") + } + + if err := b.gccld(p, ofile, ldflags, gccObjs); err != nil { return nil, nil, err } diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go index 4778048b5..0d4e26389 100644 --- a/src/cmd/go/doc.go +++ b/src/cmd/go/doc.go @@ -19,6 +19,7 @@ The commands are: env print Go environment information fix run go tool fix on packages fmt run gofmt on package sources + generate generate Go files by processing source get download and install packages and dependencies install compile and install packages and dependencies list list packages @@ -219,6 +220,98 @@ To run gofmt with specific options, run gofmt itself. See also: go fix, go vet. +Generate Go files by processing source + +Usage: + + go generate [-run regexp] [file.go... | packages] + +Generate runs commands described by directives within existing +files. Those commands can run any process but the intent is to +create or update Go source files, for instance by running yacc. + +Go generate is never run automatically by go build, go get, go test, +and so on. It must be run explicitly. + +Directives are written as a whole-line comment of the form + + //go:generate command argument... + +(note: no space in "//go") where command is the generator to be +run, corresponding to an executable file that can be run locally. +It must either be in the shell path (gofmt), a fully qualified path +(/usr/you/bin/mytool), or a command alias, described below. + +The arguments are space-separated tokens or double-quoted strings +passed to the generator as individual arguments when it is run. + +Quoted strings use Go syntax and are evaluated before execution; a +quoted string appears a single argument to the generator. + +Go generate sets several variables when it runs the generator: + + $GOARCH + The execution architecture (arm, amd64, etc.) + $GOOS + The execution operating system (linux, windows, etc.) + $GOFILE + The base name of the file. + $GOPACKAGE + The name of the package of the file containing the directive. + +Other than variable substition and quoted-string evaluation, no +special processing such as "globbing" is performed on the command +line. + +As a last step before running the command, any invocations of any +environment variables with alphanumeric names, such as $GOFILE or +$HOME, are expanded throughout the command line. The syntax for +variable expansion is $NAME on all operating systems. Due to the +order of evaluation, variables are expanded even inside quoted +strings. If the variable NAME is not set, $NAME expands to the +empty string. + +A directive of the form, + + //go:generate -command xxx args... + +specifies, for the remainder of this source file only, that the +string xxx represents the command identified by the arguments. This +can be used to create aliases or to handle multiword generators. +For example, + + //go:generate -command yacc go tool yacc + +specifies that the command "yacc" represents the generator +"go tool yacc". + +Generate processes packages in the order given on the command line, +one at a time. If the command line lists .go files, they are treated +as a single package. Within a package, generate processes the +source files in a package in file name order, one at a time. Within +a source file, generate runs generators in the order they appear +in the file, one at a time. + +If any generator returns an error exit status, "go generate" skips +all further processing for that package. + +The generator is run in the package's source directory. + +Go generate accepts one specific flag: + + -run="" + if non-empty, specifies a regular expression to + select directives whose command matches the expression. + +It also accepts the standard build flags -v, -n, and -x. +The -v flag prints the names of packages and files as they are +processed. +The -n flag prints commands that would be executed. +The -x flag prints commands as they are executed. + +For more about specifying packages, see 'go help packages'. + + Download and install packages and dependencies Usage: @@ -750,10 +843,10 @@ will result in the following request(s): If that page contains the meta tag - <meta name="go-import" content="example.org git https://code.example/r/p/exproj"> + <meta name="go-import" content="example.org git https://code.org/r/p/exproj"> the go tool will verify that https://example.org/?go-get=1 contains the -same meta tag and then git clone https://code.example/r/p/exproj into +same meta tag and then git clone https://code.org/r/p/exproj into GOPATH/src/example.org. New downloaded packages are written to the first directory diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go new file mode 100644 index 000000000..167758207 --- /dev/null +++ b/src/cmd/go/generate.go @@ -0,0 +1,355 @@ +// Copyright 2011 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. + +package main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "log" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +var cmdGenerate = &Command{ + Run: runGenerate, + UsageLine: "generate [-run regexp] [file.go... | packages]", + Short: "generate Go files by processing source", + Long: ` +Generate runs commands described by directives within existing +files. Those commands can run any process but the intent is to +create or update Go source files, for instance by running yacc. + +Go generate is never run automatically by go build, go get, go test, +and so on. It must be run explicitly. + +Directives are written as a whole-line comment of the form + + //go:generate command argument... + +(note: no space in "//go") where command is the generator to be +run, corresponding to an executable file that can be run locally. +It must either be in the shell path (gofmt), a fully qualified path +(/usr/you/bin/mytool), or a command alias, described below. + +The arguments are space-separated tokens or double-quoted strings +passed to the generator as individual arguments when it is run. + +Quoted strings use Go syntax and are evaluated before execution; a +quoted string appears a single argument to the generator. + +Go generate sets several variables when it runs the generator: + + $GOARCH + The execution architecture (arm, amd64, etc.) + $GOOS + The execution operating system (linux, windows, etc.) + $GOFILE + The base name of the file. + $GOPACKAGE + The name of the package of the file containing the directive. + +Other than variable substition and quoted-string evaluation, no +special processing such as "globbing" is performed on the command +line. + +As a last step before running the command, any invocations of any +environment variables with alphanumeric names, such as $GOFILE or +$HOME, are expanded throughout the command line. The syntax for +variable expansion is $NAME on all operating systems. Due to the +order of evaluation, variables are expanded even inside quoted +strings. If the variable NAME is not set, $NAME expands to the +empty string. + +A directive of the form, + + //go:generate -command xxx args... + +specifies, for the remainder of this source file only, that the +string xxx represents the command identified by the arguments. This +can be used to create aliases or to handle multiword generators. +For example, + + //go:generate -command yacc go tool yacc + +specifies that the command "yacc" represents the generator +"go tool yacc". + +Generate processes packages in the order given on the command line, +one at a time. If the command line lists .go files, they are treated +as a single package. Within a package, generate processes the +source files in a package in file name order, one at a time. Within +a source file, generate runs generators in the order they appear +in the file, one at a time. + +If any generator returns an error exit status, "go generate" skips +all further processing for that package. + +The generator is run in the package's source directory. + +Go generate accepts one specific flag: + + -run="" + if non-empty, specifies a regular expression to + select directives whose command matches the expression. + +It also accepts the standard build flags -v, -n, and -x. +The -v flag prints the names of packages and files as they are +processed. +The -n flag prints commands that would be executed. +The -x flag prints commands as they are executed. + +For more about specifying packages, see 'go help packages'. + `, +} + +var generateRunFlag string // generate -run flag + +func init() { + addBuildFlags(cmdGenerate) + cmdGenerate.Flag.StringVar(&generateRunFlag, "run", "", "") +} + +func runGenerate(cmd *Command, args []string) { + // Even if the arguments are .go files, this loop suffices. + for _, pkg := range packages(args) { + for _, file := range pkg.gofiles { + if !generate(pkg.Name, file) { + break + } + } + } +} + +// generate runs the generation directives for a single file. +func generate(pkg, absFile string) bool { + fd, err := os.Open(absFile) + if err != nil { + log.Fatalf("generate: %s", err) + } + defer fd.Close() + g := &Generator{ + r: fd, + path: absFile, + pkg: pkg, + commands: make(map[string][]string), + } + return g.run() +} + +// A Generator represents the state of a single Go source file +// being scanned for generator commands. +type Generator struct { + r io.Reader + path string // full rooted path name. + dir string // full rooted directory of file. + file string // base name of file. + pkg string + commands map[string][]string + lineNum int +} + +// run runs the generators in the current file. +func (g *Generator) run() (ok bool) { + // Processing below here calls g.errorf on failure, which does panic(stop). + // If we encounter an error, we abort the package. + defer func() { + e := recover() + if e != nil { + ok = false + if e != stop { + panic(e) + } + } + }() + g.dir, g.file = filepath.Split(g.path) + g.dir = filepath.Clean(g.dir) // No final separator please. + if buildV { + fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path)) + } + + s := bufio.NewScanner(g.r) + for s.Scan() { + g.lineNum++ + if !bytes.HasPrefix(s.Bytes(), []byte("//go:generate ")) && !bytes.HasPrefix(s.Bytes(), []byte("//go:generate\t")) { + continue + } + words := g.split(s.Text()) + if len(words) == 0 { + g.errorf("no arguments to directive") + } + if words[0] == "-command" { + g.setShorthand(words) + continue + } + // Run the command line. + if buildN || buildX { + fmt.Fprintf(os.Stderr, "%s\n", strings.Join(words, " ")) + } + if buildN { + continue + } + g.exec(words) + } + if s.Err() != nil { + g.errorf("error reading %s: %s", shortPath(g.path), s.Err()) + } + return true +} + +// split breaks the line into words, evaluating quoted +// strings and evaluating environment variables. +// The initial //go:generate element is dropped. +func (g *Generator) split(line string) []string { + // Parse line, obeying quoted strings. + var words []string + line = line[len("//go:generate "):] + // One (possibly quoted) word per iteration. +Words: + for { + line = strings.TrimLeft(line, " \t") + if len(line) == 0 { + break + } + if line[0] == '"' { + for i := 1; i < len(line); i++ { + c := line[i] // Only looking for ASCII so this is OK. + switch c { + case '\\': + if i+1 == len(line) { + g.errorf("bad backslash") + } + i++ // Absorb next byte (If it's a multibyte we'll get an error in Unquote). + case '"': + word, err := strconv.Unquote(line[0 : i+1]) + if err != nil { + g.errorf("bad quoted string") + } + words = append(words, word) + line = line[i+1:] + // Check the next character is space or end of line. + if len(line) > 0 && line[0] != ' ' && line[0] != '\t' { + g.errorf("expect space after quoted argument") + } + continue Words + } + } + g.errorf("mismatched quoted string") + } + i := strings.IndexAny(line, " \t") + if i < 0 { + i = len(line) + } + words = append(words, line[0:i]) + line = line[i:] + } + // Substitute command if required. + if len(words) > 0 && g.commands[words[0]] != nil { + // Replace 0th word by command substitution. + words = append(g.commands[words[0]], words[1:]...) + } + // Substitute environment variables. + for i, word := range words { + words[i] = g.expandEnv(word) + } + return words +} + +var stop = fmt.Errorf("error in generation") + +// errorf logs an error message prefixed with the file and line number. +// It then exits the program because generation stops at the first error. +func (g *Generator) errorf(format string, args ...interface{}) { + fmt.Fprintf(os.Stderr, "%s:%d: %s\n", shortPath(g.path), g.lineNum, + fmt.Sprintf(format, args...)) + panic(stop) +} + +// expandEnv expands any $XXX invocations in word. +func (g *Generator) expandEnv(word string) string { + if !strings.ContainsRune(word, '$') { + return word + } + var buf bytes.Buffer + var w int + var r rune + for i := 0; i < len(word); i += w { + r, w = utf8.DecodeRuneInString(word[i:]) + if r != '$' { + buf.WriteRune(r) + continue + } + w += g.identLength(word[i+w:]) + envVar := word[i+1 : i+w] + var sub string + switch envVar { + case "GOARCH": + sub = runtime.GOARCH + case "GOOS": + sub = runtime.GOOS + case "GOFILE": + sub = g.file + case "GOPACKAGE": + sub = g.pkg + default: + sub = os.Getenv(envVar) + } + buf.WriteString(sub) + } + return buf.String() +} + +// identLength returns the length of the identifier beginning the string. +func (g *Generator) identLength(word string) int { + for i, r := range word { + if r == '_' || unicode.IsLetter(r) || unicode.IsDigit(r) { + continue + } + return i + } + return len(word) +} + +// setShorthand installs a new shorthand as defined by a -command directive. +func (g *Generator) setShorthand(words []string) { + // Create command shorthand. + if len(words) == 1 { + g.errorf("no command specified for -command") + } + command := words[1] + if g.commands[command] != nil { + g.errorf("command %q defined multiply defined", command) + } + g.commands[command] = words[2:len(words):len(words)] // force later append to make copy +} + +// exec runs the command specified by the argument. The first word is +// the command name itself. +func (g *Generator) exec(words []string) { + cmd := exec.Command(words[0], words[1:]...) + // Standard in and out of generator should be the usual. + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + // Run the command in the package directory. + cmd.Dir = g.dir + env := []string{ + "GOARCH=" + runtime.GOARCH, + "GOOS=" + runtime.GOOS, + "GOFILE=" + g.file, + "GOPACKAGE=" + g.pkg, + } + cmd.Env = mergeEnvLists(env, os.Environ()) + err := cmd.Run() + if err != nil { + g.errorf("running %q: %s", words[0], err) + } +} diff --git a/src/cmd/go/generate_test.go b/src/cmd/go/generate_test.go new file mode 100644 index 000000000..93c0ae66e --- /dev/null +++ b/src/cmd/go/generate_test.go @@ -0,0 +1,48 @@ +// Copyright 2011 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. + +package main + +import ( + "reflect" + "runtime" + "testing" +) + +type splitTest struct { + in string + out []string +} + +var splitTests = []splitTest{ + {"", nil}, + {"x", []string{"x"}}, + {" a b\tc ", []string{"a", "b", "c"}}, + {` " a " `, []string{" a "}}, + {"$GOARCH", []string{runtime.GOARCH}}, + {"$GOOS", []string{runtime.GOOS}}, + {"$GOFILE", []string{"proc.go"}}, + {"$GOPACKAGE", []string{"sys"}}, + {"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}}, + {"/$XXNOTDEFINED/", []string{"//"}}, + {"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}}, +} + +func TestGenerateCommandParse(t *testing.T) { + g := &Generator{ + r: nil, // Unused here. + path: "/usr/ken/sys/proc.go", + dir: "/usr/ken/sys", + file: "proc.go", + pkg: "sys", + commands: make(map[string][]string), + } + g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"}) + for _, test := range splitTests { + got := g.split("//go:generate " + test.in) + if !reflect.DeepEqual(got, test.out) { + t.Errorf("split(%q): got %q expected %q", test.in, got, test.out) + } + } +} diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go index e708fcf77..a34286f54 100644 --- a/src/cmd/go/get.go +++ b/src/cmd/go/get.go @@ -151,7 +151,9 @@ func download(arg string, stk *importStack, getTestDeps bool) { } // Only process each package once. - if downloadCache[arg] { + // (Unless we're fetching test dependencies for this package, + // in which case we want to process it again.) + if downloadCache[arg] && !getTestDeps { return } downloadCache[arg] = true diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go index 5b1194aaa..eb69606de 100644 --- a/src/cmd/go/main.go +++ b/src/cmd/go/main.go @@ -79,6 +79,7 @@ var commands = []*Command{ cmdEnv, cmdFix, cmdFmt, + cmdGenerate, cmdGet, cmdInstall, cmdList, diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go index d45df265b..1af33f037 100644 --- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -244,6 +244,9 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token. importPath = dirToImportPath(filepath.Join(srcDir, path)) } if p := packageCache[importPath]; p != nil { + if perr := disallowInternal(srcDir, p, stk); perr != p { + return perr + } return reusePackage(p, stk) } @@ -258,11 +261,14 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token. // // TODO: After Go 1, decide when to pass build.AllowBinary here. // See issue 3268 for mistakes to avoid. - bp, err := buildContext.Import(path, srcDir, 0) + bp, err := buildContext.Import(path, srcDir, build.ImportComment) bp.ImportPath = importPath if gobin != "" { bp.BinDir = gobin } + if err == nil && !isLocal && bp.ImportComment != "" && bp.ImportComment != path { + err = fmt.Errorf("code in directory %s expects import %q", bp.Dir, bp.ImportComment) + } p.load(stk, bp, err) if p.Error != nil && len(importPos) > 0 { pos := importPos[0] @@ -270,6 +276,10 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token. p.Error.Pos = pos.String() } + if perr := disallowInternal(srcDir, p, stk); perr != p { + return perr + } + return p } @@ -298,6 +308,75 @@ func reusePackage(p *Package, stk *importStack) *Package { return p } +// disallowInternal checks that srcDir is allowed to import p. +// If the import is allowed, disallowInternal returns the original package p. +// If not, it returns a new package containing just an appropriate error. +func disallowInternal(srcDir string, p *Package, stk *importStack) *Package { + // golang.org/s/go14internal: + // An import of a path containing the element “internal” + // is disallowed if the importing code is outside the tree + // rooted at the parent of the “internal” directory. + // + // ... For Go 1.4, we will implement the rule first for $GOROOT, but not $GOPATH. + + // Only applies to $GOROOT. + if !p.Standard { + return p + } + + // The stack includes p.ImportPath. + // If that's the only thing on the stack, we started + // with a name given on the command line, not an + // import. Anything listed on the command line is fine. + if len(*stk) == 1 { + return p + } + + // Check for "internal" element: four cases depending on begin of string and/or end of string. + i, ok := findInternal(p.ImportPath) + if !ok { + return p + } + + // Internal is present. + // Map import path back to directory corresponding to parent of internal. + if i > 0 { + i-- // rewind over slash in ".../internal" + } + parent := p.Dir[:i+len(p.Dir)-len(p.ImportPath)] + if hasPathPrefix(filepath.ToSlash(srcDir), filepath.ToSlash(parent)) { + return p + } + + // Internal is present, and srcDir is outside parent's tree. Not allowed. + perr := *p + perr.Error = &PackageError{ + ImportStack: stk.copy(), + Err: "use of internal package not allowed", + } + perr.Incomplete = true + return &perr +} + +// findInternal looks for the final "internal" path element in the given import path. +// If there isn't one, findInternal returns ok=false. +// Otherwise, findInternal returns ok=true and the index of the "internal". +func findInternal(path string) (index int, ok bool) { + // Four cases, depending on internal at start/end of string or not. + // The order matters: we must return the index of the final element, + // because the final one produces the most restrictive requirement + // on the importer. + switch { + case strings.HasSuffix(path, "/internal"): + return len(path) - len("internal"), true + case strings.Contains(path, "/internal/"): + return strings.LastIndex(path, "/internal/") + 1, true + case path == "internal", strings.HasPrefix(path, "internal/"): + return 0, true + } + return 0, false +} + type targetDir int const ( @@ -482,7 +561,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package // Build list of imported packages and full dependency list. imports := make([]*Package, 0, len(p.Imports)) - deps := make(map[string]bool) + deps := make(map[string]*Package) for i, path := range importPaths { if path == "C" { continue @@ -502,10 +581,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package path = p1.ImportPath importPaths[i] = path } - deps[path] = true + deps[path] = p1 imports = append(imports, p1) - for _, dep := range p1.Deps { - deps[dep] = true + for _, dep := range p1.deps { + deps[dep.ImportPath] = dep } if p1.Incomplete { p.Incomplete = true @@ -519,7 +598,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package } sort.Strings(p.Deps) for _, dep := range p.Deps { - p1 := packageCache[dep] + p1 := deps[dep] if p1 == nil { panic("impossible: missing entry in package cache for " + dep + " imported by " + p.ImportPath) } diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash index c62f62940..24640e272 100755 --- a/src/cmd/go/test.bash +++ b/src/cmd/go/test.bash @@ -105,6 +105,58 @@ cp -R testdata/local "testdata/$bad" testlocal "$bad" 'with bad characters in path' rm -rf "testdata/$bad" +TEST 'internal packages in $GOROOT are respected' +if ./testgo build -v ./testdata/testinternal >testdata/std.out 2>&1; then + echo "go build ./testdata/testinternal succeeded incorrectly" + ok=false +elif ! grep 'use of internal package not allowed' testdata/std.out >/dev/null; then + echo "wrong error message for testdata/testinternal" + cat std.out + ok=false +fi + +TEST 'internal packages outside $GOROOT are not respected' +if ! ./testgo build -v ./testdata/testinternal2; then + echo "go build ./testdata/testinternal2 failed" + ok=false +fi + +export GOPATH=$(pwd)/testdata/importcom +TEST 'import comment - match' +if ! ./testgo build ./testdata/importcom/works.go; then + echo 'go build ./testdata/importcom/works.go failed' + ok=false +fi +TEST 'import comment - mismatch' +if ./testgo build ./testdata/importcom/wrongplace.go 2>testdata/err; then + echo 'go build ./testdata/importcom/wrongplace.go suceeded' + ok=false +elif ! grep 'wrongplace expects import "my/x"' testdata/err >/dev/null; then + echo 'go build did not mention incorrect import:' + cat testdata/err + ok=false +fi +TEST 'import comment - syntax error' +if ./testgo build ./testdata/importcom/bad.go 2>testdata/err; then + echo 'go build ./testdata/importcom/bad.go suceeded' + ok=false +elif ! grep 'cannot parse import comment' testdata/err >/dev/null; then + echo 'go build did not mention syntax error:' + cat testdata/err + ok=false +fi +TEST 'import comment - conflict' +if ./testgo build ./testdata/importcom/conflict.go 2>testdata/err; then + echo 'go build ./testdata/importcom/conflict.go suceeded' + ok=false +elif ! grep 'found import comments' testdata/err >/dev/null; then + echo 'go build did not mention comment conflict:' + cat testdata/err + ok=false +fi +rm -f ./testdata/err +unset GOPATH + TEST error message for syntax error in test go file says FAIL export GOPATH=$(pwd)/testdata if ./testgo test syntaxerror 2>testdata/err; then @@ -527,6 +579,17 @@ TEST go get cover unset GOPATH rm -rf $d +TEST go get -t "code.google.com/p/go-get-issue-8181/{a,b}" +d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX) +export GOPATH=$d +if ./testgo get -t code.google.com/p/go-get-issue-8181/{a,b}; then + ./testgo list ... | grep go.tools/godoc > /dev/null || ok=false +else + ok=false +fi +unset GOPATH +rm -rf $d + TEST shadowing logic export GOPATH=$(pwd)/testdata/shadow/root1:$(pwd)/testdata/shadow/root2 @@ -815,6 +878,33 @@ elif ! grep 'File with non-runnable example was built.' testdata/std.out > /dev/ ok=false fi +TEST 'go generate handles simple command' +if ! ./testgo generate ./testdata/generate/test1.go > testdata/std.out; then + echo "go test ./testdata/generate/test1.go failed to run" + ok=false +elif ! grep 'Success' testdata/std.out > /dev/null; then + echo "go test ./testdata/generate/test1.go generated wrong output" + ok=false +fi + +TEST 'go generate handles command alias' +if ! ./testgo generate ./testdata/generate/test2.go > testdata/std.out; then + echo "go test ./testdata/generate/test2.go failed to run" + ok=false +elif ! grep 'Now is the time for all good men' testdata/std.out > /dev/null; then + echo "go test ./testdata/generate/test2.go generated wrong output" + ok=false +fi + +TEST 'go generate variable substitution' +if ! ./testgo generate ./testdata/generate/test3.go > testdata/std.out; then + echo "go test ./testdata/generate/test3.go failed to run" + ok=false +elif ! grep "$GOARCH test3.go p xyzp/test3.go/123" testdata/std.out > /dev/null; then + echo "go test ./testdata/generate/test3.go generated wrong output" + ok=false +fi + # clean up if $started; then stop; fi rm -rf testdata/bin testdata/bin1 diff --git a/src/cmd/go/testdata/generate/test1.go b/src/cmd/go/testdata/generate/test1.go new file mode 100644 index 000000000..1f05734f0 --- /dev/null +++ b/src/cmd/go/testdata/generate/test1.go @@ -0,0 +1,13 @@ +// Copyright 2014 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. + +// Simple test for go generate. + +// We include a build tag that go generate should ignore. + +// +build ignore + +//go:generate echo Success + +package p diff --git a/src/cmd/go/testdata/generate/test2.go b/src/cmd/go/testdata/generate/test2.go new file mode 100644 index 000000000..ef1a3d951 --- /dev/null +++ b/src/cmd/go/testdata/generate/test2.go @@ -0,0 +1,10 @@ +// Copyright 2014 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. + +// Test that go generate handles command aliases. + +//go:generate -command run echo Now is the time +//go:generate run for all good men + +package p diff --git a/src/cmd/go/testdata/generate/test3.go b/src/cmd/go/testdata/generate/test3.go new file mode 100644 index 000000000..41ffb7ea8 --- /dev/null +++ b/src/cmd/go/testdata/generate/test3.go @@ -0,0 +1,9 @@ +// Copyright 2014 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. + +// Test go generate variable substitution. + +//go:generate echo $GOARCH $GOFILE $GOPACKAGE xyz$GOPACKAGE/$GOFILE/123 + +package p diff --git a/src/cmd/go/testdata/importcom/bad.go b/src/cmd/go/testdata/importcom/bad.go new file mode 100644 index 000000000..e104c2e99 --- /dev/null +++ b/src/cmd/go/testdata/importcom/bad.go @@ -0,0 +1,3 @@ +package p + +import "bad" diff --git a/src/cmd/go/testdata/importcom/conflict.go b/src/cmd/go/testdata/importcom/conflict.go new file mode 100644 index 000000000..995556c51 --- /dev/null +++ b/src/cmd/go/testdata/importcom/conflict.go @@ -0,0 +1,3 @@ +package p + +import "conflict" diff --git a/src/cmd/go/testdata/importcom/src/bad/bad.go b/src/cmd/go/testdata/importcom/src/bad/bad.go new file mode 100644 index 000000000..bc51fd3fd --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/bad/bad.go @@ -0,0 +1 @@ +package bad // import diff --git a/src/cmd/go/testdata/importcom/src/conflict/a.go b/src/cmd/go/testdata/importcom/src/conflict/a.go new file mode 100644 index 000000000..2d6770351 --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/conflict/a.go @@ -0,0 +1 @@ +package conflict // import "a" diff --git a/src/cmd/go/testdata/importcom/src/conflict/b.go b/src/cmd/go/testdata/importcom/src/conflict/b.go new file mode 100644 index 000000000..8fcfb3c8b --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/conflict/b.go @@ -0,0 +1 @@ +package conflict /* import "b" */ diff --git a/src/cmd/go/testdata/importcom/src/works/x/x.go b/src/cmd/go/testdata/importcom/src/works/x/x.go new file mode 100644 index 000000000..044c6eca8 --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/works/x/x.go @@ -0,0 +1 @@ +package x // import "works/x" diff --git a/src/cmd/go/testdata/importcom/src/works/x/x1.go b/src/cmd/go/testdata/importcom/src/works/x/x1.go new file mode 100644 index 000000000..2449b29df --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/works/x/x1.go @@ -0,0 +1 @@ +package x // important! not an import comment diff --git a/src/cmd/go/testdata/importcom/src/wrongplace/x.go b/src/cmd/go/testdata/importcom/src/wrongplace/x.go new file mode 100644 index 000000000..b89849da7 --- /dev/null +++ b/src/cmd/go/testdata/importcom/src/wrongplace/x.go @@ -0,0 +1 @@ +package x // import "my/x" diff --git a/src/cmd/go/testdata/importcom/works.go b/src/cmd/go/testdata/importcom/works.go new file mode 100644 index 000000000..31b55d08a --- /dev/null +++ b/src/cmd/go/testdata/importcom/works.go @@ -0,0 +1,3 @@ +package p + +import _ "works/x" diff --git a/src/cmd/go/testdata/importcom/wrongplace.go b/src/cmd/go/testdata/importcom/wrongplace.go new file mode 100644 index 000000000..e2535e01a --- /dev/null +++ b/src/cmd/go/testdata/importcom/wrongplace.go @@ -0,0 +1,3 @@ +package p + +import "wrongplace" diff --git a/src/cmd/go/testdata/testinternal/p.go b/src/cmd/go/testdata/testinternal/p.go new file mode 100644 index 000000000..e3558a53b --- /dev/null +++ b/src/cmd/go/testdata/testinternal/p.go @@ -0,0 +1,3 @@ +package p + +import _ "net/http/internal" diff --git a/src/cmd/go/testdata/testinternal2/p.go b/src/cmd/go/testdata/testinternal2/p.go new file mode 100644 index 000000000..c594f5c5e --- /dev/null +++ b/src/cmd/go/testdata/testinternal2/p.go @@ -0,0 +1,3 @@ +package p + +import _ "./x/y/z/internal/w" diff --git a/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go new file mode 100644 index 000000000..a796c0b5f --- /dev/null +++ b/src/cmd/go/testdata/testinternal2/x/y/z/internal/w/w.go @@ -0,0 +1 @@ +package w diff --git a/src/cmd/go/vcs_test.go b/src/cmd/go/vcs_test.go index f9bf75fef..14d681ba6 100644 --- a/src/cmd/go/vcs_test.go +++ b/src/cmd/go/vcs_test.go @@ -12,6 +12,9 @@ import ( // Test that RepoRootForImportPath creates the correct RepoRoot for a given importPath. // TODO(cmang): Add tests for SVN and BZR. func TestRepoRootForImportPath(t *testing.T) { + if testing.Short() { + t.Skip("skipping test to avoid external network") + } switch runtime.GOOS { case "nacl", "android": t.Skipf("no networking available on %s", runtime.GOOS) @@ -106,7 +109,7 @@ func TestRepoRootForImportPath(t *testing.T) { if want == nil { if err == nil { - t.Errorf("RepoRootForImport(%q): Error expected but not received") + t.Errorf("RepoRootForImport(%q): Error expected but not received", test.path) } continue } diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 576cae522..f322a2b0a 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -122,7 +122,7 @@ func processFile(filename string, in io.Reader, out io.Writer, stdin bool) error fmt.Fprintln(out, filename) } if *write { - err = ioutil.WriteFile(filename, res, 0) + err = ioutil.WriteFile(filename, res, 0644) if err != nil { return err } @@ -186,6 +186,11 @@ func gofmtMain() { initRewrite() if flag.NArg() == 0 { + if *write { + fmt.Fprintln(os.Stderr, "error: cannot use -w with standard input") + exitCode = 2 + return + } if err := processFile("<standard input>", os.Stdin, os.Stdout, true); err != nil { report(err) } @@ -277,14 +282,14 @@ func parse(fset *token.FileSet, filename string, src []byte, stdin bool) (*ast.F // into a function body. This handles expressions too. // Insert using a ;, not a newline, so that the line numbers // in fsrc match the ones in src. - fsrc := append(append([]byte("package p; func _() {"), src...), '}') + fsrc := append(append([]byte("package p; func _() {"), src...), '\n', '}') file, err = parser.ParseFile(fset, filename, fsrc, parserMode) if err == nil { adjust := func(orig, src []byte) []byte { // Remove the wrapping. // Gofmt has turned the ; into a \n\n. src = src[len("package p\n\nfunc _() {"):] - src = src[:len(src)-len("}\n")] + src = src[:len(src)-len("\n}\n")] // Gofmt has also indented the function body one level. // Remove that indent. src = bytes.Replace(src, []byte("\n\t"), []byte("\n"), -1) diff --git a/src/cmd/gofmt/gofmt_test.go b/src/cmd/gofmt/gofmt_test.go index ca44f3dcf..d1edb7bcc 100644 --- a/src/cmd/gofmt/gofmt_test.go +++ b/src/cmd/gofmt/gofmt_test.go @@ -6,18 +6,60 @@ package main import ( "bytes" + "flag" "io/ioutil" + "os" "path/filepath" "strings" "testing" + "text/scanner" ) -func runTest(t *testing.T, in, out, flags string) { +var update = flag.Bool("update", false, "update .golden files") + +// gofmtFlags looks for a comment of the form +// +// //gofmt flags +// +// within the first maxLines lines of the given file, +// and returns the flags string, if any. Otherwise it +// returns the empty string. +func gofmtFlags(filename string, maxLines int) string { + f, err := os.Open(filename) + if err != nil { + return "" // ignore errors - they will be found later + } + defer f.Close() + + // initialize scanner + var s scanner.Scanner + s.Init(f) + s.Error = func(*scanner.Scanner, string) {} // ignore errors + s.Mode = scanner.GoTokens &^ scanner.SkipComments // want comments + + // look for //gofmt comment + for s.Line <= maxLines { + switch s.Scan() { + case scanner.Comment: + const prefix = "//gofmt " + if t := s.TokenText(); strings.HasPrefix(t, prefix) { + return strings.TrimSpace(t[len(prefix):]) + } + case scanner.EOF: + return "" + } + + } + + return "" +} + +func runTest(t *testing.T, in, out string) { // process flags *simplifyAST = false *rewriteRule = "" stdin := false - for _, flag := range strings.Split(flags, " ") { + for _, flag := range strings.Split(gofmtFlags(in, 20), " ") { elts := strings.SplitN(flag, "=", 2) name := elts[0] value := "" @@ -56,6 +98,17 @@ func runTest(t *testing.T, in, out, flags string) { } if got := buf.Bytes(); !bytes.Equal(got, expected) { + if *update { + if in != out { + if err := ioutil.WriteFile(out, got, 0666); err != nil { + t.Error(err) + } + return + } + // in == out: don't accidentally destroy input + t.Errorf("WARNING: -update did not rewrite input file %s", in) + } + t.Errorf("(gofmt %s) != %s (see %s.gofmt)", in, out, in) d, err := diff(expected, got) if err == nil { @@ -67,53 +120,37 @@ func runTest(t *testing.T, in, out, flags string) { } } -var tests = []struct { - in, flags string -}{ - {"gofmt.go", ""}, - {"gofmt_test.go", ""}, - {"testdata/composites.input", "-s"}, - {"testdata/slices1.input", "-s"}, - {"testdata/slices2.input", "-s"}, - {"testdata/ranges.input", "-s"}, - {"testdata/old.input", ""}, - {"testdata/rewrite1.input", "-r=Foo->Bar"}, - {"testdata/rewrite2.input", "-r=int->bool"}, - {"testdata/rewrite3.input", "-r=x->x"}, - {"testdata/rewrite4.input", "-r=(x)->x"}, - {"testdata/rewrite5.input", "-r=x+x->2*x"}, - {"testdata/rewrite6.input", "-r=fun(x)->Fun(x)"}, - {"testdata/rewrite7.input", "-r=fun(x...)->Fun(x)"}, - {"testdata/rewrite8.input", "-r=interface{}->int"}, - {"testdata/stdin*.input", "-stdin"}, - {"testdata/comments.input", ""}, - {"testdata/import.input", ""}, - {"testdata/crlf.input", ""}, // test case for issue 3961; see also TestCRLF - {"testdata/typeswitch.input", ""}, // test case for issue 4470 - {"testdata/emptydecl.input", "-s"}, // test case for issue 7631 -} - +// TestRewrite processes testdata/*.input files and compares them to the +// corresponding testdata/*.golden files. The gofmt flags used to process +// a file must be provided via a comment of the form +// +// //gofmt flags +// +// in the processed file within the first 20 lines, if any. func TestRewrite(t *testing.T) { - for _, test := range tests { - match, err := filepath.Glob(test.in) - if err != nil { - t.Error(err) - continue + // determine input files + match, err := filepath.Glob("testdata/*.input") + if err != nil { + t.Fatal(err) + } + + // add larger examples + match = append(match, "gofmt.go", "gofmt_test.go") + + for _, in := range match { + out := in // for files where input and output are identical + if strings.HasSuffix(in, ".input") { + out = in[:len(in)-len(".input")] + ".golden" } - for _, in := range match { - out := in - if strings.HasSuffix(in, ".input") { - out = in[:len(in)-len(".input")] + ".golden" - } - runTest(t, in, out, test.flags) - if in != out { - // Check idempotence. - runTest(t, out, out, test.flags) - } + runTest(t, in, out) + if in != out { + // Check idempotence. + runTest(t, out, out) } } } +// Test case for issue 3961. func TestCRLF(t *testing.T) { const input = "testdata/crlf.input" // must contain CR/LF's const golden = "testdata/crlf.golden" // must not contain any CR's diff --git a/src/cmd/gofmt/testdata/composites.golden b/src/cmd/gofmt/testdata/composites.golden index b2825e732..fc9c98e62 100644 --- a/src/cmd/gofmt/testdata/composites.golden +++ b/src/cmd/gofmt/testdata/composites.golden @@ -1,3 +1,5 @@ +//gofmt -s + package P type T struct { diff --git a/src/cmd/gofmt/testdata/composites.input b/src/cmd/gofmt/testdata/composites.input index 7210dafc9..fc7598af9 100644 --- a/src/cmd/gofmt/testdata/composites.input +++ b/src/cmd/gofmt/testdata/composites.input @@ -1,3 +1,5 @@ +//gofmt -s + package P type T struct { diff --git a/src/cmd/gofmt/testdata/crlf.golden b/src/cmd/gofmt/testdata/crlf.golden index 57679f770..193dbacc7 100644 --- a/src/cmd/gofmt/testdata/crlf.golden +++ b/src/cmd/gofmt/testdata/crlf.golden @@ -2,6 +2,7 @@ Source containing CR/LF line endings. The gofmt'ed output must only have LF line endings. + Test case for issue 3961. */ package main diff --git a/src/cmd/gofmt/testdata/crlf.input b/src/cmd/gofmt/testdata/crlf.input index 61a1aa0b4..ae7e14dbf 100644 --- a/src/cmd/gofmt/testdata/crlf.input +++ b/src/cmd/gofmt/testdata/crlf.input @@ -2,6 +2,7 @@ Source containing CR/LF line endings.
The gofmt'ed output must only have LF
line endings.
+ Test case for issue 3961.
*/
package main
diff --git a/src/cmd/gofmt/testdata/emptydecl.golden b/src/cmd/gofmt/testdata/emptydecl.golden index 9fe62c973..33d6435e0 100644 --- a/src/cmd/gofmt/testdata/emptydecl.golden +++ b/src/cmd/gofmt/testdata/emptydecl.golden @@ -1,3 +1,7 @@ +//gofmt -s + +// Test case for issue 7631. + package main // Keep this declaration diff --git a/src/cmd/gofmt/testdata/emptydecl.input b/src/cmd/gofmt/testdata/emptydecl.input index d1cab00ef..4948a61f0 100644 --- a/src/cmd/gofmt/testdata/emptydecl.input +++ b/src/cmd/gofmt/testdata/emptydecl.input @@ -1,3 +1,7 @@ +//gofmt -s + +// Test case for issue 7631. + package main // Keep this declaration diff --git a/src/cmd/gofmt/testdata/ranges.golden b/src/cmd/gofmt/testdata/ranges.golden index 42168526d..506b3a035 100644 --- a/src/cmd/gofmt/testdata/ranges.golden +++ b/src/cmd/gofmt/testdata/ranges.golden @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for range simplification. package p diff --git a/src/cmd/gofmt/testdata/ranges.input b/src/cmd/gofmt/testdata/ranges.input index 4b02d5175..df5f8333c 100644 --- a/src/cmd/gofmt/testdata/ranges.input +++ b/src/cmd/gofmt/testdata/ranges.input @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for range simplification. package p diff --git a/src/cmd/gofmt/testdata/rewrite1.golden b/src/cmd/gofmt/testdata/rewrite1.golden index d9beb3705..3ee5373a7 100644 --- a/src/cmd/gofmt/testdata/rewrite1.golden +++ b/src/cmd/gofmt/testdata/rewrite1.golden @@ -1,3 +1,5 @@ +//gofmt -r=Foo->Bar + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite1.input b/src/cmd/gofmt/testdata/rewrite1.input index bdb894320..a84c8f781 100644 --- a/src/cmd/gofmt/testdata/rewrite1.input +++ b/src/cmd/gofmt/testdata/rewrite1.input @@ -1,3 +1,5 @@ +//gofmt -r=Foo->Bar + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite2.golden b/src/cmd/gofmt/testdata/rewrite2.golden index 64c67ffa6..f980e0353 100644 --- a/src/cmd/gofmt/testdata/rewrite2.golden +++ b/src/cmd/gofmt/testdata/rewrite2.golden @@ -1,3 +1,5 @@ +//gofmt -r=int->bool + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite2.input b/src/cmd/gofmt/testdata/rewrite2.input index 21171447a..489be4e07 100644 --- a/src/cmd/gofmt/testdata/rewrite2.input +++ b/src/cmd/gofmt/testdata/rewrite2.input @@ -1,3 +1,5 @@ +//gofmt -r=int->bool + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite3.golden b/src/cmd/gofmt/testdata/rewrite3.golden index 0d16d1601..261a220c6 100644 --- a/src/cmd/gofmt/testdata/rewrite3.golden +++ b/src/cmd/gofmt/testdata/rewrite3.golden @@ -1,3 +1,5 @@ +//gofmt -r=x->x + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite3.input b/src/cmd/gofmt/testdata/rewrite3.input index 0d16d1601..261a220c6 100644 --- a/src/cmd/gofmt/testdata/rewrite3.input +++ b/src/cmd/gofmt/testdata/rewrite3.input @@ -1,3 +1,5 @@ +//gofmt -r=x->x + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite4.golden b/src/cmd/gofmt/testdata/rewrite4.golden index 8dfc81a07..b05547b4b 100644 --- a/src/cmd/gofmt/testdata/rewrite4.golden +++ b/src/cmd/gofmt/testdata/rewrite4.golden @@ -1,3 +1,5 @@ +//gofmt -r=(x)->x + // Copyright 2012 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. diff --git a/src/cmd/gofmt/testdata/rewrite4.input b/src/cmd/gofmt/testdata/rewrite4.input index 164cc0451..081709920 100644 --- a/src/cmd/gofmt/testdata/rewrite4.input +++ b/src/cmd/gofmt/testdata/rewrite4.input @@ -1,3 +1,5 @@ +//gofmt -r=(x)->x + // Copyright 2012 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. diff --git a/src/cmd/gofmt/testdata/rewrite5.golden b/src/cmd/gofmt/testdata/rewrite5.golden index 5a448a63d..9beb34aee 100644 --- a/src/cmd/gofmt/testdata/rewrite5.golden +++ b/src/cmd/gofmt/testdata/rewrite5.golden @@ -1,3 +1,5 @@ +//gofmt -r=x+x->2*x + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite5.input b/src/cmd/gofmt/testdata/rewrite5.input index 0d759e69b..d7a6122d0 100644 --- a/src/cmd/gofmt/testdata/rewrite5.input +++ b/src/cmd/gofmt/testdata/rewrite5.input @@ -1,3 +1,5 @@ +//gofmt -r=x+x->2*x + // Copyright 2011 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. diff --git a/src/cmd/gofmt/testdata/rewrite6.golden b/src/cmd/gofmt/testdata/rewrite6.golden index e565dbdd9..48ec9aa0d 100644 --- a/src/cmd/gofmt/testdata/rewrite6.golden +++ b/src/cmd/gofmt/testdata/rewrite6.golden @@ -1,3 +1,5 @@ +//gofmt -r=fun(x)->Fun(x) + // 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. diff --git a/src/cmd/gofmt/testdata/rewrite6.input b/src/cmd/gofmt/testdata/rewrite6.input index 8c088b3e8..b085a84fe 100644 --- a/src/cmd/gofmt/testdata/rewrite6.input +++ b/src/cmd/gofmt/testdata/rewrite6.input @@ -1,3 +1,5 @@ +//gofmt -r=fun(x)->Fun(x) + // 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. diff --git a/src/cmd/gofmt/testdata/rewrite7.golden b/src/cmd/gofmt/testdata/rewrite7.golden index 29babad9f..8386a0b2a 100644 --- a/src/cmd/gofmt/testdata/rewrite7.golden +++ b/src/cmd/gofmt/testdata/rewrite7.golden @@ -1,3 +1,5 @@ +//gofmt -r=fun(x...)->Fun(x) + // 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. diff --git a/src/cmd/gofmt/testdata/rewrite7.input b/src/cmd/gofmt/testdata/rewrite7.input index 073e2a3e6..c1984708e 100644 --- a/src/cmd/gofmt/testdata/rewrite7.input +++ b/src/cmd/gofmt/testdata/rewrite7.input @@ -1,3 +1,5 @@ +//gofmt -r=fun(x...)->Fun(x) + // 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. diff --git a/src/cmd/gofmt/testdata/rewrite8.golden b/src/cmd/gofmt/testdata/rewrite8.golden index cfc452b03..62f0419df 100644 --- a/src/cmd/gofmt/testdata/rewrite8.golden +++ b/src/cmd/gofmt/testdata/rewrite8.golden @@ -1,3 +1,5 @@ +//gofmt -r=interface{}->int + // 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. diff --git a/src/cmd/gofmt/testdata/rewrite8.input b/src/cmd/gofmt/testdata/rewrite8.input index 235efa91c..7964c5c75 100644 --- a/src/cmd/gofmt/testdata/rewrite8.input +++ b/src/cmd/gofmt/testdata/rewrite8.input @@ -1,3 +1,5 @@ +//gofmt -r=interface{}->int + // 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. diff --git a/src/cmd/gofmt/testdata/slices1.golden b/src/cmd/gofmt/testdata/slices1.golden index 6633a5e00..04bc16f21 100644 --- a/src/cmd/gofmt/testdata/slices1.golden +++ b/src/cmd/gofmt/testdata/slices1.golden @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for slice expression simplification. package p diff --git a/src/cmd/gofmt/testdata/slices1.input b/src/cmd/gofmt/testdata/slices1.input index 27e9cb8fe..1f25c43cc 100644 --- a/src/cmd/gofmt/testdata/slices1.input +++ b/src/cmd/gofmt/testdata/slices1.input @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for slice expression simplification. package p diff --git a/src/cmd/gofmt/testdata/slices2.golden b/src/cmd/gofmt/testdata/slices2.golden index 433788e1e..ab657004e 100644 --- a/src/cmd/gofmt/testdata/slices2.golden +++ b/src/cmd/gofmt/testdata/slices2.golden @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for slice expression simplification. // Because of a dot import, these slices must remain untouched. package p diff --git a/src/cmd/gofmt/testdata/slices2.input b/src/cmd/gofmt/testdata/slices2.input index 433788e1e..ab657004e 100644 --- a/src/cmd/gofmt/testdata/slices2.input +++ b/src/cmd/gofmt/testdata/slices2.input @@ -1,3 +1,5 @@ +//gofmt -s + // Test cases for slice expression simplification. // Because of a dot import, these slices must remain untouched. package p diff --git a/src/cmd/gofmt/testdata/stdin1.golden b/src/cmd/gofmt/testdata/stdin1.golden index ff8b0b7ab..9e4dcd20f 100644 --- a/src/cmd/gofmt/testdata/stdin1.golden +++ b/src/cmd/gofmt/testdata/stdin1.golden @@ -1,3 +1,5 @@ + //gofmt -stdin + if x { y } diff --git a/src/cmd/gofmt/testdata/stdin1.golden.gofmt b/src/cmd/gofmt/testdata/stdin1.golden.gofmt deleted file mode 100644 index 1f888877d..000000000 --- a/src/cmd/gofmt/testdata/stdin1.golden.gofmt +++ /dev/null @@ -1,3 +0,0 @@ - if x { - y -} diff --git a/src/cmd/gofmt/testdata/stdin1.input b/src/cmd/gofmt/testdata/stdin1.input index ff8b0b7ab..9e4dcd20f 100644 --- a/src/cmd/gofmt/testdata/stdin1.input +++ b/src/cmd/gofmt/testdata/stdin1.input @@ -1,3 +1,5 @@ + //gofmt -stdin + if x { y } diff --git a/src/cmd/gofmt/testdata/stdin1.input.gofmt b/src/cmd/gofmt/testdata/stdin1.input.gofmt deleted file mode 100644 index 1f888877d..000000000 --- a/src/cmd/gofmt/testdata/stdin1.input.gofmt +++ /dev/null @@ -1,3 +0,0 @@ - if x { - y -} diff --git a/src/cmd/gofmt/testdata/stdin2.golden b/src/cmd/gofmt/testdata/stdin2.golden index 7eb1b54fe..57df35540 100644 --- a/src/cmd/gofmt/testdata/stdin2.golden +++ b/src/cmd/gofmt/testdata/stdin2.golden @@ -1,4 +1,4 @@ - +//gofmt -stdin var x int diff --git a/src/cmd/gofmt/testdata/stdin2.golden.gofmt b/src/cmd/gofmt/testdata/stdin2.golden.gofmt deleted file mode 100644 index 85e800300..000000000 --- a/src/cmd/gofmt/testdata/stdin2.golden.gofmt +++ /dev/null @@ -1,10 +0,0 @@ - - - -var x int - -func f() { - y := z -} - - diff --git a/src/cmd/gofmt/testdata/stdin2.input b/src/cmd/gofmt/testdata/stdin2.input index 99defd2d1..69d6bdd68 100644 --- a/src/cmd/gofmt/testdata/stdin2.input +++ b/src/cmd/gofmt/testdata/stdin2.input @@ -1,4 +1,4 @@ - +//gofmt -stdin var x int diff --git a/src/cmd/gofmt/testdata/stdin2.input.gofmt b/src/cmd/gofmt/testdata/stdin2.input.gofmt deleted file mode 100644 index 7eb1b54fe..000000000 --- a/src/cmd/gofmt/testdata/stdin2.input.gofmt +++ /dev/null @@ -1,11 +0,0 @@ - - -var x int - -func f() { - y := z - /* this is a comment */ - // this is a comment too -} - - diff --git a/src/cmd/gofmt/testdata/stdin3.golden b/src/cmd/gofmt/testdata/stdin3.golden index 1bf2f5a48..d6da0e417 100644 --- a/src/cmd/gofmt/testdata/stdin3.golden +++ b/src/cmd/gofmt/testdata/stdin3.golden @@ -1,3 +1,4 @@ + //gofmt -stdin /* note: no newline at end of file */ for i := 0; i < 10; i++ { diff --git a/src/cmd/gofmt/testdata/stdin3.golden.gofmt b/src/cmd/gofmt/testdata/stdin3.golden.gofmt deleted file mode 100644 index b4d1d4663..000000000 --- a/src/cmd/gofmt/testdata/stdin3.golden.gofmt +++ /dev/null @@ -1,7 +0,0 @@ - - - /* note: no newline at end of file */ - for i := 0; i < 10; i++ { - s += i - } -
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin3.input b/src/cmd/gofmt/testdata/stdin3.input index d963bd0d2..ab46c1063 100644 --- a/src/cmd/gofmt/testdata/stdin3.input +++ b/src/cmd/gofmt/testdata/stdin3.input @@ -1,3 +1,4 @@ + //gofmt -stdin /* note: no newline at end of file */ for i := 0; i < 10; i++ { s += i } diff --git a/src/cmd/gofmt/testdata/stdin3.input.gofmt b/src/cmd/gofmt/testdata/stdin3.input.gofmt deleted file mode 100644 index b4d1d4663..000000000 --- a/src/cmd/gofmt/testdata/stdin3.input.gofmt +++ /dev/null @@ -1,7 +0,0 @@ - - - /* note: no newline at end of file */ - for i := 0; i < 10; i++ { - s += i - } -
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin4.golden b/src/cmd/gofmt/testdata/stdin4.golden index 5f7343551..0c7acace5 100644 --- a/src/cmd/gofmt/testdata/stdin4.golden +++ b/src/cmd/gofmt/testdata/stdin4.golden @@ -1,3 +1,5 @@ + //gofmt -stdin + // comment i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.golden.gofmt b/src/cmd/gofmt/testdata/stdin4.golden.gofmt deleted file mode 100644 index 5f7343551..000000000 --- a/src/cmd/gofmt/testdata/stdin4.golden.gofmt +++ /dev/null @@ -1,3 +0,0 @@ - // comment - - i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.input b/src/cmd/gofmt/testdata/stdin4.input index f02a54fb1..1fc73f31e 100644 --- a/src/cmd/gofmt/testdata/stdin4.input +++ b/src/cmd/gofmt/testdata/stdin4.input @@ -1,3 +1,5 @@ + //gofmt -stdin + // comment i := 0 diff --git a/src/cmd/gofmt/testdata/stdin4.input.gofmt b/src/cmd/gofmt/testdata/stdin4.input.gofmt deleted file mode 100644 index 5f7343551..000000000 --- a/src/cmd/gofmt/testdata/stdin4.input.gofmt +++ /dev/null @@ -1,3 +0,0 @@ - // comment - - i := 0 diff --git a/src/cmd/gofmt/testdata/stdin5.golden b/src/cmd/gofmt/testdata/stdin5.golden new file mode 100644 index 000000000..31ce6b248 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin5.golden @@ -0,0 +1,3 @@ +//gofmt -stdin + +i := 5 // Line comment without newline.
\ No newline at end of file diff --git a/src/cmd/gofmt/testdata/stdin5.input b/src/cmd/gofmt/testdata/stdin5.input new file mode 100644 index 000000000..0a7c97d18 --- /dev/null +++ b/src/cmd/gofmt/testdata/stdin5.input @@ -0,0 +1,3 @@ +//gofmt -stdin + +i :=5// Line comment without newline.
\ No newline at end of file diff --git a/src/cmd/nm/elf.go b/src/cmd/internal/objfile/elf.go index 5aaa194dd..8495fa753 100644 --- a/src/cmd/nm/elf.go +++ b/src/cmd/internal/objfile/elf.go @@ -4,24 +4,29 @@ // Parsing of ELF executables (Linux, FreeBSD, and so on). -package main +package objfile import ( "debug/elf" "os" ) -func elfSymbols(f *os.File) []Sym { - p, err := elf.NewFile(f) +type elfFile struct { + elf *elf.File +} + +func openElf(r *os.File) (rawFile, error) { + f, err := elf.NewFile(r) if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil + return nil, err } + return &elfFile{f}, nil +} - elfSyms, err := p.Symbols() +func (f *elfFile) symbols() ([]Sym, error) { + elfSyms, err := f.elf.Symbols() if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil + return nil, err } var syms []Sym @@ -34,10 +39,10 @@ func elfSymbols(f *os.File) []Sym { sym.Code = 'B' default: i := int(s.Section) - if i < 0 || i >= len(p.Sections) { + if i < 0 || i >= len(f.elf.Sections) { break } - sect := p.Sections[i] + sect := f.elf.Sections[i] switch sect.Flags & (elf.SHF_WRITE | elf.SHF_ALLOC | elf.SHF_EXECINSTR) { case elf.SHF_ALLOC | elf.SHF_EXECINSTR: sym.Code = 'T' @@ -53,5 +58,22 @@ func elfSymbols(f *os.File) []Sym { syms = append(syms, sym) } - return syms + return syms, nil +} + +func (f *elfFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { + if sect := f.elf.Section(".text"); sect != nil { + textStart = sect.Addr + } + if sect := f.elf.Section(".gosymtab"); sect != nil { + if symtab, err = sect.Data(); err != nil { + return 0, nil, nil, err + } + } + if sect := f.elf.Section(".gopclntab"); sect != nil { + if pclntab, err = sect.Data(); err != nil { + return 0, nil, nil, err + } + } + return textStart, symtab, pclntab, nil } diff --git a/src/cmd/nm/goobj.go b/src/cmd/internal/objfile/goobj.go index b0de51db9..a4f49ebe4 100644 --- a/src/cmd/nm/goobj.go +++ b/src/cmd/internal/objfile/goobj.go @@ -4,7 +4,7 @@ // Parsing of Go intermediate object files and archives. -package main +package objfile import ( "debug/goobj" @@ -12,6 +12,18 @@ import ( "os" ) +type goobjFile struct { + goobj *goobj.Package +} + +func openGoobj(r *os.File) (rawFile, error) { + f, err := goobj.Parse(r, `""`) + if err != nil { + return nil, err + } + return &goobjFile{f}, nil +} + func goobjName(id goobj.SymID) string { if id.Version == 0 { return id.Name @@ -19,17 +31,11 @@ func goobjName(id goobj.SymID) string { return fmt.Sprintf("%s<%d>", id.Name, id.Version) } -func goobjSymbols(f *os.File) []Sym { - pkg, err := goobj.Parse(f, `""`) - if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil - } - +func (f *goobjFile) symbols() ([]Sym, error) { seen := make(map[goobj.SymID]bool) var syms []Sym - for _, s := range pkg.Syms { + for _, s := range f.goobj.Syms { seen[s.SymID] = true sym := Sym{Addr: uint64(s.Data.Offset), Name: goobjName(s.SymID), Size: int64(s.Size), Type: s.Type.Name, Code: '?'} switch s.Kind { @@ -50,7 +56,7 @@ func goobjSymbols(f *os.File) []Sym { syms = append(syms, sym) } - for _, s := range pkg.Syms { + for _, s := range f.goobj.Syms { for _, r := range s.Reloc { if !seen[r.Sym] { seen[r.Sym] = true @@ -64,5 +70,12 @@ func goobjSymbols(f *os.File) []Sym { } } - return syms + return syms, nil +} + +// pcln does not make sense for Go object files, because each +// symbol has its own individual pcln table, so there is no global +// space of addresses to map. +func (f *goobjFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { + return 0, nil, nil, fmt.Errorf("pcln not available in go object file") } diff --git a/src/cmd/nm/macho.go b/src/cmd/internal/objfile/macho.go index c60bde55b..f845792ff 100644 --- a/src/cmd/nm/macho.go +++ b/src/cmd/internal/objfile/macho.go @@ -4,36 +4,42 @@ // Parsing of Mach-O executables (OS X). -package main +package objfile import ( "debug/macho" + "fmt" "os" "sort" ) -func machoSymbols(f *os.File) []Sym { - p, err := macho.NewFile(f) +type machoFile struct { + macho *macho.File +} + +func openMacho(r *os.File) (rawFile, error) { + f, err := macho.NewFile(r) if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil + return nil, err } + return &machoFile{f}, nil +} - if p.Symtab == nil { - errorf("%s: no symbol table", f.Name()) - return nil +func (f *machoFile) symbols() ([]Sym, error) { + if f.macho.Symtab == nil { + return nil, fmt.Errorf("missing symbol table") } // Build sorted list of addresses of all symbols. // We infer the size of a symbol by looking at where the next symbol begins. var addrs []uint64 - for _, s := range p.Symtab.Syms { + for _, s := range f.macho.Symtab.Syms { addrs = append(addrs, s.Value) } sort.Sort(uint64s(addrs)) var syms []Sym - for _, s := range p.Symtab.Syms { + for _, s := range f.macho.Symtab.Syms { sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'} i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) if i < len(addrs) { @@ -41,8 +47,8 @@ func machoSymbols(f *os.File) []Sym { } if s.Sect == 0 { sym.Code = 'U' - } else if int(s.Sect) <= len(p.Sections) { - sect := p.Sections[s.Sect-1] + } else if int(s.Sect) <= len(f.macho.Sections) { + sect := f.macho.Sections[s.Sect-1] switch sect.Seg { case "__TEXT": sym.Code = 'R' @@ -59,7 +65,24 @@ func machoSymbols(f *os.File) []Sym { syms = append(syms, sym) } - return syms + return syms, nil +} + +func (f *machoFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { + if sect := f.macho.Section("__text"); sect != nil { + textStart = sect.Addr + } + if sect := f.macho.Section("__gosymtab"); sect != nil { + if symtab, err = sect.Data(); err != nil { + return 0, nil, nil, err + } + } + if sect := f.macho.Section("__gopclntab"); sect != nil { + if pclntab, err = sect.Data(); err != nil { + return 0, nil, nil, err + } + } + return textStart, symtab, pclntab, nil } type uint64s []uint64 diff --git a/src/cmd/internal/objfile/objfile.go b/src/cmd/internal/objfile/objfile.go new file mode 100644 index 000000000..09fa63e60 --- /dev/null +++ b/src/cmd/internal/objfile/objfile.go @@ -0,0 +1,72 @@ +// Copyright 2014 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. + +// Package objfile implements portable access to OS-specific executable files. +package objfile + +import ( + "debug/gosym" + "fmt" + "os" +) + +type rawFile interface { + symbols() (syms []Sym, err error) + pcln() (textStart uint64, symtab, pclntab []byte, err error) +} + +// A File is an opened executable file. +type File struct { + r *os.File + raw rawFile +} + +// A Sym is a symbol defined in an executable file. +type Sym struct { + Name string // symbol name + Addr uint64 // virtual address of symbol + Size int64 // size in bytes + Code rune // nm code (T for text, D for data, and so on) + Type string // XXX? +} + +var openers = []func(*os.File) (rawFile, error){ + openElf, + openGoobj, + openMacho, + openPE, + openPlan9, +} + +// Open opens the named file. +// The caller must call f.Close when the file is no longer needed. +func Open(name string) (*File, error) { + r, err := os.Open(name) + if err != nil { + return nil, err + } + for _, try := range openers { + if raw, err := try(r); err == nil { + return &File{r, raw}, nil + } + } + r.Close() + return nil, fmt.Errorf("open %s: unrecognized object file", name) +} + +func (f *File) Close() error { + return f.r.Close() +} + +func (f *File) Symbols() ([]Sym, error) { + return f.raw.symbols() +} + +func (f *File) PCLineTable() (*gosym.Table, error) { + textStart, symtab, pclntab, err := f.raw.pcln() + if err != nil { + return nil, err + } + return gosym.NewTable(symtab, gosym.NewLineTable(pclntab, textStart)) +} diff --git a/src/cmd/internal/objfile/pe.go b/src/cmd/internal/objfile/pe.go new file mode 100644 index 000000000..868709eaf --- /dev/null +++ b/src/cmd/internal/objfile/pe.go @@ -0,0 +1,170 @@ +// 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. + +// Parsing of PE executables (Microsoft Windows). + +package objfile + +import ( + "debug/pe" + "fmt" + "os" + "sort" +) + +type peFile struct { + pe *pe.File +} + +func openPE(r *os.File) (rawFile, error) { + f, err := pe.NewFile(r) + if err != nil { + return nil, err + } + switch f.OptionalHeader.(type) { + case *pe.OptionalHeader32, *pe.OptionalHeader64: + // ok + default: + return nil, fmt.Errorf("unrecognized PE format") + } + return &peFile{f}, nil +} + +func (f *peFile) symbols() ([]Sym, error) { + // Build sorted list of addresses of all symbols. + // We infer the size of a symbol by looking at where the next symbol begins. + var addrs []uint64 + + var imageBase uint64 + switch oh := f.pe.OptionalHeader.(type) { + case *pe.OptionalHeader32: + imageBase = uint64(oh.ImageBase) + case *pe.OptionalHeader64: + imageBase = oh.ImageBase + } + + var syms []Sym + for _, s := range f.pe.Symbols { + const ( + N_UNDEF = 0 // An undefined (extern) symbol + N_ABS = -1 // An absolute symbol (e_value is a constant, not an address) + N_DEBUG = -2 // A debugging symbol + ) + sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'} + switch s.SectionNumber { + case N_UNDEF: + sym.Code = 'U' + case N_ABS: + sym.Code = 'C' + case N_DEBUG: + sym.Code = '?' + default: + if s.SectionNumber < 0 || len(f.pe.Sections) < int(s.SectionNumber) { + return nil, fmt.Errorf("invalid section number in symbol table") + } + sect := f.pe.Sections[s.SectionNumber-1] + const ( + text = 0x20 + data = 0x40 + bss = 0x80 + permX = 0x20000000 + permR = 0x40000000 + permW = 0x80000000 + ) + ch := sect.Characteristics + switch { + case ch&text != 0: + sym.Code = 'T' + case ch&data != 0: + if ch&permW == 0 { + sym.Code = 'R' + } else { + sym.Code = 'D' + } + case ch&bss != 0: + sym.Code = 'B' + } + sym.Addr += imageBase + uint64(sect.VirtualAddress) + } + syms = append(syms, sym) + addrs = append(addrs, sym.Addr) + } + + sort.Sort(uint64s(addrs)) + for i := range syms { + j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr }) + if j < len(addrs) { + syms[i].Size = int64(addrs[j] - syms[i].Addr) + } + } + + return syms, nil +} + +func (f *peFile) pcln() (textStart uint64, symtab, pclntab []byte, err error) { + var imageBase uint64 + switch oh := f.pe.OptionalHeader.(type) { + case *pe.OptionalHeader32: + imageBase = uint64(oh.ImageBase) + case *pe.OptionalHeader64: + imageBase = oh.ImageBase + default: + return 0, nil, nil, fmt.Errorf("pe file format not recognized") + } + if sect := f.pe.Section(".text"); sect != nil { + textStart = imageBase + uint64(sect.VirtualAddress) + } + if pclntab, err = loadPETable(f.pe, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPETable(f.pe, "pclntab", "epclntab"); err2 != nil { + return 0, nil, nil, err + } + } + if symtab, err = loadPETable(f.pe, "runtime.symtab", "runtime.esymtab"); err != nil { + // Same as above. + var err2 error + if symtab, err2 = loadPETable(f.pe, "symtab", "esymtab"); err2 != nil { + return 0, nil, nil, err + } + } + return textStart, symtab, pclntab, nil +} + +func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) { + for _, s := range f.Symbols { + if s.Name != name { + continue + } + if s.SectionNumber <= 0 { + return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber) + } + if len(f.Sections) < int(s.SectionNumber) { + return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections)) + } + return s, nil + } + return nil, fmt.Errorf("no %s symbol found", name) +} + +func loadPETable(f *pe.File, sname, ename string) ([]byte, error) { + ssym, err := findPESymbol(f, sname) + if err != nil { + return nil, err + } + esym, err := findPESymbol(f, ename) + if err != nil { + return nil, err + } + if ssym.SectionNumber != esym.SectionNumber { + return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename) + } + sect := f.Sections[ssym.SectionNumber-1] + data, err := sect.Data() + if err != nil { + return nil, err + } + return data[ssym.Value:esym.Value], nil +} diff --git a/src/cmd/internal/objfile/plan9obj.go b/src/cmd/internal/objfile/plan9obj.go new file mode 100644 index 000000000..80744f82a --- /dev/null +++ b/src/cmd/internal/objfile/plan9obj.go @@ -0,0 +1,124 @@ +// Copyright 2014 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. + +// Parsing of Plan 9 a.out executables. + +package objfile + +import ( + "debug/plan9obj" + "fmt" + "os" + "sort" +) + +var validSymType = map[rune]bool{ + 'T': true, + 't': true, + 'D': true, + 'd': true, + 'B': true, + 'b': true, +} + +type plan9File struct { + plan9 *plan9obj.File +} + +func openPlan9(r *os.File) (rawFile, error) { + f, err := plan9obj.NewFile(r) + if err != nil { + return nil, err + } + return &plan9File{f}, nil +} + +func (f *plan9File) symbols() ([]Sym, error) { + plan9Syms, err := f.plan9.Symbols() + if err != nil { + return nil, err + } + + // Build sorted list of addresses of all symbols. + // We infer the size of a symbol by looking at where the next symbol begins. + var addrs []uint64 + for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } + addrs = append(addrs, s.Value) + } + sort.Sort(uint64s(addrs)) + + var syms []Sym + + for _, s := range plan9Syms { + if !validSymType[s.Type] { + continue + } + sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)} + i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) + if i < len(addrs) { + sym.Size = int64(addrs[i] - s.Value) + } + syms = append(syms, sym) + } + + return syms, nil +} + +func (f *plan9File) pcln() (textStart uint64, symtab, pclntab []byte, err error) { + textStart = f.plan9.LoadAddress + f.plan9.HdrSize + if pclntab, err = loadPlan9Table(f.plan9, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPlan9Table(f.plan9, "pclntab", "epclntab"); err2 != nil { + return 0, nil, nil, err + } + } + if symtab, err = loadPlan9Table(f.plan9, "runtime.symtab", "runtime.esymtab"); err != nil { + // Same as above. + var err2 error + if symtab, err2 = loadPlan9Table(f.plan9, "symtab", "esymtab"); err2 != nil { + return 0, nil, nil, err + } + } + return textStart, symtab, pclntab, nil +} + +func findPlan9Symbol(f *plan9obj.File, name string) (*plan9obj.Sym, error) { + syms, err := f.Symbols() + if err != nil { + return nil, err + } + for _, s := range syms { + if s.Name != name { + continue + } + return &s, nil + } + return nil, fmt.Errorf("no %s symbol found", name) +} + +func loadPlan9Table(f *plan9obj.File, sname, ename string) ([]byte, error) { + ssym, err := findPlan9Symbol(f, sname) + if err != nil { + return nil, err + } + esym, err := findPlan9Symbol(f, ename) + if err != nil { + return nil, err + } + sect := f.Section("text") + if sect == nil { + return nil, err + } + data, err := sect.Data() + if err != nil { + return nil, err + } + textStart := f.LoadAddress + f.HdrSize + return data[ssym.Value-textStart : esym.Value-textStart], nil +} diff --git a/src/cmd/ld/data.c b/src/cmd/ld/data.c index 702f559a8..7bf18f081 100644 --- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -145,7 +145,7 @@ relocsym(LSym *s) diag("%s: invalid relocation %d+%d not in [%d,%d)", s->name, off, siz, 0, s->np); continue; } - if(r->sym != S && (r->sym->type & SMASK == 0 || r->sym->type & SMASK == SXREF)) { + if(r->sym != S && ((r->sym->type & (SMASK | SHIDDEN)) == 0 || (r->sym->type & SMASK) == SXREF)) { diag("%s: not defined", r->sym->name); continue; } @@ -281,9 +281,13 @@ relocsym(LSym *s) if(thechar == '6') o = 0; } else if(HEADTYPE == Hdarwin) { - if(rs->type != SHOSTOBJ) - o += symaddr(rs) - rs->sect->vaddr; - o -= r->off; // WTF? + if(r->type == R_CALL) { + if(rs->type != SHOSTOBJ) + o += symaddr(rs) - rs->sect->vaddr; + o -= r->off; // relative to section offset, not symbol + } else { + o += r->siz; + } } else { diag("unhandled pcrel relocation for %s", headstring); } @@ -425,10 +429,10 @@ dynreloc(void) } static void -blk(LSym *start, int32 addr, int32 size) +blk(LSym *start, int64 addr, int64 size) { LSym *sym; - int32 eaddr; + int64 eaddr; uchar *p, *ep; for(sym = start; sym != nil; sym = sym->next) @@ -469,10 +473,10 @@ blk(LSym *start, int32 addr, int32 size) } void -codeblk(int32 addr, int32 size) +codeblk(int64 addr, int64 size) { LSym *sym; - int32 eaddr, n; + int64 eaddr, n; uchar *q; if(debug['a']) @@ -529,10 +533,10 @@ codeblk(int32 addr, int32 size) } void -datblk(int32 addr, int32 size) +datblk(int64 addr, int64 size) { LSym *sym; - int32 i, eaddr; + int64 i, eaddr; uchar *p, *ep; char *typ, *rsname; Reloc *r; @@ -985,8 +989,8 @@ dodata(void) sect->align = maxalign(s, SINITARR-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "noptrdata", 0)->sect = sect; - linklookup(ctxt, "enoptrdata", 0)->sect = sect; + linklookup(ctxt, "runtime.noptrdata", 0)->sect = sect; + linklookup(ctxt, "runtime.enoptrdata", 0)->sect = sect; for(; s != nil && s->type < SINITARR; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1016,9 +1020,9 @@ dodata(void) sect->align = maxalign(s, SBSS-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "data", 0)->sect = sect; - linklookup(ctxt, "edata", 0)->sect = sect; - gcdata = linklookup(ctxt, "gcdata", 0); + linklookup(ctxt, "runtime.data", 0)->sect = sect; + linklookup(ctxt, "runtime.edata", 0)->sect = sect; + gcdata = linklookup(ctxt, "runtime.gcdata", 0); proggeninit(&gen, gcdata); for(; s != nil && s->type < SBSS; s = s->next) { if(s->type == SINITARR) { @@ -1040,9 +1044,9 @@ dodata(void) sect->align = maxalign(s, SNOPTRBSS-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "bss", 0)->sect = sect; - linklookup(ctxt, "ebss", 0)->sect = sect; - gcbss = linklookup(ctxt, "gcbss", 0); + linklookup(ctxt, "runtime.bss", 0)->sect = sect; + linklookup(ctxt, "runtime.ebss", 0)->sect = sect; + gcbss = linklookup(ctxt, "runtime.gcbss", 0); proggeninit(&gen, gcbss); for(; s != nil && s->type < SNOPTRBSS; s = s->next) { s->sect = sect; @@ -1059,8 +1063,8 @@ dodata(void) sect->align = maxalign(s, SNOPTRBSS); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "noptrbss", 0)->sect = sect; - linklookup(ctxt, "enoptrbss", 0)->sect = sect; + linklookup(ctxt, "runtime.noptrbss", 0)->sect = sect; + linklookup(ctxt, "runtime.enoptrbss", 0)->sect = sect; for(; s != nil && s->type == SNOPTRBSS; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1068,7 +1072,7 @@ dodata(void) growdatsize(&datsize, s); } sect->len = datsize - sect->vaddr; - linklookup(ctxt, "end", 0)->sect = sect; + linklookup(ctxt, "runtime.end", 0)->sect = sect; // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits. if(datsize != (uint32)datsize) { @@ -1139,8 +1143,8 @@ dodata(void) sect->align = maxalign(s, STYPELINK-1); datsize = rnd(datsize, sect->align); sect->vaddr = 0; - linklookup(ctxt, "rodata", 0)->sect = sect; - linklookup(ctxt, "erodata", 0)->sect = sect; + linklookup(ctxt, "runtime.rodata", 0)->sect = sect; + linklookup(ctxt, "runtime.erodata", 0)->sect = sect; for(; s != nil && s->type < STYPELINK; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1155,8 +1159,8 @@ dodata(void) sect->align = maxalign(s, STYPELINK); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "typelink", 0)->sect = sect; - linklookup(ctxt, "etypelink", 0)->sect = sect; + linklookup(ctxt, "runtime.typelink", 0)->sect = sect; + linklookup(ctxt, "runtime.etypelink", 0)->sect = sect; for(; s != nil && s->type == STYPELINK; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1171,8 +1175,8 @@ dodata(void) sect->align = maxalign(s, SPCLNTAB-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "symtab", 0)->sect = sect; - linklookup(ctxt, "esymtab", 0)->sect = sect; + linklookup(ctxt, "runtime.symtab", 0)->sect = sect; + linklookup(ctxt, "runtime.esymtab", 0)->sect = sect; for(; s != nil && s->type < SPCLNTAB; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1187,8 +1191,8 @@ dodata(void) sect->align = maxalign(s, SELFROSECT-1); datsize = rnd(datsize, sect->align); sect->vaddr = datsize; - linklookup(ctxt, "pclntab", 0)->sect = sect; - linklookup(ctxt, "epclntab", 0)->sect = sect; + linklookup(ctxt, "runtime.pclntab", 0)->sect = sect; + linklookup(ctxt, "runtime.epclntab", 0)->sect = sect; for(; s != nil && s->type < SELFROSECT; s = s->next) { datsize = aligndatsize(datsize, s); s->sect = sect; @@ -1241,8 +1245,8 @@ textaddress(void) // and then letting threads copy down, but probably not worth it. sect = segtext.sect; sect->align = funcalign; - linklookup(ctxt, "text", 0)->sect = sect; - linklookup(ctxt, "etext", 0)->sect = sect; + linklookup(ctxt, "runtime.text", 0)->sect = sect; + linklookup(ctxt, "runtime.etext", 0)->sect = sect; va = INITTEXT; sect->vaddr = va; for(sym = ctxt->textp; sym != nil; sym = sym->next) { @@ -1353,32 +1357,32 @@ address(void) sub->value += sym->value; } - xdefine("text", STEXT, text->vaddr); - xdefine("etext", STEXT, text->vaddr + text->len); - xdefine("rodata", SRODATA, rodata->vaddr); - xdefine("erodata", SRODATA, rodata->vaddr + rodata->len); - xdefine("typelink", SRODATA, typelink->vaddr); - xdefine("etypelink", SRODATA, typelink->vaddr + typelink->len); - - sym = linklookup(ctxt, "gcdata", 0); - xdefine("egcdata", SRODATA, symaddr(sym) + sym->size); - linklookup(ctxt, "egcdata", 0)->sect = sym->sect; - - sym = linklookup(ctxt, "gcbss", 0); - xdefine("egcbss", SRODATA, symaddr(sym) + sym->size); - linklookup(ctxt, "egcbss", 0)->sect = sym->sect; - - xdefine("symtab", SRODATA, symtab->vaddr); - xdefine("esymtab", SRODATA, symtab->vaddr + symtab->len); - xdefine("pclntab", SRODATA, pclntab->vaddr); - xdefine("epclntab", SRODATA, pclntab->vaddr + pclntab->len); - xdefine("noptrdata", SNOPTRDATA, noptr->vaddr); - xdefine("enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len); - xdefine("bss", SBSS, bss->vaddr); - xdefine("ebss", SBSS, bss->vaddr + bss->len); - xdefine("data", SDATA, data->vaddr); - xdefine("edata", SDATA, data->vaddr + data->len); - xdefine("noptrbss", SNOPTRBSS, noptrbss->vaddr); - xdefine("enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len); - xdefine("end", SBSS, segdata.vaddr + segdata.len); + xdefine("runtime.text", STEXT, text->vaddr); + xdefine("runtime.etext", STEXT, text->vaddr + text->len); + xdefine("runtime.rodata", SRODATA, rodata->vaddr); + xdefine("runtime.erodata", SRODATA, rodata->vaddr + rodata->len); + xdefine("runtime.typelink", SRODATA, typelink->vaddr); + xdefine("runtime.etypelink", SRODATA, typelink->vaddr + typelink->len); + + sym = linklookup(ctxt, "runtime.gcdata", 0); + xdefine("runtime.egcdata", SRODATA, symaddr(sym) + sym->size); + linklookup(ctxt, "runtime.egcdata", 0)->sect = sym->sect; + + sym = linklookup(ctxt, "runtime.gcbss", 0); + xdefine("runtime.egcbss", SRODATA, symaddr(sym) + sym->size); + linklookup(ctxt, "runtime.egcbss", 0)->sect = sym->sect; + + xdefine("runtime.symtab", SRODATA, symtab->vaddr); + xdefine("runtime.esymtab", SRODATA, symtab->vaddr + symtab->len); + xdefine("runtime.pclntab", SRODATA, pclntab->vaddr); + xdefine("runtime.epclntab", SRODATA, pclntab->vaddr + pclntab->len); + xdefine("runtime.noptrdata", SNOPTRDATA, noptr->vaddr); + xdefine("runtime.enoptrdata", SNOPTRDATA, noptr->vaddr + noptr->len); + xdefine("runtime.bss", SBSS, bss->vaddr); + xdefine("runtime.ebss", SBSS, bss->vaddr + bss->len); + xdefine("runtime.data", SDATA, data->vaddr); + xdefine("runtime.edata", SDATA, data->vaddr + data->len); + xdefine("runtime.noptrbss", SNOPTRBSS, noptrbss->vaddr); + xdefine("runtime.enoptrbss", SNOPTRBSS, noptrbss->vaddr + noptrbss->len); + xdefine("runtime.end", SBSS, segdata.vaddr + segdata.len); } diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 5db41f9a7..7b997464d 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -1351,10 +1351,10 @@ genasmsym(void (*put)(LSym*, char*, int, vlong, vlong, int, LSym*)) // These symbols won't show up in the first loop below because we // skip STEXT symbols. Normal STEXT symbols are emitted by walking textp. - s = linklookup(ctxt, "text", 0); + s = linklookup(ctxt, "runtime.text", 0); if(s->type == STEXT) put(s, s->name, 'T', s->value, s->size, s->version, 0); - s = linklookup(ctxt, "etext", 0); + s = linklookup(ctxt, "runtime.etext", 0); if(s->type == STEXT) put(s, s->name, 'T', s->value, s->size, s->version, 0); @@ -1556,3 +1556,56 @@ diag(char *fmt, ...) errorexit(); } } + +void +checkgo(void) +{ + LSym *s; + Reloc *r; + int i; + int changed; + + if(!debug['C']) + return; + + // TODO(rsc,khr): Eventually we want to get to no Go-called C functions at all, + // which would simplify this logic quite a bit. + + // Mark every Go-called C function with cfunc=2, recursively. + do { + changed = 0; + for(s = ctxt->textp; s != nil; s = s->next) { + if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) { + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + if(r->sym == nil) + continue; + if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) { + if(r->sym->cfunc == 1) { + changed = 1; + r->sym->cfunc = 2; + } + } + } + } + } + }while(changed); + + // Complain about Go-called C functions that can split the stack + // (that can be preempted for garbage collection or trigger a stack copy). + for(s = ctxt->textp; s != nil; s = s->next) { + if(s->cfunc == 0 || (s->cfunc == 2 && s->nosplit)) { + for(i=0; i<s->nr; i++) { + r = &s->r[i]; + if(r->sym == nil) + continue; + if((r->type == R_CALL || r->type == R_CALLARM) && r->sym->type == STEXT) { + if(s->cfunc == 0 && r->sym->cfunc == 2 && !r->sym->nosplit) + print("Go %s calls C %s\n", s->name, r->sym->name); + else if(s->cfunc == 2 && s->nosplit && !r->sym->nosplit) + print("Go calls C %s calls %s\n", s->name, r->sym->name); + } + } + } + } +} diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index 4094dfa6b..067ffa0bc 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -183,12 +183,13 @@ uint16 be16(uchar *b); uint32 be32(uchar *b); uint64 be64(uchar *b); void callgraph(void); +void checkgo(void); void cflush(void); -void codeblk(int32 addr, int32 size); +void codeblk(int64 addr, int64 size); vlong cpos(void); void cseek(vlong p); void cwrite(void *buf, int n); -void datblk(int32 addr, int32 size); +void datblk(int64 addr, int64 size); int datcmp(LSym *s1, LSym *s2); vlong datoff(vlong addr); void deadcode(void); diff --git a/src/cmd/ld/pcln.c b/src/cmd/ld/pcln.c index 4c2ffa78e..3cd9e65da 100644 --- a/src/cmd/ld/pcln.c +++ b/src/cmd/ld/pcln.c @@ -90,7 +90,7 @@ renumberfiles(Link *ctxt, LSym **files, int nfiles, Pcdata *d) } dv = val - newval; newval = val; - v = (uint32)(dv<<1) ^ (uint32)(int32)(dv>>31); + v = ((uint32)dv<<1) ^ (uint32)(int32)(dv>>31); addvarint(&out, v); // pc delta @@ -119,7 +119,7 @@ pclntab(void) static Pcln zpcln; funcdata_bytes = 0; - ftab = linklookup(ctxt, "pclntab", 0); + ftab = linklookup(ctxt, "runtime.pclntab", 0); ftab->type = SPCLNTAB; ftab->reachable = 1; diff --git a/src/cmd/ld/pobj.c b/src/cmd/ld/pobj.c index d78dacd36..54c5ef247 100644 --- a/src/cmd/ld/pobj.c +++ b/src/cmd/ld/pobj.c @@ -71,6 +71,7 @@ main(int argc, char *argv[]) if(thechar == '6') flagcount("8", "assume 64-bit addresses", &debug['8']); flagfn1("B", "info: define ELF NT_GNU_BUILD_ID note", addbuildinfo); + flagcount("C", "check Go calls to C code", &debug['C']); flagint64("D", "addr: data address", &INITDAT); flagstr("E", "sym: entry symbol", &INITENTRY); if(thechar == '5') @@ -162,6 +163,7 @@ main(int argc, char *argv[]) mark(linklookup(ctxt, "runtime.read_tls_fallback", 0)); } + checkgo(); deadcode(); callgraph(); paramspace = "SP"; /* (FP) now (SP) on output */ diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index 8149a6716..c3a72c3cf 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -347,36 +347,36 @@ symtab(void) // Define these so that they'll get put into the symbol table. // data.c:/^address will provide the actual values. - xdefine("text", STEXT, 0); - xdefine("etext", STEXT, 0); - xdefine("typelink", SRODATA, 0); - xdefine("etypelink", SRODATA, 0); - xdefine("rodata", SRODATA, 0); - xdefine("erodata", SRODATA, 0); - xdefine("noptrdata", SNOPTRDATA, 0); - xdefine("enoptrdata", SNOPTRDATA, 0); - xdefine("data", SDATA, 0); - xdefine("edata", SDATA, 0); - xdefine("bss", SBSS, 0); - xdefine("ebss", SBSS, 0); - xdefine("noptrbss", SNOPTRBSS, 0); - xdefine("enoptrbss", SNOPTRBSS, 0); - xdefine("end", SBSS, 0); - xdefine("epclntab", SRODATA, 0); - xdefine("esymtab", SRODATA, 0); + xdefine("runtime.text", STEXT, 0); + xdefine("runtime.etext", STEXT, 0); + xdefine("runtime.typelink", SRODATA, 0); + xdefine("runtime.etypelink", SRODATA, 0); + xdefine("runtime.rodata", SRODATA, 0); + xdefine("runtime.erodata", SRODATA, 0); + xdefine("runtime.noptrdata", SNOPTRDATA, 0); + xdefine("runtime.enoptrdata", SNOPTRDATA, 0); + xdefine("runtime.data", SDATA, 0); + xdefine("runtime.edata", SDATA, 0); + xdefine("runtime.bss", SBSS, 0); + xdefine("runtime.ebss", SBSS, 0); + xdefine("runtime.noptrbss", SNOPTRBSS, 0); + xdefine("runtime.enoptrbss", SNOPTRBSS, 0); + xdefine("runtime.end", SBSS, 0); + xdefine("runtime.epclntab", SRODATA, 0); + xdefine("runtime.esymtab", SRODATA, 0); // garbage collection symbols - s = linklookup(ctxt, "gcdata", 0); + s = linklookup(ctxt, "runtime.gcdata", 0); s->type = SRODATA; s->size = 0; s->reachable = 1; - xdefine("egcdata", SRODATA, 0); + xdefine("runtime.egcdata", SRODATA, 0); - s = linklookup(ctxt, "gcbss", 0); + s = linklookup(ctxt, "runtime.gcbss", 0); s->type = SRODATA; s->size = 0; s->reachable = 1; - xdefine("egcbss", SRODATA, 0); + xdefine("runtime.egcbss", SRODATA, 0); // pseudo-symbols to mark locations of type, string, and go string data. s = linklookup(ctxt, "type.*", 0); @@ -397,9 +397,9 @@ symtab(void) s->reachable = 1; symgofunc = s; - symtypelink = linklookup(ctxt, "typelink", 0); + symtypelink = linklookup(ctxt, "runtime.typelink", 0); - symt = linklookup(ctxt, "symtab", 0); + symt = linklookup(ctxt, "runtime.symtab", 0); symt->type = SSYMTAB; symt->size = 0; symt->reachable = 1; diff --git a/src/cmd/link/auto.go b/src/cmd/link/auto.go index 8f0c39f8c..f9228e8ca 100644 --- a/src/cmd/link/auto.go +++ b/src/cmd/link/auto.go @@ -18,26 +18,26 @@ import ( // linkerDefined lists the symbols supplied by other parts of the linker // (runtime.go and layout.go). var linkerDefined = map[string]bool{ - "bss": true, - "data": true, - "ebss": true, - "edata": true, - "efunctab": true, - "end": true, - "enoptrbss": true, - "enoptrdata": true, - "erodata": true, - "etext": true, - "etypelink": true, - "functab": true, - "gcbss": true, - "gcdata": true, - "noptrbss": true, - "noptrdata": true, - "pclntab": true, - "rodata": true, - "text": true, - "typelink": true, + "runtime.bss": true, + "runtime.data": true, + "runtime.ebss": true, + "runtime.edata": true, + "runtime.efunctab": true, + "runtime.end": true, + "runtime.enoptrbss": true, + "runtime.enoptrdata": true, + "runtime.erodata": true, + "runtime.etext": true, + "runtime.etypelink": true, + "runtime.functab": true, + "runtime.gcbss": true, + "runtime.gcdata": true, + "runtime.noptrbss": true, + "runtime.noptrdata": true, + "runtime.pclntab": true, + "runtime.rodata": true, + "runtime.text": true, + "runtime.typelink": true, } // isAuto reports whether sym is an automatically-generated data or constant symbol. diff --git a/src/cmd/link/layout.go b/src/cmd/link/layout.go index 647702244..149ebced0 100644 --- a/src/cmd/link/layout.go +++ b/src/cmd/link/layout.go @@ -172,9 +172,9 @@ func (p *Prog) layout() { start = sect.VirtAddr end = sect.VirtAddr + sect.Size } - p.defineConst(name, start) - p.defineConst("e"+name, end) + p.defineConst("runtime."+name, start) + p.defineConst("runtime.e"+name, end) progEnd = end } - p.defineConst("end", progEnd) + p.defineConst("runtime.end", progEnd) } diff --git a/src/cmd/link/pclntab.go b/src/cmd/link/pclntab.go index b0b19ad53..232d586bf 100644 --- a/src/cmd/link/pclntab.go +++ b/src/cmd/link/pclntab.go @@ -183,7 +183,7 @@ func (p *Prog) pclntab() { pclntab := &Sym{ Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "pclntab"}, + SymID: goobj.SymID{Name: "runtime.pclntab"}, Kind: goobj.SPCLNTAB, Size: buf.Size(), Reloc: buf.Reloc(), @@ -437,7 +437,7 @@ func (it *PCIter) Next() { return } it.start = false - sv := int32(uv)>>1 ^ int32(uv)<<31>>31 + sv := int32(uv>>1) ^ int32(uv<<31)>>31 it.Value += sv // pc delta diff --git a/src/cmd/link/pclntab_test.go b/src/cmd/link/pclntab_test.go index 5696a0978..19953f579 100644 --- a/src/cmd/link/pclntab_test.go +++ b/src/cmd/link/pclntab_test.go @@ -141,7 +141,7 @@ func TestPclntab(t *testing.T) { // It returns a symbol reader for pclntab, the offset of the function information // within that symbol, and the args and frame values read out of the information. func findFunc(t *testing.T, p *Prog, name string) (r *SymReader, off, args, frame int, ok bool) { - tabsym := p.Syms[goobj.SymID{Name: "pclntab"}] + tabsym := p.Syms[goobj.SymID{Name: "runtime.pclntab"}] if tabsym == nil { t.Errorf("pclntab is missing in binary") return @@ -276,6 +276,12 @@ func checkPCData(t *testing.T, r *SymReader, name string, off, pc, pnum, val int // readPCData reads the PCData table offset off // to obtain and return the value associated with pc. func readPCData(t *testing.T, r *SymReader, name, pcdataname string, pcoff uint32, pc int) (int, bool) { + // "If pcsp, pcfile, pcln, or any of the pcdata offsets is zero, + // that table is considered missing, and all PCs take value -1." + if pcoff == 0 { + return -1, true + } + var it PCIter for it.Init(r.p, r.data[pcoff:]); !it.Done; it.Next() { if it.PC <= uint32(pc) && uint32(pc) < it.NextPC { diff --git a/src/cmd/link/runtime.go b/src/cmd/link/runtime.go index 5d37015eb..b0c1ac98a 100644 --- a/src/cmd/link/runtime.go +++ b/src/cmd/link/runtime.go @@ -15,13 +15,13 @@ func (p *Prog) runtime() { // TODO: Implement garbage collection data. p.addSym(&Sym{ Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "gcdata"}, + SymID: goobj.SymID{Name: "runtime.gcdata"}, Kind: goobj.SRODATA, }, }) p.addSym(&Sym{ Sym: &goobj.Sym{ - SymID: goobj.SymID{Name: "gcbss"}, + SymID: goobj.SymID{Name: "runtime.gcbss"}, Kind: goobj.SRODATA, }, }) diff --git a/src/cmd/link/testdata/Makefile b/src/cmd/link/testdata/Makefile index 3b1b15f73..e9651a03f 100644 --- a/src/cmd/link/testdata/Makefile +++ b/src/cmd/link/testdata/Makefile @@ -9,7 +9,7 @@ ALL=\ all: $(ALL) %.6: %.s - go tool 6a $*.s + GOARCH=amd64 GOOS=darwin go tool 6a -trimpath=$(shell pwd) $*.s pclntab.s: genpcln.go go run genpcln.go >pclntab.s diff --git a/src/cmd/link/testdata/autosection.6 b/src/cmd/link/testdata/autosection.6 Binary files differindex bc9d446e1..386f422cf 100644 --- a/src/cmd/link/testdata/autosection.6 +++ b/src/cmd/link/testdata/autosection.6 diff --git a/src/cmd/link/testdata/autosection.s b/src/cmd/link/testdata/autosection.s index 8a579b702..e0cb21723 100644 --- a/src/cmd/link/testdata/autosection.s +++ b/src/cmd/link/testdata/autosection.s @@ -4,7 +4,7 @@ // Test of section-named symbols. -#include "../../ld/textflag.h" +#include "textflag.h" TEXT start(SB),7,$0 MOVQ $autotab(SB),AX @@ -16,37 +16,37 @@ GLOBL zero(SB), $8 GLOBL zeronoptr(SB), NOPTR, $16 // text -DATA autotab+0x00(SB)/8, $text(SB) +DATA autotab+0x00(SB)/8, $runtime·text(SB) DATA autotab+0x08(SB)/8, $start(SB) -DATA autotab+0x10(SB)/8, $etext(SB) +DATA autotab+0x10(SB)/8, $runtime·etext(SB) DATA autotab+0x18(SB)/8, $start+16(SB) // data -DATA autotab+0x20(SB)/8, $data(SB) +DATA autotab+0x20(SB)/8, $runtime·data(SB) DATA autotab+0x28(SB)/8, $autotab(SB) -DATA autotab+0x30(SB)/8, $edata(SB) +DATA autotab+0x30(SB)/8, $runtime·edata(SB) DATA autotab+0x38(SB)/8, $nonzero+4(SB) // bss -DATA autotab+0x40(SB)/8, $bss(SB) +DATA autotab+0x40(SB)/8, $runtime·bss(SB) DATA autotab+0x48(SB)/8, $zero(SB) -DATA autotab+0x50(SB)/8, $ebss(SB) +DATA autotab+0x50(SB)/8, $runtime·ebss(SB) DATA autotab+0x58(SB)/8, $zero+8(SB) // noptrdata -DATA autotab+0x60(SB)/8, $noptrdata(SB) +DATA autotab+0x60(SB)/8, $runtime·noptrdata(SB) DATA autotab+0x68(SB)/8, $nonzeronoptr(SB) -DATA autotab+0x70(SB)/8, $enoptrdata(SB) +DATA autotab+0x70(SB)/8, $runtime·enoptrdata(SB) DATA autotab+0x78(SB)/8, $nonzeronoptr+8(SB) // noptrbss -DATA autotab+0x80(SB)/8, $noptrbss(SB) +DATA autotab+0x80(SB)/8, $runtime·noptrbss(SB) DATA autotab+0x88(SB)/8, $zeronoptr(SB) -DATA autotab+0x90(SB)/8, $enoptrbss(SB) +DATA autotab+0x90(SB)/8, $runtime·enoptrbss(SB) DATA autotab+0x98(SB)/8, $zeronoptr+16(SB) // end -DATA autotab+0xa0(SB)/8, $end(SB) +DATA autotab+0xa0(SB)/8, $runtime·end(SB) DATA autotab+0xa8(SB)/8, $zeronoptr+16(SB) GLOBL autotab(SB), $0xb0 diff --git a/src/cmd/link/testdata/autoweak.6 b/src/cmd/link/testdata/autoweak.6 Binary files differindex 636a4d844..5d74d4e2b 100644 --- a/src/cmd/link/testdata/autoweak.6 +++ b/src/cmd/link/testdata/autoweak.6 diff --git a/src/cmd/link/testdata/dead.6 b/src/cmd/link/testdata/dead.6 Binary files differindex bb77aafe8..9540adc1a 100644 --- a/src/cmd/link/testdata/dead.6 +++ b/src/cmd/link/testdata/dead.6 diff --git a/src/cmd/link/testdata/dead.s b/src/cmd/link/testdata/dead.s index 832ddaff6..86f31360f 100644 --- a/src/cmd/link/testdata/dead.s +++ b/src/cmd/link/testdata/dead.s @@ -17,7 +17,7 @@ TEXT text1(SB),7,$0 RET TEXT text2(SB),7,$0 - MOVQ $edata(SB),BX + MOVQ $runtime·edata(SB),BX RET DATA data1<>+0(SB)/8, $data2(SB) @@ -46,4 +46,3 @@ GLOBL dead_data1(SB), $16 GLOBL dead_data2(SB), $1 GLOBL dead_data3(SB), $1 GLOBL dead_funcdata(SB), $8 - diff --git a/src/cmd/link/testdata/genpcln.go b/src/cmd/link/testdata/genpcln.go index 684cc07a2..c10eaeae9 100644 --- a/src/cmd/link/testdata/genpcln.go +++ b/src/cmd/link/testdata/genpcln.go @@ -107,6 +107,6 @@ func main() { for f := 0; f < 3; f++ { fmt.Printf("\tCALL func%d(SB)\n", f) } - fmt.Printf("\tMOVQ $pclntab(SB), AX\n") + fmt.Printf("\tMOVQ $runtime·pclntab(SB), AX\n") fmt.Printf("\n\tRET\n") } diff --git a/src/cmd/link/testdata/hello.6 b/src/cmd/link/testdata/hello.6 Binary files differindex b129dc029..67983f1a9 100644 --- a/src/cmd/link/testdata/hello.6 +++ b/src/cmd/link/testdata/hello.6 diff --git a/src/cmd/link/testdata/layout.6 b/src/cmd/link/testdata/layout.6 Binary files differindex 8cd5bd2cd..db24ef3e5 100644 --- a/src/cmd/link/testdata/layout.6 +++ b/src/cmd/link/testdata/layout.6 diff --git a/src/cmd/link/testdata/layout.s b/src/cmd/link/testdata/layout.s index 0d492c5af..c3e55ef49 100644 --- a/src/cmd/link/testdata/layout.s +++ b/src/cmd/link/testdata/layout.s @@ -5,7 +5,7 @@ // Test of section assignment in layout.go. // Each symbol should end up in the section named by the symbol name prefix (up to the underscore). -#include "../../ld/textflag.h" +#include "textflag.h" TEXT text_start(SB),7,$0 MOVQ $rodata_sym(SB), AX diff --git a/src/cmd/link/testdata/link.hello.darwin.amd64 b/src/cmd/link/testdata/link.hello.darwin.amd64 index b1f0a93b2..0bd475dd8 100644 --- a/src/cmd/link/testdata/link.hello.darwin.amd64 +++ b/src/cmd/link/testdata/link.hello.darwin.amd64 @@ -6,8 +6,8 @@ * 00000060 00 00 00 00 00 00 00 00 19 00 00 00 38 01 00 00 |............8...| 00000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000080 00 10 00 00 00 00 00 00 d6 10 00 00 00 00 00 00 |................| -00000090 00 00 00 00 00 00 00 00 d6 10 00 00 00 00 00 00 |................| +00000080 00 10 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................| +00000090 00 00 00 00 00 00 00 00 b0 10 00 00 00 00 00 00 |................| 000000a0 07 00 00 00 05 00 00 00 03 00 00 00 00 00 00 00 |................| 000000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 |__text..........| 000000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| @@ -21,7 +21,7 @@ * 00000150 5f 5f 66 75 6e 63 74 61 62 00 00 00 00 00 00 00 |__functab.......| 00000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 |__TEXT..........| -00000170 20 20 00 00 00 00 00 00 b6 00 00 00 00 00 00 00 | ..............| +00000170 20 20 00 00 00 00 00 00 90 00 00 00 00 00 00 00 | ..............| 00000180 20 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 | ...............| * 000001a0 19 00 00 00 98 00 00 00 5f 5f 44 41 54 41 00 00 |........__DATA..| @@ -48,10 +48,7 @@ 00001070 00 00 00 00 00 00 00 00 5f 72 74 30 5f 67 6f 00 |........_rt0_go.| 00001080 02 20 00 04 20 00 06 05 02 05 02 05 02 05 02 02 |. .. ...........| 00001090 02 02 02 05 02 02 02 01 00 00 00 00 00 00 00 00 |................| -000010a0 02 00 00 00 88 00 00 00 2f 55 73 65 72 73 2f 72 |......../Users/r| -000010b0 73 63 2f 67 2f 67 6f 2f 73 72 63 2f 63 6d 64 2f |sc/g/go/src/cmd/| -000010c0 6c 69 6e 6b 2f 74 65 73 74 64 61 74 61 2f 68 65 |link/testdata/he| -000010d0 6c 6c 6f 2e 73 00 00 00 00 00 00 00 00 00 00 00 |llo.s...........| +000010a0 02 00 00 00 88 00 00 00 68 65 6c 6c 6f 2e 73 00 |........hello.s.| * 00002000 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a |hello world.| 0000200c diff --git a/src/cmd/link/testdata/pclntab.6 b/src/cmd/link/testdata/pclntab.6 Binary files differindex 918411ca5..9e7f9afdb 100644 --- a/src/cmd/link/testdata/pclntab.6 +++ b/src/cmd/link/testdata/pclntab.6 diff --git a/src/cmd/link/testdata/pclntab.s b/src/cmd/link/testdata/pclntab.s index 22c4ee0de..12dac70b0 100644 --- a/src/cmd/link/testdata/pclntab.s +++ b/src/cmd/link/testdata/pclntab.s @@ -1746,6 +1746,6 @@ TEXT start(SB),7,$0 CALL func0(SB) CALL func1(SB) CALL func2(SB) - MOVQ $pclntab(SB), AX + MOVQ $runtime·pclntab(SB), AX RET diff --git a/src/cmd/nm/nm.go b/src/cmd/nm/nm.go index a4036184e..3089e481b 100644 --- a/src/cmd/nm/nm.go +++ b/src/cmd/nm/nm.go @@ -6,13 +6,13 @@ package main import ( "bufio" - "bytes" "flag" "fmt" - "io" "log" "os" "sort" + + "cmd/internal/objfile" ) func usage() { @@ -85,55 +85,22 @@ func errorf(format string, args ...interface{}) { exitCode = 1 } -type Sym struct { - Addr uint64 - Size int64 - Code rune - Name string - Type string -} - -var parsers = []struct { - prefix []byte - parse func(*os.File) []Sym -}{ - {[]byte("!<arch>\n"), goobjSymbols}, - {[]byte("go object "), goobjSymbols}, - {[]byte("\x7FELF"), elfSymbols}, - {[]byte("\xFE\xED\xFA\xCE"), machoSymbols}, - {[]byte("\xFE\xED\xFA\xCF"), machoSymbols}, - {[]byte("\xCE\xFA\xED\xFE"), machoSymbols}, - {[]byte("\xCF\xFA\xED\xFE"), machoSymbols}, - {[]byte("MZ"), peSymbols}, - {[]byte("\x00\x00\x01\xEB"), plan9Symbols}, // 386 - {[]byte("\x00\x00\x04\x07"), plan9Symbols}, // mips - {[]byte("\x00\x00\x06\x47"), plan9Symbols}, // arm - {[]byte("\x00\x00\x8A\x97"), plan9Symbols}, // amd64 -} - func nm(file string) { - f, err := os.Open(file) + f, err := objfile.Open(file) if err != nil { errorf("%v", err) return } defer f.Close() - buf := make([]byte, 16) - io.ReadFull(f, buf) - f.Seek(0, 0) - - var syms []Sym - for _, p := range parsers { - if bytes.HasPrefix(buf, p.prefix) { - syms = p.parse(f) - goto HaveSyms - } + syms, err := f.Symbols() + if err != nil { + errorf("reading %s: %v", file, err) + } + if len(syms) == 0 { + errorf("reading %s: no symbols", file) } - errorf("%v: unknown file format", file) - return -HaveSyms: switch *sortOrder { case "address": sort.Sort(byAddr(syms)) @@ -165,19 +132,19 @@ HaveSyms: w.Flush() } -type byAddr []Sym +type byAddr []objfile.Sym func (x byAddr) Len() int { return len(x) } func (x byAddr) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byAddr) Less(i, j int) bool { return x[i].Addr < x[j].Addr } -type byName []Sym +type byName []objfile.Sym func (x byName) Len() int { return len(x) } func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x byName) Less(i, j int) bool { return x[i].Name < x[j].Name } -type bySize []Sym +type bySize []objfile.Sym func (x bySize) Len() int { return len(x) } func (x bySize) Swap(i, j int) { x[i], x[j] = x[j], x[i] } diff --git a/src/cmd/nm/nm_test.go b/src/cmd/nm/nm_test.go index 74773877f..f447e8e49 100644 --- a/src/cmd/nm/nm_test.go +++ b/src/cmd/nm/nm_test.go @@ -77,7 +77,7 @@ func TestNM(t *testing.T) { "elf/testdata/gcc-amd64-linux-exec", "macho/testdata/gcc-386-darwin-exec", "macho/testdata/gcc-amd64-darwin-exec", - "pe/testdata/gcc-amd64-mingw-exec", + // "pe/testdata/gcc-amd64-mingw-exec", // no symbols! "pe/testdata/gcc-386-mingw-exec", "plan9obj/testdata/amd64-plan9-exec", "plan9obj/testdata/386-plan9-exec", @@ -87,7 +87,7 @@ func TestNM(t *testing.T) { cmd := exec.Command(testnmpath, exepath) out, err := cmd.CombinedOutput() if err != nil { - t.Fatalf("go tool nm %v: %v\n%s", exepath, err, string(out)) + t.Errorf("go tool nm %v: %v\n%s", exepath, err, string(out)) } } diff --git a/src/cmd/nm/pe.go b/src/cmd/nm/pe.go deleted file mode 100644 index 52d05e51d..000000000 --- a/src/cmd/nm/pe.go +++ /dev/null @@ -1,98 +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. - -// Parsing of PE executables (Microsoft Windows). - -package main - -import ( - "debug/pe" - "os" - "sort" -) - -func peSymbols(f *os.File) []Sym { - p, err := pe.NewFile(f) - if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil - } - - // Build sorted list of addresses of all symbols. - // We infer the size of a symbol by looking at where the next symbol begins. - var addrs []uint64 - - var imageBase uint64 - switch oh := p.OptionalHeader.(type) { - case *pe.OptionalHeader32: - imageBase = uint64(oh.ImageBase) - case *pe.OptionalHeader64: - imageBase = oh.ImageBase - default: - errorf("parsing %s: file format not recognized", f.Name()) - return nil - } - - var syms []Sym - for _, s := range p.Symbols { - const ( - N_UNDEF = 0 // An undefined (extern) symbol - N_ABS = -1 // An absolute symbol (e_value is a constant, not an address) - N_DEBUG = -2 // A debugging symbol - ) - sym := Sym{Name: s.Name, Addr: uint64(s.Value), Code: '?'} - switch s.SectionNumber { - case N_UNDEF: - sym.Code = 'U' - case N_ABS: - sym.Code = 'C' - case N_DEBUG: - sym.Code = '?' - default: - if s.SectionNumber < 0 { - errorf("parsing %s: invalid section number %d", f.Name(), s.SectionNumber) - return nil - } - if len(p.Sections) < int(s.SectionNumber) { - errorf("parsing %s: section number %d is large then max %d", f.Name(), s.SectionNumber, len(p.Sections)) - return nil - } - sect := p.Sections[s.SectionNumber-1] - const ( - text = 0x20 - data = 0x40 - bss = 0x80 - permX = 0x20000000 - permR = 0x40000000 - permW = 0x80000000 - ) - ch := sect.Characteristics - switch { - case ch&text != 0: - sym.Code = 'T' - case ch&data != 0: - if ch&permW == 0 { - sym.Code = 'R' - } else { - sym.Code = 'D' - } - case ch&bss != 0: - sym.Code = 'B' - } - sym.Addr += imageBase + uint64(sect.VirtualAddress) - } - syms = append(syms, sym) - addrs = append(addrs, sym.Addr) - } - - sort.Sort(uint64s(addrs)) - for i := range syms { - j := sort.Search(len(addrs), func(x int) bool { return addrs[x] > syms[i].Addr }) - if j < len(addrs) { - syms[i].Size = int64(addrs[j] - syms[i].Addr) - } - } - - return syms -} diff --git a/src/cmd/nm/plan9obj.go b/src/cmd/nm/plan9obj.go deleted file mode 100644 index 006c66ebf..000000000 --- a/src/cmd/nm/plan9obj.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 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. - -// Parsing of Plan 9 a.out executables. - -package main - -import ( - "debug/plan9obj" - "os" - "sort" -) - -func plan9Symbols(f *os.File) []Sym { - p, err := plan9obj.NewFile(f) - if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil - } - - plan9Syms, err := p.Symbols() - if err != nil { - errorf("parsing %s: %v", f.Name(), err) - return nil - } - - // Build sorted list of addresses of all symbols. - // We infer the size of a symbol by looking at where the next symbol begins. - var addrs []uint64 - for _, s := range plan9Syms { - addrs = append(addrs, s.Value) - } - sort.Sort(uint64s(addrs)) - - var syms []Sym - - for _, s := range plan9Syms { - sym := Sym{Addr: s.Value, Name: s.Name, Code: rune(s.Type)} - i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) - if i < len(addrs) { - sym.Size = int64(addrs[i] - s.Value) - } - syms = append(syms, sym) - } - - return syms -} diff --git a/src/cmd/objdump/main.go b/src/cmd/objdump/main.go index 9922dcc02..1e4163296 100644 --- a/src/cmd/objdump/main.go +++ b/src/cmd/objdump/main.go @@ -101,7 +101,7 @@ func main() { keep := syms[:0] for _, sym := range syms { switch sym.Name { - case "text", "_text", "etext", "_etext": + case "runtime.text", "text", "_text", "runtime.etext", "etext", "_etext": // drop default: keep = append(keep, sym) @@ -118,7 +118,7 @@ func main() { i := sort.Search(len(syms), func(i int) bool { return syms[i].Addr > addr }) if i > 0 { s := syms[i-1] - if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.Name != "etext" && s.Name != "_etext" { + if s.Addr <= addr && addr < s.Addr+uint64(s.Size) && s.Name != "runtime.etext" && s.Name != "etext" && s.Name != "_etext" { return s.Name, s.Addr } } @@ -355,11 +355,20 @@ func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, textStart = imageBase + uint64(sect.VirtualAddress) textData, _ = sect.Data() } - if pclntab, err = loadPETable(obj, "pclntab", "epclntab"); err != nil { - return 0, nil, nil, nil, err + if pclntab, err = loadPETable(obj, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPETable(obj, "pclntab", "epclntab"); err2 != nil { + return 0, nil, nil, nil, err + } } - if symtab, err = loadPETable(obj, "symtab", "esymtab"); err != nil { - return 0, nil, nil, nil, err + if symtab, err = loadPETable(obj, "runtime.symtab", "runtime.esymtab"); err != nil { + // Same as above. + var err2 error + if symtab, err2 = loadPETable(obj, "symtab", "esymtab"); err2 != nil { + return 0, nil, nil, nil, err + } } return textStart, textData, symtab, pclntab, nil } @@ -369,11 +378,20 @@ func loadTables(f *os.File) (textStart uint64, textData, symtab, pclntab []byte, if sect := obj.Section("text"); sect != nil { textData, _ = sect.Data() } - if pclntab, err = loadPlan9Table(obj, "pclntab", "epclntab"); err != nil { - return 0, nil, nil, nil, err + if pclntab, err = loadPlan9Table(obj, "runtime.pclntab", "runtime.epclntab"); err != nil { + // We didn't find the symbols, so look for the names used in 1.3 and earlier. + // TODO: Remove code looking for the old symbols when we no longer care about 1.3. + var err2 error + if pclntab, err2 = loadPlan9Table(obj, "pclntab", "epclntab"); err2 != nil { + return 0, nil, nil, nil, err + } } - if symtab, err = loadPlan9Table(obj, "symtab", "esymtab"); err != nil { - return 0, nil, nil, nil, err + if symtab, err = loadPlan9Table(obj, "runtime.symtab", "runtime.esymtab"); err != nil { + // Same as above. + var err2 error + if symtab, err2 = loadPlan9Table(obj, "symtab", "esymtab"); err2 != nil { + return 0, nil, nil, nil, err + } } return textStart, textData, symtab, pclntab, nil } diff --git a/src/cmd/yacc/Makefile b/src/cmd/yacc/Makefile deleted file mode 100644 index f8c8169bd..000000000 --- a/src/cmd/yacc/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright 2009 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. - -TARG=expr$(shell go env GOEXE) - -$(TARG): yacc.go expr.y - go run yacc.go -p expr expr.y - go build -o $(TARG) y.go - -clean: - rm -f y.go y.output $(TARG) diff --git a/src/cmd/yacc/doc.go b/src/cmd/yacc/doc.go index ceaaf2448..702c9f0d2 100644 --- a/src/cmd/yacc/doc.go +++ b/src/cmd/yacc/doc.go @@ -20,8 +20,9 @@ written in C and documented at Adepts of the original yacc will have no trouble adapting to this form of the tool. -The file expr.y in this directory is a yacc grammar for a very simple -expression parser. It needs the flag "-p expr" (see below). +The directory $GOROOT/cmd/yacc/testdata/expr is a yacc program +for a very simple expression parser. See expr.y and main.go in that +directory for examples of how to write and build yacc programs. The generated parser is reentrant. Parse expects to be given an argument that conforms to the following interface: diff --git a/src/cmd/yacc/testdata/expr/README b/src/cmd/yacc/testdata/expr/README new file mode 100644 index 000000000..302ef57a7 --- /dev/null +++ b/src/cmd/yacc/testdata/expr/README @@ -0,0 +1,20 @@ +This directory contains a simple program demonstrating how to use +the Go version of yacc. + +To build it: + + $ go generate + $ go build + +or + + $ go generate + $ go run expr.go + +The file main.go contains the "go generate" command to run yacc to +create expr.go from expr.y. It also has the package doc comment, +as godoc will not scan the .y file. + +The actual implementation is in expr.y. + +The program is not installed in the binary distributions of Go. diff --git a/src/cmd/yacc/expr.y b/src/cmd/yacc/testdata/expr/expr.y index 77e9259da..09451949f 100644 --- a/src/cmd/yacc/expr.y +++ b/src/cmd/yacc/testdata/expr/expr.y @@ -11,11 +11,6 @@ %{ -// This tag will be copied to the generated file to prevent that file -// confusing a future build. - -// +build ignore - package main import ( diff --git a/src/cmd/yacc/testdata/expr/main.go b/src/cmd/yacc/testdata/expr/main.go new file mode 100644 index 000000000..8d5b6911f --- /dev/null +++ b/src/cmd/yacc/testdata/expr/main.go @@ -0,0 +1,15 @@ +// Copyright 2014 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. + +// This file holds the go generate command to run yacc on the grammar in expr.y. +// To build expr: +// % go generate +// % go build + +//go:generate -command yacc go tool yacc +//go:generate yacc -o expr.go -p "expr" expr.y + +// Expr is a simple expression evaluator that serves as a working example of +// how to use Go's yacc implemenation. +package main |