
Following discussions in #110443, and the following earlier discussions in https://lists.llvm.org/pipermail/llvm-dev/2017-October/117907.html, https://reviews.llvm.org/D38482, https://reviews.llvm.org/D38489, this PR attempts to overhaul the `TargetMachine` and `LLVMTargetMachine` interface classes. More specifically: 1. Makes `TargetMachine` the only class implemented under `TargetMachine.h` in the `Target` library. 2. `TargetMachine` contains target-specific interface functions that relate to IR/CodeGen/MC constructs, whereas before (at least on paper) it was supposed to have only IR/MC constructs. Any Target that doesn't want to use the independent code generator simply does not implement them, and returns either `false` or `nullptr`. 3. Renames `LLVMTargetMachine` to `CodeGenCommonTMImpl`. This renaming aims to make the purpose of `LLVMTargetMachine` clearer. Its interface was moved under the CodeGen library, to further emphasis its usage in Targets that use CodeGen directly. 4. Makes `TargetMachine` the only interface used across LLVM and its projects. With these changes, `CodeGenCommonTMImpl` is simply a set of shared function implementations of `TargetMachine`, and CodeGen users don't need to static cast to `LLVMTargetMachine` every time they need a CodeGen-specific feature of the `TargetMachine`. 5. More importantly, does not change any requirements regarding library linking. cc @arsenm @aeubanks
181 lines
6.3 KiB
C++
181 lines
6.3 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));
|
|
MachineModuleInfoWrapperPass *MMIWP =
|
|
new MachineModuleInfoWrapperPass(Target.get());
|
|
const_cast<TargetLoweringObjectFile *>(Target->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
|