Skip to content

cl: preserve nil checks for unused dereferences - #2316

Open
cpunion wants to merge 4 commits into
xgo-dev:mainfrom
cpunion:codex/unused-nil-deref-load-only-20260813
Open

cl: preserve nil checks for unused dereferences#2316
cpunion wants to merge 4 commits into
xgo-dev:mainfrom
cpunion:codex/unused-nil-deref-load-only-20260813

Conversation

@cpunion

@cpunion cpunion commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • preserve the Go-required nil panic when an unused dereference would otherwise be discarded before LLVM emits a load
  • apply the same rule to direct pointer dereferences and pointers loaded from aggregate addresses
  • remove the GOROOT xfail for fixedbugs/issue38496.go

Implementation

When a Go SSA dereference has no non-debug referrers, LLGo no longer emits an unused load and relies on LLVM to retain it. It evaluates the pointer expression, records the panic location, checks any dereference base, and emits AssertNilDeref directly.

The existing range-over-array exception remains unchanged because that synthetic unused dereference does not represent a source-level dereference that must panic.

This PR intentionally does not implement the broader explicit nil-check strategy required by derived-address and large-offset cases in nilptr2.go and nilptr.go. Those cases are classified separately by #2313. It also contains no nil-check liveness optimization, receiver analysis, or CI change.

Golden changes

Only two existing LLVM snapshots change:

  • _testdata/varinit: remove an unused global scalar load
  • _testrt/cvar: remove two unused aggregate C-variable loads

No additional golden updates are required on the latest main.

Validation

  • go test ./cl -run '^TestUnusedDerefEmitsNilGuard$' -count=1
  • go test ./cl -run '^TestRunAndTestFromTestdata$/^varinit$' -count=1
  • go test ./cl -run '^TestRunAndTestFromTestrt$/^cvar$' -count=1
  • fresh LLGo build: LLGO_BUILD_CACHE=off llgo test -run '^TestUnusedNilDerefOperationsPanic$' ./test/go
  • GOROOT fixedbugs/issue38496.go passed on Darwin/arm64 with Go 1.26.5
  • full GOROOT workflow: run 31699512427
  • targeted coverage run confirms every changed cl/compile.go statement block is hit, including the builtin-referenced effectful-array path
  • git diff --check

@cpunion cpunion added the go-test-compat Go standard-library and GOROOT test compatibility label Aug 13, 2026

@fennoai fennoai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review summary

This is a clean, well-motivated correctness fix. Consolidating the len(refs) == 0 unused-dereference handling into a single unconditional path (evaluate the pointer, record the panic location, assertNilDerefBase, AssertNilDeref) is a strict superset of the removed large-non-pointer and slice-to-array branches, and it also closes the previously-uncovered plain *p gap where an unused load could be eliminated by LLVM without emitting the required Go nil panic. The skipUnusedArrayDeref (range-over-array) exception is correctly preserved, the removed t.RawType() != nil guard was safe to drop (the checks operate on the pointer operand v.X, not on v's result type), the new block returns correctly, and the golden/xfail updates are consistent with loads now being elided in favor of explicit nil checks. Regression coverage is added at both the IR level (TestUnusedDerefEmitsNilGuard) and runtime level (TestUnusedNilDerefOperationsPanic).

Two minor, non-blocking observations are left inline. No safety, performance, or correctness regressions were found.

Comment thread cl/compile.go
Comment thread cl/compile.go Outdated
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Aug 13, 2026

Copy link
Copy Markdown

LLGo baseline benchmarks

aaf28093643b | workflow run | long-term charts

Program measurements

Platform Workload File size vs base Build vs base Run vs base
Linux cprintf 18656 B +0.0% 289.519 ms +2.5% (worse) 1.283 ms +0.8% (worse)
Linux fmtprintf 1885912 B +0.0% (worse) 2.669 s +1.1% (worse) 3.225 ms -3.3% (better)
Linux println 68480 B +0.0% 293.916 ms +1.7% (worse) 1.631 ms +3.8% (worse)
macOS cprintf 84672 B +0.0% 272.704 ms -39.6% (better) 2.260 ms -26.7% (better)
macOS fmtprintf 1890016 B +0.0% 1.940 s -20.0% (better) 10.608 ms -2.8% (better)
macOS println 121216 B +0.0% 274.782 ms -13.5% (better) 3.156 ms -1.4% (better)
Core language and compiler benchmarks
Platform Benchmark ns/op vs base
Linux BenchmarkLookupPCRandom 13.400 ns/op +1.3% (worse)
Linux BenchmarkMergeCompilerFlags 150.600 ns/op +0.0%
Linux BenchmarkMergeLinkerFlags 94.680 ns/op +0.1% (worse)
Linux BenchmarkChannelBuffered 33.680 ns/op -1.0% (better)
Linux BenchmarkChannelHandoff 26537 ns/op -0.3% (better)
Linux BenchmarkDefer 47.820 ns/op +7.0% (worse)
Linux BenchmarkDirectCall 1.557 ns/op -0.1% (better)
Linux BenchmarkGlobalRead 1.556 ns/op +0.0%
Linux BenchmarkGlobalWrite 2.493 ns/op +0.3% (worse)
Linux BenchmarkGoroutine 32148 ns/op -23.0% (better)
Linux BenchmarkInterfaceCall 7.783 ns/op -0.1% (better)
Linux BenchmarkRuntimeGetG 1.868 ns/op -14.4% (better)
macOS BenchmarkLookupPCRandom 10.630 ns/op -10.6% (better)
macOS BenchmarkMergeCompilerFlags 96.730 ns/op -21.0% (better)
macOS BenchmarkMergeLinkerFlags 62.630 ns/op -12.8% (better)
macOS BenchmarkChannelBuffered 20.530 ns/op +0.4% (worse)
macOS BenchmarkChannelHandoff 6523 ns/op -0.2% (better)
macOS BenchmarkDefer 24.630 ns/op +0.4% (worse)
macOS BenchmarkDirectCall 0.941 ns/op -0.1% (better)
macOS BenchmarkGlobalRead 0.947 ns/op +0.4% (worse)
macOS BenchmarkGlobalWrite 0.952 ns/op -3.7% (better)
macOS BenchmarkGoroutine 27694 ns/op -11.3% (better)
macOS BenchmarkInterfaceCall 4.077 ns/op +0.0%
macOS BenchmarkRuntimeGetG 1.888 ns/op -0.5% (better)

Compared with 7b0e00a8b728 measured in the same runner job.

@cpunion
cpunion force-pushed the codex/unused-nil-deref-load-only-20260813 branch from 6e5d94c to dd4eb50 Compare August 13, 2026 22:01
@cpunion
cpunion force-pushed the codex/unused-nil-deref-load-only-20260813 branch from dd4eb50 to aaf2809 Compare August 14, 2026 02:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

go-test-compat Go standard-library and GOROOT test compatibility

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant