@@ -225,11 +225,16 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass
225225 return Status::Failure;
226226 }
227227
228- // Note: We don't need to manually reorder the pointer type instruction.
229- // SPIR-V does not require types to appear in any specific relative order,
230- // as long as they are valid type declarations. FindPointerToType handles
231- // type creation correctly, and the SPIRV-Tools framework manages instruction
232- // ordering appropriately.
228+ // IMPORTANT: FindPointerToType() may create a new type instruction at the end of
229+ // the types_values section, or it may return an existing type. In either case,
230+ // SPIR-V requires all IDs to be defined before use (SSA form).
231+ //
232+ // We must ensure the pointer type instruction appears BEFORE the OpVariable
233+ // that will reference it. However, we must NOT move an existing type that is
234+ // already correctly positioned, as that could break other instructions that
235+ // depend on it being defined before their use.
236+ spvtools::opt::Instruction* new_ptr_type_inst = get_def_use_mgr ()->GetDef (new_ptr_type_id);
237+ EnsureTypeBeforeUseInTypesValues (new_ptr_type_inst, target_var);
233238
234239 // Update the variable's type to the new pointer type
235240 target_var->SetResultType (new_ptr_type_id);
@@ -276,6 +281,76 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass
276281 }
277282
278283private:
284+ // Returns true if instruction 'a' appears before instruction 'b' in module->types_values().
285+ // This is used to check if a type definition comes before its use in the SPIR-V module.
286+ bool ComesBeforeInTypesValues (const spvtools::opt::Instruction* a,
287+ const spvtools::opt::Instruction* b) const
288+ {
289+ if (a == nullptr || b == nullptr )
290+ return false ;
291+ if (a == b)
292+ return true ; // Same instruction, no reordering needed
293+
294+ bool seen_a = false ;
295+ for (auto & inst : context ()->module ()->types_values ())
296+ {
297+ if (&inst == a)
298+ {
299+ seen_a = true ;
300+ }
301+ else if (&inst == b)
302+ {
303+ return seen_a;
304+ }
305+ }
306+ // If either is not in types_values(), be conservative and return false.
307+ return false ;
308+ }
309+
310+ // Ensures 'type_inst' is placed before 'use_inst' in the types_values section.
311+ // Only moves the instruction when type_inst currently appears after use_inst.
312+ // This is necessary because SPIR-V requires all IDs to be defined before use (SSA form).
313+ //
314+ // IMPORTANT: This function only moves type_inst forward (earlier in the list), never backward.
315+ // If type_inst is already before use_inst, no action is taken to avoid breaking existing uses.
316+ //
317+ // If use_inst is not in types_values (e.g., it's in a function body), no action is taken
318+ // because the entire types_values section appears before any function in the module.
319+ void EnsureTypeBeforeUseInTypesValues (spvtools::opt::Instruction* type_inst,
320+ spvtools::opt::Instruction* use_inst)
321+ {
322+ if (type_inst == nullptr || use_inst == nullptr )
323+ return ;
324+
325+ // Check if use_inst is in types_values section.
326+ // If it's not (e.g., it's in a function body like OpAccessChain),
327+ // we don't need to move type_inst because the entire types_values section
328+ // appears before any function in the module.
329+ bool use_in_types_values = false ;
330+ for (auto & inst : context ()->module ()->types_values ())
331+ {
332+ if (&inst == use_inst)
333+ {
334+ use_in_types_values = true ;
335+ break ;
336+ }
337+ }
338+
339+ if (!use_in_types_values)
340+ return ;
341+
342+ // If type_inst already comes before use_inst, do nothing.
343+ // This is critical: moving an existing type that's already correctly positioned
344+ // could break other instructions that depend on it.
345+ if (ComesBeforeInTypesValues (type_inst, use_inst))
346+ return ;
347+
348+ // type_inst appears after use_inst (or not found), so we need to move it.
349+ // Insert it immediately before the use_inst to satisfy the SSA requirement.
350+ type_inst->RemoveFromList ();
351+ type_inst->InsertBefore (use_inst);
352+ }
353+
279354 // Recursively updates the storage class of pointer types used by instructions
280355 // that reference the target variable.
281356 bool PropagateStorageClass (spvtools::opt::Instruction& inst, std::unordered_set<uint32_t >& visited)
@@ -380,6 +455,14 @@ class ConvertUBOToPushConstantPass : public spvtools::opt::Pass
380455 if (new_result_type_id == 0 )
381456 return ;
382457
458+ // Ensure the pointer type is properly positioned in the types section.
459+ // FindPointerToType may return an existing type or create a new one at the end.
460+ // If inst is in types_values (e.g., OpConstantNull), we need to ensure the type
461+ // is defined before inst. If inst is in a function body (e.g., OpAccessChain),
462+ // no reordering is needed since types_values always precedes functions.
463+ spvtools::opt::Instruction* new_type_inst = get_def_use_mgr ()->GetDef (new_result_type_id);
464+ EnsureTypeBeforeUseInTypesValues (new_type_inst, &inst);
465+
383466 inst.SetResultType (new_result_type_id);
384467 context ()->UpdateDefUse (&inst);
385468 }
0 commit comments