github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/x86/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 x86 6 7 import ( 8 "fmt" 9 "math" 10 11 "cmd/compile/internal/gc" 12 "cmd/compile/internal/ssa" 13 "cmd/internal/obj" 14 "cmd/internal/obj/x86" 15 ) 16 17 // Smallest possible faulting page at address zero. 18 const minZeroPage = 4096 19 20 // ssaRegToReg maps ssa register numbers to obj register numbers. 21 var ssaRegToReg = []int16{ 22 x86.REG_AX, 23 x86.REG_CX, 24 x86.REG_DX, 25 x86.REG_BX, 26 x86.REG_SP, 27 x86.REG_BP, 28 x86.REG_SI, 29 x86.REG_DI, 30 x86.REG_X0, 31 x86.REG_X1, 32 x86.REG_X2, 33 x86.REG_X3, 34 x86.REG_X4, 35 x86.REG_X5, 36 x86.REG_X6, 37 x86.REG_X7, 38 0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case. 39 } 40 41 // markMoves marks any MOVXconst ops that need to avoid clobbering flags. 42 func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) { 43 flive := b.FlagsLiveAtEnd 44 if b.Control != nil && b.Control.Type.IsFlags() { 45 flive = true 46 } 47 for i := len(b.Values) - 1; i >= 0; i-- { 48 v := b.Values[i] 49 if flive && v.Op == ssa.Op386MOVLconst { 50 // The "mark" is any non-nil Aux value. 51 v.Aux = v 52 } 53 if v.Type.IsFlags() { 54 flive = false 55 } 56 for _, a := range v.Args { 57 if a.Type.IsFlags() { 58 flive = true 59 } 60 } 61 } 62 } 63 64 // loadByType returns the load instruction of the given type. 65 func loadByType(t ssa.Type) obj.As { 66 // Avoid partial register write 67 if !t.IsFloat() && t.Size() <= 2 { 68 if t.Size() == 1 { 69 return x86.AMOVBLZX 70 } else { 71 return x86.AMOVWLZX 72 } 73 } 74 // Otherwise, there's no difference between load and store opcodes. 75 return storeByType(t) 76 } 77 78 // storeByType returns the store instruction of the given type. 79 func storeByType(t ssa.Type) obj.As { 80 width := t.Size() 81 if t.IsFloat() { 82 switch width { 83 case 4: 84 return x86.AMOVSS 85 case 8: 86 return x86.AMOVSD 87 } 88 } else { 89 switch width { 90 case 1: 91 return x86.AMOVB 92 case 2: 93 return x86.AMOVW 94 case 4: 95 return x86.AMOVL 96 } 97 } 98 panic("bad store type") 99 } 100 101 // moveByType returns the reg->reg move instruction of the given type. 102 func moveByType(t ssa.Type) obj.As { 103 if t.IsFloat() { 104 switch t.Size() { 105 case 4: 106 return x86.AMOVSS 107 case 8: 108 return x86.AMOVSD 109 default: 110 panic(fmt.Sprintf("bad float register width %d:%s", t.Size(), t)) 111 } 112 } else { 113 switch t.Size() { 114 case 1: 115 // Avoids partial register write 116 return x86.AMOVL 117 case 2: 118 return x86.AMOVL 119 case 4: 120 return x86.AMOVL 121 default: 122 panic(fmt.Sprintf("bad int register width %d:%s", t.Size(), t)) 123 } 124 } 125 } 126 127 // opregreg emits instructions for 128 // dest := dest(To) op src(From) 129 // and also returns the created obj.Prog so it 130 // may be further adjusted (offset, scale, etc). 131 func opregreg(op obj.As, dest, src int16) *obj.Prog { 132 p := gc.Prog(op) 133 p.From.Type = obj.TYPE_REG 134 p.To.Type = obj.TYPE_REG 135 p.To.Reg = dest 136 p.From.Reg = src 137 return p 138 } 139 140 func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) { 141 s.SetLineno(v.Line) 142 143 if gc.Thearch.Use387 { 144 if ssaGenValue387(s, v) { 145 return // v was handled by 387 generation. 146 } 147 } 148 149 switch v.Op { 150 case ssa.Op386ADDL: 151 r := gc.SSARegNum(v) 152 r1 := gc.SSARegNum(v.Args[0]) 153 r2 := gc.SSARegNum(v.Args[1]) 154 switch { 155 case r == r1: 156 p := gc.Prog(v.Op.Asm()) 157 p.From.Type = obj.TYPE_REG 158 p.From.Reg = r2 159 p.To.Type = obj.TYPE_REG 160 p.To.Reg = r 161 case r == r2: 162 p := gc.Prog(v.Op.Asm()) 163 p.From.Type = obj.TYPE_REG 164 p.From.Reg = r1 165 p.To.Type = obj.TYPE_REG 166 p.To.Reg = r 167 default: 168 p := gc.Prog(x86.ALEAL) 169 p.From.Type = obj.TYPE_MEM 170 p.From.Reg = r1 171 p.From.Scale = 1 172 p.From.Index = r2 173 p.To.Type = obj.TYPE_REG 174 p.To.Reg = r 175 } 176 177 // 2-address opcode arithmetic 178 case ssa.Op386SUBL, 179 ssa.Op386MULL, 180 ssa.Op386ANDL, 181 ssa.Op386ORL, 182 ssa.Op386XORL, 183 ssa.Op386SHLL, 184 ssa.Op386SHRL, ssa.Op386SHRW, ssa.Op386SHRB, 185 ssa.Op386SARL, ssa.Op386SARW, ssa.Op386SARB, 186 ssa.Op386ADDSS, ssa.Op386ADDSD, ssa.Op386SUBSS, ssa.Op386SUBSD, 187 ssa.Op386MULSS, ssa.Op386MULSD, ssa.Op386DIVSS, ssa.Op386DIVSD, 188 ssa.Op386PXOR, 189 ssa.Op386ADCL, 190 ssa.Op386SBBL: 191 r := gc.SSARegNum(v) 192 if r != gc.SSARegNum(v.Args[0]) { 193 v.Fatalf("input[0] and output not in same register %s", v.LongString()) 194 } 195 opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1])) 196 197 case ssa.Op386ADDLcarry, ssa.Op386SUBLcarry: 198 // output 0 is carry/borrow, output 1 is the low 32 bits. 199 r := gc.SSARegNum0(v) 200 if r != gc.SSARegNum(v.Args[0]) { 201 v.Fatalf("input[0] and output[0] not in same register %s", v.LongString()) 202 } 203 opregreg(v.Op.Asm(), r, gc.SSARegNum(v.Args[1])) 204 205 case ssa.Op386ADDLconstcarry, ssa.Op386SUBLconstcarry: 206 // output 0 is carry/borrow, output 1 is the low 32 bits. 207 r := gc.SSARegNum0(v) 208 if r != gc.SSARegNum(v.Args[0]) { 209 v.Fatalf("input[0] and output[0] not in same register %s", v.LongString()) 210 } 211 p := gc.Prog(v.Op.Asm()) 212 p.From.Type = obj.TYPE_CONST 213 p.From.Offset = v.AuxInt 214 p.To.Type = obj.TYPE_REG 215 p.To.Reg = r 216 217 case ssa.Op386DIVL, ssa.Op386DIVW, 218 ssa.Op386DIVLU, ssa.Op386DIVWU, 219 ssa.Op386MODL, ssa.Op386MODW, 220 ssa.Op386MODLU, ssa.Op386MODWU: 221 222 // Arg[0] is already in AX as it's the only register we allow 223 // and AX is the only output 224 x := gc.SSARegNum(v.Args[1]) 225 226 // CPU faults upon signed overflow, which occurs when most 227 // negative int is divided by -1. 228 var j *obj.Prog 229 if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW || 230 v.Op == ssa.Op386MODL || v.Op == ssa.Op386MODW { 231 232 var c *obj.Prog 233 switch v.Op { 234 case ssa.Op386DIVL, ssa.Op386MODL: 235 c = gc.Prog(x86.ACMPL) 236 j = gc.Prog(x86.AJEQ) 237 gc.Prog(x86.ACDQ) //TODO: fix 238 239 case ssa.Op386DIVW, ssa.Op386MODW: 240 c = gc.Prog(x86.ACMPW) 241 j = gc.Prog(x86.AJEQ) 242 gc.Prog(x86.ACWD) 243 } 244 c.From.Type = obj.TYPE_REG 245 c.From.Reg = x 246 c.To.Type = obj.TYPE_CONST 247 c.To.Offset = -1 248 249 j.To.Type = obj.TYPE_BRANCH 250 } 251 252 // for unsigned ints, we sign extend by setting DX = 0 253 // signed ints were sign extended above 254 if v.Op == ssa.Op386DIVLU || v.Op == ssa.Op386MODLU || 255 v.Op == ssa.Op386DIVWU || v.Op == ssa.Op386MODWU { 256 c := gc.Prog(x86.AXORL) 257 c.From.Type = obj.TYPE_REG 258 c.From.Reg = x86.REG_DX 259 c.To.Type = obj.TYPE_REG 260 c.To.Reg = x86.REG_DX 261 } 262 263 p := gc.Prog(v.Op.Asm()) 264 p.From.Type = obj.TYPE_REG 265 p.From.Reg = x 266 267 // signed division, rest of the check for -1 case 268 if j != nil { 269 j2 := gc.Prog(obj.AJMP) 270 j2.To.Type = obj.TYPE_BRANCH 271 272 var n *obj.Prog 273 if v.Op == ssa.Op386DIVL || v.Op == ssa.Op386DIVW { 274 // n * -1 = -n 275 n = gc.Prog(x86.ANEGL) 276 n.To.Type = obj.TYPE_REG 277 n.To.Reg = x86.REG_AX 278 } else { 279 // n % -1 == 0 280 n = gc.Prog(x86.AXORL) 281 n.From.Type = obj.TYPE_REG 282 n.From.Reg = x86.REG_DX 283 n.To.Type = obj.TYPE_REG 284 n.To.Reg = x86.REG_DX 285 } 286 287 j.To.Val = n 288 j2.To.Val = s.Pc() 289 } 290 291 case ssa.Op386HMULL, ssa.Op386HMULW, ssa.Op386HMULB, 292 ssa.Op386HMULLU, ssa.Op386HMULWU, ssa.Op386HMULBU: 293 // the frontend rewrites constant division by 8/16/32 bit integers into 294 // HMUL by a constant 295 // SSA rewrites generate the 64 bit versions 296 297 // Arg[0] is already in AX as it's the only register we allow 298 // and DX is the only output we care about (the high bits) 299 p := gc.Prog(v.Op.Asm()) 300 p.From.Type = obj.TYPE_REG 301 p.From.Reg = gc.SSARegNum(v.Args[1]) 302 303 // IMULB puts the high portion in AH instead of DL, 304 // so move it to DL for consistency 305 if v.Type.Size() == 1 { 306 m := gc.Prog(x86.AMOVB) 307 m.From.Type = obj.TYPE_REG 308 m.From.Reg = x86.REG_AH 309 m.To.Type = obj.TYPE_REG 310 m.To.Reg = x86.REG_DX 311 } 312 313 case ssa.Op386MULLQU: 314 // AX * args[1], high 32 bits in DX (result[0]), low 32 bits in AX (result[1]). 315 p := gc.Prog(v.Op.Asm()) 316 p.From.Type = obj.TYPE_REG 317 p.From.Reg = gc.SSARegNum(v.Args[1]) 318 319 case ssa.Op386ADDLconst: 320 r := gc.SSARegNum(v) 321 a := gc.SSARegNum(v.Args[0]) 322 if r == a { 323 if v.AuxInt == 1 { 324 p := gc.Prog(x86.AINCL) 325 p.To.Type = obj.TYPE_REG 326 p.To.Reg = r 327 return 328 } 329 if v.AuxInt == -1 { 330 p := gc.Prog(x86.ADECL) 331 p.To.Type = obj.TYPE_REG 332 p.To.Reg = r 333 return 334 } 335 p := gc.Prog(v.Op.Asm()) 336 p.From.Type = obj.TYPE_CONST 337 p.From.Offset = v.AuxInt 338 p.To.Type = obj.TYPE_REG 339 p.To.Reg = r 340 return 341 } 342 p := gc.Prog(x86.ALEAL) 343 p.From.Type = obj.TYPE_MEM 344 p.From.Reg = a 345 p.From.Offset = v.AuxInt 346 p.To.Type = obj.TYPE_REG 347 p.To.Reg = r 348 349 case ssa.Op386MULLconst: 350 r := gc.SSARegNum(v) 351 if r != gc.SSARegNum(v.Args[0]) { 352 v.Fatalf("input[0] and output not in same register %s", v.LongString()) 353 } 354 p := gc.Prog(v.Op.Asm()) 355 p.From.Type = obj.TYPE_CONST 356 p.From.Offset = v.AuxInt 357 p.To.Type = obj.TYPE_REG 358 p.To.Reg = r 359 // TODO: Teach doasm to compile the three-address multiply imul $c, r1, r2 360 // then we don't need to use resultInArg0 for these ops. 361 //p.From3 = new(obj.Addr) 362 //p.From3.Type = obj.TYPE_REG 363 //p.From3.Reg = gc.SSARegNum(v.Args[0]) 364 365 case ssa.Op386SUBLconst, 366 ssa.Op386ADCLconst, 367 ssa.Op386SBBLconst, 368 ssa.Op386ANDLconst, 369 ssa.Op386ORLconst, 370 ssa.Op386XORLconst, 371 ssa.Op386SHLLconst, 372 ssa.Op386SHRLconst, ssa.Op386SHRWconst, ssa.Op386SHRBconst, 373 ssa.Op386SARLconst, ssa.Op386SARWconst, ssa.Op386SARBconst, 374 ssa.Op386ROLLconst, ssa.Op386ROLWconst, ssa.Op386ROLBconst: 375 r := gc.SSARegNum(v) 376 if r != gc.SSARegNum(v.Args[0]) { 377 v.Fatalf("input[0] and output not in same register %s", v.LongString()) 378 } 379 p := gc.Prog(v.Op.Asm()) 380 p.From.Type = obj.TYPE_CONST 381 p.From.Offset = v.AuxInt 382 p.To.Type = obj.TYPE_REG 383 p.To.Reg = r 384 case ssa.Op386SBBLcarrymask: 385 r := gc.SSARegNum(v) 386 p := gc.Prog(v.Op.Asm()) 387 p.From.Type = obj.TYPE_REG 388 p.From.Reg = r 389 p.To.Type = obj.TYPE_REG 390 p.To.Reg = r 391 case ssa.Op386LEAL1, ssa.Op386LEAL2, ssa.Op386LEAL4, ssa.Op386LEAL8: 392 r := gc.SSARegNum(v.Args[0]) 393 i := gc.SSARegNum(v.Args[1]) 394 p := gc.Prog(x86.ALEAL) 395 switch v.Op { 396 case ssa.Op386LEAL1: 397 p.From.Scale = 1 398 if i == x86.REG_SP { 399 r, i = i, r 400 } 401 case ssa.Op386LEAL2: 402 p.From.Scale = 2 403 case ssa.Op386LEAL4: 404 p.From.Scale = 4 405 case ssa.Op386LEAL8: 406 p.From.Scale = 8 407 } 408 p.From.Type = obj.TYPE_MEM 409 p.From.Reg = r 410 p.From.Index = i 411 gc.AddAux(&p.From, v) 412 p.To.Type = obj.TYPE_REG 413 p.To.Reg = gc.SSARegNum(v) 414 case ssa.Op386LEAL: 415 p := gc.Prog(x86.ALEAL) 416 p.From.Type = obj.TYPE_MEM 417 p.From.Reg = gc.SSARegNum(v.Args[0]) 418 gc.AddAux(&p.From, v) 419 p.To.Type = obj.TYPE_REG 420 p.To.Reg = gc.SSARegNum(v) 421 case ssa.Op386CMPL, ssa.Op386CMPW, ssa.Op386CMPB, 422 ssa.Op386TESTL, ssa.Op386TESTW, ssa.Op386TESTB: 423 opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[1]), gc.SSARegNum(v.Args[0])) 424 case ssa.Op386UCOMISS, ssa.Op386UCOMISD: 425 // Go assembler has swapped operands for UCOMISx relative to CMP, 426 // must account for that right here. 427 opregreg(v.Op.Asm(), gc.SSARegNum(v.Args[0]), gc.SSARegNum(v.Args[1])) 428 case ssa.Op386CMPLconst, ssa.Op386CMPWconst, ssa.Op386CMPBconst: 429 p := gc.Prog(v.Op.Asm()) 430 p.From.Type = obj.TYPE_REG 431 p.From.Reg = gc.SSARegNum(v.Args[0]) 432 p.To.Type = obj.TYPE_CONST 433 p.To.Offset = v.AuxInt 434 case ssa.Op386TESTLconst, ssa.Op386TESTWconst, ssa.Op386TESTBconst: 435 p := gc.Prog(v.Op.Asm()) 436 p.From.Type = obj.TYPE_CONST 437 p.From.Offset = v.AuxInt 438 p.To.Type = obj.TYPE_REG 439 p.To.Reg = gc.SSARegNum(v.Args[0]) 440 case ssa.Op386MOVLconst: 441 x := gc.SSARegNum(v) 442 p := gc.Prog(v.Op.Asm()) 443 p.From.Type = obj.TYPE_CONST 444 p.From.Offset = v.AuxInt 445 p.To.Type = obj.TYPE_REG 446 p.To.Reg = x 447 // If flags are live at this instruction, suppress the 448 // MOV $0,AX -> XOR AX,AX optimization. 449 if v.Aux != nil { 450 p.Mark |= x86.PRESERVEFLAGS 451 } 452 case ssa.Op386MOVSSconst, ssa.Op386MOVSDconst: 453 x := gc.SSARegNum(v) 454 p := gc.Prog(v.Op.Asm()) 455 p.From.Type = obj.TYPE_FCONST 456 p.From.Val = math.Float64frombits(uint64(v.AuxInt)) 457 p.To.Type = obj.TYPE_REG 458 p.To.Reg = x 459 case ssa.Op386MOVSSconst1, ssa.Op386MOVSDconst1: 460 var literal string 461 if v.Op == ssa.Op386MOVSDconst1 { 462 literal = fmt.Sprintf("$f64.%016x", uint64(v.AuxInt)) 463 } else { 464 literal = fmt.Sprintf("$f32.%08x", math.Float32bits(float32(math.Float64frombits(uint64(v.AuxInt))))) 465 } 466 p := gc.Prog(x86.ALEAL) 467 p.From.Type = obj.TYPE_MEM 468 p.From.Name = obj.NAME_EXTERN 469 p.From.Sym = obj.Linklookup(gc.Ctxt, literal, 0) 470 p.From.Sym.Local = true 471 p.To.Type = obj.TYPE_REG 472 p.To.Reg = gc.SSARegNum(v) 473 case ssa.Op386MOVSSconst2, ssa.Op386MOVSDconst2: 474 p := gc.Prog(v.Op.Asm()) 475 p.From.Type = obj.TYPE_MEM 476 p.From.Reg = gc.SSARegNum(v.Args[0]) 477 p.To.Type = obj.TYPE_REG 478 p.To.Reg = gc.SSARegNum(v) 479 480 case ssa.Op386MOVSSload, ssa.Op386MOVSDload, ssa.Op386MOVLload, ssa.Op386MOVWload, ssa.Op386MOVBload, ssa.Op386MOVBLSXload, ssa.Op386MOVWLSXload: 481 p := gc.Prog(v.Op.Asm()) 482 p.From.Type = obj.TYPE_MEM 483 p.From.Reg = gc.SSARegNum(v.Args[0]) 484 gc.AddAux(&p.From, v) 485 p.To.Type = obj.TYPE_REG 486 p.To.Reg = gc.SSARegNum(v) 487 case ssa.Op386MOVSDloadidx8: 488 p := gc.Prog(v.Op.Asm()) 489 p.From.Type = obj.TYPE_MEM 490 p.From.Reg = gc.SSARegNum(v.Args[0]) 491 gc.AddAux(&p.From, v) 492 p.From.Scale = 8 493 p.From.Index = gc.SSARegNum(v.Args[1]) 494 p.To.Type = obj.TYPE_REG 495 p.To.Reg = gc.SSARegNum(v) 496 case ssa.Op386MOVLloadidx4, ssa.Op386MOVSSloadidx4: 497 p := gc.Prog(v.Op.Asm()) 498 p.From.Type = obj.TYPE_MEM 499 p.From.Reg = gc.SSARegNum(v.Args[0]) 500 gc.AddAux(&p.From, v) 501 p.From.Scale = 4 502 p.From.Index = gc.SSARegNum(v.Args[1]) 503 p.To.Type = obj.TYPE_REG 504 p.To.Reg = gc.SSARegNum(v) 505 case ssa.Op386MOVWloadidx2: 506 p := gc.Prog(v.Op.Asm()) 507 p.From.Type = obj.TYPE_MEM 508 p.From.Reg = gc.SSARegNum(v.Args[0]) 509 gc.AddAux(&p.From, v) 510 p.From.Scale = 2 511 p.From.Index = gc.SSARegNum(v.Args[1]) 512 p.To.Type = obj.TYPE_REG 513 p.To.Reg = gc.SSARegNum(v) 514 case ssa.Op386MOVBloadidx1, ssa.Op386MOVWloadidx1, ssa.Op386MOVLloadidx1, ssa.Op386MOVSSloadidx1, ssa.Op386MOVSDloadidx1: 515 r := gc.SSARegNum(v.Args[0]) 516 i := gc.SSARegNum(v.Args[1]) 517 if i == x86.REG_SP { 518 r, i = i, r 519 } 520 p := gc.Prog(v.Op.Asm()) 521 p.From.Type = obj.TYPE_MEM 522 p.From.Reg = r 523 p.From.Scale = 1 524 p.From.Index = i 525 gc.AddAux(&p.From, v) 526 p.To.Type = obj.TYPE_REG 527 p.To.Reg = gc.SSARegNum(v) 528 case ssa.Op386MOVSSstore, ssa.Op386MOVSDstore, ssa.Op386MOVLstore, ssa.Op386MOVWstore, ssa.Op386MOVBstore: 529 p := gc.Prog(v.Op.Asm()) 530 p.From.Type = obj.TYPE_REG 531 p.From.Reg = gc.SSARegNum(v.Args[1]) 532 p.To.Type = obj.TYPE_MEM 533 p.To.Reg = gc.SSARegNum(v.Args[0]) 534 gc.AddAux(&p.To, v) 535 case ssa.Op386MOVSDstoreidx8: 536 p := gc.Prog(v.Op.Asm()) 537 p.From.Type = obj.TYPE_REG 538 p.From.Reg = gc.SSARegNum(v.Args[2]) 539 p.To.Type = obj.TYPE_MEM 540 p.To.Reg = gc.SSARegNum(v.Args[0]) 541 p.To.Scale = 8 542 p.To.Index = gc.SSARegNum(v.Args[1]) 543 gc.AddAux(&p.To, v) 544 case ssa.Op386MOVSSstoreidx4, ssa.Op386MOVLstoreidx4: 545 p := gc.Prog(v.Op.Asm()) 546 p.From.Type = obj.TYPE_REG 547 p.From.Reg = gc.SSARegNum(v.Args[2]) 548 p.To.Type = obj.TYPE_MEM 549 p.To.Reg = gc.SSARegNum(v.Args[0]) 550 p.To.Scale = 4 551 p.To.Index = gc.SSARegNum(v.Args[1]) 552 gc.AddAux(&p.To, v) 553 case ssa.Op386MOVWstoreidx2: 554 p := gc.Prog(v.Op.Asm()) 555 p.From.Type = obj.TYPE_REG 556 p.From.Reg = gc.SSARegNum(v.Args[2]) 557 p.To.Type = obj.TYPE_MEM 558 p.To.Reg = gc.SSARegNum(v.Args[0]) 559 p.To.Scale = 2 560 p.To.Index = gc.SSARegNum(v.Args[1]) 561 gc.AddAux(&p.To, v) 562 case ssa.Op386MOVBstoreidx1, ssa.Op386MOVWstoreidx1, ssa.Op386MOVLstoreidx1, ssa.Op386MOVSSstoreidx1, ssa.Op386MOVSDstoreidx1: 563 r := gc.SSARegNum(v.Args[0]) 564 i := gc.SSARegNum(v.Args[1]) 565 if i == x86.REG_SP { 566 r, i = i, r 567 } 568 p := gc.Prog(v.Op.Asm()) 569 p.From.Type = obj.TYPE_REG 570 p.From.Reg = gc.SSARegNum(v.Args[2]) 571 p.To.Type = obj.TYPE_MEM 572 p.To.Reg = r 573 p.To.Scale = 1 574 p.To.Index = i 575 gc.AddAux(&p.To, v) 576 case ssa.Op386MOVLstoreconst, ssa.Op386MOVWstoreconst, ssa.Op386MOVBstoreconst: 577 p := gc.Prog(v.Op.Asm()) 578 p.From.Type = obj.TYPE_CONST 579 sc := v.AuxValAndOff() 580 p.From.Offset = sc.Val() 581 p.To.Type = obj.TYPE_MEM 582 p.To.Reg = gc.SSARegNum(v.Args[0]) 583 gc.AddAux2(&p.To, v, sc.Off()) 584 case ssa.Op386MOVLstoreconstidx1, ssa.Op386MOVLstoreconstidx4, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVWstoreconstidx2, ssa.Op386MOVBstoreconstidx1: 585 p := gc.Prog(v.Op.Asm()) 586 p.From.Type = obj.TYPE_CONST 587 sc := v.AuxValAndOff() 588 p.From.Offset = sc.Val() 589 r := gc.SSARegNum(v.Args[0]) 590 i := gc.SSARegNum(v.Args[1]) 591 switch v.Op { 592 case ssa.Op386MOVBstoreconstidx1, ssa.Op386MOVWstoreconstidx1, ssa.Op386MOVLstoreconstidx1: 593 p.To.Scale = 1 594 if i == x86.REG_SP { 595 r, i = i, r 596 } 597 case ssa.Op386MOVWstoreconstidx2: 598 p.To.Scale = 2 599 case ssa.Op386MOVLstoreconstidx4: 600 p.To.Scale = 4 601 } 602 p.To.Type = obj.TYPE_MEM 603 p.To.Reg = r 604 p.To.Index = i 605 gc.AddAux2(&p.To, v, sc.Off()) 606 case ssa.Op386MOVWLSX, ssa.Op386MOVBLSX, ssa.Op386MOVWLZX, ssa.Op386MOVBLZX, 607 ssa.Op386CVTSL2SS, ssa.Op386CVTSL2SD, 608 ssa.Op386CVTTSS2SL, ssa.Op386CVTTSD2SL, 609 ssa.Op386CVTSS2SD, ssa.Op386CVTSD2SS: 610 opregreg(v.Op.Asm(), gc.SSARegNum(v), gc.SSARegNum(v.Args[0])) 611 case ssa.Op386DUFFZERO: 612 p := gc.Prog(obj.ADUFFZERO) 613 p.To.Type = obj.TYPE_ADDR 614 p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) 615 p.To.Offset = v.AuxInt 616 case ssa.Op386DUFFCOPY: 617 p := gc.Prog(obj.ADUFFCOPY) 618 p.To.Type = obj.TYPE_ADDR 619 p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) 620 p.To.Offset = v.AuxInt 621 622 case ssa.OpCopy, ssa.Op386MOVLconvert: // TODO: use MOVLreg for reg->reg copies instead of OpCopy? 623 if v.Type.IsMemory() { 624 return 625 } 626 x := gc.SSARegNum(v.Args[0]) 627 y := gc.SSARegNum(v) 628 if x != y { 629 opregreg(moveByType(v.Type), y, x) 630 } 631 case ssa.OpLoadReg: 632 if v.Type.IsFlags() { 633 v.Unimplementedf("load flags not implemented: %v", v.LongString()) 634 return 635 } 636 p := gc.Prog(loadByType(v.Type)) 637 n, off := gc.AutoVar(v.Args[0]) 638 p.From.Type = obj.TYPE_MEM 639 p.From.Node = n 640 p.From.Sym = gc.Linksym(n.Sym) 641 p.From.Offset = off 642 if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT { 643 p.From.Name = obj.NAME_PARAM 644 p.From.Offset += n.Xoffset 645 } else { 646 p.From.Name = obj.NAME_AUTO 647 } 648 p.To.Type = obj.TYPE_REG 649 p.To.Reg = gc.SSARegNum(v) 650 651 case ssa.OpStoreReg: 652 if v.Type.IsFlags() { 653 v.Unimplementedf("store flags not implemented: %v", v.LongString()) 654 return 655 } 656 p := gc.Prog(storeByType(v.Type)) 657 p.From.Type = obj.TYPE_REG 658 p.From.Reg = gc.SSARegNum(v.Args[0]) 659 n, off := gc.AutoVar(v) 660 p.To.Type = obj.TYPE_MEM 661 p.To.Node = n 662 p.To.Sym = gc.Linksym(n.Sym) 663 p.To.Offset = off 664 if n.Class == gc.PPARAM || n.Class == gc.PPARAMOUT { 665 p.To.Name = obj.NAME_PARAM 666 p.To.Offset += n.Xoffset 667 } else { 668 p.To.Name = obj.NAME_AUTO 669 } 670 case ssa.OpPhi: 671 gc.CheckLoweredPhi(v) 672 case ssa.OpInitMem: 673 // memory arg needs no code 674 case ssa.OpArg: 675 // input args need no code 676 case ssa.Op386LoweredGetClosurePtr: 677 // Closure pointer is DX. 678 gc.CheckLoweredGetClosurePtr(v) 679 case ssa.Op386LoweredGetG: 680 r := gc.SSARegNum(v) 681 // See the comments in cmd/internal/obj/x86/obj6.go 682 // near CanUse1InsnTLS for a detailed explanation of these instructions. 683 if x86.CanUse1InsnTLS(gc.Ctxt) { 684 // MOVL (TLS), r 685 p := gc.Prog(x86.AMOVL) 686 p.From.Type = obj.TYPE_MEM 687 p.From.Reg = x86.REG_TLS 688 p.To.Type = obj.TYPE_REG 689 p.To.Reg = r 690 } else { 691 // MOVL TLS, r 692 // MOVL (r)(TLS*1), r 693 p := gc.Prog(x86.AMOVL) 694 p.From.Type = obj.TYPE_REG 695 p.From.Reg = x86.REG_TLS 696 p.To.Type = obj.TYPE_REG 697 p.To.Reg = r 698 q := gc.Prog(x86.AMOVL) 699 q.From.Type = obj.TYPE_MEM 700 q.From.Reg = r 701 q.From.Index = x86.REG_TLS 702 q.From.Scale = 1 703 q.To.Type = obj.TYPE_REG 704 q.To.Reg = r 705 } 706 case ssa.Op386CALLstatic: 707 if v.Aux.(*gc.Sym) == gc.Deferreturn.Sym { 708 // Deferred calls will appear to be returning to 709 // the CALL deferreturn(SB) that we are about to emit. 710 // However, the stack trace code will show the line 711 // of the instruction byte before the return PC. 712 // To avoid that being an unrelated instruction, 713 // insert an actual hardware NOP that will have the right line number. 714 // This is different from obj.ANOP, which is a virtual no-op 715 // that doesn't make it into the instruction stream. 716 ginsnop() 717 } 718 p := gc.Prog(obj.ACALL) 719 p.To.Type = obj.TYPE_MEM 720 p.To.Name = obj.NAME_EXTERN 721 p.To.Sym = gc.Linksym(v.Aux.(*gc.Sym)) 722 if gc.Maxarg < v.AuxInt { 723 gc.Maxarg = v.AuxInt 724 } 725 case ssa.Op386CALLclosure: 726 p := gc.Prog(obj.ACALL) 727 p.To.Type = obj.TYPE_REG 728 p.To.Reg = gc.SSARegNum(v.Args[0]) 729 if gc.Maxarg < v.AuxInt { 730 gc.Maxarg = v.AuxInt 731 } 732 case ssa.Op386CALLdefer: 733 p := gc.Prog(obj.ACALL) 734 p.To.Type = obj.TYPE_MEM 735 p.To.Name = obj.NAME_EXTERN 736 p.To.Sym = gc.Linksym(gc.Deferproc.Sym) 737 if gc.Maxarg < v.AuxInt { 738 gc.Maxarg = v.AuxInt 739 } 740 case ssa.Op386CALLgo: 741 p := gc.Prog(obj.ACALL) 742 p.To.Type = obj.TYPE_MEM 743 p.To.Name = obj.NAME_EXTERN 744 p.To.Sym = gc.Linksym(gc.Newproc.Sym) 745 if gc.Maxarg < v.AuxInt { 746 gc.Maxarg = v.AuxInt 747 } 748 case ssa.Op386CALLinter: 749 p := gc.Prog(obj.ACALL) 750 p.To.Type = obj.TYPE_REG 751 p.To.Reg = gc.SSARegNum(v.Args[0]) 752 if gc.Maxarg < v.AuxInt { 753 gc.Maxarg = v.AuxInt 754 } 755 case ssa.Op386NEGL, 756 ssa.Op386BSWAPL, 757 ssa.Op386NOTL: 758 r := gc.SSARegNum(v) 759 if r != gc.SSARegNum(v.Args[0]) { 760 v.Fatalf("input[0] and output not in same register %s", v.LongString()) 761 } 762 p := gc.Prog(v.Op.Asm()) 763 p.To.Type = obj.TYPE_REG 764 p.To.Reg = r 765 case ssa.Op386BSFL, ssa.Op386BSFW, 766 ssa.Op386BSRL, ssa.Op386BSRW, 767 ssa.Op386SQRTSD: 768 p := gc.Prog(v.Op.Asm()) 769 p.From.Type = obj.TYPE_REG 770 p.From.Reg = gc.SSARegNum(v.Args[0]) 771 p.To.Type = obj.TYPE_REG 772 p.To.Reg = gc.SSARegNum(v) 773 case ssa.OpSP, ssa.OpSB, ssa.OpSelect0, ssa.OpSelect1: 774 // nothing to do 775 case ssa.Op386SETEQ, ssa.Op386SETNE, 776 ssa.Op386SETL, ssa.Op386SETLE, 777 ssa.Op386SETG, ssa.Op386SETGE, 778 ssa.Op386SETGF, ssa.Op386SETGEF, 779 ssa.Op386SETB, ssa.Op386SETBE, 780 ssa.Op386SETORD, ssa.Op386SETNAN, 781 ssa.Op386SETA, ssa.Op386SETAE: 782 p := gc.Prog(v.Op.Asm()) 783 p.To.Type = obj.TYPE_REG 784 p.To.Reg = gc.SSARegNum(v) 785 786 case ssa.Op386SETNEF: 787 p := gc.Prog(v.Op.Asm()) 788 p.To.Type = obj.TYPE_REG 789 p.To.Reg = gc.SSARegNum(v) 790 q := gc.Prog(x86.ASETPS) 791 q.To.Type = obj.TYPE_REG 792 q.To.Reg = x86.REG_AX 793 opregreg(x86.AORL, gc.SSARegNum(v), x86.REG_AX) 794 795 case ssa.Op386SETEQF: 796 p := gc.Prog(v.Op.Asm()) 797 p.To.Type = obj.TYPE_REG 798 p.To.Reg = gc.SSARegNum(v) 799 q := gc.Prog(x86.ASETPC) 800 q.To.Type = obj.TYPE_REG 801 q.To.Reg = x86.REG_AX 802 opregreg(x86.AANDL, gc.SSARegNum(v), x86.REG_AX) 803 804 case ssa.Op386InvertFlags: 805 v.Fatalf("InvertFlags should never make it to codegen %v", v.LongString()) 806 case ssa.Op386FlagEQ, ssa.Op386FlagLT_ULT, ssa.Op386FlagLT_UGT, ssa.Op386FlagGT_ULT, ssa.Op386FlagGT_UGT: 807 v.Fatalf("Flag* ops should never make it to codegen %v", v.LongString()) 808 case ssa.Op386REPSTOSL: 809 gc.Prog(x86.AREP) 810 gc.Prog(x86.ASTOSL) 811 case ssa.Op386REPMOVSL: 812 gc.Prog(x86.AREP) 813 gc.Prog(x86.AMOVSL) 814 case ssa.OpVarDef: 815 gc.Gvardef(v.Aux.(*gc.Node)) 816 case ssa.OpVarKill: 817 gc.Gvarkill(v.Aux.(*gc.Node)) 818 case ssa.OpVarLive: 819 gc.Gvarlive(v.Aux.(*gc.Node)) 820 case ssa.OpKeepAlive: 821 if !v.Args[0].Type.IsPtrShaped() { 822 v.Fatalf("keeping non-pointer alive %v", v.Args[0]) 823 } 824 n, off := gc.AutoVar(v.Args[0]) 825 if n == nil { 826 v.Fatalf("KeepLive with non-spilled value %s %s", v, v.Args[0]) 827 } 828 if off != 0 { 829 v.Fatalf("KeepLive with non-zero offset spill location %s:%d", n, off) 830 } 831 gc.Gvarlive(n) 832 case ssa.Op386LoweredNilCheck: 833 // Optimization - if the subsequent block has a load or store 834 // at the same address, we don't need to issue this instruction. 835 mem := v.Args[1] 836 for _, w := range v.Block.Succs[0].Block().Values { 837 if w.Op == ssa.OpPhi { 838 if w.Type.IsMemory() { 839 mem = w 840 } 841 continue 842 } 843 if len(w.Args) == 0 || !w.Args[len(w.Args)-1].Type.IsMemory() { 844 // w doesn't use a store - can't be a memory op. 845 continue 846 } 847 if w.Args[len(w.Args)-1] != mem { 848 v.Fatalf("wrong store after nilcheck v=%s w=%s", v, w) 849 } 850 switch w.Op { 851 case ssa.Op386MOVLload, ssa.Op386MOVWload, ssa.Op386MOVBload, 852 ssa.Op386MOVLstore, ssa.Op386MOVWstore, ssa.Op386MOVBstore, 853 ssa.Op386MOVBLSXload, ssa.Op386MOVWLSXload, 854 ssa.Op386MOVSSload, ssa.Op386MOVSDload, 855 ssa.Op386MOVSSstore, ssa.Op386MOVSDstore: 856 if w.Args[0] == v.Args[0] && w.Aux == nil && w.AuxInt >= 0 && w.AuxInt < minZeroPage { 857 if gc.Debug_checknil != 0 && int(v.Line) > 1 { 858 gc.Warnl(v.Line, "removed nil check") 859 } 860 return 861 } 862 case ssa.Op386MOVLstoreconst, ssa.Op386MOVWstoreconst, ssa.Op386MOVBstoreconst: 863 off := ssa.ValAndOff(v.AuxInt).Off() 864 if w.Args[0] == v.Args[0] && w.Aux == nil && off >= 0 && off < minZeroPage { 865 if gc.Debug_checknil != 0 && int(v.Line) > 1 { 866 gc.Warnl(v.Line, "removed nil check") 867 } 868 return 869 } 870 } 871 if w.Type.IsMemory() { 872 if w.Op == ssa.OpVarDef || w.Op == ssa.OpVarKill || w.Op == ssa.OpVarLive { 873 // these ops are OK 874 mem = w 875 continue 876 } 877 // We can't delay the nil check past the next store. 878 break 879 } 880 } 881 // Issue a load which will fault if the input is nil. 882 // TODO: We currently use the 2-byte instruction TESTB AX, (reg). 883 // Should we use the 3-byte TESTB $0, (reg) instead? It is larger 884 // but it doesn't have false dependency on AX. 885 // Or maybe allocate an output register and use MOVL (reg),reg2 ? 886 // That trades clobbering flags for clobbering a register. 887 p := gc.Prog(x86.ATESTB) 888 p.From.Type = obj.TYPE_REG 889 p.From.Reg = x86.REG_AX 890 p.To.Type = obj.TYPE_MEM 891 p.To.Reg = gc.SSARegNum(v.Args[0]) 892 gc.AddAux(&p.To, v) 893 if gc.Debug_checknil != 0 && v.Line > 1 { // v.Line==1 in generated wrappers 894 gc.Warnl(v.Line, "generated nil check") 895 } 896 case ssa.Op386FCHS: 897 v.Fatalf("FCHS in non-387 mode") 898 default: 899 v.Unimplementedf("genValue not implemented: %s", v.LongString()) 900 } 901 } 902 903 var blockJump = [...]struct { 904 asm, invasm obj.As 905 }{ 906 ssa.Block386EQ: {x86.AJEQ, x86.AJNE}, 907 ssa.Block386NE: {x86.AJNE, x86.AJEQ}, 908 ssa.Block386LT: {x86.AJLT, x86.AJGE}, 909 ssa.Block386GE: {x86.AJGE, x86.AJLT}, 910 ssa.Block386LE: {x86.AJLE, x86.AJGT}, 911 ssa.Block386GT: {x86.AJGT, x86.AJLE}, 912 ssa.Block386ULT: {x86.AJCS, x86.AJCC}, 913 ssa.Block386UGE: {x86.AJCC, x86.AJCS}, 914 ssa.Block386UGT: {x86.AJHI, x86.AJLS}, 915 ssa.Block386ULE: {x86.AJLS, x86.AJHI}, 916 ssa.Block386ORD: {x86.AJPC, x86.AJPS}, 917 ssa.Block386NAN: {x86.AJPS, x86.AJPC}, 918 } 919 920 var eqfJumps = [2][2]gc.FloatingEQNEJump{ 921 {{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPS, Index: 1}}, // next == b.Succs[0] 922 {{Jump: x86.AJNE, Index: 1}, {Jump: x86.AJPC, Index: 0}}, // next == b.Succs[1] 923 } 924 var nefJumps = [2][2]gc.FloatingEQNEJump{ 925 {{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPC, Index: 1}}, // next == b.Succs[0] 926 {{Jump: x86.AJNE, Index: 0}, {Jump: x86.AJPS, Index: 0}}, // next == b.Succs[1] 927 } 928 929 func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) { 930 s.SetLineno(b.Line) 931 932 if gc.Thearch.Use387 { 933 // Empty the 387's FP stack before the block ends. 934 flush387(s) 935 } 936 937 switch b.Kind { 938 case ssa.BlockPlain, ssa.BlockCall, ssa.BlockCheck: 939 if b.Succs[0].Block() != next { 940 p := gc.Prog(obj.AJMP) 941 p.To.Type = obj.TYPE_BRANCH 942 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 943 } 944 case ssa.BlockDefer: 945 // defer returns in rax: 946 // 0 if we should continue executing 947 // 1 if we should jump to deferreturn call 948 p := gc.Prog(x86.ATESTL) 949 p.From.Type = obj.TYPE_REG 950 p.From.Reg = x86.REG_AX 951 p.To.Type = obj.TYPE_REG 952 p.To.Reg = x86.REG_AX 953 p = gc.Prog(x86.AJNE) 954 p.To.Type = obj.TYPE_BRANCH 955 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 956 if b.Succs[0].Block() != next { 957 p := gc.Prog(obj.AJMP) 958 p.To.Type = obj.TYPE_BRANCH 959 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 960 } 961 case ssa.BlockExit: 962 gc.Prog(obj.AUNDEF) // tell plive.go that we never reach here 963 case ssa.BlockRet: 964 gc.Prog(obj.ARET) 965 case ssa.BlockRetJmp: 966 p := gc.Prog(obj.AJMP) 967 p.To.Type = obj.TYPE_MEM 968 p.To.Name = obj.NAME_EXTERN 969 p.To.Sym = gc.Linksym(b.Aux.(*gc.Sym)) 970 971 case ssa.Block386EQF: 972 gc.SSAGenFPJump(s, b, next, &eqfJumps) 973 974 case ssa.Block386NEF: 975 gc.SSAGenFPJump(s, b, next, &nefJumps) 976 977 case ssa.Block386EQ, ssa.Block386NE, 978 ssa.Block386LT, ssa.Block386GE, 979 ssa.Block386LE, ssa.Block386GT, 980 ssa.Block386ULT, ssa.Block386UGT, 981 ssa.Block386ULE, ssa.Block386UGE: 982 jmp := blockJump[b.Kind] 983 likely := b.Likely 984 var p *obj.Prog 985 switch next { 986 case b.Succs[0].Block(): 987 p = gc.Prog(jmp.invasm) 988 likely *= -1 989 p.To.Type = obj.TYPE_BRANCH 990 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()}) 991 case b.Succs[1].Block(): 992 p = gc.Prog(jmp.asm) 993 p.To.Type = obj.TYPE_BRANCH 994 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 995 default: 996 p = gc.Prog(jmp.asm) 997 p.To.Type = obj.TYPE_BRANCH 998 s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()}) 999 q := gc.Prog(obj.AJMP) 1000 q.To.Type = obj.TYPE_BRANCH 1001 s.Branches = append(s.Branches, gc.Branch{P: q, B: b.Succs[1].Block()}) 1002 } 1003 1004 // liblink reorders the instruction stream as it sees fit. 1005 // Pass along what we know so liblink can make use of it. 1006 // TODO: Once we've fully switched to SSA, 1007 // make liblink leave our output alone. 1008 switch likely { 1009 case ssa.BranchUnlikely: 1010 p.From.Type = obj.TYPE_CONST 1011 p.From.Offset = 0 1012 case ssa.BranchLikely: 1013 p.From.Type = obj.TYPE_CONST 1014 p.From.Offset = 1 1015 } 1016 1017 default: 1018 b.Unimplementedf("branch not implemented: %s. Control: %s", b.LongString(), b.Control.LongString()) 1019 } 1020 }