From: fanzha02 Date: Fri, 8 Dec 2017 08:19:32 +0000 (+0000) Subject: cmd/internal/obj/arm64: add assembler support for load with register offset X-Git-Tag: go1.11beta1~922 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=31700b83b5d9bdc2ddc474fd72b809a7b585d6da;p=gostls13.git 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/87955 Reviewed-by: Cherry Zhang --- diff --git a/src/cmd/asm/internal/arch/arm64.go b/src/cmd/asm/internal/arch/arm64.go index af45f421e9..3941e36372 100644 --- a/src/cmd/asm/internal/arch/arm64.go +++ b/src/cmd/asm/internal/arch/arm64.go @@ -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 } diff --git a/src/cmd/asm/internal/asm/parse.go b/src/cmd/asm/internal/asm/parse.go index 833693ba21..0ac1f04c10 100644 --- a/src/cmd/asm/internal/asm/parse.go +++ b/src/cmd/asm/internal/asm/parse.go @@ -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: <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 diff --git a/src/cmd/internal/obj/arm64/a.out.go b/src/cmd/internal/obj/arm64/a.out.go index 1b5d93d52a..0f03b05930 100644 --- a/src/cmd/internal/obj/arm64/a.out.go +++ b/src/cmd/internal/obj/arm64/a.out.go @@ -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 ( diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 45d793ce59..85eb5fe532 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -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)®_EXT != 0 || (a.Index-obj.RBaseARM64)®_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), */ + 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.[index], Vd.[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), */ - 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., Vn., 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[< - 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 (), @@ -43,6 +35,20 @@ Go Assembly for ARM64 Reference Manual LDXPW (), (, ) 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 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 , () Stores a byte from Rd to a memory location from Rn. diff --git a/src/cmd/internal/obj/arm64/list7.go b/src/cmd/internal/obj/arm64/list7.go index 1bf20ae71b..093af6cb06 100644 --- a/src/cmd/internal/obj/arm64/list7.go +++ b/src/cmd/internal/obj/arm64/list7.go @@ -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: diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 3fc6113112..2d457fd503 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -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.