Skip to content

Commit 9f535c4

Browse files
authored
[MLIR][LLVMIR] Basic support for dbg assign (#53)
1 parent af33d46 commit 9f535c4

File tree

7 files changed

+433
-7
lines changed

7 files changed

+433
-7
lines changed

llvm/include/llvm/IR/IntrinsicInst.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,7 @@ class DbgValueInst : public DbgVariableIntrinsic {
479479
/// \name Casting methods
480480
/// @{
481481
static bool classof(const IntrinsicInst *I) {
482-
return I->getIntrinsicID() == Intrinsic::dbg_value ||
483-
I->getIntrinsicID() == Intrinsic::dbg_assign;
482+
return I->getIntrinsicID() == Intrinsic::dbg_value;
484483
}
485484
static bool classof(const Value *V) {
486485
return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));

mlir/include/mlir/Dialect/LLVMIR/LLVMAttrDefs.td

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,32 @@ def LLVM_DILabelAttr : LLVM_Attr<"DILabel", "di_label",
780780
let assemblyFormat = "`<` struct(params) `>`";
781781
}
782782

783+
//===----------------------------------------------------------------------===//
784+
// DIAssignIDAttr
785+
//===----------------------------------------------------------------------===//
786+
787+
def LLVM_DIAssignIDAttr : LLVM_Attr<"DIAssignID", "di_assign_id",
788+
/*traits=*/[], "DINodeAttr"> {
789+
let parameters = (ins "DistinctAttr":$id);
790+
791+
let builders = [
792+
AttrBuilder<(ins), [{
793+
return $_get($_ctxt, DistinctAttr::create(UnitAttr::get($_ctxt)));
794+
}]>
795+
];
796+
797+
let summary = "LLVM dialect assignment ID metadata";
798+
let description = [{
799+
Defines an assignment ID used by debug assignment tracking to link
800+
dbg.assign intrinsics to the store instructions they describe.
801+
802+
See the following link for more details:
803+
https://llvm.org/docs/AssignmentTracking.md
804+
}];
805+
806+
let assemblyFormat = "`<` struct(params) `>`";
807+
}
808+
783809
//===----------------------------------------------------------------------===//
784810
// DIStringTypeAttr
785811
//===----------------------------------------------------------------------===//

mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,22 @@ def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value", "value",
681681
);
682682
}
683683

684+
def LLVM_DbgAssignOp : LLVM_DbgIntrOp<"dbg.assign", "value"> {
685+
let summary = "Describes how the value relates to a source language variable with assignment tracking.";
686+
let arguments = (ins
687+
LLVM_Type:$value,
688+
LLVM_DILocalVariableAttr:$varInfo,
689+
DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$locationExpr,
690+
LLVM_DIAssignIDAttr:$assignId,
691+
LLVM_AnyPointer:$address,
692+
DefaultValuedAttr<LLVM_DIExpressionAttr, "{}">:$addressExpr
693+
);
694+
let assemblyFormat = [{
695+
qualified($varInfo) (qualified($locationExpr)^)? qualified($assignId)
696+
(qualified($addressExpr)^)? `=` $value `,` $address `:` qualified(type($value)) `,` qualified(type($address)) attr-dict
697+
}];
698+
}
699+
684700
def LLVM_DbgLabelOp : LLVM_IntrOp<"dbg.label", [], [], [], 0> {
685701
let summary = "Relates the program to a debug information label.";
686702
let arguments = (ins LLVM_DILabelAttr:$label);

mlir/include/mlir/Target/LLVMIR/ModuleImport.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ namespace llvm {
2424
class BasicBlock;
2525
class CallBase;
2626
class DbgVariableIntrinsic;
27+
class DIAssignID;
2728
class Function;
2829
class Instruction;
2930
class Value;
@@ -301,6 +302,7 @@ class ModuleImport {
301302
noResultOpMapping.clear();
302303
blockMapping.clear();
303304
debugIntrinsics.clear();
305+
assignIDMapping.clear();
304306
}
305307
/// Sets the constant insertion point to the start of the given block.
306308
void setConstantInsertionPointToStart(Block *block) {
@@ -482,6 +484,9 @@ class ModuleImport {
482484
/// Mapping between LLVM comdat structs and symbol references to LLVM dialect
483485
/// comdat selector operations corresponding to these structs.
484486
DenseMap<const llvm::Comdat *, SymbolRefAttr> comdatMapping;
487+
/// Mapping between LLVM DIAssignID metadata nodes and MLIR DIAssignIDAttr
488+
/// attributes to preserve assignment tracking relationships.
489+
DenseMap<const llvm::DIAssignID *, DIAssignIDAttr> assignIDMapping;
485490
/// The stateful type translator (contains named structs).
486491
LLVM::TypeFromLLVMIRTranslator typeTranslator;
487492
/// Stateful debug information importer.

mlir/lib/Target/LLVMIR/ModuleImport.cpp

Lines changed: 160 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,8 +1978,35 @@ LogicalResult ModuleImport::convertInstruction(llvm::Instruction *inst) {
19781978
}
19791979

19801980
// Convert all instructions that have an mlirBuilder.
1981-
if (succeeded(convertInstructionImpl(builder, inst, *this, iface)))
1981+
if (succeeded(convertInstructionImpl(builder, inst, *this, iface))) {
1982+
// Handle DIAssignID metadata on store and alloca instructions for
1983+
// assignment tracking
1984+
if (auto *assignIDMD =
1985+
inst->getMetadata(llvm::LLVMContext::MD_DIAssignID)) {
1986+
if (auto *assignIDNode = dyn_cast<llvm::DIAssignID>(assignIDMD)) {
1987+
// Get the converted MLIR operation
1988+
Operation *mlirOp = lookupOperation(inst);
1989+
if (mlirOp) {
1990+
// Get or create the DIAssignIDAttr using the same mapping as debug
1991+
// intrinsics
1992+
auto it = assignIDMapping.find(assignIDNode);
1993+
DIAssignIDAttr assignIdAttr;
1994+
if (it != assignIDMapping.end()) {
1995+
assignIdAttr = it->second;
1996+
} else {
1997+
// Create new DIAssignIDAttr and add to mapping
1998+
DistinctAttr distinctId =
1999+
DistinctAttr::create(UnitAttr::get(context));
2000+
assignIdAttr = DIAssignIDAttr::get(context, distinctId);
2001+
assignIDMapping[assignIDNode] = assignIdAttr;
2002+
}
2003+
// Attach the assignment ID as an attribute to the operation
2004+
mlirOp->setAttr("DIAssignID", assignIdAttr);
2005+
}
2006+
}
2007+
}
19822008
return success();
2009+
}
19832010

19842011
return emitError(loc) << "unhandled instruction: " << diag(*inst);
19852012
}
@@ -2518,24 +2545,94 @@ ModuleImport::processDebugIntrinsic(llvm::DbgVariableIntrinsic *dbgIntr,
25182545
return emitError(loc) << "failed to convert a debug intrinsic operand: "
25192546
<< diag(*dbgIntr);
25202547

2548+
// Enhanced dominance validation with comprehensive undef value handling
2549+
bool isUndefValue = false;
2550+
bool isComplexUndef = false;
2551+
if (auto *nodeAsVal =
2552+
dyn_cast<llvm::MetadataAsValue>(dbgIntr->getArgOperand(0))) {
2553+
if (auto *node =
2554+
dyn_cast<llvm::ValueAsMetadata>(nodeAsVal->getMetadata())) {
2555+
if (isa<llvm::UndefValue>(node->getValue())) {
2556+
isUndefValue = true;
2557+
} else if (isa<llvm::PoisonValue>(node->getValue())) {
2558+
// Poison values require similar handling to undef values
2559+
isUndefValue = true;
2560+
isComplexUndef = true;
2561+
}
2562+
} else if (isa<llvm::MDNode>(nodeAsVal->getMetadata())) {
2563+
// Complex metadata nodes may contain undef values
2564+
isComplexUndef = true;
2565+
}
2566+
}
2567+
25212568
// Ensure that the debug intrinsic is inserted right after its operand is
25222569
// defined. Otherwise, the operand might not necessarily dominate the
25232570
// intrinsic. If the defining operation is a terminator, insert the intrinsic
25242571
// into a dominated block.
25252572
OpBuilder::InsertionGuard guard(builder);
2526-
if (Operation *op = argOperand->getDefiningOp();
2527-
op && op->hasTrait<OpTrait::IsTerminator>()) {
2573+
if (isUndefValue || isComplexUndef) {
2574+
// Enhanced placement logic for undef and complex values
2575+
Block *currentBlock = builder.getInsertionBlock();
2576+
if (!currentBlock) {
2577+
return emitError(loc) << "no insertion block available for debug "
2578+
"intrinsic with undef value";
2579+
}
2580+
2581+
if (currentBlock->empty()) {
2582+
return emitError(loc) << "cannot place debug intrinsic in empty block";
2583+
}
2584+
2585+
Operation *terminator = &currentBlock->back();
2586+
if (terminator->hasTrait<OpTrait::IsTerminator>()) {
2587+
builder.setInsertionPoint(terminator);
2588+
} else {
2589+
// Validate that we can safely insert at the end of the block
2590+
if (currentBlock->hasNoSuccessors()) {
2591+
return emitError(loc)
2592+
<< "cannot place debug intrinsic in block without successors";
2593+
}
2594+
}
2595+
2596+
// Additional validation for complex undef patterns
2597+
if (isComplexUndef && emitExpensiveWarnings) {
2598+
emitWarning(loc) << "debug intrinsic contains complex undef pattern that "
2599+
"may affect debugging accuracy";
2600+
}
2601+
} else if (Operation *op = argOperand->getDefiningOp();
2602+
op && op->hasTrait<OpTrait::IsTerminator>()) {
25282603
// Find a dominated block that can hold the debug intrinsic.
25292604
auto dominatedBlocks = domInfo.getNode(op->getBlock())->children();
25302605
// If no block is dominated by the terminator, this intrinisc cannot be
25312606
// converted.
25322607
if (dominatedBlocks.empty())
25332608
return emitUnsupportedWarning();
2609+
2610+
// Enhanced validation for terminator-dominated placement
2611+
Block *dominatedBlock = (*dominatedBlocks.begin())->getBlock();
2612+
if (!dominatedBlock || dominatedBlock->empty()) {
2613+
return emitError(loc)
2614+
<< "dominated block is invalid for debug intrinsic placement";
2615+
}
2616+
2617+
Operation *dominatedTerminator = dominatedBlock->getTerminator();
2618+
if (!dominatedTerminator) {
2619+
return emitError(loc) << "dominated block lacks terminator for safe "
2620+
"debug intrinsic placement";
2621+
}
2622+
25342623
// Set insertion point before the terminator, to avoid inserting something
25352624
// before landingpads.
2536-
Block *dominatedBlock = (*dominatedBlocks.begin())->getBlock();
2537-
builder.setInsertionPoint(dominatedBlock->getTerminator());
2625+
builder.setInsertionPoint(dominatedTerminator);
25382626
} else {
2627+
// Standard placement with additional validation
2628+
if (argOperand->getDefiningOp()) {
2629+
// Verify that the defining operation is in a valid state for dominance
2630+
Operation *defOp = argOperand->getDefiningOp();
2631+
if (!defOp->getBlock()) {
2632+
return emitError(loc)
2633+
<< "debug intrinsic operand has invalid defining operation";
2634+
}
2635+
}
25392636
builder.setInsertionPointAfterValue(*argOperand);
25402637
}
25412638
auto locationExprAttr =
@@ -2546,6 +2643,64 @@ ModuleImport::processDebugIntrinsic(llvm::DbgVariableIntrinsic *dbgIntr,
25462643
return builder.create<LLVM::DbgDeclareOp>(
25472644
loc, *argOperand, localVariableAttr, locationExprAttr);
25482645
})
2646+
.Case([&](llvm::DbgAssignIntrinsic *dbgAssign) {
2647+
// Convert all operands for DbgAssignIntrinsic
2648+
2649+
// Get the assignment ID (operand 3) and preserve the mapping
2650+
auto *assignIdNodeAsVal =
2651+
cast<llvm::MetadataAsValue>(dbgAssign->getArgOperand(3));
2652+
auto *assignIdNode =
2653+
cast<llvm::DIAssignID>(assignIdNodeAsVal->getMetadata());
2654+
2655+
// Enhanced assignment ID consistency verification
2656+
DIAssignIDAttr assignIdAttr;
2657+
auto it = assignIDMapping.find(assignIdNode);
2658+
if (it != assignIDMapping.end()) {
2659+
assignIdAttr = it->second;
2660+
// Verify consistency of reused assignment IDs
2661+
if (emitExpensiveWarnings) {
2662+
// Check if this assignment ID usage is consistent with previous
2663+
// usage Note: Assignment ID consistency checking requires
2664+
// traversing debug intrinsics which may be expensive, but
2665+
// provides valuable validation for debug tracking
2666+
emitWarning(loc) << "reusing assignment ID - verify consistent "
2667+
"variable tracking";
2668+
}
2669+
} else {
2670+
// Create a new DIAssignIDAttr with distinct ID for this
2671+
// DIAssignID
2672+
DistinctAttr distinctId =
2673+
DistinctAttr::create(UnitAttr::get(context));
2674+
assignIdAttr = DIAssignIDAttr::get(context, distinctId);
2675+
assignIDMapping[assignIdNode] = assignIdAttr;
2676+
2677+
// Validate that the assignment ID is properly structured
2678+
if (!assignIdNode->isDistinct()) {
2679+
emitWarning(loc)
2680+
<< "assignment ID should be distinct for proper tracking";
2681+
}
2682+
}
2683+
2684+
// Get the address operand (operand 4)
2685+
FailureOr<Value> addressOperand =
2686+
convertMetadataValue(dbgAssign->getArgOperand(4));
2687+
if (failed(addressOperand))
2688+
return (Operation *)nullptr;
2689+
2690+
// Get the address expression (operand 5)
2691+
auto *addressExprNodeAsVal =
2692+
cast<llvm::MetadataAsValue>(dbgAssign->getArgOperand(5));
2693+
auto *addressExprNode =
2694+
cast<llvm::DIExpression>(addressExprNodeAsVal->getMetadata());
2695+
auto addressExprAttr =
2696+
debugImporter->translateExpression(addressExprNode);
2697+
2698+
return builder
2699+
.create<LLVM::DbgAssignOp>(loc, *argOperand, localVariableAttr,
2700+
locationExprAttr, assignIdAttr,
2701+
*addressOperand, addressExprAttr)
2702+
.getOperation();
2703+
})
25492704
.Case([&](llvm::DbgValueInst *) {
25502705
return builder.create<LLVM::DbgValueOp>(
25512706
loc, *argOperand, localVariableAttr, locationExprAttr);
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
; RUN: mlir-translate -import-llvm -split-input-file %s | FileCheck %s
2+
3+
; Test debug record format (new debug info format with #dbg_assign records)
4+
; This file uses the new debug record format which cannot be mixed with debug intrinsics
5+
6+
; CHECK-LABEL: @test_debug_record_format
7+
define dso_local void @test_debug_record_format() local_unnamed_addr !dbg !10 {
8+
; Test undef value generation
9+
; CHECK: %[[UNDEF:.*]] = llvm.mlir.undef : i1
10+
; Test alloca with DIAssignID metadata
11+
; CHECK: %[[ALLOCA:.*]] = llvm.alloca %{{.*}} x i32 {DIAssignID = #[[ASSIGN_ID:llvm.di_assign_id<id = distinct\[[0-9]+\]<>>]], alignment = 4 : i64} : (i32) -> !llvm.ptr
12+
%1 = alloca i32, align 4, !DIAssignID !16
13+
14+
; Test dbg_assign record conversion (new debug record format)
15+
; CHECK: llvm.intr.dbg.assign #[[VAR:.*]] #[[ASSIGN_ID]] = %[[UNDEF]], %[[ALLOCA]] : i1, !llvm.ptr
16+
#dbg_assign(i1 undef, !14, !DIExpression(), !16, ptr %1, !DIExpression(), !17)
17+
18+
call void @func1(ptr noundef nonnull %1)
19+
ret void
20+
}
21+
22+
declare void @func1(ptr noundef) local_unnamed_addr
23+
24+
; -----
25+
26+
; CHECK-LABEL: @test_debug_record_shared_ids
27+
define void @test_debug_record_shared_ids() !dbg !20 {
28+
; Test undef value generation
29+
; CHECK: %[[UNDEF2:.*]] = llvm.mlir.undef : i1
30+
; Two allocas with same assignment ID should get same MLIR attribute
31+
; CHECK: %[[ALLOCA1:.*]] = llvm.alloca %{{.*}} x i32 {DIAssignID = #[[SHARED_ID:llvm.di_assign_id<id = distinct\[[0-9]+\]<>>]], alignment = 4 : i64} : (i32) -> !llvm.ptr
32+
%1 = alloca i32, align 4, !DIAssignID !26
33+
; CHECK: %[[ALLOCA2:.*]] = llvm.alloca %{{.*}} x i32 {DIAssignID = #[[SHARED_ID]], alignment = 4 : i64} : (i32) -> !llvm.ptr
34+
%2 = alloca i32, align 4, !DIAssignID !26
35+
36+
; Both dbg_assign records should reference the same assignment ID
37+
; CHECK: llvm.intr.dbg.assign #[[VAR2:.*]] #[[SHARED_ID]] = %[[UNDEF2]], %[[ALLOCA1]] : i1, !llvm.ptr
38+
#dbg_assign(i1 undef, !24, !DIExpression(), !26, ptr %1, !DIExpression(), !27)
39+
; CHECK: llvm.intr.dbg.assign #[[VAR2]] #[[SHARED_ID]] = %[[UNDEF2]], %[[ALLOCA2]] : i1, !llvm.ptr
40+
#dbg_assign(i1 undef, !24, !DIExpression(), !26, ptr %2, !DIExpression(), !27)
41+
42+
ret void
43+
}
44+
45+
!llvm.dbg.cu = !{!0}
46+
!llvm.module.flags = !{!2}
47+
48+
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug)
49+
!1 = !DIFile(filename: "test.c", directory: "/")
50+
!2 = !{i32 2, !"Debug Info Version", i32 3}
51+
!10 = distinct !DISubprogram(name: "test_debug_record_format", scope: !1, file: !1, line: 3, type: !11, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0)
52+
!11 = !DISubroutineType(types: !{null})
53+
!14 = !DILocalVariable(name: "host", scope: !10, file: !1, line: 4, type: !15)
54+
!15 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
55+
!16 = distinct !DIAssignID()
56+
!17 = !DILocation(line: 4, column: 7, scope: !10)
57+
!20 = distinct !DISubprogram(name: "test_debug_record_shared_ids", scope: !1, file: !1, line: 8, type: !11, scopeLine: 8, spFlags: DISPFlagDefinition, unit: !0)
58+
!24 = !DILocalVariable(name: "shared", scope: !20, file: !1, line: 9, type: !15)
59+
!26 = distinct !DIAssignID()
60+
!27 = !DILocation(line: 9, column: 7, scope: !20)

0 commit comments

Comments
 (0)