gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

objz.go (19056B)


      1 // Based on cmd/internal/obj/ppc64/obj9.go.
      2 //
      3 //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
      4 //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
      5 //	Portions Copyright © 1997-1999 Vita Nuova Limited
      6 //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
      7 //	Portions Copyright © 2004,2006 Bruce Ellis
      8 //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
      9 //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
     10 //	Portions Copyright © 2009 The Go Authors. All rights reserved.
     11 //
     12 // Permission is hereby granted, free of charge, to any person obtaining a copy
     13 // of this software and associated documentation files (the "Software"), to deal
     14 // in the Software without restriction, including without limitation the rights
     15 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     16 // copies of the Software, and to permit persons to whom the Software is
     17 // furnished to do so, subject to the following conditions:
     18 //
     19 // The above copyright notice and this permission notice shall be included in
     20 // all copies or substantial portions of the Software.
     21 //
     22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     23 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     24 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     25 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     26 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     27 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     28 // THE SOFTWARE.
     29 
     30 package s390x
     31 
     32 import (
     33 	"github.com/twitchyliquid64/golang-asm/obj"
     34 	"github.com/twitchyliquid64/golang-asm/objabi"
     35 	"github.com/twitchyliquid64/golang-asm/sys"
     36 	"math"
     37 )
     38 
     39 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
     40 	p.From.Class = 0
     41 	p.To.Class = 0
     42 
     43 	c := ctxtz{ctxt: ctxt, newprog: newprog}
     44 
     45 	// Rewrite BR/BL to symbol as TYPE_BRANCH.
     46 	switch p.As {
     47 	case ABR, ABL, obj.ARET, obj.ADUFFZERO, obj.ADUFFCOPY:
     48 		if p.To.Sym != nil {
     49 			p.To.Type = obj.TYPE_BRANCH
     50 		}
     51 	}
     52 
     53 	// Rewrite float constants to values stored in memory unless they are +0.
     54 	switch p.As {
     55 	case AFMOVS:
     56 		if p.From.Type == obj.TYPE_FCONST {
     57 			f32 := float32(p.From.Val.(float64))
     58 			if math.Float32bits(f32) == 0 { // +0
     59 				break
     60 			}
     61 			p.From.Type = obj.TYPE_MEM
     62 			p.From.Sym = ctxt.Float32Sym(f32)
     63 			p.From.Name = obj.NAME_EXTERN
     64 			p.From.Offset = 0
     65 		}
     66 
     67 	case AFMOVD:
     68 		if p.From.Type == obj.TYPE_FCONST {
     69 			f64 := p.From.Val.(float64)
     70 			if math.Float64bits(f64) == 0 { // +0
     71 				break
     72 			}
     73 			p.From.Type = obj.TYPE_MEM
     74 			p.From.Sym = ctxt.Float64Sym(f64)
     75 			p.From.Name = obj.NAME_EXTERN
     76 			p.From.Offset = 0
     77 		}
     78 
     79 		// put constants not loadable by LOAD IMMEDIATE into memory
     80 	case AMOVD:
     81 		if p.From.Type == obj.TYPE_CONST {
     82 			val := p.From.Offset
     83 			if int64(int32(val)) != val &&
     84 				int64(uint32(val)) != val &&
     85 				int64(uint64(val)&(0xffffffff<<32)) != val {
     86 				p.From.Type = obj.TYPE_MEM
     87 				p.From.Sym = ctxt.Int64Sym(p.From.Offset)
     88 				p.From.Name = obj.NAME_EXTERN
     89 				p.From.Offset = 0
     90 			}
     91 		}
     92 	}
     93 
     94 	// Rewrite SUB constants into ADD.
     95 	switch p.As {
     96 	case ASUBC:
     97 		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
     98 			p.From.Offset = -p.From.Offset
     99 			p.As = AADDC
    100 		}
    101 
    102 	case ASUB:
    103 		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
    104 			p.From.Offset = -p.From.Offset
    105 			p.As = AADD
    106 		}
    107 	}
    108 
    109 	if c.ctxt.Flag_dynlink {
    110 		c.rewriteToUseGot(p)
    111 	}
    112 }
    113 
    114 // Rewrite p, if necessary, to access global data via the global offset table.
    115 func (c *ctxtz) rewriteToUseGot(p *obj.Prog) {
    116 	// At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
    117 	// assembly code.
    118 	if p.As == AEXRL {
    119 		return
    120 	}
    121 
    122 	// We only care about global data: NAME_EXTERN means a global
    123 	// symbol in the Go sense, and p.Sym.Local is true for a few
    124 	// internally defined symbols.
    125 	// Rewrites must not clobber flags and therefore cannot use the
    126 	// ADD instruction.
    127 	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    128 		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
    129 		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx or REGTMP2; MOVD $<off>(Rx or REGTMP2), Rx
    130 		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
    131 			c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
    132 		}
    133 		p.From.Type = obj.TYPE_MEM
    134 		p.From.Name = obj.NAME_GOTREF
    135 		q := p
    136 		if p.From.Offset != 0 {
    137 			target := p.To.Reg
    138 			if target == REG_R0 {
    139 				// Cannot use R0 as input to address calculation.
    140 				// REGTMP might be used by the assembler.
    141 				p.To.Reg = REGTMP2
    142 			}
    143 			q = obj.Appendp(q, c.newprog)
    144 			q.As = AMOVD
    145 			q.From.Type = obj.TYPE_ADDR
    146 			q.From.Offset = p.From.Offset
    147 			q.From.Reg = p.To.Reg
    148 			q.To.Type = obj.TYPE_REG
    149 			q.To.Reg = target
    150 			p.From.Offset = 0
    151 		}
    152 	}
    153 	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
    154 		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
    155 	}
    156 	var source *obj.Addr
    157 	// MOVD sym, Ry becomes MOVD sym@GOT, REGTMP2; MOVD (REGTMP2), Ry
    158 	// MOVD Ry, sym becomes MOVD sym@GOT, REGTMP2; MOVD Ry, (REGTMP2)
    159 	// An addition may be inserted between the two MOVs if there is an offset.
    160 	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    161 		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    162 			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    163 		}
    164 		source = &p.From
    165 	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    166 		source = &p.To
    167 	} else {
    168 		return
    169 	}
    170 	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    171 		return
    172 	}
    173 	if source.Sym.Type == objabi.STLSBSS {
    174 		return
    175 	}
    176 	if source.Type != obj.TYPE_MEM {
    177 		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
    178 	}
    179 	p1 := obj.Appendp(p, c.newprog)
    180 	p2 := obj.Appendp(p1, c.newprog)
    181 
    182 	p1.As = AMOVD
    183 	p1.From.Type = obj.TYPE_MEM
    184 	p1.From.Sym = source.Sym
    185 	p1.From.Name = obj.NAME_GOTREF
    186 	p1.To.Type = obj.TYPE_REG
    187 	p1.To.Reg = REGTMP2
    188 
    189 	p2.As = p.As
    190 	p2.From = p.From
    191 	p2.To = p.To
    192 	if p.From.Name == obj.NAME_EXTERN {
    193 		p2.From.Reg = REGTMP2
    194 		p2.From.Name = obj.NAME_NONE
    195 		p2.From.Sym = nil
    196 	} else if p.To.Name == obj.NAME_EXTERN {
    197 		p2.To.Reg = REGTMP2
    198 		p2.To.Name = obj.NAME_NONE
    199 		p2.To.Sym = nil
    200 	} else {
    201 		return
    202 	}
    203 	obj.Nopout(p)
    204 }
    205 
    206 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
    207 	// TODO(minux): add morestack short-cuts with small fixed frame-size.
    208 	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
    209 		return
    210 	}
    211 
    212 	c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog}
    213 
    214 	p := c.cursym.Func.Text
    215 	textstksiz := p.To.Offset
    216 	if textstksiz == -8 {
    217 		// Compatibility hack.
    218 		p.From.Sym.Set(obj.AttrNoFrame, true)
    219 		textstksiz = 0
    220 	}
    221 	if textstksiz%8 != 0 {
    222 		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
    223 	}
    224 	if p.From.Sym.NoFrame() {
    225 		if textstksiz != 0 {
    226 			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
    227 		}
    228 	}
    229 
    230 	c.cursym.Func.Args = p.To.Val.(int32)
    231 	c.cursym.Func.Locals = int32(textstksiz)
    232 
    233 	/*
    234 	 * find leaf subroutines
    235 	 * strip NOPs
    236 	 * expand RET
    237 	 */
    238 
    239 	var q *obj.Prog
    240 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
    241 		switch p.As {
    242 		case obj.ATEXT:
    243 			q = p
    244 			p.Mark |= LEAF
    245 
    246 		case ABL, ABCL:
    247 			q = p
    248 			c.cursym.Func.Text.Mark &^= LEAF
    249 			fallthrough
    250 
    251 		case ABC,
    252 			ABRC,
    253 			ABEQ,
    254 			ABGE,
    255 			ABGT,
    256 			ABLE,
    257 			ABLT,
    258 			ABLEU,
    259 			ABLTU,
    260 			ABNE,
    261 			ABR,
    262 			ABVC,
    263 			ABVS,
    264 			ACRJ,
    265 			ACGRJ,
    266 			ACLRJ,
    267 			ACLGRJ,
    268 			ACIJ,
    269 			ACGIJ,
    270 			ACLIJ,
    271 			ACLGIJ,
    272 			ACMPBEQ,
    273 			ACMPBGE,
    274 			ACMPBGT,
    275 			ACMPBLE,
    276 			ACMPBLT,
    277 			ACMPBNE,
    278 			ACMPUBEQ,
    279 			ACMPUBGE,
    280 			ACMPUBGT,
    281 			ACMPUBLE,
    282 			ACMPUBLT,
    283 			ACMPUBNE:
    284 			q = p
    285 			p.Mark |= BRANCH
    286 
    287 		default:
    288 			q = p
    289 		}
    290 	}
    291 
    292 	autosize := int32(0)
    293 	var pLast *obj.Prog
    294 	var pPre *obj.Prog
    295 	var pPreempt *obj.Prog
    296 	wasSplit := false
    297 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
    298 		pLast = p
    299 		switch p.As {
    300 		case obj.ATEXT:
    301 			autosize = int32(textstksiz)
    302 
    303 			if p.Mark&LEAF != 0 && autosize == 0 {
    304 				// A leaf function with no locals has no frame.
    305 				p.From.Sym.Set(obj.AttrNoFrame, true)
    306 			}
    307 
    308 			if !p.From.Sym.NoFrame() {
    309 				// If there is a stack frame at all, it includes
    310 				// space to save the LR.
    311 				autosize += int32(c.ctxt.FixedFrameSize())
    312 			}
    313 
    314 			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
    315 				// A leaf function with a small stack can be marked
    316 				// NOSPLIT, avoiding a stack check.
    317 				p.From.Sym.Set(obj.AttrNoSplit, true)
    318 			}
    319 
    320 			p.To.Offset = int64(autosize)
    321 
    322 			q := p
    323 
    324 			if !p.From.Sym.NoSplit() {
    325 				p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
    326 				pPre = p
    327 				p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
    328 				wasSplit = true //need post part of split
    329 			}
    330 
    331 			if autosize != 0 {
    332 				// Make sure to save link register for non-empty frame, even if
    333 				// it is a leaf function, so that traceback works.
    334 				// Store link register before decrementing SP, so if a signal comes
    335 				// during the execution of the function prologue, the traceback
    336 				// code will not see a half-updated stack frame.
    337 				// This sequence is not async preemptible, as if we open a frame
    338 				// at the current SP, it will clobber the saved LR.
    339 				q = c.ctxt.StartUnsafePoint(p, c.newprog)
    340 
    341 				q = obj.Appendp(q, c.newprog)
    342 				q.As = AMOVD
    343 				q.From.Type = obj.TYPE_REG
    344 				q.From.Reg = REG_LR
    345 				q.To.Type = obj.TYPE_MEM
    346 				q.To.Reg = REGSP
    347 				q.To.Offset = int64(-autosize)
    348 
    349 				q = obj.Appendp(q, c.newprog)
    350 				q.As = AMOVD
    351 				q.From.Type = obj.TYPE_ADDR
    352 				q.From.Offset = int64(-autosize)
    353 				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
    354 				q.To.Type = obj.TYPE_REG
    355 				q.To.Reg = REGSP
    356 				q.Spadj = autosize
    357 
    358 				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
    359 			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
    360 				// A very few functions that do not return to their caller
    361 				// (e.g. gogo) are not identified as leaves but still have
    362 				// no frame.
    363 				c.cursym.Func.Text.Mark |= LEAF
    364 			}
    365 
    366 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    367 				c.cursym.Set(obj.AttrLeaf, true)
    368 				break
    369 			}
    370 
    371 			if c.cursym.Func.Text.From.Sym.Wrapper() {
    372 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    373 				//
    374 				//	MOVD g_panic(g), R3
    375 				//	CMP R3, $0
    376 				//	BEQ end
    377 				//	MOVD panic_argp(R3), R4
    378 				//	ADD $(autosize+8), R1, R5
    379 				//	CMP R4, R5
    380 				//	BNE end
    381 				//	ADD $8, R1, R6
    382 				//	MOVD R6, panic_argp(R3)
    383 				// end:
    384 				//	NOP
    385 				//
    386 				// The NOP is needed to give the jumps somewhere to land.
    387 				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
    388 
    389 				q = obj.Appendp(q, c.newprog)
    390 
    391 				q.As = AMOVD
    392 				q.From.Type = obj.TYPE_MEM
    393 				q.From.Reg = REGG
    394 				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
    395 				q.To.Type = obj.TYPE_REG
    396 				q.To.Reg = REG_R3
    397 
    398 				q = obj.Appendp(q, c.newprog)
    399 				q.As = ACMP
    400 				q.From.Type = obj.TYPE_REG
    401 				q.From.Reg = REG_R3
    402 				q.To.Type = obj.TYPE_CONST
    403 				q.To.Offset = 0
    404 
    405 				q = obj.Appendp(q, c.newprog)
    406 				q.As = ABEQ
    407 				q.To.Type = obj.TYPE_BRANCH
    408 				p1 := q
    409 
    410 				q = obj.Appendp(q, c.newprog)
    411 				q.As = AMOVD
    412 				q.From.Type = obj.TYPE_MEM
    413 				q.From.Reg = REG_R3
    414 				q.From.Offset = 0 // Panic.argp
    415 				q.To.Type = obj.TYPE_REG
    416 				q.To.Reg = REG_R4
    417 
    418 				q = obj.Appendp(q, c.newprog)
    419 				q.As = AADD
    420 				q.From.Type = obj.TYPE_CONST
    421 				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
    422 				q.Reg = REGSP
    423 				q.To.Type = obj.TYPE_REG
    424 				q.To.Reg = REG_R5
    425 
    426 				q = obj.Appendp(q, c.newprog)
    427 				q.As = ACMP
    428 				q.From.Type = obj.TYPE_REG
    429 				q.From.Reg = REG_R4
    430 				q.To.Type = obj.TYPE_REG
    431 				q.To.Reg = REG_R5
    432 
    433 				q = obj.Appendp(q, c.newprog)
    434 				q.As = ABNE
    435 				q.To.Type = obj.TYPE_BRANCH
    436 				p2 := q
    437 
    438 				q = obj.Appendp(q, c.newprog)
    439 				q.As = AADD
    440 				q.From.Type = obj.TYPE_CONST
    441 				q.From.Offset = c.ctxt.FixedFrameSize()
    442 				q.Reg = REGSP
    443 				q.To.Type = obj.TYPE_REG
    444 				q.To.Reg = REG_R6
    445 
    446 				q = obj.Appendp(q, c.newprog)
    447 				q.As = AMOVD
    448 				q.From.Type = obj.TYPE_REG
    449 				q.From.Reg = REG_R6
    450 				q.To.Type = obj.TYPE_MEM
    451 				q.To.Reg = REG_R3
    452 				q.To.Offset = 0 // Panic.argp
    453 
    454 				q = obj.Appendp(q, c.newprog)
    455 
    456 				q.As = obj.ANOP
    457 				p1.To.SetTarget(q)
    458 				p2.To.SetTarget(q)
    459 			}
    460 
    461 		case obj.ARET:
    462 			retTarget := p.To.Sym
    463 
    464 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    465 				if autosize == 0 {
    466 					p.As = ABR
    467 					p.From = obj.Addr{}
    468 					if retTarget == nil {
    469 						p.To.Type = obj.TYPE_REG
    470 						p.To.Reg = REG_LR
    471 					} else {
    472 						p.To.Type = obj.TYPE_BRANCH
    473 						p.To.Sym = retTarget
    474 					}
    475 					p.Mark |= BRANCH
    476 					break
    477 				}
    478 
    479 				p.As = AADD
    480 				p.From.Type = obj.TYPE_CONST
    481 				p.From.Offset = int64(autosize)
    482 				p.To.Type = obj.TYPE_REG
    483 				p.To.Reg = REGSP
    484 				p.Spadj = -autosize
    485 
    486 				q = obj.Appendp(p, c.newprog)
    487 				q.As = ABR
    488 				q.From = obj.Addr{}
    489 				q.To.Type = obj.TYPE_REG
    490 				q.To.Reg = REG_LR
    491 				q.Mark |= BRANCH
    492 				q.Spadj = autosize
    493 				break
    494 			}
    495 
    496 			p.As = AMOVD
    497 			p.From.Type = obj.TYPE_MEM
    498 			p.From.Reg = REGSP
    499 			p.From.Offset = 0
    500 			p.To.Type = obj.TYPE_REG
    501 			p.To.Reg = REG_LR
    502 
    503 			q = p
    504 
    505 			if autosize != 0 {
    506 				q = obj.Appendp(q, c.newprog)
    507 				q.As = AADD
    508 				q.From.Type = obj.TYPE_CONST
    509 				q.From.Offset = int64(autosize)
    510 				q.To.Type = obj.TYPE_REG
    511 				q.To.Reg = REGSP
    512 				q.Spadj = -autosize
    513 			}
    514 
    515 			q = obj.Appendp(q, c.newprog)
    516 			q.As = ABR
    517 			q.From = obj.Addr{}
    518 			if retTarget == nil {
    519 				q.To.Type = obj.TYPE_REG
    520 				q.To.Reg = REG_LR
    521 			} else {
    522 				q.To.Type = obj.TYPE_BRANCH
    523 				q.To.Sym = retTarget
    524 			}
    525 			q.Mark |= BRANCH
    526 			q.Spadj = autosize
    527 
    528 		case AADD:
    529 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    530 				p.Spadj = int32(-p.From.Offset)
    531 			}
    532 
    533 		case obj.AGETCALLERPC:
    534 			if cursym.Leaf() {
    535 				/* MOVD LR, Rd */
    536 				p.As = AMOVD
    537 				p.From.Type = obj.TYPE_REG
    538 				p.From.Reg = REG_LR
    539 			} else {
    540 				/* MOVD (RSP), Rd */
    541 				p.As = AMOVD
    542 				p.From.Type = obj.TYPE_MEM
    543 				p.From.Reg = REGSP
    544 			}
    545 		}
    546 	}
    547 	if wasSplit {
    548 		c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
    549 	}
    550 }
    551 
    552 func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
    553 	var q *obj.Prog
    554 
    555 	// MOVD	g_stackguard(g), R3
    556 	p = obj.Appendp(p, c.newprog)
    557 
    558 	p.As = AMOVD
    559 	p.From.Type = obj.TYPE_MEM
    560 	p.From.Reg = REGG
    561 	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
    562 	if c.cursym.CFunc() {
    563 		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
    564 	}
    565 	p.To.Type = obj.TYPE_REG
    566 	p.To.Reg = REG_R3
    567 
    568 	// Mark the stack bound check and morestack call async nonpreemptible.
    569 	// If we get preempted here, when resumed the preemption request is
    570 	// cleared, but we'll still call morestack, which will double the stack
    571 	// unnecessarily. See issue #35470.
    572 	p = c.ctxt.StartUnsafePoint(p, c.newprog)
    573 
    574 	q = nil
    575 	if framesize <= objabi.StackSmall {
    576 		// small stack: SP < stackguard
    577 		//	CMPUBGE	stackguard, SP, label-of-call-to-morestack
    578 
    579 		p = obj.Appendp(p, c.newprog)
    580 		//q1 = p
    581 		p.From.Type = obj.TYPE_REG
    582 		p.From.Reg = REG_R3
    583 		p.Reg = REGSP
    584 		p.As = ACMPUBGE
    585 		p.To.Type = obj.TYPE_BRANCH
    586 
    587 	} else if framesize <= objabi.StackBig {
    588 		// large stack: SP-framesize < stackguard-StackSmall
    589 		//	ADD $-(framesize-StackSmall), SP, R4
    590 		//	CMPUBGE stackguard, R4, label-of-call-to-morestack
    591 		p = obj.Appendp(p, c.newprog)
    592 
    593 		p.As = AADD
    594 		p.From.Type = obj.TYPE_CONST
    595 		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
    596 		p.Reg = REGSP
    597 		p.To.Type = obj.TYPE_REG
    598 		p.To.Reg = REG_R4
    599 
    600 		p = obj.Appendp(p, c.newprog)
    601 		p.From.Type = obj.TYPE_REG
    602 		p.From.Reg = REG_R3
    603 		p.Reg = REG_R4
    604 		p.As = ACMPUBGE
    605 		p.To.Type = obj.TYPE_BRANCH
    606 
    607 	} else {
    608 		// Such a large stack we need to protect against wraparound.
    609 		// If SP is close to zero:
    610 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
    611 		// The +StackGuard on both sides is required to keep the left side positive:
    612 		// SP is allowed to be slightly below stackguard. See stack.h.
    613 		//
    614 		// Preemption sets stackguard to StackPreempt, a very large value.
    615 		// That breaks the math above, so we have to check for that explicitly.
    616 		//	// stackguard is R3
    617 		//	CMP	R3, $StackPreempt
    618 		//	BEQ	label-of-call-to-morestack
    619 		//	ADD	$StackGuard, SP, R4
    620 		//	SUB	R3, R4
    621 		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
    622 		//	CMPUBGE	TEMP, R4, label-of-call-to-morestack
    623 		p = obj.Appendp(p, c.newprog)
    624 
    625 		p.As = ACMP
    626 		p.From.Type = obj.TYPE_REG
    627 		p.From.Reg = REG_R3
    628 		p.To.Type = obj.TYPE_CONST
    629 		p.To.Offset = objabi.StackPreempt
    630 
    631 		p = obj.Appendp(p, c.newprog)
    632 		q = p
    633 		p.As = ABEQ
    634 		p.To.Type = obj.TYPE_BRANCH
    635 
    636 		p = obj.Appendp(p, c.newprog)
    637 		p.As = AADD
    638 		p.From.Type = obj.TYPE_CONST
    639 		p.From.Offset = int64(objabi.StackGuard)
    640 		p.Reg = REGSP
    641 		p.To.Type = obj.TYPE_REG
    642 		p.To.Reg = REG_R4
    643 
    644 		p = obj.Appendp(p, c.newprog)
    645 		p.As = ASUB
    646 		p.From.Type = obj.TYPE_REG
    647 		p.From.Reg = REG_R3
    648 		p.To.Type = obj.TYPE_REG
    649 		p.To.Reg = REG_R4
    650 
    651 		p = obj.Appendp(p, c.newprog)
    652 		p.As = AMOVD
    653 		p.From.Type = obj.TYPE_CONST
    654 		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
    655 		p.To.Type = obj.TYPE_REG
    656 		p.To.Reg = REGTMP
    657 
    658 		p = obj.Appendp(p, c.newprog)
    659 		p.From.Type = obj.TYPE_REG
    660 		p.From.Reg = REGTMP
    661 		p.Reg = REG_R4
    662 		p.As = ACMPUBGE
    663 		p.To.Type = obj.TYPE_BRANCH
    664 	}
    665 
    666 	return p, q
    667 }
    668 
    669 func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
    670 	// Now we are at the end of the function, but logically
    671 	// we are still in function prologue. We need to fix the
    672 	// SP data and PCDATA.
    673 	spfix := obj.Appendp(p, c.newprog)
    674 	spfix.As = obj.ANOP
    675 	spfix.Spadj = -framesize
    676 
    677 	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
    678 	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
    679 
    680 	// MOVD	LR, R5
    681 	p = obj.Appendp(pcdata, c.newprog)
    682 	pPre.To.SetTarget(p)
    683 	p.As = AMOVD
    684 	p.From.Type = obj.TYPE_REG
    685 	p.From.Reg = REG_LR
    686 	p.To.Type = obj.TYPE_REG
    687 	p.To.Reg = REG_R5
    688 	if pPreempt != nil {
    689 		pPreempt.To.SetTarget(p)
    690 	}
    691 
    692 	// BL	runtime.morestack(SB)
    693 	p = obj.Appendp(p, c.newprog)
    694 
    695 	p.As = ABL
    696 	p.To.Type = obj.TYPE_BRANCH
    697 	if c.cursym.CFunc() {
    698 		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
    699 	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
    700 		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
    701 	} else {
    702 		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
    703 	}
    704 
    705 	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
    706 
    707 	// BR	start
    708 	p = obj.Appendp(p, c.newprog)
    709 
    710 	p.As = ABR
    711 	p.To.Type = obj.TYPE_BRANCH
    712 	p.To.SetTarget(c.cursym.Func.Text.Link)
    713 	return p
    714 }
    715 
    716 var unaryDst = map[obj.As]bool{
    717 	ASTCK:  true,
    718 	ASTCKC: true,
    719 	ASTCKE: true,
    720 	ASTCKF: true,
    721 	ANEG:   true,
    722 	ANEGW:  true,
    723 	AVONE:  true,
    724 	AVZERO: true,
    725 }
    726 
    727 var Links390x = obj.LinkArch{
    728 	Arch:           sys.ArchS390X,
    729 	Init:           buildop,
    730 	Preprocess:     preprocess,
    731 	Assemble:       spanz,
    732 	Progedit:       progedit,
    733 	UnaryDst:       unaryDst,
    734 	DWARFRegisters: S390XDWARFRegisters,
    735 }