Home | History | Annotate | Download | only in x86
      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 (a] 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 (a] 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 ctxt.Arch.Regsize == 4 {
     43 		switch ctxt.Headtype {
     44 		case obj.Hlinux,
     45 			obj.Hnacl,
     46 			obj.Hplan9,
     47 			obj.Hwindows:
     48 			return false
     49 		}
     50 
     51 		return true
     52 	}
     53 
     54 	switch ctxt.Headtype {
     55 	case obj.Hplan9,
     56 		obj.Hwindows:
     57 		return false
     58 	case obj.Hlinux:
     59 		return ctxt.Flag_shared == 0
     60 	}
     61 
     62 	return true
     63 }
     64 
     65 func progedit(ctxt *obj.Link, p *obj.Prog) {
     66 	// Maintain information about code generation mode.
     67 	if ctxt.Mode == 0 {
     68 		ctxt.Mode = ctxt.Arch.Regsize * 8
     69 	}
     70 	p.Mode = int8(ctxt.Mode)
     71 
     72 	switch p.As {
     73 	case AMODE:
     74 		if p.From.Type == obj.TYPE_CONST || (p.From.Type == obj.TYPE_MEM && p.From.Reg == REG_NONE) {
     75 			switch int(p.From.Offset) {
     76 			case 16, 32, 64:
     77 				ctxt.Mode = int(p.From.Offset)
     78 			}
     79 		}
     80 		obj.Nopout(p)
     81 	}
     82 
     83 	// Thread-local storage references use the TLS pseudo-register.
     84 	// As a register, TLS refers to the thread-local storage base, and it
     85 	// can only be loaded into another register:
     86 	//
     87 	//         MOVQ TLS, AX
     88 	//
     89 	// An offset from the thread-local storage base is written off(reg)(TLS*1).
     90 	// Semantically it is off(reg), but the (TLS*1) annotation marks this as
     91 	// indexing from the loaded TLS base. This emits a relocation so that
     92 	// if the linker needs to adjust the offset, it can. For example:
     93 	//
     94 	//         MOVQ TLS, AX
     95 	//         MOVQ 0(AX)(TLS*1), CX // load g into CX
     96 	//
     97 	// On systems that support direct access to the TLS memory, this
     98 	// pair of instructions can be reduced to a direct TLS memory reference:
     99 	//
    100 	//         MOVQ 0(TLS), CX // load g into CX
    101 	//
    102 	// The 2-instruction and 1-instruction forms correspond to the two code
    103 	// sequences for loading a TLS variable in the local exec model given in "ELF
    104 	// Handling For Thread-Local Storage".
    105 	//
    106 	// We apply this rewrite on systems that support the 1-instruction form.
    107 	// The decision is made using only the operating system and the -shared flag,
    108 	// not the link mode. If some link modes on a particular operating system
    109 	// require the 2-instruction form, then all builds for that operating system
    110 	// will use the 2-instruction form, so that the link mode decision can be
    111 	// delayed to link time.
    112 	//
    113 	// In this way, all supported systems use identical instructions to
    114 	// access TLS, and they are rewritten appropriately first here in
    115 	// liblink and then finally using relocations in the linker.
    116 	//
    117 	// When -shared is passed, we leave the code in the 2-instruction form but
    118 	// assemble (and relocate) them in different ways to generate the initial
    119 	// exec code sequence. It's a bit of a fluke that this is possible without
    120 	// rewriting the instructions more comprehensively, and it only does because
    121 	// we only support a single TLS variable (g).
    122 
    123 	if canuse1insntls(ctxt) {
    124 		// Reduce 2-instruction sequence to 1-instruction sequence.
    125 		// Sequences like
    126 		//	MOVQ TLS, BX
    127 		//	... off(BX)(TLS*1) ...
    128 		// become
    129 		//	NOP
    130 		//	... off(TLS) ...
    131 		//
    132 		// TODO(rsc): Remove the Hsolaris special case. It exists only to
    133 		// guarantee we are producing byte-identical binaries as before this code.
    134 		// But it should be unnecessary.
    135 		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 {
    136 			obj.Nopout(p)
    137 		}
    138 		if p.From.Type == obj.TYPE_MEM && p.From.Index == REG_TLS && REG_AX <= p.From.Reg && p.From.Reg <= REG_R15 {
    139 			p.From.Reg = REG_TLS
    140 			p.From.Scale = 0
    141 			p.From.Index = REG_NONE
    142 		}
    143 
    144 		if p.To.Type == obj.TYPE_MEM && p.To.Index == REG_TLS && REG_AX <= p.To.Reg && p.To.Reg <= REG_R15 {
    145 			p.To.Reg = REG_TLS
    146 			p.To.Scale = 0
    147 			p.To.Index = REG_NONE
    148 		}
    149 	} else {
    150 		// load_g_cx, below, always inserts the 1-instruction sequence. Rewrite it
    151 		// as the 2-instruction sequence if necessary.
    152 		//	MOVQ 0(TLS), BX
    153 		// becomes
    154 		//	MOVQ TLS, BX
    155 		//	MOVQ 0(BX)(TLS*1), BX
    156 		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 {
    157 			q := obj.Appendp(ctxt, p)
    158 			q.As = p.As
    159 			q.From = p.From
    160 			q.From.Type = obj.TYPE_MEM
    161 			q.From.Reg = p.To.Reg
    162 			q.From.Index = REG_TLS
    163 			q.From.Scale = 2 // TODO: use 1
    164 			q.To = p.To
    165 			p.From.Type = obj.TYPE_REG
    166 			p.From.Reg = REG_TLS
    167 			p.From.Index = REG_NONE
    168 			p.From.Offset = 0
    169 		}
    170 	}
    171 
    172 	// TODO: Remove.
    173 	if ctxt.Headtype == obj.Hwindows && p.Mode == 64 || ctxt.Headtype == obj.Hplan9 {
    174 		if p.From.Scale == 1 && p.From.Index == REG_TLS {
    175 			p.From.Scale = 2
    176 		}
    177 		if p.To.Scale == 1 && p.To.Index == REG_TLS {
    178 			p.To.Scale = 2
    179 		}
    180 	}
    181 
    182 	// Rewrite 0 to $0 in 3rd argment to CMPPS etc.
    183 	// That's what the tables expect.
    184 	switch p.As {
    185 	case ACMPPD, ACMPPS, ACMPSD, ACMPSS:
    186 		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 {
    187 			p.To.Type = obj.TYPE_CONST
    188 		}
    189 	}
    190 
    191 	// Rewrite CALL/JMP/RET to symbol as TYPE_BRANCH.
    192 	switch p.As {
    193 	case obj.ACALL, obj.AJMP, obj.ARET:
    194 		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
    195 			p.To.Type = obj.TYPE_BRANCH
    196 		}
    197 	}
    198 
    199 	// Rewrite MOVL/MOVQ $XXX(FP/SP) as LEAL/LEAQ.
    200 	if p.From.Type == obj.TYPE_ADDR && (ctxt.Arch.Thechar == '6' || p.From.Name != obj.NAME_EXTERN && p.From.Name != obj.NAME_STATIC) {
    201 		switch p.As {
    202 		case AMOVL:
    203 			p.As = ALEAL
    204 			p.From.Type = obj.TYPE_MEM
    205 		case AMOVQ:
    206 			p.As = ALEAQ
    207 			p.From.Type = obj.TYPE_MEM
    208 		}
    209 	}
    210 
    211 	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
    212 		if p.From3 != nil {
    213 			nacladdr(ctxt, p, p.From3)
    214 		}
    215 		nacladdr(ctxt, p, &p.From)
    216 		nacladdr(ctxt, p, &p.To)
    217 	}
    218 
    219 	// Rewrite float constants to values stored in memory.
    220 	switch p.As {
    221 	// Convert AMOVSS $(0), Xx to AXORPS Xx, Xx
    222 	case AMOVSS:
    223 		if p.From.Type == obj.TYPE_FCONST {
    224 			if p.From.Val.(float64) == 0 {
    225 				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
    226 					p.As = AXORPS
    227 					p.From = p.To
    228 					break
    229 				}
    230 			}
    231 		}
    232 		fallthrough
    233 
    234 	case AFMOVF,
    235 		AFADDF,
    236 		AFSUBF,
    237 		AFSUBRF,
    238 		AFMULF,
    239 		AFDIVF,
    240 		AFDIVRF,
    241 		AFCOMF,
    242 		AFCOMFP,
    243 		AADDSS,
    244 		ASUBSS,
    245 		AMULSS,
    246 		ADIVSS,
    247 		ACOMISS,
    248 		AUCOMISS:
    249 		if p.From.Type == obj.TYPE_FCONST {
    250 			f32 := float32(p.From.Val.(float64))
    251 			i32 := math.Float32bits(f32)
    252 			literal := fmt.Sprintf("$f32.%08x", i32)
    253 			s := obj.Linklookup(ctxt, literal, 0)
    254 			p.From.Type = obj.TYPE_MEM
    255 			p.From.Name = obj.NAME_EXTERN
    256 			p.From.Sym = s
    257 			p.From.Sym.Local = true
    258 			p.From.Offset = 0
    259 		}
    260 
    261 	case AMOVSD:
    262 		// Convert AMOVSD $(0), Xx to AXORPS Xx, Xx
    263 		if p.From.Type == obj.TYPE_FCONST {
    264 			if p.From.Val.(float64) == 0 {
    265 				if p.To.Type == obj.TYPE_REG && REG_X0 <= p.To.Reg && p.To.Reg <= REG_X15 {
    266 					p.As = AXORPS
    267 					p.From = p.To
    268 					break
    269 				}
    270 			}
    271 		}
    272 		fallthrough
    273 
    274 	case AFMOVD,
    275 		AFADDD,
    276 		AFSUBD,
    277 		AFSUBRD,
    278 		AFMULD,
    279 		AFDIVD,
    280 		AFDIVRD,
    281 		AFCOMD,
    282 		AFCOMDP,
    283 		AADDSD,
    284 		ASUBSD,
    285 		AMULSD,
    286 		ADIVSD,
    287 		ACOMISD,
    288 		AUCOMISD:
    289 		if p.From.Type == obj.TYPE_FCONST {
    290 			i64 := math.Float64bits(p.From.Val.(float64))
    291 			literal := fmt.Sprintf("$f64.%016x", i64)
    292 			s := obj.Linklookup(ctxt, literal, 0)
    293 			p.From.Type = obj.TYPE_MEM
    294 			p.From.Name = obj.NAME_EXTERN
    295 			p.From.Sym = s
    296 			p.From.Sym.Local = true
    297 			p.From.Offset = 0
    298 		}
    299 	}
    300 
    301 	if ctxt.Flag_dynlink && (p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO) {
    302 		var sym *obj.LSym
    303 		if p.As == obj.ADUFFZERO {
    304 			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
    305 		} else {
    306 			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
    307 		}
    308 		offset := p.To.Offset
    309 		p.As = AMOVQ
    310 		p.From.Type = obj.TYPE_MEM
    311 		p.From.Name = obj.NAME_GOTREF
    312 		p.From.Sym = sym
    313 		p.To.Type = obj.TYPE_REG
    314 		p.To.Reg = REG_R15
    315 		p.To.Offset = 0
    316 		p.To.Sym = nil
    317 		p1 := obj.Appendp(ctxt, p)
    318 		p1.As = AADDQ
    319 		p1.From.Type = obj.TYPE_CONST
    320 		p1.From.Offset = offset
    321 		p1.To.Type = obj.TYPE_REG
    322 		p1.To.Reg = REG_R15
    323 		p2 := obj.Appendp(ctxt, p1)
    324 		p2.As = obj.ACALL
    325 		p2.To.Type = obj.TYPE_REG
    326 		p2.To.Reg = REG_R15
    327 	}
    328 
    329 	if ctxt.Flag_dynlink {
    330 		if p.As == ALEAQ && p.From.Type == obj.TYPE_MEM && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
    331 			p.As = AMOVQ
    332 			p.From.Type = obj.TYPE_ADDR
    333 		}
    334 		if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
    335 			if p.As != AMOVQ {
    336 				ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
    337 			}
    338 			if p.To.Type != obj.TYPE_REG {
    339 				ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
    340 			}
    341 			p.From.Type = obj.TYPE_MEM
    342 			p.From.Name = obj.NAME_GOTREF
    343 			if p.From.Offset != 0 {
    344 				q := obj.Appendp(ctxt, p)
    345 				q.As = AADDQ
    346 				q.From.Type = obj.TYPE_CONST
    347 				q.From.Offset = p.From.Offset
    348 				q.To = p.To
    349 				p.From.Offset = 0
    350 			}
    351 		}
    352 		if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
    353 			ctxt.Diag("don't know how to handle %v with -dynlink", p)
    354 		}
    355 		var source *obj.Addr
    356 		if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
    357 			if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
    358 				ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    359 			}
    360 			source = &p.From
    361 		} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
    362 			source = &p.To
    363 		} else {
    364 			return
    365 		}
    366 		if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    367 			return
    368 		}
    369 		if source.Type != obj.TYPE_MEM {
    370 			ctxt.Diag("don't know how to handle %v with -dynlink", p)
    371 		}
    372 		p1 := obj.Appendp(ctxt, p)
    373 		p2 := obj.Appendp(ctxt, p1)
    374 
    375 		p1.As = AMOVQ
    376 		p1.From.Type = obj.TYPE_MEM
    377 		p1.From.Sym = source.Sym
    378 		p1.From.Name = obj.NAME_GOTREF
    379 		p1.To.Type = obj.TYPE_REG
    380 		p1.To.Reg = REG_R15
    381 
    382 		p2.As = p.As
    383 		p2.From = p.From
    384 		p2.To = p.To
    385 		if p.From.Name == obj.NAME_EXTERN {
    386 			p2.From.Reg = REG_R15
    387 			p2.From.Name = obj.NAME_NONE
    388 			p2.From.Sym = nil
    389 		} else if p.To.Name == obj.NAME_EXTERN {
    390 			p2.To.Reg = REG_R15
    391 			p2.To.Name = obj.NAME_NONE
    392 			p2.To.Sym = nil
    393 		} else {
    394 			return
    395 		}
    396 		l := p.Link
    397 		l2 := p2.Link
    398 		*p = *p1
    399 		*p1 = *p2
    400 		p.Link = l
    401 		p1.Link = l2
    402 	}
    403 }
    404 
    405 func nacladdr(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
    406 	if p.As == ALEAL || p.As == ALEAQ {
    407 		return
    408 	}
    409 
    410 	if a.Reg == REG_BP {
    411 		ctxt.Diag("invalid address: %v", p)
    412 		return
    413 	}
    414 
    415 	if a.Reg == REG_TLS {
    416 		a.Reg = REG_BP
    417 	}
    418 	if a.Type == obj.TYPE_MEM && a.Name == obj.NAME_NONE {
    419 		switch a.Reg {
    420 		// all ok
    421 		case REG_BP, REG_SP, REG_R15:
    422 			break
    423 
    424 		default:
    425 			if a.Index != REG_NONE {
    426 				ctxt.Diag("invalid address %v", p)
    427 			}
    428 			a.Index = a.Reg
    429 			if a.Index != REG_NONE {
    430 				a.Scale = 1
    431 			}
    432 			a.Reg = REG_R15
    433 		}
    434 	}
    435 }
    436 
    437 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    438 	if ctxt.Tlsg == nil {
    439 		ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0)
    440 	}
    441 
    442 	if ctxt.Headtype == obj.Hplan9 && ctxt.Plan9privates == nil {
    443 		ctxt.Plan9privates = obj.Linklookup(ctxt, "_privates", 0)
    444 	}
    445 
    446 	ctxt.Cursym = cursym
    447 
    448 	if cursym.Text == nil || cursym.Text.Link == nil {
    449 		return
    450 	}
    451 
    452 	p := cursym.Text
    453 	autoffset := int32(p.To.Offset)
    454 	if autoffset < 0 {
    455 		autoffset = 0
    456 	}
    457 
    458 	var bpsize int
    459 	if p.Mode == 64 && obj.Framepointer_enabled != 0 && autoffset > 0 {
    460 		// Make room for to save a base pointer.  If autoffset == 0,
    461 		// this might do something special like a tail jump to
    462 		// another function, so in that case we omit this.
    463 		bpsize = ctxt.Arch.Ptrsize
    464 
    465 		autoffset += int32(bpsize)
    466 		p.To.Offset += int64(bpsize)
    467 	} else {
    468 		bpsize = 0
    469 	}
    470 
    471 	textarg := int64(p.To.Val.(int32))
    472 	cursym.Args = int32(textarg)
    473 	cursym.Locals = int32(p.To.Offset)
    474 
    475 	// TODO(rsc): Remove.
    476 	if p.Mode == 32 && cursym.Locals < 0 {
    477 		cursym.Locals = 0
    478 	}
    479 
    480 	// TODO(rsc): Remove 'p.Mode == 64 &&'.
    481 	if p.Mode == 64 && autoffset < obj.StackSmall && p.From3Offset()&obj.NOSPLIT == 0 {
    482 		for q := p; q != nil; q = q.Link {
    483 			if q.As == obj.ACALL {
    484 				goto noleaf
    485 			}
    486 			if (q.As == obj.ADUFFCOPY || q.As == obj.ADUFFZERO) && autoffset >= obj.StackSmall-8 {
    487 				goto noleaf
    488 			}
    489 		}
    490 
    491 		p.From3.Offset |= obj.NOSPLIT
    492 	noleaf:
    493 	}
    494 
    495 	if p.From3Offset()&obj.NOSPLIT == 0 || p.From3Offset()&obj.WRAPPER != 0 {
    496 		p = obj.Appendp(ctxt, p)
    497 		p = load_g_cx(ctxt, p) // load g into CX
    498 	}
    499 
    500 	if cursym.Text.From3Offset()&obj.NOSPLIT == 0 {
    501 		p = stacksplit(ctxt, p, autoffset, int32(textarg)) // emit split check
    502 	}
    503 
    504 	if autoffset != 0 {
    505 		if autoffset%int32(ctxt.Arch.Regsize) != 0 {
    506 			ctxt.Diag("unaligned stack size %d", autoffset)
    507 		}
    508 		p = obj.Appendp(ctxt, p)
    509 		p.As = AADJSP
    510 		p.From.Type = obj.TYPE_CONST
    511 		p.From.Offset = int64(autoffset)
    512 		p.Spadj = autoffset
    513 	} else {
    514 		// zero-byte stack adjustment.
    515 		// Insert a fake non-zero adjustment so that stkcheck can
    516 		// recognize the end of the stack-splitting prolog.
    517 		p = obj.Appendp(ctxt, p)
    518 
    519 		p.As = obj.ANOP
    520 		p.Spadj = int32(-ctxt.Arch.Ptrsize)
    521 		p = obj.Appendp(ctxt, p)
    522 		p.As = obj.ANOP
    523 		p.Spadj = int32(ctxt.Arch.Ptrsize)
    524 	}
    525 
    526 	deltasp := autoffset
    527 
    528 	if bpsize > 0 {
    529 		// Save caller's BP
    530 		p = obj.Appendp(ctxt, p)
    531 
    532 		p.As = AMOVQ
    533 		p.From.Type = obj.TYPE_REG
    534 		p.From.Reg = REG_BP
    535 		p.To.Type = obj.TYPE_MEM
    536 		p.To.Reg = REG_SP
    537 		p.To.Scale = 1
    538 		p.To.Offset = int64(autoffset) - int64(bpsize)
    539 
    540 		// Move current frame to BP
    541 		p = obj.Appendp(ctxt, p)
    542 
    543 		p.As = ALEAQ
    544 		p.From.Type = obj.TYPE_MEM
    545 		p.From.Reg = REG_SP
    546 		p.From.Scale = 1
    547 		p.From.Offset = int64(autoffset) - int64(bpsize)
    548 		p.To.Type = obj.TYPE_REG
    549 		p.To.Reg = REG_BP
    550 	}
    551 
    552 	if cursym.Text.From3Offset()&obj.WRAPPER != 0 {
    553 		// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    554 		//
    555 		//	MOVQ g_panic(CX), BX
    556 		//	TESTQ BX, BX
    557 		//	JEQ end
    558 		//	LEAQ (autoffset+8)(SP), DI
    559 		//	CMPQ panic_argp(BX), DI
    560 		//	JNE end
    561 		//	MOVQ SP, panic_argp(BX)
    562 		// end:
    563 		//	NOP
    564 		//
    565 		// The NOP is needed to give the jumps somewhere to land.
    566 		// It is a liblink NOP, not an x86 NOP: it encodes to 0 instruction bytes.
    567 
    568 		p = obj.Appendp(ctxt, p)
    569 
    570 		p.As = AMOVQ
    571 		p.From.Type = obj.TYPE_MEM
    572 		p.From.Reg = REG_CX
    573 		p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
    574 		p.To.Type = obj.TYPE_REG
    575 		p.To.Reg = REG_BX
    576 		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
    577 			p.As = AMOVL
    578 			p.From.Type = obj.TYPE_MEM
    579 			p.From.Reg = REG_R15
    580 			p.From.Scale = 1
    581 			p.From.Index = REG_CX
    582 		}
    583 		if p.Mode == 32 {
    584 			p.As = AMOVL
    585 		}
    586 
    587 		p = obj.Appendp(ctxt, p)
    588 		p.As = ATESTQ
    589 		p.From.Type = obj.TYPE_REG
    590 		p.From.Reg = REG_BX
    591 		p.To.Type = obj.TYPE_REG
    592 		p.To.Reg = REG_BX
    593 		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
    594 			p.As = ATESTL
    595 		}
    596 
    597 		p = obj.Appendp(ctxt, p)
    598 		p.As = AJEQ
    599 		p.To.Type = obj.TYPE_BRANCH
    600 		p1 := p
    601 
    602 		p = obj.Appendp(ctxt, p)
    603 		p.As = ALEAQ
    604 		p.From.Type = obj.TYPE_MEM
    605 		p.From.Reg = REG_SP
    606 		p.From.Offset = int64(autoffset) + int64(ctxt.Arch.Regsize)
    607 		p.To.Type = obj.TYPE_REG
    608 		p.To.Reg = REG_DI
    609 		if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
    610 			p.As = ALEAL
    611 		}
    612 
    613 		p = obj.Appendp(ctxt, p)
    614 		p.As = ACMPQ
    615 		p.From.Type = obj.TYPE_MEM
    616 		p.From.Reg = REG_BX
    617 		p.From.Offset = 0 // Panic.argp
    618 		p.To.Type = obj.TYPE_REG
    619 		p.To.Reg = REG_DI
    620 		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
    621 			p.As = ACMPL
    622 			p.From.Type = obj.TYPE_MEM
    623 			p.From.Reg = REG_R15
    624 			p.From.Scale = 1
    625 			p.From.Index = REG_BX
    626 		}
    627 		if p.Mode == 32 {
    628 			p.As = ACMPL
    629 		}
    630 
    631 		p = obj.Appendp(ctxt, p)
    632 		p.As = AJNE
    633 		p.To.Type = obj.TYPE_BRANCH
    634 		p2 := p
    635 
    636 		p = obj.Appendp(ctxt, p)
    637 		p.As = AMOVQ
    638 		p.From.Type = obj.TYPE_REG
    639 		p.From.Reg = REG_SP
    640 		p.To.Type = obj.TYPE_MEM
    641 		p.To.Reg = REG_BX
    642 		p.To.Offset = 0 // Panic.argp
    643 		if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
    644 			p.As = AMOVL
    645 			p.To.Type = obj.TYPE_MEM
    646 			p.To.Reg = REG_R15
    647 			p.To.Scale = 1
    648 			p.To.Index = REG_BX
    649 		}
    650 		if p.Mode == 32 {
    651 			p.As = AMOVL
    652 		}
    653 
    654 		p = obj.Appendp(ctxt, p)
    655 		p.As = obj.ANOP
    656 		p1.Pcond = p
    657 		p2.Pcond = p
    658 	}
    659 
    660 	if ctxt.Debugzerostack != 0 && autoffset != 0 && cursym.Text.From3.Offset&obj.NOSPLIT == 0 {
    661 		// 6l -Z means zero the stack frame on entry.
    662 		// This slows down function calls but can help avoid
    663 		// false positives in garbage collection.
    664 		p = obj.Appendp(ctxt, p)
    665 
    666 		p.As = AMOVQ
    667 		p.From.Type = obj.TYPE_REG
    668 		p.From.Reg = REG_SP
    669 		p.To.Type = obj.TYPE_REG
    670 		p.To.Reg = REG_DI
    671 		if p.Mode == 32 {
    672 			p.As = AMOVL
    673 		}
    674 
    675 		p = obj.Appendp(ctxt, p)
    676 		p.As = AMOVQ
    677 		p.From.Type = obj.TYPE_CONST
    678 		p.From.Offset = int64(autoffset) / int64(ctxt.Arch.Regsize)
    679 		p.To.Type = obj.TYPE_REG
    680 		p.To.Reg = REG_CX
    681 		if p.Mode == 32 {
    682 			p.As = AMOVL
    683 		}
    684 
    685 		p = obj.Appendp(ctxt, p)
    686 		p.As = AMOVQ
    687 		p.From.Type = obj.TYPE_CONST
    688 		p.From.Offset = 0
    689 		p.To.Type = obj.TYPE_REG
    690 		p.To.Reg = REG_AX
    691 		if p.Mode == 32 {
    692 			p.As = AMOVL
    693 		}
    694 
    695 		p = obj.Appendp(ctxt, p)
    696 		p.As = AREP
    697 
    698 		p = obj.Appendp(ctxt, p)
    699 		p.As = ASTOSQ
    700 		if p.Mode == 32 {
    701 			p.As = ASTOSL
    702 		}
    703 	}
    704 
    705 	var a int
    706 	var pcsize int
    707 	for ; p != nil; p = p.Link {
    708 		pcsize = int(p.Mode) / 8
    709 		a = int(p.From.Name)
    710 		if a == obj.NAME_AUTO {
    711 			p.From.Offset += int64(deltasp) - int64(bpsize)
    712 		}
    713 		if a == obj.NAME_PARAM {
    714 			p.From.Offset += int64(deltasp) + int64(pcsize)
    715 		}
    716 		if p.From3 != nil {
    717 			a = int(p.From3.Name)
    718 			if a == obj.NAME_AUTO {
    719 				p.From3.Offset += int64(deltasp) - int64(bpsize)
    720 			}
    721 			if a == obj.NAME_PARAM {
    722 				p.From3.Offset += int64(deltasp) + int64(pcsize)
    723 			}
    724 		}
    725 		a = int(p.To.Name)
    726 		if a == obj.NAME_AUTO {
    727 			p.To.Offset += int64(deltasp) - int64(bpsize)
    728 		}
    729 		if a == obj.NAME_PARAM {
    730 			p.To.Offset += int64(deltasp) + int64(pcsize)
    731 		}
    732 
    733 		switch p.As {
    734 		default:
    735 			continue
    736 
    737 		case APUSHL, APUSHFL:
    738 			deltasp += 4
    739 			p.Spadj = 4
    740 			continue
    741 
    742 		case APUSHQ, APUSHFQ:
    743 			deltasp += 8
    744 			p.Spadj = 8
    745 			continue
    746 
    747 		case APUSHW, APUSHFW:
    748 			deltasp += 2
    749 			p.Spadj = 2
    750 			continue
    751 
    752 		case APOPL, APOPFL:
    753 			deltasp -= 4
    754 			p.Spadj = -4
    755 			continue
    756 
    757 		case APOPQ, APOPFQ:
    758 			deltasp -= 8
    759 			p.Spadj = -8
    760 			continue
    761 
    762 		case APOPW, APOPFW:
    763 			deltasp -= 2
    764 			p.Spadj = -2
    765 			continue
    766 
    767 		case obj.ARET:
    768 			break
    769 		}
    770 
    771 		if autoffset != deltasp {
    772 			ctxt.Diag("unbalanced PUSH/POP")
    773 		}
    774 
    775 		if autoffset != 0 {
    776 			if bpsize > 0 {
    777 				// Restore caller's BP
    778 				p.As = AMOVQ
    779 
    780 				p.From.Type = obj.TYPE_MEM
    781 				p.From.Reg = REG_SP
    782 				p.From.Scale = 1
    783 				p.From.Offset = int64(autoffset) - int64(bpsize)
    784 				p.To.Type = obj.TYPE_REG
    785 				p.To.Reg = REG_BP
    786 				p = obj.Appendp(ctxt, p)
    787 			}
    788 
    789 			p.As = AADJSP
    790 			p.From.Type = obj.TYPE_CONST
    791 			p.From.Offset = int64(-autoffset)
    792 			p.Spadj = -autoffset
    793 			p = obj.Appendp(ctxt, p)
    794 			p.As = obj.ARET
    795 
    796 			// If there are instructions following
    797 			// this ARET, they come from a branch
    798 			// with the same stackframe, so undo
    799 			// the cleanup.
    800 			p.Spadj = +autoffset
    801 		}
    802 
    803 		if p.To.Sym != nil { // retjmp
    804 			p.As = obj.AJMP
    805 		}
    806 	}
    807 }
    808 
    809 func indir_cx(ctxt *obj.Link, p *obj.Prog, a *obj.Addr) {
    810 	if ctxt.Headtype == obj.Hnacl && p.Mode == 64 {
    811 		a.Type = obj.TYPE_MEM
    812 		a.Reg = REG_R15
    813 		a.Index = REG_CX
    814 		a.Scale = 1
    815 		return
    816 	}
    817 
    818 	a.Type = obj.TYPE_MEM
    819 	a.Reg = REG_CX
    820 }
    821 
    822 // Append code to p to load g into cx.
    823 // Overwrites p with the first instruction (no first appendp).
    824 // Overwriting p is unusual but it lets use this in both the
    825 // prologue (caller must call appendp first) and in the epilogue.
    826 // Returns last new instruction.
    827 func load_g_cx(ctxt *obj.Link, p *obj.Prog) *obj.Prog {
    828 	p.As = AMOVQ
    829 	if ctxt.Arch.Ptrsize == 4 {
    830 		p.As = AMOVL
    831 	}
    832 	p.From.Type = obj.TYPE_MEM
    833 	p.From.Reg = REG_TLS
    834 	p.From.Offset = 0
    835 	p.To.Type = obj.TYPE_REG
    836 	p.To.Reg = REG_CX
    837 
    838 	next := p.Link
    839 	progedit(ctxt, p)
    840 	for p.Link != next {
    841 		p = p.Link
    842 	}
    843 
    844 	if p.From.Index == REG_TLS {
    845 		p.From.Scale = 2
    846 	}
    847 
    848 	return p
    849 }
    850 
    851 // Append code to p to check for stack split.
    852 // Appends to (does not overwrite) p.
    853 // Assumes g is in CX.
    854 // Returns last new instruction.
    855 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32, textarg int32) *obj.Prog {
    856 	cmp := ACMPQ
    857 	lea := ALEAQ
    858 	mov := AMOVQ
    859 	sub := ASUBQ
    860 
    861 	if ctxt.Headtype == obj.Hnacl || p.Mode == 32 {
    862 		cmp = ACMPL
    863 		lea = ALEAL
    864 		mov = AMOVL
    865 		sub = ASUBL
    866 	}
    867 
    868 	var q1 *obj.Prog
    869 	if framesize <= obj.StackSmall {
    870 		// small stack: SP <= stackguard
    871 		//	CMPQ SP, stackguard
    872 		p = obj.Appendp(ctxt, p)
    873 
    874 		p.As = int16(cmp)
    875 		p.From.Type = obj.TYPE_REG
    876 		p.From.Reg = REG_SP
    877 		indir_cx(ctxt, p, &p.To)
    878 		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
    879 		if ctxt.Cursym.Cfunc != 0 {
    880 			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
    881 		}
    882 	} else if framesize <= obj.StackBig {
    883 		// large stack: SP-framesize <= stackguard-StackSmall
    884 		//	LEAQ -xxx(SP), AX
    885 		//	CMPQ AX, stackguard
    886 		p = obj.Appendp(ctxt, p)
    887 
    888 		p.As = int16(lea)
    889 		p.From.Type = obj.TYPE_MEM
    890 		p.From.Reg = REG_SP
    891 		p.From.Offset = -(int64(framesize) - obj.StackSmall)
    892 		p.To.Type = obj.TYPE_REG
    893 		p.To.Reg = REG_AX
    894 
    895 		p = obj.Appendp(ctxt, p)
    896 		p.As = int16(cmp)
    897 		p.From.Type = obj.TYPE_REG
    898 		p.From.Reg = REG_AX
    899 		indir_cx(ctxt, p, &p.To)
    900 		p.To.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
    901 		if ctxt.Cursym.Cfunc != 0 {
    902 			p.To.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
    903 		}
    904 	} else {
    905 		// Such a large stack we need to protect against wraparound.
    906 		// If SP is close to zero:
    907 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
    908 		// The +StackGuard on both sides is required to keep the left side positive:
    909 		// SP is allowed to be slightly below stackguard. See stack.h.
    910 		//
    911 		// Preemption sets stackguard to StackPreempt, a very large value.
    912 		// That breaks the math above, so we have to check for that explicitly.
    913 		//	MOVQ	stackguard, CX
    914 		//	CMPQ	CX, $StackPreempt
    915 		//	JEQ	label-of-call-to-morestack
    916 		//	LEAQ	StackGuard(SP), AX
    917 		//	SUBQ	CX, AX
    918 		//	CMPQ	AX, $(framesize+(StackGuard-StackSmall))
    919 
    920 		p = obj.Appendp(ctxt, p)
    921 
    922 		p.As = int16(mov)
    923 		indir_cx(ctxt, p, &p.From)
    924 		p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
    925 		if ctxt.Cursym.Cfunc != 0 {
    926 			p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
    927 		}
    928 		p.To.Type = obj.TYPE_REG
    929 		p.To.Reg = REG_SI
    930 
    931 		p = obj.Appendp(ctxt, p)
    932 		p.As = int16(cmp)
    933 		p.From.Type = obj.TYPE_REG
    934 		p.From.Reg = REG_SI
    935 		p.To.Type = obj.TYPE_CONST
    936 		p.To.Offset = obj.StackPreempt
    937 		if p.Mode == 32 {
    938 			p.To.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
    939 		}
    940 
    941 		p = obj.Appendp(ctxt, p)
    942 		p.As = AJEQ
    943 		p.To.Type = obj.TYPE_BRANCH
    944 		q1 = p
    945 
    946 		p = obj.Appendp(ctxt, p)
    947 		p.As = int16(lea)
    948 		p.From.Type = obj.TYPE_MEM
    949 		p.From.Reg = REG_SP
    950 		p.From.Offset = obj.StackGuard
    951 		p.To.Type = obj.TYPE_REG
    952 		p.To.Reg = REG_AX
    953 
    954 		p = obj.Appendp(ctxt, p)
    955 		p.As = int16(sub)
    956 		p.From.Type = obj.TYPE_REG
    957 		p.From.Reg = REG_SI
    958 		p.To.Type = obj.TYPE_REG
    959 		p.To.Reg = REG_AX
    960 
    961 		p = obj.Appendp(ctxt, p)
    962 		p.As = int16(cmp)
    963 		p.From.Type = obj.TYPE_REG
    964 		p.From.Reg = REG_AX
    965 		p.To.Type = obj.TYPE_CONST
    966 		p.To.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
    967 	}
    968 
    969 	// common
    970 	jls := obj.Appendp(ctxt, p)
    971 	jls.As = AJLS
    972 	jls.To.Type = obj.TYPE_BRANCH
    973 
    974 	var last *obj.Prog
    975 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
    976 	}
    977 
    978 	call := obj.Appendp(ctxt, last)
    979 	call.Lineno = ctxt.Cursym.Text.Lineno
    980 	call.Mode = ctxt.Cursym.Text.Mode
    981 	call.As = obj.ACALL
    982 	call.To.Type = obj.TYPE_BRANCH
    983 	morestack := "runtime.morestack"
    984 	switch {
    985 	case ctxt.Cursym.Cfunc != 0:
    986 		morestack = "runtime.morestackc"
    987 	case ctxt.Cursym.Text.From3Offset()&obj.NEEDCTXT == 0:
    988 		morestack = "runtime.morestack_noctxt"
    989 	}
    990 	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
    991 
    992 	jmp := obj.Appendp(ctxt, call)
    993 	jmp.As = obj.AJMP
    994 	jmp.To.Type = obj.TYPE_BRANCH
    995 	jmp.Pcond = ctxt.Cursym.Text.Link
    996 
    997 	jls.Pcond = call
    998 	if q1 != nil {
    999 		q1.Pcond = call
   1000 	}
   1001 
   1002 	return jls
   1003 }
   1004 
   1005 func follow(ctxt *obj.Link, s *obj.LSym) {
   1006 	ctxt.Cursym = s
   1007 
   1008 	firstp := ctxt.NewProg()
   1009 	lastp := firstp
   1010 	xfol(ctxt, s.Text, &lastp)
   1011 	lastp.Link = nil
   1012 	s.Text = firstp.Link
   1013 }
   1014 
   1015 func nofollow(a int) bool {
   1016 	switch a {
   1017 	case obj.AJMP,
   1018 		obj.ARET,
   1019 		AIRETL,
   1020 		AIRETQ,
   1021 		AIRETW,
   1022 		ARETFL,
   1023 		ARETFQ,
   1024 		ARETFW,
   1025 		obj.AUNDEF:
   1026 		return true
   1027 	}
   1028 
   1029 	return false
   1030 }
   1031 
   1032 func pushpop(a int) bool {
   1033 	switch a {
   1034 	case APUSHL,
   1035 		APUSHFL,
   1036 		APUSHQ,
   1037 		APUSHFQ,
   1038 		APUSHW,
   1039 		APUSHFW,
   1040 		APOPL,
   1041 		APOPFL,
   1042 		APOPQ,
   1043 		APOPFQ,
   1044 		APOPW,
   1045 		APOPFW:
   1046 		return true
   1047 	}
   1048 
   1049 	return false
   1050 }
   1051 
   1052 func relinv(a int16) int16 {
   1053 	switch a {
   1054 	case AJEQ:
   1055 		return AJNE
   1056 	case AJNE:
   1057 		return AJEQ
   1058 	case AJLE:
   1059 		return AJGT
   1060 	case AJLS:
   1061 		return AJHI
   1062 	case AJLT:
   1063 		return AJGE
   1064 	case AJMI:
   1065 		return AJPL
   1066 	case AJGE:
   1067 		return AJLT
   1068 	case AJPL:
   1069 		return AJMI
   1070 	case AJGT:
   1071 		return AJLE
   1072 	case AJHI:
   1073 		return AJLS
   1074 	case AJCS:
   1075 		return AJCC
   1076 	case AJCC:
   1077 		return AJCS
   1078 	case AJPS:
   1079 		return AJPC
   1080 	case AJPC:
   1081 		return AJPS
   1082 	case AJOS:
   1083 		return AJOC
   1084 	case AJOC:
   1085 		return AJOS
   1086 	}
   1087 
   1088 	log.Fatalf("unknown relation: %s", obj.Aconv(int(a)))
   1089 	return 0
   1090 }
   1091 
   1092 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   1093 	var q *obj.Prog
   1094 	var i int
   1095 	var a int
   1096 
   1097 loop:
   1098 	if p == nil {
   1099 		return
   1100 	}
   1101 	if p.As == obj.AJMP {
   1102 		q = p.Pcond
   1103 		if q != nil && q.As != obj.ATEXT {
   1104 			/* mark instruction as done and continue layout at target of jump */
   1105 			p.Mark = 1
   1106 
   1107 			p = q
   1108 			if p.Mark == 0 {
   1109 				goto loop
   1110 			}
   1111 		}
   1112 	}
   1113 
   1114 	if p.Mark != 0 {
   1115 		/*
   1116 		 * p goes here, but already used it elsewhere.
   1117 		 * copy up to 4 instructions or else branch to other copy.
   1118 		 */
   1119 		i = 0
   1120 		q = p
   1121 		for ; i < 4; i, q = i+1, q.Link {
   1122 			if q == nil {
   1123 				break
   1124 			}
   1125 			if q == *last {
   1126 				break
   1127 			}
   1128 			a = int(q.As)
   1129 			if a == obj.ANOP {
   1130 				i--
   1131 				continue
   1132 			}
   1133 
   1134 			if nofollow(a) || pushpop(a) {
   1135 				break // NOTE(rsc): arm does goto copy
   1136 			}
   1137 			if q.Pcond == nil || q.Pcond.Mark != 0 {
   1138 				continue
   1139 			}
   1140 			if a == obj.ACALL || a == ALOOP {
   1141 				continue
   1142 			}
   1143 			for {
   1144 				if p.As == obj.ANOP {
   1145 					p = p.Link
   1146 					continue
   1147 				}
   1148 
   1149 				q = obj.Copyp(ctxt, p)
   1150 				p = p.Link
   1151 				q.Mark = 1
   1152 				(*last).Link = q
   1153 				*last = q
   1154 				if int(q.As) != a || q.Pcond == nil || q.Pcond.Mark != 0 {
   1155 					continue
   1156 				}
   1157 
   1158 				q.As = relinv(q.As)
   1159 				p = q.Pcond
   1160 				q.Pcond = q.Link
   1161 				q.Link = p
   1162 				xfol(ctxt, q.Link, last)
   1163 				p = q.Link
   1164 				if p.Mark != 0 {
   1165 					return
   1166 				}
   1167 				goto loop
   1168 				/* */
   1169 			}
   1170 		}
   1171 		q = ctxt.NewProg()
   1172 		q.As = obj.AJMP
   1173 		q.Lineno = p.Lineno
   1174 		q.To.Type = obj.TYPE_BRANCH
   1175 		q.To.Offset = p.Pc
   1176 		q.Pcond = p
   1177 		p = q
   1178 	}
   1179 
   1180 	/* emit p */
   1181 	p.Mark = 1
   1182 
   1183 	(*last).Link = p
   1184 	*last = p
   1185 	a = int(p.As)
   1186 
   1187 	/* continue loop with what comes after p */
   1188 	if nofollow(a) {
   1189 		return
   1190 	}
   1191 	if p.Pcond != nil && a != obj.ACALL {
   1192 		/*
   1193 		 * some kind of conditional branch.
   1194 		 * recurse to follow one path.
   1195 		 * continue loop on the other.
   1196 		 */
   1197 		q = obj.Brchain(ctxt, p.Pcond)
   1198 		if q != nil {
   1199 			p.Pcond = q
   1200 		}
   1201 		q = obj.Brchain(ctxt, p.Link)
   1202 		if q != nil {
   1203 			p.Link = q
   1204 		}
   1205 		if p.From.Type == obj.TYPE_CONST {
   1206 			if p.From.Offset == 1 {
   1207 				/*
   1208 				 * expect conditional jump to be taken.
   1209 				 * rewrite so that's the fall-through case.
   1210 				 */
   1211 				p.As = relinv(int16(a))
   1212 
   1213 				q = p.Link
   1214 				p.Link = p.Pcond
   1215 				p.Pcond = q
   1216 			}
   1217 		} else {
   1218 			q = p.Link
   1219 			if q.Mark != 0 {
   1220 				if a != ALOOP {
   1221 					p.As = relinv(int16(a))
   1222 					p.Link = p.Pcond
   1223 					p.Pcond = q
   1224 				}
   1225 			}
   1226 		}
   1227 
   1228 		xfol(ctxt, p.Link, last)
   1229 		if p.Pcond.Mark != 0 {
   1230 			return
   1231 		}
   1232 		p = p.Pcond
   1233 		goto loop
   1234 	}
   1235 
   1236 	p = p.Link
   1237 	goto loop
   1238 }
   1239 
   1240 var unaryDst = map[int]bool{
   1241 	ABSWAPL:    true,
   1242 	ABSWAPQ:    true,
   1243 	ACMPXCHG8B: true,
   1244 	ADECB:      true,
   1245 	ADECL:      true,
   1246 	ADECQ:      true,
   1247 	ADECW:      true,
   1248 	AINCB:      true,
   1249 	AINCL:      true,
   1250 	AINCQ:      true,
   1251 	AINCW:      true,
   1252 	ANEGB:      true,
   1253 	ANEGL:      true,
   1254 	ANEGQ:      true,
   1255 	ANEGW:      true,
   1256 	ANOTB:      true,
   1257 	ANOTL:      true,
   1258 	ANOTQ:      true,
   1259 	ANOTW:      true,
   1260 	APOPL:      true,
   1261 	APOPQ:      true,
   1262 	APOPW:      true,
   1263 	ASETCC:     true,
   1264 	ASETCS:     true,
   1265 	ASETEQ:     true,
   1266 	ASETGE:     true,
   1267 	ASETGT:     true,
   1268 	ASETHI:     true,
   1269 	ASETLE:     true,
   1270 	ASETLS:     true,
   1271 	ASETLT:     true,
   1272 	ASETMI:     true,
   1273 	ASETNE:     true,
   1274 	ASETOC:     true,
   1275 	ASETOS:     true,
   1276 	ASETPC:     true,
   1277 	ASETPL:     true,
   1278 	ASETPS:     true,
   1279 	AFFREE:     true,
   1280 	AFLDENV:    true,
   1281 	AFSAVE:     true,
   1282 	AFSTCW:     true,
   1283 	AFSTENV:    true,
   1284 	AFSTSW:     true,
   1285 	AFXSAVE:    true,
   1286 	AFXSAVE64:  true,
   1287 	ASTMXCSR:   true,
   1288 }
   1289 
   1290 var Linkamd64 = obj.LinkArch{
   1291 	ByteOrder:  binary.LittleEndian,
   1292 	Name:       "amd64",
   1293 	Thechar:    '6',
   1294 	Preprocess: preprocess,
   1295 	Assemble:   span6,
   1296 	Follow:     follow,
   1297 	Progedit:   progedit,
   1298 	UnaryDst:   unaryDst,
   1299 	Minlc:      1,
   1300 	Ptrsize:    8,
   1301 	Regsize:    8,
   1302 }
   1303 
   1304 var Linkamd64p32 = obj.LinkArch{
   1305 	ByteOrder:  binary.LittleEndian,
   1306 	Name:       "amd64p32",
   1307 	Thechar:    '6',
   1308 	Preprocess: preprocess,
   1309 	Assemble:   span6,
   1310 	Follow:     follow,
   1311 	Progedit:   progedit,
   1312 	UnaryDst:   unaryDst,
   1313 	Minlc:      1,
   1314 	Ptrsize:    4,
   1315 	Regsize:    8,
   1316 }
   1317 
   1318 var Link386 = obj.LinkArch{
   1319 	ByteOrder:  binary.LittleEndian,
   1320 	Name:       "386",
   1321 	Thechar:    '8',
   1322 	Preprocess: preprocess,
   1323 	Assemble:   span6,
   1324 	Follow:     follow,
   1325 	Progedit:   progedit,
   1326 	UnaryDst:   unaryDst,
   1327 	Minlc:      1,
   1328 	Ptrsize:    4,
   1329 	Regsize:    4,
   1330 }
   1331