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.
192 lines
6.9 KiB
C++
192 lines
6.9 KiB
C++
//===- 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/AsmParser/Parser.h"
|
|
#include "llvm/BinaryFormat/Magic.h"
|
|
#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 &SpirvObj, std::string &ErrMsg,
|
|
const std::vector<std::string> &AllowExtNames,
|
|
const std::vector<std::string> &Opts);
|
|
|
|
class SPIRVAPITest : public testing::Test {
|
|
protected:
|
|
bool toSpirv(StringRef Assembly, std::string &Result, std::string &ErrMsg,
|
|
const std::vector<std::string> &AllowExtNames,
|
|
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.");
|
|
}
|
|
bool Status =
|
|
SPIRVTranslateModule(M.get(), Result, ErrMsg, AllowExtNames, Opts);
|
|
if (!Status)
|
|
errs() << ErrMsg;
|
|
return Status;
|
|
}
|
|
|
|
LLVMContext Context;
|
|
std::unique_ptr<Module> M;
|
|
|
|
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)
|
|
ret void
|
|
}
|
|
|
|
declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)
|
|
)";
|
|
static constexpr StringRef OkAssembly = 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
|
|
}
|
|
)";
|
|
};
|
|
|
|
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'"));
|
|
Status = toSpirv(OkAssembly, Result, Error, {}, {"- O 5"});
|
|
EXPECT_FALSE(Status);
|
|
EXPECT_TRUE(Result.empty());
|
|
EXPECT_EQ(Error, "Invalid optimization level!");
|
|
}
|
|
|
|
TEST_F(SPIRVAPITest, checkTranslateSupportExtensionByOpts) {
|
|
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, checkTranslateSupportExtensionByArg) {
|
|
std::string Result, Error;
|
|
std::vector<std::string> ExtNames{"SPV_KHR_uniform_group_instructions"};
|
|
bool Status = toSpirv(ExtensionAssembly, Result, Error, ExtNames, {});
|
|
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
|
|
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
|
|
}
|
|
|
|
TEST_F(SPIRVAPITest, checkTranslateSupportExtensionByArgList) {
|
|
std::string Result, Error;
|
|
std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate",
|
|
"SPV_KHR_uniform_group_instructions",
|
|
"SPV_KHR_subgroup_rotate"};
|
|
bool Status = toSpirv(ExtensionAssembly, Result, Error, ExtNames, {});
|
|
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);
|
|
}
|
|
|
|
TEST_F(SPIRVAPITest, checkTranslateUnknownExtensionByArg) {
|
|
std::string Result, Error;
|
|
std::vector<std::string> ExtNames{"SPV_XYZ_my_unknown_extension"};
|
|
bool Status = toSpirv(ExtensionAssembly, Result, Error, ExtNames, {});
|
|
EXPECT_FALSE(Status);
|
|
EXPECT_TRUE(Result.empty());
|
|
EXPECT_EQ(Error, "Unknown SPIR-V extension: SPV_XYZ_my_unknown_extension");
|
|
}
|
|
|
|
#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, checkTranslateUnknownExtensionByOpts) {
|
|
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, checkTranslateWrongExtensionByOpts) {
|
|
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");
|
|
}
|
|
|
|
TEST_F(SPIRVAPITest, checkTranslateWrongExtensionByArg) {
|
|
std::string Result, Error;
|
|
std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate"};
|
|
EXPECT_DEATH_IF_SUPPORTED(
|
|
{ toSpirv(ExtensionAssembly, Result, Error, ExtNames, {}); },
|
|
"LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "
|
|
"following SPIR-V extension: SPV_KHR_uniform_group_instructions");
|
|
}
|
|
#endif
|
|
|
|
} // end namespace llvm
|