github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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 "cmd/internal/sys" 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 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 argument 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.Family == sys.AMD64 || 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 // f == 0 can't be used here due to -0, so use Float64bits 235 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 236 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 237 p.As = AXORPS 238 p.From = p.To 239 break 240 } 241 } 242 } 243 fallthrough 244 245 case AFMOVF, 246 AFADDF, 247 AFSUBF, 248 AFSUBRF, 249 AFMULF, 250 AFDIVF, 251 AFDIVRF, 252 AFCOMF, 253 AFCOMFP, 254 AADDSS, 255 ASUBSS, 256 AMULSS, 257 ADIVSS, 258 ACOMISS, 259 AUCOMISS: 260 if p.From.Type == obj.TYPE_FCONST { 261 f32 := float32(p.From.Val.(float64)) 262 i32 := math.Float32bits(f32) 263 literal := fmt.Sprintf("$f32.%08x", i32) 264 s := obj.Linklookup(ctxt, literal, 0) 265 p.From.Type = obj.TYPE_MEM 266 p.From.Name = obj.NAME_EXTERN 267 p.From.Sym = s 268 p.From.Sym.Local = true 269 p.From.Offset = 0 270 } 271 272 case AMOVSD: 273 // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx 274 if p.From.Type == obj.TYPE_FCONST { 275 // f == 0 can't be used here due to -0, so use Float64bits 276 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 277 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 278 p.As = AXORPS 279 p.From = p.To 280 break 281 } 282 } 283 } 284 fallthrough 285 286 case AFMOVD, 287 AFADDD, 288 AFSUBD, 289 AFSUBRD, 290 AFMULD, 291 AFDIVD, 292 AFDIVRD, 293 AFCOMD, 294 AFCOMDP, 295 AADDSD, 296 ASUBSD, 297 AMULSD, 298 ADIVSD, 299 ACOMISD, 300 AUCOMISD: 301 if p.From.Type == obj.TYPE_FCONST { 302 i64 := math.Float64bits(p.From.Val.(float64)) 303 literal := fmt.Sprintf("$f64.%016x", i64) 304 s := obj.Linklookup(ctxt, literal, 0) 305 p.From.Type = obj.TYPE_MEM 306 p.From.Name = obj.NAME_EXTERN 307 p.From.Sym = s 308 p.From.Sym.Local = true 309 p.From.Offset = 0 310 } 311 } 312 313 if ctxt.Flag_dynlink { 314 rewriteToUseGot(ctxt, p) 315 } 316 317 if ctxt.Flag_shared && p.Mode == 32 { 318 rewriteToPcrel(ctxt, p) 319 } 320 } 321 322 // Rewrite p, if necessary, to access global data via the global offset table. 323 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 324 var add, lea, mov obj.As 325 var reg int16 326 if p.Mode == 64 { 327 add = AADDQ 328 lea = ALEAQ 329 mov = AMOVQ 330 reg = REG_R15 331 } else { 332 add = AADDL 333 lea = ALEAL 334 mov = AMOVL 335 reg = REG_CX 336 } 337 338 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 339 // ADUFFxxx $offset 340 // becomes 341 // $MOV runtime.duffxxx@GOT, $reg 342 // $ADD $offset, $reg 343 // CALL $reg 344 var sym *obj.LSym 345 if p.As == obj.ADUFFZERO { 346 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 347 } else { 348 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 349 } 350 offset := p.To.Offset 351 p.As = mov 352 p.From.Type = obj.TYPE_MEM 353 p.From.Name = obj.NAME_GOTREF 354 p.From.Sym = sym 355 p.To.Type = obj.TYPE_REG 356 p.To.Reg = reg 357 p.To.Offset = 0 358 p.To.Sym = nil 359 p1 := obj.Appendp(ctxt, p) 360 p1.As = add 361 p1.From.Type = obj.TYPE_CONST 362 p1.From.Offset = offset 363 p1.To.Type = obj.TYPE_REG 364 p1.To.Reg = reg 365 p2 := obj.Appendp(ctxt, p1) 366 p2.As = obj.ACALL 367 p2.To.Type = obj.TYPE_REG 368 p2.To.Reg = reg 369 } 370 371 // We only care about global data: NAME_EXTERN means a global 372 // symbol in the Go sense, and p.Sym.Local is true for a few 373 // internally defined symbols. 374 if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 375 // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below 376 p.As = mov 377 p.From.Type = obj.TYPE_ADDR 378 } 379 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 380 // $MOV $sym, Rx becomes $MOV sym@GOT, Rx 381 // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx 382 // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX 383 cmplxdest := false 384 pAs := p.As 385 var dest obj.Addr 386 if p.To.Type != obj.TYPE_REG || pAs != mov { 387 if p.Mode == 64 { 388 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) 389 } 390 cmplxdest = true 391 dest = p.To 392 p.As = mov 393 p.To.Type = obj.TYPE_REG 394 p.To.Reg = REG_CX 395 p.To.Sym = nil 396 p.To.Name = obj.NAME_NONE 397 } 398 p.From.Type = obj.TYPE_MEM 399 p.From.Name = obj.NAME_GOTREF 400 q := p 401 if p.From.Offset != 0 { 402 q = obj.Appendp(ctxt, p) 403 q.As = lea 404 q.From.Type = obj.TYPE_MEM 405 q.From.Reg = p.To.Reg 406 q.From.Offset = p.From.Offset 407 q.To = p.To 408 p.From.Offset = 0 409 } 410 if cmplxdest { 411 q = obj.Appendp(ctxt, q) 412 q.As = pAs 413 q.To = dest 414 q.From.Type = obj.TYPE_REG 415 q.From.Reg = REG_CX 416 } 417 } 418 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 419 ctxt.Diag("don't know how to handle %v with -dynlink", p) 420 } 421 var source *obj.Addr 422 // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry 423 // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) 424 // An addition may be inserted between the two MOVs if there is an offset. 425 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { 426 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 427 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 428 } 429 source = &p.From 430 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { 431 source = &p.To 432 } else { 433 return 434 } 435 if p.As == obj.ACALL { 436 // When dynlinking on 386, almost any call might end up being a call 437 // to a PLT, so make sure the GOT pointer is loaded into BX. 438 // RegTo2 is set on the replacement call insn to stop it being 439 // processed when it is in turn passed to progedit. 440 if p.Mode == 64 || (p.To.Sym != nil && p.To.Sym.Local) || p.RegTo2 != 0 { 441 return 442 } 443 p1 := obj.Appendp(ctxt, p) 444 p2 := obj.Appendp(ctxt, p1) 445 446 p1.As = ALEAL 447 p1.From.Type = obj.TYPE_MEM 448 p1.From.Name = obj.NAME_STATIC 449 p1.From.Sym = obj.Linklookup(ctxt, "_GLOBAL_OFFSET_TABLE_", 0) 450 p1.To.Type = obj.TYPE_REG 451 p1.To.Reg = REG_BX 452 453 p2.As = p.As 454 p2.Scond = p.Scond 455 p2.From = p.From 456 p2.From3 = p.From3 457 p2.Reg = p.Reg 458 p2.To = p.To 459 // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr 460 // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 461 // itself gets passed to progedit. 462 p2.To.Type = obj.TYPE_MEM 463 p2.RegTo2 = 1 464 465 obj.Nopout(p) 466 return 467 468 } 469 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { 470 return 471 } 472 if source.Type != obj.TYPE_MEM { 473 ctxt.Diag("don't know how to handle %v with -dynlink", p) 474 } 475 p1 := obj.Appendp(ctxt, p) 476 p2 := obj.Appendp(ctxt, p1) 477 478 p1.As = mov 479 p1.From.Type = obj.TYPE_MEM 480 p1.From.Sym = source.Sym 481 p1.From.Name = obj.NAME_GOTREF 482 p1.To.Type = obj.TYPE_REG 483 p1.To.Reg = reg 484 485 p2.As = p.As 486 p2.From = p.From 487 p2.To = p.To 488 if p.From.Name == obj.NAME_EXTERN { 489 p2.From.Reg = reg 490 p2.From.Name = obj.NAME_NONE 491 p2.From.Sym = nil 492 } else if p.To.Name == obj.NAME_EXTERN { 493 p2.To.Reg = reg 494 p2.To.Name = obj.NAME_NONE 495 p2.To.Sym = nil 496 } else { 497 return 498 } 499 obj.Nopout(p) 500 } 501 502 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog) { 503 // RegTo2 is set on the instructions we insert here so they don't get 504 // processed twice. 505 if p.RegTo2 != 0 { 506 return 507 } 508 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 509 return 510 } 511 // Any Prog (aside from the above special cases) with an Addr with Name == 512 // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.cx 513 // inserted before it. 514 isName := func(a *obj.Addr) bool { 515 if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 { 516 return false 517 } 518 if a.Sym.Type == obj.STLSBSS { 519 return false 520 } 521 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF 522 } 523 524 if isName(&p.From) && p.From.Type == obj.TYPE_ADDR { 525 // Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting 526 // to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX" 527 // respectively. 528 if p.To.Type != obj.TYPE_REG { 529 q := obj.Appendp(ctxt, p) 530 q.As = p.As 531 q.From.Type = obj.TYPE_REG 532 q.From.Reg = REG_CX 533 q.To = p.To 534 p.As = AMOVL 535 p.To.Type = obj.TYPE_REG 536 p.To.Reg = REG_CX 537 p.To.Sym = nil 538 p.To.Name = obj.NAME_NONE 539 } 540 } 541 542 if !isName(&p.From) && !isName(&p.To) && (p.From3 == nil || !isName(p.From3)) { 543 return 544 } 545 q := obj.Appendp(ctxt, p) 546 q.RegTo2 = 1 547 r := obj.Appendp(ctxt, q) 548 r.RegTo2 = 1 549 q.As = obj.ACALL 550 q.To.Sym = obj.Linklookup(ctxt, "__x86.get_pc_thunk.cx", 0) 551 q.To.Type = obj.TYPE_MEM 552 q.To.Name = obj.NAME_EXTERN 553 q.To.Sym.Local = true 554 r.As = p.As 555 r.Scond = p.Scond 556 r.From = p.From 557 r.From3 = p.From3 558 r.Reg = p.Reg 559 r.To = p.To 560 obj.Nopout(p) 561 } 562 563 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 564 if p.As == ALEAL || p.As == ALEAQ { 565 return 566 } 567 568 if a.Reg == REG_BP { 569 ctxt.Diag("invalid address: %v", p) 570 return 571 } 572 573 if a.Reg == REG_TLS { 574 a.Reg = REG_BP 575 } 576 if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { 577 switch a.Reg { 578 // all ok 579 case REG_BP, REG_SP, REG_R15: 580 break 581 582 default: 583 if a.Index != REG_NONE { 584 ctxt.Diag("invalid address %v", p) 585 } 586 a.Index = a.Reg 587 if a.Index != REG_NONE { 588 a.Scale = 1 589 } 590 a.Reg = REG_R15 591 } 592 } 593 } 594 595 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 596 if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil { 597 ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0) 598 } 599 600 ctxt.Cursym = cursym 601 602 if cursym.Text == nil || cursym.Text.Link == nil { 603 return 604 } 605 606 p := cursym.Text 607 autoffset := int32(p.To.Offset) 608 if autoffset < 0 { 609 autoffset = 0 610 } 611 612 var bpsize int 613 if p.Mode == 64 && ctxt.Framepointer_enabled && autoffset > 0 && p.From3.Offset&obj.NOFRAME == 0 { 614 // Make room for to save a base pointer. If autoffset == 0, 615 // this might do something special like a tail jump to 616 // another function, so in that case we omit this. 617 bpsize = ctxt.Arch.PtrSize 618 autoffset += int32(bpsize) 619 p.To.Offset += int64(bpsize) 620 } else { 621 bpsize = 0 622 } 623 624 textarg := int64(p.To.Val.(int32)) 625 cursym.Args = int32(textarg) 626 cursym.Locals = int32(p.To.Offset) 627 628 // TODO(rsc): Remove. 629 if p.Mode == 32 && cursym.Locals < 0 { 630 cursym.Locals = 0 631 } 632 633 // TODO(rsc): Remove 'p.Mode == 64 &&'. 634 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { 635 for q := p; q != nil; q = q.Link { 636 if q.As == obj.ACALL { 637 goto noleaf 638 } 639 if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 { 640 goto noleaf 641 } 642 } 643 644 p.From3.Offset |= obj.NOSPLIT 645 noleaf: 646 } 647 648 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 649 p = obj.Appendp(ctxt, p) 650 p = load_g_cx(ctxt, p) // load g into CX 651 } 652 653 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 654 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 655 } 656 657 if autoffset != 0 { 658 if autoffset%int32(ctxt.Arch.RegSize) != 0 { 659 ctxt.Diag("unaligned stack size %d", autoffset) 660 } 661 p = obj.Appendp(ctxt, p) 662 p.As = AADJSP 663 p.From.Type = obj.TYPE_CONST 664 p.From.Offset = int64(autoffset) 665 p.Spadj = autoffset 666 } else { 667 // zero-byte stack adjustment. 668 // Insert a fake non-zero adjustment so that stkcheck can 669 // recognize the end of the stack-splitting prolog. 670 p = obj.Appendp(ctxt, p) 671 672 p.As = obj.ANOP 673 p.Spadj = int32(-ctxt.Arch.PtrSize) 674 p = obj.Appendp(ctxt, p) 675 p.As = obj.ANOP 676 p.Spadj = int32(ctxt.Arch.PtrSize) 677 } 678 679 deltasp := autoffset 680 681 if bpsize > 0 { 682 // Save caller's BP 683 p = obj.Appendp(ctxt, p) 684 685 p.As = AMOVQ 686 p.From.Type = obj.TYPE_REG 687 p.From.Reg = REG_BP 688 p.To.Type = obj.TYPE_MEM 689 p.To.Reg = REG_SP 690 p.To.Scale = 1 691 p.To.Offset = int64(autoffset) - int64(bpsize) 692 693 // Move current frame to BP 694 p = obj.Appendp(ctxt, p) 695 696 p.As = ALEAQ 697 p.From.Type = obj.TYPE_MEM 698 p.From.Reg = REG_SP 699 p.From.Scale = 1 700 p.From.Offset = int64(autoffset) - int64(bpsize) 701 p.To.Type = obj.TYPE_REG 702 p.To.Reg = REG_BP 703 } 704 705 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 706 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 707 // 708 // MOVQ g_panic(CX), BX 709 // TESTQ BX, BX 710 // JEQ end 711 // LEAQ (autoffset+8)(SP), DI 712 // CMPQ panic_argp(BX), DI 713 // JNE end 714 // MOVQ SP, panic_argp(BX) 715 // end: 716 // NOP 717 // 718 // The NOP is needed to give the jumps somewhere to land. 719 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 720 721 p = obj.Appendp(ctxt, p) 722 723 p.As = AMOVQ 724 p.From.Type = obj.TYPE_MEM 725 p.From.Reg = REG_CX 726 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 727 p.To.Type = obj.TYPE_REG 728 p.To.Reg = REG_BX 729 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 730 p.As = AMOVL 731 p.From.Type = obj.TYPE_MEM 732 p.From.Reg = REG_R15 733 p.From.Scale = 1 734 p.From.Index = REG_CX 735 } 736 if p.Mode == 32 { 737 p.As = AMOVL 738 } 739 740 p = obj.Appendp(ctxt, p) 741 p.As = ATESTQ 742 p.From.Type = obj.TYPE_REG 743 p.From.Reg = REG_BX 744 p.To.Type = obj.TYPE_REG 745 p.To.Reg = REG_BX 746 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 747 p.As = ATESTL 748 } 749 750 p = obj.Appendp(ctxt, p) 751 p.As = AJEQ 752 p.To.Type = obj.TYPE_BRANCH 753 p1 := p 754 755 p = obj.Appendp(ctxt, p) 756 p.As = ALEAQ 757 p.From.Type = obj.TYPE_MEM 758 p.From.Reg = REG_SP 759 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) 760 p.To.Type = obj.TYPE_REG 761 p.To.Reg = REG_DI 762 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 763 p.As = ALEAL 764 } 765 766 p = obj.Appendp(ctxt, p) 767 p.As = ACMPQ 768 p.From.Type = obj.TYPE_MEM 769 p.From.Reg = REG_BX 770 p.From.Offset = 0 // Panic.argp 771 p.To.Type = obj.TYPE_REG 772 p.To.Reg = REG_DI 773 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 774 p.As = ACMPL 775 p.From.Type = obj.TYPE_MEM 776 p.From.Reg = REG_R15 777 p.From.Scale = 1 778 p.From.Index = REG_BX 779 } 780 if p.Mode == 32 { 781 p.As = ACMPL 782 } 783 784 p = obj.Appendp(ctxt, p) 785 p.As = AJNE 786 p.To.Type = obj.TYPE_BRANCH 787 p2 := p 788 789 p = obj.Appendp(ctxt, p) 790 p.As = AMOVQ 791 p.From.Type = obj.TYPE_REG 792 p.From.Reg = REG_SP 793 p.To.Type = obj.TYPE_MEM 794 p.To.Reg = REG_BX 795 p.To.Offset = 0 // Panic.argp 796 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 797 p.As = AMOVL 798 p.To.Type = obj.TYPE_MEM 799 p.To.Reg = REG_R15 800 p.To.Scale = 1 801 p.To.Index = REG_BX 802 } 803 if p.Mode == 32 { 804 p.As = AMOVL 805 } 806 807 p = obj.Appendp(ctxt, p) 808 p.As = obj.ANOP 809 p1.Pcond = p 810 p2.Pcond = p 811 } 812 813 var a int 814 var pcsize int 815 for ; p != nil; p = p.Link { 816 pcsize = int(p.Mode) / 8 817 a = int(p.From.Name) 818 if a == obj.NAME_AUTO { 819 p.From.Offset += int64(deltasp) - int64(bpsize) 820 } 821 if a == obj.NAME_PARAM { 822 p.From.Offset += int64(deltasp) + int64(pcsize) 823 } 824 if p.From3 != nil { 825 a = int(p.From3.Name) 826 if a == obj.NAME_AUTO { 827 p.From3.Offset += int64(deltasp) - int64(bpsize) 828 } 829 if a == obj.NAME_PARAM { 830 p.From3.Offset += int64(deltasp) + int64(pcsize) 831 } 832 } 833 a = int(p.To.Name) 834 if a == obj.NAME_AUTO { 835 p.To.Offset += int64(deltasp) - int64(bpsize) 836 } 837 if a == obj.NAME_PARAM { 838 p.To.Offset += int64(deltasp) + int64(pcsize) 839 } 840 841 switch p.As { 842 default: 843 continue 844 845 case APUSHL, APUSHFL: 846 deltasp += 4 847 p.Spadj = 4 848 continue 849 850 case APUSHQ, APUSHFQ: 851 deltasp += 8 852 p.Spadj = 8 853 continue 854 855 case APUSHW, APUSHFW: 856 deltasp += 2 857 p.Spadj = 2 858 continue 859 860 case APOPL, APOPFL: 861 deltasp -= 4 862 p.Spadj = -4 863 continue 864 865 case APOPQ, APOPFQ: 866 deltasp -= 8 867 p.Spadj = -8 868 continue 869 870 case APOPW, APOPFW: 871 deltasp -= 2 872 p.Spadj = -2 873 continue 874 875 case obj.ARET: 876 break 877 } 878 879 if autoffset != deltasp { 880 ctxt.Diag("unbalanced PUSH/POP") 881 } 882 883 if autoffset != 0 { 884 if bpsize > 0 { 885 // Restore caller's BP 886 p.As = AMOVQ 887 888 p.From.Type = obj.TYPE_MEM 889 p.From.Reg = REG_SP 890 p.From.Scale = 1 891 p.From.Offset = int64(autoffset) - int64(bpsize) 892 p.To.Type = obj.TYPE_REG 893 p.To.Reg = REG_BP 894 p = obj.Appendp(ctxt, p) 895 } 896 897 p.As = AADJSP 898 p.From.Type = obj.TYPE_CONST 899 p.From.Offset = int64(-autoffset) 900 p.Spadj = -autoffset 901 p = obj.Appendp(ctxt, p) 902 p.As = obj.ARET 903 904 // If there are instructions following 905 // this ARET, they come from a branch 906 // with the same stackframe, so undo 907 // the cleanup. 908 p.Spadj = +autoffset 909 } 910 911 if p.To.Sym != nil { // retjmp 912 p.As = obj.AJMP 913 } 914 } 915 } 916 917 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 918 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 919 a.Type = obj.TYPE_MEM 920 a.Reg = REG_R15 921 a.Index = REG_CX 922 a.Scale = 1 923 return 924 } 925 926 a.Type = obj.TYPE_MEM 927 a.Reg = REG_CX 928 } 929 930 // Append code to p to load g into cx. 931 // Overwrites p with the first instruction (no first appendp). 932 // Overwriting p is unusual but it lets use this in both the 933 // prologue (caller must call appendp first) and in the epilogue. 934 // Returns last new instruction. 935 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 936 p.As = AMOVQ 937 if ctxt.Arch.PtrSize == 4 { 938 p.As = AMOVL 939 } 940 p.From.Type = obj.TYPE_MEM 941 p.From.Reg = REG_TLS 942 p.From.Offset = 0 943 p.To.Type = obj.TYPE_REG 944 p.To.Reg = REG_CX 945 946 next := p.Link 947 progedit(ctxt, p) 948 for p.Link != next { 949 p = p.Link 950 } 951 952 if p.From.Index == REG_TLS { 953 p.From.Scale = 2 954 } 955 956 return p 957 } 958 959 // Append code to p to check for stack split. 960 // Appends to (does not overwrite) p. 961 // Assumes g is in CX. 962 // Returns last new instruction. 963 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 964 cmp := ACMPQ 965 lea := ALEAQ 966 mov := AMOVQ 967 sub := ASUBQ 968 969 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 970 cmp = ACMPL 971 lea = ALEAL 972 mov = AMOVL 973 sub = ASUBL 974 } 975 976 var q1 *obj.Prog 977 if framesize <= obj.StackSmall { 978 // small stack: SP <= stackguard 979 // CMPQ SP, stackguard 980 p = obj.Appendp(ctxt, p) 981 982 p.As = cmp 983 p.From.Type = obj.TYPE_REG 984 p.From.Reg = REG_SP 985 indir_cx(ctxt, p, &p.To) 986 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 987 if ctxt.Cursym.Cfunc { 988 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 989 } 990 } else if framesize <= obj.StackBig { 991 // large stack: SP-framesize <= stackguard-StackSmall 992 // LEAQ -xxx(SP), AX 993 // CMPQ AX, stackguard 994 p = obj.Appendp(ctxt, p) 995 996 p.As = lea 997 p.From.Type = obj.TYPE_MEM 998 p.From.Reg = REG_SP 999 p.From.Offset = -(int64(framesize) - obj.StackSmall) 1000 p.To.Type = obj.TYPE_REG 1001 p.To.Reg = REG_AX 1002 1003 p = obj.Appendp(ctxt, p) 1004 p.As = cmp 1005 p.From.Type = obj.TYPE_REG 1006 p.From.Reg = REG_AX 1007 indir_cx(ctxt, p, &p.To) 1008 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1009 if ctxt.Cursym.Cfunc { 1010 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1011 } 1012 } else { 1013 // Such a large stack we need to protect against wraparound. 1014 // If SP is close to zero: 1015 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1016 // The +StackGuard on both sides is required to keep the left side positive: 1017 // SP is allowed to be slightly below stackguard. See stack.h. 1018 // 1019 // Preemption sets stackguard to StackPreempt, a very large value. 1020 // That breaks the math above, so we have to check for that explicitly. 1021 // MOVQ stackguard, CX 1022 // CMPQ CX, $StackPreempt 1023 // JEQ label-of-call-to-morestack 1024 // LEAQ StackGuard(SP), AX 1025 // SUBQ CX, AX 1026 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1027 1028 p = obj.Appendp(ctxt, p) 1029 1030 p.As = mov 1031 indir_cx(ctxt, p, &p.From) 1032 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1033 if ctxt.Cursym.Cfunc { 1034 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1035 } 1036 p.To.Type = obj.TYPE_REG 1037 p.To.Reg = REG_SI 1038 1039 p = obj.Appendp(ctxt, p) 1040 p.As = cmp 1041 p.From.Type = obj.TYPE_REG 1042 p.From.Reg = REG_SI 1043 p.To.Type = obj.TYPE_CONST 1044 p.To.Offset = obj.StackPreempt 1045 if p.Mode == 32 { 1046 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 1047 } 1048 1049 p = obj.Appendp(ctxt, p) 1050 p.As = AJEQ 1051 p.To.Type = obj.TYPE_BRANCH 1052 q1 = p 1053 1054 p = obj.Appendp(ctxt, p) 1055 p.As = lea 1056 p.From.Type = obj.TYPE_MEM 1057 p.From.Reg = REG_SP 1058 p.From.Offset = obj.StackGuard 1059 p.To.Type = obj.TYPE_REG 1060 p.To.Reg = REG_AX 1061 1062 p = obj.Appendp(ctxt, p) 1063 p.As = sub 1064 p.From.Type = obj.TYPE_REG 1065 p.From.Reg = REG_SI 1066 p.To.Type = obj.TYPE_REG 1067 p.To.Reg = REG_AX 1068 1069 p = obj.Appendp(ctxt, p) 1070 p.As = cmp 1071 p.From.Type = obj.TYPE_REG 1072 p.From.Reg = REG_AX 1073 p.To.Type = obj.TYPE_CONST 1074 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 1075 } 1076 1077 // common 1078 jls := obj.Appendp(ctxt, p) 1079 jls.As = AJLS 1080 jls.To.Type = obj.TYPE_BRANCH 1081 1082 var last *obj.Prog 1083 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 1084 } 1085 1086 spfix := obj.Appendp(ctxt, last) 1087 spfix.As = obj.ANOP 1088 spfix.Spadj = -framesize 1089 1090 call := obj.Appendp(ctxt, spfix) 1091 call.Lineno = ctxt.Cursym.Text.Lineno 1092 call.Mode = ctxt.Cursym.Text.Mode 1093 call.As = obj.ACALL 1094 call.To.Type = obj.TYPE_BRANCH 1095 call.To.Name = obj.NAME_EXTERN 1096 morestack := "runtime.morestack" 1097 switch { 1098 case ctxt.Cursym.Cfunc: 1099 morestack = "runtime.morestackc" 1100 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 1101 morestack = "runtime.morestack_noctxt" 1102 } 1103 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 1104 // When compiling 386 code for dynamic linking, the call needs to be adjusted 1105 // to follow PIC rules. This in turn can insert more instructions, so we need 1106 // to keep track of the start of the call (where the jump will be to) and the 1107 // end (which following instructions are appended to). 1108 callend := call 1109 progedit(ctxt, callend) 1110 for ; callend.Link != nil; callend = callend.Link { 1111 progedit(ctxt, callend.Link) 1112 } 1113 1114 jmp := obj.Appendp(ctxt, callend) 1115 jmp.As = obj.AJMP 1116 jmp.To.Type = obj.TYPE_BRANCH 1117 jmp.Pcond = ctxt.Cursym.Text.Link 1118 jmp.Spadj = +framesize 1119 1120 jls.Pcond = call 1121 if q1 != nil { 1122 q1.Pcond = call 1123 } 1124 1125 return jls 1126 } 1127 1128 func follow(ctxt *obj.Link, s *obj.LSym) { 1129 ctxt.Cursym = s 1130 1131 firstp := ctxt.NewProg() 1132 lastp := firstp 1133 xfol(ctxt, s.Text, &lastp) 1134 lastp.Link = nil 1135 s.Text = firstp.Link 1136 } 1137 1138 func nofollow(a obj.As) bool { 1139 switch a { 1140 case obj.AJMP, 1141 obj.ARET, 1142 AIRETL, 1143 AIRETQ, 1144 AIRETW, 1145 ARETFL, 1146 ARETFQ, 1147 ARETFW, 1148 obj.AUNDEF: 1149 return true 1150 } 1151 1152 return false 1153 } 1154 1155 func pushpop(a obj.As) bool { 1156 switch a { 1157 case APUSHL, 1158 APUSHFL, 1159 APUSHQ, 1160 APUSHFQ, 1161 APUSHW, 1162 APUSHFW, 1163 APOPL, 1164 APOPFL, 1165 APOPQ, 1166 APOPFQ, 1167 APOPW, 1168 APOPFW: 1169 return true 1170 } 1171 1172 return false 1173 } 1174 1175 func relinv(a obj.As) obj.As { 1176 switch a { 1177 case AJEQ: 1178 return AJNE 1179 case AJNE: 1180 return AJEQ 1181 case AJLE: 1182 return AJGT 1183 case AJLS: 1184 return AJHI 1185 case AJLT: 1186 return AJGE 1187 case AJMI: 1188 return AJPL 1189 case AJGE: 1190 return AJLT 1191 case AJPL: 1192 return AJMI 1193 case AJGT: 1194 return AJLE 1195 case AJHI: 1196 return AJLS 1197 case AJCS: 1198 return AJCC 1199 case AJCC: 1200 return AJCS 1201 case AJPS: 1202 return AJPC 1203 case AJPC: 1204 return AJPS 1205 case AJOS: 1206 return AJOC 1207 case AJOC: 1208 return AJOS 1209 } 1210 1211 log.Fatalf("unknown relation: %s", obj.Aconv(a)) 1212 return 0 1213 } 1214 1215 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1216 var q *obj.Prog 1217 var i int 1218 var a obj.As 1219 1220 loop: 1221 if p == nil { 1222 return 1223 } 1224 if p.As == obj.AJMP { 1225 q = p.Pcond 1226 if q != nil && q.As != obj.ATEXT { 1227 /* mark instruction as done and continue layout at target of jump */ 1228 p.Mark |= DONE 1229 1230 p = q 1231 if p.Mark&DONE == 0 { 1232 goto loop 1233 } 1234 } 1235 } 1236 1237 if p.Mark&DONE != 0 { 1238 /* 1239 * p goes here, but already used it elsewhere. 1240 * copy up to 4 instructions or else branch to other copy. 1241 */ 1242 i = 0 1243 q = p 1244 for ; i < 4; i, q = i+1, q.Link { 1245 if q == nil { 1246 break 1247 } 1248 if q == *last { 1249 break 1250 } 1251 a = q.As 1252 if a == obj.ANOP { 1253 i-- 1254 continue 1255 } 1256 1257 if nofollow(a) || pushpop(a) { 1258 break // NOTE(rsc): arm does goto copy 1259 } 1260 if q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1261 continue 1262 } 1263 if a == obj.ACALL || a == ALOOP { 1264 continue 1265 } 1266 for { 1267 if p.As == obj.ANOP { 1268 p = p.Link 1269 continue 1270 } 1271 1272 q = obj.Copyp(ctxt, p) 1273 p = p.Link 1274 q.Mark |= DONE 1275 (*last).Link = q 1276 *last = q 1277 if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1278 continue 1279 } 1280 1281 q.As = relinv(q.As) 1282 p = q.Pcond 1283 q.Pcond = q.Link 1284 q.Link = p 1285 xfol(ctxt, q.Link, last) 1286 p = q.Link 1287 if p.Mark&DONE != 0 { 1288 return 1289 } 1290 goto loop 1291 /* */ 1292 } 1293 } 1294 q = ctxt.NewProg() 1295 q.As = obj.AJMP 1296 q.Lineno = p.Lineno 1297 q.To.Type = obj.TYPE_BRANCH 1298 q.To.Offset = p.Pc 1299 q.Pcond = p 1300 p = q 1301 } 1302 1303 /* emit p */ 1304 p.Mark |= DONE 1305 1306 (*last).Link = p 1307 *last = p 1308 a = p.As 1309 1310 /* continue loop with what comes after p */ 1311 if nofollow(a) { 1312 return 1313 } 1314 if p.Pcond != nil && a != obj.ACALL { 1315 /* 1316 * some kind of conditional branch. 1317 * recurse to follow one path. 1318 * continue loop on the other. 1319 */ 1320 q = obj.Brchain(ctxt, p.Pcond) 1321 if q != nil { 1322 p.Pcond = q 1323 } 1324 q = obj.Brchain(ctxt, p.Link) 1325 if q != nil { 1326 p.Link = q 1327 } 1328 if p.From.Type == obj.TYPE_CONST { 1329 if p.From.Offset == 1 { 1330 /* 1331 * expect conditional jump to be taken. 1332 * rewrite so that's the fall-through case. 1333 */ 1334 p.As = relinv(a) 1335 1336 q = p.Link 1337 p.Link = p.Pcond 1338 p.Pcond = q 1339 } 1340 } else { 1341 q = p.Link 1342 if q.Mark&DONE != 0 { 1343 if a != ALOOP { 1344 p.As = relinv(a) 1345 p.Link = p.Pcond 1346 p.Pcond = q 1347 } 1348 } 1349 } 1350 1351 xfol(ctxt, p.Link, last) 1352 if p.Pcond.Mark&DONE != 0 { 1353 return 1354 } 1355 p = p.Pcond 1356 goto loop 1357 } 1358 1359 p = p.Link 1360 goto loop 1361 } 1362 1363 var unaryDst = map[obj.As]bool{ 1364 ABSWAPL: true, 1365 ABSWAPQ: true, 1366 ACMPXCHG8B: true, 1367 ADECB: true, 1368 ADECL: true, 1369 ADECQ: true, 1370 ADECW: true, 1371 AINCB: true, 1372 AINCL: true, 1373 AINCQ: true, 1374 AINCW: true, 1375 ANEGB: true, 1376 ANEGL: true, 1377 ANEGQ: true, 1378 ANEGW: true, 1379 ANOTB: true, 1380 ANOTL: true, 1381 ANOTQ: true, 1382 ANOTW: true, 1383 APOPL: true, 1384 APOPQ: true, 1385 APOPW: true, 1386 ASETCC: true, 1387 ASETCS: true, 1388 ASETEQ: true, 1389 ASETGE: true, 1390 ASETGT: true, 1391 ASETHI: true, 1392 ASETLE: true, 1393 ASETLS: true, 1394 ASETLT: true, 1395 ASETMI: true, 1396 ASETNE: true, 1397 ASETOC: true, 1398 ASETOS: true, 1399 ASETPC: true, 1400 ASETPL: true, 1401 ASETPS: true, 1402 AFFREE: true, 1403 AFLDENV: true, 1404 AFSAVE: true, 1405 AFSTCW: true, 1406 AFSTENV: true, 1407 AFSTSW: true, 1408 AFXSAVE: true, 1409 AFXSAVE64: true, 1410 ASTMXCSR: true, 1411 } 1412 1413 var Linkamd64 = obj.LinkArch{ 1414 Arch: sys.ArchAMD64, 1415 Preprocess: preprocess, 1416 Assemble: span6, 1417 Follow: follow, 1418 Progedit: progedit, 1419 UnaryDst: unaryDst, 1420 } 1421 1422 var Linkamd64p32 = obj.LinkArch{ 1423 Arch: sys.ArchAMD64P32, 1424 Preprocess: preprocess, 1425 Assemble: span6, 1426 Follow: follow, 1427 Progedit: progedit, 1428 UnaryDst: unaryDst, 1429 } 1430 1431 var Link386 = obj.LinkArch{ 1432 Arch: sys.Arch386, 1433 Preprocess: preprocess, 1434 Assemble: span6, 1435 Follow: follow, 1436 Progedit: progedit, 1437 UnaryDst: unaryDst, 1438 }