summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmd/5g/reg.c2
-rw-r--r--src/cmd/6g/reg.c2
-rw-r--r--src/cmd/9g/opt.h10
-rw-r--r--src/cmd/9g/peep.c896
-rw-r--r--src/cmd/9g/prog.c162
-rw-r--r--src/cmd/9g/reg.c2
-rw-r--r--src/cmd/9l/asm.c22
-rw-r--r--src/cmd/go/doc.go28
-rw-r--r--src/cmd/go/generate.go75
-rw-r--r--src/cmd/go/generate_test.go2
-rw-r--r--src/cmd/ld/lib.c24
-rw-r--r--src/cmd/pprof/internal/commands/commands.go26
-rw-r--r--src/cmd/pprof/internal/symbolizer/symbolizer.go4
-rw-r--r--src/go/build/build.go8
-rw-r--r--src/go/build/build_test.go1
-rw-r--r--src/liblink/asm9.c2
-rw-r--r--src/liblink/pass.c4
-rw-r--r--src/reflect/all_test.go101
-rw-r--r--src/reflect/export_test.go19
-rw-r--r--src/reflect/type.go4
-rw-r--r--src/runtime/defs_dragonfly_386.go80
-rw-r--r--src/runtime/proc1.go30
-rw-r--r--src/runtime/signal_dragonfly_386.go34
-rw-r--r--src/runtime/signal_dragonfly_386.h23
-rw-r--r--src/runtime/stack1.go14
25 files changed, 1438 insertions, 137 deletions
diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c
index 611310f12..30fb81601 100644
--- a/src/cmd/5g/reg.c
+++ b/src/cmd/5g/reg.c
@@ -458,7 +458,7 @@ brk:
print("\nregisterizing\n");
for(i=0; i<nregion; i++) {
if(debug['R'] && debug['v'])
- print("region %d: cost %d varno %d enter %d\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
+ print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
bit = blsh(rgp->varno);
vreg = paint2(rgp->enter, rgp->varno, 0);
vreg = allreg(vreg, rgp);
diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c
index 75f9573b2..f3dc59b14 100644
--- a/src/cmd/6g/reg.c
+++ b/src/cmd/6g/reg.c
@@ -393,7 +393,7 @@ brk:
print("\nregisterizing\n");
for(i=0; i<nregion; i++) {
if(debug['R'] && debug['v'])
- print("region %d: cost %d varno %d enter %d\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
+ print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
bit = blsh(rgp->varno);
vreg = paint2(rgp->enter, rgp->varno, 0);
vreg = allreg(vreg, rgp);
diff --git a/src/cmd/9g/opt.h b/src/cmd/9g/opt.h
index 7f15b5a69..6a07b268f 100644
--- a/src/cmd/9g/opt.h
+++ b/src/cmd/9g/opt.h
@@ -225,6 +225,16 @@ enum
void proginfo(ProgInfo*, Prog*);
+// Many Power ISA arithmetic and logical instructions come in four
+// standard variants. These bits let us map between variants.
+enum {
+ V_CC = 1<<0, // xCC (affect CR field 0 flags)
+ V_V = 1<<1, // xV (affect SO and OV flags)
+};
+
+int as2variant(int);
+int variant2as(int, int);
+
// To allow use of AJMP, ACALL, ARET in ../gc/popt.c.
enum
{
diff --git a/src/cmd/9g/peep.c b/src/cmd/9g/peep.c
index ec314d633..1f430220e 100644
--- a/src/cmd/9g/peep.c
+++ b/src/cmd/9g/peep.c
@@ -33,12 +33,295 @@
#include "gg.h"
#include "opt.h"
+static int regzer(Addr *a);
+static int subprop(Flow*);
+static int copyprop(Flow*);
+static int copy1(Addr*, Addr*, Flow*, int);
+static int copyas(Addr*, Addr*);
+static int copyau(Addr*, Addr*);
+static int copysub(Addr*, Addr*, Addr*, int);
+static int copysub1(Prog*, Addr*, Addr*, int);
+static int copyau1(Prog *p, Addr *v);
+
+static uint32 gactive;
+
void
-peep(Prog *p)
+peep(Prog *firstp)
{
- USED(p);
- // TODO(minux)
- return;
+ Graph *g;
+ Flow *r, *r1;
+ Prog *p, *p1;
+ int t;
+
+ g = flowstart(firstp, sizeof(Flow));
+ if(g == nil)
+ return;
+ gactive = 0;
+
+loop1:
+ if(debug['P'] && debug['v'])
+ dumpit("loop1", g->start, 0);
+
+ t = 0;
+ for(r=g->start; r!=nil; r=r->link) {
+ p = r->prog;
+ // TODO(austin) Handle smaller moves. arm and amd64
+ // distinguish between moves that moves that *must*
+ // sign/zero extend and moves that don't care so they
+ // can eliminate moves that don't care without
+ // breaking moves that do care. This might let us
+ // simplify or remove the next peep loop, too.
+ if(p->as == AMOVD || p->as == AFMOVD)
+ if(regtyp(&p->to)) {
+ // Try to eliminate reg->reg moves
+ if(regtyp(&p->from))
+ if(p->from.type == p->to.type) {
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ // Convert uses to $0 to uses of R0 and
+ // propagate R0
+ if(regzer(&p->from))
+ if(p->to.type == D_REG) {
+ p->from.type = D_REG;
+ p->from.reg = REGZERO;
+ if(copyprop(r)) {
+ excise(r);
+ t++;
+ } else
+ if(subprop(r) && copyprop(r)) {
+ excise(r);
+ t++;
+ }
+ }
+ }
+ }
+ if(t)
+ goto loop1;
+
+ /*
+ * look for MOVB x,R; MOVB R,R (for small MOVs not handled above)
+ */
+ for(r=g->start; r!=nil; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ default:
+ continue;
+ case AMOVH:
+ case AMOVHZ:
+ case AMOVB:
+ case AMOVBZ:
+ case AMOVW:
+ case AMOVWZ:
+ if(p->to.type != D_REG)
+ continue;
+ break;
+ }
+ r1 = r->link;
+ if(r1 == nil)
+ continue;
+ p1 = r1->prog;
+ if(p1->as != p->as)
+ continue;
+ if(p1->from.type != D_REG || p1->from.reg != p->to.reg)
+ continue;
+ if(p1->to.type != D_REG || p1->to.reg != p->to.reg)
+ continue;
+ excise(r1);
+ }
+
+ if(debug['D'] > 1)
+ goto ret; /* allow following code improvement to be suppressed */
+
+ /*
+ * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R
+ * when OP can set condition codes correctly
+ */
+ for(r=g->start; r!=nil; r=r->link) {
+ p = r->prog;
+ switch(p->as) {
+ case ACMP:
+ case ACMPW: /* always safe? */
+ if(!regzer(&p->to))
+ continue;
+ r1 = r->s1;
+ if(r1 == nil)
+ continue;
+ switch(r1->prog->as) {
+ default:
+ continue;
+ case ABCL:
+ case ABC:
+ /* the conditions can be complex and these are currently little used */
+ continue;
+ case ABEQ:
+ case ABGE:
+ case ABGT:
+ case ABLE:
+ case ABLT:
+ case ABNE:
+ case ABVC:
+ case ABVS:
+ break;
+ }
+ r1 = r;
+ do
+ r1 = uniqp(r1);
+ while (r1 != nil && r1->prog->as == ANOP);
+ if(r1 == nil)
+ continue;
+ p1 = r1->prog;
+ if(p1->to.type != D_REG || p1->to.reg != p->from.reg)
+ continue;
+ switch(p1->as) {
+ case ASUB:
+ case AADD:
+ case AXOR:
+ case AOR:
+ /* irregular instructions */
+ if(p1->from.type == D_CONST)
+ continue;
+ break;
+ }
+ switch(p1->as) {
+ default:
+ continue;
+ case AMOVW:
+ case AMOVD:
+ if(p1->from.type != D_REG)
+ continue;
+ continue;
+ case AANDCC:
+ case AANDNCC:
+ case AORCC:
+ case AORNCC:
+ case AXORCC:
+ case ASUBCC:
+ case ASUBECC:
+ case ASUBMECC:
+ case ASUBZECC:
+ case AADDCC:
+ case AADDCCC:
+ case AADDECC:
+ case AADDMECC:
+ case AADDZECC:
+ case ARLWMICC:
+ case ARLWNMCC:
+ /* don't deal with floating point instructions for now */
+/*
+ case AFABS:
+ case AFADD:
+ case AFADDS:
+ case AFCTIW:
+ case AFCTIWZ:
+ case AFDIV:
+ case AFDIVS:
+ case AFMADD:
+ case AFMADDS:
+ case AFMOVD:
+ case AFMSUB:
+ case AFMSUBS:
+ case AFMUL:
+ case AFMULS:
+ case AFNABS:
+ case AFNEG:
+ case AFNMADD:
+ case AFNMADDS:
+ case AFNMSUB:
+ case AFNMSUBS:
+ case AFRSP:
+ case AFSUB:
+ case AFSUBS:
+ case ACNTLZW:
+ case AMTFSB0:
+ case AMTFSB1:
+*/
+ case AADD:
+ case AADDV:
+ case AADDC:
+ case AADDCV:
+ case AADDME:
+ case AADDMEV:
+ case AADDE:
+ case AADDEV:
+ case AADDZE:
+ case AADDZEV:
+ case AAND:
+ case AANDN:
+ case ADIVW:
+ case ADIVWV:
+ case ADIVWU:
+ case ADIVWUV:
+ case ADIVD:
+ case ADIVDV:
+ case ADIVDU:
+ case ADIVDUV:
+ case AEQV:
+ case AEXTSB:
+ case AEXTSH:
+ case AEXTSW:
+ case AMULHW:
+ case AMULHWU:
+ case AMULLW:
+ case AMULLWV:
+ case AMULHD:
+ case AMULHDU:
+ case AMULLD:
+ case AMULLDV:
+ case ANAND:
+ case ANEG:
+ case ANEGV:
+ case ANOR:
+ case AOR:
+ case AORN:
+ case AREM:
+ case AREMV:
+ case AREMU:
+ case AREMUV:
+ case AREMD:
+ case AREMDV:
+ case AREMDU:
+ case AREMDUV:
+ case ARLWMI:
+ case ARLWNM:
+ case ASLW:
+ case ASRAW:
+ case ASRW:
+ case ASLD:
+ case ASRAD:
+ case ASRD:
+ case ASUB:
+ case ASUBV:
+ case ASUBC:
+ case ASUBCV:
+ case ASUBME:
+ case ASUBMEV:
+ case ASUBE:
+ case ASUBEV:
+ case ASUBZE:
+ case ASUBZEV:
+ case AXOR:
+ t = variant2as(p1->as, as2variant(p1->as) | V_CC);
+ break;
+ }
+ if(debug['D'])
+ print("cmp %P; %P -> ", p1, p);
+ p1->as = t;
+ if(debug['D'])
+ print("%P\n", p1);
+ excise(r);
+ continue;
+ }
+ }
+
+ret:
+ flowend(g);
}
void
@@ -56,6 +339,22 @@ excise(Flow *r)
ostats.ndelmov++;
}
+/*
+ * regzer returns 1 if a's value is 0 (a is R0 or $0)
+ */
+static int
+regzer(Addr *a)
+{
+ if(a->type == D_CONST)
+ if(a->sym == nil && a->reg == NREG)
+ if(a->offset == 0)
+ return 1;
+ if(a->type == D_REG)
+ if(a->reg == REGZERO)
+ return 1;
+ return 0;
+}
+
int
regtyp(Adr *a)
{
@@ -63,11 +362,600 @@ regtyp(Adr *a)
default:
return 0;
case D_REG:
+ if(a->reg == REGZERO)
+ return 0;
case D_FREG:
return 1;
}
}
+/*
+ * the idea is to substitute
+ * one register for another
+ * from one MOV to another
+ * MOV a, R1
+ * ADD b, R1 / no use of R2
+ * MOV R1, R2
+ * would be converted to
+ * MOV a, R2
+ * ADD b, R2
+ * MOV R2, R1
+ * hopefully, then the former or latter MOV
+ * will be eliminated by copy propagation.
+ *
+ * r0 (the argument, not the register) is the MOV at the end of the
+ * above sequences. This returns 1 if it modified any instructions.
+ */
+static int
+subprop(Flow *r0)
+{
+ Prog *p;
+ Addr *v1, *v2;
+ Flow *r;
+ int t;
+ ProgInfo info;
+
+ p = r0->prog;
+ v1 = &p->from;
+ if(!regtyp(v1))
+ return 0;
+ v2 = &p->to;
+ if(!regtyp(v2))
+ return 0;
+ for(r=uniqp(r0); r!=nil; r=uniqp(r)) {
+ if(uniqs(r) == nil)
+ break;
+ p = r->prog;
+ if(p->as == AVARDEF || p->as == AVARKILL)
+ continue;
+ proginfo(&info, p);
+ if(info.flags & Call)
+ return 0;
+
+ if((info.flags & (RightRead|RightWrite)) == RightWrite) {
+ if(p->to.type == v1->type)
+ if(p->to.reg == v1->reg)
+ goto gotit;
+ }
+
+ if(copyau(&p->from, v2) ||
+ copyau1(p, v2) ||
+ copyau(&p->to, v2))
+ break;
+ if(copysub(&p->from, v1, v2, 0) ||
+ copysub1(p, v1, v2, 0) ||
+ copysub(&p->to, v1, v2, 0))
+ break;
+ }
+ return 0;
+
+gotit:
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P']) {
+ print("gotit: %D->%D\n%P", v1, v2, r->prog);
+ if(p->from.type == v2->type)
+ print(" excise");
+ print("\n");
+ }
+ for(r=uniqs(r); r!=r0; r=uniqs(r)) {
+ p = r->prog;
+ copysub(&p->from, v1, v2, 1);
+ copysub1(p, v1, v2, 1);
+ copysub(&p->to, v1, v2, 1);
+ if(debug['P'])
+ print("%P\n", r->prog);
+ }
+ t = v1->reg;
+ v1->reg = v2->reg;
+ v2->reg = t;
+ if(debug['P'])
+ print("%P last\n", r->prog);
+ return 1;
+}
+
+/*
+ * The idea is to remove redundant copies.
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * use v2 return fail (v1->v2 move must remain)
+ * -----------------
+ * v1->v2 F=0
+ * (use v2 s/v2/v1/)*
+ * set v1 F=1
+ * set v2 return success (caller can remove v1->v2 move)
+ */
+static int
+copyprop(Flow *r0)
+{
+ Prog *p;
+ Addr *v1, *v2;
+
+ p = r0->prog;
+ v1 = &p->from;
+ v2 = &p->to;
+ if(copyas(v1, v2)) {
+ if(debug['P'])
+ print("eliminating self-move\n", r0->prog);
+ return 1;
+ }
+ gactive++;
+ if(debug['P'])
+ print("trying to eliminate %D->%D move from:\n%P\n", v1, v2, r0->prog);
+ return copy1(v1, v2, r0->s1, 0);
+}
+
+// copy1 replaces uses of v2 with v1 starting at r and returns 1 if
+// all uses were rewritten.
+static int
+copy1(Addr *v1, Addr *v2, Flow *r, int f)
+{
+ int t;
+ Prog *p;
+
+ if(r->active == gactive) {
+ if(debug['P'])
+ print("act set; return 1\n");
+ return 1;
+ }
+ r->active = gactive;
+ if(debug['P'])
+ print("copy1 replace %D with %D f=%d\n", v2, v1, f);
+ for(; r != nil; r = r->s1) {
+ p = r->prog;
+ if(debug['P'])
+ print("%P", p);
+ if(!f && uniqp(r) == nil) {
+ // Multiple predecessors; conservatively
+ // assume v1 was set on other path
+ f = 1;
+ if(debug['P'])
+ print("; merge; f=%d", f);
+ }
+ t = copyu(p, v2, nil);
+ switch(t) {
+ case 2: /* rar, can't split */
+ if(debug['P'])
+ print("; %D rar; return 0\n", v2);
+ return 0;
+
+ case 3: /* set */
+ if(debug['P'])
+ print("; %D set; return 1\n", v2);
+ return 1;
+
+ case 1: /* used, substitute */
+ case 4: /* use and set */
+ if(f) {
+ if(!debug['P'])
+ return 0;
+ if(t == 4)
+ print("; %D used+set and f=%d; return 0\n", v2, f);
+ else
+ print("; %D used and f=%d; return 0\n", v2, f);
+ return 0;
+ }
+ if(copyu(p, v2, v1)) {
+ if(debug['P'])
+ print("; sub fail; return 0\n");
+ return 0;
+ }
+ if(debug['P'])
+ print("; sub %D->%D\n => %P", v2, v1, p);
+ if(t == 4) {
+ if(debug['P'])
+ print("; %D used+set; return 1\n", v2);
+ return 1;
+ }
+ break;
+ }
+ if(!f) {
+ t = copyu(p, v1, nil);
+ if(!f && (t == 2 || t == 3 || t == 4)) {
+ f = 1;
+ if(debug['P'])
+ print("; %D set and !f; f=%d", v1, f);
+ }
+ }
+ if(debug['P'])
+ print("\n");
+ if(r->s2)
+ if(!copy1(v1, v2, r->s2, f))
+ return 0;
+ }
+ return 1;
+}
+
+// If s==nil, copyu returns the set/use of v in p; otherwise, it
+// modifies p to replace reads of v with reads of s and returns 0 for
+// success or non-zero for failure.
+//
+// If s==nil, copy returns one of the following values:
+// 1 if v only used
+// 2 if v is set and used in one address (read-alter-rewrite;
+// can't substitute)
+// 3 if v is only set
+// 4 if v is set in one address and used in another (so addresses
+// can be rewritten independently)
+// 0 otherwise (not touched)
+int
+copyu(Prog *p, Addr *v, Addr *s)
+{
+ if(p->from3.type != D_NONE)
+ // 9g never generates a from3
+ print("copyu: from3 (%D) not implemented\n", p->from3);
+
+ switch(p->as) {
+
+ default:
+ print("copyu: can't find %A\n", p->as);
+ return 2;
+
+ case ANOP: /* read p->from, write p->to */
+ case AMOVH:
+ case AMOVHZ:
+ case AMOVB:
+ case AMOVBZ:
+ case AMOVW:
+ case AMOVWZ:
+ case AMOVD:
+
+ case ANEG:
+ case ANEGCC:
+ case AADDME:
+ case AADDMECC:
+ case AADDZE:
+ case AADDZECC:
+ case ASUBME:
+ case ASUBMECC:
+ case ASUBZE:
+ case ASUBZECC:
+
+ case AFCTIW:
+ case AFCTIWZ:
+ case AFCTID:
+ case AFCTIDZ:
+ case AFCFID:
+ case AFCFIDCC:
+ case AFMOVS:
+ case AFMOVD:
+ case AFRSP:
+ case AFNEG:
+ case AFNEGCC:
+ if(s != nil) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ // Update only indirect uses of v in p->to
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ // Fix up implicit from
+ if(p->from.type == D_NONE)
+ p->from = p->to;
+ if(copyau(&p->from, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ // p->to only indirectly uses v
+ return 1;
+ return 0;
+
+ case AMOVBU: /* rar p->from, write p->to or read p->from, rar p->to */
+ case AMOVBZU:
+ case AMOVHU:
+ case AMOVHZU:
+ case AMOVWZU:
+ case AMOVDU:
+ if(p->from.type == D_OREG) {
+ if(copyas(&p->from, v))
+ // No s!=nil check; need to fail
+ // anyway in that case
+ return 2;
+ if(s != nil) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v))
+ return 3;
+ } else if (p->to.type == D_OREG) {
+ if(copyas(&p->to, v))
+ return 2;
+ if(s != nil) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ } else {
+ print("copyu: bad %P\n", p);
+ }
+ return 0;
+
+ case ARLWMI: /* read p->from, read p->reg, rar p->to */
+ case ARLWMICC:
+ if(copyas(&p->to, v))
+ return 2;
+ /* fall through */
+
+ case AADD: /* read p->from, read p->reg, write p->to */
+ case AADDC:
+ case AADDE:
+ case ASUB:
+ case ASLW:
+ case ASRW:
+ case ASRAW:
+ case ASLD:
+ case ASRD:
+ case ASRAD:
+ case AOR:
+ case AORCC:
+ case AORN:
+ case AORNCC:
+ case AAND:
+ case AANDCC:
+ case AANDN:
+ case AANDNCC:
+ case ANAND:
+ case ANANDCC:
+ case ANOR:
+ case ANORCC:
+ case AXOR:
+ case AMULHW:
+ case AMULHWU:
+ case AMULLW:
+ case AMULLD:
+ case ADIVW:
+ case ADIVD:
+ case ADIVWU:
+ case ADIVDU:
+ case AREM:
+ case AREMU:
+ case AREMD:
+ case AREMDU:
+ case ARLWNM:
+ case ARLWNMCC:
+
+ case AFADDS:
+ case AFADD:
+ case AFSUBS:
+ case AFSUB:
+ case AFMULS:
+ case AFMUL:
+ case AFDIVS:
+ case AFDIV:
+ if(s != nil) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ if(copysub1(p, v, s, 1))
+ return 1;
+ // Update only indirect uses of v in p->to
+ if(!copyas(&p->to, v))
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyas(&p->to, v)) {
+ if(p->reg == NREG)
+ // Fix up implicit reg (e.g., ADD
+ // R3,R4 -> ADD R3,R4,R4) so we can
+ // update reg and to separately.
+ p->reg = p->to.reg;
+ if(copyau(&p->from, v))
+ return 4;
+ if(copyau1(p, v))
+ return 4;
+ return 3;
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau1(p, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ABEQ:
+ case ABGT:
+ case ABGE:
+ case ABLT:
+ case ABLE:
+ case ABNE:
+ case ABVC:
+ case ABVS:
+ return 0;
+
+ case ACHECKNIL: /* read p->from */
+ case ACMP: /* read p->from, read p->to */
+ case ACMPU:
+ case ACMPW:
+ case ACMPWU:
+ case AFCMPO:
+ case AFCMPU:
+ if(s != nil) {
+ if(copysub(&p->from, v, s, 1))
+ return 1;
+ return copysub(&p->to, v, s, 1);
+ }
+ if(copyau(&p->from, v))
+ return 1;
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ABR: /* read p->to */
+ // 9g never generates a branch to a GPR (this isn't
+ // even a normal instruction; liblink turns it in to a
+ // mov and a branch).
+ if(s != nil) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 1;
+ return 0;
+
+ case ARETURN: /* funny */
+ if(s != nil)
+ return 0;
+ // All registers die at this point, so claim
+ // everything is set (and not used).
+ return 3;
+
+ case ABL: /* funny */
+ if(v->type == D_REG) {
+ if(v->reg <= REGEXT && v->reg > exregoffset)
+ return 2;
+ if(v->reg == REGARG)
+ return 2;
+ }
+ if(v->type == D_FREG) {
+ if(v->reg <= FREGEXT && v->reg > exfregoffset)
+ return 2;
+ }
+ if(p->from.type == D_REG && v->type == D_REG && p->from.reg == v->reg)
+ return 2;
+
+ if(s != nil) {
+ if(copysub(&p->to, v, s, 1))
+ return 1;
+ return 0;
+ }
+ if(copyau(&p->to, v))
+ return 4;
+ return 3;
+
+ case ADUFFZERO:
+ // R0 is zero, used by DUFFZERO, cannot be substituted.
+ // R3 is ptr to memory, used and set, cannot be substituted.
+ if(v->type == D_REG) {
+ if(v->reg == 0)
+ return 1;
+ if(v->reg == 3)
+ return 2;
+ }
+ return 0;
+
+ case ADUFFCOPY:
+ // R3, R4 are ptr to src, dst, used and set, cannot be substituted.
+ // R5 is scratch, set by DUFFCOPY, cannot be substituted.
+ if(v->type == D_REG) {
+ if(v->reg == 3 || v->reg == 4)
+ return 2;
+ if(v->reg == 5)
+ return 3;
+ }
+ return 0;
+
+ case ATEXT: /* funny */
+ if(v->type == D_REG)
+ if(v->reg == REGARG)
+ return 3;
+ return 0;
+
+ case APCDATA:
+ case AFUNCDATA:
+ case AVARDEF:
+ case AVARKILL:
+ return 0;
+ }
+}
+
+int
+a2type(Prog *p)
+{
+ ProgInfo info;
+ proginfo(&info, p);
+ if(info.flags & (SizeB|SizeW|SizeL|SizeQ))
+ return D_REG;
+ if(info.flags & (SizeF|SizeD))
+ return D_FREG;
+ return D_NONE;
+}
+
+// copyas returns 1 if a and v address the same register.
+//
+// If a is the from operand, this means this operation reads the
+// register in v. If a is the to operand, this means this operation
+// writes the register in v.
+static int
+copyas(Addr *a, Addr *v)
+{
+ if(regtyp(v))
+ if(a->type == v->type)
+ if(a->reg == v->reg)
+ return 1;
+ return 0;
+}
+
+// copyau returns 1 if a either directly or indirectly addresses the
+// same register as v.
+//
+// If a is the from operand, this means this operation reads the
+// register in v. If a is the to operand, this means the operation
+// either reads or writes the register in v (if !copyas(a, v), then
+// the operation reads the register in v).
+static int
+copyau(Addr *a, Addr *v)
+{
+ if(copyas(a, v))
+ return 1;
+ if(v->type == D_REG)
+ if(a->type == D_OREG || (a->type == D_CONST && a->reg != NREG))
+ if(v->reg == a->reg)
+ return 1;
+ return 0;
+}
+
+// copyau1 returns 1 if p->reg references the same register as v and v
+// is a direct reference.
+static int
+copyau1(Prog *p, Addr *v)
+{
+ if(regtyp(v))
+ if(p->from.type == v->type || p->to.type == v->type)
+ if(p->reg == v->reg) {
+ // Whether p->reg is a GPR or an FPR is
+ // implied by the instruction (both are
+ // numbered from 0). But the type should
+ // match v->type. Sanity check this.
+ if(a2type(p) != v->type)
+ print("botch a2type %P\n", p);
+ return 1;
+ }
+ return 0;
+}
+
+// copysub replaces v with s in a if f!=0 or indicates it if could if f==0.
+// Returns 1 on failure to substitute (it always succeeds on power64).
+static int
+copysub(Addr *a, Addr *v, Addr *s, int f)
+{
+ if(f)
+ if(copyau(a, v))
+ a->reg = s->reg;
+ return 0;
+}
+
+// copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0.
+// Returns 1 on failure to substitute (it always succeeds on power64).
+static int
+copysub1(Prog *p1, Addr *v, Addr *s, int f)
+{
+ if(f)
+ if(copyau1(p1, v))
+ p1->reg = s->reg;
+ return 0;
+}
+
int
sameaddr(Addr *a, Addr *v)
{
diff --git a/src/cmd/9g/prog.c b/src/cmd/9g/prog.c
index 51c132d18..7c0f0c795 100644
--- a/src/cmd/9g/prog.c
+++ b/src/cmd/9g/prog.c
@@ -101,9 +101,36 @@ static ProgInfo progtable[ALAST] = {
[ADUFFCOPY]= {Call},
};
+static void
+initproginfo(void)
+{
+ static int initialized;
+ int addvariant[] = {V_CC, V_V, V_CC|V_V};
+ int as, as2, i, variant;
+
+ if(initialized)
+ return;
+ initialized = 1;
+
+ // Perform one-time expansion of instructions in progtable to
+ // their CC, V, and VCC variants
+ for(as=0; as<nelem(progtable); as++) {
+ if(progtable[as].flags == 0)
+ continue;
+ variant = as2variant(as);
+ for(i=0; i<nelem(addvariant); i++) {
+ as2 = variant2as(as, variant | addvariant[i]);
+ if(as2 != 0 && progtable[as2].flags == 0)
+ progtable[as2] = progtable[as];
+ }
+ }
+}
+
void
proginfo(ProgInfo *info, Prog *p)
{
+ initproginfo();
+
*info = progtable[p->as];
if(info->flags == 0) {
*info = progtable[AADD];
@@ -143,3 +170,138 @@ proginfo(ProgInfo *info, Prog *p)
info->regset |= RtoB(3) | RtoB(4);
}
}
+
+// Instruction variants table. Initially this contains entries only
+// for the "base" form of each instruction. On the first call to
+// as2variant or variant2as, we'll add the variants to the table.
+static int varianttable[ALAST][4] = {
+ [AADD]= {AADD, AADDCC, AADDV, AADDVCC},
+ [AADDC]= {AADDC, AADDCCC, AADDCV, AADDCVCC},
+ [AADDE]= {AADDE, AADDECC, AADDEV, AADDEVCC},
+ [AADDME]= {AADDME, AADDMECC, AADDMEV, AADDMEVCC},
+ [AADDZE]= {AADDZE, AADDZECC, AADDZEV, AADDZEVCC},
+ [AAND]= {AAND, AANDCC, 0, 0},
+ [AANDN]= {AANDN, AANDNCC, 0, 0},
+ [ACNTLZD]= {ACNTLZD, ACNTLZDCC, 0, 0},
+ [ACNTLZW]= {ACNTLZW, ACNTLZWCC, 0, 0},
+ [ADIVD]= {ADIVD, ADIVDCC, ADIVDV, ADIVDVCC},
+ [ADIVDU]= {ADIVDU, ADIVDUCC, ADIVDUV, ADIVDUVCC},
+ [ADIVW]= {ADIVW, ADIVWCC, ADIVWV, ADIVWVCC},
+ [ADIVWU]= {ADIVWU, ADIVWUCC, ADIVWUV, ADIVWUVCC},
+ [AEQV]= {AEQV, AEQVCC, 0, 0},
+ [AEXTSB]= {AEXTSB, AEXTSBCC, 0, 0},
+ [AEXTSH]= {AEXTSH, AEXTSHCC, 0, 0},
+ [AEXTSW]= {AEXTSW, AEXTSWCC, 0, 0},
+ [AFABS]= {AFABS, AFABSCC, 0, 0},
+ [AFADD]= {AFADD, AFADDCC, 0, 0},
+ [AFADDS]= {AFADDS, AFADDSCC, 0, 0},
+ [AFCFID]= {AFCFID, AFCFIDCC, 0, 0},
+ [AFCTID]= {AFCTID, AFCTIDCC, 0, 0},
+ [AFCTIDZ]= {AFCTIDZ, AFCTIDZCC, 0, 0},
+ [AFCTIW]= {AFCTIW, AFCTIWCC, 0, 0},
+ [AFCTIWZ]= {AFCTIWZ, AFCTIWZCC, 0, 0},
+ [AFDIV]= {AFDIV, AFDIVCC, 0, 0},
+ [AFDIVS]= {AFDIVS, AFDIVSCC, 0, 0},
+ [AFMADD]= {AFMADD, AFMADDCC, 0, 0},
+ [AFMADDS]= {AFMADDS, AFMADDSCC, 0, 0},
+ [AFMOVD]= {AFMOVD, AFMOVDCC, 0, 0},
+ [AFMSUB]= {AFMSUB, AFMSUBCC, 0, 0},
+ [AFMSUBS]= {AFMSUBS, AFMSUBSCC, 0, 0},
+ [AFMUL]= {AFMUL, AFMULCC, 0, 0},
+ [AFMULS]= {AFMULS, AFMULSCC, 0, 0},
+ [AFNABS]= {AFNABS, AFNABSCC, 0, 0},
+ [AFNEG]= {AFNEG, AFNEGCC, 0, 0},
+ [AFNMADD]= {AFNMADD, AFNMADDCC, 0, 0},
+ [AFNMADDS]= {AFNMADDS, AFNMADDSCC, 0, 0},
+ [AFNMSUB]= {AFNMSUB, AFNMSUBCC, 0, 0},
+ [AFNMSUBS]= {AFNMSUBS, AFNMSUBSCC, 0, 0},
+ [AFRES]= {AFRES, AFRESCC, 0, 0},
+ [AFRSP]= {AFRSP, AFRSPCC, 0, 0},
+ [AFRSQRTE]= {AFRSQRTE, AFRSQRTECC, 0, 0},
+ [AFSEL]= {AFSEL, AFSELCC, 0, 0},
+ [AFSQRT]= {AFSQRT, AFSQRTCC, 0, 0},
+ [AFSQRTS]= {AFSQRTS, AFSQRTSCC, 0, 0},
+ [AFSUB]= {AFSUB, AFSUBCC, 0, 0},
+ [AFSUBS]= {AFSUBS, AFSUBSCC, 0, 0},
+ [AMTFSB0]= {AMTFSB0, AMTFSB0CC, 0, 0},
+ [AMTFSB1]= {AMTFSB1, AMTFSB1CC, 0, 0},
+ [AMULHD]= {AMULHD, AMULHDCC, 0, 0},
+ [AMULHDU]= {AMULHDU, AMULHDUCC, 0, 0},
+ [AMULHW]= {AMULHW, AMULHWCC, 0, 0},
+ [AMULHWU]= {AMULHWU, AMULHWUCC, 0, 0},
+ [AMULLD]= {AMULLD, AMULLDCC, AMULLDV, AMULLDVCC},
+ [AMULLW]= {AMULLW, AMULLWCC, AMULLWV, AMULLWVCC},
+ [ANAND]= {ANAND, ANANDCC, 0, 0},
+ [ANEG]= {ANEG, ANEGCC, ANEGV, ANEGVCC},
+ [ANOR]= {ANOR, ANORCC, 0, 0},
+ [AOR]= {AOR, AORCC, 0, 0},
+ [AORN]= {AORN, AORNCC, 0, 0},
+ [AREM]= {AREM, AREMCC, AREMV, AREMVCC},
+ [AREMD]= {AREMD, AREMDCC, AREMDV, AREMDVCC},
+ [AREMDU]= {AREMDU, AREMDUCC, AREMDUV, AREMDUVCC},
+ [AREMU]= {AREMU, AREMUCC, AREMUV, AREMUVCC},
+ [ARLDC]= {ARLDC, ARLDCCC, 0, 0},
+ [ARLDCL]= {ARLDCL, ARLDCLCC, 0, 0},
+ [ARLDCR]= {ARLDCR, ARLDCRCC, 0, 0},
+ [ARLDMI]= {ARLDMI, ARLDMICC, 0, 0},
+ [ARLWMI]= {ARLWMI, ARLWMICC, 0, 0},
+ [ARLWNM]= {ARLWNM, ARLWNMCC, 0, 0},
+ [ASLD]= {ASLD, ASLDCC, 0, 0},
+ [ASLW]= {ASLW, ASLWCC, 0, 0},
+ [ASRAD]= {ASRAD, ASRADCC, 0, 0},
+ [ASRAW]= {ASRAW, ASRAWCC, 0, 0},
+ [ASRD]= {ASRD, ASRDCC, 0, 0},
+ [ASRW]= {ASRW, ASRWCC, 0, 0},
+ [ASUB]= {ASUB, ASUBCC, ASUBV, ASUBVCC},
+ [ASUBC]= {ASUBC, ASUBCCC, ASUBCV, ASUBCVCC},
+ [ASUBE]= {ASUBE, ASUBECC, ASUBEV, ASUBEVCC},
+ [ASUBME]= {ASUBME, ASUBMECC, ASUBMEV, ASUBMEVCC},
+ [ASUBZE]= {ASUBZE, ASUBZECC, ASUBZEV, ASUBZEVCC},
+ [AXOR]= {AXOR, AXORCC, 0, 0},
+};
+
+static void
+initvariants(void)
+{
+ static int initialized;
+ int i, j;
+
+ if(initialized)
+ return;
+ initialized = 1;
+
+ for(i=0; i<nelem(varianttable); i++) {
+ if(varianttable[i][0] == 0) {
+ // Instruction has no variants
+ varianttable[i][0] = i;
+ continue;
+ }
+ // Copy base form to other variants
+ if(varianttable[i][0] == i) {
+ for(j=0; j<nelem(varianttable[i]); j++)
+ memmove(&varianttable[varianttable[i][j]], &varianttable[i], sizeof(varianttable[i]));
+ }
+ }
+}
+
+// as2variant returns the variant (V_*) flags of instruction as.
+int
+as2variant(int as)
+{
+ int i;
+ initvariants();
+ for(i=0; i<nelem(varianttable[as]); i++)
+ if(varianttable[as][i] == as)
+ return i;
+ fatal("as2variant: instruction %A is not a variant of itself", as);
+ return 0;
+}
+
+// variant2as returns the instruction as with the given variant (V_*) flags.
+// If no such variant exists, this returns 0.
+int
+variant2as(int as, int flags)
+{
+ initvariants();
+ return varianttable[as][flags];
+}
diff --git a/src/cmd/9g/reg.c b/src/cmd/9g/reg.c
index 2e546a95b..2d8dbc4f1 100644
--- a/src/cmd/9g/reg.c
+++ b/src/cmd/9g/reg.c
@@ -437,7 +437,7 @@ brk:
print("\nregisterizing\n");
for(i=0; i<nregion; i++) {
if(debug['R'] && debug['v'])
- print("region %d: cost %d varno %d enter %d\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
+ print("region %d: cost %d varno %d enter %lld\n", i, rgp->cost, rgp->varno, rgp->enter->f.prog->pc);
bit = blsh(rgp->varno);
usedreg = paint2(rgp->enter, rgp->varno, 0);
vreg = allreg(usedreg, rgp);
diff --git a/src/cmd/9l/asm.c b/src/cmd/9l/asm.c
index b8ca777c3..65a36285d 100644
--- a/src/cmd/9l/asm.c
+++ b/src/cmd/9l/asm.c
@@ -65,14 +65,6 @@ needlib(char *name)
int nelfsym = 1;
-// b is the addresses, a is the I-form branch instruction template, peform
-// addition so that the instruction jumps to address (offset) b.
-static int32
-braddoff(int32 a, int32 b)
-{
- return (((uint32)a) & 0xfc000003U) | (0x03fffffcU & (uint32)((a & 0x3fffffcU) + b));
-}
-
void
adddynrela(LSym *rel, LSym *s, Reloc *r)
{
@@ -160,7 +152,19 @@ archreloc(Reloc *r, LSym *s, vlong *val)
*val = ((vlong)o2 << 32) | o1;
return 0;
case R_CALLPOWER:
- *val = braddoff((uint32)r->add, (int32)(symaddr(r->sym) - (s->value + r->off)));
+ // Bits 6 through 29 = (S + A - P) >> 2
+ if(ctxt->arch->endian == BigEndian)
+ o1 = be32(s->p + r->off);
+ else
+ o1 = le32(s->p + r->off);
+
+ t = symaddr(r->sym) + r->add - (s->value + r->off);
+ if(t & 3)
+ ctxt->diag("relocation for %s is not aligned: %lld", s->name, t);
+ if(t << 6 >> 6 != t)
+ ctxt->diag("relocation for %s is too big: %lld", s->name, t);
+
+ *val = (o1 & 0xfc000003U) | (t & ~0xfc000003U);
return 0;
}
return -1;
diff --git a/src/cmd/go/doc.go b/src/cmd/go/doc.go
index 43a315944..65640fb48 100644
--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -234,17 +234,24 @@ 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 scans the file for directives, which are lines 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.
+(note: no leading spaces and 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.
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive 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 as a single argument to the generator.
@@ -317,7 +324,7 @@ Download and install packages and dependencies
Usage:
- go get [-d] [-fix] [-t] [-u] [build flags] [packages]
+ go get [-d] [-f] [-fix] [-t] [-u] [build flags] [packages]
Get downloads and installs the packages named by the import paths,
along with their dependencies.
@@ -325,6 +332,11 @@ along with their dependencies.
The -d flag instructs get to stop after downloading the packages; that is,
it instructs get not to install the packages.
+The -f flag, valid only when -u is set, forces get -u not to verify that
+each package has been checked out from the source control repository
+implied by its import path. This can be useful if the source is a local fork
+of the original.
+
The -fix flag instructs get to run the fix tool on the downloaded packages
before resolving dependencies or building the code.
diff --git a/src/cmd/go/generate.go b/src/cmd/go/generate.go
index a83cce8f7..baf4d2b55 100644
--- a/src/cmd/go/generate.go
+++ b/src/cmd/go/generate.go
@@ -32,20 +32,27 @@ 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 scans the file for directives, which are lines 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.
+(note: no leading spaces and 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.
+Note that go generate does not parse the file, so lines that look
+like directives in comments or multiline strings will be treated
+as directives.
+
+The arguments to the directive 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.
+quoted string appears as a single argument to the generator.
Go generate sets several variables when it runs the generator:
@@ -178,13 +185,43 @@ func (g *Generator) run() (ok bool) {
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")) {
+ // Scan for lines that start "//go:generate".
+ // Can't use bufio.Scanner because it can't handle long lines,
+ // which are likely to appear when using generate.
+ input := bufio.NewReader(g.r)
+ var err error
+ // One line per loop.
+ for {
+ g.lineNum++ // 1-indexed.
+ var buf []byte
+ buf, err = input.ReadSlice('\n')
+ if err == bufio.ErrBufferFull {
+ // Line too long - consume and ignore.
+ if isGoGenerate(buf) {
+ g.errorf("directive too long")
+ }
+ for err == bufio.ErrBufferFull {
+ _, err = input.ReadSlice('\n')
+ }
+ if err != nil {
+ break
+ }
+ continue
+ }
+
+ if err != nil {
+ // Check for marker at EOF without final \n.
+ if err == io.EOF && isGoGenerate(buf) {
+ err = io.ErrUnexpectedEOF
+ }
+ break
+ }
+
+ if !isGoGenerate(buf) {
continue
}
- words := g.split(s.Text())
+
+ words := g.split(string(buf))
if len(words) == 0 {
g.errorf("no arguments to directive")
}
@@ -201,19 +238,23 @@ func (g *Generator) run() (ok bool) {
}
g.exec(words)
}
- if s.Err() != nil {
- g.errorf("error reading %s: %s", shortPath(g.path), s.Err())
+ if err != nil && err != io.EOF {
+ g.errorf("error reading %s: %s", shortPath(g.path), err)
}
return true
}
+func isGoGenerate(buf []byte) bool {
+ return bytes.HasPrefix(buf, []byte("//go:generate ")) || bytes.HasPrefix(buf, []byte("//go:generate\t"))
+}
+
// split breaks the line into words, evaluating quoted
// strings and evaluating environment variables.
-// The initial //go:generate element is dropped.
+// The initial //go:generate element is present in line.
func (g *Generator) split(line string) []string {
// Parse line, obeying quoted strings.
var words []string
- line = line[len("//go:generate "):]
+ line = line[len("//go:generate ") : len(line)-1] // Drop preamble and final newline.
// One (possibly quoted) word per iteration.
Words:
for {
diff --git a/src/cmd/go/generate_test.go b/src/cmd/go/generate_test.go
index 93c0ae66e..660ebabbe 100644
--- a/src/cmd/go/generate_test.go
+++ b/src/cmd/go/generate_test.go
@@ -40,7 +40,7 @@ func TestGenerateCommandParse(t *testing.T) {
}
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
for _, test := range splitTests {
- got := g.split("//go:generate " + test.in)
+ got := g.split("//go:generate " + test.in + "\n")
if !reflect.DeepEqual(got, test.out) {
t.Errorf("split(%q): got %q expected %q", test.in, got, test.out)
}
diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c
index 64608d226..925274bfd 100644
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -202,7 +202,18 @@ loadlib(void)
iscgo |= strcmp(ctxt->library[i].pkg, "runtime/cgo") == 0;
objfile(ctxt->library[i].file, ctxt->library[i].pkg);
}
-
+
+ if(linkmode == LinkAuto) {
+ if(iscgo && externalobj)
+ linkmode = LinkExternal;
+ else
+ linkmode = LinkInternal;
+
+ // Force external linking for android.
+ if(strcmp(goos, "android") == 0)
+ linkmode = LinkExternal;
+ }
+
if(linkmode == LinkExternal && !iscgo) {
// This indicates a user requested -linkmode=external.
// The startup code uses an import of runtime/cgo to decide
@@ -229,17 +240,6 @@ loadlib(void)
}
}
- if(linkmode == LinkAuto) {
- if(iscgo && externalobj)
- linkmode = LinkExternal;
- else
- linkmode = LinkInternal;
-
- // Force external linking for android.
- if(strcmp(goos, "android") == 0)
- linkmode = LinkExternal;
- }
-
if(linkmode == LinkInternal) {
// Drop all the cgo_import_static declarations.
// Turns out we won't be needing them.
diff --git a/src/cmd/pprof/internal/commands/commands.go b/src/cmd/pprof/internal/commands/commands.go
index 6d3582018..51397a3c6 100644
--- a/src/cmd/pprof/internal/commands/commands.go
+++ b/src/cmd/pprof/internal/commands/commands.go
@@ -11,6 +11,7 @@ import (
"io"
"os"
"os/exec"
+ "runtime"
"strings"
"cmd/pprof/internal/plugin"
@@ -71,15 +72,27 @@ func PProf(c Completer, interactive **bool, svgpan **string) Commands {
"eog": {c, report.Dot, invokeVisualizer(interactive, invokeDot("svg"), "svg", []string{"eog"}), false, "Visualize graph through eog"},
"evince": {c, report.Dot, invokeVisualizer(interactive, invokeDot("pdf"), "pdf", []string{"evince"}), false, "Visualize graph through evince"},
"gv": {c, report.Dot, invokeVisualizer(interactive, invokeDot("ps"), "ps", []string{"gv --noantialias"}), false, "Visualize graph through gv"},
- "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers), false, "Visualize graph through web browser"},
+ "web": {c, report.Dot, invokeVisualizer(interactive, saveSVGToFile(svgpan), "svg", browsers()), false, "Visualize graph through web browser"},
// Visualize HTML directly generated by report.
- "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers), true, "Output annotated source in HTML for functions matching regexp or address"},
+ "weblist": {c, report.WebList, invokeVisualizer(interactive, awayFromTTY("html"), "html", browsers()), true, "Output annotated source in HTML for functions matching regexp or address"},
}
}
-// List of web browsers to attempt for web visualization
-var browsers = []string{"chrome", "google-chrome", "firefox", "/usr/bin/open"}
+// browsers returns a list of commands to attempt for web visualization
+// on the current platform
+func browsers() []string {
+ cmds := []string{"chrome", "google-chrome", "firefox"}
+ switch runtime.GOOS {
+ case "darwin":
+ cmds = append(cmds, "/usr/bin/open")
+ case "windows":
+ cmds = append(cmds, "cmd /c start")
+ default:
+ cmds = append(cmds, "xdg-open")
+ }
+ return cmds
+}
// NewCompleter creates an autocompletion function for a set of commands.
func NewCompleter(cs Commands) Completer {
@@ -142,6 +155,10 @@ func awayFromTTY(format string) PostProcessor {
func invokeDot(format string) PostProcessor {
divert := awayFromTTY(format)
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
+ if _, err := exec.LookPath("dot"); err != nil {
+ ui.PrintErr("Cannot find dot, have you installed Graphviz?")
+ return err
+ }
cmd := exec.Command("dot", "-T"+format)
var buf bytes.Buffer
cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
@@ -174,6 +191,7 @@ func invokeVisualizer(interactive **bool, format PostProcessor, suffix string, v
if err = format(input, tempFile, ui); err != nil {
return err
}
+ tempFile.Close() // on windows, if the file is Open, start cannot access it.
// Try visualizers until one is successful
for _, v := range visualizers {
// Separate command and arguments for exec.Command.
diff --git a/src/cmd/pprof/internal/symbolizer/symbolizer.go b/src/cmd/pprof/internal/symbolizer/symbolizer.go
index cabddaa76..86de5640d 100644
--- a/src/cmd/pprof/internal/symbolizer/symbolizer.go
+++ b/src/cmd/pprof/internal/symbolizer/symbolizer.go
@@ -32,6 +32,10 @@ func Symbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin
}
}
+ if len(prof.Mapping) == 0 {
+ return fmt.Errorf("no known mappings")
+ }
+
mt, err := newMapping(prof, obj, ui, force)
if err != nil {
return err
diff --git a/src/go/build/build.go b/src/go/build/build.go
index ed79df293..ab2976756 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -1310,11 +1310,13 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool {
// auto-tagging to apply only to files with a non-empty prefix, so
// "foo_linux.go" is tagged but "linux.go" is not. This allows new operating
// sytems, such as android, to arrive without breaking existing code with
- // innocuous source code in "android.go". The easiest fix: files without
- // underscores are always included.
- if !strings.ContainsRune(name, '_') {
+ // innocuous source code in "android.go". The easiest fix: cut everything
+ // in the name before the initial _.
+ i := strings.Index(name, "_")
+ if i < 0 {
return true
}
+ name = name[i:] // ignore everything before first _
l := strings.Split(name, "_")
if n := len(l); n > 0 && l[n-1] == "test" {
diff --git a/src/go/build/build_test.go b/src/go/build/build_test.go
index 43d09cbd1..a40def0fa 100644
--- a/src/go/build/build_test.go
+++ b/src/go/build/build_test.go
@@ -189,6 +189,7 @@ var matchFileTests = []struct {
{ctxtAndroid, "foo_plan9.go", "", false},
{ctxtAndroid, "android.go", "", true},
{ctxtAndroid, "plan9.go", "", true},
+ {ctxtAndroid, "plan9_test.go", "", true},
{ctxtAndroid, "arm.s", "", true},
{ctxtAndroid, "amd64.s", "", true},
}
diff --git a/src/liblink/asm9.c b/src/liblink/asm9.c
index 3c125b3b0..5a379270d 100644
--- a/src/liblink/asm9.c
+++ b/src/liblink/asm9.c
@@ -1589,7 +1589,7 @@ asmout(Link *ctxt, Prog *p, Optab *o, int32 *out)
ctxt->diag("odd branch target address\n%P", p);
v &= ~03;
}
- rel->add = o1 | (v & 0x03FFFFFC);
+ rel->add = v;
rel->type = R_CALLPOWER;
}
break;
diff --git a/src/liblink/pass.c b/src/liblink/pass.c
index bc8eb4367..8721a6a79 100644
--- a/src/liblink/pass.c
+++ b/src/liblink/pass.c
@@ -41,7 +41,7 @@ brchain(Link *ctxt, Prog *p)
int i;
for(i=0; i<20; i++) {
- if(p == nil || p->as != ctxt->arch->AJMP)
+ if(p == nil || p->as != ctxt->arch->AJMP || p->pcond == nil)
return p;
p = p->pcond;
}
@@ -56,7 +56,7 @@ brloop(Link *ctxt, Prog *p)
c = 0;
for(q = p; q != nil; q = q->pcond) {
- if(q->as != ctxt->arch->AJMP)
+ if(q->as != ctxt->arch->AJMP || q->pcond == nil)
break;
c++;
if(c >= 5000)
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index c62f227f4..9d092d9ac 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4060,3 +4060,104 @@ func TestLargeGCProg(t *testing.T) {
fv := ValueOf(func([256]*byte) {})
fv.Call([]Value{ValueOf([256]*byte{})})
}
+
+// Issue 9179.
+func TestCallGC(t *testing.T) {
+ f := func(a, b, c, d, e string) {
+ }
+ g := func(in []Value) []Value {
+ runtime.GC()
+ return nil
+ }
+ typ := ValueOf(f).Type()
+ f2 := MakeFunc(typ, g).Interface().(func(string, string, string, string, string))
+ f2("four", "five5", "six666", "seven77", "eight888")
+}
+
+type funcLayoutTest struct {
+ rcvr, t Type
+ argsize, retOffset uintptr
+ stack []byte
+}
+
+var funcLayoutTests []funcLayoutTest
+
+func init() {
+ var argAlign = PtrSize
+ if runtime.GOARCH == "amd64p32" {
+ argAlign = 2 * PtrSize
+ }
+ roundup := func(x uintptr, a uintptr) uintptr {
+ return (x + a - 1) / a * a
+ }
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a, b string) string { return "" }).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsPointer, BitsScalar, BitsPointer},
+ })
+
+ var r []byte
+ if PtrSize == 4 {
+ r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer}
+ } else {
+ r = []byte{BitsScalar, BitsScalar, BitsPointer}
+ }
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a, b, c uint32, p *byte, d uint16) {}).Type(),
+ roundup(3*4, PtrSize) + PtrSize + 2,
+ roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
+ r,
+ })
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a map[int]int, b uintptr, c interface{}) {}).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer},
+ })
+
+ type S struct {
+ a, b uintptr
+ c, d *byte
+ }
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ nil,
+ ValueOf(func(a S) {}).Type(),
+ 4 * PtrSize,
+ 4 * PtrSize,
+ []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer},
+ })
+
+ funcLayoutTests = append(funcLayoutTests,
+ funcLayoutTest{
+ ValueOf((*byte)(nil)).Type(),
+ ValueOf(func(a uintptr, b *int) {}).Type(),
+ 3 * PtrSize,
+ roundup(3*PtrSize, argAlign),
+ []byte{BitsPointer, BitsScalar, BitsPointer},
+ })
+}
+
+func TestFuncLayout(t *testing.T) {
+ for _, lt := range funcLayoutTests {
+ _, argsize, retOffset, stack := FuncLayout(lt.t, lt.rcvr)
+ if argsize != lt.argsize {
+ t.Errorf("funcLayout(%v, %v).argsize=%d, want %d", lt.t, lt.rcvr, argsize, lt.argsize)
+ }
+ if retOffset != lt.retOffset {
+ t.Errorf("funcLayout(%v, %v).retOffset=%d, want %d", lt.t, lt.rcvr, retOffset, lt.retOffset)
+ }
+ if !bytes.Equal(stack, lt.stack) {
+ t.Errorf("funcLayout(%v, %v).stack=%v, want %v", lt.t, lt.rcvr, stack, lt.stack)
+ }
+ }
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 0778ad37f..caaf51a50 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -17,3 +17,22 @@ func IsRO(v Value) bool {
var ArrayOf = arrayOf
var CallGC = &callGC
+
+const PtrSize = ptrSize
+const BitsPointer = bitsPointer
+const BitsScalar = bitsScalar
+
+func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte) {
+ var ft *rtype
+ var s *bitVector
+ if rcvr != nil {
+ ft, argSize, retOffset, s = funcLayout(t.(*rtype), rcvr.(*rtype))
+ } else {
+ ft, argSize, retOffset, s = funcLayout(t.(*rtype), nil)
+ }
+ frametype = ft
+ for i := uint32(0); i < s.n; i += 2 {
+ stack = append(stack, s.data[i/8]>>(i%8)&3)
+ }
+ return
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index e05a3f9d1..441459b3f 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1889,14 +1889,14 @@ func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
switch Kind(t.kind & kindMask) {
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
// 1 pointer at start of representation
- for bv.n < uint32(*offset/uintptr(ptrSize)) {
+ for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)
case Interface:
// 2 pointers
- for bv.n < uint32(*offset/uintptr(ptrSize)) {
+ for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
bv.append2(bitsScalar)
}
bv.append2(bitsPointer)
diff --git a/src/runtime/defs_dragonfly_386.go b/src/runtime/defs_dragonfly_386.go
index 1768dbac4..e9c635315 100644
--- a/src/runtime/defs_dragonfly_386.go
+++ b/src/runtime/defs_dragonfly_386.go
@@ -92,16 +92,16 @@ type rtprio struct {
}
type lwpparams struct {
- _type unsafe.Pointer
- arg *byte
- stack *byte
- tid1 *int32
- tid2 *int32
+ start_func uintptr
+ arg unsafe.Pointer
+ stack uintptr
+ tid1 unsafe.Pointer // *int32
+ tid2 unsafe.Pointer // *int32
}
type sigaltstackt struct {
- ss_sp *int8
- ss_size uint32
+ ss_sp uintptr
+ ss_size uintptr
ss_flags int32
}
@@ -110,8 +110,8 @@ type sigset struct {
}
type stackt struct {
- ss_sp *int8
- ss_size uint32
+ ss_sp uintptr
+ ss_size uintptr
ss_flags int32
}
@@ -122,39 +122,39 @@ type siginfo struct {
si_pid int32
si_uid uint32
si_status int32
- si_addr *byte
+ si_addr uintptr
si_value [4]byte
si_band int32
__spare__ [7]int32
}
type mcontext struct {
- mc_onstack int32
- mc_gs int32
- mc_fs int32
- mc_es int32
- mc_ds int32
- mc_edi int32
- mc_esi int32
- mc_ebp int32
- mc_isp int32
- mc_ebx int32
- mc_edx int32
- mc_ecx int32
- mc_eax int32
- mc_xflags int32
- mc_trapno int32
- mc_err int32
- mc_eip int32
- mc_cs int32
- mc_eflags int32
- mc_esp int32
- mc_ss int32
- mc_len int32
- mc_fpformat int32
- mc_ownedfp int32
- mc_fpregs [128]int32
- __spare__ [16]int32
+ mc_onstack uint32
+ mc_gs uint32
+ mc_fs uint32
+ mc_es uint32
+ mc_ds uint32
+ mc_edi uint32
+ mc_esi uint32
+ mc_ebp uint32
+ mc_isp uint32
+ mc_ebx uint32
+ mc_edx uint32
+ mc_ecx uint32
+ mc_eax uint32
+ mc_xflags uint32
+ mc_trapno uint32
+ mc_err uint32
+ mc_eip uint32
+ mc_cs uint32
+ mc_eflags uint32
+ mc_esp uint32
+ mc_ss uint32
+ mc_len uint32
+ mc_fpformat uint32
+ mc_ownedfp uint32
+ mc_fpregs [128]uint32
+ __spare__ [16]uint32
}
type ucontext struct {
@@ -170,11 +170,19 @@ type timespec struct {
tv_nsec int32
}
+func (ts *timespec) set_sec(x int64) {
+ ts.tv_sec = int32(x)
+}
+
type timeval struct {
tv_sec int32
tv_usec int32
}
+func (tv *timeval) set_usec(x int32) {
+ tv.tv_usec = x
+}
+
type itimerval struct {
it_interval timeval
it_value timeval
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index be01f2671..5a898ff41 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -375,6 +375,36 @@ func casgstatus(gp *g, oldval, newval uint32) {
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
for !cas(&gp.atomicstatus, oldval, newval) {
+ if oldval == _Gwaiting && gp.atomicstatus == _Grunnable {
+ systemstack(func() {
+ gothrow("casgstatus: waiting for Gwaiting but is Grunnable")
+ })
+ }
+ // Help GC if needed.
+ // if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) {
+ // gp.preemptscan = false
+ // systemstack(func() {
+ // gcphasework(gp)
+ // })
+ // }
+ }
+}
+
+// casgstatus(gp, oldstatus, Gcopystack), assuming oldstatus is Gwaiting or Grunnable.
+// Returns old status. Cannot call casgstatus directly, because we are racing with an
+// async wakeup that might come in from netpoll. If we see Gwaiting from the readgstatus,
+// it might have become Grunnable by the time we get to the cas. If we called casgstatus,
+// it would loop waiting for the status to go back to Gwaiting, which it never will.
+//go:nosplit
+func casgcopystack(gp *g) uint32 {
+ for {
+ oldstatus := readgstatus(gp) &^ _Gscan
+ if oldstatus != _Gwaiting && oldstatus != _Grunnable {
+ gothrow("copystack: bad status, not Gwaiting or Grunnable")
+ }
+ if cas(&gp.atomicstatus, oldstatus, _Gcopystack) {
+ return oldstatus
+ }
}
}
diff --git a/src/runtime/signal_dragonfly_386.go b/src/runtime/signal_dragonfly_386.go
new file mode 100644
index 000000000..a0fec1309
--- /dev/null
+++ b/src/runtime/signal_dragonfly_386.go
@@ -0,0 +1,34 @@
+// 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.
+
+package runtime
+
+import "unsafe"
+
+type sigctxt struct {
+ info *siginfo
+ ctxt unsafe.Pointer
+}
+
+func (c *sigctxt) regs() *mcontext { return &(*ucontext)(c.ctxt).uc_mcontext }
+func (c *sigctxt) eax() uint32 { return c.regs().mc_eax }
+func (c *sigctxt) ebx() uint32 { return c.regs().mc_ebx }
+func (c *sigctxt) ecx() uint32 { return c.regs().mc_ecx }
+func (c *sigctxt) edx() uint32 { return c.regs().mc_edx }
+func (c *sigctxt) edi() uint32 { return c.regs().mc_edi }
+func (c *sigctxt) esi() uint32 { return c.regs().mc_esi }
+func (c *sigctxt) ebp() uint32 { return c.regs().mc_ebp }
+func (c *sigctxt) esp() uint32 { return c.regs().mc_esp }
+func (c *sigctxt) eip() uint32 { return c.regs().mc_eip }
+func (c *sigctxt) eflags() uint32 { return c.regs().mc_eflags }
+func (c *sigctxt) cs() uint32 { return uint32(c.regs().mc_cs) }
+func (c *sigctxt) fs() uint32 { return uint32(c.regs().mc_fs) }
+func (c *sigctxt) gs() uint32 { return uint32(c.regs().mc_gs) }
+func (c *sigctxt) sigcode() uint32 { return uint32(c.info.si_code) }
+func (c *sigctxt) sigaddr() uint32 { return uint32(c.info.si_addr) }
+
+func (c *sigctxt) set_eip(x uint32) { c.regs().mc_eip = x }
+func (c *sigctxt) set_esp(x uint32) { c.regs().mc_esp = x }
+func (c *sigctxt) set_sigcode(x uint32) { c.info.si_code = int32(x) }
+func (c *sigctxt) set_sigaddr(x uint32) { c.info.si_addr = uintptr(x) }
diff --git a/src/runtime/signal_dragonfly_386.h b/src/runtime/signal_dragonfly_386.h
deleted file mode 100644
index a24f1ee96..000000000
--- a/src/runtime/signal_dragonfly_386.h
+++ /dev/null
@@ -1,23 +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.
-
-#define SIG_REGS(ctxt) (((Ucontext*)(ctxt))->uc_mcontext)
-
-#define SIG_EAX(info, ctxt) (SIG_REGS(ctxt).mc_eax)
-#define SIG_EBX(info, ctxt) (SIG_REGS(ctxt).mc_ebx)
-#define SIG_ECX(info, ctxt) (SIG_REGS(ctxt).mc_ecx)
-#define SIG_EDX(info, ctxt) (SIG_REGS(ctxt).mc_edx)
-#define SIG_EDI(info, ctxt) (SIG_REGS(ctxt).mc_edi)
-#define SIG_ESI(info, ctxt) (SIG_REGS(ctxt).mc_esi)
-#define SIG_EBP(info, ctxt) (SIG_REGS(ctxt).mc_ebp)
-#define SIG_ESP(info, ctxt) (SIG_REGS(ctxt).mc_esp)
-#define SIG_EIP(info, ctxt) (SIG_REGS(ctxt).mc_eip)
-#define SIG_EFLAGS(info, ctxt) (SIG_REGS(ctxt).mc_eflags)
-
-#define SIG_CS(info, ctxt) (SIG_REGS(ctxt).mc_cs)
-#define SIG_FS(info, ctxt) (SIG_REGS(ctxt).mc_fs)
-#define SIG_GS(info, ctxt) (SIG_REGS(ctxt).mc_gs)
-
-#define SIG_CODE0(info, ctxt) ((info)->si_code)
-#define SIG_CODE1(info, ctxt) ((uintptr)(info)->si_addr)
diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index c14347dbd..28000864d 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -718,9 +718,7 @@ func newstack() {
gothrow("stack overflow")
}
- oldstatus := readgstatus(gp)
- oldstatus &^= _Gscan
- casgstatus(gp, oldstatus, _Gcopystack) // oldstatus is Gwaiting or Grunnable
+ casgstatus(gp, _Gwaiting, _Gcopystack)
// The concurrent GC will not scan the stack while we are doing the copy since
// the gp is in a Gcopystack status.
@@ -789,15 +787,7 @@ func shrinkstack(gp *g) {
print("shrinking stack ", oldsize, "->", newsize, "\n")
}
- // This is being done in a Gscan state and was initiated by the GC so no need to move to
- // the Gcopystate.
- // The world is stopped, so the goroutine must be Gwaiting or Grunnable,
- // and what it is is not changing underfoot.
- oldstatus := readgstatus(gp) &^ _Gscan
- if oldstatus != _Gwaiting && oldstatus != _Grunnable {
- gothrow("status is not Gwaiting or Grunnable")
- }
- casgstatus(gp, oldstatus, _Gcopystack)
+ oldstatus := casgcopystack(gp)
copystack(gp, newsize)
casgstatus(gp, _Gcopystack, oldstatus)
}