liblink: fail for too-large register offset constants
Previously, liblink would silently truncate register offset constants
to 32 bits. For example,
MOVD $0x200000004(R2),R3
would assemble to
addis r31,r2,0
addi r3,r31,4
To fix this, limit C_LACON to 32 bit (signed) offsets and introduce a
new C_DACON operand type for larger register offsets. We don't
implement this currently, but at least liblink will now give an error
if it encounters an address like this.
Change-Id: I8e87def8cc4cc5b75498b0fb543ac7666cf2964e
Reviewed-on: https://go-review.googlesource.com/1758
Reviewed-by: Minux Ma <minux@golang.org>
diff --git a/src/cmd/9l/9.out.h b/src/cmd/9l/9.out.h
index 016163b..e7601f0 100644
--- a/src/cmd/9l/9.out.h
+++ b/src/cmd/9l/9.out.h
@@ -101,15 +101,16 @@
C_SPR, /* special processor register */
C_ZCON,
C_SCON, /* 16 bit signed */
- C_UCON, /* low 16 bits 0 */
+ C_UCON, /* 32 bit signed, low 16 bits 0 */
C_ADDCON, /* -0x8000 <= v < 0 */
C_ANDCON, /* 0 < v <= 0xFFFF */
C_LCON, /* other 32 */
C_DCON, /* other 64 (could subdivide further) */
- C_SACON, /* $n(REG) where n is small */
+ C_SACON, /* $n(REG) where n <= int16 */
C_SECON,
- C_LACON, /* $n(REG) where n is large */
+ C_LACON, /* $n(REG) where int16 < n <= int32 */
C_LECON,
+ C_DACON, /* $n(REG) where int32 < n */
C_SBRA,
C_LBRA,
C_SAUTO,
diff --git a/src/liblink/asm9.c b/src/liblink/asm9.c
index 5a37927..64fc651 100644
--- a/src/liblink/asm9.c
+++ b/src/liblink/asm9.c
@@ -683,7 +683,9 @@
if(a->reg != NREG) {
if(-BIG <= ctxt->instoffset && ctxt->instoffset <= BIG)
return C_SACON;
- return C_LACON;
+ if(isint32(ctxt->instoffset))
+ return C_LACON;
+ return C_DACON;
}
consize:
if(ctxt->instoffset >= 0) {
@@ -1800,12 +1802,10 @@
if(p->to.reg == REGTMP)
ctxt->diag("can't synthesize large constant\n%P", p);
v = regoff(ctxt, &p->from);
- if(v & 0x8000L)
- v += 0x10000L;
r = p->from.reg;
if(r == NREG)
r = o->param;
- o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+ o1 = AOP_IRR(OP_ADDIS, REGTMP, r, high16adjusted(v));
o2 = AOP_IRR(OP_ADDI, p->to.reg, REGTMP, v);
break;
@@ -1913,34 +1913,28 @@
case 35: /* mov r,lext/lauto/loreg ==> cau $(v>>16),sb,r'; store o(r') */
v = regoff(ctxt, &p->to);
- if(v & 0x8000L)
- v += 0x10000L;
r = p->to.reg;
if(r == NREG)
r = o->param;
- o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+ o1 = AOP_IRR(OP_ADDIS, REGTMP, r, high16adjusted(v));
o2 = AOP_IRR(opstore(ctxt, p->as), p->from.reg, REGTMP, v);
break;
case 36: /* mov bz/h/hz lext/lauto/lreg,r ==> lbz/lha/lhz etc */
v = regoff(ctxt, &p->from);
- if(v & 0x8000L)
- v += 0x10000L;
r = p->from.reg;
if(r == NREG)
r = o->param;
- o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+ o1 = AOP_IRR(OP_ADDIS, REGTMP, r, high16adjusted(v));
o2 = AOP_IRR(opload(ctxt, p->as), p->to.reg, REGTMP, v);
break;
case 37: /* movb lext/lauto/lreg,r ==> lbz o(reg),r; extsb r */
v = regoff(ctxt, &p->from);
- if(v & 0x8000L)
- v += 0x10000L;
r = p->from.reg;
if(r == NREG)
r = o->param;
- o1 = AOP_IRR(OP_ADDIS, REGTMP, r, v>>16);
+ o1 = AOP_IRR(OP_ADDIS, REGTMP, r, high16adjusted(v));
o2 = AOP_IRR(opload(ctxt, p->as), p->to.reg, REGTMP, v);
o3 = LOP_RRR(OP_EXTSB, p->to.reg, p->to.reg, 0);
break;