Skip to content

Commit 131d5ac

Browse files
committed
Handle newer panic format_args lowering patterns
1 parent 24201f6 commit 131d5ac

1 file changed

Lines changed: 157 additions & 117 deletions

File tree

crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs

Lines changed: 157 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -276,130 +276,168 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
276276
insts.reverse();
277277
Some(insts)
278278
};
279-
let fmt_args_new_call_insts = try_rev_take(3).ok_or_else(|| {
280-
FormatArgsNotRecognized("fmt::Arguments::new call: ran out of instructions".into())
281-
})?;
279+
// Newer rustc can pass the `fmt::Arguments::new_*` result directly to
280+
// panic entry points (single trailing call), while older versions go
281+
// through a local temporary (`Call -> Store -> Load`).
282+
let fmt_args_new_call_inst_count = if matches!(
283+
try_rev_take(-3).as_deref(),
284+
Some(&[Inst::Call(_, _, _), Inst::Store(_, _), Inst::Load(_, _)])
285+
) {
286+
3
287+
} else if matches!(
288+
try_rev_take(-1).as_deref(),
289+
Some(&[Inst::Call(call_ret_id, _, _)]) if call_ret_id == format_args_id
290+
) {
291+
1
292+
} else {
293+
// HACK(eddyb) gather context for new call patterns before bailing.
294+
let mut insts = SmallVec::<[Inst<Word>; 32]>::new();
295+
while let Some(extra_inst) = try_rev_take(1) {
296+
insts.extend(extra_inst);
297+
if insts.len() >= 32 {
298+
break;
299+
}
300+
}
301+
insts.reverse();
302+
303+
return Err(if insts.is_empty() {
304+
FormatArgsNotRecognized("fmt::Arguments::new call: ran out of instructions".into())
305+
} else {
306+
FormatArgsNotRecognized(format!("fmt::Arguments::new call sequence ({insts:?})",))
307+
});
308+
};
309+
let fmt_args_new_call_insts = try_rev_take(fmt_args_new_call_inst_count).unwrap();
310+
let (call_args, pieces_len, rt_args_count) = match fmt_args_new_call_insts[..] {
311+
[
312+
Inst::Call(call_ret_id, callee_id, ref call_args),
313+
Inst::Store(st_dst_id, st_val_id),
314+
Inst::Load(ld_val_id, ld_src_id),
315+
] if call_ret_id == st_val_id
316+
&& st_dst_id == ld_src_id
317+
&& ld_val_id == format_args_id =>
318+
{
319+
require_local_var(st_dst_id, "fmt::Arguments::new destination")?;
320+
321+
let Some(&(pieces_len, rt_args_count)) =
322+
cx.fmt_args_new_fn_ids.borrow().get(&callee_id)
323+
else {
324+
return Err(FormatArgsNotRecognized(
325+
"fmt::Arguments::new callee not registered".into(),
326+
));
327+
};
328+
329+
(call_args, pieces_len, rt_args_count)
330+
}
331+
[Inst::Call(call_ret_id, callee_id, ref call_args)]
332+
if call_ret_id == format_args_id =>
333+
{
334+
let Some(&(pieces_len, rt_args_count)) =
335+
cx.fmt_args_new_fn_ids.borrow().get(&callee_id)
336+
else {
337+
return Err(FormatArgsNotRecognized(
338+
"fmt::Arguments::new callee not registered".into(),
339+
));
340+
};
341+
342+
(call_args, pieces_len, rt_args_count)
343+
}
344+
_ => {
345+
// HACK(eddyb) this gathers more context before reporting.
346+
let mut insts = fmt_args_new_call_insts;
347+
insts.reverse();
348+
while let Some(extra_inst) = try_rev_take(1) {
349+
insts.extend(extra_inst);
350+
if insts.len() >= 32 {
351+
break;
352+
}
353+
}
354+
insts.reverse();
355+
356+
return Err(FormatArgsNotRecognized(format!(
357+
"fmt::Arguments::new call sequence ({insts:?})",
358+
)));
359+
}
360+
};
282361
let ((pieces_slice_ptr_id, pieces_len), (rt_args_slice_ptr_id, rt_args_count)) =
283-
match fmt_args_new_call_insts[..] {
362+
match call_args[..] {
363+
// `<core::fmt::Arguments>::new_v1_formatted`
364+
//
365+
// HACK(eddyb) this isn't fully supported,
366+
// as that would require digging into unstable
367+
// internals of `core::fmt::rt::Placeholder`s,
368+
// but the whole call still needs to be removed,
369+
// and both const str pieces and runtime args
370+
// can still be printed (even if in jankier way).
284371
[
285-
Inst::Call(call_ret_id, callee_id, ref call_args),
286-
Inst::Store(st_dst_id, st_val_id),
287-
Inst::Load(ld_val_id, ld_src_id),
288-
] if call_ret_id == st_val_id
289-
&& st_dst_id == ld_src_id
290-
&& ld_val_id == format_args_id =>
291-
{
292-
require_local_var(st_dst_id, "fmt::Arguments::new destination")?;
293-
294-
let Some(&(pieces_len, rt_args_count)) =
295-
cx.fmt_args_new_fn_ids.borrow().get(&callee_id)
296-
else {
297-
return Err(FormatArgsNotRecognized(
298-
"fmt::Arguments::new callee not registered".into(),
299-
));
300-
};
372+
pieces_slice_ptr_id,
373+
pieces_len_id,
374+
rt_args_slice_ptr_id,
375+
rt_args_len_id,
376+
fmt_placeholders_slice_ptr_id,
377+
fmt_placeholders_len_id,
378+
] if (pieces_len, rt_args_count) == (!0, !0) => {
379+
let [pieces_len, rt_args_len, fmt_placeholders_len] =
380+
match [pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
381+
.map(const_u32_as_usize)
382+
{
383+
[Some(a), Some(b), Some(c)] => [a, b, c],
384+
_ => {
385+
return Err(FormatArgsNotRecognized(
386+
"fmt::Arguments::new_v1_formatted \
387+
with dynamic lengths"
388+
.into(),
389+
));
390+
}
391+
};
301392

302-
match call_args[..] {
303-
// `<core::fmt::Arguments>::new_v1_formatted`
304-
//
305-
// HACK(eddyb) this isn't fully supported,
306-
// as that would require digging into unstable
307-
// internals of `core::fmt::rt::Placeholder`s,
308-
// but the whole call still needs to be removed,
309-
// and both const str pieces and runtime args
310-
// can still be printed (even if in jankier way).
311-
[
312-
pieces_slice_ptr_id,
313-
pieces_len_id,
314-
rt_args_slice_ptr_id,
315-
rt_args_len_id,
316-
fmt_placeholders_slice_ptr_id,
317-
fmt_placeholders_len_id,
318-
] if (pieces_len, rt_args_count) == (!0, !0) => {
319-
let [pieces_len, rt_args_len, fmt_placeholders_len] =
320-
match [pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
321-
.map(const_u32_as_usize)
322-
{
323-
[Some(a), Some(b), Some(c)] => [a, b, c],
324-
_ => {
325-
return Err(FormatArgsNotRecognized(
326-
"fmt::Arguments::new_v1_formatted \
327-
with dynamic lengths"
328-
.into(),
329-
));
330-
}
331-
};
332-
333-
let prepare_args_insts = try_rev_take(2).ok_or_else(|| {
334-
FormatArgsNotRecognized(
335-
"fmt::Arguments::new_v1_formatted call: ran out of instructions".into(),
336-
)
337-
})?;
338-
let (rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
339-
match prepare_args_insts[..] {
340-
[
341-
Inst::Bitcast(rt_args_cast_out_id, rt_args_cast_in_id),
342-
Inst::Bitcast(
343-
placeholders_cast_out_id,
344-
placeholders_cast_in_id,
345-
),
346-
] if rt_args_cast_out_id == rt_args_slice_ptr_id
347-
&& placeholders_cast_out_id
348-
== fmt_placeholders_slice_ptr_id =>
349-
{
350-
(rt_args_cast_in_id, placeholders_cast_in_id)
351-
}
352-
_ => {
353-
let mut insts = prepare_args_insts;
354-
insts.extend(fmt_args_new_call_insts);
355-
return Err(FormatArgsNotRecognized(format!(
356-
"fmt::Arguments::new_v1_formatted call sequence ({insts:?})",
357-
)));
358-
}
359-
};
360-
361-
decoded_format_args.has_unknown_fmt_placeholder_to_args_mapping =
362-
Some(fmt_placeholders_len);
363-
364-
(
365-
(pieces_slice_ptr_id, pieces_len),
366-
(Some(rt_args_slice_ptr_id), rt_args_len),
367-
)
368-
}
393+
let prepare_args_insts = try_rev_take(2).ok_or_else(|| {
394+
FormatArgsNotRecognized(
395+
"fmt::Arguments::new_v1_formatted call: ran out of instructions".into(),
396+
)
397+
})?;
398+
let (rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
399+
match prepare_args_insts[..] {
400+
[
401+
Inst::Bitcast(rt_args_cast_out_id, rt_args_cast_in_id),
402+
Inst::Bitcast(placeholders_cast_out_id, placeholders_cast_in_id),
403+
] if rt_args_cast_out_id == rt_args_slice_ptr_id
404+
&& placeholders_cast_out_id == fmt_placeholders_slice_ptr_id =>
405+
{
406+
(rt_args_cast_in_id, placeholders_cast_in_id)
407+
}
408+
_ => {
409+
let mut insts = prepare_args_insts;
410+
insts.extend(fmt_args_new_call_insts);
411+
return Err(FormatArgsNotRecognized(format!(
412+
"fmt::Arguments::new_v1_formatted call sequence ({insts:?})",
413+
)));
414+
}
415+
};
369416

370-
// `<core::fmt::Arguments>::new_v1`
371-
[pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
372-
(pieces_slice_ptr_id, pieces_len),
373-
(Some(rt_args_slice_ptr_id), rt_args_count),
374-
),
417+
decoded_format_args.has_unknown_fmt_placeholder_to_args_mapping =
418+
Some(fmt_placeholders_len);
375419

376-
// `<core::fmt::Arguments>::new_const`
377-
[pieces_slice_ptr_id] if rt_args_count == 0 => {
378-
((pieces_slice_ptr_id, pieces_len), (None, rt_args_count))
379-
}
420+
(
421+
(pieces_slice_ptr_id, pieces_len),
422+
(Some(rt_args_slice_ptr_id), rt_args_len),
423+
)
424+
}
380425

381-
_ => {
382-
return Err(FormatArgsNotRecognized(
383-
"fmt::Arguments::new call args".into(),
384-
));
385-
}
386-
}
426+
// `<core::fmt::Arguments>::new_v1`
427+
[pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
428+
(pieces_slice_ptr_id, pieces_len),
429+
(Some(rt_args_slice_ptr_id), rt_args_count),
430+
),
431+
432+
// `<core::fmt::Arguments>::new_const`
433+
[pieces_slice_ptr_id] if rt_args_count == 0 => {
434+
((pieces_slice_ptr_id, pieces_len), (None, rt_args_count))
387435
}
388-
_ => {
389-
// HACK(eddyb) this gathers more context before reporting.
390-
let mut insts = fmt_args_new_call_insts;
391-
insts.reverse();
392-
while let Some(extra_inst) = try_rev_take(1) {
393-
insts.extend(extra_inst);
394-
if insts.len() >= 32 {
395-
break;
396-
}
397-
}
398-
insts.reverse();
399436

400-
return Err(FormatArgsNotRecognized(format!(
401-
"fmt::Arguments::new call sequence ({insts:?})",
402-
)));
437+
_ => {
438+
return Err(FormatArgsNotRecognized(
439+
"fmt::Arguments::new call args".into(),
440+
));
403441
}
404442
};
405443

@@ -772,7 +810,9 @@ impl<'a, 'tcx> CodegenPanic<'a, 'tcx> for FormatArgsResult<'tcx> {
772810
match self {
773811
Ok(e) => e.codegen_panic(builder, result_type),
774812
Err(FormatArgsNotRecognized(step)) => {
775-
if let Some(current_span) = builder.current_span {
813+
if let Some(current_span) = builder.current_span
814+
&& !builder.tcx.sess.opts.unstable_opts.ui_testing
815+
{
776816
// HACK(eddyb) Cargo silences warnings in dependencies.
777817
let force_warn = |span, msg| -> rustc_errors::Diag<'_, ()> {
778818
rustc_errors::Diag::new(

0 commit comments

Comments
 (0)