github.com/d4l3k/go@v0.0.0-20151015000803-65fc379daeda/src/cmd/internal/obj/x86/obj6.go (about) 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@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 "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.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { 439 ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) 440 } 441 442 ctxt.Cursym = cursym 443 444 if cursym.Text == nil || cursym.Text.Link == nil { 445 return 446 } 447 448 p := cursym.Text 449 autoffset := int32(p.To.Offset) 450 if autoffset < 0 { 451 autoffset = 0 452 } 453 454 var bpsize int 455 if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 { 456 // Make room for to save a base pointer. If autoffset == 0, 457 // this might do something special like a tail jump to 458 // another function, so in that case we omit this. 459 bpsize = ctxt.Arch.Ptrsize 460 461 autoffset += int32(bpsize) 462 p.To.Offset += int64(bpsize) 463 } else { 464 bpsize = 0 465 } 466 467 textarg := int64(p.To.Val.(int32)) 468 cursym.Args = int32(textarg) 469 cursym.Locals = int32(p.To.Offset) 470 471 // TODO(rsc): Remove. 472 if p.Mode == 32 && cursym.Locals < 0 { 473 cursym.Locals = 0 474 } 475 476 // TODO(rsc): Remove 'p.Mode == 64 &&'. 477 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { 478 for q := p; q != nil; q = q.Link { 479 if q.As == obj.ACALL { 480 goto noleaf 481 } 482 if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 { 483 goto noleaf 484 } 485 } 486 487 p.From3.Offset |= obj.NOSPLIT 488 noleaf: 489 } 490 491 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 492 p = obj.Appendp(ctxt, p) 493 p = load_g_cx(ctxt, p) // load g into CX 494 } 495 496 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 497 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 498 } 499 500 if autoffset != 0 { 501 if autoffset%int32(ctxt.Arch.Regsize) != 0 { 502 ctxt.Diag("unaligned stack size %d", autoffset) 503 } 504 p = obj.Appendp(ctxt, p) 505 p.As = AADJSP 506 p.From.Type = obj.TYPE_CONST 507 p.From.Offset = int64(autoffset) 508 p.Spadj = autoffset 509 } else { 510 // zero-byte stack adjustment. 511 // Insert a fake non-zero adjustment so that stkcheck can 512 // recognize the end of the stack-splitting prolog. 513 p = obj.Appendp(ctxt, p) 514 515 p.As = obj.ANOP 516 p.Spadj = int32(-ctxt.Arch.Ptrsize) 517 p = obj.Appendp(ctxt, p) 518 p.As = obj.ANOP 519 p.Spadj = int32(ctxt.Arch.Ptrsize) 520 } 521 522 deltasp := autoffset 523 524 if bpsize > 0 { 525 // Save caller's BP 526 p = obj.Appendp(ctxt, p) 527 528 p.As = AMOVQ 529 p.From.Type = obj.TYPE_REG 530 p.From.Reg = REG_BP 531 p.To.Type = obj.TYPE_MEM 532 p.To.Reg = REG_SP 533 p.To.Scale = 1 534 p.To.Offset = int64(autoffset) - int64(bpsize) 535 536 // Move current frame to BP 537 p = obj.Appendp(ctxt, p) 538 539 p.As = ALEAQ 540 p.From.Type = obj.TYPE_MEM 541 p.From.Reg = REG_SP 542 p.From.Scale = 1 543 p.From.Offset = int64(autoffset) - int64(bpsize) 544 p.To.Type = obj.TYPE_REG 545 p.To.Reg = REG_BP 546 } 547 548 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 549 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 550 // 551 // MOVQ g_panic(CX), BX 552 // TESTQ BX, BX 553 // JEQ end 554 // LEAQ (autoffset+8)(SP), DI 555 // CMPQ panic_argp(BX), DI 556 // JNE end 557 // MOVQ SP, panic_argp(BX) 558 // end: 559 // NOP 560 // 561 // The NOP is needed to give the jumps somewhere to land. 562 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 563 564 p = obj.Appendp(ctxt, p) 565 566 p.As = AMOVQ 567 p.From.Type = obj.TYPE_MEM 568 p.From.Reg = REG_CX 569 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 570 p.To.Type = obj.TYPE_REG 571 p.To.Reg = REG_BX 572 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 573 p.As = AMOVL 574 p.From.Type = obj.TYPE_MEM 575 p.From.Reg = REG_R15 576 p.From.Scale = 1 577 p.From.Index = REG_CX 578 } 579 if p.Mode == 32 { 580 p.As = AMOVL 581 } 582 583 p = obj.Appendp(ctxt, p) 584 p.As = ATESTQ 585 p.From.Type = obj.TYPE_REG 586 p.From.Reg = REG_BX 587 p.To.Type = obj.TYPE_REG 588 p.To.Reg = REG_BX 589 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 590 p.As = ATESTL 591 } 592 593 p = obj.Appendp(ctxt, p) 594 p.As = AJEQ 595 p.To.Type = obj.TYPE_BRANCH 596 p1 := p 597 598 p = obj.Appendp(ctxt, p) 599 p.As = ALEAQ 600 p.From.Type = obj.TYPE_MEM 601 p.From.Reg = REG_SP 602 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize) 603 p.To.Type = obj.TYPE_REG 604 p.To.Reg = REG_DI 605 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 606 p.As = ALEAL 607 } 608 609 p = obj.Appendp(ctxt, p) 610 p.As = ACMPQ 611 p.From.Type = obj.TYPE_MEM 612 p.From.Reg = REG_BX 613 p.From.Offset = 0 // Panic.argp 614 p.To.Type = obj.TYPE_REG 615 p.To.Reg = REG_DI 616 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 617 p.As = ACMPL 618 p.From.Type = obj.TYPE_MEM 619 p.From.Reg = REG_R15 620 p.From.Scale = 1 621 p.From.Index = REG_BX 622 } 623 if p.Mode == 32 { 624 p.As = ACMPL 625 } 626 627 p = obj.Appendp(ctxt, p) 628 p.As = AJNE 629 p.To.Type = obj.TYPE_BRANCH 630 p2 := p 631 632 p = obj.Appendp(ctxt, p) 633 p.As = AMOVQ 634 p.From.Type = obj.TYPE_REG 635 p.From.Reg = REG_SP 636 p.To.Type = obj.TYPE_MEM 637 p.To.Reg = REG_BX 638 p.To.Offset = 0 // Panic.argp 639 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 640 p.As = AMOVL 641 p.To.Type = obj.TYPE_MEM 642 p.To.Reg = REG_R15 643 p.To.Scale = 1 644 p.To.Index = REG_BX 645 } 646 if p.Mode == 32 { 647 p.As = AMOVL 648 } 649 650 p = obj.Appendp(ctxt, p) 651 p.As = obj.ANOP 652 p1.Pcond = p 653 p2.Pcond = p 654 } 655 656 var a int 657 var pcsize int 658 for ; p != nil; p = p.Link { 659 pcsize = int(p.Mode) / 8 660 a = int(p.From.Name) 661 if a == obj.NAME_AUTO { 662 p.From.Offset += int64(deltasp) - int64(bpsize) 663 } 664 if a == obj.NAME_PARAM { 665 p.From.Offset += int64(deltasp) + int64(pcsize) 666 } 667 if p.From3 != nil { 668 a = int(p.From3.Name) 669 if a == obj.NAME_AUTO { 670 p.From3.Offset += int64(deltasp) - int64(bpsize) 671 } 672 if a == obj.NAME_PARAM { 673 p.From3.Offset += int64(deltasp) + int64(pcsize) 674 } 675 } 676 a = int(p.To.Name) 677 if a == obj.NAME_AUTO { 678 p.To.Offset += int64(deltasp) - int64(bpsize) 679 } 680 if a == obj.NAME_PARAM { 681 p.To.Offset += int64(deltasp) + int64(pcsize) 682 } 683 684 switch p.As { 685 default: 686 continue 687 688 case APUSHL, APUSHFL: 689 deltasp += 4 690 p.Spadj = 4 691 continue 692 693 case APUSHQ, APUSHFQ: 694 deltasp += 8 695 p.Spadj = 8 696 continue 697 698 case APUSHW, APUSHFW: 699 deltasp += 2 700 p.Spadj = 2 701 continue 702 703 case APOPL, APOPFL: 704 deltasp -= 4 705 p.Spadj = -4 706 continue 707 708 case APOPQ, APOPFQ: 709 deltasp -= 8 710 p.Spadj = -8 711 continue 712 713 case APOPW, APOPFW: 714 deltasp -= 2 715 p.Spadj = -2 716 continue 717 718 case obj.ARET: 719 break 720 } 721 722 if autoffset != deltasp { 723 ctxt.Diag("unbalanced PUSH/POP") 724 } 725 726 if autoffset != 0 { 727 if bpsize > 0 { 728 // Restore caller's BP 729 p.As = AMOVQ 730 731 p.From.Type = obj.TYPE_MEM 732 p.From.Reg = REG_SP 733 p.From.Scale = 1 734 p.From.Offset = int64(autoffset) - int64(bpsize) 735 p.To.Type = obj.TYPE_REG 736 p.To.Reg = REG_BP 737 p = obj.Appendp(ctxt, p) 738 } 739 740 p.As = AADJSP 741 p.From.Type = obj.TYPE_CONST 742 p.From.Offset = int64(-autoffset) 743 p.Spadj = -autoffset 744 p = obj.Appendp(ctxt, p) 745 p.As = obj.ARET 746 747 // If there are instructions following 748 // this ARET, they come from a branch 749 // with the same stackframe, so undo 750 // the cleanup. 751 p.Spadj = +autoffset 752 } 753 754 if p.To.Sym != nil { // retjmp 755 p.As = obj.AJMP 756 } 757 } 758 } 759 760 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 761 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 762 a.Type = obj.TYPE_MEM 763 a.Reg = REG_R15 764 a.Index = REG_CX 765 a.Scale = 1 766 return 767 } 768 769 a.Type = obj.TYPE_MEM 770 a.Reg = REG_CX 771 } 772 773 // Append code to p to load g into cx. 774 // Overwrites p with the first instruction (no first appendp). 775 // Overwriting p is unusual but it lets use this in both the 776 // prologue (caller must call appendp first) and in the epilogue. 777 // Returns last new instruction. 778 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 779 p.As = AMOVQ 780 if ctxt.Arch.Ptrsize == 4 { 781 p.As = AMOVL 782 } 783 p.From.Type = obj.TYPE_MEM 784 p.From.Reg = REG_TLS 785 p.From.Offset = 0 786 p.To.Type = obj.TYPE_REG 787 p.To.Reg = REG_CX 788 789 next := p.Link 790 progedit(ctxt, p) 791 for p.Link != next { 792 p = p.Link 793 } 794 795 if p.From.Index == REG_TLS { 796 p.From.Scale = 2 797 } 798 799 return p 800 } 801 802 // Append code to p to check for stack split. 803 // Appends to (does not overwrite) p. 804 // Assumes g is in CX. 805 // Returns last new instruction. 806 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 807 cmp := ACMPQ 808 lea := ALEAQ 809 mov := AMOVQ 810 sub := ASUBQ 811 812 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 813 cmp = ACMPL 814 lea = ALEAL 815 mov = AMOVL 816 sub = ASUBL 817 } 818 819 var q1 *obj.Prog 820 if framesize <= obj.StackSmall { 821 // small stack: SP <= stackguard 822 // CMPQ SP, stackguard 823 p = obj.Appendp(ctxt, p) 824 825 p.As = int16(cmp) 826 p.From.Type = obj.TYPE_REG 827 p.From.Reg = REG_SP 828 indir_cx(ctxt, p, &p.To) 829 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 830 if ctxt.Cursym.Cfunc != 0 { 831 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 832 } 833 } else if framesize <= obj.StackBig { 834 // large stack: SP-framesize <= stackguard-StackSmall 835 // LEAQ -xxx(SP), AX 836 // CMPQ AX, stackguard 837 p = obj.Appendp(ctxt, p) 838 839 p.As = int16(lea) 840 p.From.Type = obj.TYPE_MEM 841 p.From.Reg = REG_SP 842 p.From.Offset = -(int64(framesize) - obj.StackSmall) 843 p.To.Type = obj.TYPE_REG 844 p.To.Reg = REG_AX 845 846 p = obj.Appendp(ctxt, p) 847 p.As = int16(cmp) 848 p.From.Type = obj.TYPE_REG 849 p.From.Reg = REG_AX 850 indir_cx(ctxt, p, &p.To) 851 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 852 if ctxt.Cursym.Cfunc != 0 { 853 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 854 } 855 } else { 856 // Such a large stack we need to protect against wraparound. 857 // If SP is close to zero: 858 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 859 // The +StackGuard on both sides is required to keep the left side positive: 860 // SP is allowed to be slightly below stackguard. See stack.h. 861 // 862 // Preemption sets stackguard to StackPreempt, a very large value. 863 // That breaks the math above, so we have to check for that explicitly. 864 // MOVQ stackguard, CX 865 // CMPQ CX, $StackPreempt 866 // JEQ label-of-call-to-morestack 867 // LEAQ StackGuard(SP), AX 868 // SUBQ CX, AX 869 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 870 871 p = obj.Appendp(ctxt, p) 872 873 p.As = int16(mov) 874 indir_cx(ctxt, p, &p.From) 875 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 876 if ctxt.Cursym.Cfunc != 0 { 877 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 878 } 879 p.To.Type = obj.TYPE_REG 880 p.To.Reg = REG_SI 881 882 p = obj.Appendp(ctxt, p) 883 p.As = int16(cmp) 884 p.From.Type = obj.TYPE_REG 885 p.From.Reg = REG_SI 886 p.To.Type = obj.TYPE_CONST 887 p.To.Offset = obj.StackPreempt 888 if p.Mode == 32 { 889 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 890 } 891 892 p = obj.Appendp(ctxt, p) 893 p.As = AJEQ 894 p.To.Type = obj.TYPE_BRANCH 895 q1 = p 896 897 p = obj.Appendp(ctxt, p) 898 p.As = int16(lea) 899 p.From.Type = obj.TYPE_MEM 900 p.From.Reg = REG_SP 901 p.From.Offset = obj.StackGuard 902 p.To.Type = obj.TYPE_REG 903 p.To.Reg = REG_AX 904 905 p = obj.Appendp(ctxt, p) 906 p.As = int16(sub) 907 p.From.Type = obj.TYPE_REG 908 p.From.Reg = REG_SI 909 p.To.Type = obj.TYPE_REG 910 p.To.Reg = REG_AX 911 912 p = obj.Appendp(ctxt, p) 913 p.As = int16(cmp) 914 p.From.Type = obj.TYPE_REG 915 p.From.Reg = REG_AX 916 p.To.Type = obj.TYPE_CONST 917 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 918 } 919 920 // common 921 jls := obj.Appendp(ctxt, p) 922 jls.As = AJLS 923 jls.To.Type = obj.TYPE_BRANCH 924 925 var last *obj.Prog 926 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 927 } 928 929 call := obj.Appendp(ctxt, last) 930 call.Lineno = ctxt.Cursym.Text.Lineno 931 call.Mode = ctxt.Cursym.Text.Mode 932 call.As = obj.ACALL 933 call.To.Type = obj.TYPE_BRANCH 934 morestack := "runtime.morestack" 935 switch { 936 case ctxt.Cursym.Cfunc != 0: 937 morestack = "runtime.morestackc" 938 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 939 morestack = "runtime.morestack_noctxt" 940 } 941 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 942 943 jmp := obj.Appendp(ctxt, call) 944 jmp.As = obj.AJMP 945 jmp.To.Type = obj.TYPE_BRANCH 946 jmp.Pcond = ctxt.Cursym.Text.Link 947 948 jls.Pcond = call 949 if q1 != nil { 950 q1.Pcond = call 951 } 952 953 return jls 954 } 955 956 func follow(ctxt *obj.Link, s *obj.LSym) { 957 ctxt.Cursym = s 958 959 firstp := ctxt.NewProg() 960 lastp := firstp 961 xfol(ctxt, s.Text, &lastp) 962 lastp.Link = nil 963 s.Text = firstp.Link 964 } 965 966 func nofollow(a int) bool { 967 switch a { 968 case obj.AJMP, 969 obj.ARET, 970 AIRETL, 971 AIRETQ, 972 AIRETW, 973 ARETFL, 974 ARETFQ, 975 ARETFW, 976 obj.AUNDEF: 977 return true 978 } 979 980 return false 981 } 982 983 func pushpop(a int) bool { 984 switch a { 985 case APUSHL, 986 APUSHFL, 987 APUSHQ, 988 APUSHFQ, 989 APUSHW, 990 APUSHFW, 991 APOPL, 992 APOPFL, 993 APOPQ, 994 APOPFQ, 995 APOPW, 996 APOPFW: 997 return true 998 } 999 1000 return false 1001 } 1002 1003 func relinv(a int16) int16 { 1004 switch a { 1005 case AJEQ: 1006 return AJNE 1007 case AJNE: 1008 return AJEQ 1009 case AJLE: 1010 return AJGT 1011 case AJLS: 1012 return AJHI 1013 case AJLT: 1014 return AJGE 1015 case AJMI: 1016 return AJPL 1017 case AJGE: 1018 return AJLT 1019 case AJPL: 1020 return AJMI 1021 case AJGT: 1022 return AJLE 1023 case AJHI: 1024 return AJLS 1025 case AJCS: 1026 return AJCC 1027 case AJCC: 1028 return AJCS 1029 case AJPS: 1030 return AJPC 1031 case AJPC: 1032 return AJPS 1033 case AJOS: 1034 return AJOC 1035 case AJOC: 1036 return AJOS 1037 } 1038 1039 log.Fatalf("unknown relation: %s", obj.Aconv(int(a))) 1040 return 0 1041 } 1042 1043 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1044 var q *obj.Prog 1045 var i int 1046 var a int 1047 1048 loop: 1049 if p == nil { 1050 return 1051 } 1052 if p.As == obj.AJMP { 1053 q = p.Pcond 1054 if q != nil && q.As != obj.ATEXT { 1055 /* mark instruction as done and continue layout at target of jump */ 1056 p.Mark = 1 1057 1058 p = q 1059 if p.Mark == 0 { 1060 goto loop 1061 } 1062 } 1063 } 1064 1065 if p.Mark != 0 { 1066 /* 1067 * p goes here, but already used it elsewhere. 1068 * copy up to 4 instructions or else branch to other copy. 1069 */ 1070 i = 0 1071 q = p 1072 for ; i < 4; i, q = i+1, q.Link { 1073 if q == nil { 1074 break 1075 } 1076 if q == *last { 1077 break 1078 } 1079 a = int(q.As) 1080 if a == obj.ANOP { 1081 i-- 1082 continue 1083 } 1084 1085 if nofollow(a) || pushpop(a) { 1086 break // NOTE(rsc): arm does goto copy 1087 } 1088 if q.Pcond == nil || q.Pcond.Mark != 0 { 1089 continue 1090 } 1091 if a == obj.ACALL || a == ALOOP { 1092 continue 1093 } 1094 for { 1095 if p.As == obj.ANOP { 1096 p = p.Link 1097 continue 1098 } 1099 1100 q = obj.Copyp(ctxt, p) 1101 p = p.Link 1102 q.Mark = 1 1103 (*last).Link = q 1104 *last = q 1105 if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 { 1106 continue 1107 } 1108 1109 q.As = relinv(q.As) 1110 p = q.Pcond 1111 q.Pcond = q.Link 1112 q.Link = p 1113 xfol(ctxt, q.Link, last) 1114 p = q.Link 1115 if p.Mark != 0 { 1116 return 1117 } 1118 goto loop 1119 /* */ 1120 } 1121 } 1122 q = ctxt.NewProg() 1123 q.As = obj.AJMP 1124 q.Lineno = p.Lineno 1125 q.To.Type = obj.TYPE_BRANCH 1126 q.To.Offset = p.Pc 1127 q.Pcond = p 1128 p = q 1129 } 1130 1131 /* emit p */ 1132 p.Mark = 1 1133 1134 (*last).Link = p 1135 *last = p 1136 a = int(p.As) 1137 1138 /* continue loop with what comes after p */ 1139 if nofollow(a) { 1140 return 1141 } 1142 if p.Pcond != nil && a != obj.ACALL { 1143 /* 1144 * some kind of conditional branch. 1145 * recurse to follow one path. 1146 * continue loop on the other. 1147 */ 1148 q = obj.Brchain(ctxt, p.Pcond) 1149 if q != nil { 1150 p.Pcond = q 1151 } 1152 q = obj.Brchain(ctxt, p.Link) 1153 if q != nil { 1154 p.Link = q 1155 } 1156 if p.From.Type == obj.TYPE_CONST { 1157 if p.From.Offset == 1 { 1158 /* 1159 * expect conditional jump to be taken. 1160 * rewrite so that's the fall-through case. 1161 */ 1162 p.As = relinv(int16(a)) 1163 1164 q = p.Link 1165 p.Link = p.Pcond 1166 p.Pcond = q 1167 } 1168 } else { 1169 q = p.Link 1170 if q.Mark != 0 { 1171 if a != ALOOP { 1172 p.As = relinv(int16(a)) 1173 p.Link = p.Pcond 1174 p.Pcond = q 1175 } 1176 } 1177 } 1178 1179 xfol(ctxt, p.Link, last) 1180 if p.Pcond.Mark != 0 { 1181 return 1182 } 1183 p = p.Pcond 1184 goto loop 1185 } 1186 1187 p = p.Link 1188 goto loop 1189 } 1190 1191 var unaryDst = map[int]bool{ 1192 ABSWAPL: true, 1193 ABSWAPQ: true, 1194 ACMPXCHG8B: true, 1195 ADECB: true, 1196 ADECL: true, 1197 ADECQ: true, 1198 ADECW: true, 1199 AINCB: true, 1200 AINCL: true, 1201 AINCQ: true, 1202 AINCW: true, 1203 ANEGB: true, 1204 ANEGL: true, 1205 ANEGQ: true, 1206 ANEGW: true, 1207 ANOTB: true, 1208 ANOTL: true, 1209 ANOTQ: true, 1210 ANOTW: true, 1211 APOPL: true, 1212 APOPQ: true, 1213 APOPW: true, 1214 ASETCC: true, 1215 ASETCS: true, 1216 ASETEQ: true, 1217 ASETGE: true, 1218 ASETGT: true, 1219 ASETHI: true, 1220 ASETLE: true, 1221 ASETLS: true, 1222 ASETLT: true, 1223 ASETMI: true, 1224 ASETNE: true, 1225 ASETOC: true, 1226 ASETOS: true, 1227 ASETPC: true, 1228 ASETPL: true, 1229 ASETPS: true, 1230 AFFREE: true, 1231 AFLDENV: true, 1232 AFSAVE: true, 1233 AFSTCW: true, 1234 AFSTENV: true, 1235 AFSTSW: true, 1236 AFXSAVE: true, 1237 AFXSAVE64: true, 1238 ASTMXCSR: true, 1239 } 1240 1241 var Linkamd64 = obj.LinkArch{ 1242 ByteOrder: binary.LittleEndian, 1243 Name: "amd64", 1244 Thechar: '6', 1245 Preprocess: preprocess, 1246 Assemble: span6, 1247 Follow: follow, 1248 Progedit: progedit, 1249 UnaryDst: unaryDst, 1250 Minlc: 1, 1251 Ptrsize: 8, 1252 Regsize: 8, 1253 } 1254 1255 var Linkamd64p32 = obj.LinkArch{ 1256 ByteOrder: binary.LittleEndian, 1257 Name: "amd64p32", 1258 Thechar: '6', 1259 Preprocess: preprocess, 1260 Assemble: span6, 1261 Follow: follow, 1262 Progedit: progedit, 1263 UnaryDst: unaryDst, 1264 Minlc: 1, 1265 Ptrsize: 4, 1266 Regsize: 8, 1267 } 1268 1269 var Link386 = obj.LinkArch{ 1270 ByteOrder: binary.LittleEndian, 1271 Name: "386", 1272 Thechar: '8', 1273 Preprocess: preprocess, 1274 Assemble: span6, 1275 Follow: follow, 1276 Progedit: progedit, 1277 UnaryDst: unaryDst, 1278 Minlc: 1, 1279 Ptrsize: 4, 1280 Regsize: 4, 1281 }