@@ -7,7 +7,7 @@ use crate::builder_spirv::{BuilderCursor, SpirvConst, SpirvValue, SpirvValueExt,
77use crate :: codegen_cx:: CodegenCx ;
88use crate :: custom_insts:: { CustomInst , CustomOp } ;
99use crate :: spirv_type:: SpirvType ;
10- use itertools:: Itertools ;
10+ use itertools:: { Either , Itertools } ;
1111use rspirv:: dr:: { InsertPoint , Instruction , Operand } ;
1212use rspirv:: spirv:: { Capability , MemoryModel , MemorySemantics , Op , Scope , StorageClass , Word } ;
1313use rustc_apfloat:: { Float , Round , Status , ieee} ;
@@ -3230,6 +3230,16 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
32303230 /// * `&a` with `typeof a` and ' ',
32313231 /// * `&b` with `typeof b` and 'x'
32323232 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 > ,
32333243 }
32343244 struct FormatArgsNotRecognized ( String ) ;
32353245
@@ -3455,6 +3465,105 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
34553465 } ;
34563466
34573467 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+
34583567 // `<core::fmt::Arguments>::new_v1`
34593568 [ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
34603569 ( pieces_slice_ptr_id, pieces_len) ,
@@ -3608,58 +3717,97 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
36083717 let mut debug_printf_args = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
36093718 let message = match try_decode_and_remove_format_args ( ) {
36103719 Ok ( DecodedFormatArgs {
3611- const_pieces,
3720+ const_pieces : None , ..
3721+ } ) => "<unknown message>" . into ( ) ,
3722+
3723+ Ok ( DecodedFormatArgs {
3724+ const_pieces : Some ( const_pieces) ,
36123725 ref_arg_ids_with_ty_and_spec,
3726+ has_unknown_fmt_placeholder_to_args_mapping,
36133727 } ) => {
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 :: * } ;
36483732
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 > ( )
36633811 }
36643812
36653813 Err ( FormatArgsNotRecognized ( step) ) => {
0 commit comments