Skip to content

Commit 1a2d8dd

Browse files
authored
spirv-opt: Propagate failures in loop transformations (KhronosGroup#6330)
This CL modifies several loop transformation passes and their utility functions to propagate failures, primarily caused by ID overflows when creating new instructions or basic blocks. Many functions that previously had a void return type now return a bool or a status enum to indicate success or failure. The callers are updated to check these return values and abort the transformation if a failure occurs. This affects loop unrolling, peeling, fusion, unswitching, and LICM, as well as core IR cloning and manipulation functions. The fuzzer transformations for inlining and outlining also get assertions to detect cloning failures.
1 parent f4be95f commit 1a2d8dd

18 files changed

+268
-114
lines changed

source/fuzz/transformation_inline_function.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ void TransformationInlineFunction::Apply(
182182
}
183183

184184
auto* cloned_block = block.Clone(ir_context);
185+
// TODO: Handle the nullptr.
186+
assert(cloned_block);
185187
cloned_block = caller_function->InsertBasicBlockBefore(
186188
std::unique_ptr<opt::BasicBlock>(cloned_block), successor_block);
187189
cloned_block->GetLabel()->SetResultId(result_id_map.at(cloned_block->id()));

source/fuzz/transformation_outline_function.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,8 @@ void TransformationOutlineFunction::PopulateOutlinedFunction(
819819
// Clone the block so that it can be added to the new function.
820820
auto cloned_block =
821821
std::unique_ptr<opt::BasicBlock>(block_it->Clone(ir_context));
822+
// TODO: Handle a nullptr.
823+
assert(cloned_block);
822824

823825
// If this is the region's exit block, then the cloned block is the outlined
824826
// region's exit block.

source/opt/basic_block.cpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,19 @@ constexpr uint32_t kSelectionMergeMergeBlockIdInIdx = 0;
2929
} // namespace
3030

3131
BasicBlock* BasicBlock::Clone(IRContext* context) const {
32-
BasicBlock* clone = new BasicBlock(
33-
std::unique_ptr<Instruction>(GetLabelInst()->Clone(context)));
32+
Instruction* label_clone = GetLabelInst()->Clone(context);
33+
if (!label_clone) {
34+
return nullptr;
35+
}
36+
BasicBlock* clone = new BasicBlock(std::unique_ptr<Instruction>(label_clone));
3437
for (const auto& inst : insts_) {
3538
// Use the incoming context
36-
clone->AddInstruction(std::unique_ptr<Instruction>(inst.Clone(context)));
39+
Instruction* inst_clone = inst.Clone(context);
40+
if (!inst_clone) {
41+
delete clone;
42+
return nullptr;
43+
}
44+
clone->AddInstruction(std::unique_ptr<Instruction>(inst_clone));
3745
}
3846

3947
if (context->AreAnalysesValid(

source/opt/cfg.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
254254
}
255255

256256
// Adjust the OpPhi instructions as needed.
257-
bb->ForEachPhiInst([latch_block, bb, new_header, context](Instruction* phi) {
257+
bool ok = bb->WhileEachPhiInst([latch_block, bb, new_header,
258+
context](Instruction* phi) -> bool {
258259
std::vector<uint32_t> preheader_phi_ops;
259260
std::vector<Operand> header_phi_ops;
260261

261-
// Identify where the original inputs to original OpPhi belong: header or
262-
// preheader.
262+
// Identify where the original inputs to original OpPhi belong: header
263+
// or preheader.
263264
for (uint32_t i = 0; i < phi->NumInOperands(); i += 2) {
264265
uint32_t def_id = phi->GetSingleWordInOperand(i);
265266
uint32_t branch_id = phi->GetSingleWordInOperand(i + 1);
@@ -272,22 +273,24 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
272273
}
273274
}
274275

275-
// Create a phi instruction if and only if the preheader_phi_ops has more
276-
// than one pair.
276+
// Create a phi instruction if and only if the preheader_phi_ops has
277+
// more than one pair.
277278
if (preheader_phi_ops.size() > 2) {
278279
InstructionBuilder builder(
279280
context, &*bb->begin(),
280281
IRContext::kAnalysisDefUse | IRContext::kAnalysisInstrToBlockMapping);
281282

282-
// TODO(1841): Handle id overflow.
283283
Instruction* new_phi = builder.AddPhi(phi->type_id(), preheader_phi_ops);
284+
if (!new_phi) {
285+
return false;
286+
}
284287

285288
// Add the OpPhi to the header bb.
286289
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {new_phi->result_id()}});
287290
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
288291
} else {
289-
// An OpPhi with a single entry is just a copy. In this case use the same
290-
// instruction in the new header.
292+
// An OpPhi with a single entry is just a copy. In this case use the
293+
// same instruction in the new header.
291294
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {preheader_phi_ops[0]}});
292295
header_phi_ops.push_back({SPV_OPERAND_TYPE_ID, {bb->id()}});
293296
}
@@ -298,8 +301,13 @@ BasicBlock* CFG::SplitLoopHeader(BasicBlock* bb) {
298301
new_header->begin()->InsertBefore(std::move(phi_owner));
299302
context->set_instr_block(phi, new_header);
300303
context->AnalyzeUses(phi);
304+
return true;
301305
});
302306

307+
if (!ok) {
308+
return nullptr;
309+
}
310+
303311
// Add a branch to the new header.
304312
InstructionBuilder branch_builder(
305313
context, bb,

source/opt/function.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ Function* Function::Clone(IRContext* ctx) const {
4040
clone->blocks_.reserve(blocks_.size());
4141
for (const auto& b : blocks_) {
4242
std::unique_ptr<BasicBlock> bb(b->Clone(ctx));
43+
if (!bb) {
44+
delete clone;
45+
return nullptr;
46+
}
4347
clone->AddBasicBlock(std::move(bb));
4448
}
4549

source/opt/instruction.cpp

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,13 @@ Instruction* Instruction::Clone(IRContext* c) const {
168168
clone->dbg_line_insts_ = dbg_line_insts_;
169169
for (auto& i : clone->dbg_line_insts_) {
170170
i.unique_id_ = c->TakeNextUniqueId();
171-
if (i.IsDebugLineInst()) i.SetResultId(c->TakeNextId());
171+
if (i.IsDebugLineInst()) {
172+
uint32_t new_id = c->TakeNextId();
173+
if (new_id == 0) {
174+
return nullptr;
175+
}
176+
i.SetResultId(new_id);
177+
}
172178
}
173179
clone->dbg_scope_ = dbg_scope_;
174180
return clone;
@@ -546,27 +552,37 @@ void Instruction::ClearDbgLineInsts() {
546552
clear_dbg_line_insts();
547553
}
548554

549-
void Instruction::UpdateDebugInfoFrom(const Instruction* from,
555+
bool Instruction::UpdateDebugInfoFrom(const Instruction* from,
550556
const Instruction* line) {
551-
if (from == nullptr) return;
557+
if (from == nullptr) return true;
552558
ClearDbgLineInsts();
553559
const Instruction* fromLine = line != nullptr ? line : from;
554-
if (!fromLine->dbg_line_insts().empty())
555-
AddDebugLine(&fromLine->dbg_line_insts().back());
560+
if (!fromLine->dbg_line_insts().empty()) {
561+
if (!AddDebugLine(&fromLine->dbg_line_insts().back())) {
562+
return false;
563+
}
564+
}
556565
SetDebugScope(from->GetDebugScope());
557566
if (!IsLineInst() &&
558567
context()->AreAnalysesValid(IRContext::kAnalysisDebugInfo)) {
559568
context()->get_debug_info_mgr()->AnalyzeDebugInst(this);
560569
}
570+
return true;
561571
}
562572

563-
void Instruction::AddDebugLine(const Instruction* inst) {
573+
bool Instruction::AddDebugLine(const Instruction* inst) {
564574
dbg_line_insts_.push_back(*inst);
565575
dbg_line_insts_.back().unique_id_ = context()->TakeNextUniqueId();
566-
if (inst->IsDebugLineInst())
567-
dbg_line_insts_.back().SetResultId(context_->TakeNextId());
576+
if (inst->IsDebugLineInst()) {
577+
uint32_t new_id = context()->TakeNextId();
578+
if (new_id == 0) {
579+
return false;
580+
}
581+
dbg_line_insts_.back().SetResultId(new_id);
582+
}
568583
if (context()->AreAnalysesValid(IRContext::kAnalysisDefUse))
569584
context()->get_def_use_mgr()->AnalyzeInstDefUse(&dbg_line_insts_.back());
585+
return true;
570586
}
571587

572588
bool Instruction::IsDebugLineInst() const {

source/opt/instruction.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
318318
inline void SetDebugScope(const DebugScope& scope);
319319
inline const DebugScope& GetDebugScope() const { return dbg_scope_; }
320320
// Add debug line inst. Renew result id if Debug[No]Line
321-
void AddDebugLine(const Instruction* inst);
321+
bool AddDebugLine(const Instruction* inst);
322322
// Updates DebugInlinedAt of DebugScope and OpLine.
323323
void UpdateDebugInlinedAt(uint32_t new_inlined_at);
324324
// Clear line-related debug instructions attached to this instruction
@@ -338,7 +338,7 @@ class Instruction : public utils::IntrusiveNodeBase<Instruction> {
338338
// Updates lexical scope of DebugScope and OpLine.
339339
void UpdateLexicalScope(uint32_t scope);
340340
// Updates OpLine and DebugScope based on the information of |from|.
341-
void UpdateDebugInfoFrom(const Instruction* from,
341+
bool UpdateDebugInfoFrom(const Instruction* from,
342342
const Instruction* line = nullptr);
343343
// Remove the |index|-th operand
344344
void RemoveOperand(uint32_t index) {

source/opt/licm_pass.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ bool LICMPass::IsImmediatelyContainedInLoop(Loop* loop, Function* f,
118118
}
119119

120120
bool LICMPass::HoistInstruction(Loop* loop, Instruction* inst) {
121-
// TODO(1841): Handle failure to create pre-header.
122121
BasicBlock* pre_header_bb = loop->GetOrCreatePreHeaderBlock();
123122
if (!pre_header_bb) {
124123
return false;

source/opt/loop_descriptor.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "source/opt/dominator_tree.h"
2626
#include "source/opt/ir_context.h"
2727
#include "source/opt/iterator.h"
28+
#include "source/opt/pass.h"
2829
#include "source/opt/tree_iterator.h"
2930
#include "source/util/make_unique.h"
3031

@@ -278,6 +279,9 @@ BasicBlock* Loop::GetOrCreatePreHeaderBlock() {
278279

279280
CFG* cfg = context_->cfg();
280281
loop_header_ = cfg->SplitLoopHeader(loop_header_);
282+
if (loop_header_ == nullptr) {
283+
return nullptr;
284+
}
281285
return loop_preheader_;
282286
}
283287

@@ -920,18 +924,19 @@ Instruction* Loop::FindConditionVariable(
920924
return induction;
921925
}
922926

923-
bool LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
924-
auto modified = false;
927+
LoopDescriptor::Status LoopDescriptor::CreatePreHeaderBlocksIfMissing() {
928+
bool modified = false;
925929

926930
for (auto& loop : *this) {
927931
if (!loop.GetPreHeaderBlock()) {
932+
if (!loop.GetOrCreatePreHeaderBlock()) {
933+
return Status::kFailure;
934+
}
928935
modified = true;
929-
// TODO(1841): Handle failure to create pre-header.
930-
loop.GetOrCreatePreHeaderBlock();
931936
}
932937
}
933938

934-
return modified;
939+
return modified ? Status::kSuccessWithChange : Status::kSuccessWithoutChange;
935940
}
936941

937942
// Add and remove loops which have been marked for addition and removal to

source/opt/loop_descriptor.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,9 @@ class LoopDescriptor {
425425
using pre_iterator = TreeDFIterator<Loop>;
426426
using const_pre_iterator = TreeDFIterator<const Loop>;
427427

428+
// The status of processing a module.
429+
enum class Status { kSuccessWithChange, kSuccessWithoutChange, kFailure };
430+
428431
// Creates a loop object for all loops found in |f|.
429432
LoopDescriptor(IRContext* context, const Function* f);
430433

@@ -506,9 +509,11 @@ class LoopDescriptor {
506509
loops_to_add_.emplace_back(std::make_pair(parent, std::move(loop_to_add)));
507510
}
508511

509-
// Checks all loops in |this| and will create pre-headers for all loops
510-
// that don't have one. Returns |true| if any blocks were created.
511-
bool CreatePreHeaderBlocksIfMissing();
512+
// Creates pre-header blocks for all loops in the function that do not have
513+
// one. Returns `LoopDescriptor::Status::kSuccessWithChange` if any change is
514+
// made, `LoopDescriptor::Status::kSuccessWithoutChange` if no change is made,
515+
// and `LoopDescriptor::Status::kFailure` if it fails to create a pre-header.
516+
Status CreatePreHeaderBlocksIfMissing();
512517

513518
// Should be called to preserve the LoopAnalysis after loops have been marked
514519
// for addition with AddLoop or MarkLoopForRemoval.

0 commit comments

Comments
 (0)