github.com/alash3al/go@v0.0.0-20150827002835-d497eeb00540/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.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 var a int 661 var pcsize int 662 for ; p != nil; p = p.Link { 663 pcsize = int(p.Mode) / 8 664 a = int(p.From.Name) 665 if a == obj.NAME_AUTO { 666 p.From.Offset += int64(deltasp) - int64(bpsize) 667 } 668 if a == obj.NAME_PARAM { 669 p.From.Offset += int64(deltasp) + int64(pcsize) 670 } 671 if p.From3 != nil { 672 a = int(p.From3.Name) 673 if a == obj.NAME_AUTO { 674 p.From3.Offset += int64(deltasp) - int64(bpsize) 675 } 676 if a == obj.NAME_PARAM { 677 p.From3.Offset += int64(deltasp) + int64(pcsize) 678 } 679 } 680 a = int(p.To.Name) 681 if a == obj.NAME_AUTO { 682 p.To.Offset += int64(deltasp) - int64(bpsize) 683 } 684 if a == obj.NAME_PARAM { 685 p.To.Offset += int64(deltasp) + int64(pcsize) 686 } 687 688 switch p.As { 689 default: 690 continue 691 692 case APUSHL, APUSHFL: 693 deltasp += 4 694 p.Spadj = 4 695 continue 696 697 case APUSHQ, APUSHFQ: 698 deltasp += 8 699 p.Spadj = 8 700 continue 701 702 case APUSHW, APUSHFW: 703 deltasp += 2 704 p.Spadj = 2 705 continue 706 707 case APOPL, APOPFL: 708 deltasp -= 4 709 p.Spadj = -4 710 continue 711 712 case APOPQ, APOPFQ: 713 deltasp -= 8 714 p.Spadj = -8 715 continue 716 717 case APOPW, APOPFW: 718 deltasp -= 2 719 p.Spadj = -2 720 continue 721 722 case obj.ARET: 723 break 724 } 725 726 if autoffset != deltasp { 727 ctxt.Diag("unbalanced PUSH/POP") 728 } 729 730 if autoffset != 0 { 731 if bpsize > 0 { 732 // Restore caller's BP 733 p.As = AMOVQ 734 735 p.From.Type = obj.TYPE_MEM 736 p.From.Reg = REG_SP 737 p.From.Scale = 1 738 p.From.Offset = int64(autoffset) - int64(bpsize) 739 p.To.Type = obj.TYPE_REG 740 p.To.Reg = REG_BP 741 p = obj.Appendp(ctxt, p) 742 } 743 744 p.As = AADJSP 745 p.From.Type = obj.TYPE_CONST 746 p.From.Offset = int64(-autoffset) 747 p.Spadj = -autoffset 748 p = obj.Appendp(ctxt, p) 749 p.As = obj.ARET 750 751 // If there are instructions following 752 // this ARET, they come from a branch 753 // with the same stackframe, so undo 754 // the cleanup. 755 p.Spadj = +autoffset 756 } 757 758 if p.To.Sym != nil { // retjmp 759 p.As = obj.AJMP 760 } 761 } 762 } 763 764 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 765 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 766 a.Type = obj.TYPE_MEM 767 a.Reg = REG_R15 768 a.Index = REG_CX 769 a.Scale = 1 770 return 771 } 772 773 a.Type = obj.TYPE_MEM 774 a.Reg = REG_CX 775 } 776 777 // Append code to p to load g into cx. 778 // Overwrites p with the first instruction (no first appendp). 779 // Overwriting p is unusual but it lets use this in both the 780 // prologue (caller must call appendp first) and in the epilogue. 781 // Returns last new instruction. 782 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 783 p.As = AMOVQ 784 if ctxt.Arch.Ptrsize == 4 { 785 p.As = AMOVL 786 } 787 p.From.Type = obj.TYPE_MEM 788 p.From.Reg = REG_TLS 789 p.From.Offset = 0 790 p.To.Type = obj.TYPE_REG 791 p.To.Reg = REG_CX 792 793 next := p.Link 794 progedit(ctxt, p) 795 for p.Link != next { 796 p = p.Link 797 } 798 799 if p.From.Index == REG_TLS { 800 p.From.Scale = 2 801 } 802 803 return p 804 } 805 806 // Append code to p to check for stack split. 807 // Appends to (does not overwrite) p. 808 // Assumes g is in CX. 809 // Returns last new instruction. 810 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 811 cmp := ACMPQ 812 lea := ALEAQ 813 mov := AMOVQ 814 sub := ASUBQ 815 816 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 817 cmp = ACMPL 818 lea = ALEAL 819 mov = AMOVL 820 sub = ASUBL 821 } 822 823 var q1 *obj.Prog 824 if framesize <= obj.StackSmall { 825 // small stack: SP <= stackguard 826 // CMPQ SP, stackguard 827 p = obj.Appendp(ctxt, p) 828 829 p.As = int16(cmp) 830 p.From.Type = obj.TYPE_REG 831 p.From.Reg = REG_SP 832 indir_cx(ctxt, p, &p.To) 833 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 834 if ctxt.Cursym.Cfunc != 0 { 835 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 836 } 837 } else if framesize <= obj.StackBig { 838 // large stack: SP-framesize <= stackguard-StackSmall 839 // LEAQ -xxx(SP), AX 840 // CMPQ AX, stackguard 841 p = obj.Appendp(ctxt, p) 842 843 p.As = int16(lea) 844 p.From.Type = obj.TYPE_MEM 845 p.From.Reg = REG_SP 846 p.From.Offset = -(int64(framesize) - obj.StackSmall) 847 p.To.Type = obj.TYPE_REG 848 p.To.Reg = REG_AX 849 850 p = obj.Appendp(ctxt, p) 851 p.As = int16(cmp) 852 p.From.Type = obj.TYPE_REG 853 p.From.Reg = REG_AX 854 indir_cx(ctxt, p, &p.To) 855 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 856 if ctxt.Cursym.Cfunc != 0 { 857 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 858 } 859 } else { 860 // Such a large stack we need to protect against wraparound. 861 // If SP is close to zero: 862 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 863 // The +StackGuard on both sides is required to keep the left side positive: 864 // SP is allowed to be slightly below stackguard. See stack.h. 865 // 866 // Preemption sets stackguard to StackPreempt, a very large value. 867 // That breaks the math above, so we have to check for that explicitly. 868 // MOVQ stackguard, CX 869 // CMPQ CX, $StackPreempt 870 // JEQ label-of-call-to-morestack 871 // LEAQ StackGuard(SP), AX 872 // SUBQ CX, AX 873 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 874 875 p = obj.Appendp(ctxt, p) 876 877 p.As = int16(mov) 878 indir_cx(ctxt, p, &p.From) 879 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 880 if ctxt.Cursym.Cfunc != 0 { 881 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 882 } 883 p.To.Type = obj.TYPE_REG 884 p.To.Reg = REG_SI 885 886 p = obj.Appendp(ctxt, p) 887 p.As = int16(cmp) 888 p.From.Type = obj.TYPE_REG 889 p.From.Reg = REG_SI 890 p.To.Type = obj.TYPE_CONST 891 p.To.Offset = obj.StackPreempt 892 if p.Mode == 32 { 893 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 894 } 895 896 p = obj.Appendp(ctxt, p) 897 p.As = AJEQ 898 p.To.Type = obj.TYPE_BRANCH 899 q1 = p 900 901 p = obj.Appendp(ctxt, p) 902 p.As = int16(lea) 903 p.From.Type = obj.TYPE_MEM 904 p.From.Reg = REG_SP 905 p.From.Offset = obj.StackGuard 906 p.To.Type = obj.TYPE_REG 907 p.To.Reg = REG_AX 908 909 p = obj.Appendp(ctxt, p) 910 p.As = int16(sub) 911 p.From.Type = obj.TYPE_REG 912 p.From.Reg = REG_SI 913 p.To.Type = obj.TYPE_REG 914 p.To.Reg = REG_AX 915 916 p = obj.Appendp(ctxt, p) 917 p.As = int16(cmp) 918 p.From.Type = obj.TYPE_REG 919 p.From.Reg = REG_AX 920 p.To.Type = obj.TYPE_CONST 921 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 922 } 923 924 // common 925 jls := obj.Appendp(ctxt, p) 926 jls.As = AJLS 927 jls.To.Type = obj.TYPE_BRANCH 928 929 var last *obj.Prog 930 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 931 } 932 933 call := obj.Appendp(ctxt, last) 934 call.Lineno = ctxt.Cursym.Text.Lineno 935 call.Mode = ctxt.Cursym.Text.Mode 936 call.As = obj.ACALL 937 call.To.Type = obj.TYPE_BRANCH 938 morestack := "runtime.morestack" 939 switch { 940 case ctxt.Cursym.Cfunc != 0: 941 morestack = "runtime.morestackc" 942 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 943 morestack = "runtime.morestack_noctxt" 944 } 945 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 946 947 jmp := obj.Appendp(ctxt, call) 948 jmp.As = obj.AJMP 949 jmp.To.Type = obj.TYPE_BRANCH 950 jmp.Pcond = ctxt.Cursym.Text.Link 951 952 jls.Pcond = call 953 if q1 != nil { 954 q1.Pcond = call 955 } 956 957 return jls 958 } 959 960 func follow(ctxt *obj.Link, s *obj.LSym) { 961 ctxt.Cursym = s 962 963 firstp := ctxt.NewProg() 964 lastp := firstp 965 xfol(ctxt, s.Text, &lastp) 966 lastp.Link = nil 967 s.Text = firstp.Link 968 } 969 970 func nofollow(a int) bool { 971 switch a { 972 case obj.AJMP, 973 obj.ARET, 974 AIRETL, 975 AIRETQ, 976 AIRETW, 977 ARETFL, 978 ARETFQ, 979 ARETFW, 980 obj.AUNDEF: 981 return true 982 } 983 984 return false 985 } 986 987 func pushpop(a int) bool { 988 switch a { 989 case APUSHL, 990 APUSHFL, 991 APUSHQ, 992 APUSHFQ, 993 APUSHW, 994 APUSHFW, 995 APOPL, 996 APOPFL, 997 APOPQ, 998 APOPFQ, 999 APOPW, 1000 APOPFW: 1001 return true 1002 } 1003 1004 return false 1005 } 1006 1007 func relinv(a int16) int16 { 1008 switch a { 1009 case AJEQ: 1010 return AJNE 1011 case AJNE: 1012 return AJEQ 1013 case AJLE: 1014 return AJGT 1015 case AJLS: 1016 return AJHI 1017 case AJLT: 1018 return AJGE 1019 case AJMI: 1020 return AJPL 1021 case AJGE: 1022 return AJLT 1023 case AJPL: 1024 return AJMI 1025 case AJGT: 1026 return AJLE 1027 case AJHI: 1028 return AJLS 1029 case AJCS: 1030 return AJCC 1031 case AJCC: 1032 return AJCS 1033 case AJPS: 1034 return AJPC 1035 case AJPC: 1036 return AJPS 1037 case AJOS: 1038 return AJOC 1039 case AJOC: 1040 return AJOS 1041 } 1042 1043 log.Fatalf("unknown relation: %s", obj.Aconv(int(a))) 1044 return 0 1045 } 1046 1047 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1048 var q *obj.Prog 1049 var i int 1050 var a int 1051 1052 loop: 1053 if p == nil { 1054 return 1055 } 1056 if p.As == obj.AJMP { 1057 q = p.Pcond 1058 if q != nil && q.As != obj.ATEXT { 1059 /* mark instruction as done and continue layout at target of jump */ 1060 p.Mark = 1 1061 1062 p = q 1063 if p.Mark == 0 { 1064 goto loop 1065 } 1066 } 1067 } 1068 1069 if p.Mark != 0 { 1070 /* 1071 * p goes here, but already used it elsewhere. 1072 * copy up to 4 instructions or else branch to other copy. 1073 */ 1074 i = 0 1075 q = p 1076 for ; i < 4; i, q = i+1, q.Link { 1077 if q == nil { 1078 break 1079 } 1080 if q == *last { 1081 break 1082 } 1083 a = int(q.As) 1084 if a == obj.ANOP { 1085 i-- 1086 continue 1087 } 1088 1089 if nofollow(a) || pushpop(a) { 1090 break // NOTE(rsc): arm does goto copy 1091 } 1092 if q.Pcond == nil || q.Pcond.Mark != 0 { 1093 continue 1094 } 1095 if a == obj.ACALL || a == ALOOP { 1096 continue 1097 } 1098 for { 1099 if p.As == obj.ANOP { 1100 p = p.Link 1101 continue 1102 } 1103 1104 q = obj.Copyp(ctxt, p) 1105 p = p.Link 1106 q.Mark = 1 1107 (*last).Link = q 1108 *last = q 1109 if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 { 1110 continue 1111 } 1112 1113 q.As = relinv(q.As) 1114 p = q.Pcond 1115 q.Pcond = q.Link 1116 q.Link = p 1117 xfol(ctxt, q.Link, last) 1118 p = q.Link 1119 if p.Mark != 0 { 1120 return 1121 } 1122 goto loop 1123 /* */ 1124 } 1125 } 1126 q = ctxt.NewProg() 1127 q.As = obj.AJMP 1128 q.Lineno = p.Lineno 1129 q.To.Type = obj.TYPE_BRANCH 1130 q.To.Offset = p.Pc 1131 q.Pcond = p 1132 p = q 1133 } 1134 1135 /* emit p */ 1136 p.Mark = 1 1137 1138 (*last).Link = p 1139 *last = p 1140 a = int(p.As) 1141 1142 /* continue loop with what comes after p */ 1143 if nofollow(a) { 1144 return 1145 } 1146 if p.Pcond != nil && a != obj.ACALL { 1147 /* 1148 * some kind of conditional branch. 1149 * recurse to follow one path. 1150 * continue loop on the other. 1151 */ 1152 q = obj.Brchain(ctxt, p.Pcond) 1153 if q != nil { 1154 p.Pcond = q 1155 } 1156 q = obj.Brchain(ctxt, p.Link) 1157 if q != nil { 1158 p.Link = q 1159 } 1160 if p.From.Type == obj.TYPE_CONST { 1161 if p.From.Offset == 1 { 1162 /* 1163 * expect conditional jump to be taken. 1164 * rewrite so that's the fall-through case. 1165 */ 1166 p.As = relinv(int16(a)) 1167 1168 q = p.Link 1169 p.Link = p.Pcond 1170 p.Pcond = q 1171 } 1172 } else { 1173 q = p.Link 1174 if q.Mark != 0 { 1175 if a != ALOOP { 1176 p.As = relinv(int16(a)) 1177 p.Link = p.Pcond 1178 p.Pcond = q 1179 } 1180 } 1181 } 1182 1183 xfol(ctxt, p.Link, last) 1184 if p.Pcond.Mark != 0 { 1185 return 1186 } 1187 p = p.Pcond 1188 goto loop 1189 } 1190 1191 p = p.Link 1192 goto loop 1193 } 1194 1195 var unaryDst = map[int]bool{ 1196 ABSWAPL: true, 1197 ABSWAPQ: true, 1198 ACMPXCHG8B: true, 1199 ADECB: true, 1200 ADECL: true, 1201 ADECQ: true, 1202 ADECW: true, 1203 AINCB: true, 1204 AINCL: true, 1205 AINCQ: true, 1206 AINCW: true, 1207 ANEGB: true, 1208 ANEGL: true, 1209 ANEGQ: true, 1210 ANEGW: true, 1211 ANOTB: true, 1212 ANOTL: true, 1213 ANOTQ: true, 1214 ANOTW: true, 1215 APOPL: true, 1216 APOPQ: true, 1217 APOPW: true, 1218 ASETCC: true, 1219 ASETCS: true, 1220 ASETEQ: true, 1221 ASETGE: true, 1222 ASETGT: true, 1223 ASETHI: true, 1224 ASETLE: true, 1225 ASETLS: true, 1226 ASETLT: true, 1227 ASETMI: true, 1228 ASETNE: true, 1229 ASETOC: true, 1230 ASETOS: true, 1231 ASETPC: true, 1232 ASETPL: true, 1233 ASETPS: true, 1234 AFFREE: true, 1235 AFLDENV: true, 1236 AFSAVE: true, 1237 AFSTCW: true, 1238 AFSTENV: true, 1239 AFSTSW: true, 1240 AFXSAVE: true, 1241 AFXSAVE64: true, 1242 ASTMXCSR: true, 1243 } 1244 1245 var Linkamd64 = obj.LinkArch{ 1246 ByteOrder: binary.LittleEndian, 1247 Name: "amd64", 1248 Thechar: '6', 1249 Preprocess: preprocess, 1250 Assemble: span6, 1251 Follow: follow, 1252 Progedit: progedit, 1253 UnaryDst: unaryDst, 1254 Minlc: 1, 1255 Ptrsize: 8, 1256 Regsize: 8, 1257 } 1258 1259 var Linkamd64p32 = obj.LinkArch{ 1260 ByteOrder: binary.LittleEndian, 1261 Name: "amd64p32", 1262 Thechar: '6', 1263 Preprocess: preprocess, 1264 Assemble: span6, 1265 Follow: follow, 1266 Progedit: progedit, 1267 UnaryDst: unaryDst, 1268 Minlc: 1, 1269 Ptrsize: 4, 1270 Regsize: 8, 1271 } 1272 1273 var Link386 = obj.LinkArch{ 1274 ByteOrder: binary.LittleEndian, 1275 Name: "386", 1276 Thechar: '8', 1277 Preprocess: preprocess, 1278 Assemble: span6, 1279 Follow: follow, 1280 Progedit: progedit, 1281 UnaryDst: unaryDst, 1282 Minlc: 1, 1283 Ptrsize: 4, 1284 Regsize: 4, 1285 }