mirror of
https://github.com/golang/go
synced 2024-11-11 19:21:37 -07: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
|
f44017549ff9c3cc5eef74ebe7276cd0dfc066b6 release
|
||||||
1fdfd7dfaedb1b7702141617e621ab7328a236a1 go1.4beta1
|
1fdfd7dfaedb1b7702141617e621ab7328a236a1 go1.4beta1
|
||||||
bffdd0cae380ce1ccf3e98ed6b6cd53fece7ba72 go1.4rc1
|
bffdd0cae380ce1ccf3e98ed6b6cd53fece7ba72 go1.4rc1
|
||||||
|
6c4e66ae713704840fcea78c8055b44ba86ae5ea go1.4rc2
|
||||||
|
@ -30,21 +30,16 @@ We encourage all Go users to subscribe to
|
|||||||
<h2 id="go1">Version history</h2>
|
<h2 id="go1">Version history</h2>
|
||||||
|
|
||||||
<h3 id="release"><a href="/doc/devel/release.html">Release History</a></h3>
|
<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 <a href="/doc/devel/release.html">summary</a> of the changes between Go releases. Notes for the major releases:</p>
|
||||||
<p>
|
|
||||||
A guide for updating your code to work with Go 1.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<h4 id="release notes"><a href="/doc/go1.1">Go 1.1 Release Notes</a></h4>
|
<ul>
|
||||||
<p>
|
<li><a href="/doc/go1.4">Go 1.4</a> <small>(December 2014)</small></li>
|
||||||
A list of significant changes in Go 1.1, with instructions for updating
|
<li><a href="/doc/go1.3">Go 1.3</a> <small>(June 2014)</small></li>
|
||||||
your code where necessary.
|
<li><a href="/doc/go1.2">Go 1.2</a> <small>(December 2013)</small></li>
|
||||||
Each point release includes a similar document appropriate for that
|
<li><a href="/doc/go1.1">Go 1.1</a> <small>(May 2013)</small></li>
|
||||||
release: <a href="/doc/go1.2">Go 1.2</a>, <a href="/doc/go1.3">Go 1.3</a>,
|
<li><a href="/doc/go1">Go 1</a> <small>(March 2012)</small></li>
|
||||||
and so on.
|
</ul>
|
||||||
</p>
|
|
||||||
|
|
||||||
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
|
<h3 id="go1compat"><a href="/doc/go1compat">Go 1 and the Future of Go Programs</a></h3>
|
||||||
<p>
|
<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}
|
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}
|
||||||
|
|
||||||
var t []interface{}
|
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
|
var b []byte
|
||||||
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
|
b = append(b, "bar"...) // append string contents b == []byte{'b', 'a', 'r' }
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
# downloaded from the ICANN/IANA distribution.
|
# downloaded from the ICANN/IANA distribution.
|
||||||
|
|
||||||
# Versions to use.
|
# Versions to use.
|
||||||
CODE=2014d
|
CODE=2014j
|
||||||
DATA=2014d
|
DATA=2014j
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
rm -rf work
|
rm -rf work
|
||||||
|
Binary file not shown.
@ -458,7 +458,7 @@ brk:
|
|||||||
print("\nregisterizing\n");
|
print("\nregisterizing\n");
|
||||||
for(i=0; i<nregion; i++) {
|
for(i=0; i<nregion; i++) {
|
||||||
if(debug['R'] && debug['v'])
|
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);
|
bit = blsh(rgp->varno);
|
||||||
vreg = paint2(rgp->enter, rgp->varno, 0);
|
vreg = paint2(rgp->enter, rgp->varno, 0);
|
||||||
vreg = allreg(vreg, rgp);
|
vreg = allreg(vreg, rgp);
|
||||||
|
@ -393,7 +393,7 @@ brk:
|
|||||||
print("\nregisterizing\n");
|
print("\nregisterizing\n");
|
||||||
for(i=0; i<nregion; i++) {
|
for(i=0; i<nregion; i++) {
|
||||||
if(debug['R'] && debug['v'])
|
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);
|
bit = blsh(rgp->varno);
|
||||||
vreg = paint2(rgp->enter, rgp->varno, 0);
|
vreg = paint2(rgp->enter, rgp->varno, 0);
|
||||||
vreg = allreg(vreg, rgp);
|
vreg = allreg(vreg, rgp);
|
||||||
|
@ -225,6 +225,16 @@ enum
|
|||||||
|
|
||||||
void proginfo(ProgInfo*, Prog*);
|
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.
|
// To allow use of AJMP, ACALL, ARET in ../gc/popt.c.
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -33,12 +33,295 @@
|
|||||||
#include "gg.h"
|
#include "gg.h"
|
||||||
#include "opt.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
|
void
|
||||||
peep(Prog *p)
|
peep(Prog *firstp)
|
||||||
{
|
{
|
||||||
USED(p);
|
Graph *g;
|
||||||
// TODO(minux)
|
Flow *r, *r1;
|
||||||
return;
|
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
|
void
|
||||||
@ -56,6 +339,22 @@ excise(Flow *r)
|
|||||||
ostats.ndelmov++;
|
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
|
int
|
||||||
regtyp(Adr *a)
|
regtyp(Adr *a)
|
||||||
{
|
{
|
||||||
@ -63,11 +362,600 @@ regtyp(Adr *a)
|
|||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
case D_REG:
|
case D_REG:
|
||||||
|
if(a->reg == REGZERO)
|
||||||
|
return 0;
|
||||||
case D_FREG:
|
case D_FREG:
|
||||||
return 1;
|
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
|
int
|
||||||
sameaddr(Addr *a, Addr *v)
|
sameaddr(Addr *a, Addr *v)
|
||||||
{
|
{
|
||||||
|
@ -101,9 +101,36 @@ static ProgInfo progtable[ALAST] = {
|
|||||||
[ADUFFCOPY]= {Call},
|
[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
|
void
|
||||||
proginfo(ProgInfo *info, Prog *p)
|
proginfo(ProgInfo *info, Prog *p)
|
||||||
{
|
{
|
||||||
|
initproginfo();
|
||||||
|
|
||||||
*info = progtable[p->as];
|
*info = progtable[p->as];
|
||||||
if(info->flags == 0) {
|
if(info->flags == 0) {
|
||||||
*info = progtable[AADD];
|
*info = progtable[AADD];
|
||||||
@ -143,3 +170,138 @@ proginfo(ProgInfo *info, Prog *p)
|
|||||||
info->regset |= RtoB(3) | RtoB(4);
|
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");
|
print("\nregisterizing\n");
|
||||||
for(i=0; i<nregion; i++) {
|
for(i=0; i<nregion; i++) {
|
||||||
if(debug['R'] && debug['v'])
|
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);
|
bit = blsh(rgp->varno);
|
||||||
usedreg = paint2(rgp->enter, rgp->varno, 0);
|
usedreg = paint2(rgp->enter, rgp->varno, 0);
|
||||||
vreg = allreg(usedreg, rgp);
|
vreg = allreg(usedreg, rgp);
|
||||||
|
@ -65,14 +65,6 @@ needlib(char *name)
|
|||||||
|
|
||||||
int nelfsym = 1;
|
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
|
void
|
||||||
adddynrela(LSym *rel, LSym *s, Reloc *r)
|
adddynrela(LSym *rel, LSym *s, Reloc *r)
|
||||||
{
|
{
|
||||||
@ -160,7 +152,19 @@ archreloc(Reloc *r, LSym *s, vlong *val)
|
|||||||
*val = ((vlong)o2 << 32) | o1;
|
*val = ((vlong)o2 << 32) | o1;
|
||||||
return 0;
|
return 0;
|
||||||
case R_CALLPOWER:
|
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 0;
|
||||||
}
|
}
|
||||||
return -1;
|
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,
|
Go generate is never run automatically by go build, go get, go test,
|
||||||
and so on. It must be run explicitly.
|
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...
|
//go:generate command argument...
|
||||||
|
|
||||||
(note: no space in "//go") where command is the generator to be
|
(note: no leading spaces and no space in "//go") where command
|
||||||
run, corresponding to an executable file that can be run locally.
|
is the generator to be run, corresponding to an executable file
|
||||||
It must either be in the shell path (gofmt), a fully qualified path
|
that can be run locally. It must either be in the shell path
|
||||||
(/usr/you/bin/mytool), or a command alias, described below.
|
(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
|
Note that go generate does not parse the file, so lines that look
|
||||||
passed to the generator as individual arguments when it is run.
|
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 strings use Go syntax and are evaluated before execution; a
|
||||||
quoted string appears as a single argument to the generator.
|
quoted string appears as a single argument to the generator.
|
||||||
@ -317,7 +324,7 @@ Download and install packages and dependencies
|
|||||||
|
|
||||||
Usage:
|
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,
|
Get downloads and installs the packages named by the import paths,
|
||||||
along with their dependencies.
|
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,
|
The -d flag instructs get to stop after downloading the packages; that is,
|
||||||
it instructs get not to install the packages.
|
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
|
The -fix flag instructs get to run the fix tool on the downloaded packages
|
||||||
before resolving dependencies or building the code.
|
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,
|
Go generate is never run automatically by go build, go get, go test,
|
||||||
and so on. It must be run explicitly.
|
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...
|
//go:generate command argument...
|
||||||
|
|
||||||
(note: no space in "//go") where command is the generator to be
|
(note: no leading spaces and no space in "//go") where command
|
||||||
run, corresponding to an executable file that can be run locally.
|
is the generator to be run, corresponding to an executable file
|
||||||
It must either be in the shell path (gofmt), a fully qualified path
|
that can be run locally. It must either be in the shell path
|
||||||
(/usr/you/bin/mytool), or a command alias, described below.
|
(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
|
Note that go generate does not parse the file, so lines that look
|
||||||
passed to the generator as individual arguments when it is run.
|
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 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:
|
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))
|
fmt.Fprintf(os.Stderr, "%s\n", shortPath(g.path))
|
||||||
}
|
}
|
||||||
|
|
||||||
s := bufio.NewScanner(g.r)
|
// Scan for lines that start "//go:generate".
|
||||||
for s.Scan() {
|
// Can't use bufio.Scanner because it can't handle long lines,
|
||||||
g.lineNum++
|
// which are likely to appear when using generate.
|
||||||
if !bytes.HasPrefix(s.Bytes(), []byte("//go:generate ")) && !bytes.HasPrefix(s.Bytes(), []byte("//go:generate\t")) {
|
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
|
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 {
|
if len(words) == 0 {
|
||||||
g.errorf("no arguments to directive")
|
g.errorf("no arguments to directive")
|
||||||
}
|
}
|
||||||
@ -201,19 +238,23 @@ func (g *Generator) run() (ok bool) {
|
|||||||
}
|
}
|
||||||
g.exec(words)
|
g.exec(words)
|
||||||
}
|
}
|
||||||
if s.Err() != nil {
|
if err != nil && err != io.EOF {
|
||||||
g.errorf("error reading %s: %s", shortPath(g.path), s.Err())
|
g.errorf("error reading %s: %s", shortPath(g.path), err)
|
||||||
}
|
}
|
||||||
return true
|
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
|
// split breaks the line into words, evaluating quoted
|
||||||
// strings and evaluating environment variables.
|
// 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 {
|
func (g *Generator) split(line string) []string {
|
||||||
// Parse line, obeying quoted strings.
|
// Parse line, obeying quoted strings.
|
||||||
var words []string
|
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.
|
// One (possibly quoted) word per iteration.
|
||||||
Words:
|
Words:
|
||||||
for {
|
for {
|
||||||
|
@ -40,7 +40,7 @@ func TestGenerateCommandParse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
|
g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"})
|
||||||
for _, test := range splitTests {
|
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) {
|
if !reflect.DeepEqual(got, test.out) {
|
||||||
t.Errorf("split(%q): got %q expected %q", test.in, 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;
|
iscgo |= strcmp(ctxt->library[i].pkg, "runtime/cgo") == 0;
|
||||||
objfile(ctxt->library[i].file, ctxt->library[i].pkg);
|
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) {
|
if(linkmode == LinkExternal && !iscgo) {
|
||||||
// This indicates a user requested -linkmode=external.
|
// This indicates a user requested -linkmode=external.
|
||||||
// The startup code uses an import of runtime/cgo to decide
|
// 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) {
|
if(linkmode == LinkInternal) {
|
||||||
// Drop all the cgo_import_static declarations.
|
// Drop all the cgo_import_static declarations.
|
||||||
// Turns out we won't be needing them.
|
// Turns out we won't be needing them.
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"cmd/pprof/internal/plugin"
|
"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"},
|
"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"},
|
"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"},
|
"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.
|
// 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
|
// browsers returns a list of commands to attempt for web visualization
|
||||||
var browsers = []string{"chrome", "google-chrome", "firefox", "/usr/bin/open"}
|
// 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.
|
// NewCompleter creates an autocompletion function for a set of commands.
|
||||||
func NewCompleter(cs Commands) Completer {
|
func NewCompleter(cs Commands) Completer {
|
||||||
@ -142,6 +155,10 @@ func awayFromTTY(format string) PostProcessor {
|
|||||||
func invokeDot(format string) PostProcessor {
|
func invokeDot(format string) PostProcessor {
|
||||||
divert := awayFromTTY(format)
|
divert := awayFromTTY(format)
|
||||||
return func(input *bytes.Buffer, output io.Writer, ui plugin.UI) error {
|
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)
|
cmd := exec.Command("dot", "-T"+format)
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
cmd.Stdin, cmd.Stdout, cmd.Stderr = input, &buf, os.Stderr
|
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 {
|
if err = format(input, tempFile, ui); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
tempFile.Close() // on windows, if the file is Open, start cannot access it.
|
||||||
// Try visualizers until one is successful
|
// Try visualizers until one is successful
|
||||||
for _, v := range visualizers {
|
for _, v := range visualizers {
|
||||||
// Separate command and arguments for exec.Command.
|
// 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)
|
mt, err := newMapping(prof, obj, ui, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// 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
|
// "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
|
// sytems, such as android, to arrive without breaking existing code with
|
||||||
// innocuous source code in "android.go". The easiest fix: files without
|
// innocuous source code in "android.go". The easiest fix: cut everything
|
||||||
// underscores are always included.
|
// in the name before the initial _.
|
||||||
if !strings.ContainsRune(name, '_') {
|
i := strings.Index(name, "_")
|
||||||
|
if i < 0 {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
name = name[i:] // ignore everything before first _
|
||||||
|
|
||||||
l := strings.Split(name, "_")
|
l := strings.Split(name, "_")
|
||||||
if n := len(l); n > 0 && l[n-1] == "test" {
|
if n := len(l); n > 0 && l[n-1] == "test" {
|
||||||
|
@ -189,6 +189,7 @@ var matchFileTests = []struct {
|
|||||||
{ctxtAndroid, "foo_plan9.go", "", false},
|
{ctxtAndroid, "foo_plan9.go", "", false},
|
||||||
{ctxtAndroid, "android.go", "", true},
|
{ctxtAndroid, "android.go", "", true},
|
||||||
{ctxtAndroid, "plan9.go", "", true},
|
{ctxtAndroid, "plan9.go", "", true},
|
||||||
|
{ctxtAndroid, "plan9_test.go", "", true},
|
||||||
{ctxtAndroid, "arm.s", "", true},
|
{ctxtAndroid, "arm.s", "", true},
|
||||||
{ctxtAndroid, "amd64.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);
|
ctxt->diag("odd branch target address\n%P", p);
|
||||||
v &= ~03;
|
v &= ~03;
|
||||||
}
|
}
|
||||||
rel->add = o1 | (v & 0x03FFFFFC);
|
rel->add = v;
|
||||||
rel->type = R_CALLPOWER;
|
rel->type = R_CALLPOWER;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -41,7 +41,7 @@ brchain(Link *ctxt, Prog *p)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for(i=0; i<20; 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;
|
return p;
|
||||||
p = p->pcond;
|
p = p->pcond;
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ brloop(Link *ctxt, Prog *p)
|
|||||||
|
|
||||||
c = 0;
|
c = 0;
|
||||||
for(q = p; q != nil; q = q->pcond) {
|
for(q = p; q != nil; q = q->pcond) {
|
||||||
if(q->as != ctxt->arch->AJMP)
|
if(q->as != ctxt->arch->AJMP || q->pcond == nil)
|
||||||
break;
|
break;
|
||||||
c++;
|
c++;
|
||||||
if(c >= 5000)
|
if(c >= 5000)
|
||||||
|
@ -4060,3 +4060,104 @@ func TestLargeGCProg(t *testing.T) {
|
|||||||
fv := ValueOf(func([256]*byte) {})
|
fv := ValueOf(func([256]*byte) {})
|
||||||
fv.Call([]Value{ValueOf([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 ArrayOf = arrayOf
|
||||||
var CallGC = &callGC
|
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) {
|
switch Kind(t.kind & kindMask) {
|
||||||
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
|
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
|
||||||
// 1 pointer at start of representation
|
// 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(bitsScalar)
|
||||||
}
|
}
|
||||||
bv.append2(bitsPointer)
|
bv.append2(bitsPointer)
|
||||||
|
|
||||||
case Interface:
|
case Interface:
|
||||||
// 2 pointers
|
// 2 pointers
|
||||||
for bv.n < uint32(*offset/uintptr(ptrSize)) {
|
for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
|
||||||
bv.append2(bitsScalar)
|
bv.append2(bitsScalar)
|
||||||
}
|
}
|
||||||
bv.append2(bitsPointer)
|
bv.append2(bitsPointer)
|
||||||
|
@ -92,16 +92,16 @@ type rtprio struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type lwpparams struct {
|
type lwpparams struct {
|
||||||
_type unsafe.Pointer
|
start_func uintptr
|
||||||
arg *byte
|
arg unsafe.Pointer
|
||||||
stack *byte
|
stack uintptr
|
||||||
tid1 *int32
|
tid1 unsafe.Pointer // *int32
|
||||||
tid2 *int32
|
tid2 unsafe.Pointer // *int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type sigaltstackt struct {
|
type sigaltstackt struct {
|
||||||
ss_sp *int8
|
ss_sp uintptr
|
||||||
ss_size uint32
|
ss_size uintptr
|
||||||
ss_flags int32
|
ss_flags int32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,8 +110,8 @@ type sigset struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type stackt struct {
|
type stackt struct {
|
||||||
ss_sp *int8
|
ss_sp uintptr
|
||||||
ss_size uint32
|
ss_size uintptr
|
||||||
ss_flags int32
|
ss_flags int32
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,39 +122,39 @@ type siginfo struct {
|
|||||||
si_pid int32
|
si_pid int32
|
||||||
si_uid uint32
|
si_uid uint32
|
||||||
si_status int32
|
si_status int32
|
||||||
si_addr *byte
|
si_addr uintptr
|
||||||
si_value [4]byte
|
si_value [4]byte
|
||||||
si_band int32
|
si_band int32
|
||||||
__spare__ [7]int32
|
__spare__ [7]int32
|
||||||
}
|
}
|
||||||
|
|
||||||
type mcontext struct {
|
type mcontext struct {
|
||||||
mc_onstack int32
|
mc_onstack uint32
|
||||||
mc_gs int32
|
mc_gs uint32
|
||||||
mc_fs int32
|
mc_fs uint32
|
||||||
mc_es int32
|
mc_es uint32
|
||||||
mc_ds int32
|
mc_ds uint32
|
||||||
mc_edi int32
|
mc_edi uint32
|
||||||
mc_esi int32
|
mc_esi uint32
|
||||||
mc_ebp int32
|
mc_ebp uint32
|
||||||
mc_isp int32
|
mc_isp uint32
|
||||||
mc_ebx int32
|
mc_ebx uint32
|
||||||
mc_edx int32
|
mc_edx uint32
|
||||||
mc_ecx int32
|
mc_ecx uint32
|
||||||
mc_eax int32
|
mc_eax uint32
|
||||||
mc_xflags int32
|
mc_xflags uint32
|
||||||
mc_trapno int32
|
mc_trapno uint32
|
||||||
mc_err int32
|
mc_err uint32
|
||||||
mc_eip int32
|
mc_eip uint32
|
||||||
mc_cs int32
|
mc_cs uint32
|
||||||
mc_eflags int32
|
mc_eflags uint32
|
||||||
mc_esp int32
|
mc_esp uint32
|
||||||
mc_ss int32
|
mc_ss uint32
|
||||||
mc_len int32
|
mc_len uint32
|
||||||
mc_fpformat int32
|
mc_fpformat uint32
|
||||||
mc_ownedfp int32
|
mc_ownedfp uint32
|
||||||
mc_fpregs [128]int32
|
mc_fpregs [128]uint32
|
||||||
__spare__ [16]int32
|
__spare__ [16]uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
type ucontext struct {
|
type ucontext struct {
|
||||||
@ -170,11 +170,19 @@ type timespec struct {
|
|||||||
tv_nsec int32
|
tv_nsec int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ts *timespec) set_sec(x int64) {
|
||||||
|
ts.tv_sec = int32(x)
|
||||||
|
}
|
||||||
|
|
||||||
type timeval struct {
|
type timeval struct {
|
||||||
tv_sec int32
|
tv_sec int32
|
||||||
tv_usec int32
|
tv_usec int32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tv *timeval) set_usec(x int32) {
|
||||||
|
tv.tv_usec = x
|
||||||
|
}
|
||||||
|
|
||||||
type itimerval struct {
|
type itimerval struct {
|
||||||
it_interval timeval
|
it_interval timeval
|
||||||
it_value 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
|
// loop if gp->atomicstatus is in a scan state giving
|
||||||
// GC time to finish and change the state to oldval.
|
// GC time to finish and change the state to oldval.
|
||||||
for !cas(&gp.atomicstatus, oldval, newval) {
|
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")
|
gothrow("stack overflow")
|
||||||
}
|
}
|
||||||
|
|
||||||
oldstatus := readgstatus(gp)
|
casgstatus(gp, _Gwaiting, _Gcopystack)
|
||||||
oldstatus &^= _Gscan
|
|
||||||
casgstatus(gp, oldstatus, _Gcopystack) // oldstatus is Gwaiting or Grunnable
|
|
||||||
|
|
||||||
// The concurrent GC will not scan the stack while we are doing the copy since
|
// The concurrent GC will not scan the stack while we are doing the copy since
|
||||||
// the gp is in a Gcopystack status.
|
// the gp is in a Gcopystack status.
|
||||||
@ -789,15 +787,7 @@ func shrinkstack(gp *g) {
|
|||||||
print("shrinking stack ", oldsize, "->", newsize, "\n")
|
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
|
oldstatus := casgcopystack(gp)
|
||||||
// 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)
|
|
||||||
copystack(gp, newsize)
|
copystack(gp, newsize)
|
||||||
casgstatus(gp, _Gcopystack, oldstatus)
|
casgstatus(gp, _Gcopystack, oldstatus)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user