Skip to content

--di with --opt keeps its locations, and inlined ones are made safe for the DI scope pass - #415

Merged
ASDAlexander77 merged 1 commit into
mainfrom
di-opt-inlinable-call
Sep 30, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
di-opt-inlinable-call

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Summary

Building with debug info and optimisation together, --di --opt, failed before any code was generated. It failed for most programs, and for every -mm=rc program:

error: inlinable function call in a function with a DISubprogram location must have a debug location

Cause

--opt ran StripDebugInfo before the inliner, which removed every location. --di still ran DIScopeForLLVMFuncOpPass, which gives every function a subprogram. Any call still standing afterwards had no location inside a function that needed one, and the LLVM dialect verifier rejects that when the callee could be inlined.

Under rc that is every program, because the lowering makes the tsrel_/tsret_ helpers after the inliner has run. Stripping also threw away the very debug information --di asked for.

Fix

  1. The strip runs only without --di.
  2. A new InlinedLocationPass runs before DIScopeForLLVMFuncOpPass under --di --opt. Keeping the locations exposed two shapes the inliner leaves behind, both of which the upstream pass dereferences without checking, crashing tslang:
    • callsite(unknown at <call>), from an op that was made without a location and then inlined. The pass reads a file name from every call site's callee. The inlined op now takes the location of its call.
    • A call-site location on an op outside any function. The lowering creates a string literal's global with the location of the op that used it, and that op may have been inlined. The pass reads the enclosing function's location, and there is none. Such an op now takes the location it was written at.

Tests

test-compile-debug-info-opt (debug-info-opt.cmake) emits LLVM IR with --di --opt for five programs under gc, rc and none. It is a cmake script, like debug-info-rc.cmake, because test-runner never combines the two flags. Without the fix, four of the five fail in every model.

Every file in test/tester/tests was also swept with --di --opt and compared against --opt alone:

  • compiled to LLVM IR under gc, rc and none: none of the 570 fails only because of --di;
  • run under JIT, gc and rc: none of the 570 fails only because of --di.

Release ctest: 3293/3293 (one test already disabled).

🤖 Generated with Claude Code

…or the DI scope pass

`--di --opt` failed before any code was generated, for most programs and
every -mm=rc one: "inlinable function call in a function with a
DISubprogram location must have a debug location".

--opt ran StripDebugInfo ahead of the inliner, so every location was gone,
while --di still ran DIScopeForLLVMFuncOpPass, which gives every function a
subprogram. A call left standing then had no location in a function that
needed one, which the LLVM dialect verifier rejects for a call it could
inline. Under rc that is every program: the tsrel_/tsret_ helpers are made
by the lowering, after the inliner ran. It also stripped the very debug
information --di asked for. The strip now runs only without --di.

Keeping the locations exposed two shapes the inliner leaves that
DIScopeForLLVMFuncOpPass dereferences without checking, crashing tslang:
- `callsite(unknown at <call>)`, from an op made without a location of its
  own: the pass reads a file name out of every call site's callee;
- a call-site location on an op outside any function - the lowering makes
  a string literal's global with the location of the op that used it: the
  pass reads the enclosing function's location.
A new InlinedLocationPass runs ahead of it under --di --opt: an inlined op
with no location of its own takes its call's, and an op outside a function
takes the location it was written at.

Test test-compile-debug-info-opt (debug-info-opt.cmake): five programs to
LLVM IR with --di --opt under gc, rc and none. Without the fix it fails
for four of the five in every model. Every file in test/tester/tests
compiles with --di --opt under gc, rc and none, and runs under JIT (gc and
rc), wherever it does with --opt alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 76626a4 into main Sep 30, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the di-opt-inlinable-call branch September 30, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant