summaryrefslogtreecommitdiff
path: root/compiler/cmm/Dataflow.hs
blob: 093a8a6430f026a1ecea784513a7030e5c4de1b8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
module Dataflow {-(fixedpoint, cmmLivenessComment, cmmLiveness, CmmLive)-} where

import Cmm
import PprCmm ()

import UniqSet
import UniqFM

import FastString
import Outputable

import Maybes

import Data.List
import Data.Maybe

cmmBranchSources :: [(BlockId, [BlockId])] -> [(BlockId, [BlockId])]
cmmBranchSources input =
    [(target, [s | (s, ts) <- input, target `elem` ts])
     | target <- targets] where
        targets = nub [t | (s, ts) <- input, t <- ts]

cmmBranchTargets :: CmmBasicBlock -> UniqSet BlockId
cmmBranchTargets (BasicBlock _ stmts) =
    mkUniqSet $ concatMap target stmts where
        target (CmmBranch ident) = [ident]
        target (CmmCondBranch _ ident) = [ident]
        target (CmmSwitch _ blocks) = mapMaybe id blocks
        target _ = []

--------------------------------------------------------------------------------

-- This should really be a state monad, but that is not in the core libraries
-- so we'll hack around it here.
-- The monad we're using is: type State a = s -> s

-- The variables that were made live and killed respectively
type CmmLive = UniqSet LocalReg

type BlockEntryLiveness = BlockEnv CmmLive	-- The variables live on entry to each block

addLive new_live live = live `unionUniqSets` new_live
addKilled new_killed live = live `minusUniqSet` new_killed

-- Calculate the live and killed registers for a local block
cmmBlockLive :: UniqFM {-BlockId-} CmmLive -> CmmBasicBlock -> CmmLive
cmmBlockLive other_live (BasicBlock _ stmts) =
    foldr ((.) . (cmmStmtLive other_live)) id stmts emptyUniqSet

-- Helper for cmmLocalLiveness
cmmStmtLive :: UniqFM {-BlockId-} CmmLive -> CmmStmt -> (CmmLive -> CmmLive)
cmmStmtLive _ (CmmNop) = id
cmmStmtLive _ (CmmComment _) = id
cmmStmtLive _ (CmmAssign reg expr) =
    cmmExprLive expr . reg_liveness where
        reg_liveness =
            case reg of
              (CmmLocal reg') -> addKilled $ unitUniqSet reg'
              (CmmGlobal _) -> id
cmmStmtLive _ (CmmStore expr1 expr2) =
    cmmExprLive expr2 . cmmExprLive expr1
cmmStmtLive _ (CmmCall target results arguments _) =
    target_liveness .
    foldr ((.) . cmmExprLive) id (map fst arguments) .
    addKilled (mkUniqSet $ only_local_regs results) where
        only_local_regs [] = []
        only_local_regs ((CmmGlobal _,_):args) = only_local_regs args
        only_local_regs ((CmmLocal r,_):args) = r:only_local_regs args
        target_liveness =
            case target of
              (CmmForeignCall target _) -> cmmExprLive target
              (CmmPrim _) -> id
cmmStmtLive other_live (CmmBranch target) = addLive (lookupWithDefaultUFM other_live emptyUniqSet target)
cmmStmtLive other_live (CmmCondBranch expr target) = cmmExprLive expr . addLive (lookupWithDefaultUFM other_live emptyUniqSet target)
cmmStmtLive other_live (CmmSwitch expr targets) =
    cmmExprLive expr .
    (foldr ((.) . (addLive . lookupWithDefaultUFM other_live emptyUniqSet)) id (mapCatMaybes id targets))
cmmStmtLive _ (CmmJump expr params) =
    const (cmmExprLive expr $ foldr ((.) . cmmExprLive) id (map fst params) $ emptyUniqSet)
cmmStmtLive _ (CmmReturn params) =
    const (foldr ((.) . cmmExprLive) id (map fst params) $ emptyUniqSet)

--------

-- Helper for cmmLocalLiveness
cmmExprLive :: CmmExpr -> (CmmLive -> CmmLive)
cmmExprLive expr = addLive (mkUniqSet $ expr_liveness expr) where
    expr_liveness (CmmLit _) = []
    expr_liveness (CmmLoad expr _) = expr_liveness expr
    expr_liveness (CmmReg reg) = reg_liveness reg
    expr_liveness (CmmMachOp _ exprs) = concatMap expr_liveness exprs
    expr_liveness (CmmRegOff reg _) = reg_liveness reg
    reg_liveness (CmmLocal reg) = [reg]
    reg_liveness (CmmGlobal _) = []

cmmBlockUpdate ::
    UniqFM {-BlockId-} CmmBasicBlock
    -> BlockId
    -> Maybe BlockId
    -> UniqFM {-BlockId-} CmmLive
    -> Maybe (UniqFM {-BlockId-} CmmLive)
cmmBlockUpdate blocks node _ state =
    let old_live = lookupWithDefaultUFM state (panic "unknown block id during liveness analysis") node
        block = lookupWithDefaultUFM blocks (panic "unknown block id during liveness analysis") node
        new_live = cmmBlockLive state block
    in if (sizeUniqSet old_live) == (sizeUniqSet new_live)
       then Nothing
       else Just $ addToUFM state node new_live

cmmBlockDependants :: UniqFM {-BlockId-} (UniqSet BlockId) -> BlockId -> [BlockId]
cmmBlockDependants sources ident =
    uniqSetToList $ lookupWithDefaultUFM sources emptyUniqSet ident

cmmBlockSourcesAndTargets ::
    [CmmBasicBlock]
    -> (UniqFM {-BlockId-} (UniqSet BlockId), UniqFM (UniqSet BlockId))
cmmBlockSourcesAndTargets blocks = foldr aux (emptyUFM, emptyUFM) blocks where
    aux block (sourcesUFM, targetsUFM)  =
        (foldUniqSet add_source_edges sourcesUFM targets,
         addToUFM_Acc unionUniqSets id targetsUFM ident targets) where
            add_source_edges t ufm =
                addToUFM_Acc (flip addOneToUniqSet) unitUniqSet ufm t ident
            targets = cmmBranchTargets block
            ident = blockId block

cmmBlockNames :: [CmmBasicBlock] -> UniqFM {-BlockId-} CmmBasicBlock
cmmBlockNames blocks = listToUFM $ map block_name blocks where
    block_name b = (blockId b, b)

cmmLiveness :: [CmmBasicBlock] -> BlockEnv CmmLive
cmmLiveness blocks =
    fixedpoint (cmmBlockDependants sources) (cmmBlockUpdate blocks')
               (map blockId blocks) (listToUFM [(blockId b, emptyUniqSet) | b <- blocks]) where
                   (sources, targets) = cmmBlockSourcesAndTargets blocks
                   blocks' = cmmBlockNames blocks

cmmLivenessComment ::
    UniqFM {-BlockId-} (UniqSet LocalReg)
    -> CmmBasicBlock -> CmmBasicBlock
cmmLivenessComment live (BasicBlock ident stmts) =
    BasicBlock ident stmts' where
        stmts' = (CmmComment $ mkFastString $ showSDoc $ ppr $ live'):stmts
        live' = map CmmLocal $ uniqSetToList $ lookupWithDefaultUFM live emptyUniqSet ident

--------------------------------------------------------------------------------

-- Solve a fixed-point of a dataflow problem.
-- O(N+H*E) calls to update where
--   N = number of nodes,
--   E = number of edges,
--   H = maximum height of the lattice for any particular node.
-- dependants: map from nodes to those who's value depend on the argument node
-- update:
--   Given the node which needs to be updated, and
--   which node caused that node to need to be updated,
--   update the state.
--   (The causing node will be 'Nothing' if this is the initial update.)
--   Must return 'Nothing' if no change,
--   otherwise returrn 'Just' of the new state
-- nodes: a set of nodes that initially need updating
-- state: some sort of state (usually a map)
--        containing the initial value for each node
--
-- Sketch for proof of complexity:
-- Note that the state is threaded through the entire execution.
-- Also note that the height of the latice at any particular node
-- is the number of times 'update' can return non-Nothing for a particular node.
-- Every call (except for the top level one) must be caused by a non-Nothing
-- result and each non-Nothing result causes as many calls as it has
-- out-going edges.  Thus any particular node, n, may cause in total
-- at most H*out(n) further calls.  When summed over all nodes,
-- that is H*E.  The N term of the complexity is from the initial call
-- when 'update' will be passed 'Nothing'.
fixedpoint ::
    (node -> [node])
    -> (node -> Maybe node -> s -> Maybe s)
    -> [node] -> s -> s
fixedpoint dependants update nodes state =
    foldr (fixedpoint' Nothing) state nodes where
        fixedpoint' cause node state =
            case update node cause state of
              Nothing -> state
              Just state' ->
                  foldr (fixedpoint' (Just node)) state' (dependants node)