[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>
diff --git a/src/cmd/compile/internal/ssa/compile.go b/src/cmd/compile/internal/ssa/compile.go
index bff1a81..a9365e9 100644
--- a/src/cmd/compile/internal/ssa/compile.go
+++ b/src/cmd/compile/internal/ssa/compile.go
@@ -121,6 +121,8 @@
 	{"nilcheckelim", "generic deadcode"},
 	// nilcheckelim generates sequences of plain basic blocks
 	{"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", "lower"},
 	// tighten will be most effective when as many values have been removed as possible
diff --git a/src/cmd/compile/internal/ssa/gen/generic.rules b/src/cmd/compile/internal/ssa/gen/generic.rules
index 8d7b069..d2ab9f5 100644
--- a/src/cmd/compile/internal/ssa/gen/generic.rules
+++ b/src/cmd/compile/internal/ssa/gen/generic.rules
@@ -59,6 +59,12 @@
 (Com32 (Com32 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
 // the frontend ensures that we can only compare against nil
 // start by putting nil on the right to simplify the other rules
diff --git a/src/cmd/compile/internal/ssa/gen/genericOps.go b/src/cmd/compile/internal/ssa/gen/genericOps.go
index 59b90ad..8cd8165 100644
--- a/src/cmd/compile/internal/ssa/gen/genericOps.go
+++ b/src/cmd/compile/internal/ssa/gen/genericOps.go
@@ -313,9 +313,9 @@
 	{name: "Cvt64Fto32F"},
 
 	// Automatically inserted safety checks
-	{name: "IsNonNil"},        // arg0 != nil
-	{name: "IsInBounds"},      // 0 <= arg0 < arg1
-	{name: "IsSliceInBounds"}, // 0 <= arg0 <= arg1
+	{name: "IsNonNil", typ: "Bool"},        // arg0 != nil
+	{name: "IsInBounds", typ: "Bool"},      // 0 <= arg0 < arg1
+	{name: "IsSliceInBounds", typ: "Bool"}, // 0 <= arg0 <= arg1
 
 	// Pseudo-ops
 	{name: "PanicNilCheck"},   // trigger a dereference fault; arg0=nil ptr, arg1=mem, returns mem
diff --git a/src/cmd/compile/internal/ssa/nilcheck.go b/src/cmd/compile/internal/ssa/nilcheck.go
index 80b9e66..16cb04d 100644
--- a/src/cmd/compile/internal/ssa/nilcheck.go
+++ b/src/cmd/compile/internal/ssa/nilcheck.go
@@ -105,12 +105,11 @@
 
 		var nilBranch *Block
 		for _, w := range domTree[node.block.ID] {
-			// TODO: Since we handle the false side of OpIsNonNil
-			// correctly, look into rewriting user nil checks into
-			// OpIsNonNil so they can be eliminated also
-
-			// we are about to traverse down the 'ptr is nil' side
-			// of a nilcheck block, so save it for later
+			// We are about to traverse down the 'ptr is nil' side
+			// of a nilcheck block, so save it for later.  This doesn't
+			// remove nil checks on the false side of the OpIsNonNil branch.
+			// This is important otherwise we would remove nil checks that
+			// are not redundant.
 			if node.block.Kind == BlockIf && node.block.Control.Op == OpIsNonNil &&
 				w == node.block.Succs[1] {
 				nilBranch = w
diff --git a/src/cmd/compile/internal/ssa/nilcheck_test.go b/src/cmd/compile/internal/ssa/nilcheck_test.go
index c54f86a..1d048fb 100644
--- a/src/cmd/compile/internal/ssa/nilcheck_test.go
+++ b/src/cmd/compile/internal/ssa/nilcheck_test.go
@@ -342,3 +342,43 @@
 		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")
+		}
+	}
+}
diff --git a/src/cmd/compile/internal/ssa/rewritegeneric.go b/src/cmd/compile/internal/ssa/rewritegeneric.go
index 3a06805..dc6604f 100644
--- a/src/cmd/compile/internal/ssa/rewritegeneric.go
+++ b/src/cmd/compile/internal/ssa/rewritegeneric.go
@@ -478,6 +478,49 @@
 		goto 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:
 		// match: (IData (IMake _ data))
 		// cond:
@@ -961,6 +1004,43 @@
 		goto 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:
 		// match: (Or16 x x)
 		// cond: