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