@@ -31,9 +31,10 @@ use std::cell::{Cell, RefCell};
31
31
use std:: mem:: size_of;
32
32
use rustc_data_structures:: bit_set:: BitSet ;
33
33
use rustc_data_structures:: indexed_vec:: { IndexVec , Idx } ;
34
- use rustc_data_structures:: graph:: dominators:: Dominators ;
34
+ use rustc_data_structures:: graph:: dominators:: DominatorFrontiers ;
35
35
use ykpack;
36
36
use ykpack:: LocalIndex as TirLocal ;
37
+ use rustc_data_structures:: fx:: FxHashSet ;
37
38
38
39
const SECTION_NAME : & ' static str = ".yk_tir" ;
39
40
const TMP_EXT : & ' static str = ".yk_tir.tmp" ;
@@ -53,6 +54,8 @@ struct ConvCx<'a, 'tcx, 'gcx> {
53
54
tcx : & ' a TyCtxt < ' a , ' tcx , ' gcx > ,
54
55
/// Maps TIR variables to their definition sites.
55
56
def_sites : RefCell < Vec < BitSet < BasicBlock > > > ,
57
+ /// Maps each block to the variable it defines. This is what Appel calls `A_{orig}`.
58
+ block_defines : RefCell < IndexVec < BasicBlock , FxHashSet < TirLocal > > > ,
56
59
/// Monotonically increasing number used to give TIR variables a unique ID.
57
60
next_tir_var : Cell < TirLocal > ,
58
61
/// A mapping from MIR variables to TIR variables.
@@ -64,13 +67,15 @@ struct ConvCx<'a, 'tcx, 'gcx> {
64
67
impl < ' a , ' tcx , ' gcx > ConvCx < ' a , ' tcx , ' gcx > {
65
68
fn new ( tcx : & ' a TyCtxt < ' a , ' tcx , ' gcx > , mir : & Mir < ' tcx > ) -> Self {
66
69
let var_map = IndexVec :: new ( ) ;
70
+ let num_blks = mir. basic_blocks ( ) . len ( ) ;
67
71
68
72
Self {
69
73
tcx,
70
74
def_sites : RefCell :: new ( Vec :: new ( ) ) ,
75
+ block_defines : RefCell :: new ( IndexVec :: from_elem_n ( FxHashSet :: default ( ) , num_blks) ) ,
71
76
next_tir_var : Cell :: new ( 0 ) ,
72
77
var_map : RefCell :: new ( var_map) ,
73
- num_blks : mir . basic_blocks ( ) . len ( ) ,
78
+ num_blks : num_blks ,
74
79
}
75
80
}
76
81
@@ -101,8 +106,9 @@ impl<'a, 'tcx, 'gcx> ConvCx<'a, 'tcx, 'gcx> {
101
106
} )
102
107
}
103
108
104
- fn def_sites ( self ) -> Vec < BitSet < BasicBlock > > {
105
- self . def_sites . into_inner ( )
109
+ /// Finalise the conversion context, returning the definition sites and block defines mappings.
110
+ fn done ( self ) -> ( Vec < BitSet < BasicBlock > > , IndexVec < BasicBlock , FxHashSet < TirLocal > > ) {
111
+ ( self . def_sites . into_inner ( ) , self . block_defines . into_inner ( ) )
106
112
}
107
113
108
114
/// Add `bb` as a definition site of the TIR variable `var`.
@@ -118,6 +124,9 @@ impl<'a, 'tcx, 'gcx> ConvCx<'a, 'tcx, 'gcx> {
118
124
BitSet :: new_empty ( self . num_blks ) ) ;
119
125
}
120
126
sites[ var_usize] . insert ( bb) ;
127
+
128
+ // Also push into the inverse mapping (blocks to defined vars).
129
+ self . block_defines . borrow_mut ( ) [ bb] . insert ( var) ;
121
130
}
122
131
}
123
132
@@ -183,18 +192,19 @@ fn do_generate_tir<'a, 'tcx, 'gcx>(
183
192
let mir = tcx. optimized_mir ( * def_id) ;
184
193
let ccx = ConvCx :: new ( tcx, mir) ;
185
194
186
- // Get an initial TIR (not yet in SSA form).
187
- let pre_ssa_pack = ( & ccx, def_id, tcx. optimized_mir ( * def_id) ) . to_pack ( ) ;
188
-
189
- // Add PHI nodes.
190
- let phied_pack = PhiInserter :: new ( mir, pre_ssa_pack, ccx. def_sites ( ) ) . pack ( ) ;
195
+ let mut pack = ( & ccx, def_id, tcx. optimized_mir ( * def_id) ) . to_pack ( ) ;
196
+ {
197
+ let ykpack:: Pack :: Mir ( ykpack:: Mir { ref mut blocks, ..} ) = pack;
198
+ let ( def_sites, block_defines) = ccx. done ( ) ;
199
+ insert_phis ( blocks, mir, def_sites, block_defines) ;
200
+ }
191
201
192
202
// FIXME - rename variables with fresh SSA names.
193
203
194
204
if let Some ( ref mut e) = enc {
195
- e. serialise ( phied_pack ) ?;
205
+ e. serialise ( pack ) ?;
196
206
} else {
197
- write ! ( textdump_file. as_ref( ) . unwrap( ) , "{}" , phied_pack ) ?;
207
+ write ! ( textdump_file. as_ref( ) . unwrap( ) , "{}" , pack ) ?;
198
208
}
199
209
}
200
210
}
@@ -208,160 +218,50 @@ fn do_generate_tir<'a, 'tcx, 'gcx>(
208
218
Ok ( tir_path)
209
219
}
210
220
211
- /// Lazy computation of dominance frontiers.
212
- ///
213
- /// See p404 of the 2nd edition of the Appel book mentioned above.
221
+ /// Insert PHI nodes into the initial pre-SSA TIR pack.
214
222
///
215
- /// Since the frontier of one node may depend on the frontiers of other nodes (depending upon their
216
- /// relationships), we cache the frontiers so as to avoid computing things more than once.
217
- struct DominatorFrontiers < ' a , ' tcx > {
218
- /// The MIR we are working on.
219
- mir : & ' a Mir < ' tcx > ,
220
- /// The dominators of the above MIR.
221
- doms : Dominators < BasicBlock > ,
222
- /// The dominance frontier cache. Lazily computed. `None` means as-yet uncomputed.
223
- df : IndexVec < BasicBlock , Option < BitSet < BasicBlock > > > ,
224
- /// The number of MIR (and thus TIR) blocks.
225
- num_blks : usize ,
226
- }
227
-
228
- impl < ' a , ' tcx > DominatorFrontiers < ' a , ' tcx > {
229
- fn new ( mir : & ' a Mir < ' tcx > ) -> Self {
230
- let num_blks = mir. basic_blocks ( ) . len ( ) ;
231
- let df = IndexVec :: from_elem_n ( None , num_blks) ;
232
-
233
- Self {
234
- mir,
235
- doms : mir. dominators ( ) ,
236
- df,
237
- num_blks : mir. basic_blocks ( ) . len ( ) ,
238
- }
239
- }
240
-
241
- /// Get the dominance frontier of the given basic block, computing it if we have not already
242
- /// done so. Since computing the frontiers for a full CFG requests the same frontiers
243
- /// repeatedly, the requested frontier (and its dependencies) are inserted into a cache to
244
- /// avoid duplicate computations.
245
- fn frontier ( & mut self , n : BasicBlock ) -> & BitSet < BasicBlock > {
246
- if self . df [ n] . is_none ( ) {
247
- // We haven't yet computed dominator frontiers for this node. Compute them.
248
- let mut s: BitSet < BasicBlock > = BitSet :: new_empty ( self . num_blks ) ;
249
-
250
- // Append what Appel calls 'DF_{local}[n]'.
251
- for y in self . mir . basic_blocks ( ) [ n] . terminator ( ) . successors ( ) {
252
- if self . doms . immediate_dominator ( * y) != n {
253
- s. insert ( * y) ;
254
- }
255
- }
256
-
257
- // The second stage of the algorithm needs the children nodes in the dominator tree.
258
- let mut children = Vec :: new ( ) ;
259
- for ( b, _) in self . mir . basic_blocks ( ) . iter_enumerated ( ) {
260
- let b = BasicBlock :: from_u32 ( b. as_u32 ( ) ) ;
261
- if self . doms . is_dominated_by ( b, n) && b != n {
262
- children. push ( b) ;
263
- // Force the frontier of `b` into the cache (`self.df`). Doing this here avoids
264
- // a simultaneous mutable + immutable borrow of `self` in the final stage of
265
- // the algorithm.
266
- self . frontier ( b) ;
267
- }
268
- }
269
-
270
- // Append what Appel calls `DF_{up}[c]` for each dominator tree child `c` of `n`.
271
- for c in children {
272
- for w in self . df [ c] . as_ref ( ) . unwrap ( ) . iter ( ) {
273
- if n == w || !self . doms . is_dominated_by ( w, n) {
274
- s. insert ( w) ;
223
+ /// Algorithm reference:
224
+ /// Bottom of p406 of 'Modern Compiler Implementation in Java (2nd ed.)' by Andrew Appel.
225
+ fn insert_phis ( blocks : & mut Vec < ykpack:: BasicBlock > , mir : & Mir ,
226
+ mut def_sites : Vec < BitSet < BasicBlock > > ,
227
+ a_orig : IndexVec < BasicBlock , FxHashSet < TirLocal > > ) {
228
+ let doms = mir. dominators ( ) ;
229
+ let df = DominatorFrontiers :: new ( mir, & doms) ;
230
+ let num_tir_vars = def_sites. len ( ) ;
231
+ let num_tir_blks = a_orig. len ( ) ;
232
+
233
+ let mut a_phi: Vec < BitSet < TirLocal > > = Vec :: with_capacity ( num_tir_blks) ;
234
+ a_phi. resize ( num_tir_blks, BitSet :: new_empty ( num_tir_vars) ) ;
235
+
236
+ // We don't need the elements of `def_sites` again past this point, so we can take them out
237
+ // of `def_sites` with a draining iterator and mutate in-place.
238
+ for ( a, mut w) in def_sites. drain ( ..) . enumerate ( ) {
239
+ while !w. is_empty ( ) {
240
+ let n = bitset_pop ( & mut w) ;
241
+ for y in df. frontier ( n) . iter ( ) {
242
+ let y_usize = y. index ( ) ;
243
+ // `def_sites` is guaranteed to only contain indices expressible by `u32`.
244
+ let a_u32 = a as u32 ;
245
+ if !a_phi[ y_usize] . contains ( a_u32) {
246
+ a_phi[ y_usize] . insert ( a_u32) ;
247
+ if !a_orig[ y] . contains ( & a_u32) {
248
+ // The assertion in `tir_var()` has already checked the cast is safe.
249
+ insert_phi ( & mut blocks[ y_usize] , a as u32 , mir. predecessors_for ( y) . len ( ) ) ;
250
+ w. insert ( y) ;
275
251
}
276
252
}
277
253
}
278
- self . df [ n] = Some ( s) ;
279
254
}
280
-
281
- self . df [ n] . as_ref ( ) . unwrap ( )
282
255
}
283
256
}
284
257
285
- /// This struct deals with inserting PHI nodes into the initial pre-SSA TIR pack.
286
- ///
287
- /// See the bottom of p406 of the 2nd edition of the Appel book mentioned above.
288
- struct PhiInserter < ' a , ' tcx > {
289
- mir : & ' a Mir < ' tcx > ,
290
- pack : ykpack:: Pack ,
291
- def_sites : Vec < BitSet < BasicBlock > > ,
258
+ fn insert_phi ( block : & mut ykpack:: BasicBlock , var : TirLocal , arity : usize ) {
259
+ let lhs = ykpack:: Place :: Local ( var) ;
260
+ let rhs_vars = ( 0 ..arity) . map ( |_| lhs. clone ( ) ) . collect ( ) ;
261
+ let rhs = ykpack:: Rvalue :: Phi ( rhs_vars) ;
262
+ block. stmts . insert ( 0 , ykpack:: Statement :: Assign ( lhs, rhs) ) ;
292
263
}
293
264
294
- impl < ' a , ' tcx > PhiInserter < ' a , ' tcx > {
295
- fn new ( mir : & ' a Mir < ' tcx > , pack : ykpack:: Pack , def_sites : Vec < BitSet < BasicBlock > > ) -> Self {
296
- Self {
297
- mir,
298
- pack,
299
- def_sites,
300
- }
301
- }
302
-
303
- /// Insert PHI nodes, returning the mutated pack.
304
- fn pack ( mut self ) -> ykpack:: Pack {
305
- let mut df = DominatorFrontiers :: new ( self . mir ) ;
306
- let num_tir_vars = self . def_sites . len ( ) ;
307
-
308
- // We first need a mapping from block to the variables it defines. Appel calls this
309
- // `A_{orig}`. We can derive this from our definition sites.
310
- let ( a_orig, num_tir_blks) = {
311
- let ykpack:: Pack :: Mir ( ykpack:: Mir { ref blocks, ..} ) = self . pack ;
312
- let num_tir_blks = blocks. len ( ) ;
313
-
314
- let mut a_orig: IndexVec < BasicBlock , BitSet < TirLocal > > =
315
- IndexVec :: from_elem_n ( BitSet :: new_empty ( num_tir_vars) , num_tir_blks) ;
316
- for ( a, def_blks) in self . def_sites . iter ( ) . enumerate ( ) {
317
- for bb in def_blks. iter ( ) {
318
- // `def_sites` is guaranteed to have at most `u32::max_value()` items.
319
- a_orig[ bb] . insert ( a as u32 ) ;
320
- }
321
- }
322
- ( a_orig, num_tir_blks)
323
- } ;
324
-
325
- let mut a_phi: Vec < BitSet < TirLocal > > = Vec :: with_capacity ( num_tir_blks) ;
326
- a_phi. resize ( num_tir_blks, BitSet :: new_empty ( num_tir_vars) ) ;
327
- // We don't need the elements of `def_sites` again past this point, so we can take them out
328
- // of `def_sites` with a draining iterator and mutate in-place.
329
- for ( a, mut w) in self . def_sites . drain ( ..) . enumerate ( ) {
330
- while !w. is_empty ( ) {
331
- let n = bitset_pop ( & mut w) ;
332
- for y in df. frontier ( n) . iter ( ) {
333
- let y_usize = y. index ( ) ;
334
- // `self.def_sites` is guaranteed to only contain indices expressible by `u32`.
335
- let a_u32 = a as u32 ;
336
- if !a_phi[ y_usize] . contains ( a_u32) {
337
- // Appel would insert the Phi node here. We use a second pass to keep the
338
- // borrow checker happy (self is already borrowed in this loop).
339
- a_phi[ y_usize] . insert ( a_u32) ;
340
- if !a_orig[ y] . contains ( a_u32) {
341
- w. insert ( y) ;
342
- }
343
- }
344
- }
345
- }
346
- }
347
-
348
- // `a_phi` now tells use where to insert PHI nodes.
349
- {
350
- let ykpack:: Pack :: Mir ( ykpack:: Mir { ref mut blocks, ..} ) = self . pack ;
351
- for ( bb, mut bb_data) in blocks. iter_mut ( ) . enumerate ( ) {
352
- for a in a_phi[ bb] . iter ( ) {
353
- let lhs = ykpack:: Place :: Local ( a) ;
354
- let num_preds = self . mir . predecessors_for ( BasicBlock :: new ( bb) ) . len ( ) ;
355
- let rhs_vars = ( 0 ..num_preds) . map ( |_| lhs. clone ( ) ) . collect ( ) ;
356
- let rhs = ykpack:: Rvalue :: Phi ( rhs_vars) ;
357
- bb_data. stmts . insert ( 0 , ykpack:: Statement :: Assign ( lhs, rhs) ) ;
358
- }
359
- }
360
- }
361
-
362
- self . pack
363
- }
364
- }
365
265
366
266
/// The trait for converting MIR data structures into a bytecode packs.
367
267
trait ToPack < T > {
0 commit comments