276 lines · cpp
1//===- llvm/unittest/CodeGen/SPIRVAPITest.cpp -----------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9/// \file10/// Test that SPIR-V Backend provides an API call that translates LLVM IR Module11/// into SPIR-V.12//13//===----------------------------------------------------------------------===//14 15#include "llvm/AsmParser/Parser.h"16#include "llvm/BinaryFormat/Magic.h"17#include "llvm/IR/Module.h"18#include "llvm/Support/SourceMgr.h"19#include "llvm/TargetParser/Triple.h"20#include "gtest/gtest.h"21#include <gmock/gmock.h>22#include <string>23 24namespace llvm {25 26extern "C" LLVM_EXTERNAL_VISIBILITY bool27SPIRVTranslate(Module *M, std::string &SpirvObj, std::string &ErrMsg,28 const std::vector<std::string> &AllowExtNames,29 llvm::CodeGenOptLevel OLevel, Triple TargetTriple);30 31extern "C" bool32SPIRVTranslateModule(Module *M, std::string &SpirvObj, std::string &ErrMsg,33 const std::vector<std::string> &AllowExtNames,34 const std::vector<std::string> &Opts);35 36class SPIRVAPITest : public testing::Test {37protected:38 bool toSpirv(StringRef Assembly, std::string &Result, std::string &ErrMsg,39 const std::vector<std::string> &AllowExtNames,40 llvm::CodeGenOptLevel OLevel, Triple TargetTriple) {41 SMDiagnostic ParseError;42 LLVMContext Context;43 std::unique_ptr<Module> M =44 parseAssemblyString(Assembly, ParseError, Context);45 if (!M) {46 ParseError.print("IR parsing failed: ", errs());47 report_fatal_error("Can't parse input assembly.");48 }49 bool Status = SPIRVTranslate(M.get(), Result, ErrMsg, AllowExtNames, OLevel,50 TargetTriple);51 if (!Status)52 errs() << ErrMsg;53 return Status;54 }55 // TODO: Remove toSpirvLegacy() and related tests after existing clients56 // switch into a newer implementation of SPIRVTranslate().57 bool toSpirvLegacy(StringRef Assembly, std::string &Result,58 std::string &ErrMsg,59 const std::vector<std::string> &AllowExtNames,60 const std::vector<std::string> &Opts) {61 SMDiagnostic ParseError;62 LLVMContext Context;63 std::unique_ptr<Module> M =64 parseAssemblyString(Assembly, ParseError, Context);65 if (!M) {66 ParseError.print("IR parsing failed: ", errs());67 report_fatal_error("Can't parse input assembly.");68 }69 bool Status =70 SPIRVTranslateModule(M.get(), Result, ErrMsg, AllowExtNames, Opts);71 if (!Status)72 errs() << ErrMsg;73 return Status;74 }75 76 static constexpr StringRef ExtensionAssembly = R"(77 define dso_local spir_func void @test1() {78 entry:79 %res1 = tail call spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32 2, i32 0, i32 0)80 ret void81 }82 83 declare dso_local spir_func i32 @_Z26__spirv_GroupBitwiseAndKHR(i32, i32, i32)84 )";85 static constexpr StringRef OkAssembly = R"(86 %struct = type { [1 x i64] }87 88 define spir_kernel void @foo(ptr noundef byval(%struct) %arg) {89 entry:90 call spir_func void @bar(<2 x i32> noundef <i32 0, i32 1>)91 ret void92 }93 94 define spir_func void @bar(<2 x i32> noundef) {95 entry:96 ret void97 }98 )";99};100 101TEST_F(SPIRVAPITest, checkTranslateOk) {102 StringRef Assemblies[] = {"", OkAssembly};103 for (StringRef &Assembly : Assemblies) {104 std::string Result, Error;105 bool Status = toSpirv(Assembly, Result, Error, {}, CodeGenOptLevel::Default,106 Triple("spirv32-unknown-unknown"));107 EXPECT_TRUE(Status && Error.empty() && !Result.empty());108 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);109 }110}111 112TEST_F(SPIRVAPITest, checkTranslateSupportExtensionByArg) {113 std::string Result, Error;114 std::vector<std::string> ExtNames{"SPV_KHR_uniform_group_instructions"};115 bool Status =116 toSpirv(ExtensionAssembly, Result, Error, ExtNames,117 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));118 EXPECT_TRUE(Status && Error.empty() && !Result.empty());119 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);120}121 122TEST_F(SPIRVAPITest, checkTranslateSupportExtensionByArgList) {123 std::string Result, Error;124 std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate",125 "SPV_KHR_uniform_group_instructions",126 "SPV_KHR_subgroup_rotate"};127 bool Status =128 toSpirv(ExtensionAssembly, Result, Error, ExtNames,129 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));130 EXPECT_TRUE(Status && Error.empty() && !Result.empty());131 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);132}133 134TEST_F(SPIRVAPITest, checkTranslateAllExtensions) {135 std::string Result, Error;136 std::vector<std::string> ExtNames{"all"};137 bool Status =138 toSpirv(ExtensionAssembly, Result, Error, ExtNames,139 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));140 EXPECT_TRUE(Status && Error.empty() && !Result.empty());141 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);142}143 144TEST_F(SPIRVAPITest, checkTranslateUnknownExtensionByArg) {145 std::string Result, Error;146 std::vector<std::string> ExtNames{"SPV_XYZ_my_unknown_extension"};147 bool Status =148 toSpirv(ExtensionAssembly, Result, Error, ExtNames,149 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));150 EXPECT_FALSE(Status);151 EXPECT_TRUE(Result.empty());152 EXPECT_EQ(Error, "Unknown SPIR-V extension: SPV_XYZ_my_unknown_extension");153}154 155#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST156TEST_F(SPIRVAPITest, checkTranslateExtensionError) {157 std::string Result, Error;158 EXPECT_DEATH_IF_SUPPORTED(159 {160 toSpirv(ExtensionAssembly, Result, Error, {},161 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));162 },163 "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "164 "following SPIR-V extension: SPV_KHR_uniform_group_instructions");165}166 167TEST_F(SPIRVAPITest, checkTranslateWrongExtensionByArg) {168 std::string Result, Error;169 std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate"};170 EXPECT_DEATH_IF_SUPPORTED(171 {172 toSpirv(ExtensionAssembly, Result, Error, ExtNames,173 CodeGenOptLevel::Aggressive, Triple("spirv64-unknown-unknown"));174 },175 "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "176 "following SPIR-V extension: SPV_KHR_uniform_group_instructions");177}178#endif179 180// Legacy API calls. TODO: Remove after transition into a newer API.181TEST_F(SPIRVAPITest, checkTranslateStringOptsOk) {182 StringRef Assemblies[] = {"", OkAssembly};183 std::vector<std::string> SetOfOpts[] = {{}, {"spirv32-unknown-unknown"}};184 for (const auto &Opts : SetOfOpts) {185 for (StringRef &Assembly : Assemblies) {186 std::string Result, Error;187 bool Status = toSpirvLegacy(Assembly, Result, Error, {}, Opts);188 EXPECT_TRUE(Status && Error.empty() && !Result.empty());189 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);190 }191 }192}193 194TEST_F(SPIRVAPITest, checkTranslateStringOptsError) {195 std::string Result, Error;196 bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},197 {"spirv64v1.6-unknown-unknown", "5"});198 EXPECT_FALSE(Status);199 EXPECT_TRUE(Result.empty());200 EXPECT_EQ(Error, "Invalid optimization level!");201}202 203TEST_F(SPIRVAPITest, checkTranslateStringOptsErrorOk) {204 {205 std::string Result, Error;206 bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},207 {"spirv64v1.6-unknown-unknown", "5"});208 EXPECT_FALSE(Status);209 EXPECT_TRUE(Result.empty());210 EXPECT_EQ(Error, "Invalid optimization level!");211 }212 {213 std::string Result, Error;214 bool Status = toSpirvLegacy(OkAssembly, Result, Error, {},215 {"spirv64v1.6-unknown-unknown", "3"});216 EXPECT_TRUE(Status && Error.empty() && !Result.empty());217 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);218 }219}220 221TEST_F(SPIRVAPITest, checkTranslateStringOptsSupportExtensionByArg) {222 std::string Result, Error;223 std::vector<std::string> ExtNames{"SPV_KHR_uniform_group_instructions"};224 bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});225 EXPECT_TRUE(Status && Error.empty() && !Result.empty());226 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);227}228 229TEST_F(SPIRVAPITest, checkTranslateStringOptsSupportExtensionByArgList) {230 std::string Result, Error;231 std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate",232 "SPV_KHR_uniform_group_instructions",233 "SPV_KHR_subgroup_rotate"};234 bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});235 EXPECT_TRUE(Status && Error.empty() && !Result.empty());236 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);237}238 239TEST_F(SPIRVAPITest, checkTranslateStringOptsAllExtensions) {240 std::string Result, Error;241 std::vector<std::string> ExtNames{"all"};242 bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});243 EXPECT_TRUE(Status && Error.empty() && !Result.empty());244 EXPECT_EQ(identify_magic(Result), file_magic::spirv_object);245}246 247TEST_F(SPIRVAPITest, checkTranslateStringOptsUnknownExtensionByArg) {248 std::string Result, Error;249 std::vector<std::string> ExtNames{"SPV_XYZ_my_unknown_extension"};250 bool Status = toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {});251 EXPECT_FALSE(Status);252 EXPECT_TRUE(Result.empty());253 EXPECT_EQ(Error, "Unknown SPIR-V extension: SPV_XYZ_my_unknown_extension");254}255 256#if !defined(NDEBUG) && GTEST_HAS_DEATH_TEST257TEST_F(SPIRVAPITest, checkTranslateStringOptsExtensionError) {258 std::string Result, Error;259 EXPECT_DEATH_IF_SUPPORTED(260 { toSpirvLegacy(ExtensionAssembly, Result, Error, {}, {}); },261 "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "262 "following SPIR-V extension: SPV_KHR_uniform_group_instructions");263}264 265TEST_F(SPIRVAPITest, checkTranslateStringOptsWrongExtensionByArg) {266 std::string Result, Error;267 std::vector<std::string> ExtNames{"SPV_KHR_subgroup_rotate"};268 EXPECT_DEATH_IF_SUPPORTED(269 { toSpirvLegacy(ExtensionAssembly, Result, Error, ExtNames, {}); },270 "LLVM ERROR: __spirv_GroupBitwiseAndKHR: the builtin requires the "271 "following SPIR-V extension: SPV_KHR_uniform_group_instructions");272}273#endif274 275} // end namespace llvm276