2 Kaos - A compiler for creatures scripts
3 Copyright (C) 2005-2008 Bryan Donlan
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module Kaos
.CoreStorage
(markStorage
, Storage
(..), StorageS
(..), StorageMap
, getSM
, checkConstStorage
) where
26 import Control
.Monad
.State
hiding (State
)
27 import qualified Control
.Monad
.State
as S
28 import Kaos
.CoreFuture
29 --import Kaos.CoreAccess
30 import Kaos
.VirtRegister
32 import Control
.Monad
.Reader
34 import Kaos
.CoreInline
(inlineFallback
)
35 import Kaos
.CoreTraverse
37 import qualified Data
.Map
as M
39 dumpStorageTransitions
:: MonadKaos m
=> StorageMap
-> StorageMap
-> m
()
40 dumpStorageTransitions a b
= cmpF
(M
.toList a
) (M
.toList b
)
42 cmpF
[] [] = return ()
43 cmpF xas
@(pa
@(ka
, _
):as) xbs
@(pb
@(kb
, _
):bs
)
45 = do debugKM
$ " DEL " ++ show pa
48 = do debugKM
$ " ADD " ++ show pb
53 = do debugKM
$ " MOD " ++ show (pa
, pb
)
56 = do debugKM
$ " ADD " ++ show pb
59 = do debugKM
$ " DEL " ++ show pa
62 data Storage
= Private VirtRegister
66 deriving (Show, Eq
, Ord
, Data
, Typeable
)
68 type StorageMap
= M
.Map Slot Storage
69 data StorageS
= StorageS
{ ssStorage
:: !StorageMap
70 , ssFuture
:: !FutureS
71 } deriving (Eq
, Ord
, Show, Data
, Typeable
)
72 type MarkM a
= ReaderT FutureS
(StateT StorageMap
(VRegAllocT KaosM
)) a
74 instance Futurable StorageS
where getFuture
= getFuture
. ssFuture
75 instance LineAccess StorageS
where getLineAccess
= getLineAccess
. ssFuture
77 getSM
:: StorageS
-> StorageMap
80 asksFuture
:: (MonadReader f m
, Futurable f
)
83 asksFuture f
= asks
(f
. getFuture
)
85 getStorage
:: Slot
-> MarkM
(Maybe Storage
)
86 getStorage
= lift
. gets
. M
.lookup
87 setStorage
:: Storage
-> Slot
-> MarkM
()
88 setStorage st sl
= lift
$
89 modify
$ M
.insert sl st
90 newStorage
:: Slot
-> Future
-> MarkM
()
91 newStorage slot
(Just
(Bound b
)) = setStorage
(Private b
) slot
92 newStorage slot _
= do
93 vr
<- lift
. lift
$ newVReg
94 setStorage
(Private vr
) slot
96 markStorage
:: Core FutureS
-> KaosM
(Core StorageS
)
97 markStorage
= runVRegAllocT
. flip evalStateT M
.empty . flip runReaderT
undefined . markBlock
99 markBlock
:: Core FutureS
-> MarkM
(Core StorageS
)
100 markBlock
(CB l
) = saveCtx
$ fmap CB
$ mapM enterLine l
102 enterLine
:: (CoreLine FutureS
, FutureS
) -> MarkM
(CoreLine StorageS
, StorageS
)
103 enterLine
(line
, future
) = do
104 debugDump
"dump-storage-assignment" $ "MARKING:\n" ++ dumpCore
(fmap (const ()) $ CB
[(line
, future
)])
105 debugDump
"dump-storage-assignment" $ "LOCAL FUTURE:\n" ++ dumpMap
(getFuture future
)
106 debugDump
"dump-storage-assignment" $ "LOCAL ACCESS:\n" ++ dumpMap
(getAM
$getLineAccess future
)
108 debugDump
"dump-storage-assignment" $ "STORAGE:\n" ++ dumpMap oldStorage
109 line
' <- local
(const future
) $ markLine line
111 whenSet
"dump-storage-assignment" $
112 when (oldStorage
/= storage
) $ do
114 dumpStorageTransitions oldStorage storage
115 return (line
', StorageS storage future
)
117 markLine
:: CoreLine FutureS
-> MarkM
(CoreLine StorageS
)
118 markLine l
@(CoreNote
(ContextNote ctx
)) = do
120 return $ fmap undefined l
121 markLine l
@(CoreNote _
) = return $ fmap undefined l
122 markLine CoreTargZap
= return CoreTargZap
123 markLine l
@(CoreTouch sa
) = do
124 markLine
$ CoreLine
[ TokenSlot sa
]
125 return $ fmap undefined l
126 markLine l
@(CoreConst dest cv
) = do
127 future
<- asksFuture
(M
.lookup dest
)
131 setStorage
(Const cv
) dest
133 setStorage
(Private r
) dest
135 newStorage dest future
136 return $ fmap undefined l
138 markLine l
@(CoreAssign dest src
) = do
139 fdest
<- asksFuture
(M
.lookup dest
)
140 fsrc
<- asksFuture
(M
.lookup src
)
141 modify
$ M
.delete dest
142 case (fdest
, fsrc
) of
143 (Just
(Bound b
), _
) -> do
144 setStorage
(Private b
) dest
145 (Nothing
, _
) -> return () -- unused
146 (_
, Nothing
) -> do -- rename
147 ssrc
<- getStorage src
148 modify
$ M
.alter
(const ssrc
) dest
149 modify
$ M
.delete src
150 (Just
Read, Just
Read) -> do -- alias
151 ssrc
<- getStorage src
153 Nothing
-> fail "src storage was Nothing in markLine, alias case"
154 (Just
(Private r
)) -> setStorage
(Shared r
) src
156 ssrc
' <- getStorage src
157 setStorage
(fromJust ssrc
') dest
159 newStorage dest fdest
160 return $ fmap undefined l
162 markLine l
@(CoreLine tokens
) = do
163 let collected
= execState
(mapM_ collect tokens
) M
.empty
164 mapM_ updateStorage
$ M
.toList collected
165 return $ fmap undefined l
167 collect
:: CoreToken
-> S
.State
(M
.Map Slot AccessType
) ()
168 collect
(TokenSlot
(SA s access
)) = do
169 access
' <- gets
(fromMaybe NoAccess
. M
.lookup s
)
170 modify
$ M
.insert s
(access
' `mergeAccess` access
)
171 collect _
= return ()
173 updateStorage
(slot
, WriteAccess
) = do
174 future
<- asks
(M
.lookup slot
. getFuture
)
175 newStorage slot future
176 updateStorage _
= return ()
178 markLine l
@(CoreLoop body
) = do
179 trueFuture
<- asks getFuture
180 future
<- setupFuture l trueFuture
181 body
' <- liftK
$ markBlockFuture future body
182 body
'' <- markBlock body
'
183 return $ CoreLoop body
''
185 setupFuture
:: CoreLine FutureS
-> FutureMap
-> MarkM FutureMap
186 setupFuture _ trueFuture
= do
187 acc
<- asks getLineAccess
188 let mut
= map fst $ filter ((== MutateAccess
) . snd) (M
.toList
$ getAM acc
)
189 mutF
<- mapM fixReg mut
190 let future
' = foldl updReg trueFuture mutF
192 updReg m
(key
, newVal
) = M
.alter
(const newVal
) key m
194 stor
<- getStorage slot
196 Just
(Private r
) -> return (slot
, Just
$ Bound r
)
197 Nothing
-> return (slot
, Nothing
)
198 _
-> fail $ "Coreloop, bad fixreg storage " ++ show stor
201 markLine l
@(CoreCond cond ontrue_ onfalse_
) = do
203 future
<- setupFuture trueFuture
204 ontrue
<- liftK
$ (markBlockFuture future ontrue_
)
205 onfalse
<- liftK
$ (markBlockFuture future onfalse_
)
206 CoreLine cond
' <- markLine
(CoreLine cond
)
208 ontrue
' <- markBlock ontrue
211 onfalse
' <- markBlock onfalse
213 --- In some cases, if a variable is initialized prior to entry, and is
214 --- both read and written in one of the two branches, only to die afterward;
215 --- it may not be pinned, and thus end up reallocated in one of the branches.
216 --- In the other branch it is dead (no future, but storage remains), but
217 --- the below assertion would be tripped anyway.
219 --- To solve this, prune the storage state of all variables lost after this
222 --- Note: We do not prune s (storage after conditional), but do ensure it
223 --- is subordinate (as it will always come logically before the true/false
224 --- branches, and cannot contain write/mutate ops; but may pin important
225 --- slots as alive). This means that the dead slots come back, but since
226 --- they are unreferenced this is safe as always
227 let [s_t
', s_f
'] = map (purgeDead trueFuture
) [s_t
, s_f
]
228 let u1
= s_f
' `M
.union` s_t
'
229 let u2
= s_t
' `M
.union` s_f
'
230 when (u1
/= u2
) $ fail $ unlines["Storage states diverged:", dumpCoreLine
(fmap (const()) l
), dumpMap s_t
, dumpMap s_f
, dumpMap s
, dumpMap future
]
232 return $ CoreCond cond
' ontrue
' onfalse
'
234 purgeDead
:: FutureS
-> StorageMap
-> StorageMap
235 purgeDead trueFuture storage
=
236 M
.filterWithKey
(\k _
-> M
.member k
(getFuture trueFuture
)) storage
238 setupFuture
:: FutureS
-> MarkM
(M
.Map Slot Lookahead
)
239 setupFuture trueFuture
= do
240 let acc
= M
.toList
. getAM
$ getLineAccess trueFuture
241 let tf
' = getFuture trueFuture
242 entries
<- fmap concat $ mapM (setupEntry tf
') acc
243 return $ M
.fromList entries
244 setupEntry future
(s
, acc
) = do
245 setupEntry
' (s
, acc
) (M
.lookup s future
)
246 -- If a value is being set which will not be used in the future,
247 -- it will die before the end of the block (and thus we don't care where
249 setupEntry
' _ Nothing
= return []
250 setupEntry
' (slot
, acc
) future
= do
251 curAcc
<- getStorage slot
252 case (curAcc
, acc
) of
253 (Just
(Private r
), _
) -> return [(slot
, Bound r
)]
254 (Just
(Shared r
), ReadAccess
) -> return [(slot
, Bound r
)]
256 newStorage slot future
257 Just
(Private st
) <- getStorage slot
258 return [(slot
, Bound st
)]
259 (x
, _
) -> fail $ "trying to bind a shared slot: " ++ show (slot
, x
, acc
, fmap (const ()) l
)
261 markLine
(CoreFoldable folder body
) = do
262 body
' <- markLine body
263 return $ CoreFoldable folder body
'
265 markLine
(CoreTargReader ts slot body
) = do
266 markLine
(CoreAssign ts slot
)
267 markLine
(CoreLine
[TokenSlot
(SA ts ReadAccess
)])
268 body
' <- markBlock body
269 return $ CoreTargReader ts slot body
'
271 markLine
(CoreTargWriter slot body
) = do
272 body
' <- markBlock body
273 markLine
(CoreLine
[TokenSlot
(SA slot WriteAccess
)])
274 return $ CoreTargWriter slot body
'
276 markLine
(CoreInlineFlush l
) = return $ CoreInlineFlush l
277 markLine l
@(CoreInlineAssign level targUser dest repl
) = do
278 markLine
$ inlineFallback l
279 return $ CoreInlineAssign level targUser dest repl
281 checkConstStorage
:: Core StorageS
-> KaosM
(Core StorageS
)
282 checkConstStorage l
= do
283 saveCtx
$ mapCoreM checkConstLine l
286 checkConstLine
:: CoreLine StorageS
-> StorageS
-> KaosM
(CoreLine StorageS
, StorageS
)
287 checkConstLine l
@(CoreNote
(ContextNote cx
)) s
= do
290 checkConstLine l
@(CoreLine t
) storage
= do
294 checkTok
(TokenConstSlot s rc
) =
295 checkStorage s
(M
.lookup s
(getSM storage
)) rc
296 checkTok _
= return ()
297 checkStorage _
(Just
(Const v
)) rc
= do
300 Just s
-> compileError s
301 checkStorage _ Nothing _
=
302 return () -- will be reported in CoreToVirt
303 checkStorage _
(Just Phantom
) _
=
304 internalError
"Phantom storage found where constant expected (what does this mean??)"
305 checkStorage _ _ _
= compileError
"Constant value expected"
307 checkConstLine l s
= return (l
, s
)