-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathvpux-translate.cpp
285 lines (230 loc) · 10.2 KB
/
vpux-translate.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//
// Copyright (C) 2022 Intel Corporation.
// SPDX-License-Identifier: Apache 2.0
//
#include "vpux/compiler/NPU40XX/dialect/ELF/export.hpp"
#include "vpux/compiler/act_kernels/shave_binary_resources.h"
#include "vpux/compiler/dialect/ELFNPU37XX/export.hpp"
#include "vpux/compiler/dialect/ELFNPU37XX/import.hpp"
#include "vpux/compiler/dialect/VPU/IR/attributes.hpp"
#include "vpux/compiler/frontend/IE.hpp"
#include "vpux/compiler/init.hpp"
#include "vpux/compiler/interfaces_registry.hpp"
#include "vpux/compiler/tools/options.hpp"
#include "vpux/hwtest/hwtest.hpp"
#include "vpux/utils/core/format.hpp"
// TODO: E66812, it should be sufficient to have warnings disabled for 3-rd parties
// in CMake but it does not work for early versions of MSVC 2019
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4146)
#endif
#include <mlir/ExecutionEngine/ExecutionEngine.h>
#include <mlir/ExecutionEngine/OptUtils.h>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <mlir/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.h>
#include <mlir/Target/LLVMIR/Export.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/TargetSelect.h>
#include <mlir/IR/Dialect.h>
#include <mlir/Tools/mlir-opt/MlirOptMain.h>
#include <mlir/Tools/mlir-translate/MlirTranslateMain.h>
#include <mlir/Tools/mlir-translate/Translation.h>
#include <openvino/runtime/core.hpp>
#include <openvino/runtime/runtime.hpp>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
using namespace vpux;
namespace {
llvm::cl::opt<bool> vpuxProfiling("vpux-profiling", llvm::cl::desc("Add profilingOutput region to the imported IR"),
llvm::cl::init(false));
llvm::cl::opt<std::string> vpuxBounds("set-upper-bounds",
llvm::cl::desc("Set upper bounds for dynamic shapes. E.g: --set-upper-bounds=\"1 "
"18 3 3\", no commas between bounds values"),
llvm::cl::init(""));
llvm::cl::opt<DummyOpMode> enableDummyOpReplacement{
"dummy-op-replacement", llvm::cl::desc("Replace unsupported SW Kernel ops with Dummy ones"),
llvm::cl::init(DummyOpMode::DISABLED),
llvm::cl::values(
clEnumValN(DummyOpMode::DISABLED, "DISABLED", "No replacement for unsupported SW Kernels ops"),
clEnumValN(DummyOpMode::ENABLED, "ENABLED", "Replace unsupported SW Kernels ops with Dummy ops"))};
llvm::cl::opt<bool> dynamicShapeToStatic{
"dynamic-shape-to-static",
llvm::cl::desc("If set, we immediately apply the bounds so that we "
"have a static shape for further work. "
"If not, we store the related information in TensorAttr and the IE representation looks "
"like this: tensor<1x?x3xf32, {bounds = [1, 18, 3], ..}>."),
llvm::cl::init(false)};
enum class NetworkIOType { INPUT, OUTPUT };
//
// import-IE
//
mlir::OwningOpRef<mlir::ModuleOp> importIE(llvm::SourceMgr& sourceMgr, mlir::MLIRContext* ctx) {
if (sourceMgr.getNumBuffers() != 1) {
printTo(llvm::errs(),
"Invalid source file for IE IR, it has unsupported number of "
"buffers {0}",
sourceMgr.getNumBuffers());
return nullptr;
}
const auto netFileName = sourceMgr.getMemoryBuffer(1)->getBufferIdentifier();
if (netFileName.empty()) {
printTo(llvm::errs(), "Invalid source file for IE IR, not a file");
return nullptr;
}
ov::Core core;
std::shared_ptr<ov::Model> model;
try {
model = core.read_model(netFileName.str());
} catch (const std::exception& ex) {
printTo(llvm::errs(), "Failed to read model {0} : {1}", netFileName, ex.what());
return nullptr;
}
if (!vpuxBounds.empty()) {
std::stringstream stream(vpuxBounds);
std::vector<int> boundsFromAnOption;
int eachBoundValue = 0;
while (stream >> eachBoundValue) {
boundsFromAnOption.push_back(eachBoundValue);
}
std::map<ov::Output<ov::Node>, ov::PartialShape> newShapes;
for (const ov::Output<ov::Node>& input : model->inputs()) {
ov::PartialShape shape = input.get_partial_shape();
for (const auto& dim : shape | indexed) {
if (dim.value().is_dynamic()) {
dim.value() = ov::Dimension(0, boundsFromAnOption[dim.index()]);
}
}
newShapes[input] = shape;
}
model->reshape(newShapes);
}
mlir::OwningOpRef<mlir::ModuleOp> module;
try {
mlir::DefaultTimingManager tm;
auto rootTiming = tm.getRootScope();
// Do NOT share OV constants with MLIR here: the OV model is created
// locally and deleted at the end of the function scope, while MLIR
// module would still be used in the outer scope. deep-copying the
// constants in MLIR protects the code from use-after-free errors.
constexpr bool useSharedConstants = false;
module = IE::importNetwork(ctx, model, IE::buildOVParams(model), IE::buildOVResults(model), useSharedConstants,
rootTiming, vpuxProfiling, enableDummyOpReplacement, dynamicShapeToStatic);
} catch (const std::exception& ex) {
printTo(llvm::errs(), "Failed to translate IE IR {0} to MLIR : {1}", netFileName, ex.what());
return nullptr;
}
return module;
}
//
// import-ELF
//
mlir::OwningOpRef<mlir::ModuleOp> importELF(llvm::SourceMgr& sourceMgr, mlir::MLIRContext* ctx) {
if (sourceMgr.getNumBuffers() != 1) {
printTo(llvm::errs(),
"Invalid source file for elf, it has unsupported number of "
"buffers {0}",
sourceMgr.getNumBuffers());
return nullptr;
}
const auto elfFileName = sourceMgr.getMemoryBuffer(1)->getBufferIdentifier();
if (elfFileName.empty()) {
printTo(llvm::errs(), "Invalid source file for elf, not a file");
return nullptr;
}
mlir::OwningOpRef<mlir::ModuleOp> module;
try {
module = ELFNPU37XX::importELF(ctx, elfFileName.str());
} catch (const std::exception& ex) {
printTo(llvm::errs(), "Failed to translate elf {0} to MLIR : {1}", elfFileName, ex.what());
return nullptr;
}
return module;
}
//
// export-ELF
//
mlir::LogicalResult exportELF(mlir::ModuleOp module, llvm::raw_ostream& output) {
auto compilationMode = VPU::getCompilationMode(module.getOperation());
if (compilationMode == VPU::CompilationMode::ShaveCodeGen) {
ShaveBinaryResources::loadElfData(module);
}
mlir::DefaultTimingManager tm;
auto arch = VPU::getArch(module.getOperation());
if (arch == VPU::ArchKind::NPU37XX) {
const auto buf = ELFNPU37XX::exportToELF(module);
output.write(reinterpret_cast<const char*>(buf.data()), buf.size());
} else if (arch >= VPU::ArchKind::NPU40XX) {
const auto buf = ELF::exportToELF(module);
output.write(reinterpret_cast<const char*>(buf.data()), buf.size());
} else {
VPUX_THROW("ELF Flow not supported for ARCH {0}", VPU::stringifyArchKind(arch));
}
return mlir::success();
}
//
// export-LLVMIR
//
int dumpLLVMIR(mlir::ModuleOp module, llvm::raw_ostream& output) {
Logger log("dumpLLVMIR", vpux::LogLevel::Info);
// Register the translation to LLVM IR with the MLIR context.
mlir::registerLLVMDialectTranslation(*module->getContext());
// Convert the module to LLVM IR in a new LLVM IR context.
llvm::LLVMContext llvmContext;
auto llvmModule = mlir::translateModuleToLLVMIR(module, llvmContext);
if (!llvmModule) {
log.error("Failed to emit LLVM IR\n");
return -1;
}
// Initialize LLVM targets.
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
mlir::ExecutionEngine::setupTargetTripleAndDataLayout(llvmModule.get(), nullptr);
/// Optionally run an optimization pipeline over the llvm module.
auto optPipeline = mlir::makeOptimizingTransformer(
/*optLevel=*/0, // enableOpt ? 3 : 0, // Note: seems small diffs on my tests
/*sizeLevel=*/0,
/*targetMachine=*/nullptr);
if (auto err = optPipeline(llvmModule.get())) {
log.error("Failed to optimize LLVM IR {0}\n", err);
return -1;
}
output << "dumpLLVMIR() for output:\n" << *llvmModule << "\n";
return 0;
}
mlir::LogicalResult exportLLVMIR(mlir::ModuleOp module, llvm::raw_ostream& output) {
dumpLLVMIR(module, output);
return mlir::success();
}
} // namespace
int main(int argc, char* argv[]) {
try {
// TODO(E#84874):
// currently, arch is used for both import and export
// however, it would be a better option to extract arch info from module for the export
const auto arch = vpux::parseArchKind(argc, argv);
auto dialectRegistration = [&](mlir::DialectRegistry& registry) {
registry = vpux::createDialectRegistry(vpux::DummyOpMode::ENABLED);
auto interfacesRegistry = vpux::createInterfacesRegistry(arch);
interfacesRegistry->registerInterfaces(registry);
};
mlir::TranslateToMLIRRegistration("import-IE", "Translate OV IR to IE dialect", importIE, dialectRegistration);
mlir::TranslateToMLIRRegistration("import-HWTEST", "Translate PSS test case described by config.json to blob",
importHWTEST, dialectRegistration);
mlir::TranslateToMLIRRegistration("import-ELF", "Translate blob to ELF dialect", importELF,
dialectRegistration);
mlir::TranslateFromMLIRRegistration("export-ELF", "Translate ELF dialect to blob", exportELF,
dialectRegistration);
mlir::TranslateFromMLIRRegistration("export-LLVMIR", "Translate LLVMIR dialect to blob", exportLLVMIR,
dialectRegistration);
return mlir::asMainReturnCode(mlir::mlirTranslateMain(argc, argv, "NPU Translation Testing Tool"));
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
}