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