#include "opt.h"
static void conprop(Reg *r);
+static void elimshortmov(Reg *r);
static int prevl(Reg *r, int reg);
static void pushback(Reg *r);
static int regconsttyp(Adr*);
case AADCQ:
case ASBBL:
case ASBBQ:
+ case ARCRB:
+ case ARCRW:
case ARCRL:
case ARCRQ:
return 1;
+ case AADDB:
+ case AADDW:
case AADDL:
case AADDQ:
+ case ASUBB:
+ case ASUBW:
case ASUBL:
case ASUBQ:
case AJMP:
}
}
+ // byte, word arithmetic elimination.
+ elimshortmov(r);
+
// constant propagation
// find MOV $con,R followed by
// another MOV $con,R without
return 0;
}
+// movb elimination.
+// movb is simulated by the linker
+// when a register other than ax, bx, cx, dx
+// is used, so rewrite to other instructions
+// when possible. a movb into a register
+// can smash the entire 32-bit register without
+// causing any trouble.
+static void
+elimshortmov(Reg *r)
+{
+ Prog *p;
+
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(regtyp(&p->to)) {
+ switch(p->as) {
+ case AINCB:
+ case AINCW:
+ p->as = AINCQ;
+ break;
+ case ADECB:
+ case ADECW:
+ p->as = ADECQ;
+ break;
+ case ANEGB:
+ case ANEGW:
+ p->as = ANEGQ;
+ break;
+ case ANOTB:
+ case ANOTW:
+ p->as = ANOTQ;
+ break;
+ }
+ if(regtyp(&p->from) || p->from.type == D_CONST) {
+ // move or artihmetic into partial register.
+ // from another register or constant can be movl.
+ // we don't switch to 64-bit arithmetic if it can
+ // change how the carry bit is set (and the carry bit is needed).
+ switch(p->as) {
+ case AMOVB:
+ case AMOVW:
+ p->as = AMOVQ;
+ break;
+ case AADDB:
+ case AADDW:
+ if(!needc(p->link))
+ p->as = AADDQ;
+ break;
+ case ASUBB:
+ case ASUBW:
+ if(!needc(p->link))
+ p->as = ASUBQ;
+ break;
+ case AMULB:
+ case AMULW:
+ p->as = AMULQ;
+ break;
+ case AIMULB:
+ case AIMULW:
+ p->as = AIMULQ;
+ break;
+ case AANDB:
+ case AANDW:
+ p->as = AANDQ;
+ break;
+ case AORB:
+ case AORW:
+ p->as = AORQ;
+ break;
+ case AXORB:
+ case AXORW:
+ p->as = AXORQ;
+ break;
+ case ASHLB:
+ case ASHLW:
+ p->as = ASHLQ;
+ break;
+ }
+ } else {
+ // explicit zero extension
+ switch(p->as) {
+ case AMOVB:
+ p->as = AMOVBQZX;
+ break;
+ case AMOVW:
+ p->as = AMOVWQZX;
+ break;
+ }
+ }
+ }
+ }
+}
+
int
regconsttyp(Adr *a)
{
p1 = gins(ASHRL, ncon(1), &ax);
p1->from.index = D_DX; // double-width shift DX -> AX
p1->from.scale = 0;
+ gins(AMOVL, ncon(0), &cx);
gins(ASETCC, N, &cx);
- gins(AORB, &cx, &ax);
+ gins(AORL, &cx, &ax);
gins(ASHRL, ncon(1), &dx);
gmove(&dx, &thi);
gmove(&ax, &tlo);
#define REGEXT 0
static void conprop(Reg *r);
+static void elimshortmov(Reg *r);
// do we need the carry bit
static int
switch(p->as) {
case AADCL:
case ASBBL:
+ case ARCRB:
+ case ARCRW:
case ARCRL:
return 1;
+ case AADDB:
+ case AADDW:
case AADDL:
+ case ASUBB:
+ case ASUBW:
case ASUBL:
case AJMP:
case ARET:
p = p->link;
}
}
-
- // movb elimination.
- // movb is simulated by the linker
- // when a register other than ax, bx, cx, dx
- // is used, so rewrite to other instructions
- // when possible. a movb into a register
- // can smash the entire 32-bit register without
- // causing any trouble.
- for(r=firstr; r!=R; r=r->link) {
- p = r->prog;
- if(p->as == AMOVB && regtyp(&p->to)) {
- // movb into register.
- // from another register or constant can be movl.
- if(regtyp(&p->from) || p->from.type == D_CONST)
- p->as = AMOVL;
- else
- p->as = AMOVBLZX;
- }
- }
+
+ // byte, word arithmetic elimination.
+ elimshortmov(r);
// constant propagation
// find MOV $con,R followed by
for(r=firstr; r!=R; r=r->link) {
p = r->prog;
switch(p->as) {
- case AMOVB:
- case AMOVW:
case AMOVL:
if(regtyp(&p->to))
if(regtyp(&p->from)) {
}
break;
- case AADDB:
case AADDL:
case AADDW:
if(p->from.type != D_CONST || needc(p->link))
}
break;
- case ASUBB:
case ASUBL:
case ASUBW:
if(p->from.type != D_CONST || needc(p->link))
return 0;
}
+// movb elimination.
+// movb is simulated by the linker
+// when a register other than ax, bx, cx, dx
+// is used, so rewrite to other instructions
+// when possible. a movb into a register
+// can smash the entire 64-bit register without
+// causing any trouble.
+static void
+elimshortmov(Reg *r)
+{
+ Prog *p;
+
+ for(r=firstr; r!=R; r=r->link) {
+ p = r->prog;
+ if(regtyp(&p->to)) {
+ switch(p->as) {
+ case AINCB:
+ case AINCW:
+ p->as = AINCL;
+ break;
+ case ADECB:
+ case ADECW:
+ p->as = ADECL;
+ break;
+ case ANEGB:
+ case ANEGW:
+ p->as = ANEGL;
+ break;
+ case ANOTB:
+ case ANOTW:
+ p->as = ANOTL;
+ break;
+ }
+ if(regtyp(&p->from) || p->from.type == D_CONST) {
+ // move or artihmetic into partial register.
+ // from another register or constant can be movl.
+ // we don't switch to 32-bit arithmetic if it can
+ // change how the carry bit is set (and the carry bit is needed).
+ switch(p->as) {
+ case AMOVB:
+ case AMOVW:
+ p->as = AMOVL;
+ break;
+ case AADDB:
+ case AADDW:
+ if(!needc(p->link))
+ p->as = AADDL;
+ break;
+ case ASUBB:
+ case ASUBW:
+ if(!needc(p->link))
+ p->as = ASUBL;
+ break;
+ case AMULB:
+ case AMULW:
+ p->as = AMULL;
+ break;
+ case AIMULB:
+ case AIMULW:
+ p->as = AIMULL;
+ break;
+ case AANDB:
+ case AANDW:
+ p->as = AANDL;
+ break;
+ case AORB:
+ case AORW:
+ p->as = AORL;
+ break;
+ case AXORB:
+ case AXORW:
+ p->as = AXORL;
+ break;
+ case ASHLB:
+ case ASHLW:
+ p->as = ASHLL;
+ break;
+ }
+ } else {
+ // explicit zero extension
+ switch(p->as) {
+ case AMOVB:
+ p->as = AMOVBLZX;
+ break;
+ case AMOVW:
+ p->as = AMOVWLZX;
+ break;
+ }
+ }
+ }
+ }
+}
+
/*
* the idea is to substitute
* one register for another
case AMOVSL:
return 0;
- case AMOVB:
- case AMOVW:
case AMOVL:
if(p->to.type == v1->type)
goto gotit;
case ANOP: /* rhs store */
- case AMOVB:
- case AMOVW:
case AMOVL:
case AMOVBLSX:
case AMOVBLZX:
case AXORB:
case AXORL:
case AXORW:
+ case AMOVB:
+ case AMOVW:
if(copyas(&p->to, v))
return 2;
goto caseread;
-// $G $D/$F.go && $L $F.$A && ./$A.out
-// # switch above to 'run' when bug gets fixed.
-// # right now it only breaks on 8g
+// run
// Test for 8g register move bug. The optimizer gets confused
// about 16- vs 32-bit moves during splitContractIndex.
+// Issue 3910.
+
package main
func main() {
const c = 0x12345678
index, n, offset := splitContractIndex(c)
- if index != int((c&0xffff)>>5) || n != int(c & (1<<5-1)) || offset != (c>>16)&(1<<14-1) {
+ if index != int((c&0xffff)>>5) || n != int(c&(1<<5-1)) || offset != (c>>16)&(1<<14-1) {
println("BUG", index, n, offset)
}
}
--- /dev/null
+// run
+
+// Test for 6g register move bug. The optimizer gets confused
+// about 32- vs 64-bit moves during splitContractIndex.
+
+// Issue 3918.
+
+package main
+
+func main() {
+ const c = 0x123400005678
+ index, offset := splitContractIndex(c)
+ if index != (c&0xffffffff)>>5 || offset != c+1 {
+ println("BUG", index, offset)
+ }
+}
+
+func splitContractIndex(ce uint64) (index uint32, offset uint64) {
+ h := uint32(ce)
+ return h >> 5, ce + 1
+}