@@ -3398,10 +3398,25 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
33983398
33993399 // Take `count` instructions, advancing backwards, but returning
34003400 // instructions in their original order (and decoded to `Inst`s).
3401- let mut try_rev_take = |count| {
3401+ let mut try_rev_take = |count : isize | {
3402+ // HACK(eddyb) this is extremely silly but it's easier to do
3403+ // this than to rely on `Iterator::peekable` or anything else,
3404+ // lower down this file, without messing up the state here.
3405+ let is_peek = count < 0 ;
3406+ let count = count. unsigned_abs ( ) ;
3407+
3408+ let mut non_debug_insts_for_peek = is_peek. then ( || non_debug_insts. clone ( ) ) ;
3409+ let non_debug_insts = non_debug_insts_for_peek
3410+ . as_mut ( )
3411+ . unwrap_or ( & mut non_debug_insts) ;
3412+
3413+ // FIXME(eddyb) there might be an easier way to do this,
3414+ // e.g. maybe `map_while` + post-`collect` length check?
34023415 let maybe_rev_insts = ( 0 ..count) . map ( |_| {
34033416 let ( i, inst) = non_debug_insts. next_back ( ) ?;
3404- taken_inst_idx_range. start . set ( i) ;
3417+ if !is_peek {
3418+ taken_inst_idx_range. start . set ( i) ;
3419+ }
34053420
34063421 // HACK(eddyb) avoid the logic below that assumes only ID operands
34073422 if inst. class . opcode == Op :: CompositeExtract {
@@ -3552,15 +3567,21 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
35523567 }
35533568 } ) . collect :: < Result < _ , _ > > ( ) ?;
35543569
3555- let rev_ref_arg_ids_with_ty_and_spec = ( rev_copies_to_rt_args_array_src_ptrs
3556- . into_iter ( ) )
3557- . map ( |copy_to_rt_args_array_src_ptr| {
3570+ // HACK(eddyb) sometimes there is an extra tuple of refs,
3571+ // nowadays, but MIR opts mean it's not always guaranteed,
3572+ // hopefully it's always uniform across all the arguments.
3573+ let mut maybe_ref_args_tmp_slot_ptr = None ;
3574+
3575+ let rev_maybe_ref_arg_ids_with_ty_and_spec = ( ( 0 ..rt_args_count)
3576+ . rev ( )
3577+ . zip_eq ( rev_copies_to_rt_args_array_src_ptrs) )
3578+ . map ( |( rt_arg_idx, copy_to_rt_args_array_src_ptr) | {
35583579 let rt_arg_new_call_insts = try_rev_take ( 4 ) . ok_or_else ( || {
35593580 FormatArgsNotRecognized (
35603581 "fmt::rt::Argument::new call: ran out of instructions" . into ( ) ,
35613582 )
35623583 } ) ?;
3563- match rt_arg_new_call_insts[ ..] {
3584+ let ( ref_arg_id , ty , spec ) = match rt_arg_new_call_insts[ ..] {
35643585 [
35653586 Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
35663587 Inst :: InBoundsAccessChain ( tmp_slot_field_ptr, tmp_slot_ptr, 0 ) ,
@@ -3584,36 +3605,136 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
35843605 FormatArgsNotRecognized ( format ! (
35853606 "fmt::rt::Argument::new call sequence ({rt_arg_new_call_insts:?})"
35863607 ) )
3587- } )
3608+ } ) ?;
3609+
3610+ // HACK(eddyb) `0` (an invalid ID) is later used as a
3611+ // placeholder (see also `maybe_ref_args_tmp_slot_ptr`).
3612+ assert_ne ! ( ref_arg_id, 0 ) ;
3613+
3614+ // HACK(eddyb) `try_rev_take(-2)` is "peeking", not taking.
3615+ let maybe_ref_args_tuple_load_insts = try_rev_take ( -2 ) ;
3616+ let maybe_ref_arg_id = match maybe_ref_args_tuple_load_insts. as_deref ( ) {
3617+ Some (
3618+ & [
3619+ Inst :: InBoundsAccessChain ( field_ptr, base_ptr, field_idx) ,
3620+ Inst :: Load ( ld_val, ld_src_ptr) ,
3621+ ] ,
3622+ ) if maybe_ref_args_tmp_slot_ptr
3623+ . is_none_or ( |expected| base_ptr == expected)
3624+ && field_idx as usize == rt_arg_idx
3625+ && ( ld_val, ld_src_ptr) == ( ref_arg_id, field_ptr) =>
3626+ {
3627+ // HACK(eddyb) consume the peeked instructions.
3628+ try_rev_take ( 2 ) . unwrap ( ) ;
3629+
3630+ maybe_ref_args_tmp_slot_ptr = Some ( base_ptr) ;
3631+
3632+ // HACK(eddyb) using `0` (an invalid ID) as a
3633+ // placeholder to require further processing.
3634+ 0
3635+ }
3636+ _ => ref_arg_id,
3637+ } ;
3638+
3639+ Ok ( ( maybe_ref_arg_id, ty, spec) )
35883640 } )
35893641 . collect :: < Result < _ , _ > > ( ) ?;
35903642
35913643 decoded_format_args. ref_arg_ids_with_ty_and_spec =
3592- rev_ref_arg_ids_with_ty_and_spec ;
3644+ rev_maybe_ref_arg_ids_with_ty_and_spec ;
35933645 decoded_format_args. ref_arg_ids_with_ty_and_spec . reverse ( ) ;
3646+
3647+ // HACK(eddyb) see above for context regarding the use of
3648+ // `0` as placeholders and `maybe_ref_args_tmp_slot_ptr`.
3649+ if let Some ( ref_args_tmp_slot_ptr) = maybe_ref_args_tmp_slot_ptr {
3650+ for ( rt_arg_idx, ( maybe_ref_arg_id, ..) ) in decoded_format_args
3651+ . ref_arg_ids_with_ty_and_spec
3652+ . iter_mut ( )
3653+ . enumerate ( )
3654+ . rev ( )
3655+ {
3656+ if * maybe_ref_arg_id == 0 {
3657+ let ref_arg_store_insts = try_rev_take ( 2 ) . ok_or_else ( || {
3658+ FormatArgsNotRecognized (
3659+ "fmt::rt::Argument::new argument store: ran out of instructions" . into ( ) ,
3660+ )
3661+ } ) ?;
3662+
3663+ * maybe_ref_arg_id = match ref_arg_store_insts[ ..] {
3664+ [
3665+ Inst :: InBoundsAccessChain ( field_ptr, base_ptr, field_idx) ,
3666+ Inst :: Store ( st_dst_ptr, st_val) ,
3667+ ] if base_ptr == ref_args_tmp_slot_ptr
3668+ && field_idx as usize == rt_arg_idx
3669+ && st_dst_ptr == field_ptr =>
3670+ {
3671+ Some ( st_val)
3672+ }
3673+ _ => None ,
3674+ }
3675+ . ok_or_else ( || {
3676+ FormatArgsNotRecognized ( format ! (
3677+ "fmt::rt::Argument::new argument store sequence ({ref_arg_store_insts:?})"
3678+ ) )
3679+ } ) ?;
3680+ }
3681+ }
3682+ }
35943683 }
35953684
35963685 // If the `pieces: &[&str]` slice needs a bitcast, it'll be here.
3597- let pieces_slice_ptr_id = match try_rev_take ( 1 ) . as_deref ( ) {
3598- Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => in_id,
3686+ // HACK(eddyb) `try_rev_take(-1)` is "peeking", not taking.
3687+ let pieces_slice_ptr_id = match try_rev_take ( -1 ) . as_deref ( ) {
3688+ Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => {
3689+ // HACK(eddyb) consume the peeked instructions.
3690+ try_rev_take ( 1 ) . unwrap ( ) ;
3691+
3692+ in_id
3693+ }
35993694 _ => pieces_slice_ptr_id,
36003695 } ;
36013696 decoded_format_args. const_pieces =
3602- const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) . and_then (
3603- |piece_ids| {
3604- piece_ids
3605- . iter ( )
3606- . map ( |& piece_id| {
3607- match self . builder . lookup_const_by_id ( piece_id) ? {
3608- SpirvConst :: Composite ( piece) => {
3609- const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?)
3610- }
3611- _ => None ,
3697+ match const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) {
3698+ Some ( piece_ids) => piece_ids
3699+ . iter ( )
3700+ . map (
3701+ |& piece_id| match self . builder . lookup_const_by_id ( piece_id) ? {
3702+ SpirvConst :: Composite ( piece) => {
3703+ const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?)
36123704 }
3613- } )
3614- . collect :: < Option < _ > > ( )
3705+ _ => None ,
3706+ } ,
3707+ )
3708+ . collect :: < Option < _ > > ( ) ,
3709+ // HACK(eddyb) minor upstream blunder results in at
3710+ // least one instance of a runtime `[&str; 1]` array,
3711+ // see also this comment left on the responsible PR:
3712+ // https://github.com/rust-lang/rust/pull/129658#discussion_r2181834781
3713+ // HACK(eddyb) `try_rev_take(-5)` is "peeking", not taking.
3714+ None if pieces_len == 1 => match try_rev_take ( -5 ) . as_deref ( ) {
3715+ Some (
3716+ & [
3717+ Inst :: InBoundsAccessChain ( elem0_ptr, array_ptr, 0 ) ,
3718+ Inst :: InBoundsAccessChain ( field0_ptr, base_ptr_0, 0 ) ,
3719+ Inst :: Store ( st0_dst_ptr, st0_val) ,
3720+ Inst :: InBoundsAccessChain ( field1_ptr, base_ptr_1, 1 ) ,
3721+ Inst :: Store ( st1_dst_ptr, st1_val) ,
3722+ ] ,
3723+ ) if array_ptr == pieces_slice_ptr_id
3724+ && [ base_ptr_0, base_ptr_1] == [ elem0_ptr; 2 ]
3725+ && st0_dst_ptr == field0_ptr
3726+ && st1_dst_ptr == field1_ptr =>
3727+ {
3728+ // HACK(eddyb) consume the peeked instructions.
3729+ try_rev_take ( 5 ) . unwrap ( ) ;
3730+
3731+ const_str_as_utf8 ( & [ st0_val, st1_val] )
3732+ . map ( |s| [ s] . into_iter ( ) . collect ( ) )
3733+ }
3734+ _ => None ,
36153735 } ,
3616- ) ;
3736+ None => None ,
3737+ } ;
36173738
36183739 // Keep all instructions up to (but not including) the last one
36193740 // confirmed above to be the first instruction of `format_args!`.
0 commit comments