Skip to content

Commit bca2b6d

Browse files
[SPIR-V] Expose an API call to initialize SPIRV target and translate input LLVM IR module to SPIR-V (#107216)
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.
1 parent 69828c4 commit bca2b6d

File tree

9 files changed

+434
-0
lines changed

9 files changed

+434
-0
lines changed

llvm/lib/Target/SPIRV/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tablegen(LLVM SPIRVGenTables.inc -gen-searchable-tables)
1414
add_public_tablegen_target(SPIRVCommonTableGen)
1515

1616
add_llvm_target(SPIRVCodeGen
17+
SPIRVAPI.cpp
1718
SPIRVAsmPrinter.cpp
1819
SPIRVBuiltins.cpp
1920
SPIRVCallLowering.cpp

llvm/lib/Target/SPIRV/SPIRVAPI.cpp

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//===-- SPIRVAPI.cpp - SPIR-V Backend API ---------------------*- C++ -*---===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SPIRVCommandLine.h"
10+
#include "SPIRVSubtarget.h"
11+
#include "llvm/Analysis/TargetLibraryInfo.h"
12+
#include "llvm/CodeGen/CommandFlags.h"
13+
#include "llvm/CodeGen/MachineFunctionPass.h"
14+
#include "llvm/CodeGen/MachineModuleInfo.h"
15+
#include "llvm/CodeGen/TargetPassConfig.h"
16+
#include "llvm/CodeGen/TargetSubtargetInfo.h"
17+
#include "llvm/IR/DataLayout.h"
18+
#include "llvm/IR/LLVMContext.h"
19+
#include "llvm/IR/LegacyPassManager.h"
20+
#include "llvm/IR/Module.h"
21+
#include "llvm/IR/Verifier.h"
22+
#include "llvm/InitializePasses.h"
23+
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
24+
#include "llvm/MC/TargetRegistry.h"
25+
#include "llvm/Pass.h"
26+
#include "llvm/Support/CommandLine.h"
27+
#include "llvm/Support/FormattedStream.h"
28+
#include "llvm/Support/InitLLVM.h"
29+
#include "llvm/Support/TargetSelect.h"
30+
#include "llvm/Target/TargetLoweringObjectFile.h"
31+
#include "llvm/Target/TargetMachine.h"
32+
#include "llvm/TargetParser/SubtargetFeature.h"
33+
#include "llvm/TargetParser/Triple.h"
34+
#include <optional>
35+
#include <string>
36+
#include <utility>
37+
#include <vector>
38+
39+
using namespace llvm;
40+
41+
namespace {
42+
43+
// Mimic limited number of command line flags from llc to provide a better
44+
// user experience when passing options into the translate API call.
45+
static cl::opt<char> SpvOptLevel(" O", cl::Hidden, cl::Prefix, cl::init('0'));
46+
static cl::opt<std::string> SpvTargetTriple(" mtriple", cl::Hidden,
47+
cl::init(""));
48+
49+
// Utility to accept options in a command line style.
50+
void parseSPIRVCommandLineOptions(const std::vector<std::string> &Options,
51+
raw_ostream *Errs) {
52+
static constexpr const char *Origin = "SPIRVTranslateModule";
53+
if (!Options.empty()) {
54+
std::vector<const char *> Argv(1, Origin);
55+
for (const auto &Arg : Options)
56+
Argv.push_back(Arg.c_str());
57+
cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Origin, Errs);
58+
}
59+
}
60+
61+
std::once_flag InitOnceFlag;
62+
void InitializeSPIRVTarget() {
63+
std::call_once(InitOnceFlag, []() {
64+
LLVMInitializeSPIRVTargetInfo();
65+
LLVMInitializeSPIRVTarget();
66+
LLVMInitializeSPIRVTargetMC();
67+
LLVMInitializeSPIRVAsmPrinter();
68+
});
69+
}
70+
} // namespace
71+
72+
namespace llvm {
73+
74+
// The goal of this function is to facilitate integration of SPIRV Backend into
75+
// tools and libraries by means of exposing an API call that translate LLVM
76+
// module to SPIR-V and write results into a string as binary SPIR-V output,
77+
// providing diagnostics on fail and means of configuring translation in a style
78+
// of command line options.
79+
extern "C" LLVM_EXTERNAL_VISIBILITY bool
80+
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
81+
const std::vector<std::string> &AllowExtNames,
82+
const std::vector<std::string> &Opts) {
83+
// Fallbacks for option values.
84+
static const std::string DefaultTriple = "spirv64-unknown-unknown";
85+
static const std::string DefaultMArch = "";
86+
87+
// Parse Opts as if it'd be command line arguments.
88+
std::string Errors;
89+
raw_string_ostream ErrorStream(Errors);
90+
parseSPIRVCommandLineOptions(Opts, &ErrorStream);
91+
if (!Errors.empty()) {
92+
ErrMsg = Errors;
93+
return false;
94+
}
95+
96+
llvm::CodeGenOptLevel OLevel;
97+
if (auto Level = CodeGenOpt::parseLevel(SpvOptLevel)) {
98+
OLevel = *Level;
99+
} else {
100+
ErrMsg = "Invalid optimization level!";
101+
return false;
102+
}
103+
104+
// Overrides/ammends `-spirv-ext` command line switch (if present) by the
105+
// explicit list of allowed SPIR-V extensions.
106+
std::set<SPIRV::Extension::Extension> AllowedExtIds;
107+
StringRef UnknownExt =
108+
SPIRVExtensionsParser::checkExtensions(AllowExtNames, AllowedExtIds);
109+
if (!UnknownExt.empty()) {
110+
ErrMsg = "Unknown SPIR-V extension: " + UnknownExt.str();
111+
return false;
112+
}
113+
SPIRVSubtarget::addExtensionsToClOpt(AllowedExtIds);
114+
115+
// SPIR-V-specific target initialization.
116+
InitializeSPIRVTarget();
117+
118+
Triple TargetTriple(SpvTargetTriple.empty()
119+
? M->getTargetTriple()
120+
: Triple::normalize(SpvTargetTriple));
121+
if (TargetTriple.getTriple().empty()) {
122+
TargetTriple.setTriple(DefaultTriple);
123+
M->setTargetTriple(DefaultTriple);
124+
}
125+
const Target *TheTarget =
126+
TargetRegistry::lookupTarget(DefaultMArch, TargetTriple, ErrMsg);
127+
if (!TheTarget)
128+
return false;
129+
130+
// A call to codegen::InitTargetOptionsFromCodeGenFlags(TargetTriple)
131+
// hits the following assertion: llvm/lib/CodeGen/CommandFlags.cpp:78:
132+
// llvm::FPOpFusion::FPOpFusionMode llvm::codegen::getFuseFPOps(): Assertion
133+
// `FuseFPOpsView && "RegisterCodeGenFlags not created."' failed.
134+
TargetOptions Options;
135+
std::optional<Reloc::Model> RM;
136+
std::optional<CodeModel::Model> CM;
137+
std::unique_ptr<TargetMachine> Target =
138+
std::unique_ptr<TargetMachine>(TheTarget->createTargetMachine(
139+
TargetTriple.getTriple(), "", "", Options, RM, CM, OLevel));
140+
if (!Target) {
141+
ErrMsg = "Could not allocate target machine!";
142+
return false;
143+
}
144+
145+
if (M->getCodeModel())
146+
Target->setCodeModel(*M->getCodeModel());
147+
148+
std::string DLStr = M->getDataLayoutStr();
149+
Expected<DataLayout> MaybeDL = DataLayout::parse(
150+
DLStr.empty() ? Target->createDataLayout().getStringRepresentation()
151+
: DLStr);
152+
if (!MaybeDL) {
153+
ErrMsg = toString(MaybeDL.takeError());
154+
return false;
155+
}
156+
M->setDataLayout(MaybeDL.get());
157+
158+
TargetLibraryInfoImpl TLII(Triple(M->getTargetTriple()));
159+
legacy::PassManager PM;
160+
PM.add(new TargetLibraryInfoWrapperPass(TLII));
161+
LLVMTargetMachine &LLVMTM = static_cast<LLVMTargetMachine &>(*Target);
162+
MachineModuleInfoWrapperPass *MMIWP =
163+
new MachineModuleInfoWrapperPass(&LLVMTM);
164+
const_cast<TargetLoweringObjectFile *>(LLVMTM.getObjFileLowering())
165+
->Initialize(MMIWP->getMMI().getContext(), *Target);
166+
167+
SmallString<4096> OutBuffer;
168+
raw_svector_ostream OutStream(OutBuffer);
169+
if (Target->addPassesToEmitFile(PM, OutStream, nullptr,
170+
CodeGenFileType::ObjectFile)) {
171+
ErrMsg = "Target machine cannot emit a file of this type";
172+
return false;
173+
}
174+
175+
PM.run(*M);
176+
SpirvObj = OutBuffer.str();
177+
178+
return true;
179+
}
180+
181+
} // namespace llvm

llvm/lib/Target/SPIRV/SPIRVAPI.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===-- SPIRVAPI.h - SPIR-V Backend API interface ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H
10+
#define LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H
11+
12+
#include <string>
13+
#include <vector>
14+
15+
namespace llvm {
16+
class Module;
17+
18+
extern "C" bool
19+
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
20+
const std::vector<std::string> &AllowExtNames,
21+
const std::vector<std::string> &Opts);
22+
} // namespace llvm
23+
24+
#endif // LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H

llvm/lib/Target/SPIRV/SPIRVCommandLine.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,15 @@ bool SPIRVExtensionsParser::parse(cl::Option &O, llvm::StringRef ArgName,
113113
Vals = std::move(EnabledExtensions);
114114
return false;
115115
}
116+
117+
llvm::StringRef SPIRVExtensionsParser::checkExtensions(
118+
const std::vector<std::string> &ExtNames,
119+
std::set<SPIRV::Extension::Extension> &AllowedExtensions) {
120+
for (const auto &Ext : ExtNames) {
121+
auto It = SPIRVExtensionMap.find(Ext);
122+
if (It == SPIRVExtensionMap.end())
123+
return Ext;
124+
AllowedExtensions.insert(It->second);
125+
}
126+
return StringRef();
127+
}

llvm/lib/Target/SPIRV/SPIRVCommandLine.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717
#include "MCTargetDesc/SPIRVBaseInfo.h"
1818
#include "llvm/Support/CommandLine.h"
1919
#include <set>
20+
#include <string>
2021

2122
namespace llvm {
23+
class StringRef;
2224

2325
/// Command line parser for toggling SPIR-V extensions.
2426
struct SPIRVExtensionsParser
@@ -32,6 +34,14 @@ struct SPIRVExtensionsParser
3234
/// \return Returns true on error.
3335
bool parse(cl::Option &O, StringRef ArgName, StringRef ArgValue,
3436
std::set<SPIRV::Extension::Extension> &Vals);
37+
38+
/// Validates and converts extension names into internal enum values.
39+
///
40+
/// \return Returns a reference to the unknown SPIR-V extension name from the
41+
/// list if present, or an empty StringRef on success.
42+
static llvm::StringRef
43+
checkExtensions(const std::vector<std::string> &ExtNames,
44+
std::set<SPIRV::Extension::Extension> &AllowedExtensions);
3545
};
3646

3747
} // namespace llvm

llvm/lib/Target/SPIRV/SPIRVSubtarget.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ static cl::opt<std::set<SPIRV::Extension::Extension>, false,
3838
Extensions("spirv-ext",
3939
cl::desc("Specify list of enabled SPIR-V extensions"));
4040

41+
// Provides access to the cl::opt<...> `Extensions` variable from outside of the
42+
// module.
43+
void SPIRVSubtarget::addExtensionsToClOpt(
44+
const std::set<SPIRV::Extension::Extension> &AllowList) {
45+
Extensions.insert(AllowList.begin(), AllowList.end());
46+
}
47+
4148
// Compare version numbers, but allow 0 to mean unspecified.
4249
static bool isAtLeastVer(VersionTuple Target, VersionTuple VerToCompareTo) {
4350
return Target.empty() || Target >= VerToCompareTo;

llvm/lib/Target/SPIRV/SPIRVSubtarget.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ class SPIRVSubtarget : public SPIRVGenSubtargetInfo {
130130
}
131131

132132
static constexpr unsigned MaxLegalAddressSpace = 6;
133+
134+
// Adds known SPIR-V extensions to the global list of allowed extensions that
135+
// SPIRVSubtarget module owns as
136+
// cl::opt<std::set<SPIRV::Extension::Extension>, ...> global variable.
137+
static void
138+
addExtensionsToClOpt(const std::set<SPIRV::Extension::Extension> &AllowList);
133139
};
134140
} // namespace llvm
135141

llvm/unittests/Target/SPIRV/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ include_directories(
66
set(LLVM_LINK_COMPONENTS
77
Analysis
88
AsmParser
9+
BinaryFormat
910
Core
1011
SPIRVCodeGen
1112
SPIRVAnalysis
@@ -14,5 +15,6 @@ set(LLVM_LINK_COMPONENTS
1415

1516
add_llvm_target_unittest(SPIRVTests
1617
SPIRVConvergenceRegionAnalysisTests.cpp
18+
SPIRVAPITest.cpp
1719
)
1820

0 commit comments

Comments
 (0)