@@ -4,6 +4,7 @@ use crate::builder_spirv::{BuilderCursor, SpirvConst, SpirvValue, SpirvValueExt,
4
4
use crate :: custom_insts:: { CustomInst , CustomOp } ;
5
5
use crate :: rustc_codegen_ssa:: traits:: BaseTypeMethods ;
6
6
use crate :: spirv_type:: SpirvType ;
7
+ use itertools:: Itertools ;
7
8
use rspirv:: dr:: { InsertPoint , Instruction , Operand } ;
8
9
use rspirv:: spirv:: { Capability , MemoryModel , MemorySemantics , Op , Scope , StorageClass , Word } ;
9
10
use rustc_apfloat:: { ieee, Float , Round , Status } ;
@@ -24,6 +25,7 @@ use rustc_span::Span;
24
25
use rustc_target:: abi:: call:: FnAbi ;
25
26
use rustc_target:: abi:: { Abi , Align , Scalar , Size , WrappingRange } ;
26
27
use smallvec:: SmallVec ;
28
+ use std:: cell:: Cell ;
27
29
use std:: convert:: TryInto ;
28
30
use std:: iter:: { self , empty} ;
29
31
@@ -2411,18 +2413,49 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2411
2413
// more complex and neither immediate (`fmt::Arguments` is too big)
2412
2414
// nor simplified in MIR (e.g. promoted to a constant) in any way,
2413
2415
// so we have to try and remove the `fmt::Arguments::new` call here.
2416
+ #[ derive( Default ) ]
2417
+ struct DecodedFormatArgs { }
2418
+ struct FormatArgsNotRecognized ( String ) ;
2419
+
2414
2420
// HACK(eddyb) this is basically a `try` block.
2415
- let remove_format_args_if_possible = || -> Option < ( ) > {
2421
+ let try_decode_and_remove_format_args = || {
2422
+ let decoded_format_args = DecodedFormatArgs :: default ( ) ;
2423
+
2424
+ let const_u32_as_usize = |ct_id| match self . builder . lookup_const_by_id ( ct_id) ? {
2425
+ SpirvConst :: U32 ( x) => Some ( x as usize ) ,
2426
+ _ => None ,
2427
+ } ;
2428
+ let const_slice_as_elem_ids = |slice_ptr_and_len_ids : & [ Word ] | {
2429
+ if let [ ptr_id, len_id] = slice_ptr_and_len_ids[ ..] {
2430
+ if let SpirvConst :: PtrTo { pointee } =
2431
+ self . builder . lookup_const_by_id ( ptr_id) ?
2432
+ {
2433
+ if let SpirvConst :: Composite ( elems) =
2434
+ self . builder . lookup_const_by_id ( pointee) ?
2435
+ {
2436
+ if elems. len ( ) == const_u32_as_usize ( len_id) ? {
2437
+ return Some ( elems) ;
2438
+ }
2439
+ }
2440
+ }
2441
+ }
2442
+ None
2443
+ } ;
2444
+
2416
2445
let format_args_id = match args {
2417
2446
& [
2418
2447
SpirvValue {
2419
2448
kind : SpirvValueKind :: Def ( format_args_id) ,
2420
2449
..
2421
2450
} ,
2422
- _,
2451
+ _, // `&'static panic::Location<'static>`
2423
2452
] => format_args_id,
2424
2453
2425
- _ => return None ,
2454
+ _ => {
2455
+ return Err ( FormatArgsNotRecognized (
2456
+ "panic entry-point call args" . into ( ) ,
2457
+ ) ) ;
2458
+ }
2426
2459
} ;
2427
2460
2428
2461
let custom_ext_inst_set_import = self . ext_inst . borrow_mut ( ) . import_custom ( self ) ;
@@ -2446,8 +2479,11 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2446
2479
. take_while ( |inst| inst. class . opcode == Op :: Variable )
2447
2480
. map ( |inst| inst. result_id . unwrap ( ) )
2448
2481
. collect ( ) ;
2449
- let require_local_var =
2450
- |ptr_id| Some ( ( ) ) . filter ( |( ) | local_var_ids. contains ( & ptr_id) ) ;
2482
+ let require_local_var = |ptr_id, var| {
2483
+ Some ( ( ) )
2484
+ . filter ( |( ) | local_var_ids. contains ( & ptr_id) )
2485
+ . ok_or_else ( || FormatArgsNotRecognized ( format ! ( "{var} storage not local" ) ) )
2486
+ } ;
2451
2487
2452
2488
let mut non_debug_insts = func. blocks [ block_idx]
2453
2489
. instructions
@@ -2474,14 +2510,14 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2474
2510
Call ( ID , ID , SmallVec < [ ID ; 4 ] > ) ,
2475
2511
}
2476
2512
2477
- let mut taken_inst_idx_range = func. blocks [ block_idx] . instructions . len ( ) ..;
2513
+ let taken_inst_idx_range = Cell :: new ( func. blocks [ block_idx] . instructions . len ( ) ) ..;
2478
2514
2479
2515
// Take `count` instructions, advancing backwards, but returning
2480
2516
// instructions in their original order (and decoded to `Inst`s).
2481
2517
let mut try_rev_take = |count| {
2482
2518
let maybe_rev_insts = ( 0 ..count) . map ( |_| {
2483
2519
let ( i, inst) = non_debug_insts. next_back ( ) ?;
2484
- taken_inst_idx_range = i.. ;
2520
+ taken_inst_idx_range. start . set ( i ) ;
2485
2521
2486
2522
// HACK(eddyb) all instructions accepted below
2487
2523
// are expected to take no more than 4 operands,
@@ -2519,102 +2555,143 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2519
2555
insts. reverse ( ) ;
2520
2556
Some ( insts)
2521
2557
} ;
2558
+ let fmt_args_new_call_insts = try_rev_take ( 3 ) . ok_or_else ( || {
2559
+ FormatArgsNotRecognized (
2560
+ "fmt::Arguments::new call: ran out of instructions" . into ( ) ,
2561
+ )
2562
+ } ) ?;
2563
+ let ( ( pieces_slice_ptr_id, pieces_len_id) , ( rt_args_slice_ptr_id, rt_args_count) ) =
2564
+ match fmt_args_new_call_insts[ ..] {
2565
+ [
2566
+ Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
2567
+ Inst :: Store ( st_dst_id, st_val_id) ,
2568
+ Inst :: Load ( ld_val_id, ld_src_id) ,
2569
+ ] if self . fmt_args_new_fn_ids . borrow ( ) . contains ( & callee_id)
2570
+ && call_ret_id == st_val_id
2571
+ && st_dst_id == ld_src_id
2572
+ && ld_val_id == format_args_id =>
2573
+ {
2574
+ require_local_var ( st_dst_id, "fmt::Arguments::new destination" ) ?;
2575
+
2576
+ match call_args[ ..] {
2577
+ // `<core::fmt::Arguments>::new_v1`
2578
+ [
2579
+ pieces_slice_ptr_id,
2580
+ pieces_len_id,
2581
+ rt_args_slice_ptr_id,
2582
+ rt_args_len_id,
2583
+ ] => (
2584
+ ( pieces_slice_ptr_id, pieces_len_id) ,
2585
+ (
2586
+ Some ( rt_args_slice_ptr_id) ,
2587
+ const_u32_as_usize ( rt_args_len_id) . ok_or_else ( || {
2588
+ FormatArgsNotRecognized (
2589
+ "fmt::Arguments::new: args.len() not constant"
2590
+ . into ( ) ,
2591
+ )
2592
+ } ) ?,
2593
+ ) ,
2594
+ ) ,
2522
2595
2523
- let ( rt_args_slice_ptr_id, rt_args_count) = match try_rev_take ( 3 ) ?[ ..] {
2524
- [
2525
- Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
2526
- Inst :: Store ( st_dst_id, st_val_id) ,
2527
- Inst :: Load ( ld_val_id, ld_src_id) ,
2528
- ] if self . fmt_args_new_fn_ids . borrow ( ) . contains ( & callee_id)
2529
- && call_ret_id == st_val_id
2530
- && st_dst_id == ld_src_id
2531
- && ld_val_id == format_args_id =>
2532
- {
2533
- require_local_var ( st_dst_id) ?;
2534
- match call_args[ ..] {
2535
- // `<core::fmt::Arguments>::new_v1`
2536
- [ _, _, rt_args_slice_ptr_id, rt_args_len_id] => (
2537
- Some ( rt_args_slice_ptr_id) ,
2538
- self . builder
2539
- . lookup_const_by_id ( rt_args_len_id)
2540
- . and_then ( |ct| match ct {
2541
- SpirvConst :: U32 ( x) => Some ( x as usize ) ,
2542
- _ => None ,
2543
- } ) ?,
2544
- ) ,
2545
-
2546
- // `<core::fmt::Arguments>::new_const`
2547
- [ _, _] => ( None , 0 ) ,
2548
-
2549
- _ => return None ,
2596
+ // `<core::fmt::Arguments>::new_const`
2597
+ [ pieces_slice_ptr_id, pieces_len_id] => {
2598
+ ( ( pieces_slice_ptr_id, pieces_len_id) , ( None , 0 ) )
2599
+ }
2600
+
2601
+ _ => {
2602
+ return Err ( FormatArgsNotRecognized (
2603
+ "fmt::Arguments::new call args" . into ( ) ,
2604
+ ) ) ;
2605
+ }
2606
+ }
2550
2607
}
2551
- }
2552
- _ => return None ,
2553
- } ;
2608
+ _ => {
2609
+ // HACK(eddyb) this gathers more context before reporting.
2610
+ let mut insts = fmt_args_new_call_insts;
2611
+ insts. reverse ( ) ;
2612
+ while let Some ( extra_inst) = try_rev_take ( 1 ) {
2613
+ insts. extend ( extra_inst) ;
2614
+ if insts. len ( ) >= 32 {
2615
+ break ;
2616
+ }
2617
+ }
2618
+ insts. reverse ( ) ;
2619
+
2620
+ return Err ( FormatArgsNotRecognized ( format ! (
2621
+ "fmt::Arguments::new call sequence ({insts:?})" ,
2622
+ ) ) ) ;
2623
+ }
2624
+ } ;
2554
2625
2555
2626
// HACK(eddyb) this is the worst part: if we do have runtime
2556
2627
// arguments (from e.g. new `assert!`s being added to `core`),
2557
2628
// we have to confirm their many instructions for removal.
2558
2629
if rt_args_count > 0 {
2559
2630
let rt_args_slice_ptr_id = rt_args_slice_ptr_id. unwrap ( ) ;
2560
- let rt_args_array_ptr_id = match try_rev_take ( 1 ) ?[ ..] {
2631
+ let rt_args_array_ptr_id = match try_rev_take ( 1 ) . ok_or_else ( || {
2632
+ FormatArgsNotRecognized (
2633
+ "&[fmt::rt::Argument] bitcast: ran out of instructions" . into ( ) ,
2634
+ )
2635
+ } ) ?[ ..]
2636
+ {
2561
2637
[ Inst :: Bitcast ( out_id, in_id) ] if out_id == rt_args_slice_ptr_id => in_id,
2562
- _ => return None ,
2638
+ _ => {
2639
+ return Err ( FormatArgsNotRecognized (
2640
+ "&[fmt::rt::Argument] bitcast" . into ( ) ,
2641
+ ) ) ;
2642
+ }
2563
2643
} ;
2564
- require_local_var ( rt_args_array_ptr_id) ;
2644
+ require_local_var ( rt_args_array_ptr_id, "[fmt::rt::Argument; N]" ) ? ;
2565
2645
2566
- // Each runtime argument has its own variable, 6 instructions
2567
- // to initialize it, and 9 instructions to copy it to the
2568
- // appropriate slot in the array. The groups of 6 and 9
2646
+ // Each runtime argument has 3 instructions to call one of
2647
+ // the `fmt::rt::Argument::new_*` functions (and split its
2648
+ // scalar pair result), and 5 instructions to store it into
2649
+ // the appropriate slot in the array. The groups of 3 and 5
2569
2650
// instructions, for all runtime args, are each separate.
2570
- let copies_from_rt_arg_vars_to_rt_args_array = try_rev_take ( rt_args_count * 9 ) ?;
2571
- let copies_from_rt_arg_vars_to_rt_args_array =
2572
- copies_from_rt_arg_vars_to_rt_args_array. chunks ( 9 ) ;
2573
- let inits_of_rt_arg_vars = try_rev_take ( rt_args_count * 6 ) ?;
2574
- let inits_of_rt_arg_vars = inits_of_rt_arg_vars. chunks ( 6 ) ;
2575
-
2576
- for (
2577
- rt_arg_idx,
2578
- ( init_of_rt_arg_var_insts, copy_from_rt_arg_var_to_rt_args_array_insts) ,
2579
- ) in inits_of_rt_arg_vars
2580
- . zip ( copies_from_rt_arg_vars_to_rt_args_array)
2581
- . enumerate ( )
2651
+ let stores_to_rt_args_array =
2652
+ try_rev_take ( rt_args_count * 5 ) . ok_or_else ( || {
2653
+ FormatArgsNotRecognized (
2654
+ "[fmt::rt::Argument; N] stores: ran out of instructions" . into ( ) ,
2655
+ )
2656
+ } ) ?;
2657
+ let stores_to_rt_args_array = stores_to_rt_args_array. chunks ( 5 ) ;
2658
+ let rt_arg_new_calls = try_rev_take ( rt_args_count * 3 ) . ok_or_else ( || {
2659
+ FormatArgsNotRecognized (
2660
+ "fmt::rt::Argument::new calls: ran out of instructions" . into ( ) ,
2661
+ )
2662
+ } ) ?;
2663
+ let rt_arg_new_calls = rt_arg_new_calls. chunks ( 3 ) ;
2664
+
2665
+ for ( rt_arg_idx, ( rt_arg_new_call_insts, store_to_rt_args_array_insts) ) in
2666
+ rt_arg_new_calls. zip ( stores_to_rt_args_array) . enumerate ( )
2582
2667
{
2583
- let rt_arg_var_id = match init_of_rt_arg_var_insts [ ..] {
2668
+ let ( a , b ) = match rt_arg_new_call_insts [ ..] {
2584
2669
[
2585
- Inst :: Bitcast ( b, _) ,
2586
- Inst :: Bitcast ( a, _) ,
2587
- Inst :: AccessChain ( a_ptr, a_base_ptr, SpirvConst :: U32 ( 0 ) ) ,
2588
- Inst :: Store ( a_st_dst, a_st_val) ,
2589
- Inst :: AccessChain ( b_ptr, b_base_ptr, SpirvConst :: U32 ( 1 ) ) ,
2590
- Inst :: Store ( b_st_dst, b_st_val) ,
2591
- ] if a_base_ptr == b_base_ptr
2592
- && ( a, b) == ( a_st_val, b_st_val)
2593
- && ( a_ptr, b_ptr) == ( a_st_dst, b_st_dst) =>
2594
- {
2595
- require_local_var ( a_base_ptr) ;
2596
- a_base_ptr
2597
- }
2598
- _ => return None ,
2599
- } ;
2670
+ Inst :: Call ( call_ret_id, callee_id, ref call_args) ,
2671
+ Inst :: CompositeExtract ( a, a_parent_pair, 0 ) ,
2672
+ Inst :: CompositeExtract ( b, b_parent_pair, 1 ) ,
2673
+ ] if [ a_parent_pair, b_parent_pair] == [ call_ret_id; 2 ] => self
2674
+ . fmt_rt_arg_new_fn_ids_to_ty_and_spec
2675
+ . borrow ( )
2676
+ . get ( & callee_id)
2677
+ . and_then ( |& ( ty, spec) | match call_args[ ..] {
2678
+ [ x] => {
2679
+ decoded_format_args
2680
+ . ref_arg_ids_with_ty_and_spec
2681
+ . push ( ( x, ty, spec) ) ;
2682
+ Some ( ( a, b) )
2683
+ }
2684
+ _ => None ,
2685
+ } ) ,
2686
+ _ => None ,
2687
+ }
2688
+ . ok_or_else ( || {
2689
+ FormatArgsNotRecognized ( format ! (
2690
+ "fmt::rt::Argument::new call sequence ({rt_arg_new_call_insts:?})"
2691
+ ) )
2692
+ } ) ?;
2600
2693
2601
- // HACK(eddyb) this is only split to allow variable name reuse.
2602
- let ( copy_loads, copy_stores) =
2603
- copy_from_rt_arg_var_to_rt_args_array_insts. split_at ( 4 ) ;
2604
- let ( a, b) = match copy_loads[ ..] {
2605
- [
2606
- Inst :: AccessChain ( a_ptr, a_base_ptr, SpirvConst :: U32 ( 0 ) ) ,
2607
- Inst :: Load ( a_ld_val, a_ld_src) ,
2608
- Inst :: AccessChain ( b_ptr, b_base_ptr, SpirvConst :: U32 ( 1 ) ) ,
2609
- Inst :: Load ( b_ld_val, b_ld_src) ,
2610
- ] if [ a_base_ptr, b_base_ptr] == [ rt_arg_var_id; 2 ]
2611
- && ( a_ptr, b_ptr) == ( a_ld_src, b_ld_src) =>
2612
- {
2613
- ( a_ld_val, b_ld_val)
2614
- }
2615
- _ => return None ,
2616
- } ;
2617
- match copy_stores[ ..] {
2694
+ match store_to_rt_args_array_insts[ ..] {
2618
2695
[
2619
2696
Inst :: InBoundsAccessChain (
2620
2697
array_slot_ptr,
@@ -2630,7 +2707,11 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2630
2707
&& [ a_base_ptr, b_base_ptr] == [ array_slot_ptr; 2 ]
2631
2708
&& ( a, b) == ( a_st_val, b_st_val)
2632
2709
&& ( a_ptr, b_ptr) == ( a_st_dst, b_st_dst) => { }
2633
- _ => return None ,
2710
+ _ => {
2711
+ return Err ( FormatArgsNotRecognized ( format ! (
2712
+ "[fmt::rt::Argument; N] stores sequence ({store_to_rt_args_array_insts:?})"
2713
+ ) ) ) ;
2714
+ }
2634
2715
}
2635
2716
}
2636
2717
}
@@ -2639,11 +2720,36 @@ impl<'a, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'tcx> {
2639
2720
// confirmed above to be the first instruction of `format_args!`.
2640
2721
func. blocks [ block_idx]
2641
2722
. instructions
2642
- . truncate ( taken_inst_idx_range. start ) ;
2723
+ . truncate ( taken_inst_idx_range. start . get ( ) ) ;
2643
2724
2644
- None
2725
+ Ok ( decoded_format_args )
2645
2726
} ;
2646
- remove_format_args_if_possible ( ) ;
2727
+
2728
+ match try_decode_and_remove_format_args ( ) {
2729
+ Ok ( DecodedFormatArgs { } ) => { }
2730
+ Err ( FormatArgsNotRecognized ( step) ) => {
2731
+ if let Some ( current_span) = self . current_span {
2732
+ let mut warn = self . tcx . sess . struct_span_warn (
2733
+ current_span,
2734
+ "failed to find and remove `format_args!` construction for this `panic!`" ,
2735
+ ) ;
2736
+
2737
+ warn. note (
2738
+ "compilation may later fail due to leftover `format_args!` internals" ,
2739
+ ) ;
2740
+
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`)" )
2743
+ . help ( "check `.cargo` and environment variables for potential overrides" )
2744
+ . help ( "(or, if not using `spirv-builder` at all, add the flag manually)" ) ;
2745
+ } else {
2746
+ warn. note ( format ! ( "[RUST-GPU BUG] bailed from {step}" ) ) ;
2747
+ }
2748
+
2749
+ warn. emit ( ) ;
2750
+ }
2751
+ }
2752
+ }
2647
2753
2648
2754
// HACK(eddyb) redirect any possible panic call to an abort, to avoid
2649
2755
// needing to materialize `&core::panic::Location` or `format_args!`.
0 commit comments