• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The SwiftShader Authors. All Rights Reserved.
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 "Device.hpp"
16 #include "Driver.hpp"
17 
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 
21 #include "spirv-tools/libspirv.hpp"
22 
23 #include <cstring>
24 #include <sstream>
25 
26 namespace {
alignUp(size_t val,size_t alignment)27 size_t alignUp(size_t val, size_t alignment)
28 {
29 	return alignment * ((val + alignment - 1) / alignment);
30 }
31 }  // anonymous namespace
32 
33 struct ComputeParams
34 {
35 	size_t numElements;
36 	int localSizeX;
37 	int localSizeY;
38 	int localSizeZ;
39 
operator <<(std::ostream & os,const ComputeParams & params)40 	friend std::ostream &operator<<(std::ostream &os, const ComputeParams &params)
41 	{
42 		return os << "ComputeParams{"
43 		          << "numElements: " << params.numElements << ", "
44 		          << "localSizeX: " << params.localSizeX << ", "
45 		          << "localSizeY: " << params.localSizeY << ", "
46 		          << "localSizeZ: " << params.localSizeZ << "}";
47 	}
48 };
49 
50 class ComputeTest : public testing::TestWithParam<ComputeParams>
51 {
52 protected:
53 	static Driver driver;
54 
SetUpTestSuite()55 	static void SetUpTestSuite()
56 	{
57 		ASSERT_TRUE(driver.loadSwiftShader());
58 	}
59 
TearDownTestSuite()60 	static void TearDownTestSuite()
61 	{
62 		driver.unload();
63 	}
64 };
65 
66 Driver ComputeTest::driver;
67 
compileSpirv(const char * assembly)68 std::vector<uint32_t> compileSpirv(const char *assembly)
69 {
70 	spvtools::SpirvTools core(SPV_ENV_VULKAN_1_0);
71 
72 	core.SetMessageConsumer([](spv_message_level_t, const char *, const spv_position_t &p, const char *m) {
73 		FAIL() << p.line << ":" << p.column << ": " << m;
74 	});
75 
76 	std::vector<uint32_t> spirv;
77 	EXPECT_TRUE(core.Assemble(assembly, &spirv));
78 	EXPECT_TRUE(core.Validate(spirv));
79 
80 	// Warn if the disassembly does not match the source assembly.
81 	// We do this as debugging tests in the debugger is often made much harder
82 	// if the SSA names (%X) in the debugger do not match the source.
83 	std::string disassembled;
84 	core.Disassemble(spirv, &disassembled, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER);
85 	if(disassembled != assembly)
86 	{
87 		printf("-- WARNING: Disassembly does not match assembly: ---\n\n");
88 
89 		auto splitLines = [](const std::string &str) -> std::vector<std::string> {
90 			std::stringstream ss(str);
91 			std::vector<std::string> out;
92 			std::string line;
93 			while(std::getline(ss, line, '\n')) { out.push_back(line); }
94 			return out;
95 		};
96 
97 		auto srcLines = splitLines(std::string(assembly));
98 		auto disLines = splitLines(disassembled);
99 
100 		for(size_t line = 0; line < srcLines.size() && line < disLines.size(); line++)
101 		{
102 			auto srcLine = (line < srcLines.size()) ? srcLines[line] : "<missing>";
103 			auto disLine = (line < disLines.size()) ? disLines[line] : "<missing>";
104 			if(srcLine != disLine)
105 			{
106 				printf("%zu: '%s' != '%s'\n", line, srcLine.c_str(), disLine.c_str());
107 			}
108 		}
109 		printf("\n\n---\nExpected:\n\n%s", disassembled.c_str());
110 	}
111 
112 	return spirv;
113 }
114 
115 #define VK_ASSERT(x) ASSERT_EQ(x, VK_SUCCESS)
116 
117 // Base class for compute tests that read from an input buffer and write to an
118 // output buffer of same length.
119 class SwiftShaderVulkanBufferToBufferComputeTest : public ComputeTest
120 {
121 public:
122 	void test(const std::string &shader,
123 	          std::function<uint32_t(uint32_t idx)> input,
124 	          std::function<uint32_t(uint32_t idx)> expected);
125 };
126 
test(const std::string & shader,std::function<uint32_t (uint32_t idx)> input,std::function<uint32_t (uint32_t idx)> expected)127 void SwiftShaderVulkanBufferToBufferComputeTest::test(
128     const std::string &shader,
129     std::function<uint32_t(uint32_t idx)> input,
130     std::function<uint32_t(uint32_t idx)> expected)
131 {
132 	auto code = compileSpirv(shader.c_str());
133 
134 	const VkInstanceCreateInfo createInfo = {
135 		VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,  // sType
136 		nullptr,                                 // pNext
137 		0,                                       // flags
138 		nullptr,                                 // pApplicationInfo
139 		0,                                       // enabledLayerCount
140 		nullptr,                                 // ppEnabledLayerNames
141 		0,                                       // enabledExtensionCount
142 		nullptr,                                 // ppEnabledExtensionNames
143 	};
144 
145 	VkInstance instance = VK_NULL_HANDLE;
146 	VK_ASSERT(driver.vkCreateInstance(&createInfo, nullptr, &instance));
147 
148 	ASSERT_TRUE(driver.resolve(instance));
149 
150 	std::unique_ptr<Device> device;
151 	VK_ASSERT(Device::CreateComputeDevice(&driver, instance, device));
152 	ASSERT_TRUE(device->IsValid());
153 
154 	// struct Buffers
155 	// {
156 	//     uint32_t pad0[63];
157 	//     uint32_t magic0;
158 	//     uint32_t in[NUM_ELEMENTS]; // Aligned to 0x100
159 	//     uint32_t magic1;
160 	//     uint32_t pad1[N];
161 	//     uint32_t magic2;
162 	//     uint32_t out[NUM_ELEMENTS]; // Aligned to 0x100
163 	//     uint32_t magic3;
164 	// };
165 	static constexpr uint32_t magic0 = 0x01234567;
166 	static constexpr uint32_t magic1 = 0x89abcdef;
167 	static constexpr uint32_t magic2 = 0xfedcba99;
168 	static constexpr uint32_t magic3 = 0x87654321;
169 	size_t numElements = GetParam().numElements;
170 	size_t alignElements = 0x100 / sizeof(uint32_t);
171 	size_t magic0Offset = alignElements - 1;
172 	size_t inOffset = 1 + magic0Offset;
173 	size_t magic1Offset = numElements + inOffset;
174 	size_t magic2Offset = alignUp(magic1Offset + 1, alignElements) - 1;
175 	size_t outOffset = 1 + magic2Offset;
176 	size_t magic3Offset = numElements + outOffset;
177 	size_t buffersTotalElements = alignUp(1 + magic3Offset, alignElements);
178 	size_t buffersSize = sizeof(uint32_t) * buffersTotalElements;
179 
180 	VkDeviceMemory memory;
181 	VK_ASSERT(device->AllocateMemory(buffersSize,
182 	                                 VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
183 	                                 &memory));
184 
185 	uint32_t *buffers;
186 	VK_ASSERT(device->MapMemory(memory, 0, buffersSize, 0, (void **)&buffers));
187 
188 	buffers[magic0Offset] = magic0;
189 	buffers[magic1Offset] = magic1;
190 	buffers[magic2Offset] = magic2;
191 	buffers[magic3Offset] = magic3;
192 
193 	for(size_t i = 0; i < numElements; i++)
194 	{
195 		buffers[inOffset + i] = input((uint32_t)i);
196 	}
197 
198 	device->UnmapMemory(memory);
199 	buffers = nullptr;
200 
201 	VkBuffer bufferIn;
202 	VK_ASSERT(device->CreateStorageBuffer(memory,
203 	                                      sizeof(uint32_t) * numElements,
204 	                                      sizeof(uint32_t) * inOffset,
205 	                                      &bufferIn));
206 
207 	VkBuffer bufferOut;
208 	VK_ASSERT(device->CreateStorageBuffer(memory,
209 	                                      sizeof(uint32_t) * numElements,
210 	                                      sizeof(uint32_t) * outOffset,
211 	                                      &bufferOut));
212 
213 	VkShaderModule shaderModule;
214 	VK_ASSERT(device->CreateShaderModule(code, &shaderModule));
215 
216 	std::vector<VkDescriptorSetLayoutBinding> descriptorSetLayoutBindings = {
217 		{
218 		    0,                                  // binding
219 		    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType
220 		    1,                                  // descriptorCount
221 		    VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags
222 		    0,                                  // pImmutableSamplers
223 		},
224 		{
225 		    1,                                  // binding
226 		    VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,  // descriptorType
227 		    1,                                  // descriptorCount
228 		    VK_SHADER_STAGE_COMPUTE_BIT,        // stageFlags
229 		    0,                                  // pImmutableSamplers
230 		}
231 	};
232 
233 	VkDescriptorSetLayout descriptorSetLayout;
234 	VK_ASSERT(device->CreateDescriptorSetLayout(descriptorSetLayoutBindings, &descriptorSetLayout));
235 
236 	VkPipelineLayout pipelineLayout;
237 	VK_ASSERT(device->CreatePipelineLayout(descriptorSetLayout, &pipelineLayout));
238 
239 	VkPipeline pipeline;
240 	VK_ASSERT(device->CreateComputePipeline(shaderModule, pipelineLayout, &pipeline));
241 
242 	VkDescriptorPool descriptorPool;
243 	VK_ASSERT(device->CreateStorageBufferDescriptorPool(2, &descriptorPool));
244 
245 	VkDescriptorSet descriptorSet;
246 	VK_ASSERT(device->AllocateDescriptorSet(descriptorPool, descriptorSetLayout, &descriptorSet));
247 
248 	std::vector<VkDescriptorBufferInfo> descriptorBufferInfos = {
249 		{
250 		    bufferIn,       // buffer
251 		    0,              // offset
252 		    VK_WHOLE_SIZE,  // range
253 		},
254 		{
255 		    bufferOut,      // buffer
256 		    0,              // offset
257 		    VK_WHOLE_SIZE,  // range
258 		}
259 	};
260 	device->UpdateStorageBufferDescriptorSets(descriptorSet, descriptorBufferInfos);
261 
262 	VkCommandPool commandPool;
263 	VK_ASSERT(device->CreateCommandPool(&commandPool));
264 
265 	VkCommandBuffer commandBuffer;
266 	VK_ASSERT(device->AllocateCommandBuffer(commandPool, &commandBuffer));
267 
268 	VK_ASSERT(device->BeginCommandBuffer(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, commandBuffer));
269 
270 	driver.vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline);
271 
272 	driver.vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipelineLayout, 0, 1, &descriptorSet,
273 	                               0, nullptr);
274 
275 	driver.vkCmdDispatch(commandBuffer, (uint32_t)(numElements / GetParam().localSizeX), 1, 1);
276 
277 	VK_ASSERT(driver.vkEndCommandBuffer(commandBuffer));
278 
279 	VK_ASSERT(device->QueueSubmitAndWait(commandBuffer));
280 
281 	VK_ASSERT(device->MapMemory(memory, 0, buffersSize, 0, (void **)&buffers));
282 
283 	for(size_t i = 0; i < numElements; ++i)
284 	{
285 		auto got = buffers[i + outOffset];
286 		EXPECT_EQ(expected((uint32_t)i), got) << "Unexpected output at " << i;
287 	}
288 
289 	// Check for writes outside of bounds.
290 	EXPECT_EQ(buffers[magic0Offset], magic0);
291 	EXPECT_EQ(buffers[magic1Offset], magic1);
292 	EXPECT_EQ(buffers[magic2Offset], magic2);
293 	EXPECT_EQ(buffers[magic3Offset], magic3);
294 
295 	device->UnmapMemory(memory);
296 	buffers = nullptr;
297 
298 	device->FreeCommandBuffer(commandPool, commandBuffer);
299 	device->FreeMemory(memory);
300 	device->DestroyPipeline(pipeline);
301 	device->DestroyCommandPool(commandPool);
302 	device->DestroyPipelineLayout(pipelineLayout);
303 	device->DestroyDescriptorSetLayout(descriptorSetLayout);
304 	device->DestroyDescriptorPool(descriptorPool);
305 	device->DestroyBuffer(bufferIn);
306 	device->DestroyBuffer(bufferOut);
307 	device->DestroyShaderModule(shaderModule);
308 	device.reset(nullptr);
309 	driver.vkDestroyInstance(instance, nullptr);
310 }
311 
312 INSTANTIATE_TEST_SUITE_P(ComputeParams, SwiftShaderVulkanBufferToBufferComputeTest, testing::Values(ComputeParams{ 512, 1, 1, 1 }, ComputeParams{ 512, 2, 1, 1 }, ComputeParams{ 512, 4, 1, 1 }, ComputeParams{ 512, 8, 1, 1 }, ComputeParams{ 512, 16, 1, 1 }, ComputeParams{ 512, 32, 1, 1 },
313 
314                                                                                                     // Non-multiple of SIMD-lane.
315                                                                                                     ComputeParams{ 3, 1, 1, 1 }, ComputeParams{ 2, 1, 1, 1 }));
316 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,Memcpy)317 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, Memcpy)
318 {
319 	std::stringstream src;
320 	// #version 450
321 	// layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
322 	// layout(binding = 0, std430) buffer InBuffer
323 	// {
324 	//     int Data[];
325 	// } In;
326 	// layout(binding = 1, std430) buffer OutBuffer
327 	// {
328 	//     int Data[];
329 	// } Out;
330 	// void main()
331 	// {
332 	//     Out.Data[gl_GlobalInvocationID.x] = In.Data[gl_GlobalInvocationID.x];
333 	// }
334 	// clang-format off
335     src <<
336         "OpCapability Shader\n"
337         "OpMemoryModel Logical GLSL450\n"
338         "OpEntryPoint GLCompute %1 \"main\" %2\n"
339         "OpExecutionMode %1 LocalSize " <<
340         GetParam().localSizeX << " " <<
341         GetParam().localSizeY << " " <<
342         GetParam().localSizeZ << "\n" <<
343         "OpDecorate %3 ArrayStride 4\n"
344         "OpMemberDecorate %4 0 Offset 0\n"
345         "OpDecorate %4 BufferBlock\n"
346         "OpDecorate %5 DescriptorSet 0\n"
347         "OpDecorate %5 Binding 1\n"
348         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
349         "OpDecorate %6 DescriptorSet 0\n"
350         "OpDecorate %6 Binding 0\n"
351         "%7 = OpTypeVoid\n"
352         "%8 = OpTypeFunction %7\n"             // void()
353         "%9 = OpTypeInt 32 1\n"                // int32
354         "%10 = OpTypeInt 32 0\n"                // uint32
355         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
356         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
357         "%11 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
358         "%5 = OpVariable %11 Uniform\n"        // struct{ int32[] }* in
359         "%12 = OpConstant %9 0\n"               // int32(0)
360         "%13 = OpConstant %10 0\n"              // uint32(0)
361         "%14 = OpTypeVector %10 3\n"            // vec3<int32>
362         "%15 = OpTypePointer Input %14\n"       // vec3<int32>*
363         "%2 = OpVariable %15 Input\n"          // gl_GlobalInvocationId
364         "%16 = OpTypePointer Input %10\n"       // uint32*
365         "%6 = OpVariable %11 Uniform\n"        // struct{ int32[] }* out
366         "%17 = OpTypePointer Uniform %9\n"      // int32*
367         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
368         "%18 = OpLabel\n"
369         "%19 = OpAccessChain %16 %2 %13\n"      // &gl_GlobalInvocationId.x
370         "%20 = OpLoad %10 %19\n"                // gl_GlobalInvocationId.x
371         "%21 = OpAccessChain %17 %6 %12 %20\n"  // &in.arr[gl_GlobalInvocationId.x]
372         "%22 = OpLoad %9 %21\n"                 // in.arr[gl_GlobalInvocationId.x]
373         "%23 = OpAccessChain %17 %5 %12 %20\n"  // &out.arr[gl_GlobalInvocationId.x]
374         "OpStore %23 %22\n"               // out.arr[gl_GlobalInvocationId.x] = in[gl_GlobalInvocationId.x]
375         "OpReturn\n"
376         "OpFunctionEnd\n";
377 	// clang-format on
378 
379 	test(
380 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i; });
381 }
382 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,GlobalInvocationId)383 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, GlobalInvocationId)
384 {
385 	std::stringstream src;
386 	// clang-format off
387     src <<
388         "OpCapability Shader\n"
389         "OpMemoryModel Logical GLSL450\n"
390         "OpEntryPoint GLCompute %1 \"main\" %2\n"
391         "OpExecutionMode %1 LocalSize " <<
392         GetParam().localSizeX << " " <<
393         GetParam().localSizeY << " " <<
394         GetParam().localSizeZ << "\n" <<
395         "OpDecorate %3 ArrayStride 4\n"
396         "OpMemberDecorate %4 0 Offset 0\n"
397         "OpDecorate %4 BufferBlock\n"
398         "OpDecorate %5 DescriptorSet 0\n"
399         "OpDecorate %5 Binding 1\n"
400         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
401         "OpDecorate %6 DescriptorSet 0\n"
402         "OpDecorate %6 Binding 0\n"
403         "%7 = OpTypeVoid\n"
404         "%8 = OpTypeFunction %7\n"             // void()
405         "%9 = OpTypeInt 32 1\n"                // int32
406         "%10 = OpTypeInt 32 0\n"                // uint32
407         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
408         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
409         "%11 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
410         "%5 = OpVariable %11 Uniform\n"        // struct{ int32[] }* in
411         "%12 = OpConstant %9 0\n"               // int32(0)
412         "%13 = OpConstant %9 1\n"               // int32(1)
413         "%14 = OpConstant %10 0\n"              // uint32(0)
414         "%15 = OpConstant %10 1\n"              // uint32(1)
415         "%16 = OpConstant %10 2\n"              // uint32(2)
416         "%17 = OpTypeVector %10 3\n"            // vec3<int32>
417         "%18 = OpTypePointer Input %17\n"       // vec3<int32>*
418         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
419         "%19 = OpTypePointer Input %10\n"       // uint32*
420         "%6 = OpVariable %11 Uniform\n"        // struct{ int32[] }* out
421         "%20 = OpTypePointer Uniform %9\n"      // int32*
422         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
423         "%21 = OpLabel\n"
424         "%22 = OpAccessChain %19 %2 %14\n"      // &gl_GlobalInvocationId.x
425         "%23 = OpAccessChain %19 %2 %15\n"      // &gl_GlobalInvocationId.y
426         "%24 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.z
427         "%25 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
428         "%26 = OpLoad %10 %23\n"                // gl_GlobalInvocationId.y
429         "%27 = OpLoad %10 %24\n"                // gl_GlobalInvocationId.z
430         "%28 = OpAccessChain %20 %6 %12 %25\n"  // &in.arr[gl_GlobalInvocationId.x]
431         "%29 = OpLoad %9 %28\n"                 // out.arr[gl_GlobalInvocationId.x]
432         "%30 = OpIAdd %9 %29 %26\n"             // in[gl_GlobalInvocationId.x] + gl_GlobalInvocationId.y
433         "%31 = OpIAdd %9 %30 %27\n"             // in[gl_GlobalInvocationId.x] + gl_GlobalInvocationId.y + gl_GlobalInvocationId.z
434         "%32 = OpAccessChain %20 %5 %12 %25\n"  // &out.arr[gl_GlobalInvocationId.x]
435         "OpStore %32 %31\n"               // out.arr[gl_GlobalInvocationId.x] = in[gl_GlobalInvocationId.x] + gl_GlobalInvocationId.y + gl_GlobalInvocationId.z
436         "OpReturn\n"
437         "OpFunctionEnd\n";
438 	// clang-format on
439 
440 	// gl_GlobalInvocationId.y and gl_GlobalInvocationId.z should both be zero.
441 	test(
442 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i; });
443 }
444 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchSimple)445 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchSimple)
446 {
447 	std::stringstream src;
448 	// clang-format off
449     src <<
450         "OpCapability Shader\n"
451         "OpMemoryModel Logical GLSL450\n"
452         "OpEntryPoint GLCompute %1 \"main\" %2\n"
453         "OpExecutionMode %1 LocalSize " <<
454         GetParam().localSizeX << " " <<
455         GetParam().localSizeY << " " <<
456         GetParam().localSizeZ << "\n" <<
457         "OpDecorate %3 ArrayStride 4\n"
458         "OpMemberDecorate %4 0 Offset 0\n"
459         "OpDecorate %4 BufferBlock\n"
460         "OpDecorate %5 DescriptorSet 0\n"
461         "OpDecorate %5 Binding 1\n"
462         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
463         "OpDecorate %6 DescriptorSet 0\n"
464         "OpDecorate %6 Binding 0\n"
465         "%7 = OpTypeVoid\n"
466         "%8 = OpTypeFunction %7\n"             // void()
467         "%9 = OpTypeInt 32 1\n"                // int32
468         "%10 = OpTypeInt 32 0\n"                // uint32
469         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
470         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
471         "%11 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
472         "%5 = OpVariable %11 Uniform\n"        // struct{ int32[] }* in
473         "%12 = OpConstant %9 0\n"               // int32(0)
474         "%13 = OpConstant %10 0\n"              // uint32(0)
475         "%14 = OpTypeVector %10 3\n"            // vec3<int32>
476         "%15 = OpTypePointer Input %14\n"       // vec3<int32>*
477         "%2 = OpVariable %15 Input\n"          // gl_GlobalInvocationId
478         "%16 = OpTypePointer Input %10\n"       // uint32*
479         "%6 = OpVariable %11 Uniform\n"        // struct{ int32[] }* out
480         "%17 = OpTypePointer Uniform %9\n"      // int32*
481         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
482         "%18 = OpLabel\n"
483         "%19 = OpAccessChain %16 %2 %13\n"      // &gl_GlobalInvocationId.x
484         "%20 = OpLoad %10 %19\n"                // gl_GlobalInvocationId.x
485         "%21 = OpAccessChain %17 %6 %12 %20\n"  // &in.arr[gl_GlobalInvocationId.x]
486         "%22 = OpLoad %9 %21\n"                 // in.arr[gl_GlobalInvocationId.x]
487         "%23 = OpAccessChain %17 %5 %12 %20\n"  // &out.arr[gl_GlobalInvocationId.x]
488                                                 // Start of branch logic
489                                                 // %22 = in value
490         "OpBranch %24\n"
491         "%24 = OpLabel\n"
492         "OpBranch %25\n"
493         "%25 = OpLabel\n"
494         "OpBranch %26\n"
495         "%26 = OpLabel\n"
496         // %22 = out value
497         // End of branch logic
498         "OpStore %23 %22\n"
499         "OpReturn\n"
500         "OpFunctionEnd\n";
501 	// clang-format on
502 
503 	test(
504 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i; });
505 }
506 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchDeclareSSA)507 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchDeclareSSA)
508 {
509 	std::stringstream src;
510 	// clang-format off
511     src <<
512         "OpCapability Shader\n"
513         "OpMemoryModel Logical GLSL450\n"
514         "OpEntryPoint GLCompute %1 \"main\" %2\n"
515         "OpExecutionMode %1 LocalSize " <<
516         GetParam().localSizeX << " " <<
517         GetParam().localSizeY << " " <<
518         GetParam().localSizeZ << "\n" <<
519         "OpDecorate %3 ArrayStride 4\n"
520         "OpMemberDecorate %4 0 Offset 0\n"
521         "OpDecorate %4 BufferBlock\n"
522         "OpDecorate %5 DescriptorSet 0\n"
523         "OpDecorate %5 Binding 1\n"
524         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
525         "OpDecorate %6 DescriptorSet 0\n"
526         "OpDecorate %6 Binding 0\n"
527         "%7 = OpTypeVoid\n"
528         "%8 = OpTypeFunction %7\n"             // void()
529         "%9 = OpTypeInt 32 1\n"                // int32
530         "%10 = OpTypeInt 32 0\n"                // uint32
531         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
532         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
533         "%11 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
534         "%5 = OpVariable %11 Uniform\n"        // struct{ int32[] }* in
535         "%12 = OpConstant %9 0\n"               // int32(0)
536         "%13 = OpConstant %10 0\n"              // uint32(0)
537         "%14 = OpTypeVector %10 3\n"            // vec3<int32>
538         "%15 = OpTypePointer Input %14\n"       // vec3<int32>*
539         "%2 = OpVariable %15 Input\n"          // gl_GlobalInvocationId
540         "%16 = OpTypePointer Input %10\n"       // uint32*
541         "%6 = OpVariable %11 Uniform\n"        // struct{ int32[] }* out
542         "%17 = OpTypePointer Uniform %9\n"      // int32*
543         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
544         "%18 = OpLabel\n"
545         "%19 = OpAccessChain %16 %2 %13\n"      // &gl_GlobalInvocationId.x
546         "%20 = OpLoad %10 %19\n"                // gl_GlobalInvocationId.x
547         "%21 = OpAccessChain %17 %6 %12 %20\n"  // &in.arr[gl_GlobalInvocationId.x]
548         "%22 = OpLoad %9 %21\n"                 // in.arr[gl_GlobalInvocationId.x]
549         "%23 = OpAccessChain %17 %5 %12 %20\n"  // &out.arr[gl_GlobalInvocationId.x]
550                                                 // Start of branch logic
551                                                 // %22 = in value
552         "OpBranch %24\n"
553         "%24 = OpLabel\n"
554         "%25 = OpIAdd %9 %22 %22\n"             // %25 = in*2
555         "OpBranch %26\n"
556         "%26 = OpLabel\n"
557         "OpBranch %27\n"
558         "%27 = OpLabel\n"
559         // %25 = out value
560         // End of branch logic
561         "OpStore %23 %25\n"               // use SSA value from previous block
562         "OpReturn\n"
563         "OpFunctionEnd\n";
564 	// clang-format on
565 
566 	test(
567 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i * 2; });
568 }
569 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchConditionalSimple)570 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalSimple)
571 {
572 	std::stringstream src;
573 	// clang-format off
574     src <<
575         "OpCapability Shader\n"
576         "OpMemoryModel Logical GLSL450\n"
577         "OpEntryPoint GLCompute %1 \"main\" %2\n"
578         "OpExecutionMode %1 LocalSize " <<
579         GetParam().localSizeX << " " <<
580         GetParam().localSizeY << " " <<
581         GetParam().localSizeZ << "\n" <<
582         "OpDecorate %3 ArrayStride 4\n"
583         "OpMemberDecorate %4 0 Offset 0\n"
584         "OpDecorate %4 BufferBlock\n"
585         "OpDecorate %5 DescriptorSet 0\n"
586         "OpDecorate %5 Binding 1\n"
587         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
588         "OpDecorate %6 DescriptorSet 0\n"
589         "OpDecorate %6 Binding 0\n"
590         "%7 = OpTypeVoid\n"
591         "%8 = OpTypeFunction %7\n"             // void()
592         "%9 = OpTypeInt 32 1\n"                // int32
593         "%10 = OpTypeInt 32 0\n"                // uint32
594         "%11 = OpTypeBool\n"
595         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
596         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
597         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
598         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
599         "%13 = OpConstant %9 0\n"               // int32(0)
600         "%14 = OpConstant %9 2\n"               // int32(2)
601         "%15 = OpConstant %10 0\n"              // uint32(0)
602         "%16 = OpTypeVector %10 3\n"            // vec4<int32>
603         "%17 = OpTypePointer Input %16\n"       // vec4<int32>*
604         "%2 = OpVariable %17 Input\n"          // gl_GlobalInvocationId
605         "%18 = OpTypePointer Input %10\n"       // uint32*
606         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
607         "%19 = OpTypePointer Uniform %9\n"      // int32*
608         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
609         "%20 = OpLabel\n"
610         "%21 = OpAccessChain %18 %2 %15\n"      // &gl_GlobalInvocationId.x
611         "%22 = OpLoad %10 %21\n"                // gl_GlobalInvocationId.x
612         "%23 = OpAccessChain %19 %6 %13 %22\n"  // &in.arr[gl_GlobalInvocationId.x]
613         "%24 = OpLoad %9 %23\n"                 // in.arr[gl_GlobalInvocationId.x]
614         "%25 = OpAccessChain %19 %5 %13 %22\n"  // &out.arr[gl_GlobalInvocationId.x]
615                                                 // Start of branch logic
616                                                 // %24 = in value
617         "%26 = OpSMod %9 %24 %14\n"             // in % 2
618         "%27 = OpIEqual %11 %26 %13\n"          // (in % 2) == 0
619         "OpSelectionMerge %28 None\n"
620         "OpBranchConditional %27 %28 %28\n" // Both go to %28
621         "%28 = OpLabel\n"
622         // %26 = out value
623         // End of branch logic
624         "OpStore %25 %26\n"               // use SSA value from previous block
625         "OpReturn\n"
626         "OpFunctionEnd\n";
627 	// clang-format on
628 
629 	test(
630 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i % 2; });
631 }
632 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchConditionalTwoEmptyBlocks)633 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalTwoEmptyBlocks)
634 {
635 	std::stringstream src;
636 	// clang-format off
637     src <<
638         "OpCapability Shader\n"
639         "OpMemoryModel Logical GLSL450\n"
640         "OpEntryPoint GLCompute %1 \"main\" %2\n"
641         "OpExecutionMode %1 LocalSize " <<
642         GetParam().localSizeX << " " <<
643         GetParam().localSizeY << " " <<
644         GetParam().localSizeZ << "\n" <<
645         "OpDecorate %3 ArrayStride 4\n"
646         "OpMemberDecorate %4 0 Offset 0\n"
647         "OpDecorate %4 BufferBlock\n"
648         "OpDecorate %5 DescriptorSet 0\n"
649         "OpDecorate %5 Binding 1\n"
650         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
651         "OpDecorate %6 DescriptorSet 0\n"
652         "OpDecorate %6 Binding 0\n"
653         "%7 = OpTypeVoid\n"
654         "%8 = OpTypeFunction %7\n"             // void()
655         "%9 = OpTypeInt 32 1\n"                // int32
656         "%10 = OpTypeInt 32 0\n"                // uint32
657         "%11 = OpTypeBool\n"
658         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
659         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
660         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
661         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
662         "%13 = OpConstant %9 0\n"               // int32(0)
663         "%14 = OpConstant %9 2\n"               // int32(2)
664         "%15 = OpConstant %10 0\n"              // uint32(0)
665         "%16 = OpTypeVector %10 3\n"            // vec4<int32>
666         "%17 = OpTypePointer Input %16\n"       // vec4<int32>*
667         "%2 = OpVariable %17 Input\n"          // gl_GlobalInvocationId
668         "%18 = OpTypePointer Input %10\n"       // uint32*
669         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
670         "%19 = OpTypePointer Uniform %9\n"      // int32*
671         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
672         "%20 = OpLabel\n"
673         "%21 = OpAccessChain %18 %2 %15\n"      // &gl_GlobalInvocationId.x
674         "%22 = OpLoad %10 %21\n"                // gl_GlobalInvocationId.x
675         "%23 = OpAccessChain %19 %6 %13 %22\n"  // &in.arr[gl_GlobalInvocationId.x]
676         "%24 = OpLoad %9 %23\n"                 // in.arr[gl_GlobalInvocationId.x]
677         "%25 = OpAccessChain %19 %5 %13 %22\n"  // &out.arr[gl_GlobalInvocationId.x]
678                                                 // Start of branch logic
679                                                 // %24 = in value
680         "%26 = OpSMod %9 %24 %14\n"             // in % 2
681         "%27 = OpIEqual %11 %26 %13\n"          // (in % 2) == 0
682         "OpSelectionMerge %28 None\n"
683         "OpBranchConditional %27 %29 %30\n"
684         "%29 = OpLabel\n"                       // (in % 2) == 0
685         "OpBranch %28\n"
686         "%30 = OpLabel\n"                       // (in % 2) != 0
687         "OpBranch %28\n"
688         "%28 = OpLabel\n"
689         // %26 = out value
690         // End of branch logic
691         "OpStore %25 %26\n"               // use SSA value from previous block
692         "OpReturn\n"
693         "OpFunctionEnd\n";
694 	// clang-format on
695 
696 	test(
697 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i % 2; });
698 }
699 
700 // TODO: Test for parallel assignment
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchConditionalStore)701 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalStore)
702 {
703 	std::stringstream src;
704 	// clang-format off
705     src <<
706         "OpCapability Shader\n"
707         "OpMemoryModel Logical GLSL450\n"
708         "OpEntryPoint GLCompute %1 \"main\" %2\n"
709         "OpExecutionMode %1 LocalSize " <<
710         GetParam().localSizeX << " " <<
711         GetParam().localSizeY << " " <<
712         GetParam().localSizeZ << "\n" <<
713         "OpDecorate %3 ArrayStride 4\n"
714         "OpMemberDecorate %4 0 Offset 0\n"
715         "OpDecorate %4 BufferBlock\n"
716         "OpDecorate %5 DescriptorSet 0\n"
717         "OpDecorate %5 Binding 1\n"
718         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
719         "OpDecorate %6 DescriptorSet 0\n"
720         "OpDecorate %6 Binding 0\n"
721         "%7 = OpTypeVoid\n"
722         "%8 = OpTypeFunction %7\n"             // void()
723         "%9 = OpTypeInt 32 1\n"                // int32
724         "%10 = OpTypeInt 32 0\n"                // uint32
725         "%11 = OpTypeBool\n"
726         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
727         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
728         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
729         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
730         "%13 = OpConstant %9 0\n"               // int32(0)
731         "%14 = OpConstant %9 1\n"               // int32(1)
732         "%15 = OpConstant %9 2\n"               // int32(2)
733         "%16 = OpConstant %10 0\n"              // uint32(0)
734         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
735         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
736         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
737         "%19 = OpTypePointer Input %10\n"       // uint32*
738         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
739         "%20 = OpTypePointer Uniform %9\n"      // int32*
740         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
741         "%21 = OpLabel\n"
742         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
743         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
744         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
745         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
746         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
747                                                 // Start of branch logic
748                                                 // %25 = in value
749         "%27 = OpSMod %9 %25 %15\n"             // in % 2
750         "%28 = OpIEqual %11 %27 %13\n"          // (in % 2) == 0
751         "OpSelectionMerge %29 None\n"
752         "OpBranchConditional %28 %30 %31\n"
753         "%30 = OpLabel\n"                       // (in % 2) == 0
754         "OpStore %26 %14\n"               // write 1
755         "OpBranch %29\n"
756         "%31 = OpLabel\n"                       // (in % 2) != 0
757         "OpStore %26 %15\n"               // write 2
758         "OpBranch %29\n"
759         "%29 = OpLabel\n"
760         // End of branch logic
761         "OpReturn\n"
762         "OpFunctionEnd\n";
763 	// clang-format on
764 
765 	test(
766 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 1 : 2; });
767 }
768 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchConditionalReturnTrue)769 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalReturnTrue)
770 {
771 	std::stringstream src;
772 	// clang-format off
773     src <<
774         "OpCapability Shader\n"
775         "OpMemoryModel Logical GLSL450\n"
776         "OpEntryPoint GLCompute %1 \"main\" %2\n"
777         "OpExecutionMode %1 LocalSize " <<
778         GetParam().localSizeX << " " <<
779         GetParam().localSizeY << " " <<
780         GetParam().localSizeZ << "\n" <<
781         "OpDecorate %3 ArrayStride 4\n"
782         "OpMemberDecorate %4 0 Offset 0\n"
783         "OpDecorate %4 BufferBlock\n"
784         "OpDecorate %5 DescriptorSet 0\n"
785         "OpDecorate %5 Binding 1\n"
786         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
787         "OpDecorate %6 DescriptorSet 0\n"
788         "OpDecorate %6 Binding 0\n"
789         "%7 = OpTypeVoid\n"
790         "%8 = OpTypeFunction %7\n"             // void()
791         "%9 = OpTypeInt 32 1\n"                // int32
792         "%10 = OpTypeInt 32 0\n"                // uint32
793         "%11 = OpTypeBool\n"
794         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
795         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
796         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
797         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
798         "%13 = OpConstant %9 0\n"               // int32(0)
799         "%14 = OpConstant %9 1\n"               // int32(1)
800         "%15 = OpConstant %9 2\n"               // int32(2)
801         "%16 = OpConstant %10 0\n"              // uint32(0)
802         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
803         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
804         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
805         "%19 = OpTypePointer Input %10\n"       // uint32*
806         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
807         "%20 = OpTypePointer Uniform %9\n"      // int32*
808         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
809         "%21 = OpLabel\n"
810         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
811         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
812         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
813         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
814         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
815                                                 // Start of branch logic
816                                                 // %25 = in value
817         "%27 = OpSMod %9 %25 %15\n"             // in % 2
818         "%28 = OpIEqual %11 %27 %13\n"          // (in % 2) == 0
819         "OpSelectionMerge %29 None\n"
820         "OpBranchConditional %28 %30 %29\n"
821         "%30 = OpLabel\n"                       // (in % 2) == 0
822         "OpReturn\n"
823         "%29 = OpLabel\n"                       // merge
824         "OpStore %26 %15\n"               // write 2
825                                           // End of branch logic
826         "OpReturn\n"
827         "OpFunctionEnd\n";
828 	// clang-format on
829 
830 	test(
831 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 0 : 2; });
832 }
833 
834 // TODO: Test for parallel assignment
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,BranchConditionalPhi)835 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalPhi)
836 {
837 	std::stringstream src;
838 	// clang-format off
839     src <<
840         "OpCapability Shader\n"
841         "OpMemoryModel Logical GLSL450\n"
842         "OpEntryPoint GLCompute %1 \"main\" %2\n"
843         "OpExecutionMode %1 LocalSize " <<
844         GetParam().localSizeX << " " <<
845         GetParam().localSizeY << " " <<
846         GetParam().localSizeZ << "\n" <<
847         "OpDecorate %3 ArrayStride 4\n"
848         "OpMemberDecorate %4 0 Offset 0\n"
849         "OpDecorate %4 BufferBlock\n"
850         "OpDecorate %5 DescriptorSet 0\n"
851         "OpDecorate %5 Binding 1\n"
852         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
853         "OpDecorate %6 DescriptorSet 0\n"
854         "OpDecorate %6 Binding 0\n"
855         "%7 = OpTypeVoid\n"
856         "%8 = OpTypeFunction %7\n"             // void()
857         "%9 = OpTypeInt 32 1\n"                // int32
858         "%10 = OpTypeInt 32 0\n"                // uint32
859         "%11 = OpTypeBool\n"
860         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
861         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
862         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
863         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
864         "%13 = OpConstant %9 0\n"               // int32(0)
865         "%14 = OpConstant %9 1\n"               // int32(1)
866         "%15 = OpConstant %9 2\n"               // int32(2)
867         "%16 = OpConstant %10 0\n"              // uint32(0)
868         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
869         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
870         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
871         "%19 = OpTypePointer Input %10\n"       // uint32*
872         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
873         "%20 = OpTypePointer Uniform %9\n"      // int32*
874         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
875         "%21 = OpLabel\n"
876         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
877         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
878         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
879         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
880         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
881                                                 // Start of branch logic
882                                                 // %25 = in value
883         "%27 = OpSMod %9 %25 %15\n"             // in % 2
884         "%28 = OpIEqual %11 %27 %13\n"          // (in % 2) == 0
885         "OpSelectionMerge %29 None\n"
886         "OpBranchConditional %28 %30 %31\n"
887         "%30 = OpLabel\n"                       // (in % 2) == 0
888         "OpBranch %29\n"
889         "%31 = OpLabel\n"                       // (in % 2) != 0
890         "OpBranch %29\n"
891         "%29 = OpLabel\n"
892         "%32 = OpPhi %9 %14 %30 %15 %31\n"      // (in % 2) == 0 ? 1 : 2
893                                                 // End of branch logic
894         "OpStore %26 %32\n"
895         "OpReturn\n"
896         "OpFunctionEnd\n";
897 	// clang-format on
898 
899 	test(
900 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 1 : 2; });
901 }
902 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchEmptyCases)903 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchEmptyCases)
904 {
905 	std::stringstream src;
906 	// clang-format off
907     src <<
908         "OpCapability Shader\n"
909         "OpMemoryModel Logical GLSL450\n"
910         "OpEntryPoint GLCompute %1 \"main\" %2\n"
911         "OpExecutionMode %1 LocalSize " <<
912         GetParam().localSizeX << " " <<
913         GetParam().localSizeY << " " <<
914         GetParam().localSizeZ << "\n" <<
915         "OpDecorate %3 ArrayStride 4\n"
916         "OpMemberDecorate %4 0 Offset 0\n"
917         "OpDecorate %4 BufferBlock\n"
918         "OpDecorate %5 DescriptorSet 0\n"
919         "OpDecorate %5 Binding 1\n"
920         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
921         "OpDecorate %6 DescriptorSet 0\n"
922         "OpDecorate %6 Binding 0\n"
923         "%7 = OpTypeVoid\n"
924         "%8 = OpTypeFunction %7\n"             // void()
925         "%9 = OpTypeInt 32 1\n"                // int32
926         "%10 = OpTypeInt 32 0\n"                // uint32
927         "%11 = OpTypeBool\n"
928         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
929         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
930         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
931         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
932         "%13 = OpConstant %9 0\n"               // int32(0)
933         "%14 = OpConstant %9 2\n"               // int32(2)
934         "%15 = OpConstant %10 0\n"              // uint32(0)
935         "%16 = OpTypeVector %10 3\n"            // vec4<int32>
936         "%17 = OpTypePointer Input %16\n"       // vec4<int32>*
937         "%2 = OpVariable %17 Input\n"          // gl_GlobalInvocationId
938         "%18 = OpTypePointer Input %10\n"       // uint32*
939         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
940         "%19 = OpTypePointer Uniform %9\n"      // int32*
941         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
942         "%20 = OpLabel\n"
943         "%21 = OpAccessChain %18 %2 %15\n"      // &gl_GlobalInvocationId.x
944         "%22 = OpLoad %10 %21\n"                // gl_GlobalInvocationId.x
945         "%23 = OpAccessChain %19 %6 %13 %22\n"  // &in.arr[gl_GlobalInvocationId.x]
946         "%24 = OpLoad %9 %23\n"                 // in.arr[gl_GlobalInvocationId.x]
947         "%25 = OpAccessChain %19 %5 %13 %22\n"  // &out.arr[gl_GlobalInvocationId.x]
948                                                 // Start of branch logic
949                                                 // %24 = in value
950         "%26 = OpSMod %9 %24 %14\n"             // in % 2
951         "OpSelectionMerge %27 None\n"
952         "OpSwitch %26 %27 0 %28 1 %29\n"
953         "%28 = OpLabel\n"                       // (in % 2) == 0
954         "OpBranch %27\n"
955         "%29 = OpLabel\n"                       // (in % 2) == 1
956         "OpBranch %27\n"
957         "%27 = OpLabel\n"
958         // %26 = out value
959         // End of branch logic
960         "OpStore %25 %26\n"               // use SSA value from previous block
961         "OpReturn\n"
962         "OpFunctionEnd\n";
963 	// clang-format on
964 
965 	test(
966 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i % 2; });
967 }
968 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchStore)969 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchStore)
970 {
971 	std::stringstream src;
972 	// clang-format off
973     src <<
974         "OpCapability Shader\n"
975         "OpMemoryModel Logical GLSL450\n"
976         "OpEntryPoint GLCompute %1 \"main\" %2\n"
977         "OpExecutionMode %1 LocalSize " <<
978         GetParam().localSizeX << " " <<
979         GetParam().localSizeY << " " <<
980         GetParam().localSizeZ << "\n" <<
981         "OpDecorate %3 ArrayStride 4\n"
982         "OpMemberDecorate %4 0 Offset 0\n"
983         "OpDecorate %4 BufferBlock\n"
984         "OpDecorate %5 DescriptorSet 0\n"
985         "OpDecorate %5 Binding 1\n"
986         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
987         "OpDecorate %6 DescriptorSet 0\n"
988         "OpDecorate %6 Binding 0\n"
989         "%7 = OpTypeVoid\n"
990         "%8 = OpTypeFunction %7\n"             // void()
991         "%9 = OpTypeInt 32 1\n"                // int32
992         "%10 = OpTypeInt 32 0\n"                // uint32
993         "%11 = OpTypeBool\n"
994         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
995         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
996         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
997         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
998         "%13 = OpConstant %9 0\n"               // int32(0)
999         "%14 = OpConstant %9 1\n"               // int32(1)
1000         "%15 = OpConstant %9 2\n"               // int32(2)
1001         "%16 = OpConstant %10 0\n"              // uint32(0)
1002         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1003         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1004         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1005         "%19 = OpTypePointer Input %10\n"       // uint32*
1006         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1007         "%20 = OpTypePointer Uniform %9\n"      // int32*
1008         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1009         "%21 = OpLabel\n"
1010         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1011         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1012         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1013         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1014         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1015                                                 // Start of branch logic
1016                                                 // %25 = in value
1017         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1018         "OpSelectionMerge %28 None\n"
1019         "OpSwitch %27 %28 0 %29 1 %30\n"
1020         "%29 = OpLabel\n"                       // (in % 2) == 0
1021         "OpStore %26 %15\n"               // write 2
1022         "OpBranch %28\n"
1023         "%30 = OpLabel\n"                       // (in % 2) == 1
1024         "OpStore %26 %14\n"               // write 1
1025         "OpBranch %28\n"
1026         "%28 = OpLabel\n"
1027         // End of branch logic
1028         "OpReturn\n"
1029         "OpFunctionEnd\n";
1030 	// clang-format on
1031 
1032 	test(
1033 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 2 : 1; });
1034 }
1035 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchCaseReturn)1036 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchCaseReturn)
1037 {
1038 	std::stringstream src;
1039 	// clang-format off
1040     src <<
1041         "OpCapability Shader\n"
1042         "OpMemoryModel Logical GLSL450\n"
1043         "OpEntryPoint GLCompute %1 \"main\" %2\n"
1044         "OpExecutionMode %1 LocalSize " <<
1045         GetParam().localSizeX << " " <<
1046         GetParam().localSizeY << " " <<
1047         GetParam().localSizeZ << "\n" <<
1048         "OpDecorate %3 ArrayStride 4\n"
1049         "OpMemberDecorate %4 0 Offset 0\n"
1050         "OpDecorate %4 BufferBlock\n"
1051         "OpDecorate %5 DescriptorSet 0\n"
1052         "OpDecorate %5 Binding 1\n"
1053         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
1054         "OpDecorate %6 DescriptorSet 0\n"
1055         "OpDecorate %6 Binding 0\n"
1056         "%7 = OpTypeVoid\n"
1057         "%8 = OpTypeFunction %7\n"             // void()
1058         "%9 = OpTypeInt 32 1\n"                // int32
1059         "%10 = OpTypeInt 32 0\n"                // uint32
1060         "%11 = OpTypeBool\n"
1061         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
1062         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
1063         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
1064         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
1065         "%13 = OpConstant %9 0\n"               // int32(0)
1066         "%14 = OpConstant %9 1\n"               // int32(1)
1067         "%15 = OpConstant %9 2\n"               // int32(2)
1068         "%16 = OpConstant %10 0\n"              // uint32(0)
1069         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1070         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1071         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1072         "%19 = OpTypePointer Input %10\n"       // uint32*
1073         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1074         "%20 = OpTypePointer Uniform %9\n"      // int32*
1075         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1076         "%21 = OpLabel\n"
1077         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1078         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1079         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1080         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1081         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1082                                                 // Start of branch logic
1083                                                 // %25 = in value
1084         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1085         "OpSelectionMerge %28 None\n"
1086         "OpSwitch %27 %28 0 %29 1 %30\n"
1087         "%29 = OpLabel\n"                       // (in % 2) == 0
1088         "OpBranch %28\n"
1089         "%30 = OpLabel\n"                       // (in % 2) == 1
1090         "OpReturn\n"
1091         "%28 = OpLabel\n"
1092         "OpStore %26 %14\n"               // write 1
1093                                           // End of branch logic
1094         "OpReturn\n"
1095         "OpFunctionEnd\n";
1096 	// clang-format on
1097 
1098 	test(
1099 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 1 ? 0 : 1; });
1100 }
1101 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchDefaultReturn)1102 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchDefaultReturn)
1103 {
1104 	std::stringstream src;
1105 	// clang-format off
1106     src <<
1107         "OpCapability Shader\n"
1108         "OpMemoryModel Logical GLSL450\n"
1109         "OpEntryPoint GLCompute %1 \"main\" %2\n"
1110         "OpExecutionMode %1 LocalSize " <<
1111         GetParam().localSizeX << " " <<
1112         GetParam().localSizeY << " " <<
1113         GetParam().localSizeZ << "\n" <<
1114         "OpDecorate %3 ArrayStride 4\n"
1115         "OpMemberDecorate %4 0 Offset 0\n"
1116         "OpDecorate %4 BufferBlock\n"
1117         "OpDecorate %5 DescriptorSet 0\n"
1118         "OpDecorate %5 Binding 1\n"
1119         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
1120         "OpDecorate %6 DescriptorSet 0\n"
1121         "OpDecorate %6 Binding 0\n"
1122         "%7 = OpTypeVoid\n"
1123         "%8 = OpTypeFunction %7\n"             // void()
1124         "%9 = OpTypeInt 32 1\n"                // int32
1125         "%10 = OpTypeInt 32 0\n"                // uint32
1126         "%11 = OpTypeBool\n"
1127         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
1128         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
1129         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
1130         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
1131         "%13 = OpConstant %9 0\n"               // int32(0)
1132         "%14 = OpConstant %9 1\n"               // int32(1)
1133         "%15 = OpConstant %9 2\n"               // int32(2)
1134         "%16 = OpConstant %10 0\n"              // uint32(0)
1135         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1136         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1137         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1138         "%19 = OpTypePointer Input %10\n"       // uint32*
1139         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1140         "%20 = OpTypePointer Uniform %9\n"      // int32*
1141         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1142         "%21 = OpLabel\n"
1143         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1144         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1145         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1146         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1147         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1148                                                 // Start of branch logic
1149                                                 // %25 = in value
1150         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1151         "OpSelectionMerge %28 None\n"
1152         "OpSwitch %27 %29 1 %30\n"
1153         "%30 = OpLabel\n"                       // (in % 2) == 1
1154         "OpBranch %28\n"
1155         "%29 = OpLabel\n"                       // (in % 2) != 1
1156         "OpReturn\n"
1157         "%28 = OpLabel\n"                       // merge
1158         "OpStore %26 %14\n"               // write 1
1159                                           // End of branch logic
1160         "OpReturn\n"
1161         "OpFunctionEnd\n";
1162 	// clang-format on
1163 
1164 	test(
1165 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 1 ? 1 : 0; });
1166 }
1167 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchCaseFallthrough)1168 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchCaseFallthrough)
1169 {
1170 	std::stringstream src;
1171 	// clang-format off
1172     src <<
1173         "OpCapability Shader\n"
1174         "OpMemoryModel Logical GLSL450\n"
1175         "OpEntryPoint GLCompute %1 \"main\" %2\n"
1176         "OpExecutionMode %1 LocalSize " <<
1177         GetParam().localSizeX << " " <<
1178         GetParam().localSizeY << " " <<
1179         GetParam().localSizeZ << "\n" <<
1180         "OpDecorate %3 ArrayStride 4\n"
1181         "OpMemberDecorate %4 0 Offset 0\n"
1182         "OpDecorate %4 BufferBlock\n"
1183         "OpDecorate %5 DescriptorSet 0\n"
1184         "OpDecorate %5 Binding 1\n"
1185         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
1186         "OpDecorate %6 DescriptorSet 0\n"
1187         "OpDecorate %6 Binding 0\n"
1188         "%7 = OpTypeVoid\n"
1189         "%8 = OpTypeFunction %7\n"             // void()
1190         "%9 = OpTypeInt 32 1\n"                // int32
1191         "%10 = OpTypeInt 32 0\n"                // uint32
1192         "%11 = OpTypeBool\n"
1193         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
1194         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
1195         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
1196         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
1197         "%13 = OpConstant %9 0\n"               // int32(0)
1198         "%14 = OpConstant %9 1\n"               // int32(1)
1199         "%15 = OpConstant %9 2\n"               // int32(2)
1200         "%16 = OpConstant %10 0\n"              // uint32(0)
1201         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1202         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1203         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1204         "%19 = OpTypePointer Input %10\n"       // uint32*
1205         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1206         "%20 = OpTypePointer Uniform %9\n"      // int32*
1207         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1208         "%21 = OpLabel\n"
1209         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1210         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1211         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1212         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1213         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1214                                                 // Start of branch logic
1215                                                 // %25 = in value
1216         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1217         "OpSelectionMerge %28 None\n"
1218         "OpSwitch %27 %29 0 %30 1 %31\n"
1219         "%30 = OpLabel\n"                       // (in % 2) == 0
1220         "%32 = OpIAdd %9 %27 %14\n"             // generate an intermediate
1221         "OpStore %26 %32\n"               // write a value (overwritten later)
1222         "OpBranch %31\n"                  // fallthrough
1223         "%31 = OpLabel\n"                       // (in % 2) == 1
1224         "OpStore %26 %15\n"               // write 2
1225         "OpBranch %28\n"
1226         "%29 = OpLabel\n"                       // unreachable
1227         "OpUnreachable\n"
1228         "%28 = OpLabel\n"                       // merge
1229                                                 // End of branch logic
1230         "OpReturn\n"
1231         "OpFunctionEnd\n";
1232 	// clang-format on
1233 
1234 	test(
1235 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return 2; });
1236 }
1237 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchDefaultFallthrough)1238 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchDefaultFallthrough)
1239 {
1240 	std::stringstream src;
1241 	// clang-format off
1242     src <<
1243         "OpCapability Shader\n"
1244         "OpMemoryModel Logical GLSL450\n"
1245         "OpEntryPoint GLCompute %1 \"main\" %2\n"
1246         "OpExecutionMode %1 LocalSize " <<
1247         GetParam().localSizeX << " " <<
1248         GetParam().localSizeY << " " <<
1249         GetParam().localSizeZ << "\n" <<
1250         "OpDecorate %3 ArrayStride 4\n"
1251         "OpMemberDecorate %4 0 Offset 0\n"
1252         "OpDecorate %4 BufferBlock\n"
1253         "OpDecorate %5 DescriptorSet 0\n"
1254         "OpDecorate %5 Binding 1\n"
1255         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
1256         "OpDecorate %6 DescriptorSet 0\n"
1257         "OpDecorate %6 Binding 0\n"
1258         "%7 = OpTypeVoid\n"
1259         "%8 = OpTypeFunction %7\n"             // void()
1260         "%9 = OpTypeInt 32 1\n"                // int32
1261         "%10 = OpTypeInt 32 0\n"                // uint32
1262         "%11 = OpTypeBool\n"
1263         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
1264         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
1265         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
1266         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
1267         "%13 = OpConstant %9 0\n"               // int32(0)
1268         "%14 = OpConstant %9 1\n"               // int32(1)
1269         "%15 = OpConstant %9 2\n"               // int32(2)
1270         "%16 = OpConstant %10 0\n"              // uint32(0)
1271         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1272         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1273         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1274         "%19 = OpTypePointer Input %10\n"       // uint32*
1275         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1276         "%20 = OpTypePointer Uniform %9\n"      // int32*
1277         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1278         "%21 = OpLabel\n"
1279         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1280         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1281         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1282         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1283         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1284                                                 // Start of branch logic
1285                                                 // %25 = in value
1286         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1287         "OpSelectionMerge %28 None\n"
1288         "OpSwitch %27 %29 0 %30 1 %31\n"
1289         "%30 = OpLabel\n"                       // (in % 2) == 0
1290         "%32 = OpIAdd %9 %27 %14\n"             // generate an intermediate
1291         "OpStore %26 %32\n"               // write a value (overwritten later)
1292         "OpBranch %29\n"                  // fallthrough
1293         "%29 = OpLabel\n"                       // default
1294         "%33 = OpIAdd %9 %27 %14\n"             // generate an intermediate
1295         "OpStore %26 %33\n"               // write a value (overwritten later)
1296         "OpBranch %31\n"                  // fallthrough
1297         "%31 = OpLabel\n"                       // (in % 2) == 1
1298         "OpStore %26 %15\n"               // write 2
1299         "OpBranch %28\n"
1300         "%28 = OpLabel\n"                       // merge
1301                                                 // End of branch logic
1302         "OpReturn\n"
1303         "OpFunctionEnd\n";
1304 	// clang-format on
1305 
1306 	test(
1307 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return 2; });
1308 }
1309 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,SwitchPhi)1310 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, SwitchPhi)
1311 {
1312 	std::stringstream src;
1313 	// clang-format off
1314     src <<
1315         "OpCapability Shader\n"
1316         "OpMemoryModel Logical GLSL450\n"
1317         "OpEntryPoint GLCompute %1 \"main\" %2\n"
1318         "OpExecutionMode %1 LocalSize " <<
1319         GetParam().localSizeX << " " <<
1320         GetParam().localSizeY << " " <<
1321         GetParam().localSizeZ << "\n" <<
1322         "OpDecorate %3 ArrayStride 4\n"
1323         "OpMemberDecorate %4 0 Offset 0\n"
1324         "OpDecorate %4 BufferBlock\n"
1325         "OpDecorate %5 DescriptorSet 0\n"
1326         "OpDecorate %5 Binding 1\n"
1327         "OpDecorate %2 BuiltIn GlobalInvocationId\n"
1328         "OpDecorate %6 DescriptorSet 0\n"
1329         "OpDecorate %6 Binding 0\n"
1330         "%7 = OpTypeVoid\n"
1331         "%8 = OpTypeFunction %7\n"             // void()
1332         "%9 = OpTypeInt 32 1\n"                // int32
1333         "%10 = OpTypeInt 32 0\n"                // uint32
1334         "%11 = OpTypeBool\n"
1335         "%3 = OpTypeRuntimeArray %9\n"         // int32[]
1336         "%4 = OpTypeStruct %3\n"               // struct{ int32[] }
1337         "%12 = OpTypePointer Uniform %4\n"      // struct{ int32[] }*
1338         "%5 = OpVariable %12 Uniform\n"        // struct{ int32[] }* in
1339         "%13 = OpConstant %9 0\n"               // int32(0)
1340         "%14 = OpConstant %9 1\n"               // int32(1)
1341         "%15 = OpConstant %9 2\n"               // int32(2)
1342         "%16 = OpConstant %10 0\n"              // uint32(0)
1343         "%17 = OpTypeVector %10 3\n"            // vec4<int32>
1344         "%18 = OpTypePointer Input %17\n"       // vec4<int32>*
1345         "%2 = OpVariable %18 Input\n"          // gl_GlobalInvocationId
1346         "%19 = OpTypePointer Input %10\n"       // uint32*
1347         "%6 = OpVariable %12 Uniform\n"        // struct{ int32[] }* out
1348         "%20 = OpTypePointer Uniform %9\n"      // int32*
1349         "%1 = OpFunction %7 None %8\n"         // -- Function begin --
1350         "%21 = OpLabel\n"
1351         "%22 = OpAccessChain %19 %2 %16\n"      // &gl_GlobalInvocationId.x
1352         "%23 = OpLoad %10 %22\n"                // gl_GlobalInvocationId.x
1353         "%24 = OpAccessChain %20 %6 %13 %23\n"  // &in.arr[gl_GlobalInvocationId.x]
1354         "%25 = OpLoad %9 %24\n"                 // in.arr[gl_GlobalInvocationId.x]
1355         "%26 = OpAccessChain %20 %5 %13 %23\n"  // &out.arr[gl_GlobalInvocationId.x]
1356                                                 // Start of branch logic
1357                                                 // %25 = in value
1358         "%27 = OpSMod %9 %25 %15\n"             // in % 2
1359         "OpSelectionMerge %28 None\n"
1360         "OpSwitch %27 %29 1 %30\n"
1361         "%30 = OpLabel\n"                       // (in % 2) == 1
1362         "OpBranch %28\n"
1363         "%29 = OpLabel\n"                       // (in % 2) != 1
1364         "OpBranch %28\n"
1365         "%28 = OpLabel\n"                       // merge
1366         "%31 = OpPhi %9 %14 %30 %15 %29\n"      // (in % 2) == 1 ? 1 : 2
1367         "OpStore %26 %31\n"
1368         // End of branch logic
1369         "OpReturn\n"
1370         "OpFunctionEnd\n";
1371 	// clang-format on
1372 
1373 	test(
1374 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 1 ? 1 : 2; });
1375 }
1376 
TEST_P(SwiftShaderVulkanBufferToBufferComputeTest,LoopDivergentMergePhi)1377 TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, LoopDivergentMergePhi)
1378 {
1379 	// #version 450
1380 	// layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
1381 	// layout(binding = 0, std430) buffer InBuffer
1382 	// {
1383 	//     int Data[];
1384 	// } In;
1385 	// layout(binding = 1, std430) buffer OutBuffer
1386 	// {
1387 	//     int Data[];
1388 	// } Out;
1389 	// void main()
1390 	// {
1391 	//     int phi = 0;
1392 	//     uint lane = gl_GlobalInvocationID.x % 4;
1393 	//     for (uint i = 0; i < 4; i++)
1394 	//     {
1395 	//         if (lane == i)
1396 	//         {
1397 	//             phi = In.Data[gl_GlobalInvocationID.x];
1398 	//             break;
1399 	//         }
1400 	//     }
1401 	//     Out.Data[gl_GlobalInvocationID.x] = phi;
1402 	// }
1403 	std::stringstream src;
1404 	// clang-format off
1405     src <<
1406         "OpCapability Shader\n"
1407         "%1 = OpExtInstImport \"GLSL.std.450\"\n"
1408         "OpMemoryModel Logical GLSL450\n"
1409         "OpEntryPoint GLCompute %2 \"main\" %3\n"
1410         "OpExecutionMode %2 LocalSize " <<
1411         GetParam().localSizeX << " " <<
1412         GetParam().localSizeY << " " <<
1413         GetParam().localSizeZ << "\n" <<
1414         "OpDecorate %3 BuiltIn GlobalInvocationId\n"
1415         "OpDecorate %4 ArrayStride 4\n"
1416         "OpMemberDecorate %5 0 Offset 0\n"
1417         "OpDecorate %5 BufferBlock\n"
1418         "OpDecorate %6 DescriptorSet 0\n"
1419         "OpDecorate %6 Binding 0\n"
1420         "OpDecorate %7 ArrayStride 4\n"
1421         "OpMemberDecorate %8 0 Offset 0\n"
1422         "OpDecorate %8 BufferBlock\n"
1423         "OpDecorate %9 DescriptorSet 0\n"
1424         "OpDecorate %9 Binding 1\n"
1425         "%10 = OpTypeVoid\n"
1426         "%11 = OpTypeFunction %10\n"
1427         "%12 = OpTypeInt 32 1\n"
1428         "%13 = OpConstant %12 0\n"
1429         "%14 = OpTypeInt 32 0\n"
1430         "%15 = OpTypeVector %14 3\n"
1431         "%16 = OpTypePointer Input %15\n"
1432         "%3 = OpVariable %16 Input\n"
1433         "%17 = OpConstant %14 0\n"
1434         "%18 = OpTypePointer Input %14\n"
1435         "%19 = OpConstant %14 4\n"
1436         "%20 = OpTypeBool\n"
1437         "%4 = OpTypeRuntimeArray %12\n"
1438         "%5 = OpTypeStruct %4\n"
1439         "%21 = OpTypePointer Uniform %5\n"
1440         "%6 = OpVariable %21 Uniform\n"
1441         "%22 = OpTypePointer Uniform %12\n"
1442         "%23 = OpConstant %12 1\n"
1443         "%7 = OpTypeRuntimeArray %12\n"
1444         "%8 = OpTypeStruct %7\n"
1445         "%24 = OpTypePointer Uniform %8\n"
1446         "%9 = OpVariable %24 Uniform\n"
1447         "%2 = OpFunction %10 None %11\n"
1448         "%25 = OpLabel\n"
1449         "%26 = OpAccessChain %18 %3 %17\n"
1450         "%27 = OpLoad %14 %26\n"
1451         "%28 = OpUMod %14 %27 %19\n"
1452         "OpBranch %29\n"
1453         "%29 = OpLabel\n"
1454         "%30 = OpPhi %14 %17 %25 %31 %32\n"
1455         "%33 = OpULessThan %20 %30 %19\n"
1456         "OpLoopMerge %34 %32 None\n"
1457         "OpBranchConditional %33 %35 %34\n"
1458         "%35 = OpLabel\n"
1459         "%36 = OpIEqual %20 %28 %30\n"
1460         "OpSelectionMerge %37 None\n"
1461         "OpBranchConditional %36 %38 %37\n"
1462         "%38 = OpLabel\n"
1463         "%39 = OpAccessChain %22 %6 %13 %27\n"
1464         "%40 = OpLoad %12 %39\n"
1465         "OpBranch %34\n"
1466         "%37 = OpLabel\n"
1467         "OpBranch %32\n"
1468         "%32 = OpLabel\n"
1469         "%31 = OpIAdd %14 %30 %23\n"
1470         "OpBranch %29\n"
1471         "%34 = OpLabel\n"
1472         "%41 = OpPhi %12 %13 %29 %40 %38\n" // %40: phi
1473         "%42 = OpAccessChain %22 %9 %13 %27\n"
1474         "OpStore %42 %41\n"
1475         "OpReturn\n"
1476         "OpFunctionEnd\n";
1477 	// clang-format on
1478 
1479 	test(
1480 	    src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i; });
1481 }
1482