mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-26 01:31:06 +00:00
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:
parent
c4dc046399
commit
5e07ab1358
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user