cmd/compile: ensure that ssa.Func constant cache is consistent

It was not necessarily consistent before, we were just lucky.

Change-Id: I3a92dc724e0af7b4d810a6a0b7b1d58844eb8f87
Reviewed-on: https://go-review.googlesource.com/c/go/+/251440
Run-TryBot: David Chase <drchase@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
diff --git a/src/cmd/compile/internal/ssa/func.go b/src/cmd/compile/internal/ssa/func.go
index 6718b77..32df0c0 100644
--- a/src/cmd/compile/internal/ssa/func.go
+++ b/src/cmd/compile/internal/ssa/func.go
@@ -257,6 +257,49 @@
 	f.Warnl(f.Entry.Pos, "\t%s\t%s%s\t%s", n, key, value, f.Name)
 }
 
+// unCacheLine removes v from f's constant cache "line" for aux,
+// resets v.InCache when it is found (and removed),
+// and returns whether v was found in that line.
+func (f *Func) unCacheLine(v *Value, aux int64) bool {
+	vv := f.constants[aux]
+	for i, cv := range vv {
+		if v == cv {
+			vv[i] = vv[len(vv)-1]
+			vv[len(vv)-1] = nil
+			f.constants[aux] = vv[0 : len(vv)-1]
+			v.InCache = false
+			return true
+		}
+	}
+	return false
+}
+
+// unCache removes v from f's constant cache.
+func (f *Func) unCache(v *Value) {
+	if v.InCache {
+		aux := v.AuxInt
+		if f.unCacheLine(v, aux) {
+			return
+		}
+		if aux == 0 {
+			switch v.Op {
+			case OpConstNil:
+				aux = constNilMagic
+			case OpConstSlice:
+				aux = constSliceMagic
+			case OpConstString:
+				aux = constEmptyStringMagic
+			case OpConstInterface:
+				aux = constInterfaceMagic
+			}
+			if aux != 0 && f.unCacheLine(v, aux) {
+				return
+			}
+		}
+		f.Fatalf("unCached value %s not found in cache, auxInt=0x%x, adjusted aux=0x%x", v.LongString(), v.AuxInt, aux)
+	}
+}
+
 // freeValue frees a value. It must no longer be referenced or have any args.
 func (f *Func) freeValue(v *Value) {
 	if v.Block == nil {
@@ -270,19 +313,8 @@
 	}
 	// Clear everything but ID (which we reuse).
 	id := v.ID
-
-	// Values with zero arguments and OpOffPtr values might be cached, so remove them there.
-	nArgs := opcodeTable[v.Op].argLen
-	if nArgs == 0 || v.Op == OpOffPtr {
-		vv := f.constants[v.AuxInt]
-		for i, cv := range vv {
-			if v == cv {
-				vv[i] = vv[len(vv)-1]
-				vv[len(vv)-1] = nil
-				f.constants[v.AuxInt] = vv[0 : len(vv)-1]
-				break
-			}
-		}
+	if v.InCache {
+		f.unCache(v)
 	}
 	*v = Value{}
 	v.ID = id
@@ -548,6 +580,7 @@
 		v = f.Entry.NewValue0(src.NoXPos, op, t)
 	}
 	f.constants[c] = append(vv, v)
+	v.InCache = true
 	return v
 }
 
diff --git a/src/cmd/compile/internal/ssa/softfloat.go b/src/cmd/compile/internal/ssa/softfloat.go
index 8db4334..a8a8f83 100644
--- a/src/cmd/compile/internal/ssa/softfloat.go
+++ b/src/cmd/compile/internal/ssa/softfloat.go
@@ -18,6 +18,7 @@
 	for _, b := range f.Blocks {
 		for _, v := range b.Values {
 			if v.Type.IsFloat() {
+				f.unCache(v)
 				switch v.Op {
 				case OpPhi, OpLoad, OpArg:
 					if v.Type.Size() == 4 {
diff --git a/src/cmd/compile/internal/ssa/value.go b/src/cmd/compile/internal/ssa/value.go
index 090745d..6692df7 100644
--- a/src/cmd/compile/internal/ssa/value.go
+++ b/src/cmd/compile/internal/ssa/value.go
@@ -54,6 +54,9 @@
 	// nor a slot on Go stack, and the generation of this value is delayed to its use time.
 	OnWasmStack bool
 
+	// Is this value in the per-function constant cache? If so, remove from cache before changing it or recycling it.
+	InCache bool
+
 	// Storage for the first three args
 	argstorage [3]*Value
 }
@@ -332,6 +335,9 @@
 // of cmd/compile by almost 10%, and slows it down.
 //go:noinline
 func (v *Value) reset(op Op) {
+	if v.InCache {
+		v.Block.Func.unCache(v)
+	}
 	v.Op = op
 	v.resetArgs()
 	v.AuxInt = 0
@@ -342,6 +348,9 @@
 // It modifies v to be (Copy a).
 //go:noinline
 func (v *Value) copyOf(a *Value) {
+	if v.InCache {
+		v.Block.Func.unCache(v)
+	}
 	v.Op = OpCopy
 	v.resetArgs()
 	v.AddArg(a)