[llvm] [SPIR-V] Expose an API call to initialize SPIRV target and translate input LLVM IR module to SPIR-V (PR #107216)
Vyacheslav Levytskyy via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 4 06:55:31 PDT 2024
https://github.com/VyacheslavLevytskyy updated https://github.com/llvm/llvm-project/pull/107216
>From 66f792ddd769ee70e04e858a56a3adc6070acc57 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Wed, 4 Sep 2024 03:41:54 -0700
Subject: [PATCH 1/3] the first draft of external SPIRV API
---
llvm/lib/Target/SPIRV/CMakeLists.txt | 1 +
llvm/lib/Target/SPIRV/SPIRV.cpp | 160 +++++++++++++++++++
llvm/unittests/Target/SPIRV/CMakeLists.txt | 2 +
llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp | 98 ++++++++++++
4 files changed, 261 insertions(+)
create mode 100644 llvm/lib/Target/SPIRV/SPIRV.cpp
create mode 100644 llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 5f8aea5fc8d84d..2c5c053fe72173 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -14,6 +14,7 @@ tablegen(LLVM SPIRVGenTables.inc -gen-searchable-tables)
add_public_tablegen_target(SPIRVCommonTableGen)
add_llvm_target(SPIRVCodeGen
+ SPIRV.cpp
SPIRVAsmPrinter.cpp
SPIRVBuiltins.cpp
SPIRVCallLowering.cpp
diff --git a/llvm/lib/Target/SPIRV/SPIRV.cpp b/llvm/lib/Target/SPIRV/SPIRV.cpp
new file mode 100644
index 00000000000000..2636efb26128bf
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRV.cpp
@@ -0,0 +1,160 @@
+//===-- SPIRV.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 "llvm/Analysis/TargetLibraryInfo.h"
+#include "llvm/CodeGen/CommandFlags.h"
+// #include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
+// #include "llvm/CodeGen/LinkAllCodegenComponents.h"
+// #include "llvm/CodeGen/MIRParser/MIRParser.h"
+#include "llvm/CodeGen/MachineFunctionPass.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
+// #include "llvm/IR/AutoUpgrade.h"
+#include "llvm/IR/DataLayout.h"
+// #include "llvm/IR/DiagnosticInfo.h"
+// #include "llvm/IR/DiagnosticPrinter.h"
+#include "llvm/IR/LLVMContext.h"
+// #include "llvm/IR/LLVMRemarkStreamer.h"
+#include "llvm/IR/LegacyPassManager.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Verifier.h"
+// #include "llvm/IRReader/IRReader.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/MC/MCTargetOptionsCommandFlags.h"
+#include "llvm/MC/TargetRegistry.h"
+#include "llvm/Pass.h"
+// #include "llvm/Remarks/HotnessThresholdParser.h"
+#include "llvm/Support/CommandLine.h"
+// #include "llvm/Support/Debug.h"
+// #include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/InitLLVM.h"
+// #include "llvm/Support/PluginLoader.h"
+// #include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetSelect.h"
+// #include "llvm/Support/TimeProfiler.h"
+// #include "llvm/Support/ToolOutputFile.h"
+// #include "llvm/Support/WithColor.h"
+#include "llvm/Target/TargetLoweringObjectFile.h"
+#include "llvm/Target/TargetMachine.h"
+// #include "llvm/TargetParser/Host.h"
+#include "llvm/TargetParser/SubtargetFeature.h"
+#include "llvm/TargetParser/Triple.h"
+// #include "llvm/Transforms/Utils/Cloning.h"
+// #include <memory>
+#include <optional>
+// #include <ostream>
+#include <string>
+#include <utility>
+
+using namespace llvm;
+
+namespace {
+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
+
+extern "C" LLVM_EXTERNAL_VISIBILITY bool
+SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
+ const std::vector<std::string> &Opts) {
+ // Fallbacks for a Triple, MArch, Opt-level values.
+ static const std::string DefaultTriple = "spirv64-unknown-unknown";
+ static const std::string DefaultMArch = "";
+ static const llvm::CodeGenOptLevel OLevel = llvm::CodeGenOptLevel::None;
+
+ // Parse Opts as if it'd be command line argument.
+ std::string Errors;
+ raw_string_ostream ErrorStream(Errors);
+ parseSPIRVCommandLineOptions(Opts, &ErrorStream);
+ if (!Errors.empty()) {
+ ErrMsg = Errors;
+ return false;
+ }
+
+ // SPIR-V-specific target initialization.
+ InitializeSPIRVTarget();
+
+ Triple TargetTriple(M->getTargetTriple());
+ 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;
+}
diff --git a/llvm/unittests/Target/SPIRV/CMakeLists.txt b/llvm/unittests/Target/SPIRV/CMakeLists.txt
index 83ae215c512ca2..e9fe4883e5b024 100644
--- a/llvm/unittests/Target/SPIRV/CMakeLists.txt
+++ b/llvm/unittests/Target/SPIRV/CMakeLists.txt
@@ -6,6 +6,7 @@ include_directories(
set(LLVM_LINK_COMPONENTS
Analysis
AsmParser
+ BinaryFormat
Core
SPIRVCodeGen
SPIRVAnalysis
@@ -14,5 +15,6 @@ set(LLVM_LINK_COMPONENTS
add_llvm_target_unittest(SPIRVTests
SPIRVConvergenceRegionAnalysisTests.cpp
+ SPIRVAPITest.cpp
)
diff --git a/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
new file mode 100644
index 00000000000000..7bc7d3446762a6
--- /dev/null
+++ b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
@@ -0,0 +1,98 @@
+//===- llvm/unittest/CodeGen/SPIRVAPITest.cpp -----------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Test that SPIR-V Backend provides an API call that translates LLVM IR Module
+/// into SPIR-V.
+//
+//===----------------------------------------------------------------------===//
+
+// #include "llvm/IR/LegacyPassManager.h"
+#include "llvm/AsmParser/Parser.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/IR/Module.h"
+// #include "llvm/MC/TargetRegistry.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/SourceMgr.h"
+// #include "llvm/Support/TargetSelect.h"
+// #include "llvm/Target/TargetMachine.h"
+#include "gtest/gtest.h"
+#include <string>
+#include <utility>
+
+namespace llvm {
+
+extern "C" bool SPIRVTranslateModule(Module *M, std::string &Buffer,
+ std::string &ErrMsg,
+ const std::vector<std::string> &Opts);
+
+class SPIRVAPITest : public testing::Test {
+protected:
+ /*
+ void SetUp() override {
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ }
+ */
+
+ bool toSpirv(StringRef Assembly, std::string &Result, std::string &ErrMsg,
+ const std::vector<std::string> &Opts) {
+ SMDiagnostic ParseError;
+ M = parseAssemblyString(Assembly, ParseError, Context);
+ if (!M) {
+ ParseError.print("IR parsing failed: ", errs());
+ report_fatal_error("Can't parse input assembly.");
+ }
+ return SPIRVTranslateModule(M.get(), Result, ErrMsg, Opts);
+ }
+
+ LLVMContext Context;
+ std::unique_ptr<Module> M;
+};
+
+TEST_F(SPIRVAPITest, checkTranslateExtError) {
+ StringRef Assembly = R"(
+ define dso_local spir_func void @test1() {
+ entry:
+ %res1 = tail call spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32 2, i32 0, i32 0)
+ ret void
+ }
+
+ declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)
+ )";
+ std::string Result, Error;
+ std::vector<std::string> Opts;
+ bool Status = toSpirv(Assembly, Result, Error, Opts);
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+}
+
+TEST_F(SPIRVAPITest, checkTranslateOk) {
+ StringRef Assemblies[] = {"", R"(
+ %struct = type { [1 x i64] }
+
+ define spir_kernel void @foo(ptr noundef byval(%struct) %arg) {
+ entry:
+ call spir_func void @bar(<2 x i32> noundef <i32 0, i32 1>)
+ ret void
+ }
+
+ define spir_func void @bar(<2 x i32> noundef) {
+ entry:
+ ret void
+ }
+ )"};
+ for (StringRef &Assembly : Assemblies) {
+ std::string Result, Error;
+ std::vector<std::string> Opts;
+ bool Status = toSpirv(Assembly, Result, Error, Opts);
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+ }
+}
+
+} // end namespace llvm
>From 2806e8b8074567ddf9ca9c53e82c80ab7a161197 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Wed, 4 Sep 2024 05:22:17 -0700
Subject: [PATCH 2/3] add tests and command line args support
---
llvm/lib/Target/SPIRV/SPIRV.cpp | 33 ++++------
llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp | 67 ++++++++++++++------
2 files changed, 58 insertions(+), 42 deletions(-)
diff --git a/llvm/lib/Target/SPIRV/SPIRV.cpp b/llvm/lib/Target/SPIRV/SPIRV.cpp
index 2636efb26128bf..805887c56429d8 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRV.cpp
@@ -8,48 +8,28 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/CodeGen/CommandFlags.h"
-// #include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
-// #include "llvm/CodeGen/LinkAllCodegenComponents.h"
-// #include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
-// #include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DataLayout.h"
-// #include "llvm/IR/DiagnosticInfo.h"
-// #include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
-// #include "llvm/IR/LLVMRemarkStreamer.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
-// #include "llvm/IRReader/IRReader.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/MCTargetOptionsCommandFlags.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Pass.h"
-// #include "llvm/Remarks/HotnessThresholdParser.h"
#include "llvm/Support/CommandLine.h"
-// #include "llvm/Support/Debug.h"
-// #include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/InitLLVM.h"
-// #include "llvm/Support/PluginLoader.h"
-// #include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
-// #include "llvm/Support/TimeProfiler.h"
-// #include "llvm/Support/ToolOutputFile.h"
-// #include "llvm/Support/WithColor.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetMachine.h"
-// #include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/SubtargetFeature.h"
#include "llvm/TargetParser/Triple.h"
-// #include "llvm/Transforms/Utils/Cloning.h"
-// #include <memory>
#include <optional>
-// #include <ostream>
#include <string>
#include <utility>
@@ -61,7 +41,7 @@ void parseSPIRVCommandLineOptions(const std::vector<std::string> &Options,
static constexpr const char *Origin = "SPIRVTranslateModule";
if (!Options.empty()) {
std::vector<const char *> Argv(1, Origin);
- for (const auto& Arg : Options)
+ for (const auto &Arg : Options)
Argv.push_back(Arg.c_str());
cl::ParseCommandLineOptions(Argv.size(), Argv.data(), Origin, Errs);
}
@@ -78,6 +58,13 @@ void InitializeSPIRVTarget() {
}
} // 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> &Opts) {
@@ -86,7 +73,7 @@ SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
static const std::string DefaultMArch = "";
static const llvm::CodeGenOptLevel OLevel = llvm::CodeGenOptLevel::None;
- // Parse Opts as if it'd be command line argument.
+ // Parse Opts as if it'd be command line arguments.
std::string Errors;
raw_string_ostream ErrorStream(Errors);
parseSPIRVCommandLineOptions(Opts, &ErrorStream);
@@ -158,3 +145,5 @@ SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
return true;
}
+
+} // namespace llvm
diff --git a/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
index 7bc7d3446762a6..8561959d890f8a 100644
--- a/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
+++ b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
@@ -12,15 +12,10 @@
//
//===----------------------------------------------------------------------===//
-// #include "llvm/IR/LegacyPassManager.h"
#include "llvm/AsmParser/Parser.h"
#include "llvm/BinaryFormat/Magic.h"
#include "llvm/IR/Module.h"
-// #include "llvm/MC/TargetRegistry.h"
-#include "llvm/Pass.h"
#include "llvm/Support/SourceMgr.h"
-// #include "llvm/Support/TargetSelect.h"
-// #include "llvm/Target/TargetMachine.h"
#include "gtest/gtest.h"
#include <string>
#include <utility>
@@ -33,12 +28,6 @@ extern "C" bool SPIRVTranslateModule(Module *M, std::string &Buffer,
class SPIRVAPITest : public testing::Test {
protected:
- /*
- void SetUp() override {
- EXPECT_TRUE(Status && Error.empty() && !Result.empty());
- }
- */
-
bool toSpirv(StringRef Assembly, std::string &Result, std::string &ErrMsg,
const std::vector<std::string> &Opts) {
SMDiagnostic ParseError;
@@ -52,10 +41,8 @@ class SPIRVAPITest : public testing::Test {
LLVMContext Context;
std::unique_ptr<Module> M;
-};
-TEST_F(SPIRVAPITest, checkTranslateExtError) {
- StringRef Assembly = R"(
+ static constexpr StringRef ExtensionAssembly = R"(
define dso_local spir_func void @test1() {
entry:
%res1 = tail call spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32 2, i32 0, i32 0)
@@ -64,12 +51,7 @@ TEST_F(SPIRVAPITest, checkTranslateExtError) {
declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)
)";
- std::string Result, Error;
- std::vector<std::string> Opts;
- bool Status = toSpirv(Assembly, Result, Error, Opts);
- EXPECT_TRUE(Status && Error.empty() && !Result.empty());
- EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
-}
+};
TEST_F(SPIRVAPITest, checkTranslateOk) {
StringRef Assemblies[] = {"", R"(
@@ -95,4 +77,49 @@ TEST_F(SPIRVAPITest, checkTranslateOk) {
}
}
+TEST_F(SPIRVAPITest, checkTranslateSupportExtension) {
+ std::string Result, Error;
+ std::vector<std::string> Opts{
+ "--spirv-ext=+SPV_KHR_uniform_group_instructions"};
+ bool Status = toSpirv(ExtensionAssembly, Result, Error, Opts);
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+}
+
+TEST_F(SPIRVAPITest, checkTranslateAllExtensions) {
+ std::string Result, Error;
+ std::vector<std::string> Opts{"--spirv-ext=all"};
+ bool Status = toSpirv(ExtensionAssembly, Result, Error, Opts);
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+}
+
+#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST
+TEST_F(SPIRVAPITest, checkTranslateExtensionError) {
+ std::string Result, Error;
+ std::vector<std::string> Opts;
+ EXPECT_DEATH_IF_SUPPORTED(
+ { toSpirv(ExtensionAssembly, Result, Error, Opts); },
+ "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "
+ "following SPIR-V extension: SPV_KHR_uniform_group_instructions");
+}
+
+TEST_F(SPIRVAPITest, checkTranslateUnknownExtension) {
+ std::string Result, Error;
+ std::vector<std::string> Opts{"--spirv-ext=+SPV_XYZ_my_unknown_extension"};
+ EXPECT_DEATH_IF_SUPPORTED(
+ { toSpirv(ExtensionAssembly, Result, Error, Opts); },
+ "SPIRVTranslateModule: for the --spirv-ext option: Unknown SPIR-V");
+}
+
+TEST_F(SPIRVAPITest, checkTranslateWrongExtension) {
+ std::string Result, Error;
+ std::vector<std::string> Opts{"--spirv-ext=+SPV_KHR_subgroup_rotate"};
+ EXPECT_DEATH_IF_SUPPORTED(
+ { toSpirv(ExtensionAssembly, Result, Error, Opts); },
+ "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "
+ "following SPIR-V extension: SPV_KHR_uniform_group_instructions");
+}
+#endif
+
} // end namespace llvm
>From be3019f1b1a05cd606d1bcdccaec1d8f9b9f5301 Mon Sep 17 00:00:00 2001
From: "Levytskyy, Vyacheslav" <vyacheslav.levytskyy at intel.com>
Date: Wed, 4 Sep 2024 06:55:18 -0700
Subject: [PATCH 3/3] expose an API call to initialize SPIRV target and
translate input LLVM IR module to SPIR-V
---
llvm/lib/Target/SPIRV/CMakeLists.txt | 2 +-
llvm/lib/Target/SPIRV/SPIRV.h | 1 +
.../Target/SPIRV/{SPIRV.cpp => SPIRVAPI.cpp} | 26 ++++++++--
llvm/lib/Target/SPIRV/SPIRVAPI.h | 23 +++++++++
llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp | 47 ++++++++++++++-----
5 files changed, 82 insertions(+), 17 deletions(-)
rename llvm/lib/Target/SPIRV/{SPIRV.cpp => SPIRVAPI.cpp} (85%)
create mode 100644 llvm/lib/Target/SPIRV/SPIRVAPI.h
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index 2c5c053fe72173..df7869b1552caa 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -14,7 +14,7 @@ tablegen(LLVM SPIRVGenTables.inc -gen-searchable-tables)
add_public_tablegen_target(SPIRVCommonTableGen)
add_llvm_target(SPIRVCodeGen
- SPIRV.cpp
+ SPIRVAPI.cpp
SPIRVAsmPrinter.cpp
SPIRVBuiltins.cpp
SPIRVCallLowering.cpp
diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index 6c35a467f53bef..7a7b3827b5e4a1 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -12,6 +12,7 @@
#include "MCTargetDesc/SPIRVMCTargetDesc.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/Target/TargetMachine.h"
+#include "SPIRVAPI.h"
namespace llvm {
class SPIRVTargetMachine;
diff --git a/llvm/lib/Target/SPIRV/SPIRV.cpp b/llvm/lib/Target/SPIRV/SPIRVAPI.cpp
similarity index 85%
rename from llvm/lib/Target/SPIRV/SPIRV.cpp
rename to llvm/lib/Target/SPIRV/SPIRVAPI.cpp
index 805887c56429d8..b4ada1947a4888 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAPI.cpp
@@ -1,4 +1,4 @@
-//===-- SPIRV.cpp - SPIR-V Backend API ------------------------*- 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.
@@ -32,10 +32,19 @@
#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";
@@ -68,10 +77,9 @@ namespace llvm {
extern "C" LLVM_EXTERNAL_VISIBILITY bool
SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
const std::vector<std::string> &Opts) {
- // Fallbacks for a Triple, MArch, Opt-level values.
+ // Fallbacks for option values.
static const std::string DefaultTriple = "spirv64-unknown-unknown";
static const std::string DefaultMArch = "";
- static const llvm::CodeGenOptLevel OLevel = llvm::CodeGenOptLevel::None;
// Parse Opts as if it'd be command line arguments.
std::string Errors;
@@ -82,10 +90,20 @@ SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,
return false;
}
+ llvm::CodeGenOptLevel OLevel;
+ if (auto Level = CodeGenOpt::parseLevel(SpvOptLevel)) {
+ OLevel = *Level;
+ } else {
+ ErrMsg = "Invalid optimization level!";
+ return false;
+ }
+
// SPIR-V-specific target initialization.
InitializeSPIRVTarget();
- Triple TargetTriple(M->getTargetTriple());
+ Triple TargetTriple(SpvTargetTriple.empty()
+ ? M->getTargetTriple()
+ : Triple::normalize(SpvTargetTriple));
if (TargetTriple.getTriple().empty()) {
TargetTriple.setTriple(DefaultTriple);
M->setTargetTriple(DefaultTriple);
diff --git a/llvm/lib/Target/SPIRV/SPIRVAPI.h b/llvm/lib/Target/SPIRV/SPIRVAPI.h
new file mode 100644
index 00000000000000..c3786c6975a890
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVAPI.h
@@ -0,0 +1,23 @@
+//===-- 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 &Buffer,
+ std::string &ErrMsg,
+ const std::vector<std::string> &Opts);
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVAPI_H
diff --git a/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
index 8561959d890f8a..a9bf9fb43f8f4f 100644
--- a/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
+++ b/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
@@ -17,9 +17,12 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/SourceMgr.h"
#include "gtest/gtest.h"
+#include <gmock/gmock.h>
#include <string>
#include <utility>
+using ::testing::StartsWith;
+
namespace llvm {
extern "C" bool SPIRVTranslateModule(Module *M, std::string &Buffer,
@@ -36,7 +39,10 @@ class SPIRVAPITest : public testing::Test {
ParseError.print("IR parsing failed: ", errs());
report_fatal_error("Can't parse input assembly.");
}
- return SPIRVTranslateModule(M.get(), Result, ErrMsg, Opts);
+ bool Status = SPIRVTranslateModule(M.get(), Result, ErrMsg, Opts);
+ if (!Status)
+ errs() << ErrMsg;
+ return Status;
}
LLVMContext Context;
@@ -51,10 +57,7 @@ class SPIRVAPITest : public testing::Test {
declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)
)";
-};
-
-TEST_F(SPIRVAPITest, checkTranslateOk) {
- StringRef Assemblies[] = {"", R"(
+ static constexpr StringRef OkAssembly = R"(
%struct = type { [1 x i64] }
define spir_kernel void @foo(ptr noundef byval(%struct) %arg) {
@@ -67,16 +70,36 @@ TEST_F(SPIRVAPITest, checkTranslateOk) {
entry:
ret void
}
- )"};
- for (StringRef &Assembly : Assemblies) {
- std::string Result, Error;
- std::vector<std::string> Opts;
- bool Status = toSpirv(Assembly, Result, Error, Opts);
- EXPECT_TRUE(Status && Error.empty() && !Result.empty());
- EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+ )";
+};
+
+TEST_F(SPIRVAPITest, checkTranslateOk) {
+ StringRef Assemblies[] = {"", OkAssembly};
+ // Those command line arguments that overlap with registered by llc/codegen
+ // are to be started with the ' ' symbol.
+ std::vector<std::string> SetOfOpts[] = {
+ {}, {"- mtriple=spirv32-unknown-unknown"}};
+ for (const auto &Opts : SetOfOpts) {
+ for (StringRef &Assembly : Assemblies) {
+ std::string Result, Error;
+ bool Status = toSpirv(Assembly, Result, Error, Opts);
+ EXPECT_TRUE(Status && Error.empty() && !Result.empty());
+ EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
+ }
}
}
+TEST_F(SPIRVAPITest, checkTranslateError) {
+ std::string Result, Error;
+ bool Status =
+ toSpirv(OkAssembly, Result, Error, {"-mtriple=spirv32-unknown-unknown"});
+ EXPECT_FALSE(Status);
+ EXPECT_TRUE(Result.empty());
+ EXPECT_THAT(Error,
+ StartsWith("SPIRVTranslateModule: Unknown command line argument "
+ "'-mtriple=spirv32-unknown-unknown'"));
+}
+
TEST_F(SPIRVAPITest, checkTranslateSupportExtension) {
std::string Result, Error;
std::vector<std::string> Opts{
More information about the llvm-commits
mailing list