arm64.go (8658B)
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 }