Skip to content

Commit 8f3ac5d

Browse files
committed
opt: dissolve OpCopyLogical of OpCompositeConstruct in simplification
1 parent df5532b commit 8f3ac5d

2 files changed

Lines changed: 208 additions & 0 deletions

File tree

source/opt/folding_rules.cpp

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,82 @@ std::vector<Operand> GetExtractOperandsForElementOfCompositeConstruct(
21242124
return {};
21252125
}
21262126

2127+
// If the OpCompositeConstruct that feeds an OpCopyLogical can be retyped to
2128+
// the OpCopyLogical's result type, the layout conversion can be expressed at
2129+
// constituent granularity instead of at aggregate granularity. This rewrites
2130+
// the OpCopyLogical as an OpCompositeConstruct of the result type, using the
2131+
// same constituents where their types already match the corresponding
2132+
// field/element of the result type, and inserting per-field OpCopyLogical
2133+
// instructions only for the fields that genuinely require a layout
2134+
// conversion.
2135+
bool CompositeConstructFeedingCopyLogical(
2136+
IRContext* context, Instruction* inst,
2137+
const std::vector<const analysis::Constant*>&) {
2138+
assert(inst->opcode() == spv::Op::OpCopyLogical &&
2139+
"Wrong opcode. Should be OpCopyLogical.");
2140+
analysis::DefUseManager* def_use_mgr = context->get_def_use_mgr();
2141+
2142+
uint32_t src_id = inst->GetSingleWordInOperand(0);
2143+
Instruction* src_inst = def_use_mgr->GetDef(src_id);
2144+
if (src_inst->opcode() != spv::Op::OpCompositeConstruct) {
2145+
return false;
2146+
}
2147+
2148+
Instruction* dst_type_inst = def_use_mgr->GetDef(inst->type_id());
2149+
const uint32_t num_constituents = src_inst->NumInOperands();
2150+
2151+
// Determine the expected type id for each constituent of the destination
2152+
// type.
2153+
std::vector<uint32_t> expected_type_ids;
2154+
expected_type_ids.reserve(num_constituents);
2155+
if (dst_type_inst->opcode() == spv::Op::OpTypeStruct) {
2156+
if (dst_type_inst->NumInOperands() != num_constituents) {
2157+
return false;
2158+
}
2159+
for (uint32_t i = 0; i < num_constituents; ++i) {
2160+
expected_type_ids.push_back(dst_type_inst->GetSingleWordInOperand(i));
2161+
}
2162+
} else if (dst_type_inst->opcode() == spv::Op::OpTypeArray) {
2163+
const uint32_t elem_type_id = dst_type_inst->GetSingleWordInOperand(0);
2164+
for (uint32_t i = 0; i < num_constituents; ++i) {
2165+
expected_type_ids.push_back(elem_type_id);
2166+
}
2167+
} else {
2168+
return false;
2169+
}
2170+
2171+
// Build the new constituent list, inserting OpCopyLogical instructions for
2172+
// the fields whose types differ from the result type.
2173+
InstructionBuilder ir_builder(
2174+
context, inst,
2175+
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
2176+
std::vector<Operand> operands;
2177+
operands.reserve(num_constituents);
2178+
for (uint32_t i = 0; i < num_constituents; ++i) {
2179+
const uint32_t cid = src_inst->GetSingleWordInOperand(i);
2180+
Instruction* cdef = def_use_mgr->GetDef(cid);
2181+
if (cdef->type_id() == expected_type_ids[i]) {
2182+
operands.push_back({SPV_OPERAND_TYPE_ID, {cid}});
2183+
continue;
2184+
}
2185+
if (def_use_mgr->GetDef(expected_type_ids[i])->opcode() ==
2186+
spv::Op::OpTypePointer) {
2187+
return false;
2188+
}
2189+
Instruction* per_field_copy = ir_builder.AddUnaryOp(
2190+
expected_type_ids[i], spv::Op::OpCopyLogical, cid);
2191+
if (per_field_copy == nullptr) {
2192+
return false;
2193+
}
2194+
operands.push_back({SPV_OPERAND_TYPE_ID, {per_field_copy->result_id()}});
2195+
}
2196+
2197+
inst->SetOpcode(spv::Op::OpCompositeConstruct);
2198+
inst->SetInOperands(std::move(operands));
2199+
context->UpdateDefUse(inst);
2200+
return true;
2201+
}
2202+
21272203
bool CompositeConstructFeedingExtract(
21282204
IRContext* context, Instruction* inst,
21292205
const std::vector<const analysis::Constant*>&) {
@@ -4476,6 +4552,9 @@ void FoldingRules::AddFoldingRules() {
44764552
rules_[spv::Op::OpCompositeConstruct].push_back(
44774553
CompositeExtractFeedingConstruct);
44784554

4555+
rules_[spv::Op::OpCopyLogical].push_back(
4556+
CompositeConstructFeedingCopyLogical);
4557+
44794558
rules_[spv::Op::OpCompositeExtract].push_back(InsertFeedingExtract());
44804559
rules_[spv::Op::OpCompositeExtract].push_back(
44814560
CompositeConstructFeedingExtract);

test/opt/simplification_test.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,135 @@ OpFunctionEnd
384384

385385
SinglePassRunAndCheck<SimplificationPass>(text, text, false);
386386
}
387+
388+
TEST_F(SimplificationTest, CopyLogicalOfCompositeConstructMatchingTypes) {
389+
// OpCopyLogical fed by an OpCompositeConstruct whose constituents are
390+
// already typed exactly as the corresponding fields of the OpCopyLogical's
391+
// result type can be rewritten as an OpCompositeConstruct of the result
392+
// type.
393+
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
394+
const std::string text = R"(
395+
; CHECK: [[ptr:%\w+]] = OpVariable %_ptr_StorageBuffer_int StorageBuffer
396+
; CHECK: [[ctor:%\w+]] = OpCompositeConstruct {{%\w+}} [[ptr]]
397+
; CHECK-NOT: OpCopyLogical
398+
; CHECK: [[ctor2:%\w+]] = OpCompositeConstruct {{%\w+}} [[ptr]]
399+
; CHECK: OpStore {{%\w+}} [[ctor2]]
400+
OpCapability Shader
401+
OpMemoryModel Logical GLSL450
402+
OpEntryPoint GLCompute %main "main"
403+
OpExecutionMode %main LocalSize 1 1 1
404+
OpDecorate %ResHolder_block Block
405+
OpMemberDecorate %ResHolder_block 0 Offset 0
406+
OpDecorate %ssbo DescriptorSet 0
407+
OpDecorate %ssbo Binding 0
408+
%void = OpTypeVoid
409+
%void_fn = OpTypeFunction %void
410+
%int = OpTypeInt 32 1
411+
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
412+
%ssbo = OpVariable %_ptr_StorageBuffer_int StorageBuffer
413+
%ResHolder_block = OpTypeStruct %int
414+
%ResHolder_fn = OpTypeStruct %_ptr_StorageBuffer_int
415+
%_ptr_Function_ResHolder_fn = OpTypePointer Function %ResHolder_fn
416+
%main = OpFunction %void None %void_fn
417+
%entry = OpLabel
418+
%h = OpVariable %_ptr_Function_ResHolder_fn Function
419+
%cc = OpCompositeConstruct %ResHolder_block %ssbo
420+
%cl = OpCopyLogical %ResHolder_fn %cc
421+
OpStore %h %cl
422+
OpReturn
423+
OpFunctionEnd
424+
)";
425+
426+
SinglePassRunAndMatch<SimplificationPass>(text, false);
427+
}
428+
429+
TEST_F(SimplificationTest, CopyLogicalOfCompositeConstructMixedTypes) {
430+
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
431+
const std::string text = R"(
432+
; CHECK: [[ptr:%\w+]] = OpVariable %_ptr_StorageBuffer_int StorageBuffer
433+
; CHECK: [[u:%\w+]] = OpLoad {{%\w+}}
434+
; CHECK: [[copy:%\w+]] = OpCopyLogical {{%\w+}} [[u]]
435+
; CHECK: [[ctor:%\w+]] = OpCompositeConstruct {{%\w+}} [[copy]] [[ptr]]
436+
; CHECK-NOT: OpCopyLogical {{%\w+}} {{%\w+}} {{%\w+}}
437+
; CHECK: OpStore {{%\w+}} [[ctor]]
438+
OpCapability Shader
439+
OpMemoryModel Logical GLSL450
440+
OpEntryPoint GLCompute %main "main"
441+
OpExecutionMode %main LocalSize 1 1 1
442+
OpDecorate %Uniforms_block Block
443+
OpMemberDecorate %Uniforms_block 0 Offset 0
444+
OpDecorate %ubo DescriptorSet 0
445+
OpDecorate %ubo Binding 0
446+
OpDecorate %ssbo DescriptorSet 0
447+
OpDecorate %ssbo Binding 1
448+
%void = OpTypeVoid
449+
%void_fn = OpTypeFunction %void
450+
%int = OpTypeInt 32 1
451+
%Uniforms_block = OpTypeStruct %int
452+
%Uniforms_fn = OpTypeStruct %int
453+
%_ptr_Uniform_Uniforms_block = OpTypePointer Uniform %Uniforms_block
454+
%ubo = OpVariable %_ptr_Uniform_Uniforms_block Uniform
455+
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
456+
%ssbo = OpVariable %_ptr_StorageBuffer_int StorageBuffer
457+
%Ctx_block = OpTypeStruct %Uniforms_block %int
458+
%Ctx_fn = OpTypeStruct %Uniforms_fn %_ptr_StorageBuffer_int
459+
%_ptr_Function_Ctx_fn = OpTypePointer Function %Ctx_fn
460+
%main = OpFunction %void None %void_fn
461+
%entry = OpLabel
462+
%h = OpVariable %_ptr_Function_Ctx_fn Function
463+
%u = OpLoad %Uniforms_block %ubo
464+
%cc = OpCompositeConstruct %Ctx_block %u %ssbo
465+
%cl = OpCopyLogical %Ctx_fn %cc
466+
OpStore %h %cl
467+
OpReturn
468+
OpFunctionEnd
469+
)";
470+
471+
SinglePassRunAndMatch<SimplificationPass>(text, false);
472+
}
473+
474+
TEST_F(SimplificationTest,
475+
CopyLogicalOfCompositeConstructPhysicalStorageBufferPointerField) {
476+
SetTargetEnv(SPV_ENV_UNIVERSAL_1_4);
477+
const std::string text = R"(
478+
; CHECK: [[cc:%\w+]] = OpCompositeConstruct {{%\w+}} {{%\w+}}
479+
; CHECK: OpCopyLogical {{%\w+}} [[cc]]
480+
OpCapability Shader
481+
OpCapability PhysicalStorageBufferAddresses
482+
OpExtension "SPV_KHR_physical_storage_buffer"
483+
OpMemoryModel PhysicalStorageBuffer64 GLSL450
484+
OpEntryPoint GLCompute %main "main"
485+
OpExecutionMode %main LocalSize 1 1 1
486+
OpDecorate %Payload_a Block
487+
OpMemberDecorate %Payload_a 0 Offset 0
488+
OpDecorate %Payload_b Block
489+
OpMemberDecorate %Payload_b 0 Offset 0
490+
%void = OpTypeVoid
491+
%void_fn = OpTypeFunction %void
492+
%int = OpTypeInt 32 1
493+
%Payload_a = OpTypeStruct %int
494+
%Payload_b = OpTypeStruct %int
495+
%_ptr_PSB_Payload_a = OpTypePointer PhysicalStorageBuffer %Payload_a
496+
%_ptr_PSB_Payload_b = OpTypePointer PhysicalStorageBuffer %Payload_b
497+
%Holder_src = OpTypeStruct %_ptr_PSB_Payload_a
498+
%Holder_dst = OpTypeStruct %_ptr_PSB_Payload_b
499+
%_ptr_Function_Holder_dst = OpTypePointer Function %Holder_dst
500+
%_ptr_Function_ptr_PSB_a = OpTypePointer Function %_ptr_PSB_Payload_a
501+
%main = OpFunction %void None %void_fn
502+
%entry = OpLabel
503+
%h = OpVariable %_ptr_Function_Holder_dst Function
504+
%pvar = OpVariable %_ptr_Function_ptr_PSB_a Function
505+
%p = OpLoad %_ptr_PSB_Payload_a %pvar
506+
%cc = OpCompositeConstruct %Holder_src %p
507+
%cl = OpCopyLogical %Holder_dst %cc
508+
OpStore %h %cl
509+
OpReturn
510+
OpFunctionEnd
511+
)";
512+
513+
SinglePassRunAndMatch<SimplificationPass>(text, false);
514+
}
515+
387516
} // namespace
388517
} // namespace opt
389518
} // namespace spvtools

0 commit comments

Comments
 (0)