cmd/compile: don't require Heapaddr for heap vars in dead code DWARF generation's createConservativeVar asserts that every variable marked ir.EscHeap has a non-nil Heapaddr. This assumption only holds for reachable declarations. Escape analysis runs before SSA generation and is not reachability-aware, so it can mark a variable declared in dead code as heap-escaped. During SSA generation the ODCL for such a variable lands in an unreachable block, where stmt() returns early and never calls newHeapaddr. The variable is therefore left with Esc()==EscHeap but Heapaddr==nil, a legitimate state: the allocation was never generated, so there is no heap address to point at. Only build the heap-deref location list when Heapaddr is present, and otherwise emit a conservative variable with no location list, as was done before CL 684377. A reachable heap-escaped variable always gets a Heapaddr via newHeapaddr, so a nil Heapaddr uniquely identifies the dead-code case. Fixes #80097. Change-Id: I91e475d0256c0f5e4d2dfabcacff92904469087a Reviewed-on: https://go-review.googlesource.com/c/go/+/792960 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Keith Randall <khr@golang.org>
diff --git a/src/cmd/compile/internal/dwarfgen/dwarf.go b/src/cmd/compile/internal/dwarfgen/dwarf.go index 79bf550..3f654fa 100644 --- a/src/cmd/compile/internal/dwarfgen/dwarf.go +++ b/src/cmd/compile/internal/dwarfgen/dwarf.go
@@ -360,10 +360,14 @@ DictIndex: n.DictIndex, ClosureOffset: closureOffset(n, closureVars), } - if n.Esc() == ir.EscHeap { - if n.Heapaddr == nil { - base.Fatalf("invalid heap allocated var without Heapaddr") - } + if n.Esc() == ir.EscHeap && n.Heapaddr != nil { + // The variable was promoted to the heap and has a known heap + // address, so describe its location by dereferencing the pointer + // stored at its stack offset. A heap-escaped variable may have no + // Heapaddr if it was declared in unreachable code: escape analysis + // marks it as heap-allocated, but SSA generation skips the dead + // declaration and never allocates the address. In that case fall + // through and emit a conservative variable with no location list. debug := fn.DebugInfo.(*ssa.FuncDebug) list := createHeapDerefLocationList(n, debug.EntryID) dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
diff --git a/test/fixedbugs/issue80097.go b/test/fixedbugs/issue80097.go new file mode 100644 index 0000000..314a84f --- /dev/null +++ b/test/fixedbugs/issue80097.go
@@ -0,0 +1,23 @@ +// compile + +// 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. + +// Issue 80097: ICE "invalid heap allocated var without Heapaddr" +// when a heap-escaping variable is declared in unreachable code. +// Escape analysis marks the variable as heap-allocated, but because +// the declaration is dead, SSA generation never assigns it a heap +// address. DWARF generation must tolerate this state. + +package p + +var foo = func() int { +label: + goto label + x := [1024 * 64]*[2]*int{} + if x != x { + _ = x + } + return 1 +}()