Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

22 changed files:

Changes:

  • changelog.d/tag-inference-27005
    1
    +section: compiler
    
    2
    +synopsis: Eliminate redundant thunks introduced by tag inference.
    
    3
    +description: {
    
    4
    +  Tag inference is now more precise in tracking function results, eliminating
    
    5
    +  redundant thunks for certain functions that return an unboxed tuple.
    
    6
    +}
    
    7
    +issues: #27005
    
    8
    +mrs: !15688

  • compiler/GHC/Core/Utils.hs
    ... ... @@ -3252,7 +3252,7 @@ That is in STG after tag inference we get:
    3252 3252
         Str=<1L><ML>,
    
    3253 3253
         Unf=OtherCon []] =
    
    3254 3254
             {} \r [x y]
    
    3255
    -            case x<TagProper> of x' [Occ=Once1] {
    
    3255
    +            case x<TagVal[TagEPT]> of x' [Occ=Once1] {
    
    3256 3256
                   __DEFAULT ->
    
    3257 3257
                       case y of y' [Occ=Once1] {
    
    3258 3258
                       __DEFAULT ->
    
    ... ... @@ -3277,8 +3277,8 @@ But if we add an extra eval on `y` during worker/wrapper we this this:
    3277 3277
         Str=<1L><!L>,
    
    3278 3278
         Unf=OtherCon []] =
    
    3279 3279
             {} \r [x y]
    
    3280
    -            case y<TagProper> of y' [Occ=Once1] { __DEFAULT ->
    
    3281
    -            case x<TagProper> of x' [Occ=Once1] {
    
    3280
    +            case y<TagVal[TagEPT]> of y' [Occ=Once1] { __DEFAULT ->
    
    3281
    +            case x<TagVal[TagEPT]> of x' [Occ=Once1] {
    
    3282 3282
                   __DEFAULT ->
    
    3283 3283
                       case Find.$wmyPred y' of pred_y [Occ=Once1] {
    
    3284 3284
                       __DEFAULT ->
    

  • compiler/GHC/Stg/EnforceEpt.hs
    ... ... @@ -40,9 +40,6 @@ A pointer is Evaluated and Properly Tagged (EPT) when the pointer
    40 40
     
    
    41 41
     A binder is EPT when all the runtime pointers it binds are EPT.
    
    42 42
     
    
    43
    -Note that a lifted EPT pointer will never point to a thunk, nor will it be
    
    44
    -tagged `000` (meaning "might be a thunk").
    
    45
    -
    
    46 43
     See https://gitlab.haskell.org/ghc/ghc/-/wikis/commentary/rts/haskell-execution/pointer-tagging
    
    47 44
     for more information on pointer tagging.
    
    48 45
     
    
    ... ... @@ -57,11 +54,15 @@ Examples:
    57 54
     * In practice, GHC also guarantees that strict fields (and others) are EPT;
    
    58 55
       see Note [EPT enforcement].
    
    59 56
     
    
    60
    -Caveat:
    
    61
    -Currently, the proper tag for builtin *unlifted* data types such as `Array#` is
    
    62
    -not `001` but `000`, which is not a proper tag for lifted data.
    
    63
    -This means that UnliftedRep is not a proper sub-rep of LiftedRep.
    
    64
    -SG thinks it would be good to fix this; see #21792.
    
    57
    +Wrinkles:
    
    58
    +
    
    59
    +EPT1:  The proper tag for builtin *unlifted* data types such as `Array#` is
    
    60
    +       not `001` but `000`, which is not a proper tag for lifted data.
    
    61
    +       This means that UnliftedRep is not a proper sub-rep of LiftedRep.
    
    62
    +       SG thinks it would be good to fix this; see #21792.
    
    63
    +EPT2:  The proper tag for PAPs is `000`, so there are values from lifted data types
    
    64
    +       that are EPT with a zero tag.
    
    65
    +EPT3:  Non-pointer values don't have a tag at all, but we still treat them as EPT.
    
    65 66
     
    
    66 67
     Note [EPT enforcement]
    
    67 68
     ~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -261,18 +262,39 @@ might destroy the EPT Invariant, hence we need to enforce the EPT invariant
    261 262
     
    
    262 263
     {- Note [TagInfo of functions]
    
    263 264
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    264
    -The purpose of tag inference is really to figure out when we don't have to enter
    
    265
    -value closures. There the meaning of the tag is fairly obvious.
    
    266
    -For functions we never make use of the tag info so we have two choices:
    
    267
    -* Treat them as TagDunno
    
    268
    -* Treat them as TagProper (as they *are* tagged with their arity) and be really
    
    269
    -  careful to make sure we still enter them when needed.
    
    270
    -As it makes little difference for runtime performance I've treated functions as TagDunno in a few places where
    
    271
    -it made the code simpler. But besides implementation complexity there isn't any reason
    
    272
    -why we couldn't be more rigorous in dealing with functions.
    
    273
    -
    
    274
    -NB: It turned in #21193 that PAPs get tag zero, so the tag check can't be omitted for functions.
    
    275
    -So option two isn't really an option without reworking this anyway.
    
    265
    +'TagFun' represents an evaluated function and carries the return-value TagInfo
    
    266
    +for saturated calls. See Note [TagSig and TagInfo] in GHC.Stg.EnforceEpt.TagSig.
    
    267
    +
    
    268
    +Join points (StgLetNoEscape) are treated like functions: they are jumped to,
    
    269
    +never entered via a closure pointer. Even nullary join points get TagFun
    
    270
    +rather than TagVal TagDunno, so their return-value info flows through.
    
    271
    +
    
    272
    +Additionally we also special case bottoming functions.
    
    273
    +See Note [Bottom functions are TagBottoming].
    
    274
    +
    
    275
    +Function values when properly tagged have either their arity, 1 for very large
    
    276
    +arities, or zero for PAPs (#21193) as tag. However the runtime currently doesn't
    
    277
    +maintain the EPT status of function pointers. So while we keep track of
    
    278
    +evaluated function binders using 'TagFun' this has a weaker guarantee than for
    
    279
    +values. For functions it only represents a pointer to an evaluated function, but
    
    280
    +doesn't give any guarantee about the function's pointer tag.
    
    281
    +
    
    282
    +Sadly because PAPs get tag zero (#21193), other issues in the RTS (#21193) and
    
    283
    +the potential of over/underapplication of arguments, even evaluated function
    
    284
    +binders still require inspection of their closure before they can be applied.
    
    285
    +That is, even if we have
    
    286
    +
    
    287
    +    case f of f' -> ... f' x ...
    
    288
    +
    
    289
    +the code for `f' x` can't be made meaningfully more efficient by knowing `f'` is
    
    290
    +already evaluated.
    
    291
    +
    
    292
    +However tracking evaluation status for functions is still worthwhile, as we rely
    
    293
    +on the EPT pass to force strict constructor fields. For an application
    
    294
    +`StrictJust f` we would usually insert an eval of `f` around the constructor
    
    295
    +application. However if we infer `f` to be already evaluated we can avoid this,
    
    296
    +which was a significant performance benefit in #27005. Similarly we can still
    
    297
    +turn redundant eval-cases on functions into a no-op.
    
    276 298
     
    
    277 299
     Note [EPT enforcement debugging]
    
    278 300
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -361,7 +383,6 @@ type InferExtEq i = ( XLet i ~ XLet 'InferTaggedBinders
    361 383
     
    
    362 384
     inferTags :: Bool -> [GenStgTopBinding 'CodeGen] -> [GenStgTopBinding 'InferTaggedBinders]
    
    363 385
     inferTags for_bytecode binds =
    
    364
    -  -- pprTrace "Binds" (pprGenStgTopBindings shortStgPprOpts $ binds) $
    
    365 386
       snd (mapAccumL inferTagTopBind (initEnv for_bytecode) binds)
    
    366 387
     
    
    367 388
     -----------------------
    
    ... ... @@ -389,18 +410,20 @@ inferTagExpr env (StgApp fun args)
    389 410
         !fun_arity = idArity fun
    
    390 411
         info
    
    391 412
              -- It's important that we check for bottoms before all else.
    
    392
    -         -- See Note [Bottom functions are TagTagged] and #24806 for why.
    
    413
    +         -- See Note [Bottom functions are TagBottoming] and #24806 for why.
    
    393 414
              | isDeadEndAppSig (idDmdSig fun) (length args)
    
    394
    -         = TagTagged
    
    415
    +         = TagBottoming
    
    395 416
     
    
    396 417
              | fun_arity == 0 -- Unknown arity => Thunk or unknown call
    
    397 418
              = TagDunno
    
    398 419
     
    
    399
    -         | Just (TagSig res_info) <- tagSigInfo (idInfo fun)
    
    420
    +         -- Imported function with known return tag
    
    421
    +         | Just (TagFun res_info) <- tagSigInfo (idInfo fun)
    
    400 422
              , fun_arity == length args  -- Saturated
    
    401 423
              = res_info
    
    402 424
     
    
    403
    -         | Just (TagSig res_info) <- lookupSig env fun
    
    425
    +         -- Local function with known return tag
    
    426
    +         | Just res_info <- lookupReturnInfo env fun
    
    404 427
              , fun_arity == length args  -- Saturated
    
    405 428
              = res_info
    
    406 429
     
    
    ... ... @@ -412,7 +435,7 @@ inferTagExpr env (StgConApp con cn args tys)
    412 435
       = (inferConTag env con args, StgConApp con cn args tys)
    
    413 436
     
    
    414 437
     inferTagExpr _ (StgLit l)
    
    415
    -  = (TagTagged, StgLit l)
    
    438
    +  = (TagEPT, StgLit l)
    
    416 439
     
    
    417 440
     inferTagExpr env (StgTick tick body)
    
    418 441
       = (info, StgTick tick body')
    
    ... ... @@ -446,7 +469,7 @@ inferTagExpr in_env (StgCase scrut bndr ty alts)
    446 469
             mk_bndr :: BinderP p -> TagInfo -> (Id, TagSig)
    
    447 470
             mk_bndr tup_bndr tup_info =
    
    448 471
                 --  pprTrace "mk_ubx_bndr_info" ( ppr bndr <+> ppr info ) $
    
    449
    -            (getBinderId in_env tup_bndr, TagSig tup_info)
    
    472
    +            (getBinderId in_env tup_bndr, TagVal tup_info)
    
    450 473
             -- no case binder in alt_env here, unboxed tuple binders are dead after unarise
    
    451 474
             alt_env = extendSigEnv in_env bndrs'
    
    452 475
             (info, rhs') = inferTagExpr alt_env rhs
    
    ... ... @@ -478,16 +501,16 @@ inferTagExpr in_env (StgCase scrut bndr ty alts)
    478 501
                       , let (alt_env,bndrs') = addAltBndrInfo case_env con bndrs
    
    479 502
                             (info, rhs') = inferTagExpr alt_env rhs
    
    480 503
                       ]
    
    481
    -        alt_info = foldr combineAltInfo TagTagged infos
    
    504
    +        alt_info = foldr combineAltInfo TagBottoming infos
    
    482 505
         in ( alt_info, StgCase scrut' bndr' ty alts')
    
    483 506
       where
    
    484 507
         -- Single unboxed tuple alternative
    
    485 508
         scrut_infos bndrs = case scrut_info of
    
    486
    -      TagTagged -> Just $ replicate (length bndrs) TagProper
    
    509
    +      TagBottoming -> Just $ replicate (length bndrs) TagBottoming
    
    487 510
           TagTuple infos -> Just infos
    
    488 511
           _ -> Nothing
    
    489 512
         (scrut_info, scrut') = inferTagExpr in_env scrut
    
    490
    -    bndr' = (getBinderId in_env bndr, TagSig TagProper)
    
    513
    +    bndr' = (getBinderId in_env bndr, TagVal TagEPT)
    
    491 514
     
    
    492 515
     -- Compute binder sigs based on the constructors strict fields.
    
    493 516
     -- NB: Not used if we have tuple info from the scrutinee.
    
    ... ... @@ -503,7 +526,7 @@ addAltBndrInfo env (DataAlt con) bndrs
    503 526
         mk_bndr :: (BinderP p -> StrictnessMark -> (Id, TagSig))
    
    504 527
         mk_bndr bndr mark
    
    505 528
           | isUnliftedType (idType id) || isMarkedStrict mark
    
    506
    -      = (id, TagSig TagProper)
    
    529
    +      = (id, TagVal TagEPT)
    
    507 530
           | otherwise
    
    508 531
           = noSig env bndr
    
    509 532
             where
    
    ... ... @@ -523,7 +546,7 @@ inferTagBind in_env (StgNonRec bndr rhs)
    523 546
         (env', StgNonRec (id, out_sig) rhs')
    
    524 547
       where
    
    525 548
         id   = getBinderId in_env bndr
    
    526
    -    (in_sig,rhs') = inferTagRhs id in_env rhs
    
    549
    +    (in_sig, rhs') = inferTagRhs id in_env rhs
    
    527 550
         out_sig = mkLetSig in_env in_sig
    
    528 551
         env' = extendSigEnv in_env [(id, out_sig)]
    
    529 552
     
    
    ... ... @@ -536,13 +559,12 @@ inferTagBind in_env (StgRec pairs)
    536 559
         init_sigs         = map (initSig) $ zip in_ids rhss
    
    537 560
         (out_env, pairs') = go in_env init_sigs rhss
    
    538 561
     
    
    539
    -    go :: forall q. (OutputableInferPass q , InferExtEq q) => TagEnv q -> [TagSig] -> [GenStgRhs q]
    
    540
    -                 -> (TagSigEnv, [((Id,TagSig), GenStgRhs 'InferTaggedBinders)])
    
    562
    +    go :: forall q. (OutputableInferPass q , InferExtEq q)
    
    563
    +       => TagEnv q -> [TagSig] -> [GenStgRhs q]
    
    564
    +       -> (TagSigEnv, [((Id,TagSig), GenStgRhs 'InferTaggedBinders)])
    
    541 565
         go go_env in_sigs go_rhss
    
    542
    -      --   | pprTrace "go" (ppr in_ids $$ ppr in_sigs $$ ppr out_sigs $$ ppr rhss') False
    
    543
    -      --  = undefined
    
    544 566
            | in_sigs == out_sigs = (te_env rhs_env, out_bndrs `zip` rhss')
    
    545
    -       | otherwise     = go env' out_sigs rhss'
    
    567
    +       | otherwise           = go env' out_sigs rhss'
    
    546 568
            where
    
    547 569
              in_bndrs = in_ids `zip` in_sigs
    
    548 570
              out_bndrs = map updateBndr in_bndrs -- TODO: Keeps in_ids alive
    
    ... ... @@ -552,25 +574,28 @@ inferTagBind in_env (StgRec pairs)
    552 574
     
    
    553 575
              anaRhs :: Id -> GenStgRhs q -> (TagSig, GenStgRhs 'InferTaggedBinders)
    
    554 576
              anaRhs bnd rhs =
    
    555
    -            let (sig_rhs,rhs') = inferTagRhs bnd rhs_env rhs
    
    577
    +            let (sig_rhs, rhs') = inferTagRhs bnd rhs_env rhs
    
    556 578
                 in (mkLetSig go_env sig_rhs, rhs')
    
    557 579
     
    
    558 580
     
    
    559 581
              updateBndr :: (Id,TagSig) -> (Id,TagSig)
    
    560 582
              updateBndr (v,sig) = (setIdTagSig v sig, sig)
    
    561 583
     
    
    584
    +-- Initial signature for the fixpoint loop.
    
    562 585
     initSig :: forall p. (Id, GenStgRhs p) -> TagSig
    
    563
    --- Initial signature for the fixpoint loop
    
    564
    -initSig (_bndr, StgRhsCon {})               = TagSig TagTagged
    
    565
    -initSig (bndr, StgRhsClosure _ _ _ _ _ _) =
    
    566
    -  fromMaybe defaultSig (idTagSig_maybe bndr)
    
    567
    -  where defaultSig = (TagSig TagTagged)
    
    568
    -
    
    569
    -{- Note [Bottom functions are TagTagged]
    
    586
    +initSig (_bndr, StgRhsCon {})
    
    587
    +  = TagVal TagBottoming
    
    588
    +initSig (bndr, StgRhsClosure _ _ _ bndrs _ _)
    
    589
    +  | notNull bndrs || isJoinId bndr
    
    590
    +  = fromMaybe (TagFun TagBottoming) (idTagSig_maybe bndr)
    
    591
    +  | otherwise  -- thunk
    
    592
    +  = fromMaybe (TagVal TagBottoming) (idTagSig_maybe bndr)
    
    593
    +
    
    594
    +{- Note [Bottom functions are TagBottoming]
    
    570 595
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    571 596
     If we have a function with two branches with one
    
    572 597
     being bottom, and the other returning a tagged
    
    573
    -unboxed tuple what is the result? We give it TagTagged!
    
    598
    +unboxed tuple what is the result? We give it TagBottoming!
    
    574 599
     To answer why consider this function:
    
    575 600
     
    
    576 601
     foo :: Bool -> (# Bool, Bool #)
    
    ... ... @@ -585,7 +610,7 @@ branches are tagged.
    585 610
     This is safe because the function is still always called/entered as long
    
    586 611
     as it's applied to arguments. Since the function will never return we can give
    
    587 612
     it safely any tag sig we like.
    
    588
    -So we give it TagTagged, as it allows the combined tag sig of the case expression
    
    613
    +So we give it TagBottoming, as it allows the combined tag sig of the case expression
    
    589 614
     to be the combination of all non-bottoming branches.
    
    590 615
     
    
    591 616
     NB: After the analysis is done we go back to treating bottoming functions as
    
    ... ... @@ -603,13 +628,20 @@ inferTagRhs :: forall p.
    603 628
       -> GenStgRhs p -- ^
    
    604 629
       -> (TagSig, GenStgRhs 'InferTaggedBinders)
    
    605 630
     inferTagRhs bnd_id in_env (StgRhsClosure ext cc upd bndrs body typ)
    
    606
    -  | isDeadEndId bnd_id && (notNull) bndrs
    
    607
    -  -- See Note [Bottom functions are TagTagged]
    
    608
    -  = (TagSig TagTagged, StgRhsClosure ext cc upd out_bndrs body' typ)
    
    609
    -  | otherwise
    
    610
    -  = --pprTrace "inferTagRhsClosure" (ppr (_top, _grp_ids, env,info')) $
    
    611
    -    (TagSig info', StgRhsClosure ext cc upd out_bndrs body' typ)
    
    631
    +  | isDeadEndId bnd_id && is_fun_or_join
    
    632
    +  -- See Note [Bottom functions are TagBottoming]
    
    633
    +  = ( TagFun TagBottoming
    
    634
    +    , StgRhsClosure ext cc upd out_bndrs body' typ)
    
    635
    +  | is_fun_or_join
    
    636
    +  = ( TagFun info
    
    637
    +    , StgRhsClosure ext cc upd out_bndrs body' typ)
    
    638
    +  | otherwise  -- thunk (never a join point)
    
    639
    +  = ( TagVal TagDunno
    
    640
    +    , StgRhsClosure ext cc upd out_bndrs body' typ)
    
    612 641
       where
    
    642
    +    -- Join points are treated like functions. See Note [TagInfo of functions]
    
    643
    +    is_fun_or_join = notNull bndrs || isJoinId bnd_id
    
    644
    +
    
    613 645
         out_bndrs
    
    614 646
           | Just marks <- idCbvMarks_maybe bnd_id
    
    615 647
           -- Sometimes an we eta-expand foo with additional arguments after ww, and we also trim
    
    ... ... @@ -620,37 +652,29 @@ inferTagRhs bnd_id in_env (StgRhsClosure ext cc upd bndrs body typ)
    620 652
     
    
    621 653
         env' = extendSigEnv in_env out_bndrs
    
    622 654
         (info, body') = inferTagExpr env' body
    
    623
    -    info'
    
    624
    -      -- It's a thunk
    
    625
    -      | null bndrs
    
    626
    -      = TagDunno
    
    627
    -      -- TODO: We could preserve tuple fields for thunks
    
    628
    -      -- as well. But likely not worth the complexity.
    
    629
    -
    
    630
    -      | otherwise  = info
    
    631 655
     
    
    632 656
         mkArgSig :: BinderP p -> CbvMark -> (Id,TagSig)
    
    633 657
         mkArgSig bndp mark =
    
    634 658
           let id = getBinderId in_env bndp
    
    635 659
               tag = case mark of
    
    636
    -            MarkedCbv -> TagProper
    
    660
    +            MarkedCbv -> TagEPT
    
    637 661
                 _
    
    638
    -              | isUnliftedType (idType id) -> TagProper
    
    662
    +              | isUnliftedType (idType id) -> TagEPT
    
    639 663
                   | otherwise -> TagDunno
    
    640
    -      in (id, TagSig tag)
    
    664
    +      in (id, TagVal tag)
    
    641 665
     
    
    642 666
     inferTagRhs _ env _rhs@(StgRhsCon cc con cn ticks args typ)
    
    643 667
     -- Constructors, which have untagged arguments to strict fields
    
    644 668
     -- become thunks. We encode this by giving changing RhsCon nodes the info TagDunno
    
    645 669
       = --pprTrace "inferTagRhsCon" (ppr grp_ids) $
    
    646
    -    (TagSig (inferConTag env con args), StgRhsCon cc con cn ticks args typ)
    
    670
    +    (TagVal (inferConTag env con args), StgRhsCon cc con cn ticks args typ)
    
    647 671
     
    
    648 672
     -- Adjust let semantics to the targeted backend.
    
    649 673
     -- See Note [EPT enforcement for interpreted code]
    
    650 674
     mkLetSig :: TagEnv p -> TagSig -> TagSig
    
    651 675
     mkLetSig env in_sig
    
    652
    -  | for_bytecode = TagSig TagDunno
    
    653
    -  | otherwise = in_sig
    
    676
    +  | for_bytecode = TagVal TagDunno
    
    677
    +  | otherwise    = in_sig
    
    654 678
       where
    
    655 679
         for_bytecode = te_bytecode env
    
    656 680
     
    
    ... ... @@ -658,52 +682,53 @@ mkLetSig env in_sig
    658 682
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    659 683
     @inferConTag@ will infer the proper tag signature for a binding who's RHS is a constructor
    
    660 684
     or a StgConApp expression.
    
    661
    -Usually these will simply be TagProper. But there are exceptions.
    
    685
    +Usually these will simply be TagEPT. But there are exceptions.
    
    662 686
     If any of the fields in the constructor are strict, but any argument to these
    
    663 687
     fields is not tagged then we will have to case on the argument before storing
    
    664 688
     in the constructor. Which means for let bindings the RHS turns into a thunk
    
    665 689
     which obviously is no longer properly tagged.
    
    666 690
     For example we might start with:
    
    667 691
     
    
    668
    -    let x<TagDunno> = f ...
    
    669
    -    let c<TagProper> = StrictPair x True
    
    692
    +    let x<TagVal[TagDunno]> = f ...
    
    693
    +    let c<TagVal[TagEPT]> = StrictPair x True
    
    670 694
     
    
    671 695
     But we know during the rewrite stage x will need to be evaluated in the RHS
    
    672 696
     of `c` so we will infer:
    
    673 697
     
    
    674
    -    let x<TagDunno> = f ...
    
    675
    -    let c<TagDunno> = StrictPair x True
    
    698
    +    let x<TagVal[TagDunno]> = f ...
    
    699
    +    let c<TagVal[TagDunno]> = StrictPair x True
    
    676 700
     
    
    677 701
     Which in the rewrite stage will then be rewritten into:
    
    678 702
     
    
    679
    -    let x<TagDunno> = f ...
    
    680
    -    let c<TagDunno> = case x of x' -> StrictPair x' True
    
    703
    +    let x<TagVal[TagDunno]> = f ...
    
    704
    +    let c<TagVal[TagDunno]> = case x of x' -> StrictPair x' True
    
    681 705
     
    
    682 706
     The other exception is unboxed tuples. These will get a TagTuple
    
    683 707
     signature with a list of TagInfo about their individual binders
    
    684 708
     as argument. As example:
    
    685 709
     
    
    686
    -    let c<TagProper> = True
    
    687
    -    let x<TagDunno> = ...
    
    688
    -    let f<?> z = case z of z'<TagProper> -> (# c, x #)
    
    710
    +    let c<TagVal[TagEPT]> = True
    
    711
    +    let x<TagVal[TagDunno]> = ...
    
    712
    +    let f<?> z = case z of z'<TagVal[TagEPT]> -> (# c, x #)
    
    689 713
     
    
    690
    -Here we will infer for f the Signature <TagTuple[TagProper,TagDunno]>.
    
    714
    +Here we will infer for f the Signature <TagFun[TagTuple[TagEPT,TagDunno]]>.
    
    691 715
     This information will be used if we scrutinize a saturated application of
    
    692 716
     `f` in order to determine the taggedness of the result.
    
    693 717
     That is for `case f x of (# r1,r2 #) -> rhs` we can infer
    
    694
    -r1<TagProper> and r2<TagDunno> which allows us to skip all tag checks on `r1`
    
    718
    +r1<TagVal[TagEPT]> and r2<TagVal[TagDunno]> which allows us to skip all tag checks on `r1`
    
    695 719
     in `rhs`.
    
    696 720
     
    
    697 721
     Things get a bit more complicated with nesting:
    
    698 722
     
    
    699
    -    let closeFd<TagTuple[...]> = ...
    
    723
    +    let closeFd<TagFun[...]> = ...
    
    700 724
         let f x = ...
    
    701 725
             case x of
    
    702 726
               _ -> Solo# closeFd
    
    703 727
     
    
    704
    -The "natural" signature for the Solo# branch in `f` would be <TagTuple[TagTuple[...]]>.
    
    705
    -But we flatten this out to <TagTuple[TagDunno]> for the time being as it improves compile
    
    706
    -time and there doesn't seem to huge benefit to doing differently.
    
    728
    +We only keep track of function return types for bindings, not values:
    
    729
    +TagInfo has no constructor for "function with return info". Therefore the
    
    730
    +signature of `f` becomes <TagFun[TagTuple[TagEPT]]>. We lose the information
    
    731
    +about the return type of `closeFd`.
    
    707 732
     
    
    708 733
       -}
    
    709 734
     
    
    ... ... @@ -721,7 +746,7 @@ inferConTag env con args
    721 746
         --     text "info:" <> ppr info) $
    
    722 747
         info
    
    723 748
       where
    
    724
    -    info = if any arg_needs_eval strictArgs then TagDunno else TagProper
    
    749
    +    info = if any arg_needs_eval strictArgs then TagDunno else TagEPT
    
    725 750
         strictArgs = zipEqual args (dataConRuntimeRepStrictness con) :: ([(StgArg, StrictnessMark)])
    
    726 751
         arg_needs_eval (arg,strict)
    
    727 752
           -- lazy args
    
    ... ... @@ -730,10 +755,10 @@ inferConTag env con args
    730 755
           -- banged args need to be tagged, or require eval
    
    731 756
           = not (isTaggedInfo tag)
    
    732 757
     
    
    733
    -    flatten_arg_tag (TagTagged) = TagProper
    
    734
    -    flatten_arg_tag (TagProper ) = TagProper
    
    735
    -    flatten_arg_tag (TagTuple _) = TagDunno -- See Note [Constructor TagSigs]
    
    736
    -    flatten_arg_tag (TagDunno) = TagDunno
    
    758
    +    flatten_arg_tag TagBottoming    = TagEPT
    
    759
    +    flatten_arg_tag TagEPT          = TagEPT
    
    760
    +    flatten_arg_tag (TagTuple _)    = TagDunno -- See Note [Constructor TagSigs]
    
    761
    +    flatten_arg_tag TagDunno        = TagDunno
    
    737 762
     
    
    738 763
     
    
    739 764
     collectExportInfo :: [GenStgTopBinding 'InferTaggedBinders] -> NameEnv TagSig
    
    ... ... @@ -746,15 +771,14 @@ collectExportInfo binds =
    746 771
         collect (StgTopLifted bnd) =
    
    747 772
           case bnd of
    
    748 773
             StgNonRec (id,sig) _rhs
    
    749
    -          | TagSig TagDunno <- sig -> []
    
    774
    +          | TagVal TagDunno     <- sig -> []
    
    775
    +          | TagVal TagBottoming <- sig -> []
    
    750 776
               | otherwise -> [(idName id,sig)]
    
    751 777
             StgRec bnds -> collectRec bnds
    
    752 778
     
    
    753 779
         collectRec :: [(BinderP 'InferTaggedBinders, rhs)] -> [(Name,TagSig)]
    
    754 780
         collectRec [] = []
    
    755
    -    collectRec (bnd:bnds)
    
    756
    -      | (p,_rhs)  <- bnd
    
    757
    -      , (id,sig) <- p
    
    758
    -      , TagSig TagDunno <- sig
    
    759
    -      = (idName id,sig) : collectRec bnds
    
    760
    -      | otherwise = collectRec bnds
    781
    +    collectRec (((id,sig), _rhs) : bnds)
    
    782
    +      | TagVal TagDunno     <- sig = collectRec bnds
    
    783
    +      | TagVal TagBottoming <- sig = collectRec bnds
    
    784
    +      | otherwise                  = (idName id, sig) : collectRec bnds

  • compiler/GHC/Stg/EnforceEpt/Rewrite.hs
    ... ... @@ -239,14 +239,14 @@ For this reason we assert that we are running in interactive mode if a lookup fa
    239 239
     -}
    
    240 240
     isTagged :: Id -> RM Bool
    
    241 241
     isTagged v
    
    242
    -    -- See Note [Bottom functions are TagTagged]
    
    242
    +    -- See Note [Bottom functions are TagBottoming]
    
    243 243
         | isDeadEndId v = pure False
    
    244 244
         | otherwise = do
    
    245 245
         this_mod <- getMod
    
    246 246
         -- See Note [Tag inference for interactive contexts]
    
    247 247
         let lookupDefault v = assertPpr (isInteractiveModule this_mod)
    
    248 248
                                         (text "unknown Id:" <> ppr this_mod <+> ppr v)
    
    249
    -                                    (TagSig TagDunno)
    
    249
    +                                    (TagVal TagDunno)
    
    250 250
         case nameIsLocalOrFrom this_mod (idName v) of
    
    251 251
             True
    
    252 252
                 | definitelyUnliftedType (idType v)
    
    ... ... @@ -257,12 +257,11 @@ isTagged v
    257 257
                     !s <- getMap
    
    258 258
                     let !sig = lookupWithDefaultUFM s (lookupDefault v) v
    
    259 259
                     return $ case sig of
    
    260
    -                    TagSig info ->
    
    261
    -                        case info of
    
    262
    -                            TagDunno -> False
    
    263
    -                            TagProper -> True
    
    264
    -                            TagTagged -> True
    
    265
    -                            TagTuple _ -> True -- Consider unboxed tuples tagged.
    
    260
    +                    TagFun _             -> True  -- function closure is tagged
    
    261
    +                    TagVal TagDunno      -> False
    
    262
    +                    TagVal TagEPT        -> True
    
    263
    +                    TagVal TagBottoming  -> True
    
    264
    +                    TagVal (TagTuple _)  -> True  -- Consider unboxed tuples tagged.
    
    266 265
             -- Imported
    
    267 266
             False -> return $!
    
    268 267
                     -- Determine whether it is tagged from the LFInfo of the imported id.
    
    ... ... @@ -279,7 +278,7 @@ isTagged v
    279 278
                             -> True
    
    280 279
                         LFUnknown {}
    
    281 280
                             -> False
    
    282
    -                    LFUnlifted {}
    
    281
    +                    LFUnlifted {}  -- Unboxed, tagging irrelevant
    
    283 282
                             -> True
    
    284 283
                         LFLetNoEscape {}
    
    285 284
                         -- Shouldn't be possible. I don't think we can export letNoEscapes
    
    ... ... @@ -385,7 +384,7 @@ rewriteArg (lit@StgLitArg{}) = return lit
    385 384
     rewriteId :: Id -> RM Id
    
    386 385
     rewriteId v = do
    
    387 386
         !is_tagged <- isTagged v
    
    388
    -    if is_tagged then return $! setIdTagSig v (TagSig TagProper)
    
    387
    +    if is_tagged then return $! setIdTagSig v (TagVal TagEPT)
    
    389 388
                      else return v
    
    390 389
     
    
    391 390
     rewriteExpr :: GenStgExpr 'InferTaggedBinders -> RM (GenStgExpr 'CodeGen)
    

  • compiler/GHC/Stg/EnforceEpt/TagSig.hs
    ... ... @@ -16,65 +16,119 @@ import GHC.Types.Name.Env( NameEnv )
    16 16
     import GHC.Utils.Outputable
    
    17 17
     import GHC.Utils.Binary
    
    18 18
     import GHC.Utils.Panic.Plain
    
    19
    -import Data.Coerce
    
    20 19
     
    
    21 20
     -- | Information to be exposed in interface files which is produced
    
    22 21
     -- by the stg2stg passes.
    
    23 22
     type StgCgInfos = NameEnv TagSig
    
    24 23
     
    
    25
    -newtype TagSig  -- The signature for each binding, this is a newtype as we might
    
    26
    -                -- want to track more information in the future.
    
    27
    -  = TagSig TagInfo
    
    24
    +-- Note [TagSig and TagInfo]
    
    25
    +-- ~~~~~~~~~~~~~~~~~~~~~~~~~
    
    26
    +-- 'TagSig' describes a *binding*; 'TagInfo' describes a *runtime value*.
    
    27
    +--
    
    28
    +-- A binding denotes either a value or a function:
    
    29
    +--
    
    30
    +--   * 'TagVal' i   — the binding holds a value whose runtime shape is 'i'.
    
    31
    +--                    'i' covers thunks (TagDunno), evaluated/tagged heap
    
    32
    +--                    pointers (TagEPT), unboxed tuples (TagTuple), and
    
    33
    +--                    bottoming computations (TagBottoming).
    
    34
    +--
    
    35
    +--   * 'TagFun' i   — the binding is a function or join point. A function
    
    36
    +--                    closure is always EPT. 'i' describes what a
    
    37
    +--                    *saturated call* returns.
    
    38
    +--                    See Note [TagInfo of functions] in GHC.Stg.EnforceEpt.
    
    39
    +--
    
    40
    +-- This split keeps 'combineAltInfo' at the value level: case alternatives
    
    41
    +-- combine 'TagInfo', not 'TagSig'. An alternative that returns a function
    
    42
    +-- closure gets 'TagEPT' (closure pointer is tagged) with no tracked return
    
    43
    +-- info — the return info only matters once the function is applied, and at
    
    44
    +-- that point it's looked up from the binding's 'TagSig' via
    
    45
    +-- 'lookupReturnInfo'.
    
    46
    +
    
    47
    +-- | The signature attached to a binding.
    
    48
    +data TagSig
    
    49
    +  = TagVal TagInfo        -- ^ A value binding (thunk, constructor, etc.)
    
    50
    +  | TagFun TagInfo        -- ^ A function/join-point binding; carries the
    
    51
    +                          -- TagInfo of saturated-call return values.
    
    52
    +                          -- See Note [TagInfo of functions].
    
    28 53
       deriving (Eq)
    
    29 54
     
    
    55
    +-- Note [TagInfo lattice]
    
    56
    +-- ~~~~~~~~~~~~~~~~~~~~~~
    
    57
    +-- The TagInfo lattice describes what we know about whether a runtime value
    
    58
    +-- is properly tagged (pointer tag bits set for heap pointers):
    
    59
    +--
    
    60
    +--   TagBottoming (bottom) ⊑ {TagEPT, TagTuple _} ⊑ TagDunno (top)
    
    61
    +--
    
    62
    +-- TagBottoming is the identity element for 'combineAltInfo': it is used as
    
    63
    +-- the initial signature in fixpoint loops and for dead-end (bottoming)
    
    64
    +-- computations, since their return value can be given any tag — they never
    
    65
    +-- actually return.
    
    66
    +
    
    67
    +-- | What we know about a runtime value.
    
    30 68
     data TagInfo
    
    31
    -  = TagDunno            -- We don't know anything about the tag.
    
    32
    -  | TagTuple [TagInfo]  -- Represents a function/thunk which when evaluated
    
    33
    -                        -- will return a Unboxed tuple whos components have
    
    34
    -                        -- the given TagInfos.
    
    35
    -  | TagProper           -- Heap pointer to properly-tagged value
    
    36
    -  | TagTagged           -- Bottom of the domain.
    
    69
    +  = TagDunno            -- ^ We don't know anything about the tag.
    
    70
    +  | TagTuple [TagInfo]  -- ^ An unboxed tuple with taginfo for each element.
    
    71
    +  | TagEPT              -- ^ An evaluated and properly tagged value.
    
    72
    +                        -- See Note [Evaluated and Properly Tagged].
    
    73
    +  | TagBottoming        -- ^ Bottom of the domain.
    
    74
    +                        -- See Note [Bottom functions are TagBottoming] in GHC.Stg.EnforceEpt.
    
    37 75
       deriving (Eq)
    
    38 76
     
    
    39 77
     instance Outputable TagInfo where
    
    40
    -  ppr TagTagged      = text "TagTagged"
    
    41
    -  ppr TagDunno       = text "TagDunno"
    
    42
    -  ppr TagProper      = text "TagProper"
    
    43
    -  ppr (TagTuple tis) = text "TagTuple" <> brackets (pprWithCommas ppr tis)
    
    78
    +  ppr TagBottoming      = text "TagBottoming"
    
    79
    +  ppr TagDunno          = text "TagDunno"
    
    80
    +  ppr TagEPT            = text "TagEPT"
    
    81
    +  ppr (TagTuple tis)    = text "TagTuple" <> brackets (pprWithCommas ppr tis)
    
    44 82
     
    
    45 83
     instance Binary TagInfo where
    
    46
    -  put_ bh TagDunno  = putByte bh 1
    
    84
    +  put_ bh TagDunno        = putByte bh 1
    
    47 85
       put_ bh (TagTuple flds) = putByte bh 2 >> put_ bh flds
    
    48
    -  put_ bh TagProper = putByte bh 3
    
    49
    -  put_ bh TagTagged = putByte bh 4
    
    86
    +  put_ bh TagEPT          = putByte bh 3
    
    87
    +  put_ bh TagBottoming    = putByte bh 4
    
    50 88
     
    
    51 89
       get bh = do tag <- getByte bh
    
    52 90
                   case tag of 1 -> return TagDunno
    
    53 91
                               2 -> TagTuple <$> get bh
    
    54
    -                          3 -> return TagProper
    
    55
    -                          4 -> return TagTagged
    
    92
    +                          3 -> return TagEPT
    
    93
    +                          4 -> return TagBottoming
    
    56 94
                               _ -> panic ("get TagInfo " ++ show tag)
    
    57 95
     
    
    58 96
     instance Outputable TagSig where
    
    59
    -  ppr (TagSig ti) = char '<' <> ppr ti <> char '>'
    
    97
    +  ppr (TagVal ti) = char '<' <> text "TagVal" <> brackets (ppr ti) <> char '>'
    
    98
    +  ppr (TagFun ti) = char '<' <> text "TagFun" <> brackets (ppr ti) <> char '>'
    
    99
    +
    
    60 100
     instance OutputableBndr (Id,TagSig) where
    
    61 101
       pprInfixOcc  = ppr
    
    62 102
       pprPrefixOcc = ppr
    
    63 103
     
    
    64 104
     instance Binary TagSig where
    
    65
    -  put_ bh (TagSig sig) = put_ bh sig
    
    66
    -  get bh = pure TagSig <*> get bh
    
    105
    +  put_ bh (TagVal ti) = putByte bh 1 >> put_ bh ti
    
    106
    +  put_ bh (TagFun ti) = putByte bh 2 >> put_ bh ti
    
    107
    +  get bh = do tag <- getByte bh
    
    108
    +              case tag of 1 -> TagVal <$> get bh
    
    109
    +                          2 -> TagFun <$> get bh
    
    110
    +                          _ -> panic ("get TagSig " ++ show tag)
    
    67 111
     
    
    112
    +-- | Is the given binding known to be properly tagged (or irrelevant, as for
    
    113
    +-- unboxed values and bottoming computations)?
    
    68 114
     isTaggedSig :: TagSig -> Bool
    
    69
    -isTaggedSig (TagSig TagProper) = True
    
    70
    -isTaggedSig (TagSig TagTagged) = True
    
    71
    -isTaggedSig _ = False
    
    115
    +isTaggedSig (TagFun _)  = True
    
    116
    +isTaggedSig (TagVal ti) = isTaggedInfo ti
    
    117
    +
    
    118
    +-- | Is the given value-level tag known to be properly tagged?
    
    119
    +-- NB: unboxed tuples are *not* treated as tagged here; they are handled
    
    120
    +-- specially by the rewriter (which considers them already evaluated).
    
    121
    +isTaggedInfo :: TagInfo -> Bool
    
    122
    +isTaggedInfo TagEPT       = True
    
    123
    +isTaggedInfo TagBottoming = True
    
    124
    +isTaggedInfo _            = False
    
    72 125
     
    
    73 126
     seqTagSig :: TagSig -> ()
    
    74
    -seqTagSig = coerce seqTagInfo
    
    127
    +seqTagSig (TagVal ti) = seqTagInfo ti
    
    128
    +seqTagSig (TagFun ti) = seqTagInfo ti
    
    75 129
     
    
    76 130
     seqTagInfo :: TagInfo -> ()
    
    77
    -seqTagInfo TagTagged      = ()
    
    131
    +seqTagInfo TagBottoming   = ()
    
    78 132
     seqTagInfo TagDunno       = ()
    
    79
    -seqTagInfo TagProper      = ()
    
    80
    -seqTagInfo (TagTuple tis) = foldl' (\_unit sig -> seqTagSig (coerce sig)) () tis
    133
    +seqTagInfo TagEPT         = ()
    
    134
    +seqTagInfo (TagTuple tis) = foldl' (\_unit info -> seqTagInfo info) () tis

  • compiler/GHC/Stg/EnforceEpt/Types.hs
    ... ... @@ -35,15 +35,23 @@ type InferStgExpr = GenStgExpr 'InferTaggedBinders
    35 35
     type InferStgRhs        = GenStgRhs        'InferTaggedBinders
    
    36 36
     type InferStgAlt        = GenStgAlt        'InferTaggedBinders
    
    37 37
     
    
    38
    +-- | Combine TagInfo from the alternatives of a case expression.
    
    39
    +-- Note that this operates at the value level: case alternatives return
    
    40
    +-- values. See Note [TagSig and TagInfo].
    
    38 41
     combineAltInfo :: TagInfo -> TagInfo -> TagInfo
    
    39
    -combineAltInfo TagDunno         _              = TagDunno
    
    40
    -combineAltInfo _                TagDunno       = TagDunno
    
    41
    -combineAltInfo (TagTuple {})    TagProper      = TagDunno  -- This can happen with rep-polymorphic result, see #26107
    
    42
    -combineAltInfo TagProper       (TagTuple {})   = TagDunno  -- This can happen with rep-polymorphic result, see #26107
    
    43
    -combineAltInfo TagProper        TagProper      = TagProper
    
    42
    +combineAltInfo TagBottoming    ti              = ti
    
    43
    +combineAltInfo ti              TagBottoming    = ti
    
    44
    +combineAltInfo TagDunno        TagDunno        = TagDunno
    
    45
    +combineAltInfo TagDunno        TagEPT          = TagDunno
    
    46
    +combineAltInfo TagDunno        (TagTuple {})   = TagDunno
    
    47
    +combineAltInfo TagEPT          TagDunno        = TagDunno
    
    48
    +combineAltInfo (TagTuple {})   TagDunno        = TagDunno
    
    49
    +combineAltInfo TagEPT          TagEPT          = TagEPT
    
    50
    +-- TagEPT/TagTuple are incompatible (can arise with rep-polymorphic
    
    51
    +-- results, see #26107); fall through to TagDunno.
    
    52
    +combineAltInfo TagEPT          (TagTuple {})   = TagDunno
    
    53
    +combineAltInfo (TagTuple {})   TagEPT          = TagDunno
    
    44 54
     combineAltInfo (TagTuple is1)  (TagTuple is2)  = TagTuple (zipWithEqual combineAltInfo is1 is2)
    
    45
    -combineAltInfo (TagTagged)      ti             = ti
    
    46
    -combineAltInfo ti               TagTagged      = ti
    
    47 55
     
    
    48 56
     type TagSigEnv = IdEnv TagSig
    
    49 57
     data TagEnv p = TE { te_env :: TagSigEnv
    
    ... ... @@ -75,48 +83,58 @@ makeTagged env = TE { te_env = te_env env
    75 83
     
    
    76 84
     noSig :: TagEnv p -> BinderP p -> (Id, TagSig)
    
    77 85
     noSig env bndr
    
    78
    -  | isUnliftedType (idType var) = (var, TagSig TagProper)
    
    79
    -  | otherwise = (var, TagSig TagDunno)
    
    86
    +  | isUnliftedType (idType var) = (var, TagVal TagEPT)
    
    87
    +  | otherwise = (var, TagVal TagDunno)
    
    80 88
       where
    
    81 89
         var = getBinderId env bndr
    
    82 90
     
    
    83
    --- | Look up a sig in the given env
    
    84
    -lookupSig :: TagEnv p -> Id -> Maybe TagSig
    
    85
    -lookupSig env fun = lookupVarEnv (te_env env) fun
    
    86
    -
    
    87
    --- | Look up a sig in the env or derive it from information
    
    88
    --- in the arg itself.
    
    91
    +-- | Look up the return-value tag of a function for saturated call analysis.
    
    92
    +-- Returns 'Just retInfo' if the function binding has a 'TagFun' signature,
    
    93
    +-- 'Nothing' otherwise.
    
    94
    +lookupReturnInfo :: TagEnv p -> Id -> Maybe TagInfo
    
    95
    +lookupReturnInfo env fun = case lookupVarEnv (te_env env) fun of
    
    96
    +  Just (TagFun ret_info) -> Just ret_info
    
    97
    +  Just (TagVal _)        -> Nothing
    
    98
    +  Nothing                -> Nothing
    
    99
    +
    
    100
    +-- | Look up a value-level tag for an argument: either from the env (where
    
    101
    +-- a function-typed argument flattens to TagEPT — its closure is tagged)
    
    102
    +-- or derived from information on the variable itself.
    
    89 103
     lookupInfo :: TagEnv p -> StgArg -> TagInfo
    
    90 104
     lookupInfo env (StgVarArg var)
    
    91 105
       -- Nullary data constructors like True, False
    
    92 106
       | Just dc <- isDataConWorkId_maybe var
    
    93 107
       , isNullaryRepDataCon dc
    
    94 108
       , not for_bytecode
    
    95
    -  = TagProper
    
    109
    +  = TagEPT
    
    96 110
     
    
    97 111
       | isUnliftedType (idType var)
    
    98
    -  = TagProper
    
    112
    +  = TagEPT
    
    99 113
     
    
    100
    -  -- Variables in the environment.
    
    101
    -  | Just (TagSig info) <- lookupVarEnv (te_env env) var
    
    102
    -  = info
    
    114
    +  -- Variables in the environment. A function binding flattens to TagEPT
    
    115
    +  -- since a function closure pointer is properly tagged; its return info
    
    116
    +  -- is not relevant when the function is used as a value.
    
    117
    +  | Just sig <- lookupVarEnv (te_env env) var
    
    118
    +  = case sig of
    
    119
    +      TagVal info -> info
    
    120
    +      TagFun _    -> TagEPT
    
    103 121
     
    
    104 122
       | Just lf_info <- idLFInfo_maybe var
    
    105 123
       , not for_bytecode
    
    106 124
       =   case lf_info of
    
    107 125
               -- Function, tagged (with arity)
    
    108 126
               LFReEntrant {}
    
    109
    -              -> TagProper
    
    127
    +              -> TagEPT
    
    110 128
               -- Thunks need to be entered.
    
    111 129
               LFThunk {}
    
    112 130
                   -> TagDunno
    
    113 131
               -- Constructors, already tagged.
    
    114 132
               LFCon {}
    
    115
    -              -> TagProper
    
    133
    +              -> TagEPT
    
    116 134
               LFUnknown {}
    
    117 135
                   -> TagDunno
    
    118 136
               LFUnlifted {}
    
    119
    -              -> TagProper
    
    137
    +              -> TagEPT
    
    120 138
               -- Shouldn't be possible. I don't think we can export letNoEscapes
    
    121 139
               LFLetNoEscape {} -> panic "LFLetNoEscape exported"
    
    122 140
     
    
    ... ... @@ -126,19 +144,13 @@ lookupInfo env (StgVarArg var)
    126 144
         for_bytecode = te_bytecode env
    
    127 145
     
    
    128 146
     lookupInfo _ (StgLitArg {})
    
    129
    -  = TagProper
    
    147
    +  = TagEPT
    
    130 148
     
    
    131 149
     isDunnoSig :: TagSig -> Bool
    
    132
    -isDunnoSig (TagSig TagDunno) = True
    
    133
    -isDunnoSig (TagSig TagProper) = False
    
    134
    -isDunnoSig (TagSig TagTuple{}) = False
    
    135
    -isDunnoSig (TagSig TagTagged{}) = False
    
    136
    -
    
    137
    -isTaggedInfo :: TagInfo -> Bool
    
    138
    -isTaggedInfo TagProper = True
    
    139
    -isTaggedInfo TagTagged = True
    
    140
    -isTaggedInfo _         = False
    
    150
    +isDunnoSig (TagVal TagDunno) = True
    
    151
    +isDunnoSig _                 = False
    
    141 152
     
    
    153
    +-- | Extend the tag environment.
    
    142 154
     extendSigEnv :: TagEnv p -> [(Id,TagSig)] -> TagEnv p
    
    143 155
     extendSigEnv env@(TE { te_env = sig_env }) bndrs
    
    144 156
       = env { te_env = extendVarEnvList sig_env bndrs }

  • testsuite/tests/core-to-stg/T14895.stderr
    ... ... @@ -8,7 +8,7 @@ T14895.go
    8 8
     [GblId, Arity=2, Str=<MC(1,L)><1L>, Unf=OtherCon []] =
    
    9 9
         {} \r [f ds]
    
    10 10
             case ds of wild {
    
    11
    -          GHC.Internal.Data.Either.Left e [Occ=Once1] -> wild<TagProper>;
    
    11
    +          GHC.Internal.Data.Either.Left e [Occ=Once1] -> wild<TagVal[TagEPT]>;
    
    12 12
               GHC.Internal.Data.Either.Right a1 [Occ=Once1] ->
    
    13 13
                   let {
    
    14 14
                     go_sat [Occ=Once1] :: b
    

  • testsuite/tests/simplCore/should_compile/T4201.stdout
    1 1
       lift :: Foo -> T
    
    2
    -  [HasNoCafRefs, TagSig: <TagProper>, LambdaFormInfo: LFReEntrant 1,
    
    3
    -   Arity: 1, Strictness: <1!A>, CPR: 1,
    
    2
    +  [HasNoCafRefs, TagSig: <TagFun[TagEPT]>,
    
    3
    +   LambdaFormInfo: LFReEntrant 1, Arity: 1, Strictness: <1!A>, CPR: 1,
    
    4 4
        Unfolding: Core: <vanilla> bof `cast` (Sym N:Foo ->_R <T>_R)]

  • testsuite/tests/simplCore/should_run/T27005.hs
    1
    +module Main (main) where
    
    2
    +import GHC.Exts.Heap (GenClosure(..), getClosureData)
    
    3
    +import T27005_aux (T(..), S(..), f)
    
    4
    +import System.Exit
    
    5
    +
    
    6
    +main :: IO ()
    
    7
    +main = do
    
    8
    +    let !t = MkT (MkS (pure ()) 0)
    
    9
    +    dup <- f t
    
    10
    +    c <- getClosureData dup
    
    11
    +    case c of
    
    12
    +      ThunkClosure{} -> putStrLn "FAIL" >> exitFailure
    
    13
    +      APClosure{}    -> putStrLn "FAIL" >> exitFailure
    
    14
    +      _              -> putStrLn "OK"

  • testsuite/tests/simplCore/should_run/T27005.stdout
    1
    +OK

  • testsuite/tests/simplCore/should_run/T27005_aux.hs
    1
    +module T27005_aux (T(..), S(..), f) where
    
    2
    +import GHC.Stack
    
    3
    +
    
    4
    +class Monad m => C m
    
    5
    +instance C IO
    
    6
    +
    
    7
    +data S = MkS !(IO ()) Int
    
    8
    +data T = MkT !S
    
    9
    +
    
    10
    +{-# SPECIALISE f :: HasCallStack => T -> IO T #-}
    
    11
    +f :: (C m, HasCallStack) => T -> m T
    
    12
    +f (MkT a) = do () <- pure (); pure $! MkT a

  • testsuite/tests/simplCore/should_run/all.T
    ... ... @@ -122,3 +122,4 @@ test('AppIsHNF', normal, compile_and_run, ['-O'])
    122 122
     test('T24359b', normal, compile_and_run, ['-O'])
    
    123 123
     test('T23429', normal, compile_and_run, ['-O'])
    
    124 124
     test('T27071', normal, compile_and_run, ['-O -fworker-wrapper-cbv'])
    
    125
    +test('T27005', [], multimod_compile_and_run, ['T27005', '-O'])

  • testsuite/tests/simplStg/should_compile/T24806.hs
    ... ... @@ -7,10 +7,10 @@ data Tup2 a b = Tup2 !a !b
    7 7
     -- All branches of go return either two properly tagged values *or* are bottom.
    
    8 8
     -- This means we should see something like:
    
    9 9
     --
    
    10
    ---      (T24806.$wgo, <TagTuple[TagProper, TagProper]>) =
    
    10
    +--      (T24806.$wgo, <TagFun[TagTuple[TagEPT, TagEPT]]>) =
    
    11 11
     --
    
    12 12
     -- in the dump output.
    
    13
    --- See Note [Bottom functions are TagTagged] for details why.
    
    13
    +-- See Note [Bottom functions are TagBottoming] for details why.
    
    14 14
     go :: List a1 -> List a2 -> Tup2 (List a2) (List a2)
    
    15 15
     go Nil ys = Tup2 ys Nil
    
    16 16
     go (Cons _ xs) ys = case ys of
    

  • testsuite/tests/simplStg/should_compile/T24806.stderr
    1 1
     
    
    2 2
     ==================== CodeGenAnal STG: ====================
    
    3
    -lvl6 :: GHC.Prim.Addr#
    
    3
    +lvl6 :: GHC.Internal.Prim.Addr#
    
    4 4
     [GblId, Unf=OtherCon []] =
    
    5 5
         "T24806.hs"#;
    
    6 6
     
    
    7
    -lvl4 :: GHC.Prim.Addr#
    
    7
    +lvl4 :: GHC.Internal.Prim.Addr#
    
    8 8
     [GblId, Unf=OtherCon []] =
    
    9 9
         "T24806"#;
    
    10 10
     
    
    11
    -lvl2 :: GHC.Prim.Addr#
    
    11
    +lvl2 :: GHC.Internal.Prim.Addr#
    
    12 12
     [GblId, Unf=OtherCon []] =
    
    13 13
         "main"#;
    
    14 14
     
    
    15
    -lvl :: GHC.Prim.Addr#
    
    15
    +lvl :: GHC.Internal.Prim.Addr#
    
    16 16
     [GblId, Unf=OtherCon []] =
    
    17 17
         "undefined"#;
    
    18 18
     
    
    19
    -(T24806.$WTup2, <TagProper>) =
    
    20
    -    {} \r [(conrep, <TagDunno>) (conrep1, <TagDunno>)]
    
    21
    -        case conrep of (conrep2, <TagProper>) {
    
    22
    -        __DEFAULT ->
    
    23
    -        case conrep1 of (conrep3, <TagProper>) {
    
    24
    -        __DEFAULT -> T24806.Tup2 [conrep2 conrep3];
    
    25
    -        };
    
    26
    -        };
    
    27
    -
    
    28
    -(T24806.$WCons, <TagProper>) =
    
    29
    -    {} \r [(conrep, <TagDunno>) (conrep1, <TagDunno>)]
    
    30
    -        case conrep1 of (conrep2, <TagProper>) {
    
    31
    -        __DEFAULT -> T24806.Cons [conrep conrep2];
    
    32
    -        };
    
    19
    +(T24806.Tup2, <TagFun[TagDunno]>) =
    
    20
    +    {} \r [(eta, <TagVal[TagDunno]>) (eta, <TagVal[TagDunno]>)] T24806.Tup2 [eta eta];
    
    33 21
     
    
    34
    -(lvl1, <TagDunno>) = {} \u [] GHC.CString.unpackCString# lvl;
    
    22
    +(T24806.Nil, <TagVal[TagEPT]>) = T24806.Nil! [];
    
    35 23
     
    
    36
    -(lvl3, <TagDunno>) = {} \u [] GHC.CString.unpackCString# lvl2;
    
    24
    +(T24806.Cons, <TagFun[TagDunno]>) =
    
    25
    +    {} \r [(eta, <TagVal[TagDunno]>) (eta, <TagVal[TagDunno]>)] T24806.Cons [eta eta];
    
    37 26
     
    
    38
    -(lvl5, <TagDunno>) = {} \u [] GHC.CString.unpackCString# lvl4;
    
    27
    +(lvl1, <TagVal[TagDunno]>) =
    
    28
    +    {} \u [] GHC.Internal.CString.unpackCString# lvl;
    
    39 29
     
    
    40
    -(lvl7, <TagDunno>) = {} \u [] GHC.CString.unpackCString# lvl6;
    
    30
    +(lvl3, <TagVal[TagDunno]>) =
    
    31
    +    {} \u [] GHC.Internal.CString.unpackCString# lvl2;
    
    41 32
     
    
    42
    -(lvl8, <TagProper>) = GHC.Types.I#! [17#];
    
    33
    +(lvl5, <TagVal[TagDunno]>) =
    
    34
    +    {} \u [] GHC.Internal.CString.unpackCString# lvl4;
    
    43 35
     
    
    44
    -(lvl9, <TagProper>) = GHC.Types.I#! [12#];
    
    36
    +(lvl7, <TagVal[TagDunno]>) =
    
    37
    +    {} \u [] GHC.Internal.CString.unpackCString# lvl6;
    
    45 38
     
    
    46
    -(lvl10, <TagProper>) = GHC.Types.I#! [21#];
    
    39
    +(lvl8, <TagVal[TagEPT]>) =
    
    40
    +    GHC.Internal.Stack.Types.SrcLoc! [lvl3 lvl5 lvl7 17# 12# 17# 21#];
    
    47 41
     
    
    48
    -(lvl11, <TagProper>) =
    
    49
    -    GHC.Internal.Stack.Types.SrcLoc! [lvl3
    
    50
    -                                      lvl5
    
    51
    -                                      lvl7
    
    52
    -                                      lvl8
    
    53
    -                                      lvl9
    
    54
    -                                      lvl8
    
    55
    -                                      lvl10];
    
    56
    -
    
    57
    -(lvl12, <TagProper>) =
    
    42
    +(lvl9, <TagVal[TagEPT]>) =
    
    58 43
         GHC.Internal.Stack.Types.PushCallStack! [lvl1
    
    59
    -                                             lvl11
    
    44
    +                                             lvl8
    
    60 45
                                                  GHC.Internal.Stack.Types.EmptyCallStack];
    
    61 46
     
    
    62
    -(lvl13, <TagDunno>) = {} \u [] GHC.Internal.Err.undefined lvl12;
    
    63
    -
    
    64
    -(T24806.Tup2, <TagDunno>) =
    
    65
    -    {} \r [(eta, <TagDunno>) (eta, <TagDunno>)] T24806.Tup2 [eta eta];
    
    66
    -
    
    67
    -(T24806.Nil, <TagProper>) = T24806.Nil! [];
    
    47
    +(lvl10, <TagVal[TagDunno]>) = {} \u [] GHC.Internal.Err.undefined lvl9;
    
    68 48
     
    
    69 49
     Rec {
    
    70
    -(T24806.$wgo, <TagTuple[TagProper, TagProper]>) =
    
    71
    -    {} \r [(ds, <TagProper>) (ys, <TagProper>)]
    
    72
    -        case ds of (wild, <TagProper>) {
    
    50
    +(T24806.$wgo, <TagFun[TagTuple[TagEPT, TagEPT]]>) =
    
    51
    +    {} \r [(ds, <TagVal[TagEPT]>) (ys, <TagVal[TagEPT]>)]
    
    52
    +        case ds of (wild, <TagVal[TagEPT]>) {
    
    73 53
               T24806.Nil ->
    
    74
    -              case ys of (conrep, <TagProper>) {
    
    75
    -              __DEFAULT -> (#,#) [conrep T24806.Nil];
    
    54
    +              case ys of (wild1, <TagVal[TagEPT]>) {
    
    55
    +              __DEFAULT -> (#,#) [wild1 T24806.Nil];
    
    76 56
                   };
    
    77
    -          T24806.Cons (ds1, <TagDunno>) (xs, <TagProper>) ->
    
    78
    -              case ys of (wild1, <TagProper>) {
    
    79
    -                T24806.Nil -> lvl13;
    
    80
    -                T24806.Cons (y, <TagDunno>) (ys', <TagProper>) ->
    
    81
    -                    case T24806.$wgo xs ys' of (wild2, <TagProper>) {
    
    82
    -                    (#,#) (ww, <TagProper>) (ww1, <TagProper>) ->
    
    83
    -                    let { (sat, <TagProper>) = T24806.Cons! [y ww1];
    
    84
    -                    } in  (#,#) [ww sat];
    
    57
    +          T24806.Cons (ds1, <TagVal[TagDunno]>) (xs, <TagVal[TagEPT]>) ->
    
    58
    +              case ys of (wild1, <TagVal[TagEPT]>) {
    
    59
    +                T24806.Nil -> lvl10;
    
    60
    +                T24806.Cons (y, <TagVal[TagDunno]>) (ys', <TagVal[TagEPT]>) ->
    
    61
    +                    case T24806.$wgo xs ys' of (wild2, <TagVal[TagEPT]>) {
    
    62
    +                    (#,#) (ww, <TagVal[TagEPT]>) (ww1, <TagVal[TagEPT]>) ->
    
    63
    +                    let { ($wgo_sat, <TagVal[TagEPT]>) = T24806.Cons! [y ww1];
    
    64
    +                    } in  (#,#) [ww $wgo_sat];
    
    85 65
                         };
    
    86 66
                   };
    
    87 67
             };
    
    88 68
     end Rec }
    
    89 69
     
    
    90
    -(T24806.go, <TagProper>) =
    
    91
    -    {} \r [(ds, <TagDunno>) (ys, <TagDunno>)]
    
    92
    -        case T24806.$wgo ds ys of (wild, <TagProper>) {
    
    93
    -        (#,#) (ww, <TagProper>) (ww1, <TagProper>) -> T24806.Tup2 [ww ww1];
    
    70
    +(T24806.go, <TagFun[TagEPT]>) =
    
    71
    +    {} \r [(ds, <TagVal[TagDunno]>) (ys, <TagVal[TagDunno]>)]
    
    72
    +        case T24806.$wgo ds ys of (wild, <TagVal[TagEPT]>) {
    
    73
    +        (#,#) (ww, <TagVal[TagEPT]>) (ww1, <TagVal[TagEPT]>) ->
    
    74
    +        T24806.Tup2 [ww ww1];
    
    94 75
             };
    
    95 76
     
    
    96
    -(T24806.Cons, <TagDunno>) =
    
    97
    -    {} \r [(eta, <TagDunno>) (eta, <TagDunno>)] T24806.Cons [eta eta];
    
    98
    -
    
    99 77
     

  • testsuite/tests/simplStg/should_compile/T27005b.hs
    1
    +{-# LANGUAGE BangPatterns, UnboxedTuples #-}
    
    2
    +module T27005b where
    
    3
    +
    
    4
    +-- A function returning an unboxed tuple whose single component is itself a
    
    5
    +-- function. With the TagFun/TagVal split, `f`'s tag signature records that the
    
    6
    +-- returned component is properly tagged (TagEPT), so when `g` scrutinises the
    
    7
    +-- result of `f h` the binder `x` is already known to be evaluated. Hence the
    
    8
    +-- pair `(x, x)` is built without re-evaluating `x`.
    
    9
    +f :: (Bool -> Bool) -> (# Bool -> Bool #)
    
    10
    +f !x = (# x #)
    
    11
    +{-# NOINLINE f #-}
    
    12
    +
    
    13
    +g :: (Bool -> Bool) -> (Bool -> Bool, Bool -> Bool)
    
    14
    +g h =
    
    15
    +  case f h of
    
    16
    +    (# !x #) -> (x, x)

  • testsuite/tests/simplStg/should_compile/T27005b.stderr
    1
    +
    
    2
    +==================== CodeGenAnal STG: ====================
    
    3
    +(T27005b.f, <TagFun[TagTuple[TagEPT]]>) =
    
    4
    +    {} \r [(x, <TagVal[TagDunno]>)]
    
    5
    +        case x of (x1, <TagVal[TagEPT]>) {
    
    6
    +        __DEFAULT -> GHC.Internal.Types.MkSolo# [x1];
    
    7
    +        };
    
    8
    +
    
    9
    +(T27005b.g, <TagFun[TagEPT]>) =
    
    10
    +    {} \r [(h, <TagVal[TagDunno]>)]
    
    11
    +        case T27005b.f h of (ds, <TagVal[TagEPT]>) {
    
    12
    +        GHC.Internal.Types.MkSolo# (ipv, <TagVal[TagEPT]>) ->
    
    13
    +        case ipv of (x, <TagVal[TagEPT]>) { __DEFAULT -> (,) [x x]; };
    
    14
    +        };
    
    15
    +
    
    16
    +

  • testsuite/tests/simplStg/should_compile/all.T
    ... ... @@ -24,5 +24,6 @@ test('inferTags003', [ only_ways(['optasm']),
    24 24
                            grep_errmsg(r'(call stg\_ap\_0)', [1])
    
    25 25
                          ], compile, ['-ddump-cmm -dno-typeable-binds -O'])
    
    26 26
     test('inferTags004', normal, compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])
    
    27
    +test('T27005b', normal, compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])
    
    27 28
     
    
    28 29
     test('T24806', grep_errmsg('^\\(T24806\\.\\$wgo'), compile, ['-O -ddump-stg-tags -dno-typeable-binds -dsuppress-uniques'])

  • testsuite/tests/simplStg/should_compile/inferTags004.hs
    ... ... @@ -7,5 +7,5 @@ x = x
    7 7
     f :: a -> (# Int, a #)
    
    8 8
     -- Adapted from a TODO in EnforceEpt.
    
    9 9
     -- f's tag signature should indicate that the second component
    
    10
    --- of its result is properly tagged: TagTuple[TagDunno,TagProper]
    
    10
    +-- of its result is properly tagged: TagTuple[TagDunno,TagEPT]
    
    11 11
     f g = case g of !g' -> (# x, g' #)

  • testsuite/tests/simplStg/should_compile/inferTags004.stderr
    1 1
     
    
    2 2
     ==================== CodeGenAnal STG: ====================
    
    3 3
     Rec {
    
    4
    -(EnforceEpt004.x, <TagDunno>) = {} \u [] EnforceEpt004.x;
    
    4
    +(EnforceEpt004.x, <TagVal[TagDunno]>) = {} \u [] EnforceEpt004.x;
    
    5 5
     end Rec }
    
    6 6
     
    
    7
    -(EnforceEpt004.f, <TagTuple[TagDunno, TagProper]>) =
    
    8
    -    {} \r [(g, <TagDunno>)]
    
    9
    -        case g of (g', <TagProper>) {
    
    7
    +(EnforceEpt004.f, <TagFun[TagTuple[TagDunno, TagEPT]]>) =
    
    8
    +    {} \r [(g, <TagVal[TagDunno]>)]
    
    9
    +        case g of (g', <TagVal[TagEPT]>) {
    
    10 10
             __DEFAULT -> (#,#) [EnforceEpt004.x g'];
    
    11 11
             };
    
    12 12
     
    

  • testsuite/tests/simplStg/should_run/T27005a.hs
    1
    +{-# LANGUAGE UnboxedTuples, MagicHash #-}
    
    2
    +-- Test for EnforceEpt mixing up tag signatures between functions and their return values
    
    3
    +module Main (main) where
    
    4
    +
    
    5
    +import GHC.Exts
    
    6
    +import GHC.Exts.Heap (GenClosure(..), getClosureData)
    
    7
    +import System.Exit
    
    8
    +
    
    9
    +-- an evaluated function that returns a tuple
    
    10
    +g :: State# RealWorld -> (# State# RealWorld, () #)
    
    11
    +g s = (# s, () #)
    
    12
    +{-# NOINLINE g #-}
    
    13
    +
    
    14
    +data S = MkS !(State# RealWorld -> (# State# RealWorld, () #)) Int
    
    15
    +data T = MkT !S
    
    16
    +
    
    17
    +s :: S
    
    18
    +s = MkS g 0
    
    19
    +{-# NOINLINE s #-}
    
    20
    +
    
    21
    +t :: T
    
    22
    +t = MkT s
    
    23
    +
    
    24
    +main :: IO ()
    
    25
    +main = do
    
    26
    +    c <- getClosureData t
    
    27
    +    case c of
    
    28
    +        ThunkClosure{} -> putStrLn "FAIL: ThunkClosure" >> exitFailure
    
    29
    +        APClosure{}    -> putStrLn "FAIL: APClosure" >> exitFailure
    
    30
    +        _              -> putStrLn "OK"

  • testsuite/tests/simplStg/should_run/T27005a.stdout
    1
    +OK

  • testsuite/tests/simplStg/should_run/all.T
    ... ... @@ -21,4 +21,5 @@ test('T13536a',
    21 21
     test('inferTags001', normal, multimod_compile_and_run, ['inferTags001', 'inferTags001_a'])
    
    22 22
     test('T22042', [extra_files(['T22042a.hs']),only_ways('normal'),unless(have_dynamic(), skip)], makefile_test, ['T22042'])
    
    23 23
     test('T23783', normal, multimod_compile_and_run, ['T23783', '-O -v0'])
    
    24
    -test('unpack_enum', normal, compile_and_run, [''])
    \ No newline at end of file
    24
    +test('unpack_enum', normal, compile_and_run, [''])
    
    25
    +test('T27005a', normal, compile_and_run, [''])