break;
}
+ case spv::OpSelectionMerge:
+ break; // Nothing to do in analysis pass.
+
case spv::OpTypeVoid:
case spv::OpTypeBool:
case spv::OpTypeInt:
case spv::OpDPdyFine:
case spv::OpFwidthFine:
case spv::OpAtomicLoad:
+ case spv::OpPhi:
// Instructions that yield an intermediate value
{
Type::ID typeId = insn.word(1);
switch (block.kind)
{
case Block::Simple:
+ case Block::StructuredBranchConditional:
+ case Block::UnstructuredBranchConditional:
if (id != mainBlockId)
{
// Emit all preceeding blocks and set the activeLaneMask.
return EmitResult::Continue;
case spv::OpLabel:
- case spv::OpReturn:
- // TODO: when we do control flow, will need to do some work here.
- // Until then, there is nothing to do -- we expect there to be an initial OpLabel
- // in the entrypoint function, for which we do nothing; and a final OpReturn at the
- // end of the entrypoint function, for which we do nothing.
return EmitResult::Continue;
case spv::OpVariable:
case spv::OpBranch:
return EmitBranch(insn, state);
+ case spv::OpPhi:
+ return EmitPhi(insn, state);
+
+ case spv::OpSelectionMerge:
+ return EmitResult::Continue;
+
+ case spv::OpBranchConditional:
+ return EmitBranchConditional(insn, state);
+
+ case spv::OpUnreachable:
+ return EmitUnreachable(insn, state);
+
+ case spv::OpReturn:
+ return EmitReturn(insn, state);
+
default:
UNIMPLEMENTED("opcode: %s", OpcodeName(insn.opcode()).c_str());
break;
return EmitResult::Terminator;
}
+ SpirvShader::EmitResult SpirvShader::EmitBranchConditional(InsnIterator insn, EmitState *state) const
+ {
+ auto block = getBlock(state->currentBlock);
+ ASSERT(block.branchInstruction == insn);
+
+ auto condId = Object::ID(block.branchInstruction.word(1));
+ auto trueBlockId = Block::ID(block.branchInstruction.word(2));
+ auto falseBlockId = Block::ID(block.branchInstruction.word(3));
+
+ auto cond = GenericValue(this, state->routine, condId);
+ ASSERT_MSG(getType(getObject(condId).type).sizeInComponents == 1, "Condition must be a Boolean type scalar");
+
+ // TODO: Optimize for case where all lanes take same path.
+
+ state->addOutputActiveLaneMaskEdge(trueBlockId, cond.Int(0));
+ state->addOutputActiveLaneMaskEdge(falseBlockId, ~cond.Int(0));
+
+ return EmitResult::Terminator;
+ }
+
+
+ SpirvShader::EmitResult SpirvShader::EmitUnreachable(InsnIterator insn, EmitState *state) const
+ {
+ // TODO: Log something in this case?
+ state->setActiveLaneMask(SIMD::Int(0));
+ return EmitResult::Terminator;
+ }
+
+ SpirvShader::EmitResult SpirvShader::EmitReturn(InsnIterator insn, EmitState *state) const
+ {
+ state->setActiveLaneMask(SIMD::Int(0));
+ return EmitResult::Terminator;
+ }
+
+ SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
+ {
+ auto routine = state->routine;
+ auto typeId = Type::ID(insn.word(1));
+ auto type = getType(typeId);
+ auto objectId = Object::ID(insn.word(2));
+
+ auto &dst = routine->createIntermediate(objectId, type.sizeInComponents);
+
+ bool first = true;
+ for (uint32_t w = 3; w < insn.wordCount(); w += 2)
+ {
+ auto varId = Object::ID(insn.word(w + 0));
+ auto blockId = Block::ID(insn.word(w + 1));
+
+ auto in = GenericValue(this, routine, varId);
+ auto mask = state->getActiveLaneMaskEdge(blockId, state->currentBlock);
+
+ for (uint32_t i = 0; i < type.sizeInComponents; i++)
+ {
+ auto inMasked = in.Int(i) & mask;
+ dst.replace(i, first ? inMasked : (dst.Int(i) | inMasked));
+ }
+ first = false;
+ }
+
+ return EmitResult::Continue;
+ }
+
void SpirvShader::emitEpilog(SpirvRoutine *routine) const
{
for (auto insn : *this)
"OpFunctionEnd\n";\r
\r
test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i * 2; });\r
-}
\ No newline at end of file
+}\r
+\r
+TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalSimple)\r
+{\r
+ std::stringstream src;\r
+ src <<\r
+ "OpCapability Shader\n"\r
+ "OpMemoryModel Logical GLSL450\n"\r
+ "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+ "OpExecutionMode %1 LocalSize " <<\r
+ GetParam().localSizeX << " " <<\r
+ GetParam().localSizeY << " " <<\r
+ GetParam().localSizeZ << "\n" <<\r
+ "OpDecorate %3 ArrayStride 4\n"\r
+ "OpMemberDecorate %4 0 Offset 0\n"\r
+ "OpDecorate %4 BufferBlock\n"\r
+ "OpDecorate %5 DescriptorSet 0\n"\r
+ "OpDecorate %5 Binding 1\n"\r
+ "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+ "OpDecorate %6 DescriptorSet 0\n"\r
+ "OpDecorate %6 Binding 0\n"\r
+ "%7 = OpTypeVoid\n"\r
+ "%8 = OpTypeFunction %7\n" // void()\r
+ "%9 = OpTypeInt 32 1\n" // int32\r
+ "%10 = OpTypeInt 32 0\n" // uint32\r
+ "%11 = OpTypeBool\n"\r
+ "%3 = OpTypeRuntimeArray %9\n" // int32[]\r
+ "%4 = OpTypeStruct %3\n" // struct{ int32[] }\r
+ "%12 = OpTypePointer Uniform %4\n" // struct{ int32[] }*\r
+ "%5 = OpVariable %12 Uniform\n" // struct{ int32[] }* in\r
+ "%13 = OpConstant %9 0\n" // int32(0)\r
+ "%14 = OpConstant %9 2\n" // int32(2)\r
+ "%15 = OpConstant %10 0\n" // uint32(0)\r
+ "%16 = OpTypeVector %10 3\n" // vec4<int32>\r
+ "%17 = OpTypePointer Input %16\n" // vec4<int32>*\r
+ "%2 = OpVariable %17 Input\n" // gl_GlobalInvocationId\r
+ "%18 = OpTypePointer Input %10\n" // uint32*\r
+ "%6 = OpVariable %12 Uniform\n" // struct{ int32[] }* out\r
+ "%19 = OpTypePointer Uniform %9\n" // int32*\r
+ "%1 = OpFunction %7 None %8\n" // -- Function begin --\r
+ "%20 = OpLabel\n"\r
+ "%21 = OpAccessChain %18 %2 %15\n" // &gl_GlobalInvocationId.x\r
+ "%22 = OpLoad %10 %21\n" // gl_GlobalInvocationId.x\r
+ "%23 = OpAccessChain %19 %6 %13 %22\n" // &in.arr[gl_GlobalInvocationId.x]\r
+ "%24 = OpLoad %9 %23\n" // in.arr[gl_GlobalInvocationId.x]\r
+ "%25 = OpAccessChain %19 %5 %13 %22\n" // &out.arr[gl_GlobalInvocationId.x]\r
+ // Start of branch logic\r
+ // %24 = in value\r
+ "%26 = OpSMod %9 %24 %14\n" // in % 2\r
+ "%27 = OpIEqual %11 %26 %13\n" // (in % 2) == 0\r
+ "OpSelectionMerge %28 None\n"\r
+ "OpBranchConditional %27 %28 %28\n" // Both go to %28\r
+ "%28 = OpLabel\n"\r
+ // %26 = out value\r
+ // End of branch logic\r
+ "OpStore %25 %26\n" // use SSA value from previous block\r
+ "OpReturn\n"\r
+ "OpFunctionEnd\n";\r
+\r
+ test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i%2; });\r
+}\r
+\r
+TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalTwoEmptyBlocks)\r
+{\r
+ std::stringstream src;\r
+ src <<\r
+ "OpCapability Shader\n"\r
+ "OpMemoryModel Logical GLSL450\n"\r
+ "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+ "OpExecutionMode %1 LocalSize " <<\r
+ GetParam().localSizeX << " " <<\r
+ GetParam().localSizeY << " " <<\r
+ GetParam().localSizeZ << "\n" <<\r
+ "OpDecorate %3 ArrayStride 4\n"\r
+ "OpMemberDecorate %4 0 Offset 0\n"\r
+ "OpDecorate %4 BufferBlock\n"\r
+ "OpDecorate %5 DescriptorSet 0\n"\r
+ "OpDecorate %5 Binding 1\n"\r
+ "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+ "OpDecorate %6 DescriptorSet 0\n"\r
+ "OpDecorate %6 Binding 0\n"\r
+ "%7 = OpTypeVoid\n"\r
+ "%8 = OpTypeFunction %7\n" // void()\r
+ "%9 = OpTypeInt 32 1\n" // int32\r
+ "%10 = OpTypeInt 32 0\n" // uint32\r
+ "%11 = OpTypeBool\n"\r
+ "%3 = OpTypeRuntimeArray %9\n" // int32[]\r
+ "%4 = OpTypeStruct %3\n" // struct{ int32[] }\r
+ "%12 = OpTypePointer Uniform %4\n" // struct{ int32[] }*\r
+ "%5 = OpVariable %12 Uniform\n" // struct{ int32[] }* in\r
+ "%13 = OpConstant %9 0\n" // int32(0)\r
+ "%14 = OpConstant %9 2\n" // int32(2)\r
+ "%15 = OpConstant %10 0\n" // uint32(0)\r
+ "%16 = OpTypeVector %10 3\n" // vec4<int32>\r
+ "%17 = OpTypePointer Input %16\n" // vec4<int32>*\r
+ "%2 = OpVariable %17 Input\n" // gl_GlobalInvocationId\r
+ "%18 = OpTypePointer Input %10\n" // uint32*\r
+ "%6 = OpVariable %12 Uniform\n" // struct{ int32[] }* out\r
+ "%19 = OpTypePointer Uniform %9\n" // int32*\r
+ "%1 = OpFunction %7 None %8\n" // -- Function begin --\r
+ "%20 = OpLabel\n"\r
+ "%21 = OpAccessChain %18 %2 %15\n" // &gl_GlobalInvocationId.x\r
+ "%22 = OpLoad %10 %21\n" // gl_GlobalInvocationId.x\r
+ "%23 = OpAccessChain %19 %6 %13 %22\n" // &in.arr[gl_GlobalInvocationId.x]\r
+ "%24 = OpLoad %9 %23\n" // in.arr[gl_GlobalInvocationId.x]\r
+ "%25 = OpAccessChain %19 %5 %13 %22\n" // &out.arr[gl_GlobalInvocationId.x]\r
+ // Start of branch logic\r
+ // %24 = in value\r
+ "%26 = OpSMod %9 %24 %14\n" // in % 2\r
+ "%27 = OpIEqual %11 %26 %13\n" // (in % 2) == 0\r
+ "OpSelectionMerge %28 None\n"\r
+ "OpBranchConditional %27 %29 %30\n"\r
+ "%29 = OpLabel\n" // (in % 2) == 0\r
+ "OpBranch %28\n"\r
+ "%30 = OpLabel\n" // (in % 2) != 0\r
+ "OpBranch %28\n"\r
+ "%28 = OpLabel\n"\r
+ // %26 = out value\r
+ // End of branch logic\r
+ "OpStore %25 %26\n" // use SSA value from previous block\r
+ "OpReturn\n"\r
+ "OpFunctionEnd\n";\r
+\r
+ test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return i%2; });\r
+}\r
+\r
+// TODO: Test for parallel assignment\r
+TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalStore)\r
+{\r
+ std::stringstream src;\r
+ src <<\r
+ "OpCapability Shader\n"\r
+ "OpMemoryModel Logical GLSL450\n"\r
+ "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+ "OpExecutionMode %1 LocalSize " <<\r
+ GetParam().localSizeX << " " <<\r
+ GetParam().localSizeY << " " <<\r
+ GetParam().localSizeZ << "\n" <<\r
+ "OpDecorate %3 ArrayStride 4\n"\r
+ "OpMemberDecorate %4 0 Offset 0\n"\r
+ "OpDecorate %4 BufferBlock\n"\r
+ "OpDecorate %5 DescriptorSet 0\n"\r
+ "OpDecorate %5 Binding 1\n"\r
+ "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+ "OpDecorate %6 DescriptorSet 0\n"\r
+ "OpDecorate %6 Binding 0\n"\r
+ "%7 = OpTypeVoid\n"\r
+ "%8 = OpTypeFunction %7\n" // void()\r
+ "%9 = OpTypeInt 32 1\n" // int32\r
+ "%10 = OpTypeInt 32 0\n" // uint32\r
+ "%11 = OpTypeBool\n"\r
+ "%3 = OpTypeRuntimeArray %9\n" // int32[]\r
+ "%4 = OpTypeStruct %3\n" // struct{ int32[] }\r
+ "%12 = OpTypePointer Uniform %4\n" // struct{ int32[] }*\r
+ "%5 = OpVariable %12 Uniform\n" // struct{ int32[] }* in\r
+ "%13 = OpConstant %9 0\n" // int32(0)\r
+ "%14 = OpConstant %9 1\n" // int32(1)\r
+ "%15 = OpConstant %9 2\n" // int32(2)\r
+ "%16 = OpConstant %10 0\n" // uint32(0)\r
+ "%17 = OpTypeVector %10 3\n" // vec4<int32>\r
+ "%18 = OpTypePointer Input %17\n" // vec4<int32>*\r
+ "%2 = OpVariable %18 Input\n" // gl_GlobalInvocationId\r
+ "%19 = OpTypePointer Input %10\n" // uint32*\r
+ "%6 = OpVariable %12 Uniform\n" // struct{ int32[] }* out\r
+ "%20 = OpTypePointer Uniform %9\n" // int32*\r
+ "%1 = OpFunction %7 None %8\n" // -- Function begin --\r
+ "%21 = OpLabel\n"\r
+ "%22 = OpAccessChain %19 %2 %16\n" // &gl_GlobalInvocationId.x\r
+ "%23 = OpLoad %10 %22\n" // gl_GlobalInvocationId.x\r
+ "%24 = OpAccessChain %20 %6 %13 %23\n" // &in.arr[gl_GlobalInvocationId.x]\r
+ "%25 = OpLoad %9 %24\n" // in.arr[gl_GlobalInvocationId.x]\r
+ "%26 = OpAccessChain %20 %5 %13 %23\n" // &out.arr[gl_GlobalInvocationId.x]\r
+ // Start of branch logic\r
+ // %25 = in value\r
+ "%27 = OpSMod %9 %25 %15\n" // in % 2\r
+ "%28 = OpIEqual %11 %27 %13\n" // (in % 2) == 0\r
+ "OpSelectionMerge %29 None\n"\r
+ "OpBranchConditional %28 %30 %31\n"\r
+ "%30 = OpLabel\n" // (in % 2) == 0\r
+ "OpStore %26 %14\n" // write 1\r
+ "OpBranch %29\n"\r
+ "%31 = OpLabel\n" // (in % 2) != 0\r
+ "OpStore %26 %15\n" // write 2\r
+ "OpBranch %29\n"\r
+ "%29 = OpLabel\n"\r
+ // End of branch logic\r
+ "OpReturn\n"\r
+ "OpFunctionEnd\n";\r
+\r
+ test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 1 : 2; });\r
+}\r
+\r
+TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalReturnTrue)\r
+{\r
+ std::stringstream src;\r
+ src <<\r
+ "OpCapability Shader\n"\r
+ "OpMemoryModel Logical GLSL450\n"\r
+ "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+ "OpExecutionMode %1 LocalSize " <<\r
+ GetParam().localSizeX << " " <<\r
+ GetParam().localSizeY << " " <<\r
+ GetParam().localSizeZ << "\n" <<\r
+ "OpDecorate %3 ArrayStride 4\n"\r
+ "OpMemberDecorate %4 0 Offset 0\n"\r
+ "OpDecorate %4 BufferBlock\n"\r
+ "OpDecorate %5 DescriptorSet 0\n"\r
+ "OpDecorate %5 Binding 1\n"\r
+ "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+ "OpDecorate %6 DescriptorSet 0\n"\r
+ "OpDecorate %6 Binding 0\n"\r
+ "%7 = OpTypeVoid\n"\r
+ "%8 = OpTypeFunction %7\n" // void()\r
+ "%9 = OpTypeInt 32 1\n" // int32\r
+ "%10 = OpTypeInt 32 0\n" // uint32\r
+ "%11 = OpTypeBool\n"\r
+ "%3 = OpTypeRuntimeArray %9\n" // int32[]\r
+ "%4 = OpTypeStruct %3\n" // struct{ int32[] }\r
+ "%12 = OpTypePointer Uniform %4\n" // struct{ int32[] }*\r
+ "%5 = OpVariable %12 Uniform\n" // struct{ int32[] }* in\r
+ "%13 = OpConstant %9 0\n" // int32(0)\r
+ "%14 = OpConstant %9 1\n" // int32(1)\r
+ "%15 = OpConstant %9 2\n" // int32(2)\r
+ "%16 = OpConstant %10 0\n" // uint32(0)\r
+ "%17 = OpTypeVector %10 3\n" // vec4<int32>\r
+ "%18 = OpTypePointer Input %17\n" // vec4<int32>*\r
+ "%2 = OpVariable %18 Input\n" // gl_GlobalInvocationId\r
+ "%19 = OpTypePointer Input %10\n" // uint32*\r
+ "%6 = OpVariable %12 Uniform\n" // struct{ int32[] }* out\r
+ "%20 = OpTypePointer Uniform %9\n" // int32*\r
+ "%1 = OpFunction %7 None %8\n" // -- Function begin --\r
+ "%21 = OpLabel\n"\r
+ "%22 = OpAccessChain %19 %2 %16\n" // &gl_GlobalInvocationId.x\r
+ "%23 = OpLoad %10 %22\n" // gl_GlobalInvocationId.x\r
+ "%24 = OpAccessChain %20 %6 %13 %23\n" // &in.arr[gl_GlobalInvocationId.x]\r
+ "%25 = OpLoad %9 %24\n" // in.arr[gl_GlobalInvocationId.x]\r
+ "%26 = OpAccessChain %20 %5 %13 %23\n" // &out.arr[gl_GlobalInvocationId.x]\r
+ // Start of branch logic\r
+ // %25 = in value\r
+ "%27 = OpSMod %9 %25 %15\n" // in % 2\r
+ "%28 = OpIEqual %11 %27 %13\n" // (in % 2) == 0\r
+ "OpSelectionMerge %29 None\n"\r
+ "OpBranchConditional %28 %30 %29\n"\r
+ "%30 = OpLabel\n" // (in % 2) == 0\r
+ "OpReturn\n"\r
+ "%29 = OpLabel\n" // merge\r
+ "OpStore %26 %15\n" // write 2\r
+ // End of branch logic\r
+ "OpReturn\n"\r
+ "OpFunctionEnd\n";\r
+\r
+ test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 0 : 2; });\r
+}\r
+\r
+// TODO: Test for parallel assignment\r
+TEST_P(SwiftShaderVulkanBufferToBufferComputeTest, BranchConditionalPhi)\r
+{\r
+ std::stringstream src;\r
+ src <<\r
+ "OpCapability Shader\n"\r
+ "OpMemoryModel Logical GLSL450\n"\r
+ "OpEntryPoint GLCompute %1 \"main\" %2\n"\r
+ "OpExecutionMode %1 LocalSize " <<\r
+ GetParam().localSizeX << " " <<\r
+ GetParam().localSizeY << " " <<\r
+ GetParam().localSizeZ << "\n" <<\r
+ "OpDecorate %3 ArrayStride 4\n"\r
+ "OpMemberDecorate %4 0 Offset 0\n"\r
+ "OpDecorate %4 BufferBlock\n"\r
+ "OpDecorate %5 DescriptorSet 0\n"\r
+ "OpDecorate %5 Binding 1\n"\r
+ "OpDecorate %2 BuiltIn GlobalInvocationId\n"\r
+ "OpDecorate %6 DescriptorSet 0\n"\r
+ "OpDecorate %6 Binding 0\n"\r
+ "%7 = OpTypeVoid\n"\r
+ "%8 = OpTypeFunction %7\n" // void()\r
+ "%9 = OpTypeInt 32 1\n" // int32\r
+ "%10 = OpTypeInt 32 0\n" // uint32\r
+ "%11 = OpTypeBool\n"\r
+ "%3 = OpTypeRuntimeArray %9\n" // int32[]\r
+ "%4 = OpTypeStruct %3\n" // struct{ int32[] }\r
+ "%12 = OpTypePointer Uniform %4\n" // struct{ int32[] }*\r
+ "%5 = OpVariable %12 Uniform\n" // struct{ int32[] }* in\r
+ "%13 = OpConstant %9 0\n" // int32(0)\r
+ "%14 = OpConstant %9 1\n" // int32(1)\r
+ "%15 = OpConstant %9 2\n" // int32(2)\r
+ "%16 = OpConstant %10 0\n" // uint32(0)\r
+ "%17 = OpTypeVector %10 3\n" // vec4<int32>\r
+ "%18 = OpTypePointer Input %17\n" // vec4<int32>*\r
+ "%2 = OpVariable %18 Input\n" // gl_GlobalInvocationId\r
+ "%19 = OpTypePointer Input %10\n" // uint32*\r
+ "%6 = OpVariable %12 Uniform\n" // struct{ int32[] }* out\r
+ "%20 = OpTypePointer Uniform %9\n" // int32*\r
+ "%1 = OpFunction %7 None %8\n" // -- Function begin --\r
+ "%21 = OpLabel\n"\r
+ "%22 = OpAccessChain %19 %2 %16\n" // &gl_GlobalInvocationId.x\r
+ "%23 = OpLoad %10 %22\n" // gl_GlobalInvocationId.x\r
+ "%24 = OpAccessChain %20 %6 %13 %23\n" // &in.arr[gl_GlobalInvocationId.x]\r
+ "%25 = OpLoad %9 %24\n" // in.arr[gl_GlobalInvocationId.x]\r
+ "%26 = OpAccessChain %20 %5 %13 %23\n" // &out.arr[gl_GlobalInvocationId.x]\r
+ // Start of branch logic\r
+ // %25 = in value\r
+ "%27 = OpSMod %9 %25 %15\n" // in % 2\r
+ "%28 = OpIEqual %11 %27 %13\n" // (in % 2) == 0\r
+ "OpSelectionMerge %29 None\n"\r
+ "OpBranchConditional %28 %30 %31\n"\r
+ "%30 = OpLabel\n" // (in % 2) == 0\r
+ "OpBranch %29\n"\r
+ "%31 = OpLabel\n" // (in % 2) != 0\r
+ "OpBranch %29\n"\r
+ "%29 = OpLabel\n"\r
+ "%32 = OpPhi %9 %14 %30 %15 %31\n" // (in % 2) == 0 ? 1 : 2\r
+ // End of branch logic\r
+ "OpStore %26 %32\n"\r
+ "OpReturn\n"\r
+ "OpFunctionEnd\n";\r
+\r
+ test(src.str(), [](uint32_t i) { return i; }, [](uint32_t i) { return (i % 2) == 0 ? 1 : 2; });\r
+}\r
+\r