StgLiftLams.LiftM

ghc-8.10.3: The GHC API

Safe Haskell | None
Language | Haskell2010

StgLiftLams.LiftM

Contents

- Handling floats
- Transformation monad
  - Adding bindings
  - Substitution and binders
  - Occurrences

Description

Hides away distracting bookkeeping while lambda lifting into a `LiftM` monad.

Synopsis

- decomposeStgBinding :: GenStgBinding pass -> (RecFlag, [(BinderP pass, GenStgRhs pass)])
- mkStgBinding :: RecFlag -> [(BinderP pass, GenStgRhs pass)] -> GenStgBinding pass
- data Env = Env {
  - e_dflags :: !DynFlags
  - e_subst :: !Subst
  - e_expansions :: !(IdEnv DIdSet)
  - e_in_caffy_context :: !Bool
  }
- data FloatLang
  - = StartBindingGroup
  - | EndBindingGroup
  - | PlainTopBinding OutStgTopBinding
  - | LiftedBinding OutStgBinding
- collectFloats :: [FloatLang] -> [OutStgTopBinding]
- data LiftM a
- runLiftM :: DynFlags -> UniqSupply -> LiftM () -> [OutStgTopBinding]
- withCaffyness :: Bool -> LiftM a -> LiftM a
- startBindingGroup :: LiftM ()
- endBindingGroup :: LiftM ()
- addTopStringLit :: OutId -> ByteString -> LiftM ()
- addLiftedBinding :: OutStgBinding -> LiftM ()
- withSubstBndr :: Id -> (Id -> LiftM a) -> LiftM a
- withSubstBndrs :: Traversable f => f Id -> (f Id -> LiftM a) -> LiftM a
- withLiftedBndr :: DIdSet -> Id -> (Id -> LiftM a) -> LiftM a
- withLiftedBndrs :: Traversable f => DIdSet -> f Id -> (f Id -> LiftM a) -> LiftM a
- substOcc :: Id -> LiftM Id
- isLifted :: InId -> LiftM Bool
- formerFreeVars :: InId -> LiftM [OutId]
- liftedIdsExpander :: LiftM (DIdSet -> DIdSet)

# Documentation

decomposeStgBinding :: GenStgBinding pass -> (RecFlag, [(BinderP pass, GenStgRhs pass)]) #

    uncurry mkStgBinding . decomposeStgBinding = id

mkStgBinding :: RecFlag -> [(BinderP pass, GenStgRhs pass)] -> GenStgBinding pass #

data Env #

Environment threaded around in a scoped, `Reader`-like fashion.

Constructors

Env |
Fields e_dflags :: !DynFlags Read-only. e_subst :: !Subst We need to track the renamings of local `InId`s to their lifted `OutId`, because shadowing might make a closure's free variables unavailable at its call sites. Consider: `let f y = x + y in let x = 4 in f x` Here, `f` can't be lifted to top-level, because its free variable `x` isn't available at its call site. e_expansions :: !(IdEnv DIdSet) Lifted `Id`s don't occur as free variables in any closure anymore, because they are bound at the top-level. Every occurrence must supply the formerly free variables of the lifted `Id`, so they in turn become free variables of the call sites. This environment tracks this expansion from lifted `Id`s to their free variables. `InId`s to `OutId`s. Invariant: `Id`s not present in this map won't be substituted. e_in_caffy_context :: !Bool Are we currently analysing within a caffy context (e.g. the containing top-level binder's `idCafInfo` is `MayHaveCafRefs`)? If not, we can safely assume that functions we lift out aren't caffy either.

# Handling floats

Consider the following expression:

        f x =
          let g y = ... f y ...
          in g x

What happens when we want to lift `g`? Normally, we'd put the lifted `l_g` binding above the binding for `f`:

        g f y = ... f y ...
        f x = g f x

But this very unnecessarily turns a known call to `f` into an unknown one, in addition to complicating matters for the analysis. Instead, we'd really like to put both functions in the same recursive group, thereby preserving the known call:

        Rec {
          g y = ... f y ...
          f x = g x
        }

But we don't want this to happen for just any binding. That would create possibly huge recursive groups in the process, calling for an occurrence analyser on STG. So, we need to track when we lift a binding out of a recursive RHS and add the binding to the same recursive group as the enclosing recursive binding (which must have either already been at the top-level or decided to be lifted itself in order to preserve the known call).

This is done by expressing this kind of nesting structure as a `Writer` over `[`FloatLang`]` and flattening this expression in `runLiftM` by a call to `collectFloats`. API-wise, the analysis will not need to know about the whole `FloatLang` business and will just manipulate it indirectly through actions in `LiftM`.

data FloatLang #

We need to detect when we are lifting something out of the RHS of a recursive binding (c.f. StgLiftLams.LiftM), in which case that binding needs to be added to the same top-level recursive group. This requires we detect a certain nesting structure, which is encoded by `StartBindingGroup` and `EndBindingGroup`.

Although `collectFloats` will only ever care if the current binding to be lifted (through `LiftedBinding`) will occur inside such a binding group or not, e.g. doesn't care about the nesting level as long as its greater than 0.

Constructors

StartBindingGroup |
EndBindingGroup |
PlainTopBinding OutStgTopBinding |
LiftedBinding OutStgBinding |

#### Instances

Instances details

Outputable FloatLang # |
Instance details Defined in StgLiftLams.LiftM Methods ppr :: FloatLang -> SDoc # pprPrec :: Rational -> FloatLang -> SDoc #

collectFloats :: [FloatLang] -> [OutStgTopBinding] #

Flattens an expression in `[`FloatLang`]` into an STG program, see #floats. Important pre-conditions: The nesting of opening `StartBindinGroup`s and closing `EndBindinGroup`s is balanced. Also, it is crucial that every binding group has at least one recursive binding inside. Otherwise there's no point in announcing the binding group in the first place and an `ASSERT` will trigger.

# Transformation monad

data LiftM a #

The analysis monad consists of the following `RWST` components:

- `Env`: Reader-like context. Contains a substitution, info about how how lifted identifiers are to be expanded into applications and details such as `DynFlags` and a flag helping with determining if a lifted binding is caffy.
- ``OrdList` `FloatLang``: Writer output for the resulting STG program.
- No pure state component
- But wrapping around `UniqSM` for generating fresh lifted binders. (The `uniqAway` approach could give the same name to two different lifted binders, so this is necessary.)

#### Instances

Instances details

Monad LiftM # |
Instance details Defined in StgLiftLams.LiftM Methods (>>=) :: LiftM a -> (a -> LiftM b) -> LiftM b Source # (>>) :: LiftM a -> LiftM b -> LiftM b Source # return :: a -> LiftM a Source #
Functor LiftM # |
Instance details Defined in StgLiftLams.LiftM Methods fmap :: (a -> b) -> LiftM a -> LiftM b Source # (<$) :: a -> LiftM b -> LiftM a Source #
Applicative LiftM # |
Instance details Defined in StgLiftLams.LiftM Methods pure :: a -> LiftM a Source # (<*>) :: LiftM (a -> b) -> LiftM a -> LiftM b Source # liftA2 :: (a -> b -> c) -> LiftM a -> LiftM b -> LiftM c Source # (*>) :: LiftM a -> LiftM b -> LiftM b Source # (<*) :: LiftM a -> LiftM b -> LiftM a Source #
MonadUnique LiftM # |
Instance details Defined in StgLiftLams.LiftM Methods getUniqueSupplyM :: LiftM UniqSupply # getUniqueM :: LiftM Unique # getUniquesM :: LiftM [Unique] #
HasDynFlags LiftM # |
Instance details Defined in StgLiftLams.LiftM Methods getDynFlags :: LiftM DynFlags #

runLiftM :: DynFlags -> UniqSupply -> LiftM () -> [OutStgTopBinding] #

withCaffyness :: Bool -> LiftM a -> LiftM a #

Assumes a given caffyness for the execution of the passed action, which influences the `cafInfo` of lifted bindings.

## Adding bindings

startBindingGroup :: LiftM () #

Starts a recursive binding group. See and `collectFloats`.

endBindingGroup :: LiftM () #

Ends a recursive binding group. See and `collectFloats`.

addTopStringLit :: OutId -> ByteString -> LiftM () #

Writes a plain `StgTopStringLit` to the output.

addLiftedBinding :: OutStgBinding -> LiftM () #

Lifts a binding to top-level. Depending on whether it's declared inside a recursive RHS (see and `collectFloats`), this might be added to an existing recursive top-level binding group.

## Substitution and binders

withSubstBndr :: Id -> (Id -> LiftM a) -> LiftM a #

Takes a binder and a continuation which is called with the substituted binder. The continuation will be evaluated in a `LiftM` context in which that binder is deemed in scope. Think of it as a `local` computation: After the continuation finishes, the new binding won't be in scope anymore.

withSubstBndrs :: Traversable f => f Id -> (f Id -> LiftM a) -> LiftM a #

See `withSubstBndr`.

withLiftedBndr :: DIdSet -> Id -> (Id -> LiftM a) -> LiftM a #

Similarly to `withSubstBndr`, this function takes a set of variables to abstract over, the binder to lift (and generate a fresh, substituted name for) and a continuation in which that fresh, lifted binder is in scope.

It takes care of all the details involved with copying and adjusting the binder, fresh name generation and caffyness.

withLiftedBndrs :: Traversable f => DIdSet -> f Id -> (f Id -> LiftM a) -> LiftM a #

See `withLiftedBndr`.

## Occurrences

substOcc :: Id -> LiftM Id #

Substitutes a binder occurrence, which was brought in scope earlier by `withSubstBndr`/`withLiftedBndr`.

isLifted :: InId -> LiftM Bool #

Whether the given binding was decided to be lambda lifted.

formerFreeVars :: InId -> LiftM [OutId] #

Returns an empty list for a binding that was not lifted and the list of all local variables the binding abstracts over (so, exactly the additional arguments at adjusted call sites) otherwise.

liftedIdsExpander :: LiftM (DIdSet -> DIdSet) #

Creates an expander function for the current set of lifted binders. This expander function will replace any `InId` by their corresponding `OutId` and, in addition, will expand any lifted binders by the former free variables it abstracts over.