cmd/internal/gc: clean up bgen
This cleanup is in anticipation of implementing
jump-free booleans (CL 2284) and zero-aware
comparisons (issue 10381).
No functional changes. Passes toolstash -cmp.
Change-Id: I50f394c60fa2927e177d7fc85b75085060a9e912
Reviewed-on: https://go-review.googlesource.com/8738
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index 2ceddc9..8c7cb0e 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -345,18 +345,13 @@
Dump("cgen-res", res)
Fatal("cgen: unknown op %v", Nconv(n, obj.FmtShort|obj.FmtSign))
- // these call bgen to get a bool value
- case OOROR,
- OANDAND,
- OEQ,
- ONE,
- OLT,
- OLE,
- OGE,
- OGT,
+ // these call bgen to get a bool value
+ case OOROR, OANDAND,
+ OEQ, ONE,
+ OLT, OLE,
+ OGE, OGT,
ONOT:
p1 := Gbranch(obj.AJMP, nil, 0)
-
p2 := Pc
Thearch.Gmove(Nodbool(true), res)
p3 := Gbranch(obj.AJMP, nil, 0)
@@ -1639,22 +1634,22 @@
a.Type = n.Type
}
-/*
- * generate:
- * if(n == true) goto to;
- */
-func Bgen(n *Node, true_ bool, likely int, to *obj.Prog) {
+// Bgen generates code for branches:
+//
+// if n == wantTrue {
+// goto to
+// }
+func Bgen(n *Node, wantTrue bool, likely int, to *obj.Prog) {
if Debug['g'] != 0 {
- Dump("\nbgen", n)
+ fmt.Printf("\nbgen wantTrue=%t likely=%d to=%v\n", wantTrue, likely, to)
+ Dump("bgen", n)
}
if n == nil {
n = Nodbool(true)
}
- if n.Ninit != nil {
- Genlist(n.Ninit)
- }
+ Genlist(n.Ninit)
if n.Type == nil {
Convlit(&n, Types[TBOOL])
@@ -1663,8 +1658,7 @@
}
}
- et := int(n.Type.Etype)
- if et != TBOOL {
+ if n.Type.Etype != TBOOL {
Yyerror("cgen: bad type %v for %v", Tconv(n.Type, 0), Oconv(int(n.Op), 0))
Patch(Thearch.Gins(obj.AEND, nil, nil), to)
return
@@ -1672,204 +1666,172 @@
for n.Op == OCONVNOP {
n = n.Left
- if n.Ninit != nil {
- Genlist(n.Ninit)
- }
+ Genlist(n.Ninit)
}
if Thearch.Bgen_float != nil && n.Left != nil && Isfloat[n.Left.Type.Etype] {
- Thearch.Bgen_float(n, bool2int(true_), likely, to)
+ Thearch.Bgen_float(n, wantTrue, likely, to)
return
}
- var nl *Node
- var nr *Node
switch n.Op {
default:
- goto def
+ var tmp Node
+ Regalloc(&tmp, n.Type, nil)
+ Cgen(n, &tmp)
+ bgenNonZero(&tmp, wantTrue, likely, to)
+ Regfree(&tmp)
+ return
- // need to ask if it is bool?
+ case ONAME:
+ if n.Addable && Ctxt.Arch.Thechar != '5' && Ctxt.Arch.Thechar != '7' && Ctxt.Arch.Thechar != '9' {
+ // no need for a temporary
+ bgenNonZero(n, wantTrue, likely, to)
+ return
+ }
+ var tmp Node
+ Regalloc(&tmp, n.Type, nil)
+ Cgen(n, &tmp)
+ bgenNonZero(&tmp, wantTrue, likely, to)
+ Regfree(&tmp)
+ return
+
case OLITERAL:
- if true_ == n.Val.U.Bval {
+ // n is a constant. If n == wantTrue, jump; otherwise do nothing.
+ if !Isconst(n, CTBOOL) {
+ Fatal("bgen: non-bool const %v\n", Nconv(n, obj.FmtLong))
+ }
+ if wantTrue == n.Val.U.Bval {
Patch(Gbranch(obj.AJMP, nil, likely), to)
}
return
- case ONAME:
- if !n.Addable || Ctxt.Arch.Thechar == '5' || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' {
- goto def
- }
- var n1 Node
- Nodconst(&n1, n.Type, 0)
- Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &n1)
- a := Thearch.Optoas(ONE, n.Type)
- if !true_ {
- a = Thearch.Optoas(OEQ, n.Type)
- }
- Patch(Gbranch(a, n.Type, likely), to)
- return
-
case OANDAND, OOROR:
- if (n.Op == OANDAND) == true_ {
+ if (n.Op == OANDAND) == wantTrue {
p1 := Gbranch(obj.AJMP, nil, 0)
p2 := Gbranch(obj.AJMP, nil, 0)
Patch(p1, Pc)
- Bgen(n.Left, !true_, -likely, p2)
- Bgen(n.Right, !true_, -likely, p2)
+ 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, true_, likely, to)
- Bgen(n.Right, true_, likely, to)
+ 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
+ }
+ Bgen(n.Left, !wantTrue, likely, to)
return
case OEQ, ONE, OLT, OGT, OLE, OGE:
- nr = n.Right
- if nr == nil || nr.Type == nil {
- return
- }
- fallthrough
-
- case ONOT: // unary
- nl = n.Left
-
- if nl == nil || nl.Type == nil {
+ if n.Left == nil || n.Left.Type == nil || n.Right == nil || n.Right.Type == nil {
return
}
}
- switch n.Op {
- case ONOT:
- Bgen(nl, !true_, likely, to)
- return
+ // n.Op is one of OEQ, ONE, OLT, OGT, OLE, OGE
+ nl := n.Left
+ nr := n.Right
+ a := int(n.Op)
- case OEQ, ONE, OLT, OGT, OLE, OGE:
- a := int(n.Op)
- if !true_ {
- if Isfloat[nr.Type.Etype] {
- // brcom is not valid on floats when NaN is involved.
- p1 := Gbranch(obj.AJMP, nil, 0)
- p2 := Gbranch(obj.AJMP, nil, 0)
- Patch(p1, Pc)
- ll := n.Ninit // avoid re-genning ninit
- n.Ninit = nil
- Bgen(n, true, -likely, p2)
- n.Ninit = ll
- Patch(Gbranch(obj.AJMP, nil, 0), to)
- Patch(p2, Pc)
- return
- }
-
- a = Brcom(a)
- true_ = !true_
+ if !wantTrue {
+ if Isfloat[nr.Type.Etype] {
+ // Brcom is not valid on floats when NaN is involved.
+ p1 := Gbranch(obj.AJMP, nil, 0)
+ p2 := Gbranch(obj.AJMP, nil, 0)
+ Patch(p1, Pc)
+ ll := n.Ninit // avoid re-genning Ninit
+ n.Ninit = nil
+ Bgen(n, true, -likely, p2)
+ n.Ninit = ll
+ Patch(Gbranch(obj.AJMP, nil, 0), to)
+ Patch(p2, Pc)
+ return
}
- // make simplest on right
- if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) {
- a = Brrev(a)
- r := nl
- nl = nr
- nr = r
- }
+ a = Brcom(a)
+ }
+ wantTrue = true
- if Isslice(nl.Type) {
- // front end should only leave cmp to literal nil
- if (a != OEQ && a != ONE) || nr.Op != OLITERAL {
+ // make simplest on right
+ if nl.Op == OLITERAL || (nl.Ullman < nr.Ullman && nl.Ullman < UINF) {
+ a = Brrev(a)
+ nl, nr = nr, nl
+ }
+
+ if Isslice(nl.Type) || Isinter(nl.Type) {
+ // front end should only leave cmp to literal nil
+ if (a != OEQ && a != ONE) || nr.Op != OLITERAL {
+ if Isslice(nl.Type) {
Yyerror("illegal slice comparison")
- break
- }
-
- a = Thearch.Optoas(a, Types[Tptr])
- var n1 Node
- Igen(nl, &n1, nil)
- n1.Xoffset += int64(Array_array)
- n1.Type = Types[Tptr]
- var n2 Node
- Regalloc(&n2, Types[Tptr], &n1)
- Cgen(&n1, &n2)
- Regfree(&n1)
- var tmp Node
- Nodconst(&tmp, Types[Tptr], 0)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n2, &tmp)
- Patch(Gbranch(a, Types[Tptr], likely), to)
- Regfree(&n2)
- break
- }
-
- if Isinter(nl.Type) {
- // front end should only leave cmp to literal nil
- if (a != OEQ && a != ONE) || nr.Op != OLITERAL {
+ } else {
Yyerror("illegal interface comparison")
- break
}
-
- a = Thearch.Optoas(a, Types[Tptr])
- var n1 Node
- Igen(nl, &n1, nil)
- n1.Type = Types[Tptr]
- var n2 Node
- Regalloc(&n2, Types[Tptr], &n1)
- Cgen(&n1, &n2)
- Regfree(&n1)
- var tmp Node
- Nodconst(&tmp, Types[Tptr], 0)
- Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n2, &tmp)
- Patch(Gbranch(a, Types[Tptr], likely), to)
- Regfree(&n2)
- break
+ return
}
- if Iscomplex[nl.Type.Etype] {
- Complexbool(a, nl, nr, true_, likely, to)
- break
+ var ptr Node
+ Igen(nl, &ptr, nil)
+ if Isslice(nl.Type) {
+ ptr.Xoffset += int64(Array_array)
}
+ ptr.Type = Types[Tptr]
+ var tmp Node
+ Regalloc(&tmp, ptr.Type, &ptr)
+ Cgen(&ptr, &tmp)
+ Regfree(&ptr)
+ bgenNonZero(&tmp, a == OEQ != wantTrue, likely, to)
+ Regfree(&tmp)
+ return
+ }
- if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) {
- if !nl.Addable || Isconst(nl, CTINT) {
- var n1 Node
- Tempname(&n1, nl.Type)
- Cgen(nl, &n1)
- nl = &n1
- }
+ if Iscomplex[nl.Type.Etype] {
+ complexbool(a, nl, nr, wantTrue, likely, to)
+ return
+ }
- if !nr.Addable {
- var n2 Node
- Tempname(&n2, nr.Type)
- Cgen(nr, &n2)
- nr = &n2
- }
-
- Thearch.Cmp64(nl, nr, a, likely, to)
- break
+ if Ctxt.Arch.Regsize == 4 && Is64(nr.Type) {
+ if !nl.Addable || Isconst(nl, CTINT) {
+ nl = CgenTemp(nl)
}
+ if !nr.Addable {
+ nr = CgenTemp(nr)
+ }
+ Thearch.Cmp64(nl, nr, a, likely, to)
+ return
+ }
+ if nr.Ullman >= UINF {
var n1 Node
+ Regalloc(&n1, nl.Type, nil)
+ Cgen(nl, &n1)
+
+ var tmp Node
+ Tempname(&tmp, nl.Type)
+ Thearch.Gmove(&n1, &tmp)
+ Regfree(&n1)
+
var n2 Node
- if nr.Ullman >= UINF {
- Regalloc(&n1, nl.Type, nil)
- Cgen(nl, &n1)
+ Regalloc(&n2, nr.Type, nil)
+ Cgen(nr, &n2)
+ Regfree(&n2)
- var tmp Node
- Tempname(&tmp, nl.Type)
- Thearch.Gmove(&n1, &tmp)
- Regfree(&n1)
-
- Regalloc(&n2, nr.Type, nil)
- Cgen(nr, &n2)
-
- Regalloc(&n1, nl.Type, nil)
- Cgen(&tmp, &n1)
-
- goto cmp
- }
-
+ Regalloc(&n1, nl.Type, nil)
+ Cgen(&tmp, &n1)
+ Regfree(&n1)
+ } else {
+ var n1 Node
if !nl.Addable && Ctxt.Arch.Thechar == '8' {
Tempname(&n1, nl.Type)
} else {
Regalloc(&n1, nl.Type, nil)
+ defer Regfree(&n1)
}
Cgen(nl, &n1)
nl = &n1
@@ -1877,92 +1839,93 @@
if Smallintconst(nr) && Ctxt.Arch.Thechar != '9' {
Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), nl, nr)
Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
- if n1.Op == OREGISTER {
- Regfree(&n1)
- }
- break
+ return
}
if !nr.Addable && Ctxt.Arch.Thechar == '8' {
- var tmp Node
- Tempname(&tmp, nr.Type)
- Cgen(nr, &tmp)
- nr = &tmp
+ nr = CgenTemp(nr)
}
+ var n2 Node
Regalloc(&n2, nr.Type, nil)
Cgen(nr, &n2)
nr = &n2
+ Regfree(&n2)
+ }
- cmp:
- l, r := nl, nr
- // On x86, only < and <= work right with NaN; reverse if needed
- if Ctxt.Arch.Thechar == '6' && Isfloat[nl.Type.Etype] && (a == OGT || a == OGE) {
- l, r = r, l
- a = Brrev(a)
- }
+ l, r := nl, nr
- Thearch.Gins(Thearch.Optoas(OCMP, nr.Type), l, r)
+ // On x86, only < and <= work right with NaN; reverse if needed
+ if Ctxt.Arch.Thechar == '6' && Isfloat[nl.Type.Etype] && (a == OGT || a == OGE) {
+ l, r = r, l
+ a = Brrev(a)
+ }
- if Ctxt.Arch.Thechar == '6' && Isfloat[nr.Type.Etype] && (n.Op == OEQ || n.Op == ONE) {
- if n.Op == OEQ {
+ // 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 Isfloat[nl.Type.Etype] {
+ switch Ctxt.Arch.Thechar {
+ case '5':
+ switch n.Op {
+ case ONE:
+ Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to)
+ Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
+ default:
+ p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely)
+ Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
+ Patch(p, Pc)
+ }
+ return
+ case '6':
+ switch n.Op {
+ case OEQ:
// neither NE nor P
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)
- } else {
+ return
+ case ONE:
// either NE or P
Patch(Gbranch(Thearch.Optoas(ONE, nr.Type), nil, likely), to)
Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nil, likely), to)
+ return
}
- } else if Ctxt.Arch.Thechar == '5' && Isfloat[nl.Type.Etype] {
- if n.Op == ONE {
- Patch(Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, likely), to)
- Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
- } else {
- p := Gbranch(Thearch.Optoas(OPS, nr.Type), nr.Type, -likely)
- Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
- Patch(p, Pc)
- }
- } else if (Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9') && Isfloat[nl.Type.Etype] && (a == OLE || a == OGE) {
+ case '7', '9':
+ switch n.Op {
// On arm64 and ppc64, <= and >= mishandle NaN. Must decompose into < or > and =.
- if a == OLE {
- a = OLT
- } else {
- a = OGT
+ // TODO(josh): Convert a <= b to b > a instead?
+ case OLE, OGE:
+ if a == OLE {
+ a = OLT
+ } else {
+ a = OGT
+ }
+ Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
+ Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to)
+ return
}
- Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
- Patch(Gbranch(Thearch.Optoas(OEQ, nr.Type), nr.Type, likely), to)
- } else {
- Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
- }
- if n1.Op == OREGISTER {
- Regfree(&n1)
- }
- if n2.Op == OREGISTER {
- Regfree(&n2)
}
}
- return
+ // Not a special case. Insert an appropriate conditional jump.
+ Patch(Gbranch(Thearch.Optoas(a, nr.Type), nr.Type, likely), to)
+}
-def:
+func bgenNonZero(n *Node, wantTrue bool, likely int, to *obj.Prog) {
// TODO: Optimize on systems that can compare to zero easily.
- var n1 Node
- Regalloc(&n1, n.Type, nil)
- Cgen(n, &n1)
- var n2 Node
- Nodconst(&n2, n.Type, 0)
- Thearch.Gins(Thearch.Optoas(OCMP, n.Type), &n1, &n2)
- a := Thearch.Optoas(ONE, n.Type)
- if !true_ {
- a = Thearch.Optoas(OEQ, n.Type)
+ a := ONE
+ if !wantTrue {
+ a = OEQ
}
- Patch(Gbranch(a, n.Type, likely), to)
- Regfree(&n1)
- return
+ var zero Node
+ Nodconst(&zero, n.Type, 0)
+ Thearch.Gins(Thearch.Optoas(OCMP, n.Type), n, &zero)
+ Patch(Gbranch(Thearch.Optoas(a, n.Type), n.Type, likely), to)
}
/*