Skip to content

Commit c8e0364

Browse files
committed
[mlir][Target][LLVM] Adds an utility class for serializing operations to binary strings.
**For an explanation of these patches see D154153.** Commit message: This patch adds the utility base class `ModuleToObject`. This class provides an interface for compiling module operations into binary strings, by default this class serialize modules to LLVM bitcode. Reviewed By: mehdi_amini Differential Revision: https://reviews.llvm.org/D154100
1 parent f7dbc85 commit c8e0364

File tree

8 files changed

+475
-0
lines changed

8 files changed

+475
-0
lines changed
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//===- ModuleToObject.h - Module to object base class -----------*- C++ -*-===//
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+
// This file declares the base class for transforming operations into binary
10+
// objects.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef MLIR_TARGET_LLVM_MODULETOOBJECT_H
15+
#define MLIR_TARGET_LLVM_MODULETOOBJECT_H
16+
17+
#include "mlir/IR/Operation.h"
18+
#include "llvm/IR/Module.h"
19+
20+
namespace llvm {
21+
class TargetMachine;
22+
} // namespace llvm
23+
24+
namespace mlir {
25+
namespace LLVM {
26+
class ModuleTranslation;
27+
/// Utility base class for transforming operations into binary objects, by
28+
/// default it returns the serialized LLVM bitcode for the module. The
29+
/// operations being transformed must be translatable into LLVM IR.
30+
class ModuleToObject {
31+
public:
32+
ModuleToObject(Operation &module, StringRef triple, StringRef chip,
33+
StringRef features = {}, int optLevel = 3);
34+
virtual ~ModuleToObject() = default;
35+
36+
/// Returns the operation being serialized.
37+
Operation &getOperation();
38+
39+
/// Runs the serialization pipeline, returning `std::nullopt` on error.
40+
virtual std::optional<SmallVector<char, 0>> run();
41+
42+
protected:
43+
// Hooks to be implemented by derived classes.
44+
45+
/// Hook for loading bitcode files, returns std::nullopt on failure.
46+
virtual std::optional<SmallVector<std::unique_ptr<llvm::Module>>>
47+
loadBitcodeFiles(llvm::Module &module, llvm::TargetMachine &targetMachine) {
48+
return SmallVector<std::unique_ptr<llvm::Module>>();
49+
}
50+
51+
/// Hook for performing additional actions on a loaded bitcode file.
52+
virtual LogicalResult handleBitcodeFile(llvm::Module &module,
53+
llvm::TargetMachine &targetMachine) {
54+
return success();
55+
}
56+
57+
/// Hook for performing additional actions on the llvmModule pre linking.
58+
virtual void handleModulePreLink(llvm::Module &module,
59+
llvm::TargetMachine &targetMachine) {}
60+
61+
/// Hook for performing additional actions on the llvmModule post linking.
62+
virtual void handleModulePostLink(llvm::Module &module,
63+
llvm::TargetMachine &targetMachine) {}
64+
65+
/// Serializes the LLVM IR bitcode to an object file, by default it serializes
66+
/// to LLVM bitcode.
67+
virtual std::optional<SmallVector<char, 0>>
68+
moduleToObject(llvm::Module &llvmModule, llvm::TargetMachine &targetMachine);
69+
70+
protected:
71+
/// Create the target machine based on the target triple and chip.
72+
std::unique_ptr<llvm::TargetMachine> createTargetMachine();
73+
74+
/// Loads a bitcode file from path.
75+
std::unique_ptr<llvm::Module>
76+
loadBitcodeFile(llvm::LLVMContext &context,
77+
llvm::TargetMachine &targetMachine, StringRef path);
78+
79+
/// Loads multiple bitcode files.
80+
LogicalResult loadBitcodeFilesFromList(
81+
llvm::LLVMContext &context, llvm::TargetMachine &targetMachine,
82+
ArrayRef<std::string> fileList,
83+
SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,
84+
bool failureOnError = true);
85+
86+
/// Translates the operation to LLVM IR.
87+
std::unique_ptr<llvm::Module>
88+
translateToLLVMIR(llvm::LLVMContext &llvmContext);
89+
90+
/// Link the llvmModule to other bitcode file.
91+
LogicalResult linkFiles(llvm::Module &module,
92+
SmallVector<std::unique_ptr<llvm::Module>> &&libs);
93+
94+
/// Optimize the module.
95+
LogicalResult optimizeModule(llvm::Module &module,
96+
llvm::TargetMachine &targetMachine, int optL);
97+
98+
/// Utility function for translating to ISA, returns `std::nullopt` on
99+
/// failure.
100+
static std::optional<std::string>
101+
translateToISA(llvm::Module &llvmModule, llvm::TargetMachine &targetMachine);
102+
103+
protected:
104+
/// Module to transform to a binary object.
105+
Operation &module;
106+
107+
/// Target triple.
108+
StringRef triple;
109+
110+
/// Target chip.
111+
StringRef chip;
112+
113+
/// Target features.
114+
StringRef features;
115+
116+
/// Optimization level.
117+
int optLevel;
118+
};
119+
} // namespace LLVM
120+
} // namespace mlir
121+
122+
#endif // MLIR_TARGET_LLVM_MODULETOOBJECT_H

mlir/lib/Target/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
add_subdirectory(Cpp)
22
add_subdirectory(SPIRV)
33
add_subdirectory(LLVMIR)
4+
add_subdirectory(LLVM)

mlir/lib/Target/LLVM/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
add_mlir_library(MLIRTargetLLVM
2+
ModuleToObject.cpp
3+
4+
ADDITIONAL_HEADER_DIRS
5+
${MLIR_MAIN_INCLUDE_DIR}/mlir/Target/LLVM
6+
7+
DEPENDS
8+
intrinsics_gen
9+
10+
LINK_COMPONENTS
11+
Core
12+
IPO
13+
Passes
14+
Support
15+
Target
16+
TargetParser
17+
LINK_LIBS PUBLIC
18+
MLIRExecutionEngineUtils
19+
MLIRTargetLLVMIRExport
20+
)
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
//===- ModuleToObject.cpp - Module to object base class ---------*- C++ -*-===//
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+
// This file implements the base class for transforming Operations into binary
10+
// objects.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "mlir/Target/LLVM/ModuleToObject.h"
15+
16+
#include "mlir/ExecutionEngine/OptUtils.h"
17+
#include "mlir/IR/BuiltinOps.h"
18+
#include "mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h"
19+
#include "mlir/Target/LLVMIR/Export.h"
20+
#include "mlir/Target/LLVMIR/ModuleTranslation.h"
21+
22+
#include "llvm/Bitcode/BitcodeWriter.h"
23+
#include "llvm/IR/LegacyPassManager.h"
24+
#include "llvm/IRReader/IRReader.h"
25+
#include "llvm/Linker/Linker.h"
26+
#include "llvm/MC/TargetRegistry.h"
27+
#include "llvm/Support/FileSystem.h"
28+
#include "llvm/Support/Path.h"
29+
#include "llvm/Support/SourceMgr.h"
30+
#include "llvm/Support/raw_ostream.h"
31+
#include "llvm/Target/TargetMachine.h"
32+
#include "llvm/TargetParser/TargetParser.h"
33+
#include "llvm/Transforms/IPO/Internalize.h"
34+
35+
using namespace mlir;
36+
using namespace mlir::LLVM;
37+
38+
ModuleToObject::ModuleToObject(Operation &module, StringRef triple,
39+
StringRef chip, StringRef features, int optLevel)
40+
: module(module), triple(triple), chip(chip), features(features),
41+
optLevel(optLevel) {}
42+
43+
Operation &ModuleToObject::getOperation() { return module; }
44+
45+
std::unique_ptr<llvm::TargetMachine> ModuleToObject::createTargetMachine() {
46+
std::string error;
47+
// Load the target.
48+
const llvm::Target *target =
49+
llvm::TargetRegistry::lookupTarget(triple, error);
50+
if (!target) {
51+
getOperation().emitError() << "Failed to lookup target: " << error;
52+
return {};
53+
}
54+
55+
// Create the target machine using the target.
56+
llvm::TargetMachine *machine =
57+
target->createTargetMachine(triple, chip, features, {}, {});
58+
if (!machine) {
59+
getOperation().emitError() << "Failed to create the target machine.";
60+
return {};
61+
}
62+
return std::unique_ptr<llvm::TargetMachine>{machine};
63+
}
64+
65+
std::unique_ptr<llvm::Module>
66+
ModuleToObject::loadBitcodeFile(llvm::LLVMContext &context,
67+
llvm::TargetMachine &targetMachine,
68+
StringRef path) {
69+
llvm::SMDiagnostic error;
70+
std::unique_ptr<llvm::Module> library =
71+
llvm::getLazyIRFileModule(path, error, context);
72+
if (!library) {
73+
getOperation().emitError() << "Failed loading file from " << path
74+
<< ", error: " << error.getMessage();
75+
return nullptr;
76+
}
77+
if (failed(handleBitcodeFile(*library, targetMachine))) {
78+
return nullptr;
79+
}
80+
return library;
81+
}
82+
83+
LogicalResult ModuleToObject::loadBitcodeFilesFromList(
84+
llvm::LLVMContext &context, llvm::TargetMachine &targetMachine,
85+
ArrayRef<std::string> fileList,
86+
SmallVector<std::unique_ptr<llvm::Module>> &llvmModules,
87+
bool failureOnError) {
88+
for (const std::string &str : fileList) {
89+
// Test if the path exists, if it doesn't abort.
90+
StringRef pathRef = StringRef(str.data(), str.size());
91+
if (!llvm::sys::fs::is_regular_file(pathRef)) {
92+
getOperation().emitError()
93+
<< "File path: " << pathRef << " does not exist or is not a file.\n";
94+
return failure();
95+
}
96+
// Load the file or abort on error.
97+
if (auto bcFile = loadBitcodeFile(context, targetMachine, pathRef))
98+
llvmModules.push_back(std::move(bcFile));
99+
else if (failureOnError)
100+
return failure();
101+
}
102+
return success();
103+
}
104+
105+
std::unique_ptr<llvm::Module>
106+
ModuleToObject::translateToLLVMIR(llvm::LLVMContext &llvmContext) {
107+
return translateModuleToLLVMIR(&getOperation(), llvmContext);
108+
}
109+
110+
LogicalResult
111+
ModuleToObject::linkFiles(llvm::Module &module,
112+
SmallVector<std::unique_ptr<llvm::Module>> &&libs) {
113+
if (libs.empty())
114+
return success();
115+
llvm::Linker linker(module);
116+
for (std::unique_ptr<llvm::Module> &libModule : libs) {
117+
// This bitcode linking imports the library functions into the module,
118+
// allowing LLVM optimization passes (which must run after linking) to
119+
// optimize across the libraries and the module's code. We also only import
120+
// symbols if they are referenced by the module or a previous library since
121+
// there will be no other source of references to those symbols in this
122+
// compilation and since we don't want to bloat the resulting code object.
123+
bool err = linker.linkInModule(
124+
std::move(libModule), llvm::Linker::Flags::LinkOnlyNeeded,
125+
[](llvm::Module &m, const StringSet<> &gvs) {
126+
llvm::internalizeModule(m, [&gvs](const llvm::GlobalValue &gv) {
127+
return !gv.hasName() || (gvs.count(gv.getName()) == 0);
128+
});
129+
});
130+
// True is linker failure
131+
if (err) {
132+
getOperation().emitError("Unrecoverable failure during bitcode linking.");
133+
// We have no guaranties about the state of `ret`, so bail
134+
return failure();
135+
}
136+
}
137+
return success();
138+
}
139+
140+
LogicalResult ModuleToObject::optimizeModule(llvm::Module &module,
141+
llvm::TargetMachine &targetMachine,
142+
int optLevel) {
143+
if (optLevel < 0 || optLevel > 3)
144+
return getOperation().emitError()
145+
<< "Invalid optimization level: " << optLevel << ".";
146+
147+
targetMachine.setOptLevel(static_cast<llvm::CodeGenOpt::Level>(optLevel));
148+
149+
auto transformer =
150+
makeOptimizingTransformer(optLevel, /*sizeLevel=*/0, &targetMachine);
151+
auto error = transformer(&module);
152+
if (error) {
153+
InFlightDiagnostic mlirError = getOperation().emitError();
154+
llvm::handleAllErrors(
155+
std::move(error), [&mlirError](const llvm::ErrorInfoBase &ei) {
156+
mlirError << "Could not optimize LLVM IR: " << ei.message() << "\n";
157+
});
158+
return mlirError;
159+
}
160+
return success();
161+
}
162+
163+
std::optional<std::string>
164+
ModuleToObject::translateToISA(llvm::Module &llvmModule,
165+
llvm::TargetMachine &targetMachine) {
166+
std::string targetISA;
167+
llvm::raw_string_ostream stream(targetISA);
168+
169+
{ // Drop pstream after this to prevent the ISA from being stuck buffering
170+
llvm::buffer_ostream pstream(stream);
171+
llvm::legacy::PassManager codegenPasses;
172+
173+
if (targetMachine.addPassesToEmitFile(codegenPasses, pstream, nullptr,
174+
llvm::CGFT_AssemblyFile))
175+
return std::nullopt;
176+
177+
codegenPasses.run(llvmModule);
178+
}
179+
return stream.str();
180+
}
181+
182+
std::optional<SmallVector<char, 0>>
183+
ModuleToObject::moduleToObject(llvm::Module &llvmModule,
184+
llvm::TargetMachine &targetMachine) {
185+
SmallVector<char, 0> binaryData;
186+
// Write the LLVM module bitcode to a buffer.
187+
llvm::raw_svector_ostream outputStream(binaryData);
188+
llvm::WriteBitcodeToFile(llvmModule, outputStream);
189+
return binaryData;
190+
}
191+
192+
std::optional<SmallVector<char, 0>> ModuleToObject::run() {
193+
// Translate the module to LLVM IR.
194+
llvm::LLVMContext llvmContext;
195+
std::unique_ptr<llvm::Module> llvmModule = translateToLLVMIR(llvmContext);
196+
if (!llvmModule) {
197+
getOperation().emitError() << "Failed creating the llvm::Module.";
198+
return std::nullopt;
199+
}
200+
201+
// Create the target machine.
202+
std::unique_ptr<llvm::TargetMachine> targetMachine = createTargetMachine();
203+
if (!targetMachine)
204+
return std::nullopt;
205+
206+
// Set the data layout and target triple of the module.
207+
llvmModule->setDataLayout(targetMachine->createDataLayout());
208+
llvmModule->setTargetTriple(targetMachine->getTargetTriple().getTriple());
209+
210+
// Link bitcode files.
211+
handleModulePreLink(*llvmModule, *targetMachine);
212+
{
213+
auto libs = loadBitcodeFiles(*llvmModule, *targetMachine);
214+
if (!libs)
215+
return std::nullopt;
216+
if (libs->size())
217+
if (failed(linkFiles(*llvmModule, std::move(*libs))))
218+
return std::nullopt;
219+
handleModulePostLink(*llvmModule, *targetMachine);
220+
}
221+
222+
// Optimize the module.
223+
if (failed(optimizeModule(*llvmModule, *targetMachine, optLevel)))
224+
return std::nullopt;
225+
226+
// Return the serialized object.
227+
return moduleToObject(*llvmModule, *targetMachine);
228+
}

mlir/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_subdirectory(Pass)
1616
add_subdirectory(Support)
1717
add_subdirectory(Rewrite)
1818
add_subdirectory(TableGen)
19+
add_subdirectory(Target)
1920
add_subdirectory(Transforms)
2021

2122
if(MLIR_ENABLE_EXECUTION_ENGINE)

mlir/unittests/Target/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
add_subdirectory(LLVM)

0 commit comments

Comments
 (0)