Skip to content

Commit 50af2e5

Browse files
committed
opt: dissolve OpCopyLogical of OpCompositeConstruct in simplification
1 parent df5532b commit 50af2e5

2 files changed

Lines changed: 206 additions & 0 deletions

File tree

source/opt/folding_rules.cpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,94 @@ 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+
// Ensure every constituent is either the right type already, or can be
2172+
// converted with an OpCopyLogical (which forbids pointer types).
2173+
for (uint32_t i = 0; i < num_constituents; ++i) {
2174+
const uint32_t cid = src_inst->GetSingleWordInOperand(i);
2175+
Instruction* cdef = def_use_mgr->GetDef(cid);
2176+
if (cdef->type_id() == expected_type_ids[i]) {
2177+
continue;
2178+
}
2179+
Instruction* constituent_type_inst = def_use_mgr->GetDef(cdef->type_id());
2180+
Instruction* expected_type_inst = def_use_mgr->GetDef(expected_type_ids[i]);
2181+
if (constituent_type_inst->opcode() == spv::Op::OpTypePointer ||
2182+
expected_type_inst->opcode() == spv::Op::OpTypePointer) {
2183+
return false;
2184+
}
2185+
}
2186+
2187+
// Build the new constituent list, inserting OpCopyLogical instructions for
2188+
// the fields whose types differ from the result type.
2189+
InstructionBuilder ir_builder(
2190+
context, inst,
2191+
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
2192+
std::vector<Operand> operands;
2193+
operands.reserve(num_constituents);
2194+
for (uint32_t i = 0; i < num_constituents; ++i) {
2195+
const uint32_t cid = src_inst->GetSingleWordInOperand(i);
2196+
Instruction* cdef = def_use_mgr->GetDef(cid);
2197+
if (cdef->type_id() == expected_type_ids[i]) {
2198+
operands.push_back({SPV_OPERAND_TYPE_ID, {cid}});
2199+
continue;
2200+
}
2201+
Instruction* per_field_copy = ir_builder.AddUnaryOp(
2202+
expected_type_ids[i], spv::Op::OpCopyLogical, cid);
2203+
if (per_field_copy == nullptr) {
2204+
return false;
2205+
}
2206+
operands.push_back({SPV_OPERAND_TYPE_ID, {per_field_copy->result_id()}});
2207+
}
2208+
2209+
inst->SetOpcode(spv::Op::OpCompositeConstruct);
2210+
inst->SetInOperands(std::move(operands));
2211+
context->UpdateDefUse(inst);
2212+
return true;
2213+
}
2214+
21272215
bool CompositeConstructFeedingExtract(
21282216
IRContext* context, Instruction* inst,
21292217
const std::vector<const analysis::Constant*>&) {
@@ -4476,6 +4564,9 @@ void FoldingRules::AddFoldingRules() {
44764564
rules_[spv::Op::OpCompositeConstruct].push_back(
44774565
CompositeExtractFeedingConstruct);
44784566

4567+
rules_[spv::Op::OpCopyLogical].push_back(
4568+
CompositeConstructFeedingCopyLogical);
4569+
44794570
rules_[spv::Op::OpCompositeExtract].push_back(InsertFeedingExtract());
44804571
rules_[spv::Op::OpCompositeExtract].push_back(
44814572
CompositeConstructFeedingExtract);

test/opt/simplification_test.cpp

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,121 @@ 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+
const std::string text = R"(
394+
; CHECK: [[ptr:%\w+]] = OpVariable %_ptr_StorageBuffer_int StorageBuffer
395+
; CHECK: [[ctor:%\w+]] = OpCompositeConstruct %ResHolder_fn [[ptr]]
396+
; CHECK-NOT: OpCopyLogical
397+
; CHECK: OpStore {{%\w+}} [[ctor]]
398+
OpCapability Shader
399+
OpMemoryModel Logical GLSL450
400+
OpEntryPoint GLCompute %main "main"
401+
OpExecutionMode %main LocalSize 1 1 1
402+
OpDecorate %ResHolder_block Block
403+
OpMemberDecorate %ResHolder_block 0 Offset 0
404+
OpDecorate %ssbo DescriptorSet 0
405+
OpDecorate %ssbo Binding 0
406+
%void = OpTypeVoid
407+
%void_fn = OpTypeFunction %void
408+
%int = OpTypeInt 32 1
409+
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
410+
%ssbo = OpVariable %_ptr_StorageBuffer_int StorageBuffer
411+
%ResHolder_block = OpTypeStruct %int
412+
%ResHolder_fn = OpTypeStruct %_ptr_StorageBuffer_int
413+
%_ptr_Function_ResHolder_fn = OpTypePointer Function %ResHolder_fn
414+
%main = OpFunction %void None %void_fn
415+
%entry = OpLabel
416+
%h = OpVariable %_ptr_Function_ResHolder_fn Function
417+
%cc = OpCompositeConstruct %ResHolder_block %ssbo
418+
%cl = OpCopyLogical %ResHolder_fn %cc
419+
OpStore %h %cl
420+
OpReturn
421+
OpFunctionEnd
422+
)";
423+
424+
SinglePassRunAndMatch<SimplificationPass>(text, false);
425+
}
426+
427+
TEST_F(SimplificationTest, CopyLogicalOfCompositeConstructMixedTypes) {
428+
const std::string text = R"(
429+
; CHECK: [[ptr:%\w+]] = OpVariable %_ptr_StorageBuffer_int StorageBuffer
430+
; CHECK: [[u:%\w+]] = OpLoad %Uniforms_block
431+
; CHECK: [[copy:%\w+]] = OpCopyLogical %Uniforms_fn [[u]]
432+
; CHECK: [[ctor:%\w+]] = OpCompositeConstruct %Ctx_fn [[copy]] [[ptr]]
433+
; CHECK-NOT: OpCopyLogical %Ctx_fn
434+
; CHECK: OpStore {{%\w+}} [[ctor]]
435+
OpCapability Shader
436+
OpMemoryModel Logical GLSL450
437+
OpEntryPoint GLCompute %main "main"
438+
OpExecutionMode %main LocalSize 1 1 1
439+
OpDecorate %Uniforms_block Block
440+
OpMemberDecorate %Uniforms_block 0 Offset 0
441+
OpDecorate %ubo DescriptorSet 0
442+
OpDecorate %ubo Binding 0
443+
OpDecorate %ssbo DescriptorSet 0
444+
OpDecorate %ssbo Binding 1
445+
%void = OpTypeVoid
446+
%void_fn = OpTypeFunction %void
447+
%int = OpTypeInt 32 1
448+
%Uniforms_block = OpTypeStruct %int
449+
%Uniforms_fn = OpTypeStruct %int
450+
%_ptr_Uniform_Uniforms_block = OpTypePointer Uniform %Uniforms_block
451+
%ubo = OpVariable %_ptr_Uniform_Uniforms_block Uniform
452+
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
453+
%ssbo = OpVariable %_ptr_StorageBuffer_int StorageBuffer
454+
%Ctx_block = OpTypeStruct %Uniforms_block %int
455+
%Ctx_fn = OpTypeStruct %Uniforms_fn %_ptr_StorageBuffer_int
456+
%_ptr_Function_Ctx_fn = OpTypePointer Function %Ctx_fn
457+
%main = OpFunction %void None %void_fn
458+
%entry = OpLabel
459+
%h = OpVariable %_ptr_Function_Ctx_fn Function
460+
%u = OpLoad %Uniforms_block %ubo
461+
%cc = OpCompositeConstruct %Ctx_block %u %ssbo
462+
%cl = OpCopyLogical %Ctx_fn %cc
463+
OpStore %h %cl
464+
OpReturn
465+
OpFunctionEnd
466+
)";
467+
468+
SinglePassRunAndMatch<SimplificationPass>(text, false);
469+
}
470+
471+
TEST_F(SimplificationTest,
472+
CopyLogicalOfCompositeConstructDoesNotFoldInvalidPointerMismatch) {
473+
// The fold must not insert an OpCopyLogical between non-layout-compatible
474+
// types.
475+
const std::string text = R"(OpCapability Shader
476+
OpMemoryModel Logical GLSL450
477+
OpEntryPoint GLCompute %main "main"
478+
OpExecutionMode %main LocalSize 1 1 1
479+
OpDecorate %Block Block
480+
OpMemberDecorate %Block 0 Offset 0
481+
OpDecorate %ssbo DescriptorSet 0
482+
OpDecorate %ssbo Binding 0
483+
%void = OpTypeVoid
484+
%void_fn = OpTypeFunction %void
485+
%int = OpTypeInt 32 1
486+
%_ptr_StorageBuffer_int = OpTypePointer StorageBuffer %int
487+
%ssbo = OpVariable %_ptr_StorageBuffer_int StorageBuffer
488+
%Block = OpTypeStruct %int
489+
%Ctx_value = OpTypeStruct %int
490+
%Ctx_ptr = OpTypeStruct %_ptr_StorageBuffer_int
491+
%main = OpFunction %void None %void_fn
492+
%entry = OpLabel
493+
%cc = OpCompositeConstruct %Ctx_ptr %ssbo
494+
%cl = OpCopyLogical %Ctx_value %cc
495+
OpReturn
496+
OpFunctionEnd
497+
)";
498+
499+
SinglePassRunAndCheck<SimplificationPass>(text, text, false);
500+
}
501+
387502
} // namespace
388503
} // namespace opt
389504
} // namespace spvtools

0 commit comments

Comments
 (0)