Skip to content

Commit 7f1bce9

Browse files
committed
Fix OpSwitch target index logic in validator CFG analysis
The validator's OpSwitch operand parsing used index==1||index%2==0 which selects indices {1,2,4,6,...} — grabbing literal case values at even positions instead of block target IDs at odd positions. The correct predicate is index%2==1 which selects {1,3,5,7,...} (default target at index 1 plus case targets at indices 3,5,7,...). This caused false "continue block not reachable from loop header" errors when loops were inside switch case blocks, because the CFG was missing edges from the switch to its case targets.
1 parent c0c1012 commit 7f1bce9

4 files changed

Lines changed: 14 additions & 6 deletions

File tree

rust/spirv-tools-core/src/validation/cfg_analysis.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,19 @@ fn get_branch_targets(terminator: &Instruction) -> Vec<Id> {
6969
}
7070
Op::Switch => {
7171
// Operands: selector, default target, then pairs of (literal, target)
72+
// In rspirv's representation:
73+
// [0] = IdRef(selector)
74+
// [1] = IdRef(default_target)
75+
// [2] = LiteralBit32(case_val_0), [3] = IdRef(case_target_0)
76+
// [4] = LiteralBit32(case_val_1), [5] = IdRef(case_target_1)
77+
// ...
78+
// So targets are at all ODD indices (1, 3, 5, 7, ...)
7279
for (index, op) in terminator.operands.iter().enumerate() {
7380
if index == 0 {
7481
continue; // skip selector
7582
}
76-
// Default target is at index 1, then case targets are at even indices (2, 4, 6, ...)
77-
if index == 1 || index % 2 == 0 {
83+
// Default target is at index 1, case targets at odd indices >= 3
84+
if index % 2 == 1 {
7885
if let Operand::IdRef(target) = op {
7986
if let Some(id) = to_id(*target) {
8087
if !targets.contains(&id) {

rust/spirv-tools-core/src/validation/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1740,7 +1740,7 @@ fn is_block_operand(opcode: rspirv::spirv::Op, index: usize) -> bool {
17401740
match opcode {
17411741
rspirv::spirv::Op::Branch => index == 0,
17421742
rspirv::spirv::Op::BranchConditional => index == 1 || index == 2,
1743-
rspirv::spirv::Op::Switch => index == 1 || (index > 1 && index % 2 == 0),
1743+
rspirv::spirv::Op::Switch => index % 2 == 1,
17441744
rspirv::spirv::Op::LoopMerge => index <= 1,
17451745
rspirv::spirv::Op::SelectionMerge => index == 0,
17461746
rspirv::spirv::Op::Phi => index % 2 == 1,

rust/spirv-tools-core/src/validation/rules/cfg.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,11 +647,12 @@ impl ValidationRule for BranchTargetRule {
647647
.iter()
648648
.enumerate()
649649
.filter_map(|(idx, op)| {
650-
// Skip selector (idx 0), include default (idx 1) and case targets (even indices)
650+
// Skip selector (idx 0); default target is at idx 1,
651+
// case targets at odd indices >= 3 (literal/target pairs)
651652
if idx == 0 {
652653
return None;
653654
}
654-
if idx == 1 || idx % 2 == 0 {
655+
if idx % 2 == 1 {
655656
if let Operand::IdRef(raw) = op {
656657
return Some(to_id(*raw));
657658
}

rust/spirv-tools-core/src/validation/rules/types/general.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ fn is_block_operand(opcode: Op, index: usize) -> bool {
183183
match opcode {
184184
Op::Branch => index == 0,
185185
Op::BranchConditional => index == 1 || index == 2,
186-
Op::Switch => index == 1 || (index > 1 && index % 2 == 0),
186+
Op::Switch => index % 2 == 1,
187187
Op::LoopMerge => index <= 1,
188188
Op::SelectionMerge => index == 0,
189189
Op::Phi => index % 2 == 1,

0 commit comments

Comments
 (0)