github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/cmd/compile/internal/sparc64/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 sparc64
     6  
     7  import (
     8  	"cmd/compile/internal/gc"
     9  	"cmd/internal/obj"
    10  	"cmd/internal/obj/sparc64"
    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  	// sparc64 requires that the frame size (not counting saved LR)
    22  	// be empty or be 0 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, sparc64.AMOVD, obj.TYPE_REG, sparc64.REG_ZR, 0, obj.TYPE_MEM, sparc64.REG_BFP, lo+i)
    79  		}
    80  	} else if false && cnt <= int64(128*gc.Widthptr) {
    81  		// TODO(shawn):
    82  		// Disabled for now since it's likely that the call to duffzero
    83  		// will stomp on the link register in a tail call case; see
    84  		// mips issue https://golang.org/issue/12108
    85  		p = appendpp(p, sparc64.AMOVD, obj.TYPE_REG, sparc64.REG_BFP, 0, obj.TYPE_REG, sparc64.REG_RT1, 0)
    86  		p = appendpp(p, sparc64.AADD, obj.TYPE_CONST, 0, lo, obj.TYPE_REG, sparc64.REG_RT1, 0)
    87  		p.Reg = sparc64.REG_RT1
    88  		p = appendpp(p, obj.ADUFFZERO, obj.TYPE_NONE, 0, 0, obj.TYPE_MEM, 0, 0)
    89  		f := gc.Sysfunc("duffzero")
    90  		gc.Naddr(&p.To, f)
    91  		gc.Afunclit(&p.To, f)
    92  		// the extra +8 is to account for the prologue padding
    93  		// added by preprocess()
    94  		p.To.Offset = 8 * (128 - cnt/int64(gc.Widthptr)) + 8
    95  	} else {
    96  		//	ADD	$lo, BFP, RT1
    97  		//	ADD	$(cnt), RT1, RT2
    98  		// loop:
    99  		//	MOVD	ZR, (RT1)
   100  		//	ADD	$8, RT1
   101  		//	CMP	RT1, RT2
   102  		//	BNED	loop
   103  		p = appendpp(p, sparc64.AADD, obj.TYPE_CONST, 0, int64(lo+sparc64.StackBias), obj.TYPE_REG, sparc64.REG_RT1, 0)
   104  		p.Reg = sparc64.REG_RFP
   105  		p = appendpp(p, sparc64.AADD, obj.TYPE_CONST, 0, cnt, obj.TYPE_REG, sparc64.REG_RT2, 0)
   106  		p.Reg = sparc64.REG_RT1
   107  		p = appendpp(p, sparc64.AMOVD, obj.TYPE_REG, sparc64.REG_ZR, 0, obj.TYPE_MEM, sparc64.REG_RT1, 0)
   108  		p1 := p
   109  		p = appendpp(p, sparc64.AADD, obj.TYPE_CONST, 0, 8, obj.TYPE_REG, sparc64.REG_RT1, 0)
   110  		p = appendpp(p, sparc64.ACMP, obj.TYPE_REG, sparc64.REG_RT1, 0, obj.TYPE_NONE, 0, 0)
   111  		p.Reg = sparc64.REG_RT2
   112  		p = appendpp(p, sparc64.ABNED, obj.TYPE_NONE, 0, 0, obj.TYPE_BRANCH, 0, 0)
   113  		gc.Patch(p, p1)
   114  	}
   115  
   116  	return p
   117  }
   118  
   119  func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int64, ttype obj.AddrType, treg int, toffset int64) *obj.Prog {
   120  	q := gc.Ctxt.NewProg()
   121  	gc.Clearp(q)
   122  	q.As = as
   123  	q.Lineno = p.Lineno
   124  	q.From.Type = ftype
   125  	q.From.Reg = int16(freg)
   126  	q.From.Offset = foffset
   127  	q.To.Type = ttype
   128  	q.To.Reg = int16(treg)
   129  	q.To.Offset = toffset
   130  	q.Link = p.Link
   131  	p.Link = q
   132  	return q
   133  }
   134  
   135  func ginsnop() {
   136  	var con gc.Node
   137  	gc.Nodconst(&con, gc.Types[gc.TINT], 0)
   138  	gins(sparc64.ARNOP, nil, nil)
   139  }
   140  
   141  var panicdiv *gc.Node
   142  
   143  /*
   144   * generate division.
   145   * generates one of:
   146   *	res = nl / nr
   147   *	res = nl % nr
   148   * according to op.
   149   */
   150  func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) {
   151  	// Have to be careful about handling
   152  	// most negative int divided by -1 correctly.
   153  	// The hardware will generate undefined result.
   154  	// Also need to explicitly trap on division on zero,
   155  	// the hardware will silently generate undefined result.
   156  	// DIVW will leave unpredictable result in higher 32-bit,
   157  	// so always use DIVD/DIVDU.
   158  	t := nl.Type
   159  
   160  	t0 := t
   161  	check := false
   162  	if t.IsSigned() {
   163  		check = true
   164  		if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<<uint64(t.Width*8-1)) {
   165  			check = false
   166  		} else if gc.Isconst(nr, gc.CTINT) && nr.Int64() != -1 {
   167  			check = false
   168  		}
   169  	}
   170  
   171  	if t.Width < 8 {
   172  		if t.IsSigned() {
   173  			t = gc.Types[gc.TINT64]
   174  		} else {
   175  			t = gc.Types[gc.TUINT64]
   176  		}
   177  		check = false
   178  	}
   179  
   180  	a := optoas(gc.ODIV, t)
   181  
   182  	var tl gc.Node
   183  	gc.Regalloc(&tl, t0, nil)
   184  	var tr gc.Node
   185  	gc.Regalloc(&tr, t0, nil)
   186  	if nl.Ullman >= nr.Ullman {
   187  		gc.Cgen(nl, &tl)
   188  		gc.Cgen(nr, &tr)
   189  	} else {
   190  		gc.Cgen(nr, &tr)
   191  		gc.Cgen(nl, &tl)
   192  	}
   193  
   194  	if t != t0 {
   195  		// Convert
   196  		tl2 := tl
   197  
   198  		tr2 := tr
   199  		tl.Type = t
   200  		tr.Type = t
   201  		gmove(&tl2, &tl)
   202  		gmove(&tr2, &tr)
   203  	}
   204  
   205  	// Handle divide-by-zero panic.
   206  	p1 := gins(optoas(gc.OCMP, t), &tr, nil)
   207  	p1.Reg = sparc64.REG_ZR
   208  	p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1)
   209  	if panicdiv == nil {
   210  		panicdiv = gc.Sysfunc("panicdivide")
   211  	}
   212  	gc.Ginscall(panicdiv, -1)
   213  	gc.Patch(p1, gc.Pc)
   214  
   215  	var p2 *obj.Prog
   216  	if check {
   217  		var nm1 gc.Node
   218  		gc.Nodconst(&nm1, t, -1)
   219  		gcmp(optoas(gc.OCMP, t), &tr, &nm1)
   220  		p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1)
   221  		if op == gc.ODIV {
   222  			// a / (-1) is -a.
   223  			gins(optoas(gc.OMINUS, t), &tl, &tl)
   224  
   225  			gmove(&tl, res)
   226  		} else {
   227  			// a % (-1) is 0.
   228  			var nz gc.Node
   229  			gc.Nodconst(&nz, t, 0)
   230  
   231  			gmove(&nz, res)
   232  		}
   233  
   234  		p2 = gc.Gbranch(obj.AJMP, nil, 0)
   235  		gc.Patch(p1, gc.Pc)
   236  	}
   237  
   238  	p1 = gins(a, &tr, &tl)
   239  	if op == gc.ODIV {
   240  		gc.Regfree(&tr)
   241  		gmove(&tl, res)
   242  	} else {
   243  		// A%B = A-(A/B*B)
   244  		var tm gc.Node
   245  		gc.Regalloc(&tm, t, nil)
   246  
   247  		// patch div to use the 3 register form
   248  		// TODO(minux): add gins3?
   249  		p1.Reg = p1.To.Reg
   250  
   251  		p1.To.Reg = tm.Reg
   252  		gins(optoas(gc.OMUL, t), &tr, &tm)
   253  		gc.Regfree(&tr)
   254  		gins(optoas(gc.OSUB, t), &tm, &tl)
   255  		gc.Regfree(&tm)
   256  		gmove(&tl, res)
   257  	}
   258  
   259  	gc.Regfree(&tl)
   260  	if check {
   261  		gc.Patch(p2, gc.Pc)
   262  	}
   263  }
   264  
   265  /*
   266   * generate high multiply:
   267   *   res = (nl*nr) >> width
   268   */
   269  func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) {
   270  	// largest ullman on left.
   271  	if nl.Ullman < nr.Ullman {
   272  		nl, nr = nr, nl
   273  	}
   274  
   275  	t := nl.Type
   276  	w := t.Width * 8
   277  	var n1 gc.Node
   278  	gc.Cgenr(nl, &n1, res)
   279  	var n2 gc.Node
   280  	gc.Cgenr(nr, &n2, nil)
   281  	switch gc.Simtype[t.Etype] {
   282  	case gc.TINT8,
   283  		gc.TINT16,
   284  		gc.TINT32:
   285  		gins(optoas(gc.OMUL, t), &n2, &n1)
   286  		p := gins(sparc64.ASRAW, nil, &n1)
   287  		p.From.Type = obj.TYPE_CONST
   288  		p.From.Offset = w
   289  
   290  	case gc.TUINT8,
   291  		gc.TUINT16,
   292  		gc.TUINT32:
   293  		gins(optoas(gc.OMUL, t), &n2, &n1)
   294  		p := gins(sparc64.ASRLW, nil, &n1)
   295  		p.From.Type = obj.TYPE_CONST
   296  		p.From.Offset = w
   297  
   298  	// TODO(aram):
   299  	//case gc.TINT64,
   300  	//	gc.TUINT64:
   301  	//	if t.IsSigned() {
   302  	//		gins(sparc64.ASMULH, &n2, &n1)
   303  	//	} else {
   304  	//		gins(sparc64.AUMULH, &n2, &n1)
   305  	//	}
   306  
   307  	default:
   308  		gc.Fatalf("cgen_hmul %v", t)
   309  	}
   310  
   311  	gc.Cgen(&n1, res)
   312  	gc.Regfree(&n1)
   313  	gc.Regfree(&n2)
   314  }
   315  
   316  /*
   317   * generate shift according to op, one of:
   318   *	res = nl << nr
   319   *	res = nl >> nr
   320   */
   321  func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
   322  	a := optoas(op, nl.Type)
   323  
   324  	if nr.Op == gc.OLITERAL {
   325  		var n1 gc.Node
   326  		gc.Regalloc(&n1, nl.Type, res)
   327  		gc.Cgen(nl, &n1)
   328  		sc := uint64(nr.Int64())
   329  		if sc >= uint64(nl.Type.Width)*8 {
   330  			// large shift gets 2 shifts by width-1
   331  			var n3 gc.Node
   332  			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
   333  
   334  			gins(a, &n3, &n1)
   335  			gins(a, &n3, &n1)
   336  		} else {
   337  			gins(a, nr, &n1)
   338  		}
   339  		gmove(&n1, res)
   340  		gc.Regfree(&n1)
   341  		return
   342  	}
   343  
   344  	if nl.Ullman >= gc.UINF {
   345  		var n4 gc.Node
   346  		gc.Tempname(&n4, nl.Type)
   347  		gc.Cgen(nl, &n4)
   348  		nl = &n4
   349  	}
   350  
   351  	if nr.Ullman >= gc.UINF {
   352  		var n5 gc.Node
   353  		gc.Tempname(&n5, nr.Type)
   354  		gc.Cgen(nr, &n5)
   355  		nr = &n5
   356  	}
   357  
   358  	// Allow either uint32 or uint64 as shift type,
   359  	// to avoid unnecessary conversion from uint32 to uint64
   360  	// just to do the comparison.
   361  	tcount := gc.Types[gc.Simtype[nr.Type.Etype]]
   362  
   363  	if tcount.Etype < gc.TUINT32 {
   364  		tcount = gc.Types[gc.TUINT32]
   365  	}
   366  
   367  	var n1 gc.Node
   368  	gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX
   369  	var n3 gc.Node
   370  	gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX
   371  
   372  	var n2 gc.Node
   373  	gc.Regalloc(&n2, nl.Type, res)
   374  
   375  	if nl.Ullman >= nr.Ullman {
   376  		gc.Cgen(nl, &n2)
   377  		gc.Cgen(nr, &n1)
   378  		gmove(&n1, &n3)
   379  	} else {
   380  		gc.Cgen(nr, &n1)
   381  		gmove(&n1, &n3)
   382  		gc.Cgen(nl, &n2)
   383  	}
   384  
   385  	gc.Regfree(&n3)
   386  
   387  	// test and fix up large shifts
   388  	if !bounded {
   389  		gc.Nodconst(&n3, tcount, nl.Type.Width*8)
   390  		gcmp(optoas(gc.OCMP, tcount), &n1, &n3)
   391  		p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1)
   392  		if op == gc.ORSH && nl.Type.IsSigned() {
   393  			gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1)
   394  			gins(a, &n3, &n2)
   395  		} else {
   396  			gc.Nodconst(&n3, nl.Type, 0)
   397  			gmove(&n3, &n2)
   398  		}
   399  
   400  		gc.Patch(p1, gc.Pc)
   401  	}
   402  
   403  	gins(a, &n1, &n2)
   404  
   405  	gmove(&n2, res)
   406  
   407  	gc.Regfree(&n1)
   408  	gc.Regfree(&n2)
   409  }
   410  
   411  func clearfat(nl *gc.Node) {
   412  	/* clear a fat object */
   413  	if gc.Debug['g'] != 0 {
   414  		fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width)
   415  	}
   416  
   417  	w := uint64(nl.Type.Width)
   418  
   419  	// Avoid taking the address for simple enough types.
   420  	if gc.Componentgen(nil, nl) {
   421  		return
   422  	}
   423  
   424  	c := w % 8 // bytes
   425  	q := w / 8 // dwords
   426  
   427  	var r0 gc.Node
   428  	gc.Nodreg(&r0, gc.Types[gc.TUINT64], sparc64.REG_ZR)
   429  	var dst gc.Node
   430  
   431  	// REGRT1 is reserved on sparc64, see sparc64/gsubr.go.
   432  	gc.Nodreg(&dst, gc.Types[gc.Tptr], sparc64.REG_RT1)
   433  	gc.Agen(nl, &dst)
   434  
   435  	var boff uint64
   436  	if q > 128 {
   437  		p := gins(sparc64.ASUB, nil, &dst)
   438  		p.From.Type = obj.TYPE_CONST
   439  		p.From.Offset = 8
   440  
   441  		var end gc.Node
   442  		gc.Regalloc(&end, gc.Types[gc.Tptr], nil)
   443  		p = gins(sparc64.AMOVD, &dst, &end)
   444  		p.From.Type = obj.TYPE_ADDR
   445  		p.From.Offset = int64(q * 8)
   446  
   447  		p = gins(sparc64.AMOVD, &r0, &dst)
   448  		p.To.Type = obj.TYPE_MEM
   449  		p.To.Offset = 8
   450  		pl := p
   451  
   452  		p = gins(sparc64.AADD, nil, &dst)
   453  		p.From.Type = obj.TYPE_CONST
   454  		p.From.Offset = 8
   455  
   456  		p = gcmp(sparc64.ACMP, &dst, &end)
   457  		gc.Patch(gc.Gbranch(sparc64.ABNED, nil, 0), pl)
   458  
   459  		gc.Regfree(&end)
   460  
   461  		// The loop leaves G1 (RT1) on the last zeroed dword
   462  		boff = 8
   463  	} else if false && q >= 4 {
   464  		// TODO(shawn):
   465  		// Disabled for now since it's likely that the call to duffzero
   466  		// will stomp on the link register in a tail call case; see
   467  		// mips issue https://golang.org/issue/12108
   468  		f := gc.Sysfunc("duffzero")
   469  		p := gins(obj.ADUFFZERO, nil, f)
   470  		gc.Afunclit(&p.To, f)
   471  
   472  		// 8 and 128 = magic constants: see ../../../../runtime/mkduff.go
   473  		// the extra +8 is to account for the prologue padding
   474  		// added by preprocess()
   475  		p.To.Offset = int64(8 * (128 - q)) + 8
   476  
   477  		// duffzero leaves G1 (RT1) on the last zeroed dword
   478  		boff = 8
   479  	} else {
   480  		var p *obj.Prog
   481  		for t := uint64(0); t < q; t++ {
   482  			p = gins(sparc64.AMOVD, &r0, &dst)
   483  			p.To.Type = obj.TYPE_MEM
   484  			p.To.Offset = int64(8 * t)
   485  		}
   486  
   487  		boff = 8 * q
   488  	}
   489  
   490  	var p *obj.Prog
   491  	for t := uint64(0); t < c; t++ {
   492  		p = gins(sparc64.AMOVB, &r0, &dst)
   493  		p.To.Type = obj.TYPE_MEM
   494  		p.To.Offset = int64(t + boff)
   495  	}
   496  }
   497  
   498  // Called after regopt and peep have run.
   499  // Expand CHECKNIL pseudo-op into actual nil pointer check.
   500  func expandchecks(firstp *obj.Prog) {
   501  	var p1 *obj.Prog
   502  
   503  	for p := firstp; p != nil; p = p.Link {
   504  		if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 {
   505  			fmt.Printf("expandchecks: %v\n", p)
   506  		}
   507  		if p.As != obj.ACHECKNIL {
   508  			continue
   509  		}
   510  		if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers
   511  			gc.Warnl(p.Lineno, "generated nil check")
   512  		}
   513  		if p.From.Type != obj.TYPE_REG {
   514  			gc.Fatalf("invalid nil check %v\n", p)
   515  		}
   516  
   517  		// check is
   518  		//	ABRNZ arg, 2(PC)
   519  		//	MOVD ZR, 0(arg)
   520  		p1 = gc.Ctxt.NewProg()
   521  		gc.Clearp(p1)
   522  		p1.Link = p.Link
   523  		p.Link = p1
   524  		p1.Lineno = p.Lineno
   525  		p1.Pc = 9999
   526  
   527  		p.As = sparc64.ABRNZ
   528  		p.To.Type = obj.TYPE_BRANCH
   529  		p.To.Val = p1.Link
   530  
   531  		// crash by write to memory address 0.
   532  		p1.As = sparc64.AMOVD
   533  		p1.From.Type = obj.TYPE_REG
   534  		p1.From.Reg = sparc64.REG_ZR
   535  		p1.To.Type = obj.TYPE_MEM
   536  		p1.To.Reg = p.From.Reg
   537  		p1.To.Offset = 0
   538  	}
   539  }
   540  
   541  // res = runtime.getg()
   542  func getg(res *gc.Node) {
   543  	var n1 gc.Node
   544  	gc.Nodreg(&n1, res.Type, sparc64.REG_G)
   545  	gmove(&n1, res)
   546  }