diff --git a/src/cmd/6g/peep.c b/src/cmd/6g/peep.c index 51f47221490..22eb8dfa712 100644 --- a/src/cmd/6g/peep.c +++ b/src/cmd/6g/peep.c @@ -34,6 +34,7 @@ #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*); @@ -48,11 +49,17 @@ needc(Prog *p) 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: @@ -129,6 +136,9 @@ peep(void) } } + // byte, word arithmetic elimination. + elimshortmov(r); + // constant propagation // find MOV $con,R followed by // another MOV $con,R without @@ -448,6 +458,99 @@ regtyp(Adr *a) 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) { diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index ca54b862792..4e4261804cc 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -1582,8 +1582,9 @@ gmove(Node *f, Node *t) 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); diff --git a/src/cmd/8g/peep.c b/src/cmd/8g/peep.c index 38674d02a14..91e7bdecdd8 100644 --- a/src/cmd/8g/peep.c +++ b/src/cmd/8g/peep.c @@ -36,6 +36,7 @@ #define REGEXT 0 static void conprop(Reg *r); +static void elimshortmov(Reg *r); // do we need the carry bit static int @@ -45,9 +46,15 @@ needc(Prog *p) 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: @@ -122,25 +129,9 @@ peep(void) 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 @@ -173,8 +164,6 @@ loop1: 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)) { @@ -205,7 +194,6 @@ loop1: } break; - case AADDB: case AADDL: case AADDW: if(p->from.type != D_CONST || needc(p->link)) @@ -228,7 +216,6 @@ loop1: } break; - case ASUBB: case ASUBL: case ASUBW: if(p->from.type != D_CONST || needc(p->link)) @@ -315,6 +302,99 @@ regtyp(Adr *a) 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 @@ -407,8 +487,6 @@ subprop(Reg *r0) case AMOVSL: return 0; - case AMOVB: - case AMOVW: case AMOVL: if(p->to.type == v1->type) goto gotit; @@ -589,8 +667,6 @@ copyu(Prog *p, Adr *v, Adr *s) case ANOP: /* rhs store */ - case AMOVB: - case AMOVW: case AMOVL: case AMOVBLSX: case AMOVBLZX: @@ -655,6 +731,8 @@ copyu(Prog *p, Adr *v, Adr *s) case AXORB: case AXORL: case AXORW: + case AMOVB: + case AMOVW: if(copyas(&p->to, v)) return 2; goto caseread; diff --git a/test/bugs/bug440.go b/test/fixedbugs/bug440_32.go similarity index 65% rename from test/bugs/bug440.go rename to test/fixedbugs/bug440_32.go index 816a18c5806..2d26fbb90ab 100644 --- a/test/bugs/bug440.go +++ b/test/fixedbugs/bug440_32.go @@ -1,16 +1,16 @@ -// $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) } } diff --git a/test/fixedbugs/bug440_64.go b/test/fixedbugs/bug440_64.go new file mode 100644 index 00000000000..3ab3e565da0 --- /dev/null +++ b/test/fixedbugs/bug440_64.go @@ -0,0 +1,21 @@ +// 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 +}