github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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 call := obj.Appendp(ctxt, last) 1084 call.Lineno = ctxt.Cursym.Text.Lineno 1085 call.Mode = ctxt.Cursym.Text.Mode 1086 call.As = obj.ACALL 1087 call.To.Type = obj.TYPE_BRANCH 1088 morestack := "runtime.morestack" 1089 switch { 1090 case ctxt.Cursym.Cfunc != 0: 1091 morestack = "runtime.morestackc" 1092 case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0: 1093 morestack = "runtime.morestack_noctxt" 1094 } 1095 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 1096 1097 jmp := obj.Appendp(ctxt, call) 1098 jmp.As = obj.AJMP 1099 jmp.To.Type = obj.TYPE_BRANCH 1100 jmp.Pcond = ctxt.Cursym.Text.Link 1101 1102 jls.Pcond = call 1103 if q1 != nil { 1104 q1.Pcond = call 1105 } 1106 1107 return jls 1108 } 1109 1110 func follow(ctxt *obj.Link, s *obj.LSym) { 1111 ctxt.Cursym = s 1112 1113 firstp := ctxt.NewProg() 1114 lastp := firstp 1115 xfol(ctxt, s.Text, &lastp) 1116 lastp.Link = nil 1117 s.Text = firstp.Link 1118 } 1119 1120 func nofollow(a int) bool { 1121 switch a { 1122 case obj.AJMP, 1123 obj.ARET, 1124 AIRETL, 1125 AIRETQ, 1126 AIRETW, 1127 ARETFL, 1128 ARETFQ, 1129 ARETFW, 1130 obj.AUNDEF: 1131 return true 1132 } 1133 1134 return false 1135 } 1136 1137 func pushpop(a int) bool { 1138 switch a { 1139 case APUSHL, 1140 APUSHFL, 1141 APUSHQ, 1142 APUSHFQ, 1143 APUSHW, 1144 APUSHFW, 1145 APOPL, 1146 APOPFL, 1147 APOPQ, 1148 APOPFQ, 1149 APOPW, 1150 APOPFW: 1151 return true 1152 } 1153 1154 return false 1155 } 1156 1157 func relinv(a int16) int16 { 1158 switch a { 1159 case AJEQ: 1160 return AJNE 1161 case AJNE: 1162 return AJEQ 1163 case AJLE: 1164 return AJGT 1165 case AJLS: 1166 return AJHI 1167 case AJLT: 1168 return AJGE 1169 case AJMI: 1170 return AJPL 1171 case AJGE: 1172 return AJLT 1173 case AJPL: 1174 return AJMI 1175 case AJGT: 1176 return AJLE 1177 case AJHI: 1178 return AJLS 1179 case AJCS: 1180 return AJCC 1181 case AJCC: 1182 return AJCS 1183 case AJPS: 1184 return AJPC 1185 case AJPC: 1186 return AJPS 1187 case AJOS: 1188 return AJOC 1189 case AJOC: 1190 return AJOS 1191 } 1192 1193 log.Fatalf("unknown relation: %s", obj.Aconv(int(a))) 1194 return 0 1195 } 1196 1197 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1198 var q *obj.Prog 1199 var i int 1200 var a int 1201 1202 loop: 1203 if p == nil { 1204 return 1205 } 1206 if p.As == obj.AJMP { 1207 q = p.Pcond 1208 if q != nil && q.As != obj.ATEXT { 1209 /* mark instruction as done and continue layout at target of jump */ 1210 p.Mark = 1 1211 1212 p = q 1213 if p.Mark == 0 { 1214 goto loop 1215 } 1216 } 1217 } 1218 1219 if p.Mark != 0 { 1220 /* 1221 * p goes here, but already used it elsewhere. 1222 * copy up to 4 instructions or else branch to other copy. 1223 */ 1224 i = 0 1225 q = p 1226 for ; i < 4; i, q = i+1, q.Link { 1227 if q == nil { 1228 break 1229 } 1230 if q == *last { 1231 break 1232 } 1233 a = int(q.As) 1234 if a == obj.ANOP { 1235 i-- 1236 continue 1237 } 1238 1239 if nofollow(a) || pushpop(a) { 1240 break // NOTE(rsc): arm does goto copy 1241 } 1242 if q.Pcond == nil || q.Pcond.Mark != 0 { 1243 continue 1244 } 1245 if a == obj.ACALL || a == ALOOP { 1246 continue 1247 } 1248 for { 1249 if p.As == obj.ANOP { 1250 p = p.Link 1251 continue 1252 } 1253 1254 q = obj.Copyp(ctxt, p) 1255 p = p.Link 1256 q.Mark = 1 1257 (*last).Link = q 1258 *last = q 1259 if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 { 1260 continue 1261 } 1262 1263 q.As = relinv(q.As) 1264 p = q.Pcond 1265 q.Pcond = q.Link 1266 q.Link = p 1267 xfol(ctxt, q.Link, last) 1268 p = q.Link 1269 if p.Mark != 0 { 1270 return 1271 } 1272 goto loop 1273 /* */ 1274 } 1275 } 1276 q = ctxt.NewProg() 1277 q.As = obj.AJMP 1278 q.Lineno = p.Lineno 1279 q.To.Type = obj.TYPE_BRANCH 1280 q.To.Offset = p.Pc 1281 q.Pcond = p 1282 p = q 1283 } 1284 1285 /* emit p */ 1286 p.Mark = 1 1287 1288 (*last).Link = p 1289 *last = p 1290 a = int(p.As) 1291 1292 /* continue loop with what comes after p */ 1293 if nofollow(a) { 1294 return 1295 } 1296 if p.Pcond != nil && a != obj.ACALL { 1297 /* 1298 * some kind of conditional branch. 1299 * recurse to follow one path. 1300 * continue loop on the other. 1301 */ 1302 q = obj.Brchain(ctxt, p.Pcond) 1303 if q != nil { 1304 p.Pcond = q 1305 } 1306 q = obj.Brchain(ctxt, p.Link) 1307 if q != nil { 1308 p.Link = q 1309 } 1310 if p.From.Type == obj.TYPE_CONST { 1311 if p.From.Offset == 1 { 1312 /* 1313 * expect conditional jump to be taken. 1314 * rewrite so that's the fall-through case. 1315 */ 1316 p.As = relinv(int16(a)) 1317 1318 q = p.Link 1319 p.Link = p.Pcond 1320 p.Pcond = q 1321 } 1322 } else { 1323 q = p.Link 1324 if q.Mark != 0 { 1325 if a != ALOOP { 1326 p.As = relinv(int16(a)) 1327 p.Link = p.Pcond 1328 p.Pcond = q 1329 } 1330 } 1331 } 1332 1333 xfol(ctxt, p.Link, last) 1334 if p.Pcond.Mark != 0 { 1335 return 1336 } 1337 p = p.Pcond 1338 goto loop 1339 } 1340 1341 p = p.Link 1342 goto loop 1343 } 1344 1345 var unaryDst = map[int]bool{ 1346 ABSWAPL: true, 1347 ABSWAPQ: true, 1348 ACMPXCHG8B: true, 1349 ADECB: true, 1350 ADECL: true, 1351 ADECQ: true, 1352 ADECW: true, 1353 AINCB: true, 1354 AINCL: true, 1355 AINCQ: true, 1356 AINCW: true, 1357 ANEGB: true, 1358 ANEGL: true, 1359 ANEGQ: true, 1360 ANEGW: true, 1361 ANOTB: true, 1362 ANOTL: true, 1363 ANOTQ: true, 1364 ANOTW: true, 1365 APOPL: true, 1366 APOPQ: true, 1367 APOPW: true, 1368 ASETCC: true, 1369 ASETCS: true, 1370 ASETEQ: true, 1371 ASETGE: true, 1372 ASETGT: true, 1373 ASETHI: true, 1374 ASETLE: true, 1375 ASETLS: true, 1376 ASETLT: true, 1377 ASETMI: true, 1378 ASETNE: true, 1379 ASETOC: true, 1380 ASETOS: true, 1381 ASETPC: true, 1382 ASETPL: true, 1383 ASETPS: true, 1384 AFFREE: true, 1385 AFLDENV: true, 1386 AFSAVE: true, 1387 AFSTCW: true, 1388 AFSTENV: true, 1389 AFSTSW: true, 1390 AFXSAVE: true, 1391 AFXSAVE64: true, 1392 ASTMXCSR: true, 1393 } 1394 1395 var Linkamd64 = obj.LinkArch{ 1396 ByteOrder: binary.LittleEndian, 1397 Name: "amd64", 1398 Thechar: '6', 1399 Preprocess: preprocess, 1400 Assemble: span6, 1401 Follow: follow, 1402 Progedit: progedit, 1403 UnaryDst: unaryDst, 1404 Minlc: 1, 1405 Ptrsize: 8, 1406 Regsize: 8, 1407 } 1408 1409 var Linkamd64p32 = obj.LinkArch{ 1410 ByteOrder: binary.LittleEndian, 1411 Name: "amd64p32", 1412 Thechar: '6', 1413 Preprocess: preprocess, 1414 Assemble: span6, 1415 Follow: follow, 1416 Progedit: progedit, 1417 UnaryDst: unaryDst, 1418 Minlc: 1, 1419 Ptrsize: 4, 1420 Regsize: 8, 1421 } 1422 1423 var Link386 = obj.LinkArch{ 1424 ByteOrder: binary.LittleEndian, 1425 Name: "386", 1426 Thechar: '8', 1427 Preprocess: preprocess, 1428 Assemble: span6, 1429 Follow: follow, 1430 Progedit: progedit, 1431 UnaryDst: unaryDst, 1432 Minlc: 1, 1433 Ptrsize: 4, 1434 Regsize: 4, 1435 }