Skip to content

Commit ae829a2

Browse files
committed
Add rich error handling for NotALogicalPointer validation errors
Provides helpful diagnostics when SPIR-V validation fails due to using a pointer from an invalid source (like OpCompositeExtract) with memory operations (OpLoad/OpStore) in logical addressing mode. The error now shows: - Which instruction cannot use the pointer - What produced the invalid pointer - Explanation of logical addressing mode restrictions - The relevant SPIR-V instructions for context
1 parent 20250ae commit ae829a2

2 files changed

Lines changed: 126 additions & 1 deletion

File tree

crates/rustc_codegen_spirv/src/validation_err.rs

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ impl<'a> ValidationErrorContext<'a> {
5555
*storage_class,
5656
);
5757
}
58+
ValidationError::NotALogicalPointer {
59+
instruction,
60+
pointer,
61+
source_opcode,
62+
} => {
63+
self.emit_not_a_logical_pointer(*instruction, u32::from(*pointer), *source_opcode);
64+
}
5865
_ => {
5966
// Fall back to generic error message
6067
self.emit_generic_error(error);
@@ -140,6 +147,119 @@ impl<'a> ValidationErrorContext<'a> {
140147
}
141148
}
142149

150+
fn emit_not_a_logical_pointer(&mut self, instruction: Op, pointer_id: u32, source_opcode: Op) {
151+
// Try to find a useful span - first the pointer, then the instruction using it,
152+
// then trace back through the def chain
153+
let span = self.id_to_span(pointer_id).or_else(|| {
154+
// Try to find the load/store instruction that uses this pointer
155+
self.find_instruction_using_pointer(pointer_id, instruction)
156+
.and_then(|inst_id| self.id_to_span(inst_id))
157+
});
158+
159+
let pointer_name = self.get_name(pointer_id);
160+
let pointer_name_fallback = format!("%{}", pointer_id);
161+
let pointer_name_display = pointer_name.as_deref().unwrap_or(&pointer_name_fallback);
162+
163+
// Get SPIR-V context showing the relevant instructions
164+
let spirv_context = self.get_spirv_context_for_pointer(pointer_id, source_opcode);
165+
166+
let message = format!(
167+
"Op{:?} cannot use pointer `{}` because it was produced by Op{:?}",
168+
instruction, pointer_name_display, source_opcode
169+
);
170+
171+
let mut err = if let Some(span) = span {
172+
self.sess.dcx().struct_span_err(span, message)
173+
} else {
174+
self.sess.dcx().struct_err(message)
175+
};
176+
177+
err.note(format!(
178+
"in SPIR-V's logical addressing mode, pointers for Op{:?} must come from \
179+
specific instructions like OpVariable, OpAccessChain, or OpFunctionParameter",
180+
instruction
181+
));
182+
err.help(format!(
183+
"Op{:?} cannot produce pointers valid for memory operations in logical addressing",
184+
source_opcode
185+
));
186+
187+
// Show SPIR-V context if available
188+
if let Some(context) = spirv_context {
189+
err.note(format!("generated SPIR-V:\n{}", context));
190+
}
191+
192+
err.note("spirv-val failed");
193+
err.note(format!("module `{}`", self.filename.display()));
194+
err.emit();
195+
}
196+
197+
/// Finds an instruction that uses the given pointer ID with the specified opcode.
198+
fn find_instruction_using_pointer(&self, pointer_id: u32, opcode: Op) -> Option<u32> {
199+
let m = self.module?;
200+
for func in &m.functions {
201+
for block in &func.blocks {
202+
for inst in &block.instructions {
203+
if inst.class.opcode == opcode {
204+
// Check if any operand references our pointer
205+
for operand in &inst.operands {
206+
if let rspirv::dr::Operand::IdRef(id) = operand {
207+
if *id == pointer_id {
208+
return inst.result_id;
209+
}
210+
}
211+
}
212+
}
213+
}
214+
}
215+
}
216+
None
217+
}
218+
219+
/// Gets SPIR-V context showing the pointer-producing instruction and its use.
220+
fn get_spirv_context_for_pointer(&self, pointer_id: u32, source_opcode: Op) -> Option<String> {
221+
let m = self.module?;
222+
223+
// Find the instruction that produced the pointer
224+
let mut context_lines = Vec::new();
225+
226+
for func in &m.functions {
227+
for block in &func.blocks {
228+
for inst in &block.instructions {
229+
// Found the instruction that produced the pointer
230+
if inst.result_id == Some(pointer_id) && inst.class.opcode == source_opcode {
231+
context_lines.push(format!(" %{} = Op{:?} ...", pointer_id, source_opcode));
232+
}
233+
// Found instructions using the pointer
234+
if matches!(inst.class.opcode, Op::Load | Op::Store) {
235+
for operand in &inst.operands {
236+
if let rspirv::dr::Operand::IdRef(id) = operand {
237+
if *id == pointer_id {
238+
let result = inst
239+
.result_id
240+
.map(|r| format!("%{} = ", r))
241+
.unwrap_or_default();
242+
context_lines.push(format!(
243+
" -> {}Op{:?} %{} ...",
244+
result,
245+
inst.class.opcode,
246+
pointer_id
247+
));
248+
}
249+
}
250+
}
251+
}
252+
}
253+
}
254+
}
255+
256+
if context_lines.is_empty() {
257+
None
258+
} else {
259+
Some(context_lines.join("\n"))
260+
}
261+
}
262+
143263
/// Looks up the rustc Span for a SPIR-V ID.
144264
fn id_to_span(&mut self, id: u32) -> Option<rustc_span::Span> {
145265
self.span_regen.as_mut().and_then(|sr| {

tests/compiletests/ui/lang/core/ref/member_ref_arg-broken.stderr

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,13 @@ LL | fn h_newtyped(xyz: ((&u32, &u32, &u32),)) -> (u32, u32, u32) {
3434
= note: inlining was required due to illegal parameter type
3535
= note: called from `member_ref_arg_broken::main`
3636

37-
error: error:0:0 - OpLoad Pointer <id> '$ID[%$ID]' is not a logical pointer.
37+
error: OpLoad cannot use pointer `%38` because it was produced by OpCompositeExtract
3838
|
39+
= note: in SPIR-V's logical addressing mode, pointers for OpLoad must come from specific instructions like OpVariable, OpAccessChain, or OpFunctionParameter
40+
= help: OpCompositeExtract cannot produce pointers valid for memory operations in logical addressing
41+
= note: generated SPIR-V:
42+
%38 = OpCompositeExtract ...
43+
-> %39 = OpLoad %38 ...
3944
= note: spirv-val failed
4045
= note: module `$TEST_BUILD_DIR/lang/core/ref/member_ref_arg-broken`
4146

0 commit comments

Comments
 (0)