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