xref: /llvm-project/llvm/unittests/Target/SPIRV/SPIRVAPITest.cpp (revision df122fc734ce002632f3bfe8a5fc5010349dba16)
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