Home | History | Annotate | Download | only in s390x
      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 (a] 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 (a] 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 	"cmd/internal/obj"
     34 	"cmd/internal/sys"
     35 	"fmt"
     36 	"math"
     37 )
     38 
     39 func progedit(ctxt *obj.Link, p *obj.Prog) {
     40 	p.From.Class = 0
     41 	p.To.Class = 0
     42 
     43 	// Rewrite BR/BL to symbol as TYPE_BRANCH.
     44 	switch p.As {
     45 	case ABR,
     46 		ABL,
     47 		obj.ARET,
     48 		obj.ADUFFZERO,
     49 		obj.ADUFFCOPY:
     50 		if p.To.Sym != nil {
     51 			p.To.Type = obj.TYPE_BRANCH
     52 		}
     53 	}
     54 
     55 	// Rewrite float constants to values stored in memory unless they are +0.
     56 	switch p.As {
     57 	case AFMOVS:
     58 		if p.From.Type == obj.TYPE_FCONST {
     59 			f32 := float32(p.From.Val.(float64))
     60 			i32 := math.Float32bits(f32)
     61 			if i32 == 0 { // +0
     62 				break
     63 			}
     64 			literal := fmt.Sprintf("$f32.%08x", i32)
     65 			s := obj.Linklookup(ctxt, literal, 0)
     66 			s.Size = 4
     67 			p.From.Type = obj.TYPE_MEM
     68 			p.From.Sym = s
     69 			p.From.Sym.Set(obj.AttrLocal, true)
     70 			p.From.Name = obj.NAME_EXTERN
     71 			p.From.Offset = 0
     72 		}
     73 
     74 	case AFMOVD:
     75 		if p.From.Type == obj.TYPE_FCONST {
     76 			i64 := math.Float64bits(p.From.Val.(float64))
     77 			if i64 == 0 { // +0
     78 				break
     79 			}
     80 			literal := fmt.Sprintf("$f64.%016x", i64)
     81 			s := obj.Linklookup(ctxt, literal, 0)
     82 			s.Size = 8
     83 			p.From.Type = obj.TYPE_MEM
     84 			p.From.Sym = s
     85 			p.From.Sym.Set(obj.AttrLocal, true)
     86 			p.From.Name = obj.NAME_EXTERN
     87 			p.From.Offset = 0
     88 		}
     89 
     90 		// put constants not loadable by LOAD IMMEDIATE into memory
     91 	case AMOVD:
     92 		if p.From.Type == obj.TYPE_CONST {
     93 			val := p.From.Offset
     94 			if int64(int32(val)) != val &&
     95 				int64(uint32(val)) != val &&
     96 				int64(uint64(val)&(0xffffffff<<32)) != val {
     97 				literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
     98 				s := obj.Linklookup(ctxt, literal, 0)
     99 				s.Size = 8
    100 				p.From.Type = obj.TYPE_MEM
    101 				p.From.Sym = s
    102 				p.From.Sym.Set(obj.AttrLocal, true)
    103 				p.From.Name = obj.NAME_EXTERN
    104 				p.From.Offset = 0
    105 			}
    106 		}
    107 	}
    108 
    109 	// Rewrite SUB constants into ADD.
    110 	switch p.As {
    111 	case ASUBC:
    112 		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
    113 			p.From.Offset = -p.From.Offset
    114 			p.As = AADDC
    115 		}
    116 
    117 	case ASUB:
    118 		if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
    119 			p.From.Offset = -p.From.Offset
    120 			p.As = AADD
    121 		}
    122 	}
    123 
    124 	if ctxt.Flag_dynlink {
    125 		rewriteToUseGot(ctxt, p)
    126 	}
    127 }
    128 
    129 // Rewrite p, if necessary, to access global data via the global offset table.
    130 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
    131 	// At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
    132 	// assembly code.
    133 	if p.As == AEXRL {
    134 		return
    135 	}
    136 
    137 	// We only care about global data: NAME_EXTERN means a global
    138 	// symbol in the Go sense, and p.Sym.Local is true for a few
    139 	// internally defined symbols.
    140 	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    141 		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
    142 		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
    143 		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
    144 			ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
    145 		}
    146 		p.From.Type = obj.TYPE_MEM
    147 		p.From.Name = obj.NAME_GOTREF
    148 		q := p
    149 		if p.From.Offset != 0 {
    150 			q = obj.Appendp(ctxt, p)
    151 			q.As = AADD
    152 			q.From.Type = obj.TYPE_CONST
    153 			q.From.Offset = p.From.Offset
    154 			q.To = p.To
    155 			p.From.Offset = 0
    156 		}
    157 	}
    158 	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
    159 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    160 	}
    161 	var source *obj.Addr
    162 	// MOVD sym, Ry becomes MOVD sym@GOT, REGTMP; MOVD (REGTMP), Ry
    163 	// MOVD Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
    164 	// An addition may be inserted between the two MOVs if there is an offset.
    165 	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    166 		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    167 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    168 		}
    169 		source = &p.From
    170 	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    171 		source = &p.To
    172 	} else {
    173 		return
    174 	}
    175 	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    176 		return
    177 	}
    178 	if source.Sym.Type == obj.STLSBSS {
    179 		return
    180 	}
    181 	if source.Type != obj.TYPE_MEM {
    182 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    183 	}
    184 	p1 := obj.Appendp(ctxt, p)
    185 	p2 := obj.Appendp(ctxt, p1)
    186 
    187 	p1.As = AMOVD
    188 	p1.From.Type = obj.TYPE_MEM
    189 	p1.From.Sym = source.Sym
    190 	p1.From.Name = obj.NAME_GOTREF
    191 	p1.To.Type = obj.TYPE_REG
    192 	p1.To.Reg = REGTMP
    193 
    194 	p2.As = p.As
    195 	p2.From = p.From
    196 	p2.To = p.To
    197 	if p.From.Name == obj.NAME_EXTERN {
    198 		p2.From.Reg = REGTMP
    199 		p2.From.Name = obj.NAME_NONE
    200 		p2.From.Sym = nil
    201 	} else if p.To.Name == obj.NAME_EXTERN {
    202 		p2.To.Reg = REGTMP
    203 		p2.To.Name = obj.NAME_NONE
    204 		p2.To.Sym = nil
    205 	} else {
    206 		return
    207 	}
    208 	obj.Nopout(p)
    209 }
    210 
    211 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    212 	// TODO(minux): add morestack short-cuts with small fixed frame-size.
    213 	ctxt.Cursym = cursym
    214 
    215 	if cursym.Text == nil || cursym.Text.Link == nil {
    216 		return
    217 	}
    218 
    219 	p := cursym.Text
    220 	textstksiz := p.To.Offset
    221 	if textstksiz == -8 {
    222 		// Compatibility hack.
    223 		p.From3.Offset |= obj.NOFRAME
    224 		textstksiz = 0
    225 	}
    226 	if textstksiz%8 != 0 {
    227 		ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
    228 	}
    229 	if p.From3.Offset&obj.NOFRAME != 0 {
    230 		if textstksiz != 0 {
    231 			ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
    232 		}
    233 	}
    234 
    235 	cursym.Args = p.To.Val.(int32)
    236 	cursym.Locals = int32(textstksiz)
    237 
    238 	/*
    239 	 * find leaf subroutines
    240 	 * strip NOPs
    241 	 * expand RET
    242 	 * expand BECOME pseudo
    243 	 */
    244 	if ctxt.Debugvlog != 0 {
    245 		ctxt.Logf("%5.2f noops\n", obj.Cputime())
    246 	}
    247 
    248 	var q *obj.Prog
    249 	var q1 *obj.Prog
    250 	for p := cursym.Text; p != nil; p = p.Link {
    251 		switch p.As {
    252 		/* too hard, just leave alone */
    253 		case obj.ATEXT:
    254 			q = p
    255 
    256 			p.Mark |= LABEL | LEAF | SYNC
    257 			if p.Link != nil {
    258 				p.Link.Mark |= LABEL
    259 			}
    260 
    261 		case ASYNC,
    262 			AWORD:
    263 			q = p
    264 			p.Mark |= LABEL | SYNC
    265 			continue
    266 
    267 		case AMOVW, AMOVWZ, AMOVD:
    268 			q = p
    269 			if p.From.Reg >= REG_RESERVED || p.To.Reg >= REG_RESERVED {
    270 				p.Mark |= LABEL | SYNC
    271 			}
    272 			continue
    273 
    274 		case AFABS,
    275 			AFADD,
    276 			AFDIV,
    277 			AFMADD,
    278 			AFMOVD,
    279 			AFMOVS,
    280 			AFMSUB,
    281 			AFMUL,
    282 			AFNABS,
    283 			AFNEG,
    284 			AFNMADD,
    285 			AFNMSUB,
    286 			ALEDBR,
    287 			ALDEBR,
    288 			AFSUB:
    289 			q = p
    290 
    291 			p.Mark |= FLOAT
    292 			continue
    293 
    294 		case ABL,
    295 			ABCL,
    296 			obj.ADUFFZERO,
    297 			obj.ADUFFCOPY:
    298 			cursym.Text.Mark &^= LEAF
    299 			fallthrough
    300 
    301 		case ABC,
    302 			ABEQ,
    303 			ABGE,
    304 			ABGT,
    305 			ABLE,
    306 			ABLT,
    307 			ABLEU,
    308 			ABLTU,
    309 			ABNE,
    310 			ABR,
    311 			ABVC,
    312 			ABVS,
    313 			ACMPBEQ,
    314 			ACMPBGE,
    315 			ACMPBGT,
    316 			ACMPBLE,
    317 			ACMPBLT,
    318 			ACMPBNE,
    319 			ACMPUBEQ,
    320 			ACMPUBGE,
    321 			ACMPUBGT,
    322 			ACMPUBLE,
    323 			ACMPUBLT,
    324 			ACMPUBNE:
    325 			p.Mark |= BRANCH
    326 			q = p
    327 			q1 = p.Pcond
    328 			if q1 != nil {
    329 				for q1.As == obj.ANOP {
    330 					q1 = q1.Link
    331 					p.Pcond = q1
    332 				}
    333 
    334 				if q1.Mark&LEAF == 0 {
    335 					q1.Mark |= LABEL
    336 				}
    337 			} else {
    338 				p.Mark |= LABEL
    339 			}
    340 			q1 = p.Link
    341 			if q1 != nil {
    342 				q1.Mark |= LABEL
    343 			}
    344 			continue
    345 
    346 		case AFCMPO, AFCMPU:
    347 			q = p
    348 			p.Mark |= FCMP | FLOAT
    349 			continue
    350 
    351 		case obj.ARET:
    352 			q = p
    353 			if p.Link != nil {
    354 				p.Link.Mark |= LABEL
    355 			}
    356 			continue
    357 
    358 		case obj.ANOP:
    359 			q1 = p.Link
    360 			q.Link = q1 /* q is non-nop */
    361 			q1.Mark |= p.Mark
    362 			continue
    363 
    364 		default:
    365 			q = p
    366 			continue
    367 		}
    368 	}
    369 
    370 	autosize := int32(0)
    371 	var p1 *obj.Prog
    372 	var p2 *obj.Prog
    373 	var pLast *obj.Prog
    374 	var pPre *obj.Prog
    375 	var pPreempt *obj.Prog
    376 	wasSplit := false
    377 	for p := cursym.Text; p != nil; p = p.Link {
    378 		pLast = p
    379 		switch p.As {
    380 		case obj.ATEXT:
    381 			autosize = int32(textstksiz)
    382 
    383 			if p.Mark&LEAF != 0 && autosize == 0 {
    384 				// A leaf function with no locals has no frame.
    385 				p.From3.Offset |= obj.NOFRAME
    386 			}
    387 
    388 			if p.From3.Offset&obj.NOFRAME == 0 {
    389 				// If there is a stack frame at all, it includes
    390 				// space to save the LR.
    391 				autosize += int32(ctxt.FixedFrameSize())
    392 			}
    393 
    394 			if p.Mark&LEAF != 0 && autosize < obj.StackSmall {
    395 				// A leaf function with a small stack can be marked
    396 				// NOSPLIT, avoiding a stack check.
    397 				p.From3.Offset |= obj.NOSPLIT
    398 			}
    399 
    400 			p.To.Offset = int64(autosize)
    401 
    402 			q = p
    403 
    404 			if p.From3.Offset&obj.NOSPLIT == 0 {
    405 				p, pPreempt = stacksplitPre(ctxt, p, autosize) // emit pre part of split check
    406 				pPre = p
    407 				wasSplit = true //need post part of split
    408 			}
    409 
    410 			if autosize != 0 {
    411 				// Make sure to save link register for non-empty frame, even if
    412 				// it is a leaf function, so that traceback works.
    413 				// Store link register before decrementing SP, so if a signal comes
    414 				// during the execution of the function prologue, the traceback
    415 				// code will not see a half-updated stack frame.
    416 				q = obj.Appendp(ctxt, p)
    417 				q.As = AMOVD
    418 				q.From.Type = obj.TYPE_REG
    419 				q.From.Reg = REG_LR
    420 				q.To.Type = obj.TYPE_MEM
    421 				q.To.Reg = REGSP
    422 				q.To.Offset = int64(-autosize)
    423 
    424 				q = obj.Appendp(ctxt, q)
    425 				q.As = AMOVD
    426 				q.From.Type = obj.TYPE_ADDR
    427 				q.From.Offset = int64(-autosize)
    428 				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
    429 				q.To.Type = obj.TYPE_REG
    430 				q.To.Reg = REGSP
    431 				q.Spadj = autosize
    432 			} else if cursym.Text.Mark&LEAF == 0 {
    433 				// A very few functions that do not return to their caller
    434 				// (e.g. gogo) are not identified as leaves but still have
    435 				// no frame.
    436 				cursym.Text.Mark |= LEAF
    437 			}
    438 
    439 			if cursym.Text.Mark&LEAF != 0 {
    440 				cursym.Set(obj.AttrLeaf, true)
    441 				break
    442 			}
    443 
    444 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    445 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    446 				//
    447 				//	MOVD g_panic(g), R3
    448 				//	CMP R3, $0
    449 				//	BEQ end
    450 				//	MOVD panic_argp(R3), R4
    451 				//	ADD $(autosize+8), R1, R5
    452 				//	CMP R4, R5
    453 				//	BNE end
    454 				//	ADD $8, R1, R6
    455 				//	MOVD R6, panic_argp(R3)
    456 				// end:
    457 				//	NOP
    458 				//
    459 				// The NOP is needed to give the jumps somewhere to land.
    460 				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
    461 
    462 				q = obj.Appendp(ctxt, q)
    463 
    464 				q.As = AMOVD
    465 				q.From.Type = obj.TYPE_MEM
    466 				q.From.Reg = REGG
    467 				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
    468 				q.To.Type = obj.TYPE_REG
    469 				q.To.Reg = REG_R3
    470 
    471 				q = obj.Appendp(ctxt, q)
    472 				q.As = ACMP
    473 				q.From.Type = obj.TYPE_REG
    474 				q.From.Reg = REG_R3
    475 				q.To.Type = obj.TYPE_CONST
    476 				q.To.Offset = 0
    477 
    478 				q = obj.Appendp(ctxt, q)
    479 				q.As = ABEQ
    480 				q.To.Type = obj.TYPE_BRANCH
    481 				p1 = q
    482 
    483 				q = obj.Appendp(ctxt, q)
    484 				q.As = AMOVD
    485 				q.From.Type = obj.TYPE_MEM
    486 				q.From.Reg = REG_R3
    487 				q.From.Offset = 0 // Panic.argp
    488 				q.To.Type = obj.TYPE_REG
    489 				q.To.Reg = REG_R4
    490 
    491 				q = obj.Appendp(ctxt, q)
    492 				q.As = AADD
    493 				q.From.Type = obj.TYPE_CONST
    494 				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
    495 				q.Reg = REGSP
    496 				q.To.Type = obj.TYPE_REG
    497 				q.To.Reg = REG_R5
    498 
    499 				q = obj.Appendp(ctxt, q)
    500 				q.As = ACMP
    501 				q.From.Type = obj.TYPE_REG
    502 				q.From.Reg = REG_R4
    503 				q.To.Type = obj.TYPE_REG
    504 				q.To.Reg = REG_R5
    505 
    506 				q = obj.Appendp(ctxt, q)
    507 				q.As = ABNE
    508 				q.To.Type = obj.TYPE_BRANCH
    509 				p2 = q
    510 
    511 				q = obj.Appendp(ctxt, q)
    512 				q.As = AADD
    513 				q.From.Type = obj.TYPE_CONST
    514 				q.From.Offset = ctxt.FixedFrameSize()
    515 				q.Reg = REGSP
    516 				q.To.Type = obj.TYPE_REG
    517 				q.To.Reg = REG_R6
    518 
    519 				q = obj.Appendp(ctxt, q)
    520 				q.As = AMOVD
    521 				q.From.Type = obj.TYPE_REG
    522 				q.From.Reg = REG_R6
    523 				q.To.Type = obj.TYPE_MEM
    524 				q.To.Reg = REG_R3
    525 				q.To.Offset = 0 // Panic.argp
    526 
    527 				q = obj.Appendp(ctxt, q)
    528 
    529 				q.As = obj.ANOP
    530 				p1.Pcond = q
    531 				p2.Pcond = q
    532 			}
    533 
    534 		case obj.ARET:
    535 			if p.From.Type == obj.TYPE_CONST {
    536 				ctxt.Diag("using BECOME (%v) is not supported!", p)
    537 				break
    538 			}
    539 
    540 			retTarget := p.To.Sym
    541 
    542 			if cursym.Text.Mark&LEAF != 0 {
    543 				if autosize == 0 {
    544 					p.As = ABR
    545 					p.From = obj.Addr{}
    546 					if retTarget == nil {
    547 						p.To.Type = obj.TYPE_REG
    548 						p.To.Reg = REG_LR
    549 					} else {
    550 						p.To.Type = obj.TYPE_BRANCH
    551 						p.To.Sym = retTarget
    552 					}
    553 					p.Mark |= BRANCH
    554 					break
    555 				}
    556 
    557 				p.As = AADD
    558 				p.From.Type = obj.TYPE_CONST
    559 				p.From.Offset = int64(autosize)
    560 				p.To.Type = obj.TYPE_REG
    561 				p.To.Reg = REGSP
    562 				p.Spadj = -autosize
    563 
    564 				q = obj.Appendp(ctxt, p)
    565 				q.As = ABR
    566 				q.From = obj.Addr{}
    567 				q.To.Type = obj.TYPE_REG
    568 				q.To.Reg = REG_LR
    569 				q.Mark |= BRANCH
    570 				q.Spadj = autosize
    571 				break
    572 			}
    573 
    574 			p.As = AMOVD
    575 			p.From.Type = obj.TYPE_MEM
    576 			p.From.Reg = REGSP
    577 			p.From.Offset = 0
    578 			p.To.Type = obj.TYPE_REG
    579 			p.To.Reg = REG_LR
    580 
    581 			q = p
    582 
    583 			if autosize != 0 {
    584 				q = obj.Appendp(ctxt, q)
    585 				q.As = AADD
    586 				q.From.Type = obj.TYPE_CONST
    587 				q.From.Offset = int64(autosize)
    588 				q.To.Type = obj.TYPE_REG
    589 				q.To.Reg = REGSP
    590 				q.Spadj = -autosize
    591 			}
    592 
    593 			q = obj.Appendp(ctxt, q)
    594 			q.As = ABR
    595 			q.From = obj.Addr{}
    596 			if retTarget == nil {
    597 				q.To.Type = obj.TYPE_REG
    598 				q.To.Reg = REG_LR
    599 			} else {
    600 				q.To.Type = obj.TYPE_BRANCH
    601 				q.To.Sym = retTarget
    602 			}
    603 			q.Mark |= BRANCH
    604 			q.Spadj = autosize
    605 
    606 		case AADD:
    607 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    608 				p.Spadj = int32(-p.From.Offset)
    609 			}
    610 		}
    611 	}
    612 	if wasSplit {
    613 		pLast = stacksplitPost(ctxt, pLast, pPre, pPreempt, autosize) // emit post part of split check
    614 	}
    615 }
    616 
    617 /*
    618 // instruction scheduling
    619 	if(debug['Q'] == 0)
    620 		return;
    621 
    622 	curtext = nil;
    623 	q = nil;	// p - 1
    624 	q1 = firstp;	// top of block
    625 	o = 0;		// count of instructions
    626 	for(p = firstp; p != nil; p = p1) {
    627 		p1 = p->link;
    628 		o++;
    629 		if(p->mark & NOSCHED){
    630 			if(q1 != p){
    631 				sched(q1, q);
    632 			}
    633 			for(; p != nil; p = p->link){
    634 				if(!(p->mark & NOSCHED))
    635 					break;
    636 				q = p;
    637 			}
    638 			p1 = p;
    639 			q1 = p;
    640 			o = 0;
    641 			continue;
    642 		}
    643 		if(p->mark & (LABEL|SYNC)) {
    644 			if(q1 != p)
    645 				sched(q1, q);
    646 			q1 = p;
    647 			o = 1;
    648 		}
    649 		if(p->mark & (BRANCH|SYNC)) {
    650 			sched(q1, p);
    651 			q1 = p1;
    652 			o = 0;
    653 		}
    654 		if(o >= NSCHED) {
    655 			sched(q1, p);
    656 			q1 = p1;
    657 			o = 0;
    658 		}
    659 		q = p;
    660 	}
    661 */
    662 func stacksplitPre(ctxt *obj.Link, p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
    663 	var q *obj.Prog
    664 
    665 	// MOVD	g_stackguard(g), R3
    666 	p = obj.Appendp(ctxt, p)
    667 
    668 	p.As = AMOVD
    669 	p.From.Type = obj.TYPE_MEM
    670 	p.From.Reg = REGG
    671 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
    672 	if ctxt.Cursym.CFunc() {
    673 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
    674 	}
    675 	p.To.Type = obj.TYPE_REG
    676 	p.To.Reg = REG_R3
    677 
    678 	q = nil
    679 	if framesize <= obj.StackSmall {
    680 		// small stack: SP < stackguard
    681 		//	CMP	stackguard, SP
    682 
    683 		//p.To.Type = obj.TYPE_REG
    684 		//p.To.Reg = REGSP
    685 
    686 		// q1: BLT	done
    687 
    688 		p = obj.Appendp(ctxt, p)
    689 		//q1 = p
    690 		p.From.Type = obj.TYPE_REG
    691 		p.From.Reg = REG_R3
    692 		p.Reg = REGSP
    693 		p.As = ACMPUBGE
    694 		p.To.Type = obj.TYPE_BRANCH
    695 		//p = obj.Appendp(ctxt, p)
    696 
    697 		//p.As = ACMPU
    698 		//p.From.Type = obj.TYPE_REG
    699 		//p.From.Reg = REG_R3
    700 		//p.To.Type = obj.TYPE_REG
    701 		//p.To.Reg = REGSP
    702 
    703 		//p = obj.Appendp(ctxt, p)
    704 		//p.As = ABGE
    705 		//p.To.Type = obj.TYPE_BRANCH
    706 
    707 	} else if framesize <= obj.StackBig {
    708 		// large stack: SP-framesize < stackguard-StackSmall
    709 		//	ADD $-framesize, SP, R4
    710 		//	CMP stackguard, R4
    711 		p = obj.Appendp(ctxt, p)
    712 
    713 		p.As = AADD
    714 		p.From.Type = obj.TYPE_CONST
    715 		p.From.Offset = int64(-framesize)
    716 		p.Reg = REGSP
    717 		p.To.Type = obj.TYPE_REG
    718 		p.To.Reg = REG_R4
    719 
    720 		p = obj.Appendp(ctxt, p)
    721 		p.From.Type = obj.TYPE_REG
    722 		p.From.Reg = REG_R3
    723 		p.Reg = REG_R4
    724 		p.As = ACMPUBGE
    725 		p.To.Type = obj.TYPE_BRANCH
    726 
    727 	} else {
    728 		// Such a large stack we need to protect against wraparound.
    729 		// If SP is close to zero:
    730 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
    731 		// The +StackGuard on both sides is required to keep the left side positive:
    732 		// SP is allowed to be slightly below stackguard. See stack.h.
    733 		//
    734 		// Preemption sets stackguard to StackPreempt, a very large value.
    735 		// That breaks the math above, so we have to check for that explicitly.
    736 		//	// stackguard is R3
    737 		//	CMP	R3, $StackPreempt
    738 		//	BEQ	label-of-call-to-morestack
    739 		//	ADD	$StackGuard, SP, R4
    740 		//	SUB	R3, R4
    741 		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
    742 		//	CMPUBGE	TEMP, R4
    743 		p = obj.Appendp(ctxt, p)
    744 
    745 		p.As = ACMP
    746 		p.From.Type = obj.TYPE_REG
    747 		p.From.Reg = REG_R3
    748 		p.To.Type = obj.TYPE_CONST
    749 		p.To.Offset = obj.StackPreempt
    750 
    751 		p = obj.Appendp(ctxt, p)
    752 		q = p
    753 		p.As = ABEQ
    754 		p.To.Type = obj.TYPE_BRANCH
    755 
    756 		p = obj.Appendp(ctxt, p)
    757 		p.As = AADD
    758 		p.From.Type = obj.TYPE_CONST
    759 		p.From.Offset = obj.StackGuard
    760 		p.Reg = REGSP
    761 		p.To.Type = obj.TYPE_REG
    762 		p.To.Reg = REG_R4
    763 
    764 		p = obj.Appendp(ctxt, p)
    765 		p.As = ASUB
    766 		p.From.Type = obj.TYPE_REG
    767 		p.From.Reg = REG_R3
    768 		p.To.Type = obj.TYPE_REG
    769 		p.To.Reg = REG_R4
    770 
    771 		p = obj.Appendp(ctxt, p)
    772 		p.As = AMOVD
    773 		p.From.Type = obj.TYPE_CONST
    774 		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
    775 		p.To.Type = obj.TYPE_REG
    776 		p.To.Reg = REGTMP
    777 
    778 		p = obj.Appendp(ctxt, p)
    779 		p.From.Type = obj.TYPE_REG
    780 		p.From.Reg = REGTMP
    781 		p.Reg = REG_R4
    782 		p.As = ACMPUBGE
    783 		p.To.Type = obj.TYPE_BRANCH
    784 	}
    785 
    786 	return p, q
    787 }
    788 
    789 func stacksplitPost(ctxt *obj.Link, p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
    790 	// Now we are at the end of the function, but logically
    791 	// we are still in function prologue. We need to fix the
    792 	// SP data and PCDATA.
    793 	spfix := obj.Appendp(ctxt, p)
    794 	spfix.As = obj.ANOP
    795 	spfix.Spadj = -framesize
    796 
    797 	pcdata := obj.Appendp(ctxt, spfix)
    798 	pcdata.Lineno = ctxt.Cursym.Text.Lineno
    799 	pcdata.Mode = ctxt.Cursym.Text.Mode
    800 	pcdata.As = obj.APCDATA
    801 	pcdata.From.Type = obj.TYPE_CONST
    802 	pcdata.From.Offset = obj.PCDATA_StackMapIndex
    803 	pcdata.To.Type = obj.TYPE_CONST
    804 	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
    805 
    806 	// MOVD	LR, R5
    807 	p = obj.Appendp(ctxt, pcdata)
    808 	pPre.Pcond = p
    809 	p.As = AMOVD
    810 	p.From.Type = obj.TYPE_REG
    811 	p.From.Reg = REG_LR
    812 	p.To.Type = obj.TYPE_REG
    813 	p.To.Reg = REG_R5
    814 	if pPreempt != nil {
    815 		pPreempt.Pcond = p
    816 	}
    817 
    818 	// BL	runtime.morestack(SB)
    819 	p = obj.Appendp(ctxt, p)
    820 
    821 	p.As = ABL
    822 	p.To.Type = obj.TYPE_BRANCH
    823 	if ctxt.Cursym.CFunc() {
    824 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
    825 	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
    826 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
    827 	} else {
    828 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
    829 	}
    830 
    831 	// BR	start
    832 	p = obj.Appendp(ctxt, p)
    833 
    834 	p.As = ABR
    835 	p.To.Type = obj.TYPE_BRANCH
    836 	p.Pcond = ctxt.Cursym.Text.Link
    837 	return p
    838 }
    839 
    840 var pc_cnt int64
    841 
    842 func follow(ctxt *obj.Link, s *obj.LSym) {
    843 	ctxt.Cursym = s
    844 
    845 	pc_cnt = 0
    846 	firstp := ctxt.NewProg()
    847 	lastp := firstp
    848 	xfol(ctxt, s.Text, &lastp)
    849 	lastp.Link = nil
    850 	s.Text = firstp.Link
    851 }
    852 
    853 func relinv(a obj.As) obj.As {
    854 	switch a {
    855 	case ABEQ:
    856 		return ABNE
    857 	case ABNE:
    858 		return ABEQ
    859 
    860 	case ABGE:
    861 		return ABLT
    862 	case ABLT:
    863 		return ABGE
    864 
    865 	case ABGT:
    866 		return ABLE
    867 	case ABLE:
    868 		return ABGT
    869 
    870 	case ABVC:
    871 		return ABVS
    872 	case ABVS:
    873 		return ABVC
    874 	}
    875 
    876 	return 0
    877 }
    878 
    879 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
    880 	var q *obj.Prog
    881 	var r *obj.Prog
    882 	var b obj.As
    883 
    884 	for p != nil {
    885 		a := p.As
    886 		if a == ABR {
    887 			q = p.Pcond
    888 			if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
    889 				p.Mark |= FOLL
    890 				(*last).Link = p
    891 				*last = p
    892 				(*last).Pc = pc_cnt
    893 				pc_cnt += 1
    894 				p = p.Link
    895 				xfol(ctxt, p, last)
    896 				p = q
    897 				if p != nil && p.Mark&FOLL == 0 {
    898 					continue
    899 				}
    900 				return
    901 			}
    902 
    903 			if q != nil {
    904 				p.Mark |= FOLL
    905 				p = q
    906 				if p.Mark&FOLL == 0 {
    907 					continue
    908 				}
    909 			}
    910 		}
    911 
    912 		if p.Mark&FOLL != 0 {
    913 			q = p
    914 			for i := 0; i < 4; i, q = i+1, q.Link {
    915 				if q == *last || (q.Mark&NOSCHED != 0) {
    916 					break
    917 				}
    918 				b = 0 /* set */
    919 				a = q.As
    920 				if a == obj.ANOP {
    921 					i--
    922 					continue
    923 				}
    924 				if a != ABR && a != obj.ARET {
    925 					if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
    926 						continue
    927 					}
    928 					b = relinv(a)
    929 					if b == 0 {
    930 						continue
    931 					}
    932 				}
    933 
    934 				for {
    935 					r = ctxt.NewProg()
    936 					*r = *p
    937 					if r.Mark&FOLL == 0 {
    938 						fmt.Printf("can't happen 1\n")
    939 					}
    940 					r.Mark |= FOLL
    941 					if p != q {
    942 						p = p.Link
    943 						(*last).Link = r
    944 						*last = r
    945 						(*last).Pc = pc_cnt
    946 						pc_cnt += 1
    947 						continue
    948 					}
    949 
    950 					(*last).Link = r
    951 					*last = r
    952 					(*last).Pc = pc_cnt
    953 					pc_cnt += 1
    954 					if a == ABR || a == obj.ARET {
    955 						return
    956 					}
    957 					r.As = b
    958 					r.Pcond = p.Link
    959 					r.Link = p.Pcond
    960 					if r.Link.Mark&FOLL == 0 {
    961 						xfol(ctxt, r.Link, last)
    962 					}
    963 					if r.Pcond.Mark&FOLL == 0 {
    964 						fmt.Printf("can't happen 2\n")
    965 					}
    966 					return
    967 				}
    968 			}
    969 
    970 			a = ABR
    971 			q = ctxt.NewProg()
    972 			q.As = a
    973 			q.Lineno = p.Lineno
    974 			q.To.Type = obj.TYPE_BRANCH
    975 			q.To.Offset = p.Pc
    976 			q.Pcond = p
    977 			p = q
    978 		}
    979 
    980 		p.Mark |= FOLL
    981 		(*last).Link = p
    982 		*last = p
    983 		(*last).Pc = pc_cnt
    984 		pc_cnt += 1
    985 
    986 		if a == ABR || a == obj.ARET {
    987 			if p.Mark&NOSCHED != 0 {
    988 				p = p.Link
    989 				continue
    990 			}
    991 
    992 			return
    993 		}
    994 
    995 		if p.Pcond != nil {
    996 			if a != ABL && p.Link != nil {
    997 				xfol(ctxt, p.Link, last)
    998 				p = p.Pcond
    999 				if p == nil || (p.Mark&FOLL != 0) {
   1000 					return
   1001 				}
   1002 				continue
   1003 			}
   1004 		}
   1005 
   1006 		p = p.Link
   1007 	}
   1008 }
   1009 
   1010 var unaryDst = map[obj.As]bool{
   1011 	ASTCK:  true,
   1012 	ASTCKC: true,
   1013 	ASTCKE: true,
   1014 	ASTCKF: true,
   1015 	ANEG:   true,
   1016 	ANEGW:  true,
   1017 	AVONE:  true,
   1018 	AVZERO: true,
   1019 }
   1020 
   1021 var Links390x = obj.LinkArch{
   1022 	Arch:       sys.ArchS390X,
   1023 	Preprocess: preprocess,
   1024 	Assemble:   spanz,
   1025 	Follow:     follow,
   1026 	Progedit:   progedit,
   1027 	UnaryDst:   unaryDst,
   1028 }
   1029