Commit ceffd7fe authored by Ben Gamari's avatar Ben Gamari 🐢

Revert "An overhaul of the SRT representation"

This reverts commit eb8e692c.
parent dee22948
...@@ -14,11 +14,12 @@ module CLabel ( ...@@ -14,11 +14,12 @@ module CLabel (
pprDebugCLabel, pprDebugCLabel,
mkClosureLabel, mkClosureLabel,
mkSRTLabel, mkTopSRTLabel,
mkInfoTableLabel, mkInfoTableLabel,
mkEntryLabel, mkEntryLabel,
mkRednCountsLabel, mkRednCountsLabel,
mkConInfoTableLabel, mkConInfoTableLabel,
mkLargeSRTLabel,
mkApEntryLabel, mkApEntryLabel,
mkApInfoTableLabel, mkApInfoTableLabel,
mkClosureTableLabel, mkClosureTableLabel,
...@@ -53,7 +54,6 @@ module CLabel ( ...@@ -53,7 +54,6 @@ module CLabel (
mkSMAP_DIRTY_infoLabel, mkSMAP_DIRTY_infoLabel,
mkBadAlignmentLabel, mkBadAlignmentLabel,
mkArrWords_infoLabel, mkArrWords_infoLabel,
mkSRTInfoLabel,
mkTopTickyCtrLabel, mkTopTickyCtrLabel,
mkCAFBlackHoleInfoTableLabel, mkCAFBlackHoleInfoTableLabel,
...@@ -250,7 +250,10 @@ data CLabel ...@@ -250,7 +250,10 @@ data CLabel
| HpcTicksLabel Module | HpcTicksLabel Module
-- | Static reference table -- | Static reference table
| SRTLabel | SRTLabel !Unique
-- | Label of an StgLargeSRT
| LargeSRTLabel
{-# UNPACK #-} !Unique {-# UNPACK #-} !Unique
-- | A bitmap (function or case return) -- | A bitmap (function or case return)
...@@ -300,6 +303,8 @@ instance Ord CLabel where ...@@ -300,6 +303,8 @@ instance Ord CLabel where
compare a1 a2 compare a1 a2
compare (SRTLabel u1) (SRTLabel u2) = compare (SRTLabel u1) (SRTLabel u2) =
nonDetCmpUnique u1 u2 nonDetCmpUnique u1 u2
compare (LargeSRTLabel u1) (LargeSRTLabel u2) =
nonDetCmpUnique u1 u2
compare (LargeBitmapLabel u1) (LargeBitmapLabel u2) = compare (LargeBitmapLabel u1) (LargeBitmapLabel u2) =
nonDetCmpUnique u1 u2 nonDetCmpUnique u1 u2
compare IdLabel{} _ = LT compare IdLabel{} _ = LT
...@@ -332,6 +337,8 @@ instance Ord CLabel where ...@@ -332,6 +337,8 @@ instance Ord CLabel where
compare _ HpcTicksLabel{} = GT compare _ HpcTicksLabel{} = GT
compare SRTLabel{} _ = LT compare SRTLabel{} _ = LT
compare _ SRTLabel{} = GT compare _ SRTLabel{} = GT
compare LargeSRTLabel{} _ = LT
compare _ LargeSRTLabel{} = GT
-- | Record where a foreign label is stored. -- | Record where a foreign label is stored.
data ForeignLabelSource data ForeignLabelSource
...@@ -380,6 +387,9 @@ pprDebugCLabel lbl ...@@ -380,6 +387,9 @@ pprDebugCLabel lbl
data IdLabelInfo data IdLabelInfo
= Closure -- ^ Label for closure = Closure -- ^ Label for closure
| SRT -- ^ Static reference table (TODO: could be removed
-- with the old code generator, but might be needed
-- when we implement the New SRT Plan)
| InfoTable -- ^ Info tables for closures; always read-only | InfoTable -- ^ Info tables for closures; always read-only
| Entry -- ^ Entry point | Entry -- ^ Entry point
| Slow -- ^ Slow entry point | Slow -- ^ Slow entry point
...@@ -449,8 +459,8 @@ data DynamicLinkerLabelInfo ...@@ -449,8 +459,8 @@ data DynamicLinkerLabelInfo
-- Constructing IdLabels -- Constructing IdLabels
-- These are always local: -- These are always local:
mkSRTLabel :: Unique -> CLabel mkTopSRTLabel :: Unique -> CLabel
mkSRTLabel u = SRTLabel u mkTopSRTLabel u = SRTLabel u
mkRednCountsLabel :: Name -> CLabel mkRednCountsLabel :: Name -> CLabel
mkRednCountsLabel name = mkRednCountsLabel name =
...@@ -508,29 +518,6 @@ mkSMAP_FROZEN_DIRTY_infoLabel = CmmLabel rtsUnitId (fsLit "stg_SMALL_MUT_ARR_P ...@@ -508,29 +518,6 @@ mkSMAP_FROZEN_DIRTY_infoLabel = CmmLabel rtsUnitId (fsLit "stg_SMALL_MUT_ARR_P
mkSMAP_DIRTY_infoLabel = CmmLabel rtsUnitId (fsLit "stg_SMALL_MUT_ARR_PTRS_DIRTY") CmmInfo mkSMAP_DIRTY_infoLabel = CmmLabel rtsUnitId (fsLit "stg_SMALL_MUT_ARR_PTRS_DIRTY") CmmInfo
mkBadAlignmentLabel = CmmLabel rtsUnitId (fsLit "stg_badAlignment") CmmEntry mkBadAlignmentLabel = CmmLabel rtsUnitId (fsLit "stg_badAlignment") CmmEntry
mkSRTInfoLabel :: Int -> CLabel
mkSRTInfoLabel n = CmmLabel rtsUnitId lbl CmmInfo
where
lbl =
case n of
1 -> fsLit "stg_SRT_1"
2 -> fsLit "stg_SRT_2"
3 -> fsLit "stg_SRT_3"
4 -> fsLit "stg_SRT_4"
5 -> fsLit "stg_SRT_5"
6 -> fsLit "stg_SRT_6"
7 -> fsLit "stg_SRT_7"
8 -> fsLit "stg_SRT_8"
9 -> fsLit "stg_SRT_9"
10 -> fsLit "stg_SRT_10"
11 -> fsLit "stg_SRT_11"
12 -> fsLit "stg_SRT_12"
13 -> fsLit "stg_SRT_13"
14 -> fsLit "stg_SRT_14"
15 -> fsLit "stg_SRT_15"
16 -> fsLit "stg_SRT_16"
_ -> panic "mkSRTInfoLabel"
----- -----
mkCmmInfoLabel, mkCmmEntryLabel, mkCmmRetInfoLabel, mkCmmRetLabel, mkCmmInfoLabel, mkCmmEntryLabel, mkCmmRetInfoLabel, mkCmmRetLabel,
mkCmmCodeLabel, mkCmmDataLabel, mkCmmClosureLabel mkCmmCodeLabel, mkCmmDataLabel, mkCmmClosureLabel
...@@ -615,6 +602,9 @@ isSomeRODataLabel (IdLabel _ _ ConInfoTable) = True ...@@ -615,6 +602,9 @@ isSomeRODataLabel (IdLabel _ _ ConInfoTable) = True
isSomeRODataLabel (IdLabel _ _ InfoTable) = True isSomeRODataLabel (IdLabel _ _ InfoTable) = True
isSomeRODataLabel (IdLabel _ _ LocalInfoTable) = True isSomeRODataLabel (IdLabel _ _ LocalInfoTable) = True
isSomeRODataLabel (IdLabel _ _ BlockInfoTable) = True isSomeRODataLabel (IdLabel _ _ BlockInfoTable) = True
-- static reference tables defined in haskell (.hs)
isSomeRODataLabel (IdLabel _ _ SRT) = True
isSomeRODataLabel (SRTLabel _) = True
-- info table defined in cmm (.cmm) -- info table defined in cmm (.cmm)
isSomeRODataLabel (CmmLabel _ _ CmmInfo) = True isSomeRODataLabel (CmmLabel _ _ CmmInfo) = True
isSomeRODataLabel _lbl = False isSomeRODataLabel _lbl = False
...@@ -626,7 +616,9 @@ foreignLabelStdcallInfo _lbl = Nothing ...@@ -626,7 +616,9 @@ foreignLabelStdcallInfo _lbl = Nothing
-- Constructing Large*Labels -- Constructing Large*Labels
mkLargeSRTLabel :: Unique -> CLabel
mkBitmapLabel :: Unique -> CLabel mkBitmapLabel :: Unique -> CLabel
mkLargeSRTLabel uniq = LargeSRTLabel uniq
mkBitmapLabel uniq = LargeBitmapLabel uniq mkBitmapLabel uniq = LargeBitmapLabel uniq
-- Constructing Cost Center Labels -- Constructing Cost Center Labels
...@@ -684,6 +676,8 @@ mkAsmTempDieLabel l = mkAsmTempDerivedLabel l (fsLit "_die") ...@@ -684,6 +676,8 @@ mkAsmTempDieLabel l = mkAsmTempDerivedLabel l (fsLit "_die")
-- Convert between different kinds of label -- Convert between different kinds of label
toClosureLbl :: CLabel -> CLabel toClosureLbl :: CLabel -> CLabel
toClosureLbl (IdLabel n _ BlockInfoTable)
= pprPanic "toClosureLbl: BlockInfoTable" (ppr n)
toClosureLbl (IdLabel n c _) = IdLabel n c Closure toClosureLbl (IdLabel n c _) = IdLabel n c Closure
toClosureLbl (CmmLabel m str _) = CmmLabel m str CmmClosure toClosureLbl (CmmLabel m str _) = CmmLabel m str CmmClosure
toClosureLbl l = pprPanic "toClosureLbl" (ppr l) toClosureLbl l = pprPanic "toClosureLbl" (ppr l)
...@@ -752,6 +746,7 @@ needsCDecl :: CLabel -> Bool ...@@ -752,6 +746,7 @@ needsCDecl :: CLabel -> Bool
-- don't bother declaring Bitmap labels, we always make sure -- don't bother declaring Bitmap labels, we always make sure
-- they are defined before use. -- they are defined before use.
needsCDecl (SRTLabel _) = True needsCDecl (SRTLabel _) = True
needsCDecl (LargeSRTLabel _) = False
needsCDecl (LargeBitmapLabel _) = False needsCDecl (LargeBitmapLabel _) = False
needsCDecl (IdLabel _ _ _) = True needsCDecl (IdLabel _ _ _) = True
needsCDecl (LocalBlockLabel _) = True needsCDecl (LocalBlockLabel _) = True
...@@ -898,10 +893,12 @@ externallyVisibleCLabel (DynamicLinkerLabel _ _) = False ...@@ -898,10 +893,12 @@ externallyVisibleCLabel (DynamicLinkerLabel _ _) = False
externallyVisibleCLabel (HpcTicksLabel _) = True externallyVisibleCLabel (HpcTicksLabel _) = True
externallyVisibleCLabel (LargeBitmapLabel _) = False externallyVisibleCLabel (LargeBitmapLabel _) = False
externallyVisibleCLabel (SRTLabel _) = False externallyVisibleCLabel (SRTLabel _) = False
externallyVisibleCLabel (LargeSRTLabel _) = False
externallyVisibleCLabel (PicBaseLabel {}) = panic "externallyVisibleCLabel PicBaseLabel" externallyVisibleCLabel (PicBaseLabel {}) = panic "externallyVisibleCLabel PicBaseLabel"
externallyVisibleCLabel (DeadStripPreventer {}) = panic "externallyVisibleCLabel DeadStripPreventer" externallyVisibleCLabel (DeadStripPreventer {}) = panic "externallyVisibleCLabel DeadStripPreventer"
externallyVisibleIdLabel :: IdLabelInfo -> Bool externallyVisibleIdLabel :: IdLabelInfo -> Bool
externallyVisibleIdLabel SRT = False
externallyVisibleIdLabel LocalInfoTable = False externallyVisibleIdLabel LocalInfoTable = False
externallyVisibleIdLabel LocalEntry = False externallyVisibleIdLabel LocalEntry = False
externallyVisibleIdLabel BlockInfoTable = False externallyVisibleIdLabel BlockInfoTable = False
...@@ -957,6 +954,7 @@ labelType (DynamicLinkerLabel _ _) = DataLabel -- Is this right? ...@@ -957,6 +954,7 @@ labelType (DynamicLinkerLabel _ _) = DataLabel -- Is this right?
labelType PicBaseLabel = DataLabel labelType PicBaseLabel = DataLabel
labelType (DeadStripPreventer _) = DataLabel labelType (DeadStripPreventer _) = DataLabel
labelType (HpcTicksLabel _) = DataLabel labelType (HpcTicksLabel _) = DataLabel
labelType (LargeSRTLabel _) = DataLabel
labelType (LargeBitmapLabel _) = DataLabel labelType (LargeBitmapLabel _) = DataLabel
idInfoLabelType :: IdLabelInfo -> CLabelType idInfoLabelType :: IdLabelInfo -> CLabelType
...@@ -1045,6 +1043,7 @@ internal names. <type> is one of the following: ...@@ -1045,6 +1043,7 @@ internal names. <type> is one of the following:
info Info table info Info table
srt Static reference table srt Static reference table
srtd Static reference table descriptor
entry Entry code (function, closure) entry Entry code (function, closure)
slow Slow entry code (if any) slow Slow entry code (if any)
ret Direct return address ret Direct return address
...@@ -1183,6 +1182,7 @@ pprCLbl (StringLitLabel u) ...@@ -1183,6 +1182,7 @@ pprCLbl (StringLitLabel u)
pprCLbl (SRTLabel u) pprCLbl (SRTLabel u)
= pprUniqueAlways u <> pp_cSEP <> text "srt" = pprUniqueAlways u <> pp_cSEP <> text "srt"
pprCLbl (LargeSRTLabel u) = pprUniqueAlways u <> pp_cSEP <> text "srtd"
pprCLbl (LargeBitmapLabel u) = text "b" <> pprUniqueAlways u <> pp_cSEP <> text "btm" pprCLbl (LargeBitmapLabel u) = text "b" <> pprUniqueAlways u <> pp_cSEP <> text "btm"
-- Some bitsmaps for tuple constructors have a numeric tag (e.g. '7') -- Some bitsmaps for tuple constructors have a numeric tag (e.g. '7')
-- until that gets resolved we'll just force them to start -- until that gets resolved we'll just force them to start
...@@ -1275,6 +1275,7 @@ ppIdFlavor :: IdLabelInfo -> SDoc ...@@ -1275,6 +1275,7 @@ ppIdFlavor :: IdLabelInfo -> SDoc
ppIdFlavor x = pp_cSEP <> ppIdFlavor x = pp_cSEP <>
(case x of (case x of
Closure -> text "closure" Closure -> text "closure"
SRT -> text "srt"
InfoTable -> text "info" InfoTable -> text "info"
LocalInfoTable -> text "info" LocalInfoTable -> text "info"
Entry -> text "entry" Entry -> text "entry"
......
...@@ -18,6 +18,7 @@ module Cmm ( ...@@ -18,6 +18,7 @@ module Cmm (
-- * Info Tables -- * Info Tables
CmmTopInfo(..), CmmStackInfo(..), CmmInfoTable(..), topInfoTable, CmmTopInfo(..), CmmStackInfo(..), CmmInfoTable(..), topInfoTable,
ClosureTypeInfo(..), ClosureTypeInfo(..),
C_SRT(..), needsSRT,
ProfilingInfo(..), ConstrDescription, ProfilingInfo(..), ConstrDescription,
-- * Statements, expressions and types -- * Statements, expressions and types
...@@ -137,13 +138,24 @@ data CmmInfoTable ...@@ -137,13 +138,24 @@ data CmmInfoTable
cit_lbl :: CLabel, -- Info table label cit_lbl :: CLabel, -- Info table label
cit_rep :: SMRep, cit_rep :: SMRep,
cit_prof :: ProfilingInfo, cit_prof :: ProfilingInfo,
cit_srt :: Maybe CLabel -- empty, or a closure address cit_srt :: C_SRT
} }
data ProfilingInfo data ProfilingInfo
= NoProfilingInfo = NoProfilingInfo
| ProfilingInfo [Word8] [Word8] -- closure_type, closure_desc | ProfilingInfo [Word8] [Word8] -- closure_type, closure_desc
-- C_SRT is what StgSyn.SRT gets translated to...
-- we add a label for the table, and expect only the 'offset/length' form
data C_SRT = NoC_SRT
| C_SRT !CLabel !WordOff !StgHalfWord {-bitmap or escape-}
deriving (Eq)
needsSRT :: C_SRT -> Bool
needsSRT NoC_SRT = False
needsSRT (C_SRT _ _ _) = True
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Static Data -- Static Data
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
......
{-# LANGUAGE GADTs, BangPatterns, RecordWildCards, {-# LANGUAGE BangPatterns, GADTs #-}
GeneralizedNewtypeDeriving, NondecreasingIndentation #-}
module CmmBuildInfoTables module CmmBuildInfoTables
( CAFSet, CAFEnv, cafAnal ( CAFSet, CAFEnv, cafAnal
, doSRTs, ModuleSRTInfo, emptySRT , doSRTs, TopSRT, emptySRT, isEmptySRT, srtToData )
) where where
import GhcPrelude hiding (succ) import GhcPrelude hiding (succ)
import BlockId
import Hoopl.Block import Hoopl.Block
import Hoopl.Graph import Hoopl.Graph
import Hoopl.Label import Hoopl.Label
import Hoopl.Collections import Hoopl.Collections
import Hoopl.Dataflow import Hoopl.Dataflow
import Module
import Digraph import Digraph
import Bitmap
import CLabel import CLabel
import PprCmmDecl () import PprCmmDecl ()
import Cmm import Cmm
import CmmUtils import CmmUtils
import CmmInfo
import Data.List
import DynFlags import DynFlags
import Maybes import Maybes
import Outputable import Outputable
import SMRep import SMRep
import UniqSupply import UniqSupply
import CostCentre import Util
import StgCmmHeap
import PprCmm() import PprCmm()
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import Data.Tuple
import Control.Monad import Control.Monad
import Control.Monad.Trans.State
import Control.Monad.Trans.Class
foldSet :: (a -> b -> b) -> b -> Set a -> b
foldSet = Set.foldr
{- Note [SRTs] -----------------------------------------------------------------------
-- SRTs
SRTs are the mechanism by which the garbage collector can determine {- EXAMPLE
the live CAFs in the program.
Representation
^^^^^^^^^^^^^^
+------+
| info |
| | +-----+---+---+---+
| -------->|SRT_2| | | | | 0 |
|------| +-----+-|-+-|-+---+
| | | |
| code | | |
| | v v
An SRT is simply an object in the program's data segment. It has the
same representation as a static constructor. There are 16
pre-compiled SRT info tables: stg_SRT_1_info, .. stg_SRT_16_info,
representing SRT objects with 1-16 pointers, respectively.
The entries of an SRT object point to static closures, which are either
- FUN_STATIC, THUNK_STATIC or CONSTR
- Another SRT (actually just a CONSTR)
The final field of the SRT is the static link field, used by the
garbage collector to chain together static closures that it visits and
to determine whether a static closure has been visited or not. (see
Note [STATIC_LINK fields])
By traversing the transitive closure of an SRT, the GC will reach all
of the CAFs that are reachable from the code associated with this SRT.
If we need to create an SRT with more than 16 entries, we build a
chain of SRT objects with all but the last having 16 entries.
+-----+---+- -+---+---+
|SRT16| | | | | | 0 |
+-----+-|-+- -+-|-+---+
| |
v v
+----+---+---+---+
|SRT2| | | | | 0 |
+----+-|-+-|-+---+
| |
| |
v v
Referring to an SRT from the info table
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following things have SRTs:
- Static functions (FUN)
- Static thunks (THUNK), ie. CAFs
- Continuations (RET_SMALL, etc.)
In each case, the info table points to the SRT.
- info->srt is zero if there's no SRT, otherwise:
- info->srt == 1 and info->f.srt_offset points to the SRT
(but see TODO below, we can improve this)
e.g. for a FUN with an SRT:
StgFunInfoTable +------+
info->f.srt_offset | ------------> offset to SRT object
StgStdInfoTable +------+
info->layout.ptrs | ... |
info->layout.nptrs | ... |
info->srt | 1 |
info->type | ... |
|------|
EXAMPLE
^^^^^^^
f = \x. ... g ... f = \x. ... g ...
where where
...@@ -139,218 +62,28 @@ CmmDecls. e.g. for f_entry, we might end up with ...@@ -139,218 +62,28 @@ CmmDecls. e.g. for f_entry, we might end up with
where f1_ret is a return point, and f2_proc is a proc-point. We have where f1_ret is a return point, and f2_proc is a proc-point. We have
a CAFSet for each of these CmmDecls, let's suppose they are a CAFSet for each of these CmmDecls, let's suppose they are
[ f_entry{g_info}, f1_ret{g_info}, f2_proc{} ] [ f_entry{g_closure}, f1_ret{g_closure}, f2_proc{} ]
[ g_entry{h_info, c1_closure} ] [ g_entry{h_closure, c1_closure} ]
[ h_entry{c2_closure} ] [ h_entry{c2_closure} ]
Next, we make an SRT for each of these functions: Now, note that we cannot use g_closure and h_closure in an SRT,
because there are no static closures corresponding to these functions.
f_srt : [g_info] So we have to flatten out the structure, replacing g_closure and
g_srt : [h_info, c1_closure] h_closure with their contents:
h_srt : [c2_closure]
Now, for g_info and h_info, we want to refer to the SRTs for g and h
respectively, which we'll label g_srt and h_srt:
f_srt : [g_srt]
g_srt : [h_srt, c1_closure]
h_srt : [c2_closure]
Now, when an SRT has a single entry, we don't actually generate an SRT
closure for it, instead we just replace references to it with its
single element. So, since h_srt == c2_closure, we have
f_srt : [g_srt]
g_srt : [c2_closure, c1_closure]
h_srt : [c2_closure]
and the only SRT closure we generate is
g_srt = SRT_2 [c2_closure, c1_closure]
Optimisations
^^^^^^^^^^^^^
To reduce the code size overhead and the cost of traversing SRTs in
the GC, we want to simplify SRTs where possible. We therefore apply
the following optimisations. Each has a [keyword]; search for the
keyword in the code below to see where the optimisation is
implemented.
1. [Shortcut] we never create an SRT with a single entry, instead
we replace all references to the singleton SRT with a reference
to its element. This includes references from info tables.
i.e. instead of
+------+
| info |
| | +-----+---+---+
| -------->|SRT_1| | | 0 |
|------| +-----+-|-+---+
| | |
| code | |
| | v
closure
we can point directly to the closure:
+------+
| info |
| |
| -------->closure
|------|
| |
| code |
| |
The exception to this is when we're doing dynamic linking. In that
case, if the closure is not locally defined then we can't point to
it directly from the info table, because this is the text section
which cannot contain runtime relocations. In this case we skip this
optimisation and generate the singleton SRT, becase SRTs are in the
data section and *can* have relocatable references.
2. [FUN] If an SRT refers to a top-level function (a FUN_STATIC), then
we can shortcut the reference to point directly to the function's
SRT instead.
i.e. instead of
+---+---+---
|SRT| | |
+---+-|-+---
|
v
+---+---+
| | | 0 |
+-|-+---+
|
| +------+
| | info |
| | | +-----+---+---+
| | -------->|SRT_1| | | 0 |
`----->|------| +-----+-|-+---+
| | |
| code | |
| | v
closure
we can generate
+---+---+---
|SRT| | |
+---+-|-+---
`----------------------,
|
+---+---+ |
| | | 0 | |
+-|-+---+ |
| |
| +------+ |
| | info | v
| | | +-----+---+---+
| | -------->|SRT_1| | | 0 |
`----->|------| +-----+-|-+---+
| | |
| code | |
| | v
closure
This is quicker for the garbage collector to traverse, and avoids
setting the static link field on the function's closure.
Of course we can only do this if we know what the function's SRT
is. Due to [Shortcut] the function's SRT can be an arbitrary
closure, so this optimisation only applies within a module.
Note: we can *not* do this optimisation for top-level thunks
(CAFs), because we want the SRT to point directly to the
CAF. Otherwise the SRT would keep the CAF's static references alive
even after the CAF had been evaluated!
3. [Common] Identical SRTs can be commoned up.
4. [Filter] If an SRT A refers to an SRT B and a closure C, and B also
refers to C (perhaps transitively), then we can omit the reference
to C from A.
As an alternative to [FUN]: we could merge the FUN's SRT with the FUN
object itself.
TODO: make info->srt be an offset to the SRT, or zero if none (save
one word per info table that has an SRT)
Note that there are many other optimisations that we could do, but
aren't implemented. In general, we could omit any reference from an
SRT if everything reachable from it is also reachable from the other
fields in the SRT. Our [Filter] optimisation is a special case of
this.
Another opportunity we don't exploit is this:
A = {X,Y,Z}
B = {Y,Z}
C = {X,B}
Here we could use C = {A} and therefore [Shortcut] C = A.
-}
-- ---------------------------------------------------------------------