-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLoop.hs
105 lines (97 loc) · 4.41 KB
/
Loop.hs
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
module Bril.Optim.Loop (licm) where
import Bril.Lang.AST
import Bril.Optim.DataFlow
import Bril.Structure.CFG
import Bril.Structure.Loop
import Bril.Structure.SSA
import Control.Lens
import Data.Foldable
import Data.Ord
import Data.List
import Util.Misc
import qualified Data.HashMap.Strict as M
import qualified Data.HashSet as S
import Debug.Trace
printV s v = trace (s ++ show v) v
-- | whether this instruction should be hoisted
shouldHoist :: Instruction -> Bool
shouldHoist (Value (BinOp Div _ _) (Just _)) = False
shouldHoist (Value (BinOp FDiv _ _) (Just _)) = False
shouldHoist (Value Call {} _) = False
shouldHoist (Value Alloc {} _) = False
shouldHoist (Value Phi {} _) = False
shouldHoist (Value _ (Just _)) = True
shouldHoist _ = False
{-# INLINABLE shouldHoist #-}
-- | whether an instruction is a loop invariant given the loop body
-- the block in which each variable is defined, and a set of
-- currently known invariants
isInvariant :: S.HashSet Ident -> M.HashMap Ident Ident -> S.HashSet Ident -> Instruction -> Bool
isInvariant body defs known instr = trivially instr || shouldHoist instr && all invariant (args instr)
where
-- instruction is trivially invariant because it's defined outside the body
trivially = maybe False (\(Assignment d _) -> invariant d) . assignment
-- an identifier is invariant because it's defined outside or it's in the invariant set
invariant a = S.member a known || not (S.member (defs M.! a) body)
{-# INLINABLE isInvariant #-}
-- | find loop invariant instructions
loopInvariants :: CFG -> Loop -> M.HashMap Ident Ident -> [Instruction]
loopInvariants cfg loop defs = rec [] loopInstrs
where
-- get all the instructions in the loop
blocks = loop ^. body
instrs b = (cfg ^. instructions) M.! b
loopInstrs = foldl' (\s b -> s ++ instrs b) [] blocks
rec invs rem = if null invs' then invs else rec (invs ++ invs') rem'
where
-- get all the destination in invariant instructions
dest s = maybe s (\(Assignment d _) -> S.insert d s) . assignment
invars = foldl' dest S.empty invs
-- get all the invariant instructions and remaining
(invs', rem') = partition (isInvariant blocks defs invars) rem
{-# INLINABLE loopInvariants #-}
-- | move the given invariant instructions for the loop
moveInvariants :: CFG -> Loop -> [Instruction] -> CFG
moveInvariants cfg loop invars = instructions .~ instrs'' $ cfg
where
instrs = cfg ^. instructions
blocks = loop ^. body
preh = loop ^. preheader
-- | filtering non-invariant instructions
notinv i = not $ S.member i $ S.fromList invars
instrs' = foldl' (flip (M.adjust (filter notinv))) instrs blocks
-- add the invariant instructions
instrs'' = M.adjust (`insertAfter` invars) preh instrs'
{-# INLINABLE moveInvariants #-}
-- | this instruction can be hoisted to the preheader if
-- either it dominates all loop exits or it's dead after
-- the loop
canHoist :: CFG -> Loop -> M.HashMap Ident Ident -> M.HashMap Ident LiveVariables -> Instruction -> Bool
canHoist cfg loop defs live = maybe False hoist . assignment
where
blocks = loop ^. body
-- exit blocks for this loop
after = foldl' S.union S.empty $ loop ^. exits
-- return the set of live variables for this block
living b = case live M.! b of LiveVariables x -> x
-- we can hoist if the variable is dead after the loop
hoist (Assignment d _) = not $ any (S.member d . living) after
{-# INLINABLE canHoist #-}
-- | loop invariant code motion for a function in SSA form
licm :: Function -> Function
licm fn = finstrs .~ instrs $ fn
where
cfg = mkCFG fn
-- sort loops by size in decreasing order
-- i.e bigger/outer loops first
loops = sortOn (Down . length . (^. body)) $ mkLoops cfg
-- get the result of live variable analysis
(b, _) = invoke cfg :: (M.HashMap Ident LiveVariables, M.HashMap Ident LiveVariables)
-- apply licm to all loops
cfg' = foldl' licm' cfg loops
instrs = ((cfg' ^. instructions) M.!) =<< (cfg' ^. blocks)
licm' c l = moveInvariants c l invs
where
invs = loopInvariants c l defs
defs = definitions c
{-# INLINABLE licm #-}