Files
clang-p2996/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp
Vyacheslav Levytskyy bca2b6d23f [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.
2024-09-10 15:51:20 +02:00

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