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