ssa: isolate index range panic path - #2344
Open
visualfc wants to merge 2 commits into
Open
Conversation
visualfc
force-pushed
the
codex/check-index-range-fast-path
branch
from
August 17, 2026 02:47
49a9eab to
d3fa29e
Compare
LLGo baseline benchmarks
Program measurements
Core language and compiler benchmarks
Compared with |
Contributor
There was a problem hiding this comment.
Review: isolate CheckIndexRange in an unreachable failing branch
The core change in ssa/datastruct.go swaps the always-emitted InlineCall(CheckIndexRange, check, ...) for a hoisted IfThen(check, { Call(CheckIndexRange, true, ...); Unreachable() }). This is correct and a genuine fast-path win:
- Semantics preserved — the panic predicate (
check= signedidx < 0OR unsignedidx >= max) is unchanged. The branch is taken on exactly the same out-of-bounds set as before, and inside itCheckIndexRange(true, ...)always panics, so no out-of-bounds index can slip through. Unreachableis sound —panicBoundsnever returns, andIfThen(ssa/stmt_builder.go:230) detects theunreachableterminator and correctly suppresses the jump to the merge block, so no dead fallthrough is emitted.- Performance —
InlineCalldoes not actually inline (it'sb.Callwith a TODO), so the old code emitted an unconditional runtime call on every indexed access. The new form keeps the call off the in-bounds path and gives LLVM a clean cold/no-return branch to sink. - The updated test assertion accurately pins the intended
br/call/unreachableIR shape.
All findings below are minor/advisory; none are blocking.
visualfc
force-pushed
the
codex/check-index-range-fast-path
branch
from
August 17, 2026 03:47
d3fa29e to
7d6fc56
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
visualfc
force-pushed
the
codex/check-index-range-fast-path
branch
from
August 17, 2026 04:41
7d6fc56 to
57aabe2
Compare
visualfc
force-pushed
the
codex/check-index-range-fast-path
branch
2 times, most recently
from
August 17, 2026 07:44
65383fd to
64b5f53
Compare
visualfc
force-pushed
the
codex/check-index-range-fast-path
branch
from
August 17, 2026 11:46
64b5f53 to
76aaf3e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Move index bounds panics off the normal execution path and lower them through dedicated signed and unsigned runtime
helpers.
Previously, every checked index operation called:
even when the index was valid. The helper carried a dynamic condition and a signedness flag into the runtime.
This change branches on the bounds-check result in generated code and calls a runtime panic helper only from the
failure path:
func PanicIndex(x int, y int)
func PanicIndexU(x uint, y int)
This matches Go's runtime.panicIndex / runtime.panicIndexU lowering model.
Motivation
The previous lowering kept a runtime call on the successful index path:
%out_of_range = ...
call void @runtime.CheckIndexRange(
i1 %out_of_range,
i64 %index,
i1 %signed,
i64 %length
)
The runtime call received information that was already known by the compiler:
This unnecessarily complicated the runtime interface and the generated fast path.
Implementation
Index lowering now:
Signed indexes are lowered to:
%out_of_range = ...
br i1 %out_of_range, label %panic, label %continue
panic:
call void @runtime.PanicIndex(i64 %index, i64 %length)
br label %continue
Unsigned indexes use:
call void @runtime.PanicIndexU(i64 %index, i64 %length)
The actual argument width follows the target architecture through int and uint.
Runtime Changes
Remove the obsolete runtime helper:
func CheckIndexRange(
outOfRange bool,
index int64,
signed bool,
length int,
)
Index bounds failures now use the existing interfaces:
func PanicIndex(x int, y int)
func PanicIndexU(x uint, y int)
This removes the runtime condition and signedness parameters while preserving signed and unsigned bounds-error
formatting.
Source Line Information
The panic block intentionally retains a continuation edge instead of ending immediately with unreachable.
Although PanicIndex and PanicIndexU do not return at runtime, placing unreachable directly after the call caused
LLVM's return-address line information to resolve to the following source line. This broke recovered bounds-panic
stack traces and TestRuntimeStatementLineInfo.
Keeping a branch after the panic call gives the return address an instruction with the correct debug location. Runtime
behavior is unchanged because the panic helpers never return.
Bounds-Check Modes
The existing -B behavior is preserved:
Test Coverage
LLVM IR fixtures were updated to verify: