From d60d662ab887d95cbf0b52f665c4999739964fd3 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 15 Oct 2026 19:29:46 +0300 Subject: [PATCH 1/4] format_args_decompiler: remove excessive `core::panicking::panic_*` handling. --- .../src/builder/builder_methods.rs | 8 +- .../src/builder/format_args_decompiler.rs | 219 +--------- .../src/codegen_cx/declare.rs | 4 +- .../ui/dis/panic_builtin_bounds_check.stderr | 66 +-- .../ui/dis/panic_sequential_many.stderr | 401 +++++++++--------- 5 files changed, 260 insertions(+), 438 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs index 8a26cd9e339..10927fccbd0 100644 --- a/crates/rustc_codegen_spirv/src/builder/builder_methods.rs +++ b/crates/rustc_codegen_spirv/src/builder/builder_methods.rs @@ -3413,12 +3413,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> { } if is_panic_entry_point { - return DecodedFormatArgs::try_decode_and_remove_format_args( - self, - args, - instance_def_id, - ) - .codegen_panic(self, result_type); + return DecodedFormatArgs::try_decode_and_remove_format_args(self, args) + .codegen_panic(self, result_type); } if buffer_load_intrinsic { return self.codegen_buffer_load_intrinsic(fn_abi, result_type, args); diff --git a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs index 16b2a0fd41c..77de87bfc76 100644 --- a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs +++ b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs @@ -6,19 +6,16 @@ use crate::custom_insts::CustomOp; use crate::spirv_type::SpirvType; use either::Either; use itertools::Itertools; -use rspirv::dr::{InsertPoint, Instruction, Operand}; -use rspirv::spirv::{Op, StorageClass, Word}; -use rustc_abi::{Align, BackendRepr}; +use rspirv::dr::Operand; +use rspirv::spirv::{Op, Word}; +use rustc_abi::BackendRepr; use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; -use rustc_span::def_id::DefId; use smallvec::SmallVec; use std::borrow::Cow; use std::cell::Cell; -use crate::maybe_pqp_cg_ssa::traits::BuilderMethods; - // HACK(eddyb) even after `core::fmt::Arguments::from_str` became common // for const-string panics, `core`/`std` still lower many panic paths // (e.g. `assert_unsafe_precondition!`) through `fmt::Arguments::new*` @@ -58,7 +55,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { pub fn try_decode_and_remove_format_args<'a>( builder: &mut Builder<'a, 'tcx>, args: &[SpirvValue], - panic_def_id: Option, ) -> FormatArgsResult<'tcx> { let mut decoded_format_args = DecodedFormatArgs::default(); @@ -190,157 +186,22 @@ impl<'tcx> DecodedFormatArgs<'tcx> { Some((pieces, placeholder_count, placeholders_map_1_to_1)) }; - let panic_const_message = || { - let def_id = panic_def_id?; - let def_path = cx.tcx.def_path_str(def_id); - let name = def_path.strip_prefix("core::panicking::panic_const::")?; - match name { - "panic_const_add_overflow" => Some("attempt to add with overflow"), - "panic_const_sub_overflow" => Some("attempt to subtract with overflow"), - "panic_const_mul_overflow" => Some("attempt to multiply with overflow"), - "panic_const_div_overflow" => Some("attempt to divide with overflow"), - "panic_const_rem_overflow" => { - Some("attempt to calculate the remainder with overflow") - } - "panic_const_neg_overflow" => Some("attempt to negate with overflow"), - "panic_const_shr_overflow" => Some("attempt to shift right with overflow"), - "panic_const_shl_overflow" => Some("attempt to shift left with overflow"), - "panic_const_div_by_zero" => Some("attempt to divide by zero"), - "panic_const_rem_by_zero" => { - Some("attempt to calculate the remainder with a divisor of zero") - } - "panic_const_coroutine_resumed" => Some("coroutine resumed after completion"), - "panic_const_async_fn_resumed" => Some("`async fn` resumed after completion"), - "panic_const_async_gen_fn_resumed" => { - Some("`async gen fn` resumed after completion") - } - "panic_const_gen_fn_none" => { - Some("`gen fn` should just keep returning `None` after completion") - } - "panic_const_coroutine_resumed_panic" => Some("coroutine resumed after panicking"), - "panic_const_async_fn_resumed_panic" => Some("`async fn` resumed after panicking"), - "panic_const_async_gen_fn_resumed_panic" => { - Some("`async gen fn` resumed after panicking") - } - "panic_const_gen_fn_none_panic" => { - Some("`gen fn` should just keep returning `None` after panicking") - } - "panic_const_coroutine_resumed_drop" => Some("coroutine resumed after async drop"), - "panic_const_async_fn_resumed_drop" => Some("`async fn` resumed after async drop"), - "panic_const_async_gen_fn_resumed_drop" => { - Some("`async gen fn` resumed after async drop") - } - "panic_const_gen_fn_none_drop" => Some("`gen fn` resumed after async drop"), - _ => None, - } - }; - let mut spill_value_to_local = |value: SpirvValue| { - let ptr_ty = builder.type_ptr_to(value.ty); - - let ptr = { - // `OpVariable`s in functions must be inserted at the start of the - // first block, before any non-`OpVariable` instruction. - let mut emit = builder.emit(); - emit.select_block(Some(0)).unwrap(); - let index = { - let block = &emit.module_ref().functions[emit.selected_function().unwrap()] - .blocks[emit.selected_block().unwrap()]; - block - .instructions - .iter() - .enumerate() - .find_map(|(index, inst)| { - (inst.class.opcode != Op::Variable) - .then_some(InsertPoint::FromBegin(index)) - }) - .unwrap_or(InsertPoint::End) - }; - let result_id = emit.id(); - let inst = Instruction::new( - Op::Variable, - Some(ptr_ty), - Some(result_id), - vec![Operand::StorageClass(StorageClass::Function)], - ); - emit.insert_into_block(index, inst).unwrap(); - result_id.with_type(ptr_ty) - }; - - builder.store(value, ptr, Align::ONE); - match ptr.kind { - SpirvValueKind::Def(id) => Some(id), - _ => None, - } - }; - - if panic_def_id.is_some_and(|def_id| { - cx.tcx.def_path_str(def_id) == "core::panicking::panic_bounds_check" - }) && args.len() >= 2 - { - let index = args[0]; - let len = args[1]; - if let (Some(len_ref_id), Some(index_ref_id)) = - (spill_value_to_local(len), spill_value_to_local(index)) - { - decoded_format_args.const_pieces = Some( - [ - "index out of bounds: the len is ".into(), - " but the index is ".into(), - String::new(), - ] - .into_iter() - .collect(), - ); - decoded_format_args.ref_arg_ids_with_ty_and_spec = [ - (len_ref_id, cx.tcx.types.usize, ' '), - (index_ref_id, cx.tcx.types.usize, ' '), - ] - .into_iter() - .collect(); - return Ok(decoded_format_args); - } - } - let value_id = |value: SpirvValue| match value.kind { SpirvValueKind::Def(id) | SpirvValueKind::IllegalConst(id) => Some(id), _ => None, }; - // HACK(eddyb) `panic_explicit` doesn't take any regular arguments, - // only an (implicit) `&'static panic::Location<'static>`. - if args.len() == 1 { - decoded_format_args.const_pieces = Some( - [panic_const_message().unwrap_or("explicit panic").into()] - .into_iter() - .collect(), - ); - return Ok(decoded_format_args); - } - // HACK(eddyb) some entry-points only take a `&str`, not `fmt::Arguments`. if let [a, b, other_args @ ..] = args && let (Some(a_id), Some(b_id)) = (value_id(*a), value_id(*b)) { - let [a_ty, b_ty] = [a.ty, b.ty]; - // Optional panic flags and/or `&'static panic::Location<'static>`. - if other_args.len() <= 3 + if other_args.len() <= 2 && let Some(const_msg) = const_str_as_utf8(&[a_id, b_id]) { decoded_format_args.const_pieces = Some([const_msg].into_iter().collect()); return Ok(decoded_format_args); } - - // Dynamic `&str` panic messages (e.g. `panic_display(&msg)` where - // `msg` isn't a directly recoverable constant). - if other_args.len() <= 3 - && matches!(cx.lookup_type(a_ty), SpirvType::Pointer { .. }) - && matches!(cx.lookup_type(b_ty), SpirvType::Integer(..)) - { - decoded_format_args.const_pieces = - Some(["".into()].into_iter().collect()); - return Ok(decoded_format_args); - } } // Newer `core::fmt::Arguments` can be scalarized at the panic call-site @@ -606,10 +467,20 @@ impl<'tcx> DecodedFormatArgs<'tcx> { try_rev_take(3).unwrap(); (lookup_fmt_args_ctor(callee_id)?, call_args_storage) } else { - // We failed to recover constructor metadata for an already-split - // `fmt::Arguments` value. Keep panic lowering sound by falling - // back to an unknown panic message, without requiring decompilation. - return Ok(decoded_format_args); + // HACK(eddyb) this gathers more context before reporting. + let mut insts = try_rev_take(0).unwrap(); + insts.reverse(); + while let Some(extra_inst) = try_rev_take(1) { + insts.extend(extra_inst); + if insts.len() >= 32 { + break; + } + } + insts.reverse(); + + return Err(FormatArgsNotRecognized(format!( + "fmt::Arguments::new call sequence ({insts:?})", + ))); } } else { // Newer rustc can pass the `fmt::Arguments::new_*` result directly to @@ -622,14 +493,10 @@ impl<'tcx> DecodedFormatArgs<'tcx> { 3 } else if let Some(&[Inst::Call(call_ret_id, callee_id, _)]) = try_rev_take(-1).as_deref() + && (call_ret_id == format_args_id + || cx.fmt_args_new_fn_ids.borrow().contains_key(&callee_id)) { - if call_ret_id == format_args_id - || cx.fmt_args_new_fn_ids.borrow().contains_key(&callee_id) - { - 1 - } else { - 0 - } + 1 } else if matches!( try_rev_take(-5).as_deref(), Some(&[ @@ -644,50 +511,10 @@ impl<'tcx> DecodedFormatArgs<'tcx> { && inserted1 == format_args_id ) { 5 - } else if try_rev_take(-1).is_some() { - 0 } else { - // HACK(eddyb) gather context for new call patterns before bailing. - let mut insts = SmallVec::<[Inst; 32]>::new(); - while let Some(extra_inst) = try_rev_take(1) { - insts.extend(extra_inst); - if insts.len() >= 32 { - break; - } - } - insts.reverse(); - - if insts.is_empty() { - return Ok(decoded_format_args); - } - if !insts.iter().any(|inst| matches!(inst, Inst::Call(_, _, _))) { - return Ok(decoded_format_args); - } - return Err(FormatArgsNotRecognized(format!( - "fmt::Arguments::new call sequence ({insts:?})", - ))); + // HACK(eddyb) this reaches the `return Err` below. + 0 }; - if fmt_args_new_call_inst_count == 0 { - // HACK(eddyb) gather context for new call patterns before bailing. - let mut insts = SmallVec::<[Inst; 32]>::new(); - while let Some(extra_inst) = try_rev_take(1) { - insts.extend(extra_inst); - if insts.len() >= 32 { - break; - } - } - insts.reverse(); - - if insts.is_empty() { - return Ok(decoded_format_args); - } - if !insts.iter().any(|inst| matches!(inst, Inst::Call(_, _, _))) { - return Ok(decoded_format_args); - } - return Err(FormatArgsNotRecognized(format!( - "fmt::Arguments::new call sequence ({insts:?})", - ))); - } let fmt_args_new_call_insts = try_rev_take(fmt_args_new_call_inst_count).unwrap(); match fmt_args_new_call_insts[..] { [ diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index b01f6761eb5..9a9bb5a06dc 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -217,9 +217,7 @@ impl<'tcx> CodegenCx<'tcx> { // HACK(eddyb) there is no good way to identify these definitions // (e.g. no `#[lang = "..."]` attribute), but this works well enough. - if let Some(name) = demangled_symbol_name.strip_prefix("core::panicking::") - && (name == "panic_explicit" || name.starts_with("panic_")) - { + if demangled_symbol_name == "core::panicking::panic_nounwind_fmt" { self.panic_entry_points.borrow_mut().insert(def_id); } if let Some(generics) = demangled_symbol_name diff --git a/tests/compiletests/ui/dis/panic_builtin_bounds_check.stderr b/tests/compiletests/ui/dis/panic_builtin_bounds_check.stderr index 490e222cc51..16063e6b9f1 100644 --- a/tests/compiletests/ui/dis/panic_builtin_bounds_check.stderr +++ b/tests/compiletests/ui/dis/panic_builtin_bounds_check.stderr @@ -4,16 +4,17 @@ OpMemoryModel Logical Simple OpEntryPoint Fragment %2 "main" OpExecutionMode %2 OriginUpperLeft - %3 = OpString "/n[Rust panicked at $DIR/panic_builtin_bounds_check.rs:27:5]/n index out of bounds: the len is %u but the index is %u/n in main()/n" - %4 = OpString "$DIR/panic_builtin_bounds_check.rs" - OpDecorate %5 ArrayStride 4 + %3 = OpString "/n[Rust panicked at $SYSROOT/lib/rustlib/src/rust/library/core/src/panicking.rs:271:5]/n index out of bounds: the len is %u but the index is %u/n in main()/n" + %4 = OpString $SYSROOT/lib/rustlib/src/rust/library/core/src/panicking.rs" + %5 = OpString "$DIR/panic_builtin_bounds_check.rs" + OpDecorate %6 ArrayStride 4 %void = OpTypeVoid - %7 = OpTypeFunction %void + %8 = OpTypeFunction %void %u32 = OpTypeInt 32 0 %u32_4 = OpConstant %u32 4 - %10 = OpTypeArray %u32 %u32_4 -%_ptr_Function_10 = OpTypePointer Function %10 - %5 = OpTypeArray %u32 %u32_4 + %11 = OpTypeArray %u32 %u32_4 +%_ptr_Function_11 = OpTypePointer Function %11 + %6 = OpTypeArray %u32 %u32_4 %u32_0 = OpConstant %u32 0 %u32_1 = OpConstant %u32 1 %u32_2 = OpConstant %u32 2 @@ -21,36 +22,35 @@ %bool = OpTypeBool %u32_5 = OpConstant %u32 5 %_ptr_Function_u32 = OpTypePointer Function %u32 - %2 = OpFunction %void None %7 - %19 = OpLabel - OpLine %4 32 4 - %20 = OpVariable %_ptr_Function_10 Function - OpLine %4 32 23 - %21 = OpCompositeConstruct %5 %u32_0 %u32_1 %u32_2 %u32_3 - OpLine %4 32 4 - %22 = OpCompositeExtract %u32 %21 0 - %23 = OpCompositeExtract %u32 %21 1 - %24 = OpCompositeExtract %u32 %21 2 - %25 = OpCompositeExtract %u32 %21 3 - %26 = OpCompositeConstruct %10 %22 %23 %24 %25 - OpStore %20 %26 - OpLine %4 27 4 - %27 = OpULessThan %bool %u32_5 %u32_4 + %2 = OpFunction %void None %8 + %20 = OpLabel + OpLine %5 32 4 + %21 = OpVariable %_ptr_Function_11 Function + OpLine %5 32 23 + %22 = OpCompositeConstruct %6 %u32_0 %u32_1 %u32_2 %u32_3 + OpLine %5 27 4 + %23 = OpCompositeExtract %u32 %22 0 + %24 = OpCompositeExtract %u32 %22 1 + %25 = OpCompositeExtract %u32 %22 2 + %26 = OpCompositeExtract %u32 %22 3 + %27 = OpCompositeConstruct %11 %23 %24 %25 %26 + OpStore %21 %27 + %28 = OpULessThan %bool %u32_5 %u32_4 OpNoLine - OpSelectionMerge %28 None - OpBranchConditional %27 %29 %30 - %29 = OpLabel - OpBranch %28 + OpSelectionMerge %29 None + OpBranchConditional %28 %30 %31 %30 = OpLabel - OpLine %4 27 4 - %31 = OpExtInst %void %1 1 %3 %u32_4 %u32_5 + OpBranch %29 + %31 = OpLabel + OpLine %4 271 4 + %32 = OpExtInst %void %1 1 %3 %u32_4 %u32_5 OpNoLine OpReturn - %28 = OpLabel - OpLine %4 27 4 - %32 = OpIAdd %u32 %u32_0 %u32_5 - %33 = OpInBoundsAccessChain %_ptr_Function_u32 %20 %32 - %34 = OpLoad %u32 %33 + %29 = OpLabel + OpLine %5 27 4 + %33 = OpIAdd %u32 %u32_0 %u32_5 + %34 = OpInBoundsAccessChain %_ptr_Function_u32 %21 %33 + %35 = OpLoad %u32 %34 OpNoLine OpReturn OpFunctionEnd diff --git a/tests/compiletests/ui/dis/panic_sequential_many.stderr b/tests/compiletests/ui/dis/panic_sequential_many.stderr index 2b74fbc0a34..7f8bcdb72d3 100644 --- a/tests/compiletests/ui/dis/panic_sequential_many.stderr +++ b/tests/compiletests/ui/dis/panic_sequential_many.stderr @@ -4,8 +4,9 @@ OpMemoryModel Logical Simple OpEntryPoint Fragment %2 "main" %x %y %o OpExecutionMode %2 OriginUpperLeft - %6 = OpString "/n[Rust panicked at $DIR/panic_sequential_many.rs:31:10]/n attempt to divide by zero/n in main()/n" - %7 = OpString "$DIR/panic_sequential_many.rs" + %6 = OpString "/n[Rust panicked at $SYSROOT/lib/rustlib/src/rust/library/core/src/panicking.rs:187:5]/n attempt to divide by zero/n in main()/n" + %7 = OpString $SYSROOT/lib/rustlib/src/rust/library/core/src/panicking.rs" + %8 = OpString "$DIR/panic_sequential_many.rs" OpName %x "x" OpName %y "y" OpName %o "o" @@ -18,258 +19,258 @@ %_ptr_Input_u32 = OpTypePointer Input %u32 %_ptr_Output_u32 = OpTypePointer Output %u32 %void = OpTypeVoid - %12 = OpTypeFunction %void + %13 = OpTypeFunction %void %x = OpVariable %_ptr_Input_u32 Input %y = OpVariable %_ptr_Input_u32 Input %bool = OpTypeBool %u32_0 = OpConstant %u32 0 %o = OpVariable %_ptr_Output_u32 Output - %2 = OpFunction %void None %12 - %15 = OpLabel - OpLine %7 28 12 - %16 = OpLoad %u32 %x - OpLine %7 28 35 - %17 = OpLoad %u32 %y - OpLine %7 31 9 - %18 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %19 None - OpBranchConditional %18 %20 %21 - %20 = OpLabel - OpLine %7 31 9 - %22 = OpExtInst %void %1 1 %6 - OpNoLine - OpReturn + %2 = OpFunction %void None %13 + %16 = OpLabel + OpLine %8 28 12 + %17 = OpLoad %u32 %x + OpLine %8 28 35 + %18 = OpLoad %u32 %y + OpLine %8 31 9 + %19 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %20 None + OpBranchConditional %19 %21 %22 %21 = OpLabel - OpBranch %19 - %19 = OpLabel - OpLine %7 31 9 - %23 = OpUDiv %u32 %16 %17 - %24 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %25 None - OpBranchConditional %24 %26 %27 - %26 = OpLabel - OpLine %7 31 9 - %28 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %23 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %22 = OpLabel + OpBranch %20 + %20 = OpLabel + OpLine %8 31 9 + %24 = OpUDiv %u32 %17 %18 + %25 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %26 None + OpBranchConditional %25 %27 %28 %27 = OpLabel - OpBranch %25 - %25 = OpLabel - OpLine %7 31 9 - %29 = OpUDiv %u32 %23 %17 - %30 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %31 None - OpBranchConditional %30 %32 %33 - %32 = OpLabel - OpLine %7 31 9 - %34 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %29 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %28 = OpLabel + OpBranch %26 + %26 = OpLabel + OpLine %8 31 9 + %30 = OpUDiv %u32 %24 %18 + %31 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %32 None + OpBranchConditional %31 %33 %34 %33 = OpLabel - OpBranch %31 - %31 = OpLabel - OpLine %7 31 9 - %35 = OpUDiv %u32 %29 %17 - %36 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %37 None - OpBranchConditional %36 %38 %39 - %38 = OpLabel - OpLine %7 31 9 - %40 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %35 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %34 = OpLabel + OpBranch %32 + %32 = OpLabel + OpLine %8 31 9 + %36 = OpUDiv %u32 %30 %18 + %37 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %38 None + OpBranchConditional %37 %39 %40 %39 = OpLabel - OpBranch %37 - %37 = OpLabel - OpLine %7 31 9 - %41 = OpUDiv %u32 %35 %17 - %42 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %43 None - OpBranchConditional %42 %44 %45 - %44 = OpLabel - OpLine %7 31 9 - %46 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %41 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %40 = OpLabel + OpBranch %38 + %38 = OpLabel + OpLine %8 31 9 + %42 = OpUDiv %u32 %36 %18 + %43 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %44 None + OpBranchConditional %43 %45 %46 %45 = OpLabel - OpBranch %43 - %43 = OpLabel - OpLine %7 31 9 - %47 = OpUDiv %u32 %41 %17 - %48 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %49 None - OpBranchConditional %48 %50 %51 - %50 = OpLabel - OpLine %7 31 9 - %52 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %47 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %46 = OpLabel + OpBranch %44 + %44 = OpLabel + OpLine %8 31 9 + %48 = OpUDiv %u32 %42 %18 + %49 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %50 None + OpBranchConditional %49 %51 %52 %51 = OpLabel - OpBranch %49 - %49 = OpLabel - OpLine %7 31 9 - %53 = OpUDiv %u32 %47 %17 - %54 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %55 None - OpBranchConditional %54 %56 %57 - %56 = OpLabel - OpLine %7 31 9 - %58 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %53 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %52 = OpLabel + OpBranch %50 + %50 = OpLabel + OpLine %8 31 9 + %54 = OpUDiv %u32 %48 %18 + %55 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %56 None + OpBranchConditional %55 %57 %58 %57 = OpLabel - OpBranch %55 - %55 = OpLabel - OpLine %7 31 9 - %59 = OpUDiv %u32 %53 %17 - %60 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %61 None - OpBranchConditional %60 %62 %63 - %62 = OpLabel - OpLine %7 31 9 - %64 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %59 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %58 = OpLabel + OpBranch %56 + %56 = OpLabel + OpLine %8 31 9 + %60 = OpUDiv %u32 %54 %18 + %61 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %62 None + OpBranchConditional %61 %63 %64 %63 = OpLabel - OpBranch %61 - %61 = OpLabel - OpLine %7 31 9 - %65 = OpUDiv %u32 %59 %17 - %66 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %67 None - OpBranchConditional %66 %68 %69 - %68 = OpLabel - OpLine %7 31 9 - %70 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %65 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %64 = OpLabel + OpBranch %62 + %62 = OpLabel + OpLine %8 31 9 + %66 = OpUDiv %u32 %60 %18 + %67 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %68 None + OpBranchConditional %67 %69 %70 %69 = OpLabel - OpBranch %67 - %67 = OpLabel - OpLine %7 31 9 - %71 = OpUDiv %u32 %65 %17 - %72 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %73 None - OpBranchConditional %72 %74 %75 - %74 = OpLabel - OpLine %7 31 9 - %76 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %71 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %70 = OpLabel + OpBranch %68 + %68 = OpLabel + OpLine %8 31 9 + %72 = OpUDiv %u32 %66 %18 + %73 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %74 None + OpBranchConditional %73 %75 %76 %75 = OpLabel - OpBranch %73 - %73 = OpLabel - OpLine %7 31 9 - %77 = OpUDiv %u32 %71 %17 - %78 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %79 None - OpBranchConditional %78 %80 %81 - %80 = OpLabel - OpLine %7 31 9 - %82 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %77 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %76 = OpLabel + OpBranch %74 + %74 = OpLabel + OpLine %8 31 9 + %78 = OpUDiv %u32 %72 %18 + %79 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %80 None + OpBranchConditional %79 %81 %82 %81 = OpLabel - OpBranch %79 - %79 = OpLabel - OpLine %7 31 9 - %83 = OpUDiv %u32 %77 %17 - %84 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %85 None - OpBranchConditional %84 %86 %87 - %86 = OpLabel - OpLine %7 31 9 - %88 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %83 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %82 = OpLabel + OpBranch %80 + %80 = OpLabel + OpLine %8 31 9 + %84 = OpUDiv %u32 %78 %18 + %85 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %86 None + OpBranchConditional %85 %87 %88 %87 = OpLabel - OpBranch %85 - %85 = OpLabel - OpLine %7 31 9 - %89 = OpUDiv %u32 %83 %17 - %90 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %91 None - OpBranchConditional %90 %92 %93 - %92 = OpLabel - OpLine %7 31 9 - %94 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %89 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %88 = OpLabel + OpBranch %86 + %86 = OpLabel + OpLine %8 31 9 + %90 = OpUDiv %u32 %84 %18 + %91 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %92 None + OpBranchConditional %91 %93 %94 %93 = OpLabel - OpBranch %91 - %91 = OpLabel - OpLine %7 31 9 - %95 = OpUDiv %u32 %89 %17 - %96 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %97 None - OpBranchConditional %96 %98 %99 - %98 = OpLabel - OpLine %7 31 9 - %100 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %95 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %94 = OpLabel + OpBranch %92 + %92 = OpLabel + OpLine %8 31 9 + %96 = OpUDiv %u32 %90 %18 + %97 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %98 None + OpBranchConditional %97 %99 %100 %99 = OpLabel - OpBranch %97 - %97 = OpLabel - OpLine %7 31 9 - %101 = OpUDiv %u32 %95 %17 - %102 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %103 None - OpBranchConditional %102 %104 %105 - %104 = OpLabel - OpLine %7 31 9 - %106 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %101 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %100 = OpLabel + OpBranch %98 + %98 = OpLabel + OpLine %8 31 9 + %102 = OpUDiv %u32 %96 %18 + %103 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %104 None + OpBranchConditional %103 %105 %106 %105 = OpLabel - OpBranch %103 - %103 = OpLabel - OpLine %7 31 9 - %107 = OpUDiv %u32 %101 %17 - %108 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %109 None - OpBranchConditional %108 %110 %111 - %110 = OpLabel - OpLine %7 31 9 - %112 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %107 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %106 = OpLabel + OpBranch %104 + %104 = OpLabel + OpLine %8 31 9 + %108 = OpUDiv %u32 %102 %18 + %109 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %110 None + OpBranchConditional %109 %111 %112 %111 = OpLabel - OpBranch %109 - %109 = OpLabel - OpLine %7 31 9 - %113 = OpUDiv %u32 %107 %17 - %114 = OpIEqual %bool %17 %u32_0 - OpNoLine - OpSelectionMerge %115 None - OpBranchConditional %114 %116 %117 - %116 = OpLabel - OpLine %7 31 9 - %118 = OpExtInst %void %1 1 %6 + OpLine %7 187 4 + %113 = OpExtInst %void %1 1 %6 OpNoLine OpReturn + %112 = OpLabel + OpBranch %110 + %110 = OpLabel + OpLine %8 31 9 + %114 = OpUDiv %u32 %108 %18 + %115 = OpIEqual %bool %18 %u32_0 + OpNoLine + OpSelectionMerge %116 None + OpBranchConditional %115 %117 %118 %117 = OpLabel - OpBranch %115 - %115 = OpLabel - OpLine %7 31 4 - %119 = OpUDiv %u32 %113 %17 - OpStore %o %119 + OpLine %7 187 4 + %119 = OpExtInst %void %1 1 %6 + OpNoLine + OpReturn + %118 = OpLabel + OpBranch %116 + %116 = OpLabel + OpLine %8 31 4 + %120 = OpUDiv %u32 %114 %18 + OpStore %o %120 OpNoLine OpReturn OpFunctionEnd From 119ebfc1b22ad2499d6f9efa40d804a80772fcf8 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 15 Oct 2026 18:35:28 +0300 Subject: [PATCH 2/4] format_args_decompiler: remove old `fmt::Arguments` constructor support. --- .../src/builder/format_args_decompiler.rs | 151 +----------------- .../src/codegen_cx/declare.rs | 29 ---- .../rustc_codegen_spirv/src/codegen_cx/mod.rs | 12 +- 3 files changed, 6 insertions(+), 186 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs index 77de87bfc76..cde3fd545fc 100644 --- a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs +++ b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs @@ -36,6 +36,11 @@ pub struct DecodedFormatArgs<'tcx> { /// * `&b` with `typeof b` and 'x' ref_arg_ids_with_ty_and_spec: SmallVec<[(Word, Ty<'tcx>, char); 2]>, + // FIXME(eddyb) the comment below is outdated, and the new approach + // (see `parse_encoded_template` below) can definitely handle any + // mapping of placeholders just fine (it already parses that mapping + // in order to determine when to set this to `Some`, i.e. "not 1:1"). + // /// If `fmt::Arguments::new_v1_formatted` was used, this holds /// the length of the `&[fmt::rt::Placeholder]` slice, which /// currently cannot be directly supported, and therefore even @@ -577,7 +582,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { }; let call_args = call_args_storage.as_slice(); enum PiecesSource { - Slice { ptr_id: Word, len: usize }, EncodedTemplate { ptr_id: Word, len: usize }, DirectConstStr([Word; 2]), } @@ -598,103 +602,12 @@ impl<'tcx> DecodedFormatArgs<'tcx> { (Some(rt_args_slice_ptr_id), rt_args_count), ), - // `::new_v1_formatted` - // - // HACK(eddyb) this isn't fully supported, - // as that would require digging into unstable - // internals of `core::fmt::rt::Placeholder`s, - // but the whole call still needs to be removed, - // and both const str pieces and runtime args - // can still be printed (even if in jankier way). - ( - FmtArgsCtor::NewV1FormattedDynamic, - &[ - pieces_slice_ptr_id, - pieces_len_id, - rt_args_slice_ptr_id, - rt_args_len_id, - fmt_placeholders_slice_ptr_id, - fmt_placeholders_len_id, - ], - ) => { - let [pieces_len, rt_args_len, fmt_placeholders_len] = - match [pieces_len_id, rt_args_len_id, fmt_placeholders_len_id] - .map(const_u32_as_usize) - { - [Some(a), Some(b), Some(c)] => [a, b, c], - _ => { - return Err(FormatArgsNotRecognized( - "fmt::Arguments::new_v1_formatted \ - with dynamic lengths" - .into(), - )); - } - }; - - let prepare_args_insts = try_rev_take(2).ok_or_else(|| { - FormatArgsNotRecognized( - "fmt::Arguments::new_v1_formatted call: ran out of instructions".into(), - ) - })?; - let (rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) = - match prepare_args_insts[..] { - [ - Inst::Bitcast(rt_args_cast_out_id, rt_args_cast_in_id), - Inst::Bitcast(placeholders_cast_out_id, placeholders_cast_in_id), - ] if rt_args_cast_out_id == rt_args_slice_ptr_id - && placeholders_cast_out_id == fmt_placeholders_slice_ptr_id => - { - (rt_args_cast_in_id, placeholders_cast_in_id) - } - _ => { - return Err(FormatArgsNotRecognized(format!( - "fmt::Arguments::new_v1_formatted call sequence ({prepare_args_insts:?})", - ))); - } - }; - - decoded_format_args.has_unknown_fmt_placeholder_to_args_mapping = - Some(fmt_placeholders_len); - - ( - PiecesSource::Slice { - ptr_id: pieces_slice_ptr_id, - len: pieces_len, - }, - (Some(rt_args_slice_ptr_id), rt_args_len), - ) - } - // `::from_str` (FmtArgsCtor::FromStr, &[str_ptr_id, str_len_id]) => ( PiecesSource::DirectConstStr([str_ptr_id, str_len_id]), (None, 0), ), - // `::new_v1` - ( - FmtArgsCtor::NewV1 { - pieces_len, - rt_args_count, - }, - &[pieces_slice_ptr_id, rt_args_slice_ptr_id], - ) => ( - PiecesSource::Slice { - ptr_id: pieces_slice_ptr_id, - len: pieces_len, - }, - (Some(rt_args_slice_ptr_id), rt_args_count), - ), - - // `::new_const` - (FmtArgsCtor::NewConst { pieces_len }, &[pieces_slice_ptr_id]) => ( - PiecesSource::Slice { - ptr_id: pieces_slice_ptr_id, - len: pieces_len, - }, - (None, 0), - ), - _ => { return Err(FormatArgsNotRecognized( "fmt::Arguments::new ctor/call-args mismatch".into(), @@ -900,60 +813,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { pieces }, ), - PiecesSource::Slice { - ptr_id: pieces_slice_ptr_id, - len: pieces_len, - } => { - // If the `pieces: &[&str]` slice needs a bitcast, it'll be here. - // HACK(eddyb) `try_rev_take(-1)` is "peeking", not taking. - let pieces_slice_ptr_id = match try_rev_take(-1).as_deref() { - Some(&[Inst::Bitcast(out_id, in_id)]) if out_id == pieces_slice_ptr_id => { - // HACK(eddyb) consume the peeked instructions. - try_rev_take(1).unwrap(); - - in_id - } - _ => pieces_slice_ptr_id, - }; - - match const_slice_as_elem_ids(pieces_slice_ptr_id, pieces_len) { - Some(piece_ids) => piece_ids - .iter() - .map(|&piece_id| match cx.builder.lookup_const_by_id(piece_id)? { - SpirvConst::Composite(piece) => { - const_str_as_utf8(piece.try_into().ok()?) - } - _ => None, - }) - .collect::>(), - // HACK(eddyb) minor upstream blunder results in at - // least one instance of a runtime `[&str; 1]` array, - // see also this comment left on the responsible PR: - // https://github.com/rust-lang/rust/pull/129658#discussion_r2181834781 - // HACK(eddyb) `try_rev_take(-4)` is "peeking", not taking. - None if pieces_len == 1 => match try_rev_take(-4).as_deref() { - Some( - &[ - Inst::InBoundsAccessChain2(field0_ptr, array_ptr_0, 0, 0), - Inst::Store(st0_dst_ptr, st0_val), - Inst::InBoundsAccessChain2(field1_ptr, array_ptr_1, 0, 1), - Inst::Store(st1_dst_ptr, st1_val), - ], - ) if [array_ptr_0, array_ptr_1] == [pieces_slice_ptr_id; 2] - && st0_dst_ptr == field0_ptr - && st1_dst_ptr == field1_ptr => - { - // HACK(eddyb) consume the peeked instructions. - try_rev_take(4).unwrap(); - - const_str_as_utf8(&[st0_val, st1_val]) - .map(|s| [s].into_iter().collect()) - } - _ => None, - }, - None => None, - } - } }; if let Some((placeholder_count, placeholders_map_1_to_1)) = template_placeholder_info { diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs index 9a9bb5a06dc..3f71392ba3b 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/declare.rs @@ -237,35 +237,6 @@ impl<'tcx> CodegenCx<'tcx> { ); } } - if let Some(pieces_len) = demangled_symbol_name - .strip_prefix("::new_const::<") - .and_then(|s| s.strip_suffix(">")) - { - self.fmt_args_new_fn_ids.borrow_mut().insert( - fn_id, - FmtArgsCtor::NewConst { - pieces_len: pieces_len.parse().unwrap(), - }, - ); - } - if let Some(generics) = demangled_symbol_name - .strip_prefix("::new_v1::<") - .and_then(|s| s.strip_suffix(">")) - { - let (pieces_len, rt_args_len) = generics.split_once(", ").unwrap(); - self.fmt_args_new_fn_ids.borrow_mut().insert( - fn_id, - FmtArgsCtor::NewV1 { - pieces_len: pieces_len.parse().unwrap(), - rt_args_count: rt_args_len.parse().unwrap(), - }, - ); - } - if demangled_symbol_name == "::new_v1_formatted" { - self.fmt_args_new_fn_ids - .borrow_mut() - .insert(fn_id, FmtArgsCtor::NewV1FormattedDynamic); - } if demangled_symbol_name == "::from_str" || demangled_symbol_name == "::from_str_nonconst" { diff --git a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs index 9dae3d7d165..91a319b9bdf 100644 --- a/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs +++ b/crates/rustc_codegen_spirv/src/codegen_cx/mod.rs @@ -53,15 +53,6 @@ pub enum FmtArgsCtor { template_len: usize, rt_args_count: usize, }, - /// `Arguments::new_const(pieces)`. - NewConst { pieces_len: usize }, - /// `Arguments::new_v1(pieces, args)`. - NewV1 { - pieces_len: usize, - rt_args_count: usize, - }, - /// `Arguments::new_v1_formatted(..)` where lengths come from call operands. - NewV1FormattedDynamic, /// `Arguments::from_str(s)` / `Arguments::from_str_nonconst(s)`. FromStr, } @@ -94,8 +85,7 @@ pub struct CodegenCx<'tcx> { /// of these lang items, which we always replace with an "abort". pub panic_entry_points: RefCell>, - /// `core::fmt::Arguments::{new,new_const,new_v1,new_v1_formatted,from_str,from_str_nonconst}` - /// instances (for Rust 2021 panics). + /// `core::fmt::Arguments::{new,from_str,from_str_nonconst}` instances. pub fmt_args_new_fn_ids: RefCell>, /// `core::fmt::rt::Argument::new_*::` instances (for panics' `format_args!`), From 4181e6f780954562fb4ad5ec3f9b33cb58fd02f7 Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 15 Oct 2026 18:57:07 +0300 Subject: [PATCH 3/4] format_args_decompiler: remove other now-unused handling. --- .../src/builder/format_args_decompiler.rs | 245 ++---------------- 1 file changed, 28 insertions(+), 217 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs index cde3fd545fc..a84c089fcb5 100644 --- a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs +++ b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs @@ -3,13 +3,11 @@ use crate::builder::Builder; use crate::builder_spirv::{SpirvConst, SpirvValue, SpirvValueExt, SpirvValueKind}; use crate::codegen_cx::FmtArgsCtor; use crate::custom_insts::CustomOp; -use crate::spirv_type::SpirvType; use either::Either; use itertools::Itertools; use rspirv::dr::Operand; use rspirv::spirv::{Op, Word}; use rustc_abi::BackendRepr; -use rustc_data_structures::fx::FxHashSet; use rustc_middle::ty::Ty; use rustc_middle::ty::layout::LayoutOf; use smallvec::SmallVec; @@ -87,20 +85,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { } None }; - let array_len_from_ptr_type = |ptr_ty_id: Word| { - let pointee_ty_id = match cx.lookup_type(ptr_ty_id) { - SpirvType::Pointer { pointee } => pointee, - _ => return None, - }; - let count = match cx.lookup_type(pointee_ty_id) { - SpirvType::Array { count, .. } => count, - _ => return None, - }; - match count.kind { - SpirvValueKind::Def(count_id) => const_u32_as_usize(count_id), - _ => None, - } - }; let const_slice_as_u8s = |ptr_id: Word, len: usize| { const_slice_as_elem_ids(ptr_id, len)? .iter() @@ -209,53 +193,19 @@ impl<'tcx> DecodedFormatArgs<'tcx> { } } - // Newer `core::fmt::Arguments` can be scalarized at the panic call-site - // (`template: *const u8`, `args: *const fmt::rt::Argument`), instead of - // being passed as one aggregate value. - let split_fmt_args = match *args { + let (template_id, rt_args_ptr_id) = match *args { [template, rt_args_or_tagged_len, ref trailing @ ..] - if trailing.len() <= 3 - && matches!(cx.lookup_type(template.ty), SpirvType::Pointer { .. }) - && matches!( - cx.lookup_type(rt_args_or_tagged_len.ty), - SpirvType::Pointer { .. } | SpirvType::Integer(..) - ) + if trailing.len() <= 2 && let (Some(template_id), Some(rt_args_or_tagged_len_id)) = - (value_id(template), value_id(rt_args_or_tagged_len)) - && const_str_as_utf8(&[template_id, rt_args_or_tagged_len_id]).is_none() => + (value_id(template), value_id(rt_args_or_tagged_len)) => { - let looks_like_bool = matches!( - cx.builder.lookup_const_by_id(rt_args_or_tagged_len_id), - Some(SpirvConst::Scalar(0 | 1)) - ); - if trailing.len() == 1 && looks_like_bool { - None - } else { - Some(( - template_id, - template.ty, - rt_args_or_tagged_len_id, - rt_args_or_tagged_len.ty, - )) - } + (template_id, rt_args_or_tagged_len_id) } - _ => None, - }; - - let format_args_id = if split_fmt_args.is_none() { - match *args { - // HACK(eddyb) `panic_nounwind_fmt` takes an extra argument. - [format_args, ref trailing @ ..] if trailing.len() <= 3 => value_id(format_args) - .ok_or_else(|| FormatArgsNotRecognized("panic entry-point call args".into()))?, - - _ => { - return Err(FormatArgsNotRecognized( - "panic entry-point call args".into(), - )); - } + _ => { + return Err(FormatArgsNotRecognized( + "panic entry-point call args".into(), + )); } - } else { - 0 }; let custom_ext_inst_set_import = builder.ext_inst.borrow_mut().import_custom(builder); @@ -270,21 +220,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { let block_idx = builder.selected_block().unwrap(); let func = &mut builder.module_mut().functions[func_idx]; - // HACK(eddyb) this is used to check that all `Op{Store,Load}`s - // that may get removed, operate on local `OpVariable`s, - // i.e. are not externally observable. - let local_var_ids: FxHashSet<_> = func.blocks[0] - .instructions - .iter() - .take_while(|inst| inst.class.opcode == Op::Variable) - .map(|inst| inst.result_id.unwrap()) - .collect(); - let require_local_var = |ptr_id, var| { - Some(()) - .filter(|()| local_var_ids.contains(&ptr_id)) - .ok_or_else(|| FormatArgsNotRecognized(format!("{var} storage not local"))) - }; - let mut non_debug_insts = func.blocks[block_idx] .instructions .iter() @@ -304,7 +239,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { enum Inst { Bitcast(ID, ID), CompositeExtract(ID, ID, u32), - CompositeInsert(ID, ID, ID, u32), InBoundsAccessChain(ID, ID, u32), InBoundsAccessChain2(ID, ID, u32, u32), Store(ID, ID), @@ -351,18 +285,6 @@ impl<'tcx> DecodedFormatArgs<'tcx> { { return Some(Inst::CompositeExtract(r, x, i)); } - if inst.class.opcode == Op::CompositeInsert - && let ( - Some(r), - &[ - Operand::IdRef(inserted), - Operand::IdRef(base), - Operand::LiteralBit32(i), - ], - ) = (inst.result_id, &inst.operands[..]) - { - return Some(Inst::CompositeInsert(r, inserted, base, i)); - } // HACK(eddyb) all instructions accepted below // are expected to take no more than 4 operands, @@ -419,143 +341,31 @@ impl<'tcx> DecodedFormatArgs<'tcx> { FormatArgsNotRecognized("fmt::Arguments::new callee not registered".into()) }) }; - let (ctor, call_args_storage) = - if let Some((template_id, template_ty_id, rt_args_ptr_id, rt_args_ptr_ty_id)) = - split_fmt_args - { - if let (Some(template_len), Some(rt_args_count)) = ( - const_ptr_to_composite_len(template_id) - .or_else(|| array_len_from_ptr_type(template_ty_id)), - const_ptr_to_composite_len(rt_args_ptr_id) - .or_else(|| array_len_from_ptr_type(rt_args_ptr_ty_id)), - ) { - ( - FmtArgsCtor::NewTemplate { - template_len, - rt_args_count, - }, - SmallVec::<[Word; 8]>::from_slice(&[template_id, rt_args_ptr_id]), - ) - } else if let Some(&[Inst::Call(_, callee_id, ref call_args)]) = - try_rev_take(-1).as_deref() - && call_args.len() == 2 - && [call_args[0], call_args[1]] == [template_id, rt_args_ptr_id] - { - // Consume the matched call instruction. - try_rev_take(1).unwrap(); - ( - lookup_fmt_args_ctor(callee_id)?, - SmallVec::<[Word; 8]>::from_slice(&[template_id, rt_args_ptr_id]), - ) - } else if let Some( - &[ - Inst::Call(call_ret_id, callee_id, ref call_args), - Inst::CompositeExtract(extracted0, from0, 0), - Inst::CompositeExtract(extracted1, from1, 1), - ], - ) = try_rev_take(-3).as_deref() - && [from0, from1] == [call_ret_id; 2] - && [extracted0, extracted1] == [template_id, rt_args_ptr_id] - { - // Newer rustc, since `BackendRepr::ScalarPair` args are no - // longer forced to `PassMode::Direct`, returns the whole - // `fmt::Arguments` from its `new_*` constructor as a scalar - // pair, and splits it (via `OpCompositeExtract`s) into the - // two scalar values passed to the panic entry-point. - // - // The constructor's own arguments (i.e. `pieces`/`template` - // and the `rt::Argument` slice pointers) still carry the - // recoverable const data, so use those, like the aggregate - // (non-split) `Call`+`extract`+`insert` case does below. - let call_args_storage = call_args.iter().copied().collect(); - // Consume the matched call + both `OpCompositeExtract`s. - try_rev_take(3).unwrap(); - (lookup_fmt_args_ctor(callee_id)?, call_args_storage) - } else { - // HACK(eddyb) this gathers more context before reporting. - let mut insts = try_rev_take(0).unwrap(); - insts.reverse(); - while let Some(extra_inst) = try_rev_take(1) { - insts.extend(extra_inst); - if insts.len() >= 32 { - break; - } - } - insts.reverse(); - - return Err(FormatArgsNotRecognized(format!( - "fmt::Arguments::new call sequence ({insts:?})", - ))); - } + let (ctor, call_args_storage) = { + if let (Some(template_len), Some(rt_args_count)) = ( + const_ptr_to_composite_len(template_id), + const_ptr_to_composite_len(rt_args_ptr_id), + ) { + ( + FmtArgsCtor::NewTemplate { + template_len, + rt_args_count, + }, + SmallVec::<[Word; 8]>::from_slice(&[template_id, rt_args_ptr_id]), + ) } else { - // Newer rustc can pass the `fmt::Arguments::new_*` result directly to - // panic entry points (single trailing call), while older versions go - // through a local temporary (`Call -> Store -> Load`). - let fmt_args_new_call_inst_count = if matches!( - try_rev_take(-3).as_deref(), - Some(&[Inst::Call(_, _, _), Inst::Store(_, _), Inst::Load(_, _)]) - ) { - 3 - } else if let Some(&[Inst::Call(call_ret_id, callee_id, _)]) = - try_rev_take(-1).as_deref() - && (call_ret_id == format_args_id - || cx.fmt_args_new_fn_ids.borrow().contains_key(&callee_id)) - { - 1 - } else if matches!( - try_rev_take(-5).as_deref(), - Some(&[ - Inst::Call(call_ret_id, _, _), - Inst::CompositeExtract(extracted0, from0, 0), - Inst::CompositeExtract(extracted1, from1, 1), - Inst::CompositeInsert(inserted0, value0, _, 0), - Inst::CompositeInsert(inserted1, value1, inserted0_prev, 1), - ]) if [from0, from1] == [call_ret_id; 2] - && [value0, value1] == [extracted0, extracted1] - && inserted0 == inserted0_prev - && inserted1 == format_args_id - ) { - 5 - } else { - // HACK(eddyb) this reaches the `return Err` below. - 0 - }; - let fmt_args_new_call_insts = try_rev_take(fmt_args_new_call_inst_count).unwrap(); + let fmt_args_new_call_insts = try_rev_take(3).ok_or_else(|| { + FormatArgsNotRecognized( + "fmt::Arguments::new call: ran out of instructions".into(), + ) + })?; match fmt_args_new_call_insts[..] { - [ - Inst::Call(call_ret_id, callee_id, ref call_args), - Inst::Store(st_dst_id, st_val_id), - Inst::Load(ld_val_id, ld_src_id), - ] if call_ret_id == st_val_id - && st_dst_id == ld_src_id - && ld_val_id == format_args_id => - { - require_local_var(st_dst_id, "fmt::Arguments::new destination")?; - - ( - lookup_fmt_args_ctor(callee_id)?, - call_args.iter().copied().collect(), - ) - } - [Inst::Call(call_ret_id, callee_id, ref call_args)] - if call_ret_id == format_args_id - || cx.fmt_args_new_fn_ids.borrow().contains_key(&callee_id) => - { - ( - lookup_fmt_args_ctor(callee_id)?, - call_args.iter().copied().collect(), - ) - } [ Inst::Call(call_ret_id, callee_id, ref call_args), Inst::CompositeExtract(extracted0, from0, 0), Inst::CompositeExtract(extracted1, from1, 1), - Inst::CompositeInsert(inserted0, value0, _, 0), - Inst::CompositeInsert(inserted1, value1, inserted0_prev, 1), ] if [from0, from1] == [call_ret_id; 2] - && [value0, value1] == [extracted0, extracted1] - && inserted0 == inserted0_prev - && inserted1 == format_args_id => + && [extracted0, extracted1] == [template_id, rt_args_ptr_id] => { ( lookup_fmt_args_ctor(callee_id)?, @@ -579,7 +389,8 @@ impl<'tcx> DecodedFormatArgs<'tcx> { ))); } } - }; + } + }; let call_args = call_args_storage.as_slice(); enum PiecesSource { EncodedTemplate { ptr_id: Word, len: usize }, From 2445e7c47da94e224d3f3b432e9dd9e034e323bb Mon Sep 17 00:00:00 2001 From: Eduard-Mihai Burtescu Date: Thu, 15 Oct 2026 21:26:25 +0300 Subject: [PATCH 4/4] format_args_decompiler: localized refactors to reduce diffs from older Rust-GPU. --- .../src/builder/format_args_decompiler.rs | 167 ++++++++---------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs index a84c089fcb5..61701302fb9 100644 --- a/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs +++ b/crates/rustc_codegen_spirv/src/builder/format_args_decompiler.rs @@ -332,97 +332,90 @@ impl<'tcx> DecodedFormatArgs<'tcx> { insts.reverse(); Some(insts) }; - let lookup_fmt_args_ctor = |callee_id| { - cx.fmt_args_new_fn_ids - .borrow() - .get(&callee_id) - .copied() - .ok_or_else(|| { - FormatArgsNotRecognized("fmt::Arguments::new callee not registered".into()) - }) - }; - let (ctor, call_args_storage) = { - if let (Some(template_len), Some(rt_args_count)) = ( - const_ptr_to_composite_len(template_id), - const_ptr_to_composite_len(rt_args_ptr_id), - ) { - ( - FmtArgsCtor::NewTemplate { - template_len, - rt_args_count, - }, - SmallVec::<[Word; 8]>::from_slice(&[template_id, rt_args_ptr_id]), - ) - } else { - let fmt_args_new_call_insts = try_rev_take(3).ok_or_else(|| { - FormatArgsNotRecognized( - "fmt::Arguments::new call: ran out of instructions".into(), - ) - })?; - match fmt_args_new_call_insts[..] { - [ - Inst::Call(call_ret_id, callee_id, ref call_args), - Inst::CompositeExtract(extracted0, from0, 0), - Inst::CompositeExtract(extracted1, from1, 1), - ] if [from0, from1] == [call_ret_id; 2] - && [extracted0, extracted1] == [template_id, rt_args_ptr_id] => - { - ( - lookup_fmt_args_ctor(callee_id)?, - call_args.iter().copied().collect(), - ) - } - _ => { - // HACK(eddyb) this gathers more context before reporting. - let mut insts = fmt_args_new_call_insts; - insts.reverse(); - while let Some(extra_inst) = try_rev_take(1) { - insts.extend(extra_inst); - if insts.len() >= 32 { - break; - } - } - insts.reverse(); - return Err(FormatArgsNotRecognized(format!( - "fmt::Arguments::new call sequence ({insts:?})", - ))); - } - } - } - }; - let call_args = call_args_storage.as_slice(); enum PiecesSource { EncodedTemplate { ptr_id: Word, len: usize }, DirectConstStr([Word; 2]), } - let mut template_placeholder_info = None; - let (pieces_source, (rt_args_slice_ptr_id, rt_args_count)) = match (ctor, call_args) { - // `::new` + + let (pieces_source, (rt_args_slice_ptr_id, rt_args_count)) = if let ( + Some(template_len), + Some(rt_args_count), + ) = ( + const_ptr_to_composite_len(template_id), + const_ptr_to_composite_len(rt_args_ptr_id), + ) { ( - FmtArgsCtor::NewTemplate { - template_len, - rt_args_count, - }, - &[template_ptr_id, rt_args_slice_ptr_id], - ) => ( PiecesSource::EncodedTemplate { - ptr_id: template_ptr_id, + ptr_id: template_id, len: template_len, }, - (Some(rt_args_slice_ptr_id), rt_args_count), - ), + (Some(rt_args_ptr_id), rt_args_count), + ) + } else { + let fmt_args_new_call_insts = try_rev_take(3).ok_or_else(|| { + FormatArgsNotRecognized("fmt::Arguments::new call: ran out of instructions".into()) + })?; + match fmt_args_new_call_insts[..] { + [ + Inst::Call(call_ret_id, callee_id, ref call_args), + Inst::CompositeExtract(extracted0, from0, 0), + Inst::CompositeExtract(extracted1, from1, 1), + ] if [from0, from1] == [call_ret_id; 2] + && [extracted0, extracted1] == [template_id, rt_args_ptr_id] => + { + let Some(&fmt_args_ctor) = cx.fmt_args_new_fn_ids.borrow().get(&callee_id) + else { + return Err(FormatArgsNotRecognized( + "fmt::Arguments::new callee not registered".into(), + )); + }; - // `::from_str` - (FmtArgsCtor::FromStr, &[str_ptr_id, str_len_id]) => ( - PiecesSource::DirectConstStr([str_ptr_id, str_len_id]), - (None, 0), - ), + match (fmt_args_ctor, &call_args[..]) { + // `::new` + ( + FmtArgsCtor::NewTemplate { + template_len, + rt_args_count, + }, + &[template_ptr_id, rt_args_slice_ptr_id], + ) => ( + PiecesSource::EncodedTemplate { + ptr_id: template_ptr_id, + len: template_len, + }, + (Some(rt_args_slice_ptr_id), rt_args_count), + ), - _ => { - return Err(FormatArgsNotRecognized( - "fmt::Arguments::new ctor/call-args mismatch".into(), - )); + // `::from_str` + (FmtArgsCtor::FromStr, &[str_ptr_id, str_len_id]) => ( + PiecesSource::DirectConstStr([str_ptr_id, str_len_id]), + (None, 0), + ), + + _ => { + return Err(FormatArgsNotRecognized( + "fmt::Arguments::new call args".into(), + )); + } + } + } + _ => { + // HACK(eddyb) this gathers more context before reporting. + let mut insts = fmt_args_new_call_insts; + insts.reverse(); + while let Some(extra_inst) = try_rev_take(1) { + insts.extend(extra_inst); + if insts.len() >= 32 { + break; + } + } + insts.reverse(); + + return Err(FormatArgsNotRecognized(format!( + "fmt::Arguments::new call sequence ({insts:?})", + ))); + } } }; @@ -620,20 +613,16 @@ impl<'tcx> DecodedFormatArgs<'tcx> { len: template_len, } => parse_encoded_template(template_ptr_id, template_len).map( |(pieces, placeholder_count, placeholders_map_1_to_1)| { - template_placeholder_info = Some((placeholder_count, placeholders_map_1_to_1)); + let decoded_arg_count = decoded_format_args.ref_arg_ids_with_ty_and_spec.len(); + if !placeholders_map_1_to_1 || placeholder_count != decoded_arg_count { + decoded_format_args.has_unknown_fmt_placeholder_to_args_mapping = + Some(placeholder_count); + } pieces }, ), }; - if let Some((placeholder_count, placeholders_map_1_to_1)) = template_placeholder_info { - let decoded_arg_count = decoded_format_args.ref_arg_ids_with_ty_and_spec.len(); - if !placeholders_map_1_to_1 || placeholder_count != decoded_arg_count { - decoded_format_args.has_unknown_fmt_placeholder_to_args_mapping = - Some(placeholder_count); - } - } - // Keep all instructions up to (but not including) the last one // confirmed above to be the first instruction of `format_args!`. func.blocks[block_idx]