mirror of
https://github.com/golang/go
synced 2024-10-05 20:31:20 -06:00
[dev.ssa] cmd/compile: rewrite user nil check as OpIsNonNil
Rewite user nil checks as OpIsNonNil so our nil check elimination pass can take advantage and remove redundant checks. With make.bash this removes 10% more nilchecks (34110 vs 31088). Change-Id: Ifb01d1b6d2d759f5e2a5aaa0470e1d5a2a680212 Reviewed-on: https://go-review.googlesource.com/14321 Reviewed-by: Keith Randall <khr@golang.org> Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
This commit is contained in:
parent
617e892b87
commit
ec8a597cd2
@ -121,6 +121,8 @@ var passOrder = [...]constraint{
|
|||||||
{"nilcheckelim", "generic deadcode"},
|
{"nilcheckelim", "generic deadcode"},
|
||||||
// nilcheckelim generates sequences of plain basic blocks
|
// nilcheckelim generates sequences of plain basic blocks
|
||||||
{"nilcheckelim", "fuse"},
|
{"nilcheckelim", "fuse"},
|
||||||
|
// nilcheckelim relies on opt to rewrite user nil checks
|
||||||
|
{"opt", "nilcheckelim"},
|
||||||
// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
|
// tighten should happen before lowering to avoid splitting naturally paired instructions such as CMP/SET
|
||||||
{"tighten", "lower"},
|
{"tighten", "lower"},
|
||||||
// tighten will be most effective when as many values have been removed as possible
|
// tighten will be most effective when as many values have been removed as possible
|
||||||
|
@ -59,6 +59,12 @@
|
|||||||
(Com32 (Com32 x)) -> x
|
(Com32 (Com32 x)) -> x
|
||||||
(Com64 (Com64 x)) -> x
|
(Com64 (Com64 x)) -> x
|
||||||
|
|
||||||
|
// user nil checks
|
||||||
|
(NeqPtr p (ConstNil)) -> (IsNonNil p)
|
||||||
|
(NeqPtr (ConstNil) p) -> (IsNonNil p)
|
||||||
|
(EqPtr p (ConstNil)) -> (Not (IsNonNil p))
|
||||||
|
(EqPtr (ConstNil) p) -> (Not (IsNonNil p))
|
||||||
|
|
||||||
// slice and interface comparisons
|
// slice and interface comparisons
|
||||||
// the frontend ensures that we can only compare against nil
|
// the frontend ensures that we can only compare against nil
|
||||||
// start by putting nil on the right to simplify the other rules
|
// start by putting nil on the right to simplify the other rules
|
||||||
|
@ -313,9 +313,9 @@ var genericOps = []opData{
|
|||||||
{name: "Cvt64Fto32F"},
|
{name: "Cvt64Fto32F"},
|
||||||
|
|
||||||
// Automatically inserted safety checks
|
// Automatically inserted safety checks
|
||||||
{name: "IsNonNil"}, // arg0 != nil
|
{name: "IsNonNil", typ: "Bool"}, // arg0 != nil
|
||||||
{name: "IsInBounds"}, // 0 <= arg0 < arg1
|
{name: "IsInBounds", typ: "Bool"}, // 0 <= arg0 < arg1
|
||||||
{name: "IsSliceInBounds"}, // 0 <= arg0 <= arg1
|
{name: "IsSliceInBounds", typ: "Bool"}, // 0 <= arg0 <= arg1
|
||||||
|
|
||||||
// Pseudo-ops
|
// Pseudo-ops
|
||||||
{name: "PanicNilCheck"}, // trigger a dereference fault; arg0=nil ptr, arg1=mem, returns mem
|
{name: "PanicNilCheck"}, // trigger a dereference fault; arg0=nil ptr, arg1=mem, returns mem
|
||||||
|
@ -105,12 +105,11 @@ func nilcheckelim(f *Func) {
|
|||||||
|
|
||||||
var nilBranch *Block
|
var nilBranch *Block
|
||||||
for _, w := range domTree[node.block.ID] {
|
for _, w := range domTree[node.block.ID] {
|
||||||
// TODO: Since we handle the false side of OpIsNonNil
|
// We are about to traverse down the 'ptr is nil' side
|
||||||
// correctly, look into rewriting user nil checks into
|
// of a nilcheck block, so save it for later. This doesn't
|
||||||
// OpIsNonNil so they can be eliminated also
|
// remove nil checks on the false side of the OpIsNonNil branch.
|
||||||
|
// This is important otherwise we would remove nil checks that
|
||||||
// we are about to traverse down the 'ptr is nil' side
|
// are not redundant.
|
||||||
// of a nilcheck block, so save it for later
|
|
||||||
if node.block.Kind == BlockIf && node.block.Control.Op == OpIsNonNil &&
|
if node.block.Kind == BlockIf && node.block.Control.Op == OpIsNonNil &&
|
||||||
w == node.block.Succs[1] {
|
w == node.block.Succs[1] {
|
||||||
nilBranch = w
|
nilBranch = w
|
||||||
|
@ -342,3 +342,43 @@ func TestNilcheckInFalseBranch(t *testing.T) {
|
|||||||
t.Errorf("removed thirdCheck, but shouldn't have [false branch]")
|
t.Errorf("removed thirdCheck, but shouldn't have [false branch]")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestNilcheckUser verifies that a user nil check that dominates a generated nil check
|
||||||
|
// wil remove the generated nil check.
|
||||||
|
func TestNilcheckUser(t *testing.T) {
|
||||||
|
ptrType := &TypeImpl{Size_: 8, Ptr: true, Name: "testptr"} // dummy for testing
|
||||||
|
c := NewConfig("amd64", DummyFrontend{t})
|
||||||
|
fun := Fun(c, "entry",
|
||||||
|
Bloc("entry",
|
||||||
|
Valu("mem", OpArg, TypeMem, 0, ".mem"),
|
||||||
|
Valu("sb", OpSB, TypeInvalid, 0, nil),
|
||||||
|
Goto("checkPtr")),
|
||||||
|
Bloc("checkPtr",
|
||||||
|
Valu("ptr1", OpConstPtr, ptrType, 0, nil, "sb"),
|
||||||
|
Valu("nilptr", OpConstNil, ptrType, 0, nil, "sb"),
|
||||||
|
Valu("bool1", OpNeqPtr, TypeBool, 0, nil, "ptr1", "nilptr"),
|
||||||
|
If("bool1", "secondCheck", "exit")),
|
||||||
|
Bloc("secondCheck",
|
||||||
|
Valu("bool2", OpIsNonNil, TypeBool, 0, nil, "ptr1"),
|
||||||
|
If("bool2", "extra", "exit")),
|
||||||
|
Bloc("extra",
|
||||||
|
Goto("exit")),
|
||||||
|
Bloc("exit",
|
||||||
|
Exit("mem")))
|
||||||
|
|
||||||
|
CheckFunc(fun.f)
|
||||||
|
// we need the opt here to rewrite the user nilcheck
|
||||||
|
opt(fun.f)
|
||||||
|
nilcheckelim(fun.f)
|
||||||
|
|
||||||
|
// clean up the removed nil check
|
||||||
|
fuse(fun.f)
|
||||||
|
deadcode(fun.f)
|
||||||
|
|
||||||
|
CheckFunc(fun.f)
|
||||||
|
for _, b := range fun.f.Blocks {
|
||||||
|
if b == fun.blocks["secondCheck"] && isNilCheck(b) {
|
||||||
|
t.Errorf("secondCheck was not eliminated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -478,6 +478,49 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
|
|||||||
goto end6f10fb57a906a2c23667c770acb6abf9
|
goto end6f10fb57a906a2c23667c770acb6abf9
|
||||||
end6f10fb57a906a2c23667c770acb6abf9:
|
end6f10fb57a906a2c23667c770acb6abf9:
|
||||||
;
|
;
|
||||||
|
case OpEqPtr:
|
||||||
|
// match: (EqPtr p (ConstNil))
|
||||||
|
// cond:
|
||||||
|
// result: (Not (IsNonNil p))
|
||||||
|
{
|
||||||
|
p := v.Args[0]
|
||||||
|
if v.Args[1].Op != OpConstNil {
|
||||||
|
goto ende701cdb6a2c1fff4d4b283b7f8f6178b
|
||||||
|
}
|
||||||
|
v.Op = OpNot
|
||||||
|
v.AuxInt = 0
|
||||||
|
v.Aux = nil
|
||||||
|
v.resetArgs()
|
||||||
|
v0 := b.NewValue0(v.Line, OpIsNonNil, TypeInvalid)
|
||||||
|
v0.AddArg(p)
|
||||||
|
v0.Type = config.fe.TypeBool()
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
goto ende701cdb6a2c1fff4d4b283b7f8f6178b
|
||||||
|
ende701cdb6a2c1fff4d4b283b7f8f6178b:
|
||||||
|
;
|
||||||
|
// match: (EqPtr (ConstNil) p)
|
||||||
|
// cond:
|
||||||
|
// result: (Not (IsNonNil p))
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpConstNil {
|
||||||
|
goto end7cdc0d5c38fbffe6287c8928803b038e
|
||||||
|
}
|
||||||
|
p := v.Args[1]
|
||||||
|
v.Op = OpNot
|
||||||
|
v.AuxInt = 0
|
||||||
|
v.Aux = nil
|
||||||
|
v.resetArgs()
|
||||||
|
v0 := b.NewValue0(v.Line, OpIsNonNil, TypeInvalid)
|
||||||
|
v0.AddArg(p)
|
||||||
|
v0.Type = config.fe.TypeBool()
|
||||||
|
v.AddArg(v0)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
goto end7cdc0d5c38fbffe6287c8928803b038e
|
||||||
|
end7cdc0d5c38fbffe6287c8928803b038e:
|
||||||
|
;
|
||||||
case OpIData:
|
case OpIData:
|
||||||
// match: (IData (IMake _ data))
|
// match: (IData (IMake _ data))
|
||||||
// cond:
|
// cond:
|
||||||
@ -961,6 +1004,43 @@ func rewriteValuegeneric(v *Value, config *Config) bool {
|
|||||||
goto end3ffd7685735a83eaee8dc2577ae89d79
|
goto end3ffd7685735a83eaee8dc2577ae89d79
|
||||||
end3ffd7685735a83eaee8dc2577ae89d79:
|
end3ffd7685735a83eaee8dc2577ae89d79:
|
||||||
;
|
;
|
||||||
|
case OpNeqPtr:
|
||||||
|
// match: (NeqPtr p (ConstNil))
|
||||||
|
// cond:
|
||||||
|
// result: (IsNonNil p)
|
||||||
|
{
|
||||||
|
p := v.Args[0]
|
||||||
|
if v.Args[1].Op != OpConstNil {
|
||||||
|
goto endba798520b4d41172b110347158c44791
|
||||||
|
}
|
||||||
|
v.Op = OpIsNonNil
|
||||||
|
v.AuxInt = 0
|
||||||
|
v.Aux = nil
|
||||||
|
v.resetArgs()
|
||||||
|
v.AddArg(p)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
goto endba798520b4d41172b110347158c44791
|
||||||
|
endba798520b4d41172b110347158c44791:
|
||||||
|
;
|
||||||
|
// match: (NeqPtr (ConstNil) p)
|
||||||
|
// cond:
|
||||||
|
// result: (IsNonNil p)
|
||||||
|
{
|
||||||
|
if v.Args[0].Op != OpConstNil {
|
||||||
|
goto enddd95e9c3606d9fd48034f1a703561e45
|
||||||
|
}
|
||||||
|
p := v.Args[1]
|
||||||
|
v.Op = OpIsNonNil
|
||||||
|
v.AuxInt = 0
|
||||||
|
v.Aux = nil
|
||||||
|
v.resetArgs()
|
||||||
|
v.AddArg(p)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
goto enddd95e9c3606d9fd48034f1a703561e45
|
||||||
|
enddd95e9c3606d9fd48034f1a703561e45:
|
||||||
|
;
|
||||||
case OpOr16:
|
case OpOr16:
|
||||||
// match: (Or16 x x)
|
// match: (Or16 x x)
|
||||||
// cond:
|
// cond:
|
||||||
|
Loading…
Reference in New Issue
Block a user