github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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 { 746 // g._panic.argp = bottom-of-frame 747 // } 748 // 749 // MOVQ g_panic(CX), BX 750 // TESTQ BX, BX 751 // JEQ end 752 // LEAQ (autoffset+8)(SP), DI 753 // CMPQ panic_argp(BX), DI 754 // JNE end 755 // MOVQ SP, panic_argp(BX) 756 // end: 757 // NOP 758 // 759 // The NOP is needed to give the jumps somewhere to land. 760 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 761 762 // MOVQ g_panic(CX), BX 763 p = obj.Appendp(ctxt, p) 764 p.As = AMOVQ 765 p.From.Type = obj.TYPE_MEM 766 p.From.Reg = REG_CX 767 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // g_panic 768 p.To.Type = obj.TYPE_REG 769 p.To.Reg = REG_BX 770 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 771 p.As = AMOVL 772 p.From.Type = obj.TYPE_MEM 773 p.From.Reg = REG_R15 774 p.From.Scale = 1 775 p.From.Index = REG_CX 776 } 777 if p.Mode == 32 { 778 p.As = AMOVL 779 } 780 781 // TESTQ BX, BX 782 p = obj.Appendp(ctxt, p) 783 p.As = ATESTQ 784 p.From.Type = obj.TYPE_REG 785 p.From.Reg = REG_BX 786 p.To.Type = obj.TYPE_REG 787 p.To.Reg = REG_BX 788 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 789 p.As = ATESTL 790 } 791 792 // JEQ end 793 p = obj.Appendp(ctxt, p) 794 p.As = AJEQ 795 p.To.Type = obj.TYPE_BRANCH 796 p1 := p 797 798 // LEAQ (autoffset+8)(SP), DI 799 p = obj.Appendp(ctxt, p) 800 p.As = ALEAQ 801 p.From.Type = obj.TYPE_MEM 802 p.From.Reg = REG_SP 803 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) 804 p.To.Type = obj.TYPE_REG 805 p.To.Reg = REG_DI 806 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 807 p.As = ALEAL 808 } 809 810 // CMPQ panic_argp(BX), DI 811 p = obj.Appendp(ctxt, p) 812 p.As = ACMPQ 813 p.From.Type = obj.TYPE_MEM 814 p.From.Reg = REG_BX 815 p.From.Offset = 0 // Panic.argp 816 p.To.Type = obj.TYPE_REG 817 p.To.Reg = REG_DI 818 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 819 p.As = ACMPL 820 p.From.Type = obj.TYPE_MEM 821 p.From.Reg = REG_R15 822 p.From.Scale = 1 823 p.From.Index = REG_BX 824 } 825 if p.Mode == 32 { 826 p.As = ACMPL 827 } 828 829 // JNE end 830 p = obj.Appendp(ctxt, p) 831 p.As = AJNE 832 p.To.Type = obj.TYPE_BRANCH 833 p2 := p 834 835 // MOVQ SP, panic_argp(BX) 836 p = obj.Appendp(ctxt, p) 837 p.As = AMOVQ 838 p.From.Type = obj.TYPE_REG 839 p.From.Reg = REG_SP 840 p.To.Type = obj.TYPE_MEM 841 p.To.Reg = REG_BX 842 p.To.Offset = 0 // Panic.argp 843 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 844 p.As = AMOVL 845 p.To.Type = obj.TYPE_MEM 846 p.To.Reg = REG_R15 847 p.To.Scale = 1 848 p.To.Index = REG_BX 849 } 850 if p.Mode == 32 { 851 p.As = AMOVL 852 } 853 854 // NOP 855 p = obj.Appendp(ctxt, p) 856 p.As = obj.ANOP 857 858 // Set targets for jumps above to the NOP 859 p1.Pcond = p 860 p2.Pcond = p 861 } 862 863 for ; p != nil; p = p.Link { 864 pcsize := int(p.Mode) / 8 865 switch p.From.Name { 866 case obj.NAME_AUTO: 867 p.From.Offset += int64(deltasp) - int64(bpsize) 868 case obj.NAME_PARAM: 869 p.From.Offset += int64(deltasp) + int64(pcsize) 870 } 871 if p.From3 != nil { 872 switch p.From3.Name { 873 case obj.NAME_AUTO: 874 p.From3.Offset += int64(deltasp) - int64(bpsize) 875 case obj.NAME_PARAM: 876 p.From3.Offset += int64(deltasp) + int64(pcsize) 877 } 878 } 879 switch p.To.Name { 880 case obj.NAME_AUTO: 881 p.To.Offset += int64(deltasp) - int64(bpsize) 882 case obj.NAME_PARAM: 883 p.To.Offset += int64(deltasp) + int64(pcsize) 884 } 885 886 switch p.As { 887 default: 888 continue 889 890 case APUSHL, APUSHFL: 891 deltasp += 4 892 p.Spadj = 4 893 continue 894 895 case APUSHQ, APUSHFQ: 896 deltasp += 8 897 p.Spadj = 8 898 continue 899 900 case APUSHW, APUSHFW: 901 deltasp += 2 902 p.Spadj = 2 903 continue 904 905 case APOPL, APOPFL: 906 deltasp -= 4 907 p.Spadj = -4 908 continue 909 910 case APOPQ, APOPFQ: 911 deltasp -= 8 912 p.Spadj = -8 913 continue 914 915 case APOPW, APOPFW: 916 deltasp -= 2 917 p.Spadj = -2 918 continue 919 920 case obj.ARET: 921 // do nothing 922 } 923 924 if autoffset != deltasp { 925 ctxt.Diag("unbalanced PUSH/POP") 926 } 927 928 if autoffset != 0 { 929 if bpsize > 0 { 930 // Restore caller's BP 931 p.As = AMOVQ 932 933 p.From.Type = obj.TYPE_MEM 934 p.From.Reg = REG_SP 935 p.From.Scale = 1 936 p.From.Offset = int64(autoffset) - int64(bpsize) 937 p.To.Type = obj.TYPE_REG 938 p.To.Reg = REG_BP 939 p = obj.Appendp(ctxt, p) 940 } 941 942 p.As = AADJSP 943 p.From.Type = obj.TYPE_CONST 944 p.From.Offset = int64(-autoffset) 945 p.Spadj = -autoffset 946 p = obj.Appendp(ctxt, p) 947 p.As = obj.ARET 948 949 // If there are instructions following 950 // this ARET, they come from a branch 951 // with the same stackframe, so undo 952 // the cleanup. 953 p.Spadj = +autoffset 954 } 955 956 if p.To.Sym != nil { // retjmp 957 p.As = obj.AJMP 958 } 959 } 960 } 961 962 func isZeroArgRuntimeCall(s *obj.LSym) bool { 963 if s == nil { 964 return false 965 } 966 switch s.Name { 967 case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap": 968 return true 969 } 970 return false 971 } 972 973 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 974 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 975 a.Type = obj.TYPE_MEM 976 a.Reg = REG_R15 977 a.Index = REG_CX 978 a.Scale = 1 979 return 980 } 981 982 a.Type = obj.TYPE_MEM 983 a.Reg = REG_CX 984 } 985 986 // Append code to p to load g into cx. 987 // Overwrites p with the first instruction (no first appendp). 988 // Overwriting p is unusual but it lets use this in both the 989 // prologue (caller must call appendp first) and in the epilogue. 990 // Returns last new instruction. 991 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 992 p.As = AMOVQ 993 if ctxt.Arch.PtrSize == 4 { 994 p.As = AMOVL 995 } 996 p.From.Type = obj.TYPE_MEM 997 p.From.Reg = REG_TLS 998 p.From.Offset = 0 999 p.To.Type = obj.TYPE_REG 1000 p.To.Reg = REG_CX 1001 1002 next := p.Link 1003 progedit(ctxt, p) 1004 for p.Link != next { 1005 p = p.Link 1006 } 1007 1008 if p.From.Index == REG_TLS { 1009 p.From.Scale = 2 1010 } 1011 1012 return p 1013 } 1014 1015 // Append code to p to check for stack split. 1016 // Appends to (does not overwrite) p. 1017 // Assumes g is in CX. 1018 // Returns last new instruction. 1019 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 1020 cmp := ACMPQ 1021 lea := ALEAQ 1022 mov := AMOVQ 1023 sub := ASUBQ 1024 1025 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 1026 cmp = ACMPL 1027 lea = ALEAL 1028 mov = AMOVL 1029 sub = ASUBL 1030 } 1031 1032 var q1 *obj.Prog 1033 if framesize <= obj.StackSmall { 1034 // small stack: SP <= stackguard 1035 // CMPQ SP, stackguard 1036 p = obj.Appendp(ctxt, p) 1037 1038 p.As = cmp 1039 p.From.Type = obj.TYPE_REG 1040 p.From.Reg = REG_SP 1041 indir_cx(ctxt, p, &p.To) 1042 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1043 if ctxt.Cursym.CFunc() { 1044 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1045 } 1046 } else if framesize <= obj.StackBig { 1047 // large stack: SP-framesize <= stackguard-StackSmall 1048 // LEAQ -xxx(SP), AX 1049 // CMPQ AX, stackguard 1050 p = obj.Appendp(ctxt, p) 1051 1052 p.As = lea 1053 p.From.Type = obj.TYPE_MEM 1054 p.From.Reg = REG_SP 1055 p.From.Offset = -(int64(framesize) - obj.StackSmall) 1056 p.To.Type = obj.TYPE_REG 1057 p.To.Reg = REG_AX 1058 1059 p = obj.Appendp(ctxt, p) 1060 p.As = cmp 1061 p.From.Type = obj.TYPE_REG 1062 p.From.Reg = REG_AX 1063 indir_cx(ctxt, p, &p.To) 1064 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1065 if ctxt.Cursym.CFunc() { 1066 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1067 } 1068 } else { 1069 // Such a large stack we need to protect against wraparound. 1070 // If SP is close to zero: 1071 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1072 // The +StackGuard on both sides is required to keep the left side positive: 1073 // SP is allowed to be slightly below stackguard. See stack.h. 1074 // 1075 // Preemption sets stackguard to StackPreempt, a very large value. 1076 // That breaks the math above, so we have to check for that explicitly. 1077 // MOVQ stackguard, CX 1078 // CMPQ CX, $StackPreempt 1079 // JEQ label-of-call-to-morestack 1080 // LEAQ StackGuard(SP), AX 1081 // SUBQ CX, AX 1082 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1083 1084 p = obj.Appendp(ctxt, p) 1085 1086 p.As = mov 1087 indir_cx(ctxt, p, &p.From) 1088 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1089 if ctxt.Cursym.CFunc() { 1090 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1091 } 1092 p.To.Type = obj.TYPE_REG 1093 p.To.Reg = REG_SI 1094 1095 p = obj.Appendp(ctxt, p) 1096 p.As = cmp 1097 p.From.Type = obj.TYPE_REG 1098 p.From.Reg = REG_SI 1099 p.To.Type = obj.TYPE_CONST 1100 p.To.Offset = obj.StackPreempt 1101 if p.Mode == 32 { 1102 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 1103 } 1104 1105 p = obj.Appendp(ctxt, p) 1106 p.As = AJEQ 1107 p.To.Type = obj.TYPE_BRANCH 1108 q1 = p 1109 1110 p = obj.Appendp(ctxt, p) 1111 p.As = lea 1112 p.From.Type = obj.TYPE_MEM 1113 p.From.Reg = REG_SP 1114 p.From.Offset = obj.StackGuard 1115 p.To.Type = obj.TYPE_REG 1116 p.To.Reg = REG_AX 1117 1118 p = obj.Appendp(ctxt, p) 1119 p.As = sub 1120 p.From.Type = obj.TYPE_REG 1121 p.From.Reg = REG_SI 1122 p.To.Type = obj.TYPE_REG 1123 p.To.Reg = REG_AX 1124 1125 p = obj.Appendp(ctxt, p) 1126 p.As = cmp 1127 p.From.Type = obj.TYPE_REG 1128 p.From.Reg = REG_AX 1129 p.To.Type = obj.TYPE_CONST 1130 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 1131 } 1132 1133 // common 1134 jls := obj.Appendp(ctxt, p) 1135 jls.As = AJLS 1136 jls.To.Type = obj.TYPE_BRANCH 1137 1138 var last *obj.Prog 1139 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 1140 } 1141 1142 // Now we are at the end of the function, but logically 1143 // we are still in function prologue. We need to fix the 1144 // SP data and PCDATA. 1145 spfix := obj.Appendp(ctxt, last) 1146 spfix.As = obj.ANOP 1147 spfix.Spadj = -framesize 1148 1149 pcdata := obj.Appendp(ctxt, spfix) 1150 pcdata.Pos = ctxt.Cursym.Text.Pos 1151 pcdata.Mode = ctxt.Cursym.Text.Mode 1152 pcdata.As = obj.APCDATA 1153 pcdata.From.Type = obj.TYPE_CONST 1154 pcdata.From.Offset = obj.PCDATA_StackMapIndex 1155 pcdata.To.Type = obj.TYPE_CONST 1156 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 1157 1158 call := obj.Appendp(ctxt, pcdata) 1159 call.Pos = ctxt.Cursym.Text.Pos 1160 call.Mode = ctxt.Cursym.Text.Mode 1161 call.As = obj.ACALL 1162 call.To.Type = obj.TYPE_BRANCH 1163 call.To.Name = obj.NAME_EXTERN 1164 morestack := "runtime.morestack" 1165 switch { 1166 case ctxt.Cursym.CFunc(): 1167 morestack = "runtime.morestackc" 1168 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 1169 morestack = "runtime.morestack_noctxt" 1170 } 1171 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 1172 // When compiling 386 code for dynamic linking, the call needs to be adjusted 1173 // to follow PIC rules. This in turn can insert more instructions, so we need 1174 // to keep track of the start of the call (where the jump will be to) and the 1175 // end (which following instructions are appended to). 1176 callend := call 1177 progedit(ctxt, callend) 1178 for ; callend.Link != nil; callend = callend.Link { 1179 progedit(ctxt, callend.Link) 1180 } 1181 1182 jmp := obj.Appendp(ctxt, callend) 1183 jmp.As = obj.AJMP 1184 jmp.To.Type = obj.TYPE_BRANCH 1185 jmp.Pcond = ctxt.Cursym.Text.Link 1186 jmp.Spadj = +framesize 1187 1188 jls.Pcond = call 1189 if q1 != nil { 1190 q1.Pcond = call 1191 } 1192 1193 return jls 1194 } 1195 1196 var unaryDst = map[obj.As]bool{ 1197 ABSWAPL: true, 1198 ABSWAPQ: true, 1199 ACMPXCHG8B: true, 1200 ADECB: true, 1201 ADECL: true, 1202 ADECQ: true, 1203 ADECW: true, 1204 AINCB: true, 1205 AINCL: true, 1206 AINCQ: true, 1207 AINCW: true, 1208 ANEGB: true, 1209 ANEGL: true, 1210 ANEGQ: true, 1211 ANEGW: true, 1212 ANOTB: true, 1213 ANOTL: true, 1214 ANOTQ: true, 1215 ANOTW: true, 1216 APOPL: true, 1217 APOPQ: true, 1218 APOPW: true, 1219 ASETCC: true, 1220 ASETCS: true, 1221 ASETEQ: true, 1222 ASETGE: true, 1223 ASETGT: true, 1224 ASETHI: true, 1225 ASETLE: true, 1226 ASETLS: true, 1227 ASETLT: true, 1228 ASETMI: true, 1229 ASETNE: true, 1230 ASETOC: true, 1231 ASETOS: true, 1232 ASETPC: true, 1233 ASETPL: true, 1234 ASETPS: true, 1235 AFFREE: true, 1236 AFLDENV: true, 1237 AFSAVE: true, 1238 AFSTCW: true, 1239 AFSTENV: true, 1240 AFSTSW: true, 1241 AFXSAVE: true, 1242 AFXSAVE64: true, 1243 ASTMXCSR: true, 1244 } 1245 1246 var Linkamd64 = obj.LinkArch{ 1247 Arch: sys.ArchAMD64, 1248 Preprocess: preprocess, 1249 Assemble: span6, 1250 Progedit: progedit, 1251 UnaryDst: unaryDst, 1252 } 1253 1254 var Linkamd64p32 = obj.LinkArch{ 1255 Arch: sys.ArchAMD64P32, 1256 Preprocess: preprocess, 1257 Assemble: span6, 1258 Progedit: progedit, 1259 UnaryDst: unaryDst, 1260 } 1261 1262 var Link386 = obj.LinkArch{ 1263 Arch: sys.Arch386, 1264 Preprocess: preprocess, 1265 Assemble: span6, 1266 Progedit: progedit, 1267 UnaryDst: unaryDst, 1268 }