| ... |
... |
@@ -452,65 +452,12 @@ inferTagExpr _ (StgOpApp op args ty) |
|
452
|
452
|
inferTagExpr env (StgLet ext bind body)
|
|
453
|
453
|
= (info, StgLet ext bind' body')
|
|
454
|
454
|
where
|
|
455
|
|
- (env', bind') = inferTagBind env bind
|
|
456
|
|
- (info, body') = inferTagExpr env' body
|
|
|
455
|
+ (info, bind', body') = inferTagLet env (localFunsOfBind env False bind) bind body
|
|
457
|
456
|
|
|
458
|
457
|
inferTagExpr env (StgLetNoEscape ext bind body)
|
|
459
|
|
- | all (isJust . lookupJoinArgInfo env . fst) root_joins
|
|
460
|
|
- = (plain_info, StgLetNoEscape ext plain_bind plain_body)
|
|
461
|
|
- | otherwise
|
|
462
|
|
- = go initial_arg_infos
|
|
|
458
|
+ = (info, StgLetNoEscape ext bind' body')
|
|
463
|
459
|
where
|
|
464
|
|
- (plain_body_env, plain_bind) = inferTagBind env bind
|
|
465
|
|
- (plain_info, plain_body) = inferTagExpr plain_body_env body
|
|
466
|
|
-
|
|
467
|
|
- joins = root_joins ++ joinsInBind bind ++ joinsInExpr body
|
|
468
|
|
- join_ids = map fst joins
|
|
469
|
|
- initial_arg_infos = [replicate arity TagEPT | (_, arity) <- joins]
|
|
470
|
|
-
|
|
471
|
|
- go arg_infos
|
|
472
|
|
- | arg_infos == new_arg_infos
|
|
473
|
|
- = (info, StgLetNoEscape ext bind' body')
|
|
474
|
|
- | otherwise
|
|
475
|
|
- = go new_arg_infos
|
|
476
|
|
- where
|
|
477
|
|
- join_env = extendJoinArgEnv env (zipEqual join_ids arg_infos)
|
|
478
|
|
- (body_env, bind') = inferTagBind join_env bind
|
|
479
|
|
- (info, body') = inferTagExpr body_env body
|
|
480
|
|
- calls = collectJoinCalls (makeTagged join_env) join_ids bind' body'
|
|
481
|
|
- new_arg_infos =
|
|
482
|
|
- [ maybe initial_infos (combineArgs initial_infos) (lookupVarEnv calls join_id)
|
|
483
|
|
- | (join_id, initial_infos) <- zipEqual join_ids initial_arg_infos ]
|
|
484
|
|
-
|
|
485
|
|
- combineArgs = zipWithEqual combineAltInfo
|
|
486
|
|
-
|
|
487
|
|
- root_joins = joinsOf bind
|
|
488
|
|
-
|
|
489
|
|
- joinsOf (StgNonRec bndr rhs) = [joinOf bndr rhs]
|
|
490
|
|
- joinsOf (StgRec pairs) = [joinOf bndr rhs | (bndr, rhs) <- pairs]
|
|
491
|
|
-
|
|
492
|
|
- joinOf bndr (StgRhsClosure _ _ _ bndrs _ _)
|
|
493
|
|
- = (getBinderId env bndr, length bndrs)
|
|
494
|
|
- joinOf bndr (StgRhsCon {})
|
|
495
|
|
- = (getBinderId env bndr, idArity (getBinderId env bndr))
|
|
496
|
|
-
|
|
497
|
|
- joinsInBind (StgNonRec _ rhs) = joinsInRhs rhs
|
|
498
|
|
- joinsInBind (StgRec pairs) = concatMap (joinsInRhs . snd) pairs
|
|
499
|
|
-
|
|
500
|
|
- joinsInRhs (StgRhsClosure _ _ _ _ rhs _) = joinsInExpr rhs
|
|
501
|
|
- joinsInRhs (StgRhsCon {}) = []
|
|
502
|
|
-
|
|
503
|
|
- joinsInExpr (StgApp {}) = []
|
|
504
|
|
- joinsInExpr (StgConApp {}) = []
|
|
505
|
|
- joinsInExpr (StgLit {}) = []
|
|
506
|
|
- joinsInExpr (StgTick _ expr) = joinsInExpr expr
|
|
507
|
|
- joinsInExpr (StgOpApp {}) = []
|
|
508
|
|
- joinsInExpr (StgLet _ let_bind expr)
|
|
509
|
|
- = joinsInBind let_bind ++ joinsInExpr expr
|
|
510
|
|
- joinsInExpr (StgLetNoEscape _ let_bind expr)
|
|
511
|
|
- = joinsOf let_bind ++ joinsInBind let_bind ++ joinsInExpr expr
|
|
512
|
|
- joinsInExpr (StgCase scrut _ _ alts)
|
|
513
|
|
- = joinsInExpr scrut ++ concatMap (joinsInExpr . alt_rhs) alts
|
|
|
460
|
+ (info, bind', body') = inferTagLet env (localFunsOfBind env True bind) bind body
|
|
514
|
461
|
|
|
515
|
462
|
inferTagExpr in_env (StgCase scrut bndr ty alts)
|
|
516
|
463
|
-- Unboxed tuples get their info from the expression we scrutinise if any
|
| ... |
... |
@@ -564,28 +511,108 @@ inferTagExpr in_env (StgCase scrut bndr ty alts) |
|
564
|
511
|
(scrut_info, scrut') = inferTagExpr in_env scrut
|
|
565
|
512
|
bndr' = (getBinderId in_env bndr, TagVal TagEPT)
|
|
566
|
513
|
|
|
567
|
|
--- See Note [EPT signatures for join point arguments].
|
|
568
|
|
-collectJoinCalls
|
|
|
514
|
+localFunsOfBind
|
|
|
515
|
+ :: TagEnv p -> Bool -> GenStgBinding p -> [(Id, Int)]
|
|
|
516
|
+localFunsOfBind env is_join (StgNonRec bndr rhs)
|
|
|
517
|
+ = localFunOf env is_join bndr rhs
|
|
|
518
|
+localFunsOfBind env is_join (StgRec pairs)
|
|
|
519
|
+ = concatMap (uncurry (localFunOf env is_join)) pairs
|
|
|
520
|
+
|
|
|
521
|
+localFunOf
|
|
|
522
|
+ :: TagEnv p -> Bool -> BinderP p -> GenStgRhs p -> [(Id, Int)]
|
|
|
523
|
+localFunOf env is_join bndr (StgRhsClosure _ _ _ bndrs _ _)
|
|
|
524
|
+ | is_join || notNull bndrs
|
|
|
525
|
+ = [(getBinderId env bndr, length bndrs)]
|
|
|
526
|
+localFunOf _ _ _ (StgRhsClosure {}) = []
|
|
|
527
|
+localFunOf env is_join bndr (StgRhsCon {})
|
|
|
528
|
+ | is_join
|
|
|
529
|
+ = [(getBinderId env bndr, idArity (getBinderId env bndr))]
|
|
|
530
|
+localFunOf _ _ _ (StgRhsCon {}) = []
|
|
|
531
|
+
|
|
|
532
|
+-- See Note [EPT signatures for local function arguments].
|
|
|
533
|
+inferTagLet
|
|
|
534
|
+ :: forall p. (OutputableInferPass p, InferExtEq p)
|
|
|
535
|
+ => TagEnv p
|
|
|
536
|
+ -> [(Id, Int)]
|
|
|
537
|
+ -> GenStgBinding p
|
|
|
538
|
+ -> GenStgExpr p
|
|
|
539
|
+ -> (TagInfo, InferStgBinding, InferStgExpr)
|
|
|
540
|
+inferTagLet env root_funs bind body
|
|
|
541
|
+ | null root_funs
|
|
|
542
|
+ = (plain_info, plain_bind, plain_body)
|
|
|
543
|
+ | all (isJust . lookupFunArgInfo env . fst) root_funs
|
|
|
544
|
+ = (plain_info, plain_bind, plain_body)
|
|
|
545
|
+ | otherwise
|
|
|
546
|
+ = go initial_arg_infos
|
|
|
547
|
+ where
|
|
|
548
|
+ (plain_body_env, plain_bind) = inferTagBind env bind
|
|
|
549
|
+ (plain_info, plain_body) = inferTagExpr plain_body_env body
|
|
|
550
|
+
|
|
|
551
|
+ funs = root_funs ++ funsInBind bind ++ funsInExpr body
|
|
|
552
|
+ fun_ids = map fst funs
|
|
|
553
|
+ initial_arg_infos = [replicate arity TagEPT | (_, arity) <- funs]
|
|
|
554
|
+
|
|
|
555
|
+ go arg_infos
|
|
|
556
|
+ | arg_infos == new_arg_infos
|
|
|
557
|
+ = (info, bind', body')
|
|
|
558
|
+ | otherwise
|
|
|
559
|
+ = go new_arg_infos
|
|
|
560
|
+ where
|
|
|
561
|
+ fun_env = extendFunArgEnv env (zipEqual fun_ids arg_infos)
|
|
|
562
|
+ (body_env, bind') = inferTagBind fun_env bind
|
|
|
563
|
+ (info, body') = inferTagExpr body_env body
|
|
|
564
|
+ calls = collectFunCalls (makeTagged fun_env) funs bind' body'
|
|
|
565
|
+ new_arg_infos =
|
|
|
566
|
+ [ maybe initial_infos (combineArgs initial_infos) (lookupVarEnv calls fun_id)
|
|
|
567
|
+ | (fun_id, initial_infos) <- zipEqual fun_ids initial_arg_infos ]
|
|
|
568
|
+
|
|
|
569
|
+ combineArgs = zipWithEqual combineAltInfo
|
|
|
570
|
+
|
|
|
571
|
+ funsInBind (StgNonRec _ rhs) = funsInRhs rhs
|
|
|
572
|
+ funsInBind (StgRec pairs) = concatMap (funsInRhs . snd) pairs
|
|
|
573
|
+
|
|
|
574
|
+ funsInRhs (StgRhsClosure _ _ _ _ rhs _) = funsInExpr rhs
|
|
|
575
|
+ funsInRhs (StgRhsCon {}) = []
|
|
|
576
|
+
|
|
|
577
|
+ funsInExpr (StgApp {}) = []
|
|
|
578
|
+ funsInExpr (StgConApp {}) = []
|
|
|
579
|
+ funsInExpr (StgLit {}) = []
|
|
|
580
|
+ funsInExpr (StgTick _ expr) = funsInExpr expr
|
|
|
581
|
+ funsInExpr (StgOpApp {}) = []
|
|
|
582
|
+ funsInExpr (StgLet _ let_bind expr)
|
|
|
583
|
+ = localFunsOfBind env False let_bind ++ funsInBind let_bind ++ funsInExpr expr
|
|
|
584
|
+ funsInExpr (StgLetNoEscape _ let_bind expr)
|
|
|
585
|
+ = localFunsOfBind env True let_bind ++ funsInBind let_bind ++ funsInExpr expr
|
|
|
586
|
+ funsInExpr (StgCase scrut _ _ alts)
|
|
|
587
|
+ = funsInExpr scrut ++ concatMap (funsInExpr . alt_rhs) alts
|
|
|
588
|
+
|
|
|
589
|
+-- See Note [EPT signatures for local function arguments].
|
|
|
590
|
+collectFunCalls
|
|
569
|
591
|
:: TagEnv 'InferTaggedBinders
|
|
570
|
|
- -> [Id]
|
|
|
592
|
+ -> [(Id, Int)]
|
|
571
|
593
|
-> InferStgBinding
|
|
572
|
594
|
-> InferStgExpr
|
|
573
|
595
|
-> IdEnv [TagInfo]
|
|
574
|
|
-collectJoinCalls in_env join_ids bind body
|
|
|
596
|
+collectFunCalls in_env funs bind body
|
|
575
|
597
|
= bind_calls `plusCalls` collectExpr body_env body
|
|
576
|
598
|
where
|
|
577
|
|
- join_env = mkVarEnv [(join_id, ()) | join_id <- join_ids]
|
|
|
599
|
+ fun_env = mkVarEnv funs
|
|
578
|
600
|
(bind_calls, body_env) = collectBind in_env bind
|
|
579
|
601
|
|
|
580
|
602
|
collectExpr env (StgApp fun args)
|
|
581
|
|
- | elemVarEnv fun join_env
|
|
582
|
|
- = unitVarEnv fun (map (lookupInfo env) args)
|
|
583
|
|
- | otherwise
|
|
584
|
|
- = emptyVarEnv
|
|
585
|
|
- collectExpr _ (StgConApp {}) = emptyVarEnv
|
|
|
603
|
+ = direct_call `plusCalls` collectArgs args
|
|
|
604
|
+ where
|
|
|
605
|
+ direct_call
|
|
|
606
|
+ | Just arity <- lookupVarEnv fun_env fun
|
|
|
607
|
+ = unitVarEnv fun
|
|
|
608
|
+ (take arity (map (lookupInfo env) args) ++
|
|
|
609
|
+ replicate (arity - length args) TagDunno)
|
|
|
610
|
+ | otherwise
|
|
|
611
|
+ = emptyVarEnv
|
|
|
612
|
+ collectExpr _ (StgConApp _ _ args _) = collectArgs args
|
|
586
|
613
|
collectExpr _ (StgLit {}) = emptyVarEnv
|
|
587
|
614
|
collectExpr env (StgTick _ expr) = collectExpr env expr
|
|
588
|
|
- collectExpr _ (StgOpApp {}) = emptyVarEnv
|
|
|
615
|
+ collectExpr _ (StgOpApp _ args _) = collectArgs args
|
|
589
|
616
|
collectExpr env (StgLet _ let_bind expr)
|
|
590
|
617
|
= let_calls `plusCalls` collectExpr let_env expr
|
|
591
|
618
|
where
|
| ... |
... |
@@ -610,39 +637,57 @@ collectJoinCalls in_env join_ids bind body |
|
610
|
637
|
|
|
611
|
638
|
collectRhs env (StgRhsClosure _ _ _ bndrs rhs _)
|
|
612
|
639
|
= collectExpr (extendSigEnv env bndrs) rhs
|
|
613
|
|
- collectRhs _ (StgRhsCon {}) = emptyVarEnv
|
|
|
640
|
+ collectRhs _ (StgRhsCon _ _ _ _ args _) = collectArgs args
|
|
|
641
|
+
|
|
|
642
|
+ collectArgs = plusCallList . map collectArg
|
|
|
643
|
+
|
|
|
644
|
+ collectArg (StgVarArg var)
|
|
|
645
|
+ | Just arity <- lookupVarEnv fun_env var
|
|
|
646
|
+ = unitVarEnv var (replicate arity TagDunno)
|
|
|
647
|
+ collectArg _ = emptyVarEnv
|
|
614
|
648
|
|
|
615
|
649
|
plusCalls = plusVarEnv_C (zipWithEqual combineAltInfo)
|
|
616
|
650
|
plusCallList = foldr plusCalls emptyVarEnv
|
|
617
|
651
|
|
|
618
|
|
-{- Note [EPT signatures for join point arguments]
|
|
619
|
|
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
620
|
|
-Join points are local, non-escaping functions, and every occurrence is a
|
|
621
|
|
-saturated tail call. Consequently we can infer EPT information for their
|
|
622
|
|
-arguments by treating them like SSA block parameters: an argument is EPT when
|
|
623
|
|
-the corresponding actual argument is EPT at every jump to the join point.
|
|
624
|
|
-
|
|
625
|
|
-At each outermost StgLetNoEscape we optimistically start every argument of that
|
|
626
|
|
-join group and all nested join groups at TagEPT, infer tags for the binding and
|
|
627
|
|
-its body, and collect the argument tags at all calls to those join points.
|
|
|
652
|
+{- Note [EPT signatures for local function arguments]
|
|
|
653
|
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
654
|
+For local functions we can inspect every use and infer that a parameter is EPT
|
|
|
655
|
+when the corresponding actual argument is EPT at every call. Join points are
|
|
|
656
|
+the particularly simple case: they never escape and every occurrence is a
|
|
|
657
|
+saturated tail call, so their parameters behave like SSA block parameters.
|
|
|
658
|
+
|
|
|
659
|
+Ordinary let-bound functions can escape or be applied to too few arguments.
|
|
|
660
|
+We therefore track their parameters independently. A direct call contributes
|
|
|
661
|
+the tag of each supplied argument; an undersaturated call contributes TagDunno
|
|
|
662
|
+for the missing suffix. If the function occurs as a value (for example as an
|
|
|
663
|
+argument or constructor field), every parameter becomes TagDunno because a
|
|
|
664
|
+later call is outside the analysis. Arguments beyond the function's arity do
|
|
|
665
|
+not describe its parameters and are ignored. We still inspect every argument
|
|
|
666
|
+for escaping occurrences of other tracked functions.
|
|
|
667
|
+
|
|
|
668
|
+At each outermost local function binding we optimistically start every argument
|
|
|
669
|
+of that function group and all nested function and join groups at TagEPT, infer
|
|
|
670
|
+tags for the binding and its body, and collect the argument tags at all uses.
|
|
628
|
671
|
Combining those call-site tags gives the next, no more optimistic approximation
|
|
629
|
|
-for the join arguments. Iterating reaches the greatest fixed point, including
|
|
630
|
|
-for recursive and mutually recursive joins. Using the greatest fixed point is
|
|
631
|
|
-important for recursive calls such as @jump j x@, where @x@ is itself a
|
|
632
|
|
-parameter of @j@: such a back edge preserves EPTness rather than providing an
|
|
633
|
|
-independent reason to reject it.
|
|
634
|
|
-
|
|
635
|
|
-Solving all nested join groups simultaneously is important for compile-time
|
|
636
|
|
-performance. Independently solving a nested join group on every iteration of
|
|
637
|
|
-each enclosing group causes exponential re-analysis in deeply nested code.
|
|
638
|
|
-Nested StgLetNoEscapes therefore merely use the argument information installed
|
|
639
|
|
-by the outer solver.
|
|
|
672
|
+for the function arguments. Iterating reaches the greatest fixed point,
|
|
|
673
|
+including for recursive and mutually recursive functions. Using the greatest
|
|
|
674
|
+fixed point is important for recursive calls such as @jump j x@, where @x@ is
|
|
|
675
|
+itself a parameter of @j@: such a back edge preserves EPTness rather than
|
|
|
676
|
+providing an independent reason to reject it.
|
|
|
677
|
+
|
|
|
678
|
+Solving all nested function and join groups simultaneously is important for
|
|
|
679
|
+compile-time performance. Independently solving a nested group on every
|
|
|
680
|
+iteration of each enclosing group causes exponential re-analysis in deeply
|
|
|
681
|
+nested code.
|
|
|
682
|
+Nested bindings therefore merely use the argument information installed by the
|
|
|
683
|
+outer solver. A dead local function has no call-site evidence against the
|
|
|
684
|
+optimistic approximation, which is sound because it cannot be entered.
|
|
640
|
685
|
|
|
641
|
686
|
This is tag inference, not strictness inference. It does not evaluate an
|
|
642
|
687
|
argument or change the calling convention. It merely records that every path
|
|
643
|
|
-which enters the join point already supplies an EPT value. Existing CBV marks
|
|
644
|
|
-remain authoritative in inferTagRhs, since the rewriter enforces their EPT
|
|
645
|
|
-precondition at call sites.
|
|
|
688
|
+which enters the local function already supplies an EPT value. Existing CBV
|
|
|
689
|
+marks remain authoritative in inferTagRhs, since the rewriter enforces their
|
|
|
690
|
+EPT precondition at call sites.
|
|
646
|
691
|
-}
|
|
647
|
692
|
|
|
648
|
693
|
-- Compute binder sigs based on the constructors strict fields.
|
| ... |
... |
@@ -787,7 +832,7 @@ inferTagRhs bnd_id in_env (StgRhsClosure ext cc upd bndrs body typ) |
|
787
|
832
|
= repeat NotMarkedCbv
|
|
788
|
833
|
|
|
789
|
834
|
inferred_arg_infos =
|
|
790
|
|
- fromMaybe (repeat TagDunno) (lookupJoinArgInfo in_env bnd_id)
|
|
|
835
|
+ fromMaybe (repeat TagDunno) (lookupFunArgInfo in_env bnd_id)
|
|
791
|
836
|
|
|
792
|
837
|
env' = extendSigEnv in_env out_bndrs
|
|
793
|
838
|
(info, body') = inferTagExpr env' body
|