Home | History | Annotate | Download | only in arm
      1 // Derived from Inferno utils/5c/swt.c
      2 // http://code.google.com/p/inferno-os/source/browse/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 	"encoding/binary"
     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 procesors.
     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 rewriten 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 ctxt.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_shared != 0 {
    134 		// Shared libraries use R_ARM_TLS_IE32 instead of
    135 		// R_ARM_TLS_LE32, replacing the link time constant TLS offset in
    136 		// runtime.tlsg with an address to a GOT entry containing the
    137 		// offset. Rewrite $runtime.tlsg(SB) to runtime.tlsg(SB) to
    138 		// compensate.
    139 		if ctxt.Tlsg == nil {
    140 			ctxt.Tlsg = obj.Linklookup(ctxt, "runtime.tlsg", 0)
    141 		}
    142 
    143 		if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && p.From.Sym == ctxt.Tlsg {
    144 			p.From.Type = obj.TYPE_MEM
    145 		}
    146 		if p.To.Type == obj.TYPE_ADDR && p.To.Name == obj.NAME_EXTERN && p.To.Sym == ctxt.Tlsg {
    147 			p.To.Type = obj.TYPE_MEM
    148 		}
    149 	}
    150 }
    151 
    152 // Prog.mark
    153 const (
    154 	FOLL  = 1 << 0
    155 	LABEL = 1 << 1
    156 	LEAF  = 1 << 2
    157 )
    158 
    159 func linkcase(casep *obj.Prog) {
    160 	for p := casep; p != nil; p = p.Link {
    161 		if p.As == ABCASE {
    162 			for ; p != nil && p.As == ABCASE; p = p.Link {
    163 				p.Rel = casep
    164 			}
    165 			break
    166 		}
    167 	}
    168 }
    169 
    170 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    171 	autosize := int32(0)
    172 
    173 	ctxt.Cursym = cursym
    174 
    175 	if cursym.Text == nil || cursym.Text.Link == nil {
    176 		return
    177 	}
    178 
    179 	softfloat(ctxt, cursym)
    180 
    181 	p := cursym.Text
    182 	autoffset := int32(p.To.Offset)
    183 	if autoffset < 0 {
    184 		autoffset = 0
    185 	}
    186 	cursym.Locals = autoffset
    187 	cursym.Args = p.To.Val.(int32)
    188 
    189 	if ctxt.Debugzerostack != 0 {
    190 		if autoffset != 0 && p.From3.Offset&obj.NOSPLIT == 0 {
    191 			// MOVW $4(R13), R1
    192 			p = obj.Appendp(ctxt, p)
    193 
    194 			p.As = AMOVW
    195 			p.From.Type = obj.TYPE_ADDR
    196 			p.From.Reg = REG_R13
    197 			p.From.Offset = 4
    198 			p.To.Type = obj.TYPE_REG
    199 			p.To.Reg = REG_R1
    200 
    201 			// MOVW $n(R13), R2
    202 			p = obj.Appendp(ctxt, p)
    203 
    204 			p.As = AMOVW
    205 			p.From.Type = obj.TYPE_ADDR
    206 			p.From.Reg = REG_R13
    207 			p.From.Offset = 4 + int64(autoffset)
    208 			p.To.Type = obj.TYPE_REG
    209 			p.To.Reg = REG_R2
    210 
    211 			// MOVW $0, R3
    212 			p = obj.Appendp(ctxt, p)
    213 
    214 			p.As = AMOVW
    215 			p.From.Type = obj.TYPE_CONST
    216 			p.From.Offset = 0
    217 			p.To.Type = obj.TYPE_REG
    218 			p.To.Reg = REG_R3
    219 
    220 			// L:
    221 			//	MOVW.nil R3, 0(R1) +4
    222 			//	CMP R1, R2
    223 			//	BNE L
    224 			pl := obj.Appendp(ctxt, p)
    225 			p := pl
    226 
    227 			p.As = AMOVW
    228 			p.From.Type = obj.TYPE_REG
    229 			p.From.Reg = REG_R3
    230 			p.To.Type = obj.TYPE_MEM
    231 			p.To.Reg = REG_R1
    232 			p.To.Offset = 4
    233 			p.Scond |= C_PBIT
    234 
    235 			p = obj.Appendp(ctxt, p)
    236 			p.As = ACMP
    237 			p.From.Type = obj.TYPE_REG
    238 			p.From.Reg = REG_R1
    239 			p.Reg = REG_R2
    240 
    241 			p = obj.Appendp(ctxt, p)
    242 			p.As = ABNE
    243 			p.To.Type = obj.TYPE_BRANCH
    244 			p.Pcond = pl
    245 		}
    246 	}
    247 
    248 	/*
    249 	 * find leaf subroutines
    250 	 * strip NOPs
    251 	 * expand RET
    252 	 * expand BECOME pseudo
    253 	 */
    254 	var q1 *obj.Prog
    255 	var q *obj.Prog
    256 	for p := cursym.Text; p != nil; p = p.Link {
    257 		switch p.As {
    258 		case ACASE:
    259 			if ctxt.Flag_shared != 0 {
    260 				linkcase(p)
    261 			}
    262 
    263 		case obj.ATEXT:
    264 			p.Mark |= LEAF
    265 
    266 		case obj.ARET:
    267 			break
    268 
    269 		case ADIV, ADIVU, AMOD, AMODU:
    270 			q = p
    271 			if ctxt.Sym_div == nil {
    272 				initdiv(ctxt)
    273 			}
    274 			cursym.Text.Mark &^= LEAF
    275 			continue
    276 
    277 		case obj.ANOP:
    278 			q1 = p.Link
    279 			q.Link = q1 /* q is non-nop */
    280 			if q1 != nil {
    281 				q1.Mark |= p.Mark
    282 			}
    283 			continue
    284 
    285 		case ABL,
    286 			ABX,
    287 			obj.ADUFFZERO,
    288 			obj.ADUFFCOPY:
    289 			cursym.Text.Mark &^= LEAF
    290 			fallthrough
    291 
    292 		case ABCASE,
    293 			AB,
    294 			ABEQ,
    295 			ABNE,
    296 			ABCS,
    297 			ABHS,
    298 			ABCC,
    299 			ABLO,
    300 			ABMI,
    301 			ABPL,
    302 			ABVS,
    303 			ABVC,
    304 			ABHI,
    305 			ABLS,
    306 			ABGE,
    307 			ABLT,
    308 			ABGT,
    309 			ABLE:
    310 			q1 = p.Pcond
    311 			if q1 != nil {
    312 				for q1.As == obj.ANOP {
    313 					q1 = q1.Link
    314 					p.Pcond = q1
    315 				}
    316 			}
    317 		}
    318 
    319 		q = p
    320 	}
    321 
    322 	var o int
    323 	var p1 *obj.Prog
    324 	var p2 *obj.Prog
    325 	var q2 *obj.Prog
    326 	for p := cursym.Text; p != nil; p = p.Link {
    327 		o = int(p.As)
    328 		switch o {
    329 		case obj.ATEXT:
    330 			autosize = int32(p.To.Offset + 4)
    331 			if autosize <= 4 {
    332 				if cursym.Text.Mark&LEAF != 0 {
    333 					p.To.Offset = -4
    334 					autosize = 0
    335 				}
    336 			}
    337 
    338 			if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
    339 				if ctxt.Debugvlog != 0 {
    340 					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
    341 					ctxt.Bso.Flush()
    342 				}
    343 
    344 				cursym.Text.Mark |= LEAF
    345 			}
    346 
    347 			if cursym.Text.Mark&LEAF != 0 {
    348 				cursym.Leaf = 1
    349 				if autosize == 0 {
    350 					break
    351 				}
    352 			}
    353 
    354 			if p.From3.Offset&obj.NOSPLIT == 0 {
    355 				p = stacksplit(ctxt, p, autosize) // emit split check
    356 			}
    357 
    358 			// MOVW.W		R14,$-autosize(SP)
    359 			p = obj.Appendp(ctxt, p)
    360 
    361 			p.As = AMOVW
    362 			p.Scond |= C_WBIT
    363 			p.From.Type = obj.TYPE_REG
    364 			p.From.Reg = REGLINK
    365 			p.To.Type = obj.TYPE_MEM
    366 			p.To.Offset = int64(-autosize)
    367 			p.To.Reg = REGSP
    368 			p.Spadj = autosize
    369 
    370 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    371 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    372 				//
    373 				//	MOVW g_panic(g), R1
    374 				//	CMP $0, R1
    375 				//	B.EQ end
    376 				//	MOVW panic_argp(R1), R2
    377 				//	ADD $(autosize+4), R13, R3
    378 				//	CMP R2, R3
    379 				//	B.NE end
    380 				//	ADD $4, R13, R4
    381 				//	MOVW R4, panic_argp(R1)
    382 				// end:
    383 				//	NOP
    384 				//
    385 				// The NOP is needed to give the jumps somewhere to land.
    386 				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
    387 
    388 				p = obj.Appendp(ctxt, p)
    389 
    390 				p.As = AMOVW
    391 				p.From.Type = obj.TYPE_MEM
    392 				p.From.Reg = REGG
    393 				p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
    394 				p.To.Type = obj.TYPE_REG
    395 				p.To.Reg = REG_R1
    396 
    397 				p = obj.Appendp(ctxt, p)
    398 				p.As = ACMP
    399 				p.From.Type = obj.TYPE_CONST
    400 				p.From.Offset = 0
    401 				p.Reg = REG_R1
    402 
    403 				p = obj.Appendp(ctxt, p)
    404 				p.As = ABEQ
    405 				p.To.Type = obj.TYPE_BRANCH
    406 				p1 = p
    407 
    408 				p = obj.Appendp(ctxt, p)
    409 				p.As = AMOVW
    410 				p.From.Type = obj.TYPE_MEM
    411 				p.From.Reg = REG_R1
    412 				p.From.Offset = 0 // Panic.argp
    413 				p.To.Type = obj.TYPE_REG
    414 				p.To.Reg = REG_R2
    415 
    416 				p = obj.Appendp(ctxt, p)
    417 				p.As = AADD
    418 				p.From.Type = obj.TYPE_CONST
    419 				p.From.Offset = int64(autosize) + 4
    420 				p.Reg = REG_R13
    421 				p.To.Type = obj.TYPE_REG
    422 				p.To.Reg = REG_R3
    423 
    424 				p = obj.Appendp(ctxt, p)
    425 				p.As = ACMP
    426 				p.From.Type = obj.TYPE_REG
    427 				p.From.Reg = REG_R2
    428 				p.Reg = REG_R3
    429 
    430 				p = obj.Appendp(ctxt, p)
    431 				p.As = ABNE
    432 				p.To.Type = obj.TYPE_BRANCH
    433 				p2 = p
    434 
    435 				p = obj.Appendp(ctxt, p)
    436 				p.As = AADD
    437 				p.From.Type = obj.TYPE_CONST
    438 				p.From.Offset = 4
    439 				p.Reg = REG_R13
    440 				p.To.Type = obj.TYPE_REG
    441 				p.To.Reg = REG_R4
    442 
    443 				p = obj.Appendp(ctxt, p)
    444 				p.As = AMOVW
    445 				p.From.Type = obj.TYPE_REG
    446 				p.From.Reg = REG_R4
    447 				p.To.Type = obj.TYPE_MEM
    448 				p.To.Reg = REG_R1
    449 				p.To.Offset = 0 // Panic.argp
    450 
    451 				p = obj.Appendp(ctxt, p)
    452 
    453 				p.As = obj.ANOP
    454 				p1.Pcond = p
    455 				p2.Pcond = p
    456 			}
    457 
    458 		case obj.ARET:
    459 			obj.Nocache(p)
    460 			if cursym.Text.Mark&LEAF != 0 {
    461 				if autosize == 0 {
    462 					p.As = AB
    463 					p.From = obj.Addr{}
    464 					if p.To.Sym != nil { // retjmp
    465 						p.To.Type = obj.TYPE_BRANCH
    466 					} else {
    467 						p.To.Type = obj.TYPE_MEM
    468 						p.To.Offset = 0
    469 						p.To.Reg = REGLINK
    470 					}
    471 
    472 					break
    473 				}
    474 			}
    475 
    476 			p.As = AMOVW
    477 			p.Scond |= C_PBIT
    478 			p.From.Type = obj.TYPE_MEM
    479 			p.From.Offset = int64(autosize)
    480 			p.From.Reg = REGSP
    481 			p.To.Type = obj.TYPE_REG
    482 			p.To.Reg = REGPC
    483 
    484 			// If there are instructions following
    485 			// this ARET, they come from a branch
    486 			// with the same stackframe, so no spadj.
    487 			if p.To.Sym != nil { // retjmp
    488 				p.To.Reg = REGLINK
    489 				q2 = obj.Appendp(ctxt, p)
    490 				q2.As = AB
    491 				q2.To.Type = obj.TYPE_BRANCH
    492 				q2.To.Sym = p.To.Sym
    493 				p.To.Sym = nil
    494 				p = q2
    495 			}
    496 
    497 		case AADD:
    498 			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    499 				p.Spadj = int32(-p.From.Offset)
    500 			}
    501 
    502 		case ASUB:
    503 			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    504 				p.Spadj = int32(p.From.Offset)
    505 			}
    506 
    507 		case ADIV, ADIVU, AMOD, AMODU:
    508 			if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
    509 				ctxt.Diag("cannot divide in NOSPLIT function")
    510 			}
    511 			if ctxt.Debugdivmod != 0 {
    512 				break
    513 			}
    514 			if p.From.Type != obj.TYPE_REG {
    515 				break
    516 			}
    517 			if p.To.Type != obj.TYPE_REG {
    518 				break
    519 			}
    520 
    521 			// Make copy because we overwrite p below.
    522 			q1 := *p
    523 			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
    524 				ctxt.Diag("div already using REGTMP: %v", p)
    525 			}
    526 
    527 			/* MOV m(g),REGTMP */
    528 			p.As = AMOVW
    529 			p.Lineno = q1.Lineno
    530 			p.From.Type = obj.TYPE_MEM
    531 			p.From.Reg = REGG
    532 			p.From.Offset = 6 * 4 // offset of g.m
    533 			p.Reg = 0
    534 			p.To.Type = obj.TYPE_REG
    535 			p.To.Reg = REGTMP
    536 
    537 			/* MOV a,m_divmod(REGTMP) */
    538 			p = obj.Appendp(ctxt, p)
    539 			p.As = AMOVW
    540 			p.Lineno = q1.Lineno
    541 			p.From.Type = obj.TYPE_REG
    542 			p.From.Reg = q1.From.Reg
    543 			p.To.Type = obj.TYPE_MEM
    544 			p.To.Reg = REGTMP
    545 			p.To.Offset = 8 * 4 // offset of m.divmod
    546 
    547 			/* MOV b,REGTMP */
    548 			p = obj.Appendp(ctxt, p)
    549 			p.As = AMOVW
    550 			p.Lineno = q1.Lineno
    551 			p.From.Type = obj.TYPE_REG
    552 			p.From.Reg = q1.Reg
    553 			if q1.Reg == 0 {
    554 				p.From.Reg = q1.To.Reg
    555 			}
    556 			p.To.Type = obj.TYPE_REG
    557 			p.To.Reg = REGTMP
    558 			p.To.Offset = 0
    559 
    560 			/* CALL appropriate */
    561 			p = obj.Appendp(ctxt, p)
    562 			p.As = ABL
    563 			p.Lineno = q1.Lineno
    564 			p.To.Type = obj.TYPE_BRANCH
    565 			switch o {
    566 			case ADIV:
    567 				p.To.Sym = ctxt.Sym_div
    568 
    569 			case ADIVU:
    570 				p.To.Sym = ctxt.Sym_divu
    571 
    572 			case AMOD:
    573 				p.To.Sym = ctxt.Sym_mod
    574 
    575 			case AMODU:
    576 				p.To.Sym = ctxt.Sym_modu
    577 			}
    578 
    579 			/* MOV REGTMP, b */
    580 			p = obj.Appendp(ctxt, p)
    581 			p.As = AMOVW
    582 			p.Lineno = q1.Lineno
    583 			p.From.Type = obj.TYPE_REG
    584 			p.From.Reg = REGTMP
    585 			p.From.Offset = 0
    586 			p.To.Type = obj.TYPE_REG
    587 			p.To.Reg = q1.To.Reg
    588 
    589 		case AMOVW:
    590 			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
    591 				p.Spadj = int32(-p.To.Offset)
    592 			}
    593 			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
    594 				p.Spadj = int32(-p.From.Offset)
    595 			}
    596 			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
    597 				p.Spadj = int32(-p.From.Offset)
    598 			}
    599 		}
    600 	}
    601 }
    602 
    603 func isfloatreg(a *obj.Addr) bool {
    604 	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
    605 }
    606 
    607 func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
    608 	if ctxt.Goarm > 5 {
    609 		return
    610 	}
    611 
    612 	symsfloat := obj.Linklookup(ctxt, "_sfloat", 0)
    613 
    614 	wasfloat := 0
    615 	for p := cursym.Text; p != nil; p = p.Link {
    616 		if p.Pcond != nil {
    617 			p.Pcond.Mark |= LABEL
    618 		}
    619 	}
    620 	var next *obj.Prog
    621 	for p := cursym.Text; p != nil; p = p.Link {
    622 		switch p.As {
    623 		case AMOVW:
    624 			if isfloatreg(&p.To) || isfloatreg(&p.From) {
    625 				goto soft
    626 			}
    627 			goto notsoft
    628 
    629 		case AMOVWD,
    630 			AMOVWF,
    631 			AMOVDW,
    632 			AMOVFW,
    633 			AMOVFD,
    634 			AMOVDF,
    635 			AMOVF,
    636 			AMOVD,
    637 			ACMPF,
    638 			ACMPD,
    639 			AADDF,
    640 			AADDD,
    641 			ASUBF,
    642 			ASUBD,
    643 			AMULF,
    644 			AMULD,
    645 			ADIVF,
    646 			ADIVD,
    647 			ASQRTF,
    648 			ASQRTD,
    649 			AABSF,
    650 			AABSD:
    651 			goto soft
    652 
    653 		default:
    654 			goto notsoft
    655 		}
    656 
    657 	soft:
    658 		if wasfloat == 0 || (p.Mark&LABEL != 0) {
    659 			next = ctxt.NewProg()
    660 			*next = *p
    661 
    662 			// BL _sfloat(SB)
    663 			*p = obj.Prog{}
    664 			p.Ctxt = ctxt
    665 			p.Link = next
    666 			p.As = ABL
    667 			p.To.Type = obj.TYPE_BRANCH
    668 			p.To.Sym = symsfloat
    669 			p.Lineno = next.Lineno
    670 
    671 			p = next
    672 			wasfloat = 1
    673 		}
    674 
    675 		continue
    676 
    677 	notsoft:
    678 		wasfloat = 0
    679 	}
    680 }
    681 
    682 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
    683 	// MOVW			g_stackguard(g), R1
    684 	p = obj.Appendp(ctxt, p)
    685 
    686 	p.As = AMOVW
    687 	p.From.Type = obj.TYPE_MEM
    688 	p.From.Reg = REGG
    689 	p.From.Offset = 2 * int64(ctxt.Arch.Ptrsize) // G.stackguard0
    690 	if ctxt.Cursym.Cfunc != 0 {
    691 		p.From.Offset = 3 * int64(ctxt.Arch.Ptrsize) // G.stackguard1
    692 	}
    693 	p.To.Type = obj.TYPE_REG
    694 	p.To.Reg = REG_R1
    695 
    696 	if framesize <= obj.StackSmall {
    697 		// small stack: SP < stackguard
    698 		//	CMP	stackguard, SP
    699 		p = obj.Appendp(ctxt, p)
    700 
    701 		p.As = ACMP
    702 		p.From.Type = obj.TYPE_REG
    703 		p.From.Reg = REG_R1
    704 		p.Reg = REGSP
    705 	} else if framesize <= obj.StackBig {
    706 		// large stack: SP-framesize < stackguard-StackSmall
    707 		//	MOVW $-framesize(SP), R2
    708 		//	CMP stackguard, R2
    709 		p = obj.Appendp(ctxt, p)
    710 
    711 		p.As = AMOVW
    712 		p.From.Type = obj.TYPE_ADDR
    713 		p.From.Reg = REGSP
    714 		p.From.Offset = int64(-framesize)
    715 		p.To.Type = obj.TYPE_REG
    716 		p.To.Reg = REG_R2
    717 
    718 		p = obj.Appendp(ctxt, p)
    719 		p.As = ACMP
    720 		p.From.Type = obj.TYPE_REG
    721 		p.From.Reg = REG_R1
    722 		p.Reg = REG_R2
    723 	} else {
    724 		// Such a large stack we need to protect against wraparound
    725 		// if SP is close to zero.
    726 		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
    727 		// The +StackGuard on both sides is required to keep the left side positive:
    728 		// SP is allowed to be slightly below stackguard. See stack.h.
    729 		//	CMP $StackPreempt, R1
    730 		//	MOVW.NE $StackGuard(SP), R2
    731 		//	SUB.NE R1, R2
    732 		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
    733 		//	CMP.NE R3, R2
    734 		p = obj.Appendp(ctxt, p)
    735 
    736 		p.As = ACMP
    737 		p.From.Type = obj.TYPE_CONST
    738 		p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
    739 		p.Reg = REG_R1
    740 
    741 		p = obj.Appendp(ctxt, p)
    742 		p.As = AMOVW
    743 		p.From.Type = obj.TYPE_ADDR
    744 		p.From.Reg = REGSP
    745 		p.From.Offset = obj.StackGuard
    746 		p.To.Type = obj.TYPE_REG
    747 		p.To.Reg = REG_R2
    748 		p.Scond = C_SCOND_NE
    749 
    750 		p = obj.Appendp(ctxt, p)
    751 		p.As = ASUB
    752 		p.From.Type = obj.TYPE_REG
    753 		p.From.Reg = REG_R1
    754 		p.To.Type = obj.TYPE_REG
    755 		p.To.Reg = REG_R2
    756 		p.Scond = C_SCOND_NE
    757 
    758 		p = obj.Appendp(ctxt, p)
    759 		p.As = AMOVW
    760 		p.From.Type = obj.TYPE_ADDR
    761 		p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
    762 		p.To.Type = obj.TYPE_REG
    763 		p.To.Reg = REG_R3
    764 		p.Scond = C_SCOND_NE
    765 
    766 		p = obj.Appendp(ctxt, p)
    767 		p.As = ACMP
    768 		p.From.Type = obj.TYPE_REG
    769 		p.From.Reg = REG_R3
    770 		p.Reg = REG_R2
    771 		p.Scond = C_SCOND_NE
    772 	}
    773 
    774 	// BLS call-to-morestack
    775 	bls := obj.Appendp(ctxt, p)
    776 	bls.As = ABLS
    777 	bls.To.Type = obj.TYPE_BRANCH
    778 
    779 	var last *obj.Prog
    780 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
    781 	}
    782 
    783 	// MOVW	LR, R3
    784 	movw := obj.Appendp(ctxt, last)
    785 	movw.As = AMOVW
    786 	movw.From.Type = obj.TYPE_REG
    787 	movw.From.Reg = REGLINK
    788 	movw.To.Type = obj.TYPE_REG
    789 	movw.To.Reg = REG_R3
    790 
    791 	bls.Pcond = movw
    792 
    793 	// BL runtime.morestack
    794 	call := obj.Appendp(ctxt, movw)
    795 	call.As = obj.ACALL
    796 	call.To.Type = obj.TYPE_BRANCH
    797 	morestack := "runtime.morestack"
    798 	switch {
    799 	case ctxt.Cursym.Cfunc != 0:
    800 		morestack = "runtime.morestackc"
    801 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
    802 		morestack = "runtime.morestack_noctxt"
    803 	}
    804 	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
    805 
    806 	// B start
    807 	b := obj.Appendp(ctxt, call)
    808 	b.As = obj.AJMP
    809 	b.To.Type = obj.TYPE_BRANCH
    810 	b.Pcond = ctxt.Cursym.Text.Link
    811 
    812 	return bls
    813 }
    814 
    815 func initdiv(ctxt *obj.Link) {
    816 	if ctxt.Sym_div != nil {
    817 		return
    818 	}
    819 	ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0)
    820 	ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0)
    821 	ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0)
    822 	ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0)
    823 }
    824 
    825 func follow(ctxt *obj.Link, s *obj.LSym) {
    826 	ctxt.Cursym = s
    827 
    828 	firstp := ctxt.NewProg()
    829 	lastp := firstp
    830 	xfol(ctxt, s.Text, &lastp)
    831 	lastp.Link = nil
    832 	s.Text = firstp.Link
    833 }
    834 
    835 func relinv(a int) int {
    836 	switch a {
    837 	case ABEQ:
    838 		return ABNE
    839 	case ABNE:
    840 		return ABEQ
    841 	case ABCS:
    842 		return ABCC
    843 	case ABHS:
    844 		return ABLO
    845 	case ABCC:
    846 		return ABCS
    847 	case ABLO:
    848 		return ABHS
    849 	case ABMI:
    850 		return ABPL
    851 	case ABPL:
    852 		return ABMI
    853 	case ABVS:
    854 		return ABVC
    855 	case ABVC:
    856 		return ABVS
    857 	case ABHI:
    858 		return ABLS
    859 	case ABLS:
    860 		return ABHI
    861 	case ABGE:
    862 		return ABLT
    863 	case ABLT:
    864 		return ABGE
    865 	case ABGT:
    866 		return ABLE
    867 	case ABLE:
    868 		return ABGT
    869 	}
    870 
    871 	log.Fatalf("unknown relation: %s", Anames[a])
    872 	return 0
    873 }
    874 
    875 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
    876 	var q *obj.Prog
    877 	var r *obj.Prog
    878 	var a int
    879 	var i int
    880 
    881 loop:
    882 	if p == nil {
    883 		return
    884 	}
    885 	a = int(p.As)
    886 	if a == AB {
    887 		q = p.Pcond
    888 		if q != nil && q.As != obj.ATEXT {
    889 			p.Mark |= FOLL
    890 			p = q
    891 			if p.Mark&FOLL == 0 {
    892 				goto loop
    893 			}
    894 		}
    895 	}
    896 
    897 	if p.Mark&FOLL != 0 {
    898 		i = 0
    899 		q = p
    900 		for ; i < 4; i, q = i+1, q.Link {
    901 			if q == *last || q == nil {
    902 				break
    903 			}
    904 			a = int(q.As)
    905 			if a == obj.ANOP {
    906 				i--
    907 				continue
    908 			}
    909 
    910 			if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
    911 				goto copy
    912 			}
    913 			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
    914 				continue
    915 			}
    916 			if a != ABEQ && a != ABNE {
    917 				continue
    918 			}
    919 
    920 		copy:
    921 			for {
    922 				r = ctxt.NewProg()
    923 				*r = *p
    924 				if r.Mark&FOLL == 0 {
    925 					fmt.Printf("can't happen 1\n")
    926 				}
    927 				r.Mark |= FOLL
    928 				if p != q {
    929 					p = p.Link
    930 					(*last).Link = r
    931 					*last = r
    932 					continue
    933 				}
    934 
    935 				(*last).Link = r
    936 				*last = r
    937 				if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
    938 					return
    939 				}
    940 				r.As = ABNE
    941 				if a == ABNE {
    942 					r.As = ABEQ
    943 				}
    944 				r.Pcond = p.Link
    945 				r.Link = p.Pcond
    946 				if r.Link.Mark&FOLL == 0 {
    947 					xfol(ctxt, r.Link, last)
    948 				}
    949 				if r.Pcond.Mark&FOLL == 0 {
    950 					fmt.Printf("can't happen 2\n")
    951 				}
    952 				return
    953 			}
    954 		}
    955 
    956 		a = AB
    957 		q = ctxt.NewProg()
    958 		q.As = int16(a)
    959 		q.Lineno = p.Lineno
    960 		q.To.Type = obj.TYPE_BRANCH
    961 		q.To.Offset = p.Pc
    962 		q.Pcond = p
    963 		p = q
    964 	}
    965 
    966 	p.Mark |= FOLL
    967 	(*last).Link = p
    968 	*last = p
    969 	if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
    970 		return
    971 	}
    972 
    973 	if p.Pcond != nil {
    974 		if a != ABL && a != ABX && p.Link != nil {
    975 			q = obj.Brchain(ctxt, p.Link)
    976 			if a != obj.ATEXT && a != ABCASE {
    977 				if q != nil && (q.Mark&FOLL != 0) {
    978 					p.As = int16(relinv(a))
    979 					p.Link = p.Pcond
    980 					p.Pcond = q
    981 				}
    982 			}
    983 
    984 			xfol(ctxt, p.Link, last)
    985 			q = obj.Brchain(ctxt, p.Pcond)
    986 			if q == nil {
    987 				q = p.Pcond
    988 			}
    989 			if q.Mark&FOLL != 0 {
    990 				p.Pcond = q
    991 				return
    992 			}
    993 
    994 			p = q
    995 			goto loop
    996 		}
    997 	}
    998 
    999 	p = p.Link
   1000 	goto loop
   1001 }
   1002 
   1003 var unaryDst = map[int]bool{
   1004 	ASWI:  true,
   1005 	AWORD: true,
   1006 }
   1007 
   1008 var Linkarm = obj.LinkArch{
   1009 	ByteOrder:  binary.LittleEndian,
   1010 	Name:       "arm",
   1011 	Thechar:    '5',
   1012 	Preprocess: preprocess,
   1013 	Assemble:   span5,
   1014 	Follow:     follow,
   1015 	Progedit:   progedit,
   1016 	UnaryDst:   unaryDst,
   1017 	Minlc:      4,
   1018 	Ptrsize:    4,
   1019 	Regsize:    4,
   1020 }
   1021