Commit 31700b83 authored by fanzha02's avatar fanzha02 Committed by Brad Fitzpatrick

cmd/internal/obj/arm64: add assembler support for load with register offset

The patch adds support for LDR(register offset) instruction.
And add the test cases and negative tests.

Change-Id: I5b32c6a5065afc4571116d4896f7ebec3c0416d3
Reviewed-on: https://go-review.googlesource.com/87955Reviewed-by: 's avatarCherry Zhang <cherryyz@google.com>
parent 14393c5c
......@@ -121,12 +121,18 @@ func arm64RegisterNumber(name string, n int16) (int16, bool) {
return 0, false
}
// rm is the Rm register value, o is the extension, amount is the left shift value.
func roff(rm uint32, o uint32, amount int16) int64 {
return int64((rm&31)<<16 | o<<13 | uint32(amount)<<10)
}
// ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
rm := uint32(reg)
Rnum := (reg & 31) + int16(num<<5)
if isAmount {
if num < 0 || num > 7 {
return errors.New("shift amount out of range")
return errors.New("index shift amount is out of range")
}
}
switch ext {
......@@ -134,50 +140,96 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_UXTB + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTB + Rnum
a.Offset = roff(rm, 0, num)
case "UXTH":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_UXTH + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (1 << 13) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTH + Rnum
a.Offset = roff(rm, 1, num)
case "UXTW":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_UXTW + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (2 << 13) | (uint32(num) << 10))
// effective address of memory is a base register value and an offset register value.
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_UXTW + Rnum
if num == 0 {
a.Offset = roff(rm, 2, 2)
} else {
a.Offset = roff(rm, 2, 6)
}
} else {
a.Reg = arm64.REG_UXTW + Rnum
a.Offset = roff(rm, 2, num)
}
case "UXTX":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_UXTX + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (3 << 13) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_UXTX + Rnum
a.Offset = roff(rm, 3, num)
case "SXTB":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_SXTB + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (4 << 13) | (uint32(num) << 10))
a.Reg = arm64.REG_SXTB + Rnum
a.Offset = roff(rm, 4, num)
case "SXTH":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_SXTH + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (5 << 13) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
return errors.New("invalid shift for the register offset addressing mode")
}
a.Reg = arm64.REG_SXTH + Rnum
a.Offset = roff(rm, 5, num)
case "SXTW":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_SXTW + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (6 << 13) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTW + Rnum
if num == 0 {
a.Offset = roff(rm, 6, 2)
} else {
a.Offset = roff(rm, 6, 6)
}
} else {
a.Reg = arm64.REG_SXTW + Rnum
a.Offset = roff(rm, 6, num)
}
case "SXTX":
if !isAmount {
return errors.New("invalid register extension")
}
a.Reg = arm64.REG_SXTX + (reg & 31) + num<<5
a.Offset = int64(((rm & 31) << 16) | (7 << 13) | (uint32(num) << 10))
if a.Type == obj.TYPE_MEM {
a.Index = arm64.REG_SXTX + Rnum
if num == 0 {
a.Offset = roff(rm, 7, 2)
} else {
a.Offset = roff(rm, 7, 6)
}
} else {
a.Reg = arm64.REG_SXTX + Rnum
a.Offset = roff(rm, 7, num)
}
case "LSL":
if !isAmount {
return errors.New("invalid register extension")
}
a.Index = arm64.REG_LSL + Rnum
a.Offset = roff(rm, 3, 6)
case "B8":
if isIndex {
return errors.New("invalid register extension")
......@@ -250,7 +302,7 @@ func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, i
default:
return errors.New("unsupported register extension type: " + ext)
}
a.Type = obj.TYPE_REG
return nil
}
......
......@@ -322,6 +322,7 @@ func (p *Parser) operand(a *obj.Addr) {
p.get(')')
}
} else if p.atRegisterExtension() {
a.Type = obj.TYPE_REG
p.registerExtension(a, tok.String(), prefix)
p.expectOperandEnd()
return
......@@ -604,12 +605,20 @@ func (p *Parser) registerExtension(a *obj.Addr, name string, prefix rune) {
return
}
p.get('.')
tok := p.next()
ext := tok.String()
isIndex := false
num := int16(0)
isAmount := true // Amount is zero by default
ext := ""
if p.peek() == lex.LSH {
// (Rn)(Rm<<2), the shifted offset register.
ext = "LSL"
} else {
// (Rn)(Rm.UXTW<1), the extended offset register.
// Rm.UXTW<<3, the extended register.
p.get('.')
tok := p.next()
ext = tok.String()
}
if p.peek() == lex.LSH {
// parses left shift amount applied after extension: <<Amount
p.get(lex.LSH)
......@@ -714,8 +723,8 @@ func (p *Parser) setPseudoRegister(addr *obj.Addr, reg string, isStatic bool, pr
}
// registerIndirect parses the general form of a register indirection.
// It is can be (R1), (R2*scale), or (R1)(R2*scale) where R1 may be a simple
// register or register pair R:R or (R, R) or (R+R).
// It is can be (R1), (R2*scale), (R1)(R2*scale), (R1)(R2.SXTX<<3) or (R1)(R2<<3)
// where R1 may be a simple register or register pair R:R or (R, R) or (R+R).
// Or it might be a pseudo-indirection like (FP).
// We are sitting on the opening parenthesis.
func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
......@@ -783,19 +792,26 @@ func (p *Parser) registerIndirect(a *obj.Addr, prefix rune) {
// General form (R)(R*scale).
p.next()
tok := p.next()
r1, r2, scale, ok = p.register(tok.String(), 0)
if !ok {
p.errorf("indirect through non-register %s", tok)
}
if r2 != 0 {
p.errorf("unimplemented two-register form")
}
a.Index = r1
if scale == 0 && p.arch.Family == sys.ARM64 {
// scale is 1 by default for ARM64
a.Scale = 1
if p.atRegisterExtension() {
p.registerExtension(a, tok.String(), prefix)
} else if p.atRegisterShift() {
// (R1)(R2<<3)
p.registerExtension(a, tok.String(), prefix)
} else {
a.Scale = int16(scale)
r1, r2, scale, ok = p.register(tok.String(), 0)
if !ok {
p.errorf("indirect through non-register %s", tok)
}
if r2 != 0 {
p.errorf("unimplemented two-register form")
}
a.Index = r1
if scale == 0 && p.arch.Family == sys.ARM64 {
// scale is 1 by default for ARM64
a.Scale = 1
} else {
a.Scale = int16(scale)
}
}
p.get(')')
} else if scale != 0 {
......
......@@ -100,7 +100,13 @@ TEXT foo(SB), DUPOK|NOSPLIT, $-8
VSHL $8, V1.H8, V2.H8 // 2254184f
VSHL $2, V1.B8, V2.B8 // 22540a0f
VSHL $2, V1.B16, V2.B16 // 22540a4f
MOVD (R2)(R6.SXTW), R4 // 44c866f8
MOVD (R3)(R6), R5 // MOVD (R3)(R6*1), R5 // 656866f8
MOVD (R2)(R6), R4 // MOVD (R2)(R6*1), R4 // 446866f8
MOVWU (R19)(R18<<2), R18 // 727a72b8
MOVD (R2)(R6<<3), R4 // 447866f8
MOVD (R3)(R7.SXTX<<3), R8 // 68f867f8
MOVWU (R5)(R4.UXTW), R10 // aa4864b8
// LTYPE1 imsr ',' spreg ','
// {
// outcode($1, &$2, $4, &nullgen);
......
......@@ -5,12 +5,15 @@
TEXT errors(SB),$0
AND $1, RSP // ERROR "illegal combination"
ANDS $1, R0, RSP // ERROR "illegal combination"
MOVD.P 300(R2), R3 // ERROR "offset out of range [-255,254]"
MOVD.P R3, 344(R2) // ERROR "offset out of range [-255,254]"
ADDSW R7->32, R14, R13 // ERROR "shift amount out of range 0 to 31"
BICW R7@>33, R5, R16 // ERROR "shift amount out of range 0 to 31"
ADD R1.UXTB<<5, R2, R3 // ERROR "shift amount out of range 0 to 4"
ADDS R1.UXTX<<7, R2, R3 // ERROR "shift amount out of range 0 to 4"
BICW R7@>33, R5, R16 // ERROR "shift amount out of range 0 to 31"
MOVD.P 300(R2), R3 // ERROR "offset out of range [-255,254]"
MOVD.P R3, 344(R2) // ERROR "offset out of range [-255,254]"
MOVD (R3)(R7.SXTX<<2), R8 // ERROR "invalid index shift amount"
MOVWU (R5)(R4.UXTW<<3), R10 // ERROR "invalid index shift amount"
MOVWU (R5)(R4<<1), R10 // ERROR "invalid index shift amount"
VLD1 (R8)(R13), [V2.B16] // ERROR "illegal combination"
VLD1 8(R9), [V2.B16] // ERROR "illegal combination"
VST1 [V1.B16], (R8)(R13) // ERROR "illegal combination"
......@@ -72,4 +75,8 @@ TEXT errors(SB),$0
VRBIT V1.H4, V2.H4 // ERROR "invalid arrangement"
VUSHR $56, V1.D2, V2.H4 // ERROR "invalid arrangement"
VUSHR $127, V1.D2, V2.D2 // ERROR "shift out of range"
VLD1.P (R8)(R9.SXTX<<2), [V2.B16] // ERROR "invalid extended register"
VLD1.P (R8)(R9<<2), [V2.B16] // ERROR "invalid extended register"
VST1.P [V1.B16], (R8)(R9.UXTW) // ERROR "invalid extended register"
VST1.P [V1.B16], (R8)(R9<<1) // ERROR "invalid extended register"
RET
......@@ -178,6 +178,8 @@ const (
// constants to indicate extended register conversion. When checking,
// you should subtract obj.RBaseARM64 first. From this difference, bit 11
// indicates extended register, bits 8-10 select the conversion mode.
// REG_LSL is the index shift specifier, bit 9 indicates shifted offset register.
const REG_LSL = obj.RBaseARM64 + 1<<9
const REG_EXT = obj.RBaseARM64 + 1<<11
const (
......
......@@ -445,6 +445,10 @@ var optab = []Optab{
{AFMOVD, C_FREG, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
{AVMOVS, C_VREG, C_NONE, C_LOREG, 23, 4, 0, 0, C_XPRE},
/* load with shifted or extended register offset */
{AMOVD, C_ROFF, C_NONE, C_REG, 98, 4, 0, 0, 0},
{AMOVW, C_ROFF, C_NONE, C_REG, 98, 4, 0, 0, 0},
/* pre/post-indexed/signed-offset load/store register pair
(unscaled, signed 10-bit quad-aligned and long offset) */
{ALDP, C_NPAUTO, C_NONE, C_PAIR, 66, 4, REGSP, 0, 0},
......@@ -1027,6 +1031,10 @@ func (c *ctxt7) regoff(a *obj.Addr) uint32 {
return uint32(c.instoffset)
}
func isRegShiftOrExt(a *obj.Addr) bool {
return (a.Index-obj.RBaseARM64)&REG_EXT != 0 || (a.Index-obj.RBaseARM64)&REG_LSL != 0
}
// Maximum PC-relative displacement.
// The actual limit is ±2²⁰, but we are conservative
// to avoid needing to recompute the literal pool flush points
......@@ -1379,8 +1387,13 @@ func (c *ctxt7) aclass(a *obj.Addr) int {
case obj.NAME_NONE:
if a.Index != 0 {
if a.Offset != 0 {
if isRegShiftOrExt(a) {
// extended or shifted register offset, (Rn)(Rm.UXTW<<2) or (Rn)(Rm<<2).
return C_ROFF
}
return C_GOK
}
// register offset, (Rn)(Rm)
return C_ROFF
}
c.instoffset = a.Offset
......@@ -2317,7 +2330,7 @@ func (c *ctxt7) checkindex(p *obj.Prog, index, maxindex int) {
}
}
/* checkoffset checks whether the immediate offset is valid for VLD1.P and VST1.P*/
/* checkoffset checks whether the immediate offset is valid for VLD1.P and VST1.P */
func (c *ctxt7) checkoffset(p *obj.Prog, as obj.As) {
var offset, list, n int64
switch as {
......@@ -2352,6 +2365,23 @@ func (c *ctxt7) checkoffset(p *obj.Prog, as obj.As) {
}
}
/* checkShiftAmount checks whether the index shift amount is valid */
/* for load with register offset instructions */
func (c *ctxt7) checkShiftAmount(p *obj.Prog, as obj.As) {
amount := (p.From.Index >> 5) & 7
switch as {
case AMOVWU:
if amount != 2 && amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
case AMOVD:
if amount != 3 && amount != 0 {
c.ctxt.Diag("invalid index shift amount: %v", p)
}
}
}
func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
o1 := uint32(0)
o2 := uint32(0)
......@@ -2928,7 +2958,7 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
c.ctxt.Diag("REGTMP used in large offset load: %v", p)
}
o1 = c.omovlit(AMOVD, p, &p.From, REGTMP)
o2 = c.olsxrr(p, int32(c.opldrr(p, p.As)), int(p.To.Reg), r, REGTMP)
o2 = c.olsxrr(p, int32(c.opldrr(p, p.As, false)), int(p.To.Reg), r, REGTMP)
case 32: /* mov $con, R -> movz/movn */
o1 = c.omovconst(p.As, p, &p.From, int(p.To.Reg))
......@@ -3706,6 +3736,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
o1 |= 0x1f << 16
} else {
// register offset variant
if isRegShiftOrExt(&p.From) {
c.ctxt.Diag("invalid extended register op: %v\n", p)
}
o1 |= uint32(p.From.Index&31) << 16
}
}
......@@ -3799,6 +3832,9 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
o1 |= 0x1f << 16
} else {
// register offset variant
if isRegShiftOrExt(&p.To) {
c.ctxt.Diag("invalid extended register: %v\n", p)
}
o1 |= uint32(p.To.Index&31) << 16
}
}
......@@ -3903,6 +3939,30 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
case 90:
o1 = 0xbea71700
case 91: /* prfm imm(Rn), <prfop | $imm5> */
imm := uint32(p.From.Offset)
r := p.From.Reg
v := uint32(0xff)
if p.To.Type == obj.TYPE_CONST {
v = uint32(p.To.Offset)
if v > 31 {
c.ctxt.Diag("illegal prefetch operation\n%v", p)
}
} else {
for i := 0; i < len(prfopfield); i++ {
if prfopfield[i].reg == p.To.Reg {
v = prfopfield[i].enc
break
}
}
if v == 0xff {
c.ctxt.Diag("illegal prefetch operation:\n%v", p)
}
}
o1 = c.opldrpp(p, p.As)
o1 |= (uint32(r&31) << 5) | (uint32((imm>>3)&0xfff) << 10) | (uint32(v & 31))
case 92: /* vmov Vn.<T>[index], Vd.<T>[index] */
rf := int(p.From.Reg)
rt := int(p.To.Reg)
......@@ -3944,32 +4004,6 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
}
o1 |= (uint32(imm5&0x1f) << 16) | (uint32(imm4&0xf) << 11) | (uint32(rf&31) << 5) | uint32(rt&31)
break
case 91: /* prfm imm(Rn), <prfop | $imm5> */
imm := uint32(p.From.Offset)
r := p.From.Reg
v := uint32(0xff)
if p.To.Type == obj.TYPE_CONST {
v = uint32(p.To.Offset)
if v > 31 {
c.ctxt.Diag("illegal prefetch operation\n%v", p)
}
} else {
for i := 0; i < len(prfopfield); i++ {
if prfopfield[i].reg == p.To.Reg {
v = prfopfield[i].enc
break
}
}
if v == 0xff {
c.ctxt.Diag("illegal prefetch operation:\n%v", p)
}
}
o1 = c.opldrpp(p, p.As)
o1 |= (uint32(r&31) << 5) | ((imm >> 3) & 0xfff << 10) | (v & 31)
case 93: /* vpmull{2} Vm.<T>, Vn.<T>, Vd */
af := int((p.From.Reg >> 5) & 15)
at := int((p.To.Reg >> 5) & 15)
......@@ -4255,6 +4289,24 @@ func (c *ctxt7) asmout(p *obj.Prog, o *Optab, out []uint32) {
}
o1 |= (uint32(Q&1) << 30) | (uint32(r&31) << 16) | ((opcode & 7) << 13) | (uint32(S&1) << 12) | (uint32(size&3) << 10) | (uint32(rf&31) << 5) | uint32(rt&31)
case 98: /* MOVD (Rn)(Rm.SXTW[<<amount]),Rd */
if p.From.Offset != 0 {
// extended or shifted offset register.
c.checkShiftAmount(p, p.As)
o1 = c.opldrr(p, p.As, true)
o1 |= uint32(p.From.Offset) /* includes reg, op, etc */
} else {
// (Rn)(Rm), no extension or shift.
o1 = c.opldrr(p, p.As, false)
o1 |= uint32(p.From.Index&31) << 16
}
o1 |= uint32(p.From.Reg&31) << 5
rt := int(p.To.Reg)
if p.To.Type == obj.TYPE_NONE {
rt = REGZERO
}
o1 |= uint32(rt & 31)
}
out[0] = o1
out[1] = o2
......@@ -5584,26 +5636,31 @@ func (c *ctxt7) olsxrr(p *obj.Prog, o int32, r int, r1 int, r2 int) uint32 {
// opldrr returns the ARM64 opcode encoding corresponding to the obj.As opcode
// for load instruction with register offset.
func (c *ctxt7) opldrr(p *obj.Prog, a obj.As) uint32 {
// The offset register can be (Rn)(Rm.UXTW<<2) or (Rn)(Rm<<2) or (Rn)(Rm).
func (c *ctxt7) opldrr(p *obj.Prog, a obj.As, extension bool) uint32 {
OptionS := uint32(0x1a)
if extension {
OptionS = uint32(0) // option value and S value have been encoded into p.From.Offset.
}
switch a {
case AMOVD:
return 0x1a<<10 | 0x3<<21 | 0x1f<<27
return OptionS<<10 | 0x3<<21 | 0x1f<<27
case AMOVW:
return 0x1a<<10 | 0x5<<21 | 0x17<<27
return OptionS<<10 | 0x5<<21 | 0x17<<27
case AMOVWU:
return 0x1a<<10 | 0x3<<21 | 0x17<<27
return OptionS<<10 | 0x3<<21 | 0x17<<27
case AMOVH:
return 0x1a<<10 | 0x5<<21 | 0x0f<<27
return OptionS<<10 | 0x5<<21 | 0x0f<<27
case AMOVHU:
return 0x1a<<10 | 0x3<<21 | 0x0f<<27
return OptionS<<10 | 0x3<<21 | 0x0f<<27
case AMOVB:
return 0x1a<<10 | 0x5<<21 | 0x07<<27
return OptionS<<10 | 0x5<<21 | 0x07<<27
case AMOVBU:
return 0x1a<<10 | 0x3<<21 | 0x07<<27
return OptionS<<10 | 0x3<<21 | 0x07<<27
case AFMOVS:
return 0x1a<<10 | 0x3<<21 | 0x17<<27 | 1<<26
return OptionS<<10 | 0x3<<21 | 0x17<<27 | 1<<26
case AFMOVD:
return 0x1a<<10 | 0x3<<21 | 0x1f<<27 | 1<<26
return OptionS<<10 | 0x3<<21 | 0x1f<<27 | 1<<26
}
c.ctxt.Diag("bad opldrr %v\n%v", a, p)
return 0
......
......@@ -10,14 +10,6 @@ Go Assembly for ARM64 Reference Manual
1. Alphabetical list of basic instructions
// TODO
PRFM: Prefetch Memory (immediate)
PRFM imm(Rn), <prfop>
prfop is the prefetch operation and can have the following values:
PLDL1KEEP, PLDL1STRM, PLDL2KEEP, PLDL2STRM, PLDL3KEEP, PLDL3STRM,
PLIL1KEEP, PLIL1STRM, PLIL2KEEP, PLIL2STRM, PLIL3KEEP, PLIL3STRM,
PSTL1KEEP, PSTL1STRM, PSTL2KEEP, PSTL2STRM, PSTL3KEEP, PSTL3STRM.
PRFM imm(Rn), $imm
$imm prefetch operation is encoded as an immediate.
LDARB: Load-Acquire Register Byte
LDARB (<Rn>), <Rd>
......@@ -43,6 +35,20 @@ Go Assembly for ARM64 Reference Manual
LDXPW (<Rn>), (<Rt1>, <Rt2>)
Loads two 32-bit words from memory, and writes them to Rt1 and Rt2.
MOVD|MOVW: Load Register (register offset)
MOVD (Rn)(Rm.UXTW<<3), Rt
MOVD (Rn)(Rm.SXTX), Rt
MOVD (Rn)(Rm), Rt
PRFM: Prefetch Memory (immediate)
PRFM imm(Rn), <prfop>
prfop is the prefetch operation and can have the following values:
PLDL1KEEP, PLDL1STRM, PLDL2KEEP, PLDL2STRM, PLDL3KEEP, PLDL3STRM,
PLIL1KEEP, PLIL1STRM, PLIL2KEEP, PLIL2STRM, PLIL3KEEP, PLIL3STRM,
PSTL1KEEP, PSTL1STRM, PSTL2KEEP, PSTL2STRM, PSTL3KEEP, PSTL3STRM.
PRFM imm(Rn), $imm
$imm prefetch operation is encoded as an immediate.
STLRB: Store-Release Register Byte
STLRB <Rd>, (<Rn>)
Stores a byte from Rd to a memory location from Rn.
......
......@@ -223,6 +223,9 @@ func rconv(r int) string {
} else {
return fmt.Sprintf("%s.SXTX", regname(r))
}
// bits 0-4 indicate register, bits 5-7 indicate shift amount, bit 8 equals to 0.
case REG_LSL <= r && r < (REG_LSL+1<<8):
return fmt.Sprintf("R%d<<%d", r&31, (r>>5)&7)
case REG_ARNG <= r && r < REG_ELEM:
return fmt.Sprintf("V%d.%s", r&31, arrange((r>>5)&15))
case REG_ELEM <= r && r < REG_ELEM_END:
......
......@@ -214,7 +214,12 @@ func Dconv(p *Prog, a *Addr) string {
case TYPE_MEM:
str = Mconv(a)
if a.Index != REG_NONE {
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
if a.Scale == 0 {
// arm64 shifted or extended register offset, scale = 0.
str += fmt.Sprintf("(%v)", Rconv(int(a.Index)))
} else {
str += fmt.Sprintf("(%v*%d)", Rconv(int(a.Index)), int(a.Scale))
}
}
case TYPE_CONST:
......@@ -294,7 +299,17 @@ func Mconv(a *Addr) string {
case a.Offset == 0:
str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
case a.Offset != 0:
str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
switch objabi.GOARCH {
case "arm64":
// the register and the extension/shift are encoded in a.Offset.
if a.Index != 0 {
str = fmt.Sprintf("(%v)", Rconv(int(a.Reg)))
return str
}
fallthrough
default:
str = fmt.Sprintf("%d(%v)", a.Offset, Rconv(int(a.Reg)))
}
}
// Note: a.Reg == REG_NONE encodes the default base register for the NAME_ type.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment