@@ -7,7 +7,7 @@ use crate::builder_spirv::{BuilderCursor, SpirvConst, SpirvValue, SpirvValueExt,
7
7
use crate :: codegen_cx:: CodegenCx ;
8
8
use crate :: custom_insts:: { CustomInst , CustomOp } ;
9
9
use crate :: spirv_type:: SpirvType ;
10
- use itertools:: Itertools ;
10
+ use itertools:: { Either , Itertools } ;
11
11
use rspirv:: dr:: { InsertPoint , Instruction , Operand } ;
12
12
use rspirv:: spirv:: { Capability , MemoryModel , MemorySemantics , Op , Scope , StorageClass , Word } ;
13
13
use rustc_apfloat:: { Float , Round , Status , ieee} ;
@@ -3230,6 +3230,16 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3230
3230
/// * `&a` with `typeof a` and ' ',
3231
3231
/// * `&b` with `typeof b` and 'x'
3232
3232
ref_arg_ids_with_ty_and_spec : SmallVec < [ ( Word , Ty < ' tcx > , char ) ; 2 ] > ,
3233
+
3234
+ /// If `fmt::Arguments::new_v1_formatted` was used, this holds
3235
+ /// the length of the `&[fmt::rt::Placeholder]` slice, which
3236
+ /// currently cannot be directly supported, and therefore even
3237
+ /// if all of `ref_arg_ids_with_ty_and_spec` are printable,
3238
+ /// a much jankier fallback still has to be used, as it it were:
3239
+ ///
3240
+ /// `format!("a{{0}}b{{1}}c\n with {{…}} from: {}, {}", x, y)`
3241
+ /// (w/ `const_pieces = ["a", "b", "c"]` & `ref_args = [&x, &y]`).
3242
+ has_unknown_fmt_placeholder_to_args_mapping : Option < usize > ,
3233
3243
}
3234
3244
struct FormatArgsNotRecognized ( String ) ;
3235
3245
@@ -3455,6 +3465,105 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3455
3465
} ;
3456
3466
3457
3467
match call_args[ ..] {
3468
+ // `<core::fmt::Arguments>::new_v1_formatted`
3469
+ //
3470
+ // HACK(eddyb) this isn't fully supported,
3471
+ // as that would require digging into unstable
3472
+ // internals of `core::fmt::rt::Placeholder`s,
3473
+ // but the whole call still needs to be removed,
3474
+ // and both const str pieces and runtime args
3475
+ // can still be printed (even if in jankier way).
3476
+ [
3477
+ pieces_slice_ptr_id,
3478
+ pieces_len_id,
3479
+ rt_args_slice_ptr_id,
3480
+ rt_args_len_id,
3481
+ _fmt_placeholders_slice_ptr_id,
3482
+ fmt_placeholders_len_id,
3483
+ ] if ( pieces_len, rt_args_count) == ( !0 , !0 ) => {
3484
+ let [ pieces_len, rt_args_len, fmt_placeholders_len] = match [
3485
+ pieces_len_id,
3486
+ rt_args_len_id,
3487
+ fmt_placeholders_len_id,
3488
+ ]
3489
+ . map ( const_u32_as_usize)
3490
+ {
3491
+ [ Some ( a) , Some ( b) , Some ( c) ] => [ a, b, c] ,
3492
+ _ => {
3493
+ return Err ( FormatArgsNotRecognized (
3494
+ "fmt::Arguments::new_v1_formatted \
3495
+ with dynamic lengths"
3496
+ . into ( ) ,
3497
+ ) ) ;
3498
+ }
3499
+ } ;
3500
+
3501
+ // FIXME(eddyb) simplify the logic below after
3502
+ // https://github.com/rust-lang/rust/pull/139131
3503
+ // (~1.88) as it makes `&[rt::Placeholder]`
3504
+ // constant (via promotion to 'static).
3505
+
3506
+ // HACK(eddyb) this accounts for all of these:
3507
+ // - `rt::Placeholder` copies into array: 2 insts each
3508
+ // - `rt::UnsafeArg::new()` call: 1 inst
3509
+ // - runtime args array->slice ptr cast: 1 inst
3510
+ // - placeholders array->slice ptr cast: 1 inst
3511
+ let extra_insts = try_rev_take ( 3 + fmt_placeholders_len * 2 ) . ok_or_else ( || {
3512
+ FormatArgsNotRecognized (
3513
+ "fmt::Arguments::new_v1_formatted call: ran out of instructions" . into ( ) ,
3514
+ )
3515
+ } ) ?;
3516
+ let rt_args_slice_ptr_id = match extra_insts[ ..] {
3517
+ [ .., Inst :: Bitcast ( out_id, in_id) , Inst :: Bitcast ( ..) ]
3518
+ if out_id == rt_args_slice_ptr_id =>
3519
+ {
3520
+ in_id
3521
+ }
3522
+ _ => {
3523
+ let mut insts = extra_insts;
3524
+ insts. extend ( fmt_args_new_call_insts) ;
3525
+ return Err ( FormatArgsNotRecognized ( format ! (
3526
+ "fmt::Arguments::new_v1_formatted call sequence ({insts:?})" ,
3527
+ ) ) ) ;
3528
+ }
3529
+ } ;
3530
+
3531
+ // HACK(eddyb) even worse, each call made to
3532
+ // `rt::Placeholder::new(...)` takes anywhere
3533
+ // between 16 and 20 instructions each, due
3534
+ // to `enum`s represented as scalar pairs.
3535
+ for _ in 0 ..fmt_placeholders_len {
3536
+ try_rev_take ( 16 ) . and_then ( |insts| {
3537
+ let scalar_pairs_with_used_2nd_field = insts
3538
+ . iter ( )
3539
+ . take_while ( |inst| {
3540
+ !matches ! ( inst, Inst :: Load ( ..) )
3541
+ } )
3542
+ . filter ( |inst| {
3543
+ matches ! ( inst, Inst :: InBoundsAccessChain ( .., 1 ) )
3544
+ } )
3545
+ . count ( ) ;
3546
+ try_rev_take ( scalar_pairs_with_used_2nd_field * 2 ) ?;
3547
+ Some ( ( ) )
3548
+ } )
3549
+ . ok_or_else ( || {
3550
+ FormatArgsNotRecognized (
3551
+ "fmt::rt::Placeholder::new call: ran out of instructions"
3552
+ . into ( ) ,
3553
+ )
3554
+ } ) ?;
3555
+ }
3556
+
3557
+ decoded_format_args
3558
+ . has_unknown_fmt_placeholder_to_args_mapping =
3559
+ Some ( fmt_placeholders_len) ;
3560
+
3561
+ (
3562
+ ( pieces_slice_ptr_id, pieces_len) ,
3563
+ ( Some ( rt_args_slice_ptr_id) , rt_args_len) ,
3564
+ )
3565
+ }
3566
+
3458
3567
// `<core::fmt::Arguments>::new_v1`
3459
3568
[ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
3460
3569
( pieces_slice_ptr_id, pieces_len) ,
@@ -3608,58 +3717,97 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
3608
3717
let mut debug_printf_args = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
3609
3718
let message = match try_decode_and_remove_format_args ( ) {
3610
3719
Ok ( DecodedFormatArgs {
3611
- const_pieces,
3720
+ const_pieces : None , ..
3721
+ } ) => "<unknown message>" . into ( ) ,
3722
+
3723
+ Ok ( DecodedFormatArgs {
3724
+ const_pieces : Some ( const_pieces) ,
3612
3725
ref_arg_ids_with_ty_and_spec,
3726
+ has_unknown_fmt_placeholder_to_args_mapping,
3613
3727
} ) => {
3614
- match const_pieces {
3615
- Some ( const_pieces) => {
3616
- const_pieces
3617
- . into_iter ( )
3618
- . map ( |s| Cow :: Owned ( s. replace ( '%' , "%%" ) ) )
3619
- . interleave ( ref_arg_ids_with_ty_and_spec. iter ( ) . map (
3620
- |& ( ref_id, ty, spec) | {
3621
- use rustc_target:: abi:: {
3622
- Float :: * , Integer :: * , Primitive :: * ,
3623
- } ;
3624
-
3625
- let layout = self . layout_of ( ty) ;
3626
-
3627
- let scalar = match layout. backend_repr {
3628
- BackendRepr :: Scalar ( scalar) => Some ( scalar. primitive ( ) ) ,
3629
- _ => None ,
3630
- } ;
3631
- let debug_printf_fmt = match ( spec, scalar) {
3632
- // FIXME(eddyb) support more of these,
3633
- // potentially recursing to print ADTs.
3634
- ( ' ' | '?' , Some ( Int ( I32 , false ) ) ) => "%u" ,
3635
- ( 'x' , Some ( Int ( I32 , false ) ) ) => "%x" ,
3636
- ( ' ' | '?' , Some ( Int ( I32 , true ) ) ) => "%i" ,
3637
- ( ' ' | '?' , Some ( Float ( F32 ) ) ) => "%f" ,
3638
-
3639
- _ => "" ,
3640
- } ;
3641
-
3642
- if debug_printf_fmt. is_empty ( ) {
3643
- return Cow :: Owned (
3644
- format ! ( "{{/* unprintable {ty} */:{spec}}}" )
3645
- . replace ( '%' , "%%" ) ,
3646
- ) ;
3647
- }
3728
+ let args = ref_arg_ids_with_ty_and_spec
3729
+ . iter ( )
3730
+ . map ( |& ( ref_id, ty, spec) | {
3731
+ use rustc_target:: abi:: { Float :: * , Integer :: * , Primitive :: * } ;
3648
3732
3649
- let spirv_type = layout. spirv_type ( self . span ( ) , self ) ;
3650
- debug_printf_args. push (
3651
- self . emit ( )
3652
- . load ( spirv_type, None , ref_id, None , [ ] )
3653
- . unwrap ( )
3654
- . with_type ( spirv_type) ,
3655
- ) ;
3656
- Cow :: Borrowed ( debug_printf_fmt)
3657
- } ,
3658
- ) )
3659
- . collect :: < String > ( )
3660
- }
3661
- None => "<unknown message>" . into ( ) ,
3662
- }
3733
+ let layout = self . layout_of ( ty) ;
3734
+
3735
+ let scalar = match layout. backend_repr {
3736
+ BackendRepr :: Scalar ( scalar) => Some ( scalar. primitive ( ) ) ,
3737
+ _ => None ,
3738
+ } ;
3739
+ let debug_printf_fmt = match ( spec, scalar) {
3740
+ // FIXME(eddyb) support more of these,
3741
+ // potentially recursing to print ADTs.
3742
+ ( ' ' | '?' , Some ( Int ( I32 , false ) ) ) => "%u" ,
3743
+ ( 'x' , Some ( Int ( I32 , false ) ) ) => "%x" ,
3744
+ ( ' ' | '?' , Some ( Int ( I32 , true ) ) ) => "%i" ,
3745
+ ( ' ' | '?' , Some ( Float ( F32 ) ) ) => "%f" ,
3746
+
3747
+ _ => "" ,
3748
+ } ;
3749
+
3750
+ if debug_printf_fmt. is_empty ( ) {
3751
+ return Cow :: Owned (
3752
+ format ! ( "{{/* unprintable {ty} */:{spec}}}" ) . replace ( '%' , "%%" ) ,
3753
+ ) ;
3754
+ }
3755
+
3756
+ let spirv_type = layout. spirv_type ( self . span ( ) , self ) ;
3757
+ debug_printf_args. push (
3758
+ self . emit ( )
3759
+ . load ( spirv_type, None , ref_id, None , [ ] )
3760
+ . unwrap ( )
3761
+ . with_type ( spirv_type) ,
3762
+ ) ;
3763
+ Cow :: Borrowed ( debug_printf_fmt)
3764
+ } ) ;
3765
+
3766
+ // HACK(eddyb) due to `fmt::Arguments::new_v1_formatted`,
3767
+ // we can't always assume that all the formatting arguments
3768
+ // are used 1:1 as placeholders (i.e. between `const_pieces`).
3769
+ let ( placeholder_count, placeholders_are_args) =
3770
+ match has_unknown_fmt_placeholder_to_args_mapping {
3771
+ Some ( count) => ( count, false ) ,
3772
+ None => ( args. len ( ) , true ) ,
3773
+ } ;
3774
+
3775
+ // HACK(eddyb) extra sanity check to avoid visual mishaps.
3776
+ let valid_placeholder_count = placeholder_count
3777
+ . clamp ( const_pieces. len ( ) . saturating_sub ( 1 ) , const_pieces. len ( ) ) ;
3778
+ let placeholders_are_args =
3779
+ placeholders_are_args && placeholder_count == valid_placeholder_count;
3780
+
3781
+ // FIXME(eddyb) stop using `itertools`'s `intersperse`,
3782
+ // when it gets stabilized on `Iterator` instead.
3783
+ #[ allow( unstable_name_collisions) ]
3784
+ let ( placeholders, suffix) = if placeholders_are_args {
3785
+ ( Either :: Left ( args) , None )
3786
+ } else {
3787
+ // See also `has_unknown_fmt_placeholder_to_args_mapping`
3788
+ // comment (which has an example for 3 pieces and 2 args).
3789
+ //
3790
+ // FIXME(eddyb) this could definitely be improved, but
3791
+ // so far this only really gets hit in esoteric `core`
3792
+ // internals (UB checks and `char::encode_utf{8,16}`).
3793
+ (
3794
+ Either :: Right (
3795
+ ( 0 ..valid_placeholder_count) . map ( |i| format ! ( "{{{i}}}" ) . into ( ) ) ,
3796
+ ) ,
3797
+ Some (
3798
+ [ "\n with {…} from: " . into ( ) ]
3799
+ . into_iter ( )
3800
+ . chain ( args. intersperse ( ", " . into ( ) ) ) ,
3801
+ ) ,
3802
+ )
3803
+ } ;
3804
+
3805
+ const_pieces
3806
+ . into_iter ( )
3807
+ . map ( |s| Cow :: Owned ( s. replace ( '%' , "%%" ) ) )
3808
+ . interleave ( placeholders)
3809
+ . chain ( suffix. into_iter ( ) . flatten ( ) )
3810
+ . collect :: < String > ( )
3663
3811
}
3664
3812
3665
3813
Err ( FormatArgsNotRecognized ( step) ) => {
0 commit comments