Skip to content

[SPIR-V] Expose an API call to initialize SPIRV target and translate input LLVM IR module to SPIR-V #107216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/lib/Target/SPIRV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ tablegen(LLVM SPIRVGenTables.inc -gen-searchable-tables)
add_public_tablegen_target(SPIRVCommonTableGen)

add_llvm_target(SPIRVCodeGen
SPIRVAPI.cpp
SPIRVAsmPrinter.cpp
SPIRVBuiltins.cpp
SPIRVCallLowering.cpp
Expand Down
181 changes: 181 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVAPI.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
//===-- 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
24 changes: 24 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVAPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//===-- SPIRVAPI.h - SPIR-V Backend API interface ---------------*- 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
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H
#define LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H

#include <string>
#include <vector>

namespace llvm {
class Module;

extern "C" bool
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
const std::vector<std::string> &AllowExtNames,
const std::vector<std::string> &Opts);
} // namespace llvm

#endif // LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H
12 changes: 12 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,15 @@ bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
Vals = std::move(EnabledExtensions);
return false;
}

llvm::StringRef SPIRVExtensionsParser::checkExtensions(
const std::vector<std::string> &ExtNames,
std::set<SPIRV::Extension::Extension> &AllowedExtensions) {
for (const auto &Ext : ExtNames) {
auto It = SPIRVExtensionMap.find(Ext);
if (It == SPIRVExtensionMap.end())
return Ext;
AllowedExtensions.insert(It->second);
}
return StringRef();
}
10 changes: 10 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVCommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
#include "MCTargetDesc/SPIRVBaseInfo.h"
#include "llvm/Support/CommandLine.h"
#include <set>
#include <string>

namespace llvm {
class StringRef;

/// Command line parser for toggling SPIR-V extensions.
struct SPIRVExtensionsParser
Expand All @@ -32,6 +34,14 @@ struct SPIRVExtensionsParser
/// \return Returns true on error.
bool parse(cl::Option &O, StringRef ArgName, StringRef ArgValue,
std::set<SPIRV::Extension::Extension> &Vals);

/// Validates and converts extension names into internal enum values.
///
/// \return Returns a reference to the unknown SPIR-V extension name from the
/// list if present, or an empty StringRef on success.
static llvm::StringRef
checkExtensions(const std::vector<std::string> &ExtNames,
std::set<SPIRV::Extension::Extension> &AllowedExtensions);
};

} // namespace llvm
Expand Down
7 changes: 7 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ static cl::opt<std::set<SPIRV::Extension::Extension>, false,
Extensions("spirv-ext",
cl::desc("Specify list of enabled SPIR-V extensions"));

// Provides access to the cl::opt<...> `Extensions` variable from outside of the
// module.
void SPIRVSubtarget::addExtensionsToClOpt(
const std::set<SPIRV::Extension::Extension> &AllowList) {
Extensions.insert(AllowList.begin(), AllowList.end());
}

// Compare version numbers, but allow 0 to mean unspecified.
static bool isAtLeastVer(VersionTuple Target, VersionTuple VerToCompareTo) {
return Target.empty() || Target >= VerToCompareTo;
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,12 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
}

static constexpr unsigned MaxLegalAddressSpace = 6;

// Adds known SPIR-V extensions to the global list of allowed extensions that
// SPIRVSubtarget module owns as
// cl::opt<std::set<SPIRV::Extension::Extension>, ...> global variable.
static void
addExtensionsToClOpt(const std::set<SPIRV::Extension::Extension> &AllowList);
};
} // namespace llvm

Expand Down
2 changes: 2 additions & 0 deletions llvm/unittests/Target/SPIRV/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ include_directories(
set(LLVM_LINK_COMPONENTS
Analysis
AsmParser
BinaryFormat
Core
SPIRVCodeGen
SPIRVAnalysis
Expand All @@ -14,5 +15,6 @@ set(LLVM_LINK_COMPONENTS

add_llvm_target_unittest(SPIRVTests
SPIRVConvergenceRegionAnalysisTests.cpp
SPIRVAPITest.cpp
)

Loading
Loading