1
0
mirror of https://github.com/golang/go synced 2024-11-11 19:51:37 -07:00

cmd/compile: fold constants found by prove

It is hit ~70k times building go.
This make the go binary, 0.04% smaller.
I didn't included benchmarks because this is just constant foldings
and is hard to mesure objectively.

For example, this enable rewriting things like:
  if x == 20 {
    return x + 30 + z
  }

Into:
  if x == 20 {
    return 50 + z
  }

It's not just fixing programer's code,
the ssa generator generate code like this sometimes.

Change-Id: I0861f342b27f7227b5f1c34d8267fa0057b1bbbc
GitHub-Last-Rev: 4c2f9b5216
GitHub-Pull-Request: golang/go#52669
Reviewed-on: https://go-review.googlesource.com/c/go/+/403735
Reviewed-by: Keith Randall <khr@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
Jorropo 2022-05-04 17:03:13 +00:00 committed by Keith Randall
parent fbb47e81c1
commit e1e056fa6a
3 changed files with 100 additions and 10 deletions

View File

@ -1222,13 +1222,13 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
// Replace OpSlicemask operations in b with constants where possible. // Replace OpSlicemask operations in b with constants where possible.
x, delta := isConstDelta(v.Args[0]) x, delta := isConstDelta(v.Args[0])
if x == nil { if x == nil {
continue break
} }
// slicemask(x + y) // slicemask(x + y)
// if x is larger than -y (y is negative), then slicemask is -1. // if x is larger than -y (y is negative), then slicemask is -1.
lim, ok := ft.limits[x.ID] lim, ok := ft.limits[x.ID]
if !ok { if !ok {
continue break
} }
if lim.umin > uint64(-delta) { if lim.umin > uint64(-delta) {
if v.Args[0].Op == OpAdd64 { if v.Args[0].Op == OpAdd64 {
@ -1248,7 +1248,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
x := v.Args[0] x := v.Args[0]
lim, ok := ft.limits[x.ID] lim, ok := ft.limits[x.ID]
if !ok { if !ok {
continue break
} }
if lim.umin > 0 || lim.min > 0 || lim.max < 0 { if lim.umin > 0 || lim.min > 0 || lim.max < 0 {
if b.Func.pass.debug > 0 { if b.Func.pass.debug > 0 {
@ -1280,7 +1280,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
panic("unexpected integer size") panic("unexpected integer size")
} }
v.AuxInt = 0 v.AuxInt = 0
continue // Be sure not to fallthrough - this is no longer OpRsh. break // Be sure not to fallthrough - this is no longer OpRsh.
} }
// If the Rsh hasn't been replaced with 0, still check if it is bounded. // If the Rsh hasn't been replaced with 0, still check if it is bounded.
fallthrough fallthrough
@ -1297,7 +1297,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
by := v.Args[1] by := v.Args[1]
lim, ok := ft.limits[by.ID] lim, ok := ft.limits[by.ID]
if !ok { if !ok {
continue break
} }
bits := 8 * v.Args[0].Type.Size() bits := 8 * v.Args[0].Type.Size()
if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) { if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) {
@ -1331,6 +1331,60 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
} }
} }
} }
// Fold provable constant results.
// Helps in cases where we reuse a value after branching on its equality.
for i, arg := range v.Args {
switch arg.Op {
case OpConst64, OpConst32, OpConst16, OpConst8:
continue
}
lim, ok := ft.limits[arg.ID]
if !ok {
continue
}
var constValue int64
typ := arg.Type
bits := 8 * typ.Size()
switch {
case lim.min == lim.max:
constValue = lim.min
case lim.umin == lim.umax:
// truncate then sign extand
switch bits {
case 64:
constValue = int64(lim.umin)
case 32:
constValue = int64(int32(lim.umin))
case 16:
constValue = int64(int16(lim.umin))
case 8:
constValue = int64(int8(lim.umin))
default:
panic("unexpected integer size")
}
default:
continue
}
var c *Value
f := b.Func
switch bits {
case 64:
c = f.ConstInt64(typ, constValue)
case 32:
c = f.ConstInt32(typ, int32(constValue))
case 16:
c = f.ConstInt16(typ, int16(constValue))
case 8:
c = f.ConstInt8(typ, int8(constValue))
default:
panic("unexpected integer size")
}
v.SetArg(i, c)
if b.Func.pass.debug > 1 {
b.Func.Warnl(v.Pos, "Proved %v's arg %d (%v) is constant %d", v, i, arg, constValue)
}
}
} }
if b.Kind != BlockIf { if b.Kind != BlockIf {

View File

@ -161,7 +161,7 @@ func CmpZero4(a int64, ptr *int) {
} }
} }
func CmpToZero(a, b, d int32, e, f int64) int32 { func CmpToZero(a, b, d int32, e, f int64, deOptC0, deOptC1 bool) int32 {
// arm:`TST`,-`AND` // arm:`TST`,-`AND`
// arm64:`TSTW`,-`AND` // arm64:`TSTW`,-`AND`
// 386:`TESTL`,-`ANDL` // 386:`TESTL`,-`ANDL`
@ -201,13 +201,17 @@ func CmpToZero(a, b, d int32, e, f int64) int32 {
} else if c4 { } else if c4 {
return 5 return 5
} else if c5 { } else if c5 {
return b + d return 6
} else if c6 { } else if c6 {
return a & d
} else if c7 {
return 7 return 7
} else if c7 {
return 9
} else if c8 { } else if c8 {
return 8 return 10
} else if deOptC0 {
return b + d
} else if deOptC1 {
return a & d
} else { } else {
return 0 return 0
} }

View File

@ -0,0 +1,32 @@
// +build amd64
// errorcheck -0 -d=ssa/prove/debug=2
// Copyright 2022 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 main
func f0i(x int) int {
if x == 20 {
return x // ERROR "Proved.+is constant 20$"
}
if (x + 20) == 20 {
return x + 5 // ERROR "Proved.+is constant 0$"
}
return x / 2
}
func f0u(x uint) uint {
if x == 20 {
return x // ERROR "Proved.+is constant 20$"
}
if (x + 20) == 20 {
return x + 5 // ERROR "Proved.+is constant 0$"
}
return x / 2
}