]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: remove stores to unread parameters
authorCherry Mui <cherryyz@google.com>
Mon, 22 Sep 2025 14:57:29 +0000 (10:57 -0400)
committerCherry Mui <cherryyz@google.com>
Fri, 3 Oct 2025 19:31:20 +0000 (12:31 -0700)
Currently, we remove stores to local variables that are not read.
We don't do that for arguments. But arguments and locals are
essentially the same. Arguments are passed by value, and are not
expected to be read in the caller's frame. So we can remove the
writes to them as well. One exception is the cgo_unsafe_arg
directive, which makes all the arguments effectively address-taken.
cgo_unsafe_arg implies ABI0, so we just skip ABI0 functions'
arguments.

Cherry-picked from the dev.simd branch. This CL is not
necessarily SIMD specific. Apply early to reduce risk.

Change-Id: I8999fc50da6a87f22c1ec23e9a0c15483b6f7df8
Reviewed-on: https://go-review.googlesource.com/c/go/+/705815
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: David Chase <drchase@google.com>
Reviewed-by: Junyang Shao <shaojunyang@google.com>
Reviewed-on: https://go-review.googlesource.com/c/go/+/708865

src/cmd/compile/internal/ssa/deadstore.go
src/runtime/testdata/testprog/badtraceback.go
test/codegen/stack.go

index 9e67e8339992c7f2da958cde70bbd97db6d007ce..d0adff788c0a4f62164c24e72401061b575b4af6 100644 (file)
@@ -7,6 +7,7 @@ package ssa
 import (
        "cmd/compile/internal/ir"
        "cmd/compile/internal/types"
+       "cmd/internal/obj"
 )
 
 // dse does dead-store elimination on the Function.
@@ -213,7 +214,7 @@ func elimDeadAutosGeneric(f *Func) {
                case OpAddr, OpLocalAddr:
                        // Propagate the address if it points to an auto.
                        n, ok := v.Aux.(*ir.Name)
-                       if !ok || n.Class != ir.PAUTO {
+                       if !ok || (n.Class != ir.PAUTO && !isABIInternalParam(f, n)) {
                                return
                        }
                        if addr[v] == nil {
@@ -224,7 +225,7 @@ func elimDeadAutosGeneric(f *Func) {
                case OpVarDef:
                        // v should be eliminated if we eliminate the auto.
                        n, ok := v.Aux.(*ir.Name)
-                       if !ok || n.Class != ir.PAUTO {
+                       if !ok || (n.Class != ir.PAUTO && !isABIInternalParam(f, n)) {
                                return
                        }
                        if elim[v] == nil {
@@ -240,7 +241,7 @@ func elimDeadAutosGeneric(f *Func) {
                        // may not be used by the inline code, but will be used by
                        // panic processing).
                        n, ok := v.Aux.(*ir.Name)
-                       if !ok || n.Class != ir.PAUTO {
+                       if !ok || (n.Class != ir.PAUTO && !isABIInternalParam(f, n)) {
                                return
                        }
                        if !used.Has(n) {
@@ -373,7 +374,7 @@ func elimUnreadAutos(f *Func) {
                        if !ok {
                                continue
                        }
-                       if n.Class != ir.PAUTO {
+                       if n.Class != ir.PAUTO && !isABIInternalParam(f, n) {
                                continue
                        }
 
@@ -413,3 +414,16 @@ func elimUnreadAutos(f *Func) {
                store.Op = OpCopy
        }
 }
+
+// isABIInternalParam returns whether n is a parameter of an ABIInternal
+// function. For dead store elimination, we can treat parameters the same
+// way as autos. Storing to a parameter can be removed if it is not read
+// or address-taken.
+//
+// We check ABI here because for a cgo_unsafe_arg function (which is ABI0),
+// all the args are effectively address-taken, but not necessarily have
+// an Addr or LocalAddr op. We could probably just check for cgo_unsafe_arg,
+// but ABIInternal is mostly what matters.
+func isABIInternalParam(f *Func, n *ir.Name) bool {
+       return n.Class == ir.PPARAM && f.ABISelf.Which() == obj.ABIInternal
+}
index 09aa2b877ecf5a108812479ee0cde2c525d1da9e..455118a54371d79fbf0cb4abf5738ba30e9882c7 100644 (file)
@@ -44,6 +44,8 @@ func badLR2(arg int) {
        lrPtr := (*uintptr)(unsafe.Pointer(uintptr(unsafe.Pointer(&arg)) - lrOff))
        *lrPtr = 0xbad
 
+       runtime.KeepAlive(lrPtr) // prevent dead store elimination
+
        // Print a backtrace. This should include diagnostics for the
        // bad return PC and a hex dump.
        panic("backtrace")
index 4e45d68f3816bd416a1d93a3cea77c30ef8575ca..59284ae88862a33ccda990b4e321c83c2526afe9 100644 (file)
@@ -168,3 +168,9 @@ func getp1() *[4]int {
 func getp2() *[4]int {
        return nil
 }
+
+// Store to an argument without read can be removed.
+func storeArg(a [2]int) {
+       // amd64:-`MOVQ\t\$123,.*\.a\+\d+\(SP\)`
+       a[1] = 123
+}