1 // Copyright (c) 2017 Pierre Moreau
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <string>
16 
17 #include "gmock/gmock.h"
18 #include "test/link/linker_fixture.h"
19 
20 namespace spvtools {
21 namespace {
22 
23 using ::testing::HasSubstr;
24 using MatchingImportsToExports = spvtest::LinkerTest;
25 
TEST_F(MatchingImportsToExports,Default)26 TEST_F(MatchingImportsToExports, Default) {
27   const std::string body1 = R"(
28 OpCapability Linkage
29 OpDecorate %1 LinkageAttributes "foo" Import
30 %2 = OpTypeFloat 32
31 %1 = OpVariable %2 Uniform
32 %3 = OpVariable %2 Input
33 )";
34   const std::string body2 = R"(
35 OpCapability Linkage
36 OpDecorate %1 LinkageAttributes "foo" Export
37 %2 = OpTypeFloat 32
38 %3 = OpConstant %2 42
39 %1 = OpVariable %2 Uniform %3
40 )";
41 
42   spvtest::Binary linked_binary;
43   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
44       << GetErrorMessage();
45 
46   const std::string expected_res =
47       R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
48 %1 = OpTypeFloat 32
49 %2 = OpVariable %1 Input
50 %3 = OpConstant %1 42
51 %4 = OpVariable %1 Uniform %3
52 )";
53   std::string res_body;
54   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
55   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
56       << GetErrorMessage();
57   EXPECT_EQ(expected_res, res_body);
58 }
59 
TEST_F(MatchingImportsToExports,NotALibraryExtraExports)60 TEST_F(MatchingImportsToExports, NotALibraryExtraExports) {
61   const std::string body = R"(
62 OpCapability Linkage
63 OpDecorate %1 LinkageAttributes "foo" Export
64 %2 = OpTypeFloat 32
65 %1 = OpVariable %2 Uniform
66 )";
67 
68   spvtest::Binary linked_binary;
69   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary))
70       << GetErrorMessage();
71 
72   const std::string expected_res =
73       R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
74 %1 = OpTypeFloat 32
75 %2 = OpVariable %1 Uniform
76 )";
77   std::string res_body;
78   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
79   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
80       << GetErrorMessage();
81   EXPECT_EQ(expected_res, res_body);
82 }
83 
TEST_F(MatchingImportsToExports,LibraryExtraExports)84 TEST_F(MatchingImportsToExports, LibraryExtraExports) {
85   const std::string body = R"(
86 OpCapability Linkage
87 OpDecorate %1 LinkageAttributes "foo" Export
88 %2 = OpTypeFloat 32
89 %1 = OpVariable %2 Uniform
90 )";
91 
92   spvtest::Binary linked_binary;
93   LinkerOptions options;
94   options.SetCreateLibrary(true);
95   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body}, &linked_binary, options))
96       << GetErrorMessage();
97 
98   const std::string expected_res = R"(OpCapability Linkage
99 OpModuleProcessed "Linked by SPIR-V Tools Linker"
100 OpDecorate %1 LinkageAttributes "foo" Export
101 %2 = OpTypeFloat 32
102 %1 = OpVariable %2 Uniform
103 )";
104   std::string res_body;
105   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
106   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
107       << GetErrorMessage();
108   EXPECT_EQ(expected_res, res_body);
109 }
110 
TEST_F(MatchingImportsToExports,UnresolvedImports)111 TEST_F(MatchingImportsToExports, UnresolvedImports) {
112   const std::string body1 = R"(
113 OpCapability Linkage
114 OpDecorate %1 LinkageAttributes "foo" Import
115 %2 = OpTypeFloat 32
116 %1 = OpVariable %2 Uniform
117 )";
118   const std::string body2 = R"()";
119 
120   spvtest::Binary linked_binary;
121   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
122             AssembleAndLink({body1, body2}, &linked_binary));
123   EXPECT_THAT(GetErrorMessage(),
124               HasSubstr("Unresolved external reference to \"foo\"."));
125 }
126 
TEST_F(MatchingImportsToExports,TypeMismatch)127 TEST_F(MatchingImportsToExports, TypeMismatch) {
128   const std::string body1 = R"(
129 OpCapability Linkage
130 OpDecorate %1 LinkageAttributes "foo" Import
131 %2 = OpTypeFloat 32
132 %1 = OpVariable %2 Uniform
133 %3 = OpVariable %2 Input
134 )";
135   const std::string body2 = R"(
136 OpCapability Linkage
137 OpDecorate %1 LinkageAttributes "foo" Export
138 %2 = OpTypeInt 32 0
139 %3 = OpConstant %2 42
140 %1 = OpVariable %2 Uniform %3
141 )";
142 
143   spvtest::Binary linked_binary;
144   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
145             AssembleAndLink({body1, body2}, &linked_binary))
146       << GetErrorMessage();
147   EXPECT_THAT(
148       GetErrorMessage(),
149       HasSubstr("Type mismatch on symbol \"foo\" between imported "
150                 "variable/function %1 and exported variable/function %4"));
151 }
152 
TEST_F(MatchingImportsToExports,MultipleDefinitions)153 TEST_F(MatchingImportsToExports, MultipleDefinitions) {
154   const std::string body1 = R"(
155 OpCapability Linkage
156 OpDecorate %1 LinkageAttributes "foo" Import
157 %2 = OpTypeFloat 32
158 %1 = OpVariable %2 Uniform
159 %3 = OpVariable %2 Input
160 )";
161   const std::string body2 = R"(
162 OpCapability Linkage
163 OpDecorate %1 LinkageAttributes "foo" Export
164 %2 = OpTypeFloat 32
165 %3 = OpConstant %2 42
166 %1 = OpVariable %2 Uniform %3
167 )";
168   const std::string body3 = R"(
169 OpCapability Linkage
170 OpDecorate %1 LinkageAttributes "foo" Export
171 %2 = OpTypeFloat 32
172 %3 = OpConstant %2 -1
173 %1 = OpVariable %2 Uniform %3
174 )";
175 
176   spvtest::Binary linked_binary;
177   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
178             AssembleAndLink({body1, body2, body3}, &linked_binary))
179       << GetErrorMessage();
180   EXPECT_THAT(GetErrorMessage(),
181               HasSubstr("Too many external references, 2, were found "
182                         "for \"foo\"."));
183 }
184 
TEST_F(MatchingImportsToExports,SameNameDifferentTypes)185 TEST_F(MatchingImportsToExports, SameNameDifferentTypes) {
186   const std::string body1 = R"(
187 OpCapability Linkage
188 OpDecorate %1 LinkageAttributes "foo" Import
189 %2 = OpTypeFloat 32
190 %1 = OpVariable %2 Uniform
191 %3 = OpVariable %2 Input
192 )";
193   const std::string body2 = R"(
194 OpCapability Linkage
195 OpDecorate %1 LinkageAttributes "foo" Export
196 %2 = OpTypeInt 32 0
197 %3 = OpConstant %2 42
198 %1 = OpVariable %2 Uniform %3
199 )";
200   const std::string body3 = R"(
201 OpCapability Linkage
202 OpDecorate %1 LinkageAttributes "foo" Export
203 %2 = OpTypeFloat 32
204 %3 = OpConstant %2 12
205 %1 = OpVariable %2 Uniform %3
206 )";
207 
208   spvtest::Binary linked_binary;
209   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
210             AssembleAndLink({body1, body2, body3}, &linked_binary))
211       << GetErrorMessage();
212   EXPECT_THAT(GetErrorMessage(),
213               HasSubstr("Too many external references, 2, were found "
214                         "for \"foo\"."));
215 }
216 
TEST_F(MatchingImportsToExports,DecorationMismatch)217 TEST_F(MatchingImportsToExports, DecorationMismatch) {
218   const std::string body1 = R"(
219 OpCapability Linkage
220 OpDecorate %1 LinkageAttributes "foo" Import
221 OpDecorate %2 Constant
222 %2 = OpTypeFloat 32
223 %1 = OpVariable %2 Uniform
224 %3 = OpVariable %2 Input
225 )";
226   const std::string body2 = R"(
227 OpCapability Linkage
228 OpDecorate %1 LinkageAttributes "foo" Export
229 %2 = OpTypeFloat 32
230 %3 = OpConstant %2 42
231 %1 = OpVariable %2 Uniform %3
232 )";
233 
234   spvtest::Binary linked_binary;
235   EXPECT_EQ(SPV_ERROR_INVALID_BINARY,
236             AssembleAndLink({body1, body2}, &linked_binary))
237       << GetErrorMessage();
238   EXPECT_THAT(
239       GetErrorMessage(),
240       HasSubstr("Type mismatch on symbol \"foo\" between imported "
241                 "variable/function %1 and exported variable/function %4"));
242 }
243 
TEST_F(MatchingImportsToExports,FuncParamAttrDifferButStillMatchExportToImport)244 TEST_F(MatchingImportsToExports,
245        FuncParamAttrDifferButStillMatchExportToImport) {
246   const std::string body1 = R"(
247 OpCapability Kernel
248 OpCapability Linkage
249 OpDecorate %1 LinkageAttributes "foo" Import
250 OpDecorate %2 FuncParamAttr Zext
251 %3 = OpTypeVoid
252 %4 = OpTypeInt 32 0
253 %5 = OpTypeFunction %3 %4
254 %1 = OpFunction %3 None %5
255 %2 = OpFunctionParameter %4
256 OpFunctionEnd
257 )";
258   const std::string body2 = R"(
259 OpCapability Kernel
260 OpCapability Linkage
261 OpDecorate %1 LinkageAttributes "foo" Export
262 OpDecorate %2 FuncParamAttr Sext
263 %3 = OpTypeVoid
264 %4 = OpTypeInt 32 0
265 %5 = OpTypeFunction %3 %4
266 %1 = OpFunction %3 None %5
267 %2 = OpFunctionParameter %4
268 %6 = OpLabel
269 OpReturn
270 OpFunctionEnd
271 )";
272 
273   spvtest::Binary linked_binary;
274   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
275       << GetErrorMessage();
276 
277   const std::string expected_res = R"(OpCapability Kernel
278 OpModuleProcessed "Linked by SPIR-V Tools Linker"
279 OpDecorate %1 FuncParamAttr Sext
280 %2 = OpTypeVoid
281 %3 = OpTypeInt 32 0
282 %4 = OpTypeFunction %2 %3
283 %5 = OpFunction %2 None %4
284 %1 = OpFunctionParameter %3
285 %6 = OpLabel
286 OpReturn
287 OpFunctionEnd
288 )";
289   std::string res_body;
290   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
291   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
292       << GetErrorMessage();
293   EXPECT_EQ(expected_res, res_body);
294 }
295 
TEST_F(MatchingImportsToExports,FunctionCtrl)296 TEST_F(MatchingImportsToExports, FunctionCtrl) {
297   const std::string body1 = R"(
298 OpCapability Linkage
299 OpDecorate %1 LinkageAttributes "foo" Import
300 %2 = OpTypeVoid
301 %3 = OpTypeFunction %2
302 %4 = OpTypeFloat 32
303 %5 = OpVariable %4 Uniform
304 %1 = OpFunction %2 None %3
305 OpFunctionEnd
306 )";
307   const std::string body2 = R"(
308 OpCapability Linkage
309 OpDecorate %1 LinkageAttributes "foo" Export
310 %2 = OpTypeVoid
311 %3 = OpTypeFunction %2
312 %1 = OpFunction %2 Inline %3
313 %4 = OpLabel
314 OpReturn
315 OpFunctionEnd
316 )";
317 
318   spvtest::Binary linked_binary;
319   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
320       << GetErrorMessage();
321 
322   const std::string expected_res =
323       R"(OpModuleProcessed "Linked by SPIR-V Tools Linker"
324 %1 = OpTypeVoid
325 %2 = OpTypeFunction %1
326 %3 = OpTypeFloat 32
327 %4 = OpVariable %3 Uniform
328 %5 = OpFunction %1 Inline %2
329 %6 = OpLabel
330 OpReturn
331 OpFunctionEnd
332 )";
333   std::string res_body;
334   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
335   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
336       << GetErrorMessage();
337   EXPECT_EQ(expected_res, res_body);
338 }
339 
TEST_F(MatchingImportsToExports,UseExportedFuncParamAttr)340 TEST_F(MatchingImportsToExports, UseExportedFuncParamAttr) {
341   const std::string body1 = R"(
342 OpCapability Kernel
343 OpCapability Linkage
344 OpDecorate %1 LinkageAttributes "foo" Import
345 OpDecorate %2 FuncParamAttr Zext
346 %2 = OpDecorationGroup
347 OpGroupDecorate %2 %3 %4
348 %5 = OpTypeVoid
349 %6 = OpTypeInt 32 0
350 %7 = OpTypeFunction %5 %6
351 %1 = OpFunction %5 None %7
352 %3 = OpFunctionParameter %6
353 OpFunctionEnd
354 %8 = OpFunction %5 None %7
355 %4 = OpFunctionParameter %6
356 OpFunctionEnd
357 )";
358   const std::string body2 = R"(
359 OpCapability Kernel
360 OpCapability Linkage
361 OpDecorate %1 LinkageAttributes "foo" Export
362 OpDecorate %2 FuncParamAttr Sext
363 %3 = OpTypeVoid
364 %4 = OpTypeInt 32 0
365 %5 = OpTypeFunction %3 %4
366 %1 = OpFunction %3 None %5
367 %2 = OpFunctionParameter %4
368 %6 = OpLabel
369 OpReturn
370 OpFunctionEnd
371 )";
372 
373   spvtest::Binary linked_binary;
374   EXPECT_EQ(SPV_SUCCESS, AssembleAndLink({body1, body2}, &linked_binary))
375       << GetErrorMessage();
376 
377   const std::string expected_res = R"(OpCapability Kernel
378 OpModuleProcessed "Linked by SPIR-V Tools Linker"
379 OpDecorate %1 FuncParamAttr Zext
380 %1 = OpDecorationGroup
381 OpGroupDecorate %1 %2
382 OpDecorate %3 FuncParamAttr Sext
383 %4 = OpTypeVoid
384 %5 = OpTypeInt 32 0
385 %6 = OpTypeFunction %4 %5
386 %7 = OpFunction %4 None %6
387 %2 = OpFunctionParameter %5
388 OpFunctionEnd
389 %8 = OpFunction %4 None %6
390 %3 = OpFunctionParameter %5
391 %9 = OpLabel
392 OpReturn
393 OpFunctionEnd
394 )";
395   std::string res_body;
396   SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
397   EXPECT_EQ(SPV_SUCCESS, Disassemble(linked_binary, &res_body))
398       << GetErrorMessage();
399   EXPECT_EQ(expected_res, res_body);
400 }
401 
402 }  // namespace
403 }  // namespace spvtools
404