Skip to content

Commit cce09ba

Browse files
committed
Merge branch 'master' of github.com:kevin-valerio/go-arithmetic-panik
2 parents dc26f67 + 175c321 commit cce09ba

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+784
-226
lines changed

src/cmd/compile/internal/dwarfgen/dwarf.go

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,6 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
248248
if n.Class == ir.PPARAM || n.Class == ir.PPARAMOUT {
249249
tag = dwarf.DW_TAG_formal_parameter
250250
}
251-
if n.Esc() == ir.EscHeap {
252-
// The variable in question has been promoted to the heap.
253-
// Its address is in n.Heapaddr.
254-
// TODO(thanm): generate a better location expression
255-
}
256251
inlIndex := 0
257252
if base.Flag.GenDwarfInl > 1 {
258253
if n.InlFormal() || n.InlLocal() {
@@ -263,7 +258,7 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
263258
}
264259
}
265260
declpos := base.Ctxt.InnermostPos(n.Pos())
266-
vars = append(vars, &dwarf.Var{
261+
dvar := &dwarf.Var{
267262
Name: n.Sym().Name,
268263
IsReturnValue: isReturnValue,
269264
Tag: tag,
@@ -277,8 +272,19 @@ func createDwarfVars(fnsym *obj.LSym, complexOK bool, fn *ir.Func, apDecls []*ir
277272
ChildIndex: -1,
278273
DictIndex: n.DictIndex,
279274
ClosureOffset: closureOffset(n, closureVars),
280-
})
281-
// Record go type of to insure that it gets emitted by the linker.
275+
}
276+
if n.Esc() == ir.EscHeap {
277+
if n.Heapaddr == nil {
278+
base.Fatalf("invalid heap allocated var without Heapaddr")
279+
}
280+
debug := fn.DebugInfo.(*ssa.FuncDebug)
281+
list := createHeapDerefLocationList(n, fnsym, debug.EntryID, ssa.FuncEnd.ID)
282+
dvar.PutLocationList = func(listSym, startPC dwarf.Sym) {
283+
debug.PutLocationList(list, base.Ctxt, listSym.(*obj.LSym), startPC.(*obj.LSym))
284+
}
285+
}
286+
vars = append(vars, dvar)
287+
// Record go type to ensure that it gets emitted by the linker.
282288
fnsym.Func().RecordAutoType(reflectdata.TypeLinksym(n.Type()))
283289
}
284290

@@ -550,6 +556,29 @@ func createComplexVar(fnsym *obj.LSym, fn *ir.Func, varID ssa.VarID, closureVars
550556
return dvar
551557
}
552558

559+
// createHeapDerefLocationList creates a location list for a heap-escaped variable
560+
// that describes "dereference pointer at stack offset"
561+
func createHeapDerefLocationList(n *ir.Name, fnsym *obj.LSym, entryID, prologEndID ssa.ID) []byte {
562+
// Get the stack offset where the heap pointer is stored
563+
heapPtrOffset := n.Heapaddr.FrameOffset()
564+
if base.Ctxt.Arch.FixedFrameSize == 0 {
565+
heapPtrOffset -= int64(types.PtrSize)
566+
}
567+
if buildcfg.FramePointerEnabled {
568+
heapPtrOffset -= int64(types.PtrSize)
569+
}
570+
571+
// Create a location expression: DW_OP_fbreg <offset> DW_OP_deref
572+
var locExpr []byte
573+
var sizeIdx int
574+
locExpr, sizeIdx = ssa.SetupLocList(base.Ctxt, entryID, locExpr, ssa.BlockStart.ID, ssa.FuncEnd.ID)
575+
locExpr = append(locExpr, dwarf.DW_OP_fbreg)
576+
locExpr = dwarf.AppendSleb128(locExpr, heapPtrOffset)
577+
locExpr = append(locExpr, dwarf.DW_OP_deref)
578+
base.Ctxt.Arch.ByteOrder.PutUint16(locExpr[sizeIdx:], uint16(len(locExpr)-sizeIdx-2))
579+
return locExpr
580+
}
581+
553582
// RecordFlags records the specified command-line flags to be placed
554583
// in the DWARF info.
555584
func RecordFlags(flags ...string) {

src/cmd/compile/internal/escape/escape.go

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -122,17 +122,24 @@ type escape struct {
122122
}
123123

124124
func Funcs(all []*ir.Func) {
125-
ir.VisitFuncsBottomUp(all, Batch)
125+
// Make a cache of ir.ReassignOracles. The cache is lazily populated.
126+
// TODO(thepudds): consider adding a field on ir.Func instead. We might also be able
127+
// to use that field elsewhere, like in walk. See discussion in https://go.dev/cl/688075.
128+
reassignOracles := make(map[*ir.Func]*ir.ReassignOracle)
129+
130+
ir.VisitFuncsBottomUp(all, func(list []*ir.Func, recursive bool) {
131+
Batch(list, reassignOracles)
132+
})
126133
}
127134

128135
// Batch performs escape analysis on a minimal batch of
129136
// functions.
130-
func Batch(fns []*ir.Func, recursive bool) {
137+
func Batch(fns []*ir.Func, reassignOracles map[*ir.Func]*ir.ReassignOracle) {
131138
var b batch
132139
b.heapLoc.attrs = attrEscapes | attrPersists | attrMutates | attrCalls
133140
b.mutatorLoc.attrs = attrMutates
134141
b.calleeLoc.attrs = attrCalls
135-
b.reassignOracles = make(map[*ir.Func]*ir.ReassignOracle)
142+
b.reassignOracles = reassignOracles
136143

137144
// Construct data-flow graph from syntax trees.
138145
for _, fn := range fns {
@@ -531,19 +538,9 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
531538
if n == nil || fn == nil {
532539
return
533540
}
534-
if n.Op() != ir.OMAKESLICE && n.Op() != ir.OCONVIFACE {
535-
return
536-
}
537541

538-
// Look up a cached ReassignOracle for the function, lazily computing one if needed.
539-
ro := b.reassignOracle(fn)
540-
if ro == nil {
541-
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
542-
}
543-
544-
assignTemp := func(n ir.Node, init *ir.Nodes) {
542+
assignTemp := func(pos src.XPos, n ir.Node, init *ir.Nodes) {
545543
// Preserve any side effects of n by assigning it to an otherwise unused temp.
546-
pos := n.Pos()
547544
tmp := typecheck.TempAt(pos, fn, n.Type())
548545
init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
549546
init.Append(typecheck.Stmt(ir.NewAssignStmt(pos, tmp, n)))
@@ -561,6 +558,11 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
561558
}
562559

563560
if (*r).Op() != ir.OLITERAL {
561+
// Look up a cached ReassignOracle for the function, lazily computing one if needed.
562+
ro := b.reassignOracle(fn)
563+
if ro == nil {
564+
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
565+
}
564566
if s := ro.StaticValue(*r); s.Op() == ir.OLITERAL {
565567
lit, ok := s.(*ir.BasicLit)
566568
if !ok || lit.Val().Kind() != constant.Int {
@@ -572,8 +574,8 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
572574
return
573575
}
574576
// Preserve any side effects of the original expression, then replace it.
575-
assignTemp(*r, n.PtrInit())
576-
*r = lit
577+
assignTemp(n.Pos(), *r, n.PtrInit())
578+
*r = ir.NewBasicLit(n.Pos(), (*r).Type(), lit.Val())
577579
}
578580
}
579581
}
@@ -582,6 +584,12 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
582584
// a literal to avoid heap allocating the underlying interface value.
583585
conv := n.(*ir.ConvExpr)
584586
if conv.X.Op() != ir.OLITERAL && !conv.X.Type().IsInterface() {
587+
// TODO(thepudds): likely could avoid some work by tightening the check of conv.X's type.
588+
// Look up a cached ReassignOracle for the function, lazily computing one if needed.
589+
ro := b.reassignOracle(fn)
590+
if ro == nil {
591+
base.Fatalf("no ReassignOracle for function %v with closure parent %v", fn, fn.ClosureParent)
592+
}
585593
v := ro.StaticValue(conv.X)
586594
if v != nil && v.Op() == ir.OLITERAL && ir.ValidTypeForConst(conv.X.Type(), v.Val()) {
587595
if !base.LiteralAllocHash.MatchPos(n.Pos(), nil) {
@@ -592,9 +600,9 @@ func (b *batch) rewriteWithLiterals(n ir.Node, fn *ir.Func) {
592600
base.WarnfAt(n.Pos(), "rewriting OCONVIFACE value from %v (%v) to %v (%v)", conv.X, conv.X.Type(), v, v.Type())
593601
}
594602
// Preserve any side effects of the original expression, then replace it.
595-
assignTemp(conv.X, conv.PtrInit())
603+
assignTemp(conv.Pos(), conv.X, conv.PtrInit())
596604
v := v.(*ir.BasicLit)
597-
conv.X = ir.NewBasicLit(conv.X.Pos(), conv.X.Type(), v.Val())
605+
conv.X = ir.NewBasicLit(conv.Pos(), conv.X.Type(), v.Val())
598606
typecheck.Expr(conv)
599607
}
600608
}

src/cmd/compile/internal/ssa/debug.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ type FuncDebug struct {
4141
RegOutputParams []*ir.Name
4242
// Variable declarations that were removed during optimization
4343
OptDcl []*ir.Name
44+
// The ssa.Func.EntryID value, used to build location lists for
45+
// return values promoted to heap in later DWARF generation.
46+
EntryID ID
4447

4548
// Filled in by the user. Translates Block and Value ID to PC.
4649
//
@@ -1645,13 +1648,13 @@ func readPtr(ctxt *obj.Link, buf []byte) uint64 {
16451648

16461649
}
16471650

1648-
// setupLocList creates the initial portion of a location list for a
1651+
// SetupLocList creates the initial portion of a location list for a
16491652
// user variable. It emits the encoded start/end of the range and a
16501653
// placeholder for the size. Return value is the new list plus the
16511654
// slot in the list holding the size (to be updated later).
1652-
func setupLocList(ctxt *obj.Link, f *Func, list []byte, st, en ID) ([]byte, int) {
1653-
start, startOK := encodeValue(ctxt, f.Entry.ID, st)
1654-
end, endOK := encodeValue(ctxt, f.Entry.ID, en)
1655+
func SetupLocList(ctxt *obj.Link, entryID ID, list []byte, st, en ID) ([]byte, int) {
1656+
start, startOK := encodeValue(ctxt, entryID, st)
1657+
end, endOK := encodeValue(ctxt, entryID, en)
16551658
if !startOK || !endOK {
16561659
// This could happen if someone writes a function that uses
16571660
// >65K values on a 32-bit platform. Hopefully a degraded debugging
@@ -1800,7 +1803,6 @@ func isNamedRegParam(p abi.ABIParamAssignment) bool {
18001803
// appropriate for the ".closureptr" compiler-synthesized variable
18011804
// needed by the debugger for range func bodies.
18021805
func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, stackOffset func(LocalSlot) int32, rval *FuncDebug) {
1803-
18041806
needCloCtx := f.CloSlot != nil
18051807
pri := f.ABISelf.ABIAnalyzeFuncType(f.Type)
18061808

@@ -1911,7 +1913,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
19111913
// Param is arriving in one or more registers. We need a 2-element
19121914
// location expression for it. First entry in location list
19131915
// will correspond to lifetime in input registers.
1914-
list, sizeIdx := setupLocList(ctxt, f, rval.LocationLists[pidx],
1916+
list, sizeIdx := SetupLocList(ctxt, f.Entry.ID, rval.LocationLists[pidx],
19151917
BlockStart.ID, afterPrologVal)
19161918
if list == nil {
19171919
pidx++
@@ -1961,7 +1963,7 @@ func BuildFuncDebugNoOptimized(ctxt *obj.Link, f *Func, loggingEnabled bool, sta
19611963

19621964
// Second entry in the location list will be the stack home
19631965
// of the param, once it has been spilled. Emit that now.
1964-
list, sizeIdx = setupLocList(ctxt, f, list,
1966+
list, sizeIdx = SetupLocList(ctxt, f.Entry.ID, list,
19651967
afterPrologVal, FuncEnd.ID)
19661968
if list == nil {
19671969
pidx++

src/cmd/compile/internal/ssa/debug_lines_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,34 @@ func TestDebugLines_53456(t *testing.T) {
115115
testDebugLinesDefault(t, "-N -l", "b53456.go", "(*T).Inc", []int{15, 16, 17, 18}, true)
116116
}
117117

118+
func TestDebugLines_74576(t *testing.T) {
119+
unixOnly(t)
120+
121+
switch testGoArch() {
122+
default:
123+
// Failed on linux/riscv64 (issue 74669), but conservatively
124+
// skip many architectures like several other tests here.
125+
t.Skip("skipped for many architectures")
126+
127+
case "arm64", "amd64", "loong64":
128+
tests := []struct {
129+
file string
130+
wantStmts []int
131+
}{
132+
{"i74576a.go", []int{12, 13, 13, 14}},
133+
{"i74576b.go", []int{12, 13, 13, 14}},
134+
{"i74576c.go", []int{12, 13, 13, 14}},
135+
}
136+
t.Parallel()
137+
for _, test := range tests {
138+
t.Run(test.file, func(t *testing.T) {
139+
t.Parallel()
140+
testDebugLines(t, "-N -l", test.file, "main", test.wantStmts, false)
141+
})
142+
}
143+
}
144+
}
145+
118146
func compileAndDump(t *testing.T, file, function, moreGCFlags string) []byte {
119147
testenv.MustHaveGoBuild(t)
120148

@@ -223,6 +251,9 @@ func testInlineStack(t *testing.T, file, function string, wantStacks [][]int) {
223251
// then verifies that the statement-marked lines in that file are the same as those in wantStmts
224252
// These files must all be short because this is super-fragile.
225253
// "go build" is run in a temporary directory that is normally deleted, unless -test.v
254+
//
255+
// TODO: the tests calling this are somewhat expensive; perhaps more tests can be marked t.Parallel,
256+
// or perhaps the mechanism here can be made more efficient.
226257
func testDebugLines(t *testing.T, gcflags, file, function string, wantStmts []int, ignoreRepeats bool) {
227258
dumpBytes := compileAndDump(t, file, function, gcflags)
228259
dump := bufio.NewScanner(bytes.NewReader(dumpBytes))
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"runtime"
9+
)
10+
11+
func main() {
12+
a := 1
13+
runtime.Breakpoint()
14+
sink = a
15+
}
16+
17+
var sink any
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"runtime"
9+
)
10+
11+
func main() {
12+
a := 1
13+
runtime.Breakpoint()
14+
_ = make([]int, a)
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2025 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package main
6+
7+
import (
8+
"runtime"
9+
)
10+
11+
func main() {
12+
s := S{1, 1}
13+
runtime.Breakpoint()
14+
sink = s
15+
}
16+
17+
type S struct{ a, b uint64 }
18+
19+
var sink any

src/cmd/compile/internal/ssagen/ssa.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7611,6 +7611,9 @@ func genssa(f *ssa.Func, pp *objw.Progs) {
76117611
if base.Ctxt.Flag_locationlists {
76127612
var debugInfo *ssa.FuncDebug
76137613
debugInfo = e.curfn.DebugInfo.(*ssa.FuncDebug)
7614+
// Save off entry ID in case we need it later for DWARF generation
7615+
// for return values promoted to the heap.
7616+
debugInfo.EntryID = f.Entry.ID
76147617
if e.curfn.ABI == obj.ABIInternal && base.Flag.N != 0 {
76157618
ssa.BuildFuncDebugNoOptimized(base.Ctxt, f, base.Debug.LocationLists > 1, StackOffset, debugInfo)
76167619
} else {

src/cmd/compile/internal/types2/builtins.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,22 +91,25 @@ func (check *Checker) builtin(x *operand, call *syntax.CallExpr, id builtinId) (
9191
// to type []byte with a second argument of string type followed by ... .
9292
// This form appends the bytes of the string."
9393

94-
// get special case out of the way
94+
// Handle append(bytes, y...) special case, where
95+
// the type set of y is {string} or {string, []byte}.
9596
var sig *Signature
9697
if nargs == 2 && hasDots(call) {
9798
if ok, _ := x.assignableTo(check, NewSlice(universeByte), nil); ok {
9899
y := args[1]
100+
hasString := false
99101
typeset(y.typ, func(_, u Type) bool {
100102
if s, _ := u.(*Slice); s != nil && Identical(s.elem, universeByte) {
101103
return true
102104
}
103105
if isString(u) {
106+
hasString = true
104107
return true
105108
}
106109
y = nil
107110
return false
108111
})
109-
if y != nil {
112+
if y != nil && hasString {
110113
// setting the signature also signals that we're done
111114
sig = makeSig(x.typ, x.typ, y.typ)
112115
sig.variadic = true

src/cmd/go/internal/base/base.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,11 @@ var Go = &Command{
6262
// Lookup returns the subcommand with the given name, if any.
6363
// Otherwise it returns nil.
6464
//
65-
// Lookup ignores subcommands that have len(c.Commands) == 0 and c.Run == nil.
65+
// Lookup ignores any subcommand `sub` that has len(sub.Commands) == 0 and sub.Run == nil.
6666
// Such subcommands are only for use as arguments to "help".
6767
func (c *Command) Lookup(name string) *Command {
6868
for _, sub := range c.Commands {
69-
if sub.Name() == name && (len(c.Commands) > 0 || c.Runnable()) {
69+
if sub.Name() == name && (len(sub.Commands) > 0 || sub.Runnable()) {
7070
return sub
7171
}
7272
}

0 commit comments

Comments
 (0)