github.com/muesli/go@v0.0.0-20170208044820-e410d2a81ef2/src/cmd/internal/obj/x86/obj6.go (about) 1 // Inferno utils/6l/pass.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/pass.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package x86 32 33 import ( 34 "cmd/internal/obj" 35 "cmd/internal/sys" 36 "fmt" 37 "math" 38 "strings" 39 ) 40 41 func CanUse1InsnTLS(ctxt *obj.Link) bool { 42 if isAndroid { 43 // For android, we use a disgusting hack that assumes 44 // the thread-local storage slot for g is allocated 45 // using pthread_key_create with a fixed offset 46 // (see src/runtime/cgo/gcc_android_amd64.c). 47 // This makes access to the TLS storage (for g) doable 48 // with 1 instruction. 49 return true 50 } 51 52 if ctxt.Arch.RegSize == 4 { 53 switch ctxt.Headtype { 54 case obj.Hlinux, 55 obj.Hnacl, 56 obj.Hplan9, 57 obj.Hwindows, 58 obj.Hwindowsgui: 59 return false 60 } 61 62 return true 63 } 64 65 switch ctxt.Headtype { 66 case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui: 67 return false 68 case obj.Hlinux: 69 return !ctxt.Flag_shared 70 } 71 72 return true 73 } 74 75 func progedit(ctxt *obj.Link, p *obj.Prog) { 76 // Maintain information about code generation mode. 77 if ctxt.Mode == 0 { 78 ctxt.Mode = ctxt.Arch.RegSize * 8 79 } 80 p.Mode = int8(ctxt.Mode) 81 82 switch p.As { 83 case AMODE: 84 if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) { 85 switch int(p.From.Offset) { 86 case 16, 32, 64: 87 ctxt.Mode = int(p.From.Offset) 88 } 89 } 90 obj.Nopout(p) 91 } 92 93 // Thread-local storage references use the TLS pseudo-register. 94 // As a register, TLS refers to the thread-local storage base, and it 95 // can only be loaded into another register: 96 // 97 // MOVQ TLS, AX 98 // 99 // An offset from the thread-local storage base is written off(reg)(TLS*1). 100 // Semantically it is off(reg), but the (TLS*1) annotation marks this as 101 // indexing from the loaded TLS base. This emits a relocation so that 102 // if the linker needs to adjust the offset, it can. For example: 103 // 104 // MOVQ TLS, AX 105 // MOVQ 0(AX)(TLS*1), CX // load g into CX 106 // 107 // On systems that support direct access to the TLS memory, this 108 // pair of instructions can be reduced to a direct TLS memory reference: 109 // 110 // MOVQ 0(TLS), CX // load g into CX 111 // 112 // The 2-instruction and 1-instruction forms correspond to the two code 113 // sequences for loading a TLS variable in the local exec model given in "ELF 114 // Handling For Thread-Local Storage". 115 // 116 // We apply this rewrite on systems that support the 1-instruction form. 117 // The decision is made using only the operating system and the -shared flag, 118 // not the link mode. If some link modes on a particular operating system 119 // require the 2-instruction form, then all builds for that operating system 120 // will use the 2-instruction form, so that the link mode decision can be 121 // delayed to link time. 122 // 123 // In this way, all supported systems use identical instructions to 124 // access TLS, and they are rewritten appropriately first here in 125 // liblink and then finally using relocations in the linker. 126 // 127 // When -shared is passed, we leave the code in the 2-instruction form but 128 // assemble (and relocate) them in different ways to generate the initial 129 // exec code sequence. It's a bit of a fluke that this is possible without 130 // rewriting the instructions more comprehensively, and it only does because 131 // we only support a single TLS variable (g). 132 133 if CanUse1InsnTLS(ctxt) { 134 // Reduce 2-instruction sequence to 1-instruction sequence. 135 // Sequences like 136 // MOVQ TLS, BX 137 // ... off(BX)(TLS*1) ... 138 // become 139 // NOP 140 // ... off(TLS) ... 141 // 142 // TODO(rsc): Remove the Hsolaris special case. It exists only to 143 // guarantee we are producing byte-identical binaries as before this code. 144 // But it should be unnecessary. 145 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != obj.Hsolaris { 146 obj.Nopout(p) 147 } 148 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { 149 p.From.Reg = REG_TLS 150 p.From.Scale = 0 151 p.From.Index = REG_NONE 152 } 153 154 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 155 p.To.Reg = REG_TLS 156 p.To.Scale = 0 157 p.To.Index = REG_NONE 158 } 159 } else { 160 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it 161 // as the 2-instruction sequence if necessary. 162 // MOVQ 0(TLS), BX 163 // becomes 164 // MOVQ TLS, BX 165 // MOVQ 0(BX)(TLS*1), BX 166 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 167 q := obj.Appendp(ctxt, p) 168 q.As = p.As 169 q.From = p.From 170 q.From.Type = obj.TYPE_MEM 171 q.From.Reg = p.To.Reg 172 q.From.Index = REG_TLS 173 q.From.Scale = 2 // TODO: use 1 174 q.To = p.To 175 p.From.Type = obj.TYPE_REG 176 p.From.Reg = REG_TLS 177 p.From.Index = REG_NONE 178 p.From.Offset = 0 179 } 180 } 181 182 // TODO: Remove. 183 if (ctxt.Headtype == obj.Hwindows || ctxt.Headtype == obj.Hwindowsgui) && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 { 184 if p.From.Scale == 1 && p.From.Index == REG_TLS { 185 p.From.Scale = 2 186 } 187 if p.To.Scale == 1 && p.To.Index == REG_TLS { 188 p.To.Scale = 2 189 } 190 } 191 192 // Rewrite 0 to $0 in 3rd argument to CMPPS etc. 193 // That's what the tables expect. 194 switch p.As { 195 case ACMPPD, ACMPPS, ACMPSD, ACMPSS: 196 if p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_NONE && p.To.Reg == REG_NONE && p.To.Index == REG_NONE && p.To.Sym == nil { 197 p.To.Type = obj.TYPE_CONST 198 } 199 } 200 201 // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH. 202 switch p.As { 203 case obj.ACALL, obj.AJMP, obj.ARET: 204 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 205 p.To.Type = obj.TYPE_BRANCH 206 } 207 } 208 209 // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ. 210 if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { 211 switch p.As { 212 case AMOVL: 213 p.As = ALEAL 214 p.From.Type = obj.TYPE_MEM 215 case AMOVQ: 216 p.As = ALEAQ 217 p.From.Type = obj.TYPE_MEM 218 } 219 } 220 221 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 222 if p.From3 != nil { 223 nacladdr(ctxt, p, p.From3) 224 } 225 nacladdr(ctxt, p, &p.From) 226 nacladdr(ctxt, p, &p.To) 227 } 228 229 // Rewrite float constants to values stored in memory. 230 switch p.As { 231 // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx 232 case AMOVSS: 233 if p.From.Type == obj.TYPE_FCONST { 234 // f == 0 can't be used here due to -0, so use Float64bits 235 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 236 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 237 p.As = AXORPS 238 p.From = p.To 239 break 240 } 241 } 242 } 243 fallthrough 244 245 case AFMOVF, 246 AFADDF, 247 AFSUBF, 248 AFSUBRF, 249 AFMULF, 250 AFDIVF, 251 AFDIVRF, 252 AFCOMF, 253 AFCOMFP, 254 AADDSS, 255 ASUBSS, 256 AMULSS, 257 ADIVSS, 258 ACOMISS, 259 AUCOMISS: 260 if p.From.Type == obj.TYPE_FCONST { 261 f32 := float32(p.From.Val.(float64)) 262 i32 := math.Float32bits(f32) 263 literal := fmt.Sprintf("$f32.%08x", i32) 264 s := obj.Linklookup(ctxt, literal, 0) 265 p.From.Type = obj.TYPE_MEM 266 p.From.Name = obj.NAME_EXTERN 267 p.From.Sym = s 268 p.From.Sym.Set(obj.AttrLocal, true) 269 p.From.Offset = 0 270 } 271 272 case AMOVSD: 273 // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx 274 if p.From.Type == obj.TYPE_FCONST { 275 // f == 0 can't be used here due to -0, so use Float64bits 276 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 277 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 278 p.As = AXORPS 279 p.From = p.To 280 break 281 } 282 } 283 } 284 fallthrough 285 286 case AFMOVD, 287 AFADDD, 288 AFSUBD, 289 AFSUBRD, 290 AFMULD, 291 AFDIVD, 292 AFDIVRD, 293 AFCOMD, 294 AFCOMDP, 295 AADDSD, 296 ASUBSD, 297 AMULSD, 298 ADIVSD, 299 ACOMISD, 300 AUCOMISD: 301 if p.From.Type == obj.TYPE_FCONST { 302 i64 := math.Float64bits(p.From.Val.(float64)) 303 literal := fmt.Sprintf("$f64.%016x", i64) 304 s := obj.Linklookup(ctxt, literal, 0) 305 p.From.Type = obj.TYPE_MEM 306 p.From.Name = obj.NAME_EXTERN 307 p.From.Sym = s 308 p.From.Sym.Set(obj.AttrLocal, true) 309 p.From.Offset = 0 310 } 311 } 312 313 if ctxt.Flag_dynlink { 314 rewriteToUseGot(ctxt, p) 315 } 316 317 if ctxt.Flag_shared && p.Mode == 32 { 318 rewriteToPcrel(ctxt, p) 319 } 320 } 321 322 // Rewrite p, if necessary, to access global data via the global offset table. 323 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 324 var add, lea, mov obj.As 325 var reg int16 326 if p.Mode == 64 { 327 add = AADDQ 328 lea = ALEAQ 329 mov = AMOVQ 330 reg = REG_R15 331 } else { 332 add = AADDL 333 lea = ALEAL 334 mov = AMOVL 335 reg = REG_CX 336 if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { 337 // Special case: clobber the destination register with 338 // the PC so we don't have to clobber CX. 339 // The SSA backend depends on CX not being clobbered across LEAL. 340 // See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared). 341 reg = p.To.Reg 342 } 343 } 344 345 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 346 // ADUFFxxx $offset 347 // becomes 348 // $MOV runtime.duffxxx@GOT, $reg 349 // $ADD $offset, $reg 350 // CALL $reg 351 var sym *obj.LSym 352 if p.As == obj.ADUFFZERO { 353 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 354 } else { 355 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 356 } 357 offset := p.To.Offset 358 p.As = mov 359 p.From.Type = obj.TYPE_MEM 360 p.From.Name = obj.NAME_GOTREF 361 p.From.Sym = sym 362 p.To.Type = obj.TYPE_REG 363 p.To.Reg = reg 364 p.To.Offset = 0 365 p.To.Sym = nil 366 p1 := obj.Appendp(ctxt, p) 367 p1.As = add 368 p1.From.Type = obj.TYPE_CONST 369 p1.From.Offset = offset 370 p1.To.Type = obj.TYPE_REG 371 p1.To.Reg = reg 372 p2 := obj.Appendp(ctxt, p1) 373 p2.As = obj.ACALL 374 p2.To.Type = obj.TYPE_REG 375 p2.To.Reg = reg 376 } 377 378 // We only care about global data: NAME_EXTERN means a global 379 // symbol in the Go sense, and p.Sym.Local is true for a few 380 // internally defined symbols. 381 if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 382 // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below 383 p.As = mov 384 p.From.Type = obj.TYPE_ADDR 385 } 386 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 387 // $MOV $sym, Rx becomes $MOV sym@GOT, Rx 388 // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx 389 // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX 390 cmplxdest := false 391 pAs := p.As 392 var dest obj.Addr 393 if p.To.Type != obj.TYPE_REG || pAs != mov { 394 if p.Mode == 64 { 395 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) 396 } 397 cmplxdest = true 398 dest = p.To 399 p.As = mov 400 p.To.Type = obj.TYPE_REG 401 p.To.Reg = reg 402 p.To.Sym = nil 403 p.To.Name = obj.NAME_NONE 404 } 405 p.From.Type = obj.TYPE_MEM 406 p.From.Name = obj.NAME_GOTREF 407 q := p 408 if p.From.Offset != 0 { 409 q = obj.Appendp(ctxt, p) 410 q.As = lea 411 q.From.Type = obj.TYPE_MEM 412 q.From.Reg = p.To.Reg 413 q.From.Offset = p.From.Offset 414 q.To = p.To 415 p.From.Offset = 0 416 } 417 if cmplxdest { 418 q = obj.Appendp(ctxt, q) 419 q.As = pAs 420 q.To = dest 421 q.From.Type = obj.TYPE_REG 422 q.From.Reg = reg 423 } 424 } 425 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 426 ctxt.Diag("don't know how to handle %v with -dynlink", p) 427 } 428 var source *obj.Addr 429 // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry 430 // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) 431 // An addition may be inserted between the two MOVs if there is an offset. 432 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 433 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 434 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 435 } 436 source = &p.From 437 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 438 source = &p.To 439 } else { 440 return 441 } 442 if p.As == obj.ACALL { 443 // When dynlinking on 386, almost any call might end up being a call 444 // to a PLT, so make sure the GOT pointer is loaded into BX. 445 // RegTo2 is set on the replacement call insn to stop it being 446 // processed when it is in turn passed to progedit. 447 if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 { 448 return 449 } 450 p1 := obj.Appendp(ctxt, p) 451 p2 := obj.Appendp(ctxt, p1) 452 453 p1.As = ALEAL 454 p1.From.Type = obj.TYPE_MEM 455 p1.From.Name = obj.NAME_STATIC 456 p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0) 457 p1.To.Type = obj.TYPE_REG 458 p1.To.Reg = REG_BX 459 460 p2.As = p.As 461 p2.Scond = p.Scond 462 p2.From = p.From 463 p2.From3 = p.From3 464 p2.Reg = p.Reg 465 p2.To = p.To 466 // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr 467 // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 468 // itself gets passed to progedit. 469 p2.To.Type = obj.TYPE_MEM 470 p2.RegTo2 = 1 471 472 obj.Nopout(p) 473 return 474 475 } 476 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { 477 return 478 } 479 if source.Type != obj.TYPE_MEM { 480 ctxt.Diag("don't know how to handle %v with -dynlink", p) 481 } 482 p1 := obj.Appendp(ctxt, p) 483 p2 := obj.Appendp(ctxt, p1) 484 485 p1.As = mov 486 p1.From.Type = obj.TYPE_MEM 487 p1.From.Sym = source.Sym 488 p1.From.Name = obj.NAME_GOTREF 489 p1.To.Type = obj.TYPE_REG 490 p1.To.Reg = reg 491 492 p2.As = p.As 493 p2.From = p.From 494 p2.To = p.To 495 if p.From.Name == obj.NAME_EXTERN { 496 p2.From.Reg = reg 497 p2.From.Name = obj.NAME_NONE 498 p2.From.Sym = nil 499 } else if p.To.Name == obj.NAME_EXTERN { 500 p2.To.Reg = reg 501 p2.To.Name = obj.NAME_NONE 502 p2.To.Sym = nil 503 } else { 504 return 505 } 506 obj.Nopout(p) 507 } 508 509 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { 510 // RegTo2 is set on the instructions we insert here so they don't get 511 // processed twice. 512 if p.RegTo2 != 0 { 513 return 514 } 515 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 516 return 517 } 518 // Any Prog (aside from the above special cases) with an Addr with Name == 519 // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX 520 // inserted before it. 521 isName := func(a *obj.Addr) bool { 522 if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 { 523 return false 524 } 525 if a.Sym.Type == obj.STLSBSS { 526 return false 527 } 528 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF 529 } 530 531 if isName(&p.From) && p.From.Type == obj.TYPE_ADDR { 532 // Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting 533 // to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX" 534 // respectively. 535 if p.To.Type != obj.TYPE_REG { 536 q := obj.Appendp(ctxt, p) 537 q.As = p.As 538 q.From.Type = obj.TYPE_REG 539 q.From.Reg = REG_CX 540 q.To = p.To 541 p.As = AMOVL 542 p.To.Type = obj.TYPE_REG 543 p.To.Reg = REG_CX 544 p.To.Sym = nil 545 p.To.Name = obj.NAME_NONE 546 } 547 } 548 549 if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) { 550 return 551 } 552 var dst int16 = REG_CX 553 if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { 554 dst = p.To.Reg 555 // Why? See the comment near the top of rewriteToUseGot above. 556 // AMOVLs might be introduced by the GOT rewrites. 557 } 558 q := obj.Appendp(ctxt, p) 559 q.RegTo2 = 1 560 r := obj.Appendp(ctxt, q) 561 r.RegTo2 = 1 562 q.As = obj.ACALL 563 q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk."+strings.ToLower(Rconv(int(dst))), 0) 564 q.To.Type = obj.TYPE_MEM 565 q.To.Name = obj.NAME_EXTERN 566 q.To.Sym.Set(obj.AttrLocal, true) 567 r.As = p.As 568 r.Scond = p.Scond 569 r.From = p.From 570 r.From3 = p.From3 571 r.Reg = p.Reg 572 r.To = p.To 573 if isName(&p.From) { 574 r.From.Reg = dst 575 } 576 if isName(&p.To) { 577 r.To.Reg = dst 578 } 579 if p.From3 != nil && isName(p.From3) { 580 r.From3.Reg = dst 581 } 582 obj.Nopout(p) 583 } 584 585 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 586 if p.As == ALEAL || p.As == ALEAQ { 587 return 588 } 589 590 if a.Reg == REG_BP { 591 ctxt.Diag("invalid address: %v", p) 592 return 593 } 594 595 if a.Reg == REG_TLS { 596 a.Reg = REG_BP 597 } 598 if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { 599 switch a.Reg { 600 // all ok 601 case REG_BP, REG_SP, REG_R15: 602 break 603 604 default: 605 if a.Index != REG_NONE { 606 ctxt.Diag("invalid address %v", p) 607 } 608 a.Index = a.Reg 609 if a.Index != REG_NONE { 610 a.Scale = 1 611 } 612 a.Reg = REG_R15 613 } 614 } 615 } 616 617 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 618 if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { 619 ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) 620 } 621 622 ctxt.Cursym = cursym 623 624 if cursym.Text == nil || cursym.Text.Link == nil { 625 return 626 } 627 628 p := cursym.Text 629 autoffset := int32(p.To.Offset) 630 if autoffset < 0 { 631 autoffset = 0 632 } 633 634 hasCall := false 635 for q := p; q != nil; q = q.Link { 636 if q.As == obj.ACALL || q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO { 637 hasCall = true 638 break 639 } 640 } 641 642 var bpsize int 643 if p.Mode == 64 && ctxt.Framepointer_enabled && 644 p.From3.Offset&obj.NOFRAME == 0 && // (1) below 645 !(autoffset == 0 && p.From3.Offset&obj.NOSPLIT != 0) && // (2) below 646 !(autoffset == 0 && !hasCall) { // (3) below 647 // Make room to save a base pointer. 648 // There are 2 cases we must avoid: 649 // 1) If noframe is set (which we do for functions which tail call). 650 // 2) Scary runtime internals which would be all messed up by frame pointers. 651 // We detect these using a heuristic: frameless nosplit functions. 652 // TODO: Maybe someday we label them all with NOFRAME and get rid of this heuristic. 653 // For performance, we also want to avoid: 654 // 3) Frameless leaf functions 655 bpsize = ctxt.Arch.PtrSize 656 autoffset += int32(bpsize) 657 p.To.Offset += int64(bpsize) 658 } else { 659 bpsize = 0 660 } 661 662 textarg := int64(p.To.Val.(int32)) 663 cursym.Args = int32(textarg) 664 cursym.Locals = int32(p.To.Offset) 665 666 // TODO(rsc): Remove. 667 if p.Mode == 32 && cursym.Locals < 0 { 668 cursym.Locals = 0 669 } 670 671 // TODO(rsc): Remove 'p.Mode == 64 &&'. 672 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { 673 leaf := true 674 LeafSearch: 675 for q := p; q != nil; q = q.Link { 676 switch q.As { 677 case obj.ACALL: 678 // Treat common runtime calls that take no arguments 679 // the same as duffcopy and duffzero. 680 if !isZeroArgRuntimeCall(q.To.Sym) { 681 leaf = false 682 break LeafSearch 683 } 684 fallthrough 685 case obj.ADUFFCOPY, obj.ADUFFZERO: 686 if autoffset >= obj.StackSmall-8 { 687 leaf = false 688 break LeafSearch 689 } 690 } 691 } 692 693 if leaf { 694 p.From3.Offset |= obj.NOSPLIT 695 } 696 } 697 698 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 699 p = obj.Appendp(ctxt, p) 700 p = load_g_cx(ctxt, p) // load g into CX 701 } 702 703 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 704 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 705 } 706 707 if autoffset != 0 { 708 if autoffset%int32(ctxt.Arch.RegSize) != 0 { 709 ctxt.Diag("unaligned stack size %d", autoffset) 710 } 711 p = obj.Appendp(ctxt, p) 712 p.As = AADJSP 713 p.From.Type = obj.TYPE_CONST 714 p.From.Offset = int64(autoffset) 715 p.Spadj = autoffset 716 } 717 718 deltasp := autoffset 719 720 if bpsize > 0 { 721 // Save caller's BP 722 p = obj.Appendp(ctxt, p) 723 724 p.As = AMOVQ 725 p.From.Type = obj.TYPE_REG 726 p.From.Reg = REG_BP 727 p.To.Type = obj.TYPE_MEM 728 p.To.Reg = REG_SP 729 p.To.Scale = 1 730 p.To.Offset = int64(autoffset) - int64(bpsize) 731 732 // Move current frame to BP 733 p = obj.Appendp(ctxt, p) 734 735 p.As = ALEAQ 736 p.From.Type = obj.TYPE_MEM 737 p.From.Reg = REG_SP 738 p.From.Scale = 1 739 p.From.Offset = int64(autoffset) - int64(bpsize) 740 p.To.Type = obj.TYPE_REG 741 p.To.Reg = REG_BP 742 } 743 744 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 745 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 746 // 747 // MOVQ g_panic(CX), BX 748 // TESTQ BX, BX 749 // JEQ end 750 // LEAQ (autoffset+8)(SP), DI 751 // CMPQ panic_argp(BX), DI 752 // JNE end 753 // MOVQ SP, panic_argp(BX) 754 // end: 755 // NOP 756 // 757 // The NOP is needed to give the jumps somewhere to land. 758 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 759 760 p = obj.Appendp(ctxt, p) 761 762 p.As = AMOVQ 763 p.From.Type = obj.TYPE_MEM 764 p.From.Reg = REG_CX 765 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 766 p.To.Type = obj.TYPE_REG 767 p.To.Reg = REG_BX 768 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 769 p.As = AMOVL 770 p.From.Type = obj.TYPE_MEM 771 p.From.Reg = REG_R15 772 p.From.Scale = 1 773 p.From.Index = REG_CX 774 } 775 if p.Mode == 32 { 776 p.As = AMOVL 777 } 778 779 p = obj.Appendp(ctxt, p) 780 p.As = ATESTQ 781 p.From.Type = obj.TYPE_REG 782 p.From.Reg = REG_BX 783 p.To.Type = obj.TYPE_REG 784 p.To.Reg = REG_BX 785 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 786 p.As = ATESTL 787 } 788 789 p = obj.Appendp(ctxt, p) 790 p.As = AJEQ 791 p.To.Type = obj.TYPE_BRANCH 792 p1 := p 793 794 p = obj.Appendp(ctxt, p) 795 p.As = ALEAQ 796 p.From.Type = obj.TYPE_MEM 797 p.From.Reg = REG_SP 798 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) 799 p.To.Type = obj.TYPE_REG 800 p.To.Reg = REG_DI 801 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 802 p.As = ALEAL 803 } 804 805 p = obj.Appendp(ctxt, p) 806 p.As = ACMPQ 807 p.From.Type = obj.TYPE_MEM 808 p.From.Reg = REG_BX 809 p.From.Offset = 0 // Panic.argp 810 p.To.Type = obj.TYPE_REG 811 p.To.Reg = REG_DI 812 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 813 p.As = ACMPL 814 p.From.Type = obj.TYPE_MEM 815 p.From.Reg = REG_R15 816 p.From.Scale = 1 817 p.From.Index = REG_BX 818 } 819 if p.Mode == 32 { 820 p.As = ACMPL 821 } 822 823 p = obj.Appendp(ctxt, p) 824 p.As = AJNE 825 p.To.Type = obj.TYPE_BRANCH 826 p2 := p 827 828 p = obj.Appendp(ctxt, p) 829 p.As = AMOVQ 830 p.From.Type = obj.TYPE_REG 831 p.From.Reg = REG_SP 832 p.To.Type = obj.TYPE_MEM 833 p.To.Reg = REG_BX 834 p.To.Offset = 0 // Panic.argp 835 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 836 p.As = AMOVL 837 p.To.Type = obj.TYPE_MEM 838 p.To.Reg = REG_R15 839 p.To.Scale = 1 840 p.To.Index = REG_BX 841 } 842 if p.Mode == 32 { 843 p.As = AMOVL 844 } 845 846 p = obj.Appendp(ctxt, p) 847 p.As = obj.ANOP 848 p1.Pcond = p 849 p2.Pcond = p 850 } 851 852 for ; p != nil; p = p.Link { 853 pcsize := int(p.Mode) / 8 854 switch p.From.Name { 855 case obj.NAME_AUTO: 856 p.From.Offset += int64(deltasp) - int64(bpsize) 857 case obj.NAME_PARAM: 858 p.From.Offset += int64(deltasp) + int64(pcsize) 859 } 860 if p.From3 != nil { 861 switch p.From3.Name { 862 case obj.NAME_AUTO: 863 p.From3.Offset += int64(deltasp) - int64(bpsize) 864 case obj.NAME_PARAM: 865 p.From3.Offset += int64(deltasp) + int64(pcsize) 866 } 867 } 868 switch p.To.Name { 869 case obj.NAME_AUTO: 870 p.To.Offset += int64(deltasp) - int64(bpsize) 871 case obj.NAME_PARAM: 872 p.To.Offset += int64(deltasp) + int64(pcsize) 873 } 874 875 switch p.As { 876 default: 877 continue 878 879 case APUSHL, APUSHFL: 880 deltasp += 4 881 p.Spadj = 4 882 continue 883 884 case APUSHQ, APUSHFQ: 885 deltasp += 8 886 p.Spadj = 8 887 continue 888 889 case APUSHW, APUSHFW: 890 deltasp += 2 891 p.Spadj = 2 892 continue 893 894 case APOPL, APOPFL: 895 deltasp -= 4 896 p.Spadj = -4 897 continue 898 899 case APOPQ, APOPFQ: 900 deltasp -= 8 901 p.Spadj = -8 902 continue 903 904 case APOPW, APOPFW: 905 deltasp -= 2 906 p.Spadj = -2 907 continue 908 909 case obj.ARET: 910 // do nothing 911 } 912 913 if autoffset != deltasp { 914 ctxt.Diag("unbalanced PUSH/POP") 915 } 916 917 if autoffset != 0 { 918 if bpsize > 0 { 919 // Restore caller's BP 920 p.As = AMOVQ 921 922 p.From.Type = obj.TYPE_MEM 923 p.From.Reg = REG_SP 924 p.From.Scale = 1 925 p.From.Offset = int64(autoffset) - int64(bpsize) 926 p.To.Type = obj.TYPE_REG 927 p.To.Reg = REG_BP 928 p = obj.Appendp(ctxt, p) 929 } 930 931 p.As = AADJSP 932 p.From.Type = obj.TYPE_CONST 933 p.From.Offset = int64(-autoffset) 934 p.Spadj = -autoffset 935 p = obj.Appendp(ctxt, p) 936 p.As = obj.ARET 937 938 // If there are instructions following 939 // this ARET, they come from a branch 940 // with the same stackframe, so undo 941 // the cleanup. 942 p.Spadj = +autoffset 943 } 944 945 if p.To.Sym != nil { // retjmp 946 p.As = obj.AJMP 947 } 948 } 949 } 950 951 func isZeroArgRuntimeCall(s *obj.LSym) bool { 952 if s == nil { 953 return false 954 } 955 switch s.Name { 956 case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide": 957 return true 958 } 959 return false 960 } 961 962 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 963 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 964 a.Type = obj.TYPE_MEM 965 a.Reg = REG_R15 966 a.Index = REG_CX 967 a.Scale = 1 968 return 969 } 970 971 a.Type = obj.TYPE_MEM 972 a.Reg = REG_CX 973 } 974 975 // Append code to p to load g into cx. 976 // Overwrites p with the first instruction (no first appendp). 977 // Overwriting p is unusual but it lets use this in both the 978 // prologue (caller must call appendp first) and in the epilogue. 979 // Returns last new instruction. 980 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 981 p.As = AMOVQ 982 if ctxt.Arch.PtrSize == 4 { 983 p.As = AMOVL 984 } 985 p.From.Type = obj.TYPE_MEM 986 p.From.Reg = REG_TLS 987 p.From.Offset = 0 988 p.To.Type = obj.TYPE_REG 989 p.To.Reg = REG_CX 990 991 next := p.Link 992 progedit(ctxt, p) 993 for p.Link != next { 994 p = p.Link 995 } 996 997 if p.From.Index == REG_TLS { 998 p.From.Scale = 2 999 } 1000 1001 return p 1002 } 1003 1004 // Append code to p to check for stack split. 1005 // Appends to (does not overwrite) p. 1006 // Assumes g is in CX. 1007 // Returns last new instruction. 1008 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 1009 cmp := ACMPQ 1010 lea := ALEAQ 1011 mov := AMOVQ 1012 sub := ASUBQ 1013 1014 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 1015 cmp = ACMPL 1016 lea = ALEAL 1017 mov = AMOVL 1018 sub = ASUBL 1019 } 1020 1021 var q1 *obj.Prog 1022 if framesize <= obj.StackSmall { 1023 // small stack: SP <= stackguard 1024 // CMPQ SP, stackguard 1025 p = obj.Appendp(ctxt, p) 1026 1027 p.As = cmp 1028 p.From.Type = obj.TYPE_REG 1029 p.From.Reg = REG_SP 1030 indir_cx(ctxt, p, &p.To) 1031 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1032 if ctxt.Cursym.CFunc() { 1033 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1034 } 1035 } else if framesize <= obj.StackBig { 1036 // large stack: SP-framesize <= stackguard-StackSmall 1037 // LEAQ -xxx(SP), AX 1038 // CMPQ AX, stackguard 1039 p = obj.Appendp(ctxt, p) 1040 1041 p.As = lea 1042 p.From.Type = obj.TYPE_MEM 1043 p.From.Reg = REG_SP 1044 p.From.Offset = -(int64(framesize) - obj.StackSmall) 1045 p.To.Type = obj.TYPE_REG 1046 p.To.Reg = REG_AX 1047 1048 p = obj.Appendp(ctxt, p) 1049 p.As = cmp 1050 p.From.Type = obj.TYPE_REG 1051 p.From.Reg = REG_AX 1052 indir_cx(ctxt, p, &p.To) 1053 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1054 if ctxt.Cursym.CFunc() { 1055 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1056 } 1057 } else { 1058 // Such a large stack we need to protect against wraparound. 1059 // If SP is close to zero: 1060 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1061 // The +StackGuard on both sides is required to keep the left side positive: 1062 // SP is allowed to be slightly below stackguard. See stack.h. 1063 // 1064 // Preemption sets stackguard to StackPreempt, a very large value. 1065 // That breaks the math above, so we have to check for that explicitly. 1066 // MOVQ stackguard, CX 1067 // CMPQ CX, $StackPreempt 1068 // JEQ label-of-call-to-morestack 1069 // LEAQ StackGuard(SP), AX 1070 // SUBQ CX, AX 1071 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1072 1073 p = obj.Appendp(ctxt, p) 1074 1075 p.As = mov 1076 indir_cx(ctxt, p, &p.From) 1077 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1078 if ctxt.Cursym.CFunc() { 1079 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1080 } 1081 p.To.Type = obj.TYPE_REG 1082 p.To.Reg = REG_SI 1083 1084 p = obj.Appendp(ctxt, p) 1085 p.As = cmp 1086 p.From.Type = obj.TYPE_REG 1087 p.From.Reg = REG_SI 1088 p.To.Type = obj.TYPE_CONST 1089 p.To.Offset = obj.StackPreempt 1090 if p.Mode == 32 { 1091 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 1092 } 1093 1094 p = obj.Appendp(ctxt, p) 1095 p.As = AJEQ 1096 p.To.Type = obj.TYPE_BRANCH 1097 q1 = p 1098 1099 p = obj.Appendp(ctxt, p) 1100 p.As = lea 1101 p.From.Type = obj.TYPE_MEM 1102 p.From.Reg = REG_SP 1103 p.From.Offset = obj.StackGuard 1104 p.To.Type = obj.TYPE_REG 1105 p.To.Reg = REG_AX 1106 1107 p = obj.Appendp(ctxt, p) 1108 p.As = sub 1109 p.From.Type = obj.TYPE_REG 1110 p.From.Reg = REG_SI 1111 p.To.Type = obj.TYPE_REG 1112 p.To.Reg = REG_AX 1113 1114 p = obj.Appendp(ctxt, p) 1115 p.As = cmp 1116 p.From.Type = obj.TYPE_REG 1117 p.From.Reg = REG_AX 1118 p.To.Type = obj.TYPE_CONST 1119 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 1120 } 1121 1122 // common 1123 jls := obj.Appendp(ctxt, p) 1124 jls.As = AJLS 1125 jls.To.Type = obj.TYPE_BRANCH 1126 1127 var last *obj.Prog 1128 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 1129 } 1130 1131 // Now we are at the end of the function, but logically 1132 // we are still in function prologue. We need to fix the 1133 // SP data and PCDATA. 1134 spfix := obj.Appendp(ctxt, last) 1135 spfix.As = obj.ANOP 1136 spfix.Spadj = -framesize 1137 1138 pcdata := obj.Appendp(ctxt, spfix) 1139 pcdata.Pos = ctxt.Cursym.Text.Pos 1140 pcdata.Mode = ctxt.Cursym.Text.Mode 1141 pcdata.As = obj.APCDATA 1142 pcdata.From.Type = obj.TYPE_CONST 1143 pcdata.From.Offset = obj.PCDATA_StackMapIndex 1144 pcdata.To.Type = obj.TYPE_CONST 1145 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 1146 1147 call := obj.Appendp(ctxt, pcdata) 1148 call.Pos = ctxt.Cursym.Text.Pos 1149 call.Mode = ctxt.Cursym.Text.Mode 1150 call.As = obj.ACALL 1151 call.To.Type = obj.TYPE_BRANCH 1152 call.To.Name = obj.NAME_EXTERN 1153 morestack := "runtime.morestack" 1154 switch { 1155 case ctxt.Cursym.CFunc(): 1156 morestack = "runtime.morestackc" 1157 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 1158 morestack = "runtime.morestack_noctxt" 1159 } 1160 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 1161 // When compiling 386 code for dynamic linking, the call needs to be adjusted 1162 // to follow PIC rules. This in turn can insert more instructions, so we need 1163 // to keep track of the start of the call (where the jump will be to) and the 1164 // end (which following instructions are appended to). 1165 callend := call 1166 progedit(ctxt, callend) 1167 for ; callend.Link != nil; callend = callend.Link { 1168 progedit(ctxt, callend.Link) 1169 } 1170 1171 jmp := obj.Appendp(ctxt, callend) 1172 jmp.As = obj.AJMP 1173 jmp.To.Type = obj.TYPE_BRANCH 1174 jmp.Pcond = ctxt.Cursym.Text.Link 1175 jmp.Spadj = +framesize 1176 1177 jls.Pcond = call 1178 if q1 != nil { 1179 q1.Pcond = call 1180 } 1181 1182 return jls 1183 } 1184 1185 var unaryDst = map[obj.As]bool{ 1186 ABSWAPL: true, 1187 ABSWAPQ: true, 1188 ACMPXCHG8B: true, 1189 ADECB: true, 1190 ADECL: true, 1191 ADECQ: true, 1192 ADECW: true, 1193 AINCB: true, 1194 AINCL: true, 1195 AINCQ: true, 1196 AINCW: true, 1197 ANEGB: true, 1198 ANEGL: true, 1199 ANEGQ: true, 1200 ANEGW: true, 1201 ANOTB: true, 1202 ANOTL: true, 1203 ANOTQ: true, 1204 ANOTW: true, 1205 APOPL: true, 1206 APOPQ: true, 1207 APOPW: true, 1208 ASETCC: true, 1209 ASETCS: true, 1210 ASETEQ: true, 1211 ASETGE: true, 1212 ASETGT: true, 1213 ASETHI: true, 1214 ASETLE: true, 1215 ASETLS: true, 1216 ASETLT: true, 1217 ASETMI: true, 1218 ASETNE: true, 1219 ASETOC: true, 1220 ASETOS: true, 1221 ASETPC: true, 1222 ASETPL: true, 1223 ASETPS: true, 1224 AFFREE: true, 1225 AFLDENV: true, 1226 AFSAVE: true, 1227 AFSTCW: true, 1228 AFSTENV: true, 1229 AFSTSW: true, 1230 AFXSAVE: true, 1231 AFXSAVE64: true, 1232 ASTMXCSR: true, 1233 } 1234 1235 var Linkamd64 = obj.LinkArch{ 1236 Arch: sys.ArchAMD64, 1237 Preprocess: preprocess, 1238 Assemble: span6, 1239 Progedit: progedit, 1240 UnaryDst: unaryDst, 1241 } 1242 1243 var Linkamd64p32 = obj.LinkArch{ 1244 Arch: sys.ArchAMD64P32, 1245 Preprocess: preprocess, 1246 Assemble: span6, 1247 Progedit: progedit, 1248 UnaryDst: unaryDst, 1249 } 1250 1251 var Link386 = obj.LinkArch{ 1252 Arch: sys.Arch386, 1253 Preprocess: preprocess, 1254 Assemble: span6, 1255 Progedit: progedit, 1256 UnaryDst: unaryDst, 1257 }