@@ -38,7 +38,8 @@ module Language.PureScript.CodeGen.Optimize (
40import Data.Data
40import Data.Data
41−import Data.Maybe (fromMaybe)
41+import Data.List (nub)
42+import Data.Maybe (fromJust, isJust, fromMaybe)
42import Data.Generics
43import Data.Generics
44import Language.PureScript.Names
45import Language.PureScript.Names
@@ -95,6 +96,7 @@ isReassigned var1 = everything (||) (mkQ False check)
95 check :: JS -> Bool
96 check :: JS -> Bool
96 check (JSFunction _ args _) | var1 `elem` args = True
97 check (JSFunction _ args _) | var1 `elem` args = True
97 check (JSVariableIntroduction arg _) | var1 == arg = True
98 check (JSVariableIntroduction arg _) | var1 == arg = True
99+ check (JSAssignment (JSVar arg) _) | var1 == arg = True
98 check _ = False
100 check _ = False
100isRebound :: (Data d) => JS -> d -> Bool
102isRebound :: (Data d) => JS -> d -> Bool
@@ -254,52 +256,136 @@ tco' = everywhere (mkT convert)
254 isSelfCall _ _ = False
256 isSelfCall _ _ = False
256magicDo :: Options -> JS -> JS
258magicDo :: Options -> JS -> JS
257−magicDo opts | optionsMagicDo opts = magicDo'
259+magicDo opts | optionsMagicDo opts = inlineST . magicDo'
258 | otherwise = id
260 | otherwise = id
263+-- Inline type class dictionaries for >>= and return for the Eff monad
267+-- Prelude[">>="](dict)(m1)(function(x) {
260magicDo' :: JS -> JS
278magicDo' :: JS -> JS
261magicDo' = everywhere (mkT undo) . everywhere' (mkT convert)
279magicDo' = everywhere (mkT undo) . everywhere' (mkT convert)
281+ -- The name of the function block which is added to denote a do block
263 fnName = "__do"
282 fnName = "__do"
264−
283+ -- Desugar monomorphic calls to >>= and return for the Eff monad
265 convert :: JS -> JS
284 convert :: JS -> JS
266 convert (JSApp (JSApp ret [val]) []) | isReturn ret = val
286 convert (JSApp (JSApp ret [val]) []) | isReturn ret = val
267 convert (JSApp (JSApp bind [m]) [JSFunction Nothing ["_"] (JSBlock [JSReturn ret])]) | isBind bind =
288 convert (JSApp (JSApp bind [m]) [JSFunction Nothing ["_"] (JSBlock [JSReturn ret])]) | isBind bind =
268 JSFunction (Just fnName) [] $ JSBlock [ JSApp m [], JSReturn (JSApp ret []) ]
289 JSFunction (Just fnName) [] $ JSBlock [ JSApp m [], JSReturn (JSApp ret []) ]
269 convert (JSApp (JSApp bind [m]) [JSFunction Nothing [arg] (JSBlock [JSReturn ret])]) | isBind bind =
291 convert (JSApp (JSApp bind [m]) [JSFunction Nothing [arg] (JSBlock [JSReturn ret])]) | isBind bind =
270 JSFunction (Just fnName) [] $ JSBlock [ JSVariableIntroduction arg (Just (JSApp m [])), JSReturn (JSApp ret []) ]
292 JSFunction (Just fnName) [] $ JSBlock [ JSVariableIntroduction arg (Just (JSApp m [])), JSReturn (JSApp ret []) ]
294+ convert (JSApp (JSApp f [arg]) []) | isEffFunc "untilE" f =
295+ JSApp (JSFunction Nothing [] (JSBlock [ JSWhile (JSUnary Not (JSApp arg [])) (JSBlock []), JSReturn (JSObjectLiteral []) ])) []
297+ convert (JSApp (JSApp (JSApp f [arg1]) [arg2]) []) | isEffFunc "whileE" f =
298+ JSApp (JSFunction Nothing [] (JSBlock [ JSWhile (JSApp arg1 []) (JSBlock [ JSApp arg2 [] ]), JSReturn (JSObjectLiteral []) ])) []
271 convert other = other
299 convert other = other
272−
300+ -- Check if an expression represents a monomorphic call to >>= for the Eff monad
273 isBind (JSApp bindPoly [effDict]) | isBindPoly bindPoly && isEffDict effDict = True
301 isBind (JSApp bindPoly [effDict]) | isBindPoly bindPoly && isEffDict effDict = True
274 isBind _ = False
302 isBind _ = False
275−
303+ -- Check if an expression represents a monomorphic call to return for the Eff monad
276 isReturn (JSApp retPoly [effDict]) | isRetPoly retPoly && isEffDict effDict = True
304 isReturn (JSApp retPoly [effDict]) | isRetPoly retPoly && isEffDict effDict = True
277 isReturn _ = False
305 isReturn _ = False
278−
306+ -- Check if an expression represents the polymorphic >>= function
279 isBindPoly (JSAccessor prop (JSAccessor "Prelude" (JSVar "_ps"))) | prop == identToJs (Op ">>=") = True
307 isBindPoly (JSAccessor prop (JSAccessor "Prelude" (JSVar "_ps"))) | prop == identToJs (Op ">>=") = True
280 isBindPoly (JSIndexer (JSStringLiteral ">>=") (JSAccessor "Prelude" (JSVar "_ps"))) = True
308 isBindPoly (JSIndexer (JSStringLiteral ">>=") (JSAccessor "Prelude" (JSVar "_ps"))) = True
281 isBindPoly _ = False
309 isBindPoly _ = False
282−
310+ -- Check if an expression represents the polymorphic return function
283 isRetPoly (JSAccessor "$return" (JSAccessor "Prelude" (JSVar "_ps"))) = True
311 isRetPoly (JSAccessor "$return" (JSAccessor "Prelude" (JSVar "_ps"))) = True
284 isRetPoly (JSIndexer (JSStringLiteral "return") (JSAccessor "Prelude" (JSVar "_ps"))) = True
312 isRetPoly (JSIndexer (JSStringLiteral "return") (JSAccessor "Prelude" (JSVar "_ps"))) = True
285 isRetPoly _ = False
313 isRetPoly _ = False
286−
314+ -- Check if an expression represents a function in the Ef module
315+ isEffFunc name (JSAccessor name' (JSAccessor "Eff" (JSVar "_ps"))) | name == name' = True
316+ isEffFunc _ _ = False
287− prelude = ModuleName (ProperName "Prelude")
318+ prelude = ModuleName [ProperName "Prelude"]
288− effModule = ModuleName (ProperName "Eff")
319+ effModule = ModuleName [ProperName "Eff"]
289−
320+ -- The name of the type class dictionary for the Monad Eff instance
290 Right (Ident effDictName) = mkDictionaryValueName
321 Right (Ident effDictName) = mkDictionaryValueName
291 effModule
322 effModule
292 (Qualified (Just prelude) (ProperName "Monad"))
323 (Qualified (Just prelude) (ProperName "Monad"))
293 (TypeConstructor (Qualified (Just effModule) (ProperName "Eff")))
324 (TypeConstructor (Qualified (Just effModule) (ProperName "Eff")))
294−
325+ -- Check if an expression represents the Monad Eff dictionary
295 isEffDict (JSApp (JSVar ident) [JSObjectLiteral []]) | ident == effDictName = True
326 isEffDict (JSApp (JSVar ident) [JSObjectLiteral []]) | ident == effDictName = True
296 isEffDict (JSApp (JSAccessor prop (JSAccessor "Eff" (JSVar "_ps"))) [JSObjectLiteral []]) | prop == effDictName = True
327 isEffDict (JSApp (JSAccessor prop (JSAccessor "Eff" (JSVar "_ps"))) [JSObjectLiteral []]) | prop == effDictName = True
297 isEffDict _ = False
328 isEffDict _ = False
298−
329+ -- Remove __do function applications which remain after desugaring
299 undo :: JS -> JS
330 undo :: JS -> JS
300 undo (JSReturn (JSApp (JSFunction (Just ident) [] body) [])) | ident == fnName = body
331 undo (JSReturn (JSApp (JSFunction (Just ident) [] body) [])) | ident == fnName = body
301 undo other = other
332 undo other = other
335+-- Inline functions in the ST module
338+inlineST = everywhere (mkT convertBlock)
340+ -- Look for runST blocks and inline the STRefs there.
341+ -- If all STRefs are used in the scope of the same runST, only using { read, write, modify }STRef then
342+ -- we can be more aggressive about inlining, and actually turn STRefs into local variables.
343+ convertBlock (JSApp f [arg]) | isSTFunc "runST" f || isSTFunc "runSTArray" f =
344+ let refs = nub . findSTRefsIn $ arg
345+ usages = findAllSTUsagesIn arg
346+ allUsagesAreLocalVars = all (\u -> let v = toVar u in isJust v && fromJust v `elem` refs) usages
347+ localVarsDoNotEscape = all (\r -> length (r `appearingIn` arg) == length (filter (\u -> let v = toVar u in v == Just r) usages)) refs
348+ in everywhere (mkT $ convert allUsagesAreLocalVars) arg
349+ convertBlock other = other
350+ -- Convert a block in a safe way, preserving object wrappers of references,
351+ -- or in a more aggressive way, turning wrappers into local variables depending on the
352+ -- agg(ressive) parameter.
353+ convert agg (JSApp (JSApp f [arg]) []) | isSTFunc "newSTRef" f =
354+ if agg then arg else JSObjectLiteral [("value", arg)]
355+ convert agg (JSApp (JSApp f [ref]) []) | isSTFunc "readSTRef" f =
356+ if agg then ref else JSAccessor "value" ref
357+ convert agg (JSApp (JSApp (JSApp f [ref]) [arg]) []) | isSTFunc "writeSTRef" f =
358+ if agg then JSAssignment ref arg else JSAssignment (JSAccessor "value" ref) arg
359+ convert agg (JSApp (JSApp (JSApp f [ref]) [func]) []) | isSTFunc "modifySTRef" f =
360+ if agg then JSAssignment ref (JSApp func [ref]) else JSAssignment (JSAccessor "value" ref) (JSApp func [JSAccessor "value" ref])
361+ convert _ (JSApp (JSApp (JSApp f [arr]) [i]) []) | isSTFunc "peekSTArray" f =
363+ convert _ (JSApp (JSApp (JSApp (JSApp f [arr]) [i]) [val]) []) | isSTFunc "pokeSTArray" f =
364+ JSAssignment (JSIndexer i arr) val
365+ convert _ other = other
366+ -- Check if an expression represents a function in the ST module
367+ isSTFunc name (JSAccessor name' (JSAccessor "ST" (JSVar "_ps"))) | name == name' = True
368+ isSTFunc _ _ = False
369+ -- Find all ST Refs initialized in this block
370+ findSTRefsIn = everything (++) (mkQ [] isSTRef)
372+ isSTRef (JSVariableIntroduction ident (Just (JSApp (JSApp f [arg]) []))) | isSTFunc "newSTRef" f = [ident]
374+ -- Find all STRefs used as arguments to readSTRef, writeSTRef, modifySTRef
375+ findAllSTUsagesIn = everything (++) (mkQ [] isSTUsage)
377+ isSTUsage (JSApp (JSApp f [ref]) []) | isSTFunc "readSTRef" f = [ref]
378+ isSTUsage (JSApp (JSApp (JSApp f [ref]) [_]) []) | isSTFunc "writeSTRef" f || isSTFunc "modifySTRef" f = [ref]
380+ -- Find all uses of a variable
381+ appearingIn ref = everything (++) (mkQ [] isVar)
383+ isVar e@(JSVar v) | v == ref = [e]
385+ -- Convert a JS value to a String if it is a JSVar
386+ toVar (JSVar v) = Just v
303collapseNestedBlocks :: JS -> JS
389collapseNestedBlocks :: JS -> JS
304collapseNestedBlocks = everywhere (mkT collapse)
390collapseNestedBlocks = everywhere (mkT collapse)
@@ -382,7 +468,7 @@ inlineCommonOperators = applyAll
382 isOpDict className ty (JSApp (JSAccessor prop (JSAccessor "Prelude" (JSVar "_ps"))) [JSObjectLiteral []]) | prop == dictName = True
468 isOpDict className ty (JSApp (JSAccessor prop (JSAccessor "Prelude" (JSVar "_ps"))) [JSObjectLiteral []]) | prop == dictName = True
384 Right (Ident dictName) = mkDictionaryValueName
470 Right (Ident dictName) = mkDictionaryValueName
385− (ModuleName (ProperName "Prim"))
471+ (ModuleName [ProperName "Prim"])
386− (Qualified (Just (ModuleName (ProperName "Prelude"))) (ProperName className))
472+ (Qualified (Just (ModuleName [ProperName "Prelude"])) (ProperName className))
388 isOpDict _ _ _ = False
474 isOpDict _ _ _ = False