Skip to content

Commit 1893fcb

Browse files
committed
spirv-opt: Fail gracefully on ID overflow during spec constant folding
The instruction folder returns nullptr when it fails to create a new instruction due to ID overflow. This is ambiguous because it also returns nullptr when it simply cannot fold an instruction. This change introduces an `id_overflow_` flag in `IRContext` that is set by `TakeNextId()` when it runs out of IDs. The `FoldSpecConstantOpAndCompositePass` is updated to check this flag after attempting to fold a spec constant. If the flag is set, the pass returns `Status::Failure`, preventing further processing and providing a clear error.
1 parent 337fdb6 commit 1893fcb

File tree

4 files changed

+67
-18
lines changed

4 files changed

+67
-18
lines changed

source/opt/fold_spec_constant_op_and_composite_pass.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,24 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
9696
// Constants will be added to id_to_const_val_ and const_val_to_id_ so
9797
// that we can use the new Normal Constants when folding following Spec
9898
// Constants.
99-
case spv::Op::OpSpecConstantOp:
100-
modified |= ProcessOpSpecConstantOp(&inst_iter);
99+
case spv::Op::OpSpecConstantOp: {
100+
const auto status = ProcessOpSpecConstantOp(&inst_iter);
101+
if (status == Status::Failure) {
102+
return Status::Failure;
103+
}
104+
if (status == Status::SuccessWithChange) {
105+
modified = true;
106+
}
101107
break;
108+
}
102109
default:
103110
break;
104111
}
105112
}
106113
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
107114
}
108115

109-
bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
116+
Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
110117
Module::inst_iterator* pos) {
111118
Instruction* inst = &**pos;
112119
Instruction* folded_inst = nullptr;
@@ -116,18 +123,25 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
116123
"SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
117124

118125
folded_inst = FoldWithInstructionFolder(pos);
126+
if (context()->id_overflow()) {
127+
return Status::Failure;
128+
}
129+
119130
if (!folded_inst) {
120131
folded_inst = DoComponentWiseOperation(pos);
132+
if (context()->id_overflow()) {
133+
return Status::Failure;
134+
}
121135
}
122-
if (!folded_inst) return false;
136+
if (!folded_inst) return Status::SuccessWithoutChange;
123137

124138
// Replace the original constant with the new folded constant, kill the
125139
// original constant.
126140
uint32_t new_id = folded_inst->result_id();
127141
uint32_t old_id = inst->result_id();
128142
context()->ReplaceAllUsesWith(old_id, new_id);
129143
context()->KillDef(old_id);
130-
return true;
144+
return Status::SuccessWithChange;
131145
}
132146

133147
Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
@@ -186,7 +200,11 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
186200

187201
if (need_to_clone) {
188202
new_const_inst = new_const_inst->Clone(context());
189-
new_const_inst->SetResultId(TakeNextId());
203+
uint32_t new_id = TakeNextId();
204+
if (new_id == 0) {
205+
return nullptr;
206+
}
207+
new_const_inst->SetResultId(new_id);
190208
new_const_inst->InsertAfter(insert_pos);
191209
get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst);
192210
}

source/opt/fold_spec_constant_op_and_composite_pass.h

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <unordered_map>
2020
#include <vector>
2121

22+
#include "source/diagnostic.h"
2223
#include "source/opt/constants.h"
2324
#include "source/opt/def_use_manager.h"
2425
#include "source/opt/ir_context.h"
@@ -45,14 +46,14 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
4546
private:
4647
// Processes the OpSpecConstantOp instruction pointed by the given
4748
// instruction iterator, folds it to normal constants if possible. Returns
48-
// true if the spec constant is folded to normal constants. New instructions
49-
// will be inserted before the OpSpecConstantOp instruction pointed by the
50-
// instruction iterator. The instruction iterator, which is passed by
51-
// pointer, will still point to the original OpSpecConstantOp instruction. If
52-
// folding is done successfully, the original OpSpecConstantOp instruction
53-
// will be changed to Nop and new folded instruction will be inserted before
54-
// it.
55-
bool ProcessOpSpecConstantOp(Module::inst_iterator* pos);
49+
// kSuccess if the spec constant is folded to normal constants. New
50+
// instructions will be inserted before the OpSpecConstantOp instruction
51+
// pointed by the instruction iterator. The instruction iterator, which is
52+
// passed by pointer, will still point to the original OpSpecConstantOp
53+
// instruction. If folding is done successfully, the original OpSpecConstantOp
54+
// instruction will be changed to Nop and new folded instruction will be
55+
// inserted before it. Returns kFail if an id overflow occurs.
56+
Status ProcessOpSpecConstantOp(Module::inst_iterator* pos);
5657

5758
// Returns the result of folding the OpSpecConstantOp instruction
5859
// |inst_iter_ptr| using the instruction folder.
@@ -62,7 +63,24 @@ class FoldSpecConstantOpAndCompositePass : public Pass {
6263
// pointed by the given instruction iterator to a normal constant defining
6364
// instruction. Returns the pointer to the new constant defining instruction
6465
// if succeeded, otherwise return nullptr.
66+
// instruction if succeeded, otherwise return nullptr.
6567
Instruction* DoComponentWiseOperation(Module::inst_iterator* inst_iter_ptr);
68+
69+
// Returns the next available id, or 0 if the id overflows.
70+
uint32_t TakeNextId() {
71+
uint32_t next_id = context()->TakeNextId();
72+
if (next_id == 0) {
73+
Fail() << "ID overflow. Try running compact-ids.";
74+
}
75+
return next_id;
76+
}
77+
78+
// Records failure for the current module and returns a stream for printing
79+
// diagnostics.
80+
spvtools::DiagnosticStream Fail() {
81+
return spvtools::DiagnosticStream({}, context()->consumer(), "",
82+
SPV_ERROR_INTERNAL);
83+
}
6684
};
6785

6886
} // namespace opt

source/opt/instruction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,7 +770,7 @@ bool Instruction::IsFoldableByFoldScalar() const {
770770
// Even if the type of the instruction is foldable, its operands may not be
771771
// foldable (e.g., comparisons of 64bit types). Check that all operand types
772772
// are foldable before accepting the instruction.
773-
return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
773+
return WhileEachInId([&folder, this](const uint32_t* op_id) {
774774
Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
775775
Instruction* def_inst_type =
776776
context()->get_def_use_mgr()->GetDef(def_inst->type_id());
@@ -792,7 +792,7 @@ bool Instruction::IsFoldableByFoldVector() const {
792792
// Even if the type of the instruction is foldable, its operands may not be
793793
// foldable (e.g., comparisons of 64bit types). Check that all operand types
794794
// are foldable before accepting the instruction.
795-
return WhileEachInOperand([&folder, this](const uint32_t* op_id) {
795+
return WhileEachInId([&folder, this](const uint32_t* op_id) {
796796
Instruction* def_inst = context()->get_def_use_mgr()->GetDef(*op_id);
797797
Instruction* def_inst_type =
798798
context()->get_def_use_mgr()->GetDef(def_inst->type_id());

source/opt/ir_context.h

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ class IRContext {
109109
id_to_name_(nullptr),
110110
max_id_bound_(kDefaultMaxIdBound),
111111
preserve_bindings_(false),
112-
preserve_spec_constants_(false) {
112+
preserve_spec_constants_(false),
113+
id_overflow_(false) {
113114
SetContextMessageConsumer(syntax_context_, consumer_);
114115
module_->SetContext(this);
115116
}
@@ -127,7 +128,8 @@ class IRContext {
127128
id_to_name_(nullptr),
128129
max_id_bound_(kDefaultMaxIdBound),
129130
preserve_bindings_(false),
130-
preserve_spec_constants_(false) {
131+
preserve_spec_constants_(false),
132+
id_overflow_(false) {
131133
SetContextMessageConsumer(syntax_context_, consumer_);
132134
module_->SetContext(this);
133135
InitializeCombinators();
@@ -563,6 +565,7 @@ class IRContext {
563565
inline uint32_t TakeNextId() {
564566
uint32_t next_id = module()->TakeNextIdBound();
565567
if (next_id == 0) {
568+
id_overflow_ = true;
566569
if (consumer()) {
567570
std::string message = "ID overflow. Try running compact-ids.";
568571
consumer()(SPV_MSG_ERROR, "", {0, 0, 0}, message.c_str());
@@ -583,6 +586,13 @@ class IRContext {
583586
return next_id;
584587
}
585588

589+
// Returns true if an ID overflow has occurred since the last time the flag
590+
// was cleared.
591+
bool id_overflow() const { return id_overflow_; }
592+
593+
// Clears the ID overflow flag.
594+
void clear_id_overflow() { id_overflow_ = false; }
595+
586596
FeatureManager* get_feature_mgr() {
587597
if (!feature_mgr_.get()) {
588598
AnalyzeFeatures();
@@ -930,6 +940,9 @@ class IRContext {
930940
// Whether all specialization constants within |module_|
931941
// should be preserved.
932942
bool preserve_spec_constants_;
943+
944+
// Set to true if TakeNextId() fails.
945+
bool id_overflow_;
933946
};
934947

935948
inline IRContext::Analysis operator|(IRContext::Analysis lhs,

0 commit comments

Comments
 (0)