blob: 5eae9277c4438bf629e5a381386a6034566dedd7 [file] [log] [blame] [edit]
//===- 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 "llvm/TargetParser/Triple.h"
#include "gtest/gtest.h"
#include <gmock/gmock.h>
#include <string>
#include <utility>
using ::testing::StartsWith;
namespace llvm {
extern "C" LLVM_EXTERNAL_VISIBILITY bool
SPIRVTranslate(Module *M, std::string &SpirvObj, std::string &ErrMsg,
const std::vector<std::string> &AllowExtNames,
llvm::CodeGenOptLevel OLevel, Triple TargetTriple);
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,
llvm::CodeGenOptLevel OLevel, Triple TargetTriple) {
SMDiagnostic ParseError;
LLVMContext Context;
std::unique_ptr<Module> M =
parseAssemblyString(Assembly, ParseError, Context);
if (!M) {
ParseError.print("IR parsing failed: ", errs());
report_fatal_error("Can't parse input assembly.");
}
bool Status = SPIRVTranslate(M.get(), Result, ErrMsg, AllowExtNames, OLevel,
TargetTriple);
if (!Status)
errs() << ErrMsg;
return Status;
}
// TODO: Remove toSpirvLegacy() and related tests after existing clients
// switch into a newer implementation of SPIRVTranslate().
bool toSpirvLegacy(StringRef Assembly, std::string &Result,
std::string &ErrMsg,
const std::vector<std::string> &AllowExtNames,
const std::vector<std::string> &Opts) {
SMDiagnostic ParseError;
LLVMContext Context;
std::unique_ptr<Module> 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;
}
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};
for (StringRef &Assembly : Assemblies) {
std::string Result, Error;
bool Status = toSpirv(Assembly, Result, Error, {}, CodeGenOptLevel::Default,
Triple("spirv32-unknown-unknown"));
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,
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
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,
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
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> ExtNames{"all"};
bool Status =
toSpirv(ExtensionAssembly, Result, Error, ExtNames,
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
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,
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
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;
EXPECT_DEATH_IF_SUPPORTED(
{
toSpirv(ExtensionAssembly, Result, Error, {},
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
},
"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,
CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));
},
"LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "
"following SPIR-V extension: SPV_KHR_uniform_group_instructions");
}
#endif
// Legacy API calls. TODO: Remove after transition into a newer API.
TEST_F(SPIRVAPITest, checkTranslateStringOptsOk) {
StringRef Assemblies[] = {"", OkAssembly};
std::vector<std::string> SetOfOpts[] = {{}, {"spirv32-unknown-unknown"}};
for (const auto &Opts : SetOfOpts) {
for (StringRef &Assembly : Assemblies) {
std::string Result, Error;
bool Status = toSpirvLegacy(Assembly, Result, Error, {}, Opts);
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
}
}
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsError) {
std::string Result, Error;
bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},
{"spirv64v1.6-unknown-unknown", "5"});
EXPECT_FALSE(Status);
EXPECT_TRUE(Result.empty());
EXPECT_EQ(Error, "Invalid optimization level!");
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsErrorOk) {
{
std::string Result, Error;
bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},
{"spirv64v1.6-unknown-unknown", "5"});
EXPECT_FALSE(Status);
EXPECT_TRUE(Result.empty());
EXPECT_EQ(Error, "Invalid optimization level!");
}
{
std::string Result, Error;
bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},
{"spirv64v1.6-unknown-unknown", "3"});
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
}
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsSupportExtensionByArg) {
std::string Result, Error;
std::vector<std::string> ExtNames{"SPV_KHR_uniform_group_instructions"};
bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsSupportExtensionByArgList) {
std::string Result, Error;
std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate",
"SPV_KHR_uniform_group_instructions",
"SPV_KHR_subgroup_rotate"};
bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsAllExtensions) {
std::string Result, Error;
std::vector<std::string> ExtNames{"all"};
bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});
EXPECT_TRUE(Status && Error.empty() && !Result.empty());
EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsUnknownExtensionByArg) {
std::string Result, Error;
std::vector<std::string> ExtNames{"SPV_XYZ_my_unknown_extension"};
bool Status = toSpirvLegacy(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, checkTranslateStringOptsExtensionError) {
std::string Result, Error;
EXPECT_DEATH_IF_SUPPORTED(
{ toSpirvLegacy(ExtensionAssembly, Result, Error, {}, {}); },
"LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "
"following SPIR-V extension: SPV_KHR_uniform_group_instructions");
}
TEST_F(SPIRVAPITest, checkTranslateStringOptsWrongExtensionByArg) {
std::string Result, Error;
std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate"};
EXPECT_DEATH_IF_SUPPORTED(
{ toSpirvLegacy(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