Skip to content

Commit

Permalink
cmd/compile: fold constants found by prove
Browse files Browse the repository at this point in the history
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: 4c2f9b5
GitHub-Pull-Request: #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>
  • Loading branch information
Jorropo authored and randall77 committed May 4, 2022
1 parent fbb47e8 commit e1e056f
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 10 deletions.
64 changes: 59 additions & 5 deletions src/cmd/compile/internal/ssa/prove.go
Original file line number Diff line number Diff line change
Expand Up @@ -1222,13 +1222,13 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
// Replace OpSlicemask operations in b with constants where possible.
x, delta := isConstDelta(v.Args[0])
if x == nil {
continue
break
}
// slicemask(x + y)
// if x is larger than -y (y is negative), then slicemask is -1.
lim, ok := ft.limits[x.ID]
if !ok {
continue
break
}
if lim.umin > uint64(-delta) {
if v.Args[0].Op == OpAdd64 {
Expand All @@ -1248,7 +1248,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
x := v.Args[0]
lim, ok := ft.limits[x.ID]
if !ok {
continue
break
}
if lim.umin > 0 || lim.min > 0 || lim.max < 0 {
if b.Func.pass.debug > 0 {
Expand Down Expand Up @@ -1280,7 +1280,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
panic("unexpected integer size")
}
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.
fallthrough
Expand All @@ -1297,7 +1297,7 @@ func simplifyBlock(sdom SparseTree, ft *factsTable, b *Block) {
by := v.Args[1]
lim, ok := ft.limits[by.ID]
if !ok {
continue
break
}
bits := 8 * v.Args[0].Type.Size()
if lim.umax < uint64(bits) || (lim.max < bits && ft.isNonNegative(by)) {
Expand Down Expand Up @@ -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 {
Expand Down
14 changes: 9 additions & 5 deletions test/codegen/comparisons.go
Original file line number Diff line number Diff line change
Expand Up @@ -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`
// arm64:`TSTW`,-`AND`
// 386:`TESTL`,-`ANDL`
Expand Down Expand Up @@ -201,13 +201,17 @@ func CmpToZero(a, b, d int32, e, f int64) int32 {
} else if c4 {
return 5
} else if c5 {
return b + d
return 6
} else if c6 {
return a & d
} else if c7 {
return 7
} else if c7 {
return 9
} else if c8 {
return 8
return 10
} else if deOptC0 {
return b + d
} else if deOptC1 {
return a & d
} else {
return 0
}
Expand Down
32 changes: 32 additions & 0 deletions test/prove_constant_folding.go
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit e1e056f

Please sign in to comment.