@@ -20,11 +20,13 @@ use rustc_codegen_ssa::MemFlags;
20
20
use rustc_data_structures:: fx:: FxHashSet ;
21
21
use rustc_middle:: bug;
22
22
use rustc_middle:: middle:: codegen_fn_attrs:: CodegenFnAttrs ;
23
+ use rustc_middle:: ty:: layout:: LayoutOf ;
23
24
use rustc_middle:: ty:: Ty ;
24
25
use rustc_span:: Span ;
25
26
use rustc_target:: abi:: call:: FnAbi ;
26
27
use rustc_target:: abi:: { Abi , Align , Scalar , Size , WrappingRange } ;
27
28
use smallvec:: SmallVec ;
29
+ use std:: borrow:: Cow ;
28
30
use std:: cell:: Cell ;
29
31
use std:: convert:: TryInto ;
30
32
use std:: iter:: { self , empty} ;
@@ -2414,12 +2416,25 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2414
2416
// nor simplified in MIR (e.g. promoted to a constant) in any way,
2415
2417
// so we have to try and remove the `fmt::Arguments::new` call here.
2416
2418
#[ derive( Default ) ]
2417
- struct DecodedFormatArgs { }
2419
+ struct DecodedFormatArgs < ' tcx > {
2420
+ /// If fully constant, the `pieces: &'a [&'static str]` input
2421
+ /// of `fmt::Arguments<'a>` (i.e. the strings between args).
2422
+ const_pieces : Option < SmallVec < [ String ; 2 ] > > ,
2423
+
2424
+ /// Original references for `fmt::Arguments<'a>` dynamic arguments,
2425
+ /// i.e. the `&'a T` passed to `fmt::rt::Argument::<'a>::new_*`,
2426
+ /// tracking the type `T` and `char` formatting specifier.
2427
+ ///
2428
+ /// E.g. for `format_args!("{a} {b:x}")` they'll be:
2429
+ /// * `&a` with `typeof a` and ' ',
2430
+ /// *`&b` with `typeof b` and 'x'
2431
+ ref_arg_ids_with_ty_and_spec : SmallVec < [ ( Word , Ty < ' tcx > , char ) ; 2 ] > ,
2432
+ }
2418
2433
struct FormatArgsNotRecognized ( String ) ;
2419
2434
2420
2435
// HACK(eddyb) this is basically a `try` block.
2421
2436
let try_decode_and_remove_format_args = || {
2422
- let decoded_format_args = DecodedFormatArgs :: default ( ) ;
2437
+ let mut decoded_format_args = DecodedFormatArgs :: default ( ) ;
2423
2438
2424
2439
let const_u32_as_usize = |ct_id| match self . builder . lookup_const_by_id ( ct_id) ? {
2425
2440
SpirvConst :: U32 ( x) => Some ( x as usize ) ,
@@ -2441,6 +2456,32 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2441
2456
}
2442
2457
None
2443
2458
} ;
2459
+ let const_str_as_utf8 = |str_ptr_and_len_ids : & [ Word ] | {
2460
+ let piece_str_bytes = const_slice_as_elem_ids ( str_ptr_and_len_ids) ?
2461
+ . iter ( )
2462
+ . map ( |& id| u8:: try_from ( const_u32_as_usize ( id) ?) . ok ( ) )
2463
+ . collect :: < Option < Vec < u8 > > > ( ) ?;
2464
+ String :: from_utf8 ( piece_str_bytes) . ok ( )
2465
+ } ;
2466
+
2467
+ // HACK(eddyb) some entry-points only take a `&str`, not `fmt::Arguments`.
2468
+ if let [
2469
+ SpirvValue {
2470
+ kind : SpirvValueKind :: Def ( a_id) ,
2471
+ ..
2472
+ } ,
2473
+ SpirvValue {
2474
+ kind : SpirvValueKind :: Def ( b_id) ,
2475
+ ..
2476
+ } ,
2477
+ _, // `&'static panic::Location<'static>`
2478
+ ] = args[ ..]
2479
+ {
2480
+ if let Some ( const_msg) = const_str_as_utf8 ( & [ a_id, b_id] ) {
2481
+ decoded_format_args. const_pieces = Some ( [ const_msg] . into_iter ( ) . collect ( ) ) ;
2482
+ return Ok ( decoded_format_args) ;
2483
+ }
2484
+ }
2444
2485
2445
2486
let format_args_id = match args {
2446
2487
& [
@@ -2503,6 +2544,7 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2503
2544
#[ derive( Debug ) ]
2504
2545
enum Inst < ' tcx , ID > {
2505
2546
Bitcast ( ID , ID ) ,
2547
+ CompositeExtract ( ID , ID , u32 ) ,
2506
2548
AccessChain ( ID , ID , SpirvConst < ' tcx > ) ,
2507
2549
InBoundsAccessChain ( ID , ID , SpirvConst < ' tcx > ) ,
2508
2550
Store ( ID , ID ) ,
@@ -2519,6 +2561,15 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2519
2561
let ( i, inst) = non_debug_insts. next_back ( ) ?;
2520
2562
taken_inst_idx_range. start . set ( i) ;
2521
2563
2564
+ // HACK(eddyb) avoid the logic below that assumes only ID operands
2565
+ if inst. class . opcode == Op :: CompositeExtract {
2566
+ if let ( Some ( r) , & [ Operand :: IdRef ( x) , Operand :: LiteralInt32 ( i) ] ) =
2567
+ ( inst. result_id , & inst. operands [ ..] )
2568
+ {
2569
+ return Some ( Inst :: CompositeExtract ( r, x, i) ) ;
2570
+ }
2571
+ }
2572
+
2522
2573
// HACK(eddyb) all instructions accepted below
2523
2574
// are expected to take no more than 4 operands,
2524
2575
// and this is easier to use than an iterator.
@@ -2716,6 +2767,26 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2716
2767
}
2717
2768
}
2718
2769
2770
+ // If the `pieces: &[&str]` slice needs a bitcast, it'll be here.
2771
+ let pieces_slice_ptr_id = match try_rev_take ( 1 ) . as_deref ( ) {
2772
+ Some ( & [ Inst :: Bitcast ( out_id, in_id) ] ) if out_id == pieces_slice_ptr_id => in_id,
2773
+ _ => pieces_slice_ptr_id,
2774
+ } ;
2775
+ decoded_format_args. const_pieces =
2776
+ const_slice_as_elem_ids ( & [ pieces_slice_ptr_id, pieces_len_id] ) . and_then (
2777
+ |piece_ids| {
2778
+ piece_ids
2779
+ . iter ( )
2780
+ . map ( |& piece_id| {
2781
+ match self . builder . lookup_const_by_id ( piece_id) ? {
2782
+ SpirvConst :: Composite ( piece) => const_str_as_utf8 ( piece) ,
2783
+ _ => None ,
2784
+ }
2785
+ } )
2786
+ . collect :: < Option < _ > > ( )
2787
+ } ,
2788
+ ) ;
2789
+
2719
2790
// Keep all instructions up to (but not including) the last one
2720
2791
// confirmed above to be the first instruction of `format_args!`.
2721
2792
func. blocks [ block_idx]
@@ -2725,8 +2796,61 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2725
2796
Ok ( decoded_format_args)
2726
2797
} ;
2727
2798
2728
- match try_decode_and_remove_format_args ( ) {
2729
- Ok ( DecodedFormatArgs { } ) => { }
2799
+ let mut debug_printf_args = SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
2800
+ let message = match try_decode_and_remove_format_args ( ) {
2801
+ Ok ( DecodedFormatArgs {
2802
+ const_pieces,
2803
+ ref_arg_ids_with_ty_and_spec,
2804
+ } ) => {
2805
+ match const_pieces {
2806
+ Some ( const_pieces) => {
2807
+ const_pieces
2808
+ . into_iter ( )
2809
+ . map ( |s| Cow :: Owned ( s. replace ( '%' , "%%" ) ) )
2810
+ . interleave ( ref_arg_ids_with_ty_and_spec. iter ( ) . map (
2811
+ |& ( ref_id, ty, spec) | {
2812
+ use rustc_target:: abi:: { Integer :: * , Primitive :: * } ;
2813
+
2814
+ let layout = self . layout_of ( ty) ;
2815
+
2816
+ let scalar = match layout. abi {
2817
+ Abi :: Scalar ( scalar) => Some ( scalar. primitive ( ) ) ,
2818
+ _ => None ,
2819
+ } ;
2820
+ let debug_printf_fmt = match ( spec, scalar) {
2821
+ // FIXME(eddyb) support more of these,
2822
+ // potentially recursing to print ADTs.
2823
+ ( ' ' | '?' , Some ( Int ( I32 , false ) ) ) => "%u" ,
2824
+ ( 'x' , Some ( Int ( I32 , false ) ) ) => "%x" ,
2825
+ ( ' ' | '?' , Some ( Int ( I32 , true ) ) ) => "%i" ,
2826
+ ( ' ' | '?' , Some ( F32 ) ) => "%f" ,
2827
+
2828
+ _ => "" ,
2829
+ } ;
2830
+
2831
+ if debug_printf_fmt. is_empty ( ) {
2832
+ return Cow :: Owned (
2833
+ format ! ( "{{/* unprintable {ty} */:{spec}}}" )
2834
+ . replace ( '%' , "%%" ) ,
2835
+ ) ;
2836
+ }
2837
+
2838
+ let spirv_type = layout. spirv_type ( self . span ( ) , self ) ;
2839
+ debug_printf_args. push (
2840
+ self . emit ( )
2841
+ . load ( spirv_type, None , ref_id, None , [ ] )
2842
+ . unwrap ( )
2843
+ . with_type ( spirv_type) ,
2844
+ ) ;
2845
+ Cow :: Borrowed ( debug_printf_fmt)
2846
+ } ,
2847
+ ) )
2848
+ . collect :: < String > ( )
2849
+ }
2850
+ None => "<unknown message>" . into ( ) ,
2851
+ }
2852
+ }
2853
+
2730
2854
Err ( FormatArgsNotRecognized ( step) ) => {
2731
2855
if let Some ( current_span) = self . current_span {
2732
2856
let mut warn = self . tcx . sess . struct_span_warn (
@@ -2738,8 +2862,8 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2738
2862
"compilation may later fail due to leftover `format_args!` internals" ,
2739
2863
) ;
2740
2864
2741
- if self . tcx . sess . opts . unstable_opts . inline_mir != Some ( true ) {
2742
- warn. note ( "missing `-Zinline-mir=on ` flag (should've been set by `spirv-builder`)" )
2865
+ if self . tcx . sess . opts . unstable_opts . inline_mir != Some ( false ) {
2866
+ warn. note ( "missing `-Zinline-mir=off ` flag (should've been set by `spirv-builder`)" )
2743
2867
. help ( "check `.cargo` and environment variables for potential overrides" )
2744
2868
. help ( "(or, if not using `spirv-builder` at all, add the flag manually)" ) ;
2745
2869
} else {
@@ -2748,13 +2872,17 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2748
2872
2749
2873
warn. emit ( ) ;
2750
2874
}
2875
+ "<unknown message> (failed to find/decode `format_args!` expansion)" . into ( )
2751
2876
}
2752
- }
2877
+ } ;
2753
2878
2754
2879
// HACK(eddyb) redirect any possible panic call to an abort, to avoid
2755
2880
// needing to materialize `&core::panic::Location` or `format_args!`.
2756
- // FIXME(eddyb) find a way to extract the original message.
2757
- self . abort_with_message ( "panicked: <unknown message>" . into ( ) ) ;
2881
+ self . abort_with_message_and_debug_printf_args (
2882
+ // HACK(eddyb) `|` is an ad-hoc convention of `linker::spirt_passes::controlflow`.
2883
+ format ! ( "panicked|{message}" ) ,
2884
+ debug_printf_args,
2885
+ ) ;
2758
2886
self . undef ( result_type)
2759
2887
} else if let Some ( mode) = buffer_load_intrinsic {
2760
2888
self . codegen_buffer_load_intrinsic ( result_type, args, mode)
0 commit comments