@@ -17,6 +17,8 @@ use rustc_session::Session;
17
17
use smallvec:: SmallVec ;
18
18
use std:: mem;
19
19
20
+ type FunctionMap = FxHashMap < Word , Function > ;
21
+
20
22
// FIXME(eddyb) this is a bit silly, but this keeps being repeated everywhere.
21
23
fn next_id ( header : & mut ModuleHeader ) -> Word {
22
24
let result = header. bound ;
@@ -28,9 +30,6 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
28
30
// This algorithm gets real sad if there's recursion - but, good news, SPIR-V bans recursion
29
31
deny_recursion_in_module ( sess, module) ?;
30
32
31
- // Compute the call-graph that will drive (inside-out, aka bottom-up) inlining.
32
- let ( call_graph, func_id_to_idx) = CallGraph :: collect_with_func_id_to_idx ( module) ;
33
-
34
33
let custom_ext_inst_set_import = module
35
34
. ext_inst_imports
36
35
. iter ( )
@@ -40,7 +39,62 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
40
39
} )
41
40
. map ( |inst| inst. result_id . unwrap ( ) ) ;
42
41
43
- /*
42
+ // HACK(eddyb) compute the set of functions that may `Abort` *transitively*,
43
+ // which is only needed because of how we inline (sometimes it's outside-in,
44
+ // aka top-down, instead of always being inside-out, aka bottom-up).
45
+ //
46
+ // (inlining is needed in the first place because our custom `Abort`
47
+ // instructions get lowered to a simple `OpReturn` in entry-points, but
48
+ // that requires that they get inlined all the way up to the entry-points)
49
+ let functions_that_may_abort = custom_ext_inst_set_import
50
+ . map ( |custom_ext_inst_set_import| {
51
+ let mut may_abort_by_id = FxHashSet :: default ( ) ;
52
+
53
+ // FIXME(eddyb) use this `CallGraph` abstraction more during inlining.
54
+ let call_graph = CallGraph :: collect ( module) ;
55
+ for func_idx in call_graph. post_order ( ) {
56
+ let func_id = module. functions [ func_idx] . def_id ( ) . unwrap ( ) ;
57
+
58
+ let any_callee_may_abort = call_graph. callees [ func_idx] . iter ( ) . any ( |& callee_idx| {
59
+ may_abort_by_id. contains ( & module. functions [ callee_idx] . def_id ( ) . unwrap ( ) )
60
+ } ) ;
61
+ if any_callee_may_abort {
62
+ may_abort_by_id. insert ( func_id) ;
63
+ continue ;
64
+ }
65
+
66
+ let may_abort_directly = module. functions [ func_idx] . blocks . iter ( ) . any ( |block| {
67
+ match & block. instructions [ ..] {
68
+ [ .., last_normal_inst, terminator_inst]
69
+ if last_normal_inst. class . opcode == Op :: ExtInst
70
+ && last_normal_inst. operands [ 0 ] . unwrap_id_ref ( )
71
+ == custom_ext_inst_set_import
72
+ && CustomOp :: decode_from_ext_inst ( last_normal_inst)
73
+ == CustomOp :: Abort =>
74
+ {
75
+ assert_eq ! ( terminator_inst. class. opcode, Op :: Unreachable ) ;
76
+ true
77
+ }
78
+
79
+ _ => false ,
80
+ }
81
+ } ) ;
82
+ if may_abort_directly {
83
+ may_abort_by_id. insert ( func_id) ;
84
+ }
85
+ }
86
+
87
+ may_abort_by_id
88
+ } )
89
+ . unwrap_or_default ( ) ;
90
+
91
+ let functions = module
92
+ . functions
93
+ . iter ( )
94
+ . map ( |f| ( f. def_id ( ) . unwrap ( ) , f. clone ( ) ) )
95
+ . collect ( ) ;
96
+ let legal_globals = LegalGlobal :: gather_from_module ( module) ;
97
+
44
98
// Drop all the functions we'll be inlining. (This also means we won't waste time processing
45
99
// inlines in functions that will get inlined)
46
100
let mut dropped_ids = FxHashSet :: default ( ) ;
@@ -69,9 +123,6 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
69
123
) ) ;
70
124
}
71
125
}
72
- */
73
-
74
- let legal_globals = LegalGlobal :: gather_from_module ( module) ;
75
126
76
127
let header = module. header . as_mut ( ) . unwrap ( ) ;
77
128
// FIXME(eddyb) clippy false positive (separate `map` required for borrowck).
@@ -103,8 +154,6 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
103
154
id
104
155
} ) ,
105
156
106
- func_id_to_idx,
107
-
108
157
id_to_name : module
109
158
. debug_names
110
159
. iter ( )
@@ -124,61 +173,22 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
124
173
annotations : & mut module. annotations ,
125
174
types_global_values : & mut module. types_global_values ,
126
175
127
- legal_globals,
128
-
129
- // NOTE(eddyb) this is needed because our custom `Abort` instructions get
130
- // lowered to a simple `OpReturn` in entry-points, but that requires that
131
- // they get inlined all the way up to the entry-points in the first place.
132
- functions_that_may_abort : module
133
- . functions
134
- . iter ( )
135
- . filter_map ( |func| {
136
- let custom_ext_inst_set_import = custom_ext_inst_set_import?;
137
- func. blocks
138
- . iter ( )
139
- . any ( |block| match & block. instructions [ ..] {
140
- [ .., last_normal_inst, terminator_inst]
141
- if last_normal_inst. class . opcode == Op :: ExtInst
142
- && last_normal_inst. operands [ 0 ] . unwrap_id_ref ( )
143
- == custom_ext_inst_set_import
144
- && CustomOp :: decode_from_ext_inst ( last_normal_inst)
145
- == CustomOp :: Abort =>
146
- {
147
- assert_eq ! ( terminator_inst. class. opcode, Op :: Unreachable ) ;
148
- true
149
- }
150
-
151
- _ => false ,
152
- } )
153
- . then_some ( func. def_id ( ) . unwrap ( ) )
154
- } )
155
- . collect ( ) ,
176
+ functions : & functions,
177
+ legal_globals : & legal_globals,
178
+ functions_that_may_abort : & functions_that_may_abort,
156
179
} ;
157
-
158
- let mut functions: Vec < _ > = mem:: take ( & mut module. functions )
159
- . into_iter ( )
160
- . map ( Ok )
161
- . collect ( ) ;
162
-
163
- // Inline functions in post-order (aka inside-out aka bottom-out) - that is,
164
- // callees are processed before their callers, to avoid duplicating work.
165
- for func_idx in call_graph. post_order ( ) {
166
- let mut function = mem:: replace ( & mut functions[ func_idx] , Err ( FuncIsBeingInlined ) ) . unwrap ( ) ;
167
- inliner. inline_fn ( & mut function, & functions) ;
168
- fuse_trivial_branches ( & mut function) ;
169
- functions[ func_idx] = Ok ( function) ;
180
+ for function in & mut module. functions {
181
+ inliner. inline_fn ( function) ;
182
+ fuse_trivial_branches ( function) ;
170
183
}
171
184
172
- module. functions = functions. into_iter ( ) . map ( |func| func. unwrap ( ) ) . collect ( ) ;
173
-
174
- /*
175
185
// Drop OpName etc. for inlined functions
176
186
module. debug_names . retain ( |inst| {
177
187
!inst
178
188
. operands
179
189
. iter ( )
180
190
. any ( |op| op. id_ref_any ( ) . is_some_and ( |id| dropped_ids. contains ( & id) ) )
181
- });*/
191
+ } ) ;
182
192
183
193
Ok ( ( ) )
184
194
}
@@ -446,27 +456,19 @@ fn should_inline(
446
456
Ok ( callee_control. contains ( FunctionControl :: INLINE ) )
447
457
}
448
458
449
- /// Helper error type for `Inliner`'s `functions` field, indicating a `Function`
450
- /// was taken out of its slot because it's being inlined.
451
- #[ derive( Debug ) ]
452
- struct FuncIsBeingInlined ;
453
-
454
459
// Steps:
455
460
// Move OpVariable decls
456
461
// Rewrite return
457
462
// Renumber IDs
458
463
// Insert blocks
459
464
460
- struct Inliner < ' m > {
465
+ struct Inliner < ' m , ' map > {
461
466
/// ID of `OpExtInstImport` for our custom "extended instruction set"
462
467
/// (see `crate::custom_insts` for more details).
463
468
custom_ext_inst_set_import : Word ,
464
469
465
470
op_type_void_id : Word ,
466
471
467
- /// Map from each function's ID to its index in `functions`.
468
- func_id_to_idx : FxHashMap < Word , usize > ,
469
-
470
472
/// Pre-collected `OpName`s, that can be used to find any function's name
471
473
/// during inlining (to be able to generate debuginfo that uses names).
472
474
id_to_name : FxHashMap < Word , & ' m str > ,
@@ -483,12 +485,13 @@ struct Inliner<'m> {
483
485
annotations : & ' m mut Vec < Instruction > ,
484
486
types_global_values : & ' m mut Vec < Instruction > ,
485
487
486
- legal_globals : FxHashMap < Word , LegalGlobal > ,
487
- functions_that_may_abort : FxHashSet < Word > ,
488
+ functions : & ' map FunctionMap ,
489
+ legal_globals : & ' map FxHashMap < Word , LegalGlobal > ,
490
+ functions_that_may_abort : & ' map FxHashSet < Word > ,
488
491
// rewrite_rules: FxHashMap<Word, Word>,
489
492
}
490
493
491
- impl Inliner < ' _ > {
494
+ impl Inliner < ' _ , ' _ > {
492
495
fn id ( & mut self ) -> Word {
493
496
next_id ( self . header )
494
497
}
@@ -533,29 +536,19 @@ impl Inliner<'_> {
533
536
inst_id
534
537
}
535
538
536
- fn inline_fn (
537
- & mut self ,
538
- function : & mut Function ,
539
- functions : & [ Result < Function , FuncIsBeingInlined > ] ,
540
- ) {
539
+ fn inline_fn ( & mut self , function : & mut Function ) {
541
540
let mut block_idx = 0 ;
542
541
while block_idx < function. blocks . len ( ) {
543
542
// If we successfully inlined a block, then repeat processing on the same block, in
544
543
// case the newly inlined block has more inlined calls.
545
544
// TODO: This is quadratic
546
- if !self . inline_block ( function, block_idx, functions) {
547
- // TODO(eddyb) skip past the inlined callee without rescanning it.
545
+ if !self . inline_block ( function, block_idx) {
548
546
block_idx += 1 ;
549
547
}
550
548
}
551
549
}
552
550
553
- fn inline_block (
554
- & mut self ,
555
- caller : & mut Function ,
556
- block_idx : usize ,
557
- functions : & [ Result < Function , FuncIsBeingInlined > ] ,
558
- ) -> bool {
551
+ fn inline_block ( & mut self , caller : & mut Function , block_idx : usize ) -> bool {
559
552
// Find the first inlined OpFunctionCall
560
553
let call = caller. blocks [ block_idx]
561
554
. instructions
@@ -566,8 +559,8 @@ impl Inliner<'_> {
566
559
(
567
560
index,
568
561
inst,
569
- functions [ self . func_id_to_idx [ & inst . operands [ 0 ] . id_ref_any ( ) . unwrap ( ) ] ]
570
- . as_ref ( )
562
+ self . functions
563
+ . get ( & inst . operands [ 0 ] . id_ref_any ( ) . unwrap ( ) )
571
564
. unwrap ( ) ,
572
565
)
573
566
} )
@@ -577,8 +570,8 @@ impl Inliner<'_> {
577
570
call_inst : inst,
578
571
} ;
579
572
match should_inline (
580
- & self . legal_globals ,
581
- & self . functions_that_may_abort ,
573
+ self . legal_globals ,
574
+ self . functions_that_may_abort ,
582
575
f,
583
576
Some ( call_site) ,
584
577
) {
@@ -590,16 +583,6 @@ impl Inliner<'_> {
590
583
None => return false ,
591
584
Some ( call) => call,
592
585
} ;
593
-
594
- // Propagate "may abort" from callee to caller (i.e. as aborts get inlined).
595
- if self
596
- . functions_that_may_abort
597
- . contains ( & callee. def_id ( ) . unwrap ( ) )
598
- {
599
- self . functions_that_may_abort
600
- . insert ( caller. def_id ( ) . unwrap ( ) ) ;
601
- }
602
-
603
586
let call_result_type = {
604
587
let ty = call_inst. result_type . unwrap ( ) ;
605
588
if ty == self . op_type_void_id {
@@ -611,7 +594,6 @@ impl Inliner<'_> {
611
594
let call_result_id = call_inst. result_id . unwrap ( ) ;
612
595
613
596
// Get the debuginfo instructions that apply to the call.
614
- // TODO(eddyb) only one instruction should be necessary here w/ bottom-up.
615
597
let custom_ext_inst_set_import = self . custom_ext_inst_set_import ;
616
598
let call_debug_insts = caller. blocks [ block_idx] . instructions [ ..call_index]
617
599
. iter ( )
@@ -886,7 +868,6 @@ impl Inliner<'_> {
886
868
..
887
869
} = * self ;
888
870
889
- // TODO(eddyb) kill this as it shouldn't be needed for bottom-up inline.
890
871
// HACK(eddyb) this is terrible, but we have to deal with it because of
891
872
// how this inliner is outside-in, instead of inside-out, meaning that
892
873
// context builds up "outside" of the callee blocks, inside the caller.
0 commit comments