arm64.go raw

   1  // Copyright 2015 The Go Authors. All rights reserved.
   2  // Use of this source code is governed by a BSD-style
   3  // license that can be found in the LICENSE file.
   4  
   5  // This file encapsulates some of the odd characteristics of the ARM64
   6  // instruction set, to minimize its interaction with the core of the
   7  // assembler.
   8  
   9  package arch
  10  
  11  import (
  12  	"github.com/twitchyliquid64/golang-asm/obj"
  13  	"github.com/twitchyliquid64/golang-asm/obj/arm64"
  14  	"errors"
  15  )
  16  
  17  var arm64LS = map[string]uint8{
  18  	"P": arm64.C_XPOST,
  19  	"W": arm64.C_XPRE,
  20  }
  21  
  22  var arm64Jump = map[string]bool{
  23  	"B":     true,
  24  	"BL":    true,
  25  	"BEQ":   true,
  26  	"BNE":   true,
  27  	"BCS":   true,
  28  	"BHS":   true,
  29  	"BCC":   true,
  30  	"BLO":   true,
  31  	"BMI":   true,
  32  	"BPL":   true,
  33  	"BVS":   true,
  34  	"BVC":   true,
  35  	"BHI":   true,
  36  	"BLS":   true,
  37  	"BGE":   true,
  38  	"BLT":   true,
  39  	"BGT":   true,
  40  	"BLE":   true,
  41  	"CALL":  true,
  42  	"CBZ":   true,
  43  	"CBZW":  true,
  44  	"CBNZ":  true,
  45  	"CBNZW": true,
  46  	"JMP":   true,
  47  	"TBNZ":  true,
  48  	"TBZ":   true,
  49  }
  50  
  51  func jumpArm64(word string) bool {
  52  	return arm64Jump[word]
  53  }
  54  
  55  // IsARM64CMP reports whether the op (as defined by an arm.A* constant) is
  56  // one of the comparison instructions that require special handling.
  57  func IsARM64CMP(op obj.As) bool {
  58  	switch op {
  59  	case arm64.ACMN, arm64.ACMP, arm64.ATST,
  60  		arm64.ACMNW, arm64.ACMPW, arm64.ATSTW,
  61  		arm64.AFCMPS, arm64.AFCMPD,
  62  		arm64.AFCMPES, arm64.AFCMPED:
  63  		return true
  64  	}
  65  	return false
  66  }
  67  
  68  // IsARM64STLXR reports whether the op (as defined by an arm64.A*
  69  // constant) is one of the STLXR-like instructions that require special
  70  // handling.
  71  func IsARM64STLXR(op obj.As) bool {
  72  	switch op {
  73  	case arm64.ASTLXRB, arm64.ASTLXRH, arm64.ASTLXRW, arm64.ASTLXR,
  74  		arm64.ASTXRB, arm64.ASTXRH, arm64.ASTXRW, arm64.ASTXR,
  75  		arm64.ASTXP, arm64.ASTXPW, arm64.ASTLXP, arm64.ASTLXPW:
  76  		return true
  77  	}
  78  	// atomic instructions
  79  	if arm64.IsAtomicInstruction(op) {
  80  		return true
  81  	}
  82  	return false
  83  }
  84  
  85  // ARM64Suffix handles the special suffix for the ARM64.
  86  // It returns a boolean to indicate success; failure means
  87  // cond was unrecognized.
  88  func ARM64Suffix(prog *obj.Prog, cond string) bool {
  89  	if cond == "" {
  90  		return true
  91  	}
  92  	bits, ok := parseARM64Suffix(cond)
  93  	if !ok {
  94  		return false
  95  	}
  96  	prog.Scond = bits
  97  	return true
  98  }
  99  
 100  // parseARM64Suffix parses the suffix attached to an ARM64 instruction.
 101  // The input is a single string consisting of period-separated condition
 102  // codes, such as ".P.W". An initial period is ignored.
 103  func parseARM64Suffix(cond string) (uint8, bool) {
 104  	if cond == "" {
 105  		return 0, true
 106  	}
 107  	return parseARMCondition(cond, arm64LS, nil)
 108  }
 109  
 110  func arm64RegisterNumber(name string, n int16) (int16, bool) {
 111  	switch name {
 112  	case "F":
 113  		if 0 <= n && n <= 31 {
 114  			return arm64.REG_F0 + n, true
 115  		}
 116  	case "R":
 117  		if 0 <= n && n <= 30 { // not 31
 118  			return arm64.REG_R0 + n, true
 119  		}
 120  	case "V":
 121  		if 0 <= n && n <= 31 {
 122  			return arm64.REG_V0 + n, true
 123  		}
 124  	}
 125  	return 0, false
 126  }
 127  
 128  // IsARM64TBL reports whether the op (as defined by an arm64.A*
 129  // constant) is one of the table lookup instructions that require special
 130  // handling.
 131  func IsARM64TBL(op obj.As) bool {
 132  	return op == arm64.AVTBL
 133  }
 134  
 135  // ARM64RegisterExtension parses an ARM64 register with extension or arrangement.
 136  func ARM64RegisterExtension(a *obj.Addr, ext string, reg, num int16, isAmount, isIndex bool) error {
 137  	Rnum := (reg & 31) + int16(num<<5)
 138  	if isAmount {
 139  		if num < 0 || num > 7 {
 140  			return errors.New("index shift amount is out of range")
 141  		}
 142  	}
 143  	switch ext {
 144  	case "UXTB":
 145  		if !isAmount {
 146  			return errors.New("invalid register extension")
 147  		}
 148  		if a.Type == obj.TYPE_MEM {
 149  			return errors.New("invalid shift for the register offset addressing mode")
 150  		}
 151  		a.Reg = arm64.REG_UXTB + Rnum
 152  	case "UXTH":
 153  		if !isAmount {
 154  			return errors.New("invalid register extension")
 155  		}
 156  		if a.Type == obj.TYPE_MEM {
 157  			return errors.New("invalid shift for the register offset addressing mode")
 158  		}
 159  		a.Reg = arm64.REG_UXTH + Rnum
 160  	case "UXTW":
 161  		if !isAmount {
 162  			return errors.New("invalid register extension")
 163  		}
 164  		// effective address of memory is a base register value and an offset register value.
 165  		if a.Type == obj.TYPE_MEM {
 166  			a.Index = arm64.REG_UXTW + Rnum
 167  		} else {
 168  			a.Reg = arm64.REG_UXTW + Rnum
 169  		}
 170  	case "UXTX":
 171  		if !isAmount {
 172  			return errors.New("invalid register extension")
 173  		}
 174  		if a.Type == obj.TYPE_MEM {
 175  			return errors.New("invalid shift for the register offset addressing mode")
 176  		}
 177  		a.Reg = arm64.REG_UXTX + Rnum
 178  	case "SXTB":
 179  		if !isAmount {
 180  			return errors.New("invalid register extension")
 181  		}
 182  		a.Reg = arm64.REG_SXTB + Rnum
 183  	case "SXTH":
 184  		if !isAmount {
 185  			return errors.New("invalid register extension")
 186  		}
 187  		if a.Type == obj.TYPE_MEM {
 188  			return errors.New("invalid shift for the register offset addressing mode")
 189  		}
 190  		a.Reg = arm64.REG_SXTH + Rnum
 191  	case "SXTW":
 192  		if !isAmount {
 193  			return errors.New("invalid register extension")
 194  		}
 195  		if a.Type == obj.TYPE_MEM {
 196  			a.Index = arm64.REG_SXTW + Rnum
 197  		} else {
 198  			a.Reg = arm64.REG_SXTW + Rnum
 199  		}
 200  	case "SXTX":
 201  		if !isAmount {
 202  			return errors.New("invalid register extension")
 203  		}
 204  		if a.Type == obj.TYPE_MEM {
 205  			a.Index = arm64.REG_SXTX + Rnum
 206  		} else {
 207  			a.Reg = arm64.REG_SXTX + Rnum
 208  		}
 209  	case "LSL":
 210  		if !isAmount {
 211  			return errors.New("invalid register extension")
 212  		}
 213  		a.Index = arm64.REG_LSL + Rnum
 214  	case "B8":
 215  		if isIndex {
 216  			return errors.New("invalid register extension")
 217  		}
 218  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8B & 15) << 5)
 219  	case "B16":
 220  		if isIndex {
 221  			return errors.New("invalid register extension")
 222  		}
 223  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_16B & 15) << 5)
 224  	case "H4":
 225  		if isIndex {
 226  			return errors.New("invalid register extension")
 227  		}
 228  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4H & 15) << 5)
 229  	case "H8":
 230  		if isIndex {
 231  			return errors.New("invalid register extension")
 232  		}
 233  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_8H & 15) << 5)
 234  	case "S2":
 235  		if isIndex {
 236  			return errors.New("invalid register extension")
 237  		}
 238  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2S & 15) << 5)
 239  	case "S4":
 240  		if isIndex {
 241  			return errors.New("invalid register extension")
 242  		}
 243  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_4S & 15) << 5)
 244  	case "D1":
 245  		if isIndex {
 246  			return errors.New("invalid register extension")
 247  		}
 248  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1D & 15) << 5)
 249  	case "D2":
 250  		if isIndex {
 251  			return errors.New("invalid register extension")
 252  		}
 253  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_2D & 15) << 5)
 254  	case "Q1":
 255  		if isIndex {
 256  			return errors.New("invalid register extension")
 257  		}
 258  		a.Reg = arm64.REG_ARNG + (reg & 31) + ((arm64.ARNG_1Q & 15) << 5)
 259  	case "B":
 260  		if !isIndex {
 261  			return nil
 262  		}
 263  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_B & 15) << 5)
 264  		a.Index = num
 265  	case "H":
 266  		if !isIndex {
 267  			return nil
 268  		}
 269  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_H & 15) << 5)
 270  		a.Index = num
 271  	case "S":
 272  		if !isIndex {
 273  			return nil
 274  		}
 275  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_S & 15) << 5)
 276  		a.Index = num
 277  	case "D":
 278  		if !isIndex {
 279  			return nil
 280  		}
 281  		a.Reg = arm64.REG_ELEM + (reg & 31) + ((arm64.ARNG_D & 15) << 5)
 282  		a.Index = num
 283  	default:
 284  		return errors.New("unsupported register extension type: " + ext)
 285  	}
 286  
 287  	return nil
 288  }
 289  
 290  // ARM64RegisterArrangement parses an ARM64 vector register arrangement.
 291  func ARM64RegisterArrangement(reg int16, name, arng string) (int64, error) {
 292  	var curQ, curSize uint16
 293  	if name[0] != 'V' {
 294  		return 0, errors.New("expect V0 through V31; found: " + name)
 295  	}
 296  	if reg < 0 {
 297  		return 0, errors.New("invalid register number: " + name)
 298  	}
 299  	switch arng {
 300  	case "B8":
 301  		curSize = 0
 302  		curQ = 0
 303  	case "B16":
 304  		curSize = 0
 305  		curQ = 1
 306  	case "H4":
 307  		curSize = 1
 308  		curQ = 0
 309  	case "H8":
 310  		curSize = 1
 311  		curQ = 1
 312  	case "S2":
 313  		curSize = 2
 314  		curQ = 0
 315  	case "S4":
 316  		curSize = 2
 317  		curQ = 1
 318  	case "D1":
 319  		curSize = 3
 320  		curQ = 0
 321  	case "D2":
 322  		curSize = 3
 323  		curQ = 1
 324  	default:
 325  		return 0, errors.New("invalid arrangement in ARM64 register list")
 326  	}
 327  	return (int64(curQ) & 1 << 30) | (int64(curSize&3) << 10), nil
 328  }
 329  
 330  // ARM64RegisterListOffset generates offset encoding according to AArch64 specification.
 331  func ARM64RegisterListOffset(firstReg, regCnt int, arrangement int64) (int64, error) {
 332  	offset := int64(firstReg)
 333  	switch regCnt {
 334  	case 1:
 335  		offset |= 0x7 << 12
 336  	case 2:
 337  		offset |= 0xa << 12
 338  	case 3:
 339  		offset |= 0x6 << 12
 340  	case 4:
 341  		offset |= 0x2 << 12
 342  	default:
 343  		return 0, errors.New("invalid register numbers in ARM64 register list")
 344  	}
 345  	offset |= arrangement
 346  	// arm64 uses the 60th bit to differentiate from other archs
 347  	// For more details, refer to: obj/arm64/list7.go
 348  	offset |= 1 << 60
 349  	return offset, nil
 350  }
 351