From 6d3ebba220717906e598743cf8497f80584bd5c2 Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Tue, 7 Jul 2026 21:44:04 +0200 Subject: [PATCH 1/3] feat: lower self-recursive tail calls to while loops (#181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A self-recursive tail call of an uncurried worker stays a call: PUC Lua runs it in constant stack via TCO, but pays CALL/RET machinery and argument shuffling per iteration, and LuaJIT lacks the stable loop marker its trace compiler wants. Lower block-final self-calls to a 'while true do' loop with simultaneous multiple assignment of the parameters, during Lua code generation (the expression IR keeps no loop node, the same choice magicDo makes for statements). A body that captures a parameter inside a nested closure is left recursive: parameters of a loopified function are shared across iterations, so reassignment would corrupt the captured environment. Immediately-invoked scope wrappers are looked through transparently. The argument explist is balanced to the variable count: nil-padded when trailing Prim.undefined arguments were elided (declined after a multi-value argument), surplus values facing dropped unused parameters removed when syntactically pure, declined otherwise. Applies to any self-recursive recursive-group binding, top-level or Let-bound — an uncurried worker or a plain unary function. Non-tail self-calls and the wrapper indirection stay real calls; the one observable difference is the shape of error tracebacks. --- .../20260707_220000_unisay_loopification.md | 15 + lib/Language/PureScript/Backend/Lua.hs | 30 +- .../PureScript/Backend/Lua/Loopify.hs | 325 ++++++++++++++++++ pslua.cabal | 1 + .../Golden.LongCallbackChain.Test/golden.lua | 26 +- .../Golden.PatternMatching.Test2/golden.lua | 14 +- .../Golden.StringCodePoints.Test/golden.lua | 16 +- test/ps/output/Golden.Uncurry.Test/golden.lua | 10 +- 8 files changed, 399 insertions(+), 38 deletions(-) create mode 100644 changelog.d/20260707_220000_unisay_loopification.md create mode 100644 lib/Language/PureScript/Backend/Lua/Loopify.hs diff --git a/changelog.d/20260707_220000_unisay_loopification.md b/changelog.d/20260707_220000_unisay_loopification.md new file mode 100644 index 00000000..5cc542cd --- /dev/null +++ b/changelog.d/20260707_220000_unisay_loopification.md @@ -0,0 +1,15 @@ +### Added + +- Self-recursive tail calls lower to `while true do` loops with parameter + reassignment (#181): a recursive binding — an uncurried worker or a plain + unary function, top-level or `let`-bound — whose tail position self-calls + becomes a loop, with `return go$w(e₁, e₂)` turning into the simultaneous + multiple assignment `p₁, p₂ = e₁, e₂`. Non-tail self-calls and other exits + are untouched; a body that captures a parameter inside a nested closure + (e.g. a CPS-style accumulator) is left recursive, since reassignment would + corrupt the captured environment. PUC Lua already runs these shapes in + constant stack via tail-call optimization, so the change is a constant + factor there (no per-iteration CALL/RET and argument shuffling); under + LuaJIT the loop is the shape the trace compiler wants, completing the + uncurrying story of #24 for hot recursive workers like `span` and the fold + fallbacks. The only observable difference is the shape of error tracebacks. diff --git a/lib/Language/PureScript/Backend/Lua.hs b/lib/Language/PureScript/Backend/Lua.hs index e4a3bac4..103a9f77 100644 --- a/lib/Language/PureScript/Backend/Lua.hs +++ b/lib/Language/PureScript/Backend/Lua.hs @@ -25,6 +25,7 @@ import Language.PureScript.Backend.IR.Query (usesRuntimeLazy) import Language.PureScript.Backend.Lua.Fixture qualified as Fixture import Language.PureScript.Backend.Lua.Key qualified as Key import Language.PureScript.Backend.Lua.Linker.Foreign qualified as Foreign +import Language.PureScript.Backend.Lua.Loopify qualified as Loopify import Language.PureScript.Backend.Lua.Name qualified as Lua import Language.PureScript.Backend.Lua.Name qualified as Name import Language.PureScript.Backend.Lua.Types (ParamF (..)) @@ -87,7 +88,13 @@ fromUberModule foreigns needsRuntimeLazy appOrModule uber = (`evalStateT` 0) do <$> fromIR foreigns Set.empty modname irExp pure $ DList.fromList do (modname, name, exp) ← recBinds - pure $ mkBinding modname (fromName name) exp + -- A self-recursive member references itself through the + -- module-scope table, mirroring the Ref case of 'fromIR'. + let self = + Loopify.SelfField + Fixture.moduleName + (qualifyName modname (fromName name)) + pure $ mkBinding modname (fromName name) (Loopify.loopify self exp) returnExp ← case appOrModule of @@ -271,15 +278,20 @@ fromIR foreigns topLevelNames modname ir = case ir of then qualifyName modname name else name ) - assignments ← forM (toList grp) \(_ann, fromName → name, expr) → + assignments ← forM (toList grp) \(_ann, fromName → name, expr) → do + -- The self-reference mirrors the Ref case below: through the + -- module-scope table for a top-level name, plain otherwise. + let (target, self) + | Set.member (qualifyName modname name) topLevelNames = + ( qualifyName modname name + , Loopify.SelfField + Fixture.moduleName + (qualifyName modname name) + ) + | otherwise = (name, Loopify.SelfLocal name) goExp expr - <&> Lua.assign - ( Lua.VarName - ( if Set.member (qualifyName modname name) topLevelNames - then qualifyName modname name - else name - ) - ) + <&> Lua.assign (Lua.VarName target) + . Loopify.loopify self pure $ DList.fromList binds <> DList.fromList assignments pure . Left . DList.toList $ recs <> either DList.fromList (DList.singleton . Lua.return) body diff --git a/lib/Language/PureScript/Backend/Lua/Loopify.hs b/lib/Language/PureScript/Backend/Lua/Loopify.hs new file mode 100644 index 00000000..70d4f118 --- /dev/null +++ b/lib/Language/PureScript/Backend/Lua/Loopify.hs @@ -0,0 +1,325 @@ +{- | Loopification of self-recursive tail calls (issue #181). + +A self-recursive tail call stays a call after the uncurrying +worker/wrapper split ("Language.PureScript.Backend.IR.Uncurry"). PUC +Lua's tail-call optimization already reuses the frame, so recursion is +not a stack-safety problem — but it pays CALL\/RET machinery and +argument shuffling on every iteration, and under LuaJIT a hot recursive +worker lacks the stable loop marker the trace compiler wants. A real +@while true do@ loop is the canonically better shape: it gets a loop +trace, loop-invariant hoisting, and one frame for the whole run. + +== The transform + +A function assigned to a recursive-group binding + +> f = function(p₁, …, pₖ) +> … +> return f(e₁, …, eₘ) -- tail self-call +> … +> return r -- any other exit +> end + +becomes + +> f = function(p₁, …, pₖ) +> while true do +> … +> p₁, …, pₖ = e₁, …, eₘ -- the tail self-call, now an iteration +> … +> return r -- other exits return as before +> end +> end + +The rewritten call is a /tail/ self-call: a block-final @return@ whose +callee is the binding's own name, found by walking the block spine — +the last statement of the body and, recursively, the last statement of +each branch of a block-final @if@. This is exactly where the Lua +code generator ("Language.PureScript.Backend.Lua") puts the IR tail +positions (through 'Language.PureScript.Backend.IR.Types.Let' bodies +and 'Language.PureScript.Backend.IR.Types.IfThenElse' branches), and a +replaced @return@ leaves its enclosing blocks falling through straight +to the end of the loop body — Lua 5.1 has no @goto@, so loopification +relies on this fall-through for "continue". Every other exit path still +@return@s, which leaves the loop. Non-tail self-calls (and the curried +wrapper's delegation) are left alone: each such call starts a fresh +activation with its own loop, which is exactly the semantics it had. + +Lua's multiple assignment gives the parameter swap simultaneity for +free: the right-hand explist is fully evaluated against the current +parameter values before any variable is reassigned, with the same +value adjustment a call performs on its arguments. The two lists can +still disagree in length, because the code generator drops a trailing +run of unused parameters (the call may then pass more values than +there are variables) and trailing @Prim.undefined@ arguments (fewer) +— see Note [Nullary functions and Prim.undefined]. Rather than emit +an unbalanced assignment (semantically fine, but flagged by luacheck), +the explist is balanced to the variable count: a short list is padded +with explicit @nil@s (what the call would bind — declined when the +last argument is a call or vararg, whose multiple results the pad +would truncate where the call form spreads them), and surplus +expressions are dropped when they are syntactically pure (a literal, a +plain variable, a function literal — a value in a dropped-parameter +position is never consumed, so only its evaluation effects matter). +A self-call that cannot be balanced is left a real tail call. + +== The capture veto + +Lua closures capture variables by reference. Parameters of a loopified +function are shared across all iterations, while a fresh activation +per call gives every iteration its own; a closure created in one +iteration and surviving into the next (e.g. a CPS-style accumulator +@go (n - 1) (\\r → k (r + n))@) would observe the reassigned values. +Therefore a function whose body references a parameter from inside a +nested @function@ literal is not loopified. An immediately-invoked +zero-argument function — @(function() … end)()@, the code generator's +expression-position scope wrapper — runs within the iteration that +created it, so the analysis looks through it transparently; any +other function literal is treated as escaping. Local variables need +no veto: their @local@ declarations sit inside the loop body and are +re-created each iteration. + +The veto errs conservatively (a captured parameter name is assumed to +escape even where a human could prove it does not), which only costs +an optimization opportunity, never correctness. + +== Behaviour preservation + +PUC's tail-call optimization already guarantees O(1) stack for the +rewritten shapes, so the transform changes constants, not semantics; +the one observable difference is the shape of error tracebacks. The +transform runs during code generation on bindings that come from IR +recursive groups only — foreign (hand-written) Lua never passes +through it. +-} +module Language.PureScript.Backend.Lua.Loopify + ( Self (..) + , loopify + ) where + +import Data.List qualified as List +import Data.List.NonEmpty qualified as NE +import Data.Set qualified as Set +import Language.PureScript.Backend.Lua.Name (Name) +import Language.PureScript.Backend.Lua.Types + ( Annotated + , Comments + , Exp + , ExpF (..) + , ParamF (..) + , StatementF (..) + , TableRowF (..) + , VarF (..) + , ann + , unAnn + , pattern Ann + ) +import Prelude + +{- | How a self-reference of the binding under rewrite renders in Lua: +a plain local variable (a 'Language.PureScript.Backend.IR.Types.Let' +recursive group), or a field of the module-scope table (a top-level +recursive group). +-} +data Self + = SelfLocal Name + | SelfField Name Name + +type Block = [Annotated Comments StatementF] + +{- | Rewrite the tail self-calls of a recursive binding's function into +iterations of a @while true do@ loop. Returns the expression unchanged +when it is not a function, has no rewritable tail self-call, or the +capture veto applies. +-} +loopify ∷ Self → Exp → Exp +loopify self original = fromMaybe original do + Function params body ← pure original + carried ← NE.nonEmpty =<< traverse (paramNamed . unAnn) params + guard $ + Set.disjoint + (Set.fromList (toList carried)) + (closureRefs BodyLevel body) + case rewriteBlock self carried body of + (body', Loopified) → + pure $ Function params [ann (While (ann (Boolean True)) body')] + (_, NotLoopified) → Nothing + +-------------------------------------------------------------------------------- +-- Tail self-call rewriting ---------------------------------------------------- + +data Loopified = Loopified | NotLoopified + +instance Semigroup Loopified where + NotLoopified <> NotLoopified = NotLoopified + _ <> _ = Loopified + +-- | Rewrite the tail position of a block: its final statement. +rewriteBlock ∷ Self → NonEmpty Name → Block → (Block, Loopified) +rewriteBlock self carried block = case List.unsnoc block of + Nothing → (block, NotLoopified) + Just (leading, (comments, final)) → + let (final', looped) = rewriteFinal self carried final + in (leading <> [(comments, final')], looped) + +rewriteFinal + ∷ Self + → NonEmpty Name + → StatementF Comments + → (StatementF Comments, Loopified) +rewriteFinal self carried = \case + original@(Return [Ann (FunctionCall (Ann callee) args)]) + | isSelfCallee self callee → + case balanceExplist (length carried) args of + Just explist → + (Assign (ann . VarName <$> carried) explist, Loopified) + Nothing → (original, NotLoopified) + IfThenElse predicate thenBlock elseBlock → + let (thenBlock', loopedThen) = rewriteBlock self carried thenBlock + (elseBlock', loopedElse) = rewriteBlock self carried elseBlock + in (IfThenElse predicate thenBlock' elseBlock', loopedThen <> loopedElse) + statement → (statement, NotLoopified) + +{- | Balance the self-call's argument explist against the count of +assigned variables — see the module documentation. 'Nothing' declines +the rewrite. +-} +balanceExplist + ∷ Int + → [Annotated Comments ExpF] + → Maybe (NonEmpty (Annotated Comments ExpF)) +balanceExplist varCount args = case compare (length args) varCount of + EQ → NE.nonEmpty args + LT → do + -- Padding after a multi-value expression would truncate it to one + -- value where the call form spreads all of them. + whenJust (viaNonEmpty last args) (guard . singleValued . unAnn) + NE.nonEmpty (args <> replicate (varCount - length args) (ann Nil)) + GT → do + let (kept, surplus) = splitAt varCount args + guard (all (syntacticallyPure . unAnn) surplus) + NE.nonEmpty kept + +-- | Adjusted to exactly one value in any explist position. +singleValued ∷ ExpF Comments → Bool +singleValued = \case + FunctionCall {} → False + MethodCall {} → False + Vararg → False + _ → True + +{- | Free of evaluation effects, so an unconsumed occurrence can be +dropped: no calls (and no table constructors or index chains, which +can run metamethods). +-} +syntacticallyPure ∷ ExpF Comments → Bool +syntacticallyPure = \case + Nil → True + Boolean _ → True + Integer _ → True + Float _ → True + String _ → True + Function _params _body → True + Var (Ann (VarName _)) → True + Paren e → syntacticallyPure (unAnn e) + _ → False + +isSelfCallee ∷ Self → ExpF Comments → Bool +isSelfCallee self callee = case (self, callee) of + (SelfLocal name, Var (Ann (VarName n))) → + n == name + ( SelfField table field + , Var (Ann (VarField (Ann (Var (Ann (VarName t)))) f)) + ) → + t == table && f == field + _ → False + +-------------------------------------------------------------------------------- +-- Capture analysis ------------------------------------------------------------ + +{- | Whether the code being walked runs within the current activation +('BodyLevel') or inside a nested closure that may outlive it +('UnderClosure'). +-} +data Position = BodyLevel | UnderClosure + +{- | The names referenced from inside nested closures of a block. At +'BodyLevel' plain references are invisible (reading a parameter within +the iteration is fine); crossing into a function literal makes every +name beneath it count. An immediately-invoked zero-argument function +is looked through transparently — see the capture veto section of the +module documentation. +-} +closureRefs ∷ Position → Block → Set Name +closureRefs pos = foldMap (refsInStatement pos . unAnn) + +refsInStatement ∷ Position → StatementF Comments → Set Name +refsInStatement pos = \case + Assign vars vals → + foldMap (refsInVar pos . unAnn) vars + <> foldMap (refsInExp pos . unAnn) vals + Local _names vals → foldMap (refsInExp pos . unAnn) vals + IfThenElse predicate thenBlock elseBlock → + refsInExp pos (unAnn predicate) + <> closureRefs pos thenBlock + <> closureRefs pos elseBlock + Return exps → foldMap (refsInExp pos . unAnn) exps + CallStatement e → refsInExp pos (unAnn e) + Do block → closureRefs pos block + While predicate block → + refsInExp pos (unAnn predicate) <> closureRefs pos block + Repeat block predicate → + closureRefs pos block <> refsInExp pos (unAnn predicate) + ForNum _name start limit step block → + refsInExp pos (unAnn start) + <> refsInExp pos (unAnn limit) + <> foldMap (refsInExp pos . unAnn) step + <> closureRefs pos block + ForIn _names exps block → + foldMap (refsInExp pos . unAnn) exps <> closureRefs pos block + LocalFunction _name _params block → closureRefs UnderClosure block + Break → mempty + +refsInExp ∷ Position → ExpF Comments → Set Name +refsInExp pos = \case + -- An immediately-invoked scope wrapper runs where it stands: + FunctionCall (Ann (Function [] block)) [] → closureRefs pos block + Function _params block → closureRefs UnderClosure block + Var (Ann v) → refsInVar pos v + FunctionCall fn args → + refsInExp pos (unAnn fn) <> foldMap (refsInExp pos . unAnn) args + MethodCall obj _name args → + refsInExp pos (unAnn obj) <> foldMap (refsInExp pos . unAnn) args + TableCtor rows → foldMap (refsInRow pos . unAnn) rows + UnOp _op e → refsInExp pos (unAnn e) + BinOp _op e1 e2 → refsInExp pos (unAnn e1) <> refsInExp pos (unAnn e2) + Paren e → refsInExp pos (unAnn e) + Nil → mempty + Boolean _ → mempty + Integer _ → mempty + Float _ → mempty + String _ → mempty + Vararg → mempty + +refsInRow ∷ Position → TableRowF Comments → Set Name +refsInRow pos = \case + TableRowKV k v → refsInExp pos (unAnn k) <> refsInExp pos (unAnn v) + TableRowNV _name v → refsInExp pos (unAnn v) + TableRowV v → refsInExp pos (unAnn v) + +refsInVar ∷ Position → VarF Comments → Set Name +refsInVar pos = \case + VarName name → case pos of + UnderClosure → Set.singleton name + BodyLevel → mempty + VarIndex e1 e2 → refsInExp pos (unAnn e1) <> refsInExp pos (unAnn e2) + VarField e _name → refsInExp pos (unAnn e) + +-------------------------------------------------------------------------------- +-- Helper Functions ------------------------------------------------------------ + +paramNamed ∷ ParamF Comments → Maybe Name +paramNamed = \case + ParamNamed name → Just name + ParamUnused → Nothing + ParamVararg → Nothing diff --git a/pslua.cabal b/pslua.cabal index ddf1da9c..7263aeeb 100644 --- a/pslua.cabal +++ b/pslua.cabal @@ -147,6 +147,7 @@ library Language.PureScript.Backend.Lua.Fixture Language.PureScript.Backend.Lua.Key Language.PureScript.Backend.Lua.Linker.Foreign + Language.PureScript.Backend.Lua.Loopify Language.PureScript.Backend.Lua.Name Language.PureScript.Backend.Lua.NestingCheck Language.PureScript.Backend.Lua.Optimizer diff --git a/test/ps/output/Golden.LongCallbackChain.Test/golden.lua b/test/ps/output/Golden.LongCallbackChain.Test/golden.lua index 667634be..161ec334 100644 --- a/test/ps/output/Golden.LongCallbackChain.Test/golden.lua +++ b/test/ps/output/Golden.LongCallbackChain.Test/golden.lua @@ -27,20 +27,22 @@ M.Effect_Console_foreign = { log = function(s) return function() print(s) end end } M.Golden_LongCallbackChain_Test_withInc_S_w = function(n, k) - if (function() - if "Data.Ordering∷Ordering.LT" == (M.Data_Ord_foreign.ordIntImpl({ - ["$ctor"] = "Data.Ordering∷Ordering.LT" - })({ ["$ctor"] = "Data.Ordering∷Ordering.EQ" })({ - ["$ctor"] = "Data.Ordering∷Ordering.GT" - })(n)(0))["$ctor"] then - return true + while true do + if (function() + if "Data.Ordering∷Ordering.LT" == (M.Data_Ord_foreign.ordIntImpl({ + ["$ctor"] = "Data.Ordering∷Ordering.LT" + })({ ["$ctor"] = "Data.Ordering∷Ordering.EQ" })({ + ["$ctor"] = "Data.Ordering∷Ordering.GT" + })(n)(0))["$ctor"] then + return true + else + return false + end + end)() then + n, k = M.Data_Semiring_foreign.intAdd(n)(1), k else - return false + return k(M.Data_Semiring_foreign.intAdd(n)(1)) end - end)() then - return M.Golden_LongCallbackChain_Test_withInc_S_w(M.Data_Semiring_foreign.intAdd(n)(1), k) - else - return k(M.Data_Semiring_foreign.intAdd(n)(1)) end end M.Golden_LongCallbackChain_Test_withInc = function(withInc_S_p1) diff --git a/test/ps/output/Golden.PatternMatching.Test2/golden.lua b/test/ps/output/Golden.PatternMatching.Test2/golden.lua index 05a1faf1..18d934d6 100644 --- a/test/ps/output/Golden.PatternMatching.Test2/golden.lua +++ b/test/ps/output/Golden.PatternMatching.Test2/golden.lua @@ -1,11 +1,13 @@ local M = {} M.Golden_PatternMatching_Test2_bat = function(n) - if "Golden.PatternMatching.Test1∷N.Zero" == n["$ctor"] then - return 1 - elseif "Golden.PatternMatching.Test1∷N.Succ" == n["$ctor"] then - return M.Golden_PatternMatching_Test2_bat(n.value0) - else - return error("No patterns matched") + while true do + if "Golden.PatternMatching.Test1∷N.Zero" == n["$ctor"] then + return 1 + elseif "Golden.PatternMatching.Test1∷N.Succ" == n["$ctor"] then + n = n.value0 + else + return error("No patterns matched") + end end end return { diff --git a/test/ps/output/Golden.StringCodePoints.Test/golden.lua b/test/ps/output/Golden.StringCodePoints.Test/golden.lua index b78897ed..2fa95111 100644 --- a/test/ps/output/Golden.StringCodePoints.Test/golden.lua +++ b/test/ps/output/Golden.StringCodePoints.Test/golden.lua @@ -627,15 +627,17 @@ M.Data_String_CodePoints_toCodePointArray = M.Data_String_CodePoints_foreign._to end)(s_S_10) end)(M.Data_String_CodePoints_unsafeCodePointAt0) M.Data_String_CodePoints_codePointAtFallback_S_w = function(n, s) - local v = M.Data_String_CodePoints_uncons(s) - if "Data.Maybe∷Maybe.Just" == v["$ctor"] then - if M.Data_String_CodePoints_eq(n)(0) then - return M.Data_Maybe_Just(v.value0.head) + while true do + local v = M.Data_String_CodePoints_uncons(s) + if "Data.Maybe∷Maybe.Just" == v["$ctor"] then + if M.Data_String_CodePoints_eq(n)(0) then + return M.Data_Maybe_Just(v.value0.head) + else + n, s = M.Data_String_CodePoints_sub(n)(1), v.value0.tail + end else - return M.Data_String_CodePoints_codePointAtFallback_S_w(M.Data_String_CodePoints_sub(n)(1), v.value0.tail) + return M.Data_Maybe_Nothing end - else - return M.Data_Maybe_Nothing end end M.Data_String_CodePoints_codePointAtFallback = function( codePointAtFallback_S_p1 ) diff --git a/test/ps/output/Golden.Uncurry.Test/golden.lua b/test/ps/output/Golden.Uncurry.Test/golden.lua index 53a1a405..09e3a793 100644 --- a/test/ps/output/Golden.Uncurry.Test/golden.lua +++ b/test/ps/output/Golden.Uncurry.Test/golden.lua @@ -109,10 +109,12 @@ end M.Golden_Uncurry_Test_sumTo = function(m) local go_S_w go_S_w = function(acc, n) - if M.Data_Eq_foreign.eqIntImpl(n)(0) then - return acc - else - return go_S_w(M.Data_Semiring_foreign.intAdd(acc)(n), M.Data_Ring_foreign.intSub(n)(1)) + while true do + if M.Data_Eq_foreign.eqIntImpl(n)(0) then + return acc + else + acc, n = M.Data_Semiring_foreign.intAdd(acc)(n), M.Data_Ring_foreign.intSub(n)(1) + end end end return go_S_w(0, m) From a1b7a53df6ba9d9d068d8e339c7b35eaa1f4997f Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Tue, 7 Jul 2026 21:44:15 +0200 Subject: [PATCH 2/3] test: golden and unit coverage for loopification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The golden module pins six shapes: a unary self-recursion (loopified without a worker split), the canonical binary accumulator, a local where-bound worker, McCarthy 91 (the tail self-call becomes an iteration while the argument-position one stays a call), a CPS accumulator (the capture veto keeps it recursive — the hand-written eval oracle proves reassignment would have corrupted the captured continuations), and a dead trailing parameter (the surplus argument is dropped from the loop assignment). Unit tests cover the same decision points at the fromUberModule level — top-level and Let-bound groups, non-tail calls, the capture veto, and explist balancing (pure surplus dropped, effectful surplus declined, nil padding, multi-value decline). --- test/Language/PureScript/Backend/Lua/Spec.hs | 217 ++++- .../Golden.Loopification.Test/corefn.json | 1 + .../Golden.Loopification.Test/eval/.gitignore | 1 + .../Golden.Loopification.Test/eval/golden.txt | 6 + .../Golden.Loopification.Test/golden.ir | 830 ++++++++++++++++++ .../Golden.Loopification.Test/golden.lua | 217 +++++ test/ps/src/Golden/Loopification/Test.purs | 55 ++ 7 files changed, 1317 insertions(+), 10 deletions(-) create mode 100644 test/ps/output/Golden.Loopification.Test/corefn.json create mode 100644 test/ps/output/Golden.Loopification.Test/eval/.gitignore create mode 100644 test/ps/output/Golden.Loopification.Test/eval/golden.txt create mode 100644 test/ps/output/Golden.Loopification.Test/golden.ir create mode 100644 test/ps/output/Golden.Loopification.Test/golden.lua create mode 100644 test/ps/src/Golden/Loopification/Test.purs diff --git a/test/Language/PureScript/Backend/Lua/Spec.hs b/test/Language/PureScript/Backend/Lua/Spec.hs index a96925be..a22a87cb 100644 --- a/test/Language/PureScript/Backend/Lua/Spec.hs +++ b/test/Language/PureScript/Backend/Lua/Spec.hs @@ -58,23 +58,90 @@ spec = describe "Lua.fromUberModule" do rendered ← compileExportedExpr (naryCallOn [primUndefined, ref "b"]) rendered `shouldSatisfy` Text.isInfixOf "f(nil, b)" + describe "loopification (#181)" do + it "lowers a top-level self-recursive tail call to a while loop" do + rendered ← compileRecBinding (selfTailLoop topSelf) + rendered `shouldSatisfy` Text.isInfixOf "while true do" + rendered `shouldSatisfy` Text.isInfixOf "acc, n = n, acc" + rendered `shouldSatisfy` (not . Text.isInfixOf "M.Test_Loopify_go(n, acc)") + + it "loopifies a Let-bound recursive group the same way" do + rendered ← compileExportedExpr letRecLoop + rendered `shouldSatisfy` Text.isInfixOf "while true do" + rendered `shouldSatisfy` Text.isInfixOf "acc, n = n, acc" + + it "keeps a non-tail self-call a real call" do + rendered ← compileRecBinding (selfNonTailCall topSelf) + rendered `shouldSatisfy` (not . Text.isInfixOf "while true do") + + it "does not loopify when a closure captures a parameter" do + rendered ← compileRecBinding (selfTailLoopCapturing topSelf) + rendered `shouldSatisfy` (not . Text.isInfixOf "while true do") + + it "rewrites both tail branches and keeps the non-tail one" do + rendered ← compileRecBinding (mixedTailCalls topSelf) + rendered `shouldSatisfy` Text.isInfixOf "while true do" + rendered `shouldSatisfy` Text.isInfixOf "M.Test_Loopify_go(acc, acc)" + + it "drops a pure surplus argument facing a dropped parameter" do + rendered ← compileRecBinding (surplusArg topSelf (ref "acc")) + rendered `shouldSatisfy` Text.isInfixOf "while true do" + rendered `shouldSatisfy` Text.isInfixOf "acc = q" + rendered `shouldSatisfy` (not . Text.isInfixOf "acc = q, acc") + + it "declines on an effectful surplus argument" do + rendered ← + compileRecBinding + (surplusArg topSelf (IR.App IR.noAnn (ref "f") (ref "acc"))) + rendered `shouldSatisfy` (not . Text.isInfixOf "while true do") + + it "pads elided trailing arguments with nil" do + rendered ← compileRecBinding (shortCall topSelf (ref "x")) + rendered `shouldSatisfy` Text.isInfixOf "while true do" + rendered `shouldSatisfy` Text.isInfixOf "acc, n = x, nil" + + it "declines to pad after a multi-value argument" do + rendered ← + compileRecBinding + (shortCall topSelf (IR.App IR.noAnn (ref "g") (ref "x"))) + rendered `shouldSatisfy` (not . Text.isInfixOf "while true do") + compileExportedExpr ∷ IR.Exp → IO Text -compileExportedExpr expr = do +compileExportedExpr expr = + compileUberModule + UberModule + { uberModuleBindings = [] + , uberModuleForeigns = [] + , uberModuleExports = [(IR.Name "value", expr)] + } + +{- | Compile a module with a single self-recursive top-level binding +@go@ (a 'IR.RecursiveGroup' of one member). +-} +compileRecBinding ∷ IR.Exp → IO Text +compileRecBinding expr = + compileUberModule + UberModule + { uberModuleBindings = + [ IR.RecursiveGroup + ((IR.QName testModuleName (IR.Name "go"), expr) :| []) + ] + , uberModuleForeigns = [] + , uberModuleExports = [(IR.Name "value", topSelf)] + } + +testModuleName ∷ IR.ModuleName +testModuleName = IR.ModuleName "Test.Loopify" + +compileUberModule ∷ UberModule → IO Text +compileUberModule uberModule = do foreignPath ← Tagged <$> getCurrentDir - let - moduleName = IR.ModuleName "Test.AbsScopeIife" - uberModule = - UberModule - { uberModuleBindings = [] - , uberModuleForeigns = [] - , uberModuleExports = [(IR.Name "value", expr)] - } result ← runExceptT ( Lua.fromUberModule foreignPath (Tagged False) - (AsModule moduleName) + (AsModule testModuleName) uberModule ∷ ExceptT (Variant '[Lua.Error]) IO Lua.Types.Chunk ) @@ -152,6 +219,136 @@ naryAbsTrailingUnused = ) (ref "a") +-- Loopification fixtures ------------------------------------------------------ + +-- | The top-level binding @go@ referencing itself. +topSelf ∷ IR.Exp +topSelf = IR.Ref IR.noAnn (IR.Imported testModuleName (IR.Name "go")) + +{- | @go acc n = if p then acc else go n acc@ — the argument swap pins +the simultaneity of the parameter reassignment. +-} +selfTailLoop ∷ IR.Exp → IR.Exp +selfTailLoop self = + absN ["acc", "n"] $ + IR.IfThenElse + IR.noAnn + (ref "p") + (ref "acc") + (IR.AppN IR.noAnn self (ref "n" :| [ref "acc"])) + +{- | @go acc n = if p then acc else f (go n acc)@ — the self-call is an +argument, not a tail call. +-} +selfNonTailCall ∷ IR.Exp → IR.Exp +selfNonTailCall self = + absN ["acc", "n"] $ + IR.IfThenElse + IR.noAnn + (ref "p") + (ref "acc") + ( IR.App + IR.noAnn + (ref "f") + (IR.AppN IR.noAnn self (ref "n" :| [ref "acc"])) + ) + +{- | @go acc n = if p then acc else go (\\r → acc n) acc@ — the closure +argument captures the loop-carried parameters. +-} +selfTailLoopCapturing ∷ IR.Exp → IR.Exp +selfTailLoopCapturing self = + absN ["acc", "n"] $ + IR.IfThenElse + IR.noAnn + (ref "p") + (ref "acc") + ( IR.AppN + IR.noAnn + self + ( IR.Abs + IR.noAnn + (IR.ParamNamed IR.noAnn (IR.Name "r")) + (IR.App IR.noAnn (ref "acc") (ref "n")) + :| [ref "acc"] + ) + ) + +{- | @go acc n = if p then go n acc else f (go acc acc)@ — a tail and a +non-tail self-call side by side. +-} +mixedTailCalls ∷ IR.Exp → IR.Exp +mixedTailCalls self = + absN ["acc", "n"] $ + IR.IfThenElse + IR.noAnn + (IR.App IR.noAnn (ref "p") (ref "n")) + (IR.AppN IR.noAnn self (ref "n" :| [ref "acc"])) + ( IR.App + IR.noAnn + (ref "f") + (IR.AppN IR.noAnn self (ref "acc" :| [ref "acc"])) + ) + +-- | @\\m → let go acc n = … in go 0 m@ with a self-recursive local @go@. +letRecLoop ∷ IR.Exp +letRecLoop = + IR.Abs IR.noAnn (IR.ParamNamed IR.noAnn (IR.Name "m")) $ + IR.Let + IR.noAnn + ( IR.RecursiveGroup + ( ( IR.noAnn + , IR.Name "go" + , selfTailLoop (IR.Ref IR.noAnn (IR.Local (IR.Name "go"))) + ) + :| [] + ) + :| [] + ) + ( IR.AppN + IR.noAnn + (IR.Ref IR.noAnn (IR.Local (IR.Name "go"))) + (IR.LiteralInt IR.noAnn 0 :| [ref "m"]) + ) + +{- | @go acc _ = if p then acc else go q \@ — the second parameter +is unused (and dropped by the Lua backend), so the self-call passes one +value more than the function has variables to assign. +-} +surplusArg ∷ IR.Exp → IR.Exp → IR.Exp +surplusArg self arg = + IR.AbsN + IR.noAnn + (IR.ParamNamed IR.noAnn (IR.Name "acc") :| [IR.ParamUnused IR.noAnn]) + ( IR.IfThenElse + IR.noAnn + (ref "p") + (ref "acc") + (IR.AppN IR.noAnn self (ref "q" :| [arg])) + ) + +{- | @go acc n = if p then acc else go \ Prim.undefined@ — the +trailing undefined argument is elided from the call, leaving it one +value short of the parameter list. +-} +shortCall ∷ IR.Exp → IR.Exp → IR.Exp +shortCall self arg = + absN ["acc", "n"] $ + IR.IfThenElse + IR.noAnn + (ref "p") + (ref "acc") + (IR.AppN IR.noAnn self (arg :| [primUndefined])) + +absN ∷ [Text] → IR.Exp → IR.Exp +absN names body = case names of + n : ns → + IR.AbsN + IR.noAnn + (IR.ParamNamed IR.noAnn . IR.Name <$> (n :| ns)) + body + [] → error "absN: needs at least one parameter" + ctorExpr ∷ IR.AlgebraicType → IR.Exp ctorExpr algebraicTy = IR.ctor diff --git a/test/ps/output/Golden.Loopification.Test/corefn.json b/test/ps/output/Golden.Loopification.Test/corefn.json new file mode 100644 index 00000000..b44fa0cf --- /dev/null +++ b/test/ps/output/Golden.Loopification.Test/corefn.json @@ -0,0 +1 @@ +{"builtWith":"0.15.16","comments":[{"LineComment":" | Exercises loopification (issue #181): a self-recursive tail call of"},{"LineComment":" | an uncurried worker lowers to a `while true` loop with parameter"},{"LineComment":" | reassignment. The eval oracle pins that every shape keeps its"},{"LineComment":" | runtime behavior, loopified or not."}],"decls":[{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[20,22],"start":[20,20]}},"type":"Var","value":{"identifier":"eq","moduleName":["Data","Eq"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[20,24],"start":[20,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"eqInt","moduleName":["Data","Eq"]}},"type":"App"},"identifier":"eq"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[20,51],"start":[20,50]}},"type":"Var","value":{"identifier":"add","moduleName":["Data","Semiring"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[20,53],"start":[20,46]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"semiringInt","moduleName":["Data","Semiring"]}},"type":"App"},"identifier":"add"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[20,59],"start":[20,58]}},"type":"Var","value":{"identifier":"sub","moduleName":["Data","Ring"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[20,61],"start":[20,56]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"ringInt","moduleName":["Data","Ring"]}},"type":"App"},"identifier":"sub"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[28,51],"start":[28,50]}},"type":"Var","value":{"identifier":"mul","moduleName":["Data","Semiring"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[28,53],"start":[28,42]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"semiringInt","moduleName":["Data","Semiring"]}},"type":"App"},"identifier":"mul"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[34,16],"start":[34,15]}},"type":"Var","value":{"identifier":"greaterThan","moduleName":["Data","Ord"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[34,20],"start":[34,13]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"ordInt","moduleName":["Data","Ord"]}},"type":"App"},"identifier":"greaterThan"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[15,22],"start":[15,20]}},"type":"Var","value":{"identifier":"lessThanOrEq","moduleName":["Data","Ord"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[15,24],"start":[15,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"ordInt","moduleName":["Data","Ord"]}},"type":"App"},"identifier":"lessThanOrEq"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[50,24],"start":[50,3]}},"type":"Var","value":{"identifier":"discard","moduleName":["Control","Bind"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discardUnit","moduleName":["Control","Bind"]}},"type":"App"},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"bindEffect","moduleName":["Effect"]}},"type":"App"},"identifier":"discard"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[50,10],"start":[50,3]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Effect","Console"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"showInt","moduleName":["Data","Show"]}},"type":"App"},"identifier":"logShow"},{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"bindType":"NonRec","expression":{"abstraction":{"annotation":{"meta":{"metaType":"IsForeign"},"sourceSpan":{"end":[54,29],"start":[54,21]}},"type":"Var","value":{"identifier":"identity","moduleName":["Control","Category"]}},"annotation":{"meta":{"metaType":"IsSyntheticApp"},"sourceSpan":{"end":[54,29],"start":[54,21]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"categoryFn","moduleName":["Control","Category"]}},"type":"App"},"identifier":"identity"},{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[19,27],"start":[19,1]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[19,27],"start":[19,1]}},"argument":"acc","body":{"annotation":{"meta":null,"sourceSpan":{"end":[19,27],"start":[19,1]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[20,62],"start":[20,15]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[20,62],"start":[20,15]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[20,33],"start":[20,30]}},"type":"Var","value":{"identifier":"acc","sourcePos":[20,1]}},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[20,62],"start":[20,15]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[20,44],"start":[20,39]}},"type":"Var","value":{"identifier":"sumTo","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[20,54],"start":[20,39]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"add","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[20,53],"start":[20,46]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,49],"start":[20,46]}},"type":"Var","value":{"identifier":"acc","sourcePos":[20,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[20,53],"start":[20,46]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,53],"start":[20,52]}},"type":"Var","value":{"identifier":"n","sourcePos":[20,1]}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[20,62],"start":[20,39]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[20,61],"start":[20,56]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,57],"start":[20,56]}},"type":"Var","value":{"identifier":"n","sourcePos":[20,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[20,61],"start":[20,56]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,61],"start":[20,60]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"eq","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[20,24],"start":[20,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,19],"start":[20,18]}},"type":"Var","value":{"identifier":"n","sourcePos":[20,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[20,24],"start":[20,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[20,24],"start":[20,23]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"}],"type":"Case"},"type":"Abs"},"type":"Abs"},"identifier":"sumTo"}]},{"annotation":{"meta":null,"sourceSpan":{"end":[24,25],"start":[24,1]}},"bindType":"NonRec","expression":{"annotation":{"meta":null,"sourceSpan":{"end":[24,25],"start":[24,1]}},"argument":"m","body":{"annotation":{"meta":{"metaType":"IsWhere"},"sourceSpan":{"end":[25,22],"start":[25,16]}},"binds":[{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[27,26],"start":[27,3]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[27,26],"start":[27,3]}},"argument":"acc","body":{"annotation":{"meta":null,"sourceSpan":{"end":[27,26],"start":[27,3]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[28,62],"start":[28,14]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[28,62],"start":[28,14]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[28,32],"start":[28,29]}},"type":"Var","value":{"identifier":"acc","sourcePos":[28,3]}},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[28,62],"start":[28,14]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[28,40],"start":[28,38]}},"type":"Var","value":{"identifier":"go","sourcePos":[27,3]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,54],"start":[28,38]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"add","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,53],"start":[28,42]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,45],"start":[28,42]}},"type":"Var","value":{"identifier":"acc","sourcePos":[28,3]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[28,53],"start":[28,42]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"mul","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,53],"start":[28,42]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,49],"start":[28,48]}},"type":"Var","value":{"identifier":"n","sourcePos":[28,3]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[28,53],"start":[28,42]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,53],"start":[28,52]}},"type":"Var","value":{"identifier":"n","sourcePos":[28,3]}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[28,62],"start":[28,38]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,61],"start":[28,56]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,57],"start":[28,56]}},"type":"Var","value":{"identifier":"n","sourcePos":[28,3]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[28,61],"start":[28,56]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,61],"start":[28,60]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"eq","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[28,23],"start":[28,17]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,18],"start":[28,17]}},"type":"Var","value":{"identifier":"n","sourcePos":[28,3]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[28,23],"start":[28,17]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[28,23],"start":[28,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"}],"type":"Case"},"type":"Abs"},"type":"Abs"},"identifier":"go"}]}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[25,18],"start":[25,16]}},"type":"Var","value":{"identifier":"go","sourcePos":[27,3]}},"annotation":{"meta":null,"sourceSpan":{"end":[25,20],"start":[25,16]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[25,20],"start":[25,19]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[25,22],"start":[25,16]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[25,22],"start":[25,21]}},"type":"Var","value":{"identifier":"m","sourcePos":[25,1]}},"type":"App"},"type":"Let"},"type":"Abs"},"identifier":"sumSquares"},{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[39,37],"start":[39,1]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[39,37],"start":[39,1]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[39,37],"start":[39,1]}},"argument":"k","body":{"annotation":{"meta":null,"sourceSpan":{"end":[40,70],"start":[40,14]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[40,70],"start":[40,14]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[40,30],"start":[40,29]}},"type":"Var","value":{"identifier":"k","sourcePos":[40,1]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,32],"start":[40,29]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,32],"start":[40,31]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[40,70],"start":[40,14]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[40,44],"start":[40,38]}},"type":"Var","value":{"identifier":"sumCPS","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,52],"start":[40,38]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,51],"start":[40,46]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,47],"start":[40,46]}},"type":"Var","value":{"identifier":"n","sourcePos":[40,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[40,51],"start":[40,46]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,51],"start":[40,50]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[40,70],"start":[40,38]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,69],"start":[40,54]}},"argument":"r","body":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[40,61],"start":[40,60]}},"type":"Var","value":{"identifier":"k","sourcePos":[40,1]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,69],"start":[40,60]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"add","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,68],"start":[40,63]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,64],"start":[40,63]}},"type":"Var","value":{"identifier":"r","sourcePos":[40,55]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[40,68],"start":[40,63]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,68],"start":[40,67]}},"type":"Var","value":{"identifier":"n","sourcePos":[40,1]}},"type":"App"},"type":"App"},"type":"Abs"},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"eq","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[40,23],"start":[40,17]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,18],"start":[40,17]}},"type":"Var","value":{"identifier":"n","sourcePos":[40,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[40,23],"start":[40,17]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[40,23],"start":[40,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"}],"type":"Case"},"type":"Abs"},"type":"Abs"},"identifier":"sumCPS"}]},{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[33,19],"start":[33,1]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[33,19],"start":[33,1]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[34,58],"start":[34,10]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[34,58],"start":[34,10]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[34,32],"start":[34,26]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,27],"start":[34,26]}},"type":"Var","value":{"identifier":"n","sourcePos":[34,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[34,32],"start":[34,26]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,32],"start":[34,30]}},"type":"Literal","value":{"literalType":"IntLiteral","value":10}},"type":"App"},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[34,58],"start":[34,10]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[34,42],"start":[34,38]}},"type":"Var","value":{"identifier":"mc91","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[34,58],"start":[34,38]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[34,48],"start":[34,44]}},"type":"Var","value":{"identifier":"mc91","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[34,57],"start":[34,44]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"add","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[34,56],"start":[34,50]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,51],"start":[34,50]}},"type":"Var","value":{"identifier":"n","sourcePos":[34,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[34,56],"start":[34,50]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,56],"start":[34,54]}},"type":"Literal","value":{"literalType":"IntLiteral","value":11}},"type":"App"},"type":"App"},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"greaterThan","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[34,20],"start":[34,13]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,14],"start":[34,13]}},"type":"Var","value":{"identifier":"n","sourcePos":[34,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[34,20],"start":[34,13]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[34,20],"start":[34,17]}},"type":"Literal","value":{"literalType":"IntLiteral","value":100}},"type":"App"}],"type":"Case"},"type":"Abs"},"identifier":"mc91"}]},{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[14,24],"start":[14,1]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[14,24],"start":[14,1]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[15,54],"start":[15,15]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[15,54],"start":[15,15]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[15,31],"start":[15,30]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[15,54],"start":[15,15]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[15,46],"start":[15,37]}},"type":"Var","value":{"identifier":"countdown","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[15,54],"start":[15,37]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[15,53],"start":[15,48]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,49],"start":[15,48]}},"type":"Var","value":{"identifier":"n","sourcePos":[15,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[15,53],"start":[15,48]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,53],"start":[15,52]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"lessThanOrEq","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[15,24],"start":[15,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,19],"start":[15,18]}},"type":"Var","value":{"identifier":"n","sourcePos":[15,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[15,24],"start":[15,18]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[15,24],"start":[15,23]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"}],"type":"Case"},"type":"Abs"},"identifier":"countdown"}]},{"bindType":"Rec","binds":[{"annotation":{"meta":null,"sourceSpan":{"end":[45,31],"start":[45,1]}},"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[45,31],"start":[45,1]}},"argument":"n","body":{"annotation":{"meta":null,"sourceSpan":{"end":[45,31],"start":[45,1]}},"argument":"v","body":{"annotation":{"meta":null,"sourceSpan":{"end":[46,58],"start":[46,17]}},"caseAlternatives":[{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[46,58],"start":[46,17]}},"binderType":"LiteralBinder","literal":{"literalType":"BooleanLiteral","value":true}}],"expression":{"annotation":{"meta":null,"sourceSpan":{"end":[46,33],"start":[46,32]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"isGuarded":false},{"binders":[{"annotation":{"meta":null,"sourceSpan":{"end":[46,58],"start":[46,17]}},"binderType":"NullBinder"}],"expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[46,48],"start":[46,39]}},"type":"Var","value":{"identifier":"countDrop","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[46,56],"start":[46,39]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"sub","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[46,55],"start":[46,50]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[46,51],"start":[46,50]}},"type":"Var","value":{"identifier":"n","sourcePos":[46,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[46,55],"start":[46,50]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[46,55],"start":[46,54]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[46,58],"start":[46,39]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[46,58],"start":[46,57]}},"type":"Var","value":{"identifier":"n","sourcePos":[46,1]}},"type":"App"},"isGuarded":false}],"caseExpressions":[{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"eq","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[46,26],"start":[46,20]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[46,21],"start":[46,20]}},"type":"Var","value":{"identifier":"n","sourcePos":[46,1]}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[46,26],"start":[46,20]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[46,26],"start":[46,25]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"}],"type":"Case"},"type":"Abs"},"type":"Abs"},"identifier":"countDrop"}]},{"annotation":{"meta":null,"sourceSpan":{"end":[48,20],"start":[48,1]}},"bindType":"NonRec","expression":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[50,21],"start":[50,12]}},"type":"Var","value":{"identifier":"countdown","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[50,23],"start":[50,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[50,23],"start":[50,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":5}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[50,24],"start":[50,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[51,23],"start":[51,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[51,23],"start":[51,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[51,17],"start":[51,12]}},"type":"Var","value":{"identifier":"sumTo","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[51,19],"start":[51,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[51,19],"start":[51,18]}},"type":"Literal","value":{"literalType":"IntLiteral","value":0}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[51,22],"start":[51,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[51,22],"start":[51,20]}},"type":"Literal","value":{"literalType":"IntLiteral","value":10}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[51,23],"start":[51,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[51,23],"start":[51,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[52,25],"start":[52,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[52,25],"start":[52,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[52,22],"start":[52,12]}},"type":"Var","value":{"identifier":"sumSquares","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[52,24],"start":[52,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[52,24],"start":[52,23]}},"type":"Literal","value":{"literalType":"IntLiteral","value":4}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[52,25],"start":[52,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[52,25],"start":[52,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[53,19],"start":[53,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[53,19],"start":[53,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[53,16],"start":[53,12]}},"type":"Var","value":{"identifier":"mc91","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[53,18],"start":[53,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[53,18],"start":[53,17]}},"type":"Literal","value":{"literalType":"IntLiteral","value":1}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[53,19],"start":[53,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[53,19],"start":[53,3]}},"argument":"$__unused","body":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"discard","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[54,30],"start":[54,3]}},"argument":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[54,30],"start":[54,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[54,18],"start":[54,12]}},"type":"Var","value":{"identifier":"sumCPS","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[54,20],"start":[54,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[54,20],"start":[54,19]}},"type":"Literal","value":{"literalType":"IntLiteral","value":5}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[54,29],"start":[54,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"identity","moduleName":["Golden","Loopification","Test"]}},"type":"App"},"type":"App"},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[54,30],"start":[54,3]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[54,30],"start":[54,3]}},"argument":"$__unused","body":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[0,0],"start":[0,0]}},"type":"Var","value":{"identifier":"logShow","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[55,3]}},"argument":{"abstraction":{"abstraction":{"annotation":{"meta":null,"sourceSpan":{"end":[55,21],"start":[55,12]}},"type":"Var","value":{"identifier":"countDrop","moduleName":["Golden","Loopification","Test"]}},"annotation":{"meta":null,"sourceSpan":{"end":[55,23],"start":[55,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[55,23],"start":[55,22]}},"type":"Literal","value":{"literalType":"IntLiteral","value":3}},"type":"App"},"annotation":{"meta":null,"sourceSpan":{"end":[55,26],"start":[55,12]}},"argument":{"annotation":{"meta":null,"sourceSpan":{"end":[55,26],"start":[55,24]}},"type":"Literal","value":{"literalType":"IntLiteral","value":99}},"type":"App"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"type":"Abs"},"type":"App"},"identifier":"main"}],"exports":["countdown","sumTo","sumSquares","mc91","sumCPS","countDrop","main"],"foreign":[],"imports":[{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Control","Bind"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Control","Category"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Data","Eq"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Data","Ord"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Data","Ring"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Data","Semiring"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Data","Show"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Effect"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Effect","Console"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Golden","Loopification","Test"]},{"annotation":{"meta":null,"sourceSpan":{"end":[7,15],"start":[7,1]}},"moduleName":["Prelude"]},{"annotation":{"meta":null,"sourceSpan":{"end":[55,27],"start":[5,1]}},"moduleName":["Prim"]}],"moduleName":["Golden","Loopification","Test"],"modulePath":"src/Golden/Loopification/Test.purs","reExports":{},"sourceSpan":{"end":[55,27],"start":[5,1]}} \ No newline at end of file diff --git a/test/ps/output/Golden.Loopification.Test/eval/.gitignore b/test/ps/output/Golden.Loopification.Test/eval/.gitignore new file mode 100644 index 00000000..d2dc29bb --- /dev/null +++ b/test/ps/output/Golden.Loopification.Test/eval/.gitignore @@ -0,0 +1 @@ +actual.txt diff --git a/test/ps/output/Golden.Loopification.Test/eval/golden.txt b/test/ps/output/Golden.Loopification.Test/eval/golden.txt new file mode 100644 index 00000000..30144ed9 --- /dev/null +++ b/test/ps/output/Golden.Loopification.Test/eval/golden.txt @@ -0,0 +1,6 @@ +0 +55 +30 +91 +15 +0 diff --git a/test/ps/output/Golden.Loopification.Test/golden.ir b/test/ps/output/Golden.Loopification.Test/golden.ir new file mode 100644 index 00000000..f5391edf --- /dev/null +++ b/test/ps/output/Golden.Loopification.Test/golden.ir @@ -0,0 +1,830 @@ +UberModule + { uberModuleBindings = + [ Standalone + ( QName + { qnameModuleName = ModuleName "Data.Eq", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Eq" ) ".spago/p/prelude/26c058c2a053cf4dd7240f0d822ec096c0fecbe1/src/Data/Eq.purs" + [ ( Nothing, Name "eqIntImpl" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Show", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Show" ) ".spago/p/prelude/26c058c2a053cf4dd7240f0d822ec096c0fecbe1/src/Data/Show.purs" + [ ( Nothing, Name "showIntImpl" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Semiring", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Semiring" ) ".spago/p/prelude/26c058c2a053cf4dd7240f0d822ec096c0fecbe1/src/Data/Semiring.purs" + [ ( Nothing, Name "intAdd" ), ( Nothing, Name "intMul" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Ring", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Ring" ) ".spago/p/prelude/26c058c2a053cf4dd7240f0d822ec096c0fecbe1/src/Data/Ring.purs" + [ ( Nothing, Name "intSub" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Ord", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Data.Ord" ) ".spago/p/prelude/26c058c2a053cf4dd7240f0d822ec096c0fecbe1/src/Data/Ord.purs" + [ ( Nothing, Name "ordIntImpl" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Effect" ) ".spago/p/effect/82bac3dff904fa34534c4f5b9deeb5da359471c8/src/Effect.purs" + [ ( Nothing, Name "pureE" ), ( Nothing, Name "bindE" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Effect.Console", qnameName = Name "foreign" + }, ForeignImport Nothing + ( ModuleName "Effect.Console" ) ".spago/p/console/f82835a0b873aafe6bd7b14dd30cc150553d4ab9/src/Effect/Console.purs" + [ ( Nothing, Name "log" ) ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Eq", qnameName = Name "eqInt" }, LiteralObject Nothing + [ + ( PropName "eq", ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Eq" ) ( Name "foreign" ) ) ) + ( PropName "eqIntImpl" ) + ) + ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Semiring", qnameName = Name "semiringInt" + }, LiteralObject Nothing + [ + ( PropName "add", ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Semiring" ) ( Name "foreign" ) ) ) + ( PropName "intAdd" ) + ), + ( PropName "zero", LiteralInt Nothing 0 ), + ( PropName "mul", ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Semiring" ) ( Name "foreign" ) ) ) + ( PropName "intMul" ) + ), + ( PropName "one", LiteralInt Nothing 1 ) + ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Ord", qnameName = Name "ordInt" + }, LiteralObject Nothing + [ + ( PropName "compare", AppN Nothing + ( AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ord" ) ( Name "foreign" ) ) ) + ( PropName "ordIntImpl" ) + ) + ( Ctor Nothing SumType + ( ModuleName "Data.Ordering" ) + ( TyName "Ordering" ) + ( CtorName "LT" ) [] :| [] + ) + ) + ( Ctor Nothing SumType + ( ModuleName "Data.Ordering" ) + ( TyName "Ordering" ) + ( CtorName "EQ" ) [] :| [] + ) + ) + ( Ctor Nothing SumType + ( ModuleName "Data.Ordering" ) + ( TyName "Ordering" ) + ( CtorName "GT" ) [] :| [] + ) + ), + ( PropName "Eq0", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( Ref Nothing ( Imported ( ModuleName "Data.Eq" ) ( Name "eqInt" ) ) ) + ) + ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Data.Ord", qnameName = Name "compare" }, AbsN Nothing + ( ParamNamed Nothing ( Name "dict" ) :| [] ) + ( ObjectProp Nothing ( Ref Nothing ( Local ( Name "dict" ) ) ) ( PropName "compare" ) ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Control.Applicative", qnameName = Name "pure" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "dict" ) :| [] ) + ( ObjectProp Nothing ( Ref Nothing ( Local ( Name "dict" ) ) ) ( PropName "pure" ) ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Control.Bind", qnameName = Name "bind" }, AbsN Nothing + ( ParamNamed Nothing ( Name "dict" ) :| [] ) + ( ObjectProp Nothing ( Ref Nothing ( Local ( Name "dict" ) ) ) ( PropName "bind" ) ) + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "monadEffect" + }, LiteralObject Nothing + [ + ( PropName "Applicative0", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "applicativeEffect" ) ) ) + ), + ( PropName "Bind1", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "bindEffect" ) ) ) + ) + ] + ) :| + [ + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "bindEffect" + }, LiteralObject Nothing + [ + ( PropName "bind", ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "foreign" ) ) ) + ( PropName "bindE" ) + ), + ( PropName "Apply0", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "Lazy_applyEffect" ) ) ) + ( LiteralInt Nothing 0 :| [] ) + ) + ) + ] + ), + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "applicativeEffect" + }, LiteralObject Nothing + [ + ( PropName "pure", ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "foreign" ) ) ) + ( PropName "pureE" ) + ), + ( PropName "Apply0", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "Lazy_applyEffect" ) ) ) + ( LiteralInt Nothing 0 :| [] ) + ) + ) + ] + ), + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "Lazy_functorEffect" + }, AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Local ( Name "PSLUA_runtime_lazy" ) ) ) + ( LiteralString Nothing "functorEffect" :| [] ) + ) + ( AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( LiteralObject Nothing + [ + ( PropName "map", AbsN Nothing + ( ParamNamed Nothing ( Name "f$28" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "a$29" ) :| [] ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp Nothing + ( AppN Nothing + ( ObjectProp Nothing + ( Ref Nothing + ( Imported ( ModuleName "Effect" ) ( Name "applicativeEffect" ) ) + ) + ( PropName "Apply0" ) + ) + ( Ref Nothing + ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] + ) + ) + ( PropName "apply" ) + ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Control.Applicative" ) ( Name "pure" ) ) + ) + ( Ref Nothing + ( Imported + ( ModuleName "Effect" ) + ( Name "applicativeEffect" ) + ) :| [] + ) + ) + ( Ref Nothing ( Local ( Name "f$28" ) ) :| [] ) :| [] + ) + ) + ( Ref Nothing ( Local ( Name "a$29" ) ) :| [] ) + ) + ) + ) + ] + ) :| [] + ) + ), + ( QName + { qnameModuleName = ModuleName "Effect", qnameName = Name "Lazy_applyEffect" + }, AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Local ( Name "PSLUA_runtime_lazy" ) ) ) + ( LiteralString Nothing "applyEffect" :| [] ) + ) + ( AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( LiteralObject Nothing + [ + ( PropName "apply", Let Nothing + ( Standalone + ( Nothing, Name "bind$7", AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Control.Bind" ) ( Name "bind" ) ) ) + ( AppN Nothing + ( ObjectProp Nothing + ( Ref Nothing + ( Imported ( ModuleName "Effect" ) ( Name "monadEffect" ) ) + ) + ( PropName "Bind1" ) + ) + ( Ref Nothing + ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] + ) :| [] + ) + ) :| [] + ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "f$9" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "a$10" ) :| [] ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Local ( Name "bind$7" ) ) ) + ( Ref Nothing ( Local ( Name "f$9" ) ) :| [] ) + ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "f'$11" ) :| [] ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Local ( Name "bind$7" ) ) ) + ( Ref Nothing ( Local ( Name "a$10" ) ) :| [] ) + ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "a'$12" ) :| [] ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported + ( ModuleName "Control.Applicative" ) + ( Name "pure" ) + ) + ) + ( AppN Nothing + ( ObjectProp Nothing + ( Ref Nothing + ( Imported + ( ModuleName "Effect" ) + ( Name "monadEffect" ) + ) + ) + ( PropName "Applicative0" ) + ) + ( Ref Nothing + ( Imported + ( ModuleName "Prim" ) + ( Name "undefined" ) + ) :| [] + ) :| [] + ) + ) + ( AppN Nothing + ( Ref Nothing ( Local ( Name "f'$11" ) ) ) + ( Ref Nothing ( Local ( Name "a'$12" ) ) :| [] ) :| [] + ) + ) :| [] + ) + ) :| [] + ) + ) + ) + ) + ), + ( PropName "Functor0", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Effect" ) ( Name "Lazy_functorEffect" ) ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ) + ] + ) :| [] + ) + ) + ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "eq" + }, ObjectProp Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Eq" ) ( Name "eqInt" ) ) ) + ( PropName "eq" ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "add" + }, ObjectProp Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Semiring" ) ( Name "semiringInt" ) ) ) + ( PropName "add" ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "discard" + }, AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Control.Bind" ) ( Name "bind" ) ) ) + ( Ref Nothing ( Imported ( ModuleName "Effect" ) ( Name "bindEffect" ) ) :| [] ) + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "logShow" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "a$2" ) :| [] ) + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Effect.Console" ) ( Name "foreign" ) ) ) + ( PropName "log" ) + ) + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Show" ) ( Name "foreign" ) ) ) + ( PropName "showIntImpl" ) + ) + ( Ref Nothing ( Local ( Name "a$2" ) ) :| [] ) :| [] + ) + ) + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "sumTo$w" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "acc" ) :| [ ParamNamed Nothing ( Name "n" ) ] ) + ( IfThenElse Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "eq" ) ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ( Ref Nothing ( Local ( Name "acc" ) ) ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumTo$w" ) ) + ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "add" ) ) + ) + ( Ref Nothing ( Local ( Name "acc" ) ) :| [] ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) :| + [ AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 1 :| [] ) + ] + ) + ) + ) + ) :| + [ + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "sumTo" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "sumTo$p1" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "sumTo$p2" ) :| [] ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumTo$w" ) ) + ) + ( Ref Nothing + ( Local ( Name "sumTo$p1" ) ) :| + [ Ref Nothing ( Local ( Name "sumTo$p2" ) ) ] + ) + ) + ) + ) + ] + ), Standalone + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "sumSquares" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "m" ) :| [] ) + ( Let Nothing + ( RecursiveGroup + ( + ( Nothing, Name "go$w", AbsN Nothing + ( ParamNamed Nothing ( Name "acc" ) :| [ ParamNamed Nothing ( Name "n" ) ] ) + ( IfThenElse Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "eq" ) ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ( Ref Nothing ( Local ( Name "acc" ) ) ) + ( AppN Nothing + ( Ref Nothing ( Local ( Name "go$w" ) ) ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "add" ) ) + ) + ( Ref Nothing ( Local ( Name "acc" ) ) :| [] ) + ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp Nothing + ( Ref Nothing + ( Imported ( ModuleName "Data.Semiring" ) ( Name "semiringInt" ) ) + ) + ( PropName "mul" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) :| [] + ) :| + [ AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing + ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) + ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 1 :| [] ) + ] + ) + ) + ) + ) :| [] + ) :| [] + ) + ( AppN Nothing + ( Ref Nothing ( Local ( Name "go$w" ) ) ) + ( LiteralInt Nothing 0 :| [ Ref Nothing ( Local ( Name "m" ) ) ] ) + ) + ) + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "sumCPS$w" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "n" ) :| [ ParamNamed Nothing ( Name "k" ) ] ) + ( IfThenElse Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "eq" ) ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ( AppN Nothing ( Ref Nothing ( Local ( Name "k" ) ) ) ( LiteralInt Nothing 0 :| [] ) ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumCPS$w" ) ) + ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 1 :| [] ) :| + [ AbsN Nothing + ( ParamNamed Nothing ( Name "r" ) :| [] ) + ( AppN Nothing + ( Ref Nothing ( Local ( Name "k" ) ) ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "add" ) ) + ) + ( Ref Nothing ( Local ( Name "r" ) ) :| [] ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) :| [] + ) + ) + ] + ) + ) + ) + ) :| + [ + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "sumCPS" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "sumCPS$p1" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "sumCPS$p2" ) :| [] ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumCPS$w" ) ) + ) + ( Ref Nothing + ( Local ( Name "sumCPS$p1" ) ) :| + [ Ref Nothing ( Local ( Name "sumCPS$p2" ) ) ] + ) + ) + ) + ) + ] + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "mc91" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "n" ) :| [] ) + ( IfThenElse Nothing + ( IfThenElse Nothing + ( Eq Nothing + ( LiteralString Nothing "Data.Ordering∷Ordering.GT" ) + ( ReflectCtor Nothing + ( AppN Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Ord" ) ( Name "compare" ) ) ) + ( Ref Nothing + ( Imported ( ModuleName "Data.Ord" ) ( Name "ordInt" ) ) :| [] + ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 100 :| [] ) + ) + ) + ) ( LiteralBool Nothing True ) ( LiteralBool Nothing False ) + ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 10 :| [] ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "mc91" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "mc91" ) ) + ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "add" ) ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 11 :| [] ) :| [] + ) :| [] + ) + ) + ) + ) :| [] + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "countdown" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "n" ) :| [] ) + ( IfThenElse Nothing + ( IfThenElse Nothing + ( Eq Nothing + ( LiteralString Nothing "Data.Ordering∷Ordering.GT" ) + ( ReflectCtor Nothing + ( AppN Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing ( Imported ( ModuleName "Data.Ord" ) ( Name "compare" ) ) ) + ( Ref Nothing + ( Imported ( ModuleName "Data.Ord" ) ( Name "ordInt" ) ) :| [] + ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ) + ) ( LiteralBool Nothing False ) ( LiteralBool Nothing True ) + ) + ( LiteralInt Nothing 0 ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countdown" ) ) + ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 1 :| [] ) :| [] + ) + ) + ) + ) :| [] + ), RecursiveGroup + ( + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "countDrop$w" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "n" ) :| [ ParamUnused Nothing ] ) + ( IfThenElse Nothing + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "eq" ) ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 0 :| [] ) + ) + ( LiteralInt Nothing 0 ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countDrop$w" ) ) + ) + ( AppN Nothing + ( AppN Nothing + ( ObjectProp ( Just Always ) + ( Ref Nothing ( Imported ( ModuleName "Data.Ring" ) ( Name "foreign" ) ) ) + ( PropName "intSub" ) + ) + ( Ref Nothing ( Local ( Name "n" ) ) :| [] ) + ) + ( LiteralInt Nothing 1 :| [] ) :| + [ Ref Nothing ( Local ( Name "n" ) ) ] + ) + ) + ) + ) :| + [ + ( QName + { qnameModuleName = ModuleName "Golden.Loopification.Test", qnameName = Name "countDrop" + }, AbsN Nothing + ( ParamNamed Nothing ( Name "countDrop$p1" ) :| [] ) + ( AbsN Nothing + ( ParamNamed Nothing ( Name "countDrop$p2" ) :| [] ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countDrop$w" ) ) + ) + ( Ref Nothing + ( Local ( Name "countDrop$p1" ) ) :| + [ Ref Nothing ( Local ( Name "countDrop$p2" ) ) ] + ) + ) + ) + ) + ] + ) + ], uberModuleForeigns = [], uberModuleExports = + [ + ( Name "countdown", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countdown" ) ) + ), + ( Name "sumTo", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumTo" ) ) + ), + ( Name "sumSquares", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumSquares" ) ) + ), + ( Name "mc91", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "mc91" ) ) + ), + ( Name "sumCPS", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumCPS" ) ) + ), + ( Name "countDrop", Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countDrop" ) ) + ), + ( Name "main", AbsN Nothing + ( ParamUnused Nothing :| [] ) + ( Let Nothing + ( Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countdown" ) ) + ) + ( LiteralInt Nothing 5 :| [] ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ) :| + [ Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumTo$w" ) ) + ) + ( LiteralInt Nothing 0 :| [ LiteralInt Nothing 10 ] ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ), Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumSquares" ) ) + ) + ( LiteralInt Nothing 4 :| [] ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ), Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "mc91" ) ) + ) + ( LiteralInt Nothing 1 :| [] ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ), Standalone + ( Nothing, Name "_", AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "sumCPS$w" ) ) + ) + ( LiteralInt Nothing 5 :| + [ AbsN Nothing + ( ParamNamed Nothing ( Name "x$193" ) :| [] ) + ( Ref Nothing ( Local ( Name "x$193" ) ) ) + ] + ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ) + ] + ) + ( AppN Nothing + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "logShow" ) ) + ) + ( AppN Nothing + ( Ref Nothing + ( Imported ( ModuleName "Golden.Loopification.Test" ) ( Name "countDrop$w" ) ) + ) + ( LiteralInt Nothing 3 :| [ LiteralInt Nothing 99 ] ) :| [] + ) + ) + ( Ref Nothing ( Imported ( ModuleName "Prim" ) ( Name "undefined" ) ) :| [] ) + ) + ) + ) + ] + } \ No newline at end of file diff --git a/test/ps/output/Golden.Loopification.Test/golden.lua b/test/ps/output/Golden.Loopification.Test/golden.lua new file mode 100644 index 00000000..a90b2c47 --- /dev/null +++ b/test/ps/output/Golden.Loopification.Test/golden.lua @@ -0,0 +1,217 @@ +local function PSLUA_runtime_lazy(name) + return function(init) + local state = 0 + local val = nil + return function() + if state == 2 then + return val + elseif state == 1 then + return error(name .. " was needed before it finished initializing") + else + state = 1 + val = init() + state = 2 + return val + end + end + end +end +local M = {} +M.Data_Eq_foreign = (function() + local refEq = function(r1) return function(r2) return r1 == r2 end end + return { eqIntImpl = refEq } +end)() +M.Data_Show_foreign = { showIntImpl = function(n) return tostring(n) end } +M.Data_Semiring_foreign = { + intAdd = function(x) return function(y) return x + y end end, + intMul = function(x) return function(y) return x * y end end +} +M.Data_Ring_foreign = { + intSub = function(x) return function(y) return x - y end end +} +M.Data_Ord_foreign = (function() + local unsafeCoerceImpl = function(lt) + return function(eq) + return function(gt) + return function(x) + return function(y) + if x < y then + return lt + elseif x == y then + return eq + else + return gt + end + end + end + end + end + end + return { ordIntImpl = unsafeCoerceImpl } +end)() +M.Effect_foreign = { + pureE = function(a) return function() return a end end, + bindE = function(a) + return function(f) return function() return f(a())() end end + end +} +M.Effect_Console_foreign = { + log = function(s) return function() print(s) end end +} +M.Data_Eq_eqInt = { eq = M.Data_Eq_foreign.eqIntImpl } +M.Data_Semiring_semiringInt = { + add = M.Data_Semiring_foreign.intAdd, + zero = 0, + mul = M.Data_Semiring_foreign.intMul, + one = 1 +} +M.Data_Ord_ordInt = { + compare = M.Data_Ord_foreign.ordIntImpl({ + ["$ctor"] = "Data.Ordering∷Ordering.LT" + })({ ["$ctor"] = "Data.Ordering∷Ordering.EQ" })({ + ["$ctor"] = "Data.Ordering∷Ordering.GT" + }), + Eq0 = function() return M.Data_Eq_eqInt end +} +M.Data_Ord_compare = function(dict) return dict.compare end +M.Control_Applicative_pure = function(dict) return dict.pure end +M.Control_Bind_bind = function(dict) return dict.bind end +M.Effect_monadEffect = { + Applicative0 = function() return M.Effect_applicativeEffect end, + Bind1 = function() return M.Effect_bindEffect end +} +M.Effect_bindEffect = { + bind = M.Effect_foreign.bindE, + Apply0 = function() return M.Effect_Lazy_applyEffect(0) end +} +M.Effect_applicativeEffect = { + pure = M.Effect_foreign.pureE, + Apply0 = function() return M.Effect_Lazy_applyEffect(0) end +} +M.Effect_Lazy_functorEffect = PSLUA_runtime_lazy("functorEffect")(function() + return { + map = function(f_S_28) + return function(a_S_29) + return (M.Effect_applicativeEffect.Apply0()).apply(M.Control_Applicative_pure(M.Effect_applicativeEffect)(f_S_28))(a_S_29) + end + end + } +end) +M.Effect_Lazy_applyEffect = PSLUA_runtime_lazy("applyEffect")(function() + return { + apply = (function() + local bind_S_7 = M.Control_Bind_bind(M.Effect_monadEffect.Bind1()) + return function(f_S_9) + return function(a_S_10) + return bind_S_7(f_S_9)(function(fPrime_S_11) + return bind_S_7(a_S_10)(function(aPrime_S_12) + return M.Control_Applicative_pure(M.Effect_monadEffect.Applicative0())(fPrime_S_11(aPrime_S_12)) + end) + end) + end + end + end)(), + Functor0 = function() return M.Effect_Lazy_functorEffect(0) end + } +end) +M.Golden_Loopification_Test_eq = M.Data_Eq_eqInt.eq +M.Golden_Loopification_Test_add = M.Data_Semiring_semiringInt.add +M.Golden_Loopification_Test_discard = M.Control_Bind_bind(M.Effect_bindEffect) +M.Golden_Loopification_Test_logShow = function(a_S_2) + return M.Effect_Console_foreign.log(M.Data_Show_foreign.showIntImpl(a_S_2)) +end +M.Golden_Loopification_Test_sumTo_S_w = function(acc, n) + while true do + if M.Golden_Loopification_Test_eq(n)(0) then + return acc + else + acc, n = M.Golden_Loopification_Test_add(acc)(n), M.Data_Ring_foreign.intSub(n)(1) + end + end +end +M.Golden_Loopification_Test_sumTo = function(sumTo_S_p1) + return function(sumTo_S_p2) + return M.Golden_Loopification_Test_sumTo_S_w(sumTo_S_p1, sumTo_S_p2) + end +end +M.Golden_Loopification_Test_sumSquares = function(m) + local go_S_w + go_S_w = function(acc, n) + while true do + if M.Golden_Loopification_Test_eq(n)(0) then + return acc + else + acc, n = M.Golden_Loopification_Test_add(acc)(M.Data_Semiring_semiringInt.mul(n)(n)), M.Data_Ring_foreign.intSub(n)(1) + end + end + end + return go_S_w(0, m) +end +M.Golden_Loopification_Test_sumCPS_S_w = function(n, k) + if M.Golden_Loopification_Test_eq(n)(0) then + return k(0) + else + return M.Golden_Loopification_Test_sumCPS_S_w(M.Data_Ring_foreign.intSub(n)(1), function( r ) + return k(M.Golden_Loopification_Test_add(r)(n)) + end) + end +end +M.Golden_Loopification_Test_sumCPS = function(sumCPS_S_p1) + return function(sumCPS_S_p2) + return M.Golden_Loopification_Test_sumCPS_S_w(sumCPS_S_p1, sumCPS_S_p2) + end +end +M.Golden_Loopification_Test_mc91 = function(n) + while true do + if (function() + if "Data.Ordering∷Ordering.GT" == (M.Data_Ord_compare(M.Data_Ord_ordInt)(n)(100))["$ctor"] then + return true + else + return false + end + end)() then + return M.Data_Ring_foreign.intSub(n)(10) + else + n = M.Golden_Loopification_Test_mc91(M.Golden_Loopification_Test_add(n)(11)) + end + end +end +M.Golden_Loopification_Test_countdown = function(n) + while true do + if (function() + if "Data.Ordering∷Ordering.GT" == (M.Data_Ord_compare(M.Data_Ord_ordInt)(n)(0))["$ctor"] then + return false + else + return true + end + end)() then + return 0 + else + n = M.Data_Ring_foreign.intSub(n)(1) + end + end +end +M.Golden_Loopification_Test_countDrop_S_w = function(n) + while true do + if M.Golden_Loopification_Test_eq(n)(0) then + return 0 + else + n = M.Data_Ring_foreign.intSub(n)(1) + end + end +end +M.Golden_Loopification_Test_countDrop = function(countDrop_S_p1) + return function(countDrop_S_p2) + return M.Golden_Loopification_Test_countDrop_S_w(countDrop_S_p1, countDrop_S_p2) + end +end +return (function() + local _ = M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_countdown(5))() + local _ = M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_sumTo_S_w(0, 10))() + local _ = M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_sumSquares(4))() + local _ = M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_mc91(1))() + local _ = M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_sumCPS_S_w(5, function( x_S_193 ) + return x_S_193 + end))() + return M.Golden_Loopification_Test_logShow(M.Golden_Loopification_Test_countDrop_S_w(3, 99))() +end)() diff --git a/test/ps/src/Golden/Loopification/Test.purs b/test/ps/src/Golden/Loopification/Test.purs new file mode 100644 index 00000000..eab569c3 --- /dev/null +++ b/test/ps/src/Golden/Loopification/Test.purs @@ -0,0 +1,55 @@ +-- | Exercises loopification (issue #181): a self-recursive tail call of +-- | an uncurried worker lowers to a `while true` loop with parameter +-- | reassignment. The eval oracle pins that every shape keeps its +-- | runtime behavior, loopified or not. +module Golden.Loopification.Test where + +import Prelude + +import Effect (Effect) +import Effect.Console (logShow) + +-- Unary self-recursion: no worker/wrapper split happens (manifest arity +-- is 1), yet the binding itself is its own "worker" and loopifies. +countdown :: Int -> Int +countdown n = if n <= 0 then 0 else countdown (n - 1) + +-- The canonical accumulator loop: the binary worker loopifies, and the +-- multiple assignment swaps both parameters simultaneously. +sumTo :: Int -> Int -> Int +sumTo acc n = if n == 0 then acc else sumTo (acc + n) (n - 1) + +-- A local recursive worker: `local go` inside the enclosing function +-- loopifies the same way the top-level ones do. +sumSquares :: Int -> Int +sumSquares m = go 0 m + where + go :: Int -> Int -> Int + go acc n = if n == 0 then acc else go (acc + n * n) (n - 1) + +-- McCarthy 91: the outer self-call is a tail call and becomes a loop +-- iteration; the inner one is an argument and stays a real recursive +-- call. +mc91 :: Int -> Int +mc91 n = if n > 100 then n - 10 else mc91 (mc91 (n + 11)) + +-- The continuation accumulates closures over the loop-carried +-- parameters. Reassigning those parameters would corrupt the captured +-- environments, so this binding must not loopify. +sumCPS :: Int -> (Int -> Int) -> Int +sumCPS n k = if n == 0 then k 0 else sumCPS (n - 1) (\r -> k (r + n)) + +-- The second parameter is dead: it is dropped from the worker while the +-- recursive call still passes a value in its position, so the loop +-- assignment has more values than variables and discards the surplus. +countDrop :: Int -> Int -> Int +countDrop n _ = if n == 0 then 0 else countDrop (n - 1) n + +main :: Effect Unit +main = do + logShow (countdown 5) + logShow (sumTo 0 10) + logShow (sumSquares 4) + logShow (mc91 1) + logShow (sumCPS 5 identity) + logShow (countDrop 3 99) From 2f44a0a4b2bf2660625c6a315df0c5f154b3f322 Mon Sep 17 00:00:00 2001 From: Yura Lazarev Date: Tue, 7 Jul 2026 21:44:29 +0200 Subject: [PATCH 3/3] bench: accept counter goldens for the loopified output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FNEW census moves only by line numbers. The trace report shows the worker's blacklisted function entry (IFUNCF) replaced by a blacklisted loop (ILOOP): the loop shape landed, but its trace still aborts on the per-iteration FNEWs of the curried foreign calls, so the LuaJIT payoff waits for the curried-FFI work (#178, #186). Measured on curried_step (n=2e6, median of the run_macro harness): PUC 0.363s -> 0.348s; LuaJIT 0.196s -> 0.366s — an interim regression on this benchmark until the loop body stops allocating closures. --- bench/goldens/fnew_Bench.CurriedStep.txt | 2 +- bench/goldens/trace_curried_step.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bench/goldens/fnew_Bench.CurriedStep.txt b/bench/goldens/fnew_Bench.CurriedStep.txt index 872f5059..48798ea2 100644 --- a/bench/goldens/fnew_Bench.CurriedStep.txt +++ b/bench/goldens/fnew_Bench.CurriedStep.txt @@ -9,4 +9,4 @@ function-body FNEW sites: Bench.CurriedStep.lua:3 Bench.CurriedStep.lua:7 Bench.CurriedStep.lua:10 - Bench.CurriedStep.lua:21 + Bench.CurriedStep.lua:23 diff --git a/bench/goldens/trace_curried_step.txt b/bench/goldens/trace_curried_step.txt index 7d74555d..b0cd4b09 100644 --- a/bench/goldens/trace_curried_step.txt +++ b/bench/goldens/trace_curried_step.txt @@ -8,7 +8,7 @@ aborts (distinct site -- reason): bytecode end state (J*=compiled, I*=blacklisted): Bench.CurriedStep.lua:10 IFUNCF Bench.CurriedStep.lua:10 JFUNCF - Bench.CurriedStep.lua:15 IFUNCF + Bench.CurriedStep.lua:16 ILOOP Bench.CurriedStep.lua:3 IFUNCF Bench.CurriedStep.lua:3 JFUNCF Bench.CurriedStep.lua:7 IFUNCF