github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/compile/arm/ssa.go (about) 1 // Copyright 2016 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 package arm 6 7 import ( 8 "fmt" 9 "math" 10 "math/bits" 11 12 "github.com/go-asm/go/buildcfg" 13 14 "github.com/go-asm/go/cmd/compile/base" 15 "github.com/go-asm/go/cmd/compile/ir" 16 "github.com/go-asm/go/cmd/compile/logopt" 17 "github.com/go-asm/go/cmd/compile/ssa" 18 "github.com/go-asm/go/cmd/compile/ssagen" 19 "github.com/go-asm/go/cmd/compile/types" 20 "github.com/go-asm/go/cmd/obj" 21 "github.com/go-asm/go/cmd/obj/arm" 22 ) 23 24 // loadByType returns the load instruction of the given type. 25 func loadByType(t *types.Type) obj.As { 26 if t.IsFloat() { 27 switch t.Size() { 28 case 4: 29 return arm.AMOVF 30 case 8: 31 return arm.AMOVD 32 } 33 } else { 34 switch t.Size() { 35 case 1: 36 if t.IsSigned() { 37 return arm.AMOVB 38 } else { 39 return arm.AMOVBU 40 } 41 case 2: 42 if t.IsSigned() { 43 return arm.AMOVH 44 } else { 45 return arm.AMOVHU 46 } 47 case 4: 48 return arm.AMOVW 49 } 50 } 51 panic("bad load type") 52 } 53 54 // storeByType returns the store instruction of the given type. 55 func storeByType(t *types.Type) obj.As { 56 if t.IsFloat() { 57 switch t.Size() { 58 case 4: 59 return arm.AMOVF 60 case 8: 61 return arm.AMOVD 62 } 63 } else { 64 switch t.Size() { 65 case 1: 66 return arm.AMOVB 67 case 2: 68 return arm.AMOVH 69 case 4: 70 return arm.AMOVW 71 } 72 } 73 panic("bad store type") 74 } 75 76 // shift type is used as Offset in obj.TYPE_SHIFT operands to encode shifted register operands. 77 type shift int64 78 79 // copied from ../../../github.com/go-asm/go/obj/util.go:/TYPE_SHIFT 80 func (v shift) String() string { 81 op := "<<>>->@>"[((v>>5)&3)<<1:] 82 if v&(1<<4) != 0 { 83 // register shift 84 return fmt.Sprintf("R%d%c%cR%d", v&15, op[0], op[1], (v>>8)&15) 85 } else { 86 // constant shift 87 return fmt.Sprintf("R%d%c%c%d", v&15, op[0], op[1], (v>>7)&31) 88 } 89 } 90 91 // makeshift encodes a register shifted by a constant. 92 func makeshift(v *ssa.Value, reg int16, typ int64, s int64) shift { 93 if s < 0 || s >= 32 { 94 v.Fatalf("shift out of range: %d", s) 95 } 96 return shift(int64(reg&0xf) | typ | (s&31)<<7) 97 } 98 99 // genshift generates a Prog for r = r0 op (r1 shifted by n). 100 func genshift(s *ssagen.State, v *ssa.Value, as obj.As, r0, r1, r int16, typ int64, n int64) *obj.Prog { 101 p := s.Prog(as) 102 p.From.Type = obj.TYPE_SHIFT 103 p.From.Offset = int64(makeshift(v, r1, typ, n)) 104 p.Reg = r0 105 if r != 0 { 106 p.To.Type = obj.TYPE_REG 107 p.To.Reg = r 108 } 109 return p 110 } 111 112 // makeregshift encodes a register shifted by a register. 113 func makeregshift(r1 int16, typ int64, r2 int16) shift { 114 return shift(int64(r1&0xf) | typ | int64(r2&0xf)<<8 | 1<<4) 115 } 116 117 // genregshift generates a Prog for r = r0 op (r1 shifted by r2). 118 func genregshift(s *ssagen.State, as obj.As, r0, r1, r2, r int16, typ int64) *obj.Prog { 119 p := s.Prog(as) 120 p.From.Type = obj.TYPE_SHIFT 121 p.From.Offset = int64(makeregshift(r1, typ, r2)) 122 p.Reg = r0 123 if r != 0 { 124 p.To.Type = obj.TYPE_REG 125 p.To.Reg = r 126 } 127 return p 128 } 129 130 // find a (lsb, width) pair for BFC 131 // lsb must be in [0, 31], width must be in [1, 32 - lsb] 132 // return (0xffffffff, 0) if v is not a binary like 0...01...10...0 133 func getBFC(v uint32) (uint32, uint32) { 134 var m, l uint32 135 // BFC is not applicable with zero 136 if v == 0 { 137 return 0xffffffff, 0 138 } 139 // find the lowest set bit, for example l=2 for 0x3ffffffc 140 l = uint32(bits.TrailingZeros32(v)) 141 // m-1 represents the highest set bit index, for example m=30 for 0x3ffffffc 142 m = 32 - uint32(bits.LeadingZeros32(v)) 143 // check if v is a binary like 0...01...10...0 144 if (1<<m)-(1<<l) == v { 145 // it must be m > l for non-zero v 146 return l, m - l 147 } 148 // invalid 149 return 0xffffffff, 0 150 } 151 152 func ssaGenValue(s *ssagen.State, v *ssa.Value) { 153 switch v.Op { 154 case ssa.OpCopy, ssa.OpARMMOVWreg: 155 if v.Type.IsMemory() { 156 return 157 } 158 x := v.Args[0].Reg() 159 y := v.Reg() 160 if x == y { 161 return 162 } 163 as := arm.AMOVW 164 if v.Type.IsFloat() { 165 switch v.Type.Size() { 166 case 4: 167 as = arm.AMOVF 168 case 8: 169 as = arm.AMOVD 170 default: 171 panic("bad float size") 172 } 173 } 174 p := s.Prog(as) 175 p.From.Type = obj.TYPE_REG 176 p.From.Reg = x 177 p.To.Type = obj.TYPE_REG 178 p.To.Reg = y 179 case ssa.OpARMMOVWnop: 180 // nothing to do 181 case ssa.OpLoadReg: 182 if v.Type.IsFlags() { 183 v.Fatalf("load flags not implemented: %v", v.LongString()) 184 return 185 } 186 p := s.Prog(loadByType(v.Type)) 187 ssagen.AddrAuto(&p.From, v.Args[0]) 188 p.To.Type = obj.TYPE_REG 189 p.To.Reg = v.Reg() 190 case ssa.OpStoreReg: 191 if v.Type.IsFlags() { 192 v.Fatalf("store flags not implemented: %v", v.LongString()) 193 return 194 } 195 p := s.Prog(storeByType(v.Type)) 196 p.From.Type = obj.TYPE_REG 197 p.From.Reg = v.Args[0].Reg() 198 ssagen.AddrAuto(&p.To, v) 199 case ssa.OpARMADD, 200 ssa.OpARMADC, 201 ssa.OpARMSUB, 202 ssa.OpARMSBC, 203 ssa.OpARMRSB, 204 ssa.OpARMAND, 205 ssa.OpARMOR, 206 ssa.OpARMXOR, 207 ssa.OpARMBIC, 208 ssa.OpARMMUL, 209 ssa.OpARMADDF, 210 ssa.OpARMADDD, 211 ssa.OpARMSUBF, 212 ssa.OpARMSUBD, 213 ssa.OpARMSLL, 214 ssa.OpARMSRL, 215 ssa.OpARMSRA, 216 ssa.OpARMMULF, 217 ssa.OpARMMULD, 218 ssa.OpARMNMULF, 219 ssa.OpARMNMULD, 220 ssa.OpARMDIVF, 221 ssa.OpARMDIVD: 222 r := v.Reg() 223 r1 := v.Args[0].Reg() 224 r2 := v.Args[1].Reg() 225 p := s.Prog(v.Op.Asm()) 226 p.From.Type = obj.TYPE_REG 227 p.From.Reg = r2 228 p.Reg = r1 229 p.To.Type = obj.TYPE_REG 230 p.To.Reg = r 231 case ssa.OpARMSRR: 232 genregshift(s, arm.AMOVW, 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR) 233 case ssa.OpARMMULAF, ssa.OpARMMULAD, ssa.OpARMMULSF, ssa.OpARMMULSD, ssa.OpARMFMULAD: 234 r := v.Reg() 235 r0 := v.Args[0].Reg() 236 r1 := v.Args[1].Reg() 237 r2 := v.Args[2].Reg() 238 if r != r0 { 239 v.Fatalf("result and addend are not in the same register: %v", v.LongString()) 240 } 241 p := s.Prog(v.Op.Asm()) 242 p.From.Type = obj.TYPE_REG 243 p.From.Reg = r2 244 p.Reg = r1 245 p.To.Type = obj.TYPE_REG 246 p.To.Reg = r 247 case ssa.OpARMADDS, 248 ssa.OpARMSUBS: 249 r := v.Reg0() 250 r1 := v.Args[0].Reg() 251 r2 := v.Args[1].Reg() 252 p := s.Prog(v.Op.Asm()) 253 p.Scond = arm.C_SBIT 254 p.From.Type = obj.TYPE_REG 255 p.From.Reg = r2 256 p.Reg = r1 257 p.To.Type = obj.TYPE_REG 258 p.To.Reg = r 259 case ssa.OpARMSRAcond: 260 // ARM shift instructions uses only the low-order byte of the shift amount 261 // generate conditional instructions to deal with large shifts 262 // flag is already set 263 // SRA.HS $31, Rarg0, Rdst // shift 31 bits to get the sign bit 264 // SRA.LO Rarg1, Rarg0, Rdst 265 r := v.Reg() 266 r1 := v.Args[0].Reg() 267 r2 := v.Args[1].Reg() 268 p := s.Prog(arm.ASRA) 269 p.Scond = arm.C_SCOND_HS 270 p.From.Type = obj.TYPE_CONST 271 p.From.Offset = 31 272 p.Reg = r1 273 p.To.Type = obj.TYPE_REG 274 p.To.Reg = r 275 p = s.Prog(arm.ASRA) 276 p.Scond = arm.C_SCOND_LO 277 p.From.Type = obj.TYPE_REG 278 p.From.Reg = r2 279 p.Reg = r1 280 p.To.Type = obj.TYPE_REG 281 p.To.Reg = r 282 case ssa.OpARMBFX, ssa.OpARMBFXU: 283 p := s.Prog(v.Op.Asm()) 284 p.From.Type = obj.TYPE_CONST 285 p.From.Offset = v.AuxInt >> 8 286 p.AddRestSourceConst(v.AuxInt & 0xff) 287 p.Reg = v.Args[0].Reg() 288 p.To.Type = obj.TYPE_REG 289 p.To.Reg = v.Reg() 290 case ssa.OpARMANDconst, ssa.OpARMBICconst: 291 // try to optimize ANDconst and BICconst to BFC, which saves bytes and ticks 292 // BFC is only available on ARMv7, and its result and source are in the same register 293 if buildcfg.GOARM.Version == 7 && v.Reg() == v.Args[0].Reg() { 294 var val uint32 295 if v.Op == ssa.OpARMANDconst { 296 val = ^uint32(v.AuxInt) 297 } else { // BICconst 298 val = uint32(v.AuxInt) 299 } 300 lsb, width := getBFC(val) 301 // omit BFC for ARM's imm12 302 if 8 < width && width < 24 { 303 p := s.Prog(arm.ABFC) 304 p.From.Type = obj.TYPE_CONST 305 p.From.Offset = int64(width) 306 p.AddRestSourceConst(int64(lsb)) 307 p.To.Type = obj.TYPE_REG 308 p.To.Reg = v.Reg() 309 break 310 } 311 } 312 // fall back to ordinary form 313 fallthrough 314 case ssa.OpARMADDconst, 315 ssa.OpARMADCconst, 316 ssa.OpARMSUBconst, 317 ssa.OpARMSBCconst, 318 ssa.OpARMRSBconst, 319 ssa.OpARMRSCconst, 320 ssa.OpARMORconst, 321 ssa.OpARMXORconst, 322 ssa.OpARMSLLconst, 323 ssa.OpARMSRLconst, 324 ssa.OpARMSRAconst: 325 p := s.Prog(v.Op.Asm()) 326 p.From.Type = obj.TYPE_CONST 327 p.From.Offset = v.AuxInt 328 p.Reg = v.Args[0].Reg() 329 p.To.Type = obj.TYPE_REG 330 p.To.Reg = v.Reg() 331 case ssa.OpARMADDSconst, 332 ssa.OpARMSUBSconst, 333 ssa.OpARMRSBSconst: 334 p := s.Prog(v.Op.Asm()) 335 p.Scond = arm.C_SBIT 336 p.From.Type = obj.TYPE_CONST 337 p.From.Offset = v.AuxInt 338 p.Reg = v.Args[0].Reg() 339 p.To.Type = obj.TYPE_REG 340 p.To.Reg = v.Reg0() 341 case ssa.OpARMSRRconst: 342 genshift(s, v, arm.AMOVW, 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) 343 case ssa.OpARMADDshiftLL, 344 ssa.OpARMADCshiftLL, 345 ssa.OpARMSUBshiftLL, 346 ssa.OpARMSBCshiftLL, 347 ssa.OpARMRSBshiftLL, 348 ssa.OpARMRSCshiftLL, 349 ssa.OpARMANDshiftLL, 350 ssa.OpARMORshiftLL, 351 ssa.OpARMXORshiftLL, 352 ssa.OpARMBICshiftLL: 353 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) 354 case ssa.OpARMADDSshiftLL, 355 ssa.OpARMSUBSshiftLL, 356 ssa.OpARMRSBSshiftLL: 357 p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LL, v.AuxInt) 358 p.Scond = arm.C_SBIT 359 case ssa.OpARMADDshiftRL, 360 ssa.OpARMADCshiftRL, 361 ssa.OpARMSUBshiftRL, 362 ssa.OpARMSBCshiftRL, 363 ssa.OpARMRSBshiftRL, 364 ssa.OpARMRSCshiftRL, 365 ssa.OpARMANDshiftRL, 366 ssa.OpARMORshiftRL, 367 ssa.OpARMXORshiftRL, 368 ssa.OpARMBICshiftRL: 369 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) 370 case ssa.OpARMADDSshiftRL, 371 ssa.OpARMSUBSshiftRL, 372 ssa.OpARMRSBSshiftRL: 373 p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_LR, v.AuxInt) 374 p.Scond = arm.C_SBIT 375 case ssa.OpARMADDshiftRA, 376 ssa.OpARMADCshiftRA, 377 ssa.OpARMSUBshiftRA, 378 ssa.OpARMSBCshiftRA, 379 ssa.OpARMRSBshiftRA, 380 ssa.OpARMRSCshiftRA, 381 ssa.OpARMANDshiftRA, 382 ssa.OpARMORshiftRA, 383 ssa.OpARMXORshiftRA, 384 ssa.OpARMBICshiftRA: 385 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) 386 case ssa.OpARMADDSshiftRA, 387 ssa.OpARMSUBSshiftRA, 388 ssa.OpARMRSBSshiftRA: 389 p := genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg0(), arm.SHIFT_AR, v.AuxInt) 390 p.Scond = arm.C_SBIT 391 case ssa.OpARMXORshiftRR: 392 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_RR, v.AuxInt) 393 case ssa.OpARMMVNshiftLL: 394 genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) 395 case ssa.OpARMMVNshiftRL: 396 genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) 397 case ssa.OpARMMVNshiftRA: 398 genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) 399 case ssa.OpARMMVNshiftLLreg: 400 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL) 401 case ssa.OpARMMVNshiftRLreg: 402 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR) 403 case ssa.OpARMMVNshiftRAreg: 404 genregshift(s, v.Op.Asm(), 0, v.Args[0].Reg(), v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR) 405 case ssa.OpARMADDshiftLLreg, 406 ssa.OpARMADCshiftLLreg, 407 ssa.OpARMSUBshiftLLreg, 408 ssa.OpARMSBCshiftLLreg, 409 ssa.OpARMRSBshiftLLreg, 410 ssa.OpARMRSCshiftLLreg, 411 ssa.OpARMANDshiftLLreg, 412 ssa.OpARMORshiftLLreg, 413 ssa.OpARMXORshiftLLreg, 414 ssa.OpARMBICshiftLLreg: 415 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LL) 416 case ssa.OpARMADDSshiftLLreg, 417 ssa.OpARMSUBSshiftLLreg, 418 ssa.OpARMRSBSshiftLLreg: 419 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LL) 420 p.Scond = arm.C_SBIT 421 case ssa.OpARMADDshiftRLreg, 422 ssa.OpARMADCshiftRLreg, 423 ssa.OpARMSUBshiftRLreg, 424 ssa.OpARMSBCshiftRLreg, 425 ssa.OpARMRSBshiftRLreg, 426 ssa.OpARMRSCshiftRLreg, 427 ssa.OpARMANDshiftRLreg, 428 ssa.OpARMORshiftRLreg, 429 ssa.OpARMXORshiftRLreg, 430 ssa.OpARMBICshiftRLreg: 431 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_LR) 432 case ssa.OpARMADDSshiftRLreg, 433 ssa.OpARMSUBSshiftRLreg, 434 ssa.OpARMRSBSshiftRLreg: 435 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_LR) 436 p.Scond = arm.C_SBIT 437 case ssa.OpARMADDshiftRAreg, 438 ssa.OpARMADCshiftRAreg, 439 ssa.OpARMSUBshiftRAreg, 440 ssa.OpARMSBCshiftRAreg, 441 ssa.OpARMRSBshiftRAreg, 442 ssa.OpARMRSCshiftRAreg, 443 ssa.OpARMANDshiftRAreg, 444 ssa.OpARMORshiftRAreg, 445 ssa.OpARMXORshiftRAreg, 446 ssa.OpARMBICshiftRAreg: 447 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg(), arm.SHIFT_AR) 448 case ssa.OpARMADDSshiftRAreg, 449 ssa.OpARMSUBSshiftRAreg, 450 ssa.OpARMRSBSshiftRAreg: 451 p := genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), v.Reg0(), arm.SHIFT_AR) 452 p.Scond = arm.C_SBIT 453 case ssa.OpARMHMUL, 454 ssa.OpARMHMULU: 455 // 32-bit high multiplication 456 p := s.Prog(v.Op.Asm()) 457 p.From.Type = obj.TYPE_REG 458 p.From.Reg = v.Args[0].Reg() 459 p.Reg = v.Args[1].Reg() 460 p.To.Type = obj.TYPE_REGREG 461 p.To.Reg = v.Reg() 462 p.To.Offset = arm.REGTMP // throw away low 32-bit into tmp register 463 case ssa.OpARMMULLU: 464 // 32-bit multiplication, results 64-bit, high 32-bit in out0, low 32-bit in out1 465 p := s.Prog(v.Op.Asm()) 466 p.From.Type = obj.TYPE_REG 467 p.From.Reg = v.Args[0].Reg() 468 p.Reg = v.Args[1].Reg() 469 p.To.Type = obj.TYPE_REGREG 470 p.To.Reg = v.Reg0() // high 32-bit 471 p.To.Offset = int64(v.Reg1()) // low 32-bit 472 case ssa.OpARMMULA, ssa.OpARMMULS: 473 p := s.Prog(v.Op.Asm()) 474 p.From.Type = obj.TYPE_REG 475 p.From.Reg = v.Args[0].Reg() 476 p.Reg = v.Args[1].Reg() 477 p.To.Type = obj.TYPE_REGREG2 478 p.To.Reg = v.Reg() // result 479 p.To.Offset = int64(v.Args[2].Reg()) // addend 480 case ssa.OpARMMOVWconst: 481 p := s.Prog(v.Op.Asm()) 482 p.From.Type = obj.TYPE_CONST 483 p.From.Offset = v.AuxInt 484 p.To.Type = obj.TYPE_REG 485 p.To.Reg = v.Reg() 486 case ssa.OpARMMOVFconst, 487 ssa.OpARMMOVDconst: 488 p := s.Prog(v.Op.Asm()) 489 p.From.Type = obj.TYPE_FCONST 490 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 491 p.To.Type = obj.TYPE_REG 492 p.To.Reg = v.Reg() 493 case ssa.OpARMCMP, 494 ssa.OpARMCMN, 495 ssa.OpARMTST, 496 ssa.OpARMTEQ, 497 ssa.OpARMCMPF, 498 ssa.OpARMCMPD: 499 p := s.Prog(v.Op.Asm()) 500 p.From.Type = obj.TYPE_REG 501 // Special layout in ARM assembly 502 // Comparing to x86, the operands of ARM's CMP are reversed. 503 p.From.Reg = v.Args[1].Reg() 504 p.Reg = v.Args[0].Reg() 505 case ssa.OpARMCMPconst, 506 ssa.OpARMCMNconst, 507 ssa.OpARMTSTconst, 508 ssa.OpARMTEQconst: 509 // Special layout in ARM assembly 510 p := s.Prog(v.Op.Asm()) 511 p.From.Type = obj.TYPE_CONST 512 p.From.Offset = v.AuxInt 513 p.Reg = v.Args[0].Reg() 514 case ssa.OpARMCMPF0, 515 ssa.OpARMCMPD0: 516 p := s.Prog(v.Op.Asm()) 517 p.From.Type = obj.TYPE_REG 518 p.From.Reg = v.Args[0].Reg() 519 case ssa.OpARMCMPshiftLL, ssa.OpARMCMNshiftLL, ssa.OpARMTSTshiftLL, ssa.OpARMTEQshiftLL: 520 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LL, v.AuxInt) 521 case ssa.OpARMCMPshiftRL, ssa.OpARMCMNshiftRL, ssa.OpARMTSTshiftRL, ssa.OpARMTEQshiftRL: 522 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_LR, v.AuxInt) 523 case ssa.OpARMCMPshiftRA, ssa.OpARMCMNshiftRA, ssa.OpARMTSTshiftRA, ssa.OpARMTEQshiftRA: 524 genshift(s, v, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), 0, arm.SHIFT_AR, v.AuxInt) 525 case ssa.OpARMCMPshiftLLreg, ssa.OpARMCMNshiftLLreg, ssa.OpARMTSTshiftLLreg, ssa.OpARMTEQshiftLLreg: 526 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LL) 527 case ssa.OpARMCMPshiftRLreg, ssa.OpARMCMNshiftRLreg, ssa.OpARMTSTshiftRLreg, ssa.OpARMTEQshiftRLreg: 528 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_LR) 529 case ssa.OpARMCMPshiftRAreg, ssa.OpARMCMNshiftRAreg, ssa.OpARMTSTshiftRAreg, ssa.OpARMTEQshiftRAreg: 530 genregshift(s, v.Op.Asm(), v.Args[0].Reg(), v.Args[1].Reg(), v.Args[2].Reg(), 0, arm.SHIFT_AR) 531 case ssa.OpARMMOVWaddr: 532 p := s.Prog(arm.AMOVW) 533 p.From.Type = obj.TYPE_ADDR 534 p.From.Reg = v.Args[0].Reg() 535 p.To.Type = obj.TYPE_REG 536 p.To.Reg = v.Reg() 537 538 var wantreg string 539 // MOVW $sym+off(base), R 540 // the assembler expands it as the following: 541 // - base is SP: add constant offset to SP (R13) 542 // when constant is large, tmp register (R11) may be used 543 // - base is SB: load external address from constant pool (use relocation) 544 switch v.Aux.(type) { 545 default: 546 v.Fatalf("aux is of unknown type %T", v.Aux) 547 case *obj.LSym: 548 wantreg = "SB" 549 ssagen.AddAux(&p.From, v) 550 case *ir.Name: 551 wantreg = "SP" 552 ssagen.AddAux(&p.From, v) 553 case nil: 554 // No sym, just MOVW $off(SP), R 555 wantreg = "SP" 556 p.From.Offset = v.AuxInt 557 } 558 if reg := v.Args[0].RegName(); reg != wantreg { 559 v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg) 560 } 561 562 case ssa.OpARMMOVBload, 563 ssa.OpARMMOVBUload, 564 ssa.OpARMMOVHload, 565 ssa.OpARMMOVHUload, 566 ssa.OpARMMOVWload, 567 ssa.OpARMMOVFload, 568 ssa.OpARMMOVDload: 569 p := s.Prog(v.Op.Asm()) 570 p.From.Type = obj.TYPE_MEM 571 p.From.Reg = v.Args[0].Reg() 572 ssagen.AddAux(&p.From, v) 573 p.To.Type = obj.TYPE_REG 574 p.To.Reg = v.Reg() 575 case ssa.OpARMMOVBstore, 576 ssa.OpARMMOVHstore, 577 ssa.OpARMMOVWstore, 578 ssa.OpARMMOVFstore, 579 ssa.OpARMMOVDstore: 580 p := s.Prog(v.Op.Asm()) 581 p.From.Type = obj.TYPE_REG 582 p.From.Reg = v.Args[1].Reg() 583 p.To.Type = obj.TYPE_MEM 584 p.To.Reg = v.Args[0].Reg() 585 ssagen.AddAux(&p.To, v) 586 case ssa.OpARMMOVWloadidx, ssa.OpARMMOVBUloadidx, ssa.OpARMMOVBloadidx, ssa.OpARMMOVHUloadidx, ssa.OpARMMOVHloadidx: 587 // this is just shift 0 bits 588 fallthrough 589 case ssa.OpARMMOVWloadshiftLL: 590 p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LL, v.AuxInt) 591 p.From.Reg = v.Args[0].Reg() 592 case ssa.OpARMMOVWloadshiftRL: 593 p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_LR, v.AuxInt) 594 p.From.Reg = v.Args[0].Reg() 595 case ssa.OpARMMOVWloadshiftRA: 596 p := genshift(s, v, v.Op.Asm(), 0, v.Args[1].Reg(), v.Reg(), arm.SHIFT_AR, v.AuxInt) 597 p.From.Reg = v.Args[0].Reg() 598 case ssa.OpARMMOVWstoreidx, ssa.OpARMMOVBstoreidx, ssa.OpARMMOVHstoreidx: 599 // this is just shift 0 bits 600 fallthrough 601 case ssa.OpARMMOVWstoreshiftLL: 602 p := s.Prog(v.Op.Asm()) 603 p.From.Type = obj.TYPE_REG 604 p.From.Reg = v.Args[2].Reg() 605 p.To.Type = obj.TYPE_SHIFT 606 p.To.Reg = v.Args[0].Reg() 607 p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LL, v.AuxInt)) 608 case ssa.OpARMMOVWstoreshiftRL: 609 p := s.Prog(v.Op.Asm()) 610 p.From.Type = obj.TYPE_REG 611 p.From.Reg = v.Args[2].Reg() 612 p.To.Type = obj.TYPE_SHIFT 613 p.To.Reg = v.Args[0].Reg() 614 p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_LR, v.AuxInt)) 615 case ssa.OpARMMOVWstoreshiftRA: 616 p := s.Prog(v.Op.Asm()) 617 p.From.Type = obj.TYPE_REG 618 p.From.Reg = v.Args[2].Reg() 619 p.To.Type = obj.TYPE_SHIFT 620 p.To.Reg = v.Args[0].Reg() 621 p.To.Offset = int64(makeshift(v, v.Args[1].Reg(), arm.SHIFT_AR, v.AuxInt)) 622 case ssa.OpARMMOVBreg, 623 ssa.OpARMMOVBUreg, 624 ssa.OpARMMOVHreg, 625 ssa.OpARMMOVHUreg: 626 a := v.Args[0] 627 for a.Op == ssa.OpCopy || a.Op == ssa.OpARMMOVWreg || a.Op == ssa.OpARMMOVWnop { 628 a = a.Args[0] 629 } 630 if a.Op == ssa.OpLoadReg { 631 t := a.Type 632 switch { 633 case v.Op == ssa.OpARMMOVBreg && t.Size() == 1 && t.IsSigned(), 634 v.Op == ssa.OpARMMOVBUreg && t.Size() == 1 && !t.IsSigned(), 635 v.Op == ssa.OpARMMOVHreg && t.Size() == 2 && t.IsSigned(), 636 v.Op == ssa.OpARMMOVHUreg && t.Size() == 2 && !t.IsSigned(): 637 // arg is a proper-typed load, already zero/sign-extended, don't extend again 638 if v.Reg() == v.Args[0].Reg() { 639 return 640 } 641 p := s.Prog(arm.AMOVW) 642 p.From.Type = obj.TYPE_REG 643 p.From.Reg = v.Args[0].Reg() 644 p.To.Type = obj.TYPE_REG 645 p.To.Reg = v.Reg() 646 return 647 default: 648 } 649 } 650 if buildcfg.GOARM.Version >= 6 { 651 // generate more efficient "MOVB/MOVBU/MOVH/MOVHU Reg@>0, Reg" on ARMv6 & ARMv7 652 genshift(s, v, v.Op.Asm(), 0, v.Args[0].Reg(), v.Reg(), arm.SHIFT_RR, 0) 653 return 654 } 655 fallthrough 656 case ssa.OpARMMVN, 657 ssa.OpARMCLZ, 658 ssa.OpARMREV, 659 ssa.OpARMREV16, 660 ssa.OpARMRBIT, 661 ssa.OpARMSQRTF, 662 ssa.OpARMSQRTD, 663 ssa.OpARMNEGF, 664 ssa.OpARMNEGD, 665 ssa.OpARMABSD, 666 ssa.OpARMMOVWF, 667 ssa.OpARMMOVWD, 668 ssa.OpARMMOVFW, 669 ssa.OpARMMOVDW, 670 ssa.OpARMMOVFD, 671 ssa.OpARMMOVDF: 672 p := s.Prog(v.Op.Asm()) 673 p.From.Type = obj.TYPE_REG 674 p.From.Reg = v.Args[0].Reg() 675 p.To.Type = obj.TYPE_REG 676 p.To.Reg = v.Reg() 677 case ssa.OpARMMOVWUF, 678 ssa.OpARMMOVWUD, 679 ssa.OpARMMOVFWU, 680 ssa.OpARMMOVDWU: 681 p := s.Prog(v.Op.Asm()) 682 p.Scond = arm.C_UBIT 683 p.From.Type = obj.TYPE_REG 684 p.From.Reg = v.Args[0].Reg() 685 p.To.Type = obj.TYPE_REG 686 p.To.Reg = v.Reg() 687 case ssa.OpARMCMOVWHSconst: 688 p := s.Prog(arm.AMOVW) 689 p.Scond = arm.C_SCOND_HS 690 p.From.Type = obj.TYPE_CONST 691 p.From.Offset = v.AuxInt 692 p.To.Type = obj.TYPE_REG 693 p.To.Reg = v.Reg() 694 case ssa.OpARMCMOVWLSconst: 695 p := s.Prog(arm.AMOVW) 696 p.Scond = arm.C_SCOND_LS 697 p.From.Type = obj.TYPE_CONST 698 p.From.Offset = v.AuxInt 699 p.To.Type = obj.TYPE_REG 700 p.To.Reg = v.Reg() 701 case ssa.OpARMCALLstatic, ssa.OpARMCALLclosure, ssa.OpARMCALLinter: 702 s.Call(v) 703 case ssa.OpARMCALLtail: 704 s.TailCall(v) 705 case ssa.OpARMCALLudiv: 706 p := s.Prog(obj.ACALL) 707 p.To.Type = obj.TYPE_MEM 708 p.To.Name = obj.NAME_EXTERN 709 p.To.Sym = ir.Syms.Udiv 710 case ssa.OpARMLoweredWB: 711 p := s.Prog(obj.ACALL) 712 p.To.Type = obj.TYPE_MEM 713 p.To.Name = obj.NAME_EXTERN 714 // AuxInt encodes how many buffer entries we need. 715 p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1] 716 case ssa.OpARMLoweredPanicBoundsA, ssa.OpARMLoweredPanicBoundsB, ssa.OpARMLoweredPanicBoundsC: 717 p := s.Prog(obj.ACALL) 718 p.To.Type = obj.TYPE_MEM 719 p.To.Name = obj.NAME_EXTERN 720 p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] 721 s.UseArgs(8) // space used in callee args area by assembly stubs 722 case ssa.OpARMLoweredPanicExtendA, ssa.OpARMLoweredPanicExtendB, ssa.OpARMLoweredPanicExtendC: 723 p := s.Prog(obj.ACALL) 724 p.To.Type = obj.TYPE_MEM 725 p.To.Name = obj.NAME_EXTERN 726 p.To.Sym = ssagen.ExtendCheckFunc[v.AuxInt] 727 s.UseArgs(12) // space used in callee args area by assembly stubs 728 case ssa.OpARMDUFFZERO: 729 p := s.Prog(obj.ADUFFZERO) 730 p.To.Type = obj.TYPE_MEM 731 p.To.Name = obj.NAME_EXTERN 732 p.To.Sym = ir.Syms.Duffzero 733 p.To.Offset = v.AuxInt 734 case ssa.OpARMDUFFCOPY: 735 p := s.Prog(obj.ADUFFCOPY) 736 p.To.Type = obj.TYPE_MEM 737 p.To.Name = obj.NAME_EXTERN 738 p.To.Sym = ir.Syms.Duffcopy 739 p.To.Offset = v.AuxInt 740 case ssa.OpARMLoweredNilCheck: 741 // Issue a load which will fault if arg is nil. 742 p := s.Prog(arm.AMOVB) 743 p.From.Type = obj.TYPE_MEM 744 p.From.Reg = v.Args[0].Reg() 745 ssagen.AddAux(&p.From, v) 746 p.To.Type = obj.TYPE_REG 747 p.To.Reg = arm.REGTMP 748 if logopt.Enabled() { 749 logopt.LogOpt(v.Pos, "nilcheck", "genssa", v.Block.Func.Name) 750 } 751 if base.Debug.Nil != 0 && v.Pos.Line() > 1 { // v.Pos.Line()==1 in generated wrappers 752 base.WarnfAt(v.Pos, "generated nil check") 753 } 754 case ssa.OpARMLoweredZero: 755 // MOVW.P Rarg2, 4(R1) 756 // CMP Rarg1, R1 757 // BLE -2(PC) 758 // arg1 is the address of the last element to zero 759 // arg2 is known to be zero 760 // auxint is alignment 761 var sz int64 762 var mov obj.As 763 switch { 764 case v.AuxInt%4 == 0: 765 sz = 4 766 mov = arm.AMOVW 767 case v.AuxInt%2 == 0: 768 sz = 2 769 mov = arm.AMOVH 770 default: 771 sz = 1 772 mov = arm.AMOVB 773 } 774 p := s.Prog(mov) 775 p.Scond = arm.C_PBIT 776 p.From.Type = obj.TYPE_REG 777 p.From.Reg = v.Args[2].Reg() 778 p.To.Type = obj.TYPE_MEM 779 p.To.Reg = arm.REG_R1 780 p.To.Offset = sz 781 p2 := s.Prog(arm.ACMP) 782 p2.From.Type = obj.TYPE_REG 783 p2.From.Reg = v.Args[1].Reg() 784 p2.Reg = arm.REG_R1 785 p3 := s.Prog(arm.ABLE) 786 p3.To.Type = obj.TYPE_BRANCH 787 p3.To.SetTarget(p) 788 case ssa.OpARMLoweredMove: 789 // MOVW.P 4(R1), Rtmp 790 // MOVW.P Rtmp, 4(R2) 791 // CMP Rarg2, R1 792 // BLE -3(PC) 793 // arg2 is the address of the last element of src 794 // auxint is alignment 795 var sz int64 796 var mov obj.As 797 switch { 798 case v.AuxInt%4 == 0: 799 sz = 4 800 mov = arm.AMOVW 801 case v.AuxInt%2 == 0: 802 sz = 2 803 mov = arm.AMOVH 804 default: 805 sz = 1 806 mov = arm.AMOVB 807 } 808 p := s.Prog(mov) 809 p.Scond = arm.C_PBIT 810 p.From.Type = obj.TYPE_MEM 811 p.From.Reg = arm.REG_R1 812 p.From.Offset = sz 813 p.To.Type = obj.TYPE_REG 814 p.To.Reg = arm.REGTMP 815 p2 := s.Prog(mov) 816 p2.Scond = arm.C_PBIT 817 p2.From.Type = obj.TYPE_REG 818 p2.From.Reg = arm.REGTMP 819 p2.To.Type = obj.TYPE_MEM 820 p2.To.Reg = arm.REG_R2 821 p2.To.Offset = sz 822 p3 := s.Prog(arm.ACMP) 823 p3.From.Type = obj.TYPE_REG 824 p3.From.Reg = v.Args[2].Reg() 825 p3.Reg = arm.REG_R1 826 p4 := s.Prog(arm.ABLE) 827 p4.To.Type = obj.TYPE_BRANCH 828 p4.To.SetTarget(p) 829 case ssa.OpARMEqual, 830 ssa.OpARMNotEqual, 831 ssa.OpARMLessThan, 832 ssa.OpARMLessEqual, 833 ssa.OpARMGreaterThan, 834 ssa.OpARMGreaterEqual, 835 ssa.OpARMLessThanU, 836 ssa.OpARMLessEqualU, 837 ssa.OpARMGreaterThanU, 838 ssa.OpARMGreaterEqualU: 839 // generate boolean values 840 // use conditional move 841 p := s.Prog(arm.AMOVW) 842 p.From.Type = obj.TYPE_CONST 843 p.From.Offset = 0 844 p.To.Type = obj.TYPE_REG 845 p.To.Reg = v.Reg() 846 p = s.Prog(arm.AMOVW) 847 p.Scond = condBits[v.Op] 848 p.From.Type = obj.TYPE_CONST 849 p.From.Offset = 1 850 p.To.Type = obj.TYPE_REG 851 p.To.Reg = v.Reg() 852 case ssa.OpARMLoweredGetClosurePtr: 853 // Closure pointer is R7 (arm.REGCTXT). 854 ssagen.CheckLoweredGetClosurePtr(v) 855 case ssa.OpARMLoweredGetCallerSP: 856 // caller's SP is FixedFrameSize below the address of the first arg 857 p := s.Prog(arm.AMOVW) 858 p.From.Type = obj.TYPE_ADDR 859 p.From.Offset = -base.Ctxt.Arch.FixedFrameSize 860 p.From.Name = obj.NAME_PARAM 861 p.To.Type = obj.TYPE_REG 862 p.To.Reg = v.Reg() 863 case ssa.OpARMLoweredGetCallerPC: 864 p := s.Prog(obj.AGETCALLERPC) 865 p.To.Type = obj.TYPE_REG 866 p.To.Reg = v.Reg() 867 case ssa.OpARMFlagConstant: 868 v.Fatalf("FlagConstant op should never make it to codegen %v", v.LongString()) 869 case ssa.OpARMInvertFlags: 870 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) 871 case ssa.OpClobber, ssa.OpClobberReg: 872 // TODO: implement for clobberdead experiment. Nop is ok for now. 873 default: 874 v.Fatalf("genValue not implemented: %s", v.LongString()) 875 } 876 } 877 878 var condBits = map[ssa.Op]uint8{ 879 ssa.OpARMEqual: arm.C_SCOND_EQ, 880 ssa.OpARMNotEqual: arm.C_SCOND_NE, 881 ssa.OpARMLessThan: arm.C_SCOND_LT, 882 ssa.OpARMLessThanU: arm.C_SCOND_LO, 883 ssa.OpARMLessEqual: arm.C_SCOND_LE, 884 ssa.OpARMLessEqualU: arm.C_SCOND_LS, 885 ssa.OpARMGreaterThan: arm.C_SCOND_GT, 886 ssa.OpARMGreaterThanU: arm.C_SCOND_HI, 887 ssa.OpARMGreaterEqual: arm.C_SCOND_GE, 888 ssa.OpARMGreaterEqualU: arm.C_SCOND_HS, 889 } 890 891 var blockJump = map[ssa.BlockKind]struct { 892 asm, invasm obj.As 893 }{ 894 ssa.BlockARMEQ: {arm.ABEQ, arm.ABNE}, 895 ssa.BlockARMNE: {arm.ABNE, arm.ABEQ}, 896 ssa.BlockARMLT: {arm.ABLT, arm.ABGE}, 897 ssa.BlockARMGE: {arm.ABGE, arm.ABLT}, 898 ssa.BlockARMLE: {arm.ABLE, arm.ABGT}, 899 ssa.BlockARMGT: {arm.ABGT, arm.ABLE}, 900 ssa.BlockARMULT: {arm.ABLO, arm.ABHS}, 901 ssa.BlockARMUGE: {arm.ABHS, arm.ABLO}, 902 ssa.BlockARMUGT: {arm.ABHI, arm.ABLS}, 903 ssa.BlockARMULE: {arm.ABLS, arm.ABHI}, 904 ssa.BlockARMLTnoov: {arm.ABMI, arm.ABPL}, 905 ssa.BlockARMGEnoov: {arm.ABPL, arm.ABMI}, 906 } 907 908 // To model a 'LEnoov' ('<=' without overflow checking) branching. 909 var leJumps = [2][2]ssagen.IndexJump{ 910 {{Jump: arm.ABEQ, Index: 0}, {Jump: arm.ABPL, Index: 1}}, // next == b.Succs[0] 911 {{Jump: arm.ABMI, Index: 0}, {Jump: arm.ABEQ, Index: 0}}, // next == b.Succs[1] 912 } 913 914 // To model a 'GTnoov' ('>' without overflow checking) branching. 915 var gtJumps = [2][2]ssagen.IndexJump{ 916 {{Jump: arm.ABMI, Index: 1}, {Jump: arm.ABEQ, Index: 1}}, // next == b.Succs[0] 917 {{Jump: arm.ABEQ, Index: 1}, {Jump: arm.ABPL, Index: 0}}, // next == b.Succs[1] 918 } 919 920 func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) { 921 switch b.Kind { 922 case ssa.BlockPlain: 923 if b.Succs[0].Block() != next { 924 p := s.Prog(obj.AJMP) 925 p.To.Type = obj.TYPE_BRANCH 926 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 927 } 928 929 case ssa.BlockDefer: 930 // defer returns in R0: 931 // 0 if we should continue executing 932 // 1 if we should jump to deferreturn call 933 p := s.Prog(arm.ACMP) 934 p.From.Type = obj.TYPE_CONST 935 p.From.Offset = 0 936 p.Reg = arm.REG_R0 937 p = s.Prog(arm.ABNE) 938 p.To.Type = obj.TYPE_BRANCH 939 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[1].Block()}) 940 if b.Succs[0].Block() != next { 941 p := s.Prog(obj.AJMP) 942 p.To.Type = obj.TYPE_BRANCH 943 s.Branches = append(s.Branches, ssagen.Branch{P: p, B: b.Succs[0].Block()}) 944 } 945 946 case ssa.BlockExit, ssa.BlockRetJmp: 947 948 case ssa.BlockRet: 949 s.Prog(obj.ARET) 950 951 case ssa.BlockARMEQ, ssa.BlockARMNE, 952 ssa.BlockARMLT, ssa.BlockARMGE, 953 ssa.BlockARMLE, ssa.BlockARMGT, 954 ssa.BlockARMULT, ssa.BlockARMUGT, 955 ssa.BlockARMULE, ssa.BlockARMUGE, 956 ssa.BlockARMLTnoov, ssa.BlockARMGEnoov: 957 jmp := blockJump[b.Kind] 958 switch next { 959 case b.Succs[0].Block(): 960 s.Br(jmp.invasm, b.Succs[1].Block()) 961 case b.Succs[1].Block(): 962 s.Br(jmp.asm, b.Succs[0].Block()) 963 default: 964 if b.Likely != ssa.BranchUnlikely { 965 s.Br(jmp.asm, b.Succs[0].Block()) 966 s.Br(obj.AJMP, b.Succs[1].Block()) 967 } else { 968 s.Br(jmp.invasm, b.Succs[1].Block()) 969 s.Br(obj.AJMP, b.Succs[0].Block()) 970 } 971 } 972 973 case ssa.BlockARMLEnoov: 974 s.CombJump(b, next, &leJumps) 975 976 case ssa.BlockARMGTnoov: 977 s.CombJump(b, next, >Jumps) 978 979 default: 980 b.Fatalf("branch not implemented: %s", b.LongString()) 981 } 982 }