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
+}()