github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/src/cmd/internal/obj/x86/obj6.go (about) 1 // Inferno utils/6l/pass.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/6l/pass.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package x86 32 33 import ( 34 "cmd/internal/obj" 35 "encoding/binary" 36 "fmt" 37 "log" 38 "math" 39 ) 40 41 func CanUse1InsnTLS(ctxt *obj.Link) bool { 42 if isAndroid { 43 // For android, we use a disgusting hack that assumes 44 // the thread-local storage slot for g is allocated 45 // using pthread_key_create with a fixed offset 46 // (see src/runtime/cgo/gcc_android_amd64.c). 47 // This makes access to the TLS storage (for g) doable 48 // with 1 instruction. 49 return true 50 } 51 52 if ctxt.Arch.Regsize == 4 { 53 switch ctxt.Headtype { 54 case obj.Hlinux, 55 obj.Hnacl, 56 obj.Hplan9, 57 obj.Hwindows: 58 return false 59 } 60 61 return true 62 } 63 64 switch ctxt.Headtype { 65 case obj.Hplan9, 66 obj.Hwindows: 67 return false 68 case obj.Hlinux: 69 return ctxt.Flag_shared == 0 70 } 71 72 return true 73 } 74 75 func progedit(ctxt *obj.Link, p *obj.Prog) { 76 // Maintain information about code generation mode. 77 if ctxt.Mode == 0 { 78 ctxt.Mode = ctxt.Arch.Regsize * 8 79 } 80 p.Mode = int8(ctxt.Mode) 81 82 switch p.As { 83 case AMODE: 84 if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) { 85 switch int(p.From.Offset) { 86 case 16, 32, 64: 87 ctxt.Mode = int(p.From.Offset) 88 } 89 } 90 obj.Nopout(p) 91 } 92 93 // Thread-local storage references use the TLS pseudo-register. 94 // As a register, TLS refers to the thread-local storage base, and it 95 // can only be loaded into another register: 96 // 97 // MOVQ TLS, AX 98 // 99 // An offset from the thread-local storage base is written off(reg)(TLS*1). 100 // Semantically it is off(reg), but the (TLS*1) annotation marks this as 101 // indexing from the loaded TLS base. This emits a relocation so that 102 // if the linker needs to adjust the offset, it can. For example: 103 // 104 // MOVQ TLS, AX 105 // MOVQ 0(AX)(TLS*1), CX // load g into CX 106 // 107 // On systems that support direct access to the TLS memory, this 108 // pair of instructions can be reduced to a direct TLS memory reference: 109 // 110 // MOVQ 0(TLS), CX // load g into CX 111 // 112 // The 2-instruction and 1-instruction forms correspond to the two code 113 // sequences for loading a TLS variable in the local exec model given in "ELF 114 // Handling For Thread-Local Storage". 115 // 116 // We apply this rewrite on systems that support the 1-instruction form. 117 // The decision is made using only the operating system and the -shared flag, 118 // not the link mode. If some link modes on a particular operating system 119 // require the 2-instruction form, then all builds for that operating system 120 // will use the 2-instruction form, so that the link mode decision can be 121 // delayed to link time. 122 // 123 // In this way, all supported systems use identical instructions to 124 // access TLS, and they are rewritten appropriately first here in 125 // liblink and then finally using relocations in the linker. 126 // 127 // When -shared is passed, we leave the code in the 2-instruction form but 128 // assemble (and relocate) them in different ways to generate the initial 129 // exec code sequence. It's a bit of a fluke that this is possible without 130 // rewriting the instructions more comprehensively, and it only does because 131 // we only support a single TLS variable (g). 132 133 if CanUse1InsnTLS(ctxt) { 134 // Reduce 2-instruction sequence to 1-instruction sequence. 135 // Sequences like 136 // MOVQ TLS, BX 137 // ... off(BX)(TLS*1) ... 138 // become 139 // NOP 140 // ... off(TLS) ... 141 // 142 // TODO(rsc): Remove the Hsolaris special case. It exists only to 143 // guarantee we are producing byte-identical binaries as before this code. 144 // But it should be unnecessary. 145 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_REG && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 && ctxt.Headtype != obj.Hsolaris { 146 obj.Nopout(p) 147 } 148 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { 149 p.From.Reg = REG_TLS 150 p.From.Scale = 0 151 p.From.Index = REG_NONE 152 } 153 154 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 155 p.To.Reg = REG_TLS 156 p.To.Scale = 0 157 p.To.Index = REG_NONE 158 } 159 } else { 160 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it 161 // as the 2-instruction sequence if necessary. 162 // MOVQ 0(TLS), BX 163 // becomes 164 // MOVQ TLS, BX 165 // MOVQ 0(BX)(TLS*1), BX 166 if (p.As == AMOVQ || p.As == AMOVL) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_TLS && p.To.Type == obj.TYPE_REG && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 167 q := obj.Appendp(ctxt, p) 168 q.As = p.As 169 q.From = p.From 170 q.From.Type = obj.TYPE_MEM 171 q.From.Reg = p.To.Reg 172 q.From.Index = REG_TLS 173 q.From.Scale = 2 // TODO: use 1 174 q.To = p.To 175 p.From.Type = obj.TYPE_REG 176 p.From.Reg = REG_TLS 177 p.From.Index = REG_NONE 178 p.From.Offset = 0 179 } 180 } 181 182 // TODO: Remove. 183 if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 { 184 if p.From.Scale == 1 && p.From.Index == REG_TLS { 185 p.From.Scale = 2 186 } 187 if p.To.Scale == 1 && p.To.Index == REG_TLS { 188 p.To.Scale = 2 189 } 190 } 191 192 // Rewrite 0 to $0 in 3rd 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.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { 211 switch p.As { 212 case AMOVL: 213 p.As = ALEAL 214 p.From.Type = obj.TYPE_MEM 215 case AMOVQ: 216 p.As = ALEAQ 217 p.From.Type = obj.TYPE_MEM 218 } 219 } 220 221 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 222 if p.From3 != nil { 223 nacladdr(ctxt, p, p.From3) 224 } 225 nacladdr(ctxt, p, &p.From) 226 nacladdr(ctxt, p, &p.To) 227 } 228 229 // Rewrite float constants to values stored in memory. 230 switch p.As { 231 // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx 232 case AMOVSS: 233 if p.From.Type == obj.TYPE_FCONST { 234 // 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 != 0 && 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 && obj.Framepointer_enabled != 0 && autoffset > 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 619 autoffset += int32(bpsize) 620 p.To.Offset += int64(bpsize) 621 } else { 622 bpsize = 0 623 } 624 625 textarg := int64(p.To.Val.(int32)) 626 cursym.Args = int32(textarg) 627 cursym.Locals = int32(p.To.Offset) 628 629 // TODO(rsc): Remove. 630 if p.Mode == 32 && cursym.Locals < 0 { 631 cursym.Locals = 0 632 } 633 634 // TODO(rsc): Remove 'p.Mode == 64 &&'. 635 if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 { 636 for q := p; q != nil; q = q.Link { 637 if q.As == obj.ACALL { 638 goto noleaf 639 } 640 if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 { 641 goto noleaf 642 } 643 } 644 645 p.From3.Offset |= obj.NOSPLIT 646 noleaf: 647 } 648 649 if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 { 650 p = obj.Appendp(ctxt, p) 651 p = load_g_cx(ctxt, p) // load g into CX 652 } 653 654 if cursym.Text.From3Offset()&obj.NOSPLIT == 0 { 655 p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check 656 } 657 658 if autoffset != 0 { 659 if autoffset%int32(ctxt.Arch.Regsize) != 0 { 660 ctxt.Diag("unaligned stack size %d", autoffset) 661 } 662 p = obj.Appendp(ctxt, p) 663 p.As = AADJSP 664 p.From.Type = obj.TYPE_CONST 665 p.From.Offset = int64(autoffset) 666 p.Spadj = autoffset 667 } else { 668 // zero-byte stack adjustment. 669 // Insert a fake non-zero adjustment so that stkcheck can 670 // recognize the end of the stack-splitting prolog. 671 p = obj.Appendp(ctxt, p) 672 673 p.As = obj.ANOP 674 p.Spadj = int32(-ctxt.Arch.Ptrsize) 675 p = obj.Appendp(ctxt, p) 676 p.As = obj.ANOP 677 p.Spadj = int32(ctxt.Arch.Ptrsize) 678 } 679 680 deltasp := autoffset 681 682 if bpsize > 0 { 683 // Save caller's BP 684 p = obj.Appendp(ctxt, p) 685 686 p.As = AMOVQ 687 p.From.Type = obj.TYPE_REG 688 p.From.Reg = REG_BP 689 p.To.Type = obj.TYPE_MEM 690 p.To.Reg = REG_SP 691 p.To.Scale = 1 692 p.To.Offset = int64(autoffset) - int64(bpsize) 693 694 // Move current frame to BP 695 p = obj.Appendp(ctxt, p) 696 697 p.As = ALEAQ 698 p.From.Type = obj.TYPE_MEM 699 p.From.Reg = REG_SP 700 p.From.Scale = 1 701 p.From.Offset = int64(autoffset) - int64(bpsize) 702 p.To.Type = obj.TYPE_REG 703 p.To.Reg = REG_BP 704 } 705 706 if cursym.Text.From3Offset()&obj.WRAPPER != 0 { 707 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 708 // 709 // MOVQ g_panic(CX), BX 710 // TESTQ BX, BX 711 // JEQ end 712 // LEAQ (autoffset+8)(SP), DI 713 // CMPQ panic_argp(BX), DI 714 // JNE end 715 // MOVQ SP, panic_argp(BX) 716 // end: 717 // NOP 718 // 719 // The NOP is needed to give the jumps somewhere to land. 720 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 721 722 p = obj.Appendp(ctxt, p) 723 724 p.As = AMOVQ 725 p.From.Type = obj.TYPE_MEM 726 p.From.Reg = REG_CX 727 p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic 728 p.To.Type = obj.TYPE_REG 729 p.To.Reg = REG_BX 730 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 731 p.As = AMOVL 732 p.From.Type = obj.TYPE_MEM 733 p.From.Reg = REG_R15 734 p.From.Scale = 1 735 p.From.Index = REG_CX 736 } 737 if p.Mode == 32 { 738 p.As = AMOVL 739 } 740 741 p = obj.Appendp(ctxt, p) 742 p.As = ATESTQ 743 p.From.Type = obj.TYPE_REG 744 p.From.Reg = REG_BX 745 p.To.Type = obj.TYPE_REG 746 p.To.Reg = REG_BX 747 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 748 p.As = ATESTL 749 } 750 751 p = obj.Appendp(ctxt, p) 752 p.As = AJEQ 753 p.To.Type = obj.TYPE_BRANCH 754 p1 := p 755 756 p = obj.Appendp(ctxt, p) 757 p.As = ALEAQ 758 p.From.Type = obj.TYPE_MEM 759 p.From.Reg = REG_SP 760 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize) 761 p.To.Type = obj.TYPE_REG 762 p.To.Reg = REG_DI 763 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 764 p.As = ALEAL 765 } 766 767 p = obj.Appendp(ctxt, p) 768 p.As = ACMPQ 769 p.From.Type = obj.TYPE_MEM 770 p.From.Reg = REG_BX 771 p.From.Offset = 0 // Panic.argp 772 p.To.Type = obj.TYPE_REG 773 p.To.Reg = REG_DI 774 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 775 p.As = ACMPL 776 p.From.Type = obj.TYPE_MEM 777 p.From.Reg = REG_R15 778 p.From.Scale = 1 779 p.From.Index = REG_BX 780 } 781 if p.Mode == 32 { 782 p.As = ACMPL 783 } 784 785 p = obj.Appendp(ctxt, p) 786 p.As = AJNE 787 p.To.Type = obj.TYPE_BRANCH 788 p2 := p 789 790 p = obj.Appendp(ctxt, p) 791 p.As = AMOVQ 792 p.From.Type = obj.TYPE_REG 793 p.From.Reg = REG_SP 794 p.To.Type = obj.TYPE_MEM 795 p.To.Reg = REG_BX 796 p.To.Offset = 0 // Panic.argp 797 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 798 p.As = AMOVL 799 p.To.Type = obj.TYPE_MEM 800 p.To.Reg = REG_R15 801 p.To.Scale = 1 802 p.To.Index = REG_BX 803 } 804 if p.Mode == 32 { 805 p.As = AMOVL 806 } 807 808 p = obj.Appendp(ctxt, p) 809 p.As = obj.ANOP 810 p1.Pcond = p 811 p2.Pcond = p 812 } 813 814 var a int 815 var pcsize int 816 for ; p != nil; p = p.Link { 817 pcsize = int(p.Mode) / 8 818 a = int(p.From.Name) 819 if a == obj.NAME_AUTO { 820 p.From.Offset += int64(deltasp) - int64(bpsize) 821 } 822 if a == obj.NAME_PARAM { 823 p.From.Offset += int64(deltasp) + int64(pcsize) 824 } 825 if p.From3 != nil { 826 a = int(p.From3.Name) 827 if a == obj.NAME_AUTO { 828 p.From3.Offset += int64(deltasp) - int64(bpsize) 829 } 830 if a == obj.NAME_PARAM { 831 p.From3.Offset += int64(deltasp) + int64(pcsize) 832 } 833 } 834 a = int(p.To.Name) 835 if a == obj.NAME_AUTO { 836 p.To.Offset += int64(deltasp) - int64(bpsize) 837 } 838 if a == obj.NAME_PARAM { 839 p.To.Offset += int64(deltasp) + int64(pcsize) 840 } 841 842 switch p.As { 843 default: 844 continue 845 846 case APUSHL, APUSHFL: 847 deltasp += 4 848 p.Spadj = 4 849 continue 850 851 case APUSHQ, APUSHFQ: 852 deltasp += 8 853 p.Spadj = 8 854 continue 855 856 case APUSHW, APUSHFW: 857 deltasp += 2 858 p.Spadj = 2 859 continue 860 861 case APOPL, APOPFL: 862 deltasp -= 4 863 p.Spadj = -4 864 continue 865 866 case APOPQ, APOPFQ: 867 deltasp -= 8 868 p.Spadj = -8 869 continue 870 871 case APOPW, APOPFW: 872 deltasp -= 2 873 p.Spadj = -2 874 continue 875 876 case obj.ARET: 877 break 878 } 879 880 if autoffset != deltasp { 881 ctxt.Diag("unbalanced PUSH/POP") 882 } 883 884 if autoffset != 0 { 885 if bpsize > 0 { 886 // Restore caller's BP 887 p.As = AMOVQ 888 889 p.From.Type = obj.TYPE_MEM 890 p.From.Reg = REG_SP 891 p.From.Scale = 1 892 p.From.Offset = int64(autoffset) - int64(bpsize) 893 p.To.Type = obj.TYPE_REG 894 p.To.Reg = REG_BP 895 p = obj.Appendp(ctxt, p) 896 } 897 898 p.As = AADJSP 899 p.From.Type = obj.TYPE_CONST 900 p.From.Offset = int64(-autoffset) 901 p.Spadj = -autoffset 902 p = obj.Appendp(ctxt, p) 903 p.As = obj.ARET 904 905 // If there are instructions following 906 // this ARET, they come from a branch 907 // with the same stackframe, so undo 908 // the cleanup. 909 p.Spadj = +autoffset 910 } 911 912 if p.To.Sym != nil { // retjmp 913 p.As = obj.AJMP 914 } 915 } 916 } 917 918 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 919 if ctxt.Headtype == obj.Hnacl && p.Mode == 64 { 920 a.Type = obj.TYPE_MEM 921 a.Reg = REG_R15 922 a.Index = REG_CX 923 a.Scale = 1 924 return 925 } 926 927 a.Type = obj.TYPE_MEM 928 a.Reg = REG_CX 929 } 930 931 // Append code to p to load g into cx. 932 // Overwrites p with the first instruction (no first appendp). 933 // Overwriting p is unusual but it lets use this in both the 934 // prologue (caller must call appendp first) and in the epilogue. 935 // Returns last new instruction. 936 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog { 937 p.As = AMOVQ 938 if ctxt.Arch.Ptrsize == 4 { 939 p.As = AMOVL 940 } 941 p.From.Type = obj.TYPE_MEM 942 p.From.Reg = REG_TLS 943 p.From.Offset = 0 944 p.To.Type = obj.TYPE_REG 945 p.To.Reg = REG_CX 946 947 next := p.Link 948 progedit(ctxt, p) 949 for p.Link != next { 950 p = p.Link 951 } 952 953 if p.From.Index == REG_TLS { 954 p.From.Scale = 2 955 } 956 957 return p 958 } 959 960 // Append code to p to check for stack split. 961 // Appends to (does not overwrite) p. 962 // Assumes g is in CX. 963 // Returns last new instruction. 964 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog { 965 cmp := ACMPQ 966 lea := ALEAQ 967 mov := AMOVQ 968 sub := ASUBQ 969 970 if ctxt.Headtype == obj.Hnacl || p.Mode == 32 { 971 cmp = ACMPL 972 lea = ALEAL 973 mov = AMOVL 974 sub = ASUBL 975 } 976 977 var q1 *obj.Prog 978 if framesize <= obj.StackSmall { 979 // small stack: SP <= stackguard 980 // CMPQ SP, stackguard 981 p = obj.Appendp(ctxt, p) 982 983 p.As = cmp 984 p.From.Type = obj.TYPE_REG 985 p.From.Reg = REG_SP 986 indir_cx(ctxt, p, &p.To) 987 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 988 if ctxt.Cursym.Cfunc { 989 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 990 } 991 } else if framesize <= obj.StackBig { 992 // large stack: SP-framesize <= stackguard-StackSmall 993 // LEAQ -xxx(SP), AX 994 // CMPQ AX, stackguard 995 p = obj.Appendp(ctxt, p) 996 997 p.As = lea 998 p.From.Type = obj.TYPE_MEM 999 p.From.Reg = REG_SP 1000 p.From.Offset = -(int64(framesize) - obj.StackSmall) 1001 p.To.Type = obj.TYPE_REG 1002 p.To.Reg = REG_AX 1003 1004 p = obj.Appendp(ctxt, p) 1005 p.As = cmp 1006 p.From.Type = obj.TYPE_REG 1007 p.From.Reg = REG_AX 1008 indir_cx(ctxt, p, &p.To) 1009 p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 1010 if ctxt.Cursym.Cfunc { 1011 p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 1012 } 1013 } else { 1014 // Such a large stack we need to protect against wraparound. 1015 // If SP is close to zero: 1016 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1017 // The +StackGuard on both sides is required to keep the left side positive: 1018 // SP is allowed to be slightly below stackguard. See stack.h. 1019 // 1020 // Preemption sets stackguard to StackPreempt, a very large value. 1021 // That breaks the math above, so we have to check for that explicitly. 1022 // MOVQ stackguard, CX 1023 // CMPQ CX, $StackPreempt 1024 // JEQ label-of-call-to-morestack 1025 // LEAQ StackGuard(SP), AX 1026 // SUBQ CX, AX 1027 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1028 1029 p = obj.Appendp(ctxt, p) 1030 1031 p.As = mov 1032 indir_cx(ctxt, p, &p.From) 1033 p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0 1034 if ctxt.Cursym.Cfunc { 1035 p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1 1036 } 1037 p.To.Type = obj.TYPE_REG 1038 p.To.Reg = REG_SI 1039 1040 p = obj.Appendp(ctxt, p) 1041 p.As = cmp 1042 p.From.Type = obj.TYPE_REG 1043 p.From.Reg = REG_SI 1044 p.To.Type = obj.TYPE_CONST 1045 p.To.Offset = obj.StackPreempt 1046 if p.Mode == 32 { 1047 p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1))) 1048 } 1049 1050 p = obj.Appendp(ctxt, p) 1051 p.As = AJEQ 1052 p.To.Type = obj.TYPE_BRANCH 1053 q1 = p 1054 1055 p = obj.Appendp(ctxt, p) 1056 p.As = lea 1057 p.From.Type = obj.TYPE_MEM 1058 p.From.Reg = REG_SP 1059 p.From.Offset = obj.StackGuard 1060 p.To.Type = obj.TYPE_REG 1061 p.To.Reg = REG_AX 1062 1063 p = obj.Appendp(ctxt, p) 1064 p.As = sub 1065 p.From.Type = obj.TYPE_REG 1066 p.From.Reg = REG_SI 1067 p.To.Type = obj.TYPE_REG 1068 p.To.Reg = REG_AX 1069 1070 p = obj.Appendp(ctxt, p) 1071 p.As = cmp 1072 p.From.Type = obj.TYPE_REG 1073 p.From.Reg = REG_AX 1074 p.To.Type = obj.TYPE_CONST 1075 p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 1076 } 1077 1078 // common 1079 jls := obj.Appendp(ctxt, p) 1080 jls.As = AJLS 1081 jls.To.Type = obj.TYPE_BRANCH 1082 1083 var last *obj.Prog 1084 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 1085 } 1086 1087 spfix := obj.Appendp(ctxt, last) 1088 spfix.As = obj.ANOP 1089 spfix.Spadj = -framesize 1090 1091 call := obj.Appendp(ctxt, spfix) 1092 call.Lineno = ctxt.Cursym.Text.Lineno 1093 call.Mode = ctxt.Cursym.Text.Mode 1094 call.As = obj.ACALL 1095 call.To.Type = obj.TYPE_BRANCH 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 1105 jmp := obj.Appendp(ctxt, call) 1106 jmp.As = obj.AJMP 1107 jmp.To.Type = obj.TYPE_BRANCH 1108 jmp.Pcond = ctxt.Cursym.Text.Link 1109 jmp.Spadj = +framesize 1110 1111 jls.Pcond = call 1112 if q1 != nil { 1113 q1.Pcond = call 1114 } 1115 1116 return jls 1117 } 1118 1119 func follow(ctxt *obj.Link, s *obj.LSym) { 1120 ctxt.Cursym = s 1121 1122 firstp := ctxt.NewProg() 1123 lastp := firstp 1124 xfol(ctxt, s.Text, &lastp) 1125 lastp.Link = nil 1126 s.Text = firstp.Link 1127 } 1128 1129 func nofollow(a obj.As) bool { 1130 switch a { 1131 case obj.AJMP, 1132 obj.ARET, 1133 AIRETL, 1134 AIRETQ, 1135 AIRETW, 1136 ARETFL, 1137 ARETFQ, 1138 ARETFW, 1139 obj.AUNDEF: 1140 return true 1141 } 1142 1143 return false 1144 } 1145 1146 func pushpop(a obj.As) bool { 1147 switch a { 1148 case APUSHL, 1149 APUSHFL, 1150 APUSHQ, 1151 APUSHFQ, 1152 APUSHW, 1153 APUSHFW, 1154 APOPL, 1155 APOPFL, 1156 APOPQ, 1157 APOPFQ, 1158 APOPW, 1159 APOPFW: 1160 return true 1161 } 1162 1163 return false 1164 } 1165 1166 func relinv(a obj.As) obj.As { 1167 switch a { 1168 case AJEQ: 1169 return AJNE 1170 case AJNE: 1171 return AJEQ 1172 case AJLE: 1173 return AJGT 1174 case AJLS: 1175 return AJHI 1176 case AJLT: 1177 return AJGE 1178 case AJMI: 1179 return AJPL 1180 case AJGE: 1181 return AJLT 1182 case AJPL: 1183 return AJMI 1184 case AJGT: 1185 return AJLE 1186 case AJHI: 1187 return AJLS 1188 case AJCS: 1189 return AJCC 1190 case AJCC: 1191 return AJCS 1192 case AJPS: 1193 return AJPC 1194 case AJPC: 1195 return AJPS 1196 case AJOS: 1197 return AJOC 1198 case AJOC: 1199 return AJOS 1200 } 1201 1202 log.Fatalf("unknown relation: %s", obj.Aconv(a)) 1203 return 0 1204 } 1205 1206 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1207 var q *obj.Prog 1208 var i int 1209 var a obj.As 1210 1211 loop: 1212 if p == nil { 1213 return 1214 } 1215 if p.As == obj.AJMP { 1216 q = p.Pcond 1217 if q != nil && q.As != obj.ATEXT { 1218 /* mark instruction as done and continue layout at target of jump */ 1219 p.Mark |= DONE 1220 1221 p = q 1222 if p.Mark&DONE == 0 { 1223 goto loop 1224 } 1225 } 1226 } 1227 1228 if p.Mark&DONE != 0 { 1229 /* 1230 * p goes here, but already used it elsewhere. 1231 * copy up to 4 instructions or else branch to other copy. 1232 */ 1233 i = 0 1234 q = p 1235 for ; i < 4; i, q = i+1, q.Link { 1236 if q == nil { 1237 break 1238 } 1239 if q == *last { 1240 break 1241 } 1242 a = q.As 1243 if a == obj.ANOP { 1244 i-- 1245 continue 1246 } 1247 1248 if nofollow(a) || pushpop(a) { 1249 break // NOTE(rsc): arm does goto copy 1250 } 1251 if q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1252 continue 1253 } 1254 if a == obj.ACALL || a == ALOOP { 1255 continue 1256 } 1257 for { 1258 if p.As == obj.ANOP { 1259 p = p.Link 1260 continue 1261 } 1262 1263 q = obj.Copyp(ctxt, p) 1264 p = p.Link 1265 q.Mark |= DONE 1266 (*last).Link = q 1267 *last = q 1268 if q.As != a || q.Pcond == nil || q.Pcond.Mark&DONE != 0 { 1269 continue 1270 } 1271 1272 q.As = relinv(q.As) 1273 p = q.Pcond 1274 q.Pcond = q.Link 1275 q.Link = p 1276 xfol(ctxt, q.Link, last) 1277 p = q.Link 1278 if p.Mark&DONE != 0 { 1279 return 1280 } 1281 goto loop 1282 /* */ 1283 } 1284 } 1285 q = ctxt.NewProg() 1286 q.As = obj.AJMP 1287 q.Lineno = p.Lineno 1288 q.To.Type = obj.TYPE_BRANCH 1289 q.To.Offset = p.Pc 1290 q.Pcond = p 1291 p = q 1292 } 1293 1294 /* emit p */ 1295 p.Mark |= DONE 1296 1297 (*last).Link = p 1298 *last = p 1299 a = p.As 1300 1301 /* continue loop with what comes after p */ 1302 if nofollow(a) { 1303 return 1304 } 1305 if p.Pcond != nil && a != obj.ACALL { 1306 /* 1307 * some kind of conditional branch. 1308 * recurse to follow one path. 1309 * continue loop on the other. 1310 */ 1311 q = obj.Brchain(ctxt, p.Pcond) 1312 if q != nil { 1313 p.Pcond = q 1314 } 1315 q = obj.Brchain(ctxt, p.Link) 1316 if q != nil { 1317 p.Link = q 1318 } 1319 if p.From.Type == obj.TYPE_CONST { 1320 if p.From.Offset == 1 { 1321 /* 1322 * expect conditional jump to be taken. 1323 * rewrite so that's the fall-through case. 1324 */ 1325 p.As = relinv(a) 1326 1327 q = p.Link 1328 p.Link = p.Pcond 1329 p.Pcond = q 1330 } 1331 } else { 1332 q = p.Link 1333 if q.Mark&DONE != 0 { 1334 if a != ALOOP { 1335 p.As = relinv(a) 1336 p.Link = p.Pcond 1337 p.Pcond = q 1338 } 1339 } 1340 } 1341 1342 xfol(ctxt, p.Link, last) 1343 if p.Pcond.Mark&DONE != 0 { 1344 return 1345 } 1346 p = p.Pcond 1347 goto loop 1348 } 1349 1350 p = p.Link 1351 goto loop 1352 } 1353 1354 var unaryDst = map[obj.As]bool{ 1355 ABSWAPL: true, 1356 ABSWAPQ: true, 1357 ACMPXCHG8B: true, 1358 ADECB: true, 1359 ADECL: true, 1360 ADECQ: true, 1361 ADECW: true, 1362 AINCB: true, 1363 AINCL: true, 1364 AINCQ: true, 1365 AINCW: true, 1366 ANEGB: true, 1367 ANEGL: true, 1368 ANEGQ: true, 1369 ANEGW: true, 1370 ANOTB: true, 1371 ANOTL: true, 1372 ANOTQ: true, 1373 ANOTW: true, 1374 APOPL: true, 1375 APOPQ: true, 1376 APOPW: true, 1377 ASETCC: true, 1378 ASETCS: true, 1379 ASETEQ: true, 1380 ASETGE: true, 1381 ASETGT: true, 1382 ASETHI: true, 1383 ASETLE: true, 1384 ASETLS: true, 1385 ASETLT: true, 1386 ASETMI: true, 1387 ASETNE: true, 1388 ASETOC: true, 1389 ASETOS: true, 1390 ASETPC: true, 1391 ASETPL: true, 1392 ASETPS: true, 1393 AFFREE: true, 1394 AFLDENV: true, 1395 AFSAVE: true, 1396 AFSTCW: true, 1397 AFSTENV: true, 1398 AFSTSW: true, 1399 AFXSAVE: true, 1400 AFXSAVE64: true, 1401 ASTMXCSR: true, 1402 } 1403 1404 var Linkamd64 = obj.LinkArch{ 1405 ByteOrder: binary.LittleEndian, 1406 Name: "amd64", 1407 Thechar: '6', 1408 Preprocess: preprocess, 1409 Assemble: span6, 1410 Follow: follow, 1411 Progedit: progedit, 1412 UnaryDst: unaryDst, 1413 Minlc: 1, 1414 Ptrsize: 8, 1415 Regsize: 8, 1416 } 1417 1418 var Linkamd64p32 = obj.LinkArch{ 1419 ByteOrder: binary.LittleEndian, 1420 Name: "amd64p32", 1421 Thechar: '6', 1422 Preprocess: preprocess, 1423 Assemble: span6, 1424 Follow: follow, 1425 Progedit: progedit, 1426 UnaryDst: unaryDst, 1427 Minlc: 1, 1428 Ptrsize: 4, 1429 Regsize: 8, 1430 } 1431 1432 var Link386 = obj.LinkArch{ 1433 ByteOrder: binary.LittleEndian, 1434 Name: "386", 1435 Thechar: '8', 1436 Preprocess: preprocess, 1437 Assemble: span6, 1438 Follow: follow, 1439 Progedit: progedit, 1440 UnaryDst: unaryDst, 1441 Minlc: 1, 1442 Ptrsize: 4, 1443 Regsize: 4, 1444 }