mirror of https://github.com/golang/go.git
cmd/compile: optimize bool to int conversion
This CL teaches SSA to recognize code of the form
// b is a boolean value, i is an int of some flavor
if b {
i = 1
} else {
i = 0
}
and use b's underlying 0/1 representation for i
instead of generating jumps.
Unfortunately, it does not work on the obvious code:
func bool2int(b bool) int {
if b {
return 1
}
return 0
}
This is left for future work.
Note that the existing phiopt optimizations also don't work for:
func neg(b bool) bool {
if b {
return false
}
return true
}
In the meantime, runtime authors and the like can use:
func bool2int(b bool) int {
var i int
if b {
i = 1
} else {
i = 0
}
return i
}
This compiles to:
"".bool2int t=1 size=16 args=0x10 locals=0x0
0x0000 00000 (x.go:25) TEXT "".bool2int(SB), $0-16
0x0000 00000 (x.go:25) FUNCDATA $0, gclocals·23e8278e2b69a3a75fa59b23c49ed6ad(SB)
0x0000 00000 (x.go:25) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
0x0000 00000 (x.go:32) MOVBLZX "".b+8(FP), AX
0x0005 00005 (x.go:32) MOVBQZX AL, AX
0x0008 00008 (x.go:32) MOVQ AX, "".~r1+16(FP)
0x000d 00013 (x.go:32) RET
The extraneous MOVBQZX is #15300.
This optimization also helps range and slice.
The compiler must protect against pointers pointing
to the end of a slice/string. It does this by increasing
a pointer by either 0 or 1 * elemsize, based on a condition.
This CL optimizes away a jump in that code.
This CL triggers 382 times while compiling the standard library.
Updating code to utilize this optimization is left for future CLs.
Updates #6011
Change-Id: Ia7c1185f8aa223c543f91a3cd6d4a2a09c691c70
Reviewed-on: https://go-review.googlesource.com/22711
Run-TryBot: Josh Bleecher Snyder <josharian@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
c7b9bd7456
commit
e85265e8c2
|
|
@ -57,7 +57,16 @@ func phiopt(f *Func) {
|
|||
}
|
||||
|
||||
for _, v := range b.Values {
|
||||
if v.Op != OpPhi || !v.Type.IsBoolean() {
|
||||
if v.Op != OpPhi {
|
||||
continue
|
||||
}
|
||||
|
||||
// Look for conversions from bool to 0/1.
|
||||
if v.Type.IsInteger() {
|
||||
phioptint(v, b0, reverse)
|
||||
}
|
||||
|
||||
if !v.Type.IsBoolean() {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -110,5 +119,55 @@ func phiopt(f *Func) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func phioptint(v *Value, b0 *Block, reverse int) {
|
||||
a0 := v.Args[0]
|
||||
a1 := v.Args[1]
|
||||
if a0.Op != a1.Op {
|
||||
return
|
||||
}
|
||||
|
||||
switch a0.Op {
|
||||
case OpConst8, OpConst16, OpConst32, OpConst64:
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
negate := false
|
||||
switch {
|
||||
case a0.AuxInt == 0 && a1.AuxInt == 1:
|
||||
negate = true
|
||||
case a0.AuxInt == 1 && a1.AuxInt == 0:
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
if reverse == 1 {
|
||||
negate = !negate
|
||||
}
|
||||
|
||||
switch v.Type.Size() {
|
||||
case 1:
|
||||
v.reset(OpCopy)
|
||||
case 2:
|
||||
v.reset(OpZeroExt8to16)
|
||||
case 4:
|
||||
v.reset(OpZeroExt8to32)
|
||||
case 8:
|
||||
v.reset(OpZeroExt8to64)
|
||||
default:
|
||||
v.Fatalf("bad int size %d", v.Type.Size())
|
||||
}
|
||||
|
||||
a := b0.Control
|
||||
if negate {
|
||||
a = v.Block.NewValue1(v.Line, OpNot, a.Type, a)
|
||||
}
|
||||
v.AddArg(a)
|
||||
|
||||
f := b0.Func
|
||||
if f.pass.debug > 0 {
|
||||
f.Config.Warnl(v.Block.Line, "converted OpPhi bool -> int%d", v.Type.Size()*8)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue