Skip to content

Commit c33d697

Browse files
committed
[mlir] Add support for basic location translation to LLVM.
Summary: This revision adds basic support for emitting line table information when exporting to LLVMIR. We don't yet have a story for supporting all of the LLVM debug metadata, so this revision stubs some features(like subprograms) to enable emitting line tables. Differential Revision: https://reviews.llvm.org/D73934
1 parent c3f0ed7 commit c33d697

File tree

12 files changed

+374
-55
lines changed

12 files changed

+374
-55
lines changed

mlir/include/mlir/Target/LLVMIR/ModuleTranslation.h

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,17 @@ class Operation;
3232

3333
namespace LLVM {
3434

35+
namespace detail {
36+
class DebugTranslation;
37+
} // end namespace detail
38+
3539
class LLVMFuncOp;
3640

37-
// Implementation class for module translation. Holds a reference to the module
38-
// being translated, and the mappings between the original and the translated
39-
// functions, basic blocks and values. It is practically easier to hold these
40-
// mappings in one class since the conversion of control flow operations
41-
// needs to look up block and function mappings.
41+
/// Implementation class for module translation. Holds a reference to the module
42+
/// being translated, and the mappings between the original and the translated
43+
/// functions, basic blocks and values. It is practically easier to hold these
44+
/// mappings in one class since the conversion of control flow operations
45+
/// needs to look up block and function mappings.
4246
class ModuleTranslation {
4347
public:
4448
template <typename T = ModuleTranslation>
@@ -51,8 +55,7 @@ class ModuleTranslation {
5155
if (!llvmModule)
5256
return nullptr;
5357

54-
T translator(m);
55-
translator.llvmModule = std::move(llvmModule);
58+
T translator(m, std::move(llvmModule));
5659
translator.convertGlobals();
5760
if (failed(translator.convertFunctions()))
5861
return nullptr;
@@ -65,14 +68,12 @@ class ModuleTranslation {
6568
static Block &getModuleBody(Operation *m) { return m->getRegion(0).front(); }
6669

6770
protected:
68-
// Translate the given MLIR module expressed in MLIR LLVM IR dialect into an
69-
// LLVM IR module. The MLIR LLVM IR dialect holds a pointer to an
70-
// LLVMContext, the LLVM IR module will be created in that context.
71-
explicit ModuleTranslation(Operation *module) : mlirModule(module) {
72-
assert(satisfiesLLVMModule(mlirModule) &&
73-
"mlirModule should honor LLVM's module semantics.");
74-
}
75-
virtual ~ModuleTranslation() {}
71+
/// Translate the given MLIR module expressed in MLIR LLVM IR dialect into an
72+
/// LLVM IR module. The MLIR LLVM IR dialect holds a pointer to an
73+
/// LLVMContext, the LLVM IR module will be created in that context.
74+
ModuleTranslation(Operation *module,
75+
std::unique_ptr<llvm::Module> llvmModule);
76+
virtual ~ModuleTranslation();
7677

7778
virtual LogicalResult convertOperation(Operation &op,
7879
llvm::IRBuilder<> &builder);
@@ -94,15 +95,18 @@ class ModuleTranslation {
9495
llvm::Constant *getLLVMConstant(llvm::Type *llvmType, Attribute attr,
9596
Location loc);
9697

97-
// Original and translated module.
98+
/// Original and translated module.
9899
Operation *mlirModule;
99100
std::unique_ptr<llvm::Module> llvmModule;
100101

101-
// Mappings between llvm.mlir.global definitions and corresponding globals.
102+
/// A converter for translating debug information.
103+
std::unique_ptr<detail::DebugTranslation> debugTranslation;
104+
105+
/// Mappings between llvm.mlir.global definitions and corresponding globals.
102106
DenseMap<Operation *, llvm::GlobalValue *> globalsMapping;
103107

104108
protected:
105-
// Mappings between original and translated values, used for lookups.
109+
/// Mappings between original and translated values, used for lookups.
106110
llvm::StringMap<llvm::Function *> functionMapping;
107111
DenseMap<Value, llvm::Value *> valueMapping;
108112
DenseMap<Block *, llvm::BasicBlock *> blockMapping;

mlir/include/mlir/Transforms/Passes.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ std::unique_ptr<OpPassBase<FuncOp>> createAffineDataCopyGenerationPass(
118118
std::unique_ptr<OpPassBase<FuncOp>> createMemRefDataFlowOptPass();
119119

120120
/// Creates a pass to strip debug information from a function.
121-
std::unique_ptr<OpPassBase<FuncOp>> createStripDebugInfoPass();
121+
std::unique_ptr<Pass> createStripDebugInfoPass();
122122

123123
/// Creates a pass which tests loop fusion utilities.
124124
std::unique_ptr<OpPassBase<FuncOp>> createTestLoopFusionPass();

mlir/lib/Target/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
add_llvm_library(MLIRTargetLLVMIRModuleTranslation
2+
LLVMIR/DebugTranslation.cpp
23
LLVMIR/ModuleTranslation.cpp
34

45
ADDITIONAL_HEADER_DIRS

mlir/lib/Target/LLVMIR/ConvertToNVVMIR.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,8 @@ static llvm::Intrinsic::ID getShflBflyIntrinsicId(llvm::Type *resultType,
4848

4949
namespace {
5050
class ModuleTranslation : public LLVM::ModuleTranslation {
51-
5251
public:
53-
explicit ModuleTranslation(Operation *module)
54-
: LLVM::ModuleTranslation(module) {}
55-
~ModuleTranslation() override {}
52+
using LLVM::ModuleTranslation::ModuleTranslation;
5653

5754
protected:
5855
LogicalResult convertOperation(Operation &opInst,
@@ -66,7 +63,6 @@ class ModuleTranslation : public LLVM::ModuleTranslation {
6663
} // namespace
6764

6865
std::unique_ptr<llvm::Module> mlir::translateModuleToNVVMIR(Operation *m) {
69-
ModuleTranslation translation(m);
7066
auto llvmModule =
7167
LLVM::ModuleTranslation::translateModule<ModuleTranslation>(m);
7268
if (!llvmModule)

mlir/lib/Target/LLVMIR/ConvertToROCDLIR.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,8 @@ static llvm::Value *createDeviceFunctionCall(llvm::IRBuilder<> &builder,
5757

5858
namespace {
5959
class ModuleTranslation : public LLVM::ModuleTranslation {
60-
6160
public:
62-
explicit ModuleTranslation(Operation *module)
63-
: LLVM::ModuleTranslation(module) {}
64-
~ModuleTranslation() override {}
61+
using LLVM::ModuleTranslation::ModuleTranslation;
6562

6663
protected:
6764
LogicalResult convertOperation(Operation &opInst,
@@ -75,8 +72,6 @@ class ModuleTranslation : public LLVM::ModuleTranslation {
7572
} // namespace
7673

7774
std::unique_ptr<llvm::Module> mlir::translateModuleToROCDLIR(Operation *m) {
78-
ModuleTranslation translation(m);
79-
8075
// lower MLIR (with RODL Dialect) to LLVM IR (with ROCDL intrinsics)
8176
auto llvmModule =
8277
LLVM::ModuleTranslation::translateModule<ModuleTranslation>(m);
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
//===- DebugTranslation.cpp - MLIR to LLVM Debug conversion ---------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DebugTranslation.h"
10+
#include "mlir/Dialect/LLVMIR/LLVMDialect.h"
11+
#include "llvm/IR/Metadata.h"
12+
#include "llvm/IR/Module.h"
13+
#include "llvm/Support/FileSystem.h"
14+
#include "llvm/Support/Path.h"
15+
16+
using namespace mlir;
17+
using namespace mlir::LLVM;
18+
using namespace mlir::LLVM::detail;
19+
20+
/// A utility walker that interrupts if the operation has valid debug
21+
/// information.
22+
static WalkResult interruptIfValidLocation(Operation *op) {
23+
return op->getLoc().isa<UnknownLoc>() ? WalkResult::advance()
24+
: WalkResult::interrupt();
25+
}
26+
27+
DebugTranslation::DebugTranslation(Operation *module, llvm::Module &llvmModule)
28+
: builder(llvmModule), llvmCtx(llvmModule.getContext()),
29+
compileUnit(nullptr) {
30+
31+
// If the module has no location information, there is nothing to do.
32+
if (!module->walk(interruptIfValidLocation).wasInterrupted())
33+
return;
34+
35+
// TODO(riverriddle) Several parts of this are incorrect. Different source
36+
// languages may interpret different parts of the debug information
37+
// differently. Frontends will also want to pipe in various information, like
38+
// flags. This is fine for now as we only emit line-table information and not
39+
// types or variables. This should disappear as the debug information story
40+
// evolves; i.e. when we have proper attributes for LLVM debug metadata.
41+
compileUnit = builder.createCompileUnit(
42+
llvm::dwarf::DW_LANG_C,
43+
builder.createFile(llvmModule.getModuleIdentifier(), "/"),
44+
/*Producer=*/"mlir", /*isOptimized=*/true, /*Flags=*/"", /*RV=*/0);
45+
46+
// Mark this module as having debug information.
47+
StringRef debugVersionKey = "Debug Info Version";
48+
if (!llvmModule.getModuleFlag(debugVersionKey))
49+
llvmModule.addModuleFlag(llvm::Module::Warning, debugVersionKey,
50+
llvm::DEBUG_METADATA_VERSION);
51+
}
52+
53+
/// Finalize the translation of debug information.
54+
void DebugTranslation::finalize() { builder.finalize(); }
55+
56+
/// Attempt to extract a filename for the given loc.
57+
static FileLineColLoc extractFileLoc(Location loc) {
58+
if (auto fileLoc = loc.dyn_cast<FileLineColLoc>())
59+
return fileLoc;
60+
if (auto nameLoc = loc.dyn_cast<NameLoc>())
61+
return extractFileLoc(nameLoc.getChildLoc());
62+
if (auto opaqueLoc = loc.dyn_cast<OpaqueLoc>())
63+
return extractFileLoc(opaqueLoc.getFallbackLocation());
64+
return FileLineColLoc();
65+
}
66+
67+
/// Translate the debug information for the given function.
68+
void DebugTranslation::translate(LLVMFuncOp func, llvm::Function &llvmFunc) {
69+
// If the function doesn't have location information, there is nothing to
70+
// translate.
71+
if (!compileUnit || !func.walk(interruptIfValidLocation).wasInterrupted())
72+
return;
73+
74+
FileLineColLoc fileLoc = extractFileLoc(func.getLoc());
75+
auto *file = translateFile(fileLoc ? fileLoc.getFilename() : "<unknown>");
76+
unsigned line = fileLoc ? fileLoc.getLine() : 0;
77+
78+
// TODO(riverriddle) This is the bare essentials for now. We will likely end
79+
// up with wrapper metadata around LLVMs metadata in the future, so this
80+
// doesn't need to be smart until then.
81+
llvm::DISubroutineType *type =
82+
builder.createSubroutineType(builder.getOrCreateTypeArray(llvm::None));
83+
llvm::DISubprogram::DISPFlags spFlags = llvm::DISubprogram::SPFlagDefinition |
84+
llvm::DISubprogram::SPFlagOptimized;
85+
llvm::DISubprogram *program =
86+
builder.createFunction(compileUnit, func.getName(), func.getName(), file,
87+
line, type, line, llvm::DINode::FlagZero, spFlags);
88+
llvmFunc.setSubprogram(program);
89+
builder.finalizeSubprogram(program);
90+
}
91+
92+
//===----------------------------------------------------------------------===//
93+
// Locations
94+
//===----------------------------------------------------------------------===//
95+
96+
/// Translate the given location to an llvm debug location.
97+
const llvm::DILocation *
98+
DebugTranslation::translateLoc(Location loc, llvm::DILocalScope *scope) {
99+
if (!compileUnit)
100+
return nullptr;
101+
return translateLoc(loc, scope, /*inlinedAt=*/nullptr);
102+
}
103+
104+
/// Translate the given location to an llvm DebugLoc.
105+
const llvm::DILocation *
106+
DebugTranslation::translateLoc(Location loc, llvm::DILocalScope *scope,
107+
const llvm::DILocation *inlinedAt) {
108+
// LLVM doesn't have a representation for unknown.
109+
if (!scope || loc.isa<UnknownLoc>())
110+
return nullptr;
111+
112+
// Check for a cached instance.
113+
const auto *&llvmLoc = locationToLoc[std::make_pair(loc, scope)];
114+
if (llvmLoc)
115+
return llvmLoc;
116+
117+
switch (loc->getKind()) {
118+
case StandardAttributes::CallSiteLocation: {
119+
auto callLoc = loc.dyn_cast<CallSiteLoc>();
120+
121+
// For callsites, the caller is fed as the inlinedAt for the callee.
122+
const auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt);
123+
llvmLoc = translateLoc(callLoc.getCallee(), scope, callerLoc);
124+
break;
125+
}
126+
case StandardAttributes::FileLineColLocation: {
127+
auto fileLoc = loc.dyn_cast<FileLineColLoc>();
128+
auto *file = translateFile(fileLoc.getFilename());
129+
auto *fileScope = builder.createLexicalBlockFile(scope, file);
130+
llvmLoc = llvm::DILocation::get(llvmCtx, fileLoc.getLine(),
131+
fileLoc.getColumn(), fileScope,
132+
const_cast<llvm::DILocation *>(inlinedAt));
133+
break;
134+
}
135+
case StandardAttributes::FusedLocation: {
136+
auto fusedLoc = loc.dyn_cast<FusedLoc>();
137+
ArrayRef<Location> locations = fusedLoc.getLocations();
138+
139+
// For fused locations, merge each of the nodes.
140+
llvmLoc = translateLoc(locations.front(), scope, inlinedAt);
141+
for (Location locIt : locations.drop_front()) {
142+
llvmLoc = llvm::DILocation::getMergedLocation(
143+
llvmLoc, translateLoc(locIt, scope, inlinedAt));
144+
}
145+
break;
146+
}
147+
case StandardAttributes::NameLocation:
148+
llvmLoc = translateLoc(loc.cast<NameLoc>().getChildLoc(), scope, inlinedAt);
149+
break;
150+
case StandardAttributes::OpaqueLocation:
151+
llvmLoc = translateLoc(loc.cast<OpaqueLoc>().getFallbackLocation(), scope,
152+
inlinedAt);
153+
break;
154+
default:
155+
llvm_unreachable("unknown location kind");
156+
}
157+
return llvmLoc;
158+
}
159+
160+
/// Create an llvm debug file for the given file path.
161+
llvm::DIFile *DebugTranslation::translateFile(StringRef fileName) {
162+
auto *&file = fileMap[fileName];
163+
if (file)
164+
return file;
165+
166+
// Make sure the current working directory is up-to-date.
167+
if (currentWorkingDir.empty())
168+
llvm::sys::fs::current_path(currentWorkingDir);
169+
170+
StringRef directory = currentWorkingDir;
171+
SmallString<128> dirBuf;
172+
SmallString<128> fileBuf;
173+
if (llvm::sys::path::is_absolute(fileName)) {
174+
// Strip the common prefix (if it is more than just "/") from current
175+
// directory and FileName for a more space-efficient encoding.
176+
auto fileIt = llvm::sys::path::begin(fileName);
177+
auto fileE = llvm::sys::path::end(fileName);
178+
auto curDirIt = llvm::sys::path::begin(directory);
179+
auto curDirE = llvm::sys::path::end(directory);
180+
for (; curDirIt != curDirE && *curDirIt == *fileIt; ++curDirIt, ++fileIt)
181+
llvm::sys::path::append(dirBuf, *curDirIt);
182+
if (std::distance(llvm::sys::path::begin(directory), curDirIt) == 1) {
183+
// Don't strip the common prefix if it is only the root "/" since that
184+
// would make LLVM diagnostic locations confusing.
185+
directory = StringRef();
186+
} else {
187+
for (; fileIt != fileE; ++fileIt)
188+
llvm::sys::path::append(fileBuf, *fileIt);
189+
directory = dirBuf;
190+
fileName = fileBuf;
191+
}
192+
}
193+
return (file = builder.createFile(fileName, directory));
194+
}

0 commit comments

Comments
 (0)