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
|
/*========================== begin_copyright_notice ============================
Copyright (C) 2022 Intel Corporation
SPDX-License-Identifier: MIT
============================= end_copyright_notice ===========================*/
#include <igc/Options/Options.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringExtras.h>
#include <llvm/ADT/StringRef.h>
#include <llvm/ADT/Twine.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/MD5.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/ToolOutputFile.h>
#include <llvmWrapper/Support/TargetRegistry.h>
#include <llvmWrapper/Target/TargetMachine.h>
#include <llvm/Transforms/Utils/Cloning.h>
#include <Probe/Assertion.h>
using namespace llvm;
static cl::opt<std::string>
SymbolPrefix("symb", cl::desc("prefix symbol for emmiting BiF functions "),
cl::value_desc("prefix"), cl::init("VCEmulation64RawData"));
// Structure that holds list of platforms and an ordinal number of a
// corresponding unique BiF.
struct UniquePltf {
size_t Num;
std::vector<std::string> Platforms;
};
// Transient structure to hold platform name and path to the platform-specific
// emulation BiF.
struct PlatformModule {
std::string Platform;
std::string ModulePath;
PlatformModule(const std::string &PlatformIn, const std::string &ModulePathIn)
: Platform{PlatformIn}, ModulePath{ModulePathIn} {};
};
// Gets contents of file, reporting an error if problems are detected.
std::unique_ptr<MemoryBuffer> vcbGetFile(StringRef fileName) {
auto FileOrError = MemoryBuffer::getFileOrSTDIN(fileName);
if (!FileOrError) {
errs() << FileOrError.getError().message();
report_fatal_error("vcb: can't open file " + fileName);
}
return std::move(FileOrError.get());
}
// Generates C++ source file with a procedure that allows VC backend
// to select platform-specific emulation BiF module during the runtime.
// Arguments:
// \p HashedUniquePltfs - mapping between bitcode and list of platforms
// for which this bitcode represents platform-specific
// emulation BiF.
// \p Output - path to the output .cpp file.
//
// The generated code looks like this:
//
// ```
// static unsigned char VCEmulation64RawDataPLTF0 = { ... }
// static unsigned char VCEmulation64RawDataPLTF1 = { ... }
//
// llvm::StringRef getVCEmulation64RawDataImpl(llvm::StringRef CPUStr) {
// if (CPUStr.equals("PLTF_A") || ... )
// return {reinterpret_cast<const char*>(VCEmulation64RawDataPLTF0),
// VCEmulation64RawDataPLTF0_size};
// if (CPUStr.equals("PLTF_B") || ... )
// return ...
// ...
// }
// ```
static std::string renderPlatformLiteral(StringRef Platform) {
return (Twine("\"") + Platform + "\"").str();
};
void generateBifSelectionProcedure(
std::map<std::string, UniquePltf> &HashedUniquePltfs, std::string &Output) {
int FD;
auto EC = llvm::sys::fs::openFileForWrite(Output, FD);
if (EC)
report_fatal_error(llvm::StringRef("vcb : can't open output file " + Output));
raw_fd_ostream OS{FD, /*shouldClose=*/true};
OS << "// This file is auto generated by vcb tool, DO NOT EDIT\n\n";
OS << "#include \"IGC/common/StringMacros.hpp\"\n";
OS << "#include \"llvm/ADT/StringRef.h\"\n";
OS << "\n";
// For each unique bitcode generate a C array that contains binary data
// representing the bitcode
for (const auto &[ByteCode, UniPltf] : HashedUniquePltfs) {
std::string Pltf = SymbolPrefix + "PLTF" + std::to_string(UniPltf.Num);
OS << "static unsigned char " << Pltf << "[] = {";
bool FirstIn = true;
for (size_t i = 0; i < ByteCode.size(); i++) {
if (!FirstIn)
OS << ",";
FirstIn = false;
uint8_t Num = ByteCode[i];
OS << " 0x" << utohexstr(Num);
}
OS << "\n };\n\n"
<< "unsigned int " << Pltf << "_size = " << ByteCode.size() << ";\n\n";
}
OS << "llvm::StringRef get" << SymbolPrefix
<< "Impl(llvm::StringRef CPUStr) {\n";
// Generate a selection procedure that for each supported platform
// (taken from configuration file) selects a BLOB that represents a
// platform-specific emulation BiF corresponding to that platform.
for (const auto &[ByteCode, UniPltf] : HashedUniquePltfs) {
const auto &PltfList = UniPltf.Platforms;
std::vector<std::string> PlatformCompareExpressions;
llvm::transform(PltfList, std::back_inserter(PlatformCompareExpressions),
[](const auto &Pltf) {
return (Twine("CPUStr.equals(") +
renderPlatformLiteral(Pltf) + ")")
.str();
});
OS << " if (" << llvm::join(PlatformCompareExpressions, "\n || ")
<< ")\n"
<< " return {reinterpret_cast<const char*>(" << SymbolPrefix
<< "PLTF" << UniPltf.Num << "),\n"
<< " " << SymbolPrefix << "PLTF" << UniPltf.Num
<< "_size};\n";
}
OS << "return \"\";\n";
OS << "};\n\n";
OS.close();
}
// Parses input configuration file (see \fn vcbCompileUnique for the format)
// and returns a list of PlatformModule objects.
std::vector<PlatformModule> parseResponseFile(std::string &InputFilename) {
std::vector<PlatformModule> RetVal;
std::unique_ptr<MemoryBuffer> File = vcbGetFile(InputFilename);
auto [Paths, Platforms] = File->getBuffer().split("\n");
SmallVector<StringRef, 50> VecPaths;
SmallVector<StringRef, 50> VecPlatforms;
Platforms.split(VecPlatforms, ";");
Paths.split(VecPaths, ";");
if (VecPlatforms.size() != VecPaths.size())
report_fatal_error("vcb: incorrect Responce file - the number of "
"Platforms and Paths is different");
auto ZippedVecPlatformsPaths = llvm::zip(VecPlatforms, VecPaths);
llvm::transform(ZippedVecPlatformsPaths, std::back_inserter(RetVal),
[](const auto &ZippedPlatformArg) {
const auto &[Platform, Path] = ZippedPlatformArg;
return PlatformModule(Platform.trim().str(),
Path.trim().str());
});
return RetVal;
}
// Generates .cpp code that is used by VC backend to obtain platform-specific
// precompiled emulation BiF during runtime. One of the primary entry points
// to vcb tool.
//
// Bitcodes corresponding to different architectures that have the same
// binary representation are coalesced to reduce the size.
//
// The generated function has the following protorype:
// llvm::StringRef getVCEmulation64RawDataImpl(llvm::StringRef CPUStr);
//
// Arguments:
// \p InputFilename - path to configifuration file that contains information
// where to search for platform-specific precompiled BiF.
// \p OutputFilename - name of the output .cpp file.
//
// Note:
// Configuration file is expected to have two lines:
// 1. <BiF_Path1;Bif_Path2;Bif_Path3;...> - colon-separted paths to each
// platform-specific BiF.
// 2. <Plafrom1;Platform2;...> - colon-separated platform names
// corresponding to files listed in the previous line.
void vcbCompileUnique(std::string InputFilename, std::string OutputFilename) {
// Parse configuration.
std::map<std::string, UniquePltf> HashedUniquePltfs;
std::vector<PlatformModule> VecPltfModule = parseResponseFile(InputFilename);
// Create a mapping between coalesced BiF binary data and corresponding
// platforms.
for (const auto &[Platform, ModulePath] : VecPltfModule) {
std::string CompiledBytecode = vcbGetFile(ModulePath)->getBuffer().str();
if (HashedUniquePltfs.find(CompiledBytecode) == HashedUniquePltfs.end()) {
HashedUniquePltfs[std::move(CompiledBytecode)] =
UniquePltf{HashedUniquePltfs.size(), {Platform}};
} else {
HashedUniquePltfs[std::move(CompiledBytecode)].Platforms.push_back(
Platform);
}
}
// Finally, produce a resulting .cpp file.
generateBifSelectionProcedure(HashedUniquePltfs, OutputFilename);
}
|