Skip to content

Commit 7523238

Browse files
VyacheslavLevytskyyjsji
authored andcommitted
Add an option to translate LLVM IR to SPIR-V using the LLVM SPIRV Backend target (#2716)
This adds a new command line option "spirv-use-llvm-backend-target" that is to translate LLVM IR to SPIR-V using the LLVM SPIRV Backend target. cmake definitions are modified to search for the LLVM SPIRV Backend target while configuring the project, and when LLVM is built with SPIRV Backend support, we may use the interface exposed by SPIRV BE to translate Module to SPIR-V code, but only if a user explicitly asks for this way of LLVM IR transformation. Original commit: KhronosGroup/SPIRV-LLVM-Translator@dfeb22b8696d2f5
1 parent ba7e37b commit 7523238

File tree

7 files changed

+250
-11
lines changed

7 files changed

+250
-11
lines changed

llvm-spirv/CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,17 @@ if(LLVM_SPIRV_BUILD_EXTERNAL)
9898
)
9999
include(AddLLVM)
100100
include(HandleLLVMOptions)
101+
include(LLVM-Config)
101102

102103
message(STATUS "Found LLVM: ${LLVM_VERSION}")
103104

105+
is_llvm_target_library("SPIRV" spirv_present_result INCLUDED_TARGETS)
106+
if(spirv_present_result)
107+
message(STATUS "Found SPIR-V Backend")
108+
set(SPIRV_BACKEND_FOUND TRUE)
109+
add_compile_definitions(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
110+
endif()
111+
104112
option(CCACHE_ALLOWED "allow use of ccache" TRUE)
105113
find_program(CCACHE_EXE_FOUND ccache)
106114
if(CCACHE_EXE_FOUND AND CCACHE_ALLOWED)

llvm-spirv/include/LLVMSPIRVOpts.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ class TranslatorOpts {
149149
ExtStatusMap[Extension] = Allow;
150150
}
151151

152+
std::vector<std::string>
153+
getAllowedSPIRVExtensionNames(std::function<bool(ExtensionID)> &Filter) const;
154+
152155
VersionNumber getMaxVersion() const { return MaxVersion; }
153156

154157
bool isGenArgNameMDEnabled() const { return GenKernelArgNameMD; }
@@ -238,6 +241,9 @@ class TranslatorOpts {
238241
}
239242
BuiltinFormat getBuiltinFormat() const noexcept { return SPIRVBuiltinFormat; }
240243

244+
void setUseLLVMTarget(bool Flag) noexcept { UseLLVMTarget = Flag; }
245+
bool getUseLLVMTarget() const noexcept { return UseLLVMTarget; }
246+
241247
private:
242248
// Common translation options
243249
VersionNumber MaxVersion = VersionNumber::MaximumVersion;
@@ -284,6 +290,9 @@ class TranslatorOpts {
284290
bool PreserveAuxData = false;
285291

286292
BuiltinFormat SPIRVBuiltinFormat = BuiltinFormat::Function;
293+
294+
// Convert LLVM to SPIR-V using the LLVM SPIR-V Backend target
295+
bool UseLLVMTarget = false;
287296
};
288297

289298
} // namespace SPIRV

llvm-spirv/lib/SPIRV/LLVMSPIRVOpts.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040

4141
#include "LLVMSPIRVOpts.h"
4242

43+
#include "SPIRVEnum.h"
4344
#include <llvm/ADT/SmallVector.h>
4445
#include <llvm/ADT/StringRef.h>
4546
#include <llvm/IR/IntrinsicInst.h>
@@ -74,3 +75,17 @@ void TranslatorOpts::setSPIRVAllowUnknownIntrinsics(
7475
TranslatorOpts::ArgList IntrinsicPrefixList) noexcept {
7576
SPIRVAllowUnknownIntrinsics = IntrinsicPrefixList;
7677
}
78+
79+
std::vector<std::string> TranslatorOpts::getAllowedSPIRVExtensionNames(
80+
std::function<bool(SPIRV::ExtensionID)> &Filter) const {
81+
std::vector<std::string> AllowExtNames;
82+
AllowExtNames.reserve(ExtStatusMap.size());
83+
for (const auto &It : ExtStatusMap) {
84+
if (!It.second || !Filter(It.first))
85+
continue;
86+
std::string ExtName;
87+
SPIRVMap<ExtensionID, std::string>::find(It.first, &ExtName);
88+
AllowExtNames.push_back(ExtName);
89+
}
90+
return AllowExtNames;
91+
}

llvm-spirv/lib/SPIRV/SPIRVWriter.cpp

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6744,7 +6744,6 @@ bool LLVMToSPIRVBase::joinFPContract(Function *F, FPContract C) {
67446744
}
67456745
llvm_unreachable("Unhandled FPContract value.");
67466746
}
6747-
67486747
} // namespace SPIRV
67496748

67506749
char LLVMToSPIRVLegacy::ID = 0;
@@ -6790,8 +6789,123 @@ bool isValidLLVMModule(Module *M, SPIRVErrorLog &ErrorLog) {
67906789
return true;
67916790
}
67926791

6792+
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
6793+
namespace llvm {
6794+
extern "C" bool
6795+
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
6796+
const std::vector<std::string> &AllowExtNames,
6797+
const std::vector<std::string> &Opts);
6798+
} // namespace llvm
6799+
#if defined(_SPIRV_SUPPORT_TEXT_FMT)
6800+
namespace SPIRV {
6801+
bool convertSpirv(std::istream &IS, std::ostream &OS, std::string &ErrMsg,
6802+
bool FromText, bool ToText);
6803+
} // namespace SPIRV
6804+
#endif // _SPIRV_SUPPORT_TEXT_FMT
6805+
#endif // LLVM_SPIRV_BACKEND_TARGET_PRESENT
6806+
67936807
namespace {
67946808

6809+
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
6810+
static inline Triple::SubArchType spirvVersionToSubArch(VersionNumber VN) {
6811+
switch (VN) {
6812+
case VersionNumber::SPIRV_1_0:
6813+
return Triple::SPIRVSubArch_v10;
6814+
case VersionNumber::SPIRV_1_1:
6815+
return Triple::SPIRVSubArch_v11;
6816+
case VersionNumber::SPIRV_1_2:
6817+
return Triple::SPIRVSubArch_v12;
6818+
case VersionNumber::SPIRV_1_3:
6819+
return Triple::SPIRVSubArch_v13;
6820+
case VersionNumber::SPIRV_1_4:
6821+
return Triple::SPIRVSubArch_v14;
6822+
case VersionNumber::SPIRV_1_5:
6823+
return Triple::SPIRVSubArch_v15;
6824+
case VersionNumber::SPIRV_1_6:
6825+
return Triple::SPIRVSubArch_v16;
6826+
}
6827+
return Triple::NoSubArch;
6828+
}
6829+
6830+
bool runSpirvBackend(Module *M, std::string &Result, std::string &ErrMsg,
6831+
const SPIRV::TranslatorOpts &TranslatorOpts) {
6832+
// A list of known extensions supported by SPIR-V Backend: it's to be updated
6833+
// each time when SPIR-V Backend introduces support for a new extension.
6834+
static const std::set<ExtensionID> ImplementedBySpirvBE{
6835+
SPIRV::ExtensionID::SPV_EXT_shader_atomic_float_add,
6836+
SPIRV::ExtensionID::SPV_EXT_shader_atomic_float16_add,
6837+
SPIRV::ExtensionID::SPV_EXT_shader_atomic_float_min_max,
6838+
SPIRV::ExtensionID::SPV_INTEL_arbitrary_precision_integers,
6839+
SPIRV::ExtensionID::SPV_INTEL_cache_controls,
6840+
SPIRV::ExtensionID::SPV_INTEL_global_variable_fpga_decorations,
6841+
SPIRV::ExtensionID::SPV_INTEL_global_variable_host_access,
6842+
SPIRV::ExtensionID::SPV_INTEL_optnone,
6843+
SPIRV::ExtensionID::SPV_INTEL_usm_storage_classes,
6844+
SPIRV::ExtensionID::SPV_INTEL_subgroups,
6845+
SPIRV::ExtensionID::SPV_KHR_uniform_group_instructions,
6846+
SPIRV::ExtensionID::SPV_KHR_no_integer_wrap_decoration,
6847+
SPIRV::ExtensionID::SPV_KHR_float_controls,
6848+
SPIRV::ExtensionID::SPV_KHR_expect_assume,
6849+
SPIRV::ExtensionID::SPV_KHR_bit_instructions,
6850+
SPIRV::ExtensionID::SPV_KHR_linkonce_odr,
6851+
SPIRV::ExtensionID::SPV_INTEL_inline_assembly,
6852+
SPIRV::ExtensionID::SPV_INTEL_bfloat16_conversion,
6853+
SPIRV::ExtensionID::SPV_KHR_subgroup_rotate,
6854+
SPIRV::ExtensionID::SPV_INTEL_variable_length_array,
6855+
SPIRV::ExtensionID::SPV_INTEL_function_pointers,
6856+
SPIRV::ExtensionID::SPV_KHR_shader_clock,
6857+
SPIRV::ExtensionID::SPV_KHR_cooperative_matrix,
6858+
SPIRV::ExtensionID::SPV_KHR_non_semantic_info};
6859+
// The fallback for the Triple value.
6860+
static const std::string DefaultTriple = "spirv64-unknown-unknown";
6861+
// SPIR-V backend uses the following command line options to conform with
6862+
// Translator's way to generate SPIR-V or with requirements of the Compute
6863+
// flavor of SPIR-V. This list is subject to changes and may become empty
6864+
// eventually.
6865+
static const std::vector<std::string> Opts{"--avoid-spirv-capabilities",
6866+
"Shader",
6867+
"--translator-compatibility-mode"};
6868+
6869+
std::function<bool(SPIRV::ExtensionID)> Filter =
6870+
[](SPIRV::ExtensionID Ext) -> bool {
6871+
return ImplementedBySpirvBE.find(Ext) != ImplementedBySpirvBE.end();
6872+
};
6873+
const std::vector<std::string> AllowExtNames =
6874+
TranslatorOpts.getAllowedSPIRVExtensionNames(Filter);
6875+
6876+
// Correct the Triple value if needed
6877+
Triple TargetTriple(M->getTargetTriple());
6878+
if (TargetTriple.isSPIR()) {
6879+
TargetTriple.setArch(TargetTriple.getArch() == Triple::spir64
6880+
? Triple::spirv64
6881+
: Triple::spirv32,
6882+
TargetTriple.getSubArch());
6883+
M->setTargetTriple(TargetTriple.str());
6884+
// We need to reset Data Layout to conform with the TargetMachine
6885+
M->setDataLayout("");
6886+
}
6887+
if (TranslatorOpts.getMaxVersion() != VersionNumber::MaximumVersion) {
6888+
if (TargetTriple.getTriple().empty())
6889+
TargetTriple.setTriple(DefaultTriple);
6890+
TargetTriple.setArch(TargetTriple.getArch(),
6891+
spirvVersionToSubArch(TranslatorOpts.getMaxVersion()));
6892+
M->setTargetTriple(TargetTriple.str());
6893+
}
6894+
6895+
// Translate the Module into SPIR-V
6896+
return llvm::SPIRVTranslateModule(M, Result, ErrMsg, AllowExtNames, Opts);
6897+
}
6898+
6899+
bool runSpirvBackend(Module *M, std::ostream *OS, std::string &ErrMsg,
6900+
const SPIRV::TranslatorOpts &TranslatorOpts) {
6901+
std::string Result;
6902+
bool Status = runSpirvBackend(M, Result, ErrMsg, TranslatorOpts);
6903+
if (Status && OS)
6904+
*OS << Result;
6905+
return Status;
6906+
}
6907+
#endif // LLVM_SPIRV_BACKEND_TARGET_PRESENT
6908+
67956909
VersionNumber getVersionFromTriple(const Triple &TT, SPIRVErrorLog &ErrorLog) {
67966910
switch (TT.getSubArch()) {
67976911
case Triple::SPIRVSubArch_v10:
@@ -6885,6 +6999,28 @@ bool llvm::writeSpirv(Module *M, std::ostream &OS, std::string &ErrMsg) {
68856999

68867000
bool llvm::writeSpirv(Module *M, const SPIRV::TranslatorOpts &Opts,
68877001
std::ostream &OS, std::string &ErrMsg) {
7002+
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
7003+
// Check if a user asks to convert LLVM to SPIR-V using the LLVM SPIR-V
7004+
// Backend target
7005+
if (Opts.getUseLLVMTarget()) {
7006+
#if !defined(_SPIRV_SUPPORT_TEXT_FMT)
7007+
return runSpirvBackend(M, &OS, ErrMsg, Opts);
7008+
#else
7009+
if (!SPIRVUseTextFormat)
7010+
return runSpirvBackend(M, &OS, ErrMsg, Opts);
7011+
7012+
// SPIR-V Backend always returns a binary: let's use temporary buffers to
7013+
// store the binary representation and convert it later into Translator's
7014+
// textual representation
7015+
std::string BinResult;
7016+
if (!runSpirvBackend(M, BinResult, ErrMsg, Opts))
7017+
return false;
7018+
std::istringstream IS(BinResult);
7019+
return SPIRV::convertSpirv(IS, OS, ErrMsg, false /*FromText*/,
7020+
true /*ToText*/);
7021+
#endif // _SPIRV_SUPPORT_TEXT_FMT
7022+
}
7023+
#endif // LLVM_SPIRV_BACKEND_TARGET_PRESENT
68887024
return runSpirvWriterPasses(M, &OS, ErrMsg, Opts);
68897025
}
68907026

llvm-spirv/test/spirvbackend-usage.ll

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
; This test is to ensure that SPIR-V Backend is able to generate SPIR-V code
2+
; for Translator, and the code can be translated back to LLVM IR. The source
3+
; code contains instructions which require the SPV_KHR_uniform_group_instructions
4+
; extension.
5+
6+
; If LLVM is built without SPIR-V Backend support this test must pass as well.
7+
8+
; RUN: llvm-as %s -o %t.bc
9+
10+
; The following is to test that
11+
; RUN: llvm-spirv %t.bc -spirv-text -o %t.spv.txt -spirv-ext=+SPV_KHR_uniform_group_instructions --spirv-use-llvm-backend-target
12+
; RUN: FileCheck < %t.spv.txt %s --check-prefix=CHECK-SPIRV
13+
14+
; RUN: llvm-spirv %t.bc -o %t.spv -spirv-ext=+SPV_KHR_uniform_group_instructions --spirv-use-llvm-backend-target
15+
; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
16+
; RUN: llvm-dis %t.rev.bc
17+
; RUN: FileCheck < %t.rev.ll %s --check-prefix=CHECK-LLVM-SPV
18+
19+
; CHECK-SPIRV: Capability GroupUniformArithmeticKHR
20+
; CHECK-SPIRV: Extension "SPV_KHR_uniform_group_instructions"
21+
; CHECK-SPIRV: GroupBitwiseAndKHR
22+
23+
; CHECK-LLVM-SPV: @test1()
24+
; CHECK-LLVM-SPV: @test2()
25+
26+
target triple = "spir64-unknown-unknown"
27+
28+
define dso_local spir_func void @test1() {
29+
entry:
30+
%res1 = tail call spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32 2, i32 0, i32 0)
31+
ret void
32+
}
33+
34+
define dso_local spir_func void @test2() {
35+
entry:
36+
%res1 = tail call spir_func i32 @_Z21work_group_reduce_andi(i32 0)
37+
ret void
38+
}
39+
40+
declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)
41+
declare dso_local spir_func i32 @_Z21work_group_reduce_andi(i32)

llvm-spirv/tools/llvm-spirv/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ set(LLVM_LINK_COMPONENTS
1010
TransformUtils
1111
)
1212

13+
if(SPIRV_BACKEND_FOUND)
14+
list(APPEND LLVM_LINK_COMPONENTS "SPIRVCodeGen")
15+
endif()
16+
1317
add_llvm_tool(llvm-spirv
1418
llvm-spirv.cpp
1519
# llvm_setup_rpath messes with the rpath making llvm-spirv not executable from the build directory

llvm-spirv/tools/llvm-spirv/llvm-spirv.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,6 @@ static cl::opt<VersionNumber> MaxSPIRVVersion(
116116
clEnumValN(VersionNumber::SPIRV_1_6, "1.6", "SPIR-V 1.6")),
117117
cl::init(VersionNumber::MaximumVersion));
118118

119-
static cl::list<std::string>
120-
SPVExt("spirv-ext", cl::CommaSeparated,
121-
cl::desc("Specify list of allowed/disallowed extensions"),
122-
cl::value_desc("+SPV_extenstion1_name,-SPV_extension2_name"),
123-
cl::ValueRequired);
124-
125119
static cl::list<std::string> SPIRVAllowUnknownIntrinsics(
126120
"spirv-allow-unknown-intrinsics", cl::CommaSeparated,
127121
cl::desc("Unknown intrinsics that begin with any prefix from the "
@@ -277,6 +271,13 @@ static cl::opt<SPIRV::BuiltinFormat> SPIRVBuiltinFormat(
277271
clEnumValN(SPIRV::BuiltinFormat::Global, "global",
278272
"Use globals to represent SPIR-V builtin variables")));
279273

274+
static cl::opt<bool> SPIRVUseLLVMSPIRVBackendTarget(
275+
"spirv-use-llvm-backend-target",
276+
cl::desc("Convert LLVM to SPIR-V using the LLVM SPIR-V Backend target if "
277+
"it's available. Otherwise has no effect. Default behavior is to "
278+
"don't use the LLVM SPIR-V Backend target."),
279+
cl::init(false));
280+
280281
static std::string removeExt(const std::string &FileName) {
281282
size_t Pos = FileName.find_last_of(".");
282283
if (Pos != std::string::npos)
@@ -509,6 +510,7 @@ static int regularizeLLVM(SPIRV::TranslatorOpts &Opts) {
509510
}
510511

511512
static int parseSPVExtOption(
513+
cl::list<std::string> &SPVExtList,
512514
SPIRV::TranslatorOpts::ExtensionsStatusMap &ExtensionsStatus) {
513515
// Map name -> id for known extensions
514516
std::map<std::string, ExtensionID> ExtensionNamesMap;
@@ -531,11 +533,11 @@ static int parseSPVExtOption(
531533
for (const auto &It : ExtensionNamesMap)
532534
ExtensionsStatus[It.second] = DefaultVal;
533535

534-
if (SPVExt.empty())
536+
if (SPVExtList.empty())
535537
return 0; // Nothing to do
536538

537-
for (unsigned i = 0; i < SPVExt.size(); ++i) {
538-
const std::string &ExtString = SPVExt[i];
539+
for (unsigned i = 0; i < SPVExtList.size(); ++i) {
540+
const std::string &ExtString = SPVExtList[i];
539541
if (ExtString.empty() ||
540542
('+' != ExtString.front() && '-' != ExtString.front())) {
541543
errs() << "Invalid value of --spirv-ext, expected format is:\n"
@@ -704,6 +706,27 @@ int main(int Ac, char **Av) {
704706
sys::PrintStackTraceOnErrorSignal(Av[0]);
705707
PrettyStackTraceProgram X(Ac, Av);
706708

709+
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
710+
// SPIR-V Backend is available, and so we have a clash of command line
711+
// argument names, because both products use "spirv-ext" name. Let's rename
712+
// the command line option coming from SPIR-V Backend, as it's not supposed to
713+
// be used by a user anyway. After that we may safely add the instance of
714+
// "spirv-ext" required by LLVM/SPIRV Translator from the corresponding auto
715+
// variable (SPVExt).
716+
StringMap<llvm::cl::Option *> &RegisteredOptions =
717+
llvm::cl::getRegisteredOptions();
718+
if (RegisteredOptions.count("spirv-ext") == 1) {
719+
llvm::cl::Option *OptToDisable = RegisteredOptions["spirv-ext"];
720+
OptToDisable->setArgStr("spirv-ext-coming-from-spirv-backend");
721+
OptToDisable->setHiddenFlag(cl::Hidden);
722+
}
723+
#endif
724+
cl::list<std::string> SPVExt(
725+
"spirv-ext", cl::CommaSeparated,
726+
cl::desc("Specify list of allowed/disallowed extensions"),
727+
cl::value_desc("+SPV_extenstion1_name,-SPV_extension2_name"),
728+
cl::ValueRequired);
729+
707730
cl::ParseCommandLineOptions(Ac, Av, "LLVM/SPIR-V translator");
708731

709732
if (InputFile != "-" && isFileEmpty(InputFile)) {
@@ -714,11 +737,14 @@ int main(int Ac, char **Av) {
714737
SPIRV::TranslatorOpts::ExtensionsStatusMap ExtensionsStatus;
715738
// ExtensionsStatus will be properly initialized and update according to
716739
// values passed via --spirv-ext option in parseSPVExtOption function.
717-
int Ret = parseSPVExtOption(ExtensionsStatus);
740+
int Ret = parseSPVExtOption(SPVExt, ExtensionsStatus);
718741
if (0 != Ret)
719742
return Ret;
720743

721744
SPIRV::TranslatorOpts Opts(MaxSPIRVVersion, ExtensionsStatus);
745+
#if defined(LLVM_SPIRV_BACKEND_TARGET_PRESENT)
746+
Opts.setUseLLVMTarget(SPIRVUseLLVMSPIRVBackendTarget);
747+
#endif
722748

723749
if (ExtInst.getNumOccurrences() != 0) {
724750
if (ExtInst.getNumOccurrences() > 1) {

0 commit comments

Comments
 (0)