From 0e360c4ecdc43033c9a4f94c8b413c0354b98f49 Mon Sep 17 00:00:00 2001 From: ZhouGuangyuan Date: Tue, 18 Aug 2026 17:48:45 +0800 Subject: [PATCH] cl: deduplicate same-line panic PC anchors --- cl/caller_frame_test.go | 28 ++++++++++++++++++++++++++++ cl/compile.go | 9 +++++++++ cl/instr.go | 9 +++++++++ 3 files changed, 46 insertions(+) diff --git a/cl/caller_frame_test.go b/cl/caller_frame_test.go index a7085718c9..6b389f5f29 100644 --- a/cl/caller_frame_test.go +++ b/cl/caller_frame_test.go @@ -4,6 +4,7 @@ package cl import ( + "fmt" "go/ast" "go/importer" "go/parser" @@ -379,6 +380,8 @@ func owner() { defer inspect() defer deferredPanicLeaf() panicLeaf() + repeatedPanicLeaf(nil, 0) + branchPanicLeaf(nil, 0, true) } func panicLeaf() { @@ -393,6 +396,16 @@ func deferredPanicLeaf() { _ = *p } +func repeatedPanicLeaf(p *[1]int, i int) int { +//line repeated_panic_site.go:345 + return p[i] + p[i] +} + +func branchPanicLeaf(p *[1]int, i int, cond bool) int { +//line branch_panic_site.go:456 + if cond { return p[i] }; return p[i] +} + //go:noinline func pinnedPanicSite() { var p *int @@ -416,6 +429,21 @@ func pinnedPanicSite() { t.Fatalf("recover-visible nil dereference is missing panic-site metadata %q:\n%s", want, ir) } } + countPCLine := func(symbol, file string, line int) (count int) { + want := fmt.Sprintf(`!"%s", !"%s", i32 %d`, symbol, file, line) + for _, row := range strings.Split(ir, "\n") { + if strings.Contains(row, `!{i32 1, i64 `) && strings.Contains(row, want) { + count++ + } + } + return count + } + if got := countPCLine("example.com/foo.repeatedPanicLeaf", "repeated_panic_site.go", 345); got != 1 { + t.Fatalf("same-line panic sites in one basic block produced %d metadata records, want 1:\n%s", got, ir) + } + if got := countPCLine("example.com/foo.branchPanicLeaf", "branch_panic_site.go", 456); got != 2 { + t.Fatalf("same-line panic sites in separate basic blocks produced %d metadata records, want 2:\n%s", got, ir) + } if strings.Contains(ir, `!"non_recover_site.go"`) { t.Fatalf("ordinary pinned function unexpectedly received implicit panic-site metadata:\n%s", ir) } diff --git a/cl/compile.go b/cl/compile.go index 10be42368a..67b5d91f7a 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -167,6 +167,11 @@ type context struct { runtimeCallerFuncs map[*ssa.Function]bool panicSiteFuncs map[*ssa.Function]bool pcLineSeq uint64 + // The runtime PC-line table stores file and line, but not column. Keep the + // last emitted position within one SSA basic block so repeated checks for a + // single source line can share an anchor. + lastPCLineFile string + lastPCLineLine int options Options recoverSlots map[*ssa.Alloc]none implicitDeferResults []llssa.Expr @@ -893,6 +898,10 @@ func (p *context) debugParams(b llssa.Builder, f *ssa.Function) { } func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, doModInit bool) llssa.BasicBlock { + // A control-flow edge can enter this block without executing the preceding + // block's anchor, so deduplication must never cross a block boundary. + p.lastPCLineFile = "" + p.lastPCLineLine = 0 oldLocalBlock := p.locality.function.block p.locality.function.block = block defer func() { p.locality.function.block = oldLocalBlock }() diff --git a/cl/instr.go b/cl/instr.go index 033afe39e7..7ad03c48a7 100644 --- a/cl/instr.go +++ b/cl/instr.go @@ -1971,6 +1971,15 @@ func (p *context) emitPCLineLabel(b llssa.Builder, pos token.Pos) { if position.Line <= 0 || position.Filename == "" { return } + // Lookup uses the nearest preceding PC anchor. Within one basic block, + // another anchor for the same runtime-visible file and line cannot change + // the result, even when several implicit panic checks came from one Go + // expression. + if position.Filename == p.lastPCLineFile && position.Line == p.lastPCLineLine { + return + } + p.lastPCLineFile = position.Filename + p.lastPCLineLine = position.Line p.pcLineSeq++ id := pcLineID(p.fn.Name(), p.pcLineSeq) label := pcLineLabelName(id)