Skip to content

Commit d7ac0e0

Browse files
authored
Merge pull request KhronosGroup#6310 from s-perron/id_overflow10
spirv-opt: Fail gracefully on ID overflow during spec constant folding
2 parents ef14ec7 + 1893fcb commit d7ac0e0

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
@@ -105,17 +105,24 @@ Pass::Status FoldSpecConstantOpAndCompositePass::Process() {
105105
// Constants will be added to id_to_const_val_ and const_val_to_id_ so
106106
// that we can use the new Normal Constants when folding following Spec
107107
// Constants.
108-
case spv::Op::OpSpecConstantOp:
109-
modified |= ProcessOpSpecConstantOp(&inst_iter);
108+
case spv::Op::OpSpecConstantOp: {
109+
const auto status = ProcessOpSpecConstantOp(&inst_iter);
110+
if (status == Status::Failure) {
111+
return Status::Failure;
112+
}
113+
if (status == Status::SuccessWithChange) {
114+
modified = true;
115+
}
110116
break;
117+
}
111118
default:
112119
break;
113120
}
114121
}
115122
return modified ? Status::SuccessWithChange : Status::SuccessWithoutChange;
116123
}
117124

118-
bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
125+
Pass::Status FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
119126
Module::inst_iterator* pos) {
120127
Instruction* inst = &**pos;
121128
Instruction* folded_inst = nullptr;
@@ -125,18 +132,25 @@ bool FoldSpecConstantOpAndCompositePass::ProcessOpSpecConstantOp(
125132
"SPV_OPERAND_TYPE_SPEC_CONSTANT_OP_NUMBER type");
126133

127134
folded_inst = FoldWithInstructionFolder(pos);
135+
if (context()->id_overflow()) {
136+
return Status::Failure;
137+
}
138+
128139
if (!folded_inst) {
129140
folded_inst = DoComponentWiseOperation(pos);
141+
if (context()->id_overflow()) {
142+
return Status::Failure;
143+
}
130144
}
131-
if (!folded_inst) return false;
145+
if (!folded_inst) return Status::SuccessWithoutChange;
132146

133147
// Replace the original constant with the new folded constant, kill the
134148
// original constant.
135149
uint32_t new_id = folded_inst->result_id();
136150
uint32_t old_id = inst->result_id();
137151
context()->ReplaceAllUsesWith(old_id, new_id);
138152
context()->KillDef(old_id);
139-
return true;
153+
return Status::SuccessWithChange;
140154
}
141155

142156
Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
@@ -195,7 +209,11 @@ Instruction* FoldSpecConstantOpAndCompositePass::FoldWithInstructionFolder(
195209

196210
if (need_to_clone) {
197211
new_const_inst = new_const_inst->Clone(context());
198-
new_const_inst->SetResultId(TakeNextId());
212+
uint32_t new_id = TakeNextId();
213+
if (new_id == 0) {
214+
return nullptr;
215+
}
216+
new_const_inst->SetResultId(new_id);
199217
new_const_inst->InsertAfter(insert_pos);
200218
get_def_use_mgr()->AnalyzeInstDefUse(new_const_inst);
201219
}

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)