Home | History | Annotate | Download | only in mips
      1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
      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 mips
     31 
     32 import (
     33 	"cmd/internal/obj"
     34 	"cmd/internal/sys"
     35 	"encoding/binary"
     36 	"fmt"
     37 	"math"
     38 )
     39 
     40 func progedit(ctxt *obj.Link, p *obj.Prog) {
     41 	// Maintain information about code generation mode.
     42 	if ctxt.Mode == 0 {
     43 		switch ctxt.Arch.Family {
     44 		default:
     45 			ctxt.Diag("unsupported arch family")
     46 		case sys.MIPS:
     47 			ctxt.Mode = Mips32
     48 		case sys.MIPS64:
     49 			ctxt.Mode = Mips64
     50 		}
     51 	}
     52 
     53 	p.From.Class = 0
     54 	p.To.Class = 0
     55 
     56 	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
     57 	switch p.As {
     58 	case AJMP,
     59 		AJAL,
     60 		ARET,
     61 		obj.ADUFFZERO,
     62 		obj.ADUFFCOPY:
     63 		if p.To.Sym != nil {
     64 			p.To.Type = obj.TYPE_BRANCH
     65 		}
     66 	}
     67 
     68 	// Rewrite float constants to values stored in memory.
     69 	switch p.As {
     70 	case AMOVF:
     71 		if p.From.Type == obj.TYPE_FCONST {
     72 			f32 := float32(p.From.Val.(float64))
     73 			i32 := math.Float32bits(f32)
     74 			if i32 == 0 {
     75 				p.As = AMOVW
     76 				p.From.Type = obj.TYPE_REG
     77 				p.From.Reg = REGZERO
     78 				break
     79 			}
     80 			literal := fmt.Sprintf("$f32.%08x", i32)
     81 			s := obj.Linklookup(ctxt, literal, 0)
     82 			s.Size = 4
     83 			p.From.Type = obj.TYPE_MEM
     84 			p.From.Sym = s
     85 			p.From.Name = obj.NAME_EXTERN
     86 			p.From.Offset = 0
     87 		}
     88 
     89 	case AMOVD:
     90 		if p.From.Type == obj.TYPE_FCONST {
     91 			i64 := math.Float64bits(p.From.Val.(float64))
     92 			if i64 == 0 && ctxt.Mode&Mips64 != 0 {
     93 				p.As = AMOVV
     94 				p.From.Type = obj.TYPE_REG
     95 				p.From.Reg = REGZERO
     96 				break
     97 			}
     98 			literal := fmt.Sprintf("$f64.%016x", i64)
     99 			s := obj.Linklookup(ctxt, literal, 0)
    100 			s.Size = 8
    101 			p.From.Type = obj.TYPE_MEM
    102 			p.From.Sym = s
    103 			p.From.Name = obj.NAME_EXTERN
    104 			p.From.Offset = 0
    105 		}
    106 
    107 		// Put >32-bit constants in memory and load them
    108 	case AMOVV:
    109 		if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
    110 			literal := fmt.Sprintf("$i64.%016x", uint64(p.From.Offset))
    111 			s := obj.Linklookup(ctxt, literal, 0)
    112 			s.Size = 8
    113 			p.From.Type = obj.TYPE_MEM
    114 			p.From.Sym = s
    115 			p.From.Name = obj.NAME_EXTERN
    116 			p.From.Offset = 0
    117 		}
    118 	}
    119 
    120 	// Rewrite SUB constants into ADD.
    121 	switch p.As {
    122 	case ASUB:
    123 		if p.From.Type == obj.TYPE_CONST {
    124 			p.From.Offset = -p.From.Offset
    125 			p.As = AADD
    126 		}
    127 
    128 	case ASUBU:
    129 		if p.From.Type == obj.TYPE_CONST {
    130 			p.From.Offset = -p.From.Offset
    131 			p.As = AADDU
    132 		}
    133 
    134 	case ASUBV:
    135 		if p.From.Type == obj.TYPE_CONST {
    136 			p.From.Offset = -p.From.Offset
    137 			p.As = AADDV
    138 		}
    139 
    140 	case ASUBVU:
    141 		if p.From.Type == obj.TYPE_CONST {
    142 			p.From.Offset = -p.From.Offset
    143 			p.As = AADDVU
    144 		}
    145 	}
    146 }
    147 
    148 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    149 	// TODO(minux): add morestack short-cuts with small fixed frame-size.
    150 	ctxt.Cursym = cursym
    151 
    152 	// a switch for enabling/disabling instruction scheduling
    153 	nosched := true
    154 
    155 	if cursym.Text == nil || cursym.Text.Link == nil {
    156 		return
    157 	}
    158 
    159 	p := cursym.Text
    160 	textstksiz := p.To.Offset
    161 
    162 	cursym.Args = p.To.Val.(int32)
    163 	cursym.Locals = int32(textstksiz)
    164 
    165 	/*
    166 	 * find leaf subroutines
    167 	 * strip NOPs
    168 	 * expand RET
    169 	 * expand BECOME pseudo
    170 	 */
    171 	if ctxt.Debugvlog != 0 {
    172 		ctxt.Logf("%5.2f noops\n", obj.Cputime())
    173 	}
    174 
    175 	var q *obj.Prog
    176 	var q1 *obj.Prog
    177 	for p := cursym.Text; p != nil; p = p.Link {
    178 		switch p.As {
    179 		/* too hard, just leave alone */
    180 		case obj.ATEXT:
    181 			q = p
    182 
    183 			p.Mark |= LABEL | LEAF | SYNC
    184 			if p.Link != nil {
    185 				p.Link.Mark |= LABEL
    186 			}
    187 
    188 		/* too hard, just leave alone */
    189 		case AMOVW,
    190 			AMOVV:
    191 			q = p
    192 			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
    193 				p.Mark |= LABEL | SYNC
    194 				break
    195 			}
    196 			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
    197 				p.Mark |= LABEL | SYNC
    198 			}
    199 
    200 		/* too hard, just leave alone */
    201 		case ASYSCALL,
    202 			AWORD,
    203 			ATLBWR,
    204 			ATLBWI,
    205 			ATLBP,
    206 			ATLBR:
    207 			q = p
    208 			p.Mark |= LABEL | SYNC
    209 
    210 		case ANOR:
    211 			q = p
    212 			if p.To.Type == obj.TYPE_REG {
    213 				if p.To.Reg == REGZERO {
    214 					p.Mark |= LABEL | SYNC
    215 				}
    216 			}
    217 
    218 		case ABGEZAL,
    219 			ABLTZAL,
    220 			AJAL,
    221 			obj.ADUFFZERO,
    222 			obj.ADUFFCOPY:
    223 			cursym.Text.Mark &^= LEAF
    224 			fallthrough
    225 
    226 		case AJMP,
    227 			ABEQ,
    228 			ABGEZ,
    229 			ABGTZ,
    230 			ABLEZ,
    231 			ABLTZ,
    232 			ABNE,
    233 			ABFPT, ABFPF:
    234 			if p.As == ABFPT || p.As == ABFPF {
    235 				// We don't treat ABFPT and ABFPF as branches here,
    236 				// so that we will always fill nop (0x0) in their
    237 				// delay slot during assembly.
    238 				// This is to workaround a kernel FPU emulator bug
    239 				// where it uses the user stack to simulate the
    240 				// instruction in the delay slot if it's not 0x0,
    241 				// and somehow that leads to SIGSEGV when the kernel
    242 				// jump to the stack.
    243 				p.Mark |= SYNC
    244 			} else {
    245 				p.Mark |= BRANCH
    246 			}
    247 			q = p
    248 			q1 = p.Pcond
    249 			if q1 != nil {
    250 				for q1.As == obj.ANOP {
    251 					q1 = q1.Link
    252 					p.Pcond = q1
    253 				}
    254 
    255 				if q1.Mark&LEAF == 0 {
    256 					q1.Mark |= LABEL
    257 				}
    258 			}
    259 			//else {
    260 			//	p.Mark |= LABEL
    261 			//}
    262 			q1 = p.Link
    263 			if q1 != nil {
    264 				q1.Mark |= LABEL
    265 			}
    266 			continue
    267 
    268 		case ARET:
    269 			q = p
    270 			if p.Link != nil {
    271 				p.Link.Mark |= LABEL
    272 			}
    273 			continue
    274 
    275 		case obj.ANOP:
    276 			q1 = p.Link
    277 			q.Link = q1 /* q is non-nop */
    278 			q1.Mark |= p.Mark
    279 			continue
    280 
    281 		default:
    282 			q = p
    283 			continue
    284 		}
    285 	}
    286 
    287 	var mov, add obj.As
    288 	if ctxt.Mode&Mips64 != 0 {
    289 		add = AADDV
    290 		mov = AMOVV
    291 	} else {
    292 		add = AADDU
    293 		mov = AMOVW
    294 	}
    295 
    296 	autosize := int32(0)
    297 	var p1 *obj.Prog
    298 	var p2 *obj.Prog
    299 	for p := cursym.Text; p != nil; p = p.Link {
    300 		o := p.As
    301 		switch o {
    302 		case obj.ATEXT:
    303 			autosize = int32(textstksiz + ctxt.FixedFrameSize())
    304 			if (p.Mark&LEAF != 0) && autosize <= int32(ctxt.FixedFrameSize()) {
    305 				autosize = 0
    306 			} else if autosize&4 != 0 && ctxt.Mode&Mips64 != 0 {
    307 				autosize += 4
    308 			}
    309 
    310 			p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
    311 
    312 			if p.From3.Offset&obj.NOSPLIT == 0 {
    313 				p = stacksplit(ctxt, p, autosize) // emit split check
    314 			}
    315 
    316 			q = p
    317 
    318 			if autosize != 0 {
    319 				// Make sure to save link register for non-empty frame, even if
    320 				// it is a leaf function, so that traceback works.
    321 				// Store link register before decrement SP, so if a signal comes
    322 				// during the execution of the function prologue, the traceback
    323 				// code will not see a half-updated stack frame.
    324 				q = obj.Appendp(ctxt, q)
    325 				q.As = mov
    326 				q.Lineno = p.Lineno
    327 				q.From.Type = obj.TYPE_REG
    328 				q.From.Reg = REGLINK
    329 				q.To.Type = obj.TYPE_MEM
    330 				q.To.Offset = int64(-autosize)
    331 				q.To.Reg = REGSP
    332 
    333 				q = obj.Appendp(ctxt, q)
    334 				q.As = add
    335 				q.Lineno = p.Lineno
    336 				q.From.Type = obj.TYPE_CONST
    337 				q.From.Offset = int64(-autosize)
    338 				q.To.Type = obj.TYPE_REG
    339 				q.To.Reg = REGSP
    340 				q.Spadj = +autosize
    341 			} else if cursym.Text.Mark&LEAF == 0 {
    342 				if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
    343 					if ctxt.Debugvlog != 0 {
    344 						ctxt.Logf("save suppressed in: %s\n", cursym.Name)
    345 					}
    346 
    347 					cursym.Text.Mark |= LEAF
    348 				}
    349 			}
    350 
    351 			if cursym.Text.Mark&LEAF != 0 {
    352 				cursym.Set(obj.AttrLeaf, true)
    353 				break
    354 			}
    355 
    356 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    357 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    358 				//
    359 				//	MOV	g_panic(g), R1
    360 				//	BEQ	R1, end
    361 				//	MOV	panic_argp(R1), R2
    362 				//	ADD	$(autosize+FIXED_FRAME), R29, R3
    363 				//	BNE	R2, R3, end
    364 				//	ADD	$FIXED_FRAME, R29, R2
    365 				//	MOV	R2, panic_argp(R1)
    366 				// end:
    367 				//	NOP
    368 				//
    369 				// The NOP is needed to give the jumps somewhere to land.
    370 				// It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes.
    371 
    372 				q = obj.Appendp(ctxt, q)
    373 
    374 				q.As = mov
    375 				q.From.Type = obj.TYPE_MEM
    376 				q.From.Reg = REGG
    377 				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
    378 				q.To.Type = obj.TYPE_REG
    379 				q.To.Reg = REG_R1
    380 
    381 				q = obj.Appendp(ctxt, q)
    382 				q.As = ABEQ
    383 				q.From.Type = obj.TYPE_REG
    384 				q.From.Reg = REG_R1
    385 				q.To.Type = obj.TYPE_BRANCH
    386 				q.Mark |= BRANCH
    387 				p1 = q
    388 
    389 				q = obj.Appendp(ctxt, q)
    390 				q.As = mov
    391 				q.From.Type = obj.TYPE_MEM
    392 				q.From.Reg = REG_R1
    393 				q.From.Offset = 0 // Panic.argp
    394 				q.To.Type = obj.TYPE_REG
    395 				q.To.Reg = REG_R2
    396 
    397 				q = obj.Appendp(ctxt, q)
    398 				q.As = add
    399 				q.From.Type = obj.TYPE_CONST
    400 				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
    401 				q.Reg = REGSP
    402 				q.To.Type = obj.TYPE_REG
    403 				q.To.Reg = REG_R3
    404 
    405 				q = obj.Appendp(ctxt, q)
    406 				q.As = ABNE
    407 				q.From.Type = obj.TYPE_REG
    408 				q.From.Reg = REG_R2
    409 				q.Reg = REG_R3
    410 				q.To.Type = obj.TYPE_BRANCH
    411 				q.Mark |= BRANCH
    412 				p2 = q
    413 
    414 				q = obj.Appendp(ctxt, q)
    415 				q.As = add
    416 				q.From.Type = obj.TYPE_CONST
    417 				q.From.Offset = ctxt.FixedFrameSize()
    418 				q.Reg = REGSP
    419 				q.To.Type = obj.TYPE_REG
    420 				q.To.Reg = REG_R2
    421 
    422 				q = obj.Appendp(ctxt, q)
    423 				q.As = mov
    424 				q.From.Type = obj.TYPE_REG
    425 				q.From.Reg = REG_R2
    426 				q.To.Type = obj.TYPE_MEM
    427 				q.To.Reg = REG_R1
    428 				q.To.Offset = 0 // Panic.argp
    429 
    430 				q = obj.Appendp(ctxt, q)
    431 
    432 				q.As = obj.ANOP
    433 				p1.Pcond = q
    434 				p2.Pcond = q
    435 			}
    436 
    437 		case ARET:
    438 			if p.From.Type == obj.TYPE_CONST {
    439 				ctxt.Diag("using BECOME (%v) is not supported!", p)
    440 				break
    441 			}
    442 
    443 			retSym := p.To.Sym
    444 			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
    445 			p.To.Sym = nil
    446 
    447 			if cursym.Text.Mark&LEAF != 0 {
    448 				if autosize == 0 {
    449 					p.As = AJMP
    450 					p.From = obj.Addr{}
    451 					if retSym != nil { // retjmp
    452 						p.To.Type = obj.TYPE_BRANCH
    453 						p.To.Name = obj.NAME_EXTERN
    454 						p.To.Sym = retSym
    455 					} else {
    456 						p.To.Type = obj.TYPE_MEM
    457 						p.To.Reg = REGLINK
    458 						p.To.Offset = 0
    459 					}
    460 					p.Mark |= BRANCH
    461 					break
    462 				}
    463 
    464 				p.As = add
    465 				p.From.Type = obj.TYPE_CONST
    466 				p.From.Offset = int64(autosize)
    467 				p.To.Type = obj.TYPE_REG
    468 				p.To.Reg = REGSP
    469 				p.Spadj = -autosize
    470 
    471 				q = ctxt.NewProg()
    472 				q.As = AJMP
    473 				q.Lineno = p.Lineno
    474 				q.To.Type = obj.TYPE_MEM
    475 				q.To.Offset = 0
    476 				q.To.Reg = REGLINK
    477 				q.Mark |= BRANCH
    478 				q.Spadj = +autosize
    479 
    480 				q.Link = p.Link
    481 				p.Link = q
    482 				break
    483 			}
    484 
    485 			p.As = mov
    486 			p.From.Type = obj.TYPE_MEM
    487 			p.From.Offset = 0
    488 			p.From.Reg = REGSP
    489 			p.To.Type = obj.TYPE_REG
    490 			p.To.Reg = REG_R4
    491 			if retSym != nil { // retjmp from non-leaf, need to restore LINK register
    492 				p.To.Reg = REGLINK
    493 			}
    494 
    495 			if autosize != 0 {
    496 				q = ctxt.NewProg()
    497 				q.As = add
    498 				q.Lineno = p.Lineno
    499 				q.From.Type = obj.TYPE_CONST
    500 				q.From.Offset = int64(autosize)
    501 				q.To.Type = obj.TYPE_REG
    502 				q.To.Reg = REGSP
    503 				q.Spadj = -autosize
    504 
    505 				q.Link = p.Link
    506 				p.Link = q
    507 			}
    508 
    509 			q1 = ctxt.NewProg()
    510 			q1.As = AJMP
    511 			q1.Lineno = p.Lineno
    512 			if retSym != nil { // retjmp
    513 				q1.To.Type = obj.TYPE_BRANCH
    514 				q1.To.Name = obj.NAME_EXTERN
    515 				q1.To.Sym = retSym
    516 			} else {
    517 				q1.To.Type = obj.TYPE_MEM
    518 				q1.To.Offset = 0
    519 				q1.To.Reg = REG_R4
    520 			}
    521 			q1.Mark |= BRANCH
    522 			q1.Spadj = +autosize
    523 
    524 			q1.Link = q.Link
    525 			q.Link = q1
    526 
    527 		case AADD,
    528 			AADDU,
    529 			AADDV,
    530 			AADDVU:
    531 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    532 				p.Spadj = int32(-p.From.Offset)
    533 			}
    534 		}
    535 	}
    536 
    537 	if ctxt.Mode&Mips32 != 0 {
    538 		// rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
    539 		for p = cursym.Text; p != nil; p = p1 {
    540 			p1 = p.Link
    541 
    542 			if p.As != AMOVD {
    543 				continue
    544 			}
    545 			if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
    546 				continue
    547 			}
    548 
    549 			p.As = AMOVF
    550 			q = ctxt.NewProg()
    551 			*q = *p
    552 			q.Link = p.Link
    553 			p.Link = q
    554 			p1 = q.Link
    555 
    556 			var regOff int16
    557 			if ctxt.Arch.ByteOrder == binary.BigEndian {
    558 				regOff = 1 // load odd register first
    559 			}
    560 			if p.From.Type == obj.TYPE_MEM {
    561 				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
    562 				p.To.Reg = reg + regOff
    563 				q.To.Reg = reg + 1 - regOff
    564 				q.From.Offset += 4
    565 			} else if p.To.Type == obj.TYPE_MEM {
    566 				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
    567 				p.From.Reg = reg + regOff
    568 				q.From.Reg = reg + 1 - regOff
    569 				q.To.Offset += 4
    570 			}
    571 		}
    572 	}
    573 
    574 	if nosched {
    575 		// if we don't do instruction scheduling, simply add
    576 		// NOP after each branch instruction.
    577 		for p = cursym.Text; p != nil; p = p.Link {
    578 			if p.Mark&BRANCH != 0 {
    579 				addnop(ctxt, p)
    580 			}
    581 		}
    582 		return
    583 	}
    584 
    585 	// instruction scheduling
    586 	q = nil          // p - 1
    587 	q1 = cursym.Text // top of block
    588 	o := 0           // count of instructions
    589 	for p = cursym.Text; p != nil; p = p1 {
    590 		p1 = p.Link
    591 		o++
    592 		if p.Mark&NOSCHED != 0 {
    593 			if q1 != p {
    594 				sched(ctxt, q1, q)
    595 			}
    596 			for ; p != nil; p = p.Link {
    597 				if p.Mark&NOSCHED == 0 {
    598 					break
    599 				}
    600 				q = p
    601 			}
    602 			p1 = p
    603 			q1 = p
    604 			o = 0
    605 			continue
    606 		}
    607 		if p.Mark&(LABEL|SYNC) != 0 {
    608 			if q1 != p {
    609 				sched(ctxt, q1, q)
    610 			}
    611 			q1 = p
    612 			o = 1
    613 		}
    614 		if p.Mark&(BRANCH|SYNC) != 0 {
    615 			sched(ctxt, q1, p)
    616 			q1 = p1
    617 			o = 0
    618 		}
    619 		if o >= NSCHED {
    620 			sched(ctxt, q1, p)
    621 			q1 = p1
    622 			o = 0
    623 		}
    624 		q = p
    625 	}
    626 }
    627 
    628 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
    629 	// Leaf function with no frame is effectively NOSPLIT.
    630 	if framesize == 0 {
    631 		return p
    632 	}
    633 
    634 	var mov, add, sub obj.As
    635 
    636 	if ctxt.Mode&Mips64 != 0 {
    637 		add = AADDV
    638 		mov = AMOVV
    639 		sub = ASUBVU
    640 	} else {
    641 		add = AADDU
    642 		mov = AMOVW
    643 		sub = ASUBU
    644 	}
    645 
    646 	// MOV	g_stackguard(g), R1
    647 	p = obj.Appendp(ctxt, p)
    648 
    649 	p.As = mov
    650 	p.From.Type = obj.TYPE_MEM
    651 	p.From.Reg = REGG
    652 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
    653 	if ctxt.Cursym.CFunc() {
    654 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
    655 	}
    656 	p.To.Type = obj.TYPE_REG
    657 	p.To.Reg = REG_R1
    658 
    659 	var q *obj.Prog
    660 	if framesize <= obj.StackSmall {
    661 		// small stack: SP < stackguard
    662 		//	AGTU	SP, stackguard, R1
    663 		p = obj.Appendp(ctxt, p)
    664 
    665 		p.As = ASGTU
    666 		p.From.Type = obj.TYPE_REG
    667 		p.From.Reg = REGSP
    668 		p.Reg = REG_R1
    669 		p.To.Type = obj.TYPE_REG
    670 		p.To.Reg = REG_R1
    671 	} else if framesize <= obj.StackBig {
    672 		// large stack: SP-framesize < stackguard-StackSmall
    673 		//	ADD	$-framesize, SP, R2
    674 		//	SGTU	R2, stackguard, R1
    675 		p = obj.Appendp(ctxt, p)
    676 
    677 		p.As = add
    678 		p.From.Type = obj.TYPE_CONST
    679 		p.From.Offset = int64(-framesize)
    680 		p.Reg = REGSP
    681 		p.To.Type = obj.TYPE_REG
    682 		p.To.Reg = REG_R2
    683 
    684 		p = obj.Appendp(ctxt, p)
    685 		p.As = ASGTU
    686 		p.From.Type = obj.TYPE_REG
    687 		p.From.Reg = REG_R2
    688 		p.Reg = REG_R1
    689 		p.To.Type = obj.TYPE_REG
    690 		p.To.Reg = REG_R1
    691 	} else {
    692 		// Such a large stack we need to protect against wraparound.
    693 		// If SP is close to zero:
    694 		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
    695 		// The +StackGuard on both sides is required to keep the left side positive:
    696 		// SP is allowed to be slightly below stackguard. See stack.h.
    697 		//
    698 		// Preemption sets stackguard to StackPreempt, a very large value.
    699 		// That breaks the math above, so we have to check for that explicitly.
    700 		//	// stackguard is R1
    701 		//	MOV	$StackPreempt, R2
    702 		//	BEQ	R1, R2, label-of-call-to-morestack
    703 		//	ADD	$StackGuard, SP, R2
    704 		//	SUB	R1, R2
    705 		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
    706 		//	SGTU	R2, R1, R1
    707 		p = obj.Appendp(ctxt, p)
    708 
    709 		p.As = mov
    710 		p.From.Type = obj.TYPE_CONST
    711 		p.From.Offset = obj.StackPreempt
    712 		p.To.Type = obj.TYPE_REG
    713 		p.To.Reg = REG_R2
    714 
    715 		p = obj.Appendp(ctxt, p)
    716 		q = p
    717 		p.As = ABEQ
    718 		p.From.Type = obj.TYPE_REG
    719 		p.From.Reg = REG_R1
    720 		p.Reg = REG_R2
    721 		p.To.Type = obj.TYPE_BRANCH
    722 		p.Mark |= BRANCH
    723 
    724 		p = obj.Appendp(ctxt, p)
    725 		p.As = add
    726 		p.From.Type = obj.TYPE_CONST
    727 		p.From.Offset = obj.StackGuard
    728 		p.Reg = REGSP
    729 		p.To.Type = obj.TYPE_REG
    730 		p.To.Reg = REG_R2
    731 
    732 		p = obj.Appendp(ctxt, p)
    733 		p.As = sub
    734 		p.From.Type = obj.TYPE_REG
    735 		p.From.Reg = REG_R1
    736 		p.To.Type = obj.TYPE_REG
    737 		p.To.Reg = REG_R2
    738 
    739 		p = obj.Appendp(ctxt, p)
    740 		p.As = mov
    741 		p.From.Type = obj.TYPE_CONST
    742 		p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall
    743 		p.To.Type = obj.TYPE_REG
    744 		p.To.Reg = REG_R1
    745 
    746 		p = obj.Appendp(ctxt, p)
    747 		p.As = ASGTU
    748 		p.From.Type = obj.TYPE_REG
    749 		p.From.Reg = REG_R2
    750 		p.Reg = REG_R1
    751 		p.To.Type = obj.TYPE_REG
    752 		p.To.Reg = REG_R1
    753 	}
    754 
    755 	// q1: BNE	R1, done
    756 	p = obj.Appendp(ctxt, p)
    757 	q1 := p
    758 
    759 	p.As = ABNE
    760 	p.From.Type = obj.TYPE_REG
    761 	p.From.Reg = REG_R1
    762 	p.To.Type = obj.TYPE_BRANCH
    763 	p.Mark |= BRANCH
    764 
    765 	// MOV	LINK, R3
    766 	p = obj.Appendp(ctxt, p)
    767 
    768 	p.As = mov
    769 	p.From.Type = obj.TYPE_REG
    770 	p.From.Reg = REGLINK
    771 	p.To.Type = obj.TYPE_REG
    772 	p.To.Reg = REG_R3
    773 	if q != nil {
    774 		q.Pcond = p
    775 		p.Mark |= LABEL
    776 	}
    777 
    778 	// JAL	runtime.morestack(SB)
    779 	p = obj.Appendp(ctxt, p)
    780 
    781 	p.As = AJAL
    782 	p.To.Type = obj.TYPE_BRANCH
    783 	if ctxt.Cursym.CFunc() {
    784 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestackc", 0)
    785 	} else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 {
    786 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0)
    787 	} else {
    788 		p.To.Sym = obj.Linklookup(ctxt, "runtime.morestack", 0)
    789 	}
    790 	p.Mark |= BRANCH
    791 
    792 	// JMP	start
    793 	p = obj.Appendp(ctxt, p)
    794 
    795 	p.As = AJMP
    796 	p.To.Type = obj.TYPE_BRANCH
    797 	p.Pcond = ctxt.Cursym.Text.Link
    798 	p.Mark |= BRANCH
    799 
    800 	// placeholder for q1's jump target
    801 	p = obj.Appendp(ctxt, p)
    802 
    803 	p.As = obj.ANOP // zero-width place holder
    804 	q1.Pcond = p
    805 
    806 	return p
    807 }
    808 
    809 func addnop(ctxt *obj.Link, p *obj.Prog) {
    810 	q := ctxt.NewProg()
    811 	// we want to use the canonical NOP (SLL $0,R0,R0) here,
    812 	// however, as the assembler will always replace $0
    813 	// as R0, we have to resort to manually encode the SLL
    814 	// instruction as WORD $0.
    815 	q.As = AWORD
    816 	q.Lineno = p.Lineno
    817 	q.From.Type = obj.TYPE_CONST
    818 	q.From.Name = obj.NAME_NONE
    819 	q.From.Offset = 0
    820 
    821 	q.Link = p.Link
    822 	p.Link = q
    823 }
    824 
    825 const (
    826 	E_HILO  = 1 << 0
    827 	E_FCR   = 1 << 1
    828 	E_MCR   = 1 << 2
    829 	E_MEM   = 1 << 3
    830 	E_MEMSP = 1 << 4 /* uses offset and size */
    831 	E_MEMSB = 1 << 5 /* uses offset and size */
    832 	ANYMEM  = E_MEM | E_MEMSP | E_MEMSB
    833 	//DELAY = LOAD|BRANCH|FCMP
    834 	DELAY = BRANCH /* only schedule branch */
    835 )
    836 
    837 type Dep struct {
    838 	ireg uint32
    839 	freg uint32
    840 	cc   uint32
    841 }
    842 
    843 type Sch struct {
    844 	p       obj.Prog
    845 	set     Dep
    846 	used    Dep
    847 	soffset int32
    848 	size    uint8
    849 	nop     uint8
    850 	comp    bool
    851 }
    852 
    853 func sched(ctxt *obj.Link, p0, pe *obj.Prog) {
    854 	var sch [NSCHED]Sch
    855 
    856 	/*
    857 	 * build side structure
    858 	 */
    859 	s := sch[:]
    860 	for p := p0; ; p = p.Link {
    861 		s[0].p = *p
    862 		markregused(ctxt, &s[0])
    863 		if p == pe {
    864 			break
    865 		}
    866 		s = s[1:]
    867 	}
    868 	se := s
    869 
    870 	for i := cap(sch) - cap(se); i >= 0; i-- {
    871 		s = sch[i:]
    872 		if s[0].p.Mark&DELAY == 0 {
    873 			continue
    874 		}
    875 		if -cap(s) < -cap(se) {
    876 			if !conflict(&s[0], &s[1]) {
    877 				continue
    878 			}
    879 		}
    880 
    881 		var t []Sch
    882 		var j int
    883 		for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
    884 			t = sch[j:]
    885 			if t[0].comp {
    886 				if s[0].p.Mark&BRANCH != 0 {
    887 					goto no2
    888 				}
    889 			}
    890 			if t[0].p.Mark&DELAY != 0 {
    891 				if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
    892 					goto no2
    893 				}
    894 			}
    895 			for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
    896 				if depend(ctxt, &u[0], &t[0]) {
    897 					goto no2
    898 				}
    899 			}
    900 			goto out2
    901 		no2:
    902 		}
    903 
    904 		if s[0].p.Mark&BRANCH != 0 {
    905 			s[0].nop = 1
    906 		}
    907 		continue
    908 
    909 	out2:
    910 		// t[0] is the instruction being moved to fill the delay
    911 		stmp := t[0]
    912 		copy(t[:i-j], t[1:i-j+1])
    913 		s[0] = stmp
    914 
    915 		if t[i-j-1].p.Mark&BRANCH != 0 {
    916 			// t[i-j] is being put into a branch delay slot
    917 			// combine its Spadj with the branch instruction
    918 			t[i-j-1].p.Spadj += t[i-j].p.Spadj
    919 			t[i-j].p.Spadj = 0
    920 		}
    921 
    922 		i--
    923 	}
    924 
    925 	/*
    926 	 * put it all back
    927 	 */
    928 	var p *obj.Prog
    929 	var q *obj.Prog
    930 	for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
    931 		q = p.Link
    932 		if q != s[0].p.Link {
    933 			*p = s[0].p
    934 			p.Link = q
    935 		}
    936 		for s[0].nop != 0 {
    937 			s[0].nop--
    938 			addnop(ctxt, p)
    939 		}
    940 	}
    941 }
    942 
    943 func markregused(ctxt *obj.Link, s *Sch) {
    944 	p := &s.p
    945 	s.comp = compound(ctxt, p)
    946 	s.nop = 0
    947 	if s.comp {
    948 		s.set.ireg |= 1 << (REGTMP - REG_R0)
    949 		s.used.ireg |= 1 << (REGTMP - REG_R0)
    950 	}
    951 
    952 	ar := 0  /* dest is really reference */
    953 	ad := 0  /* source/dest is really address */
    954 	ld := 0  /* opcode is load instruction */
    955 	sz := 20 /* size of load/store for overlap computation */
    956 
    957 	/*
    958 	 * flags based on opcode
    959 	 */
    960 	switch p.As {
    961 	case obj.ATEXT:
    962 		ctxt.Autosize = int32(p.To.Offset + 8)
    963 		ad = 1
    964 
    965 	case AJAL:
    966 		c := p.Reg
    967 		if c == 0 {
    968 			c = REGLINK
    969 		}
    970 		s.set.ireg |= 1 << uint(c-REG_R0)
    971 		ar = 1
    972 		ad = 1
    973 
    974 	case ABGEZAL,
    975 		ABLTZAL:
    976 		s.set.ireg |= 1 << (REGLINK - REG_R0)
    977 		fallthrough
    978 	case ABEQ,
    979 		ABGEZ,
    980 		ABGTZ,
    981 		ABLEZ,
    982 		ABLTZ,
    983 		ABNE:
    984 		ar = 1
    985 		ad = 1
    986 
    987 	case ABFPT,
    988 		ABFPF:
    989 		ad = 1
    990 		s.used.cc |= E_FCR
    991 
    992 	case ACMPEQD,
    993 		ACMPEQF,
    994 		ACMPGED,
    995 		ACMPGEF,
    996 		ACMPGTD,
    997 		ACMPGTF:
    998 		ar = 1
    999 		s.set.cc |= E_FCR
   1000 		p.Mark |= FCMP
   1001 
   1002 	case AJMP:
   1003 		ar = 1
   1004 		ad = 1
   1005 
   1006 	case AMOVB,
   1007 		AMOVBU:
   1008 		sz = 1
   1009 		ld = 1
   1010 
   1011 	case AMOVH,
   1012 		AMOVHU:
   1013 		sz = 2
   1014 		ld = 1
   1015 
   1016 	case AMOVF,
   1017 		AMOVW,
   1018 		AMOVWL,
   1019 		AMOVWR:
   1020 		sz = 4
   1021 		ld = 1
   1022 
   1023 	case AMOVD,
   1024 		AMOVV,
   1025 		AMOVVL,
   1026 		AMOVVR:
   1027 		sz = 8
   1028 		ld = 1
   1029 
   1030 	case ADIV,
   1031 		ADIVU,
   1032 		AMUL,
   1033 		AMULU,
   1034 		AREM,
   1035 		AREMU,
   1036 		ADIVV,
   1037 		ADIVVU,
   1038 		AMULV,
   1039 		AMULVU,
   1040 		AREMV,
   1041 		AREMVU:
   1042 		s.set.cc = E_HILO
   1043 		fallthrough
   1044 	case AADD,
   1045 		AADDU,
   1046 		AADDV,
   1047 		AADDVU,
   1048 		AAND,
   1049 		ANOR,
   1050 		AOR,
   1051 		ASGT,
   1052 		ASGTU,
   1053 		ASLL,
   1054 		ASRA,
   1055 		ASRL,
   1056 		ASLLV,
   1057 		ASRAV,
   1058 		ASRLV,
   1059 		ASUB,
   1060 		ASUBU,
   1061 		ASUBV,
   1062 		ASUBVU,
   1063 		AXOR,
   1064 
   1065 		AADDD,
   1066 		AADDF,
   1067 		AADDW,
   1068 		ASUBD,
   1069 		ASUBF,
   1070 		ASUBW,
   1071 		AMULF,
   1072 		AMULD,
   1073 		AMULW,
   1074 		ADIVF,
   1075 		ADIVD,
   1076 		ADIVW:
   1077 		if p.Reg == 0 {
   1078 			if p.To.Type == obj.TYPE_REG {
   1079 				p.Reg = p.To.Reg
   1080 			}
   1081 			//if(p->reg == NREG)
   1082 			//	print("botch %P\n", p);
   1083 		}
   1084 	}
   1085 
   1086 	/*
   1087 	 * flags based on 'to' field
   1088 	 */
   1089 	c := int(p.To.Class)
   1090 	if c == 0 {
   1091 		c = aclass(ctxt, &p.To) + 1
   1092 		p.To.Class = int8(c)
   1093 	}
   1094 	c--
   1095 	switch c {
   1096 	default:
   1097 		fmt.Printf("unknown class %d %v\n", c, p)
   1098 
   1099 	case C_ZCON,
   1100 		C_SCON,
   1101 		C_ADD0CON,
   1102 		C_AND0CON,
   1103 		C_ADDCON,
   1104 		C_ANDCON,
   1105 		C_UCON,
   1106 		C_LCON,
   1107 		C_NONE,
   1108 		C_SBRA,
   1109 		C_LBRA,
   1110 		C_ADDR,
   1111 		C_TEXTSIZE:
   1112 		break
   1113 
   1114 	case C_HI,
   1115 		C_LO:
   1116 		s.set.cc |= E_HILO
   1117 
   1118 	case C_FCREG:
   1119 		s.set.cc |= E_FCR
   1120 
   1121 	case C_MREG:
   1122 		s.set.cc |= E_MCR
   1123 
   1124 	case C_ZOREG,
   1125 		C_SOREG,
   1126 		C_LOREG:
   1127 		c = int(p.To.Reg)
   1128 		s.used.ireg |= 1 << uint(c-REG_R0)
   1129 		if ad != 0 {
   1130 			break
   1131 		}
   1132 		s.size = uint8(sz)
   1133 		s.soffset = regoff(ctxt, &p.To)
   1134 
   1135 		m := uint32(ANYMEM)
   1136 		if c == REGSB {
   1137 			m = E_MEMSB
   1138 		}
   1139 		if c == REGSP {
   1140 			m = E_MEMSP
   1141 		}
   1142 
   1143 		if ar != 0 {
   1144 			s.used.cc |= m
   1145 		} else {
   1146 			s.set.cc |= m
   1147 		}
   1148 
   1149 	case C_SACON,
   1150 		C_LACON:
   1151 		s.used.ireg |= 1 << (REGSP - REG_R0)
   1152 
   1153 	case C_SECON,
   1154 		C_LECON:
   1155 		s.used.ireg |= 1 << (REGSB - REG_R0)
   1156 
   1157 	case C_REG:
   1158 		if ar != 0 {
   1159 			s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
   1160 		} else {
   1161 			s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
   1162 		}
   1163 
   1164 	case C_FREG:
   1165 		if ar != 0 {
   1166 			s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
   1167 		} else {
   1168 			s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
   1169 		}
   1170 		if ld != 0 && p.From.Type == obj.TYPE_REG {
   1171 			p.Mark |= LOAD
   1172 		}
   1173 
   1174 	case C_SAUTO,
   1175 		C_LAUTO:
   1176 		s.used.ireg |= 1 << (REGSP - REG_R0)
   1177 		if ad != 0 {
   1178 			break
   1179 		}
   1180 		s.size = uint8(sz)
   1181 		s.soffset = regoff(ctxt, &p.To)
   1182 
   1183 		if ar != 0 {
   1184 			s.used.cc |= E_MEMSP
   1185 		} else {
   1186 			s.set.cc |= E_MEMSP
   1187 		}
   1188 
   1189 	case C_SEXT,
   1190 		C_LEXT:
   1191 		s.used.ireg |= 1 << (REGSB - REG_R0)
   1192 		if ad != 0 {
   1193 			break
   1194 		}
   1195 		s.size = uint8(sz)
   1196 		s.soffset = regoff(ctxt, &p.To)
   1197 
   1198 		if ar != 0 {
   1199 			s.used.cc |= E_MEMSB
   1200 		} else {
   1201 			s.set.cc |= E_MEMSB
   1202 		}
   1203 	}
   1204 
   1205 	/*
   1206 	 * flags based on 'from' field
   1207 	 */
   1208 	c = int(p.From.Class)
   1209 	if c == 0 {
   1210 		c = aclass(ctxt, &p.From) + 1
   1211 		p.From.Class = int8(c)
   1212 	}
   1213 	c--
   1214 	switch c {
   1215 	default:
   1216 		fmt.Printf("unknown class %d %v\n", c, p)
   1217 
   1218 	case C_ZCON,
   1219 		C_SCON,
   1220 		C_ADD0CON,
   1221 		C_AND0CON,
   1222 		C_ADDCON,
   1223 		C_ANDCON,
   1224 		C_UCON,
   1225 		C_LCON,
   1226 		C_NONE,
   1227 		C_SBRA,
   1228 		C_LBRA,
   1229 		C_ADDR,
   1230 		C_TEXTSIZE:
   1231 		break
   1232 
   1233 	case C_HI,
   1234 		C_LO:
   1235 		s.used.cc |= E_HILO
   1236 
   1237 	case C_FCREG:
   1238 		s.used.cc |= E_FCR
   1239 
   1240 	case C_MREG:
   1241 		s.used.cc |= E_MCR
   1242 
   1243 	case C_ZOREG,
   1244 		C_SOREG,
   1245 		C_LOREG:
   1246 		c = int(p.From.Reg)
   1247 		s.used.ireg |= 1 << uint(c-REG_R0)
   1248 		if ld != 0 {
   1249 			p.Mark |= LOAD
   1250 		}
   1251 		s.size = uint8(sz)
   1252 		s.soffset = regoff(ctxt, &p.From)
   1253 
   1254 		m := uint32(ANYMEM)
   1255 		if c == REGSB {
   1256 			m = E_MEMSB
   1257 		}
   1258 		if c == REGSP {
   1259 			m = E_MEMSP
   1260 		}
   1261 
   1262 		s.used.cc |= m
   1263 
   1264 	case C_SACON,
   1265 		C_LACON:
   1266 		c = int(p.From.Reg)
   1267 		if c == 0 {
   1268 			c = REGSP
   1269 		}
   1270 		s.used.ireg |= 1 << uint(c-REG_R0)
   1271 
   1272 	case C_SECON,
   1273 		C_LECON:
   1274 		s.used.ireg |= 1 << (REGSB - REG_R0)
   1275 
   1276 	case C_REG:
   1277 		s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
   1278 
   1279 	case C_FREG:
   1280 		s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
   1281 		if ld != 0 && p.To.Type == obj.TYPE_REG {
   1282 			p.Mark |= LOAD
   1283 		}
   1284 
   1285 	case C_SAUTO,
   1286 		C_LAUTO:
   1287 		s.used.ireg |= 1 << (REGSP - REG_R0)
   1288 		if ld != 0 {
   1289 			p.Mark |= LOAD
   1290 		}
   1291 		if ad != 0 {
   1292 			break
   1293 		}
   1294 		s.size = uint8(sz)
   1295 		s.soffset = regoff(ctxt, &p.From)
   1296 
   1297 		s.used.cc |= E_MEMSP
   1298 
   1299 	case C_SEXT:
   1300 	case C_LEXT:
   1301 		s.used.ireg |= 1 << (REGSB - REG_R0)
   1302 		if ld != 0 {
   1303 			p.Mark |= LOAD
   1304 		}
   1305 		if ad != 0 {
   1306 			break
   1307 		}
   1308 		s.size = uint8(sz)
   1309 		s.soffset = regoff(ctxt, &p.From)
   1310 
   1311 		s.used.cc |= E_MEMSB
   1312 	}
   1313 
   1314 	c = int(p.Reg)
   1315 	if c != 0 {
   1316 		if REG_F0 <= c && c <= REG_F31 {
   1317 			s.used.freg |= 1 << uint(c-REG_F0)
   1318 		} else {
   1319 			s.used.ireg |= 1 << uint(c-REG_R0)
   1320 		}
   1321 	}
   1322 	s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
   1323 }
   1324 
   1325 /*
   1326  * test to see if two instructions can be
   1327  * interchanged without changing semantics
   1328  */
   1329 func depend(ctxt *obj.Link, sa, sb *Sch) bool {
   1330 	if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
   1331 		return true
   1332 	}
   1333 	if sb.set.ireg&sa.used.ireg != 0 {
   1334 		return true
   1335 	}
   1336 
   1337 	if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
   1338 		return true
   1339 	}
   1340 	if sb.set.freg&sa.used.freg != 0 {
   1341 		return true
   1342 	}
   1343 
   1344 	/*
   1345 	 * special case.
   1346 	 * loads from same address cannot pass.
   1347 	 * this is for hardware fifo's and the like
   1348 	 */
   1349 	if sa.used.cc&sb.used.cc&E_MEM != 0 {
   1350 		if sa.p.Reg == sb.p.Reg {
   1351 			if regoff(ctxt, &sa.p.From) == regoff(ctxt, &sb.p.From) {
   1352 				return true
   1353 			}
   1354 		}
   1355 	}
   1356 
   1357 	x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
   1358 	if x != 0 {
   1359 		/*
   1360 		 * allow SB and SP to pass each other.
   1361 		 * allow SB to pass SB iff doffsets are ok
   1362 		 * anything else conflicts
   1363 		 */
   1364 		if x != E_MEMSP && x != E_MEMSB {
   1365 			return true
   1366 		}
   1367 		x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
   1368 		if x&E_MEM != 0 {
   1369 			return true
   1370 		}
   1371 		if offoverlap(sa, sb) {
   1372 			return true
   1373 		}
   1374 	}
   1375 
   1376 	return false
   1377 }
   1378 
   1379 func offoverlap(sa, sb *Sch) bool {
   1380 	if sa.soffset < sb.soffset {
   1381 		if sa.soffset+int32(sa.size) > sb.soffset {
   1382 			return true
   1383 		}
   1384 		return false
   1385 	}
   1386 	if sb.soffset+int32(sb.size) > sa.soffset {
   1387 		return true
   1388 	}
   1389 	return false
   1390 }
   1391 
   1392 /*
   1393  * test 2 adjacent instructions
   1394  * and find out if inserted instructions
   1395  * are desired to prevent stalls.
   1396  */
   1397 func conflict(sa, sb *Sch) bool {
   1398 	if sa.set.ireg&sb.used.ireg != 0 {
   1399 		return true
   1400 	}
   1401 	if sa.set.freg&sb.used.freg != 0 {
   1402 		return true
   1403 	}
   1404 	if sa.set.cc&sb.used.cc != 0 {
   1405 		return true
   1406 	}
   1407 	return false
   1408 }
   1409 
   1410 func compound(ctxt *obj.Link, p *obj.Prog) bool {
   1411 	o := oplook(ctxt, p)
   1412 	if o.size != 4 {
   1413 		return true
   1414 	}
   1415 	if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
   1416 		return true
   1417 	}
   1418 	return false
   1419 }
   1420 
   1421 func follow(ctxt *obj.Link, s *obj.LSym) {
   1422 	ctxt.Cursym = s
   1423 
   1424 	firstp := ctxt.NewProg()
   1425 	lastp := firstp
   1426 	xfol(ctxt, s.Text, &lastp)
   1427 	lastp.Link = nil
   1428 	s.Text = firstp.Link
   1429 }
   1430 
   1431 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   1432 	var q *obj.Prog
   1433 	var r *obj.Prog
   1434 	var i int
   1435 
   1436 loop:
   1437 	if p == nil {
   1438 		return
   1439 	}
   1440 	a := p.As
   1441 	if a == AJMP {
   1442 		q = p.Pcond
   1443 		if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) {
   1444 			p.Mark |= FOLL
   1445 			(*last).Link = p
   1446 			*last = p
   1447 			p = p.Link
   1448 			xfol(ctxt, p, last)
   1449 			p = q
   1450 			if p != nil && p.Mark&FOLL == 0 {
   1451 				goto loop
   1452 			}
   1453 			return
   1454 		}
   1455 
   1456 		if q != nil {
   1457 			p.Mark |= FOLL
   1458 			p = q
   1459 			if p.Mark&FOLL == 0 {
   1460 				goto loop
   1461 			}
   1462 		}
   1463 	}
   1464 
   1465 	if p.Mark&FOLL != 0 {
   1466 		i = 0
   1467 		q = p
   1468 		for ; i < 4; i, q = i+1, q.Link {
   1469 			if q == *last || (q.Mark&NOSCHED != 0) {
   1470 				break
   1471 			}
   1472 			a = q.As
   1473 			if a == obj.ANOP {
   1474 				i--
   1475 				continue
   1476 			}
   1477 
   1478 			if a == AJMP || a == ARET || a == ARFE {
   1479 				goto copy
   1480 			}
   1481 			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   1482 				continue
   1483 			}
   1484 			if a != ABEQ && a != ABNE {
   1485 				continue
   1486 			}
   1487 
   1488 		copy:
   1489 			for {
   1490 				r = ctxt.NewProg()
   1491 				*r = *p
   1492 				if r.Mark&FOLL == 0 {
   1493 					fmt.Printf("can't happen 1\n")
   1494 				}
   1495 				r.Mark |= FOLL
   1496 				if p != q {
   1497 					p = p.Link
   1498 					(*last).Link = r
   1499 					*last = r
   1500 					continue
   1501 				}
   1502 
   1503 				(*last).Link = r
   1504 				*last = r
   1505 				if a == AJMP || a == ARET || a == ARFE {
   1506 					return
   1507 				}
   1508 				r.As = ABNE
   1509 				if a == ABNE {
   1510 					r.As = ABEQ
   1511 				}
   1512 				r.Pcond = p.Link
   1513 				r.Link = p.Pcond
   1514 				if r.Link.Mark&FOLL == 0 {
   1515 					xfol(ctxt, r.Link, last)
   1516 				}
   1517 				if r.Pcond.Mark&FOLL == 0 {
   1518 					fmt.Printf("can't happen 2\n")
   1519 				}
   1520 				return
   1521 			}
   1522 		}
   1523 
   1524 		a = AJMP
   1525 		q = ctxt.NewProg()
   1526 		q.As = a
   1527 		q.Lineno = p.Lineno
   1528 		q.To.Type = obj.TYPE_BRANCH
   1529 		q.To.Offset = p.Pc
   1530 		q.Pcond = p
   1531 		p = q
   1532 	}
   1533 
   1534 	p.Mark |= FOLL
   1535 	(*last).Link = p
   1536 	*last = p
   1537 	if a == AJMP || a == ARET || a == ARFE {
   1538 		if p.Mark&NOSCHED != 0 {
   1539 			p = p.Link
   1540 			goto loop
   1541 		}
   1542 
   1543 		return
   1544 	}
   1545 
   1546 	if p.Pcond != nil {
   1547 		if a != AJAL && p.Link != nil {
   1548 			xfol(ctxt, p.Link, last)
   1549 			p = p.Pcond
   1550 			if p == nil || (p.Mark&FOLL != 0) {
   1551 				return
   1552 			}
   1553 			goto loop
   1554 		}
   1555 	}
   1556 
   1557 	p = p.Link
   1558 	goto loop
   1559 }
   1560 
   1561 var Linkmips64 = obj.LinkArch{
   1562 	Arch:       sys.ArchMIPS64,
   1563 	Preprocess: preprocess,
   1564 	Assemble:   span0,
   1565 	Follow:     follow,
   1566 	Progedit:   progedit,
   1567 }
   1568 
   1569 var Linkmips64le = obj.LinkArch{
   1570 	Arch:       sys.ArchMIPS64LE,
   1571 	Preprocess: preprocess,
   1572 	Assemble:   span0,
   1573 	Follow:     follow,
   1574 	Progedit:   progedit,
   1575 }
   1576 
   1577 var Linkmips = obj.LinkArch{
   1578 	Arch:       sys.ArchMIPS,
   1579 	Preprocess: preprocess,
   1580 	Assemble:   span0,
   1581 	Follow:     follow,
   1582 	Progedit:   progedit,
   1583 }
   1584 
   1585 var Linkmipsle = obj.LinkArch{
   1586 	Arch:       sys.ArchMIPSLE,
   1587 	Preprocess: preprocess,
   1588 	Assemble:   span0,
   1589 	Follow:     follow,
   1590 	Progedit:   progedit,
   1591 }
   1592