github.com/zxy12/go_duplicate_1_12@v0.0.0-20200217043740-b1636fc0368b/src/cmd/internal/obj/x86/obj6.go (about) 1 // Inferno utils/6l/pass.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6l/pass.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package x86 32 33 import ( 34 "cmd/internal/obj" 35 "cmd/internal/objabi" 36 "cmd/internal/src" 37 "cmd/internal/sys" 38 "math" 39 "strings" 40 ) 41 42 func CanUse1InsnTLS(ctxt *obj.Link) bool { 43 if isAndroid { 44 // For android, we use a disgusting hack that assumes 45 // the thread-local storage slot for g is allocated 46 // using pthread_key_create with a fixed offset 47 // (see src/runtime/cgo/gcc_android_amd64.c). 48 // This makes access to the TLS storage (for g) doable 49 // with 1 instruction. 50 return true 51 } 52 53 if ctxt.Arch.Family == sys.I386 { 54 switch ctxt.Headtype { 55 case objabi.Hlinux, 56 objabi.Hnacl, 57 objabi.Hplan9, 58 objabi.Hwindows: 59 return false 60 } 61 62 return true 63 } 64 65 switch ctxt.Headtype { 66 case objabi.Hplan9, objabi.Hwindows: 67 return false 68 case objabi.Hlinux, objabi.Hfreebsd: 69 return !ctxt.Flag_shared 70 } 71 72 return true 73 } 74 75 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 76 // Thread-local storage references use the TLS pseudo-register. 77 // As a register, TLS refers to the thread-local storage base, and it 78 // can only be loaded into another register: 79 // 80 // MOVQ TLS, AX 81 // 82 // An offset from the thread-local storage base is written off(reg)(TLS*1). 83 // Semantically it is off(reg), but the (TLS*1) annotation marks this as 84 // indexing from the loaded TLS base. This emits a relocation so that 85 // if the linker needs to adjust the offset, it can. For example: 86 // 87 // MOVQ TLS, AX 88 // MOVQ 0(AX)(TLS*1), CX // load g into CX 89 // 90 // On systems that support direct access to the TLS memory, this 91 // pair of instructions can be reduced to a direct TLS memory reference: 92 // 93 // MOVQ 0(TLS), CX // load g into CX 94 // 95 // The 2-instruction and 1-instruction forms correspond to the two code 96 // sequences for loading a TLS variable in the local exec model given in "ELF 97 // Handling For Thread-Local Storage". 98 // 99 // We apply this rewrite on systems that support the 1-instruction form. 100 // The decision is made using only the operating system and the -shared flag, 101 // not the link mode. If some link modes on a particular operating system 102 // require the 2-instruction form, then all builds for that operating system 103 // will use the 2-instruction form, so that the link mode decision can be 104 // delayed to link time. 105 // 106 // In this way, all supported systems use identical instructions to 107 // access TLS, and they are rewritten appropriately first here in 108 // liblink and then finally using relocations in the linker. 109 // 110 // When -shared is passed, we leave the code in the 2-instruction form but 111 // assemble (and relocate) them in different ways to generate the initial 112 // exec code sequence. It's a bit of a fluke that this is possible without 113 // rewriting the instructions more comprehensively, and it only does because 114 // we only support a single TLS variable (g). 115 116 if CanUse1InsnTLS(ctxt) { 117 // Reduce 2-instruction sequence to 1-instruction sequence. 118 // Sequences like 119 // MOVQ TLS, BX 120 // ... off(BX)(TLS*1) ... 121 // become 122 // NOP 123 // ... off(TLS) ... 124 // 125 // TODO(rsc): Remove the Hsolaris special case. It exists only to 126 // guarantee we are producing byte-identical binaries as before this code. 127 // But it should be unnecessary. 128 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 != objabi.Hsolaris { 129 obj.Nopout(p) 130 } 131 if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 { 132 p.From.Reg = REG_TLS 133 p.From.Scale = 0 134 p.From.Index = REG_NONE 135 } 136 137 if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 { 138 p.To.Reg = REG_TLS 139 p.To.Scale = 0 140 p.To.Index = REG_NONE 141 } 142 } else { 143 // load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it 144 // as the 2-instruction sequence if necessary. 145 // MOVQ 0(TLS), BX 146 // becomes 147 // MOVQ TLS, BX 148 // MOVQ 0(BX)(TLS*1), BX 149 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 { 150 q := obj.Appendp(p, newprog) 151 q.As = p.As 152 q.From = p.From 153 q.From.Type = obj.TYPE_MEM 154 q.From.Reg = p.To.Reg 155 q.From.Index = REG_TLS 156 q.From.Scale = 2 // TODO: use 1 157 q.To = p.To 158 p.From.Type = obj.TYPE_REG 159 p.From.Reg = REG_TLS 160 p.From.Index = REG_NONE 161 p.From.Offset = 0 162 } 163 } 164 165 // TODO: Remove. 166 if ctxt.Headtype == objabi.Hwindows && ctxt.Arch.Family == sys.AMD64 || ctxt.Headtype == objabi.Hplan9 { 167 if p.From.Scale == 1 && p.From.Index == REG_TLS { 168 p.From.Scale = 2 169 } 170 if p.To.Scale == 1 && p.To.Index == REG_TLS { 171 p.To.Scale = 2 172 } 173 } 174 175 // Rewrite 0 to $0 in 3rd argument to CMPPS etc. 176 // That's what the tables expect. 177 switch p.As { 178 case ACMPPD, ACMPPS, ACMPSD, ACMPSS: 179 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 { 180 p.To.Type = obj.TYPE_CONST 181 } 182 } 183 184 // Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH. 185 switch p.As { 186 case obj.ACALL, obj.AJMP, obj.ARET: 187 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil { 188 p.To.Type = obj.TYPE_BRANCH 189 } 190 } 191 192 // Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ. 193 if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Family == sys.AMD64 || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) { 194 switch p.As { 195 case AMOVL: 196 p.As = ALEAL 197 p.From.Type = obj.TYPE_MEM 198 case AMOVQ: 199 p.As = ALEAQ 200 p.From.Type = obj.TYPE_MEM 201 } 202 } 203 204 if ctxt.Headtype == objabi.Hnacl && ctxt.Arch.Family == sys.AMD64 { 205 if p.GetFrom3() != nil { 206 nacladdr(ctxt, p, p.GetFrom3()) 207 } 208 nacladdr(ctxt, p, &p.From) 209 nacladdr(ctxt, p, &p.To) 210 } 211 212 // Rewrite float constants to values stored in memory. 213 switch p.As { 214 // Convert AMOVSS $(0), Xx to AXORPS Xx, Xx 215 case AMOVSS: 216 if p.From.Type == obj.TYPE_FCONST { 217 // f == 0 can't be used here due to -0, so use Float64bits 218 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 219 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 220 p.As = AXORPS 221 p.From = p.To 222 break 223 } 224 } 225 } 226 fallthrough 227 228 case AFMOVF, 229 AFADDF, 230 AFSUBF, 231 AFSUBRF, 232 AFMULF, 233 AFDIVF, 234 AFDIVRF, 235 AFCOMF, 236 AFCOMFP, 237 AADDSS, 238 ASUBSS, 239 AMULSS, 240 ADIVSS, 241 ACOMISS, 242 AUCOMISS: 243 if p.From.Type == obj.TYPE_FCONST { 244 f32 := float32(p.From.Val.(float64)) 245 p.From.Type = obj.TYPE_MEM 246 p.From.Name = obj.NAME_EXTERN 247 p.From.Sym = ctxt.Float32Sym(f32) 248 p.From.Offset = 0 249 } 250 251 case AMOVSD: 252 // Convert AMOVSD $(0), Xx to AXORPS Xx, Xx 253 if p.From.Type == obj.TYPE_FCONST { 254 // f == 0 can't be used here due to -0, so use Float64bits 255 if f := p.From.Val.(float64); math.Float64bits(f) == 0 { 256 if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 { 257 p.As = AXORPS 258 p.From = p.To 259 break 260 } 261 } 262 } 263 fallthrough 264 265 case AFMOVD, 266 AFADDD, 267 AFSUBD, 268 AFSUBRD, 269 AFMULD, 270 AFDIVD, 271 AFDIVRD, 272 AFCOMD, 273 AFCOMDP, 274 AADDSD, 275 ASUBSD, 276 AMULSD, 277 ADIVSD, 278 ACOMISD, 279 AUCOMISD: 280 if p.From.Type == obj.TYPE_FCONST { 281 f64 := p.From.Val.(float64) 282 p.From.Type = obj.TYPE_MEM 283 p.From.Name = obj.NAME_EXTERN 284 p.From.Sym = ctxt.Float64Sym(f64) 285 p.From.Offset = 0 286 } 287 } 288 289 if ctxt.Flag_dynlink { 290 rewriteToUseGot(ctxt, p, newprog) 291 } 292 293 if ctxt.Flag_shared && ctxt.Arch.Family == sys.I386 { 294 rewriteToPcrel(ctxt, p, newprog) 295 } 296 } 297 298 // Rewrite p, if necessary, to access global data via the global offset table. 299 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 300 var lea, mov obj.As 301 var reg int16 302 if ctxt.Arch.Family == sys.AMD64 { 303 lea = ALEAQ 304 mov = AMOVQ 305 reg = REG_R15 306 } else { 307 lea = ALEAL 308 mov = AMOVL 309 reg = REG_CX 310 if p.As == ALEAL && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { 311 // Special case: clobber the destination register with 312 // the PC so we don't have to clobber CX. 313 // The SSA backend depends on CX not being clobbered across LEAL. 314 // See cmd/compile/internal/ssa/gen/386.rules (search for Flag_shared). 315 reg = p.To.Reg 316 } 317 } 318 319 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 320 // ADUFFxxx $offset 321 // becomes 322 // $MOV runtime.duffxxx@GOT, $reg 323 // $LEA $offset($reg), $reg 324 // CALL $reg 325 // (we use LEAx rather than ADDx because ADDx clobbers 326 // flags and duffzero on 386 does not otherwise do so). 327 var sym *obj.LSym 328 if p.As == obj.ADUFFZERO { 329 sym = ctxt.Lookup("runtime.duffzero") 330 } else { 331 sym = ctxt.Lookup("runtime.duffcopy") 332 } 333 offset := p.To.Offset 334 p.As = mov 335 p.From.Type = obj.TYPE_MEM 336 p.From.Name = obj.NAME_GOTREF 337 p.From.Sym = sym 338 p.To.Type = obj.TYPE_REG 339 p.To.Reg = reg 340 p.To.Offset = 0 341 p.To.Sym = nil 342 p1 := obj.Appendp(p, newprog) 343 p1.As = lea 344 p1.From.Type = obj.TYPE_MEM 345 p1.From.Offset = offset 346 p1.From.Reg = reg 347 p1.To.Type = obj.TYPE_REG 348 p1.To.Reg = reg 349 p2 := obj.Appendp(p1, newprog) 350 p2.As = obj.ACALL 351 p2.To.Type = obj.TYPE_REG 352 p2.To.Reg = reg 353 } 354 355 // We only care about global data: NAME_EXTERN means a global 356 // symbol in the Go sense, and p.Sym.Local is true for a few 357 // internally defined symbols. 358 if p.As == lea && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 359 // $LEA sym, Rx becomes $MOV $sym, Rx which will be rewritten below 360 p.As = mov 361 p.From.Type = obj.TYPE_ADDR 362 } 363 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 364 // $MOV $sym, Rx becomes $MOV sym@GOT, Rx 365 // $MOV $sym+<off>, Rx becomes $MOV sym@GOT, Rx; $LEA <off>(Rx), Rx 366 // On 386 only, more complicated things like PUSHL $sym become $MOV sym@GOT, CX; PUSHL CX 367 cmplxdest := false 368 pAs := p.As 369 var dest obj.Addr 370 if p.To.Type != obj.TYPE_REG || pAs != mov { 371 if ctxt.Arch.Family == sys.AMD64 { 372 ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) 373 } 374 cmplxdest = true 375 dest = p.To 376 p.As = mov 377 p.To.Type = obj.TYPE_REG 378 p.To.Reg = reg 379 p.To.Sym = nil 380 p.To.Name = obj.NAME_NONE 381 } 382 p.From.Type = obj.TYPE_MEM 383 p.From.Name = obj.NAME_GOTREF 384 q := p 385 if p.From.Offset != 0 { 386 q = obj.Appendp(p, newprog) 387 q.As = lea 388 q.From.Type = obj.TYPE_MEM 389 q.From.Reg = p.To.Reg 390 q.From.Offset = p.From.Offset 391 q.To = p.To 392 p.From.Offset = 0 393 } 394 if cmplxdest { 395 q = obj.Appendp(q, newprog) 396 q.As = pAs 397 q.To = dest 398 q.From.Type = obj.TYPE_REG 399 q.From.Reg = reg 400 } 401 } 402 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 403 ctxt.Diag("don't know how to handle %v with -dynlink", p) 404 } 405 var source *obj.Addr 406 // MOVx sym, Ry becomes $MOV sym@GOT, R15; MOVx (R15), Ry 407 // MOVx Ry, sym becomes $MOV sym@GOT, R15; MOVx Ry, (R15) 408 // An addition may be inserted between the two MOVs if there is an offset. 409 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 410 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 411 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 412 } 413 source = &p.From 414 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 415 source = &p.To 416 } else { 417 return 418 } 419 if p.As == obj.ACALL { 420 // When dynlinking on 386, almost any call might end up being a call 421 // to a PLT, so make sure the GOT pointer is loaded into BX. 422 // RegTo2 is set on the replacement call insn to stop it being 423 // processed when it is in turn passed to progedit. 424 if ctxt.Arch.Family == sys.AMD64 || (p.To.Sym != nil && p.To.Sym.Local()) || p.RegTo2 != 0 { 425 return 426 } 427 p1 := obj.Appendp(p, newprog) 428 p2 := obj.Appendp(p1, newprog) 429 430 p1.As = ALEAL 431 p1.From.Type = obj.TYPE_MEM 432 p1.From.Name = obj.NAME_STATIC 433 p1.From.Sym = ctxt.Lookup("_GLOBAL_OFFSET_TABLE_") 434 p1.To.Type = obj.TYPE_REG 435 p1.To.Reg = REG_BX 436 437 p2.As = p.As 438 p2.Scond = p.Scond 439 p2.From = p.From 440 if p.RestArgs != nil { 441 p2.RestArgs = append(p2.RestArgs, p.RestArgs...) 442 } 443 p2.Reg = p.Reg 444 p2.To = p.To 445 // p.To.Type was set to TYPE_BRANCH above, but that makes checkaddr 446 // in ../pass.go complain, so set it back to TYPE_MEM here, until p2 447 // itself gets passed to progedit. 448 p2.To.Type = obj.TYPE_MEM 449 p2.RegTo2 = 1 450 451 obj.Nopout(p) 452 return 453 454 } 455 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ARET || p.As == obj.AJMP { 456 return 457 } 458 if source.Type != obj.TYPE_MEM { 459 ctxt.Diag("don't know how to handle %v with -dynlink", p) 460 } 461 p1 := obj.Appendp(p, newprog) 462 p2 := obj.Appendp(p1, newprog) 463 464 p1.As = mov 465 p1.From.Type = obj.TYPE_MEM 466 p1.From.Sym = source.Sym 467 p1.From.Name = obj.NAME_GOTREF 468 p1.To.Type = obj.TYPE_REG 469 p1.To.Reg = reg 470 471 p2.As = p.As 472 p2.From = p.From 473 p2.To = p.To 474 if p.From.Name == obj.NAME_EXTERN { 475 p2.From.Reg = reg 476 p2.From.Name = obj.NAME_NONE 477 p2.From.Sym = nil 478 } else if p.To.Name == obj.NAME_EXTERN { 479 p2.To.Reg = reg 480 p2.To.Name = obj.NAME_NONE 481 p2.To.Sym = nil 482 } else { 483 return 484 } 485 obj.Nopout(p) 486 } 487 488 func rewriteToPcrel(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 489 // RegTo2 is set on the instructions we insert here so they don't get 490 // processed twice. 491 if p.RegTo2 != 0 { 492 return 493 } 494 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 495 return 496 } 497 // Any Prog (aside from the above special cases) with an Addr with Name == 498 // NAME_EXTERN, NAME_STATIC or NAME_GOTREF has a CALL __x86.get_pc_thunk.XX 499 // inserted before it. 500 isName := func(a *obj.Addr) bool { 501 if a.Sym == nil || (a.Type != obj.TYPE_MEM && a.Type != obj.TYPE_ADDR) || a.Reg != 0 { 502 return false 503 } 504 if a.Sym.Type == objabi.STLSBSS { 505 return false 506 } 507 return a.Name == obj.NAME_EXTERN || a.Name == obj.NAME_STATIC || a.Name == obj.NAME_GOTREF 508 } 509 510 if isName(&p.From) && p.From.Type == obj.TYPE_ADDR { 511 // Handle things like "MOVL $sym, (SP)" or "PUSHL $sym" by rewriting 512 // to "MOVL $sym, CX; MOVL CX, (SP)" or "MOVL $sym, CX; PUSHL CX" 513 // respectively. 514 if p.To.Type != obj.TYPE_REG { 515 q := obj.Appendp(p, newprog) 516 q.As = p.As 517 q.From.Type = obj.TYPE_REG 518 q.From.Reg = REG_CX 519 q.To = p.To 520 p.As = AMOVL 521 p.To.Type = obj.TYPE_REG 522 p.To.Reg = REG_CX 523 p.To.Sym = nil 524 p.To.Name = obj.NAME_NONE 525 } 526 } 527 528 if !isName(&p.From) && !isName(&p.To) && (p.GetFrom3() == nil || !isName(p.GetFrom3())) { 529 return 530 } 531 var dst int16 = REG_CX 532 if (p.As == ALEAL || p.As == AMOVL) && p.To.Reg != p.From.Reg && p.To.Reg != p.From.Index { 533 dst = p.To.Reg 534 // Why? See the comment near the top of rewriteToUseGot above. 535 // AMOVLs might be introduced by the GOT rewrites. 536 } 537 q := obj.Appendp(p, newprog) 538 q.RegTo2 = 1 539 r := obj.Appendp(q, newprog) 540 r.RegTo2 = 1 541 q.As = obj.ACALL 542 thunkname := "__x86.get_pc_thunk." + strings.ToLower(rconv(int(dst))) 543 q.To.Sym = ctxt.LookupInit(thunkname, func(s *obj.LSym) { s.Set(obj.AttrLocal, true) }) 544 q.To.Type = obj.TYPE_MEM 545 q.To.Name = obj.NAME_EXTERN 546 r.As = p.As 547 r.Scond = p.Scond 548 r.From = p.From 549 r.RestArgs = p.RestArgs 550 r.Reg = p.Reg 551 r.To = p.To 552 if isName(&p.From) { 553 r.From.Reg = dst 554 } 555 if isName(&p.To) { 556 r.To.Reg = dst 557 } 558 if p.GetFrom3() != nil && isName(p.GetFrom3()) { 559 r.GetFrom3().Reg = dst 560 } 561 obj.Nopout(p) 562 } 563 564 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) { 565 if p.As == ALEAL || p.As == ALEAQ { 566 return 567 } 568 569 if a.Reg == REG_BP { 570 ctxt.Diag("invalid address: %v", p) 571 return 572 } 573 574 if a.Reg == REG_TLS { 575 a.Reg = REG_BP 576 } 577 if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE { 578 switch a.Reg { 579 // all ok 580 case REG_BP, REG_SP, REG_R15: 581 break 582 583 default: 584 if a.Index != REG_NONE { 585 ctxt.Diag("invalid address %v", p) 586 } 587 a.Index = a.Reg 588 if a.Index != REG_NONE { 589 a.Scale = 1 590 } 591 a.Reg = REG_R15 592 } 593 } 594 } 595 596 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 597 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { 598 return 599 } 600 601 p := cursym.Func.Text 602 autoffset := int32(p.To.Offset) 603 if autoffset < 0 { 604 autoffset = 0 605 } 606 607 hasCall := false 608 for q := p; q != nil; q = q.Link { 609 if q.As == obj.ACALL || q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO { 610 hasCall = true 611 break 612 } 613 } 614 615 var bpsize int 616 if ctxt.Arch.Family == sys.AMD64 && ctxt.Framepointer_enabled && 617 !p.From.Sym.NoFrame() && // (1) below 618 !(autoffset == 0 && p.From.Sym.NoSplit()) && // (2) below 619 !(autoffset == 0 && !hasCall) { // (3) below 620 // Make room to save a base pointer. 621 // There are 2 cases we must avoid: 622 // 1) If noframe is set (which we do for functions which tail call). 623 // 2) Scary runtime internals which would be all messed up by frame pointers. 624 // We detect these using a heuristic: frameless nosplit functions. 625 // TODO: Maybe someday we label them all with NOFRAME and get rid of this heuristic. 626 // For performance, we also want to avoid: 627 // 3) Frameless leaf functions 628 bpsize = ctxt.Arch.PtrSize 629 autoffset += int32(bpsize) 630 p.To.Offset += int64(bpsize) 631 } else { 632 bpsize = 0 633 } 634 635 textarg := int64(p.To.Val.(int32)) 636 cursym.Func.Args = int32(textarg) 637 cursym.Func.Locals = int32(p.To.Offset) 638 639 // TODO(rsc): Remove. 640 if ctxt.Arch.Family == sys.I386 && cursym.Func.Locals < 0 { 641 cursym.Func.Locals = 0 642 } 643 644 // TODO(rsc): Remove 'ctxt.Arch.Family == sys.AMD64 &&'. 645 if ctxt.Arch.Family == sys.AMD64 && autoffset < objabi.StackSmall && !p.From.Sym.NoSplit() { 646 leaf := true 647 LeafSearch: 648 for q := p; q != nil; q = q.Link { 649 switch q.As { 650 case obj.ACALL: 651 // Treat common runtime calls that take no arguments 652 // the same as duffcopy and duffzero. 653 if !isZeroArgRuntimeCall(q.To.Sym) { 654 leaf = false 655 break LeafSearch 656 } 657 fallthrough 658 case obj.ADUFFCOPY, obj.ADUFFZERO: 659 if autoffset >= objabi.StackSmall-8 { 660 leaf = false 661 break LeafSearch 662 } 663 } 664 } 665 666 if leaf { 667 p.From.Sym.Set(obj.AttrNoSplit, true) 668 } 669 } 670 671 if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() { 672 p = obj.Appendp(p, newprog) 673 p = load_g_cx(ctxt, p, newprog) // load g into CX 674 } 675 676 if !cursym.Func.Text.From.Sym.NoSplit() { 677 p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg)) // emit split check 678 } 679 680 // Delve debugger would like the next instruction to be noted as the end of the function prologue. 681 // TODO: are there other cases (e.g., wrapper functions) that need marking? 682 markedPrologue := false 683 684 if autoffset != 0 { 685 if autoffset%int32(ctxt.Arch.RegSize) != 0 { 686 ctxt.Diag("unaligned stack size %d", autoffset) 687 } 688 p = obj.Appendp(p, newprog) 689 p.As = AADJSP 690 p.From.Type = obj.TYPE_CONST 691 p.From.Offset = int64(autoffset) 692 p.Spadj = autoffset 693 p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd) 694 markedPrologue = true 695 } 696 697 deltasp := autoffset 698 699 if bpsize > 0 { 700 // Save caller's BP 701 p = obj.Appendp(p, newprog) 702 703 p.As = AMOVQ 704 p.From.Type = obj.TYPE_REG 705 p.From.Reg = REG_BP 706 p.To.Type = obj.TYPE_MEM 707 p.To.Reg = REG_SP 708 p.To.Scale = 1 709 p.To.Offset = int64(autoffset) - int64(bpsize) 710 if !markedPrologue { 711 p.Pos = p.Pos.WithXlogue(src.PosPrologueEnd) 712 } 713 714 // Move current frame to BP 715 p = obj.Appendp(p, newprog) 716 717 p.As = ALEAQ 718 p.From.Type = obj.TYPE_MEM 719 p.From.Reg = REG_SP 720 p.From.Scale = 1 721 p.From.Offset = int64(autoffset) - int64(bpsize) 722 p.To.Type = obj.TYPE_REG 723 p.To.Reg = REG_BP 724 } 725 726 if cursym.Func.Text.From.Sym.Wrapper() { 727 // if g._panic != nil && g._panic.argp == FP { 728 // g._panic.argp = bottom-of-frame 729 // } 730 // 731 // MOVQ g_panic(CX), BX 732 // TESTQ BX, BX 733 // JNE checkargp 734 // end: 735 // NOP 736 // ... rest of function ... 737 // checkargp: 738 // LEAQ (autoffset+8)(SP), DI 739 // CMPQ panic_argp(BX), DI 740 // JNE end 741 // MOVQ SP, panic_argp(BX) 742 // JMP end 743 // 744 // The NOP is needed to give the jumps somewhere to land. 745 // It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes. 746 // 747 // The layout is chosen to help static branch prediction: 748 // Both conditional jumps are unlikely, so they are arranged to be forward jumps. 749 750 // MOVQ g_panic(CX), BX 751 p = obj.Appendp(p, newprog) 752 p.As = AMOVQ 753 p.From.Type = obj.TYPE_MEM 754 p.From.Reg = REG_CX 755 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // g_panic 756 p.To.Type = obj.TYPE_REG 757 p.To.Reg = REG_BX 758 if ctxt.Headtype == objabi.Hnacl && ctxt.Arch.Family == sys.AMD64 { 759 p.As = AMOVL 760 p.From.Type = obj.TYPE_MEM 761 p.From.Reg = REG_R15 762 p.From.Scale = 1 763 p.From.Index = REG_CX 764 } 765 if ctxt.Arch.Family == sys.I386 { 766 p.As = AMOVL 767 } 768 769 // TESTQ BX, BX 770 p = obj.Appendp(p, newprog) 771 p.As = ATESTQ 772 p.From.Type = obj.TYPE_REG 773 p.From.Reg = REG_BX 774 p.To.Type = obj.TYPE_REG 775 p.To.Reg = REG_BX 776 if ctxt.Headtype == objabi.Hnacl || ctxt.Arch.Family == sys.I386 { 777 p.As = ATESTL 778 } 779 780 // JNE checkargp (checkargp to be resolved later) 781 jne := obj.Appendp(p, newprog) 782 jne.As = AJNE 783 jne.To.Type = obj.TYPE_BRANCH 784 785 // end: 786 // NOP 787 end := obj.Appendp(jne, newprog) 788 end.As = obj.ANOP 789 790 // Fast forward to end of function. 791 var last *obj.Prog 792 for last = end; last.Link != nil; last = last.Link { 793 } 794 795 // LEAQ (autoffset+8)(SP), DI 796 p = obj.Appendp(last, newprog) 797 p.As = ALEAQ 798 p.From.Type = obj.TYPE_MEM 799 p.From.Reg = REG_SP 800 p.From.Offset = int64(autoffset) + int64(ctxt.Arch.RegSize) 801 p.To.Type = obj.TYPE_REG 802 p.To.Reg = REG_DI 803 if ctxt.Headtype == objabi.Hnacl || ctxt.Arch.Family == sys.I386 { 804 p.As = ALEAL 805 } 806 807 // Set jne branch target. 808 jne.Pcond = p 809 810 // CMPQ panic_argp(BX), DI 811 p = obj.Appendp(p, newprog) 812 p.As = ACMPQ 813 p.From.Type = obj.TYPE_MEM 814 p.From.Reg = REG_BX 815 p.From.Offset = 0 // Panic.argp 816 p.To.Type = obj.TYPE_REG 817 p.To.Reg = REG_DI 818 if ctxt.Headtype == objabi.Hnacl && ctxt.Arch.Family == sys.AMD64 { 819 p.As = ACMPL 820 p.From.Type = obj.TYPE_MEM 821 p.From.Reg = REG_R15 822 p.From.Scale = 1 823 p.From.Index = REG_BX 824 } 825 if ctxt.Arch.Family == sys.I386 { 826 p.As = ACMPL 827 } 828 829 // JNE end 830 p = obj.Appendp(p, newprog) 831 p.As = AJNE 832 p.To.Type = obj.TYPE_BRANCH 833 p.Pcond = end 834 835 // MOVQ SP, panic_argp(BX) 836 p = obj.Appendp(p, newprog) 837 p.As = AMOVQ 838 p.From.Type = obj.TYPE_REG 839 p.From.Reg = REG_SP 840 p.To.Type = obj.TYPE_MEM 841 p.To.Reg = REG_BX 842 p.To.Offset = 0 // Panic.argp 843 if ctxt.Headtype == objabi.Hnacl && ctxt.Arch.Family == sys.AMD64 { 844 p.As = AMOVL 845 p.To.Type = obj.TYPE_MEM 846 p.To.Reg = REG_R15 847 p.To.Scale = 1 848 p.To.Index = REG_BX 849 } 850 if ctxt.Arch.Family == sys.I386 { 851 p.As = AMOVL 852 } 853 854 // JMP end 855 p = obj.Appendp(p, newprog) 856 p.As = obj.AJMP 857 p.To.Type = obj.TYPE_BRANCH 858 p.Pcond = end 859 860 // Reset p for following code. 861 p = end 862 } 863 864 for ; p != nil; p = p.Link { 865 pcsize := ctxt.Arch.RegSize 866 switch p.From.Name { 867 case obj.NAME_AUTO: 868 p.From.Offset += int64(deltasp) - int64(bpsize) 869 case obj.NAME_PARAM: 870 p.From.Offset += int64(deltasp) + int64(pcsize) 871 } 872 if p.GetFrom3() != nil { 873 switch p.GetFrom3().Name { 874 case obj.NAME_AUTO: 875 p.GetFrom3().Offset += int64(deltasp) - int64(bpsize) 876 case obj.NAME_PARAM: 877 p.GetFrom3().Offset += int64(deltasp) + int64(pcsize) 878 } 879 } 880 switch p.To.Name { 881 case obj.NAME_AUTO: 882 p.To.Offset += int64(deltasp) - int64(bpsize) 883 case obj.NAME_PARAM: 884 p.To.Offset += int64(deltasp) + int64(pcsize) 885 } 886 887 switch p.As { 888 default: 889 continue 890 891 case APUSHL, APUSHFL: 892 deltasp += 4 893 p.Spadj = 4 894 continue 895 896 case APUSHQ, APUSHFQ: 897 deltasp += 8 898 p.Spadj = 8 899 continue 900 901 case APUSHW, APUSHFW: 902 deltasp += 2 903 p.Spadj = 2 904 continue 905 906 case APOPL, APOPFL: 907 deltasp -= 4 908 p.Spadj = -4 909 continue 910 911 case APOPQ, APOPFQ: 912 deltasp -= 8 913 p.Spadj = -8 914 continue 915 916 case APOPW, APOPFW: 917 deltasp -= 2 918 p.Spadj = -2 919 continue 920 921 case obj.ARET: 922 // do nothing 923 } 924 925 if autoffset != deltasp { 926 ctxt.Diag("unbalanced PUSH/POP") 927 } 928 929 if autoffset != 0 { 930 to := p.To // Keep To attached to RET for retjmp below 931 p.To = obj.Addr{} 932 if bpsize > 0 { 933 // Restore caller's BP 934 p.As = AMOVQ 935 936 p.From.Type = obj.TYPE_MEM 937 p.From.Reg = REG_SP 938 p.From.Scale = 1 939 p.From.Offset = int64(autoffset) - int64(bpsize) 940 p.To.Type = obj.TYPE_REG 941 p.To.Reg = REG_BP 942 p = obj.Appendp(p, newprog) 943 } 944 945 p.As = AADJSP 946 p.From.Type = obj.TYPE_CONST 947 p.From.Offset = int64(-autoffset) 948 p.Spadj = -autoffset 949 p = obj.Appendp(p, newprog) 950 p.As = obj.ARET 951 p.To = to 952 953 // If there are instructions following 954 // this ARET, they come from a branch 955 // with the same stackframe, so undo 956 // the cleanup. 957 p.Spadj = +autoffset 958 } 959 960 if p.To.Sym != nil { // retjmp 961 p.As = obj.AJMP 962 } 963 } 964 } 965 966 func isZeroArgRuntimeCall(s *obj.LSym) bool { 967 if s == nil { 968 return false 969 } 970 switch s.Name { 971 case "runtime.panicindex", "runtime.panicslice", "runtime.panicdivide", "runtime.panicwrap": 972 return true 973 } 974 return false 975 } 976 977 func indir_cx(ctxt *obj.Link, a *obj.Addr) { 978 if ctxt.Headtype == objabi.Hnacl && ctxt.Arch.Family == sys.AMD64 { 979 a.Type = obj.TYPE_MEM 980 a.Reg = REG_R15 981 a.Index = REG_CX 982 a.Scale = 1 983 return 984 } 985 986 a.Type = obj.TYPE_MEM 987 a.Reg = REG_CX 988 } 989 990 // Append code to p to load g into cx. 991 // Overwrites p with the first instruction (no first appendp). 992 // Overwriting p is unusual but it lets use this in both the 993 // prologue (caller must call appendp first) and in the epilogue. 994 // Returns last new instruction. 995 func load_g_cx(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) *obj.Prog { 996 p.As = AMOVQ 997 if ctxt.Arch.PtrSize == 4 { 998 p.As = AMOVL 999 } 1000 p.From.Type = obj.TYPE_MEM 1001 p.From.Reg = REG_TLS 1002 p.From.Offset = 0 1003 p.To.Type = obj.TYPE_REG 1004 p.To.Reg = REG_CX 1005 1006 next := p.Link 1007 progedit(ctxt, p, newprog) 1008 for p.Link != next { 1009 p = p.Link 1010 } 1011 1012 if p.From.Index == REG_TLS { 1013 p.From.Scale = 2 1014 } 1015 1016 return p 1017 } 1018 1019 // Append code to p to check for stack split. 1020 // Appends to (does not overwrite) p. 1021 // Assumes g is in CX. 1022 // Returns last new instruction. 1023 func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) *obj.Prog { 1024 cmp := ACMPQ 1025 lea := ALEAQ 1026 mov := AMOVQ 1027 sub := ASUBQ 1028 1029 if ctxt.Headtype == objabi.Hnacl || ctxt.Arch.Family == sys.I386 { 1030 cmp = ACMPL 1031 lea = ALEAL 1032 mov = AMOVL 1033 sub = ASUBL 1034 } 1035 1036 var q1 *obj.Prog 1037 if framesize <= objabi.StackSmall { 1038 // small stack: SP <= stackguard 1039 // CMPQ SP, stackguard 1040 p = obj.Appendp(p, newprog) 1041 1042 p.As = cmp 1043 p.From.Type = obj.TYPE_REG 1044 p.From.Reg = REG_SP 1045 indir_cx(ctxt, &p.To) 1046 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1047 if cursym.CFunc() { 1048 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1049 } 1050 } else if framesize <= objabi.StackBig { 1051 // large stack: SP-framesize <= stackguard-StackSmall 1052 // LEAQ -xxx(SP), AX 1053 // CMPQ AX, stackguard 1054 p = obj.Appendp(p, newprog) 1055 1056 p.As = lea 1057 p.From.Type = obj.TYPE_MEM 1058 p.From.Reg = REG_SP 1059 p.From.Offset = -(int64(framesize) - objabi.StackSmall) 1060 p.To.Type = obj.TYPE_REG 1061 p.To.Reg = REG_AX 1062 1063 p = obj.Appendp(p, newprog) 1064 p.As = cmp 1065 p.From.Type = obj.TYPE_REG 1066 p.From.Reg = REG_AX 1067 indir_cx(ctxt, &p.To) 1068 p.To.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1069 if cursym.CFunc() { 1070 p.To.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1071 } 1072 } else { 1073 // Such a large stack we need to protect against wraparound. 1074 // If SP is close to zero: 1075 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 1076 // The +StackGuard on both sides is required to keep the left side positive: 1077 // SP is allowed to be slightly below stackguard. See stack.h. 1078 // 1079 // Preemption sets stackguard to StackPreempt, a very large value. 1080 // That breaks the math above, so we have to check for that explicitly. 1081 // MOVQ stackguard, CX 1082 // CMPQ CX, $StackPreempt 1083 // JEQ label-of-call-to-morestack 1084 // LEAQ StackGuard(SP), AX 1085 // SUBQ CX, AX 1086 // CMPQ AX, $(framesize+(StackGuard-StackSmall)) 1087 1088 p = obj.Appendp(p, newprog) 1089 1090 p.As = mov 1091 indir_cx(ctxt, &p.From) 1092 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 1093 if cursym.CFunc() { 1094 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 1095 } 1096 p.To.Type = obj.TYPE_REG 1097 p.To.Reg = REG_SI 1098 1099 p = obj.Appendp(p, newprog) 1100 p.As = cmp 1101 p.From.Type = obj.TYPE_REG 1102 p.From.Reg = REG_SI 1103 p.To.Type = obj.TYPE_CONST 1104 p.To.Offset = objabi.StackPreempt 1105 if ctxt.Arch.Family == sys.I386 { 1106 p.To.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1))) 1107 } 1108 1109 p = obj.Appendp(p, newprog) 1110 p.As = AJEQ 1111 p.To.Type = obj.TYPE_BRANCH 1112 q1 = p 1113 1114 p = obj.Appendp(p, newprog) 1115 p.As = lea 1116 p.From.Type = obj.TYPE_MEM 1117 p.From.Reg = REG_SP 1118 p.From.Offset = int64(objabi.StackGuard) 1119 p.To.Type = obj.TYPE_REG 1120 p.To.Reg = REG_AX 1121 1122 p = obj.Appendp(p, newprog) 1123 p.As = sub 1124 p.From.Type = obj.TYPE_REG 1125 p.From.Reg = REG_SI 1126 p.To.Type = obj.TYPE_REG 1127 p.To.Reg = REG_AX 1128 1129 p = obj.Appendp(p, newprog) 1130 p.As = cmp 1131 p.From.Type = obj.TYPE_REG 1132 p.From.Reg = REG_AX 1133 p.To.Type = obj.TYPE_CONST 1134 p.To.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall) 1135 } 1136 1137 // common 1138 jls := obj.Appendp(p, newprog) 1139 jls.As = AJLS 1140 jls.To.Type = obj.TYPE_BRANCH 1141 1142 var last *obj.Prog 1143 for last = cursym.Func.Text; last.Link != nil; last = last.Link { 1144 } 1145 1146 // Now we are at the end of the function, but logically 1147 // we are still in function prologue. We need to fix the 1148 // SP data and PCDATA. 1149 spfix := obj.Appendp(last, newprog) 1150 spfix.As = obj.ANOP 1151 spfix.Spadj = -framesize 1152 1153 pcdata := ctxt.EmitEntryLiveness(cursym, spfix, newprog) 1154 1155 call := obj.Appendp(pcdata, newprog) 1156 call.Pos = cursym.Func.Text.Pos 1157 call.As = obj.ACALL 1158 call.To.Type = obj.TYPE_BRANCH 1159 call.To.Name = obj.NAME_EXTERN 1160 morestack := "runtime.morestack" 1161 switch { 1162 case cursym.CFunc(): 1163 morestack = "runtime.morestackc" 1164 case !cursym.Func.Text.From.Sym.NeedCtxt(): 1165 morestack = "runtime.morestack_noctxt" 1166 } 1167 call.To.Sym = ctxt.Lookup(morestack) 1168 // When compiling 386 code for dynamic linking, the call needs to be adjusted 1169 // to follow PIC rules. This in turn can insert more instructions, so we need 1170 // to keep track of the start of the call (where the jump will be to) and the 1171 // end (which following instructions are appended to). 1172 callend := call 1173 progedit(ctxt, callend, newprog) 1174 for ; callend.Link != nil; callend = callend.Link { 1175 progedit(ctxt, callend.Link, newprog) 1176 } 1177 1178 jmp := obj.Appendp(callend, newprog) 1179 jmp.As = obj.AJMP 1180 jmp.To.Type = obj.TYPE_BRANCH 1181 jmp.Pcond = cursym.Func.Text.Link 1182 jmp.Spadj = +framesize 1183 1184 jls.Pcond = call 1185 if q1 != nil { 1186 q1.Pcond = call 1187 } 1188 1189 return jls 1190 } 1191 1192 var unaryDst = map[obj.As]bool{ 1193 ABSWAPL: true, 1194 ABSWAPQ: true, 1195 ABSWAPW: true, 1196 ACLFLUSH: true, 1197 ACLFLUSHOPT: true, 1198 ACMPXCHG16B: true, 1199 ACMPXCHG8B: true, 1200 ADECB: true, 1201 ADECL: true, 1202 ADECQ: true, 1203 ADECW: true, 1204 AFBSTP: true, 1205 AFFREE: true, 1206 AFLDENV: true, 1207 AFSAVE: true, 1208 AFSTCW: true, 1209 AFSTENV: true, 1210 AFSTSW: true, 1211 AFXSAVE64: true, 1212 AFXSAVE: true, 1213 AINCB: true, 1214 AINCL: true, 1215 AINCQ: true, 1216 AINCW: true, 1217 ANEGB: true, 1218 ANEGL: true, 1219 ANEGQ: true, 1220 ANEGW: true, 1221 ANOTB: true, 1222 ANOTL: true, 1223 ANOTQ: true, 1224 ANOTW: true, 1225 APOPL: true, 1226 APOPQ: true, 1227 APOPW: true, 1228 ARDFSBASEL: true, 1229 ARDFSBASEQ: true, 1230 ARDGSBASEL: true, 1231 ARDGSBASEQ: true, 1232 ARDRANDL: true, 1233 ARDRANDQ: true, 1234 ARDRANDW: true, 1235 ARDSEEDL: true, 1236 ARDSEEDQ: true, 1237 ARDSEEDW: true, 1238 ASETCC: true, 1239 ASETCS: true, 1240 ASETEQ: true, 1241 ASETGE: true, 1242 ASETGT: true, 1243 ASETHI: true, 1244 ASETLE: true, 1245 ASETLS: true, 1246 ASETLT: true, 1247 ASETMI: true, 1248 ASETNE: true, 1249 ASETOC: true, 1250 ASETOS: true, 1251 ASETPC: true, 1252 ASETPL: true, 1253 ASETPS: true, 1254 ASGDT: true, 1255 ASIDT: true, 1256 ASLDTL: true, 1257 ASLDTQ: true, 1258 ASLDTW: true, 1259 ASMSWL: true, 1260 ASMSWQ: true, 1261 ASMSWW: true, 1262 ASTMXCSR: true, 1263 ASTRL: true, 1264 ASTRQ: true, 1265 ASTRW: true, 1266 AXSAVE64: true, 1267 AXSAVE: true, 1268 AXSAVEC64: true, 1269 AXSAVEC: true, 1270 AXSAVEOPT64: true, 1271 AXSAVEOPT: true, 1272 AXSAVES64: true, 1273 AXSAVES: true, 1274 } 1275 1276 var Linkamd64 = obj.LinkArch{ 1277 Arch: sys.ArchAMD64, 1278 Init: instinit, 1279 Preprocess: preprocess, 1280 Assemble: span6, 1281 Progedit: progedit, 1282 UnaryDst: unaryDst, 1283 DWARFRegisters: AMD64DWARFRegisters, 1284 } 1285 1286 var Linkamd64p32 = obj.LinkArch{ 1287 Arch: sys.ArchAMD64P32, 1288 Init: instinit, 1289 Preprocess: preprocess, 1290 Assemble: span6, 1291 Progedit: progedit, 1292 UnaryDst: unaryDst, 1293 DWARFRegisters: AMD64DWARFRegisters, 1294 } 1295 1296 var Link386 = obj.LinkArch{ 1297 Arch: sys.Arch386, 1298 Init: instinit, 1299 Preprocess: preprocess, 1300 Assemble: span6, 1301 Progedit: progedit, 1302 UnaryDst: unaryDst, 1303 DWARFRegisters: X86DWARFRegisters, 1304 }