]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.ssa] cmd/internal/obj/arm64: fix encoding constant into some instructions
authorCherry Zhang <cherryyz@google.com>
Wed, 3 Aug 2016 15:47:40 +0000 (11:47 -0400)
committerCherry Zhang <cherryyz@google.com>
Wed, 10 Aug 2016 20:33:11 +0000 (20:33 +0000)
When a constant can be encoded in a logical instruction (BITCON), do
it this way instead of using the constant pool. The BITCON testing
code runs faster than table lookup (using map):

(on AMD64 machine, with pseudo random input)
BenchmarkIsBitcon-4    300000000          4.04 ns/op
BenchmarkTable-4       50000000         27.3 ns/op

The equivalent C code of BITCON testing is formally verified with
model checker CBMC against linear search of the lookup table.

Also handle cases when a constant can be encoded in a MOV instruction.
In this case, materializa the constant into REGTMP without using the
constant pool.

When constants need to be added to the constant pool, make sure to
check whether it fits in 32-bit. If not, store 64-bit.

Both legacy and SSA compiler backends are happy with this.

Fixes #16226.

Change-Id: I883e3069dee093a1cdc40853c42221a198a152b0
Reviewed-on: https://go-review.googlesource.com/26631
Run-TryBot: Cherry Zhang <cherryyz@google.com>
Reviewed-by: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/internal/obj/arm64/a.out.go
src/cmd/internal/obj/arm64/anames7.go
src/cmd/internal/obj/arm64/asm7.go
src/cmd/internal/obj/arm64/obj7.go

index ab0589495055da59f44403fa8d0bd3ca202c63ba..ad52be66b8dfd880201aa3a845efd06abf5cb67d 100644 (file)
@@ -274,6 +274,7 @@ const (
        C_ADDCON   // 12-bit unsigned, shifted left by 0 or 12
        C_MOVCON   // generated by a 16-bit constant, optionally inverted and/or shifted by multiple of 16
        C_BITCON   // bitfield and logical immediate masks
+       C_ABCON0   // could be C_ADDCON0 or C_BITCON
        C_ABCON    // could be C_ADDCON or C_BITCON
        C_MBCON    // could be C_MOVCON or C_BITCON
        C_LCON     // 32-bit constant
index eb348d4850d2f0a716719addb356ff8770effe45..d55b34f9ae9988b33e9c68fffb68cc29e42bbad8 100644 (file)
@@ -20,6 +20,7 @@ var cnames7 = []string{
        "ADDCON",
        "MOVCON",
        "BITCON",
+       "ABCON0",
        "ABCON",
        "MBCON",
        "LCON",
index 28bebaa3f7d7fda3d6de02e55a0d1bba1117b96b..fbf037859169c2393b44c97e67bd71a46e09f57f 100644 (file)
@@ -161,10 +161,12 @@ var optab = []Optab{
        {AADD, C_ADDCON, C_RSP, C_RSP, 2, 4, 0, 0, 0},
        {AADD, C_ADDCON, C_NONE, C_RSP, 2, 4, 0, 0, 0},
        {ACMP, C_ADDCON, C_RSP, C_NONE, 2, 4, 0, 0, 0},
-       // TODO: these don't work properly.
-       // {AADD, C_MBCON, C_RSP, C_RSP, 2, 4, 0, 0, 0},
-       // {AADD, C_MBCON, C_NONE, C_RSP, 2, 4, 0, 0, 0},
-       // {ACMP, C_MBCON, C_RSP, C_NONE, 2, 4, 0, 0, 0},
+       {AADD, C_MOVCON, C_RSP, C_RSP, 62, 8, 0, 0, 0},
+       {AADD, C_MOVCON, C_NONE, C_RSP, 62, 8, 0, 0, 0},
+       {ACMP, C_MOVCON, C_RSP, C_NONE, 62, 8, 0, 0, 0},
+       {AADD, C_BITCON, C_RSP, C_RSP, 62, 8, 0, 0, 0},
+       {AADD, C_BITCON, C_NONE, C_RSP, 62, 8, 0, 0, 0},
+       {ACMP, C_BITCON, C_RSP, C_NONE, 62, 8, 0, 0, 0},
        {AADD, C_VCON, C_RSP, C_RSP, 13, 8, 0, LFROM, 0},
        {AADD, C_VCON, C_NONE, C_RSP, 13, 8, 0, LFROM, 0},
        {ACMP, C_VCON, C_REG, C_NONE, 13, 8, 0, LFROM, 0},
@@ -188,11 +190,14 @@ var optab = []Optab{
        {AAND, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
        {ABIC, C_REG, C_REG, C_REG, 1, 4, 0, 0, 0},
        {ABIC, C_REG, C_NONE, C_REG, 1, 4, 0, 0, 0},
-       // TODO: these don't work properly.
-       // {AAND, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
-       // {AAND, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
-       // {ABIC, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
-       // {ABIC, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+       {AAND, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
+       {AAND, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+       {ABIC, C_BITCON, C_REG, C_REG, 53, 4, 0, 0, 0},
+       {ABIC, C_BITCON, C_NONE, C_REG, 53, 4, 0, 0, 0},
+       {AAND, C_MOVCON, C_REG, C_REG, 62, 8, 0, 0, 0},
+       {AAND, C_MOVCON, C_NONE, C_REG, 62, 8, 0, 0, 0},
+       {ABIC, C_MOVCON, C_REG, C_REG, 62, 8, 0, 0, 0},
+       {ABIC, C_MOVCON, C_NONE, C_REG, 62, 8, 0, 0, 0},
        {AAND, C_VCON, C_REG, C_REG, 28, 8, 0, LFROM, 0},
        {AAND, C_VCON, C_NONE, C_REG, 28, 8, 0, LFROM, 0},
        {ABIC, C_VCON, C_REG, C_REG, 28, 8, 0, LFROM, 0},
@@ -216,8 +221,8 @@ var optab = []Optab{
        // TODO: these don't work properly.
        // { AMOVW,             C_ADDCON,       C_NONE, C_REG,          2, 4, 0 , 0},
        // { AMOVD,             C_ADDCON,       C_NONE, C_REG,          2, 4, 0 , 0},
-       // { AMOVW,             C_BITCON,       C_NONE, C_REG,          53, 4, 0 , 0},
-       // { AMOVD,             C_BITCON,       C_NONE, C_REG,          53, 4, 0 , 0},
+       {AMOVW, C_BITCON, C_NONE, C_REG, 32, 4, 0, 0, 0},
+       {AMOVD, C_BITCON, C_NONE, C_REG, 32, 4, 0, 0, 0},
 
        {AMOVK, C_VCON, C_NONE, C_REG, 33, 4, 0, 0, 0},
        {AMOVD, C_AACON, C_NONE, C_REG, 4, 4, REGFROM, 0, 0},
@@ -715,15 +720,18 @@ func flushpool(ctxt *obj.Link, p *obj.Prog, skip int) {
  */
 func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
        c := aclass(ctxt, a)
+       lit := ctxt.Instoffset
        t := *ctxt.NewProg()
        t.As = AWORD
        sz := 4
 
-       // MOVW foo(SB), R is actually
-       //      MOV addr, REGTEMP
-       //      MOVW REGTEMP, R
+       // MOVD foo(SB), R is actually
+       //      MOVD addr, REGTMP
+       //      MOVD REGTMP, R
        // where addr is the address of the DWORD containing the address of foo.
-       if p.As == AMOVD || c == C_ADDR || c == C_VCON {
+       if p.As == AMOVD || c == C_ADDR || c == C_VCON || int64(lit) != int64(int32(lit)) || uint64(lit) != uint64(uint32(lit)) {
+               // conservative: don't know if we want signed or unsigned extension.
+               // in case of ambiguity, store 64-bit
                t.As = ADWORD
                sz = 8
        }
@@ -740,29 +748,12 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
                t.To.Type = a.Type
                t.To.Name = a.Name
 
-               /* This is here to work around a bug where we generate negative
-               operands that match C_MOVCON, but we use them with
-               instructions that only accept unsigned immediates. This
-               will cause oplook to return a variant of the instruction
-               that loads the negative constant from memory, rather than
-               using the immediate form. Because of that load, we get here,
-               so we need to know what to do with C_MOVCON.
-
-               The correct fix is to use the "negation" instruction variant,
-               e.g. CMN $1, R instead of CMP $-1, R, or SUB $1, R instead
-               of ADD $-1, R. */
-       case C_MOVCON,
-
-               /* This is here because MOV uint12<<12, R is disabled in optab.
-               Because of this, we need to load the constant from memory. */
-               C_ADDCON,
-
-               /* These are here because they are disabled in optab.
-               Because of this, we need to load the constant from memory. */
-               C_BITCON,
-               C_ABCON,
-               C_MBCON,
-               C_PSAUTO,
+       /* This is here because MOV uint12<<12, R is disabled in optab.
+       Because of this, we need to load the constant from memory. */
+       case C_ADDCON:
+               fallthrough
+
+       case C_PSAUTO,
                C_PPAUTO,
                C_UAUTO4K,
                C_UAUTO8K,
@@ -790,7 +781,7 @@ func addpool(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
                }
 
                t.To.Type = obj.TYPE_CONST
-               t.To.Offset = ctxt.Instoffset
+               t.To.Offset = lit
                break
        }
 
@@ -844,11 +835,137 @@ func isaddcon(v int64) bool {
        return v <= 0xFFF
 }
 
-func isbitcon(v uint64) bool {
-       /*  fancy bimm32 or bimm64? */
-       // TODO(aram):
-       return false
-       // return findmask(v) != nil || (v>>32) == 0 && findmask(v|(v<<32)) != nil
+// isbitcon returns whether a constant can be encoded into a logical instruction.
+// bitcon has a binary form of repetition of a bit sequence of length 2, 4, 8, 16, 32, or 64,
+// which itself is a rotate (w.r.t. the length of the unit) of a sequence of ones.
+// special cases: 0 and -1 are not bitcon.
+// this function needs to run against virtually all the constants, so it needs to be fast.
+// for this reason, bitcon testing and bitcon encoding are separate functions.
+func isbitcon(x uint64) bool {
+       if x == 1<<64-1 || x == 0 {
+               return false
+       }
+       // determine the period and sign-extend a unit to 64 bits
+       switch {
+       case x != x>>32|x<<32:
+               // period is 64
+               // nothing to do
+       case x != x>>16|x<<48:
+               // period is 32
+               x = uint64(int64(int32(x)))
+       case x != x>>8|x<<56:
+               // period is 16
+               x = uint64(int64(int16(x)))
+       case x != x>>4|x<<60:
+               // period is 8
+               x = uint64(int64(int8(x)))
+       default:
+               // period is 4 or 2, always true
+               // 0001, 0010, 0100, 1000 -- 0001 rotate
+               // 0011, 0110, 1100, 1001 -- 0011 rotate
+               // 0111, 1011, 1101, 1110 -- 0111 rotate
+               // 0101, 1010             -- 01   rotate, repeat
+               return true
+       }
+       return sequenceOfOnes(x) || sequenceOfOnes(^x)
+}
+
+// sequenceOfOnes tests whether a constant is a sequence of ones in binary, with leading and trailing zeros
+func sequenceOfOnes(x uint64) bool {
+       y := x & -x // lowest set bit of x. x is good iff x+y is a power of 2
+       y += x
+       return (y-1)&y == 0
+}
+
+// bitconEncode returns the encoding of a bitcon used in logical instructions
+// x is known to be a bitcon
+// a bitcon is a sequence of n ones at low bits (i.e. 1<<n-1), right rotated
+// by R bits, and repeated with period of 64, 32, 16, 8, 4, or 2.
+// it is encoded in logical instructions with 3 bitfields
+// N (1 bit) : R (6 bits) : S (6 bits), where
+// N=1           -- period=64
+// N=0, S=0xxxxx -- period=32
+// N=0, S=10xxxx -- period=16
+// N=0, S=110xxx -- period=8
+// N=0, S=1110xx -- period=4
+// N=0, S=11110x -- period=2
+// R is the shift amount, low bits of S = n-1
+func bitconEncode(x uint64, mode int) uint32 {
+       var period uint32
+       // determine the period and sign-extend a unit to 64 bits
+       switch {
+       case x != x>>32|x<<32:
+               period = 64
+       case x != x>>16|x<<48:
+               period = 32
+               x = uint64(int64(int32(x)))
+       case x != x>>8|x<<56:
+               period = 16
+               x = uint64(int64(int16(x)))
+       case x != x>>4|x<<60:
+               period = 8
+               x = uint64(int64(int8(x)))
+       case x != x>>2|x<<62:
+               period = 4
+               x = uint64(int64(x<<60) >> 60)
+       default:
+               period = 2
+               x = uint64(int64(x<<62) >> 62)
+       }
+       neg := false
+       if int64(x) < 0 {
+               x = ^x
+               neg = true
+       }
+       y := x & -x // lowest set bit of x.
+       s := log2(y)
+       n := log2(x+y) - s // x (or ^x) is a sequence of n ones left shifted by s bits
+       if neg {
+               // ^x is a sequence of n ones left shifted by s bits
+               // adjust n, s for x
+               s = n + s
+               n = period - n
+       }
+
+       N := uint32(0)
+       if mode == 64 && period == 64 {
+               N = 1
+       }
+       R := (period - s) & (period - 1) & uint32(mode-1) // shift amount of right rotate
+       S := (n - 1) | 63&^(period<<1-1)                  // low bits = #ones - 1, high bits encodes period
+       return N<<22 | R<<16 | S<<10
+}
+
+func log2(x uint64) uint32 {
+       if x == 0 {
+               panic("log2 of 0")
+       }
+       n := uint32(0)
+       if x >= 1<<32 {
+               x >>= 32
+               n += 32
+       }
+       if x >= 1<<16 {
+               x >>= 16
+               n += 16
+       }
+       if x >= 1<<8 {
+               x >>= 8
+               n += 8
+       }
+       if x >= 1<<4 {
+               x >>= 4
+               n += 4
+       }
+       if x >= 1<<2 {
+               x >>= 2
+               n += 2
+       }
+       if x >= 1<<1 {
+               x >>= 1
+               n += 1
+       }
+       return n
 }
 
 func autoclass(l int64) int {
@@ -1019,6 +1136,9 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
                        }
                        if isaddcon(v) {
                                if v <= 0xFFF {
+                                       if isbitcon(uint64(v)) {
+                                               return C_ABCON0
+                                       }
                                        return C_ADDCON0
                                }
                                if isbitcon(uint64(v)) {
@@ -1085,6 +1205,10 @@ func aclass(ctxt *obj.Link, a *obj.Addr) int {
        return C_GOK
 }
 
+func oclass(a *obj.Addr) int {
+       return int(a.Class) - 1
+}
+
 func oplook(ctxt *obj.Link, p *obj.Prog) *Optab {
        a1 := int(p.Optab)
        if a1 != 0 {
@@ -1151,17 +1275,17 @@ func cmp(a int, b int) bool {
                }
 
        case C_ADDCON0:
-               if b == C_ZCON {
+               if b == C_ZCON || b == C_ABCON0 {
                        return true
                }
 
        case C_ADDCON:
-               if b == C_ZCON || b == C_ADDCON0 || b == C_ABCON {
+               if b == C_ZCON || b == C_ABCON0 || b == C_ADDCON0 || b == C_ABCON {
                        return true
                }
 
        case C_BITCON:
-               if b == C_ABCON || b == C_MBCON {
+               if b == C_ABCON0 || b == C_ABCON || b == C_MBCON {
                        return true
                }
 
@@ -1171,7 +1295,7 @@ func cmp(a int, b int) bool {
                }
 
        case C_LCON:
-               if b == C_ZCON || b == C_BITCON || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_MBCON || b == C_MOVCON {
+               if b == C_ZCON || b == C_BITCON || b == C_ADDCON || b == C_ADDCON0 || b == C_ABCON || b == C_ABCON0 || b == C_MBCON || b == C_MOVCON {
                        return true
                }
 
@@ -2306,34 +2430,7 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
                o2 = olsr12u(ctxt, int32(opldr12(ctxt, p.As)), ((v-hi)>>uint(s))&0xFFF, REGTMP, int(p.To.Reg))
 
        case 32: /* mov $con, R -> movz/movn */
-               r := 32
-
-               if p.As == AMOVD {
-                       r = 64
-               }
-               d := p.From.Offset
-               s := movcon(d)
-               if s < 0 || s >= r {
-                       d = ^d
-                       s = movcon(d)
-                       if s < 0 || s >= r {
-                               ctxt.Diag("impossible move wide: %#x\n%v", uint64(p.From.Offset), p)
-                       }
-                       if p.As == AMOVD {
-                               o1 = opirr(ctxt, AMOVN)
-                       } else {
-                               o1 = opirr(ctxt, AMOVNW)
-                       }
-               } else {
-                       if p.As == AMOVD {
-                               o1 = opirr(ctxt, AMOVZ)
-                       } else {
-                               o1 = opirr(ctxt, AMOVZW)
-                       }
-               }
-
-               rt := int(p.To.Reg)
-               o1 |= uint32((((d >> uint(s*16)) & 0xFFFF) << 5) | int64((uint32(s)&3)<<21) | int64(rt&31))
+               o1 = omovconst(ctxt, p.As, p, &p.From, int(p.To.Reg))
 
        case 33: /* movk $uimm16 << pos */
                o1 = opirr(ctxt, p.As)
@@ -2601,8 +2698,26 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
                o1 |= uint32((p.From.Offset & 0x7F) << 5)
 
-       case 53: /* and/or/eor/bic/... $bimmN, Rn, Rd -> op (N,r,s), Rn, Rd */
-               ctxt.Diag("bitmask immediate not implemented\n%v", p)
+       case 53: /* and/or/eor/bic/... $bitcon, Rn, Rd */
+               a := p.As
+               rt := int(p.To.Reg)
+               r := int(p.Reg)
+               if r == 0 {
+                       r = rt
+               }
+               mode := 64
+               v := uint64(p.From.Offset)
+               switch p.As {
+               case AANDW, AORRW, AEORW, AANDSW:
+                       mode = 32
+               case ABIC, AORN, AEON, ABICS:
+                       v = ^v
+               case ABICW, AORNW, AEONW, ABICSW:
+                       v = ^v
+                       mode = 32
+               }
+               o1 = opirr(ctxt, a)
+               o1 |= bitconEncode(v, mode) | uint32(r&31)<<5 | uint32(rt&31)
 
        case 54: /* floating point arith */
                o1 = oprrr(ctxt, p.As)
@@ -2694,6 +2809,31 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) {
 
                o1 = ADR(0, uint32(d), uint32(p.To.Reg))
 
+       case 62: /* op $movcon, [R], R -> mov $movcon, REGTMP + op REGTMP, [R], R */
+               if p.Reg == REGTMP {
+                       ctxt.Diag("cannot use REGTMP as source: %v\n", p)
+               }
+               o1 = omovconst(ctxt, AMOVD, p, &p.From, REGTMP)
+
+               rt := int(p.To.Reg)
+               if p.To.Type == obj.TYPE_NONE {
+                       rt = REGZERO
+               }
+               r := int(p.Reg)
+               if r == 0 {
+                       r = rt
+               }
+               if p.To.Type != obj.TYPE_NONE && (p.To.Reg == REGSP || r == REGSP) {
+                       o2 = opxrrr(ctxt, p.As)
+                       o2 |= REGTMP & 31 << 16
+                       o2 |= LSL0_64
+               } else {
+                       o2 = oprrr(ctxt, p.As)
+                       o2 |= REGTMP & 31 << 16 /* shift is 0 */
+               }
+               o2 |= uint32(r&31) << 5
+               o2 |= uint32(rt & 31)
+
                /* reloc ops */
        case 64: /* movT R,addr -> adrp + add + movT R, (REGTMP) */
                o1 = ADR(1, 0, REGTMP)
@@ -3374,28 +3514,28 @@ func opirr(ctxt *obj.Link, a obj.As) uint32 {
                return 1<<31 | 0x10<<24
 
                /* op $bimm, Rn, Rd */
-       case AAND:
+       case AAND, ABIC:
                return S64 | 0<<29 | 0x24<<23
 
-       case AANDW:
+       case AANDW, ABICW:
                return S32 | 0<<29 | 0x24<<23 | 0<<22
 
-       case AORR:
+       case AORR, AORN:
                return S64 | 1<<29 | 0x24<<23
 
-       case AORRW:
+       case AORRW, AORNW:
                return S32 | 1<<29 | 0x24<<23 | 0<<22
 
-       case AEOR:
+       case AEOR, AEON:
                return S64 | 2<<29 | 0x24<<23
 
-       case AEORW:
+       case AEORW, AEONW:
                return S32 | 2<<29 | 0x24<<23 | 0<<22
 
-       case AANDS:
+       case AANDS, ABICS:
                return S64 | 3<<29 | 0x24<<23
 
-       case AANDSW:
+       case AANDSW, ABICSW:
                return S32 | 3<<29 | 0x24<<23 | 0<<22
 
        case AASR:
@@ -4100,6 +4240,52 @@ func omovlit(ctxt *obj.Link, as obj.As, p *obj.Prog, a *obj.Addr, dr int) uint32
        return uint32(o1)
 }
 
+// load a constant (MOVCON or BITCON) in a into rt
+func omovconst(ctxt *obj.Link, as obj.As, p *obj.Prog, a *obj.Addr, rt int) (o1 uint32) {
+       if c := oclass(a); c == C_BITCON || c == C_ABCON || c == C_ABCON0 {
+               // or $bitcon, REGZERO, rt
+               mode := 64
+               var as1 obj.As
+               switch as {
+               case AMOVW:
+                       as1 = AORRW
+                       mode = 32
+               case AMOVD:
+                       as1 = AORR
+               }
+               o1 = opirr(ctxt, as1)
+               o1 |= bitconEncode(uint64(a.Offset), mode) | uint32(REGZERO&31)<<5 | uint32(rt&31)
+               return o1
+       }
+
+       r := 32
+       if as == AMOVD {
+               r = 64
+       }
+       d := a.Offset
+       s := movcon(d)
+       if s < 0 || s >= r {
+               d = ^d
+               s = movcon(d)
+               if s < 0 || s >= r {
+                       ctxt.Diag("impossible move wide: %#x\n%v", uint64(a.Offset), p)
+               }
+               if as == AMOVD {
+                       o1 = opirr(ctxt, AMOVN)
+               } else {
+                       o1 = opirr(ctxt, AMOVNW)
+               }
+       } else {
+               if as == AMOVD {
+                       o1 = opirr(ctxt, AMOVZ)
+               } else {
+                       o1 = opirr(ctxt, AMOVZW)
+               }
+       }
+       o1 |= uint32((((d >> uint(s*16)) & 0xFFFF) << 5) | int64((uint32(s)&3)<<21) | int64(rt&31))
+       return o1
+}
+
 func opbfm(ctxt *obj.Link, a obj.As, r int, s int, rf int, rt int) uint32 {
        var c uint32
        o := opirr(ctxt, a)
index ffa1b416d682635b8c2e672736cec90ba6cb1541..3f606dd697ad88a04f5f250f459806404bd4717e 100644 (file)
@@ -279,20 +279,30 @@ func progedit(ctxt *obj.Link, p *obj.Prog) {
        // Rewrite negative immediates as positive immediates with
        // complementary instruction.
        switch p.As {
-       case AADD,
-               AADDW,
-               ASUB,
-               ASUBW,
-               ACMP,
-               ACMPW,
-               ACMN,
-               ACMNW:
-               if p.From.Type == obj.NAME_EXTERN && p.From.Offset < 0 {
+       case AADD, ASUB, ACMP, ACMN:
+               if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
                        p.From.Offset = -p.From.Offset
                        p.As = complements[p.As]
                }
+       case AADDW, ASUBW, ACMPW, ACMNW:
+               if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
+                       p.From.Offset = -p.From.Offset
+                       p.As = complements[p.As]
+               }
+       }
 
-               break
+       // For 32-bit logical instruction with constant,
+       // rewrite the high 32-bit to be a repetition of
+       // the low 32-bit, so that the BITCON test can be
+       // shared for both 32-bit and 64-bit. 32-bit ops
+       // will zero the high 32-bit of the destination
+       // register anyway.
+       switch p.As {
+       case AANDW, AORRW, AEORW, AANDSW:
+               if p.From.Type == obj.TYPE_CONST {
+                       v := p.From.Offset & 0xffffffff
+                       p.From.Offset = v | v<<32
+               }
        }
 
        if ctxt.Flag_dynlink {