1 // Inferno utils/6l/pass.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c 3 // 4 // Copyright 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright 1995-1997 C H Forsyth (forsyth (a] 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 (a] 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 "encoding/binary" 36 "fmt" 37 "log" 38 "math" 39 ) 40 41 func canuse1insntls(ctxt *obj.Link) bool { 42 if ctxt.Arch.Regsize == 4 { 43 switch ctxt.Headtype { 44 case obj.Hlinux, 45 obj.Hnacl, 46 obj.Hplan9, 47 obj.Hwindows: 48 return false 49 } 50 51 return true 52 } 53 54 switch ctxt.Headtype { 55 case obj.Hplan9, 56 obj.Hwindows: 57 return false 58 case obj.Hlinux: 59 return ctxt.Flag_shared == 0 60 } 61 62 return true 63 } 64 65 func progedit(ctxt *obj.Link, p *obj.Prog) { 66 // Maintain information about code generation mode. 67 if ctxt.Mode == 0 { 68 ctxt.Mode = ctxt.Arch.Regsize * 8 69 } 70 p.Mode = int8(ctxt.Mode) 71 72 switch p.As { 73 case AMODE: 74 if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) { 75 switch int(p.From.Offset) { 76 case 16, 32, 64: 77 ctxt.Mode = int(p.From.Offset) 78 } 79 } 80 obj.Nopout(p) 81 } 82 83 // Thread-local storage references use the TLS pseudo-register. 84 // As a register, TLS refers to the thread-local storage base, and it 85 // can only be loaded into another register: 86 // 87 // MOVQ TLS, AX 88 // 89 // An offset from the thread-local storage base is written off(reg)(TLS*1). 90 // Semantically it is off(reg), but the (TLS*1) annotation marks this as 91 // indexing from the loaded TLS base. This emits a relocation so that 92 // if the linker needs to adjust the offset, it can. For example: 93 // 94 // MOVQ TLS, AX 95 // MOVQ 0(AX)(TLS*1), CX // load g into CX 96 // 97 // On systems that support direct access to the TLS memory, this 98 // pair of instructions can be reduced to a direct TLS memory reference: 99 // 100 // MOVQ 0(TLS), CX // load g into CX 101 // 102 // The 2-instruction and 1-instruction forms correspond to the two code 103 // sequences for loading a TLS variable in the local exec model given in "ELF 104 // Handling For Thread-Local Storage". 105 // 106 // We apply this rewrite on systems that support the 1-instruction form. 107 // The decision is made using only the operating system and the -shared flag, 108 // not the link mode. If some link modes on a particular operating system 109 // require the 2-instruction form, then all builds for that operating system 110 // will use the 2-instruction form, so that the link mode decision can be 111 // delayed to link time. 112 // 113 // In this way, all supported systems use identical instructions to 114 // access TLS, and they are rewritten appropriately first here in 115 // liblink and then finally using relocations in the linker. 116 // 117 // When -shared is passed, we leave the code in the 2-instruction form but 118 // assemble (and relocate) them in different ways to generate the initial 119 // exec code sequence. It's a bit of a fluke that this is possible without 120 // rewriting the instructions more comprehensively, and it only does because 121 // we only support a single TLS variable (g). 122 123 if canuse1insntls(ctxt) { 124 // Reduce 2-instruction sequence to 1-instruction sequence. 125 // Sequences like 126 // MOVQ TLS, BX 127 // ... off(BX)(TLS*1) ... 128 // become 129 // NOP 130 // ... off(TLS) ... 131 // 132 // TODO(rsc): Remove the Hsolaris special case. It exists only to 133 // guarantee we are producing byte-identical binaries as before this code. 134 // But it should be unnecessary. 135 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 { 136 obj.Nopout(p) 137 } 138 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { 139 p.From.Reg = REG_TLS 140 p.From.Scale = 0 141 p.From.Index = REG_NONE 142 } 143 144 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 145 p.To.Reg = REG_TLS 146 p.To.Scale = 0 147 p.To.Index = REG_NONE 148 } 149 } else { 150 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it 151 // as the 2-instruction sequence if necessary. 152 // MOVQ 0(TLS), BX 153 // becomes 154 // MOVQ TLS, BX 155 // MOVQ 0(BX)(TLS*1), BX 156 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 { 157 q := obj.Appendp(ctxt, p) 158 q.As = p.As 159 q.From = p.From 160 q.From.Type = obj.TYPE_MEM 161 q.From.Reg = p.To.Reg 162 q.From.Index = REG_TLS 163 q.From.Scale = 2 // TODO: use 1 164 q.To = p.To 165 p.From.Type = obj.TYPE_REG 166 p.From.Reg = REG_TLS 167 p.From.Index = REG_NONE 168 p.From.Offset = 0 169 } 170 } 171 172 // TODO: Remove. 173 if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 { 174 if p.From.Scale == 1 && p.From.Index == REG_TLS { 175 p.From.Scale = 2 176 } 177 if p.To.Scale == 1 && p.To.Index == REG_TLS { 178 p.To.Scale = 2 179 } 180 } 181 182 // Rewrite 0 to $0 in 3rd argment to CMPPS etc. 183 // That's what the tables expect. 184 switch p.As { 185 case ACMPPD, ACMPPS, ACMPSD, ACMPSS: 186 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 { 187 p.To.Type = obj.TYPE_CONST 188 } 189 } 190 191 // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH. 192 switch p.As { 193 case obj.ACALL, obj.AJMP, obj.ARET: 194 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 195 p.To.Type = obj.TYPE_BRANCH 196 } 197 } 198 199 // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ. 200 if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { 201 switch p.As { 202 case AMOVL: 203 p.As = ALEAL 204 p.From.Type = obj.TYPE_MEM 205 case AMOVQ: 206 p.As = ALEAQ 207 p.From.Type = obj.TYPE_MEM 208 } 209 } 210 211 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 212 if p.From3 != nil { 213 nacladdr(ctxt, p, p.From3) 214 } 215 nacladdr(ctxt, p, &p.From) 216 nacladdr(ctxt, p, &p.To) 217 } 218 219 // Rewrite float constants to values stored in memory. 220 switch p.As { 221 // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx 222 case AMOVSS: 223 if p.From.Type == obj.TYPE_FCONST { 224 if p.From.Val.(float64) == 0 { 225 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 226 p.As = AXORPS 227 p.From = p.To 228 break 229 } 230 } 231 } 232 fallthrough 233 234 case AFMOVF, 235 AFADDF, 236 AFSUBF, 237 AFSUBRF, 238 AFMULF, 239 AFDIVF, 240 AFDIVRF, 241 AFCOMF, 242 AFCOMFP, 243 AADDSS, 244 ASUBSS, 245 AMULSS, 246 ADIVSS, 247 ACOMISS, 248 AUCOMISS: 249 if p.From.Type == obj.TYPE_FCONST { 250 f32 := float32(p.From.Val.(float64)) 251 i32 := math.Float32bits(f32) 252 literal := fmt.Sprintf("$f32.%08x", i32) 253 s := obj.Linklookup(ctxt, literal, 0) 254 p.From.Type = obj.TYPE_MEM 255 p.From.Name = obj.NAME_EXTERN 256 p.From.Sym = s 257 p.From.Sym.Local = true 258 p.From.Offset = 0 259 } 260 261 case AMOVSD: 262 // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx 263 if p.From.Type == obj.TYPE_FCONST { 264 if p.From.Val.(float64) == 0 { 265 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 266 p.As = AXORPS 267 p.From = p.To 268 break 269 } 270 } 271 } 272 fallthrough 273 274 case AFMOVD, 275 AFADDD, 276 AFSUBD, 277 AFSUBRD, 278 AFMULD, 279 AFDIVD, 280 AFDIVRD, 281 AFCOMD, 282 AFCOMDP, 283 AADDSD, 284 ASUBSD, 285 AMULSD, 286 ADIVSD, 287 ACOMISD, 288 AUCOMISD: 289 if p.From.Type == obj.TYPE_FCONST { 290 i64 := math.Float64bits(p.From.Val.(float64)) 291 literal := fmt.Sprintf("$f64.%016x", i64) 292 s := obj.Linklookup(ctxt, literal, 0) 293 p.From.Type = obj.TYPE_MEM 294 p.From.Name = obj.NAME_EXTERN 295 p.From.Sym = s 296 p.From.Sym.Local = true 297 p.From.Offset = 0 298 } 299 } 300 301 if ctxt.Flag_dynlink && (p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO) { 302 var sym *obj.LSym 303 if p.As == obj.ADUFFZERO { 304 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 305 } else { 306 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 307 } 308 offset := p.To.Offset 309 p.As = AMOVQ 310 p.From.Type = obj.TYPE_MEM 311 p.From.Name = obj.NAME_GOTREF 312 p.From.Sym = sym 313 p.To.Type = obj.TYPE_REG 314 p.To.Reg = REG_R15 315 p.To.Offset = 0 316 p.To.Sym = nil 317 p1 := obj.Appendp(ctxt, p) 318 p1.As = AADDQ 319 p1.From.Type = obj.TYPE_CONST 320 p1.From.Offset = offset 321 p1.To.Type = obj.TYPE_REG 322 p1.To.Reg = REG_R15 323 p2 := obj.Appendp(ctxt, p1) 324 p2.As = obj.ACALL 325 p2.To.Type = obj.TYPE_REG 326 p2.To.Reg = REG_R15 327 } 328 329 if ctxt.Flag_dynlink { 330 if p.As == ALEAQ && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 331 p.As = AMOVQ 332 p.From.Type = obj.TYPE_ADDR 333 } 334 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 335 if p.As != AMOVQ { 336 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 337 } 338 if p.To.Type != obj.TYPE_REG { 339 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 340 } 341 p.From.Type = obj.TYPE_MEM 342 p.From.Name = obj.NAME_GOTREF 343 if p.From.Offset != 0 { 344 q := obj.Appendp(ctxt, p) 345 q.As = AADDQ 346 q.From.Type = obj.TYPE_CONST 347 q.From.Offset = p.From.Offset 348 q.To = p.To 349 p.From.Offset = 0 350 } 351 } 352 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 353 ctxt.Diag("don't know how to handle %v with -dynlink", p) 354 } 355 var source *obj.Addr 356 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 357 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 358 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 359 } 360 source = &p.From 361 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 362 source = &p.To 363 } else { 364 return 365 } 366 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 367 return 368 } 369 if source.Type != obj.TYPE_MEM { 370 ctxt.Diag("don't know how to handle %v with -dynlink", p) 371 } 372 p1 := obj.Appendp(ctxt, p) 373 p2 := obj.Appendp(ctxt, p1) 374 375 p1.As = AMOVQ 376 p1.From.Type = obj.TYPE_MEM 377 p1.From.Sym = source.Sym 378 p1.From.Name = obj.NAME_GOTREF 379 p1.To.Type = obj.TYPE_REG 380 p1.To.Reg = REG_R15 381 382 p2.As = p.As 383 p2.From = p.From 384 p2.To = p.To 385 if p.From.Name == obj.NAME_EXTERN { 386 p2.From.Reg = REG_R15 387 p2.From.Name = obj.NAME_NONE 388 p2.From.Sym = nil 389 } else if p.To.Name == obj.NAME_EXTERN { 390 p2.To.Reg = REG_R15 391 p2.To.Name = obj.NAME_NONE 392 p2.To.Sym = nil 393 } else { 394 return 395 } 396 l := p.Link 397 l2 := p2.Link 398 *p = *p1 399 *p1 = *p2 400 p.Link = l 401 p1.Link = l2 402 } 403 } 404 405 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 406 if p.As == ALEAL || p.As == ALEAQ { 407 return 408 } 409 410 if a.Reg == REG_BP { 411 ctxt.Diag("invalid address: %v", p) 412 return 413 } 414 415 if a.Reg == REG_TLS { 416 a.Reg = REG_BP 417 } 418 if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { 419 switch a.Reg { 420 // all ok 421 case REG_BP, REG_SP, REG_R15: 422 break 423 424 default: 425 if a.Index != REG_NONE { 426 ctxt.Diag("invalid address %v", p) 427 } 428 a.Index = a.Reg 429 if a.Index != REG_NONE { 430 a.Scale = 1 431 } 432 a.Reg = REG_R15 433 } 434 } 435 } 436 437 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 438 if ctxt.Tlsg == nil { 439 ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0) 440 } 441 442 if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { 443 ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) 444 } 445 446 ctxt.Cursym = cursym 447 448 if cursym.Text == nil || cursym.Text.Link == nil { 449 return 450 } 451 452 p := cursym.Text 453 autoffset := int32(p.To.Offset) 454 if autoffset < 0 { 455 autoffset = 0 456 } 457 458 var bpsize int 459 if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 { 460 // Make room for to save a base pointer. If autoffset == 0, 461 // this might do something special like a tail jump to 462 // another function, so in that case we omit this. 463 bpsize = ctxt.Arch.Ptrsize 464 465 autoffset += int32(bpsize) 466 p.To.Offset += int64(bpsize) 467 } else { 468 bpsize = 0 469 } 470 471 textarg := int64(p.To.Val.(int32)) 472 cursym.Args = int32(textarg) 473 cursym.Locals = int32(p.To.Offset) 474 475 // TODO(rsc): Remove. 476 if p.Mode == 32 && cursym.Locals < 0 { 477 cursym.Locals = 0 478 } 479 480 // TODO(rsc): Remove 'p.Mode == 64 &&'. 481 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { 482 for q := p; q != nil; q = q.Link { 483 if q.As == obj.ACALL { 484 goto noleaf 485 } 486 if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 { 487 goto noleaf 488 } 489 } 490 491 p.From3.Offset |= obj.NOSPLIT 492 noleaf: 493 } 494 495 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 496 p = obj.Appendp(ctxt, p) 497 p = load_g_cx(ctxt, p) // load g into CX 498 } 499 500 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 501 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 502 } 503 504 if autoffset != 0 { 505 if autoffset%int32(ctxt.Arch.Regsize) != 0 { 506 ctxt.Diag("unaligned stack size %d", autoffset) 507 } 508 p = obj.Appendp(ctxt, p) 509 p.As = AADJSP 510 p.From.Type = obj.TYPE_CONST 511 p.From.Offset = int64(autoffset) 512 p.Spadj = autoffset 513 } else { 514 // zero-byte stack adjustment. 515 // Insert a fake non-zero adjustment so that stkcheck can 516 // recognize the end of the stack-splitting prolog. 517 p = obj.Appendp(ctxt, p) 518 519 p.As = obj.ANOP 520 p.Spadj = int32(-ctxt.Arch.Ptrsize) 521 p = obj.Appendp(ctxt, p) 522 p.As = obj.ANOP 523 p.Spadj = int32(ctxt.Arch.Ptrsize) 524 } 525 526 deltasp := autoffset 527 528 if bpsize > 0 { 529 // Save caller's BP 530 p = obj.Appendp(ctxt, p) 531 532 p.As = AMOVQ 533 p.From.Type = obj.TYPE_REG 534 p.From.Reg = REG_BP 535 p.To.Type = obj.TYPE_MEM 536 p.To.Reg = REG_SP 537 p.To.Scale = 1 538 p.To.Offset = int64(autoffset) - int64(bpsize) 539 540 // Move current frame to BP 541 p = obj.Appendp(ctxt, p) 542 543 p.As = ALEAQ 544 p.From.Type = obj.TYPE_MEM 545 p.From.Reg = REG_SP 546 p.From.Scale = 1 547 p.From.Offset = int64(autoffset) - int64(bpsize) 548 p.To.Type = obj.TYPE_REG 549 p.To.Reg = REG_BP 550 } 551 552 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 553 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 554 // 555 // MOVQ g_panic(CX), BX 556 // TESTQ BX, BX 557 // JEQ end 558 // LEAQ (autoffset+8)(SP), DI 559 // CMPQ panic_argp(BX), DI 560 // JNE end 561 // MOVQ SP, panic_argp(BX) 562 // end: 563 // NOP 564 // 565 // The NOP is needed to give the jumps somewhere to land. 566 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 567 568 p = obj.Appendp(ctxt, p) 569 570 p.As = AMOVQ 571 p.From.Type = obj.TYPE_MEM 572 p.From.Reg = REG_CX 573 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 574 p.To.Type = obj.TYPE_REG 575 p.To.Reg = REG_BX 576 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 577 p.As = AMOVL 578 p.From.Type = obj.TYPE_MEM 579 p.From.Reg = REG_R15 580 p.From.Scale = 1 581 p.From.Index = REG_CX 582 } 583 if p.Mode == 32 { 584 p.As = AMOVL 585 } 586 587 p = obj.Appendp(ctxt, p) 588 p.As = ATESTQ 589 p.From.Type = obj.TYPE_REG 590 p.From.Reg = REG_BX 591 p.To.Type = obj.TYPE_REG 592 p.To.Reg = REG_BX 593 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 594 p.As = ATESTL 595 } 596 597 p = obj.Appendp(ctxt, p) 598 p.As = AJEQ 599 p.To.Type = obj.TYPE_BRANCH 600 p1 := p 601 602 p = obj.Appendp(ctxt, p) 603 p.As = ALEAQ 604 p.From.Type = obj.TYPE_MEM 605 p.From.Reg = REG_SP 606 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize) 607 p.To.Type = obj.TYPE_REG 608 p.To.Reg = REG_DI 609 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 610 p.As = ALEAL 611 } 612 613 p = obj.Appendp(ctxt, p) 614 p.As = ACMPQ 615 p.From.Type = obj.TYPE_MEM 616 p.From.Reg = REG_BX 617 p.From.Offset = 0 // Panic.argp 618 p.To.Type = obj.TYPE_REG 619 p.To.Reg = REG_DI 620 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 621 p.As = ACMPL 622 p.From.Type = obj.TYPE_MEM 623 p.From.Reg = REG_R15 624 p.From.Scale = 1 625 p.From.Index = REG_BX 626 } 627 if p.Mode == 32 { 628 p.As = ACMPL 629 } 630 631 p = obj.Appendp(ctxt, p) 632 p.As = AJNE 633 p.To.Type = obj.TYPE_BRANCH 634 p2 := p 635 636 p = obj.Appendp(ctxt, p) 637 p.As = AMOVQ 638 p.From.Type = obj.TYPE_REG 639 p.From.Reg = REG_SP 640 p.To.Type = obj.TYPE_MEM 641 p.To.Reg = REG_BX 642 p.To.Offset = 0 // Panic.argp 643 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 644 p.As = AMOVL 645 p.To.Type = obj.TYPE_MEM 646 p.To.Reg = REG_R15 647 p.To.Scale = 1 648 p.To.Index = REG_BX 649 } 650 if p.Mode == 32 { 651 p.As = AMOVL 652 } 653 654 p = obj.Appendp(ctxt, p) 655 p.As = obj.ANOP 656 p1.Pcond = p 657 p2.Pcond = p 658 } 659 660 if ctxt.Debugzerostack != 0 && autoffset != 0 && cursym.Text.From3.Offset&obj.NOSPLIT == 0 { 661 // 6l -Z means zero the stack frame on entry. 662 // This slows down function calls but can help avoid 663 // false positives in garbage collection. 664 p = obj.Appendp(ctxt, p) 665 666 p.As = AMOVQ 667 p.From.Type = obj.TYPE_REG 668 p.From.Reg = REG_SP 669 p.To.Type = obj.TYPE_REG 670 p.To.Reg = REG_DI 671 if p.Mode == 32 { 672 p.As = AMOVL 673 } 674 675 p = obj.Appendp(ctxt, p) 676 p.As = AMOVQ 677 p.From.Type = obj.TYPE_CONST 678 p.From.Offset = int64(autoffset) / int64(ctxt.Arch.Regsize) 679 p.To.Type = obj.TYPE_REG 680 p.To.Reg = REG_CX 681 if p.Mode == 32 { 682 p.As = AMOVL 683 } 684 685 p = obj.Appendp(ctxt, p) 686 p.As = AMOVQ 687 p.From.Type = obj.TYPE_CONST 688 p.From.Offset = 0 689 p.To.Type = obj.TYPE_REG 690 p.To.Reg = REG_AX 691 if p.Mode == 32 { 692 p.As = AMOVL 693 } 694 695 p = obj.Appendp(ctxt, p) 696 p.As = AREP 697 698 p = obj.Appendp(ctxt, p) 699 p.As = ASTOSQ 700 if p.Mode == 32 { 701 p.As = ASTOSL 702 } 703 } 704 705 var a int 706 var pcsize int 707 for ; p != nil; p = p.Link { 708 pcsize = int(p.Mode) / 8 709 a = int(p.From.Name) 710 if a == obj.NAME_AUTO { 711 p.From.Offset += int64(deltasp) - int64(bpsize) 712 } 713 if a == obj.NAME_PARAM { 714 p.From.Offset += int64(deltasp) + int64(pcsize) 715 } 716 if p.From3 != nil { 717 a = int(p.From3.Name) 718 if a == obj.NAME_AUTO { 719 p.From3.Offset += int64(deltasp) - int64(bpsize) 720 } 721 if a == obj.NAME_PARAM { 722 p.From3.Offset += int64(deltasp) + int64(pcsize) 723 } 724 } 725 a = int(p.To.Name) 726 if a == obj.NAME_AUTO { 727 p.To.Offset += int64(deltasp) - int64(bpsize) 728 } 729 if a == obj.NAME_PARAM { 730 p.To.Offset += int64(deltasp) + int64(pcsize) 731 } 732 733 switch p.As { 734 default: 735 continue 736 737 case APUSHL, APUSHFL: 738 deltasp += 4 739 p.Spadj = 4 740 continue 741 742 case APUSHQ, APUSHFQ: 743 deltasp += 8 744 p.Spadj = 8 745 continue 746 747 case APUSHW, APUSHFW: 748 deltasp += 2 749 p.Spadj = 2 750 continue 751 752 case APOPL, APOPFL: 753 deltasp -= 4 754 p.Spadj = -4 755 continue 756 757 case APOPQ, APOPFQ: 758 deltasp -= 8 759 p.Spadj = -8 760 continue 761 762 case APOPW, APOPFW: 763 deltasp -= 2 764 p.Spadj = -2 765 continue 766 767 case obj.ARET: 768 break 769 } 770 771 if autoffset != deltasp { 772 ctxt.Diag("unbalanced PUSH/POP") 773 } 774 775 if autoffset != 0 { 776 if bpsize > 0 { 777 // Restore caller's BP 778 p.As = AMOVQ 779 780 p.From.Type = obj.TYPE_MEM 781 p.From.Reg = REG_SP 782 p.From.Scale = 1 783 p.From.Offset = int64(autoffset) - int64(bpsize) 784 p.To.Type = obj.TYPE_REG 785 p.To.Reg = REG_BP 786 p = obj.Appendp(ctxt, p) 787 } 788 789 p.As = AADJSP 790 p.From.Type = obj.TYPE_CONST 791 p.From.Offset = int64(-autoffset) 792 p.Spadj = -autoffset 793 p = obj.Appendp(ctxt, p) 794 p.As = obj.ARET 795 796 // If there are instructions following 797 // this ARET, they come from a branch 798 // with the same stackframe, so undo 799 // the cleanup. 800 p.Spadj = +autoffset 801 } 802 803 if p.To.Sym != nil { // retjmp 804 p.As = obj.AJMP 805 } 806 } 807 } 808 809 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 810 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 811 a.Type = obj.TYPE_MEM 812 a.Reg = REG_R15 813 a.Index = REG_CX 814 a.Scale = 1 815 return 816 } 817 818 a.Type = obj.TYPE_MEM 819 a.Reg = REG_CX 820 } 821 822 // Append code to p to load g into cx. 823 // Overwrites p with the first instruction (no first appendp). 824 // Overwriting p is unusual but it lets use this in both the 825 // prologue (caller must call appendp first) and in the epilogue. 826 // Returns last new instruction. 827 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 828 p.As = AMOVQ 829 if ctxt.Arch.Ptrsize == 4 { 830 p.As = AMOVL 831 } 832 p.From.Type = obj.TYPE_MEM 833 p.From.Reg = REG_TLS 834 p.From.Offset = 0 835 p.To.Type = obj.TYPE_REG 836 p.To.Reg = REG_CX 837 838 next := p.Link 839 progedit(ctxt, p) 840 for p.Link != next { 841 p = p.Link 842 } 843 844 if p.From.Index == REG_TLS { 845 p.From.Scale = 2 846 } 847 848 return p 849 } 850 851 // Append code to p to check for stack split. 852 // Appends to (does not overwrite) p. 853 // Assumes g is in CX. 854 // Returns last new instruction. 855 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 856 cmp := ACMPQ 857 lea := ALEAQ 858 mov := AMOVQ 859 sub := ASUBQ 860 861 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 862 cmp = ACMPL 863 lea = ALEAL 864 mov = AMOVL 865 sub = ASUBL 866 } 867 868 var q1 *obj.Prog 869 if framesize <= obj.StackSmall { 870 // small stack: SP <= stackguard 871 // CMPQ SP, stackguard 872 p = obj.Appendp(ctxt, p) 873 874 p.As = int16(cmp) 875 p.From.Type = obj.TYPE_REG 876 p.From.Reg = REG_SP 877 indir_cx(ctxt, p, &p.To) 878 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 879 if ctxt.Cursym.Cfunc != 0 { 880 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 881 } 882 } else if framesize <= obj.StackBig { 883 // large stack: SP-framesize <= stackguard-StackSmall 884 // LEAQ -xxx(SP), AX 885 // CMPQ AX, stackguard 886 p = obj.Appendp(ctxt, p) 887 888 p.As = int16(lea) 889 p.From.Type = obj.TYPE_MEM 890 p.From.Reg = REG_SP 891 p.From.Offset = -(int64(framesize) - obj.StackSmall) 892 p.To.Type = obj.TYPE_REG 893 p.To.Reg = REG_AX 894 895 p = obj.Appendp(ctxt, p) 896 p.As = int16(cmp) 897 p.From.Type = obj.TYPE_REG 898 p.From.Reg = REG_AX 899 indir_cx(ctxt, p, &p.To) 900 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 901 if ctxt.Cursym.Cfunc != 0 { 902 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 903 } 904 } else { 905 // Such a large stack we need to protect against wraparound. 906 // If SP is close to zero: 907 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 908 // The +StackGuard on both sides is required to keep the left side positive: 909 // SP is allowed to be slightly below stackguard. See stack.h. 910 // 911 // Preemption sets stackguard to StackPreempt, a very large value. 912 // That breaks the math above, so we have to check for that explicitly. 913 // MOVQ stackguard, CX 914 // CMPQ CX, $StackPreempt 915 // JEQ label-of-call-to-morestack 916 // LEAQ StackGuard(SP), AX 917 // SUBQ CX, AX 918 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 919 920 p = obj.Appendp(ctxt, p) 921 922 p.As = int16(mov) 923 indir_cx(ctxt, p, &p.From) 924 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 925 if ctxt.Cursym.Cfunc != 0 { 926 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 927 } 928 p.To.Type = obj.TYPE_REG 929 p.To.Reg = REG_SI 930 931 p = obj.Appendp(ctxt, p) 932 p.As = int16(cmp) 933 p.From.Type = obj.TYPE_REG 934 p.From.Reg = REG_SI 935 p.To.Type = obj.TYPE_CONST 936 p.To.Offset = obj.StackPreempt 937 if p.Mode == 32 { 938 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 939 } 940 941 p = obj.Appendp(ctxt, p) 942 p.As = AJEQ 943 p.To.Type = obj.TYPE_BRANCH 944 q1 = p 945 946 p = obj.Appendp(ctxt, p) 947 p.As = int16(lea) 948 p.From.Type = obj.TYPE_MEM 949 p.From.Reg = REG_SP 950 p.From.Offset = obj.StackGuard 951 p.To.Type = obj.TYPE_REG 952 p.To.Reg = REG_AX 953 954 p = obj.Appendp(ctxt, p) 955 p.As = int16(sub) 956 p.From.Type = obj.TYPE_REG 957 p.From.Reg = REG_SI 958 p.To.Type = obj.TYPE_REG 959 p.To.Reg = REG_AX 960 961 p = obj.Appendp(ctxt, p) 962 p.As = int16(cmp) 963 p.From.Type = obj.TYPE_REG 964 p.From.Reg = REG_AX 965 p.To.Type = obj.TYPE_CONST 966 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 967 } 968 969 // common 970 jls := obj.Appendp(ctxt, p) 971 jls.As = AJLS 972 jls.To.Type = obj.TYPE_BRANCH 973 974 var last *obj.Prog 975 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 976 } 977 978 call := obj.Appendp(ctxt, last) 979 call.Lineno = ctxt.Cursym.Text.Lineno 980 call.Mode = ctxt.Cursym.Text.Mode 981 call.As = obj.ACALL 982 call.To.Type = obj.TYPE_BRANCH 983 morestack := "runtime.morestack" 984 switch { 985 case ctxt.Cursym.Cfunc != 0: 986 morestack = "runtime.morestackc" 987 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 988 morestack = "runtime.morestack_noctxt" 989 } 990 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 991 992 jmp := obj.Appendp(ctxt, call) 993 jmp.As = obj.AJMP 994 jmp.To.Type = obj.TYPE_BRANCH 995 jmp.Pcond = ctxt.Cursym.Text.Link 996 997 jls.Pcond = call 998 if q1 != nil { 999 q1.Pcond = call 1000 } 1001 1002 return jls 1003 } 1004 1005 func follow(ctxt *obj.Link, s *obj.LSym) { 1006 ctxt.Cursym = s 1007 1008 firstp := ctxt.NewProg() 1009 lastp := firstp 1010 xfol(ctxt, s.Text, &lastp) 1011 lastp.Link = nil 1012 s.Text = firstp.Link 1013 } 1014 1015 func nofollow(a int) bool { 1016 switch a { 1017 case obj.AJMP, 1018 obj.ARET, 1019 AIRETL, 1020 AIRETQ, 1021 AIRETW, 1022 ARETFL, 1023 ARETFQ, 1024 ARETFW, 1025 obj.AUNDEF: 1026 return true 1027 } 1028 1029 return false 1030 } 1031 1032 func pushpop(a int) bool { 1033 switch a { 1034 case APUSHL, 1035 APUSHFL, 1036 APUSHQ, 1037 APUSHFQ, 1038 APUSHW, 1039 APUSHFW, 1040 APOPL, 1041 APOPFL, 1042 APOPQ, 1043 APOPFQ, 1044 APOPW, 1045 APOPFW: 1046 return true 1047 } 1048 1049 return false 1050 } 1051 1052 func relinv(a int16) int16 { 1053 switch a { 1054 case AJEQ: 1055 return AJNE 1056 case AJNE: 1057 return AJEQ 1058 case AJLE: 1059 return AJGT 1060 case AJLS: 1061 return AJHI 1062 case AJLT: 1063 return AJGE 1064 case AJMI: 1065 return AJPL 1066 case AJGE: 1067 return AJLT 1068 case AJPL: 1069 return AJMI 1070 case AJGT: 1071 return AJLE 1072 case AJHI: 1073 return AJLS 1074 case AJCS: 1075 return AJCC 1076 case AJCC: 1077 return AJCS 1078 case AJPS: 1079 return AJPC 1080 case AJPC: 1081 return AJPS 1082 case AJOS: 1083 return AJOC 1084 case AJOC: 1085 return AJOS 1086 } 1087 1088 log.Fatalf("unknown relation: %s", obj.Aconv(int(a))) 1089 return 0 1090 } 1091 1092 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1093 var q *obj.Prog 1094 var i int 1095 var a int 1096 1097 loop: 1098 if p == nil { 1099 return 1100 } 1101 if p.As == obj.AJMP { 1102 q = p.Pcond 1103 if q != nil && q.As != obj.ATEXT { 1104 /* mark instruction as done and continue layout at target of jump */ 1105 p.Mark = 1 1106 1107 p = q 1108 if p.Mark == 0 { 1109 goto loop 1110 } 1111 } 1112 } 1113 1114 if p.Mark != 0 { 1115 /* 1116 * p goes here, but already used it elsewhere. 1117 * copy up to 4 instructions or else branch to other copy. 1118 */ 1119 i = 0 1120 q = p 1121 for ; i < 4; i, q = i+1, q.Link { 1122 if q == nil { 1123 break 1124 } 1125 if q == *last { 1126 break 1127 } 1128 a = int(q.As) 1129 if a == obj.ANOP { 1130 i-- 1131 continue 1132 } 1133 1134 if nofollow(a) || pushpop(a) { 1135 break // NOTE(rsc): arm does goto copy 1136 } 1137 if q.Pcond == nil || q.Pcond.Mark != 0 { 1138 continue 1139 } 1140 if a == obj.ACALL || a == ALOOP { 1141 continue 1142 } 1143 for { 1144 if p.As == obj.ANOP { 1145 p = p.Link 1146 continue 1147 } 1148 1149 q = obj.Copyp(ctxt, p) 1150 p = p.Link 1151 q.Mark = 1 1152 (*last).Link = q 1153 *last = q 1154 if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 { 1155 continue 1156 } 1157 1158 q.As = relinv(q.As) 1159 p = q.Pcond 1160 q.Pcond = q.Link 1161 q.Link = p 1162 xfol(ctxt, q.Link, last) 1163 p = q.Link 1164 if p.Mark != 0 { 1165 return 1166 } 1167 goto loop 1168 /* */ 1169 } 1170 } 1171 q = ctxt.NewProg() 1172 q.As = obj.AJMP 1173 q.Lineno = p.Lineno 1174 q.To.Type = obj.TYPE_BRANCH 1175 q.To.Offset = p.Pc 1176 q.Pcond = p 1177 p = q 1178 } 1179 1180 /* emit p */ 1181 p.Mark = 1 1182 1183 (*last).Link = p 1184 *last = p 1185 a = int(p.As) 1186 1187 /* continue loop with what comes after p */ 1188 if nofollow(a) { 1189 return 1190 } 1191 if p.Pcond != nil && a != obj.ACALL { 1192 /* 1193 * some kind of conditional branch. 1194 * recurse to follow one path. 1195 * continue loop on the other. 1196 */ 1197 q = obj.Brchain(ctxt, p.Pcond) 1198 if q != nil { 1199 p.Pcond = q 1200 } 1201 q = obj.Brchain(ctxt, p.Link) 1202 if q != nil { 1203 p.Link = q 1204 } 1205 if p.From.Type == obj.TYPE_CONST { 1206 if p.From.Offset == 1 { 1207 /* 1208 * expect conditional jump to be taken. 1209 * rewrite so that's the fall-through case. 1210 */ 1211 p.As = relinv(int16(a)) 1212 1213 q = p.Link 1214 p.Link = p.Pcond 1215 p.Pcond = q 1216 } 1217 } else { 1218 q = p.Link 1219 if q.Mark != 0 { 1220 if a != ALOOP { 1221 p.As = relinv(int16(a)) 1222 p.Link = p.Pcond 1223 p.Pcond = q 1224 } 1225 } 1226 } 1227 1228 xfol(ctxt, p.Link, last) 1229 if p.Pcond.Mark != 0 { 1230 return 1231 } 1232 p = p.Pcond 1233 goto loop 1234 } 1235 1236 p = p.Link 1237 goto loop 1238 } 1239 1240 var unaryDst = map[int]bool{ 1241 ABSWAPL: true, 1242 ABSWAPQ: true, 1243 ACMPXCHG8B: true, 1244 ADECB: true, 1245 ADECL: true, 1246 ADECQ: true, 1247 ADECW: true, 1248 AINCB: true, 1249 AINCL: true, 1250 AINCQ: true, 1251 AINCW: true, 1252 ANEGB: true, 1253 ANEGL: true, 1254 ANEGQ: true, 1255 ANEGW: true, 1256 ANOTB: true, 1257 ANOTL: true, 1258 ANOTQ: true, 1259 ANOTW: true, 1260 APOPL: true, 1261 APOPQ: true, 1262 APOPW: true, 1263 ASETCC: true, 1264 ASETCS: true, 1265 ASETEQ: true, 1266 ASETGE: true, 1267 ASETGT: true, 1268 ASETHI: true, 1269 ASETLE: true, 1270 ASETLS: true, 1271 ASETLT: true, 1272 ASETMI: true, 1273 ASETNE: true, 1274 ASETOC: true, 1275 ASETOS: true, 1276 ASETPC: true, 1277 ASETPL: true, 1278 ASETPS: true, 1279 AFFREE: true, 1280 AFLDENV: true, 1281 AFSAVE: true, 1282 AFSTCW: true, 1283 AFSTENV: true, 1284 AFSTSW: true, 1285 AFXSAVE: true, 1286 AFXSAVE64: true, 1287 ASTMXCSR: true, 1288 } 1289 1290 var Linkamd64 = obj.LinkArch{ 1291 ByteOrder: binary.LittleEndian, 1292 Name: "amd64", 1293 Thechar: '6', 1294 Preprocess: preprocess, 1295 Assemble: span6, 1296 Follow: follow, 1297 Progedit: progedit, 1298 UnaryDst: unaryDst, 1299 Minlc: 1, 1300 Ptrsize: 8, 1301 Regsize: 8, 1302 } 1303 1304 var Linkamd64p32 = obj.LinkArch{ 1305 ByteOrder: binary.LittleEndian, 1306 Name: "amd64p32", 1307 Thechar: '6', 1308 Preprocess: preprocess, 1309 Assemble: span6, 1310 Follow: follow, 1311 Progedit: progedit, 1312 UnaryDst: unaryDst, 1313 Minlc: 1, 1314 Ptrsize: 4, 1315 Regsize: 8, 1316 } 1317 1318 var Link386 = obj.LinkArch{ 1319 ByteOrder: binary.LittleEndian, 1320 Name: "386", 1321 Thechar: '8', 1322 Preprocess: preprocess, 1323 Assemble: span6, 1324 Follow: follow, 1325 Progedit: progedit, 1326 UnaryDst: unaryDst, 1327 Minlc: 1, 1328 Ptrsize: 4, 1329 Regsize: 4, 1330 } 1331