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