Skip to content

Commit f06fb76

Browse files
committed
Guard continue blocks from RVSDG transforms and validate output IDs
Add continue_block_set checks to both selection and switch construct skip logic, preventing RVSDG transforms on constructs whose header is a loop continue block. This fixes "continue block not reachable from loop header" validation errors. Add a post-optimization safety check that scans the output module for undefined ID references (both operands and result_type). If any instruction references an ID not defined anywhere in the module, fall back to the original unoptimized module. This catches orphaned intermediates from egraph extraction edge cases.
1 parent 6fef24a commit f06fb76

1 file changed

Lines changed: 82 additions & 0 deletions

File tree

  • rust/spirv-tools-opt/src/direct

rust/spirv-tools-opt/src/direct/mod.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ pub fn optimize_module_direct(module: &Module) -> Result<Module, EgglogOptError>
630630
{
631631
let label_map = &func_block_labels[sel.func_idx];
632632
let in_loop = loop_block_set.contains(&(sel.func_idx, sel.header_block_idx))
633+
|| continue_block_set.contains(&(sel.func_idx, sel.header_block_idx))
633634
|| [&sel.then_label, &sel.else_label, &sel.merge_label]
634635
.iter()
635636
.any(|label| {
@@ -749,6 +750,7 @@ pub fn optimize_module_direct(module: &Module) -> Result<Module, EgglogOptError>
749750
{
750751
let label_map = &func_block_labels[sw.func_idx];
751752
let in_loop = loop_block_set.contains(&(sw.func_idx, sw.header_block_idx))
753+
|| continue_block_set.contains(&(sw.func_idx, sw.header_block_idx))
752754
|| sw.case_labels.iter().any(|label| {
753755
label_map.get(label).map_or(false, |&idx| {
754756
loop_block_set.contains(&(sw.func_idx, idx))
@@ -1823,6 +1825,86 @@ pub fn optimize_module_direct(module: &Module) -> Result<Module, EgglogOptError>
18231825
}
18241826
}
18251827

1828+
// Safety check: verify all referenced IDs are defined in the output module.
1829+
// If any undefined references exist (from orphaned intermediates, stale id_map
1830+
// entries, etc.), fall back to the unoptimized module to avoid validation errors.
1831+
{
1832+
let mut all_defined: HashSet<Word> = HashSet::new();
1833+
for inst in &output.types_global_values {
1834+
if let Some(id) = inst.result_id {
1835+
all_defined.insert(id);
1836+
}
1837+
}
1838+
for func in &output.functions {
1839+
if let Some(id) = func.def_id() {
1840+
all_defined.insert(id);
1841+
}
1842+
for param in &func.parameters {
1843+
if let Some(id) = param.result_id {
1844+
all_defined.insert(id);
1845+
}
1846+
}
1847+
for block in &func.blocks {
1848+
if let Some(label) = block.label.as_ref().and_then(|l| l.result_id) {
1849+
all_defined.insert(label);
1850+
}
1851+
for inst in &block.instructions {
1852+
if let Some(id) = inst.result_id {
1853+
all_defined.insert(id);
1854+
}
1855+
}
1856+
}
1857+
}
1858+
// Also include debug/annotation/entry_point IDs as defined
1859+
for inst in &output.annotations {
1860+
if let Some(id) = inst.result_id {
1861+
all_defined.insert(id);
1862+
}
1863+
}
1864+
for inst in &output.entry_points {
1865+
if let Some(id) = inst.result_id {
1866+
all_defined.insert(id);
1867+
}
1868+
}
1869+
for inst in &output.debug_string_source {
1870+
if let Some(id) = inst.result_id {
1871+
all_defined.insert(id);
1872+
}
1873+
}
1874+
for inst in &output.debug_names {
1875+
if let Some(id) = inst.result_id {
1876+
all_defined.insert(id);
1877+
}
1878+
}
1879+
1880+
let mut has_undefined = false;
1881+
'outer: for func in &output.functions {
1882+
for block in &func.blocks {
1883+
for inst in &block.instructions {
1884+
// Check result_type reference (separate from operands)
1885+
if let Some(ty) = inst.result_type {
1886+
if !all_defined.contains(&ty) {
1887+
has_undefined = true;
1888+
break 'outer;
1889+
}
1890+
}
1891+
for op in &inst.operands {
1892+
if let Some(ref_id) = op.id_ref_any() {
1893+
if !all_defined.contains(&ref_id) {
1894+
has_undefined = true;
1895+
break 'outer;
1896+
}
1897+
}
1898+
}
1899+
}
1900+
}
1901+
}
1902+
if has_undefined {
1903+
// Fall back to original module to avoid SPIR-V validation errors
1904+
return Ok(module.clone());
1905+
}
1906+
}
1907+
18261908
Ok(output)
18271909
}
18281910

0 commit comments

Comments
 (0)