spirv-fuzz: Avoid invalidating analyses when splitting blocks (#4218)
It is easy to avoid the need to invalidate the def-use analysis and
instruction to block mapping when splitting blocks, and profiling has
revealed that invalidation of def-use in particular is expensive when
splitting many blocks. This change avoids these invalidations.
diff --git a/source/fuzz/transformation_split_block.cpp b/source/fuzz/transformation_split_block.cpp
index 00ef081..e15dffa 100644
--- a/source/fuzz/transformation_split_block.cpp
+++ b/source/fuzz/transformation_split_block.cpp
@@ -109,24 +109,37 @@
split_before);
// The split does not automatically add a branch between the two parts of
// the original block, so we add one.
- block_to_split->AddInstruction(MakeUnique<opt::Instruction>(
+ auto branch_instruction = MakeUnique<opt::Instruction>(
ir_context, SpvOpBranch, 0, 0,
std::initializer_list<opt::Operand>{opt::Operand(
- spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})}));
+ spv_operand_type_t::SPV_OPERAND_TYPE_ID, {message_.fresh_id()})});
+ auto branch_instruction_ptr = branch_instruction.get();
+ block_to_split->AddInstruction(std::move(branch_instruction));
+
+ // Inform the def-use manager about the branch instruction, and record its
+ // block.
+ ir_context->get_def_use_mgr()->AnalyzeInstDefUse(branch_instruction_ptr);
+ ir_context->set_instr_block(branch_instruction_ptr, block_to_split);
+
// If we split before OpPhi instructions, we need to update their
// predecessor operand so that the block they used to be inside is now the
// predecessor.
- new_bb->ForEachPhiInst([block_to_split](opt::Instruction* phi_inst) {
+ new_bb->ForEachPhiInst([block_to_split,
+ ir_context](opt::Instruction* phi_inst) {
assert(
phi_inst->NumInOperands() == 2 &&
"Precondition: a block can only be split before an OpPhi if the block"
"has exactly one predecessor.");
phi_inst->SetInOperand(1, {block_to_split->id()});
+ ir_context->UpdateDefUse(phi_inst);
});
- // Invalidate all analyses
+ // We have updated the def-use manager and the instruction to block mapping,
+ // but other analyses (especially control flow-related ones) need to be
+ // recomputed.
ir_context->InvalidateAnalysesExceptFor(
- opt::IRContext::Analysis::kAnalysisNone);
+ opt::IRContext::Analysis::kAnalysisDefUse |
+ opt::IRContext::Analysis::kAnalysisInstrToBlockMapping);
// If the block being split was dead, the new block arising from the split is
// also dead.