Home | History | Annotate | Download | only in arm
      1 // Derived from Inferno utils/5c/swt.c
      2 // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/swt.c
      3 //
      4 //	Copyright  1994-1999 Lucent Technologies Inc.  All rights reserved.
      5 //	Portions Copyright  1995-1997 C H Forsyth (forsyth (a] terzarima.net)
      6 //	Portions Copyright  1997-1999 Vita Nuova Limited
      7 //	Portions Copyright  2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
      8 //	Portions Copyright  2004,2006 Bruce Ellis
      9 //	Portions Copyright  2005-2007 C H Forsyth (forsyth (a] terzarima.net)
     10 //	Revisions Copyright  2000-2007 Lucent Technologies Inc. and others
     11 //	Portions Copyright  2009 The Go Authors. All rights reserved.
     12 //
     13 // Permission is hereby granted, free of charge, to any person obtaining a copy
     14 // of this software and associated documentation files (the "Software"), to deal
     15 // in the Software without restriction, including without limitation the rights
     16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     17 // copies of the Software, and to permit persons to whom the Software is
     18 // furnished to do so, subject to the following conditions:
     19 //
     20 // The above copyright notice and this permission notice shall be included in
     21 // all copies or substantial portions of the Software.
     22 //
     23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     29 // THE SOFTWARE.
     30 
     31 package arm
     32 
     33 import (
     34 	"cmd/internal/obj"
     35 	"cmd/internal/sys"
     36 	"fmt"
     37 	"log"
     38 	"math"
     39 )
     40 
     41 var progedit_tlsfallback *obj.LSym
     42 
     43 func progedit(ctxt *obj.Link, p *obj.Prog) {
     44 	p.From.Class = 0
     45 	p.To.Class = 0
     46 
     47 	// Rewrite B/BL to symbol as TYPE_BRANCH.
     48 	switch p.As {
     49 	case AB,
     50 		ABL,
     51 		obj.ADUFFZERO,
     52 		obj.ADUFFCOPY:
     53 		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
     54 			p.To.Type = obj.TYPE_BRANCH
     55 		}
     56 	}
     57 
     58 	// Replace TLS register fetches on older ARM processors.
     59 	switch p.As {
     60 	// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
     61 	case AMRC:
     62 		if p.To.Offset&0xffff0fff == 0xee1d0f70 {
     63 			// Because the instruction might be rewritten to a BL which returns in R0
     64 			// the register must be zero.
     65 			if p.To.Offset&0xf000 != 0 {
     66 				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
     67 			}
     68 
     69 			if obj.GOARM < 7 {
     70 				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
     71 				if progedit_tlsfallback == nil {
     72 					progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0)
     73 				}
     74 
     75 				// MOVW	LR, R11
     76 				p.As = AMOVW
     77 
     78 				p.From.Type = obj.TYPE_REG
     79 				p.From.Reg = REGLINK
     80 				p.To.Type = obj.TYPE_REG
     81 				p.To.Reg = REGTMP
     82 
     83 				// BL	runtime.read_tls_fallback(SB)
     84 				p = obj.Appendp(ctxt, p)
     85 
     86 				p.As = ABL
     87 				p.To.Type = obj.TYPE_BRANCH
     88 				p.To.Sym = progedit_tlsfallback
     89 				p.To.Offset = 0
     90 
     91 				// MOVW	R11, LR
     92 				p = obj.Appendp(ctxt, p)
     93 
     94 				p.As = AMOVW
     95 				p.From.Type = obj.TYPE_REG
     96 				p.From.Reg = REGTMP
     97 				p.To.Type = obj.TYPE_REG
     98 				p.To.Reg = REGLINK
     99 				break
    100 			}
    101 		}
    102 
    103 		// Otherwise, MRC/MCR instructions need no further treatment.
    104 		p.As = AWORD
    105 	}
    106 
    107 	// Rewrite float constants to values stored in memory.
    108 	switch p.As {
    109 	case AMOVF:
    110 		if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
    111 			f32 := float32(p.From.Val.(float64))
    112 			i32 := math.Float32bits(f32)
    113 			literal := fmt.Sprintf("$f32.%08x", i32)
    114 			s := obj.Linklookup(ctxt, literal, 0)
    115 			p.From.Type = obj.TYPE_MEM
    116 			p.From.Sym = s
    117 			p.From.Name = obj.NAME_EXTERN
    118 			p.From.Offset = 0
    119 		}
    120 
    121 	case AMOVD:
    122 		if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
    123 			i64 := math.Float64bits(p.From.Val.(float64))
    124 			literal := fmt.Sprintf("$f64.%016x", i64)
    125 			s := obj.Linklookup(ctxt, literal, 0)
    126 			p.From.Type = obj.TYPE_MEM
    127 			p.From.Sym = s
    128 			p.From.Name = obj.NAME_EXTERN
    129 			p.From.Offset = 0
    130 		}
    131 	}
    132 
    133 	if ctxt.Flag_dynlink {
    134 		rewriteToUseGot(ctxt, p)
    135 	}
    136 }
    137 
    138 // Rewrite p, if necessary, to access global data via the global offset table.
    139 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
    140 	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
    141 		//     ADUFFxxx $offset
    142 		// becomes
    143 		//     MOVW runtime.duffxxx@GOT, R9
    144 		//     ADD $offset, R9
    145 		//     CALL (R9)
    146 		var sym *obj.LSym
    147 		if p.As == obj.ADUFFZERO {
    148 			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
    149 		} else {
    150 			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
    151 		}
    152 		offset := p.To.Offset
    153 		p.As = AMOVW
    154 		p.From.Type = obj.TYPE_MEM
    155 		p.From.Name = obj.NAME_GOTREF
    156 		p.From.Sym = sym
    157 		p.To.Type = obj.TYPE_REG
    158 		p.To.Reg = REG_R9
    159 		p.To.Name = obj.NAME_NONE
    160 		p.To.Offset = 0
    161 		p.To.Sym = nil
    162 		p1 := obj.Appendp(ctxt, p)
    163 		p1.As = AADD
    164 		p1.From.Type = obj.TYPE_CONST
    165 		p1.From.Offset = offset
    166 		p1.To.Type = obj.TYPE_REG
    167 		p1.To.Reg = REG_R9
    168 		p2 := obj.Appendp(ctxt, p1)
    169 		p2.As = obj.ACALL
    170 		p2.To.Type = obj.TYPE_MEM
    171 		p2.To.Reg = REG_R9
    172 		return
    173 	}
    174 
    175 	// We only care about global data: NAME_EXTERN means a global
    176 	// symbol in the Go sense, and p.Sym.Local is true for a few
    177 	// internally defined symbols.
    178 	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    179 		// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
    180 		// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
    181 		if p.As != AMOVW {
    182 			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
    183 		}
    184 		if p.To.Type != obj.TYPE_REG {
    185 			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
    186 		}
    187 		p.From.Type = obj.TYPE_MEM
    188 		p.From.Name = obj.NAME_GOTREF
    189 		if p.From.Offset != 0 {
    190 			q := obj.Appendp(ctxt, p)
    191 			q.As = AADD
    192 			q.From.Type = obj.TYPE_CONST
    193 			q.From.Offset = p.From.Offset
    194 			q.To = p.To
    195 			p.From.Offset = 0
    196 		}
    197 	}
    198 	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
    199 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    200 	}
    201 	var source *obj.Addr
    202 	// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
    203 	// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
    204 	// An addition may be inserted between the two MOVs if there is an offset.
    205 	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    206 		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    207 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    208 		}
    209 		source = &p.From
    210 	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    211 		source = &p.To
    212 	} else {
    213 		return
    214 	}
    215 	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    216 		return
    217 	}
    218 	if source.Sym.Type == obj.STLSBSS {
    219 		return
    220 	}
    221 	if source.Type != obj.TYPE_MEM {
    222 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    223 	}
    224 	p1 := obj.Appendp(ctxt, p)
    225 	p2 := obj.Appendp(ctxt, p1)
    226 
    227 	p1.As = AMOVW
    228 	p1.From.Type = obj.TYPE_MEM
    229 	p1.From.Sym = source.Sym
    230 	p1.From.Name = obj.NAME_GOTREF
    231 	p1.To.Type = obj.TYPE_REG
    232 	p1.To.Reg = REG_R9
    233 
    234 	p2.As = p.As
    235 	p2.From = p.From
    236 	p2.To = p.To
    237 	if p.From.Name == obj.NAME_EXTERN {
    238 		p2.From.Reg = REG_R9
    239 		p2.From.Name = obj.NAME_NONE
    240 		p2.From.Sym = nil
    241 	} else if p.To.Name == obj.NAME_EXTERN {
    242 		p2.To.Reg = REG_R9
    243 		p2.To.Name = obj.NAME_NONE
    244 		p2.To.Sym = nil
    245 	} else {
    246 		return
    247 	}
    248 	obj.Nopout(p)
    249 }
    250 
    251 // Prog.mark
    252 const (
    253 	FOLL  = 1 << 0
    254 	LABEL = 1 << 1
    255 	LEAF  = 1 << 2
    256 )
    257 
    258 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    259 	autosize := int32(0)
    260 
    261 	ctxt.Cursym = cursym
    262 
    263 	if cursym.Text == nil || cursym.Text.Link == nil {
    264 		return
    265 	}
    266 
    267 	softfloat(ctxt, cursym)
    268 
    269 	p := cursym.Text
    270 	autoffset := int32(p.To.Offset)
    271 	if autoffset < 0 {
    272 		autoffset = 0
    273 	}
    274 	cursym.Locals = autoffset
    275 	cursym.Args = p.To.Val.(int32)
    276 
    277 	/*
    278 	 * find leaf subroutines
    279 	 * strip NOPs
    280 	 * expand RET
    281 	 * expand BECOME pseudo
    282 	 */
    283 	var q1 *obj.Prog
    284 	var q *obj.Prog
    285 	for p := cursym.Text; p != nil; p = p.Link {
    286 		switch p.As {
    287 		case obj.ATEXT:
    288 			p.Mark |= LEAF
    289 
    290 		case obj.ARET:
    291 			break
    292 
    293 		case ADIV, ADIVU, AMOD, AMODU:
    294 			q = p
    295 			if ctxt.Sym_div == nil {
    296 				initdiv(ctxt)
    297 			}
    298 			cursym.Text.Mark &^= LEAF
    299 			continue
    300 
    301 		case obj.ANOP:
    302 			q1 = p.Link
    303 			q.Link = q1 /* q is non-nop */
    304 			if q1 != nil {
    305 				q1.Mark |= p.Mark
    306 			}
    307 			continue
    308 
    309 		case ABL,
    310 			ABX,
    311 			obj.ADUFFZERO,
    312 			obj.ADUFFCOPY:
    313 			cursym.Text.Mark &^= LEAF
    314 			fallthrough
    315 
    316 		case AB,
    317 			ABEQ,
    318 			ABNE,
    319 			ABCS,
    320 			ABHS,
    321 			ABCC,
    322 			ABLO,
    323 			ABMI,
    324 			ABPL,
    325 			ABVS,
    326 			ABVC,
    327 			ABHI,
    328 			ABLS,
    329 			ABGE,
    330 			ABLT,
    331 			ABGT,
    332 			ABLE:
    333 			q1 = p.Pcond
    334 			if q1 != nil {
    335 				for q1.As == obj.ANOP {
    336 					q1 = q1.Link
    337 					p.Pcond = q1
    338 				}
    339 			}
    340 		}
    341 
    342 		q = p
    343 	}
    344 
    345 	var p1 *obj.Prog
    346 	var p2 *obj.Prog
    347 	var q2 *obj.Prog
    348 	for p := cursym.Text; p != nil; p = p.Link {
    349 		o := p.As
    350 		switch o {
    351 		case obj.ATEXT:
    352 			autosize = int32(p.To.Offset + 4)
    353 			if autosize <= 4 {
    354 				if cursym.Text.Mark&LEAF != 0 {
    355 					p.To.Offset = -4
    356 					autosize = 0
    357 				}
    358 			}
    359 
    360 			if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
    361 				if ctxt.Debugvlog != 0 {
    362 					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
    363 				}
    364 
    365 				cursym.Text.Mark |= LEAF
    366 			}
    367 
    368 			if cursym.Text.Mark&LEAF != 0 {
    369 				cursym.Set(obj.AttrLeaf, true)
    370 				if autosize == 0 {
    371 					break
    372 				}
    373 			}
    374 
    375 			if p.From3.Offset&obj.NOSPLIT == 0 {
    376 				p = stacksplit(ctxt, p, autosize) // emit split check
    377 			}
    378 
    379 			// MOVW.W		R14,$-autosize(SP)
    380 			p = obj.Appendp(ctxt, p)
    381 
    382 			p.As = AMOVW
    383 			p.Scond |= C_WBIT
    384 			p.From.Type = obj.TYPE_REG
    385 			p.From.Reg = REGLINK
    386 			p.To.Type = obj.TYPE_MEM
    387 			p.To.Offset = int64(-autosize)
    388 			p.To.Reg = REGSP
    389 			p.Spadj = autosize
    390 
    391 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    392 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    393 				//
    394 				//	MOVW g_panic(g), R1
    395 				//	CMP $0, R1
    396 				//	B.EQ end
    397 				//	MOVW panic_argp(R1), R2
    398 				//	ADD $(autosize+4), R13, R3
    399 				//	CMP R2, R3
    400 				//	B.NE end
    401 				//	ADD $4, R13, R4
    402 				//	MOVW R4, panic_argp(R1)
    403 				// end:
    404 				//	NOP
    405 				//
    406 				// The NOP is needed to give the jumps somewhere to land.
    407 				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
    408 
    409 				p = obj.Appendp(ctxt, p)
    410 
    411 				p.As = AMOVW
    412 				p.From.Type = obj.TYPE_MEM
    413 				p.From.Reg = REGG
    414 				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
    415 				p.To.Type = obj.TYPE_REG
    416 				p.To.Reg = REG_R1
    417 
    418 				p = obj.Appendp(ctxt, p)
    419 				p.As = ACMP
    420 				p.From.Type = obj.TYPE_CONST
    421 				p.From.Offset = 0
    422 				p.Reg = REG_R1
    423 
    424 				p = obj.Appendp(ctxt, p)
    425 				p.As = ABEQ
    426 				p.To.Type = obj.TYPE_BRANCH
    427 				p1 = p
    428 
    429 				p = obj.Appendp(ctxt, p)
    430 				p.As = AMOVW
    431 				p.From.Type = obj.TYPE_MEM
    432 				p.From.Reg = REG_R1
    433 				p.From.Offset = 0 // Panic.argp
    434 				p.To.Type = obj.TYPE_REG
    435 				p.To.Reg = REG_R2
    436 
    437 				p = obj.Appendp(ctxt, p)
    438 				p.As = AADD
    439 				p.From.Type = obj.TYPE_CONST
    440 				p.From.Offset = int64(autosize) + 4
    441 				p.Reg = REG_R13
    442 				p.To.Type = obj.TYPE_REG
    443 				p.To.Reg = REG_R3
    444 
    445 				p = obj.Appendp(ctxt, p)
    446 				p.As = ACMP
    447 				p.From.Type = obj.TYPE_REG
    448 				p.From.Reg = REG_R2
    449 				p.Reg = REG_R3
    450 
    451 				p = obj.Appendp(ctxt, p)
    452 				p.As = ABNE
    453 				p.To.Type = obj.TYPE_BRANCH
    454 				p2 = p
    455 
    456 				p = obj.Appendp(ctxt, p)
    457 				p.As = AADD
    458 				p.From.Type = obj.TYPE_CONST
    459 				p.From.Offset = 4
    460 				p.Reg = REG_R13
    461 				p.To.Type = obj.TYPE_REG
    462 				p.To.Reg = REG_R4
    463 
    464 				p = obj.Appendp(ctxt, p)
    465 				p.As = AMOVW
    466 				p.From.Type = obj.TYPE_REG
    467 				p.From.Reg = REG_R4
    468 				p.To.Type = obj.TYPE_MEM
    469 				p.To.Reg = REG_R1
    470 				p.To.Offset = 0 // Panic.argp
    471 
    472 				p = obj.Appendp(ctxt, p)
    473 
    474 				p.As = obj.ANOP
    475 				p1.Pcond = p
    476 				p2.Pcond = p
    477 			}
    478 
    479 		case obj.ARET:
    480 			nocache(p)
    481 			if cursym.Text.Mark&LEAF != 0 {
    482 				if autosize == 0 {
    483 					p.As = AB
    484 					p.From = obj.Addr{}
    485 					if p.To.Sym != nil { // retjmp
    486 						p.To.Type = obj.TYPE_BRANCH
    487 					} else {
    488 						p.To.Type = obj.TYPE_MEM
    489 						p.To.Offset = 0
    490 						p.To.Reg = REGLINK
    491 					}
    492 
    493 					break
    494 				}
    495 			}
    496 
    497 			p.As = AMOVW
    498 			p.Scond |= C_PBIT
    499 			p.From.Type = obj.TYPE_MEM
    500 			p.From.Offset = int64(autosize)
    501 			p.From.Reg = REGSP
    502 			p.To.Type = obj.TYPE_REG
    503 			p.To.Reg = REGPC
    504 
    505 			// If there are instructions following
    506 			// this ARET, they come from a branch
    507 			// with the same stackframe, so no spadj.
    508 			if p.To.Sym != nil { // retjmp
    509 				p.To.Reg = REGLINK
    510 				q2 = obj.Appendp(ctxt, p)
    511 				q2.As = AB
    512 				q2.To.Type = obj.TYPE_BRANCH
    513 				q2.To.Sym = p.To.Sym
    514 				p.To.Sym = nil
    515 				p = q2
    516 			}
    517 
    518 		case AADD:
    519 			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    520 				p.Spadj = int32(-p.From.Offset)
    521 			}
    522 
    523 		case ASUB:
    524 			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    525 				p.Spadj = int32(p.From.Offset)
    526 			}
    527 
    528 		case ADIV, ADIVU, AMOD, AMODU:
    529 			if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
    530 				ctxt.Diag("cannot divide in NOSPLIT function")
    531 			}
    532 			if ctxt.Debugdivmod != 0 {
    533 				break
    534 			}
    535 			if p.From.Type != obj.TYPE_REG {
    536 				break
    537 			}
    538 			if p.To.Type != obj.TYPE_REG {
    539 				break
    540 			}
    541 
    542 			// Make copy because we overwrite p below.
    543 			q1 := *p
    544 			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
    545 				ctxt.Diag("div already using REGTMP: %v", p)
    546 			}
    547 
    548 			/* MOV m(g),REGTMP */
    549 			p.As = AMOVW
    550 			p.Lineno = q1.Lineno
    551 			p.From.Type = obj.TYPE_MEM
    552 			p.From.Reg = REGG
    553 			p.From.Offset = 6 * 4 // offset of g.m
    554 			p.Reg = 0
    555 			p.To.Type = obj.TYPE_REG
    556 			p.To.Reg = REGTMP
    557 
    558 			/* MOV a,m_divmod(REGTMP) */
    559 			p = obj.Appendp(ctxt, p)
    560 			p.As = AMOVW
    561 			p.Lineno = q1.Lineno
    562 			p.From.Type = obj.TYPE_REG
    563 			p.From.Reg = q1.From.Reg
    564 			p.To.Type = obj.TYPE_MEM
    565 			p.To.Reg = REGTMP
    566 			p.To.Offset = 8 * 4 // offset of m.divmod
    567 
    568 			/* MOV b, R8 */
    569 			p = obj.Appendp(ctxt, p)
    570 			p.As = AMOVW
    571 			p.Lineno = q1.Lineno
    572 			p.From.Type = obj.TYPE_REG
    573 			p.From.Reg = q1.Reg
    574 			if q1.Reg == 0 {
    575 				p.From.Reg = q1.To.Reg
    576 			}
    577 			p.To.Type = obj.TYPE_REG
    578 			p.To.Reg = REG_R8
    579 			p.To.Offset = 0
    580 
    581 			/* CALL appropriate */
    582 			p = obj.Appendp(ctxt, p)
    583 			p.As = ABL
    584 			p.Lineno = q1.Lineno
    585 			p.To.Type = obj.TYPE_BRANCH
    586 			switch o {
    587 			case ADIV:
    588 				p.To.Sym = ctxt.Sym_div
    589 
    590 			case ADIVU:
    591 				p.To.Sym = ctxt.Sym_divu
    592 
    593 			case AMOD:
    594 				p.To.Sym = ctxt.Sym_mod
    595 
    596 			case AMODU:
    597 				p.To.Sym = ctxt.Sym_modu
    598 			}
    599 
    600 			/* MOV REGTMP, b */
    601 			p = obj.Appendp(ctxt, p)
    602 			p.As = AMOVW
    603 			p.Lineno = q1.Lineno
    604 			p.From.Type = obj.TYPE_REG
    605 			p.From.Reg = REGTMP
    606 			p.From.Offset = 0
    607 			p.To.Type = obj.TYPE_REG
    608 			p.To.Reg = q1.To.Reg
    609 
    610 		case AMOVW:
    611 			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
    612 				p.Spadj = int32(-p.To.Offset)
    613 			}
    614 			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
    615 				p.Spadj = int32(-p.From.Offset)
    616 			}
    617 			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    618 				p.Spadj = int32(-p.From.Offset)
    619 			}
    620 		}
    621 	}
    622 }
    623 
    624 func isfloatreg(a *obj.Addr) bool {
    625 	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
    626 }
    627 
    628 func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
    629 	if obj.GOARM > 5 {
    630 		return
    631 	}
    632 
    633 	symsfloat := obj.Linklookup(ctxt, "_sfloat", 0)
    634 
    635 	wasfloat := 0
    636 	for p := cursym.Text; p != nil; p = p.Link {
    637 		if p.Pcond != nil {
    638 			p.Pcond.Mark |= LABEL
    639 		}
    640 	}
    641 	var next *obj.Prog
    642 	for p := cursym.Text; p != nil; p = p.Link {
    643 		switch p.As {
    644 		case AMOVW:
    645 			if isfloatreg(&p.To) || isfloatreg(&p.From) {
    646 				goto soft
    647 			}
    648 			goto notsoft
    649 
    650 		case AMOVWD,
    651 			AMOVWF,
    652 			AMOVDW,
    653 			AMOVFW,
    654 			AMOVFD,
    655 			AMOVDF,
    656 			AMOVF,
    657 			AMOVD,
    658 			ACMPF,
    659 			ACMPD,
    660 			AADDF,
    661 			AADDD,
    662 			ASUBF,
    663 			ASUBD,
    664 			AMULF,
    665 			AMULD,
    666 			ADIVF,
    667 			ADIVD,
    668 			ASQRTF,
    669 			ASQRTD,
    670 			AABSF,
    671 			AABSD,
    672 			ANEGF,
    673 			ANEGD:
    674 			goto soft
    675 
    676 		default:
    677 			goto notsoft
    678 		}
    679 
    680 	soft:
    681 		if wasfloat == 0 || (p.Mark&LABEL != 0) {
    682 			next = ctxt.NewProg()
    683 			*next = *p
    684 
    685 			// BL _sfloat(SB)
    686 			*p = obj.Prog{}
    687 			p.Ctxt = ctxt
    688 			p.Link = next
    689 			p.As = ABL
    690 			p.To.Type = obj.TYPE_BRANCH
    691 			p.To.Sym = symsfloat
    692 			p.Lineno = next.Lineno
    693 
    694 			p = next
    695 			wasfloat = 1
    696 		}
    697 
    698 		continue
    699 
    700 	notsoft:
    701 		wasfloat = 0
    702 	}
    703 }
    704 
    705 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
    706 	// MOVW			g_stackguard(g), R1
    707 	p = obj.Appendp(ctxt, p)
    708 
    709 	p.As = AMOVW
    710 	p.From.Type = obj.TYPE_MEM
    711 	p.From.Reg = REGG
    712 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
    713 	if ctxt.Cursym.CFunc() {
    714 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
    715 	}
    716 	p.To.Type = obj.TYPE_REG
    717 	p.To.Reg = REG_R1
    718 
    719 	if framesize <= obj.StackSmall {
    720 		// small stack: SP < stackguard
    721 		//	CMP	stackguard, SP
    722 		p = obj.Appendp(ctxt, p)
    723 
    724 		p.As = ACMP
    725 		p.From.Type = obj.TYPE_REG
    726 		p.From.Reg = REG_R1
    727 		p.Reg = REGSP
    728 	} else if framesize <= obj.StackBig {
    729 		// large stack: SP-framesize < stackguard-StackSmall
    730 		//	MOVW $-framesize(SP), R2
    731 		//	CMP stackguard, R2
    732 		p = obj.Appendp(ctxt, p)
    733 
    734 		p.As = AMOVW
    735 		p.From.Type = obj.TYPE_ADDR
    736 		p.From.Reg = REGSP
    737 		p.From.Offset = int64(-framesize)
    738 		p.To.Type = obj.TYPE_REG
    739 		p.To.Reg = REG_R2
    740 
    741 		p = obj.Appendp(ctxt, p)
    742 		p.As = ACMP
    743 		p.From.Type = obj.TYPE_REG
    744 		p.From.Reg = REG_R1
    745 		p.Reg = REG_R2
    746 	} else {
    747 		// Such a large stack we need to protect against wraparound
    748 		// if SP is close to zero.
    749 		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
    750 		// The +StackGuard on both sides is required to keep the left side positive:
    751 		// SP is allowed to be slightly below stackguard. See stack.h.
    752 		//	CMP $StackPreempt, R1
    753 		//	MOVW.NE $StackGuard(SP), R2
    754 		//	SUB.NE R1, R2
    755 		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
    756 		//	CMP.NE R3, R2
    757 		p = obj.Appendp(ctxt, p)
    758 
    759 		p.As = ACMP
    760 		p.From.Type = obj.TYPE_CONST
    761 		p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
    762 		p.Reg = REG_R1
    763 
    764 		p = obj.Appendp(ctxt, p)
    765 		p.As = AMOVW
    766 		p.From.Type = obj.TYPE_ADDR
    767 		p.From.Reg = REGSP
    768 		p.From.Offset = obj.StackGuard
    769 		p.To.Type = obj.TYPE_REG
    770 		p.To.Reg = REG_R2
    771 		p.Scond = C_SCOND_NE
    772 
    773 		p = obj.Appendp(ctxt, p)
    774 		p.As = ASUB
    775 		p.From.Type = obj.TYPE_REG
    776 		p.From.Reg = REG_R1
    777 		p.To.Type = obj.TYPE_REG
    778 		p.To.Reg = REG_R2
    779 		p.Scond = C_SCOND_NE
    780 
    781 		p = obj.Appendp(ctxt, p)
    782 		p.As = AMOVW
    783 		p.From.Type = obj.TYPE_ADDR
    784 		p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
    785 		p.To.Type = obj.TYPE_REG
    786 		p.To.Reg = REG_R3
    787 		p.Scond = C_SCOND_NE
    788 
    789 		p = obj.Appendp(ctxt, p)
    790 		p.As = ACMP
    791 		p.From.Type = obj.TYPE_REG
    792 		p.From.Reg = REG_R3
    793 		p.Reg = REG_R2
    794 		p.Scond = C_SCOND_NE
    795 	}
    796 
    797 	// BLS call-to-morestack
    798 	bls := obj.Appendp(ctxt, p)
    799 	bls.As = ABLS
    800 	bls.To.Type = obj.TYPE_BRANCH
    801 
    802 	var last *obj.Prog
    803 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
    804 	}
    805 
    806 	// Now we are at the end of the function, but logically
    807 	// we are still in function prologue. We need to fix the
    808 	// SP data and PCDATA.
    809 	spfix := obj.Appendp(ctxt, last)
    810 	spfix.As = obj.ANOP
    811 	spfix.Spadj = -framesize
    812 
    813 	pcdata := obj.Appendp(ctxt, spfix)
    814 	pcdata.Lineno = ctxt.Cursym.Text.Lineno
    815 	pcdata.Mode = ctxt.Cursym.Text.Mode
    816 	pcdata.As = obj.APCDATA
    817 	pcdata.From.Type = obj.TYPE_CONST
    818 	pcdata.From.Offset = obj.PCDATA_StackMapIndex
    819 	pcdata.To.Type = obj.TYPE_CONST
    820 	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
    821 
    822 	// MOVW	LR, R3
    823 	movw := obj.Appendp(ctxt, pcdata)
    824 	movw.As = AMOVW
    825 	movw.From.Type = obj.TYPE_REG
    826 	movw.From.Reg = REGLINK
    827 	movw.To.Type = obj.TYPE_REG
    828 	movw.To.Reg = REG_R3
    829 
    830 	bls.Pcond = movw
    831 
    832 	// BL runtime.morestack
    833 	call := obj.Appendp(ctxt, movw)
    834 	call.As = obj.ACALL
    835 	call.To.Type = obj.TYPE_BRANCH
    836 	morestack := "runtime.morestack"
    837 	switch {
    838 	case ctxt.Cursym.CFunc():
    839 		morestack = "runtime.morestackc"
    840 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
    841 		morestack = "runtime.morestack_noctxt"
    842 	}
    843 	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
    844 
    845 	// B start
    846 	b := obj.Appendp(ctxt, call)
    847 	b.As = obj.AJMP
    848 	b.To.Type = obj.TYPE_BRANCH
    849 	b.Pcond = ctxt.Cursym.Text.Link
    850 	b.Spadj = +framesize
    851 
    852 	return bls
    853 }
    854 
    855 func initdiv(ctxt *obj.Link) {
    856 	if ctxt.Sym_div != nil {
    857 		return
    858 	}
    859 	ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0)
    860 	ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0)
    861 	ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0)
    862 	ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0)
    863 }
    864 
    865 func follow(ctxt *obj.Link, s *obj.LSym) {
    866 	ctxt.Cursym = s
    867 
    868 	firstp := ctxt.NewProg()
    869 	lastp := firstp
    870 	xfol(ctxt, s.Text, &lastp)
    871 	lastp.Link = nil
    872 	s.Text = firstp.Link
    873 }
    874 
    875 func relinv(a obj.As) obj.As {
    876 	switch a {
    877 	case ABEQ:
    878 		return ABNE
    879 	case ABNE:
    880 		return ABEQ
    881 	case ABCS:
    882 		return ABCC
    883 	case ABHS:
    884 		return ABLO
    885 	case ABCC:
    886 		return ABCS
    887 	case ABLO:
    888 		return ABHS
    889 	case ABMI:
    890 		return ABPL
    891 	case ABPL:
    892 		return ABMI
    893 	case ABVS:
    894 		return ABVC
    895 	case ABVC:
    896 		return ABVS
    897 	case ABHI:
    898 		return ABLS
    899 	case ABLS:
    900 		return ABHI
    901 	case ABGE:
    902 		return ABLT
    903 	case ABLT:
    904 		return ABGE
    905 	case ABGT:
    906 		return ABLE
    907 	case ABLE:
    908 		return ABGT
    909 	}
    910 
    911 	log.Fatalf("unknown relation: %s", Anames[a])
    912 	return 0
    913 }
    914 
    915 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
    916 	var q *obj.Prog
    917 	var r *obj.Prog
    918 	var i int
    919 
    920 loop:
    921 	if p == nil {
    922 		return
    923 	}
    924 	a := p.As
    925 	if a == AB {
    926 		q = p.Pcond
    927 		if q != nil && q.As != obj.ATEXT {
    928 			p.Mark |= FOLL
    929 			p = q
    930 			if p.Mark&FOLL == 0 {
    931 				goto loop
    932 			}
    933 		}
    934 	}
    935 
    936 	if p.Mark&FOLL != 0 {
    937 		i = 0
    938 		q = p
    939 		for ; i < 4; i, q = i+1, q.Link {
    940 			if q == *last || q == nil {
    941 				break
    942 			}
    943 			a = q.As
    944 			if a == obj.ANOP {
    945 				i--
    946 				continue
    947 			}
    948 
    949 			if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
    950 				goto copy
    951 			}
    952 			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
    953 				continue
    954 			}
    955 			if a != ABEQ && a != ABNE {
    956 				continue
    957 			}
    958 
    959 		copy:
    960 			for {
    961 				r = ctxt.NewProg()
    962 				*r = *p
    963 				if r.Mark&FOLL == 0 {
    964 					fmt.Printf("can't happen 1\n")
    965 				}
    966 				r.Mark |= FOLL
    967 				if p != q {
    968 					p = p.Link
    969 					(*last).Link = r
    970 					*last = r
    971 					continue
    972 				}
    973 
    974 				(*last).Link = r
    975 				*last = r
    976 				if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
    977 					return
    978 				}
    979 				r.As = ABNE
    980 				if a == ABNE {
    981 					r.As = ABEQ
    982 				}
    983 				r.Pcond = p.Link
    984 				r.Link = p.Pcond
    985 				if r.Link.Mark&FOLL == 0 {
    986 					xfol(ctxt, r.Link, last)
    987 				}
    988 				if r.Pcond.Mark&FOLL == 0 {
    989 					fmt.Printf("can't happen 2\n")
    990 				}
    991 				return
    992 			}
    993 		}
    994 
    995 		a = AB
    996 		q = ctxt.NewProg()
    997 		q.As = a
    998 		q.Lineno = p.Lineno
    999 		q.To.Type = obj.TYPE_BRANCH
   1000 		q.To.Offset = p.Pc
   1001 		q.Pcond = p
   1002 		p = q
   1003 	}
   1004 
   1005 	p.Mark |= FOLL
   1006 	(*last).Link = p
   1007 	*last = p
   1008 	if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   1009 		return
   1010 	}
   1011 
   1012 	if p.Pcond != nil {
   1013 		if a != ABL && a != ABX && p.Link != nil {
   1014 			q = obj.Brchain(ctxt, p.Link)
   1015 			if a != obj.ATEXT {
   1016 				if q != nil && (q.Mark&FOLL != 0) {
   1017 					p.As = relinv(a)
   1018 					p.Link = p.Pcond
   1019 					p.Pcond = q
   1020 				}
   1021 			}
   1022 
   1023 			xfol(ctxt, p.Link, last)
   1024 			q = obj.Brchain(ctxt, p.Pcond)
   1025 			if q == nil {
   1026 				q = p.Pcond
   1027 			}
   1028 			if q.Mark&FOLL != 0 {
   1029 				p.Pcond = q
   1030 				return
   1031 			}
   1032 
   1033 			p = q
   1034 			goto loop
   1035 		}
   1036 	}
   1037 
   1038 	p = p.Link
   1039 	goto loop
   1040 }
   1041 
   1042 var unaryDst = map[obj.As]bool{
   1043 	ASWI:  true,
   1044 	AWORD: true,
   1045 }
   1046 
   1047 var Linkarm = obj.LinkArch{
   1048 	Arch:       sys.ArchARM,
   1049 	Preprocess: preprocess,
   1050 	Assemble:   span5,
   1051 	Follow:     follow,
   1052 	Progedit:   progedit,
   1053 	UnaryDst:   unaryDst,
   1054 }
   1055