Fix support for OpKill as non-terminator
Fixing this issue required using the WrapOpKill SPIRV-Tools
pass. This pass wraps OpKill instructions inside a function.
See the following for the explanation:
https://github.com/KhronosGroup/SPIRV-Tools/issues/2726
dEQP-GLES2.functional.shaders.discard.function_always fails
on ANGLE on top of SwiftShader Vulkan without this fix.
Bug b/141246700
Change-Id: I48ce3c19a1b02160fd8f23b60a75e5f8f35a4d37
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/36450
Presubmit-Ready: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
index 0498c6c..b4b1f73 100644
--- a/src/Pipeline/SpirvShader.cpp
+++ b/src/Pipeline/SpirvShader.cpp
@@ -917,12 +917,15 @@
break;
case spv::OpFunctionParameter:
- case spv::OpFunctionCall:
// These should have all been removed by preprocessing passes. If we see them here,
// our assumptions are wrong and we will probably generate wrong code.
UNREACHABLE("%s should have already been lowered.", OpcodeName(opcode).c_str());
break;
+ case spv::OpFunctionCall:
+ // TODO(b/141246700): Add full support for spv::OpFunctionCall
+ break;
+
case spv::OpFConvert:
UNSUPPORTED("SPIR-V Float16 or Float64 Capability (OpFConvert)");
break;
@@ -2693,6 +2696,9 @@
case spv::OpReturn:
return EmitReturn(insn, state);
+ case spv::OpFunctionCall:
+ return EmitFunctionCall(insn, state);
+
case spv::OpKill:
return EmitKill(insn, state);
@@ -4833,6 +4839,44 @@
return EmitResult::Terminator;
}
+ SpirvShader::EmitResult SpirvShader::EmitFunctionCall(InsnIterator insn, EmitState *state) const
+ {
+ auto functionId = Function::ID(insn.word(3));
+ const auto& functionIt = functions.find(functionId);
+ ASSERT(functionIt != functions.end());
+ auto& function = functionIt->second;
+
+ // TODO(b/141246700): Add full support for spv::OpFunctionCall
+ // The only supported function is a single OpKill wrapped in a
+ // function, as a result of the "wrap OpKill" SPIRV-Tools pass
+ ASSERT(function.blocks.size() == 1);
+ spv::Op wrapOpKill[] = { spv::OpLabel, spv::OpKill };
+
+ for (auto block : function.blocks)
+ {
+ int insnNumber = 0;
+ for (auto blockInsn : block.second)
+ {
+ if (insnNumber > 1)
+ {
+ UNIMPLEMENTED("Function block number of instructions: %d", insnNumber);
+ return EmitResult::Continue;
+ }
+ if (blockInsn.opcode() != wrapOpKill[insnNumber++])
+ {
+ UNIMPLEMENTED("Function block instruction %d : %s", insnNumber - 1, OpcodeName(blockInsn.opcode()).c_str());
+ return EmitResult::Continue;
+ }
+ if (blockInsn.opcode() == spv::OpKill)
+ {
+ EmitInstruction(blockInsn, state);
+ }
+ }
+ }
+
+ return EmitResult::Continue;
+ }
+
SpirvShader::EmitResult SpirvShader::EmitPhi(InsnIterator insn, EmitState *state) const
{
auto &function = getFunction(state->function);
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
index 4a28af6..34565cb 100644
--- a/src/Pipeline/SpirvShader.hpp
+++ b/src/Pipeline/SpirvShader.hpp
@@ -1224,6 +1224,7 @@
EmitResult EmitUnreachable(InsnIterator insn, EmitState *state) const;
EmitResult EmitReturn(InsnIterator insn, EmitState *state) const;
EmitResult EmitKill(InsnIterator insn, EmitState *state) const;
+ EmitResult EmitFunctionCall(InsnIterator insn, EmitState *state) const;
EmitResult EmitPhi(InsnIterator insn, EmitState *state) const;
EmitResult EmitImageSampleImplicitLod(Variant variant, InsnIterator insn, EmitState *state) const;
EmitResult EmitImageSampleExplicitLod(Variant variant, InsnIterator insn, EmitState *state) const;
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index 7c55daa..ebee9f2 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -193,7 +193,8 @@
}
// Full optimization list taken from spirv-opt.
- opt.RegisterPass(spvtools::CreateDeadBranchElimPass())
+ opt.RegisterPass(spvtools::CreateWrapOpKillPass())
+ .RegisterPass(spvtools::CreateDeadBranchElimPass())
.RegisterPass(spvtools::CreateMergeReturnPass())
.RegisterPass(spvtools::CreateInlineExhaustivePass())
.RegisterPass(spvtools::CreateAggressiveDCEPass())