diff --git a/flang/test/Driver/emit-asm-aarch64.f90 b/flang/test/Driver/emit-asm-aarch64.f90 --- a/flang/test/Driver/emit-asm-aarch64.f90 +++ b/flang/test/Driver/emit-asm-aarch64.f90 @@ -6,7 +6,6 @@ ! RUN: %flang -S -target aarch64-unknown-linux-gnu %s -o - | FileCheck %s ! CHECK-LABEL: _QQmain: -! CHECK-NEXT: .Lfunc_begin0: ! CHECK: ret end program diff --git a/flang/test/Driver/emit-asm-x86.f90 b/flang/test/Driver/emit-asm-x86.f90 --- a/flang/test/Driver/emit-asm-x86.f90 +++ b/flang/test/Driver/emit-asm-x86.f90 @@ -6,7 +6,6 @@ ! RUN: %flang -S -target x86_64-unknown-linux-gnu %s -o - | FileCheck %s ! CHECK-LABEL: _QQmain: -! CHECK-NEXT: .Lfunc_begin0: ! CHECK: ret end program diff --git a/flang/test/Fir/embox.fir b/flang/test/Fir/embox.fir --- a/flang/test/Fir/embox.fir +++ b/flang/test/Fir/embox.fir @@ -9,14 +9,14 @@ // CHECK-LABEL: define void @_QPtest_slice() func.func @_QPtest_slice() { -// CHECK: %[[a1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8, -// CHECK: %[[a2:.*]] = alloca [20 x i32], i64 1, align 4, -// CHECK: %[[a3:.*]] = getelementptr [20 x i32], ptr %[[a2]], i64 0, i64 0, +// CHECK: %[[a1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8 +// CHECK: %[[a2:.*]] = alloca [20 x i32], i64 1, align 4 +// CHECK: %[[a3:.*]] = getelementptr [20 x i32], ptr %[[a2]], i64 0, i64 0 // CHECK: %[[a4:.*]] = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } // CHECK: { ptr undef, i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i32 20180515, i8 1, i8 9, i8 0, i8 0, [1 x [3 x i64]] -// CHECK: [i64 1, i64 5, i64 mul (i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i64 2)]] }, ptr %[[a3]], 0, +// CHECK: [i64 1, i64 5, i64 mul (i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i64 2)]] }, ptr %[[a3]], 0 // CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[a4]], ptr %[[a1]], align 8 -// CHECK: call void @_QPtest_callee(ptr %[[a1]]), +// CHECK: call void @_QPtest_callee(ptr %[[a1]]) %c20 = arith.constant 20 : index %c1_i64 = arith.constant 1 : i64 %c10_i64 = arith.constant 10 : i64 @@ -36,17 +36,17 @@ // CHECK-LABEL: define void @_QPtest_dt_slice() func.func @_QPtest_dt_slice() { -// CHECK: %[[a1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8, -// CHECK: %[[a3:.*]] = alloca [20 x %_QFtest_dt_sliceTt], i64 1, align 8, -// CHECK: %[[a4:.*]] = getelementptr [20 x %_QFtest_dt_sliceTt], ptr %[[a3]], i64 0, i64 0, i32 0, +// CHECK: %[[a1:.*]] = alloca { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] }, align 8 +// CHECK: %[[a3:.*]] = alloca [20 x %_QFtest_dt_sliceTt], i64 1, align 8 +// CHECK: %[[a4:.*]] = getelementptr [20 x %_QFtest_dt_sliceTt], ptr %[[a3]], i64 0, i64 0, i32 0 // CHECK: %[[a5:.*]] = insertvalue { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } // CHECK-SAME: { ptr undef, i64 ptrtoint (ptr getelementptr (i32, ptr null, i32 1) to i64), i32 20180515, i8 1, i8 9, i8 0, i8 0, [1 x [3 x i64]] // CHECK-SAME: [i64 1, i64 5, i64 mul // CHECK-SAME: (i64 ptrtoint (ptr getelementptr (%_QFtest_dt_sliceTt, ptr null, i32 1) to i64), i64 2)]] } // CHECK-SAME: , ptr %[[a4]], 0 -// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[a5]], ptr %[[a1]], align 8, -// CHECK: call void @_QPtest_dt_callee(ptr %[[a1]]), +// CHECK: store { ptr, i64, i32, i8, i8, i8, i8, [1 x [3 x i64]] } %[[a5]], ptr %[[a1]], align 8 +// CHECK: call void @_QPtest_dt_callee(ptr %[[a1]]) %c20 = arith.constant 20 : index %c1_i64 = arith.constant 1 : i64 %c10_i64 = arith.constant 10 : i64 @@ -75,8 +75,8 @@ %1 = fir.slice %c1, %c2, %c1, %c1, %c3, %c1 substr %c1_i64, %c2_i64 : (index, index, index, index, index, index, i64, i64) -> !fir.slice<2> %2 = fir.embox %arg0(%0) [%1] : (!fir.ref>>, !fir.shape<2>, !fir.slice<2>) -> !fir.box>> // CHECK: %[[addr:.*]] = getelementptr [3 x [2 x [4 x i8]]], ptr %[[arg0]], i64 0, i64 0, i64 0, i64 1 - // CHECK: insertvalue {[[descriptorType:.*]]} { ptr undef, i64 mul (i64 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i64), i64 2), i32 20180515, i8 2, i8 40, i8 0, i8 0, - // CHECK-SAME: [2 x [3 x i64]] [{{\[}}3 x i64] [i64 1, i64 2, i64 4], [3 x i64] [i64 1, i64 3, i64 8]] }, + // CHECK: insertvalue {[[descriptorType:.*]]} { ptr undef, i64 mul (i64 ptrtoint (ptr getelementptr (i8, ptr null, i32 1) to i64), i64 2), i32 20180515, i8 2, i8 40, i8 0, i8 0 + // CHECK-SAME: [2 x [3 x i64]] [{{\[}}3 x i64] [i64 1, i64 2, i64 4], [3 x i64] [i64 1, i64 3, i64 8]] } // CHECK-SAME: ptr %[[addr]], 0 fir.call @takesRank2CharBox(%2) : (!fir.box>>) -> () diff --git a/flang/test/Fir/ignore-missing-type-descriptor.fir b/flang/test/Fir/ignore-missing-type-descriptor.fir --- a/flang/test/Fir/ignore-missing-type-descriptor.fir +++ b/flang/test/Fir/ignore-missing-type-descriptor.fir @@ -19,4 +19,4 @@ // CHECK: insertvalue { ptr, i64, i32, i8, i8, i8, i8, ptr, [1 x i64] } // CHECK-SAME: { ptr undef, i64 ptrtoint (ptr getelementptr (%some_not_mangled_type, ptr null, i32 1) to i64), // CHECK-SAME: i32 20180515, i8 0, i8 42, i8 0, i8 1, ptr null, [1 x i64] undef }, -// CHECK-SAME: ptr %[[ADDR]], 0, +// CHECK-SAME: ptr %[[ADDR]], 0 diff --git a/flang/test/Lower/OpenMP/omp-ordered-threads.f90 b/flang/test/Lower/OpenMP/omp-ordered-threads.f90 --- a/flang/test/Lower/OpenMP/omp-ordered-threads.f90 +++ b/flang/test/Lower/OpenMP/omp-ordered-threads.f90 @@ -12,29 +12,29 @@ !FIRDialect: omp.ordered_region { !LLVMIRDialect: omp.ordered_region { -!LLVMIR: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB0:[0-9]+]]), !dbg !{{.*}} -!LLVMIR-NEXT: call void @__kmpc_ordered(ptr @[[GLOB0]], i32 [[TMP0]]), !dbg !{{.*}} +!LLVMIR: [[TMP0:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB0:[0-9]+]]) +!LLVMIR-NEXT: call void @__kmpc_ordered(ptr @[[GLOB0]], i32 [[TMP0]]) !$OMP ORDERED a(i) = a(i-1) + 1 !FIRDialect: omp.terminator !FIRDialect-NEXT: } !LLVMIRDialect: omp.terminator !LLVMIRDialect-NEXT: } -!LLVMIR: call void @__kmpc_end_ordered(ptr @[[GLOB0]], i32 [[TMP0]]), !dbg !{{.*}} +!LLVMIR: call void @__kmpc_end_ordered(ptr @[[GLOB0]], i32 [[TMP0]]) !$OMP END ORDERED !FIRDialect: omp.ordered_region { !LLVMIRDialect: omp.ordered_region { -!LLVMIR: [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1:[0-9]+]]), !dbg !{{.*}} -!LLVMIR-NEXT: call void @__kmpc_ordered(ptr @[[GLOB1]], i32 [[TMP1]]), !dbg !{{.*}} +!LLVMIR: [[TMP1:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1:[0-9]+]]) +!LLVMIR-NEXT: call void @__kmpc_ordered(ptr @[[GLOB1]], i32 [[TMP1]]) !$OMP ORDERED THREADS a(i) = a(i-1) + 1 !FIRDialect: omp.terminator !FIRDialect-NEXT: } !LLVMIRDialect: omp.terminator !LLVMIRDialect-NEXT: } -!LLVMIR: call void @__kmpc_end_ordered(ptr @[[GLOB1]], i32 [[TMP1]]), !dbg !{{.*}} -!LLVMIR-NEXT: ret void, !dbg !{{.*}} +!LLVMIR: call void @__kmpc_end_ordered(ptr @[[GLOB1]], i32 [[TMP1]]) +!LLVMIR-NEXT: ret void !$OMP END ORDERED end diff --git a/mlir/docs/Dialects/LLVM.md b/mlir/docs/Dialects/LLVM.md --- a/mlir/docs/Dialects/LLVM.md +++ b/mlir/docs/Dialects/LLVM.md @@ -482,3 +482,13 @@ intrinsics, e.g., NVVM or ROCDL, are modeled as separate dialects. [include "Dialects/LLVMIntrinsicOps.md"] + +### Debug Info + +Debug information within the LLVM dialect is represented using locations in +combination with a set of attributes that mirror the DINode structure defined by +the debug info metadata within LLVM IR. Debug scoping information is attached +to LLVM IR dialect operations using a fused location (`FusedLoc`) whose metadata +holds the DIScopeAttr representing the debug scope. Similarly, the subprogram +of LLVM IR dialect `FuncOp` operations is attached using a fused location whose +metadata is a DISubprogramAttr. diff --git a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td --- a/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td +++ b/mlir/include/mlir/Dialect/LLVMIR/LLVMIntrinsicOps.td @@ -233,21 +233,37 @@ // Debug function intrinsics. // -def LLVM_DbgAddrOp : LLVM_Op<"dbg.addr"> { +class LLVM_DbgIntrOp : LLVM_Op { + let llvmBuilder = [{ + llvm::Module *module = builder.GetInsertBlock()->getModule(); + llvm::LLVMContext &ctx = module->getContext(); + llvm::Function *fn = + llvm::Intrinsic::getDeclaration(module, llvm::Intrinsic::}] + # !subst(".", "_", name) # [{); + builder.CreateCall(fn, { + llvm::MetadataAsValue::get(ctx, + llvm::ValueAsMetadata::get(moduleTranslation.lookupValue(opInst.getOperand(0)))), + llvm::MetadataAsValue::get(ctx, moduleTranslation.translateDebugInfo($varInfo)), + llvm::MetadataAsValue::get(ctx, llvm::DIExpression::get(ctx, llvm::None)), + }); + }]; +} + +def LLVM_DbgAddrOp : LLVM_DbgIntrOp<"dbg.addr"> { let summary = "Describe the current address of a local debug info variable."; let arguments = (ins LLVM_AnyPointer:$addr, LLVM_DILocalVariableAttr:$varInfo); let assemblyFormat = "qualified($varInfo) `=` $addr `:` type($addr) attr-dict"; } -def LLVM_DbgDeclareOp : LLVM_Op<"dbg.declare"> { +def LLVM_DbgDeclareOp : LLVM_DbgIntrOp<"dbg.declare"> { let summary = "Declare the address of a local debug info variable."; let arguments = (ins LLVM_AnyPointer:$addr, LLVM_DILocalVariableAttr:$varInfo); let assemblyFormat = "qualified($varInfo) `=` $addr `:` type($addr) attr-dict"; } -def LLVM_DbgValueOp : LLVM_Op<"dbg.value"> { +def LLVM_DbgValueOp : LLVM_DbgIntrOp<"dbg.value"> { let summary = "Describe the current value of a local debug info variable."; let arguments = (ins LLVM_Type:$value, LLVM_DILocalVariableAttr:$varInfo); diff --git a/mlir/include/mlir/IR/Location.h b/mlir/include/mlir/IR/Location.h --- a/mlir/include/mlir/IR/Location.h +++ b/mlir/include/mlir/IR/Location.h @@ -36,6 +36,21 @@ /// Walk all of the locations nested under, and including, the current. WalkResult walk(function_ref walkFn); + /// Return an instance of the given location type if one is nested under the + /// current location. Returns nullptr if one could not be found. + template + T findInstanceOf() { + T result = {}; + walk([&](auto loc) { + if (auto typedLoc = llvm::dyn_cast(loc)) { + result = typedLoc; + return WalkResult::interrupt(); + } + return WalkResult::advance(); + }); + return result; + } + /// Methods for support type inquiry through isa, cast, and dyn_cast. static bool classof(Attribute attr); }; @@ -118,6 +133,29 @@ namespace mlir { +//===----------------------------------------------------------------------===// +// FusedLoc +//===----------------------------------------------------------------------===// + +/// This class represents a fused location whose metadata is known to be an +/// instance of the given type. +template +class FusedLocWith : public FusedLoc { +public: + using FusedLoc::FusedLoc; + + /// Return the metadata associated with this fused location. + MetadataT getMetadata() const { + return FusedLoc::getMetadata().template cast(); + } + + /// Support llvm style casting. + static bool classof(Attribute attr) { + auto fusedLoc = attr.dyn_cast(); + return fusedLoc && fusedLoc.getMetadata().isa_and_nonnull(); + } +}; + //===----------------------------------------------------------------------===// // OpaqueLoc //===----------------------------------------------------------------------===// diff --git a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h --- a/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h +++ b/mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h @@ -42,6 +42,7 @@ class DebugTranslation; } // namespace detail +class DINodeAttr; class LLVMFuncOp; /// Implementation class for module translation. Holds a reference to the module @@ -175,6 +176,9 @@ /// Translates the given location. const llvm::DILocation *translateLoc(Location loc, llvm::DILocalScope *scope); + /// Translates the given LLVM debug info metadata. + llvm::Metadata *translateDebugInfo(LLVM::DINodeAttr attr); + /// Translates the contents of the given block to LLVM IR using this /// translator. The LLVM IR basic block corresponding to the given block is /// expected to exist in the mapping of this translator. Uses `builder` to diff --git a/mlir/lib/IR/Diagnostics.cpp b/mlir/lib/IR/Diagnostics.cpp --- a/mlir/lib/IR/Diagnostics.cpp +++ b/mlir/lib/IR/Diagnostics.cpp @@ -389,19 +389,6 @@ } // namespace detail } // namespace mlir -/// Return a processable FileLineColLoc from the given location. -static Optional getFileLineColLoc(Location loc) { - Optional firstFileLoc; - loc->walk([&](Location loc) { - if (FileLineColLoc fileLoc = loc.dyn_cast()) { - firstFileLoc = fileLoc; - return WalkResult::interrupt(); - } - return WalkResult::advance(); - }); - return firstFileLoc; -} - /// Return a processable CallSiteLoc from the given location. static Optional getCallSiteLoc(Location loc) { if (auto nameLoc = dyn_cast(loc)) @@ -454,7 +441,7 @@ DiagnosticSeverity kind, bool displaySourceLine) { // Extract a file location from this loc. - auto fileLoc = getFileLineColLoc(loc); + auto fileLoc = loc->findInstanceOf(); // If one doesn't exist, then print the raw message without a source location. if (!fileLoc) { @@ -469,7 +456,7 @@ // Otherwise if we are displaying the source line, try to convert the file // location to an SMLoc. if (displaySourceLine) { - auto smloc = convertLocToSMLoc(*fileLoc); + auto smloc = convertLocToSMLoc(fileLoc); if (smloc.isValid()) return mgr.PrintMessage(os, smloc, getDiagKind(kind), message); } @@ -479,8 +466,8 @@ // the constructor of SMDiagnostic that takes a location. std::string locStr; llvm::raw_string_ostream locOS(locStr); - locOS << fileLoc->getFilename().getValue() << ":" << fileLoc->getLine() << ":" - << fileLoc->getColumn(); + locOS << fileLoc.getFilename().getValue() << ":" << fileLoc.getLine() << ":" + << fileLoc.getColumn(); llvm::SMDiagnostic diag(locOS.str(), getDiagKind(kind), message.str()); diag.print(nullptr, os); } @@ -853,8 +840,8 @@ auto kind = diag.getSeverity(); // Process a FileLineColLoc. - if (auto fileLoc = getFileLineColLoc(diag.getLocation())) - return process(*fileLoc, diag.str(), kind); + if (auto fileLoc = diag.getLocation()->findInstanceOf()) + return process(fileLoc, diag.str(), kind); emitDiagnostic(diag.getLocation(), "unexpected " + getDiagKindStr(kind) + ": " + diag.str(), diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.h b/mlir/lib/Target/LLVMIR/DebugTranslation.h --- a/mlir/lib/Target/LLVMIR/DebugTranslation.h +++ b/mlir/lib/Target/LLVMIR/DebugTranslation.h @@ -14,6 +14,7 @@ #ifndef MLIR_LIB_TARGET_LLVMIR_DEBUGTRANSLATION_H_ #define MLIR_LIB_TARGET_LLVMIR_DEBUGTRANSLATION_H_ +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/Location.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" @@ -39,6 +40,17 @@ /// Translate the debug information for the given function. void translate(LLVMFuncOp func, llvm::Function &llvmFunc); + /// Translate the given LLVM debug metadata to LLVM. + llvm::DINode *translate(DINodeAttr attr); + + /// Translate the given derived LLVM debug metadata to LLVM. + template + auto translate(DIAttrT attr) { + // Infer the LLVM type from the attribute type. + using LLVMTypeT = std::remove_pointer_t; + return cast_or_null(translate(DINodeAttr(attr))); + } + private: /// Translate the given location to an llvm debug location with the given /// scope and inlinedAt parameters. @@ -48,11 +60,30 @@ /// Create an llvm debug file for the given file path. llvm::DIFile *translateFile(StringRef fileName); + /// Translate the given attribute to the corresponding llvm debug metadata. + llvm::DIBasicType *translateImpl(DIBasicTypeAttr attr); + llvm::DICompileUnit *translateImpl(DICompileUnitAttr attr); + llvm::DICompositeType *translateImpl(DICompositeTypeAttr attr); + llvm::DIDerivedType *translateImpl(DIDerivedTypeAttr attr); + llvm::DIFile *translateImpl(DIFileAttr attr); + llvm::DILexicalBlock *translateImpl(DILexicalBlockAttr attr); + llvm::DILexicalBlockFile *translateImpl(DILexicalBlockFileAttr attr); + llvm::DILocalVariable *translateImpl(DILocalVariableAttr attr); + llvm::DIScope *translateImpl(DIScopeAttr attr); + llvm::DISubprogram *translateImpl(DISubprogramAttr attr); + llvm::DISubrange *translateImpl(DISubrangeAttr attr); + llvm::DISubroutineType *translateImpl(DISubroutineTypeAttr attr); + llvm::DIType *translateImpl(DITypeAttr attr); + /// A mapping between mlir location+scope and the corresponding llvm debug /// metadata. DenseMap, const llvm::DILocation *> locationToLoc; + /// A mapping between debug attribute and the corresponding llvm debug + /// metadata. + DenseMap attrToNode; + /// A mapping between filename and llvm debug file. /// TODO: Change this to DenseMap when we can /// access the Identifier filename in FileLineColLoc. @@ -61,10 +92,12 @@ /// A string containing the current working directory of the compiler. SmallString<256> currentWorkingDir; + /// Flag indicating if debug information should be emitted. + bool debugEmissionIsEnabled; + /// Debug information fields. - llvm::DIBuilder builder; + llvm::Module &llvmModule; llvm::LLVMContext &llvmCtx; - llvm::DICompileUnit *compileUnit; }; } // namespace detail diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp --- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp @@ -8,6 +8,7 @@ #include "DebugTranslation.h" #include "mlir/Dialect/LLVMIR/LLVMDialect.h" +#include "llvm/ADT/TypeSwitch.h" #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/Support/FileSystem.h" @@ -25,23 +26,15 @@ } DebugTranslation::DebugTranslation(Operation *module, llvm::Module &llvmModule) - : builder(llvmModule), llvmCtx(llvmModule.getContext()), - compileUnit(nullptr) { - + : debugEmissionIsEnabled(false), llvmModule(llvmModule), + llvmCtx(llvmModule.getContext()) { // If the module has no location information, there is nothing to do. if (!module->walk(interruptIfValidLocation).wasInterrupted()) return; + debugEmissionIsEnabled = true; - // TODO: Several parts of this are incorrect. Different source - // languages may interpret different parts of the debug information - // differently. Frontends will also want to pipe in various information, like - // flags. This is fine for now as we only emit line-table information and not - // types or variables. This should disappear as the debug information story - // evolves; i.e. when we have proper attributes for LLVM debug metadata. - compileUnit = builder.createCompileUnit( - llvm::dwarf::DW_LANG_C, - builder.createFile(llvmModule.getModuleIdentifier(), "/"), - /*Producer=*/"mlir", /*isOptimized=*/true, /*Flags=*/"", /*RV=*/0); + // TODO: The version information should be encoded on the LLVM module itself, + // not implicitly set here. // Mark this module as having debug information. StringRef debugVersionKey = "Debug Info Version"; @@ -62,24 +55,11 @@ } /// Finalize the translation of debug information. -void DebugTranslation::finalize() { builder.finalize(); } - -/// Attempt to extract a filename for the given loc. -static FileLineColLoc extractFileLoc(Location loc) { - if (auto fileLoc = loc.dyn_cast()) - return fileLoc; - if (auto nameLoc = loc.dyn_cast()) - return extractFileLoc(nameLoc.getChildLoc()); - if (auto opaqueLoc = loc.dyn_cast()) - return extractFileLoc(opaqueLoc.getFallbackLocation()); - return FileLineColLoc(); -} +void DebugTranslation::finalize() {} /// Translate the debug information for the given function. void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) { - // If the function doesn't have location information, there is nothing to - // translate. - if (!compileUnit || !func.walk(interruptIfValidLocation).wasInterrupted()) + if (!debugEmissionIsEnabled) return; // If we are to create debug info for the function, we need to ensure that all @@ -96,23 +76,150 @@ if (hasCallWithoutDebugInfo) return; - FileLineColLoc fileLoc = extractFileLoc(func.getLoc()); - auto *file = - translateFile(fileLoc ? fileLoc.getFilename().strref() : ""); - unsigned line = fileLoc ? fileLoc.getLine() : 0; - - // TODO: This is the bare essentials for now. We will likely end - // up with wrapper metadata around LLVMs metadata in the future, so this - // doesn't need to be smart until then. - llvm::DISubroutineType *type = - builder.createSubroutineType(builder.getOrCreateTypeArray(llvm::None)); - llvm::DISubprogram::DISPFlags spFlags = llvm::DISubprogram::SPFlagDefinition | - llvm::DISubprogram::SPFlagOptimized; - llvm::DISubprogram *program = - builder.createFunction(compileUnit, func.getName(), func.getName(), file, - line, type, line, llvm::DINode::FlagZero, spFlags); - llvmFunc.setSubprogram(program); - builder.finalizeSubprogram(program); + // Look for a sub program attached to the function. + auto spLoc = + func.getLoc()->findInstanceOf>(); + if (!spLoc) + return; + llvmFunc.setSubprogram(translate(spLoc.getMetadata())); +} + +//===----------------------------------------------------------------------===// +// Attributes +//===----------------------------------------------------------------------===// + +llvm::DIBasicType *DebugTranslation::translateImpl(DIBasicTypeAttr attr) { + return llvm::DIBasicType::get( + llvmCtx, attr.getTag(), attr.getName(), attr.getSizeInBits(), + /*AlignInBits=*/0, attr.getEncoding(), llvm::DINode::FlagZero); +} + +llvm::DICompileUnit *DebugTranslation::translateImpl(DICompileUnitAttr attr) { + llvm::DIBuilder builder(llvmModule); + return builder.createCompileUnit( + attr.getSourceLanguage(), translate(attr.getFile()), attr.getProducer(), + attr.getIsOptimized(), /*Flags=*/"", /*RV=*/0); +} + +llvm::DICompositeType * +DebugTranslation::translateImpl(DICompositeTypeAttr attr) { + SmallVector elements; + for (auto member : attr.getElements()) + elements.push_back(translate(member)); + return llvm::DICompositeType::get( + llvmCtx, attr.getTag(), attr.getName(), translate(attr.getFile()), + attr.getLine(), translate(attr.getScope()), /*BaseType=*/nullptr, + attr.getSizeInBits(), attr.getAlignInBits(), + /*OffsetInBits=*/0, /*Flags=*/llvm::DINode::FlagZero, + llvm::MDNode::get(llvmCtx, elements), + /*RuntimeLang=*/0, /*VTableHolder=*/nullptr); +} + +llvm::DIDerivedType *DebugTranslation::translateImpl(DIDerivedTypeAttr attr) { + return llvm::DIDerivedType::get( + llvmCtx, attr.getTag(), attr.getName(), /*File=*/nullptr, /*Line=*/0, + /*Scope=*/nullptr, translate(attr.getBaseType()), attr.getSizeInBits(), + attr.getAlignInBits(), attr.getOffsetInBits(), + /*DWARFAddressSpace=*/llvm::None, /*Flags=*/llvm::DINode::FlagZero); +} + +llvm::DIFile *DebugTranslation::translateImpl(DIFileAttr attr) { + return llvm::DIFile::get(llvmCtx, attr.getName(), attr.getDirectory()); +} + +llvm::DILexicalBlock *DebugTranslation::translateImpl(DILexicalBlockAttr attr) { + return llvm::DILexicalBlock::getDistinct(llvmCtx, translate(attr.getScope()), + translate(attr.getFile()), + attr.getLine(), attr.getColumn()); +} + +llvm::DILexicalBlockFile * +DebugTranslation::translateImpl(DILexicalBlockFileAttr attr) { + return llvm::DILexicalBlockFile::getDistinct( + llvmCtx, translate(attr.getScope()), translate(attr.getFile()), + attr.getDescriminator()); +} + +llvm::DILocalVariable * +DebugTranslation::translateImpl(DILocalVariableAttr attr) { + return llvm::DILocalVariable::get( + llvmCtx, translate(attr.getScope()), + llvm::MDString::get(llvmCtx, attr.getName()), translate(attr.getFile()), + attr.getLine(), translate(attr.getType()), attr.getArg(), + /*Flags=*/llvm::DINode::FlagZero, attr.getAlignInBits(), + /*Annotations=*/nullptr); +} + +llvm::DIScope *DebugTranslation::translateImpl(DIScopeAttr attr) { + return cast(translate(DINodeAttr(attr))); +} + +/// Return a new subprogram that is either distinct or not, depending on +/// `isDistinct`. +template +static llvm::DISubprogram *getSubprogram(bool isDistinct, Ts &&...args) { + if (isDistinct) + return llvm::DISubprogram::getDistinct(std::forward(args)...); + return llvm::DISubprogram::get(std::forward(args)...); +} + +llvm::DISubprogram *DebugTranslation::translateImpl(DISubprogramAttr attr) { + bool isDefinition = static_cast(attr.getSubprogramFlags() & + LLVM::DISubprogramFlags::Definition); + return getSubprogram( + isDefinition, llvmCtx, translate(attr.getScope()), + llvm::MDString::get(llvmCtx, attr.getName()), + llvm::MDString::get(llvmCtx, attr.getLinkageName()), + translate(attr.getFile()), attr.getLine(), translate(attr.getType()), + attr.getScopeLine(), /*ContainingType=*/nullptr, /*VirtualIndex=*/0, + /*ThisAdjustment=*/0, llvm::DINode::FlagZero, + static_cast(attr.getSubprogramFlags()), + translate(attr.getCompileUnit())); +} + +llvm::DISubrange *DebugTranslation::translateImpl(DISubrangeAttr attr) { + auto getMetadataOrNull = [&](IntegerAttr attr) -> llvm::Metadata * { + if (!attr) + return nullptr; + return llvm::ConstantAsMetadata::get(llvm::ConstantInt::getSigned( + llvm::Type::getInt64Ty(llvmCtx), attr.getInt())); + }; + return llvm::DISubrange::get(llvmCtx, getMetadataOrNull(attr.getCount()), + getMetadataOrNull(attr.getLowerBound()), + getMetadataOrNull(attr.getUpperBound()), + getMetadataOrNull(attr.getStride())); +} + +llvm::DISubroutineType * +DebugTranslation::translateImpl(DISubroutineTypeAttr attr) { + SmallVector types; + for (auto type : attr.getTypes()) + types.push_back(translate(type)); + return llvm::DISubroutineType::get( + llvmCtx, llvm::DINode::FlagZero, attr.getCallingConvention(), + llvm::DITypeRefArray(llvm::MDNode::get(llvmCtx, types))); +} + +llvm::DIType *DebugTranslation::translateImpl(DITypeAttr attr) { + return cast(translate(DINodeAttr(attr))); +} + +llvm::DINode *DebugTranslation::translate(DINodeAttr attr) { + if (!attr) + return nullptr; + // Check for a cached instance. + if (llvm::DINode *node = attrToNode.lookup(attr)) + return node; + + llvm::DINode *node = + TypeSwitch(attr) + .Case( + [&](auto attr) { return translateImpl(attr); }); + attrToNode.insert({attr, node}); + return node; } //===----------------------------------------------------------------------===// @@ -122,8 +229,12 @@ /// Translate the given location to an llvm debug location. const llvm::DILocation * DebugTranslation::translateLoc(Location loc, llvm::DILocalScope *scope) { - if (!compileUnit) + if (!debugEmissionIsEnabled) return nullptr; + + // Check for a scope encoded with the location. + if (auto scopedLoc = loc->findInstanceOf>()) + scope = cast(translate(scopedLoc.getMetadata())); return translateLoc(loc, scope, /*inlinedAt=*/nullptr); } @@ -148,7 +259,8 @@ } else if (auto fileLoc = loc.dyn_cast()) { auto *file = translateFile(fileLoc.getFilename()); - auto *fileScope = builder.createLexicalBlockFile(scope, file); + auto *fileScope = llvm::DILexicalBlockFile::get(llvmCtx, scope, file, + /*Discriminator=*/0); llvmLoc = llvm::DILocation::get(llvmCtx, fileLoc.getLine(), fileLoc.getColumn(), fileScope, const_cast(inlinedAt)); @@ -210,5 +322,5 @@ fileName = fileBuf; } } - return (file = builder.createFile(fileName, directory)); + return (file = llvm::DIFile::get(llvmCtx, fileName, directory)); } diff --git a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp --- a/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/ModuleTranslation.cpp @@ -1139,6 +1139,10 @@ return debugTranslation->translateLoc(loc, scope); } +llvm::Metadata *ModuleTranslation::translateDebugInfo(LLVM::DINodeAttr attr) { + return debugTranslation->translate(attr); +} + llvm::NamedMDNode * ModuleTranslation::getOrInsertNamedModuleMetadata(StringRef name) { return llvmModule->getOrInsertNamedMetadata(name); diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir --- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir +++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir @@ -17,9 +17,38 @@ llvm.return loc(unknown) } loc(unknown) -// CHECK-LABEL: define void @func_with_debug() -// CHECK-SAME: !dbg ![[FUNC_LOC:[0-9]+]] -llvm.func @func_with_debug() { + +#si64 = #llvm.di_basic_type< + tag = DW_TAG_base_type, name = "si64", sizeInBits = 0, + encoding = DW_ATE_signed +> +#file = #llvm.di_file<"foo.mlir" in "/test/"> +#cu = #llvm.di_compile_unit< + sourceLanguage = DW_LANG_C, file = #file, producer = "MLIR", + isOptimized = true, emissionKind = Full +> +#spType = #llvm.di_subroutine_type +#sp = #llvm.di_subprogram< + compileUnit = #cu, scope = #file, name = "intrinsics", linkageName = "intrinsics", + file = #file, line = 3, scopeLine = 3, subprogramFlags = "Definition|Optimized", type = #spType +> +#fileScope = #llvm.di_lexical_block_file +#variable = #llvm.di_local_variable + +// CHECK-LABEL: define void @func_with_debug( +// CHECK-SAME: i64 %[[ARG:.*]]) !dbg ![[FUNC_LOC:[0-9]+]] +llvm.func @func_with_debug(%arg: i64) { + // CHECK: %[[ALLOC:.*]] = alloca + %allocCount = llvm.mlir.constant(1 : i32) : i32 + %alloc = llvm.alloca %allocCount x i64 : (i32) -> !llvm.ptr + + // CHECK: call void @llvm.dbg.value(metadata i64 %[[ARG]], metadata ![[VAR_LOC:[0-9]+]], metadata !DIExpression()) + // CHECK: call void @llvm.dbg.addr(metadata ptr %[[ALLOC]], metadata ![[VAR_LOC]], metadata !DIExpression()) + // CHECK: call void @llvm.dbg.declare(metadata ptr %[[ALLOC]], metadata ![[VAR_LOC]], metadata !DIExpression()) + llvm.dbg.value #variable = %arg : i64 + llvm.dbg.addr #variable = %alloc : !llvm.ptr + llvm.dbg.declare #variable = %alloc : !llvm.ptr + // CHECK: call void @func_no_debug(), !dbg ![[CALLSITE_LOC:[0-9]+]] llvm.call @func_no_debug() : () -> () loc(callsite("mysource.cc":3:4 at "mysource.cc":5:6)) @@ -33,9 +62,19 @@ llvm.call @func_no_debug() : () -> () loc(fused[callsite("mysource.cc":1:1 at "mysource.cc":5:6), "mysource.cc":1:1]) llvm.return -} loc("foo.mlir":1:1) +} loc(fused<#sp>["foo.mlir":1:1]) + +// CHECK: ![[CU_LOC:.*]] = distinct !DICompileUnit(language: DW_LANG_C, file: ![[CU_FILE_LOC:.*]], producer: "MLIR", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK: ![[CU_FILE_LOC]] = !DIFile(filename: "foo.mlir", directory: "/test/") + +// CHECK: ![[FUNC_LOC]] = distinct !DISubprogram(name: "intrinsics", linkageName: "intrinsics", scope: ![[CU_FILE_LOC]], file: ![[CU_FILE_LOC]], line: 3, type: ![[FUNC_TYPE:.*]], scopeLine: 3, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: ![[CU_LOC]]) +// CHECK: ![[FUNC_TYPE]] = !DISubroutineType(cc: DW_CC_normal, types: ![[ARG_TYPES:.*]]) +// CHECK: ![[ARG_TYPES]] = !{![[ARG_TYPE:.*]]} +// CHECK: ![[ARG_TYPE]] = !DIBasicType(name: "si64", encoding: DW_ATE_signed) + +// CHECK: ![[VAR_LOC]] = !DILocalVariable(name: "arg", arg: 1, scope: ![[VAR_SCOPE:.*]], file: ![[CU_FILE_LOC]], line: 6, type: ![[ARG_TYPE]]) +// CHECK: ![[VAR_SCOPE]] = distinct !DILexicalBlockFile(scope: ![[FUNC_LOC]], file: ![[CU_FILE_LOC]], discriminator: 0) -// CHECK-DAG: ![[FUNC_LOC]] = distinct !DISubprogram{{.*}}, line: 1 // CHECK-DAG: ![[CALLSITE_LOC]] = !DILocation(line: 3, column: 4, // CHECK-DAG: ![[FILE_LOC]] = !DILocation(line: 1, column: 2, // CHECK-DAG: ![[NAMED_LOC]] = !DILocation(line: 10, column: 10 diff --git a/mlir/test/Target/LLVMIR/llvmir.mlir b/mlir/test/Target/LLVMIR/llvmir.mlir --- a/mlir/test/Target/LLVMIR/llvmir.mlir +++ b/mlir/test/Target/LLVMIR/llvmir.mlir @@ -1615,25 +1615,25 @@ // Constraints string is checked at LLVM InlineAsm instruction construction time. // So we can't just use "bar" everywhere, number of in/out arguments has to match. - // CHECK-NEXT: call void asm "foo", "r"(i32 {{.*}}), !dbg !7 + // CHECK-NEXT: call void asm "foo", "r"(i32 {{.*}}) llvm.inline_asm "foo", "r" %arg0 : (i32) -> () - // CHECK-NEXT: call i8 asm "foo", "=r,r"(i32 {{.*}}), !dbg !9 + // CHECK-NEXT: call i8 asm "foo", "=r,r"(i32 {{.*}}) %0 = llvm.inline_asm "foo", "=r,r" %arg0 : (i32) -> i8 - // CHECK-NEXT: call i8 asm "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !10 + // CHECK-NEXT: call i8 asm "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}) %1 = llvm.inline_asm "foo", "=r,r,r" %arg0, %arg0 : (i32, i32) -> i8 - // CHECK-NEXT: call i8 asm sideeffect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !11 + // CHECK-NEXT: call i8 asm sideeffect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}) %2 = llvm.inline_asm has_side_effects "foo", "=r,r,r" %arg0, %arg0 : (i32, i32) -> i8 - // CHECK-NEXT: call i8 asm alignstack "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !12 + // CHECK-NEXT: call i8 asm alignstack "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}) %3 = llvm.inline_asm is_align_stack "foo", "=r,r,r" %arg0, %arg0 : (i32, i32) -> i8 - // CHECK-NEXT: call i8 asm inteldialect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}), !dbg !13 + // CHECK-NEXT: call i8 asm inteldialect "foo", "=r,r,r"(i32 {{.*}}, i32 {{.*}}) %4 = llvm.inline_asm asm_dialect = "intel" "foo", "=r,r,r" %arg0, %arg0 : (i32, i32) -> i8 - // CHECK-NEXT: call { i8, i8 } asm "foo", "=r,=r,r"(i32 {{.*}}), !dbg !14 + // CHECK-NEXT: call { i8, i8 } asm "foo", "=r,=r,r"(i32 {{.*}}) %5 = llvm.inline_asm "foo", "=r,=r,r" %arg0 : (i32) -> !llvm.struct<(i8, i8)> llvm.return diff --git a/mlir/test/Target/LLVMIR/openmp-llvm.mlir b/mlir/test/Target/LLVMIR/openmp-llvm.mlir --- a/mlir/test/Target/LLVMIR/openmp-llvm.mlir +++ b/mlir/test/Target/LLVMIR/openmp-llvm.mlir @@ -356,11 +356,8 @@ // ----- // CHECK: %struct.ident_t = type -// CHECK: @[[$parallel_loc:.*]] = private unnamed_addr constant {{.*}} c";LLVMDialectModule;wsloop_simple;{{[0-9]+}};{{[0-9]+}};;\00" -// CHECK: @[[$parallel_loc_struct:.*]] = private unnamed_addr constant %struct.ident_t {{.*}} @[[$parallel_loc]] {{.*}} - -// CHECK: @[[$wsloop_loc:.*]] = private unnamed_addr constant {{.*}} c";LLVMDialectModule;wsloop_simple;{{[0-9]+}};{{[0-9]+}};;\00" -// CHECK: @[[$wsloop_loc_struct:.*]] = private unnamed_addr constant %struct.ident_t {{.*}} @[[$wsloop_loc]] {{.*}} +// CHECK: @[[$loc:.*]] = private unnamed_addr constant {{.*}} c";unknown;unknown;{{[0-9]+}};{{[0-9]+}};;\00" +// CHECK: @[[$loc_struct:.*]] = private unnamed_addr constant %struct.ident_t {{.*}} @[[$loc]] {{.*}} // CHECK-LABEL: @wsloop_simple llvm.func @wsloop_simple(%arg0: !llvm.ptr) { @@ -373,12 +370,12 @@ // The form of the emitted IR is controlled by OpenMPIRBuilder and // tested there. Just check that the right functions are called. // CHECK: call i32 @__kmpc_global_thread_num - // CHECK: call void @__kmpc_for_static_init_{{.*}}(ptr @[[$wsloop_loc_struct]], + // CHECK: call void @__kmpc_for_static_init_{{.*}}(ptr @[[$loc_struct]], %3 = llvm.mlir.constant(2.000000e+00 : f32) : f32 %4 = llvm.getelementptr %arg0[%arg1] : (!llvm.ptr, i64) -> !llvm.ptr llvm.store %3, %4 : !llvm.ptr omp.yield - // CHECK: call void @__kmpc_for_static_fini(ptr @[[$wsloop_loc_struct]], + // CHECK: call void @__kmpc_for_static_fini(ptr @[[$loc_struct]], }) {operand_segment_sizes = array} : (i64, i64, i64) -> () omp.terminator } @@ -2317,7 +2314,7 @@ // CHECK-SAME: (i32 %[[x:.+]], i32 %[[y:.+]], ptr %[[zaddr:.+]]) module attributes {llvm.target_triple = "x86_64-unknown-linux-gnu"} { llvm.func @omp_task(%x: i32, %y: i32, %zaddr: !llvm.ptr) { - // CHECK: %[[diff:.+]] = sub i32 %[[x]], %[[y]], + // CHECK: %[[diff:.+]] = sub i32 %[[x]], %[[y]] %diff = llvm.sub %x, %y : i32 // CHECK: store i32 %[[diff]], ptr %2 llvm.store %diff, %zaddr : !llvm.ptr diff --git a/mlir/test/Target/LLVMIR/openmp-nested.mlir b/mlir/test/Target/LLVMIR/openmp-nested.mlir --- a/mlir/test/Target/LLVMIR/openmp-nested.mlir +++ b/mlir/test/Target/LLVMIR/openmp-nested.mlir @@ -38,4 +38,4 @@ // CHECK: define internal void @[[inner1]] // CHECK: %[[structArg:.+]] = alloca { ptr } -// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @3, i32 1, ptr @[[inner2:.+]], ptr %[[structArg]]) +// CHECK: call void (ptr, i32, ptr, ...) @__kmpc_fork_call(ptr @1, i32 1, ptr @[[inner2:.+]], ptr %[[structArg]])