opt: Add OpEntryPoint to DescriptorScalarReplacement pass (#5553)
Add OpEntryPoint to the list of instructions processed by the
DescriptorScalarReplacement pass. This is necessary for SPIR-V 1.4 and
above where global variables must be included in the interface.
Fixes microsoft/DirectXShaderCompiler#5962
diff --git a/source/opt/desc_sroa.cpp b/source/opt/desc_sroa.cpp
index 8da0c86..2c0f482 100644
--- a/source/opt/desc_sroa.cpp
+++ b/source/opt/desc_sroa.cpp
@@ -54,9 +54,10 @@
bool DescriptorScalarReplacement::ReplaceCandidate(Instruction* var) {
std::vector<Instruction*> access_chain_work_list;
std::vector<Instruction*> load_work_list;
+ std::vector<Instruction*> entry_point_work_list;
bool failed = !get_def_use_mgr()->WhileEachUser(
- var->result_id(),
- [this, &access_chain_work_list, &load_work_list](Instruction* use) {
+ var->result_id(), [this, &access_chain_work_list, &load_work_list,
+ &entry_point_work_list](Instruction* use) {
if (use->opcode() == spv::Op::OpName) {
return true;
}
@@ -73,6 +74,9 @@
case spv::Op::OpLoad:
load_work_list.push_back(use);
return true;
+ case spv::Op::OpEntryPoint:
+ entry_point_work_list.push_back(use);
+ return true;
default:
context()->EmitErrorMessage(
"Variable cannot be replaced: invalid instruction", use);
@@ -95,6 +99,11 @@
return false;
}
}
+ for (Instruction* use : entry_point_work_list) {
+ if (!ReplaceEntryPoint(var, use)) {
+ return false;
+ }
+ }
return true;
}
@@ -147,6 +156,42 @@
return true;
}
+bool DescriptorScalarReplacement::ReplaceEntryPoint(Instruction* var,
+ Instruction* use) {
+ // Build a new |OperandList| for |use| that removes |var| and adds its
+ // replacement variables.
+ Instruction::OperandList new_operands;
+
+ // Copy all operands except |var|.
+ bool found = false;
+ for (uint32_t idx = 0; idx < use->NumOperands(); idx++) {
+ Operand& op = use->GetOperand(idx);
+ if (op.type == SPV_OPERAND_TYPE_ID && op.words[0] == var->result_id()) {
+ found = true;
+ } else {
+ new_operands.emplace_back(op);
+ }
+ }
+
+ if (!found) {
+ context()->EmitErrorMessage(
+ "Variable cannot be replaced: invalid instruction", use);
+ return false;
+ }
+
+ // Add all new replacement variables.
+ uint32_t num_replacement_vars =
+ descsroautil::GetNumberOfElementsForArrayOrStruct(context(), var);
+ for (uint32_t i = 0; i < num_replacement_vars; i++) {
+ new_operands.push_back(
+ {SPV_OPERAND_TYPE_ID, {GetReplacementVariable(var, i)}});
+ }
+
+ use->ReplaceOperands(new_operands);
+ context()->UpdateDefUse(use);
+ return true;
+}
+
uint32_t DescriptorScalarReplacement::GetReplacementVariable(Instruction* var,
uint32_t idx) {
auto replacement_vars = replacement_variables_.find(var);
diff --git a/source/opt/desc_sroa.h b/source/opt/desc_sroa.h
index 6a24fd8..901be3e 100644
--- a/source/opt/desc_sroa.h
+++ b/source/opt/desc_sroa.h
@@ -64,6 +64,11 @@
// otherwise.
bool ReplaceLoadedValue(Instruction* var, Instruction* value);
+ // Replaces the given composite variable |var| in the OpEntryPoint with the
+ // new replacement variables, one for each element of the array |var|. Returns
+ // |true| if successful, and |false| otherwise.
+ bool ReplaceEntryPoint(Instruction* var, Instruction* use);
+
// Replaces the given OpCompositeExtract |extract| and all of its references
// with an OpLoad of a replacement variable. |var| is the variable with
// composite type whose value is being used by |extract|. Assumes that
diff --git a/test/opt/desc_sroa_test.cpp b/test/opt/desc_sroa_test.cpp
index 7a118f9..5c166d8 100644
--- a/test/opt/desc_sroa_test.cpp
+++ b/test/opt/desc_sroa_test.cpp
@@ -918,6 +918,74 @@
SinglePassRunAndMatch<DescriptorScalarReplacement>(shader, true);
}
+TEST_F(DescriptorScalarReplacementTest, ExpandArrayInOpEntryPoint) {
+ const std::string text = R"(; SPIR-V
+; Version: 1.6
+; Bound: 31
+; Schema: 0
+ OpCapability Shader
+ OpMemoryModel Logical GLSL450
+
+; CHECK: OpEntryPoint GLCompute %main "main" %output_0_ %output_1_
+
+ OpEntryPoint GLCompute %main "main" %output
+ OpExecutionMode %main LocalSize 1 1 1
+ OpSource HLSL 670
+ OpName %type_RWByteAddressBuffer "type.RWByteAddressBuffer"
+ OpName %output "output"
+ OpName %main "main"
+ OpName %src_main "src.main"
+ OpName %bb_entry "bb.entry"
+
+; CHECK: OpDecorate %output_1_ DescriptorSet 0
+; CHECK: OpDecorate %output_1_ Binding 1
+; CHECK: OpDecorate %output_0_ DescriptorSet 0
+; CHECK: OpDecorate %output_0_ Binding 0
+
+ OpDecorate %output DescriptorSet 0
+ OpDecorate %output Binding 0
+
+ OpDecorate %_runtimearr_uint ArrayStride 4
+ OpMemberDecorate %type_RWByteAddressBuffer 0 Offset 0
+ OpDecorate %type_RWByteAddressBuffer Block
+ %int = OpTypeInt 32 1
+ %int_1 = OpConstant %int 1
+ %uint = OpTypeInt 32 0
+ %uint_0 = OpConstant %uint 0
+ %uint_2 = OpConstant %uint 2
+ %uint_32 = OpConstant %uint 32
+%_runtimearr_uint = OpTypeRuntimeArray %uint
+%type_RWByteAddressBuffer = OpTypeStruct %_runtimearr_uint
+%_arr_type_RWByteAddressBuffer_uint_2 = OpTypeArray %type_RWByteAddressBuffer %uint_2
+%_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 = OpTypePointer StorageBuffer %_arr_type_RWByteAddressBuffer_uint_2
+ %void = OpTypeVoid
+ %23 = OpTypeFunction %void
+%_ptr_StorageBuffer_type_RWByteAddressBuffer = OpTypePointer StorageBuffer %type_RWByteAddressBuffer
+%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
+
+; CHECK: %output_1_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer
+; CHECK: %output_0_ = OpVariable %_ptr_StorageBuffer_type_RWByteAddressBuffer StorageBuffer
+
+ %output = OpVariable %_ptr_StorageBuffer__arr_type_RWByteAddressBuffer_uint_2 StorageBuffer
+
+ %main = OpFunction %void None %23
+ %26 = OpLabel
+ %27 = OpFunctionCall %void %src_main
+ OpReturn
+ OpFunctionEnd
+ %src_main = OpFunction %void None %23
+ %bb_entry = OpLabel
+ %28 = OpAccessChain %_ptr_StorageBuffer_type_RWByteAddressBuffer %output %int_1
+ %29 = OpShiftRightLogical %uint %uint_0 %uint_2
+ %30 = OpAccessChain %_ptr_StorageBuffer_uint %28 %uint_0 %29
+ OpStore %30 %uint_32
+ OpReturn
+ OpFunctionEnd
+ )";
+
+ SinglePassRunAndMatch<DescriptorScalarReplacement>(text, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools