github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/asm/arch/arm.go (about) 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 ARM 6 // instruction set, to minimize its interaction with the core of the 7 // assembler. 8 9 package arch 10 11 import ( 12 "strings" 13 14 "github.com/go-asm/go/cmd/obj" 15 "github.com/go-asm/go/cmd/obj/arm" 16 ) 17 18 var armLS = map[string]uint8{ 19 "U": arm.C_UBIT, 20 "S": arm.C_SBIT, 21 "W": arm.C_WBIT, 22 "P": arm.C_PBIT, 23 "PW": arm.C_WBIT | arm.C_PBIT, 24 "WP": arm.C_WBIT | arm.C_PBIT, 25 } 26 27 var armSCOND = map[string]uint8{ 28 "EQ": arm.C_SCOND_EQ, 29 "NE": arm.C_SCOND_NE, 30 "CS": arm.C_SCOND_HS, 31 "HS": arm.C_SCOND_HS, 32 "CC": arm.C_SCOND_LO, 33 "LO": arm.C_SCOND_LO, 34 "MI": arm.C_SCOND_MI, 35 "PL": arm.C_SCOND_PL, 36 "VS": arm.C_SCOND_VS, 37 "VC": arm.C_SCOND_VC, 38 "HI": arm.C_SCOND_HI, 39 "LS": arm.C_SCOND_LS, 40 "GE": arm.C_SCOND_GE, 41 "LT": arm.C_SCOND_LT, 42 "GT": arm.C_SCOND_GT, 43 "LE": arm.C_SCOND_LE, 44 "AL": arm.C_SCOND_NONE, 45 "U": arm.C_UBIT, 46 "S": arm.C_SBIT, 47 "W": arm.C_WBIT, 48 "P": arm.C_PBIT, 49 "PW": arm.C_WBIT | arm.C_PBIT, 50 "WP": arm.C_WBIT | arm.C_PBIT, 51 "F": arm.C_FBIT, 52 "IBW": arm.C_WBIT | arm.C_PBIT | arm.C_UBIT, 53 "IAW": arm.C_WBIT | arm.C_UBIT, 54 "DBW": arm.C_WBIT | arm.C_PBIT, 55 "DAW": arm.C_WBIT, 56 "IB": arm.C_PBIT | arm.C_UBIT, 57 "IA": arm.C_UBIT, 58 "DB": arm.C_PBIT, 59 "DA": 0, 60 } 61 62 var armJump = map[string]bool{ 63 "B": true, 64 "BL": true, 65 "BX": true, 66 "BEQ": true, 67 "BNE": true, 68 "BCS": true, 69 "BHS": true, 70 "BCC": true, 71 "BLO": true, 72 "BMI": true, 73 "BPL": true, 74 "BVS": true, 75 "BVC": true, 76 "BHI": true, 77 "BLS": true, 78 "BGE": true, 79 "BLT": true, 80 "BGT": true, 81 "BLE": true, 82 "CALL": true, 83 "JMP": true, 84 } 85 86 func jumpArm(word string) bool { 87 return armJump[word] 88 } 89 90 // IsARMCMP reports whether the op (as defined by an arm.A* constant) is 91 // one of the comparison instructions that require special handling. 92 func IsARMCMP(op obj.As) bool { 93 switch op { 94 case arm.ACMN, arm.ACMP, arm.ATEQ, arm.ATST: 95 return true 96 } 97 return false 98 } 99 100 // IsARMSTREX reports whether the op (as defined by an arm.A* constant) is 101 // one of the STREX-like instructions that require special handling. 102 func IsARMSTREX(op obj.As) bool { 103 switch op { 104 case arm.ASTREX, arm.ASTREXD, arm.ASWPW, arm.ASWPBU: 105 return true 106 } 107 return false 108 } 109 110 // MCR is not defined by the obj/arm; instead we define it privately here. 111 // It is encoded as an MRC with a bit inside the instruction word, 112 // passed to arch.ARMMRCOffset. 113 const aMCR = arm.ALAST + 1 114 115 // IsARMMRC reports whether the op (as defined by an arm.A* constant) is 116 // MRC or MCR. 117 func IsARMMRC(op obj.As) bool { 118 switch op { 119 case arm.AMRC, aMCR: // Note: aMCR is defined in this package. 120 return true 121 } 122 return false 123 } 124 125 // IsARMBFX reports whether the op (as defined by an arm.A* constant) is one the 126 // BFX-like instructions which are in the form of "op $width, $LSB, (Reg,) Reg". 127 func IsARMBFX(op obj.As) bool { 128 switch op { 129 case arm.ABFX, arm.ABFXU, arm.ABFC, arm.ABFI: 130 return true 131 } 132 return false 133 } 134 135 // IsARMFloatCmp reports whether the op is a floating comparison instruction. 136 func IsARMFloatCmp(op obj.As) bool { 137 switch op { 138 case arm.ACMPF, arm.ACMPD: 139 return true 140 } 141 return false 142 } 143 144 // ARMMRCOffset implements the peculiar encoding of the MRC and MCR instructions. 145 // The difference between MRC and MCR is represented by a bit high in the word, not 146 // in the usual way by the opcode itself. Asm must use AMRC for both instructions, so 147 // we return the opcode for MRC so that asm doesn't need to import obj/arm. 148 func ARMMRCOffset(op obj.As, cond string, x0, x1, x2, x3, x4, x5 int64) (offset int64, op0 obj.As, ok bool) { 149 op1 := int64(0) 150 if op == arm.AMRC { 151 op1 = 1 152 } 153 bits, ok := ParseARMCondition(cond) 154 if !ok { 155 return 156 } 157 offset = (0xe << 24) | // opcode 158 (op1 << 20) | // MCR/MRC 159 ((int64(bits) ^ arm.C_SCOND_XOR) << 28) | // scond 160 ((x0 & 15) << 8) | //coprocessor number 161 ((x1 & 7) << 21) | // coprocessor operation 162 ((x2 & 15) << 12) | // ARM register 163 ((x3 & 15) << 16) | // Crn 164 ((x4 & 15) << 0) | // Crm 165 ((x5 & 7) << 5) | // coprocessor information 166 (1 << 4) /* must be set */ 167 return offset, arm.AMRC, true 168 } 169 170 // IsARMMULA reports whether the op (as defined by an arm.A* constant) is 171 // MULA, MULS, MMULA, MMULS, MULABB, MULAWB or MULAWT, the 4-operand instructions. 172 func IsARMMULA(op obj.As) bool { 173 switch op { 174 case arm.AMULA, arm.AMULS, arm.AMMULA, arm.AMMULS, arm.AMULABB, arm.AMULAWB, arm.AMULAWT: 175 return true 176 } 177 return false 178 } 179 180 var bcode = []obj.As{ 181 arm.ABEQ, 182 arm.ABNE, 183 arm.ABCS, 184 arm.ABCC, 185 arm.ABMI, 186 arm.ABPL, 187 arm.ABVS, 188 arm.ABVC, 189 arm.ABHI, 190 arm.ABLS, 191 arm.ABGE, 192 arm.ABLT, 193 arm.ABGT, 194 arm.ABLE, 195 arm.AB, 196 obj.ANOP, 197 } 198 199 // ARMConditionCodes handles the special condition code situation for the ARM. 200 // It returns a boolean to indicate success; failure means cond was unrecognized. 201 func ARMConditionCodes(prog *obj.Prog, cond string) bool { 202 if cond == "" { 203 return true 204 } 205 bits, ok := ParseARMCondition(cond) 206 if !ok { 207 return false 208 } 209 /* hack to make B.NE etc. work: turn it into the corresponding conditional */ 210 if prog.As == arm.AB { 211 prog.As = bcode[(bits^arm.C_SCOND_XOR)&0xf] 212 bits = (bits &^ 0xf) | arm.C_SCOND_NONE 213 } 214 prog.Scond = bits 215 return true 216 } 217 218 // ParseARMCondition parses the conditions attached to an ARM instruction. 219 // The input is a single string consisting of period-separated condition 220 // codes, such as ".P.W". An initial period is ignored. 221 func ParseARMCondition(cond string) (uint8, bool) { 222 return parseARMCondition(cond, armLS, armSCOND) 223 } 224 225 func parseARMCondition(cond string, ls, scond map[string]uint8) (uint8, bool) { 226 cond = strings.TrimPrefix(cond, ".") 227 if cond == "" { 228 return arm.C_SCOND_NONE, true 229 } 230 names := strings.Split(cond, ".") 231 bits := uint8(0) 232 for _, name := range names { 233 if b, present := ls[name]; present { 234 bits |= b 235 continue 236 } 237 if b, present := scond[name]; present { 238 bits = (bits &^ arm.C_SCOND) | b 239 continue 240 } 241 return 0, false 242 } 243 return bits, true 244 } 245 246 func armRegisterNumber(name string, n int16) (int16, bool) { 247 if n < 0 || 15 < n { 248 return 0, false 249 } 250 switch name { 251 case "R": 252 return arm.REG_R0 + n, true 253 case "F": 254 return arm.REG_F0 + n, true 255 } 256 return 0, false 257 }