@@ -3284,10 +3284,25 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3284
3284
3285
3285
// Take `count` instructions, advancing backwards, but returning
3286
3286
// instructions in their original order (and decoded to `Inst`s).
3287
- let mut try_rev_take = |count| {
3287
+ let mut try_rev_take = |count : isize | {
3288
+ // HACK(eddyb) this is extremely silly but it's easier to do
3289
+ // this than to rely on `Iterator::peekable` or anything else,
3290
+ // lower down this file, without messing up the state here.
3291
+ let is_peek = count < 0 ;
3292
+ let count = count. unsigned_abs ( ) ;
3293
+
3294
+ let mut non_debug_insts_for_peek = is_peek. then ( || non_debug_insts. clone ( ) ) ;
3295
+ let non_debug_insts = non_debug_insts_for_peek
3296
+ . as_mut ( )
3297
+ . unwrap_or ( & mut non_debug_insts) ;
3298
+
3299
+ // FIXME(eddyb) there might be an easier way to do this,
3300
+ // e.g. maybe `map_while` + post-`collect` length check?
3288
3301
let maybe_rev_insts = ( 0 ..count) . map ( |_| {
3289
3302
let ( i, inst) = non_debug_insts. next_back ( ) ?;
3290
- taken_inst_idx_range. start . set ( i) ;
3303
+ if !is_peek {
3304
+ taken_inst_idx_range. start . set ( i) ;
3305
+ }
3291
3306
3292
3307
// HACK(eddyb) avoid the logic below that assumes only ID operands
3293
3308
if inst. class . opcode == Op :: CompositeExtract {
@@ -3551,15 +3566,21 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3551
3566
}
3552
3567
} ) . collect :: < Result < _ , _ > > ( ) ?;
3553
3568
3554
- let rev_ref_arg_ids_with_ty_and_spec = ( rev_copies_to_rt_args_array_src_ptrs
3555
- . into_iter ( ) )
3556
- . map ( |copy_to_rt_args_array_src_ptr| {
3569
+ // HACK(eddyb) sometimes there is an extra tuple of refs,
3570
+ // nowadays, but MIR opts mean it's not always guaranteed,
3571
+ // hopefully it's always uniform across all the arguments.
3572
+ let mut maybe_ref_args_tmp_slot_ptr = None ;
3573
+
3574
+ let rev_maybe_ref_arg_ids_with_ty_and_spec = ( ( 0 ..rt_args_count)
3575
+ . rev ( )
3576
+ . zip_eq ( rev_copies_to_rt_args_array_src_ptrs) )
3577
+ . map ( |( rt_arg_idx, copy_to_rt_args_array_src_ptr) | {
3557
3578
let rt_arg_new_call_insts = try_rev_take ( 4 ) . ok_or_else ( || {
3558
3579
FormatArgsNotRecognized (
3559
3580
"fmt::rt::Argument::new call: ran out of instructions" . into ( ) ,
3560
3581
)
3561
3582
} ) ?;
3562
- match rt_arg_new_call_insts[ ..] {
3583
+ let ( ref_arg_id , ty , spec ) = match rt_arg_new_call_insts[ ..] {
3563
3584
[
3564
3585
Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
3565
3586
Inst :: InBoundsAccessChain ( tmp_slot_field_ptr, tmp_slot_ptr, 0 ) ,
@@ -3583,36 +3604,136 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3583
3604
FormatArgsNotRecognized ( format ! (
3584
3605
"fmt::rt::Argument::new call sequence ({rt_arg_new_call_insts:?})"
3585
3606
) )
3586
- } )
3607
+ } ) ?;
3608
+
3609
+ // HACK(eddyb) `0` (an invalid ID) is later used as a
3610
+ // placeholder (see also `maybe_ref_args_tmp_slot_ptr`).
3611
+ assert_ne ! ( ref_arg_id, 0 ) ;
3612
+
3613
+ // HACK(eddyb) `try_rev_take(-2)` is "peeking", not taking.
3614
+ let maybe_ref_args_tuple_load_insts = try_rev_take ( -2 ) ;
3615
+ let maybe_ref_arg_id = match maybe_ref_args_tuple_load_insts. as_deref ( ) {
3616
+ Some (
3617
+ & [
3618
+ Inst :: InBoundsAccessChain ( field_ptr, base_ptr, field_idx) ,
3619
+ Inst :: Load ( ld_val, ld_src_ptr) ,
3620
+ ] ,
3621
+ ) if maybe_ref_args_tmp_slot_ptr
3622
+ . is_none_or ( |expected| base_ptr == expected)
3623
+ && field_idx as usize == rt_arg_idx
3624
+ && ( ld_val, ld_src_ptr) == ( ref_arg_id, field_ptr) =>
3625
+ {
3626
+ // HACK(eddyb) consume the peeked instructions.
3627
+ try_rev_take ( 2 ) . unwrap ( ) ;
3628
+
3629
+ maybe_ref_args_tmp_slot_ptr = Some ( base_ptr) ;
3630
+
3631
+ // HACK(eddyb) using `0` (an invalid ID) as a
3632
+ // placeholder to require further processing.
3633
+ 0
3634
+ }
3635
+ _ => ref_arg_id,
3636
+ } ;
3637
+
3638
+ Ok ( ( maybe_ref_arg_id, ty, spec) )
3587
3639
} )
3588
3640
. collect :: < Result < _ , _ > > ( ) ?;
3589
3641
3590
3642
decoded_format_args. ref_arg_ids_with_ty_and_spec =
3591
- rev_ref_arg_ids_with_ty_and_spec ;
3643
+ rev_maybe_ref_arg_ids_with_ty_and_spec ;
3592
3644
decoded_format_args. ref_arg_ids_with_ty_and_spec . reverse ( ) ;
3645
+
3646
+ // HACK(eddyb) see above for context regarding the use of
3647
+ // `0` as placeholders and `maybe_ref_args_tmp_slot_ptr`.
3648
+ if let Some ( ref_args_tmp_slot_ptr) = maybe_ref_args_tmp_slot_ptr {
3649
+ for ( rt_arg_idx, ( maybe_ref_arg_id, ..) ) in decoded_format_args
3650
+ . ref_arg_ids_with_ty_and_spec
3651
+ . iter_mut ( )
3652
+ . enumerate ( )
3653
+ . rev ( )
3654
+ {
3655
+ if * maybe_ref_arg_id == 0 {
3656
+ let ref_arg_store_insts = try_rev_take ( 2 ) . ok_or_else ( || {
3657
+ FormatArgsNotRecognized (
3658
+ "fmt::rt::Argument::new argument store: ran out of instructions" . into ( ) ,
3659
+ )
3660
+ } ) ?;
3661
+
3662
+ * maybe_ref_arg_id = match ref_arg_store_insts[ ..] {
3663
+ [
3664
+ Inst :: InBoundsAccessChain ( field_ptr, base_ptr, field_idx) ,
3665
+ Inst :: Store ( st_dst_ptr, st_val) ,
3666
+ ] if base_ptr == ref_args_tmp_slot_ptr
3667
+ && field_idx as usize == rt_arg_idx
3668
+ && st_dst_ptr == field_ptr =>
3669
+ {
3670
+ Some ( st_val)
3671
+ }
3672
+ _ => None ,
3673
+ }
3674
+ . ok_or_else ( || {
3675
+ FormatArgsNotRecognized ( format ! (
3676
+ "fmt::rt::Argument::new argument store sequence ({ref_arg_store_insts:?})"
3677
+ ) )
3678
+ } ) ?;
3679
+ }
3680
+ }
3681
+ }
3593
3682
}
3594
3683
3595
3684
// If the `pieces: &[&str]` slice needs a bitcast, it'll be here.
3596
- let pieces_slice_ptr_id = match try_rev_take ( 1 ) . as_deref ( ) {
3597
- Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => in_id,
3685
+ // HACK(eddyb) `try_rev_take(-1)` is "peeking", not taking.
3686
+ let pieces_slice_ptr_id = match try_rev_take ( -1 ) . as_deref ( ) {
3687
+ Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => {
3688
+ // HACK(eddyb) consume the peeked instructions.
3689
+ try_rev_take ( 1 ) . unwrap ( ) ;
3690
+
3691
+ in_id
3692
+ }
3598
3693
_ => pieces_slice_ptr_id,
3599
3694
} ;
3600
3695
decoded_format_args. const_pieces =
3601
- const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) . and_then (
3602
- |piece_ids| {
3603
- piece_ids
3604
- . iter ( )
3605
- . map ( |& piece_id| {
3606
- match self . builder . lookup_const_by_id ( piece_id) ? {
3607
- SpirvConst :: Composite ( piece) => {
3608
- const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?)
3609
- }
3610
- _ => None ,
3696
+ match const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) {
3697
+ Some ( piece_ids) => piece_ids
3698
+ . iter ( )
3699
+ . map (
3700
+ |& piece_id| match self . builder . lookup_const_by_id ( piece_id) ? {
3701
+ SpirvConst :: Composite ( piece) => {
3702
+ const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?)
3611
3703
}
3612
- } )
3613
- . collect :: < Option < _ > > ( )
3704
+ _ => None ,
3705
+ } ,
3706
+ )
3707
+ . collect :: < Option < _ > > ( ) ,
3708
+ // HACK(eddyb) minor upstream blunder results in at
3709
+ // least one instance of a runtime `[&str; 1]` array,
3710
+ // see also this comment left on the responsible PR:
3711
+ // https://github.com/rust-lang/rust/pull/129658#discussion_r2181834781
3712
+ // HACK(eddyb) `try_rev_take(-5)` is "peeking", not taking.
3713
+ None if pieces_len == 1 => match try_rev_take ( -5 ) . as_deref ( ) {
3714
+ Some (
3715
+ & [
3716
+ Inst :: InBoundsAccessChain ( elem0_ptr, array_ptr, 0 ) ,
3717
+ Inst :: InBoundsAccessChain ( field0_ptr, base_ptr_0, 0 ) ,
3718
+ Inst :: Store ( st0_dst_ptr, st0_val) ,
3719
+ Inst :: InBoundsAccessChain ( field1_ptr, base_ptr_1, 1 ) ,
3720
+ Inst :: Store ( st1_dst_ptr, st1_val) ,
3721
+ ] ,
3722
+ ) if array_ptr == pieces_slice_ptr_id
3723
+ && [ base_ptr_0, base_ptr_1] == [ elem0_ptr; 2 ]
3724
+ && st0_dst_ptr == field0_ptr
3725
+ && st1_dst_ptr == field1_ptr =>
3726
+ {
3727
+ // HACK(eddyb) consume the peeked instructions.
3728
+ try_rev_take ( 5 ) . unwrap ( ) ;
3729
+
3730
+ const_str_as_utf8 ( & [ st0_val, st1_val] )
3731
+ . map ( |s| [ s] . into_iter ( ) . collect ( ) )
3732
+ }
3733
+ _ => None ,
3614
3734
} ,
3615
- ) ;
3735
+ None => None ,
3736
+ } ;
3616
3737
3617
3738
// Keep all instructions up to (but not including) the last one
3618
3739
// confirmed above to be the first instruction of `format_args!`.
0 commit comments