github.com/dannin/go@v0.0.0-20161031215817-d35dfd405eaa/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 obj.Hwindowsgui: 60 return false 61 } 62 63 return true 64 } 65 66 switch ctxt.Headtype { 67 case obj.Hplan9, obj.Hwindows, obj.Hwindowsgui: 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 || ctxt.Headtype == obj.Hwindowsgui) && 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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.Set(obj.AttrLocal, 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 // Treat common runtime calls that take no arguments 664 // the same as duffcopy and duffzero. 665 if !isZeroArgRuntimeCall(q.To.Sym) { 666 leaf = false 667 break LeafSearch 668 } 669 fallthrough 670 case obj.ADUFFCOPY, obj.ADUFFZERO: 671 if autoffset >= obj.StackSmall-8 { 672 leaf = false 673 break LeafSearch 674 } 675 } 676 } 677 678 if leaf { 679 p.From3.Offset |= obj.NOSPLIT 680 } 681 } 682 683 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 684 p = obj.Appendp(ctxt, p) 685 p = load_g_cx(ctxt, p) // load g into CX 686 } 687 688 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 689 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 690 } 691 692 if autoffset != 0 { 693 if autoffset%int32(ctxt.Arch.RegSize) != 0 { 694 ctxt.Diag("unaligned stack size %d", autoffset) 695 } 696 p = obj.Appendp(ctxt, p) 697 p.As = AADJSP 698 p.From.Type = obj.TYPE_CONST 699 p.From.Offset = int64(autoffset) 700 p.Spadj = autoffset 701 } 702 703 deltasp := autoffset 704 705 if bpsize > 0 { 706 // Save caller's BP 707 p = obj.Appendp(ctxt, p) 708 709 p.As = AMOVQ 710 p.From.Type = obj.TYPE_REG 711 p.From.Reg = REG_BP 712 p.To.Type = obj.TYPE_MEM 713 p.To.Reg = REG_SP 714 p.To.Scale = 1 715 p.To.Offset = int64(autoffset) - int64(bpsize) 716 717 // Move current frame to BP 718 p = obj.Appendp(ctxt, p) 719 720 p.As = ALEAQ 721 p.From.Type = obj.TYPE_MEM 722 p.From.Reg = REG_SP 723 p.From.Scale = 1 724 p.From.Offset = int64(autoffset) - int64(bpsize) 725 p.To.Type = obj.TYPE_REG 726 p.To.Reg = REG_BP 727 } 728 729 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 730 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 731 // 732 // MOVQ g_panic(CX), BX 733 // TESTQ BX, BX 734 // JEQ end 735 // LEAQ (autoffset+8)(SP), DI 736 // CMPQ panic_argp(BX), DI 737 // JNE end 738 // MOVQ SP, panic_argp(BX) 739 // end: 740 // NOP 741 // 742 // The NOP is needed to give the jumps somewhere to land. 743 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 744 745 p = obj.Appendp(ctxt, p) 746 747 p.As = AMOVQ 748 p.From.Type = obj.TYPE_MEM 749 p.From.Reg = REG_CX 750 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 751 p.To.Type = obj.TYPE_REG 752 p.To.Reg = REG_BX 753 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 754 p.As = AMOVL 755 p.From.Type = obj.TYPE_MEM 756 p.From.Reg = REG_R15 757 p.From.Scale = 1 758 p.From.Index = REG_CX 759 } 760 if p.Mode == 32 { 761 p.As = AMOVL 762 } 763 764 p = obj.Appendp(ctxt, p) 765 p.As = ATESTQ 766 p.From.Type = obj.TYPE_REG 767 p.From.Reg = REG_BX 768 p.To.Type = obj.TYPE_REG 769 p.To.Reg = REG_BX 770 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 771 p.As = ATESTL 772 } 773 774 p = obj.Appendp(ctxt, p) 775 p.As = AJEQ 776 p.To.Type = obj.TYPE_BRANCH 777 p1 := p 778 779 p = obj.Appendp(ctxt, p) 780 p.As = ALEAQ 781 p.From.Type = obj.TYPE_MEM 782 p.From.Reg = REG_SP 783 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) 784 p.To.Type = obj.TYPE_REG 785 p.To.Reg = REG_DI 786 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 787 p.As = ALEAL 788 } 789 790 p = obj.Appendp(ctxt, p) 791 p.As = ACMPQ 792 p.From.Type = obj.TYPE_MEM 793 p.From.Reg = REG_BX 794 p.From.Offset = 0 // Panic.argp 795 p.To.Type = obj.TYPE_REG 796 p.To.Reg = REG_DI 797 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 798 p.As = ACMPL 799 p.From.Type = obj.TYPE_MEM 800 p.From.Reg = REG_R15 801 p.From.Scale = 1 802 p.From.Index = REG_BX 803 } 804 if p.Mode == 32 { 805 p.As = ACMPL 806 } 807 808 p = obj.Appendp(ctxt, p) 809 p.As = AJNE 810 p.To.Type = obj.TYPE_BRANCH 811 p2 := p 812 813 p = obj.Appendp(ctxt, p) 814 p.As = AMOVQ 815 p.From.Type = obj.TYPE_REG 816 p.From.Reg = REG_SP 817 p.To.Type = obj.TYPE_MEM 818 p.To.Reg = REG_BX 819 p.To.Offset = 0 // Panic.argp 820 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 821 p.As = AMOVL 822 p.To.Type = obj.TYPE_MEM 823 p.To.Reg = REG_R15 824 p.To.Scale = 1 825 p.To.Index = REG_BX 826 } 827 if p.Mode == 32 { 828 p.As = AMOVL 829 } 830 831 p = obj.Appendp(ctxt, p) 832 p.As = obj.ANOP 833 p1.Pcond = p 834 p2.Pcond = p 835 } 836 837 for ; p != nil; p = p.Link { 838 pcsize := int(p.Mode) / 8 839 switch p.From.Name { 840 case obj.NAME_AUTO: 841 p.From.Offset += int64(deltasp) - int64(bpsize) 842 case obj.NAME_PARAM: 843 p.From.Offset += int64(deltasp) + int64(pcsize) 844 } 845 if p.From3 != nil { 846 switch p.From3.Name { 847 case obj.NAME_AUTO: 848 p.From3.Offset += int64(deltasp) - int64(bpsize) 849 case obj.NAME_PARAM: 850 p.From3.Offset += int64(deltasp) + int64(pcsize) 851 } 852 } 853 switch p.To.Name { 854 case obj.NAME_AUTO: 855 p.To.Offset += int64(deltasp) - int64(bpsize) 856 case obj.NAME_PARAM: 857 p.To.Offset += int64(deltasp) + int64(pcsize) 858 } 859 860 switch p.As { 861 default: 862 continue 863 864 case APUSHL, APUSHFL: 865 deltasp += 4 866 p.Spadj = 4 867 continue 868 869 case APUSHQ, APUSHFQ: 870 deltasp += 8 871 p.Spadj = 8 872 continue 873 874 case APUSHW, APUSHFW: 875 deltasp += 2 876 p.Spadj = 2 877 continue 878 879 case APOPL, APOPFL: 880 deltasp -= 4 881 p.Spadj = -4 882 continue 883 884 case APOPQ, APOPFQ: 885 deltasp -= 8 886 p.Spadj = -8 887 continue 888 889 case APOPW, APOPFW: 890 deltasp -= 2 891 p.Spadj = -2 892 continue 893 894 case obj.ARET: 895 // do nothing 896 } 897 898 if autoffset != deltasp { 899 ctxt.Diag("unbalanced PUSH/POP") 900 } 901 902 if autoffset != 0 { 903 if bpsize > 0 { 904 // Restore caller's BP 905 p.As = AMOVQ 906 907 p.From.Type = obj.TYPE_MEM 908 p.From.Reg = REG_SP 909 p.From.Scale = 1 910 p.From.Offset = int64(autoffset) - int64(bpsize) 911 p.To.Type = obj.TYPE_REG 912 p.To.Reg = REG_BP 913 p = obj.Appendp(ctxt, p) 914 } 915 916 p.As = AADJSP 917 p.From.Type = obj.TYPE_CONST 918 p.From.Offset = int64(-autoffset) 919 p.Spadj = -autoffset 920 p = obj.Appendp(ctxt, p) 921 p.As = obj.ARET 922 923 // If there are instructions following 924 // this ARET, they come from a branch 925 // with the same stackframe, so undo 926 // the cleanup. 927 p.Spadj = +autoffset 928 } 929 930 if p.To.Sym != nil { // retjmp 931 p.As = obj.AJMP 932 } 933 } 934 } 935 936 func isZeroArgRuntimeCall(s *obj.LSym) bool { 937 if s == nil { 938 return false 939 } 940 switch s.Name { 941 case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide": 942 return true 943 } 944 return false 945 } 946 947 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 948 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 949 a.Type = obj.TYPE_MEM 950 a.Reg = REG_R15 951 a.Index = REG_CX 952 a.Scale = 1 953 return 954 } 955 956 a.Type = obj.TYPE_MEM 957 a.Reg = REG_CX 958 } 959 960 // Append code to p to load g into cx. 961 // Overwrites p with the first instruction (no first appendp). 962 // Overwriting p is unusual but it lets use this in both the 963 // prologue (caller must call appendp first) and in the epilogue. 964 // Returns last new instruction. 965 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 966 p.As = AMOVQ 967 if ctxt.Arch.PtrSize == 4 { 968 p.As = AMOVL 969 } 970 p.From.Type = obj.TYPE_MEM 971 p.From.Reg = REG_TLS 972 p.From.Offset = 0 973 p.To.Type = obj.TYPE_REG 974 p.To.Reg = REG_CX 975 976 next := p.Link 977 progedit(ctxt, p) 978 for p.Link != next { 979 p = p.Link 980 } 981 982 if p.From.Index == REG_TLS { 983 p.From.Scale = 2 984 } 985 986 return p 987 } 988 989 // Append code to p to check for stack split. 990 // Appends to (does not overwrite) p. 991 // Assumes g is in CX. 992 // Returns last new instruction. 993 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 994 cmp := ACMPQ 995 lea := ALEAQ 996 mov := AMOVQ 997 sub := ASUBQ 998 999 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 1000 cmp = ACMPL 1001 lea = ALEAL 1002 mov = AMOVL 1003 sub = ASUBL 1004 } 1005 1006 var q1 *obj.Prog 1007 if framesize <= obj.StackSmall { 1008 // small stack: SP <= stackguard 1009 // CMPQ SP, stackguard 1010 p = obj.Appendp(ctxt, p) 1011 1012 p.As = cmp 1013 p.From.Type = obj.TYPE_REG 1014 p.From.Reg = REG_SP 1015 indir_cx(ctxt, p, &p.To) 1016 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1017 if ctxt.Cursym.CFunc() { 1018 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1019 } 1020 } else if framesize <= obj.StackBig { 1021 // large stack: SP-framesize <= stackguard-StackSmall 1022 // LEAQ -xxx(SP), AX 1023 // CMPQ AX, stackguard 1024 p = obj.Appendp(ctxt, p) 1025 1026 p.As = lea 1027 p.From.Type = obj.TYPE_MEM 1028 p.From.Reg = REG_SP 1029 p.From.Offset = -(int64(framesize) - obj.StackSmall) 1030 p.To.Type = obj.TYPE_REG 1031 p.To.Reg = REG_AX 1032 1033 p = obj.Appendp(ctxt, p) 1034 p.As = cmp 1035 p.From.Type = obj.TYPE_REG 1036 p.From.Reg = REG_AX 1037 indir_cx(ctxt, p, &p.To) 1038 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1039 if ctxt.Cursym.CFunc() { 1040 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1041 } 1042 } else { 1043 // Such a large stack we need to protect against wraparound. 1044 // If SP is close to zero: 1045 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1046 // The +StackGuard on both sides is required to keep the left side positive: 1047 // SP is allowed to be slightly below stackguard. See stack.h. 1048 // 1049 // Preemption sets stackguard to StackPreempt, a very large value. 1050 // That breaks the math above, so we have to check for that explicitly. 1051 // MOVQ stackguard, CX 1052 // CMPQ CX, $StackPreempt 1053 // JEQ label-of-call-to-morestack 1054 // LEAQ StackGuard(SP), AX 1055 // SUBQ CX, AX 1056 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1057 1058 p = obj.Appendp(ctxt, p) 1059 1060 p.As = mov 1061 indir_cx(ctxt, p, &p.From) 1062 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1063 if ctxt.Cursym.CFunc() { 1064 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1065 } 1066 p.To.Type = obj.TYPE_REG 1067 p.To.Reg = REG_SI 1068 1069 p = obj.Appendp(ctxt, p) 1070 p.As = cmp 1071 p.From.Type = obj.TYPE_REG 1072 p.From.Reg = REG_SI 1073 p.To.Type = obj.TYPE_CONST 1074 p.To.Offset = obj.StackPreempt 1075 if p.Mode == 32 { 1076 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 1077 } 1078 1079 p = obj.Appendp(ctxt, p) 1080 p.As = AJEQ 1081 p.To.Type = obj.TYPE_BRANCH 1082 q1 = p 1083 1084 p = obj.Appendp(ctxt, p) 1085 p.As = lea 1086 p.From.Type = obj.TYPE_MEM 1087 p.From.Reg = REG_SP 1088 p.From.Offset = obj.StackGuard 1089 p.To.Type = obj.TYPE_REG 1090 p.To.Reg = REG_AX 1091 1092 p = obj.Appendp(ctxt, p) 1093 p.As = sub 1094 p.From.Type = obj.TYPE_REG 1095 p.From.Reg = REG_SI 1096 p.To.Type = obj.TYPE_REG 1097 p.To.Reg = REG_AX 1098 1099 p = obj.Appendp(ctxt, p) 1100 p.As = cmp 1101 p.From.Type = obj.TYPE_REG 1102 p.From.Reg = REG_AX 1103 p.To.Type = obj.TYPE_CONST 1104 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 1105 } 1106 1107 // common 1108 jls := obj.Appendp(ctxt, p) 1109 jls.As = AJLS 1110 jls.To.Type = obj.TYPE_BRANCH 1111 1112 var last *obj.Prog 1113 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 1114 } 1115 1116 // Now we are at the end of the function, but logically 1117 // we are still in function prologue. We need to fix the 1118 // SP data and PCDATA. 1119 spfix := obj.Appendp(ctxt, last) 1120 spfix.As = obj.ANOP 1121 spfix.Spadj = -framesize 1122 1123 pcdata := obj.Appendp(ctxt, spfix) 1124 pcdata.Lineno = ctxt.Cursym.Text.Lineno 1125 pcdata.Mode = ctxt.Cursym.Text.Mode 1126 pcdata.As = obj.APCDATA 1127 pcdata.From.Type = obj.TYPE_CONST 1128 pcdata.From.Offset = obj.PCDATA_StackMapIndex 1129 pcdata.To.Type = obj.TYPE_CONST 1130 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 1131 1132 call := obj.Appendp(ctxt, pcdata) 1133 call.Lineno = ctxt.Cursym.Text.Lineno 1134 call.Mode = ctxt.Cursym.Text.Mode 1135 call.As = obj.ACALL 1136 call.To.Type = obj.TYPE_BRANCH 1137 call.To.Name = obj.NAME_EXTERN 1138 morestack := "runtime.morestack" 1139 switch { 1140 case ctxt.Cursym.CFunc(): 1141 morestack = "runtime.morestackc" 1142 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 1143 morestack = "runtime.morestack_noctxt" 1144 } 1145 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 1146 // When compiling 386 code for dynamic linking, the call needs to be adjusted 1147 // to follow PIC rules. This in turn can insert more instructions, so we need 1148 // to keep track of the start of the call (where the jump will be to) and the 1149 // end (which following instructions are appended to). 1150 callend := call 1151 progedit(ctxt, callend) 1152 for ; callend.Link != nil; callend = callend.Link { 1153 progedit(ctxt, callend.Link) 1154 } 1155 1156 jmp := obj.Appendp(ctxt, callend) 1157 jmp.As = obj.AJMP 1158 jmp.To.Type = obj.TYPE_BRANCH 1159 jmp.Pcond = ctxt.Cursym.Text.Link 1160 jmp.Spadj = +framesize 1161 1162 jls.Pcond = call 1163 if q1 != nil { 1164 q1.Pcond = call 1165 } 1166 1167 return jls 1168 } 1169 1170 func follow(ctxt *obj.Link, s *obj.LSym) { 1171 ctxt.Cursym = s 1172 1173 firstp := ctxt.NewProg() 1174 lastp := firstp 1175 xfol(ctxt, s.Text, &lastp) 1176 lastp.Link = nil 1177 s.Text = firstp.Link 1178 } 1179 1180 func nofollow(a obj.As) bool { 1181 switch a { 1182 case obj.AJMP, 1183 obj.ARET, 1184 AIRETL, 1185 AIRETQ, 1186 AIRETW, 1187 ARETFL, 1188 ARETFQ, 1189 ARETFW, 1190 obj.AUNDEF: 1191 return true 1192 } 1193 1194 return false 1195 } 1196 1197 func pushpop(a obj.As) bool { 1198 switch a { 1199 case APUSHL, 1200 APUSHFL, 1201 APUSHQ, 1202 APUSHFQ, 1203 APUSHW, 1204 APUSHFW, 1205 APOPL, 1206 APOPFL, 1207 APOPQ, 1208 APOPFQ, 1209 APOPW, 1210 APOPFW: 1211 return true 1212 } 1213 1214 return false 1215 } 1216 1217 func relinv(a obj.As) obj.As { 1218 switch a { 1219 case AJEQ: 1220 return AJNE 1221 case AJNE: 1222 return AJEQ 1223 case AJLE: 1224 return AJGT 1225 case AJLS: 1226 return AJHI 1227 case AJLT: 1228 return AJGE 1229 case AJMI: 1230 return AJPL 1231 case AJGE: 1232 return AJLT 1233 case AJPL: 1234 return AJMI 1235 case AJGT: 1236 return AJLE 1237 case AJHI: 1238 return AJLS 1239 case AJCS: 1240 return AJCC 1241 case AJCC: 1242 return AJCS 1243 case AJPS: 1244 return AJPC 1245 case AJPC: 1246 return AJPS 1247 case AJOS: 1248 return AJOC 1249 case AJOC: 1250 return AJOS 1251 } 1252 1253 log.Fatalf("unknown relation: %s", a) 1254 return 0 1255 } 1256 1257 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1258 var q *obj.Prog 1259 var i int 1260 var a obj.As 1261 1262 loop: 1263 if p == nil { 1264 return 1265 } 1266 if p.As == obj.AJMP { 1267 q = p.Pcond 1268 if q != nil && q.As != obj.ATEXT { 1269 /* mark instruction as done and continue layout at target of jump */ 1270 p.Mark |= DONE 1271 1272 p = q 1273 if p.Mark&DONE == 0 { 1274 goto loop 1275 } 1276 } 1277 } 1278 1279 if p.Mark&DONE != 0 { 1280 /* 1281 * p goes here, but already used it elsewhere. 1282 * copy up to 4 instructions or else branch to other copy. 1283 */ 1284 i = 0 1285 q = p 1286 for ; i < 4; i, q = i+1, q.Link { 1287 if q == nil { 1288 break 1289 } 1290 if q == *last { 1291 break 1292 } 1293 a = q.As 1294 if a == obj.ANOP { 1295 i-- 1296 continue 1297 } 1298 1299 if nofollow(a) || pushpop(a) { 1300 break // NOTE(rsc): arm does goto copy 1301 } 1302 if q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1303 continue 1304 } 1305 if a == obj.ACALL || a == ALOOP { 1306 continue 1307 } 1308 for { 1309 if p.As == obj.ANOP { 1310 p = p.Link 1311 continue 1312 } 1313 1314 q = obj.Copyp(ctxt, p) 1315 p = p.Link 1316 q.Mark |= DONE 1317 (*last).Link = q 1318 *last = q 1319 if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1320 continue 1321 } 1322 1323 q.As = relinv(q.As) 1324 p = q.Pcond 1325 q.Pcond = q.Link 1326 q.Link = p 1327 xfol(ctxt, q.Link, last) 1328 p = q.Link 1329 if p.Mark&DONE != 0 { 1330 return 1331 } 1332 goto loop 1333 /* */ 1334 } 1335 } 1336 q = ctxt.NewProg() 1337 q.As = obj.AJMP 1338 q.Lineno = p.Lineno 1339 q.To.Type = obj.TYPE_BRANCH 1340 q.To.Offset = p.Pc 1341 q.Pcond = p 1342 p = q 1343 } 1344 1345 /* emit p */ 1346 p.Mark |= DONE 1347 1348 (*last).Link = p 1349 *last = p 1350 a = p.As 1351 1352 /* continue loop with what comes after p */ 1353 if nofollow(a) { 1354 return 1355 } 1356 if p.Pcond != nil && a != obj.ACALL { 1357 /* 1358 * some kind of conditional branch. 1359 * recurse to follow one path. 1360 * continue loop on the other. 1361 */ 1362 q = obj.Brchain(ctxt, p.Pcond) 1363 if q != nil { 1364 p.Pcond = q 1365 } 1366 q = obj.Brchain(ctxt, p.Link) 1367 if q != nil { 1368 p.Link = q 1369 } 1370 if p.From.Type == obj.TYPE_CONST { 1371 if p.From.Offset == 1 { 1372 /* 1373 * expect conditional jump to be taken. 1374 * rewrite so that's the fall-through case. 1375 */ 1376 p.As = relinv(a) 1377 1378 q = p.Link 1379 p.Link = p.Pcond 1380 p.Pcond = q 1381 } 1382 } else { 1383 q = p.Link 1384 if q.Mark&DONE != 0 { 1385 if a != ALOOP { 1386 p.As = relinv(a) 1387 p.Link = p.Pcond 1388 p.Pcond = q 1389 } 1390 } 1391 } 1392 1393 xfol(ctxt, p.Link, last) 1394 if p.Pcond.Mark&DONE != 0 { 1395 return 1396 } 1397 p = p.Pcond 1398 goto loop 1399 } 1400 1401 p = p.Link 1402 goto loop 1403 } 1404 1405 var unaryDst = map[obj.As]bool{ 1406 ABSWAPL: true, 1407 ABSWAPQ: true, 1408 ACMPXCHG8B: true, 1409 ADECB: true, 1410 ADECL: true, 1411 ADECQ: true, 1412 ADECW: true, 1413 AINCB: true, 1414 AINCL: true, 1415 AINCQ: true, 1416 AINCW: true, 1417 ANEGB: true, 1418 ANEGL: true, 1419 ANEGQ: true, 1420 ANEGW: true, 1421 ANOTB: true, 1422 ANOTL: true, 1423 ANOTQ: true, 1424 ANOTW: true, 1425 APOPL: true, 1426 APOPQ: true, 1427 APOPW: true, 1428 ASETCC: true, 1429 ASETCS: true, 1430 ASETEQ: true, 1431 ASETGE: true, 1432 ASETGT: true, 1433 ASETHI: true, 1434 ASETLE: true, 1435 ASETLS: true, 1436 ASETLT: true, 1437 ASETMI: true, 1438 ASETNE: true, 1439 ASETOC: true, 1440 ASETOS: true, 1441 ASETPC: true, 1442 ASETPL: true, 1443 ASETPS: true, 1444 AFFREE: true, 1445 AFLDENV: true, 1446 AFSAVE: true, 1447 AFSTCW: true, 1448 AFSTENV: true, 1449 AFSTSW: true, 1450 AFXSAVE: true, 1451 AFXSAVE64: true, 1452 ASTMXCSR: true, 1453 } 1454 1455 var Linkamd64 = obj.LinkArch{ 1456 Arch: sys.ArchAMD64, 1457 Preprocess: preprocess, 1458 Assemble: span6, 1459 Follow: follow, 1460 Progedit: progedit, 1461 UnaryDst: unaryDst, 1462 } 1463 1464 var Linkamd64p32 = obj.LinkArch{ 1465 Arch: sys.ArchAMD64P32, 1466 Preprocess: preprocess, 1467 Assemble: span6, 1468 Follow: follow, 1469 Progedit: progedit, 1470 UnaryDst: unaryDst, 1471 } 1472 1473 var Link386 = obj.LinkArch{ 1474 Arch: sys.Arch386, 1475 Preprocess: preprocess, 1476 Assemble: span6, 1477 Follow: follow, 1478 Progedit: progedit, 1479 UnaryDst: unaryDst, 1480 }