Allow derived access chain without uses in access chain conversion
diff --git a/source/opt/local_access_chain_convert_pass.cpp b/source/opt/local_access_chain_convert_pass.cpp
index ff5f912..14bec99 100644
--- a/source/opt/local_access_chain_convert_pass.cpp
+++ b/source/opt/local_access_chain_convert_pass.cpp
@@ -139,7 +139,17 @@
bool LocalAccessChainConvertPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
- assert(uses != nullptr);
+
+ if (!uses) {
+ // This is a variable (or access chain to a variable) that has no uses.
+ // We won't encounter loads or stores for this <result-id> per se, but
+ // this <result-id> may be derived from some other variable (or access
+ // chain). Return true here can unblock the access chain conversion of
+ // the root variable. This particular <result-id> won't be touched and
+ // can be handled in dead code elimination.
+ return true;
+ }
+
for (auto u : *uses) {
SpvOp op = u.inst->opcode();
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
diff --git a/source/opt/local_single_block_elim_pass.cpp b/source/opt/local_single_block_elim_pass.cpp
index 8fd4023..4f208af 100644
--- a/source/opt/local_single_block_elim_pass.cpp
+++ b/source/opt/local_single_block_elim_pass.cpp
@@ -30,7 +30,17 @@
bool LocalSingleBlockLoadStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
- assert(uses != nullptr);
+
+ if (!uses) {
+ // This is a variable (or access chain to a variable) that has no uses.
+ // We won't encounter loads or stores for this <result-id> per se, but
+ // this <result-id> may be derived from some other variable (or access
+ // chain). Return true here can unblock the access chain conversion of
+ // the root variable. This particular <result-id> won't be touched and
+ // can be handled in dead code elimination.
+ return true;
+ }
+
for (auto u : *uses) {
SpvOp op = u.inst->opcode();
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
diff --git a/source/opt/local_single_store_elim_pass.cpp b/source/opt/local_single_store_elim_pass.cpp
index e3a00ba..5eda1d2 100644
--- a/source/opt/local_single_store_elim_pass.cpp
+++ b/source/opt/local_single_store_elim_pass.cpp
@@ -32,7 +32,17 @@
bool LocalSingleStoreElimPass::HasOnlySupportedRefs(uint32_t ptrId) {
if (supported_ref_ptrs_.find(ptrId) != supported_ref_ptrs_.end()) return true;
analysis::UseList* uses = get_def_use_mgr()->GetUses(ptrId);
- assert(uses != nullptr);
+
+ if (!uses) {
+ // This is a variable (or access chain to a variable) that has no uses.
+ // We won't encounter loads or stores for this <result-id> per se, but
+ // this <result-id> may be derived from some other variable (or access
+ // chain). Return true here can unblock the access chain conversion of
+ // the root variable. This particular <result-id> won't be touched and
+ // can be handled in dead code elimination.
+ return true;
+ }
+
for (auto u : *uses) {
SpvOp op = u.inst->opcode();
if (IsNonPtrAccessChain(op) || op == SpvOpCopyObject) {
diff --git a/test/opt/local_access_chain_convert_test.cpp b/test/opt/local_access_chain_convert_test.cpp
index a450e6b..23221e1 100644
--- a/test/opt/local_access_chain_convert_test.cpp
+++ b/test/opt/local_access_chain_convert_test.cpp
@@ -656,6 +656,67 @@
assembly, assembly, false, true);
}
+TEST_F(LocalAccessChainConvertTest, SomeAccessChainsHaveNoUse) {
+ // Based on HLSL source code:
+ // struct S {
+ // float f;
+ // };
+
+ // float main(float input : A) : B {
+ // S local = { input };
+ // return local.f;
+ // }
+
+ const std::string predefs = R"(OpCapability Shader
+OpMemoryModel Logical GLSL450
+OpEntryPoint Vertex %main "main" %in_var_A %out_var_B
+OpName %main "main"
+OpName %in_var_A "in.var.A"
+OpName %out_var_B "out.var.B"
+OpName %S "S"
+OpName %local "local"
+%int = OpTypeInt 32 1
+%void = OpTypeVoid
+%8 = OpTypeFunction %void
+%float = OpTypeFloat 32
+%_ptr_Function_float = OpTypePointer Function %float
+%_ptr_Input_float = OpTypePointer Input %float
+%_ptr_Output_float = OpTypePointer Output %float
+%S = OpTypeStruct %float
+%_ptr_Function_S = OpTypePointer Function %S
+%int_0 = OpConstant %int 0
+%in_var_A = OpVariable %_ptr_Input_float Input
+%out_var_B = OpVariable %_ptr_Output_float Output
+%main = OpFunction %void None %8
+%15 = OpLabel
+%local = OpVariable %_ptr_Function_S Function
+%16 = OpLoad %float %in_var_A
+%17 = OpCompositeConstruct %S %16
+OpStore %local %17
+)";
+
+ const std::string before =
+ R"(%18 = OpAccessChain %_ptr_Function_float %local %int_0
+%19 = OpAccessChain %_ptr_Function_float %local %int_0
+%20 = OpLoad %float %18
+OpStore %out_var_B %20
+OpReturn
+OpFunctionEnd
+)";
+
+ const std::string after =
+ R"(%19 = OpAccessChain %_ptr_Function_float %local %int_0
+%21 = OpLoad %S %local
+%22 = OpCompositeExtract %float %21 0
+OpStore %out_var_B %22
+OpReturn
+OpFunctionEnd
+)";
+
+ SinglePassRunAndCheck<opt::LocalAccessChainConvertPass>(
+ predefs + before, predefs + after, true, true);
+}
+
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
// Assorted vector and matrix types