Skip to content

[Triton] Fix LLVMDILocalVariable pass crash with LLVM_EXTRACT_DI_LOCAL_VARIABLES#10042

Open
byoshimi-gmail wants to merge 3 commits intotriton-lang:mainfrom
byoshimi-gmail:fix-di-local-variable-crash
Open

[Triton] Fix LLVMDILocalVariable pass crash with LLVM_EXTRACT_DI_LOCAL_VARIABLES#10042
byoshimi-gmail wants to merge 3 commits intotriton-lang:mainfrom
byoshimi-gmail:fix-di-local-variable-crash

Conversation

@byoshimi-gmail
Copy link
Copy Markdown
Contributor

Summary:
Fix multiple bugs in the LLVMDILocalVariable pass that caused crashes when running with LLVM_EXTRACT_DI_LOCAL_VARIABLES=1.

Root cause: The MLIR DIRecursiveTypeAttrInterface uses isRecSelf=true as a placeholder and isRecSelf=false as its resolver. During MLIR-to-LLVM IR translation, the resolver recursiveNodeMap entry is temporary (popped after translation). Any isRecSelf=true references encountered outside the resolver translation scope trigger an assertion.

The add_di_scope pass creates a DISubprogramAttr with isRecSelf=true and embeds it in function locations and DILexicalBlockFileAttr scopes in operation locations. The LLVMDILocalVariable pass was supposed to resolve these but had several issues.

Fixes:

  1. Added null/scope guard for diSubprogramAttr to skip module-level ops that appear before or between functions in the walk.
  2. Fixed getDISubprogramAttr(Operation op) to take Operation *op.
  3. Restructured fuseFuncArgVariables to create two sets of DILocalVariableAttr: one with isRecSelf=true scope for retainedNodes (correct recursive pattern), and separate ones with the resolved isRecSelf=false scope for DbgValueOps (avoids unresolvable refs).
  4. Added fixLexicalBlockScopes to walk operations and replace DILexicalBlockFileAttr scopes that reference the old isRecSelf=true subprogram with the resolved version.
  5. Value-initialized diSubprogramAttr to avoid undefined behavior.

Test Plan:
TRITON_ALWAYS_COMPILE=1 LLVM_EXTRACT_DI_LOCAL_VARIABLES=1
python3 python/tutorials/02-fused-softmax.py

New contributor declaration

  • I am not making a trivial change, such as fixing a typo in a comment.

  • I have written a PR description following these
    rules.

  • I have run pre-commit run --from-ref origin/main --to-ref HEAD.

  • Select one of the following.

    • I have added tests.
      • /test for lit tests
      • /unittest for C++ tests
      • /python/test for end-to-end tests
    • This PR does not need a test because FILL THIS IN.
  • Select one of the following.

    • I have not added any lit tests.
    • The lit tests I have added follow these best practices,
      including the "tests should be minimal" section. (Usually running Python code
      and using the instructions it generates is not minimal.)

…L_VARIABLES

Summary:
Fix multiple bugs in the LLVMDILocalVariable pass that caused crashes
when running with LLVM_EXTRACT_DI_LOCAL_VARIABLES=1.

Root cause: The MLIR DIRecursiveTypeAttrInterface uses isRecSelf=true as
a placeholder and isRecSelf=false as its resolver. During MLIR-to-LLVM IR
translation, the resolver recursiveNodeMap entry is temporary (popped
after translation). Any isRecSelf=true references encountered outside the
resolver translation scope trigger an assertion.

The add_di_scope pass creates a DISubprogramAttr with isRecSelf=true and
embeds it in function locations and DILexicalBlockFileAttr scopes in
operation locations. The LLVMDILocalVariable pass was supposed to resolve
these but had several issues.

Fixes:
1. Added null/scope guard for diSubprogramAttr to skip module-level ops
   that appear before or between functions in the walk.
2. Fixed getDISubprogramAttr(Operation op) to take Operation *op.
3. Restructured fuseFuncArgVariables to create two sets of
   DILocalVariableAttr: one with isRecSelf=true scope for retainedNodes
   (correct recursive pattern), and separate ones with the resolved
   isRecSelf=false scope for DbgValueOps (avoids unresolvable refs).
4. Added fixLexicalBlockScopes to walk operations and replace
   DILexicalBlockFileAttr scopes that reference the old isRecSelf=true
   subprogram with the resolved version.
5. Value-initialized diSubprogramAttr to avoid undefined behavior.

Test Plan:
TRITON_ALWAYS_COMPILE=1 LLVM_EXTRACT_DI_LOCAL_VARIABLES=1 \
  python3 python/tutorials/02-fused-softmax.py
@byoshimi-gmail byoshimi-gmail requested a review from ptillet as a code owner April 15, 2026 16:41
// DISubprogramAttr construction
LLVM::DISubprogramAttr getDISubprogramAttr(Operation op) {
auto funcOp = op.getParentOfType<LLVM::LLVMFuncOp>();
LLVM::DISubprogramAttr getDISubprogramAttr(Operation *op) {
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.

Use Operation &op to avoid needing to dereference.

@plotfi
Copy link
Copy Markdown
Contributor

plotfi commented Apr 16, 2026

LGTM but @Mogball should take a look as well, as he has experience with debug info and reviewed @Youngzt998's previous work.

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.

3 participants