mirror of
https://github.com/golang/go
synced 2024-09-23 13:20:14 -06:00
[dev.garbage] all: merge dev.cc (81884b89bd88) into dev.garbage
TBR=rlh CC=golang-codereviews https://golang.org/cl/181100044
This commit is contained in:
commit
444839014b
1
.hgtags
1
.hgtags
@ -137,3 +137,4 @@ f44017549ff9c3cc5eef74ebe7276cd0dfc066b6 go1.3.3
|
||||
f44017549ff9c3cc5eef74ebe7276cd0dfc066b6 release
|
||||
1fdfd7dfaedb1b7702141617e621ab7328a236a1 go1.4beta1
|
||||
bffdd0cae380ce1ccf3e98ed6b6cd53fece7ba72 go1.4rc1
|
||||
6c4e66ae713704840fcea78c8055b44ba86ae5ea go1.4rc2
|
||||
|
@ -30,21 +30,16 @@ We encourage all Go users to subscribe to
|
||||
<h2 id="go1">Version history</h2>
|
||||
|
||||
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
|
||||
<p>A summary of the changes between Go releases.</p>
|
||||
|
||||
<h4 id="go1notes"><a href="/doc/go1">Go 1 Release Notes</a></h4>
|
||||
<p>
|
||||
A guide for updating your code to work with Go 1.
|
||||
</p>
|
||||
<p>A <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||
|
||||
<h4 id="release notes"><a href="/doc/go1.1">Go 1.1 Release Notes</a></h4>
|
||||
<p>
|
||||
A list of significant changes in Go 1.1, with instructions for updating
|
||||
your code where necessary.
|
||||
Each point release includes a similar document appropriate for that
|
||||
release: <a href="/doc/go1.2">Go 1.2</a>, <a href="/doc/go1.3">Go 1.3</a>,
|
||||
and so on.
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||
<li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
|
||||
<li><a href="/doc/go1.2">Go 1.2</a> <small>(December 2013)</small></li>
|
||||
<li><a href="/doc/go1.1">Go 1.1</a> <small>(May 2013)</small></li>
|
||||
<li><a href="/doc/go1">Go 1</a> <small>(March 2012)</small></li>
|
||||
</ul>
|
||||
|
||||
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
|
||||
<p>
|
||||
|
@ -5579,7 +5579,7 @@ s3 := append(s2, s0...) // append a slice s3 == []int{0,
|
||||
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
|
||||
|
||||
var t []interface{}
|
||||
t = append(t, 42, 3.1415, "foo") t == []interface{}{42, 3.1415, "foo"}
|
||||
t = append(t, 42, 3.1415, "foo") // t == []interface{}{42, 3.1415, "foo"}
|
||||
|
||||
var b []byte
|
||||
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
|
||||
|
@ -7,8 +7,8 @@
|
||||
# downloaded from the ICANN/IANA distribution.
|
||||
|
||||
# Versions to use.
|
||||
CODE=2014d
|
||||
DATA=2014d
|
||||
CODE=2014j
|
||||
DATA=2014j
|
||||
|
||||
set -e
|
||||
rm -rf work
|
||||
|
Binary file not shown.
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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];
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
words := g.split(s.Text())
|
||||
|
||||
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(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 {
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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" {
|
||||
|
@ -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},
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
34
src/runtime/signal_dragonfly_386.go
Normal file
34
src/runtime/signal_dragonfly_386.go
Normal file
@ -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) }
|
@ -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)
|
@ -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)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user