Debug info preservation in copy-prop-array pass (#3444)
When the pass replaces the local variable `OpVariable` ids to their
corresponding pointers, we have to update operands of DebugValue or
DebugDeclare instructions.
diff --git a/source/opt/copy_prop_arrays.cpp b/source/opt/copy_prop_arrays.cpp
index b3b90da..ba7cea7 100644
--- a/source/opt/copy_prop_arrays.cpp
+++ b/source/opt/copy_prop_arrays.cpp
@@ -29,6 +29,12 @@
const uint32_t kTypePointerStorageClassInIdx = 0;
const uint32_t kTypePointerPointeeInIdx = 1;
+bool IsOpenCL100DebugDeclareOrValue(Instruction* di) {
+ auto dbg_opcode = di->GetOpenCL100DebugOpcode();
+ return dbg_opcode == OpenCLDebugInfo100DebugDeclare ||
+ dbg_opcode == OpenCLDebugInfo100DebugValue;
+}
+
} // namespace
Pass::Status CopyPropagateArrays::Process() {
@@ -188,6 +194,8 @@
return ptr_inst->opcode() == SpvOpVariable &&
store_inst->GetSingleWordInOperand(kStorePointerInOperand) ==
ptr_inst->result_id();
+ } else if (IsOpenCL100DebugDeclareOrValue(use)) {
+ return true;
}
// Some other instruction. Be conservative.
return false;
@@ -492,6 +500,8 @@
const_mgr,
type](Instruction* use,
uint32_t) {
+ if (IsOpenCL100DebugDeclareOrValue(use)) return true;
+
switch (use->opcode()) {
case SpvOpLoad: {
analysis::Pointer* pointer_type = type->AsPointer();
@@ -565,6 +575,7 @@
}
});
}
+
void CopyPropagateArrays::UpdateUses(Instruction* original_ptr_inst,
Instruction* new_ptr_inst) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
@@ -580,6 +591,52 @@
for (auto pair : uses) {
Instruction* use = pair.first;
uint32_t index = pair.second;
+
+ if (use->GetOpenCL100DebugOpcode() != OpenCLDebugInfo100InstructionsMax) {
+ switch (use->GetOpenCL100DebugOpcode()) {
+ case OpenCLDebugInfo100DebugDeclare: {
+ if (new_ptr_inst->opcode() == SpvOpVariable ||
+ new_ptr_inst->opcode() == SpvOpFunctionParameter) {
+ context()->ForgetUses(use);
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+ context()->AnalyzeUses(use);
+ } else {
+ // Based on the spec, we cannot use a pointer other than OpVariable
+ // or OpFunctionParameter for DebugDeclare. We have to use
+ // DebugValue with Deref.
+
+ context()->ForgetUses(use);
+
+ // Change DebugDeclare to DebugValue.
+ use->SetOperand(
+ index - 2,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugValue)});
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+
+ // Add Deref operation.
+ Instruction* dbg_expr =
+ def_use_mgr->GetDef(use->GetSingleWordOperand(index + 1));
+ auto* deref_expr_instr =
+ context()->get_debug_info_mgr()->DerefDebugExpression(dbg_expr);
+ use->SetOperand(index + 1, {deref_expr_instr->result_id()});
+
+ context()->AnalyzeUses(deref_expr_instr);
+ context()->AnalyzeUses(use);
+ }
+ break;
+ }
+ case OpenCLDebugInfo100DebugValue:
+ context()->ForgetUses(use);
+ use->SetOperand(index, {new_ptr_inst->result_id()});
+ context()->AnalyzeUses(use);
+ break;
+ default:
+ assert(false && "Don't know how to rewrite instruction");
+ break;
+ }
+ continue;
+ }
+
switch (use->opcode()) {
case SpvOpLoad: {
// Replace the actual use.
diff --git a/source/opt/debug_info_manager.cpp b/source/opt/debug_info_manager.cpp
index 6dc3e16..60ad71b 100644
--- a/source/opt/debug_info_manager.cpp
+++ b/source/opt/debug_info_manager.cpp
@@ -36,6 +36,7 @@
static const uint32_t kDebugOperationOperandOperationIndex = 4;
static const uint32_t kOpVariableOperandStorageClassIndex = 2;
static const uint32_t kDebugLocalVariableOperandParentIndex = 9;
+static const uint32_t kDebugOperationOperandOpCodeIndex = 4;
namespace spvtools {
namespace opt {
@@ -244,6 +245,51 @@
return chain_head_id;
}
+Instruction* DebugInfoManager::GetDebugOperationWithDeref() {
+ if (deref_operation_ != nullptr) return deref_operation_;
+
+ uint32_t result_id = context()->TakeNextId();
+ std::unique_ptr<Instruction> deref_operation(new Instruction(
+ context(), SpvOpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
+ result_id,
+ {
+ {SPV_OPERAND_TYPE_ID,
+ {context()
+ ->get_feature_mgr()
+ ->GetExtInstImportId_OpenCL100DebugInfo()}},
+ {SPV_OPERAND_TYPE_EXTENSION_INSTRUCTION_NUMBER,
+ {static_cast<uint32_t>(OpenCLDebugInfo100DebugOperation)}},
+ {SPV_OPERAND_TYPE_CLDEBUG100_DEBUG_OPERATION,
+ {static_cast<uint32_t>(OpenCLDebugInfo100Deref)}},
+ }));
+
+ // Add to the front of |ext_inst_debuginfo_|.
+ deref_operation_ =
+ context()->module()->ext_inst_debuginfo_begin()->InsertBefore(
+ std::move(deref_operation));
+
+ RegisterDbgInst(deref_operation_);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_operation_);
+ return deref_operation_;
+}
+
+Instruction* DebugInfoManager::DerefDebugExpression(Instruction* dbg_expr) {
+ assert(dbg_expr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugExpression);
+ std::unique_ptr<Instruction> deref_expr(dbg_expr->Clone(context()));
+ deref_expr->SetResultId(context()->TakeNextId());
+ deref_expr->InsertOperand(
+ kDebugExpressOperandOperationIndex,
+ {SPV_OPERAND_TYPE_ID, {GetDebugOperationWithDeref()->result_id()}});
+ auto* deref_expr_instr =
+ context()->ext_inst_debuginfo_end()->InsertBefore(std::move(deref_expr));
+ AnalyzeDebugInst(deref_expr_instr);
+ if (context()->AreAnalysesValid(IRContext::Analysis::kAnalysisDefUse))
+ context()->get_def_use_mgr()->AnalyzeInstDefUse(deref_expr_instr);
+ return deref_expr_instr;
+}
+
Instruction* DebugInfoManager::GetDebugInfoNone() {
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
@@ -484,6 +530,13 @@
RegisterDbgFunction(dbg_inst);
}
+ if (deref_operation_ == nullptr &&
+ dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugOperation &&
+ dbg_inst->GetSingleWordOperand(kDebugOperationOperandOpCodeIndex) ==
+ OpenCLDebugInfo100Deref) {
+ deref_operation_ = dbg_inst;
+ }
+
if (debug_info_none_inst_ == nullptr &&
dbg_inst->GetOpenCL100DebugOpcode() == OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = dbg_inst;
@@ -505,6 +558,7 @@
}
void DebugInfoManager::AnalyzeDebugInsts(Module& module) {
+ deref_operation_ = nullptr;
debug_info_none_inst_ = nullptr;
empty_debug_expr_inst_ = nullptr;
module.ForEachInst([this](Instruction* cpi) { AnalyzeDebugInst(cpi); });
@@ -554,6 +608,22 @@
}
}
+ if (deref_operation_ == instr) {
+ deref_operation_ = nullptr;
+ for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
+ dbg_instr_itr != context()->module()->ext_inst_debuginfo_end();
+ ++dbg_instr_itr) {
+ if (instr != &*dbg_instr_itr &&
+ dbg_instr_itr->GetOpenCL100DebugOpcode() ==
+ OpenCLDebugInfo100DebugOperation &&
+ dbg_instr_itr->GetSingleWordOperand(
+ kDebugOperationOperandOpCodeIndex) == OpenCLDebugInfo100Deref) {
+ deref_operation_ = &*dbg_instr_itr;
+ break;
+ }
+ }
+ }
+
if (debug_info_none_inst_ == instr) {
debug_info_none_inst_ = nullptr;
for (auto dbg_instr_itr = context()->module()->ext_inst_debuginfo_begin();
@@ -563,6 +633,7 @@
dbg_instr_itr->GetOpenCL100DebugOpcode() ==
OpenCLDebugInfo100DebugInfoNone) {
debug_info_none_inst_ = &*dbg_instr_itr;
+ break;
}
}
}
@@ -574,6 +645,7 @@
++dbg_instr_itr) {
if (instr != &*dbg_instr_itr && IsEmptyDebugExpression(&*dbg_instr_itr)) {
empty_debug_expr_inst_ = &*dbg_instr_itr;
+ break;
}
}
}
diff --git a/source/opt/debug_info_manager.h b/source/opt/debug_info_manager.h
index 7353d56..e8de5a4 100644
--- a/source/opt/debug_info_manager.h
+++ b/source/opt/debug_info_manager.h
@@ -95,6 +95,10 @@
uint32_t CreateDebugInlinedAt(const Instruction* line,
const DebugScope& scope);
+ // Clones DebugExpress instruction |dbg_expr| and add Deref Operation
+ // in the front of the Operation list of |dbg_expr|.
+ Instruction* DerefDebugExpression(Instruction* dbg_expr);
+
// Returns a DebugInfoNone instruction.
Instruction* GetDebugInfoNone();
@@ -149,6 +153,9 @@
// does not exists.
Instruction* GetDbgInst(uint32_t id);
+ // Returns a DebugOperation instruction with OpCode Deref.
+ Instruction* GetDebugOperationWithDeref();
+
// Registers the debug instruction |inst| into |id_to_dbg_inst_| using id of
// |inst| as a key.
void RegisterDbgInst(Instruction* inst);
@@ -197,6 +204,9 @@
std::unordered_map<uint32_t, std::unordered_set<Instruction*>>
var_id_to_dbg_decl_;
+ // DebugOperation whose OpCode is OpenCLDebugInfo100Deref.
+ Instruction* deref_operation_;
+
// DebugInfoNone instruction. We need only a single DebugInfoNone.
// To reuse the existing one, we keep it using this member variable.
Instruction* debug_info_none_inst_;
diff --git a/source/opt/instruction.h b/source/opt/instruction.h
index 7d8fed8..870a225 100644
--- a/source/opt/instruction.h
+++ b/source/opt/instruction.h
@@ -306,6 +306,10 @@
void RemoveOperand(uint32_t index) {
operands_.erase(operands_.begin() + index);
}
+ // Insert an operand before the |index|-th operand
+ void InsertOperand(uint32_t index, Operand&& operand) {
+ operands_.insert(operands_.begin() + index, operand);
+ }
// The following methods are similar to the above, but are for in operands.
uint32_t NumInOperands() const {
diff --git a/source/opt/pass.h b/source/opt/pass.h
index a119207..a8c9c4b 100644
--- a/source/opt/pass.h
+++ b/source/opt/pass.h
@@ -71,10 +71,6 @@
return context()->get_def_use_mgr();
}
- analysis::DebugInfoManager* get_debug_info_mgr() const {
- return context()->get_debug_info_mgr();
- }
-
analysis::DecorationManager* get_decoration_mgr() const {
return context()->get_decoration_mgr();
}
diff --git a/source/opt/ssa_rewrite_pass.cpp b/source/opt/ssa_rewrite_pass.cpp
index 6eed1fd..1477db4 100644
--- a/source/opt/ssa_rewrite_pass.cpp
+++ b/source/opt/ssa_rewrite_pass.cpp
@@ -307,7 +307,8 @@
}
if (pass_->IsTargetVar(var_id)) {
WriteVariable(var_id, bb, val_id);
- pass_->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id, inst);
+ pass_->context()->get_debug_info_mgr()->AddDebugValue(inst, var_id, val_id,
+ inst);
#if SSA_REWRITE_DEBUGGING_LEVEL > 1
std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
@@ -490,7 +491,7 @@
// Add DebugValue for the new OpPhi instruction.
insert_it->SetDebugScope(local_var->GetDebugScope());
- pass_->get_debug_info_mgr()->AddDebugValue(
+ pass_->context()->get_debug_info_mgr()->AddDebugValue(
&*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
&*insert_it);
diff --git a/test/opt/copy_prop_array_test.cpp b/test/opt/copy_prop_array_test.cpp
index 1afee9c..72bc7f6 100644
--- a/test/opt/copy_prop_array_test.cpp
+++ b/test/opt/copy_prop_array_test.cpp
@@ -1622,6 +1622,198 @@
SinglePassRunAndMatch<CopyPropagateArrays>(text, true);
}
+TEST_F(CopyPropArrayPassTest, DebugDeclare) {
+ const std::string before =
+ R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+%null_expr = OpExtInst %void %ext DebugExpression
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+%decl = OpExtInst %void %ext DebugDeclare %dbg_f %23 %null_expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
+TEST_F(CopyPropArrayPassTest, DebugValue) {
+ const std::string before =
+ R"(OpCapability Shader
+%ext = OpExtInstImport "OpenCL.DebugInfo.100"
+OpMemoryModel Logical GLSL450
+OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
+OpExecutionMode %main OriginUpperLeft
+OpSource HLSL 600
+%file_name = OpString "test"
+%float_name = OpString "float"
+%main_name = OpString "main"
+%f_name = OpString "f"
+OpName %type_MyCBuffer "type.MyCBuffer"
+OpMemberName %type_MyCBuffer 0 "Data"
+OpName %MyCBuffer "MyCBuffer"
+OpName %main "main"
+OpName %in_var_INDEX "in.var.INDEX"
+OpName %out_var_SV_Target "out.var.SV_Target"
+OpDecorate %_arr_v4float_uint_8 ArrayStride 16
+OpMemberDecorate %type_MyCBuffer 0 Offset 0
+OpDecorate %type_MyCBuffer Block
+OpDecorate %in_var_INDEX Flat
+OpDecorate %in_var_INDEX Location 0
+OpDecorate %out_var_SV_Target Location 0
+OpDecorate %MyCBuffer DescriptorSet 0
+OpDecorate %MyCBuffer Binding 0
+%float = OpTypeFloat 32
+%v4float = OpTypeVector %float 4
+%uint = OpTypeInt 32 0
+%uint_8 = OpConstant %uint 8
+%uint_32 = OpConstant %uint 32
+%_arr_v4float_uint_8 = OpTypeArray %v4float %uint_8
+%type_MyCBuffer = OpTypeStruct %_arr_v4float_uint_8
+%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
+%void = OpTypeVoid
+%13 = OpTypeFunction %void
+%int = OpTypeInt 32 1
+%_ptr_Input_int = OpTypePointer Input %int
+%_ptr_Output_v4float = OpTypePointer Output %v4float
+%_arr_v4float_uint_8_0 = OpTypeArray %v4float %uint_8
+%_ptr_Function__arr_v4float_uint_8_0 = OpTypePointer Function %_arr_v4float_uint_8_0
+%int_0 = OpConstant %int 0
+%_ptr_Uniform__arr_v4float_uint_8 = OpTypePointer Uniform %_arr_v4float_uint_8
+%_ptr_Function_v4float = OpTypePointer Function %v4float
+%MyCBuffer = OpVariable %_ptr_Uniform_type_MyCBuffer Uniform
+%in_var_INDEX = OpVariable %_ptr_Input_int Input
+%out_var_SV_Target = OpVariable %_ptr_Output_v4float Output
+
+; CHECK: [[deref:%\w+]] = OpExtInst %void [[ext:%\w+]] DebugOperation Deref
+; CHECK: [[deref_expr:%\w+]] = OpExtInst %void [[ext]] DebugExpression [[deref]]
+%deref = OpExtInst %void %ext DebugOperation Deref
+%expr = OpExtInst %void %ext DebugExpression %deref
+%src = OpExtInst %void %ext DebugSource %file_name
+%cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
+%dbg_tf = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
+%main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_tf
+%dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 0 0 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
+
+; CHECK: [[dbg_f:%\w+]] = OpExtInst %void [[ext]] DebugLocalVariable
+%dbg_f = OpExtInst %void %ext DebugLocalVariable %f_name %dbg_tf %src 0 0 %dbg_main FlagIsLocal
+%main = OpFunction %void None %13
+%22 = OpLabel
+%23 = OpVariable %_ptr_Function__arr_v4float_uint_8_0 Function
+%24 = OpLoad %int %in_var_INDEX
+%25 = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+%26 = OpLoad %_arr_v4float_uint_8 %25
+%27 = OpCompositeExtract %v4float %26 0
+%28 = OpCompositeExtract %v4float %26 1
+%29 = OpCompositeExtract %v4float %26 2
+%30 = OpCompositeExtract %v4float %26 3
+%31 = OpCompositeExtract %v4float %26 4
+%32 = OpCompositeExtract %v4float %26 5
+%33 = OpCompositeExtract %v4float %26 6
+%34 = OpCompositeExtract %v4float %26 7
+%35 = OpCompositeConstruct %_arr_v4float_uint_8_0 %27 %28 %29 %30 %31 %32 %33 %34
+OpStore %23 %35
+
+; CHECK: OpAccessChain
+; CHECK: [[newptr:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_8 %MyCBuffer %int_0
+; CHECK: OpExtInst %void [[ext]] DebugValue [[dbg_f]] [[newptr]] [[deref_expr]]
+; CHECK: [[element_ptr:%\w+]] = OpAccessChain %_ptr_Uniform_v4float [[newptr]] %24
+; CHECK: [[load:%\w+]] = OpLoad %v4float [[element_ptr]]
+; CHECK: OpStore %out_var_SV_Target [[load]]
+%decl = OpExtInst %void %ext DebugValue %dbg_f %23 %expr
+%36 = OpAccessChain %_ptr_Function_v4float %23 %24
+%37 = OpLoad %v4float %36
+OpStore %out_var_SV_Target %37
+OpReturn
+OpFunctionEnd
+)";
+
+ SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
+ SetDisassembleOptions(SPV_BINARY_TO_TEXT_OPTION_NO_HEADER |
+ SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
+ SinglePassRunAndMatch<CopyPropagateArrays>(before, false);
+}
+
} // namespace
} // namespace opt
} // namespace spvtools