Home | History | Annotate | Download | only in arm64
      1 // Copyright 2009 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package arm64
      6 
      7 import (
      8 	"cmd/compile/internal/gc"
      9 	"cmd/internal/obj"
     10 	"cmd/internal/obj/arm64"
     11 	"fmt"
     12 )
     13 
     14 func defframe(ptxt *obj.Prog) {
     15 	var n *gc.Node
     16 
     17 	// fill in argument size, stack size
     18 	ptxt.To.Type = obj.TYPE_TEXTSIZE
     19 
     20 	ptxt.To.Val = int32(gc.Rnd(gc.Curfn.Type.Argwid, int64(gc.Widthptr)))
     21 	frame := uint32(gc.Rnd(gc.Stksize+gc.Maxarg, int64(gc.Widthreg)))
     22 
     23 	// arm64 requires that the frame size (not counting saved LR)
     24 	// be empty or be 8 mod 16. If not, pad it.
     25 	if frame != 0 && frame%16 != 8 {
     26 		frame += 8
     27 	}
     28 
     29 	ptxt.To.Offset = int64(frame)
     30 
     31 	// insert code to zero ambiguously live variables
     32 	// so that the garbage collector only sees initialized values
     33 	// when it looks for pointers.
     34 	p := ptxt
     35 
     36 	hi := int64(0)
     37 	lo := hi
     38 
     39 	// iterate through declarations - they are sorted in decreasing xoffset order.
     40 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
     41 		n = l.N
     42 		if !n.Name.Needzero {
     43 			continue
     44 		}
     45 		if n.Class != gc.PAUTO {
     46 			gc.Fatal("needzero class %d", n.Class)
     47 		}
     48 		if n.Type.Width%int64(gc.Widthptr) != 0 || n.Xoffset%int64(gc.Widthptr) != 0 || n.Type.Width == 0 {
     49 			gc.Fatal("var %v has size %d offset %d", gc.Nconv(n, obj.FmtLong), int(n.Type.Width), int(n.Xoffset))
     50 		}
     51 
     52 		if lo != hi && n.Xoffset+n.Type.Width >= lo-int64(2*gc.Widthreg) {
     53 			// merge with range we already have
     54 			lo = n.Xoffset
     55 
     56 			continue
     57 		}
     58 
     59 		// zero old range
     60 		p = zerorange(p, int64(frame), lo, hi)
     61 
     62 		// set new range
     63 		hi = n.Xoffset + n.Type.Width
     64 
     65 		lo = n.Xoffset
     66 	}
     67 
     68 	// zero final range
     69 	zerorange(p, int64(frame), lo, hi)
     70 }
     71 
     72 var darwin = obj.Getgoos() == "darwin"
     73 
     74 func zerorange(p *obj.Prog, frame int64, lo int64, hi int64) *obj.Prog {
     75 	cnt := hi - lo
     76 	if cnt == 0 {
     77 		return p
     78 	}
     79 	if cnt < int64(4*gc.Widthptr) {
     80 		for i := int64(0); i < cnt; i += int64(gc.Widthptr) {
     81 			p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGSP, 8+frame+lo+i)
     82 		}
     83 	} else if cnt <= int64(128*gc.Widthptr) && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
     84 		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
     85 		p = appendpp(p, arm64.AADD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGRT1, 0)
     86 		p.Reg = arm64.REGRT1
     87 		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
     88 		f := gc.Sysfunc("duffzero")
     89 		gc.Naddr(&p.To, f)
     90 		gc.Afunclit(&p.To, f)
     91 		p.To.Offset = 4 * (128 - cnt/int64(gc.Widthptr))
     92 	} else {
     93 		p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, 8+frame+lo-8, obj.TYPE_REG, arm64.REGTMP, 0)
     94 		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGSP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
     95 		p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT1, 0)
     96 		p.Reg = arm64.REGRT1
     97 		p = appendpp(p, arm64.AMOVD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, arm64.REGTMP, 0)
     98 		p = appendpp(p, arm64.AADD, obj.TYPE_REG, arm64.REGTMP, 0, obj.TYPE_REG, arm64.REGRT2, 0)
     99 		p.Reg = arm64.REGRT1
    100 		p = appendpp(p, arm64.AMOVD, obj.TYPE_REG, arm64.REGZERO, 0, obj.TYPE_MEM, arm64.REGRT1, int64(gc.Widthptr))
    101 		p.Scond = arm64.C_XPRE
    102 		p1 := p
    103 		p = appendpp(p, arm64.ACMP, obj.TYPE_REG, arm64.REGRT1, 0, obj.TYPE_NONE, 0, 0)
    104 		p.Reg = arm64.REGRT2
    105 		p = appendpp(p, arm64.ABNE, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
    106 		gc.Patch(p, p1)
    107 	}
    108 
    109 	return p
    110 }
    111 
    112 func appendpp(p *obj.Prog, as int, ftype int, freg int, foffset int64, ttype int, treg int, toffset int64) *obj.Prog {
    113 	q := gc.Ctxt.NewProg()
    114 	gc.Clearp(q)
    115 	q.As = int16(as)
    116 	q.Lineno = p.Lineno
    117 	q.From.Type = int16(ftype)
    118 	q.From.Reg = int16(freg)
    119 	q.From.Offset = foffset
    120 	q.To.Type = int16(ttype)
    121 	q.To.Reg = int16(treg)
    122 	q.To.Offset = toffset
    123 	q.Link = p.Link
    124 	p.Link = q
    125 	return q
    126 }
    127 
    128 func ginsnop() {
    129 	var con gc.Node
    130 	gc.Nodconst(&con, gc.Types[gc.TINT], 0)
    131 	gins(arm64.AHINT, &con, nil)
    132 }
    133 
    134 var panicdiv *gc.Node
    135 
    136 /*
    137  * generate division.
    138  * generates one of:
    139  *	res = nl / nr
    140  *	res = nl % nr
    141  * according to op.
    142  */
    143 func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
    144 	// Have to be careful about handling
    145 	// most negative int divided by -1 correctly.
    146 	// The hardware will generate undefined result.
    147 	// Also need to explicitly trap on division on zero,
    148 	// the hardware will silently generate undefined result.
    149 	// DIVW will leave unpredicable result in higher 32-bit,
    150 	// so always use DIVD/DIVDU.
    151 	t := nl.Type
    152 
    153 	t0 := t
    154 	check := 0
    155 	if gc.Issigned[t.Etype] {
    156 		check = 1
    157 		if gc.Isconst(nl, gc.CTINT) && nl.Int() != -(1<<uint64(t.Width*8-1)) {
    158 			check = 0
    159 		} else if gc.Isconst(nr, gc.CTINT) && nr.Int() != -1 {
    160 			check = 0
    161 		}
    162 	}
    163 
    164 	if t.Width < 8 {
    165 		if gc.Issigned[t.Etype] {
    166 			t = gc.Types[gc.TINT64]
    167 		} else {
    168 			t = gc.Types[gc.TUINT64]
    169 		}
    170 		check = 0
    171 	}
    172 
    173 	a := optoas(gc.ODIV, t)
    174 
    175 	var tl gc.Node
    176 	gc.Regalloc(&tl, t0, nil)
    177 	var tr gc.Node
    178 	gc.Regalloc(&tr, t0, nil)
    179 	if nl.Ullman >= nr.Ullman {
    180 		gc.Cgen(nl, &tl)
    181 		gc.Cgen(nr, &tr)
    182 	} else {
    183 		gc.Cgen(nr, &tr)
    184 		gc.Cgen(nl, &tl)
    185 	}
    186 
    187 	if t != t0 {
    188 		// Convert
    189 		tl2 := tl
    190 
    191 		tr2 := tr
    192 		tl.Type = t
    193 		tr.Type = t
    194 		gmove(&tl2, &tl)
    195 		gmove(&tr2, &tr)
    196 	}
    197 
    198 	// Handle divide-by-zero panic.
    199 	p1 := gins(optoas(gc.OCMP, t), &tr, nil)
    200 	p1.Reg = arm64.REGZERO
    201 	p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
    202 	if panicdiv == nil {
    203 		panicdiv = gc.Sysfunc("panicdivide")
    204 	}
    205 	gc.Ginscall(panicdiv, -1)
    206 	gc.Patch(p1, gc.Pc)
    207 
    208 	var p2 *obj.Prog
    209 	if check != 0 {
    210 		var nm1 gc.Node
    211 		gc.Nodconst(&nm1, t, -1)
    212 		gcmp(optoas(gc.OCMP, t), &tr, &nm1)
    213 		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
    214 		if op == gc.ODIV {
    215 			// a / (-1) is -a.
    216 			gins(optoas(gc.OMINUS, t), &tl, &tl)
    217 
    218 			gmove(&tl, res)
    219 		} else {
    220 			// a % (-1) is 0.
    221 			var nz gc.Node
    222 			gc.Nodconst(&nz, t, 0)
    223 
    224 			gmove(&nz, res)
    225 		}
    226 
    227 		p2 = gc.Gbranch(obj.AJMP, nil, 0)
    228 		gc.Patch(p1, gc.Pc)
    229 	}
    230 
    231 	p1 = gins(a, &tr, &tl)
    232 	if op == gc.ODIV {
    233 		gc.Regfree(&tr)
    234 		gmove(&tl, res)
    235 	} else {
    236 		// A%B = A-(A/B*B)
    237 		var tm gc.Node
    238 		gc.Regalloc(&tm, t, nil)
    239 
    240 		// patch div to use the 3 register form
    241 		// TODO(minux): add gins3?
    242 		p1.Reg = p1.To.Reg
    243 
    244 		p1.To.Reg = tm.Reg
    245 		gins(optoas(gc.OMUL, t), &tr, &tm)
    246 		gc.Regfree(&tr)
    247 		gins(optoas(gc.OSUB, t), &tm, &tl)
    248 		gc.Regfree(&tm)
    249 		gmove(&tl, res)
    250 	}
    251 
    252 	gc.Regfree(&tl)
    253 	if check != 0 {
    254 		gc.Patch(p2, gc.Pc)
    255 	}
    256 }
    257 
    258 /*
    259  * generate high multiply:
    260  *   res = (nl*nr) >> width
    261  */
    262 func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
    263 	// largest ullman on left.
    264 	if nl.Ullman < nr.Ullman {
    265 		tmp := (*gc.Node)(nl)
    266 		nl = nr
    267 		nr = tmp
    268 	}
    269 
    270 	t := (*gc.Type)(nl.Type)
    271 	w := int(int(t.Width * 8))
    272 	var n1 gc.Node
    273 	gc.Cgenr(nl, &n1, res)
    274 	var n2 gc.Node
    275 	gc.Cgenr(nr, &n2, nil)
    276 	switch gc.Simtype[t.Etype] {
    277 	case gc.TINT8,
    278 		gc.TINT16,
    279 		gc.TINT32:
    280 		gins(optoas(gc.OMUL, t), &n2, &n1)
    281 		p := (*obj.Prog)(gins(arm64.AASR, nil, &n1))
    282 		p.From.Type = obj.TYPE_CONST
    283 		p.From.Offset = int64(w)
    284 
    285 	case gc.TUINT8,
    286 		gc.TUINT16,
    287 		gc.TUINT32:
    288 		gins(optoas(gc.OMUL, t), &n2, &n1)
    289 		p := (*obj.Prog)(gins(arm64.ALSR, nil, &n1))
    290 		p.From.Type = obj.TYPE_CONST
    291 		p.From.Offset = int64(w)
    292 
    293 	case gc.TINT64,
    294 		gc.TUINT64:
    295 		if gc.Issigned[t.Etype] {
    296 			gins(arm64.ASMULH, &n2, &n1)
    297 		} else {
    298 			gins(arm64.AUMULH, &n2, &n1)
    299 		}
    300 
    301 	default:
    302 		gc.Fatal("cgen_hmul %v", t)
    303 	}
    304 
    305 	gc.Cgen(&n1, res)
    306 	gc.Regfree(&n1)
    307 	gc.Regfree(&n2)
    308 }
    309 
    310 /*
    311  * generate shift according to op, one of:
    312  *	res = nl << nr
    313  *	res = nl >> nr
    314  */
    315 func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
    316 	a := int(optoas(op, nl.Type))
    317 
    318 	if nr.Op == gc.OLITERAL {
    319 		var n1 gc.Node
    320 		gc.Regalloc(&n1, nl.Type, res)
    321 		gc.Cgen(nl, &n1)
    322 		sc := uint64(nr.Int())
    323 		if sc >= uint64(nl.Type.Width*8) {
    324 			// large shift gets 2 shifts by width-1
    325 			var n3 gc.Node
    326 			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
    327 
    328 			gins(a, &n3, &n1)
    329 			gins(a, &n3, &n1)
    330 		} else {
    331 			gins(a, nr, &n1)
    332 		}
    333 		gmove(&n1, res)
    334 		gc.Regfree(&n1)
    335 		return
    336 	}
    337 
    338 	if nl.Ullman >= gc.UINF {
    339 		var n4 gc.Node
    340 		gc.Tempname(&n4, nl.Type)
    341 		gc.Cgen(nl, &n4)
    342 		nl = &n4
    343 	}
    344 
    345 	if nr.Ullman >= gc.UINF {
    346 		var n5 gc.Node
    347 		gc.Tempname(&n5, nr.Type)
    348 		gc.Cgen(nr, &n5)
    349 		nr = &n5
    350 	}
    351 
    352 	// Allow either uint32 or uint64 as shift type,
    353 	// to avoid unnecessary conversion from uint32 to uint64
    354 	// just to do the comparison.
    355 	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
    356 
    357 	if tcount.Etype < gc.TUINT32 {
    358 		tcount = gc.Types[gc.TUINT32]
    359 	}
    360 
    361 	var n1 gc.Node
    362 	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
    363 	var n3 gc.Node
    364 	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
    365 
    366 	var n2 gc.Node
    367 	gc.Regalloc(&n2, nl.Type, res)
    368 
    369 	if nl.Ullman >= nr.Ullman {
    370 		gc.Cgen(nl, &n2)
    371 		gc.Cgen(nr, &n1)
    372 		gmove(&n1, &n3)
    373 	} else {
    374 		gc.Cgen(nr, &n1)
    375 		gmove(&n1, &n3)
    376 		gc.Cgen(nl, &n2)
    377 	}
    378 
    379 	gc.Regfree(&n3)
    380 
    381 	// test and fix up large shifts
    382 	if !bounded {
    383 		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
    384 		gcmp(optoas(gc.OCMP, tcount), &n1, &n3)
    385 		p1 := (*obj.Prog)(gc.Gbranch(optoas(gc.OLT, tcount), nil, +1))
    386 		if op == gc.ORSH && gc.Issigned[nl.Type.Etype] {
    387 			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
    388 			gins(a, &n3, &n2)
    389 		} else {
    390 			gc.Nodconst(&n3, nl.Type, 0)
    391 			gmove(&n3, &n2)
    392 		}
    393 
    394 		gc.Patch(p1, gc.Pc)
    395 	}
    396 
    397 	gins(a, &n1, &n2)
    398 
    399 	gmove(&n2, res)
    400 
    401 	gc.Regfree(&n1)
    402 	gc.Regfree(&n2)
    403 }
    404 
    405 func clearfat(nl *gc.Node) {
    406 	/* clear a fat object */
    407 	if gc.Debug['g'] != 0 {
    408 		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
    409 	}
    410 
    411 	w := uint64(uint64(nl.Type.Width))
    412 
    413 	// Avoid taking the address for simple enough types.
    414 	if gc.Componentgen(nil, nl) {
    415 		return
    416 	}
    417 
    418 	c := uint64(w % 8) // bytes
    419 	q := uint64(w / 8) // dwords
    420 
    421 	var r0 gc.Node
    422 	gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO)
    423 	var dst gc.Node
    424 
    425 	// REGRT1 is reserved on arm64, see arm64/gsubr.go.
    426 	gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1)
    427 	gc.Agen(nl, &dst)
    428 
    429 	var boff uint64
    430 	if q > 128 {
    431 		p := gins(arm64.ASUB, nil, &dst)
    432 		p.From.Type = obj.TYPE_CONST
    433 		p.From.Offset = 8
    434 
    435 		var end gc.Node
    436 		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
    437 		p = gins(arm64.AMOVD, &dst, &end)
    438 		p.From.Type = obj.TYPE_ADDR
    439 		p.From.Offset = int64(q * 8)
    440 
    441 		p = gins(arm64.AMOVD, &r0, &dst)
    442 		p.To.Type = obj.TYPE_MEM
    443 		p.To.Offset = 8
    444 		p.Scond = arm64.C_XPRE
    445 		pl := (*obj.Prog)(p)
    446 
    447 		p = gcmp(arm64.ACMP, &dst, &end)
    448 		gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl)
    449 
    450 		gc.Regfree(&end)
    451 
    452 		// The loop leaves R16 on the last zeroed dword
    453 		boff = 8
    454 	} else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend
    455 		p := gins(arm64.ASUB, nil, &dst)
    456 		p.From.Type = obj.TYPE_CONST
    457 		p.From.Offset = 8
    458 		f := (*gc.Node)(gc.Sysfunc("duffzero"))
    459 		p = gins(obj.ADUFFZERO, nil, f)
    460 		gc.Afunclit(&p.To, f)
    461 
    462 		// 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s
    463 		p.To.Offset = int64(4 * (128 - q))
    464 
    465 		// duffzero leaves R16 on the last zeroed dword
    466 		boff = 8
    467 	} else {
    468 		var p *obj.Prog
    469 		for t := uint64(0); t < q; t++ {
    470 			p = gins(arm64.AMOVD, &r0, &dst)
    471 			p.To.Type = obj.TYPE_MEM
    472 			p.To.Offset = int64(8 * t)
    473 		}
    474 
    475 		boff = 8 * q
    476 	}
    477 
    478 	var p *obj.Prog
    479 	for t := uint64(0); t < c; t++ {
    480 		p = gins(arm64.AMOVB, &r0, &dst)
    481 		p.To.Type = obj.TYPE_MEM
    482 		p.To.Offset = int64(t + boff)
    483 	}
    484 }
    485 
    486 // Called after regopt and peep have run.
    487 // Expand CHECKNIL pseudo-op into actual nil pointer check.
    488 func expandchecks(firstp *obj.Prog) {
    489 	var p1 *obj.Prog
    490 
    491 	for p := (*obj.Prog)(firstp); p != nil; p = p.Link {
    492 		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
    493 			fmt.Printf("expandchecks: %v\n", p)
    494 		}
    495 		if p.As != obj.ACHECKNIL {
    496 			continue
    497 		}
    498 		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
    499 			gc.Warnl(int(p.Lineno), "generated nil check")
    500 		}
    501 		if p.From.Type != obj.TYPE_REG {
    502 			gc.Fatal("invalid nil check %v\n", p)
    503 		}
    504 
    505 		// check is
    506 		//	CBNZ arg, 2(PC)
    507 		//	MOVD ZR, 0(arg)
    508 		p1 = gc.Ctxt.NewProg()
    509 		gc.Clearp(p1)
    510 		p1.Link = p.Link
    511 		p.Link = p1
    512 		p1.Lineno = p.Lineno
    513 		p1.Pc = 9999
    514 
    515 		p.As = arm64.ACBNZ
    516 		p.To.Type = obj.TYPE_BRANCH
    517 		p.To.Val = p1.Link
    518 
    519 		// crash by write to memory address 0.
    520 		p1.As = arm64.AMOVD
    521 		p1.From.Type = obj.TYPE_REG
    522 		p1.From.Reg = arm64.REGZERO
    523 		p1.To.Type = obj.TYPE_MEM
    524 		p1.To.Reg = p.From.Reg
    525 		p1.To.Offset = 0
    526 	}
    527 }
    528 
    529 // res = runtime.getg()
    530 func getg(res *gc.Node) {
    531 	var n1 gc.Node
    532 	gc.Nodreg(&n1, res.Type, arm64.REGG)
    533 	gmove(&n1, res)
    534 }
    535