@@ -37,14 +37,25 @@ pub fn inline(sess: &Session, module: &mut Module) -> super::Result<()> {
37
37
. find ( |inst| inst. class . opcode == Op :: TypeVoid )
38
38
. map ( |inst| inst. result_id . unwrap ( ) )
39
39
. unwrap_or ( 0 ) ;
40
-
40
+ let ptr_map: FxHashMap < _ , _ > = module
41
+ . types_global_values
42
+ . iter ( )
43
+ . filter_map ( |inst| {
44
+ if inst. class . opcode == Op :: TypePointer
45
+ && inst. operands [ 0 ] . unwrap_storage_class ( ) == StorageClass :: Function
46
+ {
47
+ Some ( ( inst. operands [ 1 ] . unwrap_id_ref ( ) , inst. result_id . unwrap ( ) ) )
48
+ } else {
49
+ None
50
+ }
51
+ } )
52
+ . collect ( ) ;
41
53
let invalid_args = module. functions . iter ( ) . flat_map ( get_invalid_args) . collect ( ) ;
42
-
43
- // Drop all the functions we'll be inlining. (This also means we won't waste time processing
44
- // inlines in functions that will get inlined)
45
54
let mut inliner = Inliner {
46
55
header : module. header . as_mut ( ) . unwrap ( ) ,
56
+ types_global_values : & mut module. types_global_values ,
47
57
void,
58
+ ptr_map,
48
59
functions : & functions,
49
60
needs_inline : & to_delete,
50
61
invalid_args,
@@ -272,7 +283,9 @@ fn args_invalid(invalid_args: &FxHashSet<Word>, call: &Instruction) -> bool {
272
283
273
284
struct Inliner < ' m , ' map > {
274
285
header : & ' m mut ModuleHeader ,
286
+ types_global_values : & ' m mut Vec < Instruction > ,
275
287
void : Word ,
288
+ ptr_map : FxHashMap < Word , Word > ,
276
289
functions : & ' map FunctionMap ,
277
290
needs_inline : & ' map [ bool ] ,
278
291
invalid_args : FxHashSet < Word > ,
@@ -285,6 +298,25 @@ impl Inliner<'_, '_> {
285
298
result
286
299
}
287
300
301
+ fn ptr_ty ( & mut self , pointee : Word ) -> Word {
302
+ let existing = self . ptr_map . get ( & pointee) ;
303
+ if let Some ( existing) = existing {
304
+ return * existing;
305
+ }
306
+ let inst_id = self . id ( ) ;
307
+ self . types_global_values . push ( Instruction :: new (
308
+ Op :: TypePointer ,
309
+ None ,
310
+ Some ( inst_id) ,
311
+ vec ! [
312
+ Operand :: StorageClass ( StorageClass :: Function ) ,
313
+ Operand :: IdRef ( pointee) ,
314
+ ] ,
315
+ ) ) ;
316
+ self . ptr_map . insert ( pointee, inst_id) ;
317
+ inst_id
318
+ }
319
+
288
320
fn inline_fn ( & mut self , functions : & mut [ Function ] , index : usize ) {
289
321
let mut function = take ( & mut functions[ index] ) ;
290
322
let mut block_idx = 0 ;
@@ -348,23 +380,27 @@ impl Inliner<'_, '_> {
348
380
} ) ;
349
381
let mut rewrite_rules = callee_parameters. zip ( call_arguments) . collect ( ) ;
350
382
383
+ let return_variable = if call_result_type. is_some ( ) {
384
+ Some ( self . id ( ) )
385
+ } else {
386
+ None
387
+ } ;
351
388
let return_jump = self . id ( ) ;
352
389
// Rewrite OpReturns of the callee.
353
- let ( mut inlined_blocks, phi_pairs) = get_inlined_blocks ( callee, return_jump) ;
390
+ let ( mut inlined_blocks, return_values) =
391
+ get_inlined_blocks ( callee, return_variable, return_jump) ;
354
392
// Clone the IDs of the callee, because otherwise they'd be defined multiple times if the
355
393
// fn is inlined multiple times.
356
394
self . add_clone_id_rules ( & mut rewrite_rules, & inlined_blocks) ;
357
395
// If any of the OpReturns were invalid, return will also be invalid.
358
- for ( value, _ ) in & phi_pairs {
396
+ for value in & return_values {
359
397
if self . invalid_args . contains ( value) {
360
398
self . invalid_args . insert ( call_result_id) ;
361
399
self . invalid_args
362
400
. insert ( * rewrite_rules. get ( value) . unwrap_or ( value) ) ;
363
401
}
364
402
}
365
403
apply_rewrite_rules ( & rewrite_rules, & mut inlined_blocks) ;
366
- // unnecessary: invalidate_more_args(&rewrite_rules, &mut self.invalid_args);
367
- // as no values from inside the inlined function ever make it directly out.
368
404
369
405
// Split the block containing the OpFunctionCall into two, around the call.
370
406
let mut post_call_block_insts = caller. blocks [ block_idx]
@@ -374,27 +410,32 @@ impl Inliner<'_, '_> {
374
410
let call = caller. blocks [ block_idx] . instructions . pop ( ) . unwrap ( ) ;
375
411
assert ! ( call. class. opcode == Op :: FunctionCall ) ;
376
412
413
+ if let Some ( call_result_type) = call_result_type {
414
+ // Generate the storage space for the return value: Do this *after* the split above,
415
+ // because if block_idx=0, inserting a variable here shifts call_index.
416
+ insert_opvariable (
417
+ & mut caller. blocks [ 0 ] ,
418
+ self . ptr_ty ( call_result_type) ,
419
+ return_variable. unwrap ( ) ,
420
+ ) ;
421
+ }
422
+
377
423
// Move the variables over from the inlined function to here.
378
424
let mut callee_header = take ( & mut inlined_blocks[ 0 ] ) . instructions ;
379
425
// TODO: OpLine handling
380
426
let num_variables = callee_header. partition_point ( |inst| inst. class . opcode == Op :: Variable ) ;
381
427
// Rather than fuse blocks, generate a new jump here. Branch fusing will take care of
382
428
// it, and we maintain the invariant that current block has finished processing.
383
- let first_block_id = self . id ( ) ;
429
+ let jump_to = self . id ( ) ;
384
430
inlined_blocks[ 0 ] = Block {
385
- label : Some ( Instruction :: new (
386
- Op :: Label ,
387
- None ,
388
- Some ( first_block_id) ,
389
- vec ! [ ] ,
390
- ) ) ,
431
+ label : Some ( Instruction :: new ( Op :: Label , None , Some ( jump_to) , vec ! [ ] ) ) ,
391
432
instructions : callee_header. split_off ( num_variables) ,
392
433
} ;
393
434
caller. blocks [ block_idx] . instructions . push ( Instruction :: new (
394
435
Op :: Branch ,
395
436
None ,
396
437
None ,
397
- vec ! [ Operand :: IdRef ( first_block_id ) ] ,
438
+ vec ! [ Operand :: IdRef ( jump_to ) ] ,
398
439
) ) ;
399
440
// Move the OpVariables of the callee to the caller.
400
441
insert_opvariables ( & mut caller. blocks [ 0 ] , callee_header) ;
@@ -405,17 +446,10 @@ impl Inliner<'_, '_> {
405
446
post_call_block_insts. insert (
406
447
0 ,
407
448
Instruction :: new (
408
- Op :: Phi ,
449
+ Op :: Load ,
409
450
Some ( call_result_type) ,
410
451
Some ( call_result_id) ,
411
- phi_pairs
412
- . into_iter ( )
413
- . flat_map ( |( value, parent) | {
414
- use std:: iter;
415
- iter:: once ( Operand :: IdRef ( * rewrite_rules. get ( & value) . unwrap_or ( & value) ) )
416
- . chain ( iter:: once ( Operand :: IdRef ( rewrite_rules[ & parent] ) ) )
417
- } )
418
- . collect ( ) ,
452
+ vec ! [ Operand :: IdRef ( return_variable. unwrap( ) ) ] ,
419
453
) ,
420
454
) ;
421
455
}
@@ -448,21 +482,53 @@ impl Inliner<'_, '_> {
448
482
}
449
483
}
450
484
451
- fn get_inlined_blocks ( function : & Function , return_jump : Word ) -> ( Vec < Block > , Vec < ( Word , Word ) > ) {
485
+ fn get_inlined_blocks (
486
+ function : & Function ,
487
+ return_variable : Option < Word > ,
488
+ return_jump : Word ,
489
+ ) -> ( Vec < Block > , Vec < Word > ) {
452
490
let mut blocks = function. blocks . clone ( ) ;
453
- let mut phipairs = Vec :: new ( ) ;
491
+ let mut values = Vec :: new ( ) ;
454
492
for block in & mut blocks {
455
493
let last = block. instructions . last ( ) . unwrap ( ) ;
456
494
if let Op :: Return | Op :: ReturnValue = last. class . opcode {
457
495
if Op :: ReturnValue == last. class . opcode {
458
496
let return_value = last. operands [ 0 ] . id_ref_any ( ) . unwrap ( ) ;
459
- phipairs. push ( ( return_value, block. label_id ( ) . unwrap ( ) ) ) ;
497
+ values. push ( return_value) ;
498
+ block. instructions . insert (
499
+ block. instructions . len ( ) - 1 ,
500
+ Instruction :: new (
501
+ Op :: Store ,
502
+ None ,
503
+ None ,
504
+ vec ! [
505
+ Operand :: IdRef ( return_variable. unwrap( ) ) ,
506
+ Operand :: IdRef ( return_value) ,
507
+ ] ,
508
+ ) ,
509
+ ) ;
510
+ } else {
511
+ assert ! ( return_variable. is_none( ) ) ;
460
512
}
461
513
* block. instructions . last_mut ( ) . unwrap ( ) =
462
514
Instruction :: new ( Op :: Branch , None , None , vec ! [ Operand :: IdRef ( return_jump) ] ) ;
463
515
}
464
516
}
465
- ( blocks, phipairs)
517
+ ( blocks, values)
518
+ }
519
+
520
+ fn insert_opvariable ( block : & mut Block , ptr_ty : Word , result_id : Word ) {
521
+ let index = block
522
+ . instructions
523
+ . partition_point ( |inst| inst. class . opcode == Op :: Variable ) ;
524
+
525
+ let inst = Instruction :: new (
526
+ Op :: Variable ,
527
+ Some ( ptr_ty) ,
528
+ Some ( result_id) ,
529
+ vec ! [ Operand :: StorageClass ( StorageClass :: Function ) ] ,
530
+ ) ;
531
+ block. instructions . insert ( index, inst)
466
532
}
467
533
468
534
fn insert_opvariables ( block : & mut Block , insts : Vec < Instruction > ) {
@@ -474,7 +540,6 @@ fn insert_opvariables(block: &mut Block, insts: Vec<Instruction>) {
474
540
475
541
fn fuse_trivial_branches ( function : & mut Function ) {
476
542
let mut chain_list = compute_outgoing_1to1_branches ( & function. blocks ) ;
477
- let mut rewrite_rules = FxHashMap :: default ( ) ;
478
543
479
544
for block_idx in 0 ..chain_list. len ( ) {
480
545
let mut next = chain_list[ block_idx] . take ( ) ;
@@ -490,16 +555,6 @@ fn fuse_trivial_branches(function: &mut Function) {
490
555
}
491
556
Some ( next_idx) => {
492
557
let mut dest_insts = take ( & mut function. blocks [ next_idx] . instructions ) ;
493
- dest_insts. retain ( |inst| {
494
- if inst. class . opcode == Op :: Phi {
495
- assert_eq ! ( inst. operands. len( ) , 2 ) ;
496
- rewrite_rules
497
- . insert ( inst. result_id . unwrap ( ) , inst. operands [ 0 ] . unwrap_id_ref ( ) ) ;
498
- false
499
- } else {
500
- true
501
- }
502
- } ) ;
503
558
let self_insts = & mut function. blocks [ block_idx] . instructions ;
504
559
self_insts. pop ( ) ; // pop the branch
505
560
self_insts. append ( & mut dest_insts) ;
@@ -509,14 +564,6 @@ fn fuse_trivial_branches(function: &mut Function) {
509
564
}
510
565
}
511
566
function. blocks . retain ( |b| !b. instructions . is_empty ( ) ) ;
512
- // Calculate a closure, as these rules can be transitive
513
- let mut rewrite_rules_new = rewrite_rules. clone ( ) ;
514
- for value in rewrite_rules_new. values_mut ( ) {
515
- while let Some ( next) = rewrite_rules. get ( value) {
516
- * value = * next;
517
- }
518
- }
519
- apply_rewrite_rules ( & rewrite_rules_new, & mut function. blocks ) ;
520
567
}
521
568
522
569
fn compute_outgoing_1to1_branches ( blocks : & [ Block ] ) -> Vec < Option < usize > > {
0 commit comments