github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/arm64/ggen.go (about)

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