cmd/compile/internal/ssa: check used const op sizes
SSA constants carry both an opcode width and a result type. A value like
Const32 <int> on 64-bit targets can be interpreted inconsistently by
later passes, allowing forms such as And64 x (Const32 [-1]) to escape
SSA checking.
Add a checkFunc invariant that rejects used constant values whose result
type is wider than the constant opcode can represent. Add a unit test for
the mismatched Const32 <int> case.
For #79877
diff --git a/src/cmd/compile/internal/ssa/check.go b/src/cmd/compile/internal/ssa/check.go
index 9396f8d..3cfefd1 100644
--- a/src/cmd/compile/internal/ssa/check.go
+++ b/src/cmd/compile/internal/ssa/check.go
@@ -282,6 +282,14 @@
f.Fatalf("unexpected floating-point type %v", v.LongString())
}
+ // Check that used constant values are not wider than their
+ // constant op. For example, Const32 <int> is invalid on
+ // 64-bit architectures because later passes may interpret
+ // it as either a 32-bit constant or a 64-bit int.
+ if constSize := constOpSize(v.Op); constSize != 0 && v.Uses != 0 && v.Type.Size() > constSize {
+ f.Fatalf("bad type size for %s: want at most %d bytes, have %s", v.LongString(), constSize, v.Type.String())
+ }
+
// Check types.
// TODO: more type checks?
switch c := f.Config; v.Op {
@@ -510,6 +518,21 @@
memCheck(f)
}
+func constOpSize(op Op) int64 {
+ switch op {
+ case OpConst8:
+ return 1
+ case OpConst16:
+ return 2
+ case OpConst32, OpConst32F:
+ return 4
+ case OpConst64, OpConst64F:
+ return 8
+ default:
+ return 0
+ }
+}
+
func memCheck(f *Func) {
// Check that if a tuple has a memory type, it is second.
for _, b := range f.Blocks {
diff --git a/src/cmd/compile/internal/ssa/check_test.go b/src/cmd/compile/internal/ssa/check_test.go
new file mode 100644
index 0000000..69069267
--- /dev/null
+++ b/src/cmd/compile/internal/ssa/check_test.go
@@ -0,0 +1,52 @@
+// Copyright 2026 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 ssa
+
+import (
+ "cmd/compile/internal/types"
+ "cmd/internal/src"
+ "fmt"
+ "strings"
+ "testing"
+)
+
+type panicFrontend struct {
+ TestFrontend
+}
+
+func (d panicFrontend) Fatalf(_ src.XPos, msg string, args ...any) {
+ panic(fmt.Sprintf(msg, args...))
+}
+
+func TestCheckConstOpTypeSize(t *testing.T) {
+ c := testConfig(t)
+ c.fe = panicFrontend{c.Frontend().(TestFrontend)}
+
+ fun := c.Fun("entry",
+ Bloc("entry",
+ Valu("x", OpConst64, c.config.Types.Int, 1<<34, nil),
+ Valu("mask", OpConst32, c.config.Types.Int, -1, nil),
+ Valu("and", OpAnd64, c.config.Types.Int, 0, nil, "x", "mask"),
+ Valu("mem", OpInitMem, types.TypeMem, 0, nil),
+ Exit("mem")))
+
+ err := checkFuncError(fun.f)
+ if err == "" {
+ t.Fatal("expected checkFunc to fail")
+ }
+ if !strings.Contains(err, "bad type size") || !strings.Contains(err, "Const32 <int>") {
+ t.Fatalf("checkFunc failed with unexpected error: %s", err)
+ }
+}
+
+func checkFuncError(f *Func) (err string) {
+ defer func() {
+ if r := recover(); r != nil {
+ err = fmt.Sprint(r)
+ }
+ }()
+ checkFunc(f)
+ return ""
+}
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
We want a complete solution that fails on absolutely all generic ops, not just consts.
In other words you also need to handle for example `(And64 x (And32 y z))`.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
We want a complete solution that fails on absolutely all generic ops, not just consts.
In other words you also need to handle for example `(And64 x (And32 y z))`.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |