The goal of this PR is to facilitate integration of SPIRV Backend into misc 3rd party tools and libraries by means of exposing an API call that translate LLVM module to SPIR-V and write results into a string as binary SPIR-V output, providing diagnostics on fail and means of configuring translation in a style of command line options. An example of a use case may be Khronos Translator that provides bidirectional translation LLVM IR <=> SPIR-V, where LLVM IR => SPIR-V step may be substituted by the call to SPIR-V Backend API, implemented by this PR.
182 lines
6.4 KiB
C++
182 lines
6.4 KiB
C++
//===-- SPIRVAPI.cpp - SPIR-V Backend API ---------------------*- C++ -*---===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "SPIRVCommandLine.h"
|
|
#include "SPIRVSubtarget.h"
|
|
#include "llvm/Analysis/TargetLibraryInfo.h"
|
|
#include "llvm/CodeGen/CommandFlags.h"
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineModuleInfo.h"
|
|
#include "llvm/CodeGen/TargetPassConfig.h"
|
|
#include "llvm/CodeGen/TargetSubtargetInfo.h"
|
|
#include "llvm/IR/DataLayout.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/LegacyPassManager.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/Verifier.h"
|
|
#include "llvm/InitializePasses.h"
|
|
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
|
|
#include "llvm/MC/TargetRegistry.h"
|
|
#include "llvm/Pass.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#include "llvm/Support/InitLLVM.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Target/TargetLoweringObjectFile.h"
|
|
#include "llvm/Target/TargetMachine.h"
|
|
#include "llvm/TargetParser/SubtargetFeature.h"
|
|
#include "llvm/TargetParser/Triple.h"
|
|
#include <optional>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
using namespace llvm;
|
|
|
|
namespace {
|
|
|
|
// Mimic limited number of command line flags from llc to provide a better
|
|
// user experience when passing options into the translate API call.
|
|
static cl::opt<char> SpvOptLevel(" O", cl::Hidden, cl::Prefix, cl::init('0'));
|
|
static cl::opt<std::string> SpvTargetTriple(" mtriple", cl::Hidden,
|
|
cl::init(""));
|
|
|
|
// Utility to accept options in a command line style.
|
|
void parseSPIRVCommandLineOptions(const std::vector<std::string> &Options,
|
|
raw_ostream *Errs) {
|
|
static constexpr const char *Origin = "SPIRVTranslateModule";
|
|
if (!Options.empty()) {
|
|
std::vector<const char *> Argv(1, Origin);
|
|
for (const auto &Arg : Options)
|
|
Argv.push_back(Arg.c_str());
|
|
cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Origin, Errs);
|
|
}
|
|
}
|
|
|
|
std::once_flag InitOnceFlag;
|
|
void InitializeSPIRVTarget() {
|
|
std::call_once(InitOnceFlag, []() {
|
|
LLVMInitializeSPIRVTargetInfo();
|
|
LLVMInitializeSPIRVTarget();
|
|
LLVMInitializeSPIRVTargetMC();
|
|
LLVMInitializeSPIRVAsmPrinter();
|
|
});
|
|
}
|
|
} // namespace
|
|
|
|
namespace llvm {
|
|
|
|
// The goal of this function is to facilitate integration of SPIRV Backend into
|
|
// tools and libraries by means of exposing an API call that translate LLVM
|
|
// module to SPIR-V and write results into a string as binary SPIR-V output,
|
|
// providing diagnostics on fail and means of configuring translation in a style
|
|
// of command line options.
|
|
extern "C" LLVM_EXTERNAL_VISIBILITY bool
|
|
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
|
|
const std::vector<std::string> &AllowExtNames,
|
|
const std::vector<std::string> &Opts) {
|
|
// Fallbacks for option values.
|
|
static const std::string DefaultTriple = "spirv64-unknown-unknown";
|
|
static const std::string DefaultMArch = "";
|
|
|
|
// Parse Opts as if it'd be command line arguments.
|
|
std::string Errors;
|
|
raw_string_ostream ErrorStream(Errors);
|
|
parseSPIRVCommandLineOptions(Opts, &ErrorStream);
|
|
if (!Errors.empty()) {
|
|
ErrMsg = Errors;
|
|
return false;
|
|
}
|
|
|
|
llvm::CodeGenOptLevel OLevel;
|
|
if (auto Level = CodeGenOpt::parseLevel(SpvOptLevel)) {
|
|
OLevel = *Level;
|
|
} else {
|
|
ErrMsg = "Invalid optimization level!";
|
|
return false;
|
|
}
|
|
|
|
// Overrides/ammends `-spirv-ext` command line switch (if present) by the
|
|
// explicit list of allowed SPIR-V extensions.
|
|
std::set<SPIRV::Extension::Extension> AllowedExtIds;
|
|
StringRef UnknownExt =
|
|
SPIRVExtensionsParser::checkExtensions(AllowExtNames, AllowedExtIds);
|
|
if (!UnknownExt.empty()) {
|
|
ErrMsg = "Unknown SPIR-V extension: " + UnknownExt.str();
|
|
return false;
|
|
}
|
|
SPIRVSubtarget::addExtensionsToClOpt(AllowedExtIds);
|
|
|
|
// SPIR-V-specific target initialization.
|
|
InitializeSPIRVTarget();
|
|
|
|
Triple TargetTriple(SpvTargetTriple.empty()
|
|
? M->getTargetTriple()
|
|
: Triple::normalize(SpvTargetTriple));
|
|
if (TargetTriple.getTriple().empty()) {
|
|
TargetTriple.setTriple(DefaultTriple);
|
|
M->setTargetTriple(DefaultTriple);
|
|
}
|
|
const Target *TheTarget =
|
|
TargetRegistry::lookupTarget(DefaultMArch, TargetTriple, ErrMsg);
|
|
if (!TheTarget)
|
|
return false;
|
|
|
|
// A call to codegen::InitTargetOptionsFromCodeGenFlags(TargetTriple)
|
|
// hits the following assertion: llvm/lib/CodeGen/CommandFlags.cpp:78:
|
|
// llvm::FPOpFusion::FPOpFusionMode llvm::codegen::getFuseFPOps(): Assertion
|
|
// `FuseFPOpsView && "RegisterCodeGenFlags not created."' failed.
|
|
TargetOptions Options;
|
|
std::optional<Reloc::Model> RM;
|
|
std::optional<CodeModel::Model> CM;
|
|
std::unique_ptr<TargetMachine> Target =
|
|
std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
|
|
TargetTriple.getTriple(), "", "", Options, RM, CM, OLevel));
|
|
if (!Target) {
|
|
ErrMsg = "Could not allocate target machine!";
|
|
return false;
|
|
}
|
|
|
|
if (M->getCodeModel())
|
|
Target->setCodeModel(*M->getCodeModel());
|
|
|
|
std::string DLStr = M->getDataLayoutStr();
|
|
Expected<DataLayout> MaybeDL = DataLayout::parse(
|
|
DLStr.empty() ? Target->createDataLayout().getStringRepresentation()
|
|
: DLStr);
|
|
if (!MaybeDL) {
|
|
ErrMsg = toString(MaybeDL.takeError());
|
|
return false;
|
|
}
|
|
M->setDataLayout(MaybeDL.get());
|
|
|
|
TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple()));
|
|
legacy::PassManager PM;
|
|
PM.add(new TargetLibraryInfoWrapperPass(TLII));
|
|
LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine &>(*Target);
|
|
MachineModuleInfoWrapperPass *MMIWP =
|
|
new MachineModuleInfoWrapperPass(&LLVMTM);
|
|
const_cast<TargetLoweringObjectFile *>(LLVMTM.getObjFileLowering())
|
|
->Initialize(MMIWP->getMMI().getContext(), *Target);
|
|
|
|
SmallString<4096> OutBuffer;
|
|
raw_svector_ostream OutStream(OutBuffer);
|
|
if (Target->addPassesToEmitFile(PM, OutStream, nullptr,
|
|
CodeGenFileType::ObjectFile)) {
|
|
ErrMsg = "Target machine cannot emit a file of this type";
|
|
return false;
|
|
}
|
|
|
|
PM.run(*M);
|
|
SpirvObj = OutBuffer.str();
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace llvm
|