123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350 |
- // Copyright 2015 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // This file encapsulates some of the odd characteristics of the ARM64
- // instruction set, to minimize its interaction with the core of the
- // assembler.
- package arch
- import (
- "github.com/twitchyliquid64/golang-asm/obj"
- "github.com/twitchyliquid64/golang-asm/obj/arm64"
- "errors"
- )
- var arm64LS = map[string]uint8{
- "P": arm64.C_XPOST,
- "W": arm64.C_XPRE,
- }
- var arm64Jump = map[string]bool{
- "B": true,
- "BL": true,
- "BEQ": true,
- "BNE": true,
- "BCS": true,
- "BHS": true,
- "BCC": true,
- "BLO": true,
- "BMI": true,
- "BPL": true,
- "BVS": true,
- "BVC": true,
- "BHI": true,
- "BLS": true,
- "BGE": true,
- "BLT": true,
- "BGT": true,
- "BLE": true,
- "CALL": true,
- "CBZ": true,
- "CBZW": true,
- "CBNZ": true,
- "CBNZW": true,
- "JMP": true,
- "TBNZ": true,
- "TBZ": true,
- }
- func jumpArm64(word string) bool {
- return arm64Jump[word]
- }
- // IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
- // one of the comparison instructions that require special handling.
- func IsARM64CMP(op obj.As) bool {
- switch op {
- case arm64.ACMN, arm64.ACMP, arm64.ATST,
- arm64.ACMNW, arm64.ACMPW, arm64.ATSTW,
- arm64.AFCMPS, arm64.AFCMPD,
- arm64.AFCMPES, arm64.AFCMPED:
- return true
- }
- return false
- }
- // IsARM64STLXR reports whether the op (as defined by an arm64.A*
- // constant) is one of the STLXR-like instructions that require special
- // handling.
- func IsARM64STLXR(op obj.As) bool {
- switch op {
- case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR,
- arm64.ASTXRB, arm64.ASTXRH, arm64.ASTXRW, arm64.ASTXR,
- arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
- return true
- }
- // atomic instructions
- if arm64.IsAtomicInstruction(op) {
- return true
- }
- return false
- }
- // ARM64Suffix handles the special suffix for the ARM64.
- // It returns a boolean to indicate success; failure means
- // cond was unrecognized.
- func ARM64Suffix(prog *obj.Prog, cond string) bool {
- if cond == "" {
- return true
- }
- bits, ok := parseARM64Suffix(cond)
- if !ok {
- return false
- }
- prog.Scond = bits
- return true
- }
- // parseARM64Suffix parses the suffix attached to an ARM64 instruction.
- // The input is a single string consisting of period-separated condition
- // codes, such as ".P.W". An initial period is ignored.
- func parseARM64Suffix(cond string) (uint8, bool) {
- if cond == "" {
- return 0, true
- }
- return parseARMCondition(cond, arm64LS, nil)
- }
- func arm64RegisterNumber(name string, n int16) (int16, bool) {
- switch name {
- case "F":
- if 0 <= n && n <= 31 {
- return arm64.REG_F0 + n, true
- }
- case "R":
- if 0 <= n && n <= 30 { // not 31
- return arm64.REG_R0 + n, true
- }
- case "V":
- if 0 <= n && n <= 31 {
- return arm64.REG_V0 + n, true
- }
- }
- return 0, false
- }
- // IsARM64TBL reports whether the op (as defined by an arm64.A*
- // constant) is one of the table lookup instructions that require special
- // handling.
- func IsARM64TBL(op obj.As) bool {
- return op == arm64.AVTBL
- }
- // ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
- func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
- Rnum := (reg & 31) + int16(num<<5)
- if isAmount {
- if num < 0 || num > 7 {
- return errors.New("index shift amount is out of range")
- }
- }
- switch ext {
- case "UXTB":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTB + Rnum
- case "UXTH":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTH + Rnum
- case "UXTW":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- // 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
- } else {
- a.Reg = arm64.REG_UXTW + Rnum
- }
- case "UXTX":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_UXTX + Rnum
- case "SXTB":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_SXTB + Rnum
- case "SXTH":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- return errors.New("invalid shift for the register offset addressing mode")
- }
- a.Reg = arm64.REG_SXTH + Rnum
- case "SXTW":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- a.Index = arm64.REG_SXTW + Rnum
- } else {
- a.Reg = arm64.REG_SXTW + Rnum
- }
- case "SXTX":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- if a.Type == obj.TYPE_MEM {
- a.Index = arm64.REG_SXTX + Rnum
- } else {
- a.Reg = arm64.REG_SXTX + Rnum
- }
- case "LSL":
- if !isAmount {
- return errors.New("invalid register extension")
- }
- a.Index = arm64.REG_LSL + Rnum
- case "B8":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
- case "B16":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
- case "H4":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
- case "H8":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
- case "S2":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
- case "S4":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
- case "D1":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
- case "D2":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
- case "Q1":
- if isIndex {
- return errors.New("invalid register extension")
- }
- a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
- case "B":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
- a.Index = num
- case "H":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
- a.Index = num
- case "S":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
- a.Index = num
- case "D":
- if !isIndex {
- return nil
- }
- a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
- a.Index = num
- default:
- return errors.New("unsupported register extension type: " + ext)
- }
- return nil
- }
- // ARM64RegisterArrangement parses an ARM64 vector register arrangement.
- func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
- var curQ, curSize uint16
- if name[0] != 'V' {
- return 0, errors.New("expect V0 through V31; found: " + name)
- }
- if reg < 0 {
- return 0, errors.New("invalid register number: " + name)
- }
- switch arng {
- case "B8":
- curSize = 0
- curQ = 0
- case "B16":
- curSize = 0
- curQ = 1
- case "H4":
- curSize = 1
- curQ = 0
- case "H8":
- curSize = 1
- curQ = 1
- case "S2":
- curSize = 2
- curQ = 0
- case "S4":
- curSize = 2
- curQ = 1
- case "D1":
- curSize = 3
- curQ = 0
- case "D2":
- curSize = 3
- curQ = 1
- default:
- return 0, errors.New("invalid arrangement in ARM64 register list")
- }
- return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
- }
- // ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
- func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
- offset := int64(firstReg)
- switch regCnt {
- case 1:
- offset |= 0x7 << 12
- case 2:
- offset |= 0xa << 12
- case 3:
- offset |= 0x6 << 12
- case 4:
- offset |= 0x2 << 12
- default:
- return 0, errors.New("invalid register numbers in ARM64 register list")
- }
- offset |= arrangement
- // arm64 uses the 60th bit to differentiate from other archs
- // For more details, refer to: obj/arm64/list7.go
- offset |= 1 << 60
- return offset, nil
- }
|