Skip to content

Commit d44ffaf

Browse files
authored
spirv-opt: Handle ID overflow in AggressiveDCEPass (#6366)
Refactored AggressiveDCEPass to return Pass::Status instead of bool in several key functions. This allows the pass to gracefully fail and propagate the error when the module's ID bound is reached, preventing crashes due to null pointers or zero IDs. Part of #6260
1 parent f856af4 commit d44ffaf

File tree

3 files changed

+51
-18
lines changed

3 files changed

+51
-18
lines changed

source/opt/aggressive_dead_code_elim_pass.cpp

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,29 +273,30 @@ void AggressiveDCEPass::AddBreaksAndContinuesToWorklist(
273273
});
274274
}
275275

276-
bool AggressiveDCEPass::AggressiveDCE(Function* func) {
277-
if (func->IsDeclaration()) return false;
276+
Pass::Status AggressiveDCEPass::AggressiveDCE(Function* func) {
277+
if (func->IsDeclaration()) return Pass::Status::SuccessWithoutChange;
278278
std::list<BasicBlock*> structured_order;
279279
cfg()->ComputeStructuredOrder(func, &*func->begin(), &structured_order);
280280
live_local_vars_.clear();
281281
InitializeWorkList(func, structured_order);
282282
ProcessWorkList(func);
283-
ProcessDebugInformation(structured_order);
283+
if (ProcessDebugInformation(structured_order) == Pass::Status::Failure)
284+
return Pass::Status::Failure;
284285
ProcessWorkList(func);
285286
return KillDeadInstructions(func, structured_order);
286287
}
287288

288-
void AggressiveDCEPass::ProcessDebugInformation(
289+
Pass::Status AggressiveDCEPass::ProcessDebugInformation(
289290
std::list<BasicBlock*>& structured_order) {
290291
for (auto bi = structured_order.begin(); bi != structured_order.end(); bi++) {
291-
(*bi)->ForEachInst([this](Instruction* inst) {
292+
bool succeeded = (*bi)->WhileEachInst([this](Instruction* inst) {
292293
// DebugDeclare is not dead. It must be converted to DebugValue in a
293294
// later pass
294295
if (inst->IsNonSemanticInstruction() &&
295296
inst->GetShader100DebugOpcode() ==
296297
NonSemanticShaderDebugInfo100DebugDeclare) {
297298
AddToWorklist(inst);
298-
return;
299+
return true;
299300
}
300301

301302
// If the Value of a DebugValue is killed, set Value operand to Undef
@@ -307,6 +308,9 @@ void AggressiveDCEPass::ProcessDebugInformation(
307308
if (!live_insts_.Set(def->unique_id())) {
308309
AddToWorklist(inst);
309310
uint32_t undef_id = Type2Undef(def->type_id());
311+
if (undef_id == 0) {
312+
return false;
313+
}
310314
inst->SetInOperand(kDebugValueValue, {undef_id});
311315
context()->get_def_use_mgr()->UpdateDefUse(inst);
312316
id = inst->GetSingleWordInOperand(kDebugValueLocalVariable);
@@ -318,14 +322,18 @@ void AggressiveDCEPass::ProcessDebugInformation(
318322
auto expression = get_def_use_mgr()->GetDef(id);
319323
AddToWorklist(expression);
320324
context()->get_def_use_mgr()->UpdateDefUse(expression);
321-
return;
325+
return true;
322326
}
323327
}
328+
return true;
324329
});
330+
331+
if (!succeeded) return Pass::Status::Failure;
325332
}
333+
return Pass::Status::SuccessWithoutChange;
326334
}
327335

328-
bool AggressiveDCEPass::KillDeadInstructions(
336+
Pass::Status AggressiveDCEPass::KillDeadInstructions(
329337
const Function* func, std::list<BasicBlock*>& structured_order) {
330338
bool modified = false;
331339
for (auto bi = structured_order.begin(); bi != structured_order.end();) {
@@ -360,6 +368,9 @@ bool AggressiveDCEPass::KillDeadInstructions(
360368
// Find an undef for the return value and make sure it gets kept by
361369
// the pass.
362370
auto undef_id = Type2Undef(func->type_id());
371+
if (undef_id == 0) {
372+
return Pass::Status::Failure;
373+
}
363374
auto undef = get_def_use_mgr()->GetDef(undef_id);
364375
live_insts_.Set(undef->unique_id());
365376
merge_terminator->SetOpcode(spv::Op::OpReturnValue);
@@ -378,7 +389,8 @@ bool AggressiveDCEPass::KillDeadInstructions(
378389
++bi;
379390
}
380391
}
381-
return modified;
392+
return modified ? Pass::Status::SuccessWithChange
393+
: Pass::Status::SuccessWithoutChange;
382394
}
383395

384396
void AggressiveDCEPass::ProcessWorkList(Function* func) {
@@ -638,7 +650,7 @@ void AggressiveDCEPass::InitializeWorkList(
638650
}
639651
}
640652

641-
void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
653+
Pass::Status AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
642654
// Keep all execution modes.
643655
for (auto& exec : get_module()->execution_modes()) {
644656
AddToWorklist(&exec);
@@ -713,6 +725,9 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
713725
}
714726
if (debug_global_seen) {
715727
auto dbg_none = context()->get_debug_info_mgr()->GetDebugInfoNone();
728+
if (dbg_none == nullptr) {
729+
return Pass::Status::Failure;
730+
}
716731
AddToWorklist(dbg_none);
717732
}
718733

@@ -726,6 +741,7 @@ void AggressiveDCEPass::InitializeModuleScopeLiveInstructions() {
726741
AddToWorklist(&dbg);
727742
}
728743
}
744+
return Pass::Status::SuccessWithoutChange;
729745
}
730746

731747
Pass::Status AggressiveDCEPass::ProcessImpl() {
@@ -753,15 +769,23 @@ Pass::Status AggressiveDCEPass::ProcessImpl() {
753769
// Eliminate Dead functions.
754770
bool modified = EliminateDeadFunctions();
755771

756-
InitializeModuleScopeLiveInstructions();
772+
if (InitializeModuleScopeLiveInstructions() == Pass::Status::Failure) {
773+
return Pass::Status::Failure;
774+
}
757775

758776
// Run |AggressiveDCE| on the remaining functions. The order does not matter,
759777
// since |AggressiveDCE| is intra-procedural. This can mean that function
760778
// will become dead if all function call to them are removed. These dead
761779
// function will still be in the module after this pass. We expect this to be
762780
// rare.
763781
for (Function& fp : *context()->module()) {
764-
modified |= AggressiveDCE(&fp);
782+
Pass::Status function_status = AggressiveDCE(&fp);
783+
if (function_status == Pass::Status::Failure) {
784+
return Pass::Status::Failure;
785+
}
786+
if (function_status == Pass::Status::SuccessWithChange) {
787+
modified = true;
788+
}
765789
}
766790

767791
// If the decoration manager is kept live then the context will try to keep it

source/opt/aggressive_dead_code_elim_pass.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ class AggressiveDCEPass : public MemPass {
8585
}
8686

8787
// Adds entry points, execution modes and workgroup size decorations to the
88-
// worklist for processing with the first function.
89-
void InitializeModuleScopeLiveInstructions();
88+
// worklist for processing with the first function. Returns
89+
// Pass::Status::Failure if it could not create a required debug instruction.
90+
// Returns Pass::Status::SuccessWithoutChange otherwise.
91+
Pass::Status InitializeModuleScopeLiveInstructions();
9092

9193
// Add |inst| to worklist_ and live_insts_.
9294
void AddToWorklist(Instruction* inst) {
@@ -136,7 +138,7 @@ class AggressiveDCEPass : public MemPass {
136138
// existing control structures will remain. This can leave not-insignificant
137139
// sequences of ultimately useless code.
138140
// TODO(): Remove useless control constructs.
139-
bool AggressiveDCE(Function* func);
141+
Pass::Status AggressiveDCE(Function* func);
140142

141143
Pass::Status ProcessImpl();
142144

@@ -154,11 +156,17 @@ class AggressiveDCEPass : public MemPass {
154156
// marked as live in the work list. DebugDeclare's are marked live now, and
155157
// DebugValue Value operands are set to OpUndef. The work list will be empty
156158
// at the end.
157-
void ProcessDebugInformation(std::list<BasicBlock*>& structured_order);
159+
// Returns Pass::Status::Failure if it could not create an OpUndef.
160+
// Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
161+
Pass::Status ProcessDebugInformation(
162+
std::list<BasicBlock*>& structured_order);
158163

159164
// Kills any instructions in |func| that have not been marked as live.
160-
bool KillDeadInstructions(const Function* func,
161-
std::list<BasicBlock*>& structured_order);
165+
// Returns Pass::Status::Failure if it could not create an OpUndef.
166+
// Otherwise, returns Pass::Status::SuccessWithChange if it made changes,
167+
// and Pass::Status::SuccessWithoutChange otherwise.
168+
Pass::Status KillDeadInstructions(const Function* func,
169+
std::list<BasicBlock*>& structured_order);
162170

163171
// Adds the instructions that define the operands of |inst| to the work list.
164172
void AddOperandsToWorkList(const Instruction* inst);

source/opt/debug_info_manager.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ Instruction* DebugInfoManager::GetDebugInfoNone() {
390390
if (debug_info_none_inst_ != nullptr) return debug_info_none_inst_;
391391

392392
uint32_t result_id = context()->TakeNextId();
393+
if (result_id == 0) return nullptr;
393394
std::unique_ptr<Instruction> dbg_info_none_inst(new Instruction(
394395
context(), spv::Op::OpExtInst, context()->get_type_mgr()->GetVoidTypeId(),
395396
result_id,

0 commit comments

Comments
 (0)