From: Keith Randall Date: Wed, 14 Sep 2016 21:02:50 +0000 (-0700) Subject: cmd/compile: delete lots of the legacy backend X-Git-Tag: go1.8beta1~1313 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=e7cc9c4f4dee2d6f8a77ce177172ef4ae4ff0c90;p=gostls13.git cmd/compile: delete lots of the legacy backend It's not everything, but it is a good start. I tried to make the CL delete only. goimports forced a few exceptions to that rule. Update #16357 Change-Id: I041925cb2fe68bb7ae1617af862b22c48da649c1 Reviewed-on: https://go-review.googlesource.com/29168 Run-TryBot: Keith Randall TryBot-Result: Gobot Gobot Reviewed-by: Brad Fitzpatrick Reviewed-by: Josh Bleecher Snyder Reviewed-by: Martin Möhrmann --- diff --git a/src/cmd/compile/internal/amd64/cgen.go b/src/cmd/compile/internal/amd64/cgen.go deleted file mode 100644 index 1fdb8072ec..0000000000 --- a/src/cmd/compile/internal/amd64/cgen.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package amd64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -func blockcopy(n, ns *gc.Node, osrc, odst, w int64) { - var noddi gc.Node - gc.Nodreg(&noddi, gc.Types[gc.Tptr], x86.REG_DI) - var nodsi gc.Node - gc.Nodreg(&nodsi, gc.Types[gc.Tptr], x86.REG_SI) - - var nodl gc.Node - var nodr gc.Node - if n.Ullman >= ns.Ullman { - gc.Agenr(n, &nodr, &nodsi) - if ns.Op == gc.ONAME { - gc.Gvardef(ns) - } - gc.Agenr(ns, &nodl, &noddi) - } else { - if ns.Op == gc.ONAME { - gc.Gvardef(ns) - } - gc.Agenr(ns, &nodl, &noddi) - gc.Agenr(n, &nodr, &nodsi) - } - - if nodl.Reg != x86.REG_DI { - gmove(&nodl, &noddi) - } - if nodr.Reg != x86.REG_SI { - gmove(&nodr, &nodsi) - } - gc.Regfree(&nodl) - gc.Regfree(&nodr) - - c := w % 8 // bytes - q := w / 8 // quads - - var oldcx gc.Node - var cx gc.Node - savex(x86.REG_CX, &cx, &oldcx, nil, gc.Types[gc.TINT64]) - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - if osrc < odst && odst < osrc+w { - // reverse direction - gins(x86.ASTD, nil, nil) // set direction flag - if c > 0 { - gconreg(addptr, w-1, x86.REG_SI) - gconreg(addptr, w-1, x86.REG_DI) - - gconreg(movptr, c, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- - } - - if q > 0 { - if c > 0 { - gconreg(addptr, -7, x86.REG_SI) - gconreg(addptr, -7, x86.REG_DI) - } else { - gconreg(addptr, w-8, x86.REG_SI) - gconreg(addptr, w-8, x86.REG_DI) - } - - gconreg(movptr, q, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)-,*(DI)- - } - - // we leave with the flag clear - gins(x86.ACLD, nil, nil) - } else { - // normal direction - if q > 128 || (gc.Nacl && q >= 4) || (obj.GOOS == "plan9" && q >= 4) { - gconreg(movptr, q, x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ - } else if q >= 4 { - var oldx0 gc.Node - var x0 gc.Node - savex(x86.REG_X0, &x0, &oldx0, nil, gc.Types[gc.TFLOAT64]) - - p := gins(obj.ADUFFCOPY, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) - - // 64 blocks taking 14 bytes each - // see ../../../../runtime/mkduff.go - p.To.Offset = 14 * (64 - q/2) - restx(&x0, &oldx0) - - if q%2 != 0 { - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ - } - } else if !gc.Nacl && c == 0 { - // We don't need the MOVSQ side-effect of updating SI and DI, - // and issuing a sequence of MOVQs directly is faster. - nodsi.Op = gc.OINDREG - - noddi.Op = gc.OINDREG - for q > 0 { - gmove(&nodsi, &cx) // MOVQ x+(SI),CX - gmove(&cx, &noddi) // MOVQ CX,x+(DI) - nodsi.Xoffset += 8 - noddi.Xoffset += 8 - q-- - } - } else { - for q > 0 { - gins(x86.AMOVSQ, nil, nil) // MOVQ *(SI)+,*(DI)+ - q-- - } - } - - // copy the remaining c bytes - if w < 4 || c <= 1 || (odst < osrc && osrc < odst+w) { - for c > 0 { - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ - c-- - } - } else if w < 8 || c <= 4 { - nodsi.Op = gc.OINDREG - noddi.Op = gc.OINDREG - cx.Type = gc.Types[gc.TINT32] - nodsi.Type = gc.Types[gc.TINT32] - noddi.Type = gc.Types[gc.TINT32] - if c > 4 { - nodsi.Xoffset = 0 - noddi.Xoffset = 0 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } - - nodsi.Xoffset = c - 4 - noddi.Xoffset = c - 4 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } else { - nodsi.Op = gc.OINDREG - noddi.Op = gc.OINDREG - cx.Type = gc.Types[gc.TINT64] - nodsi.Type = gc.Types[gc.TINT64] - noddi.Type = gc.Types[gc.TINT64] - nodsi.Xoffset = c - 8 - noddi.Xoffset = c - 8 - gmove(&nodsi, &cx) - gmove(&cx, &noddi) - } - } - - restx(&cx, &oldcx) -} diff --git a/src/cmd/compile/internal/amd64/galign.go b/src/cmd/compile/internal/amd64/galign.go index b7ce5a01e5..22a25af1d7 100644 --- a/src/cmd/compile/internal/amd64/galign.go +++ b/src/cmd/compile/internal/amd64/galign.go @@ -11,20 +11,13 @@ import ( ) var ( - addptr = x86.AADDQ - movptr = x86.AMOVQ leaptr = x86.ALEAQ - cmpptr = x86.ACMPQ ) func betypeinit() { if obj.GOARCH == "amd64p32" { - addptr = x86.AADDL - movptr = x86.AMOVL leaptr = x86.ALEAL - cmpptr = x86.ACMPL } - if gc.Ctxt.Flag_dynlink || obj.GOOS == "nacl" { resvd = append(resvd, x86.REG_R15) } @@ -50,40 +43,10 @@ func Main() { gc.Thearch.FREGMAX = x86.REG_X15 gc.Thearch.MAXWIDTH = 1 << 50 - gc.Thearch.AddIndex = addindex gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_bmul = cgen_bmul - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginsboolval = ginsboolval - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = FtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = ssaMarkMoves diff --git a/src/cmd/compile/internal/amd64/ggen.go b/src/cmd/compile/internal/amd64/ggen.go index e8390f2f95..4145ff2f37 100644 --- a/src/cmd/compile/internal/amd64/ggen.go +++ b/src/cmd/compile/internal/amd64/ggen.go @@ -181,627 +181,3 @@ func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int6 p.Link = q return q } - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will trap. - // Also the byte divide instruction needs AH, - // which we otherwise don't have to deal with. - // Easiest way to avoid for int8, int16: use int32. - // For int32 and int64, use explicit test. - // Could use int64 hw for int32. - t := nl.Type - - t0 := t - check := false - if t.IsSigned() { - check = true - if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<= nr.Ullman { - savex(x86.REG_AX, &ax, &oldax, res, t0) - gc.Cgen(nl, &ax) - gc.Regalloc(&ax, t0, &ax) // mark ax live during cgen - gc.Cgen(nr, &n3) - gc.Regfree(&ax) - } else { - gc.Cgen(nr, &n3) - savex(x86.REG_AX, &ax, &oldax, res, t0) - gc.Cgen(nl, &ax) - } - - if t != t0 { - // Convert - ax1 := ax - - n31 := n3 - ax.Type = t - n3.Type = t - gmove(&ax1, &ax) - gmove(&n31, &n3) - } - - var n4 gc.Node - if gc.Nacl { - // Native Client does not relay the divide-by-zero trap - // to the executing program, so we must insert a check - // for ourselves. - gc.Nodconst(&n4, t, 0) - - gins(optoas(gc.OCMP, t), &n3, &n4) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - } - - var p2 *obj.Prog - if check { - gc.Nodconst(&n4, t, -1) - gins(optoas(gc.OCMP, t), &n3, &n4) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), nil, &ax) - - gmove(&ax, res) - } else { - // a % (-1) is 0. - gc.Nodconst(&n4, t, 0) - - gmove(&n4, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - var olddx gc.Node - var dx gc.Node - savex(x86.REG_DX, &dx, &olddx, res, t) - if !t.IsSigned() { - gc.Nodconst(&n4, t, 0) - gmove(&n4, &dx) - } else { - gins(optoas(gc.OEXTEND, t), nil, nil) - } - gins(a, &n3, nil) - gc.Regfree(&n3) - if op == gc.ODIV { - gmove(&ax, res) - } else { - gmove(&dx, res) - } - restx(&dx, &olddx) - if check { - gc.Patch(p2, gc.Pc) - } - restx(&ax, &oldax) -} - -/* - * register dr is one of the special ones (AX, CX, DI, SI, etc.). - * we need to use it. if it is already allocated as a temporary - * (r > 1; can only happen if a routine like sgen passed a - * special as cgen's res and then cgen used regalloc to reuse - * it as its own temporary), then move it for now to another - * register. caller must call restx to move it back. - * the move is not necessary if dr == res, because res is - * known to be dead. - */ -func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) { - r := uint8(gc.GetReg(dr)) - - // save current ax and dx if they are live - // and not the destination - *oldx = gc.Node{} - - gc.Nodreg(x, t, dr) - if r > 1 && !gc.Samereg(x, res) { - gc.Regalloc(oldx, gc.Types[gc.TINT64], nil) - x.Type = gc.Types[gc.TINT64] - gmove(x, oldx) - x.Type = t - // TODO(marvin): Fix Node.EType type union. - oldx.Etype = gc.EType(r) // squirrel away old r value - gc.SetReg(dr, 1) - } -} - -func restx(x *gc.Node, oldx *gc.Node) { - if oldx.Op != 0 { - x.Type = gc.Types[gc.TINT64] - gc.SetReg(int(x.Reg), int(oldx.Etype)) - gmove(oldx, x) - gc.Regfree(oldx) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - t := nl.Type - a := optoas(gc.OHMUL, t) - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - var ax, oldax, dx, olddx gc.Node - savex(x86.REG_AX, &ax, &oldax, res, gc.Types[gc.TUINT64]) - savex(x86.REG_DX, &dx, &olddx, res, gc.Types[gc.TUINT64]) - gmove(&n1, &ax) - gins(a, &n2, nil) - gc.Regfree(&n2) - gc.Regfree(&n1) - - if t.Width == 1 { - // byte multiply behaves differently. - var byteAH, byteDX gc.Node - gc.Nodreg(&byteAH, t, x86.REG_AH) - gc.Nodreg(&byteDX, t, x86.REG_DX) - gmove(&byteAH, &byteDX) - } - gmove(&dx, res) - - restx(&ax, &oldax) - restx(&dx, &olddx) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - rcx := gc.GetReg(x86.REG_CX) - var n1 gc.Node - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT64], x86.REG_CX) - - var oldcx gc.Node - if rcx > 0 && !gc.Samereg(&cx, res) { - gc.Regalloc(&oldcx, gc.Types[gc.TUINT64], nil) - gmove(&cx, &oldcx) - } - - cx.Type = tcount - - var n2 gc.Node - if gc.Samereg(&cx, res) { - gc.Regalloc(&n2, nl.Type, nil) - } else { - gc.Regalloc(&n2, nl.Type, res) - } - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) - if op == gc.ORSH && nl.Type.IsSigned() { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - if oldcx.Op != 0 { - cx.Type = gc.Types[gc.TUINT64] - gmove(&oldcx, &cx) - gc.Regfree(&oldcx) - } - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate byte multiply: - * res = nl * nr - * there is no 2-operand byte multiply instruction so - * we do a full-width multiplication and truncate afterwards. - */ -func cgen_bmul(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { - if optoas(op, nl.Type) != x86.AIMULB { - return false - } - - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - // generate operands in "8-bit" registers. - var n1b gc.Node - gc.Regalloc(&n1b, nl.Type, res) - - gc.Cgen(nl, &n1b) - var n2b gc.Node - gc.Regalloc(&n2b, nr.Type, nil) - gc.Cgen(nr, &n2b) - - // perform full-width multiplication. - t := gc.Types[gc.TUINT64] - - if nl.Type.IsSigned() { - t = gc.Types[gc.TINT64] - } - var n1 gc.Node - gc.Nodreg(&n1, t, int(n1b.Reg)) - var n2 gc.Node - gc.Nodreg(&n2, t, int(n2b.Reg)) - a := optoas(op, t) - gins(a, &n2, &n1) - - // truncate. - gmove(&n1, res) - - gc.Regfree(&n1b) - gc.Regfree(&n2b) - return true -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - w := nl.Type.Width - - if w > 1024 || (w >= 64 && (gc.Nacl || isPlan9)) { - var oldn1 gc.Node - var n1 gc.Node - savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) - gc.Agen(nl, &n1) - - var ax gc.Node - var oldax gc.Node - savex(x86.REG_AX, &ax, &oldax, nil, gc.Types[gc.Tptr]) - gconreg(x86.AMOVL, 0, x86.REG_AX) - gconreg(movptr, w/8, x86.REG_CX) - - gins(x86.AREP, nil, nil) // repeat - gins(x86.ASTOSQ, nil, nil) // STOQ AL,*(DI)+ - - if w%8 != 0 { - n1.Op = gc.OINDREG - clearfat_tail(&n1, w%8) - } - - restx(&n1, &oldn1) - restx(&ax, &oldax) - return - } - - if w >= 64 { - var oldn1 gc.Node - var n1 gc.Node - savex(x86.REG_DI, &n1, &oldn1, nil, gc.Types[gc.Tptr]) - gc.Agen(nl, &n1) - - var vec_zero gc.Node - var old_x0 gc.Node - savex(x86.REG_X0, &vec_zero, &old_x0, nil, gc.Types[gc.TFLOAT64]) - gins(x86.AXORPS, &vec_zero, &vec_zero) - - if di := dzDI(w); di != 0 { - gconreg(addptr, di, x86.REG_DI) - } - p := gins(obj.ADUFFZERO, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - p.To.Offset = dzOff(w) - - if w%16 != 0 { - n1.Op = gc.OINDREG - n1.Xoffset -= 16 - w%16 - gins(x86.AMOVUPS, &vec_zero, &n1) - } - - restx(&vec_zero, &old_x0) - restx(&n1, &oldn1) - return - } - - // NOTE: Must use agen, not igen, so that optimizer sees address - // being taken. We are not writing on field boundaries. - var n1 gc.Node - gc.Agenr(nl, &n1, nil) - n1.Op = gc.OINDREG - - clearfat_tail(&n1, w) - - gc.Regfree(&n1) -} - -func clearfat_tail(n1 *gc.Node, b int64) { - if b >= 16 && isPlan9 { - var z gc.Node - gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) - q := b / 8 - for ; q > 0; q-- { - n1.Type = z.Type - gins(x86.AMOVQ, &z, n1) - n1.Xoffset += 8 - b -= 8 - } - if b != 0 { - n1.Xoffset -= 8 - b - gins(x86.AMOVQ, &z, n1) - } - return - } - if b >= 16 { - var vec_zero gc.Node - gc.Regalloc(&vec_zero, gc.Types[gc.TFLOAT64], nil) - gins(x86.AXORPS, &vec_zero, &vec_zero) - - for b >= 16 { - gins(x86.AMOVUPS, &vec_zero, n1) - n1.Xoffset += 16 - b -= 16 - } - - // MOVUPS X0, off(base) is a few bytes shorter than MOV 0, off(base) - if b != 0 { - n1.Xoffset -= 16 - b - gins(x86.AMOVUPS, &vec_zero, n1) - } - - gc.Regfree(&vec_zero) - return - } - - // Write sequence of MOV 0, off(base) instead of using STOSQ. - // The hope is that although the code will be slightly longer, - // the MOVs will have no dependencies and pipeline better - // than the unrolled STOSQ loop. - var z gc.Node - gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) - if b >= 8 { - n1.Type = z.Type - gins(x86.AMOVQ, &z, n1) - n1.Xoffset += 8 - b -= 8 - - if b != 0 { - n1.Xoffset -= 8 - b - gins(x86.AMOVQ, &z, n1) - } - return - } - - if b >= 4 { - gc.Nodconst(&z, gc.Types[gc.TUINT32], 0) - n1.Type = z.Type - gins(x86.AMOVL, &z, n1) - n1.Xoffset += 4 - b -= 4 - - if b != 0 { - n1.Xoffset -= 4 - b - gins(x86.AMOVL, &z, n1) - } - return - } - - if b >= 2 { - gc.Nodconst(&z, gc.Types[gc.TUINT16], 0) - n1.Type = z.Type - gins(x86.AMOVW, &z, n1) - n1.Xoffset += 2 - b -= 2 - } - - gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) - for b > 0 { - n1.Type = z.Type - gins(x86.AMOVB, &z, n1) - n1.Xoffset++ - b-- - } - -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - - // check is - // CMP arg, $0 - // JNE 2(PC) (likely) - // MOV AX, 0 - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = cmpptr - p.To.Type = obj.TYPE_CONST - p.To.Offset = 0 - p1.As = x86.AJNE - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = 1 // likely - p1.To.Type = obj.TYPE_BRANCH - p1.To.Val = p2.Link - - // crash by write to memory address 0. - // if possible, since we know arg is 0, use 0(arg), - // which will be shorter to encode than plain 0. - p2.As = x86.AMOVL - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = x86.REG_AX - if regtyp(&p.From) { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = p.From.Reg - } else { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = x86.REG_NONE - } - - p2.To.Offset = 0 - } -} - -// addr += index*width if possible. -func addindex(index *gc.Node, width int64, addr *gc.Node) bool { - switch width { - case 1, 2, 4, 8: - p1 := gins(x86.ALEAQ, index, addr) - p1.From.Type = obj.TYPE_MEM - p1.From.Scale = int16(width) - p1.From.Index = p1.From.Reg - p1.From.Reg = p1.To.Reg - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Regalloc(&n1, res.Type, res) - mov := optoas(gc.OAS, gc.Types[gc.Tptr]) - p := gins(mov, nil, &n1) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_TLS - p = gins(mov, nil, &n1) - p.From = p.To - p.From.Type = obj.TYPE_MEM - p.From.Index = x86.REG_TLS - p.From.Scale = 1 - gmove(&n1, res) - gc.Regfree(&n1) -} diff --git a/src/cmd/compile/internal/amd64/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go index 09bead2df9..76fca896e7 100644 --- a/src/cmd/compile/internal/amd64/gsubr.go +++ b/src/cmd/compile/internal/amd64/gsubr.go @@ -31,7 +31,6 @@ package amd64 import ( - "cmd/compile/internal/big" "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/x86" @@ -48,523 +47,6 @@ var resvd = []int{ x86.REG_SP, // for stack } -/* - * generate - * as $c, reg - */ -func gconreg(as obj.As, c int64, reg int) { - var nr gc.Node - - switch as { - case x86.AADDL, - x86.AMOVL, - x86.ALEAL: - gc.Nodreg(&nr, gc.Types[gc.TINT32], reg) - - default: - gc.Nodreg(&nr, gc.Types[gc.TINT64], reg) - } - - ginscon(as, c, &nr) -} - -/* - * generate - * as $c, n - */ -func ginscon(as obj.As, c int64, n2 *gc.Node) { - var n1 gc.Node - - switch as { - case x86.AADDL, - x86.AMOVL, - x86.ALEAL: - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - - default: - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - } - - if as != x86.AMOVQ && (c < -(1<<31) || c >= 1<<31) { - // cannot have 64-bit immediate in ADD, etc. - // instead, MOV into register first. - var ntmp gc.Node - gc.Regalloc(&ntmp, gc.Types[gc.TINT64], nil) - - gins(x86.AMOVQ, &n1, &ntmp) - gins(as, &ntmp, n2) - gc.Regfree(&ntmp) - return - } - - gins(as, &n1, n2) -} - -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() && n1.Op == gc.OLITERAL && gc.Smallintconst(n1) && n2.Op != gc.OLITERAL { - // Reverse comparison to place constant last. - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - // General case. - var r1, r2, g1, g2 gc.Node - - // A special case to make write barriers more efficient. - // Comparing the first field of a named struct can be done directly. - base := n1 - if n1.Op == gc.ODOT && n1.Left.Type.IsStruct() && n1.Left.Type.Field(0).Sym == n1.Sym { - base = n1.Left - } - - if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG { - r1 = *n1 - } else { - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - } - if n2.Op == gc.OLITERAL && t.IsInteger() && gc.Smallintconst(n2) { - r2 = *n2 - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - } - gins(optoas(gc.OCMP, t), &r1, &r2) - if r1.Op == gc.OREGISTER { - gc.Regfree(&g1) - gc.Regfree(&r1) - } - if r2.Op == gc.OREGISTER { - gc.Regfree(&g2) - gc.Regfree(&r2) - } - return gc.Gbranch(optoas(op, t), nil, likely) -} - -func ginsboolval(a obj.As, n *gc.Node) { - gins(jmptoset(a), nil, n) -} - -// set up nodes representing 2^63 -var ( - bigi gc.Node - bigf gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - - bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %L -> %L\n", f, t) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var a obj.As - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = tt // so big switch will choose a simple mov - - // some constants can't move directly to memory. - if gc.Ismem(t) { - // float constants come from memory. - if gc.Isfloat[tt] { - goto hard - } - - // 64-bit immediates are really 32-bit sign-extended - // unless moving into a register. - if gc.Isint[tt] { - if i := con.Int64(); int64(int32(i)) != i { - goto hard - } - } - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Dump("f", f) - gc.Dump("t", t) - gc.Fatalf("gmove %L -> %L", f.Type, t.Type) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TINT8<<16 | gc.TUINT8, - gc.TUINT8<<16 | gc.TINT8, - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TINT16<<16 | gc.TUINT16, - gc.TUINT16<<16 | gc.TINT16, - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = x86.AMOVL - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = x86.AMOVQL - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = x86.AMOVQ - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16: - a = x86.AMOVBWSX - - goto rdst - - case gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = x86.AMOVBLSX - goto rdst - - case gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = x86.AMOVBQSX - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16: - a = x86.AMOVBWZX - - goto rdst - - case gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = x86.AMOVBLZX - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = x86.AMOVBQZX - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = x86.AMOVWLSX - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = x86.AMOVWQSX - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = x86.AMOVWLZX - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = x86.AMOVWQZX - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = x86.AMOVLQSX - - goto rdst - - // AMOVL into a register zeros the top of the register, - // so this is not always necessary, but if we rely on AMOVL - // the optimizer is almost certain to screw with us. - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = x86.AMOVLQZX - - goto rdst - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32: - a = x86.ACVTTSS2SL - - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = x86.ACVTTSD2SL - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT64: - a = x86.ACVTTSS2SQ - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT64: - a = x86.ACVTTSD2SQ - goto rdst - - // convert via int32. - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hard - - // algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - case gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - a := x86.ACVTTSS2SQ - - if ft == gc.TFLOAT64 { - a = x86.ACVTTSD2SQ - } - bignodes() - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], nil) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[ft], nil) - var r4 gc.Node - gc.Regalloc(&r4, gc.Types[tt], nil) - gins(optoas(gc.OAS, f.Type), f, &r1) - gins(optoas(gc.OCMP, f.Type), &bigf, &r1) - p1 := gc.Gbranch(optoas(gc.OLE, f.Type), nil, +1) - gins(a, &r1, &r2) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gins(optoas(gc.OAS, f.Type), &bigf, &r3) - gins(optoas(gc.OSUB, f.Type), &r3, &r1) - gins(a, &r1, &r2) - gins(x86.AMOVQ, &bigi, &r4) - gins(x86.AXORQ, &r4, &r2) - gc.Patch(p2, gc.Pc) - gmove(&r2, t) - gc.Regfree(&r4) - gc.Regfree(&r3) - gc.Regfree(&r2) - gc.Regfree(&r1) - return - - /* - * integer to float - */ - case gc.TINT32<<16 | gc.TFLOAT32: - a = x86.ACVTSL2SS - - goto rdst - - case gc.TINT32<<16 | gc.TFLOAT64: - a = x86.ACVTSL2SD - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT32: - a = x86.ACVTSQ2SS - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT64: - a = x86.ACVTSQ2SD - goto rdst - - // convert via int32 - case gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hard - - // algorithm is: - // if small enough, use native int64 -> uint64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - a := x86.ACVTSQ2SS - - if tt == gc.TFLOAT64 { - a = x86.ACVTSQ2SD - } - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TUINT64], 0) - var one gc.Node - gc.Nodconst(&one, gc.Types[gc.TUINT64], 1) - var r1 gc.Node - gc.Regalloc(&r1, f.Type, f) - var r2 gc.Node - gc.Regalloc(&r2, t.Type, t) - var r3 gc.Node - gc.Regalloc(&r3, f.Type, nil) - var r4 gc.Node - gc.Regalloc(&r4, f.Type, nil) - gmove(f, &r1) - gins(x86.ACMPQ, &r1, &zero) - p1 := gc.Gbranch(x86.AJLT, nil, +1) - gins(a, &r1, &r2) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gmove(&r1, &r3) - gins(x86.ASHRQ, &one, &r3) - gmove(&r1, &r4) - gins(x86.AANDL, &one, &r4) - gins(x86.AORQ, &r4, &r3) - gins(a, &r3, &r2) - gins(optoas(gc.OADD, t.Type), &r2, &r2) - gc.Patch(p2, gc.Pc) - gmove(&r2, t) - gc.Regfree(&r4) - gc.Regfree(&r3) - gc.Regfree(&r2) - gc.Regfree(&r1) - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = x86.ACVTSS2SD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = x86.ACVTSD2SS - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - { - var r1 gc.Node - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - var r1 gc.Node - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - func samaddr(f *gc.Node, t *gc.Node) bool { if f.Op != t.Op { return false @@ -679,745 +161,3 @@ func ginsnop() { gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) gins(x86.AXCHGL, ®, ®) } - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OMOD_ = uint32(gc.OMOD) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OPS_ = uint32(gc.OPS) << 16 - OPC_ = uint32(gc.OPC) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - OSQRT_ = uint32(gc.OSQRT) << 16 - OADDR_ = uint32(gc.OADDR) << 16 - OINC_ = uint32(gc.OINC) << 16 - ODEC_ = uint32(gc.ODEC) << 16 - OLROT_ = uint32(gc.OLROT) << 16 - ORROTC_ = uint32(gc.ORROTC) << 16 - OEXTEND_ = uint32(gc.OEXTEND) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry %v-%v", op, t) - - case OADDR_ | gc.TPTR32: - a = x86.ALEAL - - case OADDR_ | gc.TPTR64: - a = x86.ALEAQ - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = x86.AJEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = x86.AJNE - - case OPS_ | gc.TBOOL, - OPS_ | gc.TINT8, - OPS_ | gc.TUINT8, - OPS_ | gc.TINT16, - OPS_ | gc.TUINT16, - OPS_ | gc.TINT32, - OPS_ | gc.TUINT32, - OPS_ | gc.TINT64, - OPS_ | gc.TUINT64, - OPS_ | gc.TPTR32, - OPS_ | gc.TPTR64, - OPS_ | gc.TFLOAT32, - OPS_ | gc.TFLOAT64: - a = x86.AJPS - - case OPC_ | gc.TBOOL, - OPC_ | gc.TINT8, - OPC_ | gc.TUINT8, - OPC_ | gc.TINT16, - OPC_ | gc.TUINT16, - OPC_ | gc.TINT32, - OPC_ | gc.TUINT32, - OPC_ | gc.TINT64, - OPC_ | gc.TUINT64, - OPC_ | gc.TPTR32, - OPC_ | gc.TPTR64, - OPC_ | gc.TFLOAT32, - OPC_ | gc.TFLOAT64: - a = x86.AJPC - - case OLT_ | gc.TINT8, - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64: - a = x86.AJLT - - case OLT_ | gc.TUINT8, - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64: - a = x86.AJCS - - case OLE_ | gc.TINT8, - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64: - a = x86.AJLE - - case OLE_ | gc.TUINT8, - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64: - a = x86.AJLS - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64: - a = x86.AJGT - - case OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64, - OLT_ | gc.TFLOAT32, - OLT_ | gc.TFLOAT64: - a = x86.AJHI - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64: - a = x86.AJGE - - case OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64, - OLE_ | gc.TFLOAT32, - OLE_ | gc.TFLOAT64: - a = x86.AJCC - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TUINT8: - a = x86.ACMPB - - case OCMP_ | gc.TINT16, - OCMP_ | gc.TUINT16: - a = x86.ACMPW - - case OCMP_ | gc.TINT32, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TPTR32: - a = x86.ACMPL - - case OCMP_ | gc.TINT64, - OCMP_ | gc.TUINT64, - OCMP_ | gc.TPTR64: - a = x86.ACMPQ - - case OCMP_ | gc.TFLOAT32: - a = x86.AUCOMISS - - case OCMP_ | gc.TFLOAT64: - a = x86.AUCOMISD - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8, - OAS_ | gc.TUINT8: - a = x86.AMOVB - - case OAS_ | gc.TINT16, - OAS_ | gc.TUINT16: - a = x86.AMOVW - - case OAS_ | gc.TINT32, - OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = x86.AMOVL - - case OAS_ | gc.TINT64, - OAS_ | gc.TUINT64, - OAS_ | gc.TPTR64: - a = x86.AMOVQ - - case OAS_ | gc.TFLOAT32: - a = x86.AMOVSS - - case OAS_ | gc.TFLOAT64: - a = x86.AMOVSD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8: - a = x86.AADDB - - case OADD_ | gc.TINT16, - OADD_ | gc.TUINT16: - a = x86.AADDW - - case OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32: - a = x86.AADDL - - case OADD_ | gc.TINT64, - OADD_ | gc.TUINT64, - OADD_ | gc.TPTR64: - a = x86.AADDQ - - case OADD_ | gc.TFLOAT32: - a = x86.AADDSS - - case OADD_ | gc.TFLOAT64: - a = x86.AADDSD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8: - a = x86.ASUBB - - case OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16: - a = x86.ASUBW - - case OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32: - a = x86.ASUBL - - case OSUB_ | gc.TINT64, - OSUB_ | gc.TUINT64, - OSUB_ | gc.TPTR64: - a = x86.ASUBQ - - case OSUB_ | gc.TFLOAT32: - a = x86.ASUBSS - - case OSUB_ | gc.TFLOAT64: - a = x86.ASUBSD - - case OINC_ | gc.TINT8, - OINC_ | gc.TUINT8: - a = x86.AINCB - - case OINC_ | gc.TINT16, - OINC_ | gc.TUINT16: - a = x86.AINCW - - case OINC_ | gc.TINT32, - OINC_ | gc.TUINT32, - OINC_ | gc.TPTR32: - a = x86.AINCL - - case OINC_ | gc.TINT64, - OINC_ | gc.TUINT64, - OINC_ | gc.TPTR64: - a = x86.AINCQ - - case ODEC_ | gc.TINT8, - ODEC_ | gc.TUINT8: - a = x86.ADECB - - case ODEC_ | gc.TINT16, - ODEC_ | gc.TUINT16: - a = x86.ADECW - - case ODEC_ | gc.TINT32, - ODEC_ | gc.TUINT32, - ODEC_ | gc.TPTR32: - a = x86.ADECL - - case ODEC_ | gc.TINT64, - ODEC_ | gc.TUINT64, - ODEC_ | gc.TPTR64: - a = x86.ADECQ - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8: - a = x86.ANEGB - - case OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16: - a = x86.ANEGW - - case OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32: - a = x86.ANEGL - - case OMINUS_ | gc.TINT64, - OMINUS_ | gc.TUINT64, - OMINUS_ | gc.TPTR64: - a = x86.ANEGQ - - case OAND_ | gc.TBOOL, - OAND_ | gc.TINT8, - OAND_ | gc.TUINT8: - a = x86.AANDB - - case OAND_ | gc.TINT16, - OAND_ | gc.TUINT16: - a = x86.AANDW - - case OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32: - a = x86.AANDL - - case OAND_ | gc.TINT64, - OAND_ | gc.TUINT64, - OAND_ | gc.TPTR64: - a = x86.AANDQ - - case OOR_ | gc.TBOOL, - OOR_ | gc.TINT8, - OOR_ | gc.TUINT8: - a = x86.AORB - - case OOR_ | gc.TINT16, - OOR_ | gc.TUINT16: - a = x86.AORW - - case OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32: - a = x86.AORL - - case OOR_ | gc.TINT64, - OOR_ | gc.TUINT64, - OOR_ | gc.TPTR64: - a = x86.AORQ - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8: - a = x86.AXORB - - case OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16: - a = x86.AXORW - - case OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32: - a = x86.AXORL - - case OXOR_ | gc.TINT64, - OXOR_ | gc.TUINT64, - OXOR_ | gc.TPTR64: - a = x86.AXORQ - - case OLROT_ | gc.TINT8, - OLROT_ | gc.TUINT8: - a = x86.AROLB - - case OLROT_ | gc.TINT16, - OLROT_ | gc.TUINT16: - a = x86.AROLW - - case OLROT_ | gc.TINT32, - OLROT_ | gc.TUINT32, - OLROT_ | gc.TPTR32: - a = x86.AROLL - - case OLROT_ | gc.TINT64, - OLROT_ | gc.TUINT64, - OLROT_ | gc.TPTR64: - a = x86.AROLQ - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8: - a = x86.ASHLB - - case OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16: - a = x86.ASHLW - - case OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32: - a = x86.ASHLL - - case OLSH_ | gc.TINT64, - OLSH_ | gc.TUINT64, - OLSH_ | gc.TPTR64: - a = x86.ASHLQ - - case ORSH_ | gc.TUINT8: - a = x86.ASHRB - - case ORSH_ | gc.TUINT16: - a = x86.ASHRW - - case ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32: - a = x86.ASHRL - - case ORSH_ | gc.TUINT64, - ORSH_ | gc.TPTR64: - a = x86.ASHRQ - - case ORSH_ | gc.TINT8: - a = x86.ASARB - - case ORSH_ | gc.TINT16: - a = x86.ASARW - - case ORSH_ | gc.TINT32: - a = x86.ASARL - - case ORSH_ | gc.TINT64: - a = x86.ASARQ - - case ORROTC_ | gc.TINT8, - ORROTC_ | gc.TUINT8: - a = x86.ARCRB - - case ORROTC_ | gc.TINT16, - ORROTC_ | gc.TUINT16: - a = x86.ARCRW - - case ORROTC_ | gc.TINT32, - ORROTC_ | gc.TUINT32: - a = x86.ARCRL - - case ORROTC_ | gc.TINT64, - ORROTC_ | gc.TUINT64: - a = x86.ARCRQ - - case OHMUL_ | gc.TINT8, - OMUL_ | gc.TINT8, - OMUL_ | gc.TUINT8: - a = x86.AIMULB - - case OHMUL_ | gc.TINT16, - OMUL_ | gc.TINT16, - OMUL_ | gc.TUINT16: - a = x86.AIMULW - - case OHMUL_ | gc.TINT32, - OMUL_ | gc.TINT32, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32: - a = x86.AIMULL - - case OHMUL_ | gc.TINT64, - OMUL_ | gc.TINT64, - OMUL_ | gc.TUINT64, - OMUL_ | gc.TPTR64: - a = x86.AIMULQ - - case OHMUL_ | gc.TUINT8: - a = x86.AMULB - - case OHMUL_ | gc.TUINT16: - a = x86.AMULW - - case OHMUL_ | gc.TUINT32, - OHMUL_ | gc.TPTR32: - a = x86.AMULL - - case OHMUL_ | gc.TUINT64, - OHMUL_ | gc.TPTR64: - a = x86.AMULQ - - case OMUL_ | gc.TFLOAT32: - a = x86.AMULSS - - case OMUL_ | gc.TFLOAT64: - a = x86.AMULSD - - case ODIV_ | gc.TINT8, - OMOD_ | gc.TINT8: - a = x86.AIDIVB - - case ODIV_ | gc.TUINT8, - OMOD_ | gc.TUINT8: - a = x86.ADIVB - - case ODIV_ | gc.TINT16, - OMOD_ | gc.TINT16: - a = x86.AIDIVW - - case ODIV_ | gc.TUINT16, - OMOD_ | gc.TUINT16: - a = x86.ADIVW - - case ODIV_ | gc.TINT32, - OMOD_ | gc.TINT32: - a = x86.AIDIVL - - case ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - OMOD_ | gc.TUINT32, - OMOD_ | gc.TPTR32: - a = x86.ADIVL - - case ODIV_ | gc.TINT64, - OMOD_ | gc.TINT64: - a = x86.AIDIVQ - - case ODIV_ | gc.TUINT64, - ODIV_ | gc.TPTR64, - OMOD_ | gc.TUINT64, - OMOD_ | gc.TPTR64: - a = x86.ADIVQ - - case OEXTEND_ | gc.TINT16: - a = x86.ACWD - - case OEXTEND_ | gc.TINT32: - a = x86.ACDQ - - case OEXTEND_ | gc.TINT64: - a = x86.ACQO - - case ODIV_ | gc.TFLOAT32: - a = x86.ADIVSS - - case ODIV_ | gc.TFLOAT64: - a = x86.ADIVSD - - case OSQRT_ | gc.TFLOAT64: - a = x86.ASQRTSD - } - - return a -} - -// jmptoset returns ASETxx for AJxx. -func jmptoset(jmp obj.As) obj.As { - switch jmp { - case x86.AJEQ: - return x86.ASETEQ - case x86.AJNE: - return x86.ASETNE - case x86.AJLT: - return x86.ASETLT - case x86.AJCS: - return x86.ASETCS - case x86.AJLE: - return x86.ASETLE - case x86.AJLS: - return x86.ASETLS - case x86.AJGT: - return x86.ASETGT - case x86.AJHI: - return x86.ASETHI - case x86.AJGE: - return x86.ASETGE - case x86.AJCC: - return x86.ASETCC - case x86.AJMI: - return x86.ASETMI - case x86.AJOC: - return x86.ASETOC - case x86.AJOS: - return x86.ASETOS - case x86.AJPC: - return x86.ASETPC - case x86.AJPL: - return x86.ASETPL - case x86.AJPS: - return x86.ASETPS - } - gc.Fatalf("jmptoset: no entry for %v", jmp) - panic("unreachable") -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -var clean [20]gc.Node - -var cleani int = 0 - -func sudoclean() { - if clean[cleani-1].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-1]) - } - if clean[cleani-2].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-2]) - } - cleani -= 2 -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - if n.Type == nil { - return false - } - - *a = obj.Addr{} - - switch n.Op { - case gc.OLITERAL: - if !gc.Isconst(n, gc.CTINT) { - break - } - v := n.Int64() - if v >= 32000 || v <= -32000 { - break - } - switch as { - default: - return false - - case x86.AADDB, - x86.AADDW, - x86.AADDL, - x86.AADDQ, - x86.ASUBB, - x86.ASUBW, - x86.ASUBL, - x86.ASUBQ, - x86.AANDB, - x86.AANDW, - x86.AANDL, - x86.AANDQ, - x86.AORB, - x86.AORW, - x86.AORL, - x86.AORQ, - x86.AXORB, - x86.AXORW, - x86.AXORL, - x86.AXORQ, - x86.AINCB, - x86.AINCW, - x86.AINCL, - x86.AINCQ, - x86.ADECB, - x86.ADECW, - x86.ADECL, - x86.ADECQ, - x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ: - break - } - - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - gc.Naddr(a, n) - return true - - case gc.ODOT, - gc.ODOTPTR: - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - var nn *gc.Node - var oary [10]int64 - o := gc.Dotoffset(n, oary[:], &nn) - if nn == nil { - sudoclean() - return false - } - - if nn.Addable && o == 1 && oary[0] >= 0 { - // directly addressable set of DOTs - n1 := *nn - - n1.Type = n.Type - n1.Xoffset += oary[0] - gc.Naddr(a, &n1) - return true - } - - gc.Regalloc(reg, gc.Types[gc.Tptr], nil) - n1 := *reg - n1.Op = gc.OINDREG - if oary[0] >= 0 { - gc.Agen(nn, reg) - n1.Xoffset = oary[0] - } else { - gc.Cgen(nn, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[0] + 1) - } - - for i := 1; i < o; i++ { - if oary[i] >= 0 { - gc.Fatalf("can't happen") - } - gins(movptr, &n1, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[i] + 1) - } - - a.Type = obj.TYPE_NONE - a.Index = x86.REG_NONE - gc.Fixlargeoffset(&n1) - gc.Naddr(a, &n1) - return true - - case gc.OINDEX: - return false - } - - return false -} diff --git a/src/cmd/compile/internal/amd64/peep.go b/src/cmd/compile/internal/amd64/peep.go deleted file mode 100644 index d74f670c5d..0000000000 --- a/src/cmd/compile/internal/amd64/peep.go +++ /dev/null @@ -1,1025 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package amd64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -var gactive uint32 - -const ( - exregoffset = x86.REG_R15 -) - -// do we need the carry bit -func needc(p *obj.Prog) bool { - for p != nil { - flags := progcarryflags(p) - if flags&gc.UseCarry != 0 { - return true - } - if flags&(gc.SetCarry|gc.KillCarry) != 0 { - return false - } - p = p.Link - } - - return false -} - -func rnops(r *gc.Flow) *gc.Flow { - if r != nil { - var p *obj.Prog - var r1 *gc.Flow - for { - p = r.Prog - if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { - break - } - r1 = gc.Uniqs(r) - if r1 == nil { - break - } - r = r1 - } - } - - return r -} - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - // byte, word arithmetic elimination. - elimshortmov(g) - - // constant propagation - // find MOV $con,R followed by - // another MOV $con,R without - // setting R in the interim - var p *obj.Prog - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.ALEAL, - x86.ALEAQ: - if regtyp(&p.To) { - if p.From.Sym != nil { - if p.From.Index == x86.REG_NONE { - conprop(r) - } - } - } - - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { - conprop(r) - } - } - } - } - - var r *gc.Flow - var r1 *gc.Flow - var p1 *obj.Prog - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVL, - x86.AMOVQ, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if regtyp(&p.From) { - if copyprop(g, r) { - excise(r) - t++ - } else if subprop(r) && copyprop(g, r) { - excise(r) - t++ - } - } - } - - case x86.AMOVBLZX, - x86.AMOVWLZX, - x86.AMOVBLSX, - x86.AMOVWLSX: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVL - t++ - } - } - } - - case x86.AMOVBQSX, - x86.AMOVBQZX, - x86.AMOVWQSX, - x86.AMOVWQZX, - x86.AMOVLQSX, - x86.AMOVLQZX, - x86.AMOVQL: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVQ - t++ - } - } - } - - case x86.AADDL, - x86.AADDQ, - x86.AADDW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.AADDQ { - p.As = x86.ADECQ - } else if p.As == x86.AADDL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.AADDQ { - p.As = x86.AINCQ - } else if p.As == x86.AADDL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - case x86.ASUBL, - x86.ASUBQ, - x86.ASUBW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.ASUBQ { - p.As = x86.AINCQ - } else if p.As == x86.ASUBL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.ASUBQ { - p.As = x86.ADECQ - } else if p.As == x86.ASUBL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - } - } - - if t != 0 { - goto loop1 - } - - // MOVLQZX removal. - // The MOVLQZX exists to avoid being confused for a - // MOVL that is just copying 32-bit data around during - // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 - // if it is dominated by an earlier ADDL/MOVL/etc into R1 that - // will have already cleared the high bits. - // - // MOVSD removal. - // We never use packed registers, so a MOVSD between registers - // can be replaced by MOVAPD, which moves the pair of float64s - // instead of just the lower one. We only use the lower one, but - // the processor can do better if we do moves using both. - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if p.As == x86.AMOVLQZX { - if regtyp(&p.From) { - if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg { - if prevl(r, p.From.Reg) { - excise(r) - } - } - } - } - - if p.As == x86.AMOVSD { - if regtyp(&p.From) { - if regtyp(&p.To) { - p.As = x86.AMOVAPD - } - } - } - } - - // load pipelining - // push any load from memory as early as possible - // to give it time to complete before use. - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVQ, - x86.AMOVLQZX: - if regtyp(&p.To) && !regconsttyp(&p.From) { - pushback(r) - } - } - } - - gc.Flowend(g) -} - -func pushback(r0 *gc.Flow) { - var r *gc.Flow - var p *obj.Prog - - var b *gc.Flow - p0 := r0.Prog - for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { - p = r.Prog - if p.As != obj.ANOP { - if !regconsttyp(&p.From) || !regtyp(&p.To) { - break - } - if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 { - break - } - } - - if p.As == obj.ACALL { - break - } - b = r - } - - if b == nil { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("no pushback: %v\n", r0.Prog) - if r != nil { - fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) - } - } - - return - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("pushback\n") - for r := b; ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } - - t := *r0.Prog - for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { - p0 = r.Link.Prog - p = r.Prog - p0.As = p.As - p0.Lineno = p.Lineno - p0.From = p.From - p0.To = p.To - - if r == b { - break - } - } - - p0 = r.Prog - p0.As = t.As - p0.Lineno = t.Lineno - p0.From = t.From - p0.To = t.To - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tafter\n") - for r := b; ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - - obj.Nopout(p) - - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15) -} - -// movb elimination. -// movb is simulated by the linker -// when a register other than ax, bx, cx, dx -// is used, so rewrite to other instructions -// when possible. a movb into a register -// can smash the entire 32-bit register without -// causing any trouble. -// -// TODO: Using the Q forms here instead of the L forms -// seems unnecessary, and it makes the instructions longer. -func elimshortmov(g *gc.Graph) { - var p *obj.Prog - - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if regtyp(&p.To) { - switch p.As { - case x86.AINCB, - x86.AINCW: - p.As = x86.AINCQ - - case x86.ADECB, - x86.ADECW: - p.As = x86.ADECQ - - case x86.ANEGB, - x86.ANEGW: - p.As = x86.ANEGQ - - case x86.ANOTB, - x86.ANOTW: - p.As = x86.ANOTQ - } - - if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or arithmetic into partial register. - // from another register or constant can be movl. - // we don't switch to 64-bit arithmetic if it can - // change how the carry bit is set (and the carry bit is needed). - switch p.As { - case x86.AMOVB, - x86.AMOVW: - p.As = x86.AMOVQ - - case x86.AADDB, - x86.AADDW: - if !needc(p.Link) { - p.As = x86.AADDQ - } - - case x86.ASUBB, - x86.ASUBW: - if !needc(p.Link) { - p.As = x86.ASUBQ - } - - case x86.AMULB, - x86.AMULW: - p.As = x86.AMULQ - - case x86.AIMULB, - x86.AIMULW: - p.As = x86.AIMULQ - - case x86.AANDB, - x86.AANDW: - p.As = x86.AANDQ - - case x86.AORB, - x86.AORW: - p.As = x86.AORQ - - case x86.AXORB, - x86.AXORW: - p.As = x86.AXORQ - - case x86.ASHLB, - x86.ASHLW: - p.As = x86.ASHLQ - } - } else if p.From.Type != obj.TYPE_REG { - // explicit zero extension, but don't - // do that if source is a byte register - // (only AH can occur and it's forbidden). - switch p.As { - case x86.AMOVB: - p.As = x86.AMOVBQZX - - case x86.AMOVW: - p.As = x86.AMOVWQZX - } - } - } - } -} - -// is 'a' a register or constant? -func regconsttyp(a *obj.Addr) bool { - if regtyp(a) { - return true - } - switch a.Type { - case obj.TYPE_CONST, - obj.TYPE_FCONST, - obj.TYPE_SCONST, - obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants. - return true - } - - return false -} - -// is reg guaranteed to be truncated by a previous L instruction? -func prevl(r0 *gc.Flow, reg int16) bool { - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - p := r.Prog - if p.To.Type == obj.TYPE_REG && p.To.Reg == reg { - flags := progflags(p) - if flags&gc.RightWrite != 0 { - if flags&gc.SizeL != 0 { - return true - } - return false - } - } - } - return false -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("subprop %v\n", r0.Prog) - } - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) - } - return false - } - - v2 := &p.To - if !regtyp(v2) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - } - - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\t? %v\n", r.Prog) - } - if gc.Uniqs(r) == nil { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tno unique successor\n") - } - break - } - - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tfound %v; return 0\n", p) - } - return false - } - - if p.Info.Reguse|p.Info.Regset != 0 { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tfound %v; return 0\n", p) - } - return false - } - - if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type && p.From.Reg == v2.Reg { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - v1.Reg, v2.Reg = v2.Reg, v1.Reg - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - - if copyau(&p.From, v2) || copyau(&p.To, v2) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) - } - break - } - - if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopysub failed\n") - } - break - } - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tran off end; return 0\n") - } - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("copyprop %v\n", r0.Prog) - } - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, false) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - return true -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - case obj.AJMP: - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: - if s != nil { - return 1 - } - return 3 - - case obj.ACALL: - if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset { - return 2 - } - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 2 - } - if v.Type == p.From.Type && v.Reg == p.From.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - case obj.ATEXT: - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 3 - } - return 0 - } - - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - return 0 - } - - if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { - return 2 - } - - if (p.Info.Reguse|p.Info.Regset)&FtoB(int(v.Reg)) != 0 { - return 2 - } - - if p.Info.Flags&gc.LeftAddr != 0 { - if copyas(&p.From, v) { - return 2 - } - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { - if copyas(&p.To, v) { - return 2 - } - } - - if p.Info.Flags&gc.RightWrite != 0 { - if copyas(&p.To, v) { - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - return 0 - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - } - - if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - } - return 0 -} - -/* - * direct reference, - * could be set/use depending on - * semantics - */ -func copyas(a *obj.Addr, v *obj.Addr) bool { - if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B { - gc.Fatalf("use of byte register") - } - if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B { - gc.Fatalf("use of byte register") - } - - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: copyas returned 1\n") - } - return true - } - - if regtyp(v) { - if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: found indir use - return 1\n") - } - return true - } - - if a.Index == v.Reg { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tcopyau: found index use - return 1\n") - } - return true - } - } - return false -} - -// copysub substitute s for v in a. -// copysub returns true on failure to substitute. TODO(dfc) reverse this logic, copysub should return false on failure -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if copyas(a, v) { - if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_R15 || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X0+15 { - if f { - a.Reg = s.Reg - } - } - return false - } - - if regtyp(v) { - if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { - if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { - return true /* can't use BP-base with index */ - } - if f { - a.Reg = s.Reg - } - } - if a.Index == v.Reg { - if f { - a.Index = s.Reg - } - } - } - return false -} - -func conprop(r0 *gc.Flow) { - p0 := r0.Prog - v0 := &p0.To - r := r0 - -loop: - r = gc.Uniqs(r) - if r == nil || r == r0 { - return - } - if gc.Uniqp(r) == nil { - return - } - - p := r.Prog - t := copyu(p, v0, nil) - switch t { - case 0, // miss - 1: // use - goto loop - - case 2, // rar - 4: // use and set - break - - case 3: // set - if p.As == p0.As { - if p.From.Type == p0.From.Type { - if p.From.Reg == p0.From.Reg { - if p.From.Node == p0.From.Node { - if p.From.Offset == p0.From.Offset { - if p.From.Scale == p0.From.Scale { - if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { - if p.From.Index == p0.From.Index { - excise(r) - goto loop - } - } - } - } - } - } - } - } - } -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP -} diff --git a/src/cmd/compile/internal/amd64/reg.go b/src/cmd/compile/internal/amd64/reg.go index 361b1e2d6c..bff70a2964 100644 --- a/src/cmd/compile/internal/amd64/reg.go +++ b/src/cmd/compile/internal/amd64/reg.go @@ -30,72 +30,7 @@ package amd64 -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj/x86" -) - -const ( - NREGVAR = 32 -) - -var regname = []string{ - ".AX", - ".CX", - ".DX", - ".BX", - ".SP", - ".BP", - ".SI", - ".DI", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".X0", - ".X1", - ".X2", - ".X3", - ".X4", - ".X5", - ".X6", - ".X7", - ".X8", - ".X9", - ".X10", - ".X11", - ".X12", - ".X13", - ".X14", - ".X15", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - return RtoB(x86.REG_SP) -} - -func doregbits(r int) uint64 { - b := uint64(0) - if r >= x86.REG_AX && r <= x86.REG_R15 { - b |= RtoB(r) - } else if r >= x86.REG_AL && r <= x86.REG_R15B { - b |= RtoB(r - x86.REG_AL + x86.REG_AX) - } else if r >= x86.REG_AH && r <= x86.REG_BH { - b |= RtoB(r - x86.REG_AH + x86.REG_AX) - } else if r >= x86.REG_X0 && r <= x86.REG_X0+15 { - b |= FtoB(r) - } - return b -} +import "cmd/internal/obj/x86" // For ProgInfo. const ( @@ -115,38 +50,3 @@ func RtoB(r int) uint64 { } return 1 << uint(r-x86.REG_AX) } - -func BtoR(b uint64) int { - b &= 0xffff - if gc.Nacl { - b &^= (1<<(x86.REG_BP-x86.REG_AX) | 1<<(x86.REG_R15-x86.REG_AX)) - } else if gc.Ctxt.Framepointer_enabled { - // BP is part of the calling convention if framepointer_enabled. - b &^= (1 << (x86.REG_BP - x86.REG_AX)) - } - if b == 0 { - return 0 - } - return gc.Bitno(b) + x86.REG_AX -} - -/* - * bit reg - * 16 X0 - * ... - * 31 X15 - */ -func FtoB(f int) uint64 { - if f < x86.REG_X0 || f > x86.REG_X15 { - return 0 - } - return 1 << uint(f-x86.REG_X0+16) -} - -func BtoF(b uint64) int { - b &= 0xFFFF0000 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 16 + x86.REG_X0 -} diff --git a/src/cmd/compile/internal/arm/cgen.go b/src/cmd/compile/internal/arm/cgen.go deleted file mode 100644 index c60df08dad..0000000000 --- a/src/cmd/compile/internal/arm/cgen.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package arm - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -/* - * generate array index into res. - * n might be any size; res is 32-bit. - * returns Prog* to patch to panic call. - */ -func cgenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - if !gc.Is64(n.Type) { - gc.Cgen(n, res) - return nil - } - - var tmp gc.Node - gc.Tempname(&tmp, gc.Types[gc.TINT64]) - gc.Cgen(n, &tmp) - var lo gc.Node - var hi gc.Node - split64(&tmp, &lo, &hi) - gmove(&lo, res) - if bounded { - splitclean() - return nil - } - - var n1 gc.Node - gc.Regalloc(&n1, gc.Types[gc.TINT32], nil) - var n2 gc.Node - gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) - gmove(&hi, &n1) - gmove(&zero, &n2) - gins(arm.ACMP, &n1, &n2) - gc.Regfree(&n2) - gc.Regfree(&n1) - splitclean() - return gc.Gbranch(arm.ABNE, nil, -1) -} - -func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - gc.Tempname(res, n.Type) - return cgenindex(n, res, bounded) -} - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op obj.As - switch align { - default: - gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = arm.AMOVB - - case 2: - op = arm.AMOVH - - case 4: - op = arm.AMOVW - } - - if w%int64(align) != 0 { - gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - if osrc%int64(align) != 0 || odst%int64(align) != 0 { - gc.Fatalf("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) - } - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - if osrc < odst && odst < osrc+w { - dir = -dir - } - - if op == arm.AMOVW && !gc.Nacl && dir > 0 && c >= 4 && c <= 128 { - var r0 gc.Node - r0.Op = gc.OREGISTER - r0.Reg = arm.REG_R0 - var r1 gc.Node - r1.Op = gc.OREGISTER - r1.Reg = arm.REG_R0 + 1 - var r2 gc.Node - r2.Op = gc.OREGISTER - r2.Reg = arm.REG_R0 + 2 - - var src gc.Node - gc.Regalloc(&src, gc.Types[gc.Tptr], &r1) - var dst gc.Node - gc.Regalloc(&dst, gc.Types[gc.Tptr], &r2) - if n.Ullman >= res.Ullman { - // eval n first - gc.Agen(n, &src) - - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - // eval res first - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - gc.Agen(n, &src) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], &r0) - f := gc.Sysfunc("duffcopy") - p := gins(obj.ADUFFCOPY, nil, f) - gc.Afunclit(&p.To, f) - - // 8 and 128 = magic constants: see ../../runtime/asm_arm.s - p.To.Offset = 8 * (128 - int64(c)) - - gc.Regfree(&tmp) - gc.Regfree(&src) - gc.Regfree(&dst) - return - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(arm.AMOVW, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.TUINT32], nil) - - // set up end marker - var nend gc.Node - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.TUINT32], nil) - - p := gins(arm.AMOVW, &src, &nend) - p.From.Type = obj.TYPE_ADDR - if dir < 0 { - p.From.Offset = int64(dir) - } else { - p.From.Offset = w - } - } - - // move src and dest to the end of block if necessary - if dir < 0 { - p := gins(arm.AMOVW, &src, &src) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w + int64(dir) - - p = gins(arm.AMOVW, &dst, &dst) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w + int64(dir) - } - - // move - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond |= arm.C_PBIT - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond |= arm.C_PBIT - - p = gins(arm.ACMP, &src, nil) - raddr(&nend, p) - - gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - var p *obj.Prog - for ; c > 0; c-- { - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond |= arm.C_PBIT - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond |= arm.C_PBIT - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/compile/internal/arm/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go deleted file mode 100644 index 33e840615c..0000000000 --- a/src/cmd/compile/internal/arm/cgen64.go +++ /dev/null @@ -1,859 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package arm - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" -) - -/* - * attempt to generate 64-bit - * res = n - * return 1 on success, 0 if op not handled. - */ -func cgen64(n *gc.Node, res *gc.Node) { - if res.Op != gc.OINDREG && res.Op != gc.ONAME { - gc.Dump("n", n) - gc.Dump("res", res) - gc.Fatalf("cgen64 %v of %v", n.Op, res.Op) - } - - l := n.Left - var t1 gc.Node - if !l.Addable { - gc.Tempname(&t1, l.Type) - gc.Cgen(l, &t1) - l = &t1 - } - - var hi1 gc.Node - var lo1 gc.Node - split64(l, &lo1, &hi1) - switch n.Op { - default: - gc.Fatalf("cgen64 %v", n.Op) - - case gc.OMINUS: - var lo2 gc.Node - var hi2 gc.Node - split64(res, &lo2, &hi2) - - gc.Regalloc(&t1, lo1.Type, nil) - var al gc.Node - gc.Regalloc(&al, lo1.Type, nil) - var ah gc.Node - gc.Regalloc(&ah, hi1.Type, nil) - - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - - gmove(ncon(0), &t1) - p1 := gins(arm.ASUB, &al, &t1) - p1.Scond |= arm.C_SBIT - gins(arm.AMOVW, &t1, &lo2) - - gmove(ncon(0), &t1) - gins(arm.ASBC, &ah, &t1) - gins(arm.AMOVW, &t1, &hi2) - - gc.Regfree(&t1) - gc.Regfree(&al) - gc.Regfree(&ah) - splitclean() - splitclean() - return - - case gc.OCOM: - gc.Regalloc(&t1, lo1.Type, nil) - gmove(ncon(^uint32(0)), &t1) - - var lo2 gc.Node - var hi2 gc.Node - split64(res, &lo2, &hi2) - var n1 gc.Node - gc.Regalloc(&n1, lo1.Type, nil) - - gins(arm.AMOVW, &lo1, &n1) - gins(arm.AEOR, &t1, &n1) - gins(arm.AMOVW, &n1, &lo2) - - gins(arm.AMOVW, &hi1, &n1) - gins(arm.AEOR, &t1, &n1) - gins(arm.AMOVW, &n1, &hi2) - - gc.Regfree(&t1) - gc.Regfree(&n1) - splitclean() - splitclean() - return - - // binary operators. - // common setup below. - case gc.OADD, - gc.OSUB, - gc.OMUL, - gc.OLSH, - gc.ORSH, - gc.OAND, - gc.OOR, - gc.OXOR, - gc.OLROT: - break - } - - // setup for binary operators - r := n.Right - - if r != nil && !r.Addable { - var t2 gc.Node - gc.Tempname(&t2, r.Type) - gc.Cgen(r, &t2) - r = &t2 - } - - var hi2 gc.Node - var lo2 gc.Node - if gc.Is64(r.Type) { - split64(r, &lo2, &hi2) - } - - var al gc.Node - gc.Regalloc(&al, lo1.Type, nil) - var ah gc.Node - gc.Regalloc(&ah, hi1.Type, nil) - - // Do op. Leave result in ah:al. - switch n.Op { - default: - gc.Fatalf("cgen64: not implemented: %v\n", n) - - // TODO: Constants - case gc.OADD: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi2, &bh) - gins(arm.AMOVW, &lo2, &bl) - p1 := gins(arm.AADD, &bl, &al) - p1.Scond |= arm.C_SBIT - gins(arm.AADC, &bh, &ah) - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO: Constants. - case gc.OSUB: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo2, &bl) - gins(arm.AMOVW, &hi2, &bh) - p1 := gins(arm.ASUB, &bl, &al) - p1.Scond |= arm.C_SBIT - gins(arm.ASBC, &bh, &ah) - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO(kaib): this can be done with 4 regs and does not need 6 - case gc.OMUL: - var bl gc.Node - gc.Regalloc(&bl, gc.Types[gc.TPTR32], nil) - - var bh gc.Node - gc.Regalloc(&bh, gc.Types[gc.TPTR32], nil) - var cl gc.Node - gc.Regalloc(&cl, gc.Types[gc.TPTR32], nil) - var ch gc.Node - gc.Regalloc(&ch, gc.Types[gc.TPTR32], nil) - - // load args into bh:bl and bh:bl. - gins(arm.AMOVW, &hi1, &bh) - - gins(arm.AMOVW, &lo1, &bl) - gins(arm.AMOVW, &hi2, &ch) - gins(arm.AMOVW, &lo2, &cl) - - // bl * cl -> ah al - p1 := gins(arm.AMULLU, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bl.Reg - p1.Reg = cl.Reg - p1.To.Type = obj.TYPE_REGREG - p1.To.Reg = ah.Reg - p1.To.Offset = int64(al.Reg) - - //print("%v\n", p1); - - // bl * ch + ah -> ah - p1 = gins(arm.AMULA, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bl.Reg - p1.Reg = ch.Reg - p1.To.Type = obj.TYPE_REGREG2 - p1.To.Reg = ah.Reg - p1.To.Offset = int64(ah.Reg) - - //print("%v\n", p1); - - // bh * cl + ah -> ah - p1 = gins(arm.AMULA, nil, nil) - - p1.From.Type = obj.TYPE_REG - p1.From.Reg = bh.Reg - p1.Reg = cl.Reg - p1.To.Type = obj.TYPE_REGREG2 - p1.To.Reg = ah.Reg - p1.To.Offset = int64(ah.Reg) - - //print("%v\n", p1); - - gc.Regfree(&bh) - - gc.Regfree(&bl) - gc.Regfree(&ch) - gc.Regfree(&cl) - - // We only rotate by a constant c in [0,64). - // if c >= 32: - // lo, hi = hi, lo - // c -= 32 - // if c == 0: - // no-op - // else: - // t = hi - // shld hi:lo, c - // shld lo:t, c - case gc.OLROT: - v := uint64(r.Int64()) - - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - if v >= 32 { - // reverse during load to do the first 32 bits of rotate - v -= 32 - - gins(arm.AMOVW, &hi1, &bl) - gins(arm.AMOVW, &lo1, &bh) - } else { - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - } - - if v == 0 { - gins(arm.AMOVW, &bh, &ah) - gins(arm.AMOVW, &bl, &al) - } else { - // rotate by 1 <= v <= 31 - // MOVW bl<>(32-v), ah - // OR bh>>(32-v), al - gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v), &al) - - gshift(arm.AMOVW, &bh, arm.SHIFT_LL, int32(v), &ah) - gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) - gshift(arm.AORR, &bh, arm.SHIFT_LR, int32(32-v), &al) - } - - gc.Regfree(&bl) - gc.Regfree(&bh) - - case gc.OLSH: - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - - var p6 *obj.Prog - var s gc.Node - var n1 gc.Node - var creg gc.Node - var p1 *obj.Prog - var p2 *obj.Prog - var p3 *obj.Prog - var p4 *obj.Prog - var p5 *obj.Prog - if r.Op == gc.OLITERAL { - v := uint64(r.Int64()) - if v >= 64 { - // TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al) - // here and below (verify it optimizes to EOR) - gins(arm.AEOR, &al, &al) - - gins(arm.AEOR, &ah, &ah) - } else if v > 32 { - gins(arm.AEOR, &al, &al) - - // MOVW bl<<(v-32), ah - gshift(arm.AMOVW, &bl, arm.SHIFT_LL, int32(v-32), &ah) - } else if v == 32 { - gins(arm.AEOR, &al, &al) - gins(arm.AMOVW, &bl, &ah) - } else if v > 0 { - // MOVW bl<>(32-v), ah - gshift(arm.AORR, &bl, arm.SHIFT_LR, int32(32-v), &ah) - } else { - gins(arm.AMOVW, &bl, &al) - gins(arm.AMOVW, &bh, &ah) - } - - goto olsh_break - } - - gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) - if gc.Is64(r.Type) { - // shift is >= 1<<32 - var cl gc.Node - var ch gc.Node - split64(r, &cl, &ch) - - gmove(&ch, &s) - gins(arm.ATST, &s, nil) - p6 = gc.Gbranch(arm.ABNE, nil, 0) - gmove(&cl, &s) - splitclean() - } else { - gmove(r, &s) - p6 = nil - } - - gins(arm.ATST, &s, nil) - - // shift == 0 - p1 = gins(arm.AMOVW, &bl, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bh, &ah) - p1.Scond = arm.C_SCOND_EQ - p2 = gc.Gbranch(arm.ABEQ, nil, 0) - - // shift is < 32 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO bl<>creg, ah - p1 = gregshift(arm.AORR, &bl, arm.SHIFT_LR, &creg, &ah) - - p1.Scond = arm.C_SCOND_LO - - // BLO end - p3 = gc.Gbranch(arm.ABLO, nil, 0) - - // shift == 32 - p1 = gins(arm.AEOR, &al, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bl, &ah) - p1.Scond = arm.C_SCOND_EQ - p4 = gc.Gbranch(arm.ABEQ, nil, 0) - - // shift is < 64 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // EOR.LO al, al - p1 = gins(arm.AEOR, &al, &al) - - p1.Scond = arm.C_SCOND_LO - - // MOVW.LO creg>>1, creg - p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO creg, s - p1 = gins(arm.ASUB, &creg, &s) - - p1.Scond = arm.C_SCOND_LO - - // MOVW bl<= 64 - if p6 != nil { - gc.Patch(p6, gc.Pc) - } - gins(arm.AEOR, &al, &al) - gins(arm.AEOR, &ah, &ah) - - gc.Patch(p2, gc.Pc) - gc.Patch(p3, gc.Pc) - gc.Patch(p4, gc.Pc) - gc.Patch(p5, gc.Pc) - gc.Regfree(&s) - gc.Regfree(&creg) - - olsh_break: - gc.Regfree(&bl) - gc.Regfree(&bh) - - case gc.ORSH: - var bl gc.Node - gc.Regalloc(&bl, lo1.Type, nil) - var bh gc.Node - gc.Regalloc(&bh, hi1.Type, nil) - gins(arm.AMOVW, &hi1, &bh) - gins(arm.AMOVW, &lo1, &bl) - - var p4 *obj.Prog - var p5 *obj.Prog - var n1 gc.Node - var p6 *obj.Prog - var s gc.Node - var p1 *obj.Prog - var p2 *obj.Prog - var creg gc.Node - var p3 *obj.Prog - if r.Op == gc.OLITERAL { - v := uint64(r.Int64()) - if v >= 64 { - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) - - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &al, &al) - gins(arm.AEOR, &ah, &ah) - } - } else if v > 32 { - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->(v-32), al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v-32), &al) - - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - // MOVW bh>>(v-32), al - gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v-32), &al) - - gins(arm.AEOR, &ah, &ah) - } - } else if v == 32 { - gins(arm.AMOVW, &bh, &al) - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &ah, &ah) - } - } else if v > 0 { - // MOVW bl>>v, al - gshift(arm.AMOVW, &bl, arm.SHIFT_LR, int32(v), &al) - - // OR bh<<(32-v), al - gshift(arm.AORR, &bh, arm.SHIFT_LL, int32(32-v), &al) - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->v, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, int32(v), &ah) - } else { - // MOVW bh>>v, ah - gshift(arm.AMOVW, &bh, arm.SHIFT_LR, int32(v), &ah) - } - } else { - gins(arm.AMOVW, &bl, &al) - gins(arm.AMOVW, &bh, &ah) - } - - goto orsh_break - } - - gc.Regalloc(&s, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&creg, gc.Types[gc.TUINT32], nil) - if gc.Is64(r.Type) { - // shift is >= 1<<32 - var ch gc.Node - var cl gc.Node - split64(r, &cl, &ch) - - gmove(&ch, &s) - gins(arm.ATST, &s, nil) - var p1 *obj.Prog - if bh.Type.Etype == gc.TINT32 { - p1 = gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - p1 = gins(arm.AEOR, &ah, &ah) - } - p1.Scond = arm.C_SCOND_NE - p6 = gc.Gbranch(arm.ABNE, nil, 0) - gmove(&cl, &s) - splitclean() - } else { - gmove(r, &s) - p6 = nil - } - - gins(arm.ATST, &s, nil) - - // shift == 0 - p1 = gins(arm.AMOVW, &bl, &al) - - p1.Scond = arm.C_SCOND_EQ - p1 = gins(arm.AMOVW, &bh, &ah) - p1.Scond = arm.C_SCOND_EQ - p2 = gc.Gbranch(arm.ABEQ, nil, 0) - - // check if shift is < 32 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 32) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO bl>>s, al - p1 = gregshift(arm.AMOVW, &bl, arm.SHIFT_LR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO s,creg - p1 = gins(arm.ASUB, &s, &creg) - - p1.Scond = arm.C_SCOND_LO - - // OR.LO bh<<(32-s), al - p1 = gregshift(arm.AORR, &bh, arm.SHIFT_LL, &creg, &al) - - p1.Scond = arm.C_SCOND_LO - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->s, ah - p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &ah) - } else { - // MOVW bh>>s, ah - p1 = gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &ah) - } - - p1.Scond = arm.C_SCOND_LO - - // BLO end - p3 = gc.Gbranch(arm.ABLO, nil, 0) - - // shift == 32 - p1 = gins(arm.AMOVW, &bh, &al) - - p1.Scond = arm.C_SCOND_EQ - if bh.Type.Etype == gc.TINT32 { - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &ah) - } else { - gins(arm.AEOR, &ah, &ah) - } - p4 = gc.Gbranch(arm.ABEQ, nil, 0) - - // check if shift is < 64 - gc.Nodconst(&n1, gc.Types[gc.TUINT32], 64) - - gmove(&n1, &creg) - gins(arm.ACMP, &s, &creg) - - // MOVW.LO creg>>1, creg - p1 = gshift(arm.AMOVW, &creg, arm.SHIFT_LR, 1, &creg) - - p1.Scond = arm.C_SCOND_LO - - // SUB.LO creg, s - p1 = gins(arm.ASUB, &creg, &s) - - p1.Scond = arm.C_SCOND_LO - - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->(s-32), al - p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_AR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - } else { - // MOVW bh>>(v-32), al - p1 := gregshift(arm.AMOVW, &bh, arm.SHIFT_LR, &s, &al) - - p1.Scond = arm.C_SCOND_LO - } - - // BLO end - p5 = gc.Gbranch(arm.ABLO, nil, 0) - - // s >= 64 - if p6 != nil { - gc.Patch(p6, gc.Pc) - } - if bh.Type.Etype == gc.TINT32 { - // MOVW bh->31, al - gshift(arm.AMOVW, &bh, arm.SHIFT_AR, 31, &al) - } else { - gins(arm.AEOR, &al, &al) - } - - gc.Patch(p2, gc.Pc) - gc.Patch(p3, gc.Pc) - gc.Patch(p4, gc.Pc) - gc.Patch(p5, gc.Pc) - gc.Regfree(&s) - gc.Regfree(&creg) - - orsh_break: - gc.Regfree(&bl) - gc.Regfree(&bh) - - // TODO(kaib): literal optimizations - // make constant the right side (it usually is anyway). - // if(lo1.op == OLITERAL) { - // nswap(&lo1, &lo2); - // nswap(&hi1, &hi2); - // } - // if(lo2.op == OLITERAL) { - // // special cases for constants. - // lv = mpgetfix(lo2.val.u.xval); - // hv = mpgetfix(hi2.val.u.xval); - // splitclean(); // right side - // split64(res, &lo2, &hi2); - // switch(n->op) { - // case OXOR: - // gmove(&lo1, &lo2); - // gmove(&hi1, &hi2); - // switch(lv) { - // case 0: - // break; - // case 0xffffffffu: - // gins(ANOTL, N, &lo2); - // break; - // default: - // gins(AXORL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // break; - // case 0xffffffffu: - // gins(ANOTL, N, &hi2); - // break; - // default: - // gins(AXORL, ncon(hv), &hi2); - // break; - // } - // break; - - // case OAND: - // switch(lv) { - // case 0: - // gins(AMOVL, ncon(0), &lo2); - // break; - // default: - // gmove(&lo1, &lo2); - // if(lv != 0xffffffffu) - // gins(AANDL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // gins(AMOVL, ncon(0), &hi2); - // break; - // default: - // gmove(&hi1, &hi2); - // if(hv != 0xffffffffu) - // gins(AANDL, ncon(hv), &hi2); - // break; - // } - // break; - - // case OOR: - // switch(lv) { - // case 0: - // gmove(&lo1, &lo2); - // break; - // case 0xffffffffu: - // gins(AMOVL, ncon(0xffffffffu), &lo2); - // break; - // default: - // gmove(&lo1, &lo2); - // gins(AORL, ncon(lv), &lo2); - // break; - // } - // switch(hv) { - // case 0: - // gmove(&hi1, &hi2); - // break; - // case 0xffffffffu: - // gins(AMOVL, ncon(0xffffffffu), &hi2); - // break; - // default: - // gmove(&hi1, &hi2); - // gins(AORL, ncon(hv), &hi2); - // break; - // } - // break; - // } - // splitclean(); - // splitclean(); - // goto out; - // } - case gc.OXOR, - gc.OAND, - gc.OOR: - var n1 gc.Node - gc.Regalloc(&n1, lo1.Type, nil) - - gins(arm.AMOVW, &lo1, &al) - gins(arm.AMOVW, &hi1, &ah) - gins(arm.AMOVW, &lo2, &n1) - gins(optoas(n.Op, lo1.Type), &n1, &al) - gins(arm.AMOVW, &hi2, &n1) - gins(optoas(n.Op, lo1.Type), &n1, &ah) - gc.Regfree(&n1) - } - - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - - split64(res, &lo1, &hi1) - gins(arm.AMOVW, &al, &lo1) - gins(arm.AMOVW, &ah, &hi1) - splitclean() - - //out: - gc.Regfree(&al) - - gc.Regfree(&ah) -} - -/* - * generate comparison of nl, nr, both 64-bit. - * nl is memory; nr is constant or memory. - */ -func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) { - var lo1 gc.Node - var hi1 gc.Node - var lo2 gc.Node - var hi2 gc.Node - var r1 gc.Node - var r2 gc.Node - - split64(nl, &lo1, &hi1) - split64(nr, &lo2, &hi2) - - // compare most significant word; - // if they differ, we're done. - t := hi1.Type - - gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) - gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) - gins(arm.AMOVW, &hi1, &r1) - gins(arm.AMOVW, &hi2, &r2) - gins(arm.ACMP, &r1, &r2) - gc.Regfree(&r1) - gc.Regfree(&r2) - - var br *obj.Prog - switch op { - default: - gc.Fatalf("cmp64 %v %v", op, t) - - // cmp hi - // bne L - // cmp lo - // beq to - // L: - case gc.OEQ: - br = gc.Gbranch(arm.ABNE, nil, -likely) - - // cmp hi - // bne to - // cmp lo - // bne to - case gc.ONE: - gc.Patch(gc.Gbranch(arm.ABNE, nil, likely), to) - - // cmp hi - // bgt to - // blt L - // cmp lo - // bge to (or bgt to) - // L: - case gc.OGE, - gc.OGT: - gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) - - // cmp hi - // blt to - // bgt L - // cmp lo - // ble to (or jlt to) - // L: - case gc.OLE, - gc.OLT: - gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) - } - - // compare least significant word - t = lo1.Type - - gc.Regalloc(&r1, gc.Types[gc.TINT32], nil) - gc.Regalloc(&r2, gc.Types[gc.TINT32], nil) - gins(arm.AMOVW, &lo1, &r1) - gins(arm.AMOVW, &lo2, &r2) - gins(arm.ACMP, &r1, &r2) - gc.Regfree(&r1) - gc.Regfree(&r2) - - // jump again - gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) - - // point first branch down here if appropriate - if br != nil { - gc.Patch(br, gc.Pc) - } - - splitclean() - splitclean() -} diff --git a/src/cmd/compile/internal/arm/galign.go b/src/cmd/compile/internal/arm/galign.go index afd86e44c8..acd150846f 100644 --- a/src/cmd/compile/internal/arm/galign.go +++ b/src/cmd/compile/internal/arm/galign.go @@ -28,38 +28,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen64 = cgen64 - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Cmp64 = cmp64 gc.Thearch.Defframe = defframe - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Cgenindex = cgenindex - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {} diff --git a/src/cmd/compile/internal/arm/ggen.go b/src/cmd/compile/internal/arm/ggen.go index 15d13ed5a0..24b63b4252 100644 --- a/src/cmd/compile/internal/arm/ggen.go +++ b/src/cmd/compile/internal/arm/ggen.go @@ -111,440 +111,9 @@ func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int3 return q } -/* - * generate high multiply - * res = (nl * nr) >> wordsize - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - t := nl.Type - w := t.Width * 8 - var n1 gc.Node - gc.Regalloc(&n1, t, res) - gc.Cgen(nl, &n1) - var n2 gc.Node - gc.Regalloc(&n2, t, nil) - gc.Cgen(nr, &n2) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16: - gins(optoas(gc.OMUL, t), &n2, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) - - case gc.TUINT8, - gc.TUINT16: - gins(optoas(gc.OMUL, t), &n2, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(w), &n1) - - // perform a long multiplication. - case gc.TINT32, - gc.TUINT32: - var p *obj.Prog - if t.IsSigned() { - p = gins(arm.AMULL, &n2, nil) - } else { - p = gins(arm.AMULLU, &n2, nil) - } - - // n2 * n1 -> (n1 n2) - p.Reg = n1.Reg - - p.To.Type = obj.TYPE_REGREG - p.To.Reg = n1.Reg - p.To.Offset = int64(n2.Reg) - - default: - gc.Fatalf("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Type.Width > 4 { - gc.Fatalf("cgen_shift %v", nl.Type) - } - - w := int(nl.Type.Width * 8) - - if op == gc.OLROT { - v := nr.Int64() - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - if w == 32 { - gc.Cgen(nl, &n1) - gshift(arm.AMOVW, &n1, arm.SHIFT_RR, int32(w)-int32(v), &n1) - } else { - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, nil) - gc.Cgen(nl, &n2) - gshift(arm.AMOVW, &n2, arm.SHIFT_LL, int32(v), &n1) - gshift(arm.AORR, &n2, arm.SHIFT_LR, int32(w)-int32(v), &n1) - gc.Regfree(&n2) - - // Ensure sign/zero-extended result. - gins(optoas(gc.OAS, nl.Type), &n1, &n1) - } - - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc == 0 { - } else // nothing to do - if sc >= uint64(nl.Type.Width*8) { - if op == gc.ORSH && nl.Type.IsSigned() { - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(w), &n1) - } else { - gins(arm.AEOR, &n1, &n1) - } - } else { - if op == gc.ORSH && nl.Type.IsSigned() { - gshift(arm.AMOVW, &n1, arm.SHIFT_AR, int32(sc), &n1) - } else if op == gc.ORSH { - gshift(arm.AMOVW, &n1, arm.SHIFT_LR, int32(sc), &n1) // OLSH - } else { - gshift(arm.AMOVW, &n1, arm.SHIFT_LL, int32(sc), &n1) - } - } - - if w < 32 && op == gc.OLSH { - gins(optoas(gc.OAS, nl.Type), &n1, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - tr := nr.Type - var t gc.Node - var n1 gc.Node - var n2 gc.Node - var n3 gc.Node - if tr.Width > 4 { - var nt gc.Node - gc.Tempname(&nt, nr.Type) - if nl.Ullman >= nr.Ullman { - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - gc.Cgen(nr, &nt) - n1 = nt - } else { - gc.Cgen(nr, &nt) - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - } - - var hi gc.Node - var lo gc.Node - split64(&nt, &lo, &hi) - gc.Regalloc(&n1, gc.Types[gc.TUINT32], nil) - gc.Regalloc(&n3, gc.Types[gc.TUINT32], nil) - gmove(&lo, &n1) - gmove(&hi, &n3) - splitclean() - gins(arm.ATST, &n3, nil) - gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) - p1 := gins(arm.AMOVW, &t, &n1) - p1.Scond = arm.C_SCOND_NE - tr = gc.Types[gc.TUINT32] - gc.Regfree(&n3) - } else { - if nl.Ullman >= nr.Ullman { - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - gc.Regalloc(&n1, nr.Type, nil) - gc.Cgen(nr, &n1) - } else { - gc.Regalloc(&n1, nr.Type, nil) - gc.Cgen(nr, &n1) - gc.Regalloc(&n2, nl.Type, res) - gc.Cgen(nl, &n2) - } - } - - // test for shift being 0 - gins(arm.ATST, &n1, nil) - - p3 := gc.Gbranch(arm.ABEQ, nil, -1) - - // test and fix up large shifts - // TODO: if(!bounded), don't emit some of this. - gc.Regalloc(&n3, tr, nil) - - gc.Nodconst(&t, gc.Types[gc.TUINT32], int64(w)) - gmove(&t, &n3) - gins(arm.ACMP, &n1, &n3) - if op == gc.ORSH { - var p1 *obj.Prog - var p2 *obj.Prog - if nl.Type.IsSigned() { - p1 = gshift(arm.AMOVW, &n2, arm.SHIFT_AR, int32(w)-1, &n2) - p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_AR, &n1, &n2) - } else { - p1 = gins(arm.AEOR, &n2, &n2) - p2 = gregshift(arm.AMOVW, &n2, arm.SHIFT_LR, &n1, &n2) - } - - p1.Scond = arm.C_SCOND_HS - p2.Scond = arm.C_SCOND_LO - } else { - p1 := gins(arm.AEOR, &n2, &n2) - p2 := gregshift(arm.AMOVW, &n2, arm.SHIFT_LL, &n1, &n2) - p1.Scond = arm.C_SCOND_HS - p2.Scond = arm.C_SCOND_LO - } - - gc.Regfree(&n3) - - gc.Patch(p3, gc.Pc) - - // Left-shift of smaller word must be sign/zero-extended. - if w < 32 && op == gc.OLSH { - gins(optoas(gc.OAS, nl.Type), &n2, &n2) - } - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - w := uint32(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 4 // bytes - q := w / 4 // quads - - if nl.Type.Align < 4 { - q = 0 - c = w - } - - var r0 gc.Node - r0.Op = gc.OREGISTER - - r0.Reg = arm.REG_R0 - var r1 gc.Node - r1.Op = gc.OREGISTER - r1.Reg = arm.REG_R1 - var dst gc.Node - gc.Regalloc(&dst, gc.Types[gc.Tptr], &r1) - gc.Agen(nl, &dst) - var nc gc.Node - gc.Nodconst(&nc, gc.Types[gc.TUINT32], 0) - var nz gc.Node - gc.Regalloc(&nz, gc.Types[gc.TUINT32], &r0) - gc.Cgen(&nc, &nz) - - if q > 128 { - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p := gins(arm.AMOVW, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q) * 4 - - p = gins(arm.AMOVW, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 4 - p.Scond |= arm.C_PBIT - pl := p - - p = gins(arm.ACMP, &dst, nil) - raddr(&end, p) - gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl) - - gc.Regfree(&end) - } else if q >= 4 && !gc.Nacl { - f := gc.Sysfunc("duffzero") - p := gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_arm.s - p.To.Offset = 4 * (128 - int64(q)) - } else { - var p *obj.Prog - for q > 0 { - p = gins(arm.AMOVW, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 4 - p.Scond |= arm.C_PBIT - - //print("1. %v\n", p); - q-- - } - } - - if c > 4 { - // Loop to zero unaligned memory. - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p := gins(arm.AMOVW, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(c) - - p = gins(arm.AMOVB, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 1 - p.Scond |= arm.C_PBIT - pl := p - - p = gins(arm.ACMP, &dst, nil) - raddr(&end, p) - gc.Patch(gc.Gbranch(arm.ABNE, nil, 0), pl) - - gc.Regfree(&end) - c = 0 - } - var p *obj.Prog - for c > 0 { - p = gins(arm.AMOVB, &nz, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 1 - p.Scond |= arm.C_PBIT - - //print("2. %v\n", p); - c-- - } - - gc.Regfree(&dst) - gc.Regfree(&nz) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var reg int - var p1 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatalf("invalid nil check %v", p) - } - reg = int(p.From.Reg) - - // check is - // CMP arg, $0 - // MOV.EQ arg, 0(arg) - p1 = gc.Ctxt.NewProg() - - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - p1.As = arm.AMOVW - p1.From.Type = obj.TYPE_REG - p1.From.Reg = int16(reg) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = int16(reg) - p1.To.Offset = 0 - p1.Scond = arm.C_SCOND_EQ - p.As = arm.ACMP - p.From.Type = obj.TYPE_CONST - p.From.Reg = 0 - p.From.Offset = 0 - p.Reg = int16(reg) - } -} - func ginsnop() { var r gc.Node gc.Nodreg(&r, gc.Types[gc.TINT], arm.REG_R0) p := gins(arm.AAND, &r, &r) p.Scond = arm.C_SCOND_EQ } - -/* - * generate - * as $c, n - */ -func ginscon(as obj.As, c int64, n *gc.Node) { - var n1 gc.Node - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - var n2 gc.Node - gc.Regalloc(&n2, gc.Types[gc.TINT32], nil) - gmove(&n1, &n2) - gins(as, &n2, n) - gc.Regfree(&n2) -} - -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() && n1.Op == gc.OLITERAL && n1.Int64() == 0 && n2.Op != gc.OLITERAL { - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - var r1, r2, g1, g2 gc.Node - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - if t.IsInteger() && n2.Op == gc.OLITERAL && n2.Int64() == 0 { - gins(arm.ACMP, &r1, n2) - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - gins(optoas(gc.OCMP, t), &r1, &r2) - gc.Regfree(&g2) - gc.Regfree(&r2) - } - gc.Regfree(&g1) - gc.Regfree(&r1) - return gc.Gbranch(optoas(op, t), nil, likely) -} - -// addr += index*width if possible. -func addindex(index *gc.Node, width int64, addr *gc.Node) bool { - switch width { - case 2: - gshift(arm.AADD, index, arm.SHIFT_LL, 1, addr) - return true - case 4: - gshift(arm.AADD, index, arm.SHIFT_LL, 2, addr) - return true - case 8: - gshift(arm.AADD, index, arm.SHIFT_LL, 3, addr) - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, arm.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/compile/internal/arm/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go index 1a7ebbc0b1..2ef4760013 100644 --- a/src/cmd/compile/internal/arm/gsubr.go +++ b/src/cmd/compile/internal/arm/gsubr.go @@ -42,587 +42,6 @@ var resvd = []int{ arm.REG_R10, // reserved for g } -/* - * return constant i node. - * overwritten by next call, but useful in calls to gins. - */ - -var ncon_n gc.Node - -func ncon(i uint32) *gc.Node { - if ncon_n.Type == nil { - gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) - } - ncon_n.SetInt(int64(i)) - return &ncon_n -} - -var sclean [10]gc.Node - -var nsclean int - -/* - * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. - */ -func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { - if !gc.Is64(n.Type) { - gc.Fatalf("split64 %v", n.Type) - } - - if nsclean >= len(sclean) { - gc.Fatalf("split64 clean") - } - sclean[nsclean].Op = gc.OEMPTY - nsclean++ - switch n.Op { - default: - switch n.Op { - default: - var n1 gc.Node - if !dotaddable(n, &n1) { - gc.Igen(n, &n1, nil) - sclean[nsclean-1] = n1 - } - - n = &n1 - - case gc.ONAME, gc.OINDREG: - // nothing - } - - *lo = *n - *hi = *n - lo.Type = gc.Types[gc.TUINT32] - if n.Type.Etype == gc.TINT64 { - hi.Type = gc.Types[gc.TINT32] - } else { - hi.Type = gc.Types[gc.TUINT32] - } - hi.Xoffset += 4 - - case gc.OLITERAL: - var n1 gc.Node - n.Convconst(&n1, n.Type) - i := n1.Int64() - gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) - i >>= 32 - if n.Type.Etype == gc.TINT64 { - gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) - } else { - gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) - } - } -} - -func splitclean() { - if nsclean <= 0 { - gc.Fatalf("splitclean") - } - nsclean-- - if sclean[nsclean].Op != gc.OEMPTY { - gc.Regfree(&sclean[nsclean]) - } -} - -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", f, t) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands; - // except 64-bit, which always copies via registers anyway. - var a obj.As - var r1 gc.Node - if !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT32]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm.AMOVW, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT32]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm.AMOVW, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = gc.Simsimtype(con.Type) - - // constants can't move directly to memory - if gc.Ismem(t) && !gc.Is64(t.Type) { - goto hard - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - // should not happen - gc.Fatalf("gmove %v -> %v", f, t) - return - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8: // same size - if !gc.Ismem(f) { - a = arm.AMOVB - break - } - fallthrough - - case gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8: - a = arm.AMOVBS - - case gc.TUINT8<<16 | gc.TUINT8: - if !gc.Ismem(f) { - a = arm.AMOVB - break - } - fallthrough - - case gc.TINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8: - a = arm.AMOVBU - - case gc.TINT64<<16 | gc.TINT8, // truncate low word - gc.TUINT64<<16 | gc.TINT8: - a = arm.AMOVBS - - goto trunc64 - - case gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = arm.AMOVBU - goto trunc64 - - case gc.TINT16<<16 | gc.TINT16: // same size - if !gc.Ismem(f) { - a = arm.AMOVH - break - } - fallthrough - - case gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, // truncate - gc.TUINT32<<16 | gc.TINT16: - a = arm.AMOVHS - - case gc.TUINT16<<16 | gc.TUINT16: - if !gc.Ismem(f) { - a = arm.AMOVH - break - } - fallthrough - - case gc.TINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16: - a = arm.AMOVHU - - case gc.TINT64<<16 | gc.TINT16, // truncate low word - gc.TUINT64<<16 | gc.TINT16: - a = arm.AMOVHS - - goto trunc64 - - case gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = arm.AMOVHU - goto trunc64 - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = arm.AMOVW - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Regalloc(&r1, t.Type, nil) - gins(arm.AMOVW, &flo, &r1) - gins(arm.AMOVW, &r1, t) - gc.Regfree(&r1) - splitclean() - return - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - var r1 gc.Node - gc.Regalloc(&r1, flo.Type, nil) - var r2 gc.Node - gc.Regalloc(&r2, fhi.Type, nil) - gins(arm.AMOVW, &flo, &r1) - gins(arm.AMOVW, &fhi, &r2) - gins(arm.AMOVW, &r1, &tlo) - gins(arm.AMOVW, &r2, &thi) - gc.Regfree(&r1) - gc.Regfree(&r2) - splitclean() - splitclean() - return - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = arm.AMOVBS - - goto rdst - - case gc.TINT8<<16 | gc.TINT64, // convert via int32 - gc.TINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = arm.AMOVBU - - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 - gc.TUINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = arm.AMOVHS - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, // convert via int32 - gc.TINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = arm.AMOVHU - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 - gc.TUINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - - var r1 gc.Node - gc.Regalloc(&r1, tlo.Type, nil) - var r2 gc.Node - gc.Regalloc(&r2, thi.Type, nil) - gmove(f, &r1) - p1 := gins(arm.AMOVW, &r1, &r2) - p1.From.Type = obj.TYPE_SHIFT - p1.From.Offset = 2<<5 | 31<<7 | int64(r1.Reg)&15 // r1->31 - p1.From.Reg = 0 - - //print("gmove: %v\n", p1); - gins(arm.AMOVW, &r1, &tlo) - - gins(arm.AMOVW, &r2, &thi) - gc.Regfree(&r1) - gc.Regfree(&r2) - splitclean() - return - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - - gmove(f, &tlo) - var r1 gc.Node - gc.Regalloc(&r1, thi.Type, nil) - gins(arm.AMOVW, ncon(0), &r1) - gins(arm.AMOVW, &r1, &thi) - gc.Regfree(&r1) - splitclean() - return - - // case CASE(TFLOAT64, TUINT64): - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TUINT32, - - // case CASE(TFLOAT32, TUINT64): - - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - fa := arm.AMOVF - - a := arm.AMOVFW - if ft == gc.TFLOAT64 { - fa = arm.AMOVD - a = arm.AMOVDW - } - - ta := arm.AMOVW - switch tt { - case gc.TINT8: - ta = arm.AMOVBS - - case gc.TUINT8: - ta = arm.AMOVBU - - case gc.TINT16: - ta = arm.AMOVHS - - case gc.TUINT16: - ta = arm.AMOVHU - } - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - gins(fa, f, &r1) // load to fpu - p1 := gins(a, &r1, &r1) // convert to w - switch tt { - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - p1.Scond |= arm.C_UBIT - } - - gins(arm.AMOVW, &r1, &r2) // copy to cpu - gins(ta, &r2, t) // store - gc.Regfree(&r1) - gc.Regfree(&r2) - return - - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT64: - fa := arm.AMOVW - - switch ft { - case gc.TINT8: - fa = arm.AMOVBS - - case gc.TUINT8: - fa = arm.AMOVBU - - case gc.TINT16: - fa = arm.AMOVHS - - case gc.TUINT16: - fa = arm.AMOVHU - } - - a := arm.AMOVWF - ta := arm.AMOVF - if tt == gc.TFLOAT64 { - a = arm.AMOVWD - ta = arm.AMOVD - } - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - var r2 gc.Node - gc.Regalloc(&r2, gc.Types[tt], t) - gins(fa, f, &r1) // load to cpu - gins(arm.AMOVW, &r1, &r2) // copy to fpu - p1 := gins(a, &r2, &r2) // convert - switch ft { - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - p1.Scond |= arm.C_UBIT - } - - gins(ta, &r2, t) // store - gc.Regfree(&r1) - gc.Regfree(&r2) - return - - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - gc.Fatalf("gmove UINT64, TFLOAT not implemented") - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = arm.AMOVF - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = arm.AMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) - gins(arm.AMOVF, f, &r1) - gins(arm.AMOVFD, &r1, &r1) - gins(arm.AMOVD, &r1, t) - gc.Regfree(&r1) - return - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], t) - gins(arm.AMOVD, f, &r1) - gins(arm.AMOVDF, &r1, &r1) - gins(arm.AMOVF, &r1, t) - gc.Regfree(&r1) - return - } - - gins(a, f, t) - return - - // TODO(kaib): we almost always require a register dest anyway, this can probably be - // removed. - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // truncate 64 bit integer -trunc64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - gc.Regalloc(&r1, t.Type, nil) - gins(a, &flo, &r1) - gins(a, &r1, t) - gc.Regfree(&r1) - splitclean() - return -} - -func samaddr(f *gc.Node, t *gc.Node) bool { - if f.Op != t.Op { - return false - } - - switch f.Op { - case gc.OREGISTER: - if f.Reg != t.Reg { - break - } - return true - } - - return false -} - /* * generate one instruction: * as f, t @@ -719,507 +138,3 @@ func raddr(n *gc.Node, p *obj.Prog) { p.Reg = a.Reg } } - -/* generate a constant shift - * arm encodes a shift by 32 as 0, thus asking for 0 shift is illegal. - */ -func gshift(as obj.As, lhs *gc.Node, stype int32, sval int32, rhs *gc.Node) *obj.Prog { - if sval <= 0 || sval > 32 { - gc.Fatalf("bad shift value: %d", sval) - } - - sval = sval & 0x1f - - p := gins(as, nil, rhs) - p.From.Type = obj.TYPE_SHIFT - p.From.Offset = int64(stype) | int64(sval)<<7 | int64(lhs.Reg)&15 - return p -} - -/* generate a register shift - */ -func gregshift(as obj.As, lhs *gc.Node, stype int32, reg *gc.Node, rhs *gc.Node) *obj.Prog { - p := gins(as, nil, rhs) - p.From.Type = obj.TYPE_SHIFT - p.From.Offset = int64(stype) | (int64(reg.Reg)&15)<<8 | 1<<4 | int64(lhs.Reg)&15 - return p -} - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OMOD_ = uint32(gc.OMOD) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OPS_ = uint32(gc.OPS) << 16 - OAS_ = uint32(gc.OAS) << 16 - OSQRT_ = uint32(gc.OSQRT) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry %v-%v etype %v simtype %v", op, t, gc.Types[t.Etype], gc.Types[gc.Simtype[t.Etype]]) - - /* case CASE(OADDR, TPTR32): - a = ALEAL; - break; - - case CASE(OADDR, TPTR64): - a = ALEAQ; - break; - */ - // TODO(kaib): make sure the conditional branches work on all edge cases - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = arm.ABEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = arm.ABNE - - case OLT_ | gc.TINT8, - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64, - OLT_ | gc.TFLOAT32, - OLT_ | gc.TFLOAT64: - a = arm.ABLT - - case OLT_ | gc.TUINT8, - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64: - a = arm.ABLO - - case OLE_ | gc.TINT8, - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64, - OLE_ | gc.TFLOAT32, - OLE_ | gc.TFLOAT64: - a = arm.ABLE - - case OLE_ | gc.TUINT8, - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64: - a = arm.ABLS - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64, - OGT_ | gc.TFLOAT32, - OGT_ | gc.TFLOAT64: - a = arm.ABGT - - case OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64: - a = arm.ABHI - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64, - OGE_ | gc.TFLOAT32, - OGE_ | gc.TFLOAT64: - a = arm.ABGE - - case OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64: - a = arm.ABHS - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TUINT8, - OCMP_ | gc.TINT16, - OCMP_ | gc.TUINT16, - OCMP_ | gc.TINT32, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TPTR32: - a = arm.ACMP - - case OCMP_ | gc.TFLOAT32: - a = arm.ACMPF - - case OCMP_ | gc.TFLOAT64: - a = arm.ACMPD - - case OPS_ | gc.TFLOAT32, - OPS_ | gc.TFLOAT64: - a = arm.ABVS - - case OAS_ | gc.TBOOL: - a = arm.AMOVB - - case OAS_ | gc.TINT8: - a = arm.AMOVBS - - case OAS_ | gc.TUINT8: - a = arm.AMOVBU - - case OAS_ | gc.TINT16: - a = arm.AMOVHS - - case OAS_ | gc.TUINT16: - a = arm.AMOVHU - - case OAS_ | gc.TINT32, - OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = arm.AMOVW - - case OAS_ | gc.TFLOAT32: - a = arm.AMOVF - - case OAS_ | gc.TFLOAT64: - a = arm.AMOVD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8, - OADD_ | gc.TINT16, - OADD_ | gc.TUINT16, - OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32: - a = arm.AADD - - case OADD_ | gc.TFLOAT32: - a = arm.AADDF - - case OADD_ | gc.TFLOAT64: - a = arm.AADDD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8, - OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16, - OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32: - a = arm.ASUB - - case OSUB_ | gc.TFLOAT32: - a = arm.ASUBF - - case OSUB_ | gc.TFLOAT64: - a = arm.ASUBD - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8, - OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16, - OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32: - a = arm.ARSB - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8, - OAND_ | gc.TINT16, - OAND_ | gc.TUINT16, - OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32: - a = arm.AAND - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8, - OOR_ | gc.TINT16, - OOR_ | gc.TUINT16, - OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32: - a = arm.AORR - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8, - OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16, - OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32: - a = arm.AEOR - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8, - OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16, - OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32: - a = arm.ASLL - - case ORSH_ | gc.TUINT8, - ORSH_ | gc.TUINT16, - ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32: - a = arm.ASRL - - case ORSH_ | gc.TINT8, - ORSH_ | gc.TINT16, - ORSH_ | gc.TINT32: - a = arm.ASRA - - case OMUL_ | gc.TUINT8, - OMUL_ | gc.TUINT16, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32: - a = arm.AMULU - - case OMUL_ | gc.TINT8, - OMUL_ | gc.TINT16, - OMUL_ | gc.TINT32: - a = arm.AMUL - - case OMUL_ | gc.TFLOAT32: - a = arm.AMULF - - case OMUL_ | gc.TFLOAT64: - a = arm.AMULD - - case ODIV_ | gc.TUINT8, - ODIV_ | gc.TUINT16, - ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32: - a = arm.ADIVU - - case ODIV_ | gc.TINT8, - ODIV_ | gc.TINT16, - ODIV_ | gc.TINT32: - a = arm.ADIV - - case OMOD_ | gc.TUINT8, - OMOD_ | gc.TUINT16, - OMOD_ | gc.TUINT32, - OMOD_ | gc.TPTR32: - a = arm.AMODU - - case OMOD_ | gc.TINT8, - OMOD_ | gc.TINT16, - OMOD_ | gc.TINT32: - a = arm.AMOD - - // case CASE(OEXTEND, TINT16): - // a = ACWD; - // break; - - // case CASE(OEXTEND, TINT32): - // a = ACDQ; - // break; - - // case CASE(OEXTEND, TINT64): - // a = ACQO; - // break; - - case ODIV_ | gc.TFLOAT32: - a = arm.ADIVF - - case ODIV_ | gc.TFLOAT64: - a = arm.ADIVD - - case OSQRT_ | gc.TFLOAT64: - a = arm.ASQRTD - } - - return a -} - -const ( - ODynam = 1 << 0 - OPtrto = 1 << 1 -) - -var clean [20]gc.Node - -var cleani int = 0 - -func sudoclean() { - if clean[cleani-1].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-1]) - } - if clean[cleani-2].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-2]) - } - cleani -= 2 -} - -func dotaddable(n *gc.Node, n1 *gc.Node) bool { - if n.Op != gc.ODOT { - return false - } - - var oary [10]int64 - var nn *gc.Node - o := gc.Dotoffset(n, oary[:], &nn) - if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { - *n1 = *nn - n1.Type = n.Type - n1.Xoffset += oary[0] - return true - } - - return false -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - if n.Type == nil { - return false - } - - *a = obj.Addr{} - - switch n.Op { - case gc.OLITERAL: - if !gc.Isconst(n, gc.CTINT) { - break - } - v := n.Int64() - if v >= 32000 || v <= -32000 { - break - } - switch as { - default: - return false - - case arm.AADD, - arm.ASUB, - arm.AAND, - arm.AORR, - arm.AEOR, - arm.AMOVB, - arm.AMOVBS, - arm.AMOVBU, - arm.AMOVH, - arm.AMOVHS, - arm.AMOVHU, - arm.AMOVW: - break - } - - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - gc.Naddr(a, n) - return true - - case gc.ODOT, - gc.ODOTPTR: - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - var nn *gc.Node - var oary [10]int64 - o := gc.Dotoffset(n, oary[:], &nn) - if nn == nil { - sudoclean() - return false - } - - if nn.Addable && o == 1 && oary[0] >= 0 { - // directly addressable set of DOTs - n1 := *nn - - n1.Type = n.Type - n1.Xoffset += oary[0] - gc.Naddr(a, &n1) - return true - } - - gc.Regalloc(reg, gc.Types[gc.Tptr], nil) - n1 := *reg - n1.Op = gc.OINDREG - if oary[0] >= 0 { - gc.Agen(nn, reg) - n1.Xoffset = oary[0] - } else { - gc.Cgen(nn, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[0] + 1) - } - - for i := 1; i < o; i++ { - if oary[i] >= 0 { - gc.Fatalf("can't happen") - } - gins(arm.AMOVW, &n1, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[i] + 1) - } - - a.Type = obj.TYPE_NONE - a.Name = obj.NAME_NONE - n1.Type = n.Type - gc.Naddr(a, &n1) - return true - - case gc.OINDEX: - return false - } - - return false -} diff --git a/src/cmd/compile/internal/arm/peep.go b/src/cmd/compile/internal/arm/peep.go deleted file mode 100644 index 12678dfebf..0000000000 --- a/src/cmd/compile/internal/arm/peep.go +++ /dev/null @@ -1,1734 +0,0 @@ -// Inferno utils/5c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package arm - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm" - "fmt" -) - -var gactive uint32 - -// UNUSED -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - /* - * elide shift into TYPE_SHIFT operand of subsequent instruction - */ - // if(shiftprop(r)) { - // excise(r); - // t++; - // break; - // } - case arm.ASLL, - arm.ASRL, - arm.ASRA: - break - - case arm.AMOVB, - arm.AMOVH, - arm.AMOVW, - arm.AMOVF, - arm.AMOVD: - if regtyp(&p.From) { - if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) { - if p.Scond == arm.C_SCOND_NONE { - if copyprop(g, r) { - excise(r) - t++ - break - } - - if subprop(r) && copyprop(g, r) { - excise(r) - t++ - break - } - } - } - } - - case arm.AMOVHS, - arm.AMOVHU, - arm.AMOVBS, - arm.AMOVBU: - if p.From.Type == obj.TYPE_REG { - if shortprop(r) { - t++ - } - } - } - } - - /* - if(p->scond == C_SCOND_NONE) - if(regtyp(&p->to)) - if(isdconst(&p->from)) { - constprop(&p->from, &p->to, r->s1); - } - break; - */ - if t != 0 { - goto loop1 - } - - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - /* - * EOR -1,x,y => MVN x,y - */ - case arm.AEOR: - if isdconst(&p.From) && p.From.Offset == -1 { - p.As = arm.AMVN - p.From.Type = obj.TYPE_REG - if p.Reg != 0 { - p.From.Reg = p.Reg - } else { - p.From.Reg = p.To.Reg - } - p.Reg = 0 - } - } - } - - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case arm.AMOVW, - arm.AMOVB, - arm.AMOVBS, - arm.AMOVBU: - if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 { - xtramodes(g, r, &p.From) - } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 { - xtramodes(g, r, &p.To) - } else { - continue - } - } - } - - // case ACMP: - // /* - // * elide CMP $0,x if calculation of x can set condition codes - // */ - // if(isdconst(&p->from) || p->from.offset != 0) - // continue; - // r2 = r->s1; - // if(r2 == nil) - // continue; - // t = r2->prog->as; - // switch(t) { - // default: - // continue; - // case ABEQ: - // case ABNE: - // case ABMI: - // case ABPL: - // break; - // case ABGE: - // t = ABPL; - // break; - // case ABLT: - // t = ABMI; - // break; - // case ABHI: - // t = ABNE; - // break; - // case ABLS: - // t = ABEQ; - // break; - // } - // r1 = r; - // do - // r1 = uniqp(r1); - // while (r1 != nil && r1->prog->as == ANOP); - // if(r1 == nil) - // continue; - // p1 = r1->prog; - // if(p1->to.type != TYPE_REG) - // continue; - // if(p1->to.reg != p->reg) - // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg)) - // continue; - // - // switch(p1->as) { - // default: - // continue; - // case AMOVW: - // if(p1->from.type != TYPE_REG) - // continue; - // case AAND: - // case AEOR: - // case AORR: - // case ABIC: - // case AMVN: - // case ASUB: - // case ARSB: - // case AADD: - // case AADC: - // case ASBC: - // case ARSC: - // break; - // } - // p1->scond |= C_SBIT; - // r2->prog->as = t; - // excise(r); - // continue; - - // predicate(g); - - gc.Flowend(g) -} - -func regtyp(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15) -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - // TODO(rsc): Whatever invalidated the info should have done this call. - proginfo(p) - - if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG { - p.Info.Flags |= gc.RegRead - p.Info.Flags &^= (gc.CanRegRead | gc.RightRead) - p.Reg = p.To.Reg - } - - switch p.As { - case arm.AMULLU, - arm.AMULA, - arm.AMVN: - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - if p.Scond == arm.C_SCOND_NONE { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub1(p, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - v1.Reg, v2.Reg = v2.Reg, v1.Reg - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, false) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %vused+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %vused and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - return true -} - -// UNUSED -/* - * The idea is to remove redundant constants. - * $c1->v1 - * ($c1->v2 s/$c1/v1)* - * set v1 return - * The v1->v2 should be eliminated by copy propagation. - */ -func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) { - if gc.Debug['P'] != 0 { - fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1)) - } - var p *obj.Prog - for ; r != nil; r = r.S1 { - p = r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if gc.Uniqp(r) == nil { - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; return\n") - } - return - } - - if p.As == arm.AMOVW && copyas(&p.From, c1) { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1)) - } - p.From = *v1 - } else if copyu(p, v1, nil) > 1 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1)) - } - return - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - constprop(c1, v1, r.S2) - } - } -} - -/* - * shortprop eliminates redundant zero/sign extensions. - * - * MOVBS x, R - * - * MOVBS R, R' - * - * changed to - * - * MOVBS x, R - * ... - * MOVB R, R' (compiled to mov) - * - * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU. - */ -func shortprop(r *gc.Flow) bool { - p := r.Prog - r1 := findpre(r, &p.From) - if r1 == nil { - return false - } - - p1 := r1.Prog - if p1.As == p.As { - // Two consecutive extensions. - goto gotit - } - - if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 { - // Loaded an immediate. - goto gotit - } - - return false - -gotit: - if gc.Debug['P'] != 0 { - fmt.Printf("shortprop\n%v\n%v", p1, p) - } - switch p.As { - case arm.AMOVBS, - arm.AMOVBU: - p.As = arm.AMOVB - - case arm.AMOVHS, - arm.AMOVHU: - p.As = arm.AMOVH - } - - if gc.Debug['P'] != 0 { - fmt.Printf(" => %v\n", p.As) - } - return true -} - -// UNUSED -/* - * ASLL x,y,w - * .. (not use w, not set x y w) - * AXXX w,a,b (a != w) - * .. (not use w) - * (set w) - * ----------- changed to - * .. - * AXXX (x< 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) { - if gc.Debug['P'] != 0 { - fmt.Printf("\targs modified; FAILURE\n") - } - return false - } - - continue - case 3: /* set, not used */ - { - if gc.Debug['P'] != 0 { - fmt.Printf("\tBOTCH: noref; FAILURE\n") - } - return false - } - } - - break - } - - /* check whether substitution can be done */ - switch p1.As { - default: - if gc.Debug['P'] != 0 { - fmt.Printf("\tnon-dpi; FAILURE\n") - } - return false - - case arm.AAND, - arm.AEOR, - arm.AADD, - arm.AADC, - arm.AORR, - arm.ASUB, - arm.ASBC, - arm.ARSB, - arm.ARSC: - if p1.Reg == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && p1.To.Reg == n) { - if p1.From.Type != obj.TYPE_REG { - if gc.Debug['P'] != 0 { - fmt.Printf("\tcan't swap; FAILURE\n") - } - return false - } - - p1.Reg = p1.From.Reg - p1.From.Reg = n - switch p1.As { - case arm.ASUB: - p1.As = arm.ARSB - - case arm.ARSB: - p1.As = arm.ASUB - - case arm.ASBC: - p1.As = arm.ARSC - - case arm.ARSC: - p1.As = arm.ASBC - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\t=>%v", p1) - } - } - fallthrough - - case arm.ABIC, - arm.ATST, - arm.ACMP, - arm.ACMN: - if p1.Reg == n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tcan't swap; FAILURE\n") - } - return false - } - - if p1.Reg == 0 && p1.To.Reg == n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tshift result used twice; FAILURE\n") - } - return false - } - - // case AMVN: - if p1.From.Type == obj.TYPE_SHIFT { - if gc.Debug['P'] != 0 { - fmt.Printf("\tshift result used in shift; FAILURE\n") - } - return false - } - - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != n { - if gc.Debug['P'] != 0 { - fmt.Printf("\tBOTCH: where is it used?; FAILURE\n") - } - return false - } - } - - /* check whether shift result is used subsequently */ - p2 := p1 - - if p1.To.Reg != n { - var p1 *obj.Prog - for { - r1 = gc.Uniqs(r1) - if r1 == nil { - if gc.Debug['P'] != 0 { - fmt.Printf("\tinconclusive; FAILURE\n") - } - return false - } - - p1 = r1.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("\n%v", p1) - } - switch copyu(p1, &p.To, nil) { - case 0: /* not used or set */ - continue - - case 3: /* set, not used */ - break - - default: /* used */ - if gc.Debug['P'] != 0 { - fmt.Printf("\treused; FAILURE\n") - } - return false - } - - break - } - } - - /* make the substitution */ - p2.From.Reg = 0 - o := p.Reg - if o == 0 { - o = p.To.Reg - } - o &= 15 - - switch p.From.Type { - case obj.TYPE_CONST: - o |= int16(p.From.Offset&0x1f) << 7 - - case obj.TYPE_REG: - o |= 1<<4 | (p.From.Reg&15)<<8 - } - - switch p.As { - case arm.ASLL: - o |= 0 << 5 - - case arm.ASRL: - o |= 1 << 5 - - case arm.ASRA: - o |= 2 << 5 - } - - p2.From = obj.Addr{} - p2.From.Type = obj.TYPE_SHIFT - p2.From.Offset = int64(o) - if gc.Debug['P'] != 0 { - fmt.Printf("\t=>%v\tSUCCEED\n", p2) - } - return true -} - -/* - * findpre returns the last instruction mentioning v - * before r. It must be a set, and there must be - * a unique path from that instruction to r. - */ -func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow { - var r1 *gc.Flow - - for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) { - if gc.Uniqs(r1) != r { - return nil - } - switch copyu(r1.Prog, v, nil) { - case 1, /* used */ - 2: /* read-alter-rewrite */ - return nil - - case 3, /* set */ - 4: /* set and used */ - return r1 - } - } - - return nil -} - -/* - * findinc finds ADD instructions with a constant - * argument which falls within the immed_12 range. - */ -func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow { - var r1 *gc.Flow - var p *obj.Prog - - for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) { - if gc.Uniqp(r1) != r { - return nil - } - switch copyu(r1.Prog, v, nil) { - case 0: /* not touched */ - continue - - case 4: /* set and used */ - p = r1.Prog - - if p.As == arm.AADD { - if isdconst(&p.From) { - if p.From.Offset > -4096 && p.From.Offset < 4096 { - return r1 - } - } - } - fallthrough - - default: - return nil - } - } - - return nil -} - -func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool { - if r == r2 { - return true - } - n := int(0) - var a [3]obj.Addr - if p.Reg != 0 && p.Reg != p.To.Reg { - a[n].Type = obj.TYPE_REG - a[n].Reg = p.Reg - n++ - } - - switch p.From.Type { - case obj.TYPE_SHIFT: - a[n].Type = obj.TYPE_REG - a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf)) - n++ - fallthrough - - case obj.TYPE_REG: - a[n].Type = obj.TYPE_REG - a[n].Reg = p.From.Reg - n++ - } - - if n == 0 { - return true - } - var i int - for ; r != nil && r != r2; r = gc.Uniqs(r) { - p = r.Prog - for i = 0; i < n; i++ { - if copyu(p, &a[i], nil) > 1 { - return false - } - } - } - - return true -} - -func findu1(r *gc.Flow, v *obj.Addr) bool { - for ; r != nil; r = r.S1 { - if r.Active != 0 { - return false - } - r.Active = 1 - switch copyu(r.Prog, v, nil) { - case 1, /* used */ - 2, /* read-alter-rewrite */ - 4: /* set and used */ - return true - - case 3: /* set */ - return false - } - - if r.S2 != nil { - if findu1(r.S2, v) { - return true - } - } - } - - return false -} - -func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool { - for r1 := g.Start; r1 != nil; r1 = r1.Link { - r1.Active = 0 - } - return findu1(r, v) -} - -/* - * xtramodes enables the ARM post increment and - * shift offset addressing modes to transform - * MOVW 0(R3),R1 - * ADD $4,R3,R3 - * into - * MOVW.P 4(R3),R1 - * and - * ADD R0,R1 - * MOVBU 0(R1),R0 - * into - * MOVBU R0<<0(R1),R0 - */ -func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool { - p := r.Prog - v := *a - v.Type = obj.TYPE_REG - r1 := findpre(r, &v) - if r1 != nil { - p1 := r1.Prog - if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg { - switch p1.As { - case arm.AADD: - if p1.Scond&arm.C_SBIT != 0 { - // avoid altering ADD.S/ADC sequences. - break - } - - if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) { - if nochange(gc.Uniqs(r1), r, p1) { - if a != &p.From || v.Reg != p.To.Reg { - if finduse(g, r.S1, &v) { - if p1.Reg == 0 || p1.Reg == v.Reg { - /* pre-indexing */ - p.Scond |= arm.C_WBIT - } else { - return false - } - } - } - - switch p1.From.Type { - /* register offset */ - case obj.TYPE_REG: - if gc.Nacl { - return false - } - *a = obj.Addr{} - a.Type = obj.TYPE_SHIFT - a.Offset = int64(p1.From.Reg) & 15 - - /* scaled register offset */ - case obj.TYPE_SHIFT: - if gc.Nacl { - return false - } - *a = obj.Addr{} - a.Type = obj.TYPE_SHIFT - fallthrough - - /* immediate offset */ - case obj.TYPE_CONST, - obj.TYPE_ADDR: - a.Offset = p1.From.Offset - } - - if p1.Reg != 0 { - a.Reg = p1.Reg - } - excise(r1) - return true - } - } - - case arm.AMOVW: - if p1.From.Type == obj.TYPE_REG { - r2 := findinc(r1, r, &p1.From) - if r2 != nil { - var r3 *gc.Flow - for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) { - } - if r3 == r { - /* post-indexing */ - p1 := r2.Prog - - a.Reg = p1.To.Reg - a.Offset = p1.From.Offset - p.Scond |= arm.C_PBIT - if !finduse(g, r, &r1.Prog.To) { - excise(r1) - } - excise(r2) - return true - } - } - } - } - } - } - - if a != &p.From || a.Reg != p.To.Reg { - r1 := findinc(r, nil, &v) - if r1 != nil { - /* post-indexing */ - p1 := r1.Prog - - a.Offset = p1.From.Offset - p.Scond |= arm.C_PBIT - excise(r1) - return true - } - } - - return false -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", p.As) - return 2 - - case arm.AMOVM: - if v.Type != obj.TYPE_REG { - return 0 - } - if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */ - if s != nil { - if p.From.Offset&(1<type to v->name and enable. - //if(v->type == NAME_AUTO || v->type == NAME_PARAM) { - // if(v->offset == a->offset) - // return 1; - //} - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_ADDR && a.Reg != 0 { - if a.Reg == v.Reg { - return true - } - } else if a.Type == obj.TYPE_MEM { - if a.Reg == v.Reg { - return true - } - } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { - if a.Reg == v.Reg { - return true - } - if a.Offset == int64(v.Reg) { - return true - } - } else if a.Type == obj.TYPE_SHIFT { - if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { - return true - } - if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { - return true - } - } - } - - return false -} - -/* - * compare v to the center - * register in p (p->reg) - */ -func copyau1(p *obj.Prog, v *obj.Addr) bool { - if v.Type == obj.TYPE_REG && v.Reg == 0 { - return false - } - return p.Reg == v.Reg -} - -// copysub substitute s for v in a. -// copysub returns true on failure to substitute. -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau(a, v) { - if a.Type == obj.TYPE_SHIFT { - if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { - a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf - } - if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { - a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8 - } - } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { - if a.Offset == int64(v.Reg) { - a.Offset = int64(s.Reg) - } - if a.Reg == v.Reg { - a.Reg = s.Reg - } - } else { - a.Reg = s.Reg - } - } - return false -} - -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau1(p1, v) { - p1.Reg = s.Reg - } - return false -} - -var predinfo = []struct { - opcode obj.As - notopcode obj.As - scond int - notscond int -}{ - {arm.ABEQ, arm.ABNE, 0x0, 0x1}, - {arm.ABNE, arm.ABEQ, 0x1, 0x0}, - {arm.ABCS, arm.ABCC, 0x2, 0x3}, - {arm.ABHS, arm.ABLO, 0x2, 0x3}, - {arm.ABCC, arm.ABCS, 0x3, 0x2}, - {arm.ABLO, arm.ABHS, 0x3, 0x2}, - {arm.ABMI, arm.ABPL, 0x4, 0x5}, - {arm.ABPL, arm.ABMI, 0x5, 0x4}, - {arm.ABVS, arm.ABVC, 0x6, 0x7}, - {arm.ABVC, arm.ABVS, 0x7, 0x6}, - {arm.ABHI, arm.ABLS, 0x8, 0x9}, - {arm.ABLS, arm.ABHI, 0x9, 0x8}, - {arm.ABGE, arm.ABLT, 0xA, 0xB}, - {arm.ABLT, arm.ABGE, 0xB, 0xA}, - {arm.ABGT, arm.ABLE, 0xC, 0xD}, - {arm.ABLE, arm.ABGT, 0xD, 0xC}, -} - -type Joininfo struct { - start *gc.Flow - last *gc.Flow - end *gc.Flow - len int -} - -const ( - Join = iota - Split - End - Branch - Setcond - Toolong -) - -const ( - Falsecond = iota - Truecond - Delbranch - Keepbranch -) - -func isbranch(p *obj.Prog) bool { - return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE) -} - -func predicable(p *obj.Prog) bool { - switch p.As { - case obj.ANOP, - obj.AXXX, - obj.AGLOBL, - obj.ATEXT, - arm.AWORD: - return false - } - - if isbranch(p) { - return false - } - return true -} - -/* - * Depends on an analysis of the encodings performed by 5l. - * These seem to be all of the opcodes that lead to the "S" bit - * being set in the instruction encodings. - * - * C_SBIT may also have been set explicitly in p->scond. - */ -func modifiescpsr(p *obj.Prog) bool { - switch p.As { - case arm.AMULLU, - arm.AMULA, - arm.AMULU, - arm.ADIVU, - arm.ATEQ, - arm.ACMN, - arm.ATST, - arm.ACMP, - arm.AMUL, - arm.ADIV, - arm.AMOD, - arm.AMODU, - arm.ABL: - return true - } - - if p.Scond&arm.C_SBIT != 0 { - return true - } - return false -} - -/* - * Find the maximal chain of instructions starting with r which could - * be executed conditionally - */ -func joinsplit(r *gc.Flow, j *Joininfo) int { - j.start = r - j.last = r - j.len = 0 - for { - if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) { - j.end = r - return Join - } - - if r.S1 != nil && r.S2 != nil { - j.end = r - return Split - } - - j.last = r - if r.Prog.As != obj.ANOP { - j.len++ - } - if r.S1 == nil && r.S2 == nil { - j.end = r.Link - return End - } - - if r.S2 != nil { - j.end = r.S2 - return Branch - } - - if modifiescpsr(r.Prog) { - j.end = r.S1 - return Setcond - } - - r = r.S1 - if j.len >= 4 { - break - } - } - - j.end = r - return Toolong -} - -func successor(r *gc.Flow) *gc.Flow { - if r.S1 != nil { - return r.S1 - } - return r.S2 -} - -func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) { - if j.len == 0 { - return - } - var pred int - if cond == Truecond { - pred = predinfo[rstart.Prog.As-arm.ABEQ].scond - } else { - pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond - } - - for r := j.start; ; r = successor(r) { - if r.Prog.As == arm.AB { - if r != j.last || branch == Delbranch { - excise(r) - } else { - if cond == Truecond { - r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].opcode - } else { - r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].notopcode - } - } - } else if predicable(r.Prog) { - r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred) - } - if r.S1 != r.Link { - r.S1 = r.Link - r.Link.P1 = r - } - - if r == j.last { - break - } - } -} - -func predicate(g *gc.Graph) { - var t1 int - var t2 int - var j1 Joininfo - var j2 Joininfo - - for r := g.Start; r != nil; r = r.Link { - if isbranch(r.Prog) { - t1 = joinsplit(r.S1, &j1) - t2 = joinsplit(r.S2, &j2) - if j1.last.Link != j2.start { - continue - } - if j1.end == j2.end { - if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) { - applypred(r, &j1, Falsecond, Delbranch) - applypred(r, &j2, Truecond, Delbranch) - excise(r) - continue - } - } - - if t1 == End || t1 == Branch { - applypred(r, &j1, Falsecond, Keepbranch) - excise(r) - continue - } - } - } -} - -func isdconst(a *obj.Addr) bool { - return a.Type == obj.TYPE_CONST -} - -func isfloatreg(a *obj.Addr) bool { - return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15 -} - -func stackaddr(a *obj.Addr) bool { - return regtyp(a) && a.Reg == arm.REGSP -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func excise(r *gc.Flow) { - p := r.Prog - obj.Nopout(p) -} diff --git a/src/cmd/compile/internal/arm/reg.go b/src/cmd/compile/internal/arm/reg.go index 729cab4b8d..53de6943c5 100644 --- a/src/cmd/compile/internal/arm/reg.go +++ b/src/cmd/compile/internal/arm/reg.go @@ -31,59 +31,6 @@ package arm import "cmd/internal/obj/arm" -import "cmd/compile/internal/gc" - -const ( - NREGVAR = 32 -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - return RtoB(arm.REGSP) | RtoB(arm.REGLINK) | RtoB(arm.REGPC) -} - -func doregbits(r int) uint64 { - return 0 -} /* * bit reg @@ -116,21 +63,3 @@ func RtoB(r int) uint64 { return 0 } - -func BtoR(b uint64) int { - // TODO Allow R0 and R1, but be careful with a 0 return - // TODO Allow R9. Only R10 is reserved now (just g, not m). - b &= 0x11fc // excluded R9 and R10 for m and g, but not R12 - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm.REG_R0 -} - -func BtoF(b uint64) int { - b &= 0xfffc0000 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 16 + arm.REG_F0 -} diff --git a/src/cmd/compile/internal/arm64/cgen.go b/src/cmd/compile/internal/arm64/cgen.go deleted file mode 100644 index 87f349814f..0000000000 --- a/src/cmd/compile/internal/arm64/cgen.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package arm64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" -) - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op obj.As - switch align { - default: - gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = arm64.AMOVB - - case 2: - op = arm64.AMOVH - - case 4: - op = arm64.AMOVW - - case 8: - op = arm64.AMOVD - } - - if w%int64(align) != 0 { - gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - if osrc%int64(align) != 0 || odst%int64(align) != 0 { - gc.Fatalf("sgen: unaligned offset src %d or dst %d (align %d)", osrc, odst, align) - } - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - - if osrc < odst && odst < osrc+w { - dir = -dir - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(arm64.AMOVD, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - - // set up end marker - var nend gc.Node - - // move src and dest to the end of block if necessary - if dir < 0 { - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - gins(arm64.AMOVD, &src, &nend) - } - - p := gins(arm64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - p = gins(arm64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - } else { - p := gins(arm64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - p = gins(arm64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - p := gins(arm64.AMOVD, &src, &nend) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - } - } - - // move - // TODO: enable duffcopy for larger copies. - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond = arm64.C_XPRE - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond = arm64.C_XPRE - - p = gcmp(arm64.ACMP, &src, &nend) - - gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - // TODO(austin): Instead of generating ADD $-8,R8; ADD - // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just - // generate the offsets directly and eliminate the - // ADDs. That will produce shorter, more - // pipeline-able code. - var p *obj.Prog - for ; c > 0; c-- { - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - p.Scond = arm64.C_XPRE - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - p.Scond = arm64.C_XPRE - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/compile/internal/arm64/galign.go b/src/cmd/compile/internal/arm64/galign.go index 12f9b0515f..696434ba69 100644 --- a/src/cmd/compile/internal/arm64/galign.go +++ b/src/cmd/compile/internal/arm64/galign.go @@ -29,38 +29,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.AddSetCarry = AddSetCarry - gc.Thearch.RightShiftWithCarry = RightShiftWithCarry - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {} diff --git a/src/cmd/compile/internal/arm64/ggen.go b/src/cmd/compile/internal/arm64/ggen.go index aebd3d023a..48a6a4c82d 100644 --- a/src/cmd/compile/internal/arm64/ggen.go +++ b/src/cmd/compile/internal/arm64/ggen.go @@ -8,7 +8,6 @@ import ( "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/arm64" - "fmt" ) func defframe(ptxt *obj.Prog) { @@ -127,450 +126,3 @@ func ginsnop() { gc.Nodconst(&con, gc.Types[gc.TINT], 0) gins(arm64.AHINT, &con, nil) } - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will generate undefined result. - // Also need to explicitly trap on division on zero, - // the hardware will silently generate undefined result. - // DIVW will leave unpredictable result in higher 32-bit, - // so always use DIVD/DIVDU. - t := nl.Type - - t0 := t - check := false - if t.IsSigned() { - check = true - if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := gins(optoas(gc.OCMP, t), &tr, nil) - p1.Reg = arm64.REGZERO - p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - var p2 *obj.Prog - if check { - var nm1 gc.Node - gc.Nodconst(&nm1, t, -1) - gcmp(optoas(gc.OCMP, t), &tr, &nm1) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), &tl, &tl) - - gmove(&tl, res) - } else { - // a % (-1) is 0. - var nz gc.Node - gc.Nodconst(&nz, t, 0) - - gmove(&nz, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - p1 = gins(a, &tr, &tl) - if op == gc.ODIV { - gc.Regfree(&tr) - gmove(&tl, res) - } else { - // A%B = A-(A/B*B) - var tm gc.Node - gc.Regalloc(&tm, t, nil) - - // patch div to use the 3 register form - // TODO(minux): add gins3? - p1.Reg = p1.To.Reg - - p1.To.Reg = tm.Reg - gins(optoas(gc.OMUL, t), &tr, &tm) - gc.Regfree(&tr) - gins(optoas(gc.OSUB, t), &tm, &tl) - gc.Regfree(&tm) - gmove(&tl, res) - } - - gc.Regfree(&tl) - if check { - gc.Patch(p2, gc.Pc) - } -} - -// RightShiftWithCarry generates a constant unsigned -// right shift with carry. -// -// res = n >> shift // with carry -func RightShiftWithCarry(n *gc.Node, shift uint, res *gc.Node) { - // Extra 1 is for carry bit. - maxshift := uint(n.Type.Width*8 + 1) - if shift == 0 { - gmove(n, res) - } else if shift < maxshift { - // 1. clear rightmost bit of target - var n1 gc.Node - gc.Nodconst(&n1, n.Type, 1) - gins(optoas(gc.ORSH, n.Type), &n1, n) - gins(optoas(gc.OLSH, n.Type), &n1, n) - // 2. add carry flag to target - var n2 gc.Node - gc.Nodconst(&n1, n.Type, 0) - gc.Regalloc(&n2, n.Type, nil) - gins(optoas(gc.OAS, n.Type), &n1, &n2) - gins(arm64.AADC, &n2, n) - // 3. right rotate 1 bit - gc.Nodconst(&n1, n.Type, 1) - gins(arm64.AROR, &n1, n) - - // ARM64 backend doesn't eliminate shifts by 0. It is manually checked here. - if shift > 1 { - var n3 gc.Node - gc.Nodconst(&n3, n.Type, int64(shift-1)) - cgen_shift(gc.ORSH, true, n, &n3, res) - } else { - gmove(n, res) - } - gc.Regfree(&n2) - } else { - gc.Fatalf("RightShiftWithCarry: shift(%v) is bigger than max size(%v)", shift, maxshift) - } -} - -// AddSetCarry generates add and set carry. -// -// res = nl + nr // with carry flag set -func AddSetCarry(nl *gc.Node, nr *gc.Node, res *gc.Node) { - gins(arm64.AADDS, nl, nr) - gmove(nr, res) -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - t := nl.Type - w := t.Width * 8 - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(arm64.AASR, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(arm64.ALSR, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TINT64, - gc.TUINT64: - if t.IsSigned() { - gins(arm64.ASMULH, &n2, &n1) - } else { - gins(arm64.AUMULH, &n2, &n1) - } - - default: - gc.Fatalf("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width)*8 { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gcmp(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) - if op == gc.ORSH && nl.Type.IsSigned() { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - w := uint64(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 8 // bytes - q := w / 8 // dwords - - var r0 gc.Node - gc.Nodreg(&r0, gc.Types[gc.TUINT64], arm64.REGZERO) - var dst gc.Node - - // REGRT1 is reserved on arm64, see arm64/gsubr.go. - gc.Nodreg(&dst, gc.Types[gc.Tptr], arm64.REGRT1) - gc.Agen(nl, &dst) - - var boff uint64 - if q > 128 { - p := gins(arm64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p = gins(arm64.AMOVD, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q * 8) - - p = gins(arm64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 8 - p.Scond = arm64.C_XPRE - pl := p - - p = gcmp(arm64.ACMP, &dst, &end) - gc.Patch(gc.Gbranch(arm64.ABNE, nil, 0), pl) - - gc.Regfree(&end) - - // The loop leaves R16 on the last zeroed dword - boff = 8 - } else if q >= 4 && !darwin { // darwin ld64 cannot handle BR26 reloc with non-zero addend - p := gins(arm64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - f := gc.Sysfunc("duffzero") - p = gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_arm64x.s - p.To.Offset = int64(4 * (128 - q)) - - // duffzero leaves R16 on the last zeroed dword - boff = 8 - } else { - var p *obj.Prog - for t := uint64(0); t < q; t++ { - p = gins(arm64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(8 * t) - } - - boff = 8 * q - } - - var p *obj.Prog - for t := uint64(0); t < c; t++ { - p = gins(arm64.AMOVB, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(t + boff) - } -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatalf("invalid nil check %v\n", p) - } - - // check is - // CBNZ arg, 2(PC) - // MOVD ZR, 0(arg) - p1 = gc.Ctxt.NewProg() - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - - p.As = arm64.ACBNZ - p.To.Type = obj.TYPE_BRANCH - p.To.Val = p1.Link - - // crash by write to memory address 0. - p1.As = arm64.AMOVD - p1.From.Type = obj.TYPE_REG - p1.From.Reg = arm64.REGZERO - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = p.From.Reg - p1.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, arm64.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/compile/internal/arm64/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go index 8bff5788f6..9e73959923 100644 --- a/src/cmd/compile/internal/arm64/gsubr.go +++ b/src/cmd/compile/internal/arm64/gsubr.go @@ -98,371 +98,6 @@ func ginscon2(as obj.As, n2 *gc.Node, c int64) { gc.Regfree(&ntmp) } -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { - // Reverse comparison to place constant last. - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - - var r1, r2, g1, g2 gc.Node - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - if t.IsInteger() && gc.Isconst(n2, gc.CTINT) { - ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64()) - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - gcmp(optoas(gc.OCMP, t), &r1, &r2) - gc.Regfree(&g2) - gc.Regfree(&r2) - } - gc.Regfree(&g1) - gc.Regfree(&r1) - return gc.Gbranch(optoas(op, t), nil, likely) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %L -> %L\n", f, t) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var r1 gc.Node - var a obj.As - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT32, - gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT32, - gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(arm64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = tt // so big switch will choose a simple mov - - // constants can't move directly to memory. - if gc.Ismem(t) { - goto hard - } - } - - // value -> value copy, first operand in memory. - // any floating point operand requires register - // src, so goto hard to copy to register first. - if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) { - cvt = gc.Types[ft] - goto hard - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatalf("gmove %L -> %L", f.Type, t.Type) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = arm64.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, // same size - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - // truncate - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = arm64.AMOVBU - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = arm64.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, // same size - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - // truncate - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = arm64.AMOVHU - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, - // truncate - gc.TUINT64<<16 | gc.TINT32: - a = arm64.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, // same size - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = arm64.AMOVWU - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = arm64.AMOVD - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = arm64.AMOVB - - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = arm64.AMOVBU - - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = arm64.AMOVH - - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = arm64.AMOVHU - - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = arm64.AMOVW - - goto rdst - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = arm64.AMOVWU - - goto rdst - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32: - a = arm64.AFCVTZSSW - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = arm64.AFCVTZSDW - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT64: - a = arm64.AFCVTZSS - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT64: - a = arm64.AFCVTZSD - goto rdst - - case gc.TFLOAT32<<16 | gc.TUINT32: - a = arm64.AFCVTZUSW - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT32: - a = arm64.AFCVTZUDW - goto rdst - - case gc.TFLOAT32<<16 | gc.TUINT64: - a = arm64.AFCVTZUS - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT64: - a = arm64.AFCVTZUD - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TUINT32] - - goto hard - - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT32: - a = arm64.ASCVTFWS - - goto rdst - - case gc.TINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT64: - a = arm64.ASCVTFWD - - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT32: - a = arm64.ASCVTFS - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT64: - a = arm64.ASCVTFD - goto rdst - - case gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT32: - a = arm64.AUCVTFWS - - goto rdst - - case gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT64: - a = arm64.AUCVTFWD - - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT32: - a = arm64.AUCVTFS - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT64: - a = arm64.AUCVTFD - goto rdst - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = arm64.AFMOVS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = arm64.AFMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = arm64.AFCVTSD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = arm64.AFCVTDS - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - // gins is called by the front end. // It synthesizes some multiple-instruction sequences // so the front end can stay simpler. @@ -582,398 +217,3 @@ func gcmp(as obj.As, lhs *gc.Node, rhs *gc.Node) *obj.Prog { raddr(lhs, p) return p } - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - OSQRT_ = uint32(gc.OSQRT) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry for op=%v type=%v", op, t) - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = arm64.ABEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = arm64.ABNE - - case OLT_ | gc.TINT8, - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64: - a = arm64.ABLT - - case OLT_ | gc.TUINT8, - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64, - OLT_ | gc.TFLOAT32, - OLT_ | gc.TFLOAT64: - a = arm64.ABLO - - case OLE_ | gc.TINT8, - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64: - a = arm64.ABLE - - case OLE_ | gc.TUINT8, - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64, - OLE_ | gc.TFLOAT32, - OLE_ | gc.TFLOAT64: - a = arm64.ABLS - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64, - OGT_ | gc.TFLOAT32, - OGT_ | gc.TFLOAT64: - a = arm64.ABGT - - case OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64: - a = arm64.ABHI - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64, - OGE_ | gc.TFLOAT32, - OGE_ | gc.TFLOAT64: - a = arm64.ABGE - - case OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64: - a = arm64.ABHS - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TINT16, - OCMP_ | gc.TINT32, - OCMP_ | gc.TPTR32, - OCMP_ | gc.TINT64, - OCMP_ | gc.TUINT8, - OCMP_ | gc.TUINT16, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TUINT64, - OCMP_ | gc.TPTR64: - a = arm64.ACMP - - case OCMP_ | gc.TFLOAT32: - a = arm64.AFCMPS - - case OCMP_ | gc.TFLOAT64: - a = arm64.AFCMPD - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8: - a = arm64.AMOVB - - case OAS_ | gc.TUINT8: - a = arm64.AMOVBU - - case OAS_ | gc.TINT16: - a = arm64.AMOVH - - case OAS_ | gc.TUINT16: - a = arm64.AMOVHU - - case OAS_ | gc.TINT32: - a = arm64.AMOVW - - case OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = arm64.AMOVWU - - case OAS_ | gc.TINT64, - OAS_ | gc.TUINT64, - OAS_ | gc.TPTR64: - a = arm64.AMOVD - - case OAS_ | gc.TFLOAT32: - a = arm64.AFMOVS - - case OAS_ | gc.TFLOAT64: - a = arm64.AFMOVD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8, - OADD_ | gc.TINT16, - OADD_ | gc.TUINT16, - OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32, - OADD_ | gc.TINT64, - OADD_ | gc.TUINT64, - OADD_ | gc.TPTR64: - a = arm64.AADD - - case OADD_ | gc.TFLOAT32: - a = arm64.AFADDS - - case OADD_ | gc.TFLOAT64: - a = arm64.AFADDD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8, - OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16, - OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32, - OSUB_ | gc.TINT64, - OSUB_ | gc.TUINT64, - OSUB_ | gc.TPTR64: - a = arm64.ASUB - - case OSUB_ | gc.TFLOAT32: - a = arm64.AFSUBS - - case OSUB_ | gc.TFLOAT64: - a = arm64.AFSUBD - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8, - OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16, - OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32, - OMINUS_ | gc.TINT64, - OMINUS_ | gc.TUINT64, - OMINUS_ | gc.TPTR64: - a = arm64.ANEG - - case OMINUS_ | gc.TFLOAT32: - a = arm64.AFNEGS - - case OMINUS_ | gc.TFLOAT64: - a = arm64.AFNEGD - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8, - OAND_ | gc.TINT16, - OAND_ | gc.TUINT16, - OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32, - OAND_ | gc.TINT64, - OAND_ | gc.TUINT64, - OAND_ | gc.TPTR64: - a = arm64.AAND - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8, - OOR_ | gc.TINT16, - OOR_ | gc.TUINT16, - OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32, - OOR_ | gc.TINT64, - OOR_ | gc.TUINT64, - OOR_ | gc.TPTR64: - a = arm64.AORR - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8, - OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16, - OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32, - OXOR_ | gc.TINT64, - OXOR_ | gc.TUINT64, - OXOR_ | gc.TPTR64: - a = arm64.AEOR - - // TODO(minux): handle rotates - //case CASE(OLROT, TINT8): - //case CASE(OLROT, TUINT8): - //case CASE(OLROT, TINT16): - //case CASE(OLROT, TUINT16): - //case CASE(OLROT, TINT32): - //case CASE(OLROT, TUINT32): - //case CASE(OLROT, TPTR32): - //case CASE(OLROT, TINT64): - //case CASE(OLROT, TUINT64): - //case CASE(OLROT, TPTR64): - // a = 0//???; RLDC? - // break; - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8, - OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16, - OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32, - OLSH_ | gc.TINT64, - OLSH_ | gc.TUINT64, - OLSH_ | gc.TPTR64: - a = arm64.ALSL - - case ORSH_ | gc.TUINT8, - ORSH_ | gc.TUINT16, - ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32, - ORSH_ | gc.TUINT64, - ORSH_ | gc.TPTR64: - a = arm64.ALSR - - case ORSH_ | gc.TINT8, - ORSH_ | gc.TINT16, - ORSH_ | gc.TINT32, - ORSH_ | gc.TINT64: - a = arm64.AASR - - case OHMUL_ | gc.TINT64: - a = arm64.ASMULH - - case OHMUL_ | gc.TUINT64, - OHMUL_ | gc.TPTR64: - a = arm64.AUMULH - - case OMUL_ | gc.TINT8, - OMUL_ | gc.TINT16, - OMUL_ | gc.TINT32: - a = arm64.ASMULL - - case OMUL_ | gc.TINT64: - a = arm64.AMUL - - case OMUL_ | gc.TUINT8, - OMUL_ | gc.TUINT16, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32: - // don't use word multiply, the high 32-bit are undefined. - a = arm64.AUMULL - - case OMUL_ | gc.TUINT64, - OMUL_ | gc.TPTR64: - a = arm64.AMUL // for 64-bit multiplies, signedness doesn't matter. - - case OMUL_ | gc.TFLOAT32: - a = arm64.AFMULS - - case OMUL_ | gc.TFLOAT64: - a = arm64.AFMULD - - case ODIV_ | gc.TINT8, - ODIV_ | gc.TINT16, - ODIV_ | gc.TINT32, - ODIV_ | gc.TINT64: - a = arm64.ASDIV - - case ODIV_ | gc.TUINT8, - ODIV_ | gc.TUINT16, - ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - ODIV_ | gc.TUINT64, - ODIV_ | gc.TPTR64: - a = arm64.AUDIV - - case ODIV_ | gc.TFLOAT32: - a = arm64.AFDIVS - - case ODIV_ | gc.TFLOAT64: - a = arm64.AFDIVD - - case OSQRT_ | gc.TFLOAT64: - a = arm64.AFSQRTD - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -func xgen(n *gc.Node, a *gc.Node, o int) bool { - // TODO(minux) - - return -1 != 0 /*TypeKind(100016)*/ -} - -func sudoclean() { - return -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - // TODO(minux) - - *a = obj.Addr{} - return false -} diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go deleted file mode 100644 index 6e0b527208..0000000000 --- a/src/cmd/compile/internal/arm64/peep.go +++ /dev/null @@ -1,797 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package arm64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/arm64" - "fmt" -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - - // TODO(minux) Handle smaller moves. arm and amd64 - // distinguish between moves that *must* sign/zero - // extend and moves that don't care so they - // can eliminate moves that don't care without - // breaking moves that do care. This might let us - // simplify or remove the next peep loop, too. - if p.As == arm64.AMOVD || p.As == arm64.AFMOVD { - if regtyp(&p.To) { - // Try to eliminate reg->reg moves - if regtyp(&p.From) { - if p.From.Type == p.To.Type { - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - } - } - } - - if t != 0 { - goto loop1 - } - - /* - * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) - */ - var p1 *obj.Prog - var r1 *gc.Flow - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case arm64.AMOVH, - arm64.AMOVHU, - arm64.AMOVB, - arm64.AMOVBU, - arm64.AMOVW, - arm64.AMOVWU: - if p.To.Type != obj.TYPE_REG { - continue - } - } - - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != p.As { - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { - continue - } - excise(r1) - } - - if gc.Debug['D'] > 1 { - goto ret /* allow following code improvement to be suppressed */ - } - - // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case arm64.AMOVD: - if p.To.Type != obj.TYPE_REG { - continue - } - if p.From.Type != obj.TYPE_CONST { - continue - } - if p.From.Offset < 0 || 4096 <= p.From.Offset { - continue - } - } - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm. - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG { - continue - } - if gc.Debug['P'] != 0 { - fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, p1.As, p, p1) - } - p1.From.Type = obj.TYPE_CONST - p1.From = p.From - excise(r) - } - - /* TODO(minux): - * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R - * when OP can set condition codes correctly - */ - -ret: - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - // TODO(rsc): Floating point register exclusions? - return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R1 - * ADD b, R1 / no use of R2 - * MOV R1, R2 - * would be converted to - * MOV a, R2 - * ADD b, R2 - * MOV R2, R1 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - * - * r0 (the argument, not the register) is the MOV at the end of the - * above sequences. This returns 1 if it modified any instructions. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub1(p, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - v1.Reg, v2.Reg = v2.Reg, v1.Reg - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail (v1->v2 move must remain) - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success (caller can remove v1->v2 move) - */ -func copyprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - if gc.Debug['P'] != 0 { - fmt.Printf("eliminating self-move: %v\n", r0.Prog) - } - return true - } - - gactive++ - if gc.Debug['P'] != 0 { - fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) - } - return copy1(v1, v2, r0.S1, false) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns 1 if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = true - - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// 1 if v only used -// 2 if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// 3 if v is only set -// 4 if v is set in one address and used in another (so addresses -// can be rewritten independently) -// 0 otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - if p.From3Type() != obj.TYPE_NONE { - // 7g never generates a from3 - fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) - } - if p.RegTo2 != obj.REG_NONE { - // 7g never generates a to2 - fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2))) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", p.As) - return 2 - - case obj.ANOP, /* read p->from, write p->to */ - arm64.ANEG, - arm64.AFNEGD, - arm64.AFNEGS, - arm64.AFSQRTD, - arm64.AFCVTZSD, - arm64.AFCVTZSS, - arm64.AFCVTZSDW, - arm64.AFCVTZSSW, - arm64.AFCVTZUD, - arm64.AFCVTZUS, - arm64.AFCVTZUDW, - arm64.AFCVTZUSW, - arm64.AFCVTSD, - arm64.AFCVTDS, - arm64.ASCVTFD, - arm64.ASCVTFS, - arm64.ASCVTFWD, - arm64.ASCVTFWS, - arm64.AUCVTFD, - arm64.AUCVTFS, - arm64.AUCVTFWD, - arm64.AUCVTFWS, - arm64.AMOVB, - arm64.AMOVBU, - arm64.AMOVH, - arm64.AMOVHU, - arm64.AMOVW, - arm64.AMOVWU, - arm64.AMOVD, - arm64.AFMOVS, - arm64.AFMOVD: - if p.Scond == 0 { - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - // p->to only indirectly uses v - return 1 - } - - return 0 - } - - /* rar p->from, write p->to or read p->from, rar p->to */ - if p.From.Type == obj.TYPE_MEM { - if copyas(&p.From, v) { - // No s!=nil check; need to fail - // anyway in that case - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyas(&p.To, v) { - return 3 - } - } else if p.To.Type == obj.TYPE_MEM { - if copyas(&p.To, v) { - return 2 - } - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - } else { - fmt.Printf("copyu: bad %v\n", p) - } - - return 0 - - case arm64.AADD, /* read p->from, read p->reg, write p->to */ - arm64.AADDS, - arm64.ASUB, - arm64.AADC, - arm64.AAND, - arm64.AORR, - arm64.AEOR, - arm64.AROR, - arm64.AMUL, - arm64.ASMULL, - arm64.AUMULL, - arm64.ASMULH, - arm64.AUMULH, - arm64.ASDIV, - arm64.AUDIV, - arm64.ALSL, - arm64.ALSR, - arm64.AASR, - arm64.AFADDD, - arm64.AFADDS, - arm64.AFSUBD, - arm64.AFSUBS, - arm64.AFMULD, - arm64.AFMULS, - arm64.AFDIVD, - arm64.AFDIVS: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub1(p, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - // Fix up implicit reg (e.g., ADD - // R3,R4 -> ADD R3,R4,R4) so we can - // update reg and to separately. - p.Reg = p.To.Reg - } - - if copyau(&p.From, v) { - return 4 - } - if copyau1(p, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - case arm64.ABEQ, - arm64.ABNE, - arm64.ABGE, - arm64.ABLT, - arm64.ABGT, - arm64.ABLE, - arm64.ABLO, - arm64.ABLS, - arm64.ABHI, - arm64.ABHS: - return 0 - - case obj.ACHECKNIL, /* read p->from */ - arm64.ACMP, /* read p->from, read p->reg */ - arm64.AFCMPD, - arm64.AFCMPS: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub1(p, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - return 0 - - case arm64.AB: /* read p->to */ - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: /* funny */ - if s != nil { - return 0 - } - - // All registers die at this point, so claim - // everything is set (and not used). - return 3 - - case arm64.ABL: /* funny */ - if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - // R31 is zero, used by DUFFZERO, cannot be substituted. - // R16 is ptr to memory, used and set, cannot be substituted. - case obj.ADUFFZERO: - if v.Type == obj.TYPE_REG { - if v.Reg == 31 { - return 1 - } - if v.Reg == 16 { - return 2 - } - } - - return 0 - - // R16, R17 are ptr to src, dst, used and set, cannot be substituted. - // R27 is scratch, set by DUFFCOPY, cannot be substituted. - case obj.ADUFFCOPY: - if v.Type == obj.TYPE_REG { - if v.Reg == 16 || v.Reg == 17 { - return 2 - } - if v.Reg == 27 { - return 3 - } - } - - return 0 - - case arm64.AHINT, - obj.ATEXT, - obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL, - obj.AVARLIVE, - obj.AUSEFIELD: - return 0 - } -} - -// copyas returns true if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - return regtyp(v) && a.Type == v.Type && a.Reg == v.Reg -} - -// copyau returns true if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns true if p->reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg -} - -// copysub replaces v with s in a if f==true or indicates it if could if f==false. -// Returns true on failure to substitute (it always succeeds on arm64). -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau(a, v) { - a.Reg = s.Reg - } - return false -} - -// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false. -// Returns true on failure to substitute (it always succeeds on arm64). -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau1(p1, v) { - p1.Reg = s.Reg - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if regtyp(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP -} diff --git a/src/cmd/compile/internal/arm64/reg.go b/src/cmd/compile/internal/arm64/reg.go index dfbbff5ff1..0df68b6ee2 100644 --- a/src/cmd/compile/internal/arm64/reg.go +++ b/src/cmd/compile/internal/arm64/reg.go @@ -30,105 +30,7 @@ package arm64 -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj/arm64" -) - -const ( - NREGVAR = 64 /* 32 general + 32 floating */ -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".R16", - ".R17", - ".R18", - ".R19", - ".R20", - ".R21", - ".R22", - ".R23", - ".R24", - ".R25", - ".R26", - ".R27", - ".R28", - ".R29", - ".R30", - ".R31", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", - ".F16", - ".F17", - ".F18", - ".F19", - ".F20", - ".F21", - ".F22", - ".F23", - ".F24", - ".F25", - ".F26", - ".F27", - ".F28", - ".F29", - ".F30", - ".F31", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - regbits := RtoB(arm64.REGRT1) | RtoB(arm64.REGRT2) | RtoB(arm64.REGPR) - - // Exclude R26 - R31. - for r := arm64.REGMAX + 1; r <= arm64.REGZERO; r++ { - regbits |= RtoB(r) - } - - // Also exclude floating point registers with fixed constants - regbits |= RtoB(arm64.REG_F27) | RtoB(arm64.REG_F28) | RtoB(arm64.REG_F29) | RtoB(arm64.REG_F30) | RtoB(arm64.REG_F31) - - return regbits -} - -func doregbits(r int) uint64 { - return 0 -} +import "cmd/internal/obj/arm64" /* * track register variables including external registers: @@ -151,19 +53,3 @@ func RtoB(r int) uint64 { } return 0 } - -func BtoR(b uint64) int { - b &= 0xffffffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm64.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 32 - if b == 0 { - return 0 - } - return gc.Bitno(b) + arm64.REG_F0 -} diff --git a/src/cmd/compile/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go index 363079a423..3d8a7c1edd 100644 --- a/src/cmd/compile/internal/gc/cgen.go +++ b/src/cmd/compile/internal/gc/cgen.go @@ -4,2634 +4,7 @@ package gc -import ( - "cmd/internal/obj" - "cmd/internal/obj/ppc64" - "cmd/internal/sys" - "fmt" -) - -// generate: -// res = n; -// simplifies and calls Thearch.Gmove. -// if wb is true, need to emit write barriers. -func Cgen(n, res *Node) { - cgen_wb(n, res, false) -} - -func cgen_wb(n, res *Node, wb bool) { - if Debug['g'] != 0 { - op := "cgen" - if wb { - op = "cgen_wb" - } - Dump("\n"+op+"-n", n) - Dump(op+"-res", res) - } - - if n == nil || n.Type == nil { - return - } - - if res == nil || res.Type == nil { - Fatalf("cgen: res nil") - } - - for n.Op == OCONVNOP { - n = n.Left - } - - switch n.Op { - case OARRAYBYTESTRTMP: - sgen_wb(n.Left, res, n.Type.Width, wb) - return - - case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR: - cgen_slice(n, res, wb) - return - - case OEFACE: - if res.Op != ONAME || !res.Addable || wb { - var n1 Node - Tempname(&n1, n.Type) - Cgen_eface(n, &n1) - cgen_wb(&n1, res, wb) - } else { - Cgen_eface(n, res) - } - return - - case ODOTTYPE: - cgen_dottype(n, res, nil, wb) - return - - case OAPPEND: - cgen_append(n, res) - return - } - - if n.Ullman >= UINF { - if n.Op == OINDREG { - Fatalf("cgen: this is going to miscompile") - } - if res.Ullman >= UINF { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - cgen_wb(&n1, res, wb) - return - } - } - - if Isfat(n.Type) { - if n.Type.Width < 0 { - Fatalf("forgot to compute width for %v", n.Type) - } - sgen_wb(n, res, n.Type.Width, wb) - return - } - - if !res.Addable { - if n.Ullman > res.Ullman { - if Ctxt.Arch.RegSize == 4 && Is64(n.Type) { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - cgen_wb(&n1, res, wb) - return - } - - var n1 Node - Regalloc(&n1, n.Type, res) - Cgen(n, &n1) - if n1.Ullman > res.Ullman { - Dump("n1", &n1) - Dump("res", res) - Fatalf("loop in cgen") - } - - cgen_wb(&n1, res, wb) - Regfree(&n1) - return - } - - if res.Ullman < UINF { - if Complexop(n, res) { - Complexgen(n, res) - return - } - - f := true // gen through register - switch n.Op { - case OLITERAL: - if Smallintconst(n) { - f = false - } - - case OREGISTER: - f = false - } - - if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 && !wb { - a := Thearch.Optoas(OAS, res.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, res, &addr) { - var p1 *obj.Prog - if f { - var n2 Node - Regalloc(&n2, res.Type, nil) - Cgen(n, &n2) - p1 = Thearch.Gins(a, &n2, nil) - Regfree(&n2) - } else { - p1 = Thearch.Gins(a, n, nil) - } - p1.To = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - Thearch.Sudoclean() - return - } - } - } - - if Ctxt.Arch.Family == sys.I386 { - // no registers to speak of - var n1, n2 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Igen(res, &n2, nil) - cgen_wb(&n1, &n2, wb) - Regfree(&n2) - return - } - - var n1 Node - Igen(res, &n1, nil) - cgen_wb(n, &n1, wb) - Regfree(&n1) - return - } - - // update addressability for string, slice - // can't do in walk because n->left->addable - // changes if n->left is an escaping local variable. - switch n.Op { - case OSPTR, OLEN: - if n.Left.Type.IsSlice() || n.Left.Type.IsString() { - n.Addable = n.Left.Addable - } - - case OCAP: - if n.Left.Type.IsSlice() { - n.Addable = n.Left.Addable - } - - case OITAB, OIDATA: - n.Addable = n.Left.Addable - } - - if wb { - if Simtype[res.Type.Etype] != Tptr { - Fatalf("cgen_wb of type %v", res.Type) - } - if n.Ullman >= UINF { - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - n = &n1 - } - cgen_wbptr(n, res) - return - } - - // Write barrier now handled. Code below this line can ignore wb. - - if Ctxt.Arch.Family == sys.ARM { // TODO(rsc): Maybe more often? - // if both are addressable, move - if n.Addable && res.Addable { - if Is64(n.Type) || Is64(res.Type) || n.Op == OREGISTER || res.Op == OREGISTER || n.Type.IsComplex() || res.Type.IsComplex() { - Thearch.Gmove(n, res) - } else { - var n1 Node - Regalloc(&n1, n.Type, nil) - Thearch.Gmove(n, &n1) - Cgen(&n1, res) - Regfree(&n1) - } - - return - } - - // if both are not addressable, use a temporary. - if !n.Addable && !res.Addable { - // could use regalloc here sometimes, - // but have to check for ullman >= UINF. - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Cgen(&n1, res) - return - } - - // if result is not addressable directly but n is, - // compute its address and then store via the address. - if !res.Addable { - var n1 Node - Igen(res, &n1, nil) - Cgen(n, &n1) - Regfree(&n1) - return - } - } - - if Complexop(n, res) { - Complexgen(n, res) - return - } - - if Ctxt.Arch.InFamily(sys.AMD64, sys.I386, sys.S390X) && n.Addable { - Thearch.Gmove(n, res) - return - } - - if Ctxt.Arch.InFamily(sys.ARM64, sys.MIPS64, sys.PPC64) { - // if both are addressable, move - if n.Addable { - if n.Op == OREGISTER || res.Op == OREGISTER { - Thearch.Gmove(n, res) - } else { - var n1 Node - Regalloc(&n1, n.Type, nil) - Thearch.Gmove(n, &n1) - Cgen(&n1, res) - Regfree(&n1) - } - return - } - } - - // if n is sudoaddable generate addr and move - if Ctxt.Arch.Family == sys.ARM && !Is64(n.Type) && !Is64(res.Type) && !n.Type.IsComplex() && !res.Type.IsComplex() { - a := Thearch.Optoas(OAS, n.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, n, &addr) { - if res.Op != OREGISTER { - var n2 Node - Regalloc(&n2, res.Type, nil) - p1 := Thearch.Gins(a, nil, &n2) - p1.From = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - Thearch.Gmove(&n2, res) - Regfree(&n2) - } else { - p1 := Thearch.Gins(a, nil, res) - p1.From = addr - if Debug['g'] != 0 { - fmt.Printf("%v [ignore previous line]\n", p1) - } - } - Thearch.Sudoclean() - return - } - } - - nl := n.Left - nr := n.Right - - if nl != nil && nl.Ullman >= UINF { - if nr != nil && nr.Ullman >= UINF { - var n1 Node - Tempname(&n1, nl.Type) - Cgen(nl, &n1) - n2 := *n - n2.Left = &n1 - Cgen(&n2, res) - return - } - } - - // 64-bit ops are hard on 32-bit machine. - if Ctxt.Arch.RegSize == 4 && (Is64(n.Type) || Is64(res.Type) || n.Left != nil && Is64(n.Left.Type)) { - switch n.Op { - // math goes to cgen64. - case OMINUS, - OCOM, - OADD, - OSUB, - OMUL, - OLROT, - OLSH, - ORSH, - OAND, - OOR, - OXOR: - Thearch.Cgen64(n, res) - return - } - } - - if Thearch.Cgen_float != nil && nl != nil && n.Type.IsFloat() && nl.Type.IsFloat() { - Thearch.Cgen_float(n, res) - return - } - - if !n.Type.IsComplex() && Ctxt.Arch.RegSize == 8 { - a := Thearch.Optoas(OAS, n.Type) - var addr obj.Addr - if Thearch.Sudoaddable(a, n, &addr) { - if res.Op == OREGISTER { - p1 := Thearch.Gins(a, nil, res) - p1.From = addr - } else { - var n2 Node - Regalloc(&n2, n.Type, nil) - p1 := Thearch.Gins(a, nil, &n2) - p1.From = addr - Thearch.Gins(a, &n2, res) - Regfree(&n2) - } - - Thearch.Sudoclean() - return - } - } - - var a obj.As - switch n.Op { - default: - Dump("cgen", n) - Dump("cgen-res", res) - Fatalf("cgen: unknown op %+S", n) - - case OOROR, OANDAND, - OEQ, ONE, - OLT, OLE, - OGE, OGT, - ONOT: - Bvgen(n, res, true) - return - - case OPLUS: - Cgen(nl, res) - return - - // unary - case OCOM: - a := Thearch.Optoas(OXOR, nl.Type) - - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - var n2 Node - Nodconst(&n2, nl.Type, -1) - Thearch.Gins(a, &n2, &n1) - cgen_norm(n, &n1, res) - return - - case OMINUS: - if nl.Type.IsFloat() { - nr = Nodintconst(-1) - nr = convlit(nr, n.Type) - a = Thearch.Optoas(OMUL, nl.Type) - goto sbop - } - - a := Thearch.Optoas(n.Op, nl.Type) - // unary - var n1 Node - Regalloc(&n1, nl.Type, res) - - Cgen(nl, &n1) - if Ctxt.Arch.Family == sys.ARM { - var n2 Node - Nodconst(&n2, nl.Type, 0) - Thearch.Gins(a, &n2, &n1) - } else if Ctxt.Arch.Family == sys.ARM64 { - Thearch.Gins(a, &n1, &n1) - } else { - Thearch.Gins(a, nil, &n1) - } - cgen_norm(n, &n1, res) - return - - case OSQRT: - var n1 Node - Regalloc(&n1, nl.Type, res) - Cgen(n.Left, &n1) - Thearch.Gins(Thearch.Optoas(OSQRT, nl.Type), &n1, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - return - - case OGETG: - Thearch.Getg(res) - return - - // symmetric binary - case OAND, - OOR, - OXOR, - OADD, - OMUL: - if n.Op == OMUL && Thearch.Cgen_bmul != nil && Thearch.Cgen_bmul(n.Op, nl, nr, res) { - break - } - a = Thearch.Optoas(n.Op, nl.Type) - goto sbop - - // asymmetric binary - case OSUB: - a = Thearch.Optoas(n.Op, nl.Type) - goto abop - - case OHMUL: - Thearch.Cgen_hmul(nl, nr, res) - - case OCONV: - if Eqtype(n.Type, nl.Type) || Noconv(n.Type, nl.Type) { - Cgen(nl, res) - return - } - - if Ctxt.Arch.Family == sys.I386 { - var n1 Node - var n2 Node - Tempname(&n2, n.Type) - Mgen(nl, &n1, res) - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - Mfree(&n1) - break - } - - var n1 Node - var n2 Node - if Ctxt.Arch.Family == sys.ARM { - if nl.Addable && !Is64(nl.Type) { - Regalloc(&n1, nl.Type, res) - Thearch.Gmove(nl, &n1) - } else { - if n.Type.Width > int64(Widthptr) || Is64(nl.Type) || nl.Type.IsFloat() { - Tempname(&n1, nl.Type) - } else { - Regalloc(&n1, nl.Type, res) - } - Cgen(nl, &n1) - } - if n.Type.Width > int64(Widthptr) || Is64(n.Type) || n.Type.IsFloat() { - Tempname(&n2, n.Type) - } else { - Regalloc(&n2, n.Type, nil) - } - } else { - if n.Type.Width > nl.Type.Width { - // If loading from memory, do conversion during load, - // so as to avoid use of 8-bit register in, say, int(*byteptr). - switch nl.Op { - case ODOT, ODOTPTR, OINDEX, OIND, ONAME: - Igen(nl, &n1, res) - Regalloc(&n2, n.Type, res) - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - Regfree(&n1) - return - } - } - Regalloc(&n1, nl.Type, res) - Regalloc(&n2, n.Type, &n1) - Cgen(nl, &n1) - } - - // if we do the conversion n1 -> n2 here - // reusing the register, then gmove won't - // have to allocate its own register. - Thearch.Gmove(&n1, &n2) - Thearch.Gmove(&n2, res) - if n2.Op == OREGISTER { - Regfree(&n2) - } - if n1.Op == OREGISTER { - Regfree(&n1) - } - - case ODOT, - ODOTPTR, - OINDEX, - OIND: - var n1 Node - Igen(n, &n1, res) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OITAB: - // interface table is first word of interface value - var n1 Node - Igen(nl, &n1, res) - n1.Type = n.Type - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OIDATA: - // interface data is second word of interface value - var n1 Node - Igen(nl, &n1, res) - n1.Type = n.Type - n1.Xoffset += int64(Widthptr) - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OSPTR: - // pointer is the first word of string or slice. - if Isconst(nl, CTSTR) { - var n1 Node - Regalloc(&n1, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1) - Datastring(nl.Val().U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - var n1 Node - Igen(nl, &n1, res) - n1.Type = n.Type - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OLEN: - if nl.Type.IsMap() || nl.Type.IsChan() { - // map and chan have len in the first int-sized word. - // a zero pointer means zero length - var n1 Node - Regalloc(&n1, Types[Tptr], res) - - Cgen(nl, &n1) - - var n2 Node - Nodconst(&n2, Types[Tptr], 0) - p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) - - n2 = n1 - n2.Op = OINDREG - n2.Type = Types[Simtype[TINT]] - Thearch.Gmove(&n2, &n1) - - Patch(p1, Pc) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - if nl.Type.IsString() || nl.Type.IsSlice() { - // both slice and string have len one pointer into the struct. - // a zero pointer means zero length - var n1 Node - Igen(nl, &n1, res) - - n1.Type = Types[Simtype[TUINT]] - n1.Xoffset += int64(Array_nel) - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - Fatalf("cgen: OLEN: unknown type %L", nl.Type) - - case OCAP: - if nl.Type.IsChan() { - // chan has cap in the second int-sized word. - // a zero pointer means zero length - var n1 Node - Regalloc(&n1, Types[Tptr], res) - - Cgen(nl, &n1) - - var n2 Node - Nodconst(&n2, Types[Tptr], 0) - p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) - - n2 = n1 - n2.Op = OINDREG - n2.Xoffset = int64(Widthint) - n2.Type = Types[Simtype[TINT]] - Thearch.Gmove(&n2, &n1) - - Patch(p1, Pc) - - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - if nl.Type.IsSlice() { - var n1 Node - Igen(nl, &n1, res) - n1.Type = Types[Simtype[TUINT]] - n1.Xoffset += int64(Array_cap) - Thearch.Gmove(&n1, res) - Regfree(&n1) - break - } - - Fatalf("cgen: OCAP: unknown type %L", nl.Type) - - case OADDR: - if n.Bounded { // let race detector avoid nil checks - Disable_checknil++ - } - Agen(nl, res) - if n.Bounded { - Disable_checknil-- - } - - case OCALLMETH: - cgen_callmeth(n, 0) - cgen_callret(n, res) - - case OCALLINTER: - cgen_callinter(n, res, 0) - cgen_callret(n, res) - - case OCALLFUNC: - cgen_call(n, 0) - cgen_callret(n, res) - - case OMOD, ODIV: - if n.Type.IsFloat() || Thearch.Dodiv == nil { - a = Thearch.Optoas(n.Op, nl.Type) - goto abop - } - - if nl.Ullman >= nr.Ullman { - var n1 Node - Regalloc(&n1, nl.Type, res) - Cgen(nl, &n1) - cgen_div(n.Op, &n1, nr, res) - Regfree(&n1) - } else { - var n2 Node - if !Smallintconst(nr) { - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - } else { - n2 = *nr - } - - cgen_div(n.Op, nl, &n2, res) - if n2.Op != OLITERAL { - Regfree(&n2) - } - } - - case OLSH, ORSH, OLROT: - Thearch.Cgen_shift(n.Op, n.Bounded, nl, nr, res) - } - - return - - // put simplest on right - we'll generate into left - // and then adjust it using the computation of right. - // constants and variables have the same ullman - // count, so look for constants specially. - // - // an integer constant we can use as an immediate - // is simpler than a variable - we can use the immediate - // in the adjustment instruction directly - so it goes - // on the right. - // - // other constants, like big integers or floating point - // constants, require a mov into a register, so those - // might as well go on the left, so we can reuse that - // register for the computation. -sbop: // symmetric binary - if nl.Ullman < nr.Ullman || (nl.Ullman == nr.Ullman && (Smallintconst(nl) || (nr.Op == OLITERAL && !Smallintconst(nr)))) { - nl, nr = nr, nl - } - -abop: // asymmetric binary - var n1 Node - var n2 Node - if Ctxt.Arch.Family == sys.I386 { - // no registers, sigh - if Smallintconst(nr) { - var n1 Node - Mgen(nl, &n1, res) - var n2 Node - Regalloc(&n2, nl.Type, &n1) - Thearch.Gmove(&n1, &n2) - Thearch.Gins(a, nr, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - Mfree(&n1) - } else if nl.Ullman >= nr.Ullman { - var nt Node - Tempname(&nt, nl.Type) - Cgen(nl, &nt) - var n2 Node - Mgen(nr, &n2, nil) - var n1 Node - Regalloc(&n1, nl.Type, res) - Thearch.Gmove(&nt, &n1) - Thearch.Gins(a, &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Mfree(&n2) - } else { - var n2 Node - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - Thearch.Gins(a, &n2, &n1) - Regfree(&n2) - Thearch.Gmove(&n1, res) - Regfree(&n1) - } - return - } - - if nl.Ullman >= nr.Ullman { - Regalloc(&n1, nl.Type, res) - Cgen(nl, &n1) - - if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm - n2 = *nr - } else { - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - } - } else { - if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.ARM && Ctxt.Arch.Family != sys.ARM64 && Ctxt.Arch.Family != sys.PPC64 { // TODO(rsc): Check opcode for arm - n2 = *nr - } else { - Regalloc(&n2, nr.Type, res) - Cgen(nr, &n2) - } - - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - } - - Thearch.Gins(a, &n2, &n1) - if n2.Op != OLITERAL { - Regfree(&n2) - } - cgen_norm(n, &n1, res) -} - -var sys_wbptr *Node - -func cgen_wbptr(n, res *Node) { - if Curfn != nil { - if Curfn.Func.Pragma&Nowritebarrier != 0 { - Yyerror("write barrier prohibited") - } - if Curfn.Func.WBLineno == 0 { - Curfn.Func.WBLineno = lineno - } - } - if Debug_wb > 0 { - Warn("write barrier") - } - - var dst, src Node - Igen(res, &dst, nil) - if n.Op == OREGISTER { - src = *n - Regrealloc(&src) - } else { - Cgenr(n, &src, nil) - } - - wbVar := syslook("writeBarrier") - wbEnabled := NodSym(ODOT, wbVar, wbVar.Type.Field(0).Sym) - wbEnabled = typecheck(wbEnabled, Erv) - pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst) - pjmp := Gbranch(obj.AJMP, nil, 0) - Patch(pbr, Pc) - var adst Node - Agenr(&dst, &adst, &dst) - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &adst, nil) - a := &p.To - a.Type = obj.TYPE_MEM - a.Reg = int16(Thearch.REGSP) - a.Offset = Ctxt.FixedFrameSize() - p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p2.To = p.To - p2.To.Offset += int64(Widthptr) - Regfree(&adst) - if sys_wbptr == nil { - sys_wbptr = writebarrierfn("writebarrierptr", Types[Tptr], Types[Tptr]) - } - Ginscall(sys_wbptr, 0) - Patch(pjmp, Pc) - - Regfree(&dst) - Regfree(&src) -} - -func cgen_wbfat(n, res *Node) { - if Curfn != nil { - if Curfn.Func.Pragma&Nowritebarrier != 0 { - Yyerror("write barrier prohibited") - } - if Curfn.Func.WBLineno == 0 { - Curfn.Func.WBLineno = lineno - } - } - if Debug_wb > 0 { - Warn("write barrier") - } - needType := true - funcName := "typedmemmove" - var dst, src Node - if n.Ullman >= res.Ullman { - Agenr(n, &src, nil) - Agenr(res, &dst, nil) - } else { - Agenr(res, &dst, nil) - Agenr(n, &src, nil) - } - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &dst, nil) - a := &p.To - a.Type = obj.TYPE_MEM - a.Reg = int16(Thearch.REGSP) - a.Offset = Ctxt.FixedFrameSize() - if needType { - a.Offset += int64(Widthptr) - } - p2 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p2.To = p.To - p2.To.Offset += int64(Widthptr) - Regfree(&dst) - if needType { - src.Type = Types[Tptr] - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), typename(n.Type), &src) - p3 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, nil) - p3.To = p2.To - p3.To.Offset -= 2 * int64(Widthptr) - } - Regfree(&src) - Ginscall(writebarrierfn(funcName, Types[Tptr], Types[Tptr]), 0) -} - -// cgen_norm moves n1 to res, truncating to expected type if necessary. -// n1 is a register, and cgen_norm frees it. -func cgen_norm(n, n1, res *Node) { - switch Ctxt.Arch.Family { - case sys.AMD64, sys.I386: - // We use sized math, so the result is already truncated. - default: - switch n.Op { - case OADD, OSUB, OMUL, ODIV, OCOM, OMINUS: - // TODO(rsc): What about left shift? - Thearch.Gins(Thearch.Optoas(OAS, n.Type), n1, n1) - } - } - - Thearch.Gmove(n1, res) - Regfree(n1) -} - -func Mgen(n *Node, n1 *Node, rg *Node) { - n1.Op = OEMPTY - - if n.Addable { - *n1 = *n - if n1.Op == OREGISTER || n1.Op == OINDREG { - reg[n.Reg-int16(Thearch.REGMIN)]++ - } - return - } - - Tempname(n1, n.Type) - Cgen(n, n1) - if n.Type.Width <= int64(Widthptr) || n.Type.IsFloat() { - n2 := *n1 - Regalloc(n1, n.Type, rg) - Thearch.Gmove(&n2, n1) - } -} - -func Mfree(n *Node) { - if n.Op == OREGISTER { - Regfree(n) - } -} - -// allocate a register (reusing res if possible) and generate -// a = n -// The caller must call Regfree(a). -func Cgenr(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("cgenr-n", n) - } - - if Isfat(n.Type) { - Fatalf("cgenr on fat node") - } - - if n.Addable { - Regalloc(a, n.Type, res) - Thearch.Gmove(n, a) - return - } - - switch n.Op { - case ONAME, - ODOT, - ODOTPTR, - OINDEX, - OCALLFUNC, - OCALLMETH, - OCALLINTER: - var n1 Node - Igen(n, &n1, res) - Regalloc(a, n.Type, &n1) - Thearch.Gmove(&n1, a) - Regfree(&n1) - - default: - Regalloc(a, n.Type, res) - Cgen(n, a) - } -} - -// allocate a register (reusing res if possible) and generate -// a = &n -// The caller must call Regfree(a). -// The generated code checks that the result is not nil. -func Agenr(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nagenr-n", n) - } - - nl := n.Left - nr := n.Right - - switch n.Op { - case ODOT, ODOTPTR, OCALLFUNC, OCALLMETH, OCALLINTER: - var n1 Node - Igen(n, &n1, res) - Regalloc(a, Types[Tptr], &n1) - Agen(&n1, a) - Regfree(&n1) - - case OIND: - Cgenr(n.Left, a, res) - if !n.Left.NonNil { - Cgen_checknil(a) - } else if Debug_checknil != 0 && n.Lineno > 1 { - Warnl(n.Lineno, "removed nil check") - } - - case OINDEX: - if Ctxt.Arch.Family == sys.ARM { - var p2 *obj.Prog // to be patched to panicindex. - w := uint32(n.Type.Width) - bounded := Debug['B'] != 0 || n.Bounded - var n1 Node - var n3 Node - if nr.Addable { - var tmp Node - if !Isconst(nr, CTINT) { - Tempname(&tmp, Types[TINT32]) - } - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - if !Isconst(nr, CTINT) { - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - } else if nl.Addable { - if !Isconst(nr, CTINT) { - var tmp Node - Tempname(&tmp, Types[TINT32]) - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - } else { - var tmp Node - Tempname(&tmp, Types[TINT32]) - p2 = Thearch.Cgenindex(nr, &tmp, bounded) - nr = &tmp - if !Isconst(nl, CTSTR) { - Agenr(nl, &n3, res) - } - Regalloc(&n1, tmp.Type, nil) - Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) - } - - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) - // w is width - - // constant index - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatalf("constant string constant index") - } - v := uint64(nr.Int64()) - var n2 Node - if nl.Type.IsSlice() || nl.Type.IsString() { - if Debug['B'] == 0 && !n.Bounded { - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_nel) - Nodconst(&n2, Types[TUINT32], int64(v)) - p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &n1, &n2, +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_array) - Thearch.Gmove(&n1, &n3) - } - - Nodconst(&n2, Types[Tptr], int64(v*uint64(w))) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - *a = n3 - break - } - - var n2 Node - Regalloc(&n2, Types[TINT32], &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - var n4 Node - if Debug['B'] == 0 && !n.Bounded { - // check bounds - if Isconst(nl, CTSTR) { - Nodconst(&n4, Types[TUINT32], int64(len(nl.Val().U.(string)))) - } else if nl.Type.IsSlice() || nl.Type.IsString() { - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_nel) - Regalloc(&n4, Types[TUINT32], nil) - Thearch.Gmove(&n1, &n4) - } else { - Nodconst(&n4, Types[TUINT32], nl.Type.NumElem()) - } - p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1) - if n4.Op == OREGISTER { - Regfree(&n4) - } - if p2 != nil { - Patch(p2, Pc) - } - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - if Isconst(nl, CTSTR) { - Regalloc(&n3, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) - Datastring(nl.Val().U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - } else if nl.Type.IsSlice() || nl.Type.IsString() { - n1 = n3 - n1.Op = OINDREG - n1.Type = Types[Tptr] - n1.Xoffset = int64(Array_array) - Thearch.Gmove(&n1, &n3) - } - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) - } else { - // Not a power of 2. Use multiply. - Regalloc(&n4, Types[TUINT32], nil) - Nodconst(&n1, Types[TUINT32], int64(w)) - Thearch.Gmove(&n1, &n4) - Thearch.Gins(Thearch.Optoas(OMUL, Types[TUINT32]), &n4, &n2) - Regfree(&n4) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - *a = n3 - Regfree(&n2) - break - } - if Ctxt.Arch.Family == sys.I386 { - var p2 *obj.Prog // to be patched to panicindex. - w := uint32(n.Type.Width) - bounded := Debug['B'] != 0 || n.Bounded - var n3 Node - var tmp Node - var n1 Node - if nr.Addable { - // Generate &nl first, and move nr into register. - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - if !Isconst(nr, CTINT) { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - } else if nl.Addable { - // Generate nr first, and move &nl into register. - if !Isconst(nr, CTINT) { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - Regalloc(&n1, tmp.Type, nil) - Thearch.Gmove(&tmp, &n1) - } - - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - } else { - p2 = Thearch.Igenindex(nr, &tmp, bounded) - nr = &tmp - if !Isconst(nl, CTSTR) { - Igen(nl, &n3, res) - } - Regalloc(&n1, tmp.Type, nil) - Thearch.Gins(Thearch.Optoas(OAS, tmp.Type), &tmp, &n1) - } - - // For fixed array we really want the pointer in n3. - var n2 Node - if nl.Type.IsArray() { - Regalloc(&n2, Types[Tptr], &n3) - Agen(&n3, &n2) - Regfree(&n3) - n3 = n2 - } - - // &a[0] is in n3 (allocated in res) - // i is in n1 (if not constant) - // len(a) is in nlen (if needed) - // w is width - - // constant index - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatalf("constant string constant index") // front end should handle - } - v := uint64(nr.Int64()) - if nl.Type.IsSlice() || nl.Type.IsString() { - if Debug['B'] == 0 && !n.Bounded { - nlen := n3 - nlen.Type = Types[TUINT32] - nlen.Xoffset += int64(Array_nel) - Nodconst(&n2, Types[TUINT32], int64(v)) - p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - } - - // Load base pointer in n2 = n3. - Regalloc(&n2, Types[Tptr], &n3) - - n3.Type = Types[Tptr] - n3.Xoffset += int64(Array_array) - Thearch.Gmove(&n3, &n2) - Regfree(&n3) - if v*uint64(w) != 0 { - Nodconst(&n1, Types[Tptr], int64(v*uint64(w))) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n1, &n2) - } - *a = n2 - break - } - - // i is in register n1, extend to 32 bits. - t := Types[TUINT32] - - if n1.Type.IsSigned() { - t = Types[TINT32] - } - - Regalloc(&n2, t, &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - if Debug['B'] == 0 && !n.Bounded { - // check bounds - t := Types[TUINT32] - - var nlen Node - if Isconst(nl, CTSTR) { - Nodconst(&nlen, t, int64(len(nl.Val().U.(string)))) - } else if nl.Type.IsSlice() || nl.Type.IsString() { - nlen = n3 - nlen.Type = t - nlen.Xoffset += int64(Array_nel) - } else { - Nodconst(&nlen, t, nl.Type.NumElem()) - } - - p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) - if p2 != nil { - Patch(p2, Pc) - } - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - if Isconst(nl, CTSTR) { - Regalloc(&n3, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3) - Datastring(nl.Val().U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) - goto indexdone1 - } - - // Load base pointer in n3. - Regalloc(&tmp, Types[Tptr], &n3) - - if nl.Type.IsSlice() || nl.Type.IsString() { - n3.Type = Types[Tptr] - n3.Xoffset += int64(Array_array) - Thearch.Gmove(&n3, &tmp) - } - - Regfree(&n3) - n3 = tmp - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, Types[TUINT32]), int64(log2(uint64(w))), &n2) - } else { - // Not a power of 2. Use multiply. - Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT32]), int64(w), &n2) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - - indexdone1: - *a = n3 - Regfree(&n2) - break - } - - freelen := 0 - w := uint64(n.Type.Width) - - // Generate the non-addressable child first. - var n3 Node - var nlen Node - var tmp Node - var n1 Node - if nr.Addable { - goto irad - } - if nl.Addable { - Cgenr(nr, &n1, nil) - if !Isconst(nl, CTSTR) { - if nl.Type.IsArray() { - Agenr(nl, &n3, res) - } else { - Igen(nl, &nlen, res) - freelen = 1 - nlen.Type = Types[Tptr] - nlen.Xoffset += int64(Array_array) - Regalloc(&n3, Types[Tptr], res) - Thearch.Gmove(&nlen, &n3) - nlen.Type = Types[Simtype[TUINT]] - nlen.Xoffset += int64(Array_nel) - int64(Array_array) - } - } - - goto index - } - - Tempname(&tmp, nr.Type) - Cgen(nr, &tmp) - nr = &tmp - - irad: - if !Isconst(nl, CTSTR) { - if nl.Type.IsArray() { - Agenr(nl, &n3, res) - } else { - if !nl.Addable { - if res != nil && res.Op == OREGISTER { // give up res, which we don't need yet. - Regfree(res) - } - - // igen will need an addressable node. - var tmp2 Node - Tempname(&tmp2, nl.Type) - Cgen(nl, &tmp2) - nl = &tmp2 - - if res != nil && res.Op == OREGISTER { // reacquire res - Regrealloc(res) - } - } - - Igen(nl, &nlen, res) - freelen = 1 - nlen.Type = Types[Tptr] - nlen.Xoffset += int64(Array_array) - Regalloc(&n3, Types[Tptr], res) - Thearch.Gmove(&nlen, &n3) - nlen.Type = Types[Simtype[TUINT]] - nlen.Xoffset += int64(Array_nel) - int64(Array_array) - } - } - - if !Isconst(nr, CTINT) { - Cgenr(nr, &n1, nil) - } - - goto index - - // &a is in &n3 (allocated in res) - // i is in &n1 (if not constant) - // len(a) is in nlen (if needed) - // w is width - - // constant index - index: - if Isconst(nr, CTINT) { - if Isconst(nl, CTSTR) { - Fatalf("constant string constant index") // front end should handle - } - v := uint64(nr.Int64()) - if nl.Type.IsSlice() || nl.Type.IsString() { - if Debug['B'] == 0 && !n.Bounded { - p1 := Thearch.Ginscmp(OGT, Types[Simtype[TUINT]], &nlen, Nodintconst(int64(v)), +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - Regfree(&nlen) - } - - if v*w != 0 { - Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), int64(v*w), &n3) - } - *a = n3 - break - } - - // type of the index - t := Types[TUINT64] - - if n1.Type.IsSigned() { - t = Types[TINT64] - } - - var n2 Node - Regalloc(&n2, t, &n1) // i - Thearch.Gmove(&n1, &n2) - Regfree(&n1) - - if Debug['B'] == 0 && !n.Bounded { - // check bounds - t = Types[Simtype[TUINT]] - - if Is64(nr.Type) { - t = Types[TUINT64] - } - if Isconst(nl, CTSTR) { - Nodconst(&nlen, t, int64(len(nl.Val().U.(string)))) - } else if nl.Type.IsSlice() || nl.Type.IsString() { - // nlen already initialized - } else { - Nodconst(&nlen, t, nl.Type.NumElem()) - } - - p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) - Ginscall(Panicindex, -1) - Patch(p1, Pc) - } - - if Isconst(nl, CTSTR) { - Regalloc(&n3, Types[Tptr], res) - p1 := Thearch.Gins(Thearch.Optoas(OAS, n3.Type), nil, &n3) // XXX was LEAQ! - Datastring(nl.Val().U.(string), &p1.From) - p1.From.Type = obj.TYPE_ADDR - Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3) - goto indexdone - } - - if w == 0 { - // nothing to do - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&n2, int64(w), &n3) { - // done by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } else { - if w&(w-1) == 0 { - // Power of 2. Use shift. - Thearch.Ginscon(Thearch.Optoas(OLSH, t), int64(log2(w)), &n2) - } else { - // Not a power of 2. Use multiply. - Thearch.Ginscon(Thearch.Optoas(OMUL, t), int64(w), &n2) - } - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n3) - } - - indexdone: - *a = n3 - Regfree(&n2) - if freelen != 0 { - Regfree(&nlen) - } - - default: - Regalloc(a, Types[Tptr], res) - Agen(n, a) - } -} - -// log2 returns the logarithm base 2 of n. n must be a power of 2. -func log2(n uint64) int { - x := 0 - for n>>uint(x) != 1 { - x++ - } - return x -} - -// generate: -// res = &n; -// The generated code checks that the result is not nil. -func Agen(n *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nagen-res", res) - Dump("agen-r", n) - } - - if n == nil || n.Type == nil { - return - } - - for n.Op == OCONVNOP { - n = n.Left - } - - if Isconst(n, CTNIL) && n.Type.Width > int64(Widthptr) { - // Use of a nil interface or nil slice. - // Create a temporary we can take the address of and read. - // The generated code is just going to panic, so it need not - // be terribly efficient. See issue 3670. - var n1 Node - Tempname(&n1, n.Type) - - Gvardef(&n1) - Thearch.Clearfat(&n1) - var n2 Node - Regalloc(&n2, Types[Tptr], res) - var n3 Node - n3.Op = OADDR - n3.Left = &n1 - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n3, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - return - } - - if n.Op == OINDREG && n.Xoffset == 0 { - // Generate MOVW R0, R1 instead of MOVW $0(R0), R1. - // This allows better move propagation in the back ends - // (and maybe it helps the processor). - n1 := *n - n1.Op = OREGISTER - n1.Type = res.Type - Thearch.Gmove(&n1, res) - return - } - - if n.Addable { - if n.Op == OREGISTER { - Fatalf("agen OREGISTER") - } - var n1 Node - n1.Op = OADDR - n1.Left = n - var n2 Node - Regalloc(&n2, Types[Tptr], res) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &n1, &n2) - Thearch.Gmove(&n2, res) - Regfree(&n2) - return - } - - nl := n.Left - - switch n.Op { - default: - Dump("bad agen", n) - Fatalf("agen: unknown op %+S", n) - - case OCALLMETH: - cgen_callmeth(n, 0) - cgen_aret(n, res) - - case OCALLINTER: - cgen_callinter(n, res, 0) - cgen_aret(n, res) - - case OCALLFUNC: - cgen_call(n, 0) - cgen_aret(n, res) - - case OEFACE, ODOTTYPE, OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR, OARRAYBYTESTRTMP: - var n1 Node - Tempname(&n1, n.Type) - Cgen(n, &n1) - Agen(&n1, res) - - case OINDEX: - var n1 Node - Agenr(n, &n1, res) - Thearch.Gmove(&n1, res) - Regfree(&n1) - - case OIND: - Cgen(nl, res) - if !nl.NonNil { - Cgen_checknil(res) - } else if Debug_checknil != 0 && n.Lineno > 1 { - Warnl(n.Lineno, "removed nil check") - } - - case ODOT: - Agen(nl, res) - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - - case ODOTPTR: - Cgen(nl, res) - if !nl.NonNil { - Cgen_checknil(res) - } else if Debug_checknil != 0 && n.Lineno > 1 { - Warnl(n.Lineno, "removed nil check") - } - if n.Xoffset != 0 { - addOffset(res, n.Xoffset) - } - } -} - -func addOffset(res *Node, offset int64) { - if Ctxt.Arch.InFamily(sys.AMD64, sys.I386) { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), Nodintconst(offset), res) - return - } - - var n1, n2 Node - Regalloc(&n1, Types[Tptr], nil) - Thearch.Gmove(res, &n1) - Regalloc(&n2, Types[Tptr], nil) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), Nodintconst(offset), &n2) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Regfree(&n2) -} - -// Igen computes the address &n, stores it in a register r, -// and rewrites a to refer to *r. The chosen r may be the -// stack pointer, it may be borrowed from res, or it may -// be a newly allocated register. The caller must call Regfree(a) -// to free r when the address is no longer needed. -// The generated code ensures that &n is not nil. -func Igen(n *Node, a *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\nigen-n", n) - } - - switch n.Op { - case ONAME: - if n.Class == PAUTOHEAP { - Dump("igen", n) - Fatalf("bad name") - } - *a = *n - return - - case OINDREG: - // Increase the refcount of the register so that igen's caller - // has to call Regfree. - if n.Reg != int16(Thearch.REGSP) { - reg[n.Reg-int16(Thearch.REGMIN)]++ - } - *a = *n - return - - case ODOT: - Igen(n.Left, a, res) - a.Xoffset += n.Xoffset - a.Type = n.Type - Fixlargeoffset(a) - return - - case ODOTPTR: - Cgenr(n.Left, a, res) - if !n.Left.NonNil { - Cgen_checknil(a) - } else if Debug_checknil != 0 && n.Lineno > 1 { - Warnl(n.Lineno, "removed nil check") - } - a.Op = OINDREG - a.Xoffset += n.Xoffset - a.Type = n.Type - Fixlargeoffset(a) - return - - case OCALLFUNC, OCALLMETH, OCALLINTER: - switch n.Op { - case OCALLFUNC: - cgen_call(n, 0) - - case OCALLMETH: - cgen_callmeth(n, 0) - - case OCALLINTER: - cgen_callinter(n, nil, 0) - } - - fp := n.Left.Type.Results().Field(0) - *a = Node{} - a.Op = OINDREG - a.Reg = int16(Thearch.REGSP) - a.Addable = true - a.Xoffset = fp.Offset + Ctxt.FixedFrameSize() - a.Type = n.Type - return - - case OINDEX: - // Index of fixed-size array by constant can - // put the offset in the addressing. - // Could do the same for slice except that we need - // to use the real index for the bounds checking. - if n.Left.Type.IsArray() || (n.Left.Type.IsPtr() && n.Left.Left.Type.IsArray()) { - if Isconst(n.Right, CTINT) { - // Compute &a. - if !n.Left.Type.IsPtr() { - Igen(n.Left, a, res) - } else { - var n1 Node - Igen(n.Left, &n1, res) - Cgen_checknil(&n1) - Regalloc(a, Types[Tptr], res) - Thearch.Gmove(&n1, a) - Regfree(&n1) - a.Op = OINDREG - } - - // Compute &a[i] as &a + i*width. - a.Type = n.Type - - a.Xoffset += n.Right.Int64() * n.Type.Width - Fixlargeoffset(a) - return - } - } - } - - Agenr(n, a, res) - a.Op = OINDREG - a.Type = n.Type -} - -// Bgen generates code for branches: -// -// if n == wantTrue { -// goto to -// } -func Bgen(n *Node, wantTrue bool, likely int, to *obj.Prog) { - bgenx(n, nil, wantTrue, likely, to) -} - -// Bvgen generates code for calculating boolean values: -// res = n == wantTrue -func Bvgen(n, res *Node, wantTrue bool) { - if Thearch.Ginsboolval == nil { - // Direct value generation not implemented for this architecture. - // Implement using jumps. - bvgenjump(n, res, wantTrue, true) - return - } - bgenx(n, res, wantTrue, 0, nil) -} - -// bvgenjump implements boolean value generation using jumps: -// if n == wantTrue { -// res = 1 -// } else { -// res = 0 -// } -// geninit controls whether n's Ninit is generated. -func bvgenjump(n, res *Node, wantTrue, geninit bool) { - init := n.Ninit - if !geninit { - n.Ninit.Set(nil) - } - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Pc - Thearch.Gmove(Nodbool(true), res) - p3 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n, wantTrue, 0, p2) - Thearch.Gmove(Nodbool(false), res) - Patch(p3, Pc) - n.Ninit.MoveNodes(&init) -} - -// bgenx is the backend for Bgen and Bvgen. -// If res is nil, it generates a branch. -// Otherwise, it generates a boolean value. -func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { - if Debug['g'] != 0 { - fmt.Printf("\nbgenx wantTrue=%t likely=%d to=%v\n", wantTrue, likely, to) - Dump("n", n) - Dump("res", res) - } - - genval := res != nil - - if n == nil { - n = Nodbool(true) - } - - Genlist(n.Ninit) - - if n.Type == nil { - n = convlit(n, Types[TBOOL]) - if n.Type == nil { - return - } - } - - if !n.Type.IsBoolean() { - Fatalf("bgen: bad type %v for %v", n.Type, n.Op) - } - - for n.Op == OCONVNOP { - n = n.Left - Genlist(n.Ninit) - } - - if Thearch.Bgen_float != nil && n.Left != nil && n.Left.Type.IsFloat() { - if genval { - bvgenjump(n, res, wantTrue, false) - return - } - Thearch.Bgen_float(n, wantTrue, likely, to) - return - } - - switch n.Op { - default: - if genval { - Cgen(n, res) - if !wantTrue { - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) - } - return - } - - var tmp Node - Regalloc(&tmp, n.Type, nil) - Cgen(n, &tmp) - bgenNonZero(&tmp, nil, wantTrue, likely, to) - Regfree(&tmp) - return - - case ONAME: - // Some architectures might need a temporary or other help here, - // but they don't support direct generation of a bool value yet. - // We can fix that as we go. - mayNeedTemp := Ctxt.Arch.InFamily(sys.ARM, sys.ARM64, sys.MIPS64, sys.PPC64, sys.S390X) - - if genval { - if mayNeedTemp { - Fatalf("genval ONAMES not fully implemented") - } - Cgen(n, res) - if !wantTrue { - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) - } - return - } - - if n.Addable && !mayNeedTemp { - // no need for a temporary - bgenNonZero(n, nil, wantTrue, likely, to) - return - } - var tmp Node - Regalloc(&tmp, n.Type, nil) - Cgen(n, &tmp) - bgenNonZero(&tmp, nil, wantTrue, likely, to) - Regfree(&tmp) - return - - case OLITERAL: - // n is a constant. - if !Isconst(n, CTBOOL) { - Fatalf("bgen: non-bool const %L\n", n) - } - if genval { - Cgen(Nodbool(wantTrue == n.Val().U.(bool)), res) - return - } - // If n == wantTrue, jump; otherwise do nothing. - if wantTrue == n.Val().U.(bool) { - Patch(Gbranch(obj.AJMP, nil, likely), to) - } - return - - case OANDAND, OOROR: - and := (n.Op == OANDAND) == wantTrue - if genval { - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p2, Pc) - Cgen(Nodbool(!and), res) - p3 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n.Left, wantTrue != and, 0, p2) - Bvgen(n.Right, res, wantTrue) - Patch(p3, Pc) - return - } - - if and { - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - Bgen(n.Left, !wantTrue, -likely, p2) - Bgen(n.Right, !wantTrue, -likely, p2) - p1 = Gbranch(obj.AJMP, nil, 0) - Patch(p1, to) - Patch(p2, Pc) - } else { - Bgen(n.Left, wantTrue, likely, to) - Bgen(n.Right, wantTrue, likely, to) - } - return - - case ONOT: // unary - if n.Left == nil || n.Left.Type == nil { - return - } - bgenx(n.Left, res, !wantTrue, likely, to) - return - - case OEQ, ONE, OLT, OGT, OLE, OGE: - if n.Left == nil || n.Left.Type == nil || n.Right == nil || n.Right.Type == nil { - return - } - } - - // n.Op is one of OEQ, ONE, OLT, OGT, OLE, OGE - nl := n.Left - nr := n.Right - op := n.Op - - if !wantTrue { - if nr.Type.IsFloat() { - // Brcom is not valid on floats when NaN is involved. - ll := n.Ninit // avoid re-genning Ninit - n.Ninit.Set(nil) - if genval { - bgenx(n, res, true, likely, to) - Thearch.Gins(Thearch.Optoas(OXOR, Types[TUINT8]), Nodintconst(1), res) // res = !res - n.Ninit.Set(ll.Slice()) - return - } - p1 := Gbranch(obj.AJMP, nil, 0) - p2 := Gbranch(obj.AJMP, nil, 0) - Patch(p1, Pc) - bgenx(n, res, true, -likely, p2) - Patch(Gbranch(obj.AJMP, nil, 0), to) - Patch(p2, Pc) - n.Ninit.Set(ll.Slice()) - return - } - - op = Brcom(op) - } - wantTrue = true - - // make simplest on right - if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) { - op = Brrev(op) - nl, nr = nr, nl - } - - if nl.Type.IsSlice() || nl.Type.IsInterface() { - // front end should only leave cmp to literal nil - if (op != OEQ && op != ONE) || nr.Op != OLITERAL { - if nl.Type.IsSlice() { - Yyerror("illegal slice comparison") - } else { - Yyerror("illegal interface comparison") - } - return - } - - var ptr Node - Igen(nl, &ptr, nil) - if nl.Type.IsSlice() { - ptr.Xoffset += int64(Array_array) - } - ptr.Type = Types[Tptr] - var tmp Node - Regalloc(&tmp, ptr.Type, &ptr) - Cgen(&ptr, &tmp) - Regfree(&ptr) - bgenNonZero(&tmp, res, op == OEQ != wantTrue, likely, to) - Regfree(&tmp) - return - } - - if nl.Type.IsComplex() { - complexbool(op, nl, nr, res, wantTrue, likely, to) - return - } - - if Ctxt.Arch.RegSize == 4 && Is64(nr.Type) { - if genval { - // TODO: Teach Cmp64 to generate boolean values and remove this. - bvgenjump(n, res, wantTrue, false) - return - } - if !nl.Addable || Isconst(nl, CTINT) { - nl = CgenTemp(nl) - } - if !nr.Addable { - nr = CgenTemp(nr) - } - Thearch.Cmp64(nl, nr, op, likely, to) - return - } - - if nr.Ullman >= UINF { - var n1 Node - Regalloc(&n1, nl.Type, nil) - Cgen(nl, &n1) - nl = &n1 - - var tmp Node - Tempname(&tmp, nl.Type) - Thearch.Gmove(&n1, &tmp) - Regfree(&n1) - - var n2 Node - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - nr = &n2 - - Regalloc(&n1, nl.Type, nil) - Cgen(&tmp, &n1) - Regfree(&n1) - Regfree(&n2) - } else { - var n1 Node - if !nl.Addable && Ctxt.Arch.Family == sys.I386 { - Tempname(&n1, nl.Type) - } else { - Regalloc(&n1, nl.Type, nil) - defer Regfree(&n1) - } - Cgen(nl, &n1) - nl = &n1 - - if Smallintconst(nr) && Ctxt.Arch.Family != sys.MIPS64 && Ctxt.Arch.Family != sys.PPC64 { - Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr) - bins(nr.Type, res, op, likely, to) - return - } - - if !nr.Addable && Ctxt.Arch.Family == sys.I386 { - nr = CgenTemp(nr) - } - - var n2 Node - Regalloc(&n2, nr.Type, nil) - Cgen(nr, &n2) - nr = &n2 - Regfree(&n2) - } - - l, r := nl, nr - - // On x86, only < and <= work right with NaN; reverse if needed - if Ctxt.Arch.Family == sys.AMD64 && nl.Type.IsFloat() && (op == OGT || op == OGE) { - l, r = r, l - op = Brrev(op) - } - - // MIPS does not have CMP instruction - if Ctxt.Arch.Family == sys.MIPS64 { - p := Thearch.Ginscmp(op, nr.Type, l, r, likely) - Patch(p, to) - return - } - - // Do the comparison. - Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), l, r) - - // Handle floating point special cases. - // Note that 8g has Bgen_float and is handled above. - if nl.Type.IsFloat() { - switch Ctxt.Arch.Family { - case sys.ARM: - if genval { - Fatalf("genval 5g Isfloat special cases not implemented") - } - switch n.Op { - case ONE: - Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to) - Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to) - default: - p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely) - Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to) - Patch(p, Pc) - } - return - case sys.AMD64: - switch n.Op { - case OEQ: - // neither NE nor P - if genval { - var reg Node - Regalloc(®, Types[TBOOL], nil) - Thearch.Ginsboolval(Thearch.Optoas(OEQ, nr.Type), ®) - Thearch.Ginsboolval(Thearch.Optoas(OPC, nr.Type), res) - Thearch.Gins(Thearch.Optoas(OAND, Types[TBOOL]), ®, res) - Regfree(®) - } else { - p1 := Gbranch(Thearch.Optoas(ONE, nr.Type), nil, -likely) - p2 := Gbranch(Thearch.Optoas(OPS, nr.Type), nil, -likely) - Patch(Gbranch(obj.AJMP, nil, 0), to) - Patch(p1, Pc) - Patch(p2, Pc) - } - return - case ONE: - // either NE or P - if genval { - var reg Node - Regalloc(®, Types[TBOOL], nil) - Thearch.Ginsboolval(Thearch.Optoas(ONE, nr.Type), ®) - Thearch.Ginsboolval(Thearch.Optoas(OPS, nr.Type), res) - Thearch.Gins(Thearch.Optoas(OOR, Types[TBOOL]), ®, res) - Regfree(®) - } else { - Patch(Gbranch(Thearch.Optoas(ONE, nr.Type), nil, likely), to) - Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nil, likely), to) - } - return - } - case sys.ARM64, sys.PPC64: - if genval { - Fatalf("genval 7g, 9g Isfloat special cases not implemented") - } - switch n.Op { - // On arm64 and ppc64, <= and >= mishandle NaN. Must decompose into < or > and =. - // TODO(josh): Convert a <= b to b > a instead? - case OLE, OGE: - if op == OLE { - op = OLT - } else { - op = OGT - } - Patch(Gbranch(Thearch.Optoas(op, nr.Type), nr.Type, likely), to) - Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to) - return - } - } - } - - // Not a special case. Insert the conditional jump or value gen. - bins(nr.Type, res, op, likely, to) -} - -func bgenNonZero(n, res *Node, wantTrue bool, likely int, to *obj.Prog) { - // TODO: Optimize on systems that can compare to zero easily. - var op Op = ONE - if !wantTrue { - op = OEQ - } - - // MIPS does not have CMP instruction - if Thearch.LinkArch.Family == sys.MIPS64 { - p := Gbranch(Thearch.Optoas(op, n.Type), n.Type, likely) - Naddr(&p.From, n) - Patch(p, to) - return - } - - var zero Node - Nodconst(&zero, n.Type, 0) - Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &zero) - bins(n.Type, res, op, likely, to) -} - -// bins inserts an instruction to handle the result of a compare. -// If res is non-nil, it inserts appropriate value generation instructions. -// If res is nil, it inserts a branch to to. -func bins(typ *Type, res *Node, op Op, likely int, to *obj.Prog) { - a := Thearch.Optoas(op, typ) - if res != nil { - // value gen - Thearch.Ginsboolval(a, res) - } else { - // jump - Patch(Gbranch(a, typ, likely), to) - } -} - -// stkof returns n's offset from SP if n is on the stack -// (either a local variable or the return value from a function call -// or the arguments to a function call). -// If n is not on the stack, stkof returns -1000. -// If n is on the stack but in an unknown location -// (due to array index arithmetic), stkof returns +1000. -// -// NOTE(rsc): It is possible that the ODOT and OINDEX cases -// are not relevant here, since it shouldn't be possible for them -// to be involved in an overlapping copy. Only function results -// from one call and the arguments to the next can overlap in -// any non-trivial way. If they can be dropped, then this function -// becomes much simpler and also more trustworthy. -// The fact that it works at all today is probably due to the fact -// that ODOT and OINDEX are irrelevant. -func stkof(n *Node) int64 { - switch n.Op { - case OINDREG: - if n.Reg != int16(Thearch.REGSP) { - return -1000 // not on stack - } - return n.Xoffset - - case ODOT: - t := n.Left.Type - if t.IsPtr() { - break - } - off := stkof(n.Left) - if off == -1000 || off == +1000 { - return off - } - return off + n.Xoffset - - case OINDEX: - t := n.Left.Type - if !t.IsArray() { - break - } - off := stkof(n.Left) - if off == -1000 || off == +1000 { - return off - } - if Isconst(n.Right, CTINT) { - return off + t.Elem().Width*n.Right.Int64() - } - return +1000 // on stack but not sure exactly where - - case OCALLMETH, OCALLINTER, OCALLFUNC: - t := n.Left.Type - if t.IsPtr() { - t = t.Elem() - } - - f := t.Results().Field(0) - if f != nil { - return f.Offset + Ctxt.FixedFrameSize() - } - } - - // botch - probably failing to recognize address - // arithmetic on the above. eg INDEX and DOT - return -1000 // not on stack -} - -// block copy: -// memmove(&ns, &n, w); -// if wb is true, needs write barrier. -func sgen_wb(n *Node, ns *Node, w int64, wb bool) { - if Debug['g'] != 0 { - op := "sgen" - if wb { - op = "sgen-wb" - } - fmt.Printf("\n%s w=%d\n", op, w) - Dump("r", n) - Dump("res", ns) - } - - if n.Ullman >= UINF && ns.Ullman >= UINF { - Fatalf("sgen UINF") - } - - if w < 0 { - Fatalf("sgen copy %d", w) - } - - // If copying .args, that's all the results, so record definition sites - // for them for the liveness analysis. - if ns.Op == ONAME && ns.Sym.Name == ".args" { - for _, ln := range Curfn.Func.Dcl { - if ln.Class == PPARAMOUT { - Gvardef(ln) - } - } - } - - // Avoid taking the address for simple enough types. - if componentgen_wb(n, ns, wb) { - return - } - - if w == 0 { - // evaluate side effects only - var nodr Node - Regalloc(&nodr, Types[Tptr], nil) - Agen(ns, &nodr) - Agen(n, &nodr) - Regfree(&nodr) - return - } - - // offset on the stack - osrc := stkof(n) - odst := stkof(ns) - - if odst != -1000 { - // on stack, write barrier not needed after all - wb = false - } - - if osrc != -1000 && odst != -1000 && (osrc == 1000 || odst == 1000) || wb && osrc != -1000 { - // osrc and odst both on stack, and at least one is in - // an unknown position. Could generate code to test - // for forward/backward copy, but instead just copy - // to a temporary location first. - // - // OR: write barrier needed and source is on stack. - // Invoking the write barrier will use the stack to prepare its call. - // Copy to temporary. - var tmp Node - Tempname(&tmp, n.Type) - sgen_wb(n, &tmp, w, false) - sgen_wb(&tmp, ns, w, wb) - return - } - - if wb { - cgen_wbfat(n, ns) - return - } - - Thearch.Blockcopy(n, ns, osrc, odst, w) -} - -// generate: -// call f -// proc=-1 normal call but no return -// proc=0 normal call -// proc=1 goroutine run in new proc -// proc=2 defer call save away stack -// proc=3 normal call to C pointer (not Go func value) -func Ginscall(f *Node, proc int) { - if f.Type != nil { - extra := int32(0) - if proc == 1 || proc == 2 { - extra = 2 * int32(Widthptr) - } - Setmaxarg(f.Type, extra) - } - - switch proc { - default: - Fatalf("Ginscall: bad proc %d", proc) - - case 0, // normal call - -1: // normal call but no return - if f.Op == ONAME && f.Class == PFUNC { - if f == Deferreturn { - // Deferred calls will appear to be returning to the CALL - // deferreturn(SB) that we are about to emit. However, the - // stack scanning code will think that the instruction - // before the CALL is executing. To avoid the scanning - // code making bad assumptions (both cosmetic such as - // showing the wrong line number and fatal, such as being - // confused over whether a stack slot contains a pointer - // or a scalar) insert an actual hardware NOP that will - // have the right line number. This is different from - // obj.ANOP, which is a virtual no-op that doesn't make it - // into the instruction stream. - Thearch.Ginsnop() - - if Thearch.LinkArch.Family == sys.PPC64 { - // On ppc64, when compiling Go into position - // independent code on ppc64le we insert an - // instruction to reload the TOC pointer from the - // stack as well. See the long comment near - // jmpdefer in runtime/asm_ppc64.s for why. - // If the MOVD is not needed, insert a hardware NOP - // so that the same number of instructions are used - // on ppc64 in both shared and non-shared modes. - if Ctxt.Flag_shared { - p := Thearch.Gins(ppc64.AMOVD, nil, nil) - p.From.Type = obj.TYPE_MEM - p.From.Offset = 24 - p.From.Reg = ppc64.REGSP - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REG_R2 - } else { - Thearch.Ginsnop() - } - } - } - - p := Thearch.Gins(obj.ACALL, nil, f) - Afunclit(&p.To, f) - if proc == -1 || Noreturn(p) { - Thearch.Gins(obj.AUNDEF, nil, nil) - } - break - } - - var reg Node - Nodreg(®, Types[Tptr], Thearch.REGCTXT) - var r1 Node - Nodreg(&r1, Types[Tptr], Thearch.REGCALLX) - Thearch.Gmove(f, ®) - reg.Op = OINDREG - Thearch.Gmove(®, &r1) - reg.Op = OREGISTER - Thearch.Gins(obj.ACALL, ®, &r1) - - case 3: // normal call of c function pointer - Thearch.Gins(obj.ACALL, nil, f) - - case 1, // call in new proc (go) - 2: // deferred call (defer) - var stk Node - - // size of arguments at 0(SP) - stk.Op = OINDREG - stk.Reg = int16(Thearch.REGSP) - stk.Xoffset = Ctxt.FixedFrameSize() - Thearch.Ginscon(Thearch.Optoas(OAS, Types[TINT32]), int64(Argsize(f.Type)), &stk) - - // FuncVal* at 8(SP) - stk.Xoffset = int64(Widthptr) + Ctxt.FixedFrameSize() - - var reg Node - Nodreg(®, Types[Tptr], Thearch.REGCALLX2) - Thearch.Gmove(f, ®) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), ®, &stk) - - if proc == 1 { - Ginscall(Newproc, 0) - } else { - if !hasdefer { - Fatalf("hasdefer=0 but has defer") - } - Ginscall(Deferproc, 0) - } - - if proc == 2 { - Nodreg(®, Types[TINT32], Thearch.REGRETURN) - p := Thearch.Ginscmp(OEQ, Types[TINT32], ®, Nodintconst(0), +1) - cgen_ret(nil) - Patch(p, Pc) - } - } -} - -// n is call to interface method. -// generate res = n. -func cgen_callinter(n *Node, res *Node, proc int) { - i := n.Left - if i.Op != ODOTINTER { - Fatalf("cgen_callinter: not ODOTINTER %v", i.Op) - } - - i = i.Left // interface - - if !i.Addable { - var tmpi Node - Tempname(&tmpi, i.Type) - Cgen(i, &tmpi) - i = &tmpi - } - - Genlist(n.List) // assign the args - - // i is now addable, prepare an indirected - // register to hold its address. - var nodi Node - Igen(i, &nodi, res) // REG = &inter - - var nodsp Node - Nodindreg(&nodsp, Types[Tptr], Thearch.REGSP) - nodsp.Xoffset = Ctxt.FixedFrameSize() - if proc != 0 { - nodsp.Xoffset += 2 * int64(Widthptr) // leave room for size & fn - } - nodi.Type = Types[Tptr] - nodi.Xoffset += int64(Widthptr) - Cgen(&nodi, &nodsp) // {0, 8(nacl), or 16}(SP) = 8(REG) -- i.data - - var nodo Node - Regalloc(&nodo, Types[Tptr], res) - - nodi.Type = Types[Tptr] - nodi.Xoffset -= int64(Widthptr) - Cgen(&nodi, &nodo) // REG = 0(REG) -- i.tab - Regfree(&nodi) - - var nodr Node - Regalloc(&nodr, Types[Tptr], &nodo) - if n.Left.Xoffset == BADWIDTH { - Fatalf("cgen_callinter: badwidth") - } - Cgen_checknil(&nodo) // in case offset is huge - nodo.Op = OINDREG - nodo.Xoffset = n.Left.Xoffset + 3*int64(Widthptr) + 8 - if proc == 0 { - // plain call: use direct c function pointer - more efficient - Cgen(&nodo, &nodr) // REG = 32+offset(REG) -- i.tab->fun[f] - proc = 3 - } else { - // go/defer. generate go func value. - Agen(&nodo, &nodr) // REG = &(32+offset(REG)) -- i.tab->fun[f] - } - - nodr.Type = n.Left.Type - Ginscall(&nodr, proc) - - Regfree(&nodr) - Regfree(&nodo) -} - -// generate function call; -// proc=0 normal call -// proc=1 goroutine run in new proc -// proc=2 defer call save away stack -func cgen_call(n *Node, proc int) { - if n == nil { - return - } - - var afun Node - if n.Left.Ullman >= UINF { - // if name involves a fn call - // precompute the address of the fn - Tempname(&afun, Types[Tptr]) - - Cgen(n.Left, &afun) - } - - Genlist(n.List) // assign the args - t := n.Left.Type - - // call tempname pointer - if n.Left.Ullman >= UINF { - var nod Node - Regalloc(&nod, Types[Tptr], nil) - Cgen_as(&nod, &afun) - nod.Type = t - Ginscall(&nod, proc) - Regfree(&nod) - return - } - - // call pointer - if n.Left.Op != ONAME || n.Left.Class != PFUNC { - var nod Node - Regalloc(&nod, Types[Tptr], nil) - Cgen_as(&nod, n.Left) - nod.Type = t - Ginscall(&nod, proc) - Regfree(&nod) - return - } - - // call direct - n.Left.Name.Method = true - - Ginscall(n.Left, proc) -} - -// call to n has already been generated. -// generate: -// res = return value from call. -func cgen_callret(n *Node, res *Node) { - t := n.Left.Type - if t.Etype == TPTR32 || t.Etype == TPTR64 { - t = t.Elem() - } - - fp := t.Results().Field(0) - if fp == nil { - Fatalf("cgen_callret: nil") - } - - var nod Node - nod.Op = OINDREG - nod.Reg = int16(Thearch.REGSP) - nod.Addable = true - - nod.Xoffset = fp.Offset + Ctxt.FixedFrameSize() - nod.Type = fp.Type - Cgen_as(res, &nod) -} - -// call to n has already been generated. -// generate: -// res = &return value from call. -func cgen_aret(n *Node, res *Node) { - t := n.Left.Type - if t.IsPtr() { - t = t.Elem() - } - - fp := t.Results().Field(0) - if fp == nil { - Fatalf("cgen_aret: nil") - } - - var nod1 Node - nod1.Op = OINDREG - nod1.Reg = int16(Thearch.REGSP) - nod1.Addable = true - nod1.Xoffset = fp.Offset + Ctxt.FixedFrameSize() - nod1.Type = fp.Type - - if res.Op != OREGISTER { - var nod2 Node - Regalloc(&nod2, Types[Tptr], res) - Agen(&nod1, &nod2) - Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &nod2, res) - Regfree(&nod2) - } else { - Agen(&nod1, res) - } -} - -// generate return. -// n->left is assignments to return values. -func cgen_ret(n *Node) { - if n != nil { - Genlist(n.List) // copy out args - } - if hasdefer { - Ginscall(Deferreturn, 0) - } - Genlist(Curfn.Func.Exit) - p := Thearch.Gins(obj.ARET, nil, nil) - if n != nil && n.Op == ORETJMP { - p.To.Type = obj.TYPE_MEM - p.To.Name = obj.NAME_EXTERN - p.To.Sym = Linksym(n.Left.Sym) - } -} +import "cmd/internal/sys" // hasHMUL64 reports whether the architecture supports 64-bit // signed and unsigned high multiplication (OHMUL). @@ -2680,933 +53,3 @@ func hasAddSetCarry() bool { Fatalf("unknown architecture") return false } - -// generate division according to op, one of: -// res = nl / nr -// res = nl % nr -func cgen_div(op Op, nl *Node, nr *Node, res *Node) { - var w int - - // Architectures need to support 64-bit high multiplications - // (OHMUL) in order to perform divide by constant optimizations. - if nr.Op != OLITERAL || !hasHMUL64() { - goto longdiv - } - w = int(nl.Type.Width * 8) - - // Front end handled 32-bit division. We only need to handle 64-bit. - // Try to do division using multiplication: (2^w)/d. - // See Hacker's Delight, chapter 10. - switch Simtype[nl.Type.Etype] { - default: - goto longdiv - - case TUINT64: - var m Magic - m.W = w - m.Ud = uint64(nr.Int64()) - Umagic(&m) - if m.Bad != 0 { - break - } - - // In order to add the numerator we need to be able to - // avoid overflow. This is done by shifting the result of the - // addition right by 1 and inserting the carry bit into - // the MSB. For now this needs the RROTC instruction. - // TODO(mundaym): Hacker's Delight 2nd ed. chapter 10 proposes - // an alternative sequence of instructions for architectures - // (TODO: MIPS64, PPC64, S390X) that do not have a shift - // right with carry instruction. - if m.Ua != 0 && !hasRROTC64() && !hasRightShiftWithCarry() { - goto longdiv - } - if op == OMOD { - goto longmod - } - - var n1 Node - Cgenr(nl, &n1, nil) - var n2 Node - Nodconst(&n2, nl.Type, int64(m.Um)) - var n3 Node - Regalloc(&n3, nl.Type, res) - Thearch.Cgen_hmul(&n1, &n2, &n3) - - if m.Ua != 0 { - // Need to add numerator accounting for overflow. - if hasAddSetCarry() { - Thearch.AddSetCarry(&n1, &n3, &n3) - } else { - Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) - } - - if !hasRROTC64() { - Thearch.RightShiftWithCarry(&n3, uint(m.S), &n3) - } else { - Nodconst(&n2, nl.Type, 1) - Thearch.Gins(Thearch.Optoas(ORROTC, nl.Type), &n2, &n3) - Nodconst(&n2, nl.Type, int64(m.S)-1) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) - } - } else { - Nodconst(&n2, nl.Type, int64(m.S)) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift dx - } - - Thearch.Gmove(&n3, res) - Regfree(&n1) - Regfree(&n3) - return - - case TINT64: - var m Magic - m.W = w - m.Sd = nr.Int64() - Smagic(&m) - if m.Bad != 0 { - break - } - if op == OMOD { - goto longmod - } - - var n1 Node - Cgenr(nl, &n1, res) - var n2 Node - Nodconst(&n2, nl.Type, m.Sm) - var n3 Node - Regalloc(&n3, nl.Type, nil) - Thearch.Cgen_hmul(&n1, &n2, &n3) - - if m.Sm < 0 { - // Need to add numerator (cannot overflow). - Thearch.Gins(Thearch.Optoas(OADD, nl.Type), &n1, &n3) - } - - Nodconst(&n2, nl.Type, int64(m.S)) - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n3) // shift n3 - - Nodconst(&n2, nl.Type, int64(w)-1) - - Thearch.Gins(Thearch.Optoas(ORSH, nl.Type), &n2, &n1) // -1 iff num is neg - Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n1, &n3) // added - - if m.Sd < 0 { - // This could probably be removed by factoring it into - // the multiplier. - Thearch.Gins(Thearch.Optoas(OMINUS, nl.Type), nil, &n3) - } - - Thearch.Gmove(&n3, res) - Regfree(&n1) - Regfree(&n3) - return - } - - goto longdiv - - // Division and mod using (slow) hardware instruction. -longdiv: - Thearch.Dodiv(op, nl, nr, res) - - return - - // Mod using formula A%B = A-(A/B*B) but - // we know that there is a fast algorithm for A/B. -longmod: - var n1 Node - Regalloc(&n1, nl.Type, res) - - Cgen(nl, &n1) - var n2 Node - Regalloc(&n2, nl.Type, nil) - cgen_div(ODIV, &n1, nr, &n2) - a := Thearch.Optoas(OMUL, nl.Type) - - if !Smallintconst(nr) { - var n3 Node - Regalloc(&n3, nl.Type, nil) - Cgen(nr, &n3) - Thearch.Gins(a, &n3, &n2) - Regfree(&n3) - } else { - Thearch.Gins(a, nr, &n2) - } - Thearch.Gins(Thearch.Optoas(OSUB, nl.Type), &n2, &n1) - Thearch.Gmove(&n1, res) - Regfree(&n1) - Regfree(&n2) -} - -func Fixlargeoffset(n *Node) { - if n == nil { - return - } - if n.Op != OINDREG { - return - } - if n.Reg == int16(Thearch.REGSP) { // stack offset cannot be large - return - } - if n.Xoffset != int64(int32(n.Xoffset)) { - // offset too large, add to register instead. - a := *n - - a.Op = OREGISTER - a.Type = Types[Tptr] - a.Xoffset = 0 - Cgen_checknil(&a) - Thearch.Ginscon(Thearch.Optoas(OADD, Types[Tptr]), n.Xoffset, &a) - n.Xoffset = 0 - } -} - -func cgen_append(n, res *Node) { - if Debug['g'] != 0 { - Dump("cgen_append-n", n) - Dump("cgen_append-res", res) - } - for _, n1 := range n.List.Slice() { - if n1.Ullman >= UINF { - Fatalf("append with function call arguments") - } - } - - // res = append(src, x, y, z) - // - // If res and src are the same, we can avoid writing to base and cap - // unless we grow the underlying array. - needFullUpdate := !samesafeexpr(res, n.List.First()) - - // Copy src triple into base, len, cap. - base := temp(Types[Tptr]) - len := temp(Types[TUINT]) - cap := temp(Types[TUINT]) - - var src Node - Igen(n.List.First(), &src, nil) - src.Type = Types[Tptr] - Thearch.Gmove(&src, base) - src.Type = Types[TUINT] - src.Xoffset += int64(Widthptr) - Thearch.Gmove(&src, len) - src.Xoffset += int64(Widthptr) - Thearch.Gmove(&src, cap) - - // if len+argc <= cap goto L1 - var rlen Node - Regalloc(&rlen, Types[TUINT], nil) - Thearch.Gmove(len, &rlen) - Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(n.List.Len()-1), &rlen) - p := Thearch.Ginscmp(OLE, Types[TUINT], &rlen, cap, +1) - // Note: rlen and src are Regrealloc'ed below at the target of the - // branch we just emitted; do not reuse these Go variables for - // other purposes. They need to still describe the same things - // below that they describe right here. - Regfree(&src) - - // base, len, cap = growslice(type, base, len, cap, newlen) - var arg Node - arg.Op = OINDREG - arg.Reg = int16(Thearch.REGSP) - arg.Addable = true - arg.Xoffset = Ctxt.FixedFrameSize() - arg.Type = Ptrto(Types[TUINT8]) - Cgen(typename(res.Type.Elem()), &arg) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[Tptr] - Cgen(base, &arg) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[TUINT] - Cgen(len, &arg) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[TUINT] - Cgen(cap, &arg) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[TUINT] - Cgen(&rlen, &arg) - arg.Xoffset += int64(Widthptr) - Regfree(&rlen) - - fn := syslook("growslice") - fn = substArgTypes(fn, res.Type.Elem(), res.Type.Elem()) - Ginscall(fn, 0) - - if Widthptr == 4 && Widthreg == 8 { - arg.Xoffset += 4 - } - - arg.Type = Types[Tptr] - Cgen(&arg, base) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[TUINT] - Cgen(&arg, len) - arg.Xoffset += int64(Widthptr) - - arg.Type = Types[TUINT] - Cgen(&arg, cap) - - // Update res with base, len+argc, cap. - if needFullUpdate { - if Debug_append > 0 { - Warn("append: full update") - } - Patch(p, Pc) - } - if res.Op == ONAME { - Gvardef(res) - } - var dst, r1 Node - Igen(res, &dst, nil) - dst.Type = Types[TUINT] - dst.Xoffset += int64(Widthptr) - Regalloc(&r1, Types[TUINT], nil) - Thearch.Gmove(len, &r1) - Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(n.List.Len()-1), &r1) - Thearch.Gmove(&r1, &dst) - Regfree(&r1) - dst.Xoffset += int64(Widthptr) - Thearch.Gmove(cap, &dst) - dst.Type = Types[Tptr] - dst.Xoffset -= 2 * int64(Widthptr) - cgen_wb(base, &dst, needwritebarrier(&dst, base)) - Regfree(&dst) - - if !needFullUpdate { - if Debug_append > 0 { - Warn("append: len-only update") - } - // goto L2; - // L1: - // update len only - // L2: - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - // At the goto above, src refers to cap and rlen holds the new len - if src.Op == OREGISTER || src.Op == OINDREG { - Regrealloc(&src) - } - Regrealloc(&rlen) - src.Xoffset -= int64(Widthptr) - Thearch.Gmove(&rlen, &src) - Regfree(&src) - Regfree(&rlen) - Patch(q, Pc) - } - - // Copy data into place. - // Could do write barrier check around entire copy instead of each element. - // Could avoid reloading registers on each iteration if we know the cgen_wb - // is not going to use a write barrier. - i := 0 - var r2 Node - for _, n2 := range n.List.Slice()[1:] { - Regalloc(&r1, Types[Tptr], nil) - Thearch.Gmove(base, &r1) - Regalloc(&r2, Types[TUINT], nil) - Thearch.Gmove(len, &r2) - if i > 0 { - Thearch.Gins(Thearch.Optoas(OADD, Types[TUINT]), Nodintconst(int64(i)), &r2) - } - w := res.Type.Elem().Width - if Thearch.AddIndex != nil && Thearch.AddIndex(&r2, w, &r1) { - // r1 updated by back end - } else if w == 1 { - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1) - } else { - Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT]), w, &r2) - Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1) - } - Regfree(&r2) - - r1.Op = OINDREG - r1.Type = res.Type.Elem() - cgen_wb(n2, &r1, needwritebarrier(&r1, n2)) - Regfree(&r1) - i++ - } -} - -// Generate res = n, where n is x[i:j] or x[i:j:k]. -// If wb is true, need write barrier updating res's base pointer. -// On systems with 32-bit ints, i, j, k are guaranteed to be 32-bit values. -func cgen_slice(n, res *Node, wb bool) { - if Debug['g'] != 0 { - Dump("cgen_slice-n", n) - Dump("cgen_slice-res", res) - } - - needFullUpdate := !samesafeexpr(n.Left, res) - - // orderexpr has made sure that x is safe (but possibly expensive) - // and i, j, k are cheap. On a system with registers (anything but 386) - // we can evaluate x first and then know we have enough registers - // for i, j, k as well. - var x, xbase, xlen, xcap, i, j, k Node - if n.Op != OSLICEARR && n.Op != OSLICE3ARR { - Igen(n.Left, &x, nil) - } - - indexRegType := Types[TUINT] - if Widthreg > Widthptr { // amd64p32 - indexRegType = Types[TUINT64] - } - - // On most systems, we use registers. - // The 386 has basically no registers, so substitute functions - // that can work with temporaries instead. - regalloc := Regalloc - ginscon := Thearch.Ginscon - gins := Thearch.Gins - if Thearch.LinkArch.Family == sys.I386 { - regalloc = func(n *Node, t *Type, reuse *Node) { - Tempname(n, t) - } - ginscon = func(as obj.As, c int64, n *Node) { - var n1 Node - Regalloc(&n1, n.Type, n) - Thearch.Gmove(n, &n1) - Thearch.Ginscon(as, c, &n1) - Thearch.Gmove(&n1, n) - Regfree(&n1) - } - gins = func(as obj.As, f, t *Node) *obj.Prog { - var n1 Node - Regalloc(&n1, t.Type, t) - Thearch.Gmove(t, &n1) - Thearch.Gins(as, f, &n1) - Thearch.Gmove(&n1, t) - Regfree(&n1) - return nil - } - } - - panics := make([]*obj.Prog, 0, 6) // 3 loads + 3 checks - - loadlen := func() { - if xlen.Op != 0 { - return - } - if n.Op == OSLICEARR || n.Op == OSLICE3ARR { - Nodconst(&xlen, indexRegType, n.Left.Type.Elem().NumElem()) - return - } - if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) { - Nodconst(&xlen, indexRegType, int64(len(n.Left.Val().U.(string)))) - return - } - regalloc(&xlen, indexRegType, nil) - x.Xoffset += int64(Widthptr) - x.Type = Types[TUINT] - Thearch.Gmove(&x, &xlen) - x.Xoffset -= int64(Widthptr) - } - - loadcap := func() { - if xcap.Op != 0 { - return - } - if n.Op == OSLICEARR || n.Op == OSLICE3ARR || n.Op == OSLICESTR { - loadlen() - xcap = xlen - if xcap.Op == OREGISTER { - Regrealloc(&xcap) - } - return - } - regalloc(&xcap, indexRegType, nil) - x.Xoffset += 2 * int64(Widthptr) - x.Type = Types[TUINT] - Thearch.Gmove(&x, &xcap) - x.Xoffset -= 2 * int64(Widthptr) - } - - x1, x2, x3 := n.SliceBounds() // unevaluated index arguments - - // load computes src into targ, but if src refers to the len or cap of n.Left, - // load copies those from xlen, xcap, loading xlen if needed. - // If targ.Op == OREGISTER on return, it must be Regfreed, - // but it should not be modified without first checking whether it is - // xlen or xcap's register. - load := func(src, targ *Node) { - if src == nil { - return - } - switch src.Op { - case OLITERAL: - *targ = *src - return - case OLEN: - // NOTE(rsc): This doesn't actually trigger, because order.go - // has pulled all the len and cap calls into separate assignments - // to temporaries. There are tests in test/sliceopt.go that could - // be enabled if this is fixed. - if samesafeexpr(n.Left, src.Left) { - if Debug_slice > 0 { - Warn("slice: reuse len") - } - loadlen() - *targ = xlen - if targ.Op == OREGISTER { - Regrealloc(targ) - } - return - } - case OCAP: - // NOTE(rsc): This doesn't actually trigger; see note in case OLEN above. - if samesafeexpr(n.Left, src.Left) { - if Debug_slice > 0 { - Warn("slice: reuse cap") - } - loadcap() - *targ = xcap - if targ.Op == OREGISTER { - Regrealloc(targ) - } - return - } - } - if i.Op != 0 && samesafeexpr(x1, src) { - if Debug_slice > 0 { - Warn("slice: reuse 1st index") - } - *targ = i - if targ.Op == OREGISTER { - Regrealloc(targ) - } - return - } - if j.Op != 0 && samesafeexpr(x2, src) { - if Debug_slice > 0 { - Warn("slice: reuse 2nd index") - } - *targ = j - if targ.Op == OREGISTER { - Regrealloc(targ) - } - return - } - if Thearch.Cgenindex != nil { - regalloc(targ, indexRegType, nil) - p := Thearch.Cgenindex(src, targ, false) - if p != nil { - panics = append(panics, p) - } - } else if Thearch.Igenindex != nil { - p := Thearch.Igenindex(src, targ, false) - if p != nil { - panics = append(panics, p) - } - } else { - regalloc(targ, indexRegType, nil) - var tmp Node - Cgenr(src, &tmp, targ) - Thearch.Gmove(&tmp, targ) - Regfree(&tmp) - } - } - - load(x1, &i) - load(x2, &j) - load(x3, &k) - - // i defaults to 0. - if i.Op == 0 { - Nodconst(&i, indexRegType, 0) - } - - // j defaults to len(x) - if j.Op == 0 { - loadlen() - j = xlen - if j.Op == OREGISTER { - Regrealloc(&j) - } - } - - // k defaults to cap(x) - // Only need to load it if we're recalculating cap or doing a full update. - if k.Op == 0 && n.Op != OSLICESTR && (!iszero(&i) || needFullUpdate) { - loadcap() - k = xcap - if k.Op == OREGISTER { - Regrealloc(&k) - } - } - - // Check constant indexes for negative values, and against constant length if known. - // The func obvious below checks for out-of-order constant indexes. - var bound int64 = -1 - if n.Op == OSLICEARR || n.Op == OSLICE3ARR { - bound = n.Left.Type.Elem().NumElem() - } else if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) { - bound = int64(len(n.Left.Val().U.(string))) - } - if Isconst(&i, CTINT) { - if i.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && i.Val().U.(*Mpint).CmpInt64(bound) > 0 { - Yyerror("slice index out of bounds") - } - } - if Isconst(&j, CTINT) { - if j.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && j.Val().U.(*Mpint).CmpInt64(bound) > 0 { - Yyerror("slice index out of bounds") - } - } - if Isconst(&k, CTINT) { - if k.Val().U.(*Mpint).CmpInt64(0) < 0 || bound >= 0 && k.Val().U.(*Mpint).CmpInt64(bound) > 0 { - Yyerror("slice index out of bounds") - } - } - - // same reports whether n1 and n2 are the same register or constant. - same := func(n1, n2 *Node) bool { - return n1.Op == OREGISTER && n2.Op == OREGISTER && n1.Reg == n2.Reg || - n1.Op == ONAME && n2.Op == ONAME && n1.Orig == n2.Orig && n1.Type == n2.Type && n1.Xoffset == n2.Xoffset || - n1.Op == OLITERAL && n2.Op == OLITERAL && n1.Val().U.(*Mpint).Cmp(n2.Val().U.(*Mpint)) == 0 - } - - // obvious reports whether n1 <= n2 is obviously true, - // and it calls Yyerror if n1 <= n2 is obviously false. - obvious := func(n1, n2 *Node) bool { - if Debug['B'] != 0 { // -B disables bounds checks - return true - } - if same(n1, n2) { - return true // n1 == n2 - } - if iszero(n1) { - return true // using unsigned compare, so 0 <= n2 always true - } - if xlen.Op != 0 && same(n1, &xlen) && xcap.Op != 0 && same(n2, &xcap) { - return true // len(x) <= cap(x) always true - } - if Isconst(n1, CTINT) && Isconst(n2, CTINT) { - if n1.Val().U.(*Mpint).Cmp(n2.Val().U.(*Mpint)) <= 0 { - return true // n1, n2 constants such that n1 <= n2 - } - Yyerror("slice index out of bounds") - return true - } - return false - } - - compare := func(n1, n2 *Node) { - // n1 might be a 64-bit constant, even on 32-bit architectures, - // but it will be represented in 32 bits. - if Ctxt.Arch.RegSize == 4 && Is64(n1.Type) { - if n1.Val().U.(*Mpint).CmpInt64(1<<31) >= 0 { - Fatalf("missed slice out of bounds check") - } - var tmp Node - Nodconst(&tmp, indexRegType, n1.Int64()) - n1 = &tmp - } - p := Thearch.Ginscmp(OGT, indexRegType, n1, n2, -1) - panics = append(panics, p) - } - - loadcap() - max := &xcap - if k.Op != 0 && (n.Op == OSLICE3 || n.Op == OSLICE3ARR) { - if obvious(&k, max) { - if Debug_slice > 0 { - Warn("slice: omit check for 3rd index") - } - } else { - compare(&k, max) - } - max = &k - } - if j.Op != 0 { - if obvious(&j, max) { - if Debug_slice > 0 { - Warn("slice: omit check for 2nd index") - } - } else { - compare(&j, max) - } - max = &j - } - if i.Op != 0 { - if obvious(&i, max) { - if Debug_slice > 0 { - Warn("slice: omit check for 1st index") - } - } else { - compare(&i, max) - } - max = &i - } - if k.Op != 0 && i.Op != 0 { - obvious(&i, &k) // emit compile-time error for x[3:n:2] - } - - if len(panics) > 0 { - p := Gbranch(obj.AJMP, nil, 0) - for _, q := range panics { - Patch(q, Pc) - } - Ginscall(panicslice, -1) - Patch(p, Pc) - } - - // Checks are done. - // Compute new len as j-i, cap as k-i. - // If i and j are same register, len is constant 0. - // If i and k are same register, cap is constant 0. - // If j and k are same register, len and cap are same. - - // Done with xlen and xcap. - // Now safe to modify j and k even if they alias xlen, xcap. - if xlen.Op == OREGISTER { - Regfree(&xlen) - } - if xcap.Op == OREGISTER { - Regfree(&xcap) - } - - // are j and k the same value? - sameJK := same(&j, &k) - - if i.Op != 0 { - // j -= i - if same(&i, &j) { - if Debug_slice > 0 { - Warn("slice: result len == 0") - } - if j.Op == OREGISTER { - Regfree(&j) - } - Nodconst(&j, indexRegType, 0) - } else { - switch j.Op { - case OLITERAL: - if Isconst(&i, CTINT) { - Nodconst(&j, indexRegType, j.Int64()-i.Int64()) - if Debug_slice > 0 { - Warn("slice: result len == %d", j.Int64()) - } - break - } - fallthrough - case ONAME: - if !istemp(&j) { - var r Node - regalloc(&r, indexRegType, nil) - Thearch.Gmove(&j, &r) - j = r - } - fallthrough - case OREGISTER: - if i.Op == OLITERAL { - v := i.Int64() - if v != 0 { - ginscon(Thearch.Optoas(OSUB, indexRegType), v, &j) - } - } else { - gins(Thearch.Optoas(OSUB, indexRegType), &i, &j) - } - } - } - - // k -= i if k different from j and cap is needed.j - // (The modifications to j above cannot affect i: if j and i were aliased, - // we replace j with a constant 0 instead of doing a subtraction, - // leaving i unmodified.) - if k.Op == 0 { - if Debug_slice > 0 && n.Op != OSLICESTR { - Warn("slice: result cap not computed") - } - // no need - } else if same(&i, &k) { - if k.Op == OREGISTER { - Regfree(&k) - } - Nodconst(&k, indexRegType, 0) - if Debug_slice > 0 { - Warn("slice: result cap == 0") - } - } else if sameJK { - if Debug_slice > 0 { - Warn("slice: result cap == result len") - } - // k and j were the same value; make k-i the same as j-i. - if k.Op == OREGISTER { - Regfree(&k) - } - k = j - if k.Op == OREGISTER { - Regrealloc(&k) - } - } else { - switch k.Op { - case OLITERAL: - if Isconst(&i, CTINT) { - Nodconst(&k, indexRegType, k.Int64()-i.Int64()) - if Debug_slice > 0 { - Warn("slice: result cap == %d", k.Int64()) - } - break - } - fallthrough - case ONAME: - if !istemp(&k) { - var r Node - regalloc(&r, indexRegType, nil) - Thearch.Gmove(&k, &r) - k = r - } - fallthrough - case OREGISTER: - if same(&i, &k) { - Regfree(&k) - Nodconst(&k, indexRegType, 0) - if Debug_slice > 0 { - Warn("slice: result cap == 0") - } - } else if i.Op == OLITERAL { - v := i.Int64() - if v != 0 { - ginscon(Thearch.Optoas(OSUB, indexRegType), v, &k) - } - } else { - gins(Thearch.Optoas(OSUB, indexRegType), &i, &k) - } - } - } - } - - adjustBase := true - if i.Op == 0 || iszero(&i) { - if Debug_slice > 0 { - Warn("slice: skip base adjustment for 1st index 0") - } - adjustBase = false - } else if k.Op != 0 && iszero(&k) || k.Op == 0 && iszero(&j) { - if Debug_slice > 0 { - if n.Op == OSLICESTR { - Warn("slice: skip base adjustment for string len == 0") - } else { - Warn("slice: skip base adjustment for cap == 0") - } - } - adjustBase = false - } - - if !adjustBase && !needFullUpdate { - if Debug_slice > 0 { - if k.Op != 0 { - Warn("slice: len/cap-only update") - } else { - Warn("slice: len-only update") - } - } - if i.Op == OREGISTER { - Regfree(&i) - } - // Write len (and cap if needed) back to x. - x.Xoffset += int64(Widthptr) - x.Type = Types[TUINT] - Thearch.Gmove(&j, &x) - x.Xoffset -= int64(Widthptr) - if k.Op != 0 { - x.Xoffset += 2 * int64(Widthptr) - x.Type = Types[TUINT] - Thearch.Gmove(&k, &x) - x.Xoffset -= 2 * int64(Widthptr) - } - Regfree(&x) - } else { - // Compute new base. May smash i. - if n.Op == OSLICEARR || n.Op == OSLICE3ARR { - Cgenr(n.Left, &xbase, nil) - Cgen_checknil(&xbase) - } else { - var ptr *Type - if n.Op == OSLICESTR { - ptr = ptrToUint8 - } else { - ptr = Ptrto(n.Type.Elem()) - } - regalloc(&xbase, ptr, nil) - x.Type = xbase.Type - Thearch.Gmove(&x, &xbase) - Regfree(&x) - } - if i.Op != 0 && adjustBase { - // Branch around the base adjustment if the resulting cap will be 0. - var p *obj.Prog - size := &k - if k.Op == 0 { - size = &j - } - if Isconst(size, CTINT) { - // zero was checked above, must be non-zero. - } else { - var tmp Node - Nodconst(&tmp, indexRegType, 0) - p = Thearch.Ginscmp(OEQ, indexRegType, size, &tmp, -1) - } - var w int64 - if n.Op == OSLICESTR { - w = 1 // res is string, elem size is 1 (byte) - } else { - w = res.Type.Elem().Width // res is []T, elem size is T.width - } - if Isconst(&i, CTINT) { - ginscon(Thearch.Optoas(OADD, xbase.Type), i.Int64()*w, &xbase) - } else if Thearch.AddIndex != nil && Thearch.AddIndex(&i, w, &xbase) { - // done by back end - } else if w == 1 { - gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase) - } else { - if i.Op == ONAME && !istemp(&i) { - var tmp Node - Tempname(&tmp, i.Type) - Thearch.Gmove(&i, &tmp) - i = tmp - } - ginscon(Thearch.Optoas(OMUL, i.Type), w, &i) - gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase) - } - if p != nil { - Patch(p, Pc) - } - } - if i.Op == OREGISTER { - Regfree(&i) - } - - // Write len, cap, base to result. - if res.Op == ONAME { - Gvardef(res) - } - Igen(res, &x, nil) - x.Xoffset += int64(Widthptr) - x.Type = Types[TUINT] - Thearch.Gmove(&j, &x) - x.Xoffset -= int64(Widthptr) - if k.Op != 0 { - x.Xoffset += 2 * int64(Widthptr) - Thearch.Gmove(&k, &x) - x.Xoffset -= 2 * int64(Widthptr) - } - x.Type = xbase.Type - cgen_wb(&xbase, &x, wb) - Regfree(&xbase) - Regfree(&x) - } - - if j.Op == OREGISTER { - Regfree(&j) - } - if k.Op == OREGISTER { - Regfree(&k) - } -} diff --git a/src/cmd/compile/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go deleted file mode 100644 index 96a1dfb3c2..0000000000 --- a/src/cmd/compile/internal/gc/cplx.go +++ /dev/null @@ -1,474 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package gc - -import "cmd/internal/obj" - -func overlap_cplx(f *Node, t *Node) bool { - // check whether f and t could be overlapping stack references. - // not exact, because it's hard to check for the stack register - // in portable code. close enough: worst case we will allocate - // an extra temporary and the registerizer will clean it up. - return f.Op == OINDREG && t.Op == OINDREG && f.Xoffset+f.Type.Width >= t.Xoffset && t.Xoffset+t.Type.Width >= f.Xoffset -} - -func complexbool(op Op, nl, nr, res *Node, wantTrue bool, likely int, to *obj.Prog) { - // make both sides addable in ullman order - if nr != nil { - if nl.Ullman > nr.Ullman && !nl.Addable { - nl = CgenTemp(nl) - } - - if !nr.Addable { - nr = CgenTemp(nr) - } - } - if !nl.Addable { - nl = CgenTemp(nl) - } - - // Break nl and nr into real and imaginary components. - var lreal, limag, rreal, rimag Node - subnode(&lreal, &limag, nl) - subnode(&rreal, &rimag, nr) - - // build tree - // if branching: - // real(l) == real(r) && imag(l) == imag(r) - // if generating a value, use a branch-free version: - // real(l) == real(r) & imag(l) == imag(r) - realeq := Node{ - Op: OEQ, - Left: &lreal, - Right: &rreal, - Type: Types[TBOOL], - } - imageq := Node{ - Op: OEQ, - Left: &limag, - Right: &rimag, - Type: Types[TBOOL], - } - and := Node{ - Op: OANDAND, - Left: &realeq, - Right: &imageq, - Type: Types[TBOOL], - } - - if res != nil { - // generating a value - and.Op = OAND - if op == ONE { - and.Op = OOR - realeq.Op = ONE - imageq.Op = ONE - } - Bvgen(&and, res, true) - return - } - - // generating a branch - if op == ONE { - wantTrue = !wantTrue - } - - Bgen(&and, wantTrue, likely, to) -} - -// break addable nc-complex into nr-real and ni-imaginary -func subnode(nr *Node, ni *Node, nc *Node) { - if !nc.Addable { - Fatalf("subnode not addable") - } - - tc := Simsimtype(nc.Type) - tc = cplxsubtype(tc) - t := Types[tc] - - if nc.Op == OLITERAL { - u := nc.Val().U.(*Mpcplx) - nodfconst(nr, t, &u.Real) - nodfconst(ni, t, &u.Imag) - return - } - - *nr = *nc - nr.Type = t - - *ni = *nc - ni.Type = t - ni.Xoffset += t.Width -} - -// generate code res = -nl -func minus(nl *Node, res *Node) { - var ra Node - ra.Op = OMINUS - ra.Left = nl - ra.Type = nl.Type - Cgen(&ra, res) -} - -// build and execute tree -// real(res) = -real(nl) -// imag(res) = -imag(nl) -func complexminus(nl *Node, res *Node) { - var n1 Node - var n2 Node - var n5 Node - var n6 Node - - subnode(&n1, &n2, nl) - subnode(&n5, &n6, res) - - minus(&n1, &n5) - minus(&n2, &n6) -} - -// build and execute tree -// real(res) = real(nl) op real(nr) -// imag(res) = imag(nl) op imag(nr) -func complexadd(op Op, nl *Node, nr *Node, res *Node) { - var n1 Node - var n2 Node - var n3 Node - var n4 Node - var n5 Node - var n6 Node - - subnode(&n1, &n2, nl) - subnode(&n3, &n4, nr) - subnode(&n5, &n6, res) - - var ra Node - ra.Op = op - ra.Left = &n1 - ra.Right = &n3 - ra.Type = n1.Type - Cgen(&ra, &n5) - - ra = Node{} - ra.Op = op - ra.Left = &n2 - ra.Right = &n4 - ra.Type = n2.Type - Cgen(&ra, &n6) -} - -// build and execute tree -// tmp = real(nl)*real(nr) - imag(nl)*imag(nr) -// imag(res) = real(nl)*imag(nr) + imag(nl)*real(nr) -// real(res) = tmp -func complexmul(nl *Node, nr *Node, res *Node) { - var n1 Node - var n2 Node - var n3 Node - var n4 Node - var n5 Node - var n6 Node - var tmp Node - - subnode(&n1, &n2, nl) - subnode(&n3, &n4, nr) - subnode(&n5, &n6, res) - Tempname(&tmp, n5.Type) - - // real part -> tmp - var rm1 Node - - rm1.Op = OMUL - rm1.Left = &n1 - rm1.Right = &n3 - rm1.Type = n1.Type - - var rm2 Node - rm2.Op = OMUL - rm2.Left = &n2 - rm2.Right = &n4 - rm2.Type = n2.Type - - var ra Node - ra.Op = OSUB - ra.Left = &rm1 - ra.Right = &rm2 - ra.Type = rm1.Type - Cgen(&ra, &tmp) - - // imag part - rm1 = Node{} - - rm1.Op = OMUL - rm1.Left = &n1 - rm1.Right = &n4 - rm1.Type = n1.Type - - rm2 = Node{} - rm2.Op = OMUL - rm2.Left = &n2 - rm2.Right = &n3 - rm2.Type = n2.Type - - ra = Node{} - ra.Op = OADD - ra.Left = &rm1 - ra.Right = &rm2 - ra.Type = rm1.Type - Cgen(&ra, &n6) - - // tmp ->real part - Cgen(&tmp, &n5) -} - -func nodfconst(n *Node, t *Type, fval *Mpflt) { - *n = Node{} - n.Op = OLITERAL - n.Addable = true - ullmancalc(n) - n.SetVal(Val{fval}) - n.Type = t - - if !t.IsFloat() { - Fatalf("nodfconst: bad type %v", t) - } -} - -func Complexop(n *Node, res *Node) bool { - if n != nil && n.Type != nil { - if n.Type.IsComplex() { - goto maybe - } - } - - if res != nil && res.Type != nil { - if res.Type.IsComplex() { - goto maybe - } - } - - if n.Op == OREAL || n.Op == OIMAG { - //dump("\ncomplex-yes", n); - return true - } - - //dump("\ncomplex-no", n); - return false - -maybe: - switch n.Op { - case OCONV, // implemented ops - OADD, - OSUB, - OMUL, - OMINUS, - OCOMPLEX, - OREAL, - OIMAG: - //dump("\ncomplex-yes", n); - return true - - case ODOT, - ODOTPTR, - OINDEX, - OIND, - ONAME: - //dump("\ncomplex-yes", n); - return true - } - - //dump("\ncomplex-no", n); - return false -} - -func Complexmove(f *Node, t *Node) { - if Debug['g'] != 0 { - Dump("\ncomplexmove-f", f) - Dump("complexmove-t", t) - } - - if !t.Addable { - Fatalf("complexmove: to not addable") - } - - ft := Simsimtype(f.Type) - tt := Simsimtype(t.Type) - // complex to complex move/convert. - // make f addable. - // also use temporary if possible stack overlap. - if (ft == TCOMPLEX64 || ft == TCOMPLEX128) && (tt == TCOMPLEX64 || tt == TCOMPLEX128) { - if !f.Addable || overlap_cplx(f, t) { - var tmp Node - Tempname(&tmp, f.Type) - Complexmove(f, &tmp) - f = &tmp - } - - var n1 Node - var n2 Node - subnode(&n1, &n2, f) - var n4 Node - var n3 Node - subnode(&n3, &n4, t) - - Cgen(&n1, &n3) - Cgen(&n2, &n4) - } else { - Fatalf("complexmove: unknown conversion: %v -> %v\n", f.Type, t.Type) - } -} - -func Complexgen(n *Node, res *Node) { - if Debug['g'] != 0 { - Dump("\ncomplexgen-n", n) - Dump("complexgen-res", res) - } - - for n.Op == OCONVNOP { - n = n.Left - } - - // pick off float/complex opcodes - switch n.Op { - case OCOMPLEX: - if res.Addable { - var n1 Node - var n2 Node - subnode(&n1, &n2, res) - var tmp Node - Tempname(&tmp, n1.Type) - Cgen(n.Left, &tmp) - Cgen(n.Right, &n2) - Cgen(&tmp, &n1) - return - } - - case OREAL, OIMAG: - nl := n.Left - if !nl.Addable { - var tmp Node - Tempname(&tmp, nl.Type) - Complexgen(nl, &tmp) - nl = &tmp - } - - var n1 Node - var n2 Node - subnode(&n1, &n2, nl) - if n.Op == OREAL { - Cgen(&n1, res) - return - } - - Cgen(&n2, res) - return - } - - // perform conversion from n to res - tl := Simsimtype(res.Type) - - tl = cplxsubtype(tl) - tr := Simsimtype(n.Type) - tr = cplxsubtype(tr) - if tl != tr { - if !n.Addable { - var n1 Node - Tempname(&n1, n.Type) - Complexmove(n, &n1) - n = &n1 - } - - Complexmove(n, res) - return - } - - if !res.Addable { - var n1 Node - Igen(res, &n1, nil) - Cgen(n, &n1) - Regfree(&n1) - return - } - - if n.Addable { - Complexmove(n, res) - return - } - - switch n.Op { - default: - Dump("complexgen: unknown op", n) - Fatalf("complexgen: unknown op %v", n.Op) - - case ODOT, - ODOTPTR, - OINDEX, - OIND, - OCALLFUNC, - OCALLMETH, - OCALLINTER: - var n1 Node - Igen(n, &n1, res) - - Complexmove(&n1, res) - Regfree(&n1) - return - - case OCONV, - OADD, - OSUB, - OMUL, - OMINUS, - OCOMPLEX, - OREAL, - OIMAG: - break - } - - nl := n.Left - if nl == nil { - return - } - nr := n.Right - - // make both sides addable in ullman order - var tnl Node - if nr != nil { - if nl.Ullman > nr.Ullman && !nl.Addable { - Tempname(&tnl, nl.Type) - Cgen(nl, &tnl) - nl = &tnl - } - - if !nr.Addable { - var tnr Node - Tempname(&tnr, nr.Type) - Cgen(nr, &tnr) - nr = &tnr - } - } - - if !nl.Addable { - Tempname(&tnl, nl.Type) - Cgen(nl, &tnl) - nl = &tnl - } - - switch n.Op { - default: - Fatalf("complexgen: unknown op %v", n.Op) - - case OCONV: - Complexmove(nl, res) - - case OMINUS: - complexminus(nl, res) - - case OADD, OSUB: - complexadd(n.Op, nl, nr, res) - - case OMUL: - complexmul(nl, nr, res) - } -} diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index 21594c6504..f948fc6100 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -6,11 +6,7 @@ package gc -import ( - "cmd/internal/obj" - "cmd/internal/sys" - "fmt" -) +import "fmt" // TODO: labellist should become part of a "compilation state" for functions. var labellist []*Label @@ -291,370 +287,6 @@ func stmtlabel(n *Node) *Label { return nil } -// compile statements -func Genlist(l Nodes) { - for _, n := range l.Slice() { - gen(n) - } -} - -// generate code to start new proc running call n. -func cgen_proc(n *Node, proc int) { - switch n.Left.Op { - default: - Fatalf("cgen_proc: unknown call %v", n.Left.Op) - - case OCALLMETH: - cgen_callmeth(n.Left, proc) - - case OCALLINTER: - cgen_callinter(n.Left, nil, proc) - - case OCALLFUNC: - cgen_call(n.Left, proc) - } -} - -// generate declaration. -// have to allocate heap copy -// for escaped variables. -func cgen_dcl(n *Node) { - if Debug['g'] != 0 { - Dump("\ncgen-dcl", n) - } - if n.Op != ONAME { - Dump("cgen_dcl", n) - Fatalf("cgen_dcl") - } - - if n.Class == PAUTOHEAP { - Fatalf("cgen_dcl %v", n) - } -} - -// generate discard of value -func cgen_discard(nr *Node) { - if nr == nil { - return - } - - switch nr.Op { - case ONAME: - if nr.Class != PAUTOHEAP && nr.Class != PEXTERN && nr.Class != PFUNC { - gused(nr) - } - - // unary - case OADD, - OAND, - ODIV, - OEQ, - OGE, - OGT, - OLE, - OLSH, - OLT, - OMOD, - OMUL, - ONE, - OOR, - ORSH, - OSUB, - OXOR: - cgen_discard(nr.Left) - - cgen_discard(nr.Right) - - // binary - case OCAP, - OCOM, - OLEN, - OMINUS, - ONOT, - OPLUS: - cgen_discard(nr.Left) - - case OIND: - Cgen_checknil(nr.Left) - - // special enough to just evaluate - default: - var tmp Node - Tempname(&tmp, nr.Type) - - Cgen_as(&tmp, nr) - gused(&tmp) - } -} - -// clearslim generates code to zero a slim node. -func Clearslim(n *Node) { - var z Node - z.Op = OLITERAL - z.Type = n.Type - z.Addable = true - - switch Simtype[n.Type.Etype] { - case TCOMPLEX64, TCOMPLEX128: - z.SetVal(Val{new(Mpcplx)}) - z.Val().U.(*Mpcplx).Real.SetFloat64(0.0) - z.Val().U.(*Mpcplx).Imag.SetFloat64(0.0) - - case TFLOAT32, TFLOAT64: - var zero Mpflt - zero.SetFloat64(0.0) - z.SetVal(Val{&zero}) - - case TPTR32, TPTR64, TCHAN, TMAP: - z.SetVal(Val{new(NilVal)}) - - case TBOOL: - z.SetVal(Val{false}) - - case TINT8, - TINT16, - TINT32, - TINT64, - TUINT8, - TUINT16, - TUINT32, - TUINT64: - z.SetVal(Val{new(Mpint)}) - z.Val().U.(*Mpint).SetInt64(0) - - default: - Fatalf("clearslim called on type %v", n.Type) - } - - ullmancalc(&z) - Cgen(&z, n) -} - -// generate: -// res = iface{typ, data} -// n->left is typ -// n->right is data -func Cgen_eface(n *Node, res *Node) { - // the right node of an eface may contain function calls that uses res as an argument, - // so it's important that it is done first - - tmp := temp(Types[Tptr]) - Cgen(n.Right, tmp) - - Gvardef(res) - - dst := *res - dst.Type = Types[Tptr] - dst.Xoffset += int64(Widthptr) - Cgen(tmp, &dst) - - dst.Xoffset -= int64(Widthptr) - Cgen(n.Left, &dst) -} - -// generate one of: -// res, resok = x.(T) -// res = x.(T) (when resok == nil) -// n.Left is x -// n.Type is T -func cgen_dottype(n *Node, res, resok *Node, wb bool) { - if Debug_typeassert > 0 { - Warn("type assertion inlined") - } - // iface := n.Left - // r1 := iword(iface) - // if n.Left is non-empty interface { - // r1 = *r1 - // } - // if r1 == T { - // res = idata(iface) - // resok = true - // } else { - // assert[EI]2T(x, T, nil) // (when resok == nil; does not return) - // resok = false // (when resok != nil) - // } - // - var iface Node - Igen(n.Left, &iface, res) - var r1, r2 Node - byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) - Regalloc(&r1, byteptr, nil) - iface.Type = byteptr - Cgen(&iface, &r1) - if !n.Left.Type.IsEmptyInterface() { - // Holding itab, want concrete type in second word. - p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) - r2 = r1 - r2.Op = OINDREG - r2.Xoffset = int64(Widthptr) - Cgen(&r2, &r1) - Patch(p, Pc) - } - Regalloc(&r2, byteptr, nil) - Cgen(typename(n.Type), &r2) - p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) - Regfree(&r2) // not needed for success path; reclaimed on one failure path - iface.Xoffset += int64(Widthptr) - Cgen(&iface, &r1) - Regfree(&iface) - - if resok == nil { - r1.Type = res.Type - cgen_wb(&r1, res, wb) - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - Regrealloc(&r2) // reclaim from above, for this failure path - fn := syslook("panicdottype") - dowidth(fn.Type) - call := Nod(OCALLFUNC, fn, nil) - r1.Type = byteptr - r2.Type = byteptr - call.List.Set([]*Node{&r1, &r2, typename(n.Left.Type)}) - call.List.Set(ascompatte(OCALLFUNC, call, false, fn.Type.Params(), call.List.Slice(), 0, nil)) - gen(call) - Regfree(&r1) - Regfree(&r2) - Thearch.Gins(obj.AUNDEF, nil, nil) - Patch(q, Pc) - } else { - // This half is handling the res, resok = x.(T) case, - // which is called from gen, not cgen, and is consequently fussier - // about blank assignments. We have to avoid calling cgen for those. - r1.Type = res.Type - if !isblank(res) { - cgen_wb(&r1, res, wb) - } - Regfree(&r1) - if !isblank(resok) { - Cgen(Nodbool(true), resok) - } - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - if !isblank(res) { - n := nodnil() - n.Type = res.Type - Cgen(n, res) - } - if !isblank(resok) { - Cgen(Nodbool(false), resok) - } - Patch(q, Pc) - } -} - -// generate: -// res, resok = x.(T) -// n.Left is x -// n.Type is T -func Cgen_As2dottype(n, res, resok *Node) { - if Debug_typeassert > 0 { - Warn("type assertion inlined") - } - // iface := n.Left - // r1 := iword(iface) - // if n.Left is non-empty interface { - // r1 = *r1 - // } - // if r1 == T { - // res = idata(iface) - // resok = true - // } else { - // res = nil - // resok = false - // } - // - var iface Node - Igen(n.Left, &iface, nil) - var r1, r2 Node - byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte) - Regalloc(&r1, byteptr, res) - iface.Type = byteptr - Cgen(&iface, &r1) - if !n.Left.Type.IsEmptyInterface() { - // Holding itab, want concrete type in second word. - p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) - r2 = r1 - r2.Op = OINDREG - r2.Xoffset = int64(Widthptr) - Cgen(&r2, &r1) - Patch(p, Pc) - } - Regalloc(&r2, byteptr, nil) - Cgen(typename(n.Type), &r2) - p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) - iface.Type = n.Type - iface.Xoffset += int64(Widthptr) - Cgen(&iface, &r1) - if iface.Op != 0 { - Regfree(&iface) - } - Cgen(&r1, res) - q := Gbranch(obj.AJMP, nil, 0) - Patch(p, Pc) - - fn := syslook("panicdottype") - dowidth(fn.Type) - call := Nod(OCALLFUNC, fn, nil) - call.List.Set([]*Node{&r1, &r2, typename(n.Left.Type)}) - call.List.Set(ascompatte(OCALLFUNC, call, false, fn.Type.Params(), call.List.Slice(), 0, nil)) - gen(call) - Regfree(&r1) - Regfree(&r2) - Thearch.Gins(obj.AUNDEF, nil, nil) - Patch(q, Pc) -} - -// gather series of offsets -// >=0 is direct addressed field -// <0 is pointer to next field (+1) -func Dotoffset(n *Node, oary []int64, nn **Node) int { - var i int - - switch n.Op { - case ODOT: - if n.Xoffset == BADWIDTH { - Dump("bad width in dotoffset", n) - Fatalf("bad width in dotoffset") - } - - i = Dotoffset(n.Left, oary, nn) - if i > 0 { - if oary[i-1] >= 0 { - oary[i-1] += n.Xoffset - } else { - oary[i-1] -= n.Xoffset - } - break - } - - if i < 10 { - oary[i] = n.Xoffset - i++ - } - - case ODOTPTR: - if n.Xoffset == BADWIDTH { - Dump("bad width in dotoffset", n) - Fatalf("bad width in dotoffset") - } - - i = Dotoffset(n.Left, oary, nn) - if i < 10 { - oary[i] = -(n.Xoffset + 1) - i++ - } - - default: - *nn = n - return 0 - } - - if i >= 10 { - *nn = nil - } - return i -} - // make a new off the books func Tempname(nn *Node, t *Type) { if Curfn == nil { @@ -697,347 +329,6 @@ func temp(t *Type) *Node { return n.Orig } -func gen(n *Node) { - //dump("gen", n); - - lno := setlineno(n) - - wasregalloc := Anyregalloc() - - if n == nil { - goto ret - } - - if n.Ninit.Len() > 0 { - Genlist(n.Ninit) - } - - setlineno(n) - - switch n.Op { - default: - Fatalf("gen: unknown op %+S", n) - - case OCASE, - OFALL, - OXCASE, - OXFALL, - ODCLCONST, - ODCLFUNC, - ODCLTYPE: - break - - case OEMPTY: - break - - case OBLOCK: - Genlist(n.List) - - case OLABEL: - if isblanksym(n.Left.Sym) { - break - } - - lab := newlab(n) - - // if there are pending gotos, resolve them all to the current pc. - var p2 *obj.Prog - for p1 := lab.Gotopc; p1 != nil; p1 = p2 { - p2 = unpatch(p1) - Patch(p1, Pc) - } - - lab.Gotopc = nil - if lab.Labelpc == nil { - lab.Labelpc = Pc - } - - if n.Name.Defn != nil { - switch n.Name.Defn.Op { - // so stmtlabel can find the label - case OFOR, OSWITCH, OSELECT: - n.Name.Defn.Sym = lab.Sym - } - } - - // if label is defined, emit jump to it. - // otherwise save list of pending gotos in lab->gotopc. - // the list is linked through the normal jump target field - // to avoid a second list. (the jumps are actually still - // valid code, since they're just going to another goto - // to the same label. we'll unwind it when we learn the pc - // of the label in the OLABEL case above.) - case OGOTO: - lab := newlab(n) - - if lab.Labelpc != nil { - gjmp(lab.Labelpc) - } else { - lab.Gotopc = gjmp(lab.Gotopc) - } - - case OBREAK: - if n.Left != nil { - lab := n.Left.Sym.Label - if lab == nil { - Yyerror("break label not defined: %v", n.Left.Sym) - break - } - - lab.Used = true - if lab.Breakpc == nil { - Yyerror("invalid break label %v", n.Left.Sym) - break - } - - gjmp(lab.Breakpc) - break - } - - if breakpc == nil { - Yyerror("break is not in a loop") - break - } - - gjmp(breakpc) - - case OCONTINUE: - if n.Left != nil { - lab := n.Left.Sym.Label - if lab == nil { - Yyerror("continue label not defined: %v", n.Left.Sym) - break - } - - lab.Used = true - if lab.Continpc == nil { - Yyerror("invalid continue label %v", n.Left.Sym) - break - } - - gjmp(lab.Continpc) - break - } - - if continpc == nil { - Yyerror("continue is not in a loop") - break - } - - gjmp(continpc) - - case OFOR: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - scontin := continpc - continpc = Pc - - // define break and continue labels - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - lab.Continpc = continpc - } - - gen(n.Right) // contin: incr - Patch(p1, Pc) // test: - Bgen(n.Left, false, -1, breakpc) // if(!test) goto break - Genlist(n.Nbody) // body - gjmp(continpc) - Patch(breakpc, Pc) // done: - continpc = scontin - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - lab.Continpc = nil - } - - case OIF: - p1 := gjmp(nil) // goto test - p2 := gjmp(nil) // p2: goto else - Patch(p1, Pc) // test: - Bgen(n.Left, false, int(-n.Likely), p2) // if(!test) goto p2 - Genlist(n.Nbody) // then - p3 := gjmp(nil) // goto done - Patch(p2, Pc) // else: - Genlist(n.Rlist) // else - Patch(p3, Pc) // done: - - case OSWITCH: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - - // define break label - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - } - - Patch(p1, Pc) // test: - Genlist(n.Nbody) // switch(test) body - Patch(breakpc, Pc) // done: - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - } - - case OSELECT: - sbreak := breakpc - p1 := gjmp(nil) // goto test - breakpc = gjmp(nil) // break: goto done - - // define break label - lab := stmtlabel(n) - if lab != nil { - lab.Breakpc = breakpc - } - - Patch(p1, Pc) // test: - Genlist(n.Nbody) // select() body - Patch(breakpc, Pc) // done: - breakpc = sbreak - if lab != nil { - lab.Breakpc = nil - } - - case ODCL: - cgen_dcl(n.Left) - - case OAS: - if gen_as_init(n, false) { - break - } - Cgen_as(n.Left, n.Right) - - case OASWB: - Cgen_as_wb(n.Left, n.Right, true) - - case OAS2DOTTYPE: - cgen_dottype(n.Rlist.First(), n.List.First(), n.List.Second(), needwritebarrier(n.List.First(), n.Rlist.First())) - - case OCALLMETH: - cgen_callmeth(n, 0) - - case OCALLINTER: - cgen_callinter(n, nil, 0) - - case OCALLFUNC: - cgen_call(n, 0) - - case OPROC: - cgen_proc(n, 1) - - case ODEFER: - cgen_proc(n, 2) - - case ORETURN, ORETJMP: - cgen_ret(n) - - // Function calls turned into compiler intrinsics. - // At top level, can just ignore the call and make sure to preserve side effects in the argument, if any. - case OGETG: - // nothing - case OSQRT: - cgen_discard(n.Left) - - case OCHECKNIL: - Cgen_checknil(n.Left) - - case OVARKILL: - Gvarkill(n.Left) - - case OVARLIVE: - Gvarlive(n.Left) - } - -ret: - if Anyregalloc() != wasregalloc { - Dump("node", n) - Fatalf("registers left allocated") - } - - lineno = lno -} - -func Cgen_as(nl, nr *Node) { - Cgen_as_wb(nl, nr, false) -} - -func Cgen_as_wb(nl, nr *Node, wb bool) { - if Debug['g'] != 0 { - op := "cgen_as" - if wb { - op = "cgen_as_wb" - } - Dump(op, nl) - Dump(op+" = ", nr) - } - - for nr != nil && nr.Op == OCONVNOP { - nr = nr.Left - } - - if nl == nil || isblank(nl) { - cgen_discard(nr) - return - } - - if nr == nil || iszero(nr) { - tl := nl.Type - if tl == nil { - return - } - if Isfat(tl) { - if nl.Op == ONAME { - Gvardef(nl) - } - Thearch.Clearfat(nl) - return - } - - Clearslim(nl) - return - } - - tl := nl.Type - if tl == nil { - return - } - - cgen_wb(nr, nl, wb) -} - -func cgen_callmeth(n *Node, proc int) { - // generate a rewrite in n2 for the method call - // (p.f)(...) goes to (f)(p,...) - - l := n.Left - - if l.Op != ODOTMETH { - Fatalf("cgen_callmeth: not dotmethod: %v", l) - } - - n2 := *n - n2.Op = OCALLFUNC - n2.Left = newname(l.Sym) - n2.Left.Type = l.Type - - if n2.Left.Op == ONAME { - n2.Left.Class = PFUNC - } - cgen_call(&n2, proc) -} - -// CgenTemp creates a temporary node, assigns n to it, and returns it. -func CgenTemp(n *Node) *Node { - var tmp Node - Tempname(&tmp, n.Type) - Cgen(n, &tmp) - return &tmp -} - func checklabels() { for _, lab := range labellist { if lab.Def == nil { @@ -1060,252 +351,3 @@ func checklabels() { } } } - -// Componentgen copies a composite value by moving its individual components. -// Slices, strings and interfaces are supported. Small structs or arrays with -// elements of basic type are also supported. -// nr is nil when assigning a zero value. -func Componentgen(nr, nl *Node) bool { - return componentgen_wb(nr, nl, false) -} - -// componentgen_wb is like componentgen but if wb==true emits write barriers for pointer updates. -func componentgen_wb(nr, nl *Node, wb bool) bool { - // Don't generate any code for complete copy of a variable into itself. - // It's useless, and the VARDEF will incorrectly mark the old value as dead. - // (This check assumes that the arguments passed to componentgen did not - // themselves come from Igen, or else we could have Op==ONAME but - // with a Type and Xoffset describing an individual field, not the entire - // variable.) - if nl.Op == ONAME && nl == nr { - return true - } - - // Count number of moves required to move components. - // If using write barrier, can only emit one pointer. - // TODO(rsc): Allow more pointers, for reflect.Value. - const maxMoves = 8 - n := 0 - numPtr := 0 - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - n++ - if Simtype[t.Etype] == Tptr && t != itable { - numPtr++ - } - return n <= maxMoves && (!wb || numPtr <= 1) - }) - if n > maxMoves || wb && numPtr > 1 { - return false - } - - // Must call emitVardef after evaluating rhs but before writing to lhs. - emitVardef := func() { - // Emit vardef if needed. - if nl.Op == ONAME { - switch nl.Type.Etype { - case TARRAY, TSLICE, TSTRING, TINTER, TSTRUCT: - Gvardef(nl) - } - } - } - - isConstString := Isconst(nr, CTSTR) - - if !cadable(nl) && nr != nil && !cadable(nr) && !isConstString { - return false - } - - var nodl Node - if cadable(nl) { - nodl = *nl - } else { - if nr != nil && !cadable(nr) && !isConstString { - return false - } - if nr == nil || isConstString || nl.Ullman >= nr.Ullman { - Igen(nl, &nodl, nil) - defer Regfree(&nodl) - } - } - lbase := nodl.Xoffset - - // Special case: zeroing. - var nodr Node - if nr == nil { - // When zeroing, prepare a register containing zero. - // TODO(rsc): Check that this is actually generating the best code. - if Thearch.REGZERO != 0 { - // cpu has a dedicated zero register - Nodreg(&nodr, Types[TUINT], Thearch.REGZERO) - } else { - // no dedicated zero register - var zero Node - Nodconst(&zero, nl.Type, 0) - Regalloc(&nodr, Types[TUINT], nil) - Thearch.Gmove(&zero, &nodr) - defer Regfree(&nodr) - } - - emitVardef() - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - nodl.Type = t - nodl.Xoffset = lbase + offset - nodr.Type = t - if t.IsFloat() { - // TODO(rsc): Cache zero register like we do for integers? - Clearslim(&nodl) - } else { - Thearch.Gmove(&nodr, &nodl) - } - return true - }) - return true - } - - // Special case: assignment of string constant. - if isConstString { - emitVardef() - - // base - nodl.Type = Ptrto(Types[TUINT8]) - Regalloc(&nodr, Types[Tptr], nil) - p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr) - Datastring(nr.Val().U.(string), &p.From) - p.From.Type = obj.TYPE_ADDR - Thearch.Gmove(&nodr, &nodl) - Regfree(&nodr) - - // length - nodl.Type = Types[Simtype[TUINT]] - nodl.Xoffset += int64(Array_nel) - int64(Array_array) - Nodconst(&nodr, nodl.Type, int64(len(nr.Val().U.(string)))) - Thearch.Gmove(&nodr, &nodl) - return true - } - - // General case: copy nl = nr. - nodr = *nr - if !cadable(nr) { - if nr.Ullman >= UINF && nodl.Op == OINDREG { - Fatalf("miscompile") - } - Igen(nr, &nodr, nil) - defer Regfree(&nodr) - } - rbase := nodr.Xoffset - - if nodl.Op == 0 { - Igen(nl, &nodl, nil) - defer Regfree(&nodl) - lbase = nodl.Xoffset - } - - emitVardef() - var ( - ptrType *Type - ptrOffset int64 - ) - visitComponents(nl.Type, 0, func(t *Type, offset int64) bool { - if wb && Simtype[t.Etype] == Tptr && t != itable { - if ptrType != nil { - Fatalf("componentgen_wb %v", nl.Type) - } - ptrType = t - ptrOffset = offset - return true - } - nodl.Type = t - nodl.Xoffset = lbase + offset - nodr.Type = t - nodr.Xoffset = rbase + offset - Thearch.Gmove(&nodr, &nodl) - return true - }) - if ptrType != nil { - nodl.Type = ptrType - nodl.Xoffset = lbase + ptrOffset - nodr.Type = ptrType - nodr.Xoffset = rbase + ptrOffset - cgen_wbptr(&nodr, &nodl) - } - return true -} - -// visitComponents walks the individual components of the type t, -// walking into array elements, struct fields, the real and imaginary -// parts of complex numbers, and on 32-bit systems the high and -// low halves of 64-bit integers. -// It calls f for each such component, passing the component (aka element) -// type and memory offset, assuming t starts at startOffset. -// If f ever returns false, visitComponents returns false without any more -// calls to f. Otherwise visitComponents returns true. -func visitComponents(t *Type, startOffset int64, f func(elem *Type, elemOffset int64) bool) bool { - switch t.Etype { - case TINT64: - if Widthreg == 8 { - break - } - // NOTE: Assuming little endian (signed top half at offset 4). - // We don't have any 32-bit big-endian systems. - if !Thearch.LinkArch.InFamily(sys.ARM, sys.I386) { - Fatalf("unknown 32-bit architecture") - } - return f(Types[TUINT32], startOffset) && - f(Types[TINT32], startOffset+4) - - case TUINT64: - if Widthreg == 8 { - break - } - return f(Types[TUINT32], startOffset) && - f(Types[TUINT32], startOffset+4) - - case TCOMPLEX64: - return f(Types[TFLOAT32], startOffset) && - f(Types[TFLOAT32], startOffset+4) - - case TCOMPLEX128: - return f(Types[TFLOAT64], startOffset) && - f(Types[TFLOAT64], startOffset+8) - - case TINTER: - return f(itable, startOffset) && - f(Ptrto(Types[TUINT8]), startOffset+int64(Widthptr)) - - case TSTRING: - return f(Ptrto(Types[TUINT8]), startOffset) && - f(Types[Simtype[TUINT]], startOffset+int64(Widthptr)) - - case TSLICE: - return f(Ptrto(t.Elem()), startOffset+int64(Array_array)) && - f(Types[Simtype[TUINT]], startOffset+int64(Array_nel)) && - f(Types[Simtype[TUINT]], startOffset+int64(Array_cap)) - - case TARRAY: - // Short-circuit [1e6]struct{}. - if t.Elem().Width == 0 { - return true - } - - for i := int64(0); i < t.NumElem(); i++ { - if !visitComponents(t.Elem(), startOffset+i*t.Elem().Width, f) { - return false - } - } - return true - - case TSTRUCT: - for _, field := range t.Fields().Slice() { - if !visitComponents(field.Type, startOffset+field.Offset, f) { - return false - } - } - return true - } - return f(t, startOffset) -} - -func cadable(n *Node) bool { - // Note: Not sure why you can have n.Op == ONAME without n.Addable, but you can. - return n.Addable && n.Op == ONAME -} diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 97fa7e2ddd..ecd8b227cd 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -368,68 +368,11 @@ type Arch struct { MAXWIDTH int64 ReservedRegs []int - AddIndex func(*Node, int64, *Node) bool // optional - Betypeinit func() - Bgen_float func(*Node, bool, int, *obj.Prog) // optional - Cgen64 func(*Node, *Node) // only on 32-bit systems - Cgenindex func(*Node, *Node, bool) *obj.Prog - Cgen_bmul func(Op, *Node, *Node, *Node) bool - Cgen_float func(*Node, *Node) // optional - Cgen_hmul func(*Node, *Node, *Node) - RightShiftWithCarry func(*Node, uint, *Node) // only on systems without RROTC instruction - AddSetCarry func(*Node, *Node, *Node) // only on systems when ADD does not update carry flag - Cgen_shift func(Op, bool, *Node, *Node, *Node) - Clearfat func(*Node) - Cmp64 func(*Node, *Node, Op, int, *obj.Prog) // only on 32-bit systems - Defframe func(*obj.Prog) - Dodiv func(Op, *Node, *Node, *Node) - Excise func(*Flow) - Expandchecks func(*obj.Prog) - Getg func(*Node) - Gins func(obj.As, *Node, *Node) *obj.Prog - - // Ginscmp generates code comparing n1 to n2 and jumping away if op is satisfied. - // The returned prog should be Patch'ed with the jump target. - // If op is not satisfied, code falls through to the next emitted instruction. - // Likely is the branch prediction hint: +1 for likely, -1 for unlikely, 0 for no opinion. - // - // Ginscmp must be able to handle all kinds of arguments for n1 and n2, - // not just simple registers, although it can assume that there are no - // function calls needed during the evaluation, and on 32-bit systems - // the values are guaranteed not to be 64-bit values, so no in-memory - // temporaries are necessary. - Ginscmp func(op Op, t *Type, n1, n2 *Node, likely int) *obj.Prog - - // Ginsboolval inserts instructions to convert the result - // of a just-completed comparison to a boolean value. - // The first argument is the conditional jump instruction - // corresponding to the desired value. - // The second argument is the destination. - // If not present, Ginsboolval will be emulated with jumps. - Ginsboolval func(obj.As, *Node) - - Ginscon func(obj.As, int64, *Node) - Ginsnop func() - Gmove func(*Node, *Node) - Igenindex func(*Node, *Node, bool) *obj.Prog - Peep func(*obj.Prog) - Proginfo func(*obj.Prog) // fills in Prog.Info - Regtyp func(*obj.Addr) bool - Sameaddr func(*obj.Addr, *obj.Addr) bool - Smallindir func(*obj.Addr, *obj.Addr) bool - Stackaddr func(*obj.Addr) bool - Blockcopy func(*Node, *Node, int64, int64, int64) - Sudoaddable func(obj.As, *Node, *obj.Addr) bool - Sudoclean func() - Excludedregs func() uint64 - RtoB func(int) uint64 - FtoB func(int) uint64 - BtoR func(uint64) int - BtoF func(uint64) int - Optoas func(Op, *Type) obj.As - Doregbits func(int) uint64 - Regnames func(*int) []string - Use387 bool // should 8g use 387 FP instructions instead of sse2. + Betypeinit func() + Defframe func(*obj.Prog) + Gins func(obj.As, *Node, *Node) *obj.Prog + Proginfo func(*obj.Prog) // fills in Prog.Info + Use387 bool // should 8g use 387 FP instructions instead of sse2. // SSARegToReg maps ssa register numbers to obj register numbers. SSARegToReg []int16 diff --git a/src/cmd/compile/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go index 02d09df5a3..59ac9afd92 100644 --- a/src/cmd/compile/internal/gc/gsubr.go +++ b/src/cmd/compile/internal/gc/gsubr.go @@ -692,42 +692,6 @@ func ginit() { } } -func gclean() { - for _, r := range Thearch.ReservedRegs { - reg[r-Thearch.REGMIN]-- - } - - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - n := reg[r-Thearch.REGMIN] - if n != 0 { - if Debug['v'] != 0 { - Regdump() - } - Yyerror("reg %v left allocated", obj.Rconv(r)) - } - } - - for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { - n := reg[r-Thearch.REGMIN] - if n != 0 { - if Debug['v'] != 0 { - Regdump() - } - Yyerror("reg %v left allocated", obj.Rconv(r)) - } - } -} - -func Anyregalloc() bool { - n := 0 - for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { - if reg[r-Thearch.REGMIN] == 0 { - n++ - } - } - return n > len(Thearch.ReservedRegs) -} - // allocate register of type t, leave in n. // if o != N, o may be reusable register. // caller must Regfree(n). @@ -829,49 +793,6 @@ func Regfree(n *Node) { } } -// Reginuse reports whether r is in use. -func Reginuse(r int) bool { - switch { - case Thearch.REGMIN <= r && r <= Thearch.REGMAX, - Thearch.FREGMIN <= r && r <= Thearch.FREGMAX: - // ok - default: - Fatalf("reginuse: reg out of range") - } - - return reg[r-Thearch.REGMIN] > 0 -} - -// Regrealloc(n) undoes the effect of Regfree(n), -// so that a register can be given up but then reclaimed. -func Regrealloc(n *Node) { - if n.Op != OREGISTER && n.Op != OINDREG { - Fatalf("regrealloc: not a register") - } - i := int(n.Reg) - if i == Thearch.REGSP { - return - } - switch { - case Thearch.REGMIN <= i && i <= Thearch.REGMAX, - Thearch.FREGMIN <= i && i <= Thearch.FREGMAX: - // ok - default: - Fatalf("regrealloc: reg out of range") - } - - i -= Thearch.REGMIN - if reg[i] == 0 && Debug['v'] > 0 { - if regstk[i] == nil { - regstk[i] = make([]byte, 4096) - } - stk := regstk[i] - n := runtime.Stack(stk[:cap(stk)], false) - regstk[i] = stk[:n] - } - reg[i]++ -} - func Regdump() { if Debug['v'] == 0 { fmt.Printf("run compiler with -v for register allocation sites\n") diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 16e62a390b..0fcc78ab71 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -302,34 +302,6 @@ func allocauto(ptxt *obj.Prog) { } } -func Cgen_checknil(n *Node) { - if Disable_checknil != 0 { - return - } - - // Ideally we wouldn't see any integer types here, but we do. - if n.Type == nil || (!n.Type.IsPtr() && !n.Type.IsInteger() && n.Type.Etype != TUNSAFEPTR) { - Dump("checknil", n) - Fatalf("bad checknil") - } - - // Most architectures require that the address to be checked is - // in a register (it could be in memory). - needsReg := !Thearch.LinkArch.InFamily(sys.AMD64, sys.I386) - - // Move the address to be checked into a register if necessary. - if (needsReg && n.Op != OREGISTER) || !n.Addable || n.Op == OLITERAL { - var reg Node - Regalloc(®, Types[Tptr], n) - Cgen(n, ®) - Thearch.Gins(obj.ACHECKNIL, ®, nil) - Regfree(®) - return - } - - Thearch.Gins(obj.ACHECKNIL, n, nil) -} - func compile(fn *Node) { if Newproc == nil { Newproc = Sysfunc("newproc") diff --git a/src/cmd/compile/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go deleted file mode 100644 index 520dcab041..0000000000 --- a/src/cmd/compile/internal/gc/popt.go +++ /dev/null @@ -1,1094 +0,0 @@ -// Derived from Inferno utils/6c/gc.h -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/gc.h -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// "Portable" optimizations. - -package gc - -import ( - "cmd/internal/obj" - "fmt" - "sort" - "strings" -) - -type OptStats struct { - Ncvtreg int32 - Nspill int32 - Nreload int32 - Ndelmov int32 - Nvar int32 - Naddr int32 -} - -var Ostats OptStats - -var noreturn_symlist [10]*Sym - -// p is a call instruction. Does the call fail to return? -func Noreturn(p *obj.Prog) bool { - if noreturn_symlist[0] == nil { - noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg) - noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg) - noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg) - noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg) - noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg) - noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg) - noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg) - noreturn_symlist[7] = Pkglookup("block", Runtimepkg) - } - - if p.To.Node == nil { - return false - } - s := ((p.To.Node).(*Node)).Sym - if s == nil { - return false - } - for i := 0; noreturn_symlist[i] != nil; i++ { - if s == noreturn_symlist[i] { - return true - } - } - return false -} - -// JMP chasing and removal. -// -// The code generator depends on being able to write out jump -// instructions that it can jump to now but fill in later. -// the linker will resolve them nicely, but they make the code -// longer and more difficult to follow during debugging. -// Remove them. - -// what instruction does a JMP to p eventually land on? -func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog { - n := 0 - for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH { - n++ - if n > 10 { - *jmploop = 1 - break - } - - p = p.To.Val.(*obj.Prog) - } - - return p -} - -// reuse reg pointer for mark/sweep state. -// leave reg==nil at end because alive==nil. -var alive interface{} = nil -var dead interface{} = 1 - -// mark all code reachable from firstp as alive -func mark(firstp *obj.Prog) { - for p := firstp; p != nil; p = p.Link { - if p.Opt != dead { - break - } - p.Opt = alive - if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil { - mark(p.To.Val.(*obj.Prog)) - } - if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF { - break - } - } -} - -func fixjmp(firstp *obj.Prog) { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\nfixjmp\n") - } - - // pass 1: resolve jump to jump, mark all code as dead. - jmploop := 0 - - for p := firstp; p != nil; p = p.Link { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v\n", p) - } - if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP { - if Debug['N'] == 0 { - p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("->%v\n", p) - } - } - } - - p.Opt = dead - } - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\n") - } - - // pass 2: mark all reachable code alive - mark(firstp) - - // pass 3: delete dead code (mostly JMPs). - var last *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.Opt == dead { - if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET { - // This is the final ARET, and the code so far doesn't have one. - // Let it stay. The register allocator assumes that all live code in - // the function can be traversed by starting at all the RET instructions - // and following predecessor links. If we remove the final RET, - // this assumption will not hold in the case of an infinite loop - // at the end of a function. - // Keep the RET but mark it dead for the liveness analysis. - p.Mode = 1 - } else { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("del %v\n", p) - } - continue - } - } - - if last != nil { - last.Link = p - } - last = p - } - - last.Link = nil - - // pass 4: elide JMP to next instruction. - // only safe if there are no jumps to JMPs anymore. - if jmploop == 0 && Debug['N'] == 0 { - var last *obj.Prog - for p := firstp; p != nil; p = p.Link { - if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("del %v\n", p) - } - continue - } - - if last != nil { - last.Link = p - } - last = p - } - - last.Link = nil - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\n") - for p := firstp; p != nil; p = p.Link { - fmt.Printf("%v\n", p) - } - fmt.Printf("\n") - } -} - -// Control flow analysis. The Flow structures hold predecessor and successor -// information as well as basic loop analysis. -// -// graph = Flowstart(firstp, nil) -// ... use flow graph ... -// Flowend(graph) // free graph -// -// Typical uses of the flow graph are to iterate over all the flow-relevant instructions: -// -// for f := graph.Start; f != nil; f = f.Link {} -// -// or, given an instruction f, to iterate over all the predecessors, which is -// f.P1 and this list: -// -// for f2 := f.P2; f2 != nil; f2 = f2.P2link {} -// -// The second argument (newData) to Flowstart specifies a func to create object -// for every f.Data field, for use by the client. -// If newData is nil, f.Data will be nil. - -type Graph struct { - Start *Flow - Num int - - // After calling flowrpo, rpo lists the flow nodes in reverse postorder, - // and each non-dead Flow node f has g->rpo[f->rpo] == f. - Rpo []*Flow -} - -type Flow struct { - Prog *obj.Prog // actual instruction - P1 *Flow // predecessors of this instruction: p1, - P2 *Flow // and then p2 linked though p2link. - P2link *Flow - S1 *Flow // successors of this instruction (at most two: s1 and s2). - S2 *Flow - Link *Flow // next instruction in function code - - Active int32 // usable by client - - Id int32 // sequence number in flow graph - Rpo int32 // reverse post ordering - Loop uint16 // x5 for every loop - Refset bool // diagnostic generated - - Data interface{} // for use by client -} - -var flowmark int - -// MaxFlowProg is the maximum size program (counted in instructions) -// for which the flow code will build a graph. Functions larger than this limit -// will not have flow graphs and consequently will not be optimized. -const MaxFlowProg = 50000 - -var ffcache []Flow // reusable []Flow, to reduce allocation - -func growffcache(n int) { - if n > cap(ffcache) { - n = (n * 5) / 4 - if n > MaxFlowProg { - n = MaxFlowProg - } - ffcache = make([]Flow, n) - } - ffcache = ffcache[:n] -} - -func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { - // Count and mark instructions to annotate. - nf := 0 - - for p := firstp; p != nil; p = p.Link { - p.Opt = nil // should be already, but just in case - Thearch.Proginfo(p) - if p.Info.Flags&Skip != 0 { - continue - } - p.Opt = &flowmark - nf++ - } - - if nf == 0 { - return nil - } - - if nf >= MaxFlowProg { - if Debug['v'] != 0 { - Warn("%v is too big (%d instructions)", Curfn.Func.Nname.Sym, nf) - } - return nil - } - - // Allocate annotations and assign to instructions. - graph := new(Graph) - - growffcache(nf) - ff := ffcache - start := &ff[0] - id := 0 - var last *Flow - for p := firstp; p != nil; p = p.Link { - if p.Opt == nil { - continue - } - f := &ff[0] - ff = ff[1:] - p.Opt = f - f.Prog = p - if last != nil { - last.Link = f - } - last = f - if newData != nil { - f.Data = newData() - } - f.Id = int32(id) - id++ - } - - // Fill in pred/succ information. - var f1 *Flow - var p *obj.Prog - for f := start; f != nil; f = f.Link { - p = f.Prog - if p.Info.Flags&Break == 0 { - f1 = f.Link - f.S1 = f1 - f1.P1 = f - } - - if p.To.Type == obj.TYPE_BRANCH { - if p.To.Val == nil { - Fatalf("pnil %v", p) - } - f1 = p.To.Val.(*obj.Prog).Opt.(*Flow) - if f1 == nil { - Fatalf("fnil %v / %v", p, p.To.Val.(*obj.Prog)) - } - if f1 == f { - //fatal("self loop %v", p); - continue - } - - f.S2 = f1 - f.P2link = f1.P2 - f1.P2 = f - } - } - - graph.Start = start - graph.Num = nf - return graph -} - -func Flowend(graph *Graph) { - for f := graph.Start; f != nil; f = f.Link { - f.Prog.Info.Flags = 0 // drop cached proginfo - f.Prog.Opt = nil - } - clear := ffcache[:graph.Num] - for i := range clear { - clear[i] = Flow{} - } -} - -// find looping structure -// -// 1) find reverse postordering -// 2) find approximate dominators, -// the actual dominators if the flow graph is reducible -// otherwise, dominators plus some other non-dominators. -// See Matthew S. Hecht and Jeffrey D. Ullman, -// "Analysis of a Simple Algorithm for Global Data Flow Problems", -// Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, -// Oct. 1-3, 1973, pp. 207-217. -// 3) find all nodes with a predecessor dominated by the current node. -// such a node is a loop head. -// recursively, all preds with a greater rpo number are in the loop -func postorder(r *Flow, rpo2r []*Flow, n int32) int32 { - r.Rpo = 1 - r1 := r.S1 - if r1 != nil && r1.Rpo == 0 { - n = postorder(r1, rpo2r, n) - } - r1 = r.S2 - if r1 != nil && r1.Rpo == 0 { - n = postorder(r1, rpo2r, n) - } - rpo2r[n] = r - n++ - return n -} - -func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 { - if rpo1 == -1 { - return rpo2 - } - var t int32 - for rpo1 != rpo2 { - if rpo1 > rpo2 { - t = rpo2 - rpo2 = rpo1 - rpo1 = t - } - - for rpo1 < rpo2 { - t = idom[rpo2] - if t >= rpo2 { - Fatalf("bad idom") - } - rpo2 = t - } - } - - return rpo1 -} - -func doms(idom []int32, r int32, s int32) bool { - for s > r { - s = idom[s] - } - return s == r -} - -func loophead(idom []int32, r *Flow) bool { - src := r.Rpo - if r.P1 != nil && doms(idom, src, r.P1.Rpo) { - return true - } - for r = r.P2; r != nil; r = r.P2link { - if doms(idom, src, r.Rpo) { - return true - } - } - return false -} - -func loopmark(rpo2r **Flow, head int32, r *Flow) { - if r.Rpo < head || r.Active == head { - return - } - r.Active = head - r.Loop += LOOP - if r.P1 != nil { - loopmark(rpo2r, head, r.P1) - } - for r = r.P2; r != nil; r = r.P2link { - loopmark(rpo2r, head, r) - } -} - -func flowrpo(g *Graph) { - g.Rpo = make([]*Flow, g.Num) - idom := make([]int32, g.Num) - - for r1 := g.Start; r1 != nil; r1 = r1.Link { - r1.Active = 0 - } - - rpo2r := g.Rpo - d := postorder(g.Start, rpo2r, 0) - nr := int32(g.Num) - if d > nr { - Fatalf("too many reg nodes %d %d", d, nr) - } - nr = d - var r1 *Flow - for i := int32(0); i < nr/2; i++ { - r1 = rpo2r[i] - rpo2r[i] = rpo2r[nr-1-i] - rpo2r[nr-1-i] = r1 - } - - for i := int32(0); i < nr; i++ { - rpo2r[i].Rpo = i - } - - idom[0] = 0 - var me int32 - for i := int32(0); i < nr; i++ { - r1 = rpo2r[i] - me = r1.Rpo - d = -1 - - // rpo2r[r.Rpo] == r protects against considering dead code, - // which has r.Rpo == 0. - if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { - d = r1.P1.Rpo - } - for r1 = r1.P2; r1 != nil; r1 = r1.P2link { - if rpo2r[r1.Rpo] == r1 && r1.Rpo < me { - d = rpolca(idom, d, r1.Rpo) - } - } - idom[i] = d - } - - for i := int32(0); i < nr; i++ { - r1 = rpo2r[i] - r1.Loop++ - if r1.P2 != nil && loophead(idom, r1) { - loopmark(&rpo2r[0], i, r1) - } - } - - for r1 := g.Start; r1 != nil; r1 = r1.Link { - r1.Active = 0 - } -} - -func Uniqp(r *Flow) *Flow { - r1 := r.P1 - if r1 == nil { - r1 = r.P2 - if r1 == nil || r1.P2link != nil { - return nil - } - } else if r.P2 != nil { - return nil - } - return r1 -} - -func Uniqs(r *Flow) *Flow { - r1 := r.S1 - if r1 == nil { - r1 = r.S2 - if r1 == nil { - return nil - } - } else if r.S2 != nil { - return nil - } - return r1 -} - -// The compilers assume they can generate temporary variables -// as needed to preserve the right semantics or simplify code -// generation and the back end will still generate good code. -// This results in a large number of ephemeral temporary variables. -// Merge temps with non-overlapping lifetimes and equal types using the -// greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation", -// ACM TOPLAS 1999. - -type TempVar struct { - node *Node - def *Flow // definition of temp var - use *Flow // use list, chained through Flow.data - merge *TempVar // merge var with this one - start int64 // smallest Prog.pc in live range - end int64 // largest Prog.pc in live range - addr bool // address taken - no accurate end - removed bool // removed from program -} - -// startcmp sorts TempVars by start, then id, then symbol name. -type startcmp []*TempVar - -func (x startcmp) Len() int { return len(x) } -func (x startcmp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } -func (x startcmp) Less(i, j int) bool { - a := x[i] - b := x[j] - - if a.start < b.start { - return true - } - if a.start > b.start { - return false - } - - // Order what's left by id or symbol name, - // just so that sort is forced into a specific ordering, - // so that the result of the sort does not depend on - // the sort implementation. - if a.def != b.def { - return int(a.def.Id-b.def.Id) < 0 - } - if a.node != b.node { - return a.node.Sym.Name < b.node.Sym.Name - } - return false -} - -// Is n available for merging? -func canmerge(n *Node) bool { - return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp") -} - -func mergetemp(firstp *obj.Prog) { - const ( - debugmerge = 0 - ) - - g := Flowstart(firstp, nil) - if g == nil { - return - } - - // Build list of all mergeable variables. - var vars []*TempVar - for _, n := range Curfn.Func.Dcl { - if canmerge(n) { - v := &TempVar{} - vars = append(vars, v) - n.SetOpt(v) - v.node = n - } - } - - // Build list of uses. - // We assume that the earliest reference to a temporary is its definition. - // This is not true of variables in general but our temporaries are all - // single-use (that's why we have so many!). - for f := g.Start; f != nil; f = f.Link { - p := f.Prog - if p.From.Node != nil && ((p.From.Node).(*Node)).Opt() != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt() != nil { - Fatalf("double node %v", p) - } - var v *TempVar - n, _ := p.From.Node.(*Node) - if n != nil { - v, _ = n.Opt().(*TempVar) - } - if v == nil { - n, _ = p.To.Node.(*Node) - if n != nil { - v, _ = n.Opt().(*TempVar) - } - } - if v != nil { - if v.def == nil { - v.def = f - } - f.Data = v.use - v.use = f - if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) { - v.addr = true - } - } - } - - if debugmerge > 1 && Debug['v'] != 0 { - Dumpit("before", g.Start, 0) - } - - nkill := 0 - - // Special case. - for _, v := range vars { - if v.addr { - continue - } - - // Used in only one instruction, which had better be a write. - f := v.use - if f != nil && f.Data.(*Flow) == nil { - p := f.Prog - if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 { - p.As = obj.ANOP - p.To = obj.Addr{} - v.removed = true - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("drop write-only %v\n", v.node.Sym) - } - } else { - Fatalf("temp used and not set: %v", p) - } - nkill++ - continue - } - - // Written in one instruction, read in the next, otherwise unused, - // no jumps to the next instruction. Happens mainly in 386 compiler. - f = v.use - if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f { - p := f.Prog - p1 := f.Link.Prog - const ( - SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD - ) - if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny { - p1.From = p.From - Thearch.Excise(f) - v.removed = true - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("drop immediate-use %v\n", v.node.Sym) - } - } - - nkill++ - continue - } - } - - // Traverse live range of each variable to set start, end. - // Each flood uses a new value of gen so that we don't have - // to clear all the r.Active words after each variable. - gen := uint32(0) - - for _, v := range vars { - gen++ - for f := v.use; f != nil; f = f.Data.(*Flow) { - mergewalk(v, f, gen) - } - if v.addr { - gen++ - for f := v.use; f != nil; f = f.Data.(*Flow) { - varkillwalk(v, f, gen) - } - } - } - - // Sort variables by start. - bystart := make([]*TempVar, len(vars)) - copy(bystart, vars) - sort.Sort(startcmp(bystart)) - - // List of in-use variables, sorted by end, so that the ones that - // will last the longest are the earliest ones in the array. - // The tail inuse[nfree:] holds no-longer-used variables. - // In theory we should use a sorted tree so that insertions are - // guaranteed O(log n) and then the loop is guaranteed O(n log n). - // In practice, it doesn't really matter. - inuse := make([]*TempVar, len(bystart)) - - ninuse := 0 - nfree := len(bystart) - for _, v := range bystart { - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %#v: removed=%t\n", v.node, v.removed) - } - - if v.removed { - continue - } - - // Expire no longer in use. - for ninuse > 0 && inuse[ninuse-1].end < v.start { - ninuse-- - nfree-- - inuse[nfree] = inuse[ninuse] - } - - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %#v: removed=%t nfree=%d nvar=%d\n", v.node, v.removed, nfree, len(bystart)) - } - - // Find old temp to reuse if possible. - t := v.node.Type - - for j := nfree; j < len(inuse); j++ { - v1 := inuse[j] - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("consider %#v: maybe %#v: type=%v,%v addrtaken=%v,%v\n", v.node, v1.node, t, v1.node.Type, v.node.Addrtaken, v1.node.Addrtaken) - } - - // Require the types to match but also require the addrtaken bits to match. - // If a variable's address is taken, that disables registerization for the individual - // words of the variable (for example, the base,len,cap of a slice). - // We don't want to merge a non-addressed var with an addressed one and - // inhibit registerization of the former. - if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken { - inuse[j] = inuse[nfree] - nfree++ - if v1.merge != nil { - v.merge = v1.merge - } else { - v.merge = v1 - } - nkill++ - break - } - } - - // Sort v into inuse. - j := ninuse - ninuse++ - - for j > 0 && inuse[j-1].end < v.end { - inuse[j] = inuse[j-1] - j-- - } - - inuse[j] = v - } - - if debugmerge > 0 && Debug['v'] != 0 { - fmt.Printf("%v [%d - %d]\n", Curfn.Func.Nname.Sym, len(vars), nkill) - for _, v := range vars { - fmt.Printf("var %#v %v %d-%d", v.node, v.node.Type, v.start, v.end) - if v.addr { - fmt.Printf(" addr=true") - } - if v.removed { - fmt.Printf(" removed=true") - } - if v.merge != nil { - fmt.Printf(" merge %#v", v.merge.node) - } - if v.start == v.end && v.def != nil { - fmt.Printf(" %v", v.def.Prog) - } - fmt.Printf("\n") - } - - if debugmerge > 1 && Debug['v'] != 0 { - Dumpit("after", g.Start, 0) - } - } - - // Update node references to use merged temporaries. - for f := g.Start; f != nil; f = f.Link { - p := f.Prog - n, _ := p.From.Node.(*Node) - if n != nil { - v, _ := n.Opt().(*TempVar) - if v != nil && v.merge != nil { - p.From.Node = v.merge.node - } - } - n, _ = p.To.Node.(*Node) - if n != nil { - v, _ := n.Opt().(*TempVar) - if v != nil && v.merge != nil { - p.To.Node = v.merge.node - } - } - } - - // Delete merged nodes from declaration list. - dcl := make([]*Node, 0, len(Curfn.Func.Dcl)-nkill) - for _, n := range Curfn.Func.Dcl { - v, _ := n.Opt().(*TempVar) - if v != nil && (v.merge != nil || v.removed) { - continue - } - dcl = append(dcl, n) - } - Curfn.Func.Dcl = dcl - - // Clear aux structures. - for _, v := range vars { - v.node.SetOpt(nil) - } - - Flowend(g) -} - -func mergewalk(v *TempVar, f0 *Flow, gen uint32) { - var p *obj.Prog - var f1 *Flow - - for f1 = f0; f1 != nil; f1 = f1.P1 { - if uint32(f1.Active) == gen { - break - } - f1.Active = int32(gen) - p = f1.Prog - if v.end < p.Pc { - v.end = p.Pc - } - if f1 == v.def { - v.start = p.Pc - break - } - } - - var f2 *Flow - for f := f0; f != f1; f = f.P1 { - for f2 = f.P2; f2 != nil; f2 = f2.P2link { - mergewalk(v, f2, gen) - } - } -} - -func varkillwalk(v *TempVar, f0 *Flow, gen uint32) { - var p *obj.Prog - var f1 *Flow - - for f1 = f0; f1 != nil; f1 = f1.S1 { - if uint32(f1.Active) == gen { - break - } - f1.Active = int32(gen) - p = f1.Prog - if v.end < p.Pc { - v.end = p.Pc - } - if v.start > p.Pc { - v.start = p.Pc - } - if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) { - break - } - } - - for f := f0; f != f1; f = f.S1 { - varkillwalk(v, f.S2, gen) - } -} - -// Eliminate redundant nil pointer checks. -// -// The code generation pass emits a CHECKNIL for every possibly nil pointer. -// This pass removes a CHECKNIL if every predecessor path has already -// checked this value for nil. -// -// Simple backwards flood from check to definition. -// Run prog loop backward from end of program to beginning to avoid quadratic -// behavior removing a run of checks. -// -// Assume that stack variables with address not taken can be loaded multiple times -// from memory without being rechecked. Other variables need to be checked on -// each load. - -var killed int // f.Data is either nil or &killed - -func nilopt(firstp *obj.Prog) { - g := Flowstart(firstp, nil) - if g == nil { - return - } - - if Debug_checknil > 1 { // || strcmp(curfn->nname->sym->name, "f1") == 0 - Dumpit("nilopt", g.Start, 0) - } - - ncheck := 0 - nkill := 0 - var p *obj.Prog - for f := g.Start; f != nil; f = f.Link { - p = f.Prog - if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) { - continue - } - ncheck++ - if Thearch.Stackaddr(&p.From) { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(p.Lineno, "removed nil check of SP address") - } - f.Data = &killed - continue - } - - nilwalkfwd(f) - if f.Data != nil { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(p.Lineno, "removed nil check before indirect") - } - continue - } - - nilwalkback(f) - if f.Data != nil { - if Debug_checknil != 0 && p.Lineno > 1 { - Warnl(p.Lineno, "removed repeated nil check") - } - continue - } - } - - for f := g.Start; f != nil; f = f.Link { - if f.Data != nil { - nkill++ - Thearch.Excise(f) - } - } - - Flowend(g) - - if Debug_checknil > 1 { - fmt.Printf("%v: removed %d of %d nil checks\n", Curfn.Func.Nname.Sym, nkill, ncheck) - } -} - -func nilwalkback(fcheck *Flow) { - for f := fcheck; f != nil; f = Uniqp(f) { - p := f.Prog - if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { - // Found initialization of value we're checking for nil. - // without first finding the check, so this one is unchecked. - return - } - - if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - } -} - -// Here is a more complex version that scans backward across branches. -// It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason -// to keep the check (setting fcheck->kill = 0). -// It doesn't handle copying of aggregates as well as I would like, -// nor variables with their address taken, -// and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. -/* -for(f1 = f0; f1 != nil; f1 = f1->p1) { - if(f1->active == gen) - break; - f1->active = gen; - p = f1->prog; - - // If same check, stop this loop but still check - // alternate predecessors up to this point. - if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) - break; - - if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { - // Found initialization of value we're checking for nil. - // without first finding the check, so this one is unchecked. - fcheck->kill = 0; - return; - } - - if(f1->p1 == nil && f1->p2 == nil) { - print("lost pred for %v\n", fcheck->prog); - for(f1=f0; f1!=nil; f1=f1->p1) { - thearch.proginfo(&info, f1->prog); - print("\t%v %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from); - } - fatal("lost pred trail"); - } -} - -for(f = f0; f != f1; f = f->p1) - for(f2 = f->p2; f2 != nil; f2 = f2->p2link) - nilwalkback(fcheck, f2, gen); -*/ - -func nilwalkfwd(fcheck *Flow) { - // If the path down from rcheck dereferences the address - // (possibly with a small offset) before writing to memory - // and before any subsequent checks, it's okay to wait for - // that implicit check. Only consider this basic block to - // avoid problems like: - // _ = *x // should panic - // for {} // no writes but infinite loop may be considered visible - - var last *Flow - for f := Uniqs(fcheck); f != nil; f = Uniqs(f) { - p := f.Prog - if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - - if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) { - fcheck.Data = &killed - return - } - - // Stop if another nil check happens. - if p.As == obj.ACHECKNIL { - return - } - - // Stop if value is lost. - if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { - return - } - - // Stop if memory write. - if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) { - return - } - - // Stop if we jump backward. - if last != nil && f.Id <= last.Id { - return - } - last = f - } -} diff --git a/src/cmd/compile/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go deleted file mode 100644 index 6ccb99f33d..0000000000 --- a/src/cmd/compile/internal/gc/reg.go +++ /dev/null @@ -1,1532 +0,0 @@ -// Derived from Inferno utils/6c/reg.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/reg.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package gc - -import ( - "bytes" - "cmd/internal/obj" - "cmd/internal/sys" - "fmt" - "sort" - "strings" -) - -// A Var represents a single variable that may be stored in a register. -// That variable may itself correspond to a hardware register, -// to represent the use of registers in the unoptimized instruction stream. -type Var struct { - offset int64 - node *Node - nextinnode *Var - width int - id int // index in vars - name int8 - etype EType - addr int8 -} - -// Bits represents a set of Vars, stored as a bit set of var numbers -// (the index in vars, or equivalently v.id). -type Bits struct { - b [BITS]uint64 -} - -const ( - BITS = 3 - NVAR = BITS * 64 -) - -var ( - vars [NVAR]Var // variables under consideration - nvar int // number of vars - - regbits uint64 // bits for hardware registers - - zbits Bits // zero - externs Bits // global variables - params Bits // function parameters and results - ivar Bits // function parameters (inputs) - ovar Bits // function results (outputs) - consts Bits // constant values - addrs Bits // variables with address taken -) - -// A Reg is a wrapper around a single Prog (one instruction) that holds -// register optimization information while the optimizer runs. -// r->prog is the instruction. -type Reg struct { - set Bits // regopt variables written by this instruction. - use1 Bits // regopt variables read by prog->from. - use2 Bits // regopt variables read by prog->to. - - // refahead/refbehind are the regopt variables whose current - // value may be used in the following/preceding instructions - // up to a CALL (or the value is clobbered). - refbehind Bits - refahead Bits - - // calahead/calbehind are similar, but for variables in - // instructions that are reachable after hitting at least one - // CALL. - calbehind Bits - calahead Bits - - regdiff Bits - act Bits - regu uint64 // register used bitmap -} - -// A Rgn represents a single regopt variable over a region of code -// where a register could potentially be dedicated to that variable. -// The code encompassed by a Rgn is defined by the flow graph, -// starting at enter, flood-filling forward while varno is refahead -// and backward while varno is refbehind, and following branches. -// A single variable may be represented by multiple disjoint Rgns and -// each Rgn may choose a different register for that variable. -// Registers are allocated to regions greedily in order of descending -// cost. -type Rgn struct { - enter *Flow - cost int16 - varno int16 - regno int16 -} - -// The Plan 9 C compilers used a limit of 600 regions, -// but the yacc-generated parser in y.go has 3100 regions. -// We set MaxRgn large enough to handle that. -// There's not a huge cost to having too many regions: -// the main processing traces the live area for each variable, -// which is limited by the number of variables times the area, -// not the raw region count. If there are many regions, they -// are almost certainly small and easy to trace. -// The only operation that scales with region count is the -// sorting by cost, which uses sort.Sort and is therefore -// guaranteed n log n. -const MaxRgn = 6000 - -var ( - region []Rgn - nregion int -) - -type rcmp []Rgn - -func (x rcmp) Len() int { - return len(x) -} - -func (x rcmp) Swap(i, j int) { - x[i], x[j] = x[j], x[i] -} - -func (x rcmp) Less(i, j int) bool { - p1 := &x[i] - p2 := &x[j] - if p1.cost != p2.cost { - return int(p2.cost)-int(p1.cost) < 0 - } - if p1.varno != p2.varno { - return int(p2.varno)-int(p1.varno) < 0 - } - if p1.enter != p2.enter { - return int(p2.enter.Id-p1.enter.Id) < 0 - } - return false -} - -func setaddrs(bit Bits) { - var i int - var n int - var v *Var - var node *Node - - for bany(&bit) { - // convert each bit to a variable - i = bnum(&bit) - - node = vars[i].node - n = int(vars[i].name) - biclr(&bit, uint(i)) - - // disable all pieces of that variable - for i = 0; i < nvar; i++ { - v = &vars[i] - if v.node == node && int(v.name) == n { - v.addr = 2 - } - } - } -} - -var regnodes [64]*Node - -func walkvardef(n *Node, f *Flow, active int) { - var f1 *Flow - var bn int - var v *Var - - for f1 = f; f1 != nil; f1 = f1.S1 { - if f1.Active == int32(active) { - break - } - f1.Active = int32(active) - if f1.Prog.As == obj.AVARKILL && f1.Prog.To.Node == n { - break - } - for v, _ = n.Opt().(*Var); v != nil; v = v.nextinnode { - bn = v.id - biset(&(f1.Data.(*Reg)).act, uint(bn)) - } - - if f1.Prog.As == obj.ACALL { - break - } - } - - for f2 := f; f2 != f1; f2 = f2.S1 { - if f2.S2 != nil { - walkvardef(n, f2.S2, active) - } - } -} - -// add mov b,rn -// just after r -func addmove(r *Flow, bn int, rn int, f int) { - p1 := Ctxt.NewProg() - Clearp(p1) - p1.Pc = 9999 - - p := r.Prog - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - - v := &vars[bn] - - a := &p1.To - a.Offset = v.offset - a.Etype = uint8(v.etype) - a.Type = obj.TYPE_MEM - a.Name = v.name - a.Node = v.node - a.Sym = Linksym(v.node.Sym) - - /* NOTE(rsc): 9g did - if(a->etype == TARRAY) - a->type = TYPE_ADDR; - else if(a->sym == nil) - a->type = TYPE_CONST; - */ - p1.As = Thearch.Optoas(OAS, Types[uint8(v.etype)]) - - // TODO(rsc): Remove special case here. - if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) && v.etype == TBOOL { - p1.As = Thearch.Optoas(OAS, Types[TUINT8]) - } - p1.From.Type = obj.TYPE_REG - p1.From.Reg = int16(rn) - p1.From.Name = obj.NAME_NONE - if f == 0 { - p1.From = *a - *a = obj.Addr{} - a.Type = obj.TYPE_REG - a.Reg = int16(rn) - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v ===add=== %v\n", p, p1) - } - Ostats.Nspill++ -} - -func overlap_reg(o1 int64, w1 int, o2 int64, w2 int) bool { - t1 := o1 + int64(w1) - t2 := o2 + int64(w2) - - if t1 <= o2 || t2 <= o1 { - return false - } - - return true -} - -func mkvar(f *Flow, a *obj.Addr) Bits { - // mark registers used - if a.Type == obj.TYPE_NONE { - return zbits - } - - r := f.Data.(*Reg) - r.use1.b[0] |= Thearch.Doregbits(int(a.Index)) // TODO: Use RtoB - - var n int - switch a.Type { - default: - regu := Thearch.Doregbits(int(a.Reg)) | Thearch.RtoB(int(a.Reg)) // TODO: Use RtoB - if regu == 0 { - return zbits - } - bit := zbits - bit.b[0] = regu - return bit - - // TODO(rsc): Remove special case here. - case obj.TYPE_ADDR: - var bit Bits - if Thearch.LinkArch.InFamily(sys.MIPS64, sys.ARM, sys.ARM64, sys.PPC64) { - goto memcase - } - a.Type = obj.TYPE_MEM - bit = mkvar(f, a) - setaddrs(bit) - a.Type = obj.TYPE_ADDR - Ostats.Naddr++ - return zbits - - memcase: - fallthrough - - case obj.TYPE_MEM: - if r != nil { - r.use1.b[0] |= Thearch.RtoB(int(a.Reg)) - } - - /* NOTE: 5g did - if(r->f.prog->scond & (C_PBIT|C_WBIT)) - r->set.b[0] |= RtoB(a->reg); - */ - switch a.Name { - default: - // Note: This case handles NAME_EXTERN and NAME_STATIC. - // We treat these as requiring eager writes to memory, due to - // the possibility of a fault handler looking at them, so there is - // not much point in registerizing the loads. - // If we later choose the set of candidate variables from a - // larger list, these cases could be deprioritized instead of - // removed entirely. - return zbits - - case obj.NAME_PARAM, - obj.NAME_AUTO: - n = int(a.Name) - } - } - - node, _ := a.Node.(*Node) - if node == nil || node.Op != ONAME || node.Orig == nil { - return zbits - } - node = node.Orig - if node.Orig != node { - Fatalf("%v: bad node", Ctxt.Dconv(a)) - } - if node.Sym == nil || node.Sym.Name[0] == '.' { - return zbits - } - et := EType(a.Etype) - o := a.Offset - w := a.Width - if w < 0 { - Fatalf("bad width %d for %v", w, Ctxt.Dconv(a)) - } - - flag := 0 - var v *Var - for i := 0; i < nvar; i++ { - v = &vars[i] - if v.node == node && int(v.name) == n { - if v.offset == o { - if v.etype == et { - if int64(v.width) == w { - // TODO(rsc): Remove special case for arm here. - if flag == 0 || Thearch.LinkArch.Family != sys.ARM { - return blsh(uint(i)) - } - } - } - } - - // if they overlap, disable both - if overlap_reg(v.offset, v.width, o, int(w)) { - // print("disable overlap %s %d %d %d %d, %E != %E\n", s->name, v->offset, v->width, o, w, v->etype, et); - v.addr = 1 - - flag = 1 - } - } - } - - switch et { - case 0, TFUNC: - return zbits - } - - if nvar >= NVAR { - if Debug['w'] > 1 && node != nil { - Fatalf("variable not optimized: %#v", node) - } - if Debug['v'] > 0 { - Warn("variable not optimized: %#v", node) - } - - // If we're not tracking a word in a variable, mark the rest as - // having its address taken, so that we keep the whole thing - // live at all calls. otherwise we might optimize away part of - // a variable but not all of it. - var v *Var - for i := 0; i < nvar; i++ { - v = &vars[i] - if v.node == node { - v.addr = 1 - } - } - - return zbits - } - - i := nvar - nvar++ - v = &vars[i] - v.id = i - v.offset = o - v.name = int8(n) - v.etype = et - v.width = int(w) - v.addr = int8(flag) // funny punning - v.node = node - - // node->opt is the head of a linked list - // of Vars within the given Node, so that - // we can start at a Var and find all the other - // Vars in the same Go variable. - v.nextinnode, _ = node.Opt().(*Var) - - node.SetOpt(v) - - bit := blsh(uint(i)) - if n == obj.NAME_EXTERN || n == obj.NAME_STATIC { - for z := 0; z < BITS; z++ { - externs.b[z] |= bit.b[z] - } - } - if n == obj.NAME_PARAM { - for z := 0; z < BITS; z++ { - params.b[z] |= bit.b[z] - } - } - - if node.Class == PPARAM { - for z := 0; z < BITS; z++ { - ivar.b[z] |= bit.b[z] - } - } - if node.Class == PPARAMOUT { - for z := 0; z < BITS; z++ { - ovar.b[z] |= bit.b[z] - } - } - - // Treat values with their address taken as live at calls, - // because the garbage collector's liveness analysis in plive.go does. - // These must be consistent or else we will elide stores and the garbage - // collector will see uninitialized data. - // The typical case where our own analysis is out of sync is when the - // node appears to have its address taken but that code doesn't actually - // get generated and therefore doesn't show up as an address being - // taken when we analyze the instruction stream. - // One instance of this case is when a closure uses the same name as - // an outer variable for one of its own variables declared with :=. - // The parser flags the outer variable as possibly shared, and therefore - // sets addrtaken, even though it ends up not being actually shared. - // If we were better about _ elision, _ = &x would suffice too. - // The broader := in a closure problem is mentioned in a comment in - // closure.go:/^typecheckclosure and dcl.go:/^oldname. - if node.Addrtaken { - v.addr = 1 - } - - // Disable registerization for globals, because: - // (1) we might panic at any time and we want the recovery code - // to see the latest values (issue 1304). - // (2) we don't know what pointers might point at them and we want - // loads via those pointers to see updated values and vice versa (issue 7995). - // - // Disable registerization for results if using defer, because the deferred func - // might recover and return, causing the current values to be used. - if node.Class == PEXTERN || (hasdefer && node.Class == PPARAMOUT) { - v.addr = 1 - } - - if Debug['R'] != 0 { - fmt.Printf("bit=%2d et=%v w=%d+%d %#v %v flag=%d\n", i, et, o, w, node, Ctxt.Dconv(a), v.addr) - } - Ostats.Nvar++ - - return bit -} - -var change int - -func prop(f *Flow, ref Bits, cal Bits) { - var f1 *Flow - var r1 *Reg - var z int - var i int - var v *Var - var v1 *Var - - for f1 = f; f1 != nil; f1 = f1.P1 { - r1 = f1.Data.(*Reg) - for z = 0; z < BITS; z++ { - ref.b[z] |= r1.refahead.b[z] - if ref.b[z] != r1.refahead.b[z] { - r1.refahead.b[z] = ref.b[z] - change = 1 - } - - cal.b[z] |= r1.calahead.b[z] - if cal.b[z] != r1.calahead.b[z] { - r1.calahead.b[z] = cal.b[z] - change = 1 - } - } - - switch f1.Prog.As { - case obj.ACALL: - if Noreturn(f1.Prog) { - break - } - - // Mark all input variables (ivar) as used, because that's what the - // liveness bitmaps say. The liveness bitmaps say that so that a - // panic will not show stale values in the parameter dump. - // Mark variables with a recent VARDEF (r1->act) as used, - // so that the optimizer flushes initializations to memory, - // so that if a garbage collection happens during this CALL, - // the collector will see initialized memory. Again this is to - // match what the liveness bitmaps say. - for z = 0; z < BITS; z++ { - cal.b[z] |= ref.b[z] | externs.b[z] | ivar.b[z] | r1.act.b[z] - ref.b[z] = 0 - } - - // cal.b is the current approximation of what's live across the call. - // Every bit in cal.b is a single stack word. For each such word, - // find all the other tracked stack words in the same Go variable - // (struct/slice/string/interface) and mark them live too. - // This is necessary because the liveness analysis for the garbage - // collector works at variable granularity, not at word granularity. - // It is fundamental for slice/string/interface: the garbage collector - // needs the whole value, not just some of the words, in order to - // interpret the other bits correctly. Specifically, slice needs a consistent - // ptr and cap, string needs a consistent ptr and len, and interface - // needs a consistent type word and data word. - for z = 0; z < BITS; z++ { - if cal.b[z] == 0 { - continue - } - for i = 0; i < 64; i++ { - if z*64+i >= nvar || (cal.b[z]>>uint(i))&1 == 0 { - continue - } - v = &vars[z*64+i] - if v.node.Opt() == nil { // v represents fixed register, not Go variable - continue - } - - // v->node->opt is the head of a linked list of Vars - // corresponding to tracked words from the Go variable v->node. - // Walk the list and set all the bits. - // For a large struct this could end up being quadratic: - // after the first setting, the outer loop (for z, i) would see a 1 bit - // for all of the remaining words in the struct, and for each such - // word would go through and turn on all the bits again. - // To avoid the quadratic behavior, we only turn on the bits if - // v is the head of the list or if the head's bit is not yet turned on. - // This will set the bits at most twice, keeping the overall loop linear. - v1, _ = v.node.Opt().(*Var) - - if v == v1 || !btest(&cal, uint(v1.id)) { - for ; v1 != nil; v1 = v1.nextinnode { - biset(&cal, uint(v1.id)) - } - } - } - } - - case obj.ATEXT: - for z = 0; z < BITS; z++ { - cal.b[z] = 0 - ref.b[z] = 0 - } - - case obj.ARET: - for z = 0; z < BITS; z++ { - cal.b[z] = externs.b[z] | ovar.b[z] - ref.b[z] = 0 - } - } - - for z = 0; z < BITS; z++ { - ref.b[z] = ref.b[z]&^r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z] - cal.b[z] &^= (r1.set.b[z] | r1.use1.b[z] | r1.use2.b[z]) - r1.refbehind.b[z] = ref.b[z] - r1.calbehind.b[z] = cal.b[z] - } - - if f1.Active != 0 { - break - } - f1.Active = 1 - } - - var r *Reg - var f2 *Flow - for ; f != f1; f = f.P1 { - r = f.Data.(*Reg) - for f2 = f.P2; f2 != nil; f2 = f2.P2link { - prop(f2, r.refbehind, r.calbehind) - } - } -} - -func synch(f *Flow, dif Bits) { - var r1 *Reg - var z int - - for f1 := f; f1 != nil; f1 = f1.S1 { - r1 = f1.Data.(*Reg) - for z = 0; z < BITS; z++ { - dif.b[z] = dif.b[z]&^(^r1.refbehind.b[z]&r1.refahead.b[z]) | r1.set.b[z] | r1.regdiff.b[z] - if dif.b[z] != r1.regdiff.b[z] { - r1.regdiff.b[z] = dif.b[z] - change = 1 - } - } - - if f1.Active != 0 { - break - } - f1.Active = 1 - for z = 0; z < BITS; z++ { - dif.b[z] &^= (^r1.calbehind.b[z] & r1.calahead.b[z]) - } - if f1.S2 != nil { - synch(f1.S2, dif) - } - } -} - -func allreg(b uint64, r *Rgn) uint64 { - v := &vars[r.varno] - r.regno = 0 - switch v.etype { - default: - Fatalf("unknown etype %d/%v", Bitno(b), v.etype) - - case TINT8, - TUINT8, - TINT16, - TUINT16, - TINT32, - TUINT32, - TINT64, - TUINT64, - TINT, - TUINT, - TUINTPTR, - TBOOL, - TPTR32, - TPTR64: - i := Thearch.BtoR(^b) - if i != 0 && r.cost > 0 { - r.regno = int16(i) - return Thearch.RtoB(i) - } - - case TFLOAT32, TFLOAT64: - i := Thearch.BtoF(^b) - if i != 0 && r.cost > 0 { - r.regno = int16(i) - return Thearch.FtoB(i) - } - } - - return 0 -} - -func LOAD(r *Reg, z int) uint64 { - return ^r.refbehind.b[z] & r.refahead.b[z] -} - -func STORE(r *Reg, z int) uint64 { - return ^r.calbehind.b[z] & r.calahead.b[z] -} - -// Cost parameters -const ( - CLOAD = 5 // cost of load - CREF = 5 // cost of reference if not registerized - LOOP = 3 // loop execution count (applied in popt.go) -) - -func paint1(f *Flow, bn int) { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - r := f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - return - } - var f1 *Flow - var r1 *Reg - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb != 0 { - break - } - f = f1 - r = r1 - } - - if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { - change -= CLOAD * int(f.Loop) - } - - for { - r.act.b[z] |= bb - - if f.Prog.As != obj.ANOP { // don't give credit for NOPs - if r.use1.b[z]&bb != 0 { - change += CREF * int(f.Loop) - } - if (r.use2.b[z]|r.set.b[z])&bb != 0 { - change += CREF * int(f.Loop) - } - } - - if STORE(r, z)&r.regdiff.b[z]&bb != 0 { - change -= CLOAD * int(f.Loop) - } - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - paint1(f1, bn) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - paint1(f1, bn) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } -} - -func paint2(f *Flow, bn int, depth int) uint64 { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - vreg := regbits - r := f.Data.(*Reg) - if r.act.b[z]&bb == 0 { - return vreg - } - var r1 *Reg - var f1 *Flow - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb == 0 { - break - } - f = f1 - r = r1 - } - - for { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" paint2 %d %v\n", depth, f.Prog) - } - - r.act.b[z] &^= bb - - vreg |= r.regu - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - vreg |= paint2(f1, bn, depth+1) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - vreg |= paint2(f1, bn, depth+1) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb == 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } - - return vreg -} - -func paint3(f *Flow, bn int, rb uint64, rn int) { - z := bn / 64 - bb := uint64(1 << uint(bn%64)) - r := f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - return - } - var r1 *Reg - var f1 *Flow - for { - if r.refbehind.b[z]&bb == 0 { - break - } - f1 = f.P1 - if f1 == nil { - break - } - r1 = f1.Data.(*Reg) - if r1.refahead.b[z]&bb == 0 { - break - } - if r1.act.b[z]&bb != 0 { - break - } - f = f1 - r = r1 - } - - if LOAD(r, z)&^(r.set.b[z]&^(r.use1.b[z]|r.use2.b[z]))&bb != 0 { - addmove(f, bn, rn, 0) - } - var p *obj.Prog - for { - r.act.b[z] |= bb - p = f.Prog - - if r.use1.b[z]&bb != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v", p) - } - addreg(&p.From, rn) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" ===change== %v\n", p) - } - } - - if (r.use2.b[z]|r.set.b[z])&bb != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("%v", p) - } - addreg(&p.To, rn) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf(" ===change== %v\n", p) - } - } - - if STORE(r, z)&r.regdiff.b[z]&bb != 0 { - addmove(f, bn, rn, 1) - } - r.regu |= rb - - if r.refbehind.b[z]&bb != 0 { - for f1 = f.P2; f1 != nil; f1 = f1.P2link { - if (f1.Data.(*Reg)).refahead.b[z]&bb != 0 { - paint3(f1, bn, rb, rn) - } - } - } - - if r.refahead.b[z]&bb == 0 { - break - } - f1 = f.S2 - if f1 != nil { - if (f1.Data.(*Reg)).refbehind.b[z]&bb != 0 { - paint3(f1, bn, rb, rn) - } - } - f = f.S1 - if f == nil { - break - } - r = f.Data.(*Reg) - if r.act.b[z]&bb != 0 { - break - } - if r.refbehind.b[z]&bb == 0 { - break - } - } -} - -func addreg(a *obj.Addr, rn int) { - a.Sym = nil - a.Node = nil - a.Offset = 0 - a.Type = obj.TYPE_REG - a.Reg = int16(rn) - a.Name = 0 - - Ostats.Ncvtreg++ -} - -func dumpone(f *Flow, isreg int) { - fmt.Printf("%d:%v", f.Loop, f.Prog) - if isreg != 0 { - r := f.Data.(*Reg) - var bit Bits - for z := 0; z < BITS; z++ { - bit.b[z] = r.set.b[z] | r.use1.b[z] | r.use2.b[z] | r.refbehind.b[z] | r.refahead.b[z] | r.calbehind.b[z] | r.calahead.b[z] | r.regdiff.b[z] | r.act.b[z] | 0 - } - if bany(&bit) { - fmt.Printf("\t") - if bany(&r.set) { - fmt.Printf(" s:%v", &r.set) - } - if bany(&r.use1) { - fmt.Printf(" u1:%v", &r.use1) - } - if bany(&r.use2) { - fmt.Printf(" u2:%v", &r.use2) - } - if bany(&r.refbehind) { - fmt.Printf(" rb:%v ", &r.refbehind) - } - if bany(&r.refahead) { - fmt.Printf(" ra:%v ", &r.refahead) - } - if bany(&r.calbehind) { - fmt.Printf(" cb:%v ", &r.calbehind) - } - if bany(&r.calahead) { - fmt.Printf(" ca:%v ", &r.calahead) - } - if bany(&r.regdiff) { - fmt.Printf(" d:%v ", &r.regdiff) - } - if bany(&r.act) { - fmt.Printf(" a:%v ", &r.act) - } - } - } - - fmt.Printf("\n") -} - -func Dumpit(str string, r0 *Flow, isreg int) { - var r1 *Flow - - fmt.Printf("\n%s\n", str) - for r := r0; r != nil; r = r.Link { - dumpone(r, isreg) - r1 = r.P2 - if r1 != nil { - fmt.Printf("\tpred:") - for ; r1 != nil; r1 = r1.P2link { - fmt.Printf(" %.4d", uint(int(r1.Prog.Pc))) - } - if r.P1 != nil { - fmt.Printf(" (and %.4d)", uint(int(r.P1.Prog.Pc))) - } else { - fmt.Printf(" (only)") - } - fmt.Printf("\n") - } - - // Print successors if it's not just the next one - if r.S1 != r.Link || r.S2 != nil { - fmt.Printf("\tsucc:") - if r.S1 != nil { - fmt.Printf(" %.4d", uint(int(r.S1.Prog.Pc))) - } - if r.S2 != nil { - fmt.Printf(" %.4d", uint(int(r.S2.Prog.Pc))) - } - fmt.Printf("\n") - } - } -} - -func regopt(firstp *obj.Prog) { - mergetemp(firstp) - - // control flow is more complicated in generated go code - // than in generated c code. define pseudo-variables for - // registers, so we have complete register usage information. - var nreg int - regnames := Thearch.Regnames(&nreg) - - nvar = nreg - for i := 0; i < nreg; i++ { - vars[i] = Var{} - } - for i := 0; i < nreg; i++ { - if regnodes[i] == nil { - regnodes[i] = newname(Lookup(regnames[i])) - } - vars[i].node = regnodes[i] - } - - regbits = Thearch.Excludedregs() - externs = zbits - params = zbits - consts = zbits - addrs = zbits - ivar = zbits - ovar = zbits - - // pass 1 - // build aux data structure - // allocate pcs - // find use and set of variables - g := Flowstart(firstp, func() interface{} { return new(Reg) }) - if g == nil { - for i := 0; i < nvar; i++ { - vars[i].node.SetOpt(nil) - } - return - } - - firstf := g.Start - - for f := firstf; f != nil; f = f.Link { - p := f.Prog - // AVARLIVE must be considered a use, do not skip it. - // Otherwise the variable will be optimized away, - // and the whole point of AVARLIVE is to keep it on the stack. - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - - // Avoid making variables for direct-called functions. - if p.As == obj.ACALL && p.To.Type == obj.TYPE_MEM && p.To.Name == obj.NAME_EXTERN { - continue - } - - // from vs to doesn't matter for registers. - r := f.Data.(*Reg) - r.use1.b[0] |= p.Info.Reguse | p.Info.Regindex - r.set.b[0] |= p.Info.Regset - - bit := mkvar(f, &p.From) - if bany(&bit) { - if p.Info.Flags&LeftAddr != 0 { - setaddrs(bit) - } - if p.Info.Flags&LeftRead != 0 { - for z := 0; z < BITS; z++ { - r.use1.b[z] |= bit.b[z] - } - } - if p.Info.Flags&LeftWrite != 0 { - for z := 0; z < BITS; z++ { - r.set.b[z] |= bit.b[z] - } - } - } - - // Compute used register for reg - if p.Info.Flags&RegRead != 0 { - r.use1.b[0] |= Thearch.RtoB(int(p.Reg)) - } - - // Currently we never generate three register forms. - // If we do, this will need to change. - if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { - Fatalf("regopt not implemented for from3") - } - - bit = mkvar(f, &p.To) - if bany(&bit) { - if p.Info.Flags&RightAddr != 0 { - setaddrs(bit) - } - if p.Info.Flags&RightRead != 0 { - for z := 0; z < BITS; z++ { - r.use2.b[z] |= bit.b[z] - } - } - if p.Info.Flags&RightWrite != 0 { - for z := 0; z < BITS; z++ { - r.set.b[z] |= bit.b[z] - } - } - } - } - - for i := 0; i < nvar; i++ { - v := &vars[i] - if v.addr != 0 { - bit := blsh(uint(i)) - for z := 0; z < BITS; z++ { - addrs.b[z] |= bit.b[z] - } - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("bit=%2d addr=%d et=%v w=%-2d s=%v + %d\n", i, v.addr, v.etype, v.width, v.node, v.offset) - } - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass1", firstf, 1) - } - - // pass 2 - // find looping structure - flowrpo(g) - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass2", firstf, 1) - } - - // pass 2.5 - // iterate propagating fat vardef covering forward - // r->act records vars with a VARDEF since the last CALL. - // (r->act will be reused in pass 5 for something else, - // but we'll be done with it by then.) - active := 0 - - for f := firstf; f != nil; f = f.Link { - f.Active = 0 - r := f.Data.(*Reg) - r.act = zbits - } - - for f := firstf; f != nil; f = f.Link { - p := f.Prog - if p.As == obj.AVARDEF && Isfat(((p.To.Node).(*Node)).Type) && ((p.To.Node).(*Node)).Opt() != nil { - active++ - walkvardef(p.To.Node.(*Node), f, active) - } - } - - // pass 3 - // iterate propagating usage - // back until flow graph is complete - var f1 *Flow - var i int - var f *Flow -loop1: - change = 0 - - for f = firstf; f != nil; f = f.Link { - f.Active = 0 - } - for f = firstf; f != nil; f = f.Link { - if f.Prog.As == obj.ARET { - prop(f, zbits, zbits) - } - } - - // pick up unreachable code -loop11: - i = 0 - - for f = firstf; f != nil; f = f1 { - f1 = f.Link - if f1 != nil && f1.Active != 0 && f.Active == 0 { - prop(f, zbits, zbits) - i = 1 - } - } - - if i != 0 { - goto loop11 - } - if change != 0 { - goto loop1 - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass3", firstf, 1) - } - - // pass 4 - // iterate propagating register/variable synchrony - // forward until graph is complete -loop2: - change = 0 - - for f = firstf; f != nil; f = f.Link { - f.Active = 0 - } - synch(firstf, zbits) - if change != 0 { - goto loop2 - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass4", firstf, 1) - } - - // pass 4.5 - // move register pseudo-variables into regu. - mask := uint64((1 << uint(nreg)) - 1) - for f := firstf; f != nil; f = f.Link { - r := f.Data.(*Reg) - r.regu = (r.refbehind.b[0] | r.set.b[0]) & mask - r.set.b[0] &^= mask - r.use1.b[0] &^= mask - r.use2.b[0] &^= mask - r.refbehind.b[0] &^= mask - r.refahead.b[0] &^= mask - r.calbehind.b[0] &^= mask - r.calahead.b[0] &^= mask - r.regdiff.b[0] &^= mask - r.act.b[0] &^= mask - } - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass4.5", firstf, 1) - } - - // pass 5 - // isolate regions - // calculate costs (paint1) - var bit Bits - if f := firstf; f != nil { - r := f.Data.(*Reg) - for z := 0; z < BITS; z++ { - bit.b[z] = (r.refahead.b[z] | r.calahead.b[z]) &^ (externs.b[z] | params.b[z] | addrs.b[z] | consts.b[z]) - } - if bany(&bit) && !f.Refset { - // should never happen - all variables are preset - if Debug['w'] != 0 { - fmt.Printf("%v: used and not set: %v\n", f.Prog.Line(), &bit) - } - f.Refset = true - } - } - - for f := firstf; f != nil; f = f.Link { - (f.Data.(*Reg)).act = zbits - } - nregion = 0 - region = region[:0] - for f := firstf; f != nil; f = f.Link { - r := f.Data.(*Reg) - for z := 0; z < BITS; z++ { - bit.b[z] = r.set.b[z] &^ (r.refahead.b[z] | r.calahead.b[z] | addrs.b[z]) - } - if bany(&bit) && !f.Refset { - if Debug['w'] != 0 { - fmt.Printf("%v: set and not used: %v\n", f.Prog.Line(), &bit) - } - f.Refset = true - Thearch.Excise(f) - } - - for z := 0; z < BITS; z++ { - bit.b[z] = LOAD(r, z) &^ (r.act.b[z] | addrs.b[z]) - } - for bany(&bit) { - i = bnum(&bit) - change = 0 - paint1(f, i) - biclr(&bit, uint(i)) - if change <= 0 { - continue - } - if nregion >= MaxRgn { - nregion++ - continue - } - - region = append(region, Rgn{ - enter: f, - cost: int16(change), - varno: int16(i), - }) - nregion++ - } - } - - if false && Debug['v'] != 0 && strings.Contains(Curfn.Func.Nname.Sym.Name, "Parse") { - Warn("regions: %d\n", nregion) - } - if nregion >= MaxRgn { - if Debug['v'] != 0 { - Warn("too many regions: %d\n", nregion) - } - nregion = MaxRgn - } - - sort.Sort(rcmp(region[:nregion])) - - if Debug['R'] != 0 && Debug['v'] != 0 { - Dumpit("pass5", firstf, 1) - } - - // pass 6 - // determine used registers (paint2) - // replace code (paint3) - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("\nregisterizing\n") - } - for i := 0; i < nregion; i++ { - rgp := ®ion[i] - if Debug['R'] != 0 && Debug['v'] != 0 { - fmt.Printf("region %d: cost %d varno %d enter %d\n", i, rgp.cost, rgp.varno, rgp.enter.Prog.Pc) - } - bit = blsh(uint(rgp.varno)) - usedreg := paint2(rgp.enter, int(rgp.varno), 0) - vreg := allreg(usedreg, rgp) - if rgp.regno != 0 { - if Debug['R'] != 0 && Debug['v'] != 0 { - v := &vars[rgp.varno] - fmt.Printf("registerize %v+%d (bit=%2d et=%v) in %v usedreg=%#x vreg=%#x\n", v.node, v.offset, rgp.varno, v.etype, obj.Rconv(int(rgp.regno)), usedreg, vreg) - } - - paint3(rgp.enter, int(rgp.varno), vreg, int(rgp.regno)) - } - } - - // free aux structures. peep allocates new ones. - for i := 0; i < nvar; i++ { - vars[i].node.SetOpt(nil) - } - Flowend(g) - firstf = nil - - if Debug['R'] != 0 && Debug['v'] != 0 { - // Rebuild flow graph, since we inserted instructions - g := Flowstart(firstp, nil) - firstf = g.Start - Dumpit("pass6", firstf, 0) - Flowend(g) - firstf = nil - } - - // pass 7 - // peep-hole on basic block - if Debug['R'] == 0 || Debug['P'] != 0 { - Thearch.Peep(firstp) - } - - // eliminate nops - for p := firstp; p != nil; p = p.Link { - for p.Link != nil && p.Link.As == obj.ANOP { - p.Link = p.Link.Link - } - if p.To.Type == obj.TYPE_BRANCH { - for p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.ANOP { - p.To.Val = p.To.Val.(*obj.Prog).Link - } - } - } - - if Debug['R'] != 0 { - if Ostats.Ncvtreg != 0 || Ostats.Nspill != 0 || Ostats.Nreload != 0 || Ostats.Ndelmov != 0 || Ostats.Nvar != 0 || Ostats.Naddr != 0 || false { - fmt.Printf("\nstats\n") - } - - if Ostats.Ncvtreg != 0 { - fmt.Printf("\t%4d cvtreg\n", Ostats.Ncvtreg) - } - if Ostats.Nspill != 0 { - fmt.Printf("\t%4d spill\n", Ostats.Nspill) - } - if Ostats.Nreload != 0 { - fmt.Printf("\t%4d reload\n", Ostats.Nreload) - } - if Ostats.Ndelmov != 0 { - fmt.Printf("\t%4d delmov\n", Ostats.Ndelmov) - } - if Ostats.Nvar != 0 { - fmt.Printf("\t%4d var\n", Ostats.Nvar) - } - if Ostats.Naddr != 0 { - fmt.Printf("\t%4d addr\n", Ostats.Naddr) - } - - Ostats = OptStats{} - } -} - -// bany reports whether any bits in a are set. -func bany(a *Bits) bool { - for _, x := range &a.b { // & to avoid making a copy of a.b - if x != 0 { - return true - } - } - return false -} - -// bnum reports the lowest index of a 1 bit in a. -func bnum(a *Bits) int { - for i, x := range &a.b { // & to avoid making a copy of a.b - if x != 0 { - return 64*i + Bitno(x) - } - } - - Fatalf("bad in bnum") - return 0 -} - -// blsh returns a Bits with 1 at index n, 0 elsewhere (1<>= 32 - } - if b&(1<<16-1) == 0 { - n += 16 - b >>= 16 - } - if b&(1<<8-1) == 0 { - n += 8 - b >>= 8 - } - if b&(1<<4-1) == 0 { - n += 4 - b >>= 4 - } - if b&(1<<2-1) == 0 { - n += 2 - b >>= 2 - } - if b&1 == 0 { - n++ - } - return n -} - -// String returns a space-separated list of the variables represented by bits. -func (bits Bits) String() string { - // Note: This method takes a value receiver, both for convenience - // and to make it safe to modify the bits as we process them. - // Even so, most prints above use &bits, because then the value - // being stored in the interface{} is a pointer and does not require - // an allocation and copy to create the interface{}. - var buf bytes.Buffer - sep := "" - for bany(&bits) { - i := bnum(&bits) - buf.WriteString(sep) - sep = " " - v := &vars[i] - if v.node == nil || v.node.Sym == nil { - fmt.Fprintf(&buf, "$%d", i) - } else { - fmt.Fprintf(&buf, "%s(%d)", v.node.Sym.Name, i) - if v.offset != 0 { - fmt.Fprintf(&buf, "%+d", v.offset) - } - } - biclr(&bits, uint(i)) - } - return buf.String() -} diff --git a/src/cmd/compile/internal/gc/sizeof_test.go b/src/cmd/compile/internal/gc/sizeof_test.go index a01da13883..1a0e53057c 100644 --- a/src/cmd/compile/internal/gc/sizeof_test.go +++ b/src/cmd/compile/internal/gc/sizeof_test.go @@ -22,7 +22,6 @@ func TestSizeof(t *testing.T) { _32bit uintptr // size on 32bit platforms _64bit uintptr // size on 64bit platforms }{ - {Flow{}, 52, 88}, {Func{}, 96, 168}, {Name{}, 52, 80}, {Node{}, 92, 144}, diff --git a/src/cmd/compile/internal/mips64/cgen.go b/src/cmd/compile/internal/mips64/cgen.go deleted file mode 100644 index 998afeadcf..0000000000 --- a/src/cmd/compile/internal/mips64/cgen.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mips64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/mips" -) - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op obj.As - switch align { - default: - gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = mips.AMOVB - - case 2: - op = mips.AMOVH - - case 4: - op = mips.AMOVW - - case 8: - op = mips.AMOVV - } - - if w%int64(align) != 0 { - gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - - if osrc < odst && odst < osrc+w { - dir = -dir - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(mips.AMOVV, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - - // set up end marker - var nend gc.Node - - // move src and dest to the end of block if necessary - if dir < 0 { - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - gins(mips.AMOVV, &src, &nend) - } - - p := gins(mips.AADDV, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - p = gins(mips.AADDV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - } else { - p := gins(mips.AADDV, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - p = gins(mips.AADDV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - p := gins(mips.AMOVV, &src, &nend) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - } - } - - // move - // TODO: enable duffcopy for larger copies. - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - ploop := p - - p = gins(mips.AADDV, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(dir) - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - - p = gins(mips.AADDV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(dir) - - gc.Patch(ginsbranch(mips.ABNE, nil, &src, &nend, 0), ploop) - gc.Regfree(&nend) - } else { - // TODO: Instead of generating ADDV $-8,R8; ADDV - // $-8,R7; n*(MOVV 8(R8),R9; ADDV $8,R8; MOVV R9,8(R7); - // ADDV $8,R7;) just generate the offsets directly and - // eliminate the ADDs. That will produce shorter, more - // pipeline-able code. - var p *obj.Prog - for ; c > 0; c-- { - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - - p = gins(mips.AADDV, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(dir) - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - - p = gins(mips.AADDV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(dir) - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/compile/internal/mips64/galign.go b/src/cmd/compile/internal/mips64/galign.go index 8abe651128..ca1cb689ff 100644 --- a/src/cmd/compile/internal/mips64/galign.go +++ b/src/cmd/compile/internal/mips64/galign.go @@ -32,36 +32,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = func(s *gc.SSAGenState, b *ssa.Block) {} diff --git a/src/cmd/compile/internal/mips64/ggen.go b/src/cmd/compile/internal/mips64/ggen.go index 599ca06d36..49ec8da8bf 100644 --- a/src/cmd/compile/internal/mips64/ggen.go +++ b/src/cmd/compile/internal/mips64/ggen.go @@ -8,7 +8,6 @@ import ( "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/mips" - "fmt" ) func defframe(ptxt *obj.Prog) { @@ -122,370 +121,3 @@ func ginsnop() { gc.Nodreg(®, gc.Types[gc.TINT], mips.REG_R0) gins(mips.ANOR, ®, ®) } - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - t := nl.Type - - t0 := t - - if t.Width < 8 { - if t.IsSigned() { - t = gc.Types[gc.TINT64] - } else { - t = gc.Types[gc.TUINT64] - } - } - - a := optoas(gc.ODIV, t) - - var tl gc.Node - gc.Regalloc(&tl, t0, nil) - var tr gc.Node - gc.Regalloc(&tr, t0, nil) - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := ginsbranch(mips.ABNE, nil, &tr, nil, 0) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - gins3(a, &tr, &tl, nil) - gc.Regfree(&tr) - if op == gc.ODIV { - var lo gc.Node - gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO) - gins(mips.AMOVV, &lo, &tl) - } else { // remainder in REG_HI - var hi gc.Node - gc.Nodreg(&hi, gc.Types[gc.TUINT64], mips.REG_HI) - gins(mips.AMOVV, &hi, &tl) - } - gmove(&tl, res) - gc.Regfree(&tl) -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - t := nl.Type - w := t.Width * 8 - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins3(optoas(gc.OMUL, t), &n2, &n1, nil) - var lo gc.Node - gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO) - gins(mips.AMOVV, &lo, &n1) - p := gins(mips.ASRAV, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins3(optoas(gc.OMUL, t), &n2, &n1, nil) - var lo gc.Node - gc.Nodreg(&lo, gc.Types[gc.TUINT64], mips.REG_LO) - gins(mips.AMOVV, &lo, &n1) - p := gins(mips.ASRLV, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TINT64, - gc.TUINT64: - if t.IsSigned() { - gins3(mips.AMULV, &n2, &n1, nil) - } else { - gins3(mips.AMULVU, &n2, &n1, nil) - } - var hi gc.Node - gc.Nodreg(&hi, gc.Types[gc.TUINT64], mips.REG_HI) - gins(mips.AMOVV, &hi, &n1) - - default: - gc.Fatalf("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - var rtmp gc.Node - gc.Nodreg(&rtmp, tcount, mips.REGTMP) - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins3(mips.ASGTU, &n3, &n1, &rtmp) - p1 := ginsbranch(mips.ABNE, nil, &rtmp, nil, 0) - if op == gc.ORSH && nl.Type.IsSigned() { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - w := uint64(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 8 // bytes - q := w / 8 // dwords - - if gc.Reginuse(mips.REGRT1) { - gc.Fatalf("%v in use during clearfat", obj.Rconv(mips.REGRT1)) - } - - var r0 gc.Node - gc.Nodreg(&r0, gc.Types[gc.TUINT64], mips.REGZERO) - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], mips.REGRT1) - gc.Regrealloc(&dst) - gc.Agen(nl, &dst) - - var boff uint64 - if q > 128 { - p := gins(mips.ASUBV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p = gins(mips.AMOVV, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q * 8) - - p = gins(mips.AMOVV, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 8 - pl := p - - p = gins(mips.AADDV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - gc.Patch(ginsbranch(mips.ABNE, nil, &dst, &end, 0), pl) - - gc.Regfree(&end) - - // The loop leaves R1 on the last zeroed dword - boff = 8 - // TODO(dfc): https://golang.org/issue/12108 - // If DUFFZERO is used inside a tail call (see genwrapper) it will - // overwrite the link register. - } else if false && q >= 4 { - p := gins(mips.ASUBV, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - f := gc.Sysfunc("duffzero") - p = gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 8 and 128 = magic constants: see ../../runtime/asm_mips64x.s - p.To.Offset = int64(8 * (128 - q)) - - // duffzero leaves R1 on the last zeroed dword - boff = 8 - } else { - var p *obj.Prog - for t := uint64(0); t < q; t++ { - p = gins(mips.AMOVV, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(8 * t) - } - - boff = 8 * q - } - - var p *obj.Prog - for t := uint64(0); t < c; t++ { - p = gins(mips.AMOVB, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(t + boff) - } - - gc.Regfree(&dst) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatalf("invalid nil check %v\n", p) - } - - // check is - // BNE arg, 2(PC) - // MOVV R0, 0(R0) - p1 = gc.Ctxt.NewProg() - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - - p.As = mips.ABNE - p.To.Type = obj.TYPE_BRANCH - p.To.Val = p1.Link - - // crash by write to memory address 0. - p1.As = mips.AMOVV - p1.From.Type = obj.TYPE_REG - p1.From.Reg = mips.REGZERO - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = mips.REGZERO - p1.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, mips.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/compile/internal/mips64/gsubr.go b/src/cmd/compile/internal/mips64/gsubr.go index fd0efc36ba..ecf83594ce 100644 --- a/src/cmd/compile/internal/mips64/gsubr.go +++ b/src/cmd/compile/internal/mips64/gsubr.go @@ -31,7 +31,6 @@ package mips64 import ( - "cmd/compile/internal/big" "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/mips" @@ -51,7 +50,7 @@ var resvd = []int{ /* * generate - * as $c, n + * as $c, n */ func ginscon(as obj.As, c int64, n2 *gc.Node) { var n1 gc.Node @@ -73,495 +72,6 @@ func ginscon(as obj.As, c int64, n2 *gc.Node) { rawgins(as, &n1, n2) } -// generate branch -// n1, n2 are registers -func ginsbranch(as obj.As, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - p := gc.Gbranch(as, t, likely) - gc.Naddr(&p.From, n1) - if n2 != nil { - p.Reg = n2.Reg - } - return p -} - -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if !t.IsFloat() && (op == gc.OLT || op == gc.OGE) { - // swap nodes to fit SGT instruction - n1, n2 = n2, n1 - } - if t.IsFloat() && (op == gc.OLT || op == gc.OLE) { - // swap nodes to fit CMPGT, CMPGE instructions and reverse relation - n1, n2 = n2, n1 - if op == gc.OLT { - op = gc.OGT - } else { - op = gc.OGE - } - } - - var r1, r2, g1, g2 gc.Node - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - - var p *obj.Prog - var ntmp gc.Node - gc.Nodreg(&ntmp, gc.Types[gc.TINT], mips.REGTMP) - - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32, - gc.TINT64: - if op == gc.OEQ || op == gc.ONE { - p = ginsbranch(optoas(op, t), nil, &r1, &r2, likely) - } else { - gins3(mips.ASGT, &r1, &r2, &ntmp) - - p = ginsbranch(optoas(op, t), nil, &ntmp, nil, likely) - } - - case gc.TBOOL, - gc.TUINT8, - gc.TUINT16, - gc.TUINT32, - gc.TUINT64, - gc.TPTR32, - gc.TPTR64: - if op == gc.OEQ || op == gc.ONE { - p = ginsbranch(optoas(op, t), nil, &r1, &r2, likely) - } else { - gins3(mips.ASGTU, &r1, &r2, &ntmp) - - p = ginsbranch(optoas(op, t), nil, &ntmp, nil, likely) - } - - case gc.TFLOAT32: - switch op { - default: - gc.Fatalf("ginscmp: no entry for op=%v type=%v", op, t) - - case gc.OEQ, - gc.ONE: - gins3(mips.ACMPEQF, &r1, &r2, nil) - - case gc.OGE: - gins3(mips.ACMPGEF, &r1, &r2, nil) - - case gc.OGT: - gins3(mips.ACMPGTF, &r1, &r2, nil) - } - p = gc.Gbranch(optoas(op, t), nil, likely) - - case gc.TFLOAT64: - switch op { - default: - gc.Fatalf("ginscmp: no entry for op=%v type=%v", op, t) - - case gc.OEQ, - gc.ONE: - gins3(mips.ACMPEQD, &r1, &r2, nil) - - case gc.OGE: - gins3(mips.ACMPGED, &r1, &r2, nil) - - case gc.OGT: - gins3(mips.ACMPGTD, &r1, &r2, nil) - } - p = gc.Gbranch(optoas(op, t), nil, likely) - } - - gc.Regfree(&g2) - gc.Regfree(&r2) - gc.Regfree(&g1) - gc.Regfree(&r1) - - return p -} - -// set up nodes representing 2^63 -var ( - bigi gc.Node - bigf gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - - bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %L -> %L\n", f, t) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var r2 gc.Node - var r1 gc.Node - var a obj.As - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT32, - gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(mips.AMOVV, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT32, - gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(mips.AMOVV, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = tt // so big switch will choose a simple mov - - // constants can't move directly to memory. - if gc.Ismem(t) { - goto hard - } - } - - // value -> value copy, first operand in memory. - // any floating point operand requires register - // src, so goto hard to copy to register first. - if gc.Ismem(f) && ft != tt && (gc.Isfloat[ft] || gc.Isfloat[tt]) { - cvt = gc.Types[ft] - goto hard - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatalf("gmove %L -> %L", f.Type, t.Type) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = mips.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, // same size - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, // truncate - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = mips.AMOVBU - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = mips.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, // same size - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, // truncate - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = mips.AMOVHU - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32: - a = mips.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, // same size - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, // truncate - gc.TUINT64<<16 | gc.TUINT32: - a = mips.AMOVWU - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = mips.AMOVV - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = mips.AMOVB - - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = mips.AMOVBU - - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = mips.AMOVH - - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = mips.AMOVHU - - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = mips.AMOVW - - goto rdst - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = mips.AMOVWU - - goto rdst - - //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); - //return; - // algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT64, - gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32, - gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - bignodes() - - gc.Regalloc(&r1, gc.Types[gc.TFLOAT64], nil) - gmove(f, &r1) - if tt == gc.TUINT64 { - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) - gmove(&bigf, &r2) - gins3(mips.ACMPGED, &r1, &r2, nil) - p1 := gc.Gbranch(mips.ABFPF, nil, 0) - gins(mips.ASUBD, &r2, &r1) - gc.Patch(p1, gc.Pc) - gc.Regfree(&r2) - } - - gc.Regalloc(&r2, gc.Types[gc.TINT64], t) - gins(mips.ATRUNCDV, &r1, &r1) - gins(mips.AMOVV, &r1, &r2) - gc.Regfree(&r1) - - if tt == gc.TUINT64 { - p1 := gc.Gbranch(mips.ABFPF, nil, 0) // use FCR0 here again - gc.Nodreg(&r1, gc.Types[gc.TINT64], mips.REGTMP) - gmove(&bigi, &r1) - gins(mips.AADDVU, &r1, &r2) - gc.Patch(p1, gc.Pc) - } - - gmove(&r2, t) - gc.Regfree(&r2) - return - - //warn("gmove: convert int to float not implemented: %N -> %N\n", f, t); - //return; - // algorithm is: - // if small enough, use native int64 -> float64 conversion. - // otherwise, halve (x -> (x>>1)|(x&1)), convert, and double. - /* - * integer to float - */ - case gc.TINT32<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64, - gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - bignodes() - - var rtmp gc.Node - gc.Regalloc(&r1, gc.Types[gc.TINT64], nil) - gmove(f, &r1) - if ft == gc.TUINT64 { - gc.Nodreg(&rtmp, gc.Types[gc.TUINT64], mips.REGTMP) - gmove(&bigi, &rtmp) - gins(mips.AAND, &r1, &rtmp) - p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil) - p2 := gins3(mips.AAND, nil, &r1, &r3) - p2.From.Type = obj.TYPE_CONST - p2.From.Offset = 1 - p3 := gins(mips.ASRLV, nil, &r1) - p3.From.Type = obj.TYPE_CONST - p3.From.Offset = 1 - gins(mips.AOR, &r3, &r1) - gc.Regfree(&r3) - gc.Patch(p1, gc.Pc) - } - - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t) - gins(mips.AMOVV, &r1, &r2) - gins(mips.AMOVVD, &r2, &r2) - gc.Regfree(&r1) - - if ft == gc.TUINT64 { - p1 := ginsbranch(mips.ABEQ, nil, &rtmp, nil, 0) - gins(mips.AADDD, &r2, &r2) - gc.Patch(p1, gc.Pc) - } - - gmove(&r2, t) - gc.Regfree(&r2) - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = mips.AMOVF - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = mips.AMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = mips.AMOVFD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = mips.AMOVDF - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - // gins is called by the front end. // It synthesizes some multiple-instruction sequences // so the front end can stay simpler. @@ -575,19 +85,6 @@ func gins(as obj.As, f, t *gc.Node) *obj.Prog { return rawgins(as, f, t) } -/* - * generate one instruction: - * as f, r, t - * r must be register, if not nil - */ -func gins3(as obj.As, f, r, t *gc.Node) *obj.Prog { - p := rawgins(as, f, t) - if r != nil { - p.Reg = r.Reg - } - return p -} - /* * generate one instruction: * as f, t @@ -683,384 +180,3 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog { return p } - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry for op=%v type=%v", op, t) - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64: - a = mips.ABEQ - - case OEQ_ | gc.TFLOAT32, // ACMPEQF - OEQ_ | gc.TFLOAT64: // ACMPEQD - a = mips.ABFPT - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64: - a = mips.ABNE - - case ONE_ | gc.TFLOAT32, // ACMPEQF - ONE_ | gc.TFLOAT64: // ACMPEQD - a = mips.ABFPF - - case OLT_ | gc.TINT8, // ASGT - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64, - OLT_ | gc.TUINT8, // ASGTU - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64: - a = mips.ABNE - - case OLT_ | gc.TFLOAT32, // ACMPGEF - OLT_ | gc.TFLOAT64: // ACMPGED - a = mips.ABFPT - - case OLE_ | gc.TINT8, // ASGT - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64, - OLE_ | gc.TUINT8, // ASGTU - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64: - a = mips.ABEQ - - case OLE_ | gc.TFLOAT32, // ACMPGTF - OLE_ | gc.TFLOAT64: // ACMPGTD - a = mips.ABFPT - - case OGT_ | gc.TINT8, // ASGT - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64, - OGT_ | gc.TUINT8, // ASGTU - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64: - a = mips.ABNE - - case OGT_ | gc.TFLOAT32, // ACMPGTF - OGT_ | gc.TFLOAT64: // ACMPGTD - a = mips.ABFPT - - case OGE_ | gc.TINT8, // ASGT - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64, - OGE_ | gc.TUINT8, // ASGTU - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64: - a = mips.ABEQ - - case OGE_ | gc.TFLOAT32, // ACMPGEF - OGE_ | gc.TFLOAT64: // ACMPGED - a = mips.ABFPT - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8: - a = mips.AMOVB - - case OAS_ | gc.TUINT8: - a = mips.AMOVBU - - case OAS_ | gc.TINT16: - a = mips.AMOVH - - case OAS_ | gc.TUINT16: - a = mips.AMOVHU - - case OAS_ | gc.TINT32: - a = mips.AMOVW - - case OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = mips.AMOVWU - - case OAS_ | gc.TINT64, - OAS_ | gc.TUINT64, - OAS_ | gc.TPTR64: - a = mips.AMOVV - - case OAS_ | gc.TFLOAT32: - a = mips.AMOVF - - case OAS_ | gc.TFLOAT64: - a = mips.AMOVD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8, - OADD_ | gc.TINT16, - OADD_ | gc.TUINT16, - OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32: - a = mips.AADDU - - case OADD_ | gc.TINT64, - OADD_ | gc.TUINT64, - OADD_ | gc.TPTR64: - a = mips.AADDVU - - case OADD_ | gc.TFLOAT32: - a = mips.AADDF - - case OADD_ | gc.TFLOAT64: - a = mips.AADDD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8, - OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16, - OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32: - a = mips.ASUBU - - case OSUB_ | gc.TINT64, - OSUB_ | gc.TUINT64, - OSUB_ | gc.TPTR64: - a = mips.ASUBVU - - case OSUB_ | gc.TFLOAT32: - a = mips.ASUBF - - case OSUB_ | gc.TFLOAT64: - a = mips.ASUBD - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8, - OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16, - OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32, - OMINUS_ | gc.TINT64, - OMINUS_ | gc.TUINT64, - OMINUS_ | gc.TPTR64: - a = mips.ASUBVU - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8, - OAND_ | gc.TINT16, - OAND_ | gc.TUINT16, - OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32, - OAND_ | gc.TINT64, - OAND_ | gc.TUINT64, - OAND_ | gc.TPTR64: - a = mips.AAND - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8, - OOR_ | gc.TINT16, - OOR_ | gc.TUINT16, - OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32, - OOR_ | gc.TINT64, - OOR_ | gc.TUINT64, - OOR_ | gc.TPTR64: - a = mips.AOR - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8, - OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16, - OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32, - OXOR_ | gc.TINT64, - OXOR_ | gc.TUINT64, - OXOR_ | gc.TPTR64: - a = mips.AXOR - - // TODO(minux): handle rotates - //case CASE(OLROT, TINT8): - //case CASE(OLROT, TUINT8): - //case CASE(OLROT, TINT16): - //case CASE(OLROT, TUINT16): - //case CASE(OLROT, TINT32): - //case CASE(OLROT, TUINT32): - //case CASE(OLROT, TPTR32): - //case CASE(OLROT, TINT64): - //case CASE(OLROT, TUINT64): - //case CASE(OLROT, TPTR64): - // a = 0//???; RLDC? - // break; - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8, - OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16, - OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32, - OLSH_ | gc.TINT64, - OLSH_ | gc.TUINT64, - OLSH_ | gc.TPTR64: - a = mips.ASLLV - - case ORSH_ | gc.TUINT8, - ORSH_ | gc.TUINT16, - ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32, - ORSH_ | gc.TUINT64, - ORSH_ | gc.TPTR64: - a = mips.ASRLV - - case ORSH_ | gc.TINT8, - ORSH_ | gc.TINT16, - ORSH_ | gc.TINT32, - ORSH_ | gc.TINT64: - a = mips.ASRAV - - // TODO(minux): handle rotates - //case CASE(ORROTC, TINT8): - //case CASE(ORROTC, TUINT8): - //case CASE(ORROTC, TINT16): - //case CASE(ORROTC, TUINT16): - //case CASE(ORROTC, TINT32): - //case CASE(ORROTC, TUINT32): - //case CASE(ORROTC, TINT64): - //case CASE(ORROTC, TUINT64): - // a = 0//??? RLDC?? - // break; - - case OHMUL_ | gc.TINT64: - a = mips.AMULV - - case OHMUL_ | gc.TUINT64, - OHMUL_ | gc.TPTR64: - a = mips.AMULVU - - case OMUL_ | gc.TINT8, - OMUL_ | gc.TINT16, - OMUL_ | gc.TINT32, - OMUL_ | gc.TINT64: - a = mips.AMULV - - case OMUL_ | gc.TUINT8, - OMUL_ | gc.TUINT16, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32, - OMUL_ | gc.TUINT64, - OMUL_ | gc.TPTR64: - a = mips.AMULVU - - case OMUL_ | gc.TFLOAT32: - a = mips.AMULF - - case OMUL_ | gc.TFLOAT64: - a = mips.AMULD - - case ODIV_ | gc.TINT8, - ODIV_ | gc.TINT16, - ODIV_ | gc.TINT32, - ODIV_ | gc.TINT64: - a = mips.ADIVV - - case ODIV_ | gc.TUINT8, - ODIV_ | gc.TUINT16, - ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - ODIV_ | gc.TUINT64, - ODIV_ | gc.TPTR64: - a = mips.ADIVVU - - case ODIV_ | gc.TFLOAT32: - a = mips.ADIVF - - case ODIV_ | gc.TFLOAT64: - a = mips.ADIVD - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -func xgen(n *gc.Node, a *gc.Node, o int) bool { - // TODO(minux) - - return -1 != 0 /*TypeKind(100016)*/ -} - -func sudoclean() { - return -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - // TODO(minux) - - *a = obj.Addr{} - return false -} diff --git a/src/cmd/compile/internal/mips64/peep.go b/src/cmd/compile/internal/mips64/peep.go deleted file mode 100644 index ce639ac9a6..0000000000 --- a/src/cmd/compile/internal/mips64/peep.go +++ /dev/null @@ -1,772 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package mips64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/mips" - "fmt" -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - - // TODO(austin) Handle smaller moves. arm and amd64 - // distinguish between moves that moves that *must* - // sign/zero extend and moves that don't care so they - // can eliminate moves that don't care without - // breaking moves that do care. This might let us - // simplify or remove the next peep loop, too. - if p.As == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD { - if regtyp(&p.To) { - // Try to eliminate reg->reg moves - if regtyp(&p.From) { - if isfreg(&p.From) == isfreg(&p.To) { - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - - // Convert uses to $0 to uses of R0 and - // propagate R0 - if regzer(&p.From) { - if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) { - p.From.Type = obj.TYPE_REG - p.From.Reg = mips.REGZERO - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - } - } - } - - if t != 0 { - goto loop1 - } - - /* - * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) - */ - var p1 *obj.Prog - var r1 *gc.Flow - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case mips.AMOVH, - mips.AMOVHU, - mips.AMOVB, - mips.AMOVBU, - mips.AMOVW, - mips.AMOVWU: - if p.To.Type != obj.TYPE_REG { - continue - } - } - - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != p.As { - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { - continue - } - excise(r1) - } - - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -// regzer returns true if a's value is 0 (a is R0 or $0) -func regzer(a *obj.Addr) bool { - if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { - if a.Sym == nil && a.Reg == 0 { - if a.Offset == 0 { - return true - } - } - } - return a.Type == obj.TYPE_REG && a.Reg == mips.REGZERO -} - -func regtyp(a *obj.Addr) bool { - // TODO(rsc): Floating point register exclusions? - return a.Type == obj.TYPE_REG && mips.REG_R0 <= a.Reg && a.Reg <= mips.REG_F31 && a.Reg != mips.REGZERO -} - -func isfreg(a *obj.Addr) bool { - return mips.REG_F0 <= a.Reg && a.Reg <= mips.REG_F31 -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R1 - * ADD b, R1 / no use of R2 - * MOV R1, R2 - * would be converted to - * MOV a, R2 - * ADD b, R2 - * MOV R2, R1 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - * - * r0 (the argument, not the register) is the MOV at the end of the - * above sequences. This returns 1 if it modified any instructions. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub1(p, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - v1.Reg, v2.Reg = v2.Reg, v1.Reg - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail (v1->v2 move must remain) - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success (caller can remove v1->v2 move) - */ -func copyprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - if gc.Debug['P'] != 0 { - fmt.Printf("eliminating self-move: %v\n", r0.Prog) - } - return true - } - - gactive++ - if gc.Debug['P'] != 0 { - fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) - } - return copy1(v1, v2, r0.S1, false) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns true if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = true - - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// 1 if v only used -// 2 if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// 3 if v is only set -// 4 if v is set in one address and used in another (so addresses -// can be rewritten independently) -// 0 otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - if p.From3Type() != obj.TYPE_NONE { - // never generates a from3 - fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", p.As) - return 2 - - case obj.ANOP, /* read p->from, write p->to */ - mips.AMOVV, - mips.AMOVF, - mips.AMOVD, - mips.AMOVH, - mips.AMOVHU, - mips.AMOVB, - mips.AMOVBU, - mips.AMOVW, - mips.AMOVWU, - mips.AMOVFD, - mips.AMOVDF, - mips.AMOVDW, - mips.AMOVWD, - mips.AMOVFW, - mips.AMOVWF, - mips.AMOVDV, - mips.AMOVVD, - mips.AMOVFV, - mips.AMOVVF, - mips.ATRUNCFV, - mips.ATRUNCDV, - mips.ATRUNCFW, - mips.ATRUNCDW: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - // p->to only indirectly uses v - return 1 - } - - return 0 - - case mips.ASGT, /* read p->from, read p->reg, write p->to */ - mips.ASGTU, - - mips.AADD, - mips.AADDU, - mips.ASUB, - mips.ASUBU, - mips.ASLL, - mips.ASRL, - mips.ASRA, - mips.AOR, - mips.ANOR, - mips.AAND, - mips.AXOR, - - mips.AADDV, - mips.AADDVU, - mips.ASUBV, - mips.ASUBVU, - mips.ASLLV, - mips.ASRLV, - mips.ASRAV, - - mips.AADDF, - mips.AADDD, - mips.ASUBF, - mips.ASUBD, - mips.AMULF, - mips.AMULD, - mips.ADIVF, - mips.ADIVD: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub1(p, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - // Fix up implicit reg (e.g., ADD - // R3,R4 -> ADD R3,R4,R4) so we can - // update reg and to separately. - p.Reg = p.To.Reg - } - - if copyau(&p.From, v) { - return 4 - } - if copyau1(p, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ACHECKNIL, /* read p->from */ - mips.ABEQ, /* read p->from, read p->reg */ - mips.ABNE, - mips.ABGTZ, - mips.ABGEZ, - mips.ABLTZ, - mips.ABLEZ, - - mips.ACMPEQD, - mips.ACMPEQF, - mips.ACMPGED, - mips.ACMPGEF, - mips.ACMPGTD, - mips.ACMPGTF, - mips.ABFPF, - mips.ABFPT, - - mips.AMUL, - mips.AMULU, - mips.ADIV, - mips.ADIVU, - mips.AMULV, - mips.AMULVU, - mips.ADIVV, - mips.ADIVVU: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub1(p, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - return 0 - - case mips.AJMP: /* read p->to */ - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case mips.ARET: /* funny */ - if s != nil { - return 0 - } - - // All registers die at this point, so claim - // everything is set (and not used). - return 3 - - case mips.AJAL: /* funny */ - if v.Type == obj.TYPE_REG { - // TODO(rsc): REG_R0 and REG_F0 used to be - // (when register numbers started at 0) exregoffset and exfregoffset, - // which are unset entirely. - // It's strange that this handles R0 and F0 differently from the other - // registers. Possible failure to optimize? - if mips.REG_R0 < v.Reg && v.Reg <= mips.REG_R31 { - return 2 - } - if v.Reg == mips.REGARG { - return 2 - } - if mips.REG_F0 < v.Reg && v.Reg <= mips.REG_F31 { - return 2 - } - } - - if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - // R0 is zero, used by DUFFZERO, cannot be substituted. - // R1 is ptr to memory, used and set, cannot be substituted. - case obj.ADUFFZERO: - if v.Type == obj.TYPE_REG { - if v.Reg == 0 { - return 1 - } - if v.Reg == 1 { - return 2 - } - } - - return 0 - - // R1, R2 are ptr to src, dst, used and set, cannot be substituted. - // R3 is scratch, set by DUFFCOPY, cannot be substituted. - case obj.ADUFFCOPY: - if v.Type == obj.TYPE_REG { - if v.Reg == 1 || v.Reg == 2 { - return 2 - } - if v.Reg == 3 { - return 3 - } - } - - return 0 - - case obj.ATEXT: /* funny */ - if v.Type == obj.TYPE_REG { - if v.Reg == mips.REGARG { - return 3 - } - } - return 0 - - case obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL, - obj.AVARLIVE, - obj.AUSEFIELD: - return 0 - } -} - -// copyas returns 1 if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - if regtyp(v) { - if a.Type == v.Type { - if a.Reg == v.Reg { - return true - } - } - } - return false -} - -// copyau returns 1 if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns true if p->reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg -} - -// copysub replaces v with s in a if f==true or indicates it if could if f==false. -// Returns true on failure to substitute (it always succeeds on mips). -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau(a, v) { - a.Reg = s.Reg - } - return false -} - -// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false. -// Returns true on failure to substitute (it always succeeds on mips). -// TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau1(p1, v) { - p1.Reg = s.Reg - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if regtyp(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == mips.REGSP -} diff --git a/src/cmd/compile/internal/mips64/reg.go b/src/cmd/compile/internal/mips64/reg.go index d3849332c8..8655f5b5a0 100644 --- a/src/cmd/compile/internal/mips64/reg.go +++ b/src/cmd/compile/internal/mips64/reg.go @@ -31,93 +31,6 @@ package mips64 import "cmd/internal/obj/mips" -import "cmd/compile/internal/gc" - -const ( - NREGVAR = 64 /* 32 general + 32 floating */ -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".R16", - ".R17", - ".R18", - ".R19", - ".R20", - ".R21", - ".R22", - ".R23", - ".R24", - ".R25", - ".R26", - ".R27", - ".R28", - ".R29", - ".R30", - ".R31", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", - ".F16", - ".F17", - ".F18", - ".F19", - ".F20", - ".F21", - ".F22", - ".F23", - ".F24", - ".F25", - ".F26", - ".F27", - ".F28", - ".F29", - ".F30", - ".F31", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - regbits := 1<<0 | RtoB(mips.REGSP) | RtoB(mips.REGG) | RtoB(mips.REGSB) | RtoB(mips.REGTMP) | RtoB(mips.REGLINK) | RtoB(mips.REG_R26) | RtoB(mips.REG_R27) - return regbits -} - -func doregbits(r int) uint64 { - return 0 -} /* * track register variables including external registers: @@ -140,19 +53,3 @@ func RtoB(r int) uint64 { } return 0 } - -func BtoR(b uint64) int { - b &= 0xffffffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + mips.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 32 - if b == 0 { - return 0 - } - return gc.Bitno(b) + mips.REG_F0 -} diff --git a/src/cmd/compile/internal/ppc64/cgen.go b/src/cmd/compile/internal/ppc64/cgen.go deleted file mode 100644 index f4cc9c48dc..0000000000 --- a/src/cmd/compile/internal/ppc64/cgen.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package ppc64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" -) - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - // determine alignment. - // want to avoid unaligned access, so have to use - // smaller operations for less aligned types. - // for example moving [4]byte must use 4 MOVB not 1 MOVW. - align := int(n.Type.Align) - - var op obj.As - switch align { - default: - gc.Fatalf("sgen: invalid alignment %d for %v", align, n.Type) - - case 1: - op = ppc64.AMOVBU - - case 2: - op = ppc64.AMOVHU - - case 4: - op = ppc64.AMOVWZU // there is no lwau, only lwaux - - case 8: - op = ppc64.AMOVDU - } - - if w%int64(align) != 0 { - gc.Fatalf("sgen: unaligned size %d (align=%d) for %v", w, align, n.Type) - } - c := int32(w / int64(align)) - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - dir := align - - if osrc < odst && odst < osrc+w { - dir = -dir - } - - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(ppc64.AMOVD, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - - // set up end marker - var nend gc.Node - - // move src and dest to the end of block if necessary - if dir < 0 { - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - gins(ppc64.AMOVD, &src, &nend) - } - - p := gins(ppc64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - p = gins(ppc64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - } else { - p := gins(ppc64.AADD, nil, &src) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - p = gins(ppc64.AADD, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(-dir) - - if c >= 4 { - gc.Regalloc(&nend, gc.Types[gc.Tptr], nil) - p := gins(ppc64.AMOVD, &src, &nend) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - } - } - - // move - // TODO: enable duffcopy for larger copies. - if c >= 4 { - p := gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - ploop := p - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - - p = gins(ppc64.ACMP, &src, &nend) - - gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), ploop) - gc.Regfree(&nend) - } else { - // TODO(austin): Instead of generating ADD $-8,R8; ADD - // $-8,R7; n*(MOVDU 8(R8),R9; MOVDU R9,8(R7);) just - // generate the offsets directly and eliminate the - // ADDs. That will produce shorter, more - // pipeline-able code. - var p *obj.Prog - for ; c > 0; c-- { - p = gins(op, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = int64(dir) - - p = gins(op, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(dir) - } - } - - gc.Regfree(&dst) - gc.Regfree(&src) - gc.Regfree(&tmp) -} diff --git a/src/cmd/compile/internal/ppc64/galign.go b/src/cmd/compile/internal/ppc64/galign.go index a3fab794e6..5491c125e1 100644 --- a/src/cmd/compile/internal/ppc64/galign.go +++ b/src/cmd/compile/internal/ppc64/galign.go @@ -35,36 +35,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = ssaMarkMoves diff --git a/src/cmd/compile/internal/ppc64/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go index eeda4a2f2c..f36eedc553 100644 --- a/src/cmd/compile/internal/ppc64/ggen.go +++ b/src/cmd/compile/internal/ppc64/ggen.go @@ -8,7 +8,6 @@ import ( "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/ppc64" - "fmt" ) func defframe(ptxt *obj.Prog) { @@ -114,440 +113,3 @@ func ginsnop() { gc.Nodreg(®, gc.Types[gc.TINT], ppc64.REG_R0) gins(ppc64.AOR, ®, ®) } - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will generate undefined result. - // Also need to explicitly trap on division on zero, - // the hardware will silently generate undefined result. - // DIVW will leave unpredictable result in higher 32-bit, - // so always use DIVD/DIVDU. - t := nl.Type - - t0 := t - check := false - if t.IsSigned() { - check = true - if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := gins(optoas(gc.OCMP, t), &tr, nil) - - p1.To.Type = obj.TYPE_REG - p1.To.Reg = ppc64.REGZERO - p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - var p2 *obj.Prog - if check { - var nm1 gc.Node - gc.Nodconst(&nm1, t, -1) - gins(optoas(gc.OCMP, t), &tr, &nm1) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), nil, &tl) - - gmove(&tl, res) - } else { - // a % (-1) is 0. - var nz gc.Node - gc.Nodconst(&nz, t, 0) - - gmove(&nz, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - p1 = gins(a, &tr, &tl) - if op == gc.ODIV { - gc.Regfree(&tr) - gmove(&tl, res) - } else { - // A%B = A-(A/B*B) - var tm gc.Node - gc.Regalloc(&tm, t, nil) - - // patch div to use the 3 register form - // TODO(minux): add gins3? - p1.Reg = p1.To.Reg - - p1.To.Reg = tm.Reg - gins(optoas(gc.OMUL, t), &tr, &tm) - gc.Regfree(&tr) - gins(optoas(gc.OSUB, t), &tm, &tl) - gc.Regfree(&tm) - gmove(&tl, res) - } - - gc.Regfree(&tl) - if check { - gc.Patch(p2, gc.Pc) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - t := nl.Type - w := t.Width * 8 - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(ppc64.ASRAD, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(ppc64.ASRD, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = w - - case gc.TINT64, - gc.TUINT64: - if t.IsSigned() { - gins(ppc64.AMULHD, &n2, &n1) - } else { - gins(ppc64.AMULHDU, &n2, &n1) - } - - default: - gc.Fatalf("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, +1) - if op == gc.ORSH && nl.Type.IsSigned() { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - w := uint64(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 8 // bytes - q := w / 8 // dwords - - if gc.Reginuse(ppc64.REGRT1) { - gc.Fatalf("%v in use during clearfat", obj.Rconv(ppc64.REGRT1)) - } - - var r0 gc.Node - gc.Nodreg(&r0, gc.Types[gc.TUINT64], ppc64.REGZERO) - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], ppc64.REGRT1) - gc.Regrealloc(&dst) - gc.Agen(nl, &dst) - - var boff uint64 - if q > 128 { - p := gins(ppc64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p = gins(ppc64.AMOVD, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = int64(q * 8) - - p = gins(ppc64.AMOVDU, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = 8 - pl := p - - p = gins(ppc64.ACMP, &dst, &end) - gc.Patch(gc.Gbranch(ppc64.ABNE, nil, 0), pl) - - gc.Regfree(&end) - - // The loop leaves R3 on the last zeroed dword - boff = 8 - } else if q >= 4 { - p := gins(ppc64.ASUB, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 8 - f := gc.Sysfunc("duffzero") - p = gins(obj.ADUFFZERO, nil, f) - gc.Afunclit(&p.To, f) - - // 4 and 128 = magic constants: see ../../runtime/asm_ppc64x.s - p.To.Offset = int64(4 * (128 - q)) - - // duffzero leaves R3 on the last zeroed dword - boff = 8 - } else { - var p *obj.Prog - for t := uint64(0); t < q; t++ { - p = gins(ppc64.AMOVD, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(8 * t) - } - - boff = 8 * q - } - - var p *obj.Prog - for t := uint64(0); t < c; t++ { - p = gins(ppc64.AMOVB, &r0, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = int64(t + boff) - } - - gc.Regfree(&dst) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatalf("invalid nil check %v\n", p) - } - - /* - // check is - // TD $4, R0, arg (R0 is always zero) - // eqv. to: - // tdeq r0, arg - // NOTE: this needs special runtime support to make SIGTRAP recoverable. - reg = p->from.reg; - p->as = ATD; - p->from = p->to = p->from3 = zprog.from; - p->from.type = TYPE_CONST; - p->from.offset = 4; - p->from.reg = 0; - p->reg = REGZERO; - p->to.type = TYPE_REG; - p->to.reg = reg; - */ - // check is - // CMP arg, R0 - // BNE 2(PC) [likely] - // MOVD R0, 0(R0) - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = ppc64.ACMP - p.To.Type = obj.TYPE_REG - p.To.Reg = ppc64.REGZERO - p1.As = ppc64.ABNE - - //p1->from.type = TYPE_CONST; - //p1->from.offset = 1; // likely - p1.To.Type = obj.TYPE_BRANCH - - p1.To.Val = p2.Link - - // crash by write to memory address 0. - p2.As = ppc64.AMOVD - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = ppc64.REGZERO - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = ppc64.REGZERO - p2.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, ppc64.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/compile/internal/ppc64/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go index 86dc241c36..3fa151feed 100644 --- a/src/cmd/compile/internal/ppc64/gsubr.go +++ b/src/cmd/compile/internal/ppc64/gsubr.go @@ -31,7 +31,6 @@ package ppc64 import ( - "cmd/compile/internal/big" "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/ppc64" @@ -112,438 +111,6 @@ func ginscon2(as obj.As, n2 *gc.Node, c int64) { gc.Regfree(&ntmp) } -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { - // Reverse comparison to place constant last. - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - - var r1, r2, g1, g2 gc.Node - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - if t.IsInteger() && gc.Isconst(n2, gc.CTINT) { - ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64()) - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - rawgins(optoas(gc.OCMP, t), &r1, &r2) - gc.Regfree(&g2) - gc.Regfree(&r2) - } - gc.Regfree(&g1) - gc.Regfree(&r1) - return gc.Gbranch(optoas(op, t), nil, likely) -} - -// set up nodes representing 2^63 -var ( - bigi gc.Node - bigf gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - - bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64]) -} - -/* - * generate move: - * t = f - * hard part is conversions. - */ -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %L -> %L\n", f, t) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - // cannot have two memory operands - var r2 gc.Node - var r1 gc.Node - var a obj.As - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - switch tt { - default: - f.Convconst(&con, t.Type) - - case gc.TINT32, - gc.TINT16, - gc.TINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(ppc64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - case gc.TUINT32, - gc.TUINT16, - gc.TUINT8: - var con gc.Node - f.Convconst(&con, gc.Types[gc.TUINT64]) - var r1 gc.Node - gc.Regalloc(&r1, con.Type, t) - gins(ppc64.AMOVD, &con, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - f = &con - ft = tt // so big switch will choose a simple mov - - // constants can't move directly to memory. - if gc.Ismem(t) { - goto hard - } - } - - // float constants come from memory. - //if(isfloat[tt]) - // goto hard; - - // 64-bit immediates are also from memory. - //if(isint[tt]) - // goto hard; - //// 64-bit immediates are really 32-bit sign-extended - //// unless moving into a register. - //if(isint[tt]) { - // if(mpcmpfixfix(con.val.u.xval, minintval[TINT32]) < 0) - // goto hard; - // if(mpcmpfixfix(con.val.u.xval, maxintval[TINT32]) > 0) - // goto hard; - //} - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatalf("gmove %L -> %L", f.Type, t.Type) - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, - // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = ppc64.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, // same size - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - // truncate - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = ppc64.AMOVBZ - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, - // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = ppc64.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, // same size - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - // truncate - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = ppc64.AMOVHZ - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, - // truncate - gc.TUINT64<<16 | gc.TINT32: - a = ppc64.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, // same size - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = ppc64.AMOVWZ - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = ppc64.AMOVD - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = ppc64.AMOVB - - goto rdst - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = ppc64.AMOVBZ - - goto rdst - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = ppc64.AMOVH - - goto rdst - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = ppc64.AMOVHZ - - goto rdst - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - a = ppc64.AMOVW - - goto rdst - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - a = ppc64.AMOVWZ - - goto rdst - - //warn("gmove: convert float to int not implemented: %N -> %N\n", f, t); - //return; - // algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT64, - gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32, - gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - bignodes() - - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[ft], f) - gmove(f, &r1) - if tt == gc.TUINT64 { - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) - gmove(&bigf, &r2) - gins(ppc64.AFCMPU, &r1, &r2) - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1) - gins(ppc64.AFSUB, &r2, &r1) - gc.Patch(p1, gc.Pc) - gc.Regfree(&r2) - } - - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], nil) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[gc.TINT64], t) - gins(ppc64.AFCTIDZ, &r1, &r2) - p1 := gins(ppc64.AFMOVD, &r2, nil) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = ppc64.REGSP - p1.To.Offset = -8 - p1 = gins(ppc64.AMOVD, nil, &r3) - p1.From.Type = obj.TYPE_MEM - p1.From.Reg = ppc64.REGSP - p1.From.Offset = -8 - gc.Regfree(&r2) - gc.Regfree(&r1) - if tt == gc.TUINT64 { - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TFLOAT64]), nil, +1) // use CR0 here again - gc.Nodreg(&r1, gc.Types[gc.TINT64], ppc64.REGTMP) - gins(ppc64.AMOVD, &bigi, &r1) - gins(ppc64.AADD, &r1, &r3) - gc.Patch(p1, gc.Pc) - } - - gmove(&r3, t) - gc.Regfree(&r3) - return - - /* - * integer to float - */ - case gc.TINT32<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64, - gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - bignodes() - - // The algorithm is: - // if small enough, use native int64 -> float64 conversion, - // otherwise halve (x -> (x>>1)|(x&1)), convert, and double. - // Note: could use FCFIDU instead if target supports it. - var r1 gc.Node - gc.Regalloc(&r1, gc.Types[gc.TINT64], nil) - gmove(f, &r1) - if ft == gc.TUINT64 { - gc.Nodreg(&r2, gc.Types[gc.TUINT64], ppc64.REGTMP) - gmove(&bigi, &r2) - gins(ppc64.ACMPU, &r1, &r2) - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1) - var r3 gc.Node - gc.Regalloc(&r3, gc.Types[gc.TUINT64], nil) - p2 := gins(ppc64.AANDCC, nil, &r3) // andi. - p2.Reg = r1.Reg - p2.From.Type = obj.TYPE_CONST - p2.From.Offset = 1 - p3 := gins(ppc64.ASRD, nil, &r1) - p3.From.Type = obj.TYPE_CONST - p3.From.Offset = 1 - gins(ppc64.AOR, &r3, &r1) - gc.Regfree(&r3) - gc.Patch(p1, gc.Pc) - } - gc.Regalloc(&r2, gc.Types[gc.TFLOAT64], t) - p1 := gins(ppc64.AMOVD, &r1, nil) - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = ppc64.REGSP - p1.To.Offset = -8 - p1 = gins(ppc64.AFMOVD, nil, &r2) - p1.From.Type = obj.TYPE_MEM - p1.From.Reg = ppc64.REGSP - p1.From.Offset = -8 - gins(ppc64.AFCFID, &r2, &r2) - gc.Regfree(&r1) - if ft == gc.TUINT64 { - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT64]), nil, +1) // use CR0 here again - gins(ppc64.AFADD, &r2, &r2) - gc.Patch(p1, gc.Pc) - } - gmove(&r2, t) - gc.Regfree(&r2) - return - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = ppc64.AFMOVS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = ppc64.AFMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = ppc64.AFMOVS - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = ppc64.AFRSP - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - // gins is called by the front end. // It synthesizes some multiple-instruction sequences // so the front end can stay simpler. @@ -675,396 +242,3 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog { return p } - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - OSQRT_ = uint32(gc.OSQRT) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry for op=%v type=%v", op, t) - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = ppc64.ABEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = ppc64.ABNE - - case OLT_ | gc.TINT8, // ACMP - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64, - OLT_ | gc.TUINT8, - // ACMPU - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64, - OLT_ | gc.TFLOAT32, - // AFCMPU - OLT_ | gc.TFLOAT64: - a = ppc64.ABLT - - case OLE_ | gc.TINT8, // ACMP - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64, - OLE_ | gc.TUINT8, - // ACMPU - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64: - // No OLE for floats, because it mishandles NaN. - // Front end must reverse comparison or use OLT and OEQ together. - a = ppc64.ABLE - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64, - OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64, - OGT_ | gc.TFLOAT32, - OGT_ | gc.TFLOAT64: - a = ppc64.ABGT - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64, - OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64: - // No OGE for floats, because it mishandles NaN. - // Front end must reverse comparison or use OLT and OEQ together. - a = ppc64.ABGE - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TINT16, - OCMP_ | gc.TINT32, - OCMP_ | gc.TPTR32, - OCMP_ | gc.TINT64: - a = ppc64.ACMP - - case OCMP_ | gc.TUINT8, - OCMP_ | gc.TUINT16, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TUINT64, - OCMP_ | gc.TPTR64: - a = ppc64.ACMPU - - case OCMP_ | gc.TFLOAT32, - OCMP_ | gc.TFLOAT64: - a = ppc64.AFCMPU - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8: - a = ppc64.AMOVB - - case OAS_ | gc.TUINT8: - a = ppc64.AMOVBZ - - case OAS_ | gc.TINT16: - a = ppc64.AMOVH - - case OAS_ | gc.TUINT16: - a = ppc64.AMOVHZ - - case OAS_ | gc.TINT32: - a = ppc64.AMOVW - - case OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = ppc64.AMOVWZ - - case OAS_ | gc.TINT64, - OAS_ | gc.TUINT64, - OAS_ | gc.TPTR64: - a = ppc64.AMOVD - - case OAS_ | gc.TFLOAT32: - a = ppc64.AFMOVS - - case OAS_ | gc.TFLOAT64: - a = ppc64.AFMOVD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8, - OADD_ | gc.TINT16, - OADD_ | gc.TUINT16, - OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32, - OADD_ | gc.TINT64, - OADD_ | gc.TUINT64, - OADD_ | gc.TPTR64: - a = ppc64.AADD - - case OADD_ | gc.TFLOAT32: - a = ppc64.AFADDS - - case OADD_ | gc.TFLOAT64: - a = ppc64.AFADD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8, - OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16, - OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32, - OSUB_ | gc.TINT64, - OSUB_ | gc.TUINT64, - OSUB_ | gc.TPTR64: - a = ppc64.ASUB - - case OSUB_ | gc.TFLOAT32: - a = ppc64.AFSUBS - - case OSUB_ | gc.TFLOAT64: - a = ppc64.AFSUB - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8, - OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16, - OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32, - OMINUS_ | gc.TINT64, - OMINUS_ | gc.TUINT64, - OMINUS_ | gc.TPTR64: - a = ppc64.ANEG - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8, - OAND_ | gc.TINT16, - OAND_ | gc.TUINT16, - OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32, - OAND_ | gc.TINT64, - OAND_ | gc.TUINT64, - OAND_ | gc.TPTR64: - a = ppc64.AAND - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8, - OOR_ | gc.TINT16, - OOR_ | gc.TUINT16, - OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32, - OOR_ | gc.TINT64, - OOR_ | gc.TUINT64, - OOR_ | gc.TPTR64: - a = ppc64.AOR - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8, - OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16, - OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32, - OXOR_ | gc.TINT64, - OXOR_ | gc.TUINT64, - OXOR_ | gc.TPTR64: - a = ppc64.AXOR - - // TODO(minux): handle rotates - //case CASE(OLROT, TINT8): - //case CASE(OLROT, TUINT8): - //case CASE(OLROT, TINT16): - //case CASE(OLROT, TUINT16): - //case CASE(OLROT, TINT32): - //case CASE(OLROT, TUINT32): - //case CASE(OLROT, TPTR32): - //case CASE(OLROT, TINT64): - //case CASE(OLROT, TUINT64): - //case CASE(OLROT, TPTR64): - // a = 0//???; RLDC? - // break; - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8, - OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16, - OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32, - OLSH_ | gc.TINT64, - OLSH_ | gc.TUINT64, - OLSH_ | gc.TPTR64: - a = ppc64.ASLD - - case ORSH_ | gc.TUINT8, - ORSH_ | gc.TUINT16, - ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32, - ORSH_ | gc.TUINT64, - ORSH_ | gc.TPTR64: - a = ppc64.ASRD - - case ORSH_ | gc.TINT8, - ORSH_ | gc.TINT16, - ORSH_ | gc.TINT32, - ORSH_ | gc.TINT64: - a = ppc64.ASRAD - - // TODO(minux): handle rotates - //case CASE(ORROTC, TINT8): - //case CASE(ORROTC, TUINT8): - //case CASE(ORROTC, TINT16): - //case CASE(ORROTC, TUINT16): - //case CASE(ORROTC, TINT32): - //case CASE(ORROTC, TUINT32): - //case CASE(ORROTC, TINT64): - //case CASE(ORROTC, TUINT64): - // a = 0//??? RLDC?? - // break; - - case OHMUL_ | gc.TINT64: - a = ppc64.AMULHD - - case OHMUL_ | gc.TUINT64, - OHMUL_ | gc.TPTR64: - a = ppc64.AMULHDU - - case OMUL_ | gc.TINT8, - OMUL_ | gc.TINT16, - OMUL_ | gc.TINT32, - OMUL_ | gc.TINT64: - a = ppc64.AMULLD - - case OMUL_ | gc.TUINT8, - OMUL_ | gc.TUINT16, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32, - // don't use word multiply, the high 32-bit are undefined. - OMUL_ | gc.TUINT64, - OMUL_ | gc.TPTR64: - // for 64-bit multiplies, signedness doesn't matter. - a = ppc64.AMULLD - - case OMUL_ | gc.TFLOAT32: - a = ppc64.AFMULS - - case OMUL_ | gc.TFLOAT64: - a = ppc64.AFMUL - - case ODIV_ | gc.TINT8, - ODIV_ | gc.TINT16, - ODIV_ | gc.TINT32, - ODIV_ | gc.TINT64: - a = ppc64.ADIVD - - case ODIV_ | gc.TUINT8, - ODIV_ | gc.TUINT16, - ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - ODIV_ | gc.TUINT64, - ODIV_ | gc.TPTR64: - a = ppc64.ADIVDU - - case ODIV_ | gc.TFLOAT32: - a = ppc64.AFDIVS - - case ODIV_ | gc.TFLOAT64: - a = ppc64.AFDIV - - case OSQRT_ | gc.TFLOAT64: - a = ppc64.AFSQRT - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -func xgen(n *gc.Node, a *gc.Node, o int) bool { - // TODO(minux) - - return -1 != 0 /*TypeKind(100016)*/ -} - -func sudoclean() { - return -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - // TODO(minux) - - *a = obj.Addr{} - return false -} diff --git a/src/cmd/compile/internal/ppc64/peep.go b/src/cmd/compile/internal/ppc64/peep.go deleted file mode 100644 index ebdcc03292..0000000000 --- a/src/cmd/compile/internal/ppc64/peep.go +++ /dev/null @@ -1,1032 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package ppc64 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/ppc64" - "fmt" -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - var p *obj.Prog - var r *gc.Flow - var t obj.As -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - - // TODO(austin) Handle smaller moves. arm and amd64 - // distinguish between moves that moves that *must* - // sign/zero extend and moves that don't care so they - // can eliminate moves that don't care without - // breaking moves that do care. This might let us - // simplify or remove the next peep loop, too. - if p.As == ppc64.AMOVD || p.As == ppc64.AFMOVD { - if regtyp(&p.To) { - // Try to eliminate reg->reg moves - if regtyp(&p.From) { - if p.From.Type == p.To.Type { - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - - // Convert uses to $0 to uses of R0 and - // propagate R0 - if regzer(&p.From) { - if p.To.Type == obj.TYPE_REG { - p.From.Type = obj.TYPE_REG - p.From.Reg = ppc64.REGZERO - if copyprop(r) { - excise(r) - t++ - } else if subprop(r) && copyprop(r) { - excise(r) - t++ - } - } - } - } - } - } - - if t != 0 { - goto loop1 - } - - /* - * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) - */ - var p1 *obj.Prog - var r1 *gc.Flow - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - default: - continue - - case ppc64.AMOVH, - ppc64.AMOVHZ, - ppc64.AMOVB, - ppc64.AMOVBZ, - ppc64.AMOVW, - ppc64.AMOVWZ: - if p.To.Type != obj.TYPE_REG { - continue - } - } - - r1 = r.Link - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.As != p.As { - continue - } - if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { - continue - } - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { - continue - } - excise(r1) - } - - if gc.Debug['D'] > 1 { - goto ret /* allow following code improvement to be suppressed */ - } - - /* - * look for OP x,y,R; CMP R, $0 -> OPCC x,y,R - * when OP can set condition codes correctly - */ - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case ppc64.ACMP, - ppc64.ACMPW: /* always safe? */ - if !regzer(&p.To) { - continue - } - r1 = r.S1 - if r1 == nil { - continue - } - switch r1.Prog.As { - default: - continue - - /* the conditions can be complex and these are currently little used */ - case ppc64.ABCL, - ppc64.ABC: - continue - - case ppc64.ABEQ, - ppc64.ABGE, - ppc64.ABGT, - ppc64.ABLE, - ppc64.ABLT, - ppc64.ABNE, - ppc64.ABVC, - ppc64.ABVS: - break - } - - r1 = r - for { - r1 = gc.Uniqp(r1) - if r1 == nil || r1.Prog.As != obj.ANOP { - break - } - } - - if r1 == nil { - continue - } - p1 = r1.Prog - if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.From.Reg { - continue - } - switch p1.As { - /* irregular instructions */ - case ppc64.ASUB, - ppc64.AADD, - ppc64.AXOR, - ppc64.AOR: - if p1.From.Type == obj.TYPE_CONST || p1.From.Type == obj.TYPE_ADDR { - continue - } - } - - switch p1.As { - default: - continue - - case ppc64.AMOVW, - ppc64.AMOVD: - if p1.From.Type != obj.TYPE_REG { - continue - } - continue - - case ppc64.AANDCC, - ppc64.AANDNCC, - ppc64.AORCC, - ppc64.AORNCC, - ppc64.AXORCC, - ppc64.ASUBCC, - ppc64.ASUBECC, - ppc64.ASUBMECC, - ppc64.ASUBZECC, - ppc64.AADDCC, - ppc64.AADDCCC, - ppc64.AADDECC, - ppc64.AADDMECC, - ppc64.AADDZECC, - ppc64.ARLWMICC, - ppc64.ARLWNMCC, - /* don't deal with floating point instructions for now */ - /* - case AFABS: - case AFADD: - case AFADDS: - case AFCTIW: - case AFCTIWZ: - case AFDIV: - case AFDIVS: - case AFMADD: - case AFMADDS: - case AFMOVD: - case AFMSUB: - case AFMSUBS: - case AFMUL: - case AFMULS: - case AFNABS: - case AFNEG: - case AFNMADD: - case AFNMADDS: - case AFNMSUB: - case AFNMSUBS: - case AFRSP: - case AFSUB: - case AFSUBS: - case ACNTLZW: - case AMTFSB0: - case AMTFSB1: - */ - ppc64.AADD, - ppc64.AADDV, - ppc64.AADDC, - ppc64.AADDCV, - ppc64.AADDME, - ppc64.AADDMEV, - ppc64.AADDE, - ppc64.AADDEV, - ppc64.AADDZE, - ppc64.AADDZEV, - ppc64.AAND, - ppc64.AANDN, - ppc64.ADIVW, - ppc64.ADIVWV, - ppc64.ADIVWU, - ppc64.ADIVWUV, - ppc64.ADIVD, - ppc64.ADIVDV, - ppc64.ADIVDU, - ppc64.ADIVDUV, - ppc64.AEQV, - ppc64.AEXTSB, - ppc64.AEXTSH, - ppc64.AEXTSW, - ppc64.AMULHW, - ppc64.AMULHWU, - ppc64.AMULLW, - ppc64.AMULLWV, - ppc64.AMULHD, - ppc64.AMULHDU, - ppc64.AMULLD, - ppc64.AMULLDV, - ppc64.ANAND, - ppc64.ANEG, - ppc64.ANEGV, - ppc64.ANOR, - ppc64.AOR, - ppc64.AORN, - ppc64.AREM, - ppc64.AREMV, - ppc64.AREMU, - ppc64.AREMUV, - ppc64.AREMD, - ppc64.AREMDV, - ppc64.AREMDU, - ppc64.AREMDUV, - ppc64.ARLWMI, - ppc64.ARLWNM, - ppc64.ASLW, - ppc64.ASRAW, - ppc64.ASRW, - ppc64.ASLD, - ppc64.ASRAD, - ppc64.ASRD, - ppc64.ASUB, - ppc64.ASUBV, - ppc64.ASUBC, - ppc64.ASUBCV, - ppc64.ASUBME, - ppc64.ASUBMEV, - ppc64.ASUBE, - ppc64.ASUBEV, - ppc64.ASUBZE, - ppc64.ASUBZEV, - ppc64.AXOR: - t = variant2as(p1.As, as2variant(p1.As)|V_CC) - } - - if gc.Debug['D'] != 0 { - fmt.Printf("cmp %v; %v -> ", p1, p) - } - p1.As = t - if gc.Debug['D'] != 0 { - fmt.Printf("%v\n", p1) - } - excise(r) - continue - } - } - -ret: - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -// regzer returns true if a's value is 0 (a is R0 or $0) -func regzer(a *obj.Addr) bool { - if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { - if a.Sym == nil && a.Reg == 0 { - if a.Offset == 0 { - return true - } - } - } - return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGZERO -} - -func regtyp(a *obj.Addr) bool { - // TODO(rsc): Floating point register exclusions? - return a.Type == obj.TYPE_REG && ppc64.REG_R0 <= a.Reg && a.Reg <= ppc64.REG_F31 && a.Reg != ppc64.REGZERO -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R1 - * ADD b, R1 / no use of R2 - * MOV R1, R2 - * would be converted to - * MOV a, R2 - * ADD b, R2 - * MOV R2, R1 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - * - * r0 (the argument, not the register) is the MOV at the end of the - * above sequences. This returns 1 if it modified any instructions. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { - if p.To.Type == v1.Type { - if p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub1(p, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - v1.Reg, v2.Reg = v2.Reg, v1.Reg - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - } - } - - if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail (v1->v2 move must remain) - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success (caller can remove v1->v2 move) - */ -func copyprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - if gc.Debug['P'] != 0 { - fmt.Printf("eliminating self-move: %v\n", r0.Prog) - } - return true - } - - gactive++ - if gc.Debug['P'] != 0 { - fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) - } - return copy1(v1, v2, r0.S1, false) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns 1 if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = true - - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// 1 if v only used -// 2 if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// 3 if v is only set -// 4 if v is set in one address and used in another (so addresses -// can be rewritten independently) -// 0 otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - if p.From3Type() != obj.TYPE_NONE { - // 9g never generates a from3 - fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", p.As) - return 2 - - case obj.ANOP, /* read p->from, write p->to */ - ppc64.AMOVH, - ppc64.AMOVHZ, - ppc64.AMOVB, - ppc64.AMOVBZ, - ppc64.AMOVW, - ppc64.AMOVWZ, - ppc64.AMOVD, - ppc64.ANEG, - ppc64.ANEGCC, - ppc64.AADDME, - ppc64.AADDMECC, - ppc64.AADDZE, - ppc64.AADDZECC, - ppc64.ASUBME, - ppc64.ASUBMECC, - ppc64.ASUBZE, - ppc64.ASUBZECC, - ppc64.AFCTIW, - ppc64.AFCTIWZ, - ppc64.AFCTID, - ppc64.AFCTIDZ, - ppc64.AFCFID, - ppc64.AFCFIDCC, - ppc64.AFCFIDU, - ppc64.AFCFIDUCC, - ppc64.AFMOVS, - ppc64.AFMOVD, - ppc64.AFRSP, - ppc64.AFNEG, - ppc64.AFNEGCC, - ppc64.AFSQRT: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - // p->to only indirectly uses v - return 1 - } - - return 0 - - case ppc64.AMOVBU, /* rar p->from, write p->to or read p->from, rar p->to */ - ppc64.AMOVBZU, - ppc64.AMOVHU, - ppc64.AMOVHZU, - ppc64.AMOVWZU, - ppc64.AMOVDU: - if p.From.Type == obj.TYPE_MEM { - if copyas(&p.From, v) { - // No s!=nil check; need to fail - // anyway in that case - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyas(&p.To, v) { - return 3 - } - } else if p.To.Type == obj.TYPE_MEM { - if copyas(&p.To, v) { - return 2 - } - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - } else { - fmt.Printf("copyu: bad %v\n", p) - } - - return 0 - - case ppc64.ARLWMI, /* read p->from, read p->reg, rar p->to */ - ppc64.ARLWMICC: - if copyas(&p.To, v) { - return 2 - } - fallthrough - - /* fall through */ - case ppc64.AADD, - /* read p->from, read p->reg, write p->to */ - ppc64.AADDC, - ppc64.AADDE, - ppc64.ASUB, - ppc64.ASLW, - ppc64.ASRW, - ppc64.ASRAW, - ppc64.ASLD, - ppc64.ASRD, - ppc64.ASRAD, - ppc64.AOR, - ppc64.AORCC, - ppc64.AORN, - ppc64.AORNCC, - ppc64.AAND, - ppc64.AANDCC, - ppc64.AANDN, - ppc64.AANDNCC, - ppc64.ANAND, - ppc64.ANANDCC, - ppc64.ANOR, - ppc64.ANORCC, - ppc64.AXOR, - ppc64.AMULHW, - ppc64.AMULHWU, - ppc64.AMULLW, - ppc64.AMULLD, - ppc64.ADIVW, - ppc64.ADIVD, - ppc64.ADIVWU, - ppc64.ADIVDU, - ppc64.AREM, - ppc64.AREMU, - ppc64.AREMD, - ppc64.AREMDU, - ppc64.ARLWNM, - ppc64.ARLWNMCC, - ppc64.AFADDS, - ppc64.AFADD, - ppc64.AFSUBS, - ppc64.AFSUB, - ppc64.AFMULS, - ppc64.AFMUL, - ppc64.AFDIVS, - ppc64.AFDIV: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub1(p, v, s, true) { - return 1 - } - - // Update only indirect uses of v in p->to - if !copyas(&p.To, v) { - if copysub(&p.To, v, s, true) { - return 1 - } - } - return 0 - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - // Fix up implicit reg (e.g., ADD - // R3,R4 -> ADD R3,R4,R4) so we can - // update reg and to separately. - p.Reg = p.To.Reg - } - - if copyau(&p.From, v) { - return 4 - } - if copyau1(p, v) { - return 4 - } - return 3 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau1(p, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - case ppc64.ABEQ, - ppc64.ABGT, - ppc64.ABGE, - ppc64.ABLT, - ppc64.ABLE, - ppc64.ABNE, - ppc64.ABVC, - ppc64.ABVS: - return 0 - - case obj.ACHECKNIL, /* read p->from */ - ppc64.ACMP, /* read p->from, read p->to */ - ppc64.ACMPU, - ppc64.ACMPW, - ppc64.ACMPWU, - ppc64.AFCMPO, - ppc64.AFCMPU: - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - return 0 - - // 9g never generates a branch to a GPR (this isn't - // even a normal instruction; liblink turns it in to a - // mov and a branch). - case ppc64.ABR: /* read p->to */ - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: /* funny */ - if s != nil { - return 0 - } - - // All registers die at this point, so claim - // everything is set (and not used). - return 3 - - case ppc64.ABL: /* funny */ - if v.Type == obj.TYPE_REG { - // TODO(rsc): REG_R0 and REG_F0 used to be - // (when register numbers started at 0) exregoffset and exfregoffset, - // which are unset entirely. - // It's strange that this handles R0 and F0 differently from the other - // registers. Possible failure to optimize? - if ppc64.REG_R0 < v.Reg && v.Reg <= ppc64.REGEXT { - return 2 - } - if v.Reg == ppc64.REGARG { - return 2 - } - if ppc64.REG_F0 < v.Reg && v.Reg <= ppc64.FREGEXT { - return 2 - } - } - - if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - if copyau(&p.To, v) { - return 4 - } - return 3 - - // R0 is zero, used by DUFFZERO, cannot be substituted. - // R3 is ptr to memory, used and set, cannot be substituted. - case obj.ADUFFZERO: - if v.Type == obj.TYPE_REG { - if v.Reg == 0 { - return 1 - } - if v.Reg == 3 { - return 2 - } - } - - return 0 - - // R3, R4 are ptr to src, dst, used and set, cannot be substituted. - // R5 is scratch, set by DUFFCOPY, cannot be substituted. - case obj.ADUFFCOPY: - if v.Type == obj.TYPE_REG { - if v.Reg == 3 || v.Reg == 4 { - return 2 - } - if v.Reg == 5 { - return 3 - } - } - - return 0 - - case obj.ATEXT: /* funny */ - if v.Type == obj.TYPE_REG { - if v.Reg == ppc64.REGARG { - return 3 - } - } - return 0 - - case obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL, - obj.AVARLIVE, - obj.AUSEFIELD: - return 0 - } -} - -// copyas returns true if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - return regtyp(v) && a.Type == v.Type && a.Reg == v.Reg -} - -// copyau returns true if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns true if p->reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg -} - -// copysub replaces v with s in a if f==true or indicates it if could if f==false. -// Returns true on failure to substitute (it always succeeds on ppc64). -// TODO(dfc) remove unused return value and callers where f=false. -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau(a, v) { - a.Reg = s.Reg - } - return false -} - -// copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false. -// Returns true on failure to substitute (it always succeeds on ppc64). -// TODO(dfc) remove unused return value and callers where f=false. -func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { - if f && copyau1(p1, v) { - p1.Reg = s.Reg - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if regtyp(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == ppc64.REGSP -} diff --git a/src/cmd/compile/internal/ppc64/reg.go b/src/cmd/compile/internal/ppc64/reg.go index f7033f6ef8..2dce8cda9d 100644 --- a/src/cmd/compile/internal/ppc64/reg.go +++ b/src/cmd/compile/internal/ppc64/reg.go @@ -31,100 +31,6 @@ package ppc64 import "cmd/internal/obj/ppc64" -import "cmd/compile/internal/gc" - -const ( - NREGVAR = 64 /* 32 general + 32 floating */ -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".R16", - ".R17", - ".R18", - ".R19", - ".R20", - ".R21", - ".R22", - ".R23", - ".R24", - ".R25", - ".R26", - ".R27", - ".R28", - ".R29", - ".R30", - ".R31", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", - ".F16", - ".F17", - ".F18", - ".F19", - ".F20", - ".F21", - ".F22", - ".F23", - ".F24", - ".F25", - ".F26", - ".F27", - ".F28", - ".F29", - ".F30", - ".F31", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - regbits := 1<<0 | RtoB(ppc64.REGSP) | RtoB(ppc64.REGG) | RtoB(ppc64.REGTLS) | RtoB(ppc64.REGTMP) - - if gc.Ctxt.Flag_shared { - // When compiling Go into PIC, R2 is reserved to be the TOC pointer - // and R12 so that calls via function pointer can stomp on it. - regbits |= RtoB(ppc64.REG_R2) - regbits |= RtoB(ppc64.REG_R12) - } - return regbits -} - -func doregbits(r int) uint64 { - return 0 -} /* * track register variables including external registers: @@ -147,19 +53,3 @@ func RtoB(r int) uint64 { } return 0 } - -func BtoR(b uint64) int { - b &= 0xffffffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + ppc64.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 32 - if b == 0 { - return 0 - } - return gc.Bitno(b) + ppc64.REG_F0 -} diff --git a/src/cmd/compile/internal/s390x/cgen.go b/src/cmd/compile/internal/s390x/cgen.go deleted file mode 100644 index 28bb34e0ef..0000000000 --- a/src/cmd/compile/internal/s390x/cgen.go +++ /dev/null @@ -1,178 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package s390x - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/s390x" -) - -type direction int - -const ( - _FORWARDS direction = iota - _BACKWARDS -) - -// blockcopy copies w bytes from &n to &res -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - var dst gc.Node - var src gc.Node - if n.Ullman >= res.Ullman { - gc.Agenr(n, &dst, res) // temporarily use dst - gc.Regalloc(&src, gc.Types[gc.Tptr], nil) - gins(s390x.AMOVD, &dst, &src) - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agen(res, &dst) - } else { - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - gc.Agenr(res, &dst, res) - gc.Agenr(n, &src, nil) - } - defer gc.Regfree(&src) - defer gc.Regfree(&dst) - - var tmp gc.Node - gc.Regalloc(&tmp, gc.Types[gc.Tptr], nil) - defer gc.Regfree(&tmp) - - offset := int64(0) - dir := _FORWARDS - if osrc < odst && odst < osrc+w { - // Reverse. Can't use MVC, fall back onto basic moves. - dir = _BACKWARDS - const copiesPerIter = 2 - if w >= 8*copiesPerIter { - cnt := w - (w % (8 * copiesPerIter)) - ginscon(s390x.AADD, w, &src) - ginscon(s390x.AADD, w, &dst) - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p := gins(s390x.ASUB, nil, &end) - p.From.Type = obj.TYPE_CONST - p.From.Offset = cnt - p.Reg = src.Reg - - var label *obj.Prog - for i := 0; i < copiesPerIter; i++ { - offset := int64(-8 * (i + 1)) - p := gins(s390x.AMOVD, &src, &tmp) - p.From.Type = obj.TYPE_MEM - p.From.Offset = offset - if i == 0 { - label = p - } - p = gins(s390x.AMOVD, &tmp, &dst) - p.To.Type = obj.TYPE_MEM - p.To.Offset = offset - } - - ginscon(s390x.ASUB, 8*copiesPerIter, &src) - ginscon(s390x.ASUB, 8*copiesPerIter, &dst) - gins(s390x.ACMP, &src, &end) - gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), label) - gc.Regfree(&end) - - w -= cnt - } else { - offset = w - } - } - - if dir == _FORWARDS && w > 1024 { - // Loop over MVCs - cnt := w - (w % 256) - - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - add := gins(s390x.AADD, nil, &end) - add.From.Type = obj.TYPE_CONST - add.From.Offset = cnt - add.Reg = src.Reg - - mvc := gins(s390x.AMVC, &src, &dst) - mvc.From.Type = obj.TYPE_MEM - mvc.From.Offset = 0 - mvc.To.Type = obj.TYPE_MEM - mvc.To.Offset = 0 - mvc.From3 = new(obj.Addr) - mvc.From3.Type = obj.TYPE_CONST - mvc.From3.Offset = 256 - - ginscon(s390x.AADD, 256, &src) - ginscon(s390x.AADD, 256, &dst) - gins(s390x.ACMP, &src, &end) - gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), mvc) - gc.Regfree(&end) - - w -= cnt - } - - for w > 0 { - cnt := w - // If in reverse we can only do 8, 4, 2 or 1 bytes at a time. - if dir == _BACKWARDS { - switch { - case cnt >= 8: - cnt = 8 - case cnt >= 4: - cnt = 4 - case cnt >= 2: - cnt = 2 - } - } else if cnt > 256 { - cnt = 256 - } - - switch cnt { - case 8, 4, 2, 1: - op := s390x.AMOVB - switch cnt { - case 8: - op = s390x.AMOVD - case 4: - op = s390x.AMOVW - case 2: - op = s390x.AMOVH - } - load := gins(op, &src, &tmp) - load.From.Type = obj.TYPE_MEM - load.From.Offset = offset - - store := gins(op, &tmp, &dst) - store.To.Type = obj.TYPE_MEM - store.To.Offset = offset - - if dir == _BACKWARDS { - load.From.Offset -= cnt - store.To.Offset -= cnt - } - - default: - p := gins(s390x.AMVC, &src, &dst) - p.From.Type = obj.TYPE_MEM - p.From.Offset = offset - p.To.Type = obj.TYPE_MEM - p.To.Offset = offset - p.From3 = new(obj.Addr) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = cnt - } - - switch dir { - case _FORWARDS: - offset += cnt - case _BACKWARDS: - offset -= cnt - } - w -= cnt - } -} diff --git a/src/cmd/compile/internal/s390x/galign.go b/src/cmd/compile/internal/s390x/galign.go index f7a0f5605a..93ece5a7ff 100644 --- a/src/cmd/compile/internal/s390x/galign.go +++ b/src/cmd/compile/internal/s390x/galign.go @@ -27,36 +27,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = dodiv - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = isReg - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = RtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = ssaMarkMoves diff --git a/src/cmd/compile/internal/s390x/ggen.go b/src/cmd/compile/internal/s390x/ggen.go index 1dd353a0ec..cfaf88da36 100644 --- a/src/cmd/compile/internal/s390x/ggen.go +++ b/src/cmd/compile/internal/s390x/ggen.go @@ -8,7 +8,6 @@ import ( "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/s390x" - "fmt" ) // clearLoopCutOff is the (somewhat arbitrary) value above which it is better @@ -165,413 +164,3 @@ func ginsnop() { gc.Nodreg(®, gc.Types[gc.TINT], s390x.REG_R0) gins(s390x.AOR, ®, ®) } - -var panicdiv *gc.Node - -/* - * generate division. - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will generate undefined result. - // Also need to explicitly trap on division on zero, - // the hardware will silently generate undefined result. - // DIVW will leave unpredicable result in higher 32-bit, - // so always use DIVD/DIVDU. - t := nl.Type - - t0 := t - check := 0 - if t.IsSigned() { - check = 1 - if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -(1<= nr.Ullman { - gc.Cgen(nl, &tl) - gc.Cgen(nr, &tr) - } else { - gc.Cgen(nr, &tr) - gc.Cgen(nl, &tl) - } - - if t != t0 { - // Convert - tl2 := tl - - tr2 := tr - tl.Type = t - tr.Type = t - gmove(&tl2, &tl) - gmove(&tr2, &tr) - } - - // Handle divide-by-zero panic. - p1 := gins(optoas(gc.OCMP, t), &tr, nil) - - p1.To.Type = obj.TYPE_CONST - p1.To.Offset = 0 - p1 = gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if panicdiv == nil { - panicdiv = gc.Sysfunc("panicdivide") - } - gc.Ginscall(panicdiv, -1) - gc.Patch(p1, gc.Pc) - - var p2 *obj.Prog - if check != 0 { - var nm1 gc.Node - gc.Nodconst(&nm1, t, -1) - gins(optoas(gc.OCMP, t), &tr, &nm1) - p1 := gc.Gbranch(optoas(gc.ONE, t), nil, +1) - if op == gc.ODIV { - // a / (-1) is -a. - gins(optoas(gc.OMINUS, t), nil, &tl) - - gmove(&tl, res) - } else { - // a % (-1) is 0. - var nz gc.Node - gc.Nodconst(&nz, t, 0) - - gmove(&nz, res) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - } - - p1 = gins(a, &tr, &tl) - if op == gc.ODIV { - gc.Regfree(&tr) - gmove(&tl, res) - } else { - // A%B = A-(A/B*B) - var tm gc.Node - gc.Regalloc(&tm, t, nil) - - // patch div to use the 3 register form - // TODO(minux): add gins3? - p1.Reg = p1.To.Reg - - p1.To.Reg = tm.Reg - gins(optoas(gc.OMUL, t), &tr, &tm) - gc.Regfree(&tr) - gins(optoas(gc.OSUB, t), &tm, &tl) - gc.Regfree(&tm) - gmove(&tl, res) - } - - gc.Regfree(&tl) - if check != 0 { - gc.Patch(p2, gc.Pc) - } -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - t := nl.Type - w := int(t.Width) * 8 - var n1 gc.Node - gc.Cgenr(nl, &n1, res) - var n2 gc.Node - gc.Cgenr(nr, &n2, nil) - switch gc.Simtype[t.Etype] { - case gc.TINT8, - gc.TINT16, - gc.TINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(s390x.ASRAD, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TUINT8, - gc.TUINT16, - gc.TUINT32: - gins(optoas(gc.OMUL, t), &n2, &n1) - p := gins(s390x.ASRD, nil, &n1) - p.From.Type = obj.TYPE_CONST - p.From.Offset = int64(w) - - case gc.TINT64: - gins(s390x.AMULHD, &n2, &n1) - - case gc.TUINT64: - gins(s390x.AMULHDU, &n2, &n1) - - default: - gc.Fatalf("cgen_hmul %v", t) - } - - gc.Cgen(&n1, res) - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gc.Cgen(nl, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - var n3 gc.Node - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - - gins(a, &n3, &n1) - gins(a, &n3, &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - if nl.Ullman >= gc.UINF { - var n4 gc.Node - gc.Tempname(&n4, nl.Type) - gc.Cgen(nl, &n4) - nl = &n4 - } - - if nr.Ullman >= gc.UINF { - var n5 gc.Node - gc.Tempname(&n5, nr.Type) - gc.Cgen(nr, &n5) - nr = &n5 - } - - // Allow either uint32 or uint64 as shift type, - // to avoid unnecessary conversion from uint32 to uint64 - // just to do the comparison. - tcount := gc.Types[gc.Simtype[nr.Type.Etype]] - - if tcount.Etype < gc.TUINT32 { - tcount = gc.Types[gc.TUINT32] - } - - var n1 gc.Node - gc.Regalloc(&n1, nr.Type, nil) // to hold the shift type in CX - var n3 gc.Node - gc.Regalloc(&n3, tcount, &n1) // to clear high bits of CX - - var n2 gc.Node - gc.Regalloc(&n2, nl.Type, res) - - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - } else { - gc.Cgen(nr, &n1) - gmove(&n1, &n3) - gc.Cgen(nl, &n2) - } - - gc.Regfree(&n3) - - // test and fix up large shifts - if !bounded { - gc.Nodconst(&n3, tcount, nl.Type.Width*8) - gins(optoas(gc.OCMP, tcount), &n1, &n3) - p1 := gc.Gbranch(optoas(gc.OLT, tcount), nil, 1) - if op == gc.ORSH && nl.Type.IsSigned() { - gc.Nodconst(&n3, gc.Types[gc.TUINT32], nl.Type.Width*8-1) - gins(a, &n3, &n2) - } else { - gc.Nodconst(&n3, nl.Type, 0) - gmove(&n3, &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -// clearfat clears (i.e. replaces with zeros) the value pointed to by nl. -func clearfat(nl *gc.Node) { - if gc.Debug['g'] != 0 { - fmt.Printf("clearfat %v (%v, size: %d)\n", nl, nl.Type, nl.Type.Width) - } - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - var dst gc.Node - gc.Regalloc(&dst, gc.Types[gc.Tptr], nil) - gc.Agen(nl, &dst) - - var boff int64 - w := nl.Type.Width - if w > clearLoopCutoff { - // Generate a loop clearing 256 bytes per iteration using XCs. - var end gc.Node - gc.Regalloc(&end, gc.Types[gc.Tptr], nil) - p := gins(s390x.AMOVD, &dst, &end) - p.From.Type = obj.TYPE_ADDR - p.From.Offset = w - (w % 256) - - p = gins(s390x.AXC, &dst, &dst) - p.From.Type = obj.TYPE_MEM - p.From.Offset = 0 - p.To.Type = obj.TYPE_MEM - p.To.Offset = 0 - p.From3 = new(obj.Addr) - p.From3.Offset = 256 - p.From3.Type = obj.TYPE_CONST - pl := p - - ginscon(s390x.AADD, 256, &dst) - gins(s390x.ACMP, &dst, &end) - gc.Patch(gc.Gbranch(s390x.ABNE, nil, 0), pl) - gc.Regfree(&end) - w = w % 256 - } - - // Generate instructions to clear the remaining memory. - for w > 0 { - n := w - - // Can clear at most 256 bytes per instruction. - if n > 256 { - n = 256 - } - - switch n { - // Handle very small clears using moves. - case 8, 4, 2, 1: - ins := s390x.AMOVB - switch n { - case 8: - ins = s390x.AMOVD - case 4: - ins = s390x.AMOVW - case 2: - ins = s390x.AMOVH - } - p := gins(ins, nil, &dst) - p.From.Type = obj.TYPE_CONST - p.From.Offset = 0 - p.To.Type = obj.TYPE_MEM - p.To.Offset = boff - - // Handle clears that would require multiple moves with a XC. - default: - p := gins(s390x.AXC, &dst, &dst) - p.From.Type = obj.TYPE_MEM - p.From.Offset = boff - p.To.Type = obj.TYPE_MEM - p.To.Offset = boff - p.From3 = new(obj.Addr) - p.From3.Offset = n - p.From3.Type = obj.TYPE_CONST - } - - boff += n - w -= n - } - - gc.Regfree(&dst) -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - for p := firstp; p != nil; p = p.Link { - if gc.Debug_checknil != 0 && gc.Ctxt.Debugvlog != 0 { - fmt.Printf("expandchecks: %v\n", p) - } - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - if p.From.Type != obj.TYPE_REG { - gc.Fatalf("invalid nil check %v\n", p) - } - - // check is - // CMPBNE arg, $0, 2(PC) [likely] - // MOVD R0, 0(R0) - p1 := gc.Ctxt.NewProg() - - gc.Clearp(p1) - p1.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p1.Pc = 9999 - p.As = s390x.ACMPBNE - p.From3 = new(obj.Addr) - p.From3.Type = obj.TYPE_CONST - p.From3.Offset = 0 - - p.To.Type = obj.TYPE_BRANCH - p.To.Val = p1.Link - - // crash by write to memory address 0. - p1.As = s390x.AMOVD - - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = 0 - p1.To.Type = obj.TYPE_MEM - p1.To.Reg = s390x.REGZERO - p1.To.Offset = 0 - } -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Nodreg(&n1, res.Type, s390x.REGG) - gmove(&n1, res) -} diff --git a/src/cmd/compile/internal/s390x/gsubr.go b/src/cmd/compile/internal/s390x/gsubr.go index 66b6588480..b8925ff40d 100644 --- a/src/cmd/compile/internal/s390x/gsubr.go +++ b/src/cmd/compile/internal/s390x/gsubr.go @@ -102,34 +102,6 @@ func ginscon2(as obj.As, n2 *gc.Node, c int64) { gc.Regfree(&ntmp) } -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { - // Reverse comparison to place constant last. - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - - var r1, r2, g1, g2 gc.Node - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - if t.IsInteger() && gc.Isconst(n2, gc.CTINT) { - ginscon2(optoas(gc.OCMP, t), &r1, n2.Int64()) - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - rawgins(optoas(gc.OCMP, t), &r1, &r2) - gc.Regfree(&g2) - gc.Regfree(&r2) - } - gc.Regfree(&g1) - gc.Regfree(&r1) - return gc.Gbranch(optoas(op, t), nil, likely) -} - // gmvc tries to move f to t using a mvc instruction. // If successful it returns true, otherwise it returns false. func gmvc(f, t *gc.Node) bool { @@ -177,338 +149,6 @@ func gmvc(f, t *gc.Node) bool { return true } -// generate move: -// t = f -// hard part is conversions. -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %L -> %L\n", f, t) - } - - ft := int(gc.Simsimtype(f.Type)) - tt := int(gc.Simsimtype(t.Type)) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - var a obj.As - - // cannot have two memory operands - if gc.Ismem(f) && gc.Ismem(t) { - if gmvc(f, t) { - return - } - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = tt // so big switch will choose a simple mov - - // some constants can't move directly to memory. - if gc.Ismem(t) { - // float constants come from memory. - if t.Type.IsFloat() { - goto hard - } - - // all immediates are 16-bit sign-extended - // unless moving into a register. - if t.Type.IsInteger() { - if i := con.Int64(); int64(int16(i)) != i { - goto hard - } - } - - // immediate moves to memory have a 12-bit unsigned displacement - if t.Xoffset < 0 || t.Xoffset >= 4096-8 { - goto hard - } - } - } - - // a float-to-int or int-to-float conversion requires the source operand in a register - if gc.Ismem(f) && ((f.Type.IsFloat() && t.Type.IsInteger()) || (f.Type.IsInteger() && t.Type.IsFloat())) { - cvt = f.Type - goto hard - } - - // a float32-to-float64 or float64-to-float32 conversion requires the source operand in a register - if gc.Ismem(f) && f.Type.IsFloat() && t.Type.IsFloat() && (ft != tt) { - cvt = f.Type - goto hard - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - switch uint32(ft)<<16 | uint32(tt) { - default: - gc.Fatalf("gmove %L -> %L", f.Type, t.Type) - - // integer copy and truncate - case gc.TINT8<<16 | gc.TINT8, - gc.TUINT8<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TINT8, - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TINT8, - gc.TUINT64<<16 | gc.TINT8: - a = s390x.AMOVB - - case gc.TINT8<<16 | gc.TUINT8, - gc.TUINT8<<16 | gc.TUINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - a = s390x.AMOVBZ - - case gc.TINT16<<16 | gc.TINT16, - gc.TUINT16<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TINT16, - gc.TUINT32<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TINT16, - gc.TUINT64<<16 | gc.TINT16: - a = s390x.AMOVH - - case gc.TINT16<<16 | gc.TUINT16, - gc.TUINT16<<16 | gc.TUINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - a = s390x.AMOVHZ - - case gc.TINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TINT32, - gc.TUINT64<<16 | gc.TINT32: - a = s390x.AMOVW - - case gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TUINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - a = s390x.AMOVWZ - - case gc.TINT64<<16 | gc.TINT64, - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - a = s390x.AMOVD - - // sign extend int8 - case gc.TINT8<<16 | gc.TINT16, - gc.TINT8<<16 | gc.TUINT16, - gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32, - gc.TINT8<<16 | gc.TINT64, - gc.TINT8<<16 | gc.TUINT64: - a = s390x.AMOVB - goto rdst - - // sign extend uint8 - case gc.TUINT8<<16 | gc.TINT16, - gc.TUINT8<<16 | gc.TUINT16, - gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32, - gc.TUINT8<<16 | gc.TINT64, - gc.TUINT8<<16 | gc.TUINT64: - a = s390x.AMOVBZ - goto rdst - - // sign extend int16 - case gc.TINT16<<16 | gc.TINT32, - gc.TINT16<<16 | gc.TUINT32, - gc.TINT16<<16 | gc.TINT64, - gc.TINT16<<16 | gc.TUINT64: - a = s390x.AMOVH - goto rdst - - // zero extend uint16 - case gc.TUINT16<<16 | gc.TINT32, - gc.TUINT16<<16 | gc.TUINT32, - gc.TUINT16<<16 | gc.TINT64, - gc.TUINT16<<16 | gc.TUINT64: - a = s390x.AMOVHZ - goto rdst - - // sign extend int32 - case gc.TINT32<<16 | gc.TINT64, - gc.TINT32<<16 | gc.TUINT64: - a = s390x.AMOVW - goto rdst - - // zero extend uint32 - case gc.TUINT32<<16 | gc.TINT64, - gc.TUINT32<<16 | gc.TUINT64: - a = s390x.AMOVWZ - goto rdst - - // float to integer - case gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT32<<16 | gc.TUINT16: - cvt = gc.Types[gc.TUINT32] - goto hard - - case gc.TFLOAT32<<16 | gc.TUINT32: - a = s390x.ACLFEBR - goto rdst - - case gc.TFLOAT32<<16 | gc.TUINT64: - a = s390x.ACLGEBR - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TUINT16: - cvt = gc.Types[gc.TUINT32] - goto hard - - case gc.TFLOAT64<<16 | gc.TUINT32: - a = s390x.ACLFDBR - goto rdst - - case gc.TFLOAT64<<16 | gc.TUINT64: - a = s390x.ACLGDBR - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TINT16: - cvt = gc.Types[gc.TINT32] - goto hard - - case gc.TFLOAT32<<16 | gc.TINT32: - a = s390x.ACFEBRA - goto rdst - - case gc.TFLOAT32<<16 | gc.TINT64: - a = s390x.ACGEBRA - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TINT16: - cvt = gc.Types[gc.TINT32] - goto hard - - case gc.TFLOAT64<<16 | gc.TINT32: - a = s390x.ACFDBRA - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT64: - a = s390x.ACGDBRA - goto rdst - - // integer to float - case gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT32: - cvt = gc.Types[gc.TUINT32] - goto hard - - case gc.TUINT32<<16 | gc.TFLOAT32: - a = s390x.ACELFBR - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT32: - a = s390x.ACELGBR - goto rdst - - case gc.TUINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TUINT32] - goto hard - - case gc.TUINT32<<16 | gc.TFLOAT64: - a = s390x.ACDLFBR - goto rdst - - case gc.TUINT64<<16 | gc.TFLOAT64: - a = s390x.ACDLGBR - goto rdst - - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT32: - cvt = gc.Types[gc.TINT32] - goto hard - - case gc.TINT32<<16 | gc.TFLOAT32: - a = s390x.ACEFBRA - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT32: - a = s390x.ACEGBRA - goto rdst - - case gc.TINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - goto hard - - case gc.TINT32<<16 | gc.TFLOAT64: - a = s390x.ACDFBRA - goto rdst - - case gc.TINT64<<16 | gc.TFLOAT64: - a = s390x.ACDGBRA - goto rdst - - // float to float - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = s390x.AFMOVS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = s390x.AFMOVD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = s390x.ALDEBR - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = s390x.ALEDBR - goto rdst - } - - gins(a, f, t) - return - - // requires register destination -rdst: - if t != nil && t.Op == gc.OREGISTER { - gins(a, f, t) - return - } else { - var r1 gc.Node - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - var r1 gc.Node - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - func intLiteral(n *gc.Node) (x int64, ok bool) { switch { case n == nil: @@ -599,512 +239,3 @@ func rawgins(as obj.As, f *gc.Node, t *gc.Node) *obj.Prog { return p } - -// optoas returns the Axxx equivalent of Oxxx for type t -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - OSQRT_ = uint32(gc.OSQRT) << 16 - OLROT_ = uint32(gc.OLROT) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry for op=%v type=%v", op, t) - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = s390x.ABEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = s390x.ABNE - - case OLT_ | gc.TINT8, // ACMP - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64, - OLT_ | gc.TUINT8, - // ACMPU - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64, - OLT_ | gc.TFLOAT32, - // AFCMPU - OLT_ | gc.TFLOAT64: - a = s390x.ABLT - - case OLE_ | gc.TINT8, // ACMP - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64, - OLE_ | gc.TUINT8, - // ACMPU - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64, - OLE_ | gc.TFLOAT32, - OLE_ | gc.TFLOAT64: - a = s390x.ABLE - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64, - OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64, - OGT_ | gc.TFLOAT32, - OGT_ | gc.TFLOAT64: - a = s390x.ABGT - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64, - OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64, - OGE_ | gc.TFLOAT32, - OGE_ | gc.TFLOAT64: - a = s390x.ABGE - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TINT16, - OCMP_ | gc.TINT32, - OCMP_ | gc.TPTR32, - OCMP_ | gc.TINT64: - a = s390x.ACMP - - case OCMP_ | gc.TUINT8, - OCMP_ | gc.TUINT16, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TUINT64, - OCMP_ | gc.TPTR64: - a = s390x.ACMPU - - case OCMP_ | gc.TFLOAT32: - a = s390x.ACEBR - - case OCMP_ | gc.TFLOAT64: - a = s390x.AFCMPU - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8: - a = s390x.AMOVB - - case OAS_ | gc.TUINT8: - a = s390x.AMOVBZ - - case OAS_ | gc.TINT16: - a = s390x.AMOVH - - case OAS_ | gc.TUINT16: - a = s390x.AMOVHZ - - case OAS_ | gc.TINT32: - a = s390x.AMOVW - - case OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = s390x.AMOVWZ - - case OAS_ | gc.TINT64, - OAS_ | gc.TUINT64, - OAS_ | gc.TPTR64: - a = s390x.AMOVD - - case OAS_ | gc.TFLOAT32: - a = s390x.AFMOVS - - case OAS_ | gc.TFLOAT64: - a = s390x.AFMOVD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8, - OADD_ | gc.TINT16, - OADD_ | gc.TUINT16, - OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32, - OADD_ | gc.TINT64, - OADD_ | gc.TUINT64, - OADD_ | gc.TPTR64: - a = s390x.AADD - - case OADD_ | gc.TFLOAT32: - a = s390x.AFADDS - - case OADD_ | gc.TFLOAT64: - a = s390x.AFADD - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8, - OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16, - OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32, - OSUB_ | gc.TINT64, - OSUB_ | gc.TUINT64, - OSUB_ | gc.TPTR64: - a = s390x.ASUB - - case OSUB_ | gc.TFLOAT32: - a = s390x.AFSUBS - - case OSUB_ | gc.TFLOAT64: - a = s390x.AFSUB - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8, - OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16, - OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32, - OMINUS_ | gc.TINT64, - OMINUS_ | gc.TUINT64, - OMINUS_ | gc.TPTR64: - a = s390x.ANEG - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8, - OAND_ | gc.TINT16, - OAND_ | gc.TUINT16, - OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32, - OAND_ | gc.TINT64, - OAND_ | gc.TUINT64, - OAND_ | gc.TPTR64: - a = s390x.AAND - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8, - OOR_ | gc.TINT16, - OOR_ | gc.TUINT16, - OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32, - OOR_ | gc.TINT64, - OOR_ | gc.TUINT64, - OOR_ | gc.TPTR64: - a = s390x.AOR - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8, - OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16, - OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32, - OXOR_ | gc.TINT64, - OXOR_ | gc.TUINT64, - OXOR_ | gc.TPTR64: - a = s390x.AXOR - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8, - OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16, - OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32, - OLSH_ | gc.TINT64, - OLSH_ | gc.TUINT64, - OLSH_ | gc.TPTR64: - a = s390x.ASLD - - case ORSH_ | gc.TUINT8, - ORSH_ | gc.TUINT16, - ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32, - ORSH_ | gc.TUINT64, - ORSH_ | gc.TPTR64: - a = s390x.ASRD - - case ORSH_ | gc.TINT8, - ORSH_ | gc.TINT16, - ORSH_ | gc.TINT32, - ORSH_ | gc.TINT64: - a = s390x.ASRAD - - case OHMUL_ | gc.TINT64: - a = s390x.AMULHD - - case OHMUL_ | gc.TUINT64, - OHMUL_ | gc.TPTR64: - a = s390x.AMULHDU - - case OMUL_ | gc.TINT8, - OMUL_ | gc.TINT16, - OMUL_ | gc.TINT32, - OMUL_ | gc.TINT64: - a = s390x.AMULLD - - case OMUL_ | gc.TUINT8, - OMUL_ | gc.TUINT16, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32, - // don't use word multiply, the high 32-bit are undefined. - OMUL_ | gc.TUINT64, - OMUL_ | gc.TPTR64: - // for 64-bit multiplies, signedness doesn't matter. - a = s390x.AMULLD - - case OMUL_ | gc.TFLOAT32: - a = s390x.AFMULS - - case OMUL_ | gc.TFLOAT64: - a = s390x.AFMUL - - case ODIV_ | gc.TINT8, - ODIV_ | gc.TINT16, - ODIV_ | gc.TINT32, - ODIV_ | gc.TINT64: - a = s390x.ADIVD - - case ODIV_ | gc.TUINT8, - ODIV_ | gc.TUINT16, - ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - ODIV_ | gc.TUINT64, - ODIV_ | gc.TPTR64: - a = s390x.ADIVDU - - case ODIV_ | gc.TFLOAT32: - a = s390x.AFDIVS - - case ODIV_ | gc.TFLOAT64: - a = s390x.AFDIV - - case OSQRT_ | gc.TFLOAT64: - a = s390x.AFSQRT - - case OLROT_ | gc.TUINT32, - OLROT_ | gc.TPTR32, - OLROT_ | gc.TINT32: - a = s390x.ARLL - - case OLROT_ | gc.TUINT64, - OLROT_ | gc.TPTR64, - OLROT_ | gc.TINT64: - a = s390x.ARLLG - } - - return a -} - -const ( - ODynam = 1 << 0 - OAddable = 1 << 1 -) - -var clean [20]gc.Node - -var cleani int = 0 - -func sudoclean() { - if clean[cleani-1].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-1]) - } - if clean[cleani-2].Op != gc.OEMPTY { - gc.Regfree(&clean[cleani-2]) - } - cleani -= 2 -} - -/* - * generate code to compute address of n, - * a reference to a (perhaps nested) field inside - * an array or struct. - * return 0 on failure, 1 on success. - * on success, leaves usable address in a. - * - * caller is responsible for calling sudoclean - * after successful sudoaddable, - * to release the register used for a. - */ -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - if n.Type == nil { - return false - } - - *a = obj.Addr{} - - switch n.Op { - case gc.OLITERAL: - if !gc.Isconst(n, gc.CTINT) { - return false - } - v := n.Int64() - switch as { - default: - return false - - // operations that can cope with a 32-bit immediate - // TODO(mundaym): logical operations can work on high bits - case s390x.AADD, - s390x.AADDC, - s390x.ASUB, - s390x.AMULLW, - s390x.AAND, - s390x.AOR, - s390x.AXOR, - s390x.ASLD, - s390x.ASLW, - s390x.ASRAW, - s390x.ASRAD, - s390x.ASRW, - s390x.ASRD, - s390x.AMOVB, - s390x.AMOVBZ, - s390x.AMOVH, - s390x.AMOVHZ, - s390x.AMOVW, - s390x.AMOVWZ, - s390x.AMOVD: - if int64(int32(v)) != v { - return false - } - - // for comparisons avoid immediates unless they can - // fit into a int8/uint8 - // this favours combined compare and branch instructions - case s390x.ACMP: - if int64(int8(v)) != v { - return false - } - case s390x.ACMPU: - if int64(uint8(v)) != v { - return false - } - } - - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - gc.Naddr(a, n) - return true - - case gc.ODOT, - gc.ODOTPTR: - cleani += 2 - reg := &clean[cleani-1] - reg1 := &clean[cleani-2] - reg.Op = gc.OEMPTY - reg1.Op = gc.OEMPTY - var nn *gc.Node - var oary [10]int64 - o := gc.Dotoffset(n, oary[:], &nn) - if nn == nil { - sudoclean() - return false - } - - if nn.Addable && o == 1 && oary[0] >= 0 { - // directly addressable set of DOTs - n1 := *nn - - n1.Type = n.Type - n1.Xoffset += oary[0] - // check that the offset fits into a 12-bit displacement - if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { - sudoclean() - return false - } - gc.Naddr(a, &n1) - return true - } - - gc.Regalloc(reg, gc.Types[gc.Tptr], nil) - n1 := *reg - n1.Op = gc.OINDREG - if oary[0] >= 0 { - gc.Agen(nn, reg) - n1.Xoffset = oary[0] - } else { - gc.Cgen(nn, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[0] + 1) - } - - for i := 1; i < o; i++ { - if oary[i] >= 0 { - gc.Fatalf("can't happen") - } - gins(s390x.AMOVD, &n1, reg) - gc.Cgen_checknil(reg) - n1.Xoffset = -(oary[i] + 1) - } - - a.Type = obj.TYPE_NONE - a.Index = 0 - // check that the offset fits into a 12-bit displacement - if n1.Xoffset < 0 || n1.Xoffset >= (1<<12)-8 { - tmp := n1 - tmp.Op = gc.OREGISTER - tmp.Type = gc.Types[gc.Tptr] - tmp.Xoffset = 0 - gc.Cgen_checknil(&tmp) - ginscon(s390x.AADD, n1.Xoffset, &tmp) - n1.Xoffset = 0 - } - gc.Naddr(a, &n1) - return true - } - - return false -} diff --git a/src/cmd/compile/internal/s390x/peep.go b/src/cmd/compile/internal/s390x/peep.go deleted file mode 100644 index 6400f6104a..0000000000 --- a/src/cmd/compile/internal/s390x/peep.go +++ /dev/null @@ -1,1664 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package s390x - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/s390x" - "fmt" -) - -type usage int - -const ( - _None usage = iota // no usage found - _Read // only read from - _ReadWriteSame // both read from and written to in a single operand - _Write // only written to - _ReadWriteDiff // both read from and written to in different operands -) - -var gactive uint32 - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - run := func(name string, pass func(r *gc.Flow) int) int { - n := pass(g.Start) - if gc.Debug['P'] != 0 { - fmt.Println(name, ":", n) - } - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit(name, g.Start, 0) - } - return n - } - - for { - n := 0 - n += run("constant propagation", constantPropagation) - n += run("copy propagation", copyPropagation) - n += run("cast propagation", castPropagation) - n += run("remove load-hit-stores", removeLoadHitStores) - n += run("dead code elimination", deadCodeElimination) - if n == 0 { - break - } - } - run("fuse op moves", fuseOpMoves) - run("fuse clears", fuseClear) - run("load pipelining", loadPipelining) - run("fuse compare branch", fuseCompareBranch) - run("simplify ops", simplifyOps) - run("dead code elimination", deadCodeElimination) - - // TODO(mundaym): load/store multiple aren't currently handled by copyu - // so this pass must be last. - run("fuse multiple", fuseMultiple) - - gc.Flowend(g) -} - -func pushback(r0 *gc.Flow) { - var r *gc.Flow - - var b *gc.Flow - p0 := r0.Prog - for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { - p := r.Prog - if p.As != obj.ANOP { - if !(isReg(&p.From) || isConst(&p.From)) || !isReg(&p.To) { - break - } - if copyu(p, &p0.To, nil) != _None || copyu(p0, &p.To, nil) != _None { - break - } - } - - if p.As == obj.ACALL { - break - } - b = r - } - - if b == nil { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("no pushback: %v\n", r0.Prog) - if r != nil { - fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) - } - } - - return - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("pushback\n") - for r := b; ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } - - t := *r0.Prog - for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { - p0 = r.Link.Prog - p := r.Prog - p0.As = p.As - p0.Lineno = p.Lineno - p0.From = p.From - p0.To = p.To - p0.From3 = p.From3 - p0.Reg = p.Reg - p0.RegTo2 = p.RegTo2 - if r == b { - break - } - } - - p0 = r.Prog - p0.As = t.As - p0.Lineno = t.Lineno - p0.From = t.From - p0.To = t.To - p0.From3 = t.From3 - p0.Reg = t.Reg - p0.RegTo2 = t.RegTo2 - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\tafter\n") - for r := b; ; r = r.Link { - fmt.Printf("\t%v\n", r.Prog) - if r == r0 { - break - } - } - } -} - -// excise replaces the given instruction with a NOP and clears -// its operands. -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - obj.Nopout(p) - gc.Ostats.Ndelmov++ -} - -// isZero returns true if a is either the constant 0 or the register -// REGZERO. -func isZero(a *obj.Addr) bool { - if a.Type == obj.TYPE_CONST && a.Offset == 0 { - return true - } - if a.Type == obj.TYPE_REG && a.Reg == s390x.REGZERO { - return true - } - return false -} - -// isReg returns true if a is a general purpose or floating point -// register (GPR or FPR). -// -// TODO(mundaym): currently this excludes REGZER0, but not other -// special registers. -func isReg(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && - s390x.REG_R0 <= a.Reg && - a.Reg <= s390x.REG_F15 && - a.Reg != s390x.REGZERO -} - -// isGPR returns true if a is a general purpose register (GPR). -// REGZERO is treated as a GPR. -func isGPR(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && - s390x.REG_R0 <= a.Reg && - a.Reg <= s390x.REG_R15 -} - -// isFPR returns true if a is a floating point register (FPR). -func isFPR(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && - s390x.REG_F0 <= a.Reg && - a.Reg <= s390x.REG_F15 -} - -// isConst returns true if a refers to a constant (integer or -// floating point, not string currently). -func isConst(a *obj.Addr) bool { - return a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_FCONST -} - -// isBDMem returns true if a refers to a memory location addressable by a -// base register (B) and a displacement (D), such as: -// x+8(R1) -// and -// 0(R10) -// It returns false if the address contains an index register (X) such as: -// 16(R1)(R2*1) -// or if a relocation is required. -func isBDMem(a *obj.Addr) bool { - return a.Type == obj.TYPE_MEM && - a.Index == 0 && - (a.Name == obj.NAME_NONE || a.Name == obj.NAME_AUTO || a.Name == obj.NAME_PARAM) -} - -// the idea is to substitute -// one register for another -// from one MOV to another -// MOV a, R1 -// ADD b, R1 / no use of R2 -// MOV R1, R2 -// would be converted to -// MOV a, R2 -// ADD b, R2 -// MOV R2, R1 -// hopefully, then the former or latter MOV -// will be eliminated by copy propagation. -// -// r0 (the argument, not the register) is the MOV at the end of the -// above sequences. subprop returns true if it modified any instructions. -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !isReg(v1) { - return false - } - v2 := &p.To - if !isReg(v2) { - return false - } - cast := false - switch p.As { - case s390x.AMOVW, s390x.AMOVWZ, - s390x.AMOVH, s390x.AMOVHZ, - s390x.AMOVB, s390x.AMOVBZ: - cast = true - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - switch copyu(p, v1, nil) { - case _Write, _ReadWriteDiff: - if p.As == obj.ACALL { - return false - } - if (!cast || p.As == r0.Prog.As) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2) - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2) - copysub1(p, v1, v2) - copysub(&p.To, v1, v2) - } - v1.Reg, v2.Reg = v2.Reg, v1.Reg - return true - } - if cast { - return false - } - case _ReadWriteSame: - if cast { - return false - } - } - if copyu(p, v2, nil) != _None { - return false - } - } - return false -} - -// The idea is to remove redundant copies. -// v1->v2 F=0 -// (use v2 s/v2/v1/)* -// set v1 F=1 -// use v2 return fail (v1->v2 move must remain) -// ----------------- -// v1->v2 F=0 -// (use v2 s/v2/v1/)* -// set v1 F=1 -// set v2 return success (caller can remove v1->v2 move) -func copyprop(r *gc.Flow) bool { - p := r.Prog - - canSub := false - switch p.As { - case s390x.AFMOVS, s390x.AFMOVD, s390x.AMOVD: - canSub = true - default: - for rr := gc.Uniqp(r); rr != nil; rr = gc.Uniqp(rr) { - if gc.Uniqs(rr) == nil { - break - } - switch copyu(rr.Prog, &p.From, nil) { - case _Read, _None: - continue - } - // write - if rr.Prog.As == p.As { - canSub = true - } - break - } - } - if !canSub { - return false - } - if copyas(&p.From, &p.To) { - return true - } - - gactive++ - return copy1(&p.From, &p.To, r.S1, 0) -} - -// copy1 replaces uses of v2 with v1 starting at r and returns true if -// all uses were rewritten. -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { - if uint32(r.Active) == gactive { - return true - } - r.Active = int32(gactive) - for ; r != nil; r = r.S1 { - p := r.Prog - if f == 0 && gc.Uniqp(r) == nil { - // Multiple predecessors; conservatively - // assume v1 was set on other path - f = 1 - } - t := copyu(p, v2, nil) - switch t { - case _ReadWriteSame: - return false - case _Write: - return true - case _Read, _ReadWriteDiff: - if f != 0 { - return false - } - if copyu(p, v2, v1) != 0 { - return false - } - if t == _ReadWriteDiff { - return true - } - } - if f == 0 { - switch copyu(p, v1, nil) { - case _ReadWriteSame, _ReadWriteDiff, _Write: - f = 1 - } - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - return true -} - -// If s==nil, copyu returns the set/use of v in p; otherwise, it -// modifies p to replace reads of v with reads of s and returns 0 for -// success or non-zero for failure. -// -// If s==nil, copy returns one of the following values: -// _Read if v only used -// _ReadWriteSame if v is set and used in one address (read-alter-rewrite; -// can't substitute) -// _Write if v is only set -// _ReadWriteDiff if v is set in one address and used in another (so addresses -// can be rewritten independently) -// _None otherwise (not touched) -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) usage { - if p.From3Type() != obj.TYPE_NONE && p.From3Type() != obj.TYPE_CONST { - // Currently we never generate a From3 with anything other than a constant in it. - fmt.Printf("copyu: From3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) - } - - switch p.As { - default: - fmt.Printf("copyu: can't find %v\n", p.As) - return _ReadWriteSame - - case // read p.From, write p.To - s390x.AMOVH, - s390x.AMOVHZ, - s390x.AMOVB, - s390x.AMOVBZ, - s390x.AMOVW, - s390x.AMOVWZ, - s390x.AMOVD, - s390x.ANEG, - s390x.AADDME, - s390x.AADDZE, - s390x.ASUBME, - s390x.ASUBZE, - s390x.AFMOVS, - s390x.AFMOVD, - s390x.ALEDBR, - s390x.AFNEG, - s390x.ALDEBR, - s390x.ACLFEBR, - s390x.ACLGEBR, - s390x.ACLFDBR, - s390x.ACLGDBR, - s390x.ACFEBRA, - s390x.ACGEBRA, - s390x.ACFDBRA, - s390x.ACGDBRA, - s390x.ACELFBR, - s390x.ACELGBR, - s390x.ACDLFBR, - s390x.ACDLGBR, - s390x.ACEFBRA, - s390x.ACEGBRA, - s390x.ACDFBRA, - s390x.ACDGBRA, - s390x.AFSQRT: - - if s != nil { - copysub(&p.From, v, s) - - // Update only indirect uses of v in p.To - if !copyas(&p.To, v) { - copysub(&p.To, v, s) - } - return _None - } - - if copyas(&p.To, v) { - // Fix up implicit from - if p.From.Type == obj.TYPE_NONE { - p.From = p.To - } - if copyau(&p.From, v) { - return _ReadWriteDiff - } - return _Write - } - - if copyau(&p.From, v) { - return _Read - } - if copyau(&p.To, v) { - // p.To only indirectly uses v - return _Read - } - - return _None - - // read p.From, read p.Reg, write p.To - case s390x.AADD, - s390x.AADDC, - s390x.AADDE, - s390x.ASUB, - s390x.ASLW, - s390x.ASRW, - s390x.ASRAW, - s390x.ASLD, - s390x.ASRD, - s390x.ASRAD, - s390x.ARLL, - s390x.ARLLG, - s390x.AOR, - s390x.AORN, - s390x.AAND, - s390x.AANDN, - s390x.ANAND, - s390x.ANOR, - s390x.AXOR, - s390x.AMULLW, - s390x.AMULLD, - s390x.AMULHD, - s390x.AMULHDU, - s390x.ADIVW, - s390x.ADIVD, - s390x.ADIVWU, - s390x.ADIVDU, - s390x.AFADDS, - s390x.AFADD, - s390x.AFSUBS, - s390x.AFSUB, - s390x.AFMULS, - s390x.AFMUL, - s390x.AFDIVS, - s390x.AFDIV: - if s != nil { - copysub(&p.From, v, s) - copysub1(p, v, s) - - // Update only indirect uses of v in p.To - if !copyas(&p.To, v) { - copysub(&p.To, v, s) - } - } - - if copyas(&p.To, v) { - if p.Reg == 0 { - p.Reg = p.To.Reg - } - if copyau(&p.From, v) || copyau1(p, v) { - return _ReadWriteDiff - } - return _Write - } - - if copyau(&p.From, v) { - return _Read - } - if copyau1(p, v) { - return _Read - } - if copyau(&p.To, v) { - return _Read - } - return _None - - case s390x.ABEQ, - s390x.ABGT, - s390x.ABGE, - s390x.ABLT, - s390x.ABLE, - s390x.ABNE, - s390x.ABVC, - s390x.ABVS: - return _None - - case obj.ACHECKNIL, // read p.From - s390x.ACMP, // read p.From, read p.To - s390x.ACMPU, - s390x.ACMPW, - s390x.ACMPWU, - s390x.AFCMPO, - s390x.AFCMPU, - s390x.ACEBR, - s390x.AMVC, - s390x.ACLC, - s390x.AXC, - s390x.AOC, - s390x.ANC: - if s != nil { - copysub(&p.From, v, s) - copysub(&p.To, v, s) - return _None - } - - if copyau(&p.From, v) { - return _Read - } - if copyau(&p.To, v) { - return _Read - } - return _None - - case s390x.ACMPBNE, s390x.ACMPBEQ, - s390x.ACMPBLT, s390x.ACMPBLE, - s390x.ACMPBGT, s390x.ACMPBGE, - s390x.ACMPUBNE, s390x.ACMPUBEQ, - s390x.ACMPUBLT, s390x.ACMPUBLE, - s390x.ACMPUBGT, s390x.ACMPUBGE: - if s != nil { - copysub(&p.From, v, s) - copysub1(p, v, s) - return _None - } - if copyau(&p.From, v) { - return _Read - } - if copyau1(p, v) { - return _Read - } - return _None - - case s390x.ACLEAR: - if s != nil { - copysub(&p.To, v, s) - return _None - } - if copyau(&p.To, v) { - return _Read - } - return _None - - // go never generates a branch to a GPR - // read p.To - case s390x.ABR: - if s != nil { - copysub(&p.To, v, s) - return _None - } - - if copyau(&p.To, v) { - return _Read - } - return _None - - case obj.ARET, obj.AUNDEF: - if s != nil { - return _None - } - - // All registers die at this point, so claim - // everything is set (and not used). - return _Write - - case s390x.ABL: - if v.Type == obj.TYPE_REG { - if s390x.REGARG != -1 && v.Reg == s390x.REGARG { - return _ReadWriteSame - } - if p.From.Type == obj.TYPE_REG && p.From.Reg == v.Reg { - return _ReadWriteSame - } - if v.Reg == s390x.REGZERO { - // Deliberately inserted nops set R0. - return _ReadWriteSame - } - if v.Reg == s390x.REGCTXT { - // Context register for closures. - // TODO(mundaym): not sure if we need to exclude this. - return _ReadWriteSame - } - } - if s != nil { - copysub(&p.To, v, s) - return _None - } - if copyau(&p.To, v) { - return _ReadWriteDiff - } - return _Write - - case obj.ATEXT: - if v.Type == obj.TYPE_REG { - if v.Reg == s390x.REGARG { - return _Write - } - } - return _None - - case obj.APCDATA, - obj.AFUNCDATA, - obj.AVARDEF, - obj.AVARKILL, - obj.AVARLIVE, - obj.AUSEFIELD, - obj.ANOP: - return _None - } -} - -// copyas returns 1 if a and v address the same register. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means this operation -// writes the register in v. -func copyas(a *obj.Addr, v *obj.Addr) bool { - if isReg(v) { - if a.Type == v.Type { - if a.Reg == v.Reg { - return true - } - } - } - return false -} - -// copyau returns 1 if a either directly or indirectly addresses the -// same register as v. -// -// If a is the from operand, this means this operation reads the -// register in v. If a is the to operand, this means the operation -// either reads or writes the register in v (if !copyas(a, v), then -// the operation reads the register in v). -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if v.Type == obj.TYPE_REG { - if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { - if v.Reg == a.Reg { - return true - } - } - } - return false -} - -// copyau1 returns 1 if p.Reg references the same register as v and v -// is a direct reference. -func copyau1(p *obj.Prog, v *obj.Addr) bool { - if isReg(v) && v.Reg != 0 { - if p.Reg == v.Reg { - return true - } - } - return false -} - -// copysub replaces v.Reg with s.Reg if a.Reg and v.Reg are direct -// references to the same register. -func copysub(a, v, s *obj.Addr) { - if copyau(a, v) { - a.Reg = s.Reg - } -} - -// copysub1 replaces p.Reg with s.Reg if p.Reg and v.Reg are direct -// references to the same register. -func copysub1(p *obj.Prog, v, s *obj.Addr) { - if copyau1(p, v) { - p.Reg = s.Reg - } -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type { - return false - } - if isReg(v) && a.Reg == v.Reg { - return true - } - if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { - // TODO(mundaym): is the offset enough here? Node? - if v.Offset == a.Offset { - return true - } - } - return false -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return reg.Type == obj.TYPE_REG && - a.Type == obj.TYPE_MEM && - a.Reg == reg.Reg && - 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - // TODO(mundaym): the name implies this should check - // for TYPE_ADDR with a base register REGSP. - return a.Type == obj.TYPE_REG && a.Reg == s390x.REGSP -} - -// isMove returns true if p is a move. Moves may imply -// sign/zero extension. -func isMove(p *obj.Prog) bool { - switch p.As { - case s390x.AMOVD, - s390x.AMOVW, s390x.AMOVWZ, - s390x.AMOVH, s390x.AMOVHZ, - s390x.AMOVB, s390x.AMOVBZ, - s390x.AFMOVD, s390x.AFMOVS: - return true - } - return false -} - -// isLoad returns true if p is a move from memory to a register. -func isLoad(p *obj.Prog) bool { - if !isMove(p) { - return false - } - if !(isGPR(&p.To) || isFPR(&p.To)) { - return false - } - if p.From.Type != obj.TYPE_MEM { - return false - } - return true -} - -// isStore returns true if p is a move from a register to memory. -func isStore(p *obj.Prog) bool { - if !isMove(p) { - return false - } - if !(isGPR(&p.From) || isFPR(&p.From) || isConst(&p.From)) { - return false - } - if p.To.Type != obj.TYPE_MEM { - return false - } - return true -} - -// sameStackMem returns true if a and b are both memory operands -// and address the same location which must reside on the stack. -func sameStackMem(a, b *obj.Addr) bool { - if a.Type != obj.TYPE_MEM || - b.Type != obj.TYPE_MEM || - a.Name != b.Name || - a.Sym != b.Sym || - a.Node != b.Node || - a.Reg != b.Reg || - a.Index != b.Index || - a.Offset != b.Offset { - return false - } - switch a.Name { - case obj.NAME_NONE: - return a.Reg == s390x.REGSP - case obj.NAME_PARAM, obj.NAME_AUTO: - // params and autos are always on the stack - return true - } - return false -} - -// removeLoadHitStores trys to remove loads that take place -// immediately after a store to the same location. Returns -// true if load-hit-stores were removed. -// -// For example: -// MOVD R1, 0(R15) -// MOVD 0(R15), R2 -// Would become: -// MOVD R1, 0(R15) -// MOVD R1, R2 -func removeLoadHitStores(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - if !isStore(p) { - continue - } - for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { - pp := rr.Prog - if gc.Uniqp(rr) == nil { - break - } - if pp.As == obj.ANOP { - continue - } - if isLoad(pp) && sameStackMem(&p.To, &pp.From) { - if size(p.As) >= size(pp.As) && isGPR(&p.From) == isGPR(&pp.To) { - pp.From = p.From - } - } - if !isMove(pp) || isStore(pp) { - break - } - if copyau(&p.From, &pp.To) { - break - } - } - } - return n -} - -// size returns the width of the given move. -func size(as obj.As) int { - switch as { - case s390x.AMOVD, s390x.AFMOVD: - return 8 - case s390x.AMOVW, s390x.AMOVWZ, s390x.AFMOVS: - return 4 - case s390x.AMOVH, s390x.AMOVHZ: - return 2 - case s390x.AMOVB, s390x.AMOVBZ: - return 1 - } - return -1 -} - -// castPropagation tries to eliminate unecessary casts. -// -// For example: -// MOVHZ R1, R2 // uint16 -// MOVB R2, 0(R15) // int8 -// Can be simplified to: -// MOVB R1, 0(R15) -func castPropagation(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - if !isMove(p) || !isGPR(&p.To) { - continue - } - - // r is a move with a destination register - var move *gc.Flow - for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { - if gc.Uniqp(rr) == nil { - // branch target: leave alone - break - } - pp := rr.Prog - if isMove(pp) && copyas(&pp.From, &p.To) { - if pp.To.Type == obj.TYPE_MEM { - if p.From.Type == obj.TYPE_MEM || - p.From.Type == obj.TYPE_ADDR { - break - } - if p.From.Type == obj.TYPE_CONST && - int64(int16(p.From.Offset)) != p.From.Offset { - break - } - } - move = rr - break - } - if pp.As == obj.ANOP { - continue - } - break - } - if move == nil { - continue - } - - // we have a move that reads from our destination reg, check if any future - // instructions also read from the reg - mp := move.Prog - if !copyas(&mp.From, &mp.To) { - safe := false - for rr := gc.Uniqs(move); rr != nil; rr = gc.Uniqs(rr) { - if gc.Uniqp(rr) == nil { - break - } - switch copyu(rr.Prog, &p.To, nil) { - case _None: - continue - case _Write: - safe = true - } - break - } - if !safe { - continue - } - } - - // at this point we have something like: - // MOV* const/mem/reg, reg - // MOV* reg, reg/mem - // now check if this is a cast that cannot be forward propagated - execute := false - if p.As == mp.As || isZero(&p.From) || size(p.As) == size(mp.As) { - execute = true - } else if isGPR(&p.From) && size(p.As) >= size(mp.As) { - execute = true - } - - if execute { - mp.From = p.From - excise(r) - n++ - } - } - return n -} - -// fuseClear merges memory clear operations. -// -// Looks for this pattern (sequence of clears): -// MOVD R0, n(R15) -// MOVD R0, n+8(R15) -// MOVD R0, n+16(R15) -// Replaces with: -// CLEAR $24, n(R15) -func fuseClear(r *gc.Flow) int { - n := 0 - var align int64 - var clear *obj.Prog - for ; r != nil; r = r.Link { - // If there is a branch into the instruction stream then - // we can't fuse into previous instructions. - if gc.Uniqp(r) == nil { - clear = nil - } - - p := r.Prog - if p.As == obj.ANOP { - continue - } - if p.As == s390x.AXC { - if p.From.Reg == p.To.Reg && p.From.Offset == p.To.Offset { - // TODO(mundaym): merge clears? - p.As = s390x.ACLEAR - p.From.Offset = p.From3.Offset - p.From3 = nil - p.From.Type = obj.TYPE_CONST - p.From.Reg = 0 - clear = p - } else { - clear = nil - } - continue - } - - // Is our source a constant zero? - if !isZero(&p.From) { - clear = nil - continue - } - - // Are we moving to memory? - if p.To.Type != obj.TYPE_MEM || - p.To.Index != 0 || - p.To.Offset >= 4096 || - !(p.To.Name == obj.NAME_NONE || p.To.Name == obj.NAME_AUTO || p.To.Name == obj.NAME_PARAM) { - clear = nil - continue - } - - size := int64(0) - switch p.As { - default: - clear = nil - continue - case s390x.AMOVB, s390x.AMOVBZ: - size = 1 - case s390x.AMOVH, s390x.AMOVHZ: - size = 2 - case s390x.AMOVW, s390x.AMOVWZ: - size = 4 - case s390x.AMOVD: - size = 8 - } - - // doubleword aligned clears should be kept doubleword - // aligned - if (size == 8 && align != 8) || (size != 8 && align == 8) { - clear = nil - } - - if clear != nil && - clear.To.Reg == p.To.Reg && - clear.To.Name == p.To.Name && - clear.To.Node == p.To.Node && - clear.To.Sym == p.To.Sym { - - min := clear.To.Offset - max := clear.To.Offset + clear.From.Offset - - // previous clear is already clearing this region - if min <= p.To.Offset && max >= p.To.Offset+size { - excise(r) - n++ - continue - } - - // merge forwards - if max == p.To.Offset { - clear.From.Offset += size - excise(r) - n++ - continue - } - - // merge backwards - if min-size == p.To.Offset { - clear.From.Offset += size - clear.To.Offset -= size - excise(r) - n++ - continue - } - } - - // transform into clear - p.From.Type = obj.TYPE_CONST - p.From.Offset = size - p.From.Reg = 0 - p.As = s390x.ACLEAR - clear = p - align = size - } - return n -} - -// fuseMultiple merges memory loads and stores into load multiple and -// store multiple operations. -// -// Looks for this pattern (sequence of loads or stores): -// MOVD R1, 0(R15) -// MOVD R2, 8(R15) -// MOVD R3, 16(R15) -// Replaces with: -// STMG R1, R3, 0(R15) -func fuseMultiple(r *gc.Flow) int { - n := 0 - var fused *obj.Prog - for ; r != nil; r = r.Link { - // If there is a branch into the instruction stream then - // we can't fuse into previous instructions. - if gc.Uniqp(r) == nil { - fused = nil - } - - p := r.Prog - - isStore := isGPR(&p.From) && isBDMem(&p.To) - isLoad := isGPR(&p.To) && isBDMem(&p.From) - - // are we a candidate? - size := int64(0) - switch p.As { - default: - fused = nil - continue - case obj.ANOP: - // skip over nops - continue - case s390x.AMOVW, s390x.AMOVWZ: - size = 4 - // TODO(mundaym): 32-bit load multiple is currently not supported - // as it requires sign/zero extension. - if !isStore { - fused = nil - continue - } - case s390x.AMOVD: - size = 8 - if !isLoad && !isStore { - fused = nil - continue - } - } - - // If we merge two loads/stores with different source/destination Nodes - // then we will lose a reference the second Node which means that the - // compiler might mark the Node as unused and free its slot on the stack. - // TODO(mundaym): allow this by adding a dummy reference to the Node. - if fused == nil || - fused.From.Node != p.From.Node || - fused.From.Type != p.From.Type || - fused.To.Node != p.To.Node || - fused.To.Type != p.To.Type { - fused = p - continue - } - - // check two addresses - ca := func(a, b *obj.Addr, offset int64) bool { - return a.Reg == b.Reg && a.Offset+offset == b.Offset && - a.Sym == b.Sym && a.Name == b.Name - } - - switch fused.As { - default: - fused = p - case s390x.AMOVW, s390x.AMOVWZ: - if size == 4 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 4) { - fused.As = s390x.ASTMY - fused.Reg = p.From.Reg - excise(r) - n++ - } else { - fused = p - } - case s390x.AMOVD: - if size == 8 && fused.From.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, 8) { - fused.As = s390x.ASTMG - fused.Reg = p.From.Reg - excise(r) - n++ - } else if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, 8) { - fused.As = s390x.ALMG - fused.Reg = fused.To.Reg - fused.To.Reg = p.To.Reg - excise(r) - n++ - } else { - fused = p - } - case s390x.ASTMG, s390x.ASTMY: - if (fused.As == s390x.ASTMY && size != 4) || - (fused.As == s390x.ASTMG && size != 8) { - fused = p - continue - } - offset := size * int64(fused.Reg-fused.From.Reg+1) - if fused.Reg+1 == p.From.Reg && ca(&fused.To, &p.To, offset) { - fused.Reg = p.From.Reg - excise(r) - n++ - } else { - fused = p - } - case s390x.ALMG: - offset := 8 * int64(fused.To.Reg-fused.Reg+1) - if size == 8 && fused.To.Reg+1 == p.To.Reg && ca(&fused.From, &p.From, offset) { - fused.To.Reg = p.To.Reg - excise(r) - n++ - } else { - fused = p - } - } - } - return n -} - -// simplifyOps looks for side-effect free ops that can be removed or -// replaced with moves. -// -// For example: -// XOR $0, R1 => NOP -// ADD $0, R1, R2 => MOVD R1, R2 -func simplifyOps(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - - // if the target is R0 then this is a required NOP - if isGPR(&p.To) && p.To.Reg == s390x.REGZERO { - continue - } - - switch p.As { - case s390x.AADD, s390x.ASUB, - s390x.AOR, s390x.AXOR, - s390x.ASLW, s390x.ASRW, s390x.ASRAW, - s390x.ASLD, s390x.ASRD, s390x.ASRAD, - s390x.ARLL, s390x.ARLLG: - if isZero(&p.From) && isGPR(&p.To) { - if p.Reg == 0 || p.Reg == p.To.Reg { - excise(r) - n++ - } else { - p.As = s390x.AMOVD - p.From.Type = obj.TYPE_REG - p.From.Reg = p.Reg - p.Reg = 0 - } - } - case s390x.AMULLW, s390x.AAND: - if isZero(&p.From) && isGPR(&p.To) { - p.As = s390x.AMOVD - p.From.Type = obj.TYPE_REG - p.From.Reg = s390x.REGZERO - p.Reg = 0 - } - } - } - return n -} - -// fuseOpMoves looks for moves following 2-operand operations and trys to merge them into -// a 3-operand operation. -// -// For example: -// ADD R1, R2 -// MOVD R2, R3 -// might become -// ADD R1, R2, R3 -func fuseOpMoves(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - switch p.As { - case s390x.AADD: - case s390x.ASUB: - if isConst(&p.From) && int64(int16(p.From.Offset)) != p.From.Offset { - continue - } - case s390x.ASLW, - s390x.ASRW, - s390x.ASRAW, - s390x.ASLD, - s390x.ASRD, - s390x.ASRAD, - s390x.ARLL, - s390x.ARLLG: - // ok - p.From will be a reg or a constant - case s390x.AOR, - s390x.AORN, - s390x.AAND, - s390x.AANDN, - s390x.ANAND, - s390x.ANOR, - s390x.AXOR, - s390x.AMULLW, - s390x.AMULLD: - if isConst(&p.From) { - // these instructions can either use 3 register form - // or have an immediate but not both - continue - } - default: - continue - } - - if p.Reg != 0 && p.Reg != p.To.Reg { - continue - } - - var move *gc.Flow - rr := gc.Uniqs(r) - for { - if rr == nil || gc.Uniqp(rr) == nil || rr == r { - break - } - pp := rr.Prog - switch copyu(pp, &p.To, nil) { - case _None: - rr = gc.Uniqs(rr) - continue - case _Read: - if move == nil && pp.As == s390x.AMOVD && isGPR(&pp.From) && isGPR(&pp.To) { - move = rr - rr = gc.Uniqs(rr) - continue - } - case _Write: - if move == nil { - // dead code - excise(r) - n++ - } else { - for prev := gc.Uniqp(move); prev != r; prev = gc.Uniqp(prev) { - if copyu(prev.Prog, &move.Prog.To, nil) != 0 { - move = nil - break - } - } - if move == nil { - break - } - p.Reg, p.To.Reg = p.To.Reg, move.Prog.To.Reg - excise(move) - n++ - - // clean up - if p.From.Reg == p.To.Reg && isCommutative(p.As) { - p.From.Reg, p.Reg = p.Reg, 0 - } - if p.To.Reg == p.Reg { - p.Reg = 0 - } - // we could try again if p has become a 2-operand op - // but in testing nothing extra was extracted - } - } - break - } - } - return n -} - -// isCommutative returns true if the order of input operands -// does not affect the result. For example: -// x + y == y + x so ADD is commutative -// x ^ y == y ^ x so XOR is commutative -func isCommutative(as obj.As) bool { - switch as { - case s390x.AADD, - s390x.AOR, - s390x.AAND, - s390x.AXOR, - s390x.AMULLW, - s390x.AMULLD: - return true - } - return false -} - -// applyCast applies the cast implied by the given move -// instruction to v and returns the result. -func applyCast(cast obj.As, v int64) int64 { - switch cast { - case s390x.AMOVWZ: - return int64(uint32(v)) - case s390x.AMOVHZ: - return int64(uint16(v)) - case s390x.AMOVBZ: - return int64(uint8(v)) - case s390x.AMOVW: - return int64(int32(v)) - case s390x.AMOVH: - return int64(int16(v)) - case s390x.AMOVB: - return int64(int8(v)) - } - return v -} - -// constantPropagation removes redundant constant copies. -func constantPropagation(r *gc.Flow) int { - n := 0 - // find MOV $con,R followed by - // another MOV $con,R without - // setting R in the interim - for ; r != nil; r = r.Link { - p := r.Prog - if isMove(p) { - if !isReg(&p.To) { - continue - } - if !isConst(&p.From) { - continue - } - } else { - continue - } - - rr := r - for { - rr = gc.Uniqs(rr) - if rr == nil || rr == r { - break - } - if gc.Uniqp(rr) == nil { - break - } - - pp := rr.Prog - t := copyu(pp, &p.To, nil) - switch t { - case _None: - continue - case _Read: - if !isGPR(&pp.From) || !isMove(pp) { - continue - } - if p.From.Type == obj.TYPE_CONST { - v := applyCast(p.As, p.From.Offset) - if isGPR(&pp.To) { - if int64(int32(v)) == v || ((v>>32)<<32) == v { - pp.From.Reg = 0 - pp.From.Offset = v - pp.From.Type = obj.TYPE_CONST - n++ - } - } else if int64(int16(v)) == v { - pp.From.Reg = 0 - pp.From.Offset = v - pp.From.Type = obj.TYPE_CONST - n++ - } - } - continue - case _Write: - if p.As != pp.As || p.From.Type != pp.From.Type { - break - } - if p.From.Type == obj.TYPE_CONST && p.From.Offset == pp.From.Offset { - excise(rr) - n++ - continue - } else if p.From.Type == obj.TYPE_FCONST { - if p.From.Val.(float64) == pp.From.Val.(float64) { - excise(rr) - n++ - continue - } - } - } - break - } - } - return n -} - -// copyPropagation tries to eliminate register-to-register moves. -func copyPropagation(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - if isMove(p) && isReg(&p.To) { - // Convert uses to $0 to uses of R0 and - // propagate R0 - if isGPR(&p.To) && isZero(&p.From) { - p.From.Type = obj.TYPE_REG - p.From.Reg = s390x.REGZERO - } - - // Try to eliminate reg->reg moves - if isGPR(&p.From) || isFPR(&p.From) { - if copyprop(r) || (subprop(r) && copyprop(r)) { - excise(r) - n++ - } - } - } - } - return n -} - -// loadPipelining pushes any load from memory as early as possible. -func loadPipelining(r *gc.Flow) int { - for ; r != nil; r = r.Link { - p := r.Prog - if isLoad(p) { - pushback(r) - } - } - return 0 -} - -// fuseCompareBranch finds comparisons followed by a branch and converts -// them into a compare-and-branch instruction (which avoid setting the -// condition code). -func fuseCompareBranch(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - r1 := gc.Uniqs(r) - if r1 == nil { - continue - } - p1 := r1.Prog - - var ins obj.As - switch p.As { - case s390x.ACMP: - switch p1.As { - case s390x.ABCL, s390x.ABC: - continue - case s390x.ABEQ: - ins = s390x.ACMPBEQ - case s390x.ABGE: - ins = s390x.ACMPBGE - case s390x.ABGT: - ins = s390x.ACMPBGT - case s390x.ABLE: - ins = s390x.ACMPBLE - case s390x.ABLT: - ins = s390x.ACMPBLT - case s390x.ABNE: - ins = s390x.ACMPBNE - default: - continue - } - - case s390x.ACMPU: - switch p1.As { - case s390x.ABCL, s390x.ABC: - continue - case s390x.ABEQ: - ins = s390x.ACMPUBEQ - case s390x.ABGE: - ins = s390x.ACMPUBGE - case s390x.ABGT: - ins = s390x.ACMPUBGT - case s390x.ABLE: - ins = s390x.ACMPUBLE - case s390x.ABLT: - ins = s390x.ACMPUBLT - case s390x.ABNE: - ins = s390x.ACMPUBNE - default: - continue - } - - case s390x.ACMPW, s390x.ACMPWU: - continue - - default: - continue - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("cnb %v; %v ", p, p1) - } - - if p1.To.Sym != nil { - continue - } - - if p.To.Type == obj.TYPE_REG { - p1.As = ins - p1.From = p.From - p1.Reg = p.To.Reg - p1.From3 = nil - } else if p.To.Type == obj.TYPE_CONST { - switch p.As { - case s390x.ACMP, s390x.ACMPW: - if (p.To.Offset < -(1 << 7)) || (p.To.Offset >= ((1 << 7) - 1)) { - continue - } - case s390x.ACMPU, s390x.ACMPWU: - if p.To.Offset >= (1 << 8) { - continue - } - default: - } - p1.As = ins - p1.From = p.From - p1.Reg = 0 - p1.From3 = new(obj.Addr) - *(p1.From3) = p.To - } else { - continue - } - - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v\n", p1) - } - excise(r) - n++ - } - return n -} - -// deadCodeElimination removes writes to registers which are written -// to again before they are next read. -func deadCodeElimination(r *gc.Flow) int { - n := 0 - for ; r != nil; r = r.Link { - p := r.Prog - // Currently there are no instructions which write to multiple - // registers in copyu. This check will need to change if there - // ever are. - if !(isGPR(&p.To) || isFPR(&p.To)) || copyu(p, &p.To, nil) != _Write { - continue - } - for rr := gc.Uniqs(r); rr != nil; rr = gc.Uniqs(rr) { - t := copyu(rr.Prog, &p.To, nil) - if t == _None { - continue - } - if t == _Write { - excise(r) - n++ - } - break - } - } - return n -} diff --git a/src/cmd/compile/internal/s390x/reg.go b/src/cmd/compile/internal/s390x/reg.go index b42314d9f4..835abc12ec 100644 --- a/src/cmd/compile/internal/s390x/reg.go +++ b/src/cmd/compile/internal/s390x/reg.go @@ -31,65 +31,6 @@ package s390x import "cmd/internal/obj/s390x" -import "cmd/compile/internal/gc" - -const ( - NREGVAR = 32 /* 16 general + 16 floating */ -) - -var regname = []string{ - ".R0", - ".R1", - ".R2", - ".R3", - ".R4", - ".R5", - ".R6", - ".R7", - ".R8", - ".R9", - ".R10", - ".R11", - ".R12", - ".R13", - ".R14", - ".R15", - ".F0", - ".F1", - ".F2", - ".F3", - ".F4", - ".F5", - ".F6", - ".F7", - ".F8", - ".F9", - ".F10", - ".F11", - ".F12", - ".F13", - ".F14", - ".F15", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - // Exclude registers with fixed functions - return RtoB(s390x.REG_R0) | - RtoB(s390x.REGSP) | - RtoB(s390x.REGG) | - RtoB(s390x.REGTMP) | - RtoB(s390x.REGTMP2) | - RtoB(s390x.REG_LR) -} - -func doregbits(r int) uint64 { - return 0 -} /* * track register variables including external registers: @@ -111,20 +52,3 @@ func RtoB(r int) uint64 { } return 0 } - -func BtoR(b uint64) int { - b &= 0xffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + s390x.REG_R0 -} - -func BtoF(b uint64) int { - b >>= 16 - b &= 0xffff - if b == 0 { - return 0 - } - return gc.Bitno(b) + s390x.REG_F0 -} diff --git a/src/cmd/compile/internal/x86/cgen.go b/src/cmd/compile/internal/x86/cgen.go deleted file mode 100644 index 90a773d533..0000000000 --- a/src/cmd/compile/internal/x86/cgen.go +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package x86 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -/* - * generate an addressable node in res, containing the value of n. - * n is an array index, and might be any size; res width is <= 32-bit. - * returns Prog* to patch to panic call. - */ -func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { - if !gc.Is64(n.Type) { - if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) { - // nothing to do. - *res = *n - } else { - gc.Tempname(res, gc.Types[gc.TUINT32]) - gc.Cgen(n, res) - } - - return nil - } - - var tmp gc.Node - gc.Tempname(&tmp, gc.Types[gc.TINT64]) - gc.Cgen(n, &tmp) - var lo gc.Node - var hi gc.Node - split64(&tmp, &lo, &hi) - gc.Tempname(res, gc.Types[gc.TUINT32]) - gmove(&lo, res) - if bounded { - splitclean() - return nil - } - - var zero gc.Node - gc.Nodconst(&zero, gc.Types[gc.TINT32], 0) - gins(x86.ACMPL, &hi, &zero) - splitclean() - return gc.Gbranch(x86.AJNE, nil, +1) -} - -func blockcopy(n, res *gc.Node, osrc, odst, w int64) { - var dst gc.Node - gc.Nodreg(&dst, gc.Types[gc.Tptr], x86.REG_DI) - var src gc.Node - gc.Nodreg(&src, gc.Types[gc.Tptr], x86.REG_SI) - - var tsrc gc.Node - gc.Tempname(&tsrc, gc.Types[gc.Tptr]) - var tdst gc.Node - gc.Tempname(&tdst, gc.Types[gc.Tptr]) - if !n.Addable { - gc.Agen(n, &tsrc) - } - if !res.Addable { - gc.Agen(res, &tdst) - } - if n.Addable { - gc.Agen(n, &src) - } else { - gmove(&tsrc, &src) - } - - if res.Op == gc.ONAME { - gc.Gvardef(res) - } - - if res.Addable { - gc.Agen(res, &dst) - } else { - gmove(&tdst, &dst) - } - - c := int32(w % 4) // bytes - q := int32(w / 4) // doublewords - - // if we are copying forward on the stack and - // the src and dst overlap, then reverse direction - if osrc < odst && odst < osrc+w { - // reverse direction - gins(x86.ASTD, nil, nil) // set direction flag - if c > 0 { - gconreg(x86.AADDL, w-1, x86.REG_SI) - gconreg(x86.AADDL, w-1, x86.REG_DI) - - gconreg(x86.AMOVL, int64(c), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)-,*(DI)- - } - - if q > 0 { - if c > 0 { - gconreg(x86.AADDL, -3, x86.REG_SI) - gconreg(x86.AADDL, -3, x86.REG_DI) - } else { - gconreg(x86.AADDL, w-4, x86.REG_SI) - gconreg(x86.AADDL, w-4, x86.REG_DI) - } - - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)-,*(DI)- - } - - // we leave with the flag clear - gins(x86.ACLD, nil, nil) - } else { - gins(x86.ACLD, nil, nil) // paranoia. TODO(rsc): remove? - - // normal direction - if q > 128 || (q >= 4 && gc.Nacl) { - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ - } else if q >= 4 { - p := gins(obj.ADUFFCOPY, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffcopy", gc.Runtimepkg)) - - // 10 and 128 = magic constants: see ../../runtime/asm_386.s - p.To.Offset = 10 * (128 - int64(q)) - } else if !gc.Nacl && c == 0 { - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) - - // We don't need the MOVSL side-effect of updating SI and DI, - // and issuing a sequence of MOVLs directly is faster. - src.Op = gc.OINDREG - - dst.Op = gc.OINDREG - for q > 0 { - gmove(&src, &cx) // MOVL x+(SI),CX - gmove(&cx, &dst) // MOVL CX,x+(DI) - src.Xoffset += 4 - dst.Xoffset += 4 - q-- - } - } else { - for q > 0 { - gins(x86.AMOVSL, nil, nil) // MOVL *(SI)+,*(DI)+ - q-- - } - } - - for c > 0 { - gins(x86.AMOVSB, nil, nil) // MOVB *(SI)+,*(DI)+ - c-- - } - } -} diff --git a/src/cmd/compile/internal/x86/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go deleted file mode 100644 index ea52d6951a..0000000000 --- a/src/cmd/compile/internal/x86/cgen64.go +++ /dev/null @@ -1,598 +0,0 @@ -// Copyright 2009 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package x86 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" -) - -/* - * attempt to generate 64-bit - * res = n - * return 1 on success, 0 if op not handled. - */ -func cgen64(n *gc.Node, res *gc.Node) { - if res.Op != gc.OINDREG && res.Op != gc.ONAME { - gc.Dump("n", n) - gc.Dump("res", res) - gc.Fatalf("cgen64 %v of %v", n.Op, res.Op) - } - - switch n.Op { - default: - gc.Fatalf("cgen64 %v", n.Op) - - case gc.OMINUS: - gc.Cgen(n.Left, res) - var hi1 gc.Node - var lo1 gc.Node - split64(res, &lo1, &hi1) - gins(x86.ANEGL, nil, &lo1) - gins(x86.AADCL, ncon(0), &hi1) - gins(x86.ANEGL, nil, &hi1) - splitclean() - return - - case gc.OCOM: - gc.Cgen(n.Left, res) - var lo1 gc.Node - var hi1 gc.Node - split64(res, &lo1, &hi1) - gins(x86.ANOTL, nil, &lo1) - gins(x86.ANOTL, nil, &hi1) - splitclean() - return - - // binary operators. - // common setup below. - case gc.OADD, - gc.OSUB, - gc.OMUL, - gc.OLROT, - gc.OLSH, - gc.ORSH, - gc.OAND, - gc.OOR, - gc.OXOR: - break - } - - l := n.Left - r := n.Right - if !l.Addable { - var t1 gc.Node - gc.Tempname(&t1, l.Type) - gc.Cgen(l, &t1) - l = &t1 - } - - if r != nil && !r.Addable { - var t2 gc.Node - gc.Tempname(&t2, r.Type) - gc.Cgen(r, &t2) - r = &t2 - } - - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TINT32], x86.REG_AX) - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TINT32], x86.REG_CX) - var dx gc.Node - gc.Nodreg(&dx, gc.Types[gc.TINT32], x86.REG_DX) - - // Setup for binary operation. - var hi1 gc.Node - var lo1 gc.Node - split64(l, &lo1, &hi1) - - var lo2 gc.Node - var hi2 gc.Node - if gc.Is64(r.Type) { - split64(r, &lo2, &hi2) - } - - // Do op. Leave result in DX:AX. - switch n.Op { - // TODO: Constants - case gc.OADD: - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.AADDL, &lo2, &ax) - gins(x86.AADCL, &hi2, &dx) - - // TODO: Constants. - case gc.OSUB: - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.ASUBL, &lo2, &ax) - gins(x86.ASBBL, &hi2, &dx) - - case gc.OMUL: - // let's call the next three EX, FX and GX - var ex, fx, gx gc.Node - gc.Regalloc(&ex, gc.Types[gc.TPTR32], nil) - gc.Regalloc(&fx, gc.Types[gc.TPTR32], nil) - gc.Regalloc(&gx, gc.Types[gc.TPTR32], nil) - - // load args into DX:AX and EX:GX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - gins(x86.AMOVL, &lo2, &gx) - gins(x86.AMOVL, &hi2, &ex) - - // if DX and EX are zero, use 32 x 32 -> 64 unsigned multiply. - gins(x86.AMOVL, &dx, &fx) - - gins(x86.AORL, &ex, &fx) - p1 := gc.Gbranch(x86.AJNE, nil, 0) - gins(x86.AMULL, &gx, nil) // implicit &ax - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // full 64x64 -> 64, from 32x32 -> 64. - gins(x86.AIMULL, &gx, &dx) - - gins(x86.AMOVL, &ax, &fx) - gins(x86.AIMULL, &ex, &fx) - gins(x86.AADDL, &dx, &fx) - gins(x86.AMOVL, &gx, &dx) - gins(x86.AMULL, &dx, nil) // implicit &ax - gins(x86.AADDL, &fx, &dx) - gc.Patch(p2, gc.Pc) - - gc.Regfree(&ex) - gc.Regfree(&fx) - gc.Regfree(&gx) - - // We only rotate by a constant c in [0,64). - // if c >= 32: - // lo, hi = hi, lo - // c -= 32 - // if c == 0: - // no-op - // else: - // t = hi - // shld hi:lo, c - // shld lo:t, c - case gc.OLROT: - v := uint64(r.Int64()) - - if v >= 32 { - // reverse during load to do the first 32 bits of rotate - v -= 32 - - gins(x86.AMOVL, &lo1, &dx) - gins(x86.AMOVL, &hi1, &ax) - } else { - gins(x86.AMOVL, &lo1, &ax) - gins(x86.AMOVL, &hi1, &dx) - } - - if v == 0 { - } else // done - { - gins(x86.AMOVL, &dx, &cx) - p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - p1 = gins(x86.ASHLL, ncon(uint32(v)), &ax) - p1.From.Index = x86.REG_CX // double-width shift - p1.From.Scale = 0 - } - - case gc.OLSH: - if r.Op == gc.OLITERAL { - v := uint64(r.Int64()) - if v >= 64 { - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - split64(res, &lo2, &hi2) - gins(x86.AMOVL, ncon(0), &lo2) - gins(x86.AMOVL, ncon(0), &hi2) - splitclean() - return - } - - if v >= 32 { - if gc.Is64(r.Type) { - splitclean() - } - split64(res, &lo2, &hi2) - gmove(&lo1, &hi2) - if v > 32 { - gins(x86.ASHLL, ncon(uint32(v-32)), &hi2) - } - - gins(x86.AMOVL, ncon(0), &lo2) - splitclean() - splitclean() - return - } - - // general shift - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - p1 := gins(x86.ASHLL, ncon(uint32(v)), &dx) - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - gins(x86.ASHLL, ncon(uint32(v)), &ax) - break - } - - // load value into DX:AX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - - // load shift value into register. - // if high bits are set, zero value. - var p1 *obj.Prog - - if gc.Is64(r.Type) { - gins(x86.ACMPL, &hi2, ncon(0)) - p1 = gc.Gbranch(x86.AJNE, nil, +1) - gins(x86.AMOVL, &lo2, &cx) - } else { - cx.Type = gc.Types[gc.TUINT32] - gmove(r, &cx) - } - - // if shift count is >=64, zero value - gins(x86.ACMPL, &cx, ncon(64)) - - p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - if p1 != nil { - gc.Patch(p1, gc.Pc) - } - gins(x86.AXORL, &dx, &dx) - gins(x86.AXORL, &ax, &ax) - gc.Patch(p2, gc.Pc) - - // if shift count is >= 32, zero low. - gins(x86.ACMPL, &cx, ncon(32)) - - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - gins(x86.AMOVL, &ax, &dx) - gins(x86.ASHLL, &cx, &dx) // SHLL only uses bottom 5 bits of count - gins(x86.AXORL, &ax, &ax) - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // general shift - p1 = gins(x86.ASHLL, &cx, &dx) - - p1.From.Index = x86.REG_AX // double-width shift - p1.From.Scale = 0 - gins(x86.ASHLL, &cx, &ax) - gc.Patch(p2, gc.Pc) - - case gc.ORSH: - if r.Op == gc.OLITERAL { - v := uint64(r.Int64()) - if v >= 64 { - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - split64(res, &lo2, &hi2) - if hi1.Type.Etype == gc.TINT32 { - gmove(&hi1, &lo2) - gins(x86.ASARL, ncon(31), &lo2) - gmove(&hi1, &hi2) - gins(x86.ASARL, ncon(31), &hi2) - } else { - gins(x86.AMOVL, ncon(0), &lo2) - gins(x86.AMOVL, ncon(0), &hi2) - } - - splitclean() - return - } - - if v >= 32 { - if gc.Is64(r.Type) { - splitclean() - } - split64(res, &lo2, &hi2) - gmove(&hi1, &lo2) - if v > 32 { - gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v-32)), &lo2) - } - if hi1.Type.Etype == gc.TINT32 { - gmove(&hi1, &hi2) - gins(x86.ASARL, ncon(31), &hi2) - } else { - gins(x86.AMOVL, ncon(0), &hi2) - } - splitclean() - splitclean() - return - } - - // general shift - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - p1 := gins(x86.ASHRL, ncon(uint32(v)), &ax) - p1.From.Index = x86.REG_DX // double-width shift - p1.From.Scale = 0 - gins(optoas(gc.ORSH, hi1.Type), ncon(uint32(v)), &dx) - break - } - - // load value into DX:AX. - gins(x86.AMOVL, &lo1, &ax) - - gins(x86.AMOVL, &hi1, &dx) - - // load shift value into register. - // if high bits are set, zero value. - var p1 *obj.Prog - - if gc.Is64(r.Type) { - gins(x86.ACMPL, &hi2, ncon(0)) - p1 = gc.Gbranch(x86.AJNE, nil, +1) - gins(x86.AMOVL, &lo2, &cx) - } else { - cx.Type = gc.Types[gc.TUINT32] - gmove(r, &cx) - } - - // if shift count is >=64, zero or sign-extend value - gins(x86.ACMPL, &cx, ncon(64)) - - p2 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - if p1 != nil { - gc.Patch(p1, gc.Pc) - } - if hi1.Type.Etype == gc.TINT32 { - gins(x86.ASARL, ncon(31), &dx) - gins(x86.AMOVL, &dx, &ax) - } else { - gins(x86.AXORL, &dx, &dx) - gins(x86.AXORL, &ax, &ax) - } - - gc.Patch(p2, gc.Pc) - - // if shift count is >= 32, sign-extend hi. - gins(x86.ACMPL, &cx, ncon(32)) - - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - gins(x86.AMOVL, &dx, &ax) - if hi1.Type.Etype == gc.TINT32 { - gins(x86.ASARL, &cx, &ax) // SARL only uses bottom 5 bits of count - gins(x86.ASARL, ncon(31), &dx) - } else { - gins(x86.ASHRL, &cx, &ax) - gins(x86.AXORL, &dx, &dx) - } - - p2 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // general shift - p1 = gins(x86.ASHRL, &cx, &ax) - - p1.From.Index = x86.REG_DX // double-width shift - p1.From.Scale = 0 - gins(optoas(gc.ORSH, hi1.Type), &cx, &dx) - gc.Patch(p2, gc.Pc) - - // make constant the right side (it usually is anyway). - case gc.OXOR, - gc.OAND, - gc.OOR: - if lo1.Op == gc.OLITERAL { - nswap(&lo1, &lo2) - nswap(&hi1, &hi2) - } - - if lo2.Op == gc.OLITERAL { - // special cases for constants. - lv := uint32(lo2.Int64()) - hv := uint32(hi2.Int64()) - splitclean() // right side - split64(res, &lo2, &hi2) - switch n.Op { - case gc.OXOR: - gmove(&lo1, &lo2) - gmove(&hi1, &hi2) - switch lv { - case 0: - break - - case 0xffffffff: - gins(x86.ANOTL, nil, &lo2) - - default: - gins(x86.AXORL, ncon(lv), &lo2) - } - - switch hv { - case 0: - break - - case 0xffffffff: - gins(x86.ANOTL, nil, &hi2) - - default: - gins(x86.AXORL, ncon(hv), &hi2) - } - - case gc.OAND: - switch lv { - case 0: - gins(x86.AMOVL, ncon(0), &lo2) - - default: - gmove(&lo1, &lo2) - if lv != 0xffffffff { - gins(x86.AANDL, ncon(lv), &lo2) - } - } - - switch hv { - case 0: - gins(x86.AMOVL, ncon(0), &hi2) - - default: - gmove(&hi1, &hi2) - if hv != 0xffffffff { - gins(x86.AANDL, ncon(hv), &hi2) - } - } - - case gc.OOR: - switch lv { - case 0: - gmove(&lo1, &lo2) - - case 0xffffffff: - gins(x86.AMOVL, ncon(0xffffffff), &lo2) - - default: - gmove(&lo1, &lo2) - gins(x86.AORL, ncon(lv), &lo2) - } - - switch hv { - case 0: - gmove(&hi1, &hi2) - - case 0xffffffff: - gins(x86.AMOVL, ncon(0xffffffff), &hi2) - - default: - gmove(&hi1, &hi2) - gins(x86.AORL, ncon(hv), &hi2) - } - } - - splitclean() - splitclean() - return - } - - gins(x86.AMOVL, &lo1, &ax) - gins(x86.AMOVL, &hi1, &dx) - gins(optoas(n.Op, lo1.Type), &lo2, &ax) - gins(optoas(n.Op, lo1.Type), &hi2, &dx) - } - - if gc.Is64(r.Type) { - splitclean() - } - splitclean() - - split64(res, &lo1, &hi1) - gins(x86.AMOVL, &ax, &lo1) - gins(x86.AMOVL, &dx, &hi1) - splitclean() -} - -/* - * generate comparison of nl, nr, both 64-bit. - * nl is memory; nr is constant or memory. - */ -func cmp64(nl *gc.Node, nr *gc.Node, op gc.Op, likely int, to *obj.Prog) { - var lo1 gc.Node - var hi1 gc.Node - var lo2 gc.Node - var hi2 gc.Node - var rr gc.Node - - split64(nl, &lo1, &hi1) - split64(nr, &lo2, &hi2) - - // compare most significant word; - // if they differ, we're done. - t := hi1.Type - - if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { - gins(x86.ACMPL, &hi1, &hi2) - } else { - gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) - gins(x86.AMOVL, &hi1, &rr) - gins(x86.ACMPL, &rr, &hi2) - gc.Regfree(&rr) - } - - var br *obj.Prog - switch op { - default: - gc.Fatalf("cmp64 %v %v", op, t) - - // cmp hi - // jne L - // cmp lo - // jeq to - // L: - case gc.OEQ: - br = gc.Gbranch(x86.AJNE, nil, -likely) - - // cmp hi - // jne to - // cmp lo - // jne to - case gc.ONE: - gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) - - // cmp hi - // jgt to - // jlt L - // cmp lo - // jge to (or jgt to) - // L: - case gc.OGE, - gc.OGT: - gc.Patch(gc.Gbranch(optoas(gc.OGT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OLT, t), nil, -likely) - - // cmp hi - // jlt to - // jgt L - // cmp lo - // jle to (or jlt to) - // L: - case gc.OLE, - gc.OLT: - gc.Patch(gc.Gbranch(optoas(gc.OLT, t), nil, likely), to) - - br = gc.Gbranch(optoas(gc.OGT, t), nil, -likely) - } - - // compare least significant word - t = lo1.Type - - if nl.Op == gc.OLITERAL || nr.Op == gc.OLITERAL { - gins(x86.ACMPL, &lo1, &lo2) - } else { - gc.Regalloc(&rr, gc.Types[gc.TINT32], nil) - gins(x86.AMOVL, &lo1, &rr) - gins(x86.ACMPL, &rr, &lo2) - gc.Regfree(&rr) - } - - // jump again - gc.Patch(gc.Gbranch(optoas(op, t), nil, likely), to) - - // point first branch down here if appropriate - if br != nil { - gc.Patch(br, gc.Pc) - } - - splitclean() - splitclean() -} diff --git a/src/cmd/compile/internal/x86/galign.go b/src/cmd/compile/internal/x86/galign.go index 9fb4712e92..269ff56938 100644 --- a/src/cmd/compile/internal/x86/galign.go +++ b/src/cmd/compile/internal/x86/galign.go @@ -40,42 +40,9 @@ func Main() { gc.Thearch.ReservedRegs = resvd gc.Thearch.Betypeinit = betypeinit - gc.Thearch.Bgen_float = bgen_float - gc.Thearch.Cgen64 = cgen64 - gc.Thearch.Cgen_bmul = cgen_bmul - gc.Thearch.Cgen_float = cgen_float - gc.Thearch.Cgen_hmul = cgen_hmul - gc.Thearch.Cgen_shift = cgen_shift - gc.Thearch.Clearfat = clearfat - gc.Thearch.Cmp64 = cmp64 gc.Thearch.Defframe = defframe - gc.Thearch.Dodiv = cgen_div - gc.Thearch.Excise = excise - gc.Thearch.Expandchecks = expandchecks - gc.Thearch.Getg = getg gc.Thearch.Gins = gins - gc.Thearch.Ginscmp = ginscmp - gc.Thearch.Ginscon = ginscon - gc.Thearch.Ginsnop = ginsnop - gc.Thearch.Gmove = gmove - gc.Thearch.Igenindex = igenindex - gc.Thearch.Peep = peep gc.Thearch.Proginfo = proginfo - gc.Thearch.Regtyp = regtyp - gc.Thearch.Sameaddr = sameaddr - gc.Thearch.Smallindir = smallindir - gc.Thearch.Stackaddr = stackaddr - gc.Thearch.Blockcopy = blockcopy - gc.Thearch.Sudoaddable = sudoaddable - gc.Thearch.Sudoclean = sudoclean - gc.Thearch.Excludedregs = excludedregs - gc.Thearch.RtoB = RtoB - gc.Thearch.FtoB = FtoB - gc.Thearch.BtoR = BtoR - gc.Thearch.BtoF = BtoF - gc.Thearch.Optoas = optoas - gc.Thearch.Doregbits = doregbits - gc.Thearch.Regnames = regnames gc.Thearch.SSARegToReg = ssaRegToReg gc.Thearch.SSAMarkMoves = ssaMarkMoves diff --git a/src/cmd/compile/internal/x86/ggen.go b/src/cmd/compile/internal/x86/ggen.go index 71dbd74078..f7012661db 100644 --- a/src/cmd/compile/internal/x86/ggen.go +++ b/src/cmd/compile/internal/x86/ggen.go @@ -99,821 +99,3 @@ func appendpp(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int, foffset int6 p.Link = q return q } - -func clearfat(nl *gc.Node) { - /* clear a fat object */ - if gc.Debug['g'] != 0 { - gc.Dump("\nclearfat", nl) - } - - w := uint32(nl.Type.Width) - - // Avoid taking the address for simple enough types. - if gc.Componentgen(nil, nl) { - return - } - - c := w % 4 // bytes - q := w / 4 // quads - - if q < 4 { - // Write sequence of MOV 0, off(base) instead of using STOSL. - // The hope is that although the code will be slightly longer, - // the MOVs will have no dependencies and pipeline better - // than the unrolled STOSL loop. - // NOTE: Must use agen, not igen, so that optimizer sees address - // being taken. We are not writing on field boundaries. - var n1 gc.Node - gc.Regalloc(&n1, gc.Types[gc.Tptr], nil) - - gc.Agen(nl, &n1) - n1.Op = gc.OINDREG - var z gc.Node - gc.Nodconst(&z, gc.Types[gc.TUINT64], 0) - for ; q > 0; q-- { - n1.Type = z.Type - gins(x86.AMOVL, &z, &n1) - n1.Xoffset += 4 - } - - gc.Nodconst(&z, gc.Types[gc.TUINT8], 0) - for ; c > 0; c-- { - n1.Type = z.Type - gins(x86.AMOVB, &z, &n1) - n1.Xoffset++ - } - - gc.Regfree(&n1) - return - } - - var n1 gc.Node - gc.Nodreg(&n1, gc.Types[gc.Tptr], x86.REG_DI) - gc.Agen(nl, &n1) - gconreg(x86.AMOVL, 0, x86.REG_AX) - - if q > 128 || (q >= 4 && gc.Nacl) { - gconreg(x86.AMOVL, int64(q), x86.REG_CX) - gins(x86.AREP, nil, nil) // repeat - gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ - } else if q >= 4 { - p := gins(obj.ADUFFZERO, nil, nil) - p.To.Type = obj.TYPE_ADDR - p.To.Sym = gc.Linksym(gc.Pkglookup("duffzero", gc.Runtimepkg)) - - // 1 and 128 = magic constants: see ../../runtime/asm_386.s - p.To.Offset = 1 * (128 - int64(q)) - } else { - for q > 0 { - gins(x86.ASTOSL, nil, nil) // STOL AL,*(DI)+ - q-- - } - } - - for c > 0 { - gins(x86.ASTOSB, nil, nil) // STOB AL,*(DI)+ - c-- - } -} - -var panicdiv *gc.Node - -/* - * generate division. - * caller must set: - * ax = allocated AX register - * dx = allocated DX register - * generates one of: - * res = nl / nr - * res = nl % nr - * according to op. - */ -func dodiv(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.Node) { - // Have to be careful about handling - // most negative int divided by -1 correctly. - // The hardware will trap. - // Also the byte divide instruction needs AH, - // which we otherwise don't have to deal with. - // Easiest way to avoid for int8, int16: use int32. - // For int32 and int64, use explicit test. - // Could use int64 hw for int32. - t := nl.Type - - t0 := t - check := false - if t.IsSigned() { - check = true - if gc.Isconst(nl, gc.CTINT) && nl.Int64() != -1< 0 && !gc.Samereg(x, res) { - gc.Tempname(oldx, gc.Types[gc.TINT32]) - gmove(x, oldx) - } - - gc.Regalloc(x, t, x) -} - -func restx(x *gc.Node, oldx *gc.Node) { - gc.Regfree(x) - - if oldx.Op != 0 { - x.Type = gc.Types[gc.TINT32] - gmove(oldx, x) - } -} - -/* - * generate division according to op, one of: - * res = nl / nr - * res = nl % nr - */ -func cgen_div(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if gc.Is64(nl.Type) { - gc.Fatalf("cgen_div %v", nl.Type) - } - - var t *gc.Type - if nl.Type.IsSigned() { - t = gc.Types[gc.TINT32] - } else { - t = gc.Types[gc.TUINT32] - } - var ax gc.Node - var oldax gc.Node - savex(x86.REG_AX, &ax, &oldax, res, t) - var olddx gc.Node - var dx gc.Node - savex(x86.REG_DX, &dx, &olddx, res, t) - dodiv(op, nl, nr, res, &ax, &dx) - restx(&dx, &olddx) - restx(&ax, &oldax) -} - -/* - * generate shift according to op, one of: - * res = nl << nr - * res = nl >> nr - */ -func cgen_shift(op gc.Op, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) { - if nl.Type.Width > 4 { - gc.Fatalf("cgen_shift %v", nl.Type) - } - - w := int(nl.Type.Width * 8) - - a := optoas(op, nl.Type) - - if nr.Op == gc.OLITERAL { - var n2 gc.Node - gc.Tempname(&n2, nl.Type) - gc.Cgen(nl, &n2) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gmove(&n2, &n1) - sc := uint64(nr.Int64()) - if sc >= uint64(nl.Type.Width*8) { - // large shift gets 2 shifts by width-1 - gins(a, ncon(uint32(w)-1), &n1) - - gins(a, ncon(uint32(w)-1), &n1) - } else { - gins(a, nr, &n1) - } - gmove(&n1, res) - gc.Regfree(&n1) - return - } - - var oldcx gc.Node - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) - if gc.GetReg(x86.REG_CX) > 1 && !gc.Samereg(&cx, res) { - gc.Tempname(&oldcx, gc.Types[gc.TUINT32]) - gmove(&cx, &oldcx) - } - - var n1 gc.Node - var nt gc.Node - if nr.Type.Width > 4 { - gc.Tempname(&nt, nr.Type) - n1 = nt - } else { - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - gc.Regalloc(&n1, nr.Type, &n1) // to hold the shift type in CX - } - - var n2 gc.Node - if gc.Samereg(&cx, res) { - gc.Regalloc(&n2, nl.Type, nil) - } else { - gc.Regalloc(&n2, nl.Type, res) - } - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &n2) - gc.Cgen(nr, &n1) - } else { - gc.Cgen(nr, &n1) - gc.Cgen(nl, &n2) - } - - // test and fix up large shifts - if bounded { - if nr.Type.Width > 4 { - // delayed reg alloc - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX - var lo gc.Node - var hi gc.Node - split64(&nt, &lo, &hi) - gmove(&lo, &n1) - splitclean() - } - } else { - var p1 *obj.Prog - if nr.Type.Width > 4 { - // delayed reg alloc - gc.Nodreg(&n1, gc.Types[gc.TUINT32], x86.REG_CX) - - gc.Regalloc(&n1, gc.Types[gc.TUINT32], &n1) // to hold the shift type in CX - var lo gc.Node - var hi gc.Node - split64(&nt, &lo, &hi) - gmove(&lo, &n1) - gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &hi, ncon(0)) - p2 := gc.Gbranch(optoas(gc.ONE, gc.Types[gc.TUINT32]), nil, +1) - gins(optoas(gc.OCMP, gc.Types[gc.TUINT32]), &n1, ncon(uint32(w))) - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - splitclean() - gc.Patch(p2, gc.Pc) - } else { - gins(optoas(gc.OCMP, nr.Type), &n1, ncon(uint32(w))) - p1 = gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TUINT32]), nil, +1) - } - - if op == gc.ORSH && nl.Type.IsSigned() { - gins(a, ncon(uint32(w)-1), &n2) - } else { - gmove(ncon(0), &n2) - } - - gc.Patch(p1, gc.Pc) - } - - gins(a, &n1, &n2) - - if oldcx.Op != 0 { - gmove(&oldcx, &cx) - } - - gmove(&n2, res) - - gc.Regfree(&n1) - gc.Regfree(&n2) -} - -/* - * generate byte multiply: - * res = nl * nr - * there is no 2-operand byte multiply instruction so - * we do a full-width multiplication and truncate afterwards. - */ -func cgen_bmul(op gc.Op, nl *gc.Node, nr *gc.Node, res *gc.Node) bool { - if optoas(op, nl.Type) != x86.AIMULB { - return false - } - - // copy from byte to full registers - t := gc.Types[gc.TUINT32] - - if nl.Type.IsSigned() { - t = gc.Types[gc.TINT32] - } - - // largest ullman on left. - if nl.Ullman < nr.Ullman { - nl, nr = nr, nl - } - - var nt gc.Node - gc.Tempname(&nt, nl.Type) - gc.Cgen(nl, &nt) - var n1 gc.Node - gc.Regalloc(&n1, t, res) - gc.Cgen(nr, &n1) - var n2 gc.Node - gc.Regalloc(&n2, t, nil) - gmove(&nt, &n2) - a := optoas(op, t) - gins(a, &n2, &n1) - gc.Regfree(&n2) - gmove(&n1, res) - gc.Regfree(&n1) - - return true -} - -/* - * generate high multiply: - * res = (nl*nr) >> width - */ -func cgen_hmul(nl *gc.Node, nr *gc.Node, res *gc.Node) { - var n1 gc.Node - var n2 gc.Node - - t := nl.Type - a := optoas(gc.OHMUL, t) - - // gen nl in n1. - gc.Tempname(&n1, t) - gc.Cgen(nl, &n1) - - // gen nr in n2. - gc.Regalloc(&n2, t, res) - gc.Cgen(nr, &n2) - - var ax, oldax, dx, olddx gc.Node - savex(x86.REG_AX, &ax, &oldax, res, gc.Types[gc.TUINT32]) - savex(x86.REG_DX, &dx, &olddx, res, gc.Types[gc.TUINT32]) - - gmove(&n2, &ax) - gins(a, &n1, nil) - gc.Regfree(&n2) - - if t.Width == 1 { - // byte multiply behaves differently. - var byteAH, byteDX gc.Node - gc.Nodreg(&byteAH, t, x86.REG_AH) - gc.Nodreg(&byteDX, t, x86.REG_DX) - gmove(&byteAH, &byteDX) - } - - gmove(&dx, res) - - restx(&ax, &oldax) - restx(&dx, &olddx) -} - -/* - * generate floating-point operation. - */ -func cgen_float(n *gc.Node, res *gc.Node) { - nl := n.Left - switch n.Op { - case gc.OEQ, - gc.ONE, - gc.OLT, - gc.OLE, - gc.OGE: - p1 := gc.Gbranch(obj.AJMP, nil, 0) - p2 := gc.Pc - gmove(gc.Nodbool(true), res) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gc.Bgen(n, true, 0, p2) - gmove(gc.Nodbool(false), res) - gc.Patch(p3, gc.Pc) - return - - case gc.OPLUS: - gc.Cgen(nl, res) - return - - case gc.OCONV: - if gc.Eqtype(n.Type, nl.Type) || gc.Noconv(n.Type, nl.Type) { - gc.Cgen(nl, res) - return - } - - var n2 gc.Node - gc.Tempname(&n2, n.Type) - var n1 gc.Node - gc.Mgen(nl, &n1, res) - gmove(&n1, &n2) - gmove(&n2, res) - gc.Mfree(&n1) - return - } - - if gc.Thearch.Use387 { - cgen_float387(n, res) - } else { - cgen_floatsse(n, res) - } -} - -// floating-point. 387 (not SSE2) -func cgen_float387(n *gc.Node, res *gc.Node) { - var f0 gc.Node - var f1 gc.Node - - nl := n.Left - nr := n.Right - gc.Nodreg(&f0, nl.Type, x86.REG_F0) - gc.Nodreg(&f1, n.Type, x86.REG_F0+1) - if nr != nil { - // binary - if nl.Ullman >= nr.Ullman { - gc.Cgen(nl, &f0) - if nr.Addable { - gins(foptoas(n.Op, n.Type, 0), nr, &f0) - } else { - gc.Cgen(nr, &f0) - gins(foptoas(n.Op, n.Type, Fpop), &f0, &f1) - } - } else { - gc.Cgen(nr, &f0) - if nl.Addable { - gins(foptoas(n.Op, n.Type, Frev), nl, &f0) - } else { - gc.Cgen(nl, &f0) - gins(foptoas(n.Op, n.Type, Frev|Fpop), &f0, &f1) - } - } - - gmove(&f0, res) - return - } - - // unary - gc.Cgen(nl, &f0) - - if n.Op != gc.OCONV && n.Op != gc.OPLUS { - gins(foptoas(n.Op, n.Type, 0), nil, nil) - } - gmove(&f0, res) - return -} - -func cgen_floatsse(n *gc.Node, res *gc.Node) { - var a obj.As - - nl := n.Left - nr := n.Right - switch n.Op { - default: - gc.Dump("cgen_floatsse", n) - gc.Fatalf("cgen_floatsse %v", n.Op) - return - - case gc.OMINUS, - gc.OCOM: - nr = gc.NegOne(n.Type) - a = foptoas(gc.OMUL, nl.Type, 0) - goto sbop - - // symmetric binary - case gc.OADD, - gc.OMUL: - a = foptoas(n.Op, nl.Type, 0) - - goto sbop - - // asymmetric binary - case gc.OSUB, - gc.OMOD, - gc.ODIV: - a = foptoas(n.Op, nl.Type, 0) - - goto abop - } - -sbop: // symmetric binary - if nl.Ullman < nr.Ullman || nl.Op == gc.OLITERAL { - nl, nr = nr, nl - } - -abop: // asymmetric binary - if nl.Ullman >= nr.Ullman { - var nt gc.Node - gc.Tempname(&nt, nl.Type) - gc.Cgen(nl, &nt) - var n2 gc.Node - gc.Mgen(nr, &n2, nil) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, res) - gmove(&nt, &n1) - gins(a, &n2, &n1) - gmove(&n1, res) - gc.Regfree(&n1) - gc.Mfree(&n2) - } else { - var n2 gc.Node - gc.Regalloc(&n2, nr.Type, res) - gc.Cgen(nr, &n2) - var n1 gc.Node - gc.Regalloc(&n1, nl.Type, nil) - gc.Cgen(nl, &n1) - gins(a, &n2, &n1) - gc.Regfree(&n2) - gmove(&n1, res) - gc.Regfree(&n1) - } - - return -} - -func bgen_float(n *gc.Node, wantTrue bool, likely int, to *obj.Prog) { - nl := n.Left - nr := n.Right - op := n.Op - if !wantTrue { - // brcom is not valid on floats when NaN is involved. - p1 := gc.Gbranch(obj.AJMP, nil, 0) - p2 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - - // No need to avoid re-genning ninit. - bgen_float(n, true, -likely, p2) - - gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) - gc.Patch(p2, gc.Pc) - return - } - - if gc.Thearch.Use387 { - op = gc.Brrev(op) // because the args are stacked - if op == gc.OGE || op == gc.OGT { - // only < and <= work right with NaN; reverse if needed - nl, nr = nr, nl - op = gc.Brrev(op) - } - - var ax, n2, tmp gc.Node - gc.Nodreg(&tmp, nr.Type, x86.REG_F0) - gc.Nodreg(&n2, nr.Type, x86.REG_F0+1) - gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) - if gc.Simsimtype(nr.Type) == gc.TFLOAT64 { - if nl.Ullman > nr.Ullman { - gc.Cgen(nl, &tmp) - gc.Cgen(nr, &tmp) - gins(x86.AFXCHD, &tmp, &n2) - } else { - gc.Cgen(nr, &tmp) - gc.Cgen(nl, &tmp) - } - gins(x86.AFUCOMPP, &tmp, &n2) - } else { - // TODO(rsc): The moves back and forth to memory - // here are for truncating the value to 32 bits. - // This handles 32-bit comparison but presumably - // all the other ops have the same problem. - // We need to figure out what the right general - // solution is, besides telling people to use float64. - var t1 gc.Node - gc.Tempname(&t1, gc.Types[gc.TFLOAT32]) - - var t2 gc.Node - gc.Tempname(&t2, gc.Types[gc.TFLOAT32]) - gc.Cgen(nr, &t1) - gc.Cgen(nl, &t2) - gmove(&t2, &tmp) - gins(x86.AFCOMFP, &t1, &tmp) - } - gins(x86.AFSTSW, nil, &ax) - gins(x86.ASAHF, nil, nil) - } else { - // Not 387 - if !nl.Addable { - nl = gc.CgenTemp(nl) - } - if !nr.Addable { - nr = gc.CgenTemp(nr) - } - - var n2 gc.Node - gc.Regalloc(&n2, nr.Type, nil) - gmove(nr, &n2) - nr = &n2 - - if nl.Op != gc.OREGISTER { - var n3 gc.Node - gc.Regalloc(&n3, nl.Type, nil) - gmove(nl, &n3) - nl = &n3 - } - - if op == gc.OGE || op == gc.OGT { - // only < and <= work right with NopN; reverse if needed - nl, nr = nr, nl - op = gc.Brrev(op) - } - - gins(foptoas(gc.OCMP, nr.Type, 0), nl, nr) - if nl.Op == gc.OREGISTER { - gc.Regfree(nl) - } - gc.Regfree(nr) - } - - switch op { - case gc.OEQ: - // neither NE nor P - p1 := gc.Gbranch(x86.AJNE, nil, -likely) - p2 := gc.Gbranch(x86.AJPS, nil, -likely) - gc.Patch(gc.Gbranch(obj.AJMP, nil, 0), to) - gc.Patch(p1, gc.Pc) - gc.Patch(p2, gc.Pc) - case gc.ONE: - // either NE or P - gc.Patch(gc.Gbranch(x86.AJNE, nil, likely), to) - gc.Patch(gc.Gbranch(x86.AJPS, nil, likely), to) - default: - gc.Patch(gc.Gbranch(optoas(op, nr.Type), nil, likely), to) - } -} - -// Called after regopt and peep have run. -// Expand CHECKNIL pseudo-op into actual nil pointer check. -func expandchecks(firstp *obj.Prog) { - var p1 *obj.Prog - var p2 *obj.Prog - - for p := firstp; p != nil; p = p.Link { - if p.As != obj.ACHECKNIL { - continue - } - if gc.Debug_checknil != 0 && p.Lineno > 1 { // p->lineno==1 in generated wrappers - gc.Warnl(p.Lineno, "generated nil check") - } - - // check is - // CMP arg, $0 - // JNE 2(PC) (likely) - // MOV AX, 0 - p1 = gc.Ctxt.NewProg() - - p2 = gc.Ctxt.NewProg() - gc.Clearp(p1) - gc.Clearp(p2) - p1.Link = p2 - p2.Link = p.Link - p.Link = p1 - p1.Lineno = p.Lineno - p2.Lineno = p.Lineno - p1.Pc = 9999 - p2.Pc = 9999 - p.As = x86.ACMPL - p.To.Type = obj.TYPE_CONST - p.To.Offset = 0 - p1.As = x86.AJNE - p1.From.Type = obj.TYPE_CONST - p1.From.Offset = 1 // likely - p1.To.Type = obj.TYPE_BRANCH - p1.To.Val = p2.Link - - // crash by write to memory address 0. - // if possible, since we know arg is 0, use 0(arg), - // which will be shorter to encode than plain 0. - p2.As = x86.AMOVL - - p2.From.Type = obj.TYPE_REG - p2.From.Reg = x86.REG_AX - if regtyp(&p.From) { - p2.To.Type = obj.TYPE_MEM - p2.To.Reg = p.From.Reg - } else { - p2.To.Type = obj.TYPE_MEM - } - p2.To.Offset = 0 - } -} - -// addr += index*width if possible. -func addindex(index *gc.Node, width int64, addr *gc.Node) bool { - switch width { - case 1, 2, 4, 8: - p1 := gins(x86.ALEAL, index, addr) - p1.From.Type = obj.TYPE_MEM - p1.From.Scale = int16(width) - p1.From.Index = p1.From.Reg - p1.From.Reg = p1.To.Reg - return true - } - return false -} - -// res = runtime.getg() -func getg(res *gc.Node) { - var n1 gc.Node - gc.Regalloc(&n1, res.Type, res) - mov := optoas(gc.OAS, gc.Types[gc.Tptr]) - p := gins(mov, nil, &n1) - p.From.Type = obj.TYPE_REG - p.From.Reg = x86.REG_TLS - p = gins(mov, nil, &n1) - p.From = p.To - p.From.Type = obj.TYPE_MEM - p.From.Index = x86.REG_TLS - p.From.Scale = 1 - gmove(&n1, res) - gc.Regfree(&n1) -} diff --git a/src/cmd/compile/internal/x86/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go index 9ad63052ca..a0d36a2d70 100644 --- a/src/cmd/compile/internal/x86/gsubr.go +++ b/src/cmd/compile/internal/x86/gsubr.go @@ -31,566 +31,12 @@ package x86 import ( - "cmd/compile/internal/big" "cmd/compile/internal/gc" "cmd/internal/obj" "cmd/internal/obj/x86" "fmt" ) -// TODO(rsc): Can make this bigger if we move -// the text segment up higher in 8l for all GOOS. -// At the same time, can raise StackBig in ../../runtime/stack.h. -var unmappedzero uint32 = 4096 - -// foptoas flags -const ( - Frev = 1 << 0 - Fpop = 1 << 1 - Fpop2 = 1 << 2 -) - -/* - * return Axxx for Oxxx on type t. - */ -func optoas(op gc.Op, t *gc.Type) obj.As { - if t == nil { - gc.Fatalf("optoas: t is nil") - } - - // avoid constant conversions in switches below - const ( - OMINUS_ = uint32(gc.OMINUS) << 16 - OLSH_ = uint32(gc.OLSH) << 16 - ORSH_ = uint32(gc.ORSH) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OMOD_ = uint32(gc.OMOD) << 16 - OOR_ = uint32(gc.OOR) << 16 - OAND_ = uint32(gc.OAND) << 16 - OXOR_ = uint32(gc.OXOR) << 16 - OEQ_ = uint32(gc.OEQ) << 16 - ONE_ = uint32(gc.ONE) << 16 - OLT_ = uint32(gc.OLT) << 16 - OLE_ = uint32(gc.OLE) << 16 - OGE_ = uint32(gc.OGE) << 16 - OGT_ = uint32(gc.OGT) << 16 - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OHMUL_ = uint32(gc.OHMUL) << 16 - OADDR_ = uint32(gc.OADDR) << 16 - OINC_ = uint32(gc.OINC) << 16 - ODEC_ = uint32(gc.ODEC) << 16 - OLROT_ = uint32(gc.OLROT) << 16 - OEXTEND_ = uint32(gc.OEXTEND) << 16 - OCOM_ = uint32(gc.OCOM) << 16 - ) - - a := obj.AXXX - switch uint32(op)<<16 | uint32(gc.Simtype[t.Etype]) { - default: - gc.Fatalf("optoas: no entry %v-%v", op, t) - - case OADDR_ | gc.TPTR32: - a = x86.ALEAL - - case OEQ_ | gc.TBOOL, - OEQ_ | gc.TINT8, - OEQ_ | gc.TUINT8, - OEQ_ | gc.TINT16, - OEQ_ | gc.TUINT16, - OEQ_ | gc.TINT32, - OEQ_ | gc.TUINT32, - OEQ_ | gc.TINT64, - OEQ_ | gc.TUINT64, - OEQ_ | gc.TPTR32, - OEQ_ | gc.TPTR64, - OEQ_ | gc.TFLOAT32, - OEQ_ | gc.TFLOAT64: - a = x86.AJEQ - - case ONE_ | gc.TBOOL, - ONE_ | gc.TINT8, - ONE_ | gc.TUINT8, - ONE_ | gc.TINT16, - ONE_ | gc.TUINT16, - ONE_ | gc.TINT32, - ONE_ | gc.TUINT32, - ONE_ | gc.TINT64, - ONE_ | gc.TUINT64, - ONE_ | gc.TPTR32, - ONE_ | gc.TPTR64, - ONE_ | gc.TFLOAT32, - ONE_ | gc.TFLOAT64: - a = x86.AJNE - - case OLT_ | gc.TINT8, - OLT_ | gc.TINT16, - OLT_ | gc.TINT32, - OLT_ | gc.TINT64: - a = x86.AJLT - - case OLT_ | gc.TUINT8, - OLT_ | gc.TUINT16, - OLT_ | gc.TUINT32, - OLT_ | gc.TUINT64: - a = x86.AJCS - - case OLE_ | gc.TINT8, - OLE_ | gc.TINT16, - OLE_ | gc.TINT32, - OLE_ | gc.TINT64: - a = x86.AJLE - - case OLE_ | gc.TUINT8, - OLE_ | gc.TUINT16, - OLE_ | gc.TUINT32, - OLE_ | gc.TUINT64: - a = x86.AJLS - - case OGT_ | gc.TINT8, - OGT_ | gc.TINT16, - OGT_ | gc.TINT32, - OGT_ | gc.TINT64: - a = x86.AJGT - - case OGT_ | gc.TUINT8, - OGT_ | gc.TUINT16, - OGT_ | gc.TUINT32, - OGT_ | gc.TUINT64, - OLT_ | gc.TFLOAT32, - OLT_ | gc.TFLOAT64: - a = x86.AJHI - - case OGE_ | gc.TINT8, - OGE_ | gc.TINT16, - OGE_ | gc.TINT32, - OGE_ | gc.TINT64: - a = x86.AJGE - - case OGE_ | gc.TUINT8, - OGE_ | gc.TUINT16, - OGE_ | gc.TUINT32, - OGE_ | gc.TUINT64, - OLE_ | gc.TFLOAT32, - OLE_ | gc.TFLOAT64: - a = x86.AJCC - - case OCMP_ | gc.TBOOL, - OCMP_ | gc.TINT8, - OCMP_ | gc.TUINT8: - a = x86.ACMPB - - case OCMP_ | gc.TINT16, - OCMP_ | gc.TUINT16: - a = x86.ACMPW - - case OCMP_ | gc.TINT32, - OCMP_ | gc.TUINT32, - OCMP_ | gc.TPTR32: - a = x86.ACMPL - - case OAS_ | gc.TBOOL, - OAS_ | gc.TINT8, - OAS_ | gc.TUINT8: - a = x86.AMOVB - - case OAS_ | gc.TINT16, - OAS_ | gc.TUINT16: - a = x86.AMOVW - - case OAS_ | gc.TINT32, - OAS_ | gc.TUINT32, - OAS_ | gc.TPTR32: - a = x86.AMOVL - - case OAS_ | gc.TFLOAT32: - a = x86.AMOVSS - - case OAS_ | gc.TFLOAT64: - a = x86.AMOVSD - - case OADD_ | gc.TINT8, - OADD_ | gc.TUINT8: - a = x86.AADDB - - case OADD_ | gc.TINT16, - OADD_ | gc.TUINT16: - a = x86.AADDW - - case OADD_ | gc.TINT32, - OADD_ | gc.TUINT32, - OADD_ | gc.TPTR32: - a = x86.AADDL - - case OSUB_ | gc.TINT8, - OSUB_ | gc.TUINT8: - a = x86.ASUBB - - case OSUB_ | gc.TINT16, - OSUB_ | gc.TUINT16: - a = x86.ASUBW - - case OSUB_ | gc.TINT32, - OSUB_ | gc.TUINT32, - OSUB_ | gc.TPTR32: - a = x86.ASUBL - - case OINC_ | gc.TINT8, - OINC_ | gc.TUINT8: - a = x86.AINCB - - case OINC_ | gc.TINT16, - OINC_ | gc.TUINT16: - a = x86.AINCW - - case OINC_ | gc.TINT32, - OINC_ | gc.TUINT32, - OINC_ | gc.TPTR32: - a = x86.AINCL - - case ODEC_ | gc.TINT8, - ODEC_ | gc.TUINT8: - a = x86.ADECB - - case ODEC_ | gc.TINT16, - ODEC_ | gc.TUINT16: - a = x86.ADECW - - case ODEC_ | gc.TINT32, - ODEC_ | gc.TUINT32, - ODEC_ | gc.TPTR32: - a = x86.ADECL - - case OCOM_ | gc.TINT8, - OCOM_ | gc.TUINT8: - a = x86.ANOTB - - case OCOM_ | gc.TINT16, - OCOM_ | gc.TUINT16: - a = x86.ANOTW - - case OCOM_ | gc.TINT32, - OCOM_ | gc.TUINT32, - OCOM_ | gc.TPTR32: - a = x86.ANOTL - - case OMINUS_ | gc.TINT8, - OMINUS_ | gc.TUINT8: - a = x86.ANEGB - - case OMINUS_ | gc.TINT16, - OMINUS_ | gc.TUINT16: - a = x86.ANEGW - - case OMINUS_ | gc.TINT32, - OMINUS_ | gc.TUINT32, - OMINUS_ | gc.TPTR32: - a = x86.ANEGL - - case OAND_ | gc.TINT8, - OAND_ | gc.TUINT8: - a = x86.AANDB - - case OAND_ | gc.TINT16, - OAND_ | gc.TUINT16: - a = x86.AANDW - - case OAND_ | gc.TINT32, - OAND_ | gc.TUINT32, - OAND_ | gc.TPTR32: - a = x86.AANDL - - case OOR_ | gc.TINT8, - OOR_ | gc.TUINT8: - a = x86.AORB - - case OOR_ | gc.TINT16, - OOR_ | gc.TUINT16: - a = x86.AORW - - case OOR_ | gc.TINT32, - OOR_ | gc.TUINT32, - OOR_ | gc.TPTR32: - a = x86.AORL - - case OXOR_ | gc.TINT8, - OXOR_ | gc.TUINT8: - a = x86.AXORB - - case OXOR_ | gc.TINT16, - OXOR_ | gc.TUINT16: - a = x86.AXORW - - case OXOR_ | gc.TINT32, - OXOR_ | gc.TUINT32, - OXOR_ | gc.TPTR32: - a = x86.AXORL - - case OLROT_ | gc.TINT8, - OLROT_ | gc.TUINT8: - a = x86.AROLB - - case OLROT_ | gc.TINT16, - OLROT_ | gc.TUINT16: - a = x86.AROLW - - case OLROT_ | gc.TINT32, - OLROT_ | gc.TUINT32, - OLROT_ | gc.TPTR32: - a = x86.AROLL - - case OLSH_ | gc.TINT8, - OLSH_ | gc.TUINT8: - a = x86.ASHLB - - case OLSH_ | gc.TINT16, - OLSH_ | gc.TUINT16: - a = x86.ASHLW - - case OLSH_ | gc.TINT32, - OLSH_ | gc.TUINT32, - OLSH_ | gc.TPTR32: - a = x86.ASHLL - - case ORSH_ | gc.TUINT8: - a = x86.ASHRB - - case ORSH_ | gc.TUINT16: - a = x86.ASHRW - - case ORSH_ | gc.TUINT32, - ORSH_ | gc.TPTR32: - a = x86.ASHRL - - case ORSH_ | gc.TINT8: - a = x86.ASARB - - case ORSH_ | gc.TINT16: - a = x86.ASARW - - case ORSH_ | gc.TINT32: - a = x86.ASARL - - case OHMUL_ | gc.TINT8, - OMUL_ | gc.TINT8, - OMUL_ | gc.TUINT8: - a = x86.AIMULB - - case OHMUL_ | gc.TINT16, - OMUL_ | gc.TINT16, - OMUL_ | gc.TUINT16: - a = x86.AIMULW - - case OHMUL_ | gc.TINT32, - OMUL_ | gc.TINT32, - OMUL_ | gc.TUINT32, - OMUL_ | gc.TPTR32: - a = x86.AIMULL - - case OHMUL_ | gc.TUINT8: - a = x86.AMULB - - case OHMUL_ | gc.TUINT16: - a = x86.AMULW - - case OHMUL_ | gc.TUINT32, - OHMUL_ | gc.TPTR32: - a = x86.AMULL - - case ODIV_ | gc.TINT8, - OMOD_ | gc.TINT8: - a = x86.AIDIVB - - case ODIV_ | gc.TUINT8, - OMOD_ | gc.TUINT8: - a = x86.ADIVB - - case ODIV_ | gc.TINT16, - OMOD_ | gc.TINT16: - a = x86.AIDIVW - - case ODIV_ | gc.TUINT16, - OMOD_ | gc.TUINT16: - a = x86.ADIVW - - case ODIV_ | gc.TINT32, - OMOD_ | gc.TINT32: - a = x86.AIDIVL - - case ODIV_ | gc.TUINT32, - ODIV_ | gc.TPTR32, - OMOD_ | gc.TUINT32, - OMOD_ | gc.TPTR32: - a = x86.ADIVL - - case OEXTEND_ | gc.TINT16: - a = x86.ACWD - - case OEXTEND_ | gc.TINT32: - a = x86.ACDQ - } - - return a -} - -func foptoas(op gc.Op, t *gc.Type, flg int) obj.As { - a := obj.AXXX - et := gc.Simtype[t.Etype] - - // avoid constant conversions in switches below - const ( - OCMP_ = uint32(gc.OCMP) << 16 - OAS_ = uint32(gc.OAS) << 16 - OADD_ = uint32(gc.OADD) << 16 - OSUB_ = uint32(gc.OSUB) << 16 - OMUL_ = uint32(gc.OMUL) << 16 - ODIV_ = uint32(gc.ODIV) << 16 - OMINUS_ = uint32(gc.OMINUS) << 16 - ) - - if !gc.Thearch.Use387 { - switch uint32(op)<<16 | uint32(et) { - default: - gc.Fatalf("foptoas-sse: no entry %v-%v", op, t) - - case OCMP_ | gc.TFLOAT32: - a = x86.AUCOMISS - - case OCMP_ | gc.TFLOAT64: - a = x86.AUCOMISD - - case OAS_ | gc.TFLOAT32: - a = x86.AMOVSS - - case OAS_ | gc.TFLOAT64: - a = x86.AMOVSD - - case OADD_ | gc.TFLOAT32: - a = x86.AADDSS - - case OADD_ | gc.TFLOAT64: - a = x86.AADDSD - - case OSUB_ | gc.TFLOAT32: - a = x86.ASUBSS - - case OSUB_ | gc.TFLOAT64: - a = x86.ASUBSD - - case OMUL_ | gc.TFLOAT32: - a = x86.AMULSS - - case OMUL_ | gc.TFLOAT64: - a = x86.AMULSD - - case ODIV_ | gc.TFLOAT32: - a = x86.ADIVSS - - case ODIV_ | gc.TFLOAT64: - a = x86.ADIVSD - } - - return a - } - - // If we need Fpop, it means we're working on - // two different floating-point registers, not memory. - // There the instruction only has a float64 form. - if flg&Fpop != 0 { - et = gc.TFLOAT64 - } - - // clear Frev if unneeded - switch op { - case gc.OADD, - gc.OMUL: - flg &^= Frev - } - - switch uint32(op)<<16 | (uint32(et)<<8 | uint32(flg)) { - case OADD_ | (gc.TFLOAT32<<8 | 0): - return x86.AFADDF - - case OADD_ | (gc.TFLOAT64<<8 | 0): - return x86.AFADDD - - case OADD_ | (gc.TFLOAT64<<8 | Fpop): - return x86.AFADDDP - - case OSUB_ | (gc.TFLOAT32<<8 | 0): - return x86.AFSUBF - - case OSUB_ | (gc.TFLOAT32<<8 | Frev): - return x86.AFSUBRF - - case OSUB_ | (gc.TFLOAT64<<8 | 0): - return x86.AFSUBD - - case OSUB_ | (gc.TFLOAT64<<8 | Frev): - return x86.AFSUBRD - - case OSUB_ | (gc.TFLOAT64<<8 | Fpop): - return x86.AFSUBDP - - case OSUB_ | (gc.TFLOAT64<<8 | (Fpop | Frev)): - return x86.AFSUBRDP - - case OMUL_ | (gc.TFLOAT32<<8 | 0): - return x86.AFMULF - - case OMUL_ | (gc.TFLOAT64<<8 | 0): - return x86.AFMULD - - case OMUL_ | (gc.TFLOAT64<<8 | Fpop): - return x86.AFMULDP - - case ODIV_ | (gc.TFLOAT32<<8 | 0): - return x86.AFDIVF - - case ODIV_ | (gc.TFLOAT32<<8 | Frev): - return x86.AFDIVRF - - case ODIV_ | (gc.TFLOAT64<<8 | 0): - return x86.AFDIVD - - case ODIV_ | (gc.TFLOAT64<<8 | Frev): - return x86.AFDIVRD - - case ODIV_ | (gc.TFLOAT64<<8 | Fpop): - return x86.AFDIVDP - - case ODIV_ | (gc.TFLOAT64<<8 | (Fpop | Frev)): - return x86.AFDIVRDP - - case OCMP_ | (gc.TFLOAT32<<8 | 0): - return x86.AFCOMF - - case OCMP_ | (gc.TFLOAT32<<8 | Fpop): - return x86.AFCOMFP - - case OCMP_ | (gc.TFLOAT64<<8 | 0): - return x86.AFCOMD - - case OCMP_ | (gc.TFLOAT64<<8 | Fpop): - return x86.AFCOMDP - - case OCMP_ | (gc.TFLOAT64<<8 | Fpop2): - return x86.AFCOMDPP - - case OMINUS_ | (gc.TFLOAT32<<8 | 0): - return x86.AFCHS - - case OMINUS_ | (gc.TFLOAT64<<8 | 0): - return x86.AFCHS - } - - gc.Fatalf("foptoas %v %v %#x", op, t, flg) - return 0 -} - var resvd = []int{ // REG_DI, // for movstring // REG_SI, // for movstring @@ -601,1129 +47,6 @@ var resvd = []int{ x86.REG_SP, // for stack } -/* - * generate - * as $c, reg - */ -func gconreg(as obj.As, c int64, reg int) { - var n1 gc.Node - var n2 gc.Node - - gc.Nodconst(&n1, gc.Types[gc.TINT64], c) - gc.Nodreg(&n2, gc.Types[gc.TINT64], reg) - gins(as, &n1, &n2) -} - -/* - * generate - * as $c, n - */ -func ginscon(as obj.As, c int64, n2 *gc.Node) { - var n1 gc.Node - gc.Nodconst(&n1, gc.Types[gc.TINT32], c) - gins(as, &n1, n2) -} - -func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { - if t.IsInteger() || t.Etype == gc.Tptr { - if (n1.Op == gc.OLITERAL || n1.Op == gc.OADDR && n1.Left.Op == gc.ONAME) && n2.Op != gc.OLITERAL { - // Reverse comparison to place constant (including address constant) last. - op = gc.Brrev(op) - n1, n2 = n2, n1 - } - } - - // General case. - var r1, r2, g1, g2 gc.Node - - // A special case to make write barriers more efficient. - // Comparing the first field of a named struct can be done directly. - base := n1 - if n1.Op == gc.ODOT && n1.Left.Type.IsStruct() && n1.Left.Type.Field(0).Sym == n1.Sym { - base = n1.Left - } - - if base.Op == gc.ONAME && base.Class != gc.PAUTOHEAP || n1.Op == gc.OINDREG { - r1 = *n1 - } else { - gc.Regalloc(&r1, t, n1) - gc.Regalloc(&g1, n1.Type, &r1) - gc.Cgen(n1, &g1) - gmove(&g1, &r1) - } - if n2.Op == gc.OLITERAL && t.IsInteger() || n2.Op == gc.OADDR && n2.Left.Op == gc.ONAME && n2.Left.Class == gc.PEXTERN { - r2 = *n2 - } else { - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - } - gins(optoas(gc.OCMP, t), &r1, &r2) - if r1.Op == gc.OREGISTER { - gc.Regfree(&g1) - gc.Regfree(&r1) - } - if r2.Op == gc.OREGISTER { - gc.Regfree(&g2) - gc.Regfree(&r2) - } - return gc.Gbranch(optoas(op, t), nil, likely) -} - -/* - * swap node contents - */ -func nswap(a *gc.Node, b *gc.Node) { - t := *a - *a = *b - *b = t -} - -/* - * return constant i node. - * overwritten by next call, but useful in calls to gins. - */ - -var ncon_n gc.Node - -func ncon(i uint32) *gc.Node { - if ncon_n.Type == nil { - gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0) - } - ncon_n.SetInt(int64(i)) - return &ncon_n -} - -var sclean [10]gc.Node - -var nsclean int - -/* - * n is a 64-bit value. fill in lo and hi to refer to its 32-bit halves. - */ -func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) { - if !gc.Is64(n.Type) { - gc.Fatalf("split64 %v", n.Type) - } - - if nsclean >= len(sclean) { - gc.Fatalf("split64 clean") - } - sclean[nsclean].Op = gc.OEMPTY - nsclean++ - switch n.Op { - default: - switch n.Op { - default: - var n1 gc.Node - if !dotaddable(n, &n1) { - gc.Igen(n, &n1, nil) - sclean[nsclean-1] = n1 - } - - n = &n1 - - case gc.ONAME, gc.OINDREG: - // nothing - } - - *lo = *n - *hi = *n - lo.Type = gc.Types[gc.TUINT32] - if n.Type.Etype == gc.TINT64 { - hi.Type = gc.Types[gc.TINT32] - } else { - hi.Type = gc.Types[gc.TUINT32] - } - hi.Xoffset += 4 - - case gc.OLITERAL: - var n1 gc.Node - n.Convconst(&n1, n.Type) - i := n1.Int64() - gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i))) - i >>= 32 - if n.Type.Etype == gc.TINT64 { - gc.Nodconst(hi, gc.Types[gc.TINT32], int64(int32(i))) - } else { - gc.Nodconst(hi, gc.Types[gc.TUINT32], int64(uint32(i))) - } - } -} - -func splitclean() { - if nsclean <= 0 { - gc.Fatalf("splitclean") - } - nsclean-- - if sclean[nsclean].Op != gc.OEMPTY { - gc.Regfree(&sclean[nsclean]) - } -} - -// set up nodes representing fp constants -var ( - zerof gc.Node - two63f gc.Node - two64f gc.Node - bignodes_did bool -) - -func bignodes() { - if bignodes_did { - return - } - bignodes_did = true - - gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0) - zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64]) - - var i big.Int - i.SetInt64(1) - i.Lsh(&i, 63) - var bigi gc.Node - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - bigi.SetBigInt(&i) - bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64]) - - gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0) - i.Lsh(&i, 1) - bigi.SetBigInt(&i) - bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64]) -} - -func memname(n *gc.Node, t *gc.Type) { - gc.Tempname(n, t) - n.Sym = gc.Lookup("." + n.Sym.Name[1:]) // keep optimizer from registerizing - n.Orig.Sym = n.Sym -} - -func gmove(f *gc.Node, t *gc.Node) { - if gc.Debug['M'] != 0 { - fmt.Printf("gmove %v -> %v\n", f, t) - } - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - if gc.Iscomplex[ft] || gc.Iscomplex[tt] { - gc.Complexmove(f, t) - return - } - - if gc.Isfloat[ft] || gc.Isfloat[tt] { - floatmove(f, t) - return - } - - // cannot have two integer memory operands; - // except 64-bit, which always copies via registers anyway. - var r1 gc.Node - var a obj.As - if gc.Isint[ft] && gc.Isint[tt] && !gc.Is64(f.Type) && !gc.Is64(t.Type) && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = gc.Simsimtype(con.Type) - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - // should not happen - gc.Fatalf("gmove %v -> %v", f, t) - return - - /* - * integer copy and truncate - */ - case gc.TINT8<<16 | gc.TINT8, // same size - gc.TINT8<<16 | gc.TUINT8, - gc.TUINT8<<16 | gc.TINT8, - gc.TUINT8<<16 | gc.TUINT8: - a = x86.AMOVB - - case gc.TINT16<<16 | gc.TINT8, // truncate - gc.TUINT16<<16 | gc.TINT8, - gc.TINT32<<16 | gc.TINT8, - gc.TUINT32<<16 | gc.TINT8, - gc.TINT16<<16 | gc.TUINT8, - gc.TUINT16<<16 | gc.TUINT8, - gc.TINT32<<16 | gc.TUINT8, - gc.TUINT32<<16 | gc.TUINT8: - a = x86.AMOVB - - goto rsrc - - case gc.TINT64<<16 | gc.TINT8, // truncate low word - gc.TUINT64<<16 | gc.TINT8, - gc.TINT64<<16 | gc.TUINT8, - gc.TUINT64<<16 | gc.TUINT8: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVB, &r1, t) - splitclean() - return - - case gc.TINT16<<16 | gc.TINT16, // same size - gc.TINT16<<16 | gc.TUINT16, - gc.TUINT16<<16 | gc.TINT16, - gc.TUINT16<<16 | gc.TUINT16: - a = x86.AMOVW - - case gc.TINT32<<16 | gc.TINT16, // truncate - gc.TUINT32<<16 | gc.TINT16, - gc.TINT32<<16 | gc.TUINT16, - gc.TUINT32<<16 | gc.TUINT16: - a = x86.AMOVW - - goto rsrc - - case gc.TINT64<<16 | gc.TINT16, // truncate low word - gc.TUINT64<<16 | gc.TINT16, - gc.TINT64<<16 | gc.TUINT16, - gc.TUINT64<<16 | gc.TUINT16: - var flo gc.Node - var fhi gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVW, &r1, t) - splitclean() - return - - case gc.TINT32<<16 | gc.TINT32, // same size - gc.TINT32<<16 | gc.TUINT32, - gc.TUINT32<<16 | gc.TINT32, - gc.TUINT32<<16 | gc.TUINT32: - a = x86.AMOVL - - case gc.TINT64<<16 | gc.TINT32, // truncate - gc.TUINT64<<16 | gc.TINT32, - gc.TINT64<<16 | gc.TUINT32, - gc.TUINT64<<16 | gc.TUINT32: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var r1 gc.Node - gc.Nodreg(&r1, t.Type, x86.REG_AX) - gmove(&flo, &r1) - gins(x86.AMOVL, &r1, t) - splitclean() - return - - case gc.TINT64<<16 | gc.TINT64, // same size - gc.TINT64<<16 | gc.TUINT64, - gc.TUINT64<<16 | gc.TINT64, - gc.TUINT64<<16 | gc.TUINT64: - var fhi gc.Node - var flo gc.Node - split64(f, &flo, &fhi) - - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - if f.Op == gc.OLITERAL { - gins(x86.AMOVL, &flo, &tlo) - gins(x86.AMOVL, &fhi, &thi) - } else { - // Implementation of conversion-free x = y for int64 or uint64 x. - // This is generated by the code that copies small values out of closures, - // and that code has DX live, so avoid DX and just use AX twice. - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[gc.TUINT32], x86.REG_AX) - gins(x86.AMOVL, &flo, &r1) - gins(x86.AMOVL, &r1, &tlo) - gins(x86.AMOVL, &fhi, &r1) - gins(x86.AMOVL, &r1, &thi) - } - - splitclean() - splitclean() - return - - /* - * integer up-conversions - */ - case gc.TINT8<<16 | gc.TINT16, // sign extend int8 - gc.TINT8<<16 | gc.TUINT16: - a = x86.AMOVBWSX - - goto rdst - - case gc.TINT8<<16 | gc.TINT32, - gc.TINT8<<16 | gc.TUINT32: - a = x86.AMOVBLSX - goto rdst - - case gc.TINT8<<16 | gc.TINT64, // convert via int32 - gc.TINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT8<<16 | gc.TINT16, // zero extend uint8 - gc.TUINT8<<16 | gc.TUINT16: - a = x86.AMOVBWZX - - goto rdst - - case gc.TUINT8<<16 | gc.TINT32, - gc.TUINT8<<16 | gc.TUINT32: - a = x86.AMOVBLZX - goto rdst - - case gc.TUINT8<<16 | gc.TINT64, // convert via uint32 - gc.TUINT8<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT16<<16 | gc.TINT32, // sign extend int16 - gc.TINT16<<16 | gc.TUINT32: - a = x86.AMOVWLSX - - goto rdst - - case gc.TINT16<<16 | gc.TINT64, // convert via int32 - gc.TINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - case gc.TUINT16<<16 | gc.TINT32, // zero extend uint16 - gc.TUINT16<<16 | gc.TUINT32: - a = x86.AMOVWLZX - - goto rdst - - case gc.TUINT16<<16 | gc.TINT64, // convert via uint32 - gc.TUINT16<<16 | gc.TUINT64: - cvt = gc.Types[gc.TUINT32] - - goto hard - - case gc.TINT32<<16 | gc.TINT64, // sign extend int32 - gc.TINT32<<16 | gc.TUINT64: - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - - var flo gc.Node - gc.Nodreg(&flo, tlo.Type, x86.REG_AX) - var fhi gc.Node - gc.Nodreg(&fhi, thi.Type, x86.REG_DX) - gmove(f, &flo) - gins(x86.ACDQ, nil, nil) - gins(x86.AMOVL, &flo, &tlo) - gins(x86.AMOVL, &fhi, &thi) - splitclean() - return - - case gc.TUINT32<<16 | gc.TINT64, // zero extend uint32 - gc.TUINT32<<16 | gc.TUINT64: - var tlo gc.Node - var thi gc.Node - split64(t, &tlo, &thi) - - gmove(f, &tlo) - gins(x86.AMOVL, ncon(0), &thi) - splitclean() - return - } - - gins(a, f, t) - return - - // requires register source -rsrc: - gc.Regalloc(&r1, f.Type, t) - - gmove(f, &r1) - gins(a, &r1, t) - gc.Regfree(&r1) - return - - // requires register destination -rdst: - { - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - -func floatmove(f *gc.Node, t *gc.Node) { - var r1 gc.Node - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - // cannot have two floating point memory operands. - if gc.Isfloat[ft] && gc.Isfloat[tt] && gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - - // convert constant to desired type - if f.Op == gc.OLITERAL { - var con gc.Node - f.Convconst(&con, t.Type) - f = &con - ft = gc.Simsimtype(con.Type) - - // some constants can't move directly to memory. - if gc.Ismem(t) { - // float constants come from memory. - if gc.Isfloat[tt] { - goto hard - } - } - } - - // value -> value copy, only one memory operand. - // figure out the instruction to use. - // break out of switch for one-instruction gins. - // goto rdst for "destination must be register". - // goto hard for "convert to cvt type first". - // otherwise handle and return. - - switch uint32(ft)<<16 | uint32(tt) { - default: - if gc.Thearch.Use387 { - floatmove_387(f, t) - } else { - floatmove_sse(f, t) - } - return - - // float to very long integer. - case gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT64: - if f.Op == gc.OREGISTER { - cvt = f.Type - goto hardmem - } - - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &r1) - } else { - gins(x86.AFMOVD, f, &r1) - } - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - if tt == gc.TINT16 { - gins(x86.AFMOVWP, &r1, t) - } else if tt == gc.TINT32 { - gins(x86.AFMOVLP, &r1, t) - } else { - gins(x86.AFMOVVP, &r1, t) - } - gins(x86.AFLDCW, &t1, nil) - return - - case gc.TFLOAT32<<16 | gc.TUINT64, - gc.TFLOAT64<<16 | gc.TUINT64: - if !gc.Ismem(f) { - cvt = f.Type - goto hardmem - } - - bignodes() - var f0 gc.Node - gc.Nodreg(&f0, gc.Types[ft], x86.REG_F0) - var f1 gc.Node - gc.Nodreg(&f1, gc.Types[ft], x86.REG_F0+1) - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TUINT16], x86.REG_AX) - - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &f0) - } else { - gins(x86.AFMOVD, f, &f0) - } - - // if 0 > v { answer = 0 } - gins(x86.AFMOVD, &zerof, &f0) - gins(x86.AFUCOMP, &f0, &f1) - gins(x86.AFSTSW, nil, &ax) - gins(x86.ASAHF, nil, nil) - p1 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) - - // if 1<<64 <= v { answer = 0 too } - gins(x86.AFMOVD, &two64f, &f0) - - gins(x86.AFUCOMP, &f0, &f1) - gins(x86.AFSTSW, nil, &ax) - gins(x86.ASAHF, nil, nil) - p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[tt]), nil, 0) - gc.Patch(p1, gc.Pc) - gins(x86.AFMOVVP, &f0, t) // don't care about t, but will pop the stack - var thi gc.Node - var tlo gc.Node - split64(t, &tlo, &thi) - gins(x86.AMOVL, ncon(0), &tlo) - gins(x86.AMOVL, ncon(0), &thi) - splitclean() - p1 = gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p2, gc.Pc) - - // in range; algorithm is: - // if small enough, use native float64 -> int64 conversion. - // otherwise, subtract 2^63, convert, and add it back. - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - - // actual work - gins(x86.AFMOVD, &two63f, &f0) - - gins(x86.AFUCOMP, &f0, &f1) - gins(x86.AFSTSW, nil, &ax) - gins(x86.ASAHF, nil, nil) - p2 = gc.Gbranch(optoas(gc.OLE, gc.Types[tt]), nil, 0) - gins(x86.AFMOVVP, &f0, t) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p2, gc.Pc) - gins(x86.AFMOVD, &two63f, &f0) - gins(x86.AFSUBDP, &f0, &f1) - gins(x86.AFMOVVP, &f0, t) - split64(t, &tlo, &thi) - gins(x86.AXORL, ncon(0x80000000), &thi) // + 2^63 - gc.Patch(p3, gc.Pc) - splitclean() - - // restore rounding mode - gins(x86.AFLDCW, &t1, nil) - - gc.Patch(p1, gc.Pc) - return - - /* - * integer to float - */ - case gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64: - if t.Op == gc.OREGISTER { - goto hardmem - } - var f0 gc.Node - gc.Nodreg(&f0, t.Type, x86.REG_F0) - gins(x86.AFMOVV, f, &f0) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &f0, t) - } else { - gins(x86.AFMOVDP, &f0, t) - } - return - - // algorithm is: - // if small enough, use native int64 -> float64 conversion. - // otherwise, halve (rounding to odd?), convert, and double. - case gc.TUINT64<<16 | gc.TFLOAT32, - gc.TUINT64<<16 | gc.TFLOAT64: - var ax gc.Node - gc.Nodreg(&ax, gc.Types[gc.TUINT32], x86.REG_AX) - - var dx gc.Node - gc.Nodreg(&dx, gc.Types[gc.TUINT32], x86.REG_DX) - var cx gc.Node - gc.Nodreg(&cx, gc.Types[gc.TUINT32], x86.REG_CX) - var t1 gc.Node - gc.Tempname(&t1, f.Type) - var tlo gc.Node - var thi gc.Node - split64(&t1, &tlo, &thi) - gmove(f, &t1) - gins(x86.ACMPL, &thi, ncon(0)) - p1 := gc.Gbranch(x86.AJLT, nil, 0) - - // native - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) - - gins(x86.AFMOVV, &t1, &r1) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &r1, t) - } else { - gins(x86.AFMOVDP, &r1, t) - } - p2 := gc.Gbranch(obj.AJMP, nil, 0) - - // simulated - gc.Patch(p1, gc.Pc) - - gmove(&tlo, &ax) - gmove(&thi, &dx) - p1 = gins(x86.ASHRL, ncon(1), &ax) - p1.From.Index = x86.REG_DX // double-width shift DX -> AX - p1.From.Scale = 0 - gins(x86.AMOVL, ncon(0), &cx) - gins(x86.ASETCC, nil, &cx) - gins(x86.AORL, &cx, &ax) - gins(x86.ASHRL, ncon(1), &dx) - gmove(&dx, &thi) - gmove(&ax, &tlo) - gc.Nodreg(&r1, gc.Types[tt], x86.REG_F0) - var r2 gc.Node - gc.Nodreg(&r2, gc.Types[tt], x86.REG_F0+1) - gins(x86.AFMOVV, &t1, &r1) - gins(x86.AFMOVD, &r1, &r1) - gins(x86.AFADDDP, &r1, &r2) - if tt == gc.TFLOAT32 { - gins(x86.AFMOVFP, &r1, t) - } else { - gins(x86.AFMOVDP, &r1, t) - } - gc.Patch(p2, gc.Pc) - splitclean() - return - } - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return -} - -func floatmove_387(f *gc.Node, t *gc.Node) { - var r1 gc.Node - var a obj.As - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - cvt := t.Type - - switch uint32(ft)<<16 | uint32(tt) { - default: - goto fatal - - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT32, - gc.TFLOAT32<<16 | gc.TINT64, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT32, - gc.TFLOAT64<<16 | gc.TINT64: - if t.Op == gc.OREGISTER { - goto hardmem - } - var r1 gc.Node - gc.Nodreg(&r1, gc.Types[ft], x86.REG_F0) - if f.Op != gc.OREGISTER { - if ft == gc.TFLOAT32 { - gins(x86.AFMOVF, f, &r1) - } else { - gins(x86.AFMOVD, f, &r1) - } - } - - // set round to zero mode during conversion - var t1 gc.Node - memname(&t1, gc.Types[gc.TUINT16]) - - var t2 gc.Node - memname(&t2, gc.Types[gc.TUINT16]) - gins(x86.AFSTCW, nil, &t1) - gins(x86.AMOVW, ncon(0xf7f), &t2) - gins(x86.AFLDCW, &t2, nil) - if tt == gc.TINT16 { - gins(x86.AFMOVWP, &r1, t) - } else if tt == gc.TINT32 { - gins(x86.AFMOVLP, &r1, t) - } else { - gins(x86.AFMOVVP, &r1, t) - } - gins(x86.AFLDCW, &t1, nil) - return - - // convert via int32. - case gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - var t1 gc.Node - gc.Tempname(&t1, gc.Types[gc.TINT32]) - - gmove(f, &t1) - switch tt { - default: - gc.Fatalf("gmove %v", t) - - case gc.TINT8: - gins(x86.ACMPL, &t1, ncon(-0x80&(1<<32-1))) - p1 := gc.Gbranch(optoas(gc.OLT, gc.Types[gc.TINT32]), nil, -1) - gins(x86.ACMPL, &t1, ncon(0x7f)) - p2 := gc.Gbranch(optoas(gc.OGT, gc.Types[gc.TINT32]), nil, -1) - p3 := gc.Gbranch(obj.AJMP, nil, 0) - gc.Patch(p1, gc.Pc) - gc.Patch(p2, gc.Pc) - gmove(ncon(-0x80&(1<<32-1)), &t1) - gc.Patch(p3, gc.Pc) - gmove(&t1, t) - - case gc.TUINT8: - gins(x86.ATESTL, ncon(0xffffff00), &t1) - p1 := gc.Gbranch(x86.AJEQ, nil, +1) - gins(x86.AMOVL, ncon(0), &t1) - gc.Patch(p1, gc.Pc) - gmove(&t1, t) - - case gc.TUINT16: - gins(x86.ATESTL, ncon(0xffff0000), &t1) - p1 := gc.Gbranch(x86.AJEQ, nil, +1) - gins(x86.AMOVL, ncon(0), &t1) - gc.Patch(p1, gc.Pc) - gmove(&t1, t) - } - - return - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - /* - * integer to float - */ - case gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TINT32<<16 | gc.TFLOAT32, - gc.TINT32<<16 | gc.TFLOAT64, - gc.TINT64<<16 | gc.TFLOAT32, - gc.TINT64<<16 | gc.TFLOAT64: - if t.Op != gc.OREGISTER { - goto hard - } - if f.Op == gc.OREGISTER { - cvt = f.Type - goto hardmem - } - - switch ft { - case gc.TINT16: - a = x86.AFMOVW - - case gc.TINT32: - a = x86.AFMOVL - - default: - a = x86.AFMOVV - } - - // convert via int32 memory - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hardmem - - // convert via int64 memory - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - // The way the code generator uses floating-point - // registers, a move from F0 to F0 is intended as a no-op. - // On the x86, it's not: it pushes a second copy of F0 - // on the floating point stack. So toss it away here. - // Also, F0 is the *only* register we ever evaluate - // into, so we should only see register/register as F0/F0. - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32, - gc.TFLOAT64<<16 | gc.TFLOAT64: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { - goto fatal - } - return - } - - a = x86.AFMOVF - if ft == gc.TFLOAT64 { - a = x86.AFMOVD - } - if gc.Ismem(t) { - if f.Op != gc.OREGISTER || f.Reg != x86.REG_F0 { - gc.Fatalf("gmove %v", f) - } - a = x86.AFMOVFP - if ft == gc.TFLOAT64 { - a = x86.AFMOVDP - } - } - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - if f.Reg != x86.REG_F0 || t.Reg != x86.REG_F0 { - goto fatal - } - return - } - - if f.Op == gc.OREGISTER { - gins(x86.AFMOVDP, f, t) - } else { - gins(x86.AFMOVF, f, t) - } - return - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - if gc.Ismem(f) && gc.Ismem(t) { - goto hard - } - if f.Op == gc.OREGISTER && t.Op == gc.OREGISTER { - var r1 gc.Node - gc.Tempname(&r1, gc.Types[gc.TFLOAT32]) - gins(x86.AFMOVFP, f, &r1) - gins(x86.AFMOVF, &r1, t) - return - } - - if f.Op == gc.OREGISTER { - gins(x86.AFMOVFP, f, t) - } else { - gins(x86.AFMOVD, f, t) - } - return - } - - gins(a, f, t) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return - - // should not happen -fatal: - gc.Fatalf("gmove %L -> %L", f, t) - - return -} - -func floatmove_sse(f *gc.Node, t *gc.Node) { - var r1 gc.Node - var cvt *gc.Type - var a obj.As - - ft := gc.Simsimtype(f.Type) - tt := gc.Simsimtype(t.Type) - - switch uint32(ft)<<16 | uint32(tt) { - // should not happen - default: - gc.Fatalf("gmove %v -> %v", f, t) - - return - - // convert via int32. - /* - * float to integer - */ - case gc.TFLOAT32<<16 | gc.TINT16, - gc.TFLOAT32<<16 | gc.TINT8, - gc.TFLOAT32<<16 | gc.TUINT16, - gc.TFLOAT32<<16 | gc.TUINT8, - gc.TFLOAT64<<16 | gc.TINT16, - gc.TFLOAT64<<16 | gc.TINT8, - gc.TFLOAT64<<16 | gc.TUINT16, - gc.TFLOAT64<<16 | gc.TUINT8: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64. - case gc.TFLOAT32<<16 | gc.TUINT32, - gc.TFLOAT64<<16 | gc.TUINT32: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - case gc.TFLOAT32<<16 | gc.TINT32: - a = x86.ACVTTSS2SL - goto rdst - - case gc.TFLOAT64<<16 | gc.TINT32: - a = x86.ACVTTSD2SL - goto rdst - - // convert via int32 memory - /* - * integer to float - */ - case gc.TINT8<<16 | gc.TFLOAT32, - gc.TINT8<<16 | gc.TFLOAT64, - gc.TINT16<<16 | gc.TFLOAT32, - gc.TINT16<<16 | gc.TFLOAT64, - gc.TUINT16<<16 | gc.TFLOAT32, - gc.TUINT16<<16 | gc.TFLOAT64, - gc.TUINT8<<16 | gc.TFLOAT32, - gc.TUINT8<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT32] - - goto hard - - // convert via int64 memory - case gc.TUINT32<<16 | gc.TFLOAT32, - gc.TUINT32<<16 | gc.TFLOAT64: - cvt = gc.Types[gc.TINT64] - - goto hardmem - - case gc.TINT32<<16 | gc.TFLOAT32: - a = x86.ACVTSL2SS - goto rdst - - case gc.TINT32<<16 | gc.TFLOAT64: - a = x86.ACVTSL2SD - goto rdst - - /* - * float to float - */ - case gc.TFLOAT32<<16 | gc.TFLOAT32: - a = x86.AMOVSS - - case gc.TFLOAT64<<16 | gc.TFLOAT64: - a = x86.AMOVSD - - case gc.TFLOAT32<<16 | gc.TFLOAT64: - a = x86.ACVTSS2SD - goto rdst - - case gc.TFLOAT64<<16 | gc.TFLOAT32: - a = x86.ACVTSD2SS - goto rdst - } - - gins(a, f, t) - return - - // requires register intermediate -hard: - gc.Regalloc(&r1, cvt, t) - - gmove(f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return - - // requires memory intermediate -hardmem: - gc.Tempname(&r1, cvt) - - gmove(f, &r1) - gmove(&r1, t) - return - - // requires register destination -rdst: - gc.Regalloc(&r1, t.Type, t) - - gins(a, f, &r1) - gmove(&r1, t) - gc.Regfree(&r1) - return -} - func samaddr(f *gc.Node, t *gc.Node) bool { if f.Op != t.Op { return false @@ -1816,29 +139,3 @@ func ginsnop() { gc.Nodreg(®, gc.Types[gc.TINT], x86.REG_AX) gins(x86.AXCHGL, ®, ®) } - -func dotaddable(n *gc.Node, n1 *gc.Node) bool { - if n.Op != gc.ODOT { - return false - } - - var oary [10]int64 - var nn *gc.Node - o := gc.Dotoffset(n, oary[:], &nn) - if nn != nil && nn.Addable && o == 1 && oary[0] >= 0 { - *n1 = *nn - n1.Type = n.Type - n1.Xoffset += oary[0] - return true - } - - return false -} - -func sudoclean() { -} - -func sudoaddable(as obj.As, n *gc.Node, a *obj.Addr) bool { - *a = obj.Addr{} - return false -} diff --git a/src/cmd/compile/internal/x86/peep.go b/src/cmd/compile/internal/x86/peep.go deleted file mode 100644 index 6a07fe24cd..0000000000 --- a/src/cmd/compile/internal/x86/peep.go +++ /dev/null @@ -1,807 +0,0 @@ -// Derived from Inferno utils/6c/peep.c -// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c -// -// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. -// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) -// Portions Copyright © 1997-1999 Vita Nuova Limited -// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) -// Portions Copyright © 2004,2006 Bruce Ellis -// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) -// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others -// Portions Copyright © 2009 The Go Authors. All rights reserved. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -package x86 - -import ( - "cmd/compile/internal/gc" - "cmd/internal/obj" - "cmd/internal/obj/x86" - "fmt" -) - -const ( - REGEXT = 0 - exregoffset = x86.REG_DI -) - -var gactive uint32 - -// do we need the carry bit -func needc(p *obj.Prog) bool { - for p != nil { - if p.Info.Flags&gc.UseCarry != 0 { - return true - } - if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 { - return false - } - p = p.Link - } - - return false -} - -func rnops(r *gc.Flow) *gc.Flow { - if r != nil { - var p *obj.Prog - var r1 *gc.Flow - for { - p = r.Prog - if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { - break - } - r1 = gc.Uniqs(r) - if r1 == nil { - break - } - r = r1 - } - } - - return r -} - -func peep(firstp *obj.Prog) { - g := gc.Flowstart(firstp, nil) - if g == nil { - return - } - gactive = 0 - - // byte, word arithmetic elimination. - elimshortmov(g) - - // constant propagation - // find MOV $con,R followed by - // another MOV $con,R without - // setting R in the interim - var p *obj.Prog - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.ALEAL: - if regtyp(&p.To) { - if p.From.Sym != nil { - if p.From.Index == x86.REG_NONE { - conprop(r) - } - } - } - - case x86.AMOVB, - x86.AMOVW, - x86.AMOVL, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { - conprop(r) - } - } - } - } - - var r1 *gc.Flow - var p1 *obj.Prog - var r *gc.Flow - var t int -loop1: - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - gc.Dumpit("loop1", g.Start, 0) - } - - t = 0 - for r = g.Start; r != nil; r = r.Link { - p = r.Prog - switch p.As { - case x86.AMOVL, - x86.AMOVSS, - x86.AMOVSD: - if regtyp(&p.To) { - if regtyp(&p.From) { - if copyprop(g, r) { - excise(r) - t++ - } else if subprop(r) && copyprop(g, r) { - excise(r) - t++ - } - } - } - - case x86.AMOVBLZX, - x86.AMOVWLZX, - x86.AMOVBLSX, - x86.AMOVWLSX: - if regtyp(&p.To) { - r1 = rnops(gc.Uniqs(r)) - if r1 != nil { - p1 = r1.Prog - if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { - p1.As = x86.AMOVL - t++ - } - } - } - - case x86.AADDL, - x86.AADDW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.AADDL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.AADDL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - case x86.ASUBL, - x86.ASUBW: - if p.From.Type != obj.TYPE_CONST || needc(p.Link) { - break - } - if p.From.Offset == -1 { - if p.As == x86.ASUBL { - p.As = x86.AINCL - } else { - p.As = x86.AINCW - } - p.From = obj.Addr{} - break - } - - if p.From.Offset == 1 { - if p.As == x86.ASUBL { - p.As = x86.ADECL - } else { - p.As = x86.ADECW - } - p.From = obj.Addr{} - break - } - } - } - - if t != 0 { - goto loop1 - } - - // MOVSD removal. - // We never use packed registers, so a MOVSD between registers - // can be replaced by MOVAPD, which moves the pair of float64s - // instead of just the lower one. We only use the lower one, but - // the processor can do better if we do moves using both. - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if p.As == x86.AMOVSD { - if regtyp(&p.From) { - if regtyp(&p.To) { - p.As = x86.AMOVAPD - } - } - } - } - - gc.Flowend(g) -} - -func excise(r *gc.Flow) { - p := r.Prog - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("%v ===delete===\n", p) - } - - obj.Nopout(p) - - gc.Ostats.Ndelmov++ -} - -func regtyp(a *obj.Addr) bool { - if gc.Ctxt.Flag_shared && a.Type == obj.TYPE_REG && a.Reg == x86.REG_CX { - // don't propagate CX, it is used implicitly by PIC global references - return false - } - return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7) -} - -// movb elimination. -// movb is simulated by the linker -// when a register other than ax, bx, cx, dx -// is used, so rewrite to other instructions -// when possible. a movb into a register -// can smash the entire 64-bit register without -// causing any trouble. -func elimshortmov(g *gc.Graph) { - var p *obj.Prog - - for r := g.Start; r != nil; r = r.Link { - p = r.Prog - if regtyp(&p.To) { - switch p.As { - case x86.AINCB, - x86.AINCW: - p.As = x86.AINCL - - case x86.ADECB, - x86.ADECW: - p.As = x86.ADECL - - case x86.ANEGB, - x86.ANEGW: - p.As = x86.ANEGL - - case x86.ANOTB, - x86.ANOTW: - p.As = x86.ANOTL - } - - if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { - // move or arithmetic into partial register. - // from another register or constant can be movl. - // we don't switch to 32-bit arithmetic if it can - // change how the carry bit is set (and the carry bit is needed). - switch p.As { - case x86.AMOVB, - x86.AMOVW: - p.As = x86.AMOVL - - case x86.AADDB, - x86.AADDW: - if !needc(p.Link) { - p.As = x86.AADDL - } - - case x86.ASUBB, - x86.ASUBW: - if !needc(p.Link) { - p.As = x86.ASUBL - } - - case x86.AMULB, - x86.AMULW: - p.As = x86.AMULL - - case x86.AIMULB, - x86.AIMULW: - p.As = x86.AIMULL - - case x86.AANDB, - x86.AANDW: - p.As = x86.AANDL - - case x86.AORB, - x86.AORW: - p.As = x86.AORL - - case x86.AXORB, - x86.AXORW: - p.As = x86.AXORL - - case x86.ASHLB, - x86.ASHLW: - p.As = x86.ASHLL - } - } else { - // explicit zero extension - switch p.As { - case x86.AMOVB: - p.As = x86.AMOVBLZX - - case x86.AMOVW: - p.As = x86.AMOVWLZX - } - } - } - } -} - -/* - * the idea is to substitute - * one register for another - * from one MOV to another - * MOV a, R0 - * ADD b, R0 / no use of R1 - * MOV R0, R1 - * would be converted to - * MOV a, R1 - * ADD b, R1 - * MOV R1, R0 - * hopefully, then the former or latter MOV - * will be eliminated by copy propagation. - */ -func subprop(r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - if !regtyp(v1) { - return false - } - v2 := &p.To - if !regtyp(v2) { - return false - } - for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { - if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { - fmt.Printf("\t? %v\n", r.Prog) - } - if gc.Uniqs(r) == nil { - break - } - p = r.Prog - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - continue - } - if p.Info.Flags&gc.Call != 0 { - return false - } - - if p.Info.Reguse|p.Info.Regset != 0 { - return false - } - - if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) - if p.From.Type == v2.Type && p.From.Reg == v2.Reg { - fmt.Printf(" excise") - } - fmt.Printf("\n") - } - - for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { - p = r.Prog - copysub(&p.From, v1, v2, true) - copysub(&p.To, v1, v2, true) - if gc.Debug['P'] != 0 { - fmt.Printf("%v\n", r.Prog) - } - } - - t := int(v1.Reg) - v1.Reg = v2.Reg - v2.Reg = int16(t) - if gc.Debug['P'] != 0 { - fmt.Printf("%v last\n", r.Prog) - } - return true - } - - if copyau(&p.From, v2) || copyau(&p.To, v2) { - break - } - if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { - break - } - } - - return false -} - -/* - * The idea is to remove redundant copies. - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * use v2 return fail - * ----------------- - * v1->v2 F=0 - * (use v2 s/v2/v1/)* - * set v1 F=1 - * set v2 return success - */ -func copyprop(g *gc.Graph, r0 *gc.Flow) bool { - p := r0.Prog - v1 := &p.From - v2 := &p.To - if copyas(v1, v2) { - return true - } - gactive++ - return copy1(v1, v2, r0.S1, false) -} - -func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { - if uint32(r.Active) == gactive { - if gc.Debug['P'] != 0 { - fmt.Printf("act set; return 1\n") - } - return true - } - - r.Active = int32(gactive) - if gc.Debug['P'] != 0 { - fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) - } - for ; r != nil; r = r.S1 { - p := r.Prog - if gc.Debug['P'] != 0 { - fmt.Printf("%v", p) - } - if !f && gc.Uniqp(r) == nil { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; merge; f=%v", f) - } - } - - switch t := copyu(p, v2, nil); t { - case 2: /* rar, can't split */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) - } - return false - - case 3: /* set */ - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - - case 1, /* used, substitute */ - 4: /* use and set */ - if f { - if gc.Debug['P'] == 0 { - return false - } - if t == 4 { - fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } else { - fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) - } - return false - } - - if copyu(p, v2, v1) != 0 { - if gc.Debug['P'] != 0 { - fmt.Printf("; sub fail; return 0\n") - } - return false - } - - if gc.Debug['P'] != 0 { - fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) - } - if t == 4 { - if gc.Debug['P'] != 0 { - fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) - } - return true - } - } - - if !f { - t := copyu(p, v1, nil) - if t == 2 || t == 3 || t == 4 { - f = true - if gc.Debug['P'] != 0 { - fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) - } - } - } - - if gc.Debug['P'] != 0 { - fmt.Printf("\n") - } - if r.S2 != nil { - if !copy1(v1, v2, r.S2, f) { - return false - } - } - } - return true -} - -/* - * return - * 1 if v only used (and substitute), - * 2 if read-alter-rewrite - * 3 if set - * 4 if set and used - * 0 otherwise (not touched) - */ -func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { - switch p.As { - case obj.AJMP: - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 1 - } - return 0 - - case obj.ARET: - if s != nil { - return 1 - } - return 3 - - case obj.ACALL: - if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset { - return 2 - } - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 2 - } - if v.Type == p.From.Type && v.Reg == p.From.Reg { - return 2 - } - - if s != nil { - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - - if copyau(&p.To, v) { - return 4 - } - return 3 - - case obj.ATEXT: - if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { - return 3 - } - return 0 - } - - if p.As == obj.AVARDEF || p.As == obj.AVARKILL { - return 0 - } - - if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { - return 2 - } - - if p.Info.Flags&gc.LeftAddr != 0 { - if copyas(&p.From, v) { - return 2 - } - } - - if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { - if copyas(&p.To, v) { - return 2 - } - } - - if p.Info.Flags&gc.RightWrite != 0 { - if copyas(&p.To, v) { - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - return 0 - } - if copyau(&p.From, v) { - return 4 - } - return 3 - } - } - - if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { - if s != nil { - if copysub(&p.From, v, s, true) { - return 1 - } - if copysub(&p.To, v, s, true) { - return 1 - } - return 0 - } - if copyau(&p.From, v) { - return 1 - } - if copyau(&p.To, v) { - return 1 - } - } - return 0 -} - -/* - * direct reference, - * could be set/use depending on - * semantics - */ -func copyas(a *obj.Addr, v *obj.Addr) bool { - if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { - gc.Fatalf("use of byte register") - } - if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { - gc.Fatalf("use of byte register") - } - - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -func sameaddr(a *obj.Addr, v *obj.Addr) bool { - if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { - return false - } - if regtyp(v) { - return true - } - if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { - if v.Offset == a.Offset { - return true - } - } - return false -} - -/* - * either direct or indirect - */ -func copyau(a *obj.Addr, v *obj.Addr) bool { - if copyas(a, v) { - return true - } - if regtyp(v) { - if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { - return true - } - if a.Index == v.Reg { - return true - } - } - - return false -} - -// copysub substitute s for v in a. -// copysub returns true on failure to substitute. -// TODO(dfc) reverse this logic to return false on sunstitution failure. -func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { - if copyas(a, v) { - if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 { - if f { - a.Reg = s.Reg - } - } - return false - } - - if regtyp(v) { - if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { - if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE { - return true /* can't use BP-base with index */ - } - if f { - a.Reg = s.Reg - } - } - - if a.Index == v.Reg { - if f { - a.Index = s.Reg - } - } - } - return false -} - -func conprop(r0 *gc.Flow) { - var p *obj.Prog - - p0 := r0.Prog - v0 := &p0.To - r := r0 - -loop: - r = gc.Uniqs(r) - if r == nil || r == r0 { - return - } - if gc.Uniqp(r) == nil { - return - } - - p = r.Prog - switch copyu(p, v0, nil) { - case 0, // miss - 1: // use - goto loop - - case 2, // rar - 4: // use and set - break - - case 3: // set - if p.As == p0.As { - if p.From.Type == p0.From.Type { - if p.From.Reg == p0.From.Reg { - if p.From.Node == p0.From.Node { - if p.From.Offset == p0.From.Offset { - if p.From.Scale == p0.From.Scale { - if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { - if p.From.Index == p0.From.Index { - excise(r) - goto loop - } - } - } - } - } - } - } - } - } -} - -func smallindir(a *obj.Addr, reg *obj.Addr) bool { - return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 -} - -func stackaddr(a *obj.Addr) bool { - return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP -} diff --git a/src/cmd/compile/internal/x86/reg.go b/src/cmd/compile/internal/x86/reg.go index 2d7bd221ad..af9da3f3ec 100644 --- a/src/cmd/compile/internal/x86/reg.go +++ b/src/cmd/compile/internal/x86/reg.go @@ -31,57 +31,6 @@ package x86 import "cmd/internal/obj/x86" -import "cmd/compile/internal/gc" - -const ( - NREGVAR = 16 /* 8 integer + 8 floating */ -) - -var regname = []string{ - ".ax", - ".cx", - ".dx", - ".bx", - ".sp", - ".bp", - ".si", - ".di", - ".x0", - ".x1", - ".x2", - ".x3", - ".x4", - ".x5", - ".x6", - ".x7", -} - -func regnames(n *int) []string { - *n = NREGVAR - return regname -} - -func excludedregs() uint64 { - if gc.Ctxt.Flag_shared { - return RtoB(x86.REG_SP) | RtoB(x86.REG_CX) - } else { - return RtoB(x86.REG_SP) - } -} - -func doregbits(r int) uint64 { - b := uint64(0) - if r >= x86.REG_AX && r <= x86.REG_DI { - b |= RtoB(r) - } else if r >= x86.REG_AL && r <= x86.REG_BL { - b |= RtoB(r - x86.REG_AL + x86.REG_AX) - } else if r >= x86.REG_AH && r <= x86.REG_BH { - b |= RtoB(r - x86.REG_AH + x86.REG_AX) - } else if r >= x86.REG_X0 && r <= x86.REG_X0+7 { - b |= FtoB(r) - } - return b -} func RtoB(r int) uint64 { if r < x86.REG_AX || r > x86.REG_DI { @@ -89,26 +38,3 @@ func RtoB(r int) uint64 { } return 1 << uint(r-x86.REG_AX) } - -func BtoR(b uint64) int { - b &= 0xff - if b == 0 { - return 0 - } - return gc.Bitno(b) + x86.REG_AX -} - -func FtoB(f int) uint64 { - if f < x86.REG_X0 || f > x86.REG_X7 { - return 0 - } - return 1 << uint(f-x86.REG_X0+8) -} - -func BtoF(b uint64) int { - b &= 0xFF00 - if b == 0 { - return 0 - } - return gc.Bitno(b) - 8 + x86.REG_X0 -}