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