@@ -183,6 +183,7 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
183183 enum Inst < ID > {
184184 Bitcast ( ID , ID ) ,
185185 CompositeExtract ( ID , ID , u32 ) ,
186+ CompositeInsert ( ID , ID , ID , u32 ) ,
186187 InBoundsAccessChain ( ID , ID , u32 ) ,
187188 InBoundsAccessChain2 ( ID , ID , u32 , u32 ) ,
188189 Store ( ID , ID ) ,
@@ -229,6 +230,18 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
229230 {
230231 return Some ( Inst :: CompositeExtract ( r, x, i) ) ;
231232 }
233+ if inst. class . opcode == Op :: CompositeInsert
234+ && let (
235+ Some ( r) ,
236+ & [
237+ Operand :: IdRef ( inserted) ,
238+ Operand :: IdRef ( base) ,
239+ Operand :: LiteralBit32 ( i) ,
240+ ] ,
241+ ) = ( inst. result_id , & inst. operands [ ..] )
242+ {
243+ return Some ( Inst :: CompositeInsert ( r, inserted, base, i) ) ;
244+ }
232245
233246 // HACK(eddyb) all instructions accepted below
234247 // are expected to take no more than 4 operands,
@@ -289,6 +302,20 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
289302 Some ( & [ Inst :: Call ( call_ret_id, _, _) ] ) if call_ret_id == format_args_id
290303 ) {
291304 1
305+ } else if matches ! (
306+ try_rev_take( -5 ) . as_deref( ) ,
307+ Some ( & [
308+ Inst :: Call ( call_ret_id, _, _) ,
309+ Inst :: CompositeExtract ( extracted0, from0, 0 ) ,
310+ Inst :: CompositeExtract ( extracted1, from1, 1 ) ,
311+ Inst :: CompositeInsert ( inserted0, value0, _, 0 ) ,
312+ Inst :: CompositeInsert ( inserted1, value1, inserted0_prev, 1 ) ,
313+ ] ) if [ from0, from1] == [ call_ret_id; 2 ]
314+ && [ value0, value1] == [ extracted0, extracted1]
315+ && inserted0 == inserted0_prev
316+ && inserted1 == format_args_id
317+ ) {
318+ 5
292319 } else {
293320 // HACK(eddyb) gather context for new call patterns before bailing.
294321 let mut insts = SmallVec :: < [ Inst < Word > ; 32 ] > :: new ( ) ;
@@ -341,6 +368,27 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
341368
342369 ( call_args, pieces_len, rt_args_count)
343370 }
371+ [
372+ Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
373+ Inst :: CompositeExtract ( extracted0, from0, 0 ) ,
374+ Inst :: CompositeExtract ( extracted1, from1, 1 ) ,
375+ Inst :: CompositeInsert ( inserted0, value0, _, 0 ) ,
376+ Inst :: CompositeInsert ( inserted1, value1, inserted0_prev, 1 ) ,
377+ ] if [ from0, from1] == [ call_ret_id; 2 ]
378+ && [ value0, value1] == [ extracted0, extracted1]
379+ && inserted0 == inserted0_prev
380+ && inserted1 == format_args_id =>
381+ {
382+ let Some ( & ( pieces_len, rt_args_count) ) =
383+ cx. fmt_args_new_fn_ids . borrow ( ) . get ( & callee_id)
384+ else {
385+ return Err ( FormatArgsNotRecognized (
386+ "fmt::Arguments::new callee not registered" . into ( ) ,
387+ ) ) ;
388+ } ;
389+
390+ ( call_args, pieces_len, rt_args_count)
391+ }
344392 _ => {
345393 // HACK(eddyb) this gathers more context before reporting.
346394 let mut insts = fmt_args_new_call_insts;
@@ -358,88 +406,112 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
358406 ) ) ) ;
359407 }
360408 } ;
361- let ( ( pieces_slice_ptr_id, pieces_len) , ( rt_args_slice_ptr_id, rt_args_count) ) =
362- match call_args[ ..] {
363- // `<core::fmt::Arguments>::new_v1_formatted`
364- //
365- // HACK(eddyb) this isn't fully supported,
366- // as that would require digging into unstable
367- // internals of `core::fmt::rt::Placeholder`s,
368- // but the whole call still needs to be removed,
369- // and both const str pieces and runtime args
370- // can still be printed (even if in jankier way).
371- [
372- pieces_slice_ptr_id,
373- pieces_len_id,
374- rt_args_slice_ptr_id,
375- rt_args_len_id,
376- fmt_placeholders_slice_ptr_id,
377- fmt_placeholders_len_id,
378- ] if ( pieces_len, rt_args_count) == ( !0 , !0 ) => {
379- let [ pieces_len, rt_args_len, fmt_placeholders_len] =
380- match [ pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
381- . map ( const_u32_as_usize)
382- {
383- [ Some ( a) , Some ( b) , Some ( c) ] => [ a, b, c] ,
384- _ => {
385- return Err ( FormatArgsNotRecognized (
386- "fmt::Arguments::new_v1_formatted \
409+ const FMT_ARGS_FROM_STR_PIECES_LEN : usize = !1 ;
410+ enum PiecesSource {
411+ Slice { ptr_id : Word , len : usize } ,
412+ DirectConstStr ( [ Word ; 2 ] ) ,
413+ }
414+ let ( pieces_source, ( rt_args_slice_ptr_id, rt_args_count) ) = match call_args[ ..] {
415+ // `<core::fmt::Arguments>::new_v1_formatted`
416+ //
417+ // HACK(eddyb) this isn't fully supported,
418+ // as that would require digging into unstable
419+ // internals of `core::fmt::rt::Placeholder`s,
420+ // but the whole call still needs to be removed,
421+ // and both const str pieces and runtime args
422+ // can still be printed (even if in jankier way).
423+ [
424+ pieces_slice_ptr_id,
425+ pieces_len_id,
426+ rt_args_slice_ptr_id,
427+ rt_args_len_id,
428+ fmt_placeholders_slice_ptr_id,
429+ fmt_placeholders_len_id,
430+ ] if ( pieces_len, rt_args_count) == ( !0 , !0 ) => {
431+ let [ pieces_len, rt_args_len, fmt_placeholders_len] =
432+ match [ pieces_len_id, rt_args_len_id, fmt_placeholders_len_id]
433+ . map ( const_u32_as_usize)
434+ {
435+ [ Some ( a) , Some ( b) , Some ( c) ] => [ a, b, c] ,
436+ _ => {
437+ return Err ( FormatArgsNotRecognized (
438+ "fmt::Arguments::new_v1_formatted \
387439 with dynamic lengths"
388- . into ( ) ,
389- ) ) ;
390- }
391- } ;
440+ . into ( ) ,
441+ ) ) ;
442+ }
443+ } ;
392444
393- let prepare_args_insts = try_rev_take ( 2 ) . ok_or_else ( || {
394- FormatArgsNotRecognized (
395- "fmt::Arguments::new_v1_formatted call: ran out of instructions" . into ( ) ,
396- )
397- } ) ?;
398- let ( rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
399- match prepare_args_insts[ ..] {
400- [
401- Inst :: Bitcast ( rt_args_cast_out_id, rt_args_cast_in_id) ,
402- Inst :: Bitcast ( placeholders_cast_out_id, placeholders_cast_in_id) ,
403- ] if rt_args_cast_out_id == rt_args_slice_ptr_id
404- && placeholders_cast_out_id == fmt_placeholders_slice_ptr_id =>
405- {
406- ( rt_args_cast_in_id, placeholders_cast_in_id)
407- }
408- _ => {
409- let mut insts = prepare_args_insts;
410- insts. extend ( fmt_args_new_call_insts) ;
411- return Err ( FormatArgsNotRecognized ( format ! (
412- "fmt::Arguments::new_v1_formatted call sequence ({insts:?})" ,
413- ) ) ) ;
414- }
415- } ;
445+ let prepare_args_insts = try_rev_take ( 2 ) . ok_or_else ( || {
446+ FormatArgsNotRecognized (
447+ "fmt::Arguments::new_v1_formatted call: ran out of instructions" . into ( ) ,
448+ )
449+ } ) ?;
450+ let ( rt_args_slice_ptr_id, _fmt_placeholders_slice_ptr_id) =
451+ match prepare_args_insts[ ..] {
452+ [
453+ Inst :: Bitcast ( rt_args_cast_out_id, rt_args_cast_in_id) ,
454+ Inst :: Bitcast ( placeholders_cast_out_id, placeholders_cast_in_id) ,
455+ ] if rt_args_cast_out_id == rt_args_slice_ptr_id
456+ && placeholders_cast_out_id == fmt_placeholders_slice_ptr_id =>
457+ {
458+ ( rt_args_cast_in_id, placeholders_cast_in_id)
459+ }
460+ _ => {
461+ let mut insts = prepare_args_insts;
462+ insts. extend ( fmt_args_new_call_insts) ;
463+ return Err ( FormatArgsNotRecognized ( format ! (
464+ "fmt::Arguments::new_v1_formatted call sequence ({insts:?})" ,
465+ ) ) ) ;
466+ }
467+ } ;
416468
417- decoded_format_args. has_unknown_fmt_placeholder_to_args_mapping =
418- Some ( fmt_placeholders_len) ;
469+ decoded_format_args. has_unknown_fmt_placeholder_to_args_mapping =
470+ Some ( fmt_placeholders_len) ;
419471
420- (
421- ( pieces_slice_ptr_id, pieces_len) ,
422- ( Some ( rt_args_slice_ptr_id) , rt_args_len) ,
423- )
424- }
472+ (
473+ PiecesSource :: Slice {
474+ ptr_id : pieces_slice_ptr_id,
475+ len : pieces_len,
476+ } ,
477+ ( Some ( rt_args_slice_ptr_id) , rt_args_len) ,
478+ )
479+ }
425480
426- // `<core::fmt::Arguments>::new_v1`
427- [ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
428- ( pieces_slice_ptr_id, pieces_len) ,
429- ( Some ( rt_args_slice_ptr_id) , rt_args_count) ,
430- ) ,
481+ // `<core::fmt::Arguments>::from_str`
482+ [ str_ptr_id, str_len_id]
483+ if ( pieces_len, rt_args_count) == ( FMT_ARGS_FROM_STR_PIECES_LEN , 0 ) =>
484+ {
485+ (
486+ PiecesSource :: DirectConstStr ( [ str_ptr_id, str_len_id] ) ,
487+ ( None , 0 ) ,
488+ )
489+ }
431490
432- // `<core::fmt::Arguments>::new_const`
433- [ pieces_slice_ptr_id] if rt_args_count == 0 => {
434- ( ( pieces_slice_ptr_id, pieces_len) , ( None , rt_args_count) )
435- }
491+ // `<core::fmt::Arguments>::new_v1`
492+ [ pieces_slice_ptr_id, rt_args_slice_ptr_id] => (
493+ PiecesSource :: Slice {
494+ ptr_id : pieces_slice_ptr_id,
495+ len : pieces_len,
496+ } ,
497+ ( Some ( rt_args_slice_ptr_id) , rt_args_count) ,
498+ ) ,
436499
437- _ => {
438- return Err ( FormatArgsNotRecognized (
439- "fmt::Arguments::new call args" . into ( ) ,
440- ) ) ;
441- }
442- } ;
500+ // `<core::fmt::Arguments>::new_const`
501+ [ pieces_slice_ptr_id] if rt_args_count == 0 => (
502+ PiecesSource :: Slice {
503+ ptr_id : pieces_slice_ptr_id,
504+ len : pieces_len,
505+ } ,
506+ ( None , rt_args_count) ,
507+ ) ,
508+
509+ _ => {
510+ return Err ( FormatArgsNotRecognized (
511+ "fmt::Arguments::new call args" . into ( ) ,
512+ ) ) ;
513+ }
514+ } ;
443515
444516 // HACK(eddyb) this is the worst part: if we do have runtime
445517 // arguments (from e.g. new `assert!`s being added to `core`),
@@ -626,52 +698,65 @@ impl<'tcx> DecodedFormatArgs<'tcx> {
626698 }
627699 }
628700
629- // If the `pieces: &[&str]` slice needs a bitcast, it'll be here.
630- // HACK(eddyb) `try_rev_take(-1)` is "peeking", not taking.
631- let pieces_slice_ptr_id = match try_rev_take ( -1 ) . as_deref ( ) {
632- Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => {
633- // HACK(eddyb) consume the peeked instructions.
634- try_rev_take ( 1 ) . unwrap ( ) ;
635-
636- in_id
701+ decoded_format_args. const_pieces = match pieces_source {
702+ PiecesSource :: DirectConstStr ( str_ref) => {
703+ const_str_as_utf8 ( & str_ref) . map ( |s| [ s] . into_iter ( ) . collect ( ) )
637704 }
638- _ => pieces_slice_ptr_id,
639- } ;
640- decoded_format_args. const_pieces =
641- match const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) {
642- Some ( piece_ids) => piece_ids
643- . iter ( )
644- . map ( |& piece_id| match cx. builder . lookup_const_by_id ( piece_id) ? {
645- SpirvConst :: Composite ( piece) => const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?) ,
646- _ => None ,
647- } )
648- . collect :: < Option < _ > > ( ) ,
649- // HACK(eddyb) minor upstream blunder results in at
650- // least one instance of a runtime `[&str; 1]` array,
651- // see also this comment left on the responsible PR:
652- // https://github.com/rust-lang/rust/pull/129658#discussion_r2181834781
653- // HACK(eddyb) `try_rev_take(-4)` is "peeking", not taking.
654- None if pieces_len == 1 => match try_rev_take ( -4 ) . as_deref ( ) {
655- Some (
656- & [
657- Inst :: InBoundsAccessChain2 ( field0_ptr, array_ptr_0, 0 , 0 ) ,
658- Inst :: Store ( st0_dst_ptr, st0_val) ,
659- Inst :: InBoundsAccessChain2 ( field1_ptr, array_ptr_1, 0 , 1 ) ,
660- Inst :: Store ( st1_dst_ptr, st1_val) ,
661- ] ,
662- ) if [ array_ptr_0, array_ptr_1] == [ pieces_slice_ptr_id; 2 ]
663- && st0_dst_ptr == field0_ptr
664- && st1_dst_ptr == field1_ptr =>
665- {
705+ PiecesSource :: Slice {
706+ ptr_id : pieces_slice_ptr_id,
707+ len : pieces_len,
708+ } => {
709+ // If the `pieces: &[&str]` slice needs a bitcast, it'll be here.
710+ // HACK(eddyb) `try_rev_take(-1)` is "peeking", not taking.
711+ let pieces_slice_ptr_id = match try_rev_take ( -1 ) . as_deref ( ) {
712+ Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => {
666713 // HACK(eddyb) consume the peeked instructions.
667- try_rev_take ( 4 ) . unwrap ( ) ;
714+ try_rev_take ( 1 ) . unwrap ( ) ;
668715
669- const_str_as_utf8 ( & [ st0_val , st1_val ] ) . map ( |s| [ s ] . into_iter ( ) . collect ( ) )
716+ in_id
670717 }
671- _ => None ,
672- } ,
673- None => None ,
674- } ;
718+ _ => pieces_slice_ptr_id,
719+ } ;
720+
721+ match const_slice_as_elem_ids ( pieces_slice_ptr_id, pieces_len) {
722+ Some ( piece_ids) => piece_ids
723+ . iter ( )
724+ . map ( |& piece_id| match cx. builder . lookup_const_by_id ( piece_id) ? {
725+ SpirvConst :: Composite ( piece) => {
726+ const_str_as_utf8 ( piece. try_into ( ) . ok ( ) ?)
727+ }
728+ _ => None ,
729+ } )
730+ . collect :: < Option < _ > > ( ) ,
731+ // HACK(eddyb) minor upstream blunder results in at
732+ // least one instance of a runtime `[&str; 1]` array,
733+ // see also this comment left on the responsible PR:
734+ // https://github.com/rust-lang/rust/pull/129658#discussion_r2181834781
735+ // HACK(eddyb) `try_rev_take(-4)` is "peeking", not taking.
736+ None if pieces_len == 1 => match try_rev_take ( -4 ) . as_deref ( ) {
737+ Some (
738+ & [
739+ Inst :: InBoundsAccessChain2 ( field0_ptr, array_ptr_0, 0 , 0 ) ,
740+ Inst :: Store ( st0_dst_ptr, st0_val) ,
741+ Inst :: InBoundsAccessChain2 ( field1_ptr, array_ptr_1, 0 , 1 ) ,
742+ Inst :: Store ( st1_dst_ptr, st1_val) ,
743+ ] ,
744+ ) if [ array_ptr_0, array_ptr_1] == [ pieces_slice_ptr_id; 2 ]
745+ && st0_dst_ptr == field0_ptr
746+ && st1_dst_ptr == field1_ptr =>
747+ {
748+ // HACK(eddyb) consume the peeked instructions.
749+ try_rev_take ( 4 ) . unwrap ( ) ;
750+
751+ const_str_as_utf8 ( & [ st0_val, st1_val] )
752+ . map ( |s| [ s] . into_iter ( ) . collect ( ) )
753+ }
754+ _ => None ,
755+ } ,
756+ None => None ,
757+ }
758+ }
759+ } ;
675760
676761 // Keep all instructions up to (but not including) the last one
677762 // confirmed above to be the first instruction of `format_args!`.
0 commit comments