Handle more cases in copy propagate arrays.

When we change the type of an object that gets stored, we do not want to
change the type of the memory location being stored to.  In order to
still be able to do the rewrite, we will decompose and rebuild the
object so it is the type that can be stored.

Fixes #1416.
This commit is contained in:
Steven Perron 2018-03-26 14:43:24 -04:00 committed by Steven Perron
parent c4dc046399
commit 5e07ab1358
3 changed files with 362 additions and 164 deletions

View File

@ -343,86 +343,88 @@ bool CopyPropagateArrays::CanUpdateUses(ir::Instruction* original_ptr_inst,
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
return def_use_mgr->WhileEachUse(original_ptr_inst, [this, type_id, type_mgr,
const_mgr](
ir::Instruction* use,
uint32_t index) {
analysis::Pointer* pointer_type = nullptr;
switch (use->opcode()) {
case SpvOpLoad: {
pointer_type = type_mgr->GetType(type_id)->AsPointer();
uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type());
analysis::Type* type = type_mgr->GetType(type_id);
if (type->AsRuntimeArray()) {
return false;
}
if (new_type_id != use->type_id()) {
return CanUpdateUses(use, new_type_id);
}
return true;
}
case SpvOpAccessChain: {
pointer_type = type_mgr->GetType(type_id)->AsPointer();
const analysis::Type* pointee_type = pointer_type->pointee_type();
if (!type->AsStruct() && !type->AsArray() && !type->AsPointer()) {
// If the type is not an aggregate, then the desired type must be the
// same as the current type. No work to do, and we can do that.
return true;
}
std::vector<uint32_t> access_chain;
for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
const analysis::Constant* index_const =
const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
if (index_const) {
access_chain.push_back(index_const->AsIntConstant()->GetU32());
} else {
// Variable index means the type is a type where every element
// is the same type. Use element 0 to get the type.
access_chain.push_back(0);
return def_use_mgr->WhileEachUse(
original_ptr_inst,
[this, type_mgr, const_mgr, type](ir::Instruction* use, uint32_t) {
switch (use->opcode()) {
case SpvOpLoad: {
analysis::Pointer* pointer_type = type->AsPointer();
uint32_t new_type_id =
type_mgr->GetId(pointer_type->pointee_type());
if (new_type_id != use->type_id()) {
return CanUpdateUses(use, new_type_id);
}
return true;
}
}
case SpvOpAccessChain: {
analysis::Pointer* pointer_type = type->AsPointer();
const analysis::Type* pointee_type = pointer_type->pointee_type();
const analysis::Type* new_pointee_type =
type_mgr->GetMemberType(pointee_type, access_chain);
opt::analysis::Pointer pointerTy(new_pointee_type,
pointer_type->storage_class());
uint32_t new_pointer_type_id =
context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
std::vector<uint32_t> access_chain;
for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
const analysis::Constant* index_const =
const_mgr->FindDeclaredConstant(
use->GetSingleWordInOperand(i));
if (index_const) {
access_chain.push_back(index_const->AsIntConstant()->GetU32());
} else {
// Variable index means the type is a type where every element
// is the same type. Use element 0 to get the type.
access_chain.push_back(0);
}
}
if (new_pointer_type_id != use->type_id()) {
return CanUpdateUses(use, new_pointer_type_id);
}
return true;
}
case SpvOpCompositeExtract: {
std::vector<uint32_t> access_chain;
for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
const analysis::Constant* index_const =
const_mgr->FindDeclaredConstant(use->GetSingleWordInOperand(i));
if (index_const) {
access_chain.push_back(index_const->AsIntConstant()->GetU32());
} else {
// Variable index means the type is an type where every element
// is the same type. Use element 0 to get the type.
access_chain.push_back(0);
const analysis::Type* new_pointee_type =
type_mgr->GetMemberType(pointee_type, access_chain);
opt::analysis::Pointer pointerTy(new_pointee_type,
pointer_type->storage_class());
uint32_t new_pointer_type_id =
context()->get_type_mgr()->GetTypeInstruction(&pointerTy);
if (new_pointer_type_id != use->type_id()) {
return CanUpdateUses(use, new_pointer_type_id);
}
return true;
}
case SpvOpCompositeExtract: {
std::vector<uint32_t> access_chain;
for (uint32_t i = 1; i < use->NumInOperands(); ++i) {
access_chain.push_back(use->GetSingleWordInOperand(i));
}
const analysis::Type* new_type =
type_mgr->GetMemberType(type, access_chain);
uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
if (new_type_id != use->type_id()) {
return CanUpdateUses(use, new_type_id);
}
return true;
}
case SpvOpStore:
// Can't handle changing the type of a store. There are too many
// other things that might need to change as well. Not worth the
// effort. Punting for now.
// TODO (s-perron): This can be handled by expanding the store into
// a series of extracts, composite constructs, and a store.
return true;
default:
return false;
}
const analysis::Type* type = type_mgr->GetType(type_id);
const analysis::Type* new_type =
type_mgr->GetMemberType(type, access_chain);
uint32_t new_type_id = type_mgr->GetTypeInstruction(new_type);
if (new_type_id != use->type_id()) {
return CanUpdateUses(use, new_type_id);
}
return true;
}
case SpvOpStore:
// Can't handle changing the type of a store. There are too many other
// things that might need to change as well. Not worth the effort.
// Punting for now.
// TODO (s-perron): This can be handled by expanding the store into a
// series of extracts, composite constructs, and a store.
return index != 1;
default:
return false;
}
});
});
}
void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
ir::Instruction* new_ptr_inst) {
@ -435,13 +437,20 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
analysis::DefUseManager* def_use_mgr = context()->get_def_use_mgr();
def_use_mgr->ForEachUse(original_ptr_inst, [this, new_ptr_inst, type_mgr,
const_mgr](ir::Instruction* use,
uint32_t index) {
std::vector<std::pair<ir::Instruction*, uint32_t> > uses;
def_use_mgr->ForEachUse(original_ptr_inst,
[&uses](ir::Instruction* use, uint32_t index) {
uses.push_back({use, index});
});
for (auto pair : uses) {
ir::Instruction* use = pair.first;
uint32_t index = pair.second;
analysis::Pointer* pointer_type = nullptr;
switch (use->opcode()) {
case SpvOpLoad: {
// Replace the actual use.
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
// Update the type.
@ -449,11 +458,15 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
uint32_t new_type_id = type_mgr->GetId(pointer_type->pointee_type());
if (new_type_id != use->type_id()) {
use->SetResultType(new_type_id);
context()->AnalyzeUses(use);
UpdateUses(use, use);
} else {
context()->AnalyzeUses(use);
}
} break;
case SpvOpAccessChain: {
// Update the actual use.
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
// Update the result type.
@ -485,11 +498,15 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
if (new_pointer_type_id != use->type_id()) {
use->SetResultType(new_pointer_type_id);
context()->AnalyzeUses(use);
UpdateUses(use, use);
} else {
context()->AnalyzeUses(use);
}
} break;
case SpvOpCompositeExtract: {
// Update the actual use.
context()->ForgetUses(use);
use->SetOperand(index, {new_ptr_inst->result_id()});
std::vector<uint32_t> access_chain;
@ -504,7 +521,10 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
if (new_type_id != use->type_id()) {
use->SetResultType(new_type_id);
context()->AnalyzeUses(use);
UpdateUses(use, use);
} else {
context()->AnalyzeUses(use);
}
} break;
case SpvOpStore:
@ -512,17 +532,99 @@ void CopyPropagateArrays::UpdateUses(ir::Instruction* original_ptr_inst,
// variable. We do not want to replace it. Instead, it will become
// dead after all of the loads are removed, and ADCE will get rid of it.
//
// If the use is the object being stored, we do not know how to change
// the type, so we assume that |CanUpdateUse| would have returned false,
// and we should not have called this function.
assert(index != 1 && "Have to change the type of the stored object.");
// If the use is the object being stored, we will create a copy of the
// object turning it into the correct type. The copy is done by
// decomposing the object into the base type, which must be the same,
// and then rebuilding them.
if (index == 1) {
ir::Instruction* target_pointer = def_use_mgr->GetDef(
use->GetSingleWordInOperand(kStorePointerInOperand));
pointer_type =
type_mgr->GetType(target_pointer->type_id())->AsPointer();
uint32_t copy =
GenerateCopy(original_ptr_inst,
type_mgr->GetId(pointer_type->pointee_type()), use);
context()->ForgetUses(use);
use->SetInOperand(index, {copy});
context()->AnalyzeUses(use);
}
break;
default:
assert(false && "Don't know how to rewrite instruction");
break;
}
});
}
}
uint32_t CopyPropagateArrays::GenerateCopy(
ir::Instruction* object_inst, uint32_t new_type_id,
ir::Instruction* insertion_position) {
analysis::TypeManager* type_mgr = context()->get_type_mgr();
analysis::ConstantManager* const_mgr = context()->get_constant_mgr();
uint32_t original_type_id = object_inst->type_id();
if (original_type_id == new_type_id) {
return object_inst->result_id();
}
opt::InstructionBuilder ir_builder(
context(), insertion_position,
ir::IRContext::kAnalysisInstrToBlockMapping |
ir::IRContext::kAnalysisDefUse);
analysis::Type* original_type = type_mgr->GetType(original_type_id);
analysis::Type* new_type = type_mgr->GetType(new_type_id);
if (const analysis::Array* original_array_type = original_type->AsArray()) {
uint32_t original_element_type_id =
type_mgr->GetId(original_array_type->element_type());
analysis::Array* new_array_type = new_type->AsArray();
assert(new_array_type != nullptr && "Can't copy an array to a non-array.");
uint32_t new_element_type_id =
type_mgr->GetId(new_array_type->element_type());
std::vector<uint32_t> element_ids;
const analysis::Constant* length_const =
const_mgr->FindDeclaredConstant(original_array_type->LengthId());
assert(length_const->AsIntConstant());
uint32_t array_length = length_const->AsIntConstant()->GetU32();
for (uint32_t i = 0; i < array_length; i++) {
ir::Instruction* extract = ir_builder.AddCompositeExtract(
original_element_type_id, object_inst->result_id(), {i});
element_ids.push_back(
GenerateCopy(extract, new_element_type_id, insertion_position));
}
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
->result_id();
} else if (const analysis::Struct* original_struct_type =
original_type->AsStruct()) {
analysis::Struct* new_struct_type = new_type->AsStruct();
const std::vector<analysis::Type*>& original_types =
original_struct_type->element_types();
const std::vector<analysis::Type*>& new_types =
new_struct_type->element_types();
std::vector<uint32_t> element_ids;
for (uint32_t i = 0; i < original_types.size(); i++) {
ir::Instruction* extract = ir_builder.AddCompositeExtract(
type_mgr->GetId(original_types[i]), object_inst->result_id(), {i});
element_ids.push_back(GenerateCopy(extract, type_mgr->GetId(new_types[i]),
insertion_position));
}
return ir_builder.AddCompositeConstruct(new_type_id, element_ids)
->result_id();
} else {
// If we do not have an aggregate type, then we have a problem. Either we
// found multiple instances of the same type, or we are copying to an
// incompatible type. Either way the code is illegal.
assert(false &&
"Don't know how to copy this type. Code is likely illegal.");
}
return 0;
}
void CopyPropagateArrays::MemoryObject::GetMember(

View File

@ -185,6 +185,12 @@ class CopyPropagateArrays : public MemPass {
// Return true if |UpdateUses| is able to change all of the uses of
// |original_ptr_inst| to |type_id| and still have valid code.
bool CanUpdateUses(ir::Instruction* original_ptr_inst, uint32_t type_id);
// Returns the id whose value is the same as |object_to_copy| except its type
// is |new_type_id|. Any instructions need to generate this value will be
// inserted before |insertion_position|.
uint32_t GenerateCopy(ir::Instruction* object_to_copy, uint32_t new_type_id,
ir::Instruction* insertion_position);
};
} // namespace opt

View File

@ -191,6 +191,179 @@ OpFunctionEnd
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);
SinglePassRunAndMatch<opt::CopyPropagateArrays>(text, false);
}
// Test decomposing an object when we need to "rewrite" a store.
TEST_F(CopyPropArrayPassTest, DecomposeObjectForArrayStore) {
const std::string text =
R"( OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
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_2 ArrayStride 16
OpDecorate %_arr__arr_v4float_uint_2_uint_2 ArrayStride 32
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_2 = OpConstant %uint 2
%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
%_arr__arr_v4float_uint_2_uint_2 = OpTypeArray %_arr_v4float_uint_2 %uint_2
%type_MyCBuffer = OpTypeStruct %_arr__arr_v4float_uint_2_uint_2
%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
%void = OpTypeVoid
%14 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2
%_arr__arr_v4float_uint_2_0_uint_2 = OpTypeArray %_arr_v4float_uint_2_0 %uint_2
%_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 = OpTypePointer Function %_arr__arr_v4float_uint_2_0_uint_2
%int_0 = OpConstant %int 0
%_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_v4float_uint_2_uint_2
%_ptr_Function__arr_v4float_uint_2_0 = OpTypePointer Function %_arr_v4float_uint_2_0
%_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
%main = OpFunction %void None %14
%25 = OpLabel
%26 = OpVariable %_ptr_Function__arr_v4float_uint_2_0 Function
%27 = OpVariable %_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 Function
%28 = OpLoad %int %in_var_INDEX
%29 = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0
%30 = OpLoad %_arr__arr_v4float_uint_2_uint_2 %29
%31 = OpCompositeExtract %_arr_v4float_uint_2 %30 0
%32 = OpCompositeExtract %v4float %31 0
%33 = OpCompositeExtract %v4float %31 1
%34 = OpCompositeConstruct %_arr_v4float_uint_2_0 %32 %33
%35 = OpCompositeExtract %_arr_v4float_uint_2 %30 1
%36 = OpCompositeExtract %v4float %35 0
%37 = OpCompositeExtract %v4float %35 1
%38 = OpCompositeConstruct %_arr_v4float_uint_2_0 %36 %37
%39 = OpCompositeConstruct %_arr__arr_v4float_uint_2_0_uint_2 %34 %38
OpStore %27 %39
; CHECK: [[access_chain:%\w+]] = OpAccessChain %_ptr_Uniform__arr_v4float_uint_2
%40 = OpAccessChain %_ptr_Function__arr_v4float_uint_2_0 %27 %28
; CHECK: [[load:%\w+]] = OpLoad %_arr_v4float_uint_2 [[access_chain]]
%41 = OpLoad %_arr_v4float_uint_2_0 %40
; CHECK: [[extract1:%\w+]] = OpCompositeExtract %v4float [[load]] 0
; CHECK: [[extract2:%\w+]] = OpCompositeExtract %v4float [[load]] 1
; CHECK: [[construct:%\w+]] = OpCompositeConstruct %_arr_v4float_uint_2_0 [[extract1]] [[extract2]]
; CHEKC: OpStore %26 [[construct]]
OpStore %26 %41
%42 = OpAccessChain %_ptr_Function_v4float %26 %28
%43 = OpLoad %v4float %42
OpStore %out_var_SV_Target %43
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<opt::CopyPropagateArrays>(text, false);
}
// Test decomposing an object when we need to "rewrite" a store.
TEST_F(CopyPropArrayPassTest, DecomposeObjectForStructStore) {
const std::string text =
R"( OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
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"
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
; CHECK: OpDecorate [[decorated_type:%\w+]] GLSLPacked
OpDecorate %struct GLSLPacked
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%uint = OpTypeInt 32 0
%uint_2 = OpConstant %uint 2
; CHECK: [[decorated_type]] = OpTypeStruct
%struct = OpTypeStruct %float %uint
%_arr_struct_uint_2 = OpTypeArray %struct %uint_2
%type_MyCBuffer = OpTypeStruct %_arr_struct_uint_2
%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
%void = OpTypeVoid
%14 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%_ptr_Output_v4float = OpTypePointer Output %v4float
; CHECK: [[struct:%\w+]] = OpTypeStruct %float %uint
%struct_0 = OpTypeStruct %float %uint
%_arr_struct_0_uint_2 = OpTypeArray %struct_0 %uint_2
%_ptr_Function__arr_struct_0_uint_2 = OpTypePointer Function %_arr_struct_0_uint_2
%int_0 = OpConstant %int 0
%_ptr_Uniform__arr_struct_uint_2 = OpTypePointer Uniform %_arr_struct_uint_2
; CHECK: [[decorated_ptr:%\w+]] = OpTypePointer Uniform [[decorated_type]]
%_ptr_Function_struct_0 = OpTypePointer Function %struct_0
%_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
%main = OpFunction %void None %14
%25 = OpLabel
%26 = OpVariable %_ptr_Function_struct_0 Function
%27 = OpVariable %_ptr_Function__arr_struct_0_uint_2 Function
%28 = OpLoad %int %in_var_INDEX
%29 = OpAccessChain %_ptr_Uniform__arr_struct_uint_2 %MyCBuffer %int_0
%30 = OpLoad %_arr_struct_uint_2 %29
%31 = OpCompositeExtract %struct %30 0
%32 = OpCompositeExtract %v4float %31 0
%33 = OpCompositeExtract %v4float %31 1
%34 = OpCompositeConstruct %struct_0 %32 %33
%35 = OpCompositeExtract %struct %30 1
%36 = OpCompositeExtract %float %35 0
%37 = OpCompositeExtract %uint %35 1
%38 = OpCompositeConstruct %struct_0 %36 %37
%39 = OpCompositeConstruct %_arr_struct_0_uint_2 %34 %38
OpStore %27 %39
; CHECK: [[access_chain:%\w+]] = OpAccessChain [[decorated_ptr]]
%40 = OpAccessChain %_ptr_Function_struct_0 %27 %28
; CHECK: [[load:%\w+]] = OpLoad [[decorated_type]] [[access_chain]]
%41 = OpLoad %struct_0 %40
; CHECK: [[extract1:%\w+]] = OpCompositeExtract %float [[load]] 0
; CHECK: [[extract2:%\w+]] = OpCompositeExtract %uint [[load]] 1
; CHECK: [[construct:%\w+]] = OpCompositeConstruct [[struct]] [[extract1]] [[extract2]]
; CHEKC: OpStore %26 [[construct]]
OpStore %26 %41
%42 = OpAccessChain %_ptr_Function_v4float %26 %28
%43 = OpLoad %v4float %42
OpStore %out_var_SV_Target %43
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<opt::CopyPropagateArrays>(text, false);
}
#endif // SPIRV_EFFCEE
// This test will place a load before the store. We cannot propagate in this
@ -516,87 +689,4 @@ OpFunctionEnd
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
// This test is okay except that we would have to change type of the store
// "OpStore %26 %41". We don't handle this yet.
TEST_F(CopyPropArrayPassTest, CantRewriteStore) {
const std::string text =
R"( OpCapability Shader
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %in_var_INDEX %out_var_SV_Target
OpExecutionMode %main OriginUpperLeft
OpSource HLSL 600
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_2 ArrayStride 16
OpDecorate %_arr__arr_v4float_uint_2_uint_2 ArrayStride 32
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_2 = OpConstant %uint 2
%_arr_v4float_uint_2 = OpTypeArray %v4float %uint_2
%_arr__arr_v4float_uint_2_uint_2 = OpTypeArray %_arr_v4float_uint_2 %uint_2
%type_MyCBuffer = OpTypeStruct %_arr__arr_v4float_uint_2_uint_2
%_ptr_Uniform_type_MyCBuffer = OpTypePointer Uniform %type_MyCBuffer
%void = OpTypeVoid
%14 = OpTypeFunction %void
%int = OpTypeInt 32 1
%_ptr_Input_int = OpTypePointer Input %int
%_ptr_Output_v4float = OpTypePointer Output %v4float
%_arr_v4float_uint_2_0 = OpTypeArray %v4float %uint_2
%_arr__arr_v4float_uint_2_0_uint_2 = OpTypeArray %_arr_v4float_uint_2_0 %uint_2
%_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 = OpTypePointer Function %_arr__arr_v4float_uint_2_0_uint_2
%int_0 = OpConstant %int 0
%_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 = OpTypePointer Uniform %_arr__arr_v4float_uint_2_uint_2
%_ptr_Function__arr_v4float_uint_2_0 = OpTypePointer Function %_arr_v4float_uint_2_0
%_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
%main = OpFunction %void None %14
%25 = OpLabel
%26 = OpVariable %_ptr_Function__arr_v4float_uint_2_0 Function
%27 = OpVariable %_ptr_Function__arr__arr_v4float_uint_2_0_uint_2 Function
%28 = OpLoad %int %in_var_INDEX
%29 = OpAccessChain %_ptr_Uniform__arr__arr_v4float_uint_2_uint_2 %MyCBuffer %int_0
%30 = OpLoad %_arr__arr_v4float_uint_2_uint_2 %29
%31 = OpCompositeExtract %_arr_v4float_uint_2 %30 0
%32 = OpCompositeExtract %v4float %31 0
%33 = OpCompositeExtract %v4float %31 1
%34 = OpCompositeConstruct %_arr_v4float_uint_2_0 %32 %33
%35 = OpCompositeExtract %_arr_v4float_uint_2 %30 1
%36 = OpCompositeExtract %v4float %35 0
%37 = OpCompositeExtract %v4float %35 1
%38 = OpCompositeConstruct %_arr_v4float_uint_2_0 %36 %37
%39 = OpCompositeConstruct %_arr__arr_v4float_uint_2_0_uint_2 %34 %38
OpStore %27 %39
%40 = OpAccessChain %_ptr_Function__arr_v4float_uint_2_0 %27 %28
%41 = OpLoad %_arr_v4float_uint_2_0 %40
OpStore %26 %41
%42 = OpAccessChain %_ptr_Function_v4float %26 %28
%43 = OpLoad %v4float %42
OpStore %out_var_SV_Target %43
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);
auto result = SinglePassRunAndDisassemble<opt::CopyPropagateArrays>(
text, /* skip_nop = */ true, /* do_validation = */ false);
EXPECT_EQ(opt::Pass::Status::SuccessWithoutChange, std::get<1>(result));
}
} // namespace