From fdbff1b9d9c4194a0364ffc0c30679e77b5e13c7 Mon Sep 17 00:00:00 2001 From: Lukasz Czajka Date: Thu, 25 Jan 2024 14:40:36 +0100 Subject: [PATCH 1/4] apply --- runtime/src/tree/apply.jvt | 57 +++++++++++++++++++ .../Compiler/Tree/Data/InfoTable/Base.hs | 6 ++ .../Tree/Data/InfoTableBuilder/Base.hs | 29 +++++----- .../Compiler/Tree/Data/TransformationId.hs | 4 +- .../Tree/Data/TransformationId/Strings.hs | 3 + src/Juvix/Compiler/Tree/Extra/Apply.hs | 46 +++++++++++++++ src/Juvix/Compiler/Tree/Language.hs | 4 +- src/Juvix/Compiler/Tree/Transformation.hs | 2 + .../Compiler/Tree/Transformation/Apply.hs | 47 +++++++++++++++ .../Compiler/Tree/Transformation/Base.hs | 2 +- test/Tree/Eval/Positive.hs | 3 + test/Tree/Transformation.hs | 4 +- test/Tree/Transformation/Apply.hs | 33 +++++++++++ 13 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 runtime/src/tree/apply.jvt create mode 100644 src/Juvix/Compiler/Tree/Extra/Apply.hs create mode 100644 src/Juvix/Compiler/Tree/Transformation/Apply.hs create mode 100644 test/Tree/Transformation/Apply.hs diff --git a/runtime/src/tree/apply.jvt b/runtime/src/tree/apply.jvt new file mode 100644 index 0000000000..02763d03de --- /dev/null +++ b/runtime/src/tree/apply.jvt @@ -0,0 +1,57 @@ + +function juvix_apply_1(*, *) : *; +function juvix_apply_2(*, *, *) : *; +function juvix_apply_3(*, *, *, *) : *; +function juvix_apply_4(*, *, *, *, *) : *; + +function juvix_apply_1(*, *) : * { + br(eq(1, argsnum(arg[0]))) { + true: call(arg[0], arg[1]) + false: cextend(arg[0], arg[1]) + } +} + +function juvix_apply_2(*, *, *) : * { + save[n](argsnum(arg[0])) { + br(eq(2, n)) { + true: call(arg[0], arg[1], arg[2]) + false: br(eq(1, n)) { + true: call[juvix_apply_1](call(arg[0], arg[1]), arg[2]) + false: cextend(arg[0], arg[1], arg[2]) + } + } + } +} + +function juvix_apply_3(*, *, *, *) : * { + save[n](argsnum(arg[0])) { + br(eq(3, n)) { + true: call(arg[0], arg[1], arg[2], arg[3]) + false: br(lt(3, n)) { + true: cextend(arg[0], arg[1], arg[2], arg[3]) + false: br(eq(2, n)) { + true: call[juvix_apply_1](call(arg[0], arg[1], arg[2]), arg[3]) + false: call[juvix_apply_2](call(arg[0], arg[1]), arg[2], arg[3]) + } + } + } + } +} + +function juvix_apply_4(*, *, *, *, *) : * { + save[n](argsnum(arg[0])) { + br(eq(4, n)) { + true: call(arg[0], arg[1], arg[2], arg[3], arg[4]) + false: br(lt(4, n)) { + true: cextend(arg[0], arg[1], arg[2], arg[3], arg[4]) + false: br(eq(3, n)) { + true: call[juvix_apply_1](call(arg[0], arg[1], arg[2], arg[3]), arg[4]) + false: br(eq(2, n)) { + true: call[juvix_apply_2](call(arg[0], arg[1], arg[2]), arg[3], arg[4]) + false: call[juvix_apply_3](call(arg[0], arg[1]), arg[2], arg[3], arg[4]) + } + } + } + } + } +} diff --git a/src/Juvix/Compiler/Tree/Data/InfoTable/Base.hs b/src/Juvix/Compiler/Tree/Data/InfoTable/Base.hs index 50bb780f8a..15a73e10bf 100644 --- a/src/Juvix/Compiler/Tree/Data/InfoTable/Base.hs +++ b/src/Juvix/Compiler/Tree/Data/InfoTable/Base.hs @@ -81,3 +81,9 @@ lookupConstrInfo infoTable tag = fromMaybe (error "invalid constructor tag") (Ha lookupInductiveInfo :: InfoTable' a e -> Symbol -> InductiveInfo lookupInductiveInfo infoTable sym = fromMaybe (error "invalid inductive symbol") (HashMap.lookup sym (infoTable ^. infoInductives)) + +getNextSymbolId :: InfoTable' a e -> Word +getNextSymbolId tab = maximum (0 : map (^. symbolId) (HashMap.keys (tab ^. infoFunctions) ++ HashMap.keys (tab ^. infoInductives))) + 1 + +getNextUserTag :: InfoTable' a e -> Word +getNextUserTag tab = maximum (0 : mapMaybe getUserTagId (HashMap.keys (tab ^. infoConstrs))) + 1 diff --git a/src/Juvix/Compiler/Tree/Data/InfoTableBuilder/Base.hs b/src/Juvix/Compiler/Tree/Data/InfoTableBuilder/Base.hs index 98285d8425..bf0cb741d1 100644 --- a/src/Juvix/Compiler/Tree/Data/InfoTableBuilder/Base.hs +++ b/src/Juvix/Compiler/Tree/Data/InfoTableBuilder/Base.hs @@ -47,20 +47,21 @@ emptyBuilderState = _stateIdents = mempty } -runInfoTableBuilderWithTab :: InfoTable' t e -> Sem (InfoTableBuilder' t e ': r) b -> Sem r (InfoTable' t e, b) -runInfoTableBuilderWithTab tab = fmap (first (^. stateInfoTable)) . runInfoTableBuilder' st - where - st = - BuilderState - { _stateNextSymbolId = maximum (0 : map (^. symbolId) (HashMap.keys (tab ^. infoFunctions) ++ HashMap.keys (tab ^. infoInductives))), - _stateNextUserTag = maximum (0 : mapMaybe getUserTagId (HashMap.keys (tab ^. infoConstrs))), - _stateInfoTable = tab, - _stateIdents = - HashMap.fromList $ - map (\fi -> (fi ^. functionName, IdentFun (fi ^. functionSymbol))) (HashMap.elems (tab ^. infoFunctions)) - ++ map (\ii -> (ii ^. inductiveName, IdentInd (ii ^. inductiveSymbol))) (HashMap.elems (tab ^. infoInductives)) - ++ map (\ci -> (ci ^. constructorName, IdentConstr (ci ^. constructorTag))) (HashMap.elems (tab ^. infoConstrs)) - } +builderStateFromInfoTable :: InfoTable' t e -> BuilderState' t e +builderStateFromInfoTable tab = + BuilderState + { _stateNextSymbolId = getNextSymbolId tab, + _stateNextUserTag = getNextUserTag tab, + _stateInfoTable = tab, + _stateIdents = + HashMap.fromList $ + map (\fi -> (fi ^. functionName, IdentFun (fi ^. functionSymbol))) (HashMap.elems (tab ^. infoFunctions)) + ++ map (\ii -> (ii ^. inductiveName, IdentInd (ii ^. inductiveSymbol))) (HashMap.elems (tab ^. infoInductives)) + ++ map (\ci -> (ci ^. constructorName, IdentConstr (ci ^. constructorTag))) (HashMap.elems (tab ^. infoConstrs)) + } + +runInfoTableBuilderWithInfoTable :: InfoTable' t e -> Sem (InfoTableBuilder' t e ': r) b -> Sem r (InfoTable' t e, b) +runInfoTableBuilderWithInfoTable tab = fmap (first (^. stateInfoTable)) . runInfoTableBuilder' (builderStateFromInfoTable tab) runInfoTableBuilder :: Sem (InfoTableBuilder' t e ': r) b -> Sem r (InfoTable' t e, b) runInfoTableBuilder = fmap (first (^. stateInfoTable)) . runInfoTableBuilder' emptyBuilderState diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId.hs b/src/Juvix/Compiler/Tree/Data/TransformationId.hs index 59b5ae1dc4..ba3142298d 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId.hs @@ -8,6 +8,7 @@ data TransformationId = Identity | IdentityU | IdentityD + | Apply deriving stock (Data, Bounded, Enum, Show) data PipelineId @@ -18,7 +19,7 @@ data PipelineId type TransformationLikeId = TransformationLikeId' TransformationId PipelineId toNockTransformations :: [TransformationId] -toNockTransformations = [] +toNockTransformations = [Apply] toAsmTransformations :: [TransformationId] toAsmTransformations = [] @@ -29,6 +30,7 @@ instance TransformationId' TransformationId where Identity -> strIdentity IdentityU -> strIdentityU IdentityD -> strIdentityD + Apply -> strApply instance PipelineId' TransformationId PipelineId where pipelineText :: PipelineId -> Text diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs index 3a462d6cd1..344a097cf4 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs @@ -16,3 +16,6 @@ strIdentityU = "identity-umap" strIdentityD :: Text strIdentityD = "identity-dmap" + +strApply :: Text +strApply = "apply" diff --git a/src/Juvix/Compiler/Tree/Extra/Apply.hs b/src/Juvix/Compiler/Tree/Extra/Apply.hs new file mode 100644 index 0000000000..abc5463831 --- /dev/null +++ b/src/Juvix/Compiler/Tree/Extra/Apply.hs @@ -0,0 +1,46 @@ +module Juvix.Compiler.Tree.Extra.Apply where + +import Data.FileEmbed qualified as FE +import Data.HashMap.Strict qualified as HashMap +import Data.Text.Encoding +import Juvix.Compiler.Tree.Data.InfoTable +import Juvix.Compiler.Tree.Data.InfoTableBuilder +import Juvix.Compiler.Tree.Language +import Juvix.Compiler.Tree.Translation.FromSource + +data ApplyBuiltins = ApplyBuiltins + { -- | The number of `juvix_apply_n` functions. + _applyBuiltinsNum :: Int, + -- | Maps `n` to the function `juvix_apply_n`. + _applyBuiltinsMap :: HashMap Int Symbol + } + +makeLenses ''ApplyBuiltins + +addApplyBuiltins :: InfoTable -> (ApplyBuiltins, InfoTable) +addApplyBuiltins tab = (blts, bs' ^. stateInfoTable) + where + bs :: BuilderState + bs = builderStateFromInfoTable tab + + bs' :: BuilderState + bs' = + fromRight impossible $ + parseText' bs $ + decodeUtf8 $(FE.makeRelativeToProject "runtime/src/tree/apply.jvt" >>= FE.embedFile) + + blts :: ApplyBuiltins + blts = + ApplyBuiltins + { _applyBuiltinsNum = 4, + _applyBuiltinsMap = + HashMap.fromList $ map mkApply [1 .. 4] + } + + mkApply :: Int -> (Int, Symbol) + mkApply x = (x, f) + where + idt = "juvix_apply_" <> show x + f = case fromJust $ HashMap.lookup idt (bs' ^. stateIdents) of + IdentFun s -> s + _ -> impossible diff --git a/src/Juvix/Compiler/Tree/Language.hs b/src/Juvix/Compiler/Tree/Language.hs index 210d28742b..296615de42 100644 --- a/src/Juvix/Compiler/Tree/Language.hs +++ b/src/Juvix/Compiler/Tree/Language.hs @@ -41,10 +41,12 @@ data Node -- (any branch may be omitted). Case NodeCase | -- | Execute nested code with temporary stack extended with a given value. - -- Used to implement Core.Let and Core.Case. JVT codes: 'save(x) {}', + -- Used to implement Core.Let. JVT codes: 'save(x) {}', -- 'save[](x) {}'. Save NodeSave +-- TODO: CallClosures arguments should be non-empty + data BinaryOpcode = IntAdd | IntSub diff --git a/src/Juvix/Compiler/Tree/Transformation.hs b/src/Juvix/Compiler/Tree/Transformation.hs index 4f338bc9a3..d4a30f0a76 100644 --- a/src/Juvix/Compiler/Tree/Transformation.hs +++ b/src/Juvix/Compiler/Tree/Transformation.hs @@ -6,6 +6,7 @@ module Juvix.Compiler.Tree.Transformation where import Juvix.Compiler.Tree.Data.TransformationId +import Juvix.Compiler.Tree.Transformation.Apply import Juvix.Compiler.Tree.Transformation.Base import Juvix.Compiler.Tree.Transformation.Identity @@ -17,3 +18,4 @@ applyTransformations ts tbl = foldM (flip appTrans) tbl ts Identity -> return . identity IdentityU -> return . identityU IdentityD -> return . identityD + Apply -> return . computeApply diff --git a/src/Juvix/Compiler/Tree/Transformation/Apply.hs b/src/Juvix/Compiler/Tree/Transformation/Apply.hs new file mode 100644 index 0000000000..b71d8f560f --- /dev/null +++ b/src/Juvix/Compiler/Tree/Transformation/Apply.hs @@ -0,0 +1,47 @@ +module Juvix.Compiler.Tree.Transformation.Apply where + +import Data.HashMap.Strict qualified as HashMap +import Juvix.Compiler.Tree.Extra.Apply +import Juvix.Compiler.Tree.Extra.Recursors +import Juvix.Compiler.Tree.Transformation.Base + +computeFunctionApply :: ApplyBuiltins -> Node -> Node +computeFunctionApply blts = umap go + where + go :: Node -> Node + go = \case + CallClosures NodeCallClosures {..} -> goApply _nodeCallClosuresFun _nodeCallClosuresArgs + node -> node + + -- TODO: NonEmpty instead of list + goApply :: Node -> [Node] -> Node + goApply cl args + | n <= m = mkApply cl args + | otherwise = goApply (mkApply cl (take m args)) (drop m args) + where + n = length args + m = blts ^. applyBuiltinsNum + + mkApply :: Node -> [Node] -> Node + mkApply cl args = + Call + NodeCall + { _nodeCallType = CallFun sym, + _nodeCallArgs = cl : args + } + where + sym = fromJust $ HashMap.lookup (length args) (blts ^. applyBuiltinsMap) + +computeApply :: InfoTable -> InfoTable +computeApply tab = mapT (const (computeFunctionApply blts)) tab' + where + (blts, tab') = addApplyBuiltins tab + +checkNoCallClosures :: InfoTable -> Bool +checkNoCallClosures tab = + all (ufold (\b bs -> b && and bs) go . (^. functionCode)) (tab ^. infoFunctions) + where + go :: Node -> Bool + go = \case + CallClosures {} -> False + _ -> True diff --git a/src/Juvix/Compiler/Tree/Transformation/Base.hs b/src/Juvix/Compiler/Tree/Transformation/Base.hs index f73e4002f7..ba6e4475a5 100644 --- a/src/Juvix/Compiler/Tree/Transformation/Base.hs +++ b/src/Juvix/Compiler/Tree/Transformation/Base.hs @@ -34,7 +34,7 @@ mapT f = over infoFunctions (HashMap.mapWithKey (over functionCode . f)) mapT' :: (Symbol -> Node -> Sem (InfoTableBuilder ': r) Node) -> InfoTable -> Sem r InfoTable mapT' f tab = fmap fst $ - runInfoTableBuilderWithTab tab $ + runInfoTableBuilderWithInfoTable tab $ mapM_ (\(sym, fi) -> overM functionCode (f sym) fi >>= registerFunction) (HashMap.toList (tab ^. infoFunctions)) diff --git a/test/Tree/Eval/Positive.hs b/test/Tree/Eval/Positive.hs index 49ed936317..8cf0797ed3 100644 --- a/test/Tree/Eval/Positive.hs +++ b/test/Tree/Eval/Positive.hs @@ -26,6 +26,9 @@ testDescr PosTest {..} = _testAssertion = Steps $ treeEvalAssertion file' expected' [] (const (return ())) } +filterTests :: [String] -> [PosTest] -> [PosTest] +filterTests names = filter (\PosTest {..} -> _name `elem` names) + filterOutTests :: [String] -> [PosTest] -> [PosTest] filterOutTests out = filter (\PosTest {..} -> _name `notElem` out) diff --git a/test/Tree/Transformation.hs b/test/Tree/Transformation.hs index bd056da0c4..bc8ea92b31 100644 --- a/test/Tree/Transformation.hs +++ b/test/Tree/Transformation.hs @@ -1,11 +1,13 @@ module Tree.Transformation where import Base +import Tree.Transformation.Apply qualified as Apply import Tree.Transformation.Identity qualified as Identity allTests :: TestTree allTests = testGroup "JuvixTree transformations" - [ Identity.allTests + [ Identity.allTests, + Apply.allTests ] diff --git a/test/Tree/Transformation/Apply.hs b/test/Tree/Transformation/Apply.hs new file mode 100644 index 0000000000..8aa9d16e53 --- /dev/null +++ b/test/Tree/Transformation/Apply.hs @@ -0,0 +1,33 @@ +module Tree.Transformation.Apply (allTests) where + +import Base +import Juvix.Compiler.Tree.Transformation +import Juvix.Compiler.Tree.Transformation.Apply (checkNoCallClosures) +import Tree.Eval.Positive qualified as Eval +import Tree.Transformation.Base + +allTests :: TestTree +allTests = + testGroup + "Apply" + ( map liftTest $ + Eval.filterTests + [ "Test007: Higher-order functions", + "Test022: Self-application", + "Test025: Dynamic closure extension", + "Test032: Church numerals" + ] + Eval.tests + ) + +pipe :: [TransformationId] +pipe = [Apply] + +liftTest :: Eval.PosTest -> TestTree +liftTest _testEval = + fromTest + Test + { _testTransformations = pipe, + _testAssertion = assertBool "check no CallClosures" . checkNoCallClosures, + _testEval + } From 582ad7da8b992e2d62598908c8b2288dea29b3a1 Mon Sep 17 00:00:00 2001 From: Lukasz Czajka Date: Thu, 25 Jan 2024 14:57:21 +0100 Subject: [PATCH 2/4] require at least 1 argument supplied to ccall --- src/Juvix/Compiler/Asm/Extra/Recursors.hs | 3 +++ src/Juvix/Compiler/Asm/Language.hs | 6 +++--- src/Juvix/Compiler/Asm/Translation/FromTree.hs | 2 +- src/Juvix/Compiler/Core/Extra/Stripped/Base.hs | 2 +- src/Juvix/Compiler/Core/Language/Nodes.hs | 2 +- .../Compiler/Core/Translation/Stripped/FromCore.hs | 4 ++-- src/Juvix/Compiler/Tree/Evaluator.hs | 4 ++-- src/Juvix/Compiler/Tree/Extra/Base.hs | 4 ++-- src/Juvix/Compiler/Tree/Language.hs | 4 +--- src/Juvix/Compiler/Tree/Pretty/Base.hs | 2 +- src/Juvix/Compiler/Tree/Transformation/Apply.hs | 10 +++++----- src/Juvix/Compiler/Tree/Translation/FromAsm.hs | 2 +- src/Juvix/Compiler/Tree/Translation/FromCore.hs | 7 ++++--- src/Juvix/Compiler/Tree/Translation/FromSource.hs | 6 ++++-- 14 files changed, 31 insertions(+), 27 deletions(-) diff --git a/src/Juvix/Compiler/Asm/Extra/Recursors.hs b/src/Juvix/Compiler/Asm/Extra/Recursors.hs index 99a6206694..3bf2550030 100644 --- a/src/Juvix/Compiler/Asm/Extra/Recursors.hs +++ b/src/Juvix/Compiler/Asm/Extra/Recursors.hs @@ -197,6 +197,9 @@ recurse' sig = go True fixMemCallClosures :: Memory -> InstrCallClosures -> Sem r Memory fixMemCallClosures mem InstrCallClosures {..} = do + when (_callClosuresArgsNum < 1) $ + throw $ + AsmError loc "invalid closure call: expected at least one supplied argument" when (null (mem ^. memoryValueStack)) $ throw $ AsmError loc "invalid closure call: value stack is empty" diff --git a/src/Juvix/Compiler/Asm/Language.hs b/src/Juvix/Compiler/Asm/Language.hs index e0a456a8b1..52254d43e0 100644 --- a/src/Juvix/Compiler/Asm/Language.hs +++ b/src/Juvix/Compiler/Asm/Language.hs @@ -163,13 +163,13 @@ newtype InstrExtendClosure = InstrExtendClosure data InstrCall = InstrCall { _callType :: CallType, - -- | The number of arguments supplied to the call. + -- | The number of arguments supplied to the call. Can be 0. _callArgsNum :: Int } newtype InstrCallClosures = InstrCallClosures - { -- | The number of arguments supplied to the call. This does not include the - -- called closure on top of the stack. + { -- | The number of arguments supplied to the call. Should be greater than 0. + -- This does not include the called closure on top of the stack. _callClosuresArgsNum :: Int } diff --git a/src/Juvix/Compiler/Asm/Translation/FromTree.hs b/src/Juvix/Compiler/Asm/Translation/FromTree.hs index 7b63a4ee7a..65046e645e 100644 --- a/src/Juvix/Compiler/Asm/Translation/FromTree.hs +++ b/src/Juvix/Compiler/Asm/Translation/FromTree.hs @@ -148,7 +148,7 @@ genCode fi = goCallClosures :: Bool -> Tree.NodeCallClosures -> Code' goCallClosures isTail Tree.NodeCallClosures {..} = DL.append - (goArgs _nodeCallClosuresArgs) + (goArgs (toList _nodeCallClosuresArgs)) ( DL.snoc (go False _nodeCallClosuresFun) $ mkInstr $ (if isTail then TailCallClosures else CallClosures) $ diff --git a/src/Juvix/Compiler/Core/Extra/Stripped/Base.hs b/src/Juvix/Compiler/Core/Extra/Stripped/Base.hs index 34b30a48d6..52d4437062 100644 --- a/src/Juvix/Compiler/Core/Extra/Stripped/Base.hs +++ b/src/Juvix/Compiler/Core/Extra/Stripped/Base.hs @@ -20,7 +20,7 @@ mkIdent' = mkIdent (IdentInfo "" Nothing TyDynamic) mkConstant :: ConstantValue -> Node mkConstant cv = NCst (Constant () cv) -mkApps :: Fun -> [Node] -> Node +mkApps :: Fun -> NonEmpty Node -> Node mkApps l r = NApp (Apps () l r) mkBuiltinApp :: BuiltinOp -> [Node] -> Node diff --git a/src/Juvix/Compiler/Core/Language/Nodes.hs b/src/Juvix/Compiler/Core/Language/Nodes.hs index 33751e37b6..2dec75ed0a 100644 --- a/src/Juvix/Compiler/Core/Language/Nodes.hs +++ b/src/Juvix/Compiler/Core/Language/Nodes.hs @@ -59,7 +59,7 @@ data App' i a = App data Apps' i f a = Apps { _appsInfo :: i, _appsFun :: !f, - _appsArgs :: ![a] + _appsArgs :: !(NonEmpty a) } deriving stock (Generic) diff --git a/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs b/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs index 2a0f238fba..dfc4a88827 100644 --- a/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs +++ b/src/Juvix/Compiler/Core/Translation/Stripped/FromCore.hs @@ -175,8 +175,8 @@ translateNode node = case node of let (tgt, args) = unfoldApps' node args' = map translateNode args in case tgt of - NVar v -> Stripped.mkApps (Stripped.FunVar $ translateVar v) args' - NIdt idt -> Stripped.mkApps (Stripped.FunIdent $ translateIdent idt) args' + NVar v -> Stripped.mkApps (Stripped.FunVar $ translateVar v) (nonEmpty' args') + NIdt idt -> Stripped.mkApps (Stripped.FunIdent $ translateIdent idt) (nonEmpty' args') _ -> unsupported NBlt BuiltinApp {..} -> Stripped.mkBuiltinApp _builtinAppOp (map translateNode _builtinAppArgs) diff --git a/src/Juvix/Compiler/Tree/Evaluator.hs b/src/Juvix/Compiler/Tree/Evaluator.hs index b4a7ddf170..0ca20a32be 100644 --- a/src/Juvix/Compiler/Tree/Evaluator.hs +++ b/src/Juvix/Compiler/Tree/Evaluator.hs @@ -207,7 +207,7 @@ hEval hout tab = eval' [] mempty goCallClosures :: NodeCallClosures -> Value goCallClosures NodeCallClosures {..} = - let !vs = map' (eval' args temps) _nodeCallClosuresArgs + let !vs = map' (eval' args temps) (toList _nodeCallClosuresArgs) in go (eval' args temps _nodeCallClosuresFun) vs where go :: Value -> [Value] -> Value @@ -299,7 +299,7 @@ hRunIO hin hout infoTable = \case CallClosures NodeCallClosures { _nodeCallClosuresFun = valueToNode f, - _nodeCallClosuresArgs = [valueToNode x'] + _nodeCallClosuresArgs = valueToNode x' :| [] } !x'' = hEval hout infoTable code hRunIO hin hout infoTable x'' diff --git a/src/Juvix/Compiler/Tree/Extra/Base.hs b/src/Juvix/Compiler/Tree/Extra/Base.hs index a5911d73d1..5809e71142 100644 --- a/src/Juvix/Compiler/Tree/Extra/Base.hs +++ b/src/Juvix/Compiler/Tree/Extra/Base.hs @@ -177,11 +177,11 @@ destruct = \case } CallClosures NodeCallClosures {..} -> NodeDetails - { _nodeChildren = map noTempVar (_nodeCallClosuresFun : _nodeCallClosuresArgs), + { _nodeChildren = map noTempVar (_nodeCallClosuresFun : toList _nodeCallClosuresArgs), _nodeReassemble = someChildren $ \(arg :| args) -> CallClosures NodeCallClosures - { _nodeCallClosuresArgs = args, + { _nodeCallClosuresArgs = nonEmpty' args, _nodeCallClosuresFun = arg } } diff --git a/src/Juvix/Compiler/Tree/Language.hs b/src/Juvix/Compiler/Tree/Language.hs index 296615de42..4461853ca0 100644 --- a/src/Juvix/Compiler/Tree/Language.hs +++ b/src/Juvix/Compiler/Tree/Language.hs @@ -45,8 +45,6 @@ data Node -- 'save[](x) {}'. Save NodeSave --- TODO: CallClosures arguments should be non-empty - data BinaryOpcode = IntAdd | IntSub @@ -108,7 +106,7 @@ data NodeCall = NodeCall data NodeCallClosures = NodeCallClosures { _nodeCallClosuresFun :: Node, - _nodeCallClosuresArgs :: [Node] + _nodeCallClosuresArgs :: NonEmpty Node } data NodeBranch = NodeBranch diff --git a/src/Juvix/Compiler/Tree/Pretty/Base.hs b/src/Juvix/Compiler/Tree/Pretty/Base.hs index a77a7ccc7d..4373273905 100644 --- a/src/Juvix/Compiler/Tree/Pretty/Base.hs +++ b/src/Juvix/Compiler/Tree/Pretty/Base.hs @@ -280,7 +280,7 @@ instance PrettyCode NodeCall where instance PrettyCode NodeCallClosures where ppCode NodeCallClosures {..} = do - args <- ppCodeArgs (_nodeCallClosuresFun : _nodeCallClosuresArgs) + args <- ppCodeArgs (_nodeCallClosuresFun : toList _nodeCallClosuresArgs) return $ primitive Str.instrCcall <> parens args instance PrettyCode NodeBranch where diff --git a/src/Juvix/Compiler/Tree/Transformation/Apply.hs b/src/Juvix/Compiler/Tree/Transformation/Apply.hs index b71d8f560f..6a4e6f29c1 100644 --- a/src/Juvix/Compiler/Tree/Transformation/Apply.hs +++ b/src/Juvix/Compiler/Tree/Transformation/Apply.hs @@ -13,21 +13,21 @@ computeFunctionApply blts = umap go CallClosures NodeCallClosures {..} -> goApply _nodeCallClosuresFun _nodeCallClosuresArgs node -> node - -- TODO: NonEmpty instead of list - goApply :: Node -> [Node] -> Node + goApply :: Node -> NonEmpty Node -> Node goApply cl args | n <= m = mkApply cl args - | otherwise = goApply (mkApply cl (take m args)) (drop m args) + | otherwise = goApply (mkApply cl (nonEmpty' $ take m args')) (nonEmpty' $ drop m args') where + args' = toList args n = length args m = blts ^. applyBuiltinsNum - mkApply :: Node -> [Node] -> Node + mkApply :: Node -> NonEmpty Node -> Node mkApply cl args = Call NodeCall { _nodeCallType = CallFun sym, - _nodeCallArgs = cl : args + _nodeCallArgs = cl : toList args } where sym = fromJust $ HashMap.lookup (length args) (blts ^. applyBuiltinsMap) diff --git a/src/Juvix/Compiler/Tree/Translation/FromAsm.hs b/src/Juvix/Compiler/Tree/Translation/FromAsm.hs index ecb060fe7e..b53f456305 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromAsm.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromAsm.hs @@ -322,7 +322,7 @@ goFunction infoTab fi = do CallClosures NodeCallClosures { _nodeCallClosuresFun = cl, - _nodeCallClosuresArgs = args + _nodeCallClosuresArgs = nonEmpty' args } pushTempStack :: Sem r a -> Sem r a diff --git a/src/Juvix/Compiler/Tree/Translation/FromCore.hs b/src/Juvix/Compiler/Tree/Translation/FromCore.hs index aceb44b120..9817d2f435 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromCore.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromCore.hs @@ -82,7 +82,8 @@ genCode infoTable fi = goApps :: Int -> BinderList MemRef -> Core.Apps -> Node goApps tempSize refs Core.Apps {..} = - let suppliedArgs = map (go tempSize refs) _appsArgs + let suppliedArgs' = fmap (go tempSize refs) _appsArgs + suppliedArgs = toList suppliedArgs' suppliedArgsNum = length suppliedArgs in case _appsFun of Core.FunIdent Core.Ident {..} -> @@ -113,7 +114,7 @@ genCode infoTable fi = { _nodeCallType = CallFun _identSymbol, _nodeCallArgs = take argsNum suppliedArgs }, - _nodeCallClosuresArgs = drop argsNum suppliedArgs + _nodeCallClosuresArgs = nonEmpty' $ drop argsNum suppliedArgs } where argsNum = getArgsNum _identSymbol @@ -121,7 +122,7 @@ genCode infoTable fi = CallClosures $ NodeCallClosures { _nodeCallClosuresFun = MemRef $ BL.lookup _varIndex refs, - _nodeCallClosuresArgs = suppliedArgs + _nodeCallClosuresArgs = suppliedArgs' } goBuiltinApp :: Int -> BinderList MemRef -> Core.BuiltinApp -> Node diff --git a/src/Juvix/Compiler/Tree/Translation/FromSource.hs b/src/Juvix/Compiler/Tree/Translation/FromSource.hs index c1ccf7722e..06ba23447d 100644 --- a/src/Juvix/Compiler/Tree/Translation/FromSource.hs +++ b/src/Juvix/Compiler/Tree/Translation/FromSource.hs @@ -197,14 +197,16 @@ parseCCall = do off <- P.getOffset args <- parseArgs case args of + [_] -> + parseFailure off "expected at least two arguments" arg : args' -> return NodeCallClosures { _nodeCallClosuresFun = arg, - _nodeCallClosuresArgs = args' + _nodeCallClosuresArgs = nonEmpty' args' } [] -> - parseFailure off "expected at least one argument" + parseFailure off "expected at least two arguments" parseBranch :: (Members '[Reader ParserSig, InfoTableBuilder, State LocalParams] r) => From 50f12523f14671938fc53dc35d6f8b7d960fd737 Mon Sep 17 00:00:00 2001 From: Lukasz Czajka Date: Thu, 25 Jan 2024 15:51:27 +0100 Subject: [PATCH 3/4] pipeline --- src/Juvix/Compiler/Pipeline.hs | 4 ++-- src/Juvix/Compiler/Tree.hs | 2 ++ src/Juvix/Compiler/Tree/Data/TransformationId.hs | 10 +++++----- .../Compiler/Tree/Data/TransformationId/Strings.hs | 4 ++-- src/Juvix/Compiler/Tree/Pipeline.hs | 14 ++++++++++++++ 5 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 src/Juvix/Compiler/Tree/Pipeline.hs diff --git a/src/Juvix/Compiler/Pipeline.hs b/src/Juvix/Compiler/Pipeline.hs index 05de506ba6..15b6a8c635 100644 --- a/src/Juvix/Compiler/Pipeline.hs +++ b/src/Juvix/Compiler/Pipeline.hs @@ -177,10 +177,10 @@ coreToVampIR' = Core.toStored' >=> storedCoreToVampIR' -------------------------------------------------------------------------------- treeToAsm :: Tree.InfoTable -> Sem r Asm.InfoTable -treeToAsm = return . Asm.fromTree +treeToAsm = Tree.toAsm >=> return . Asm.fromTree treeToNockma :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural) -treeToNockma = treeToAsm >=> asmToNockma +treeToNockma = Tree.toNockma >=> treeToAsm >=> asmToNockma treeToMiniC :: (Members '[Error JuvixError, Reader EntryPoint] r) => Tree.InfoTable -> Sem r C.MiniCResult treeToMiniC = treeToAsm >=> asmToMiniC diff --git a/src/Juvix/Compiler/Tree.hs b/src/Juvix/Compiler/Tree.hs index 807cba1932..baffc910f6 100644 --- a/src/Juvix/Compiler/Tree.hs +++ b/src/Juvix/Compiler/Tree.hs @@ -2,9 +2,11 @@ module Juvix.Compiler.Tree ( module Juvix.Compiler.Tree.Language, module Juvix.Compiler.Tree.Data.InfoTable, module Juvix.Compiler.Tree.Translation.FromCore, + module Juvix.Compiler.Tree.Pipeline, ) where import Juvix.Compiler.Tree.Data.InfoTable import Juvix.Compiler.Tree.Language +import Juvix.Compiler.Tree.Pipeline import Juvix.Compiler.Tree.Translation.FromCore diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId.hs b/src/Juvix/Compiler/Tree/Data/TransformationId.hs index ba3142298d..2e1b139721 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId.hs @@ -12,14 +12,14 @@ data TransformationId deriving stock (Data, Bounded, Enum, Show) data PipelineId - = PipelineNock + = PipelineNockma | PipelineAsm deriving stock (Data, Bounded, Enum) type TransformationLikeId = TransformationLikeId' TransformationId PipelineId -toNockTransformations :: [TransformationId] -toNockTransformations = [Apply] +toNockmaTransformations :: [TransformationId] +toNockmaTransformations = [Apply] toAsmTransformations :: [TransformationId] toAsmTransformations = [] @@ -35,10 +35,10 @@ instance TransformationId' TransformationId where instance PipelineId' TransformationId PipelineId where pipelineText :: PipelineId -> Text pipelineText = \case - PipelineNock -> strNockPipeline + PipelineNockma -> strNockmaPipeline PipelineAsm -> strAsmPipeline pipeline :: PipelineId -> [TransformationId] pipeline = \case - PipelineNock -> toNockTransformations + PipelineNockma -> toNockmaTransformations PipelineAsm -> toAsmTransformations diff --git a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs index 344a097cf4..fa473dc2ec 100644 --- a/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs +++ b/src/Juvix/Compiler/Tree/Data/TransformationId/Strings.hs @@ -2,8 +2,8 @@ module Juvix.Compiler.Tree.Data.TransformationId.Strings where import Juvix.Prelude -strNockPipeline :: Text -strNockPipeline = "pipeline-nock" +strNockmaPipeline :: Text +strNockmaPipeline = "pipeline-nockma" strAsmPipeline :: Text strAsmPipeline = "pipeline-asm" diff --git a/src/Juvix/Compiler/Tree/Pipeline.hs b/src/Juvix/Compiler/Tree/Pipeline.hs new file mode 100644 index 0000000000..001b92bc50 --- /dev/null +++ b/src/Juvix/Compiler/Tree/Pipeline.hs @@ -0,0 +1,14 @@ +module Juvix.Compiler.Tree.Pipeline + ( module Juvix.Compiler.Tree.Pipeline, + module Juvix.Compiler.Tree.Data.InfoTable, + ) +where + +import Juvix.Compiler.Tree.Data.InfoTable +import Juvix.Compiler.Tree.Transformation + +toNockma :: InfoTable -> Sem r InfoTable +toNockma = applyTransformations toNockmaTransformations + +toAsm :: InfoTable -> Sem r InfoTable +toAsm = applyTransformations toAsmTransformations From 564d70dd88d500dca56e8f2de40925bafc744ca5 Mon Sep 17 00:00:00 2001 From: Lukasz Czajka Date: Thu, 25 Jan 2024 16:27:07 +0100 Subject: [PATCH 4/4] remove asm/apply --- app/Commands/Dev/Nockma/Eval.hs | 1 + cntlines.sh | 8 +- runtime/src/asm/apply.jva | 193 ------------------ src/Juvix/Compiler/Asm/Extra/Apply.hs | 55 ----- src/Juvix/Compiler/Asm/Pipeline.hs | 2 +- src/Juvix/Compiler/Asm/Transformation.hs | 2 - .../Compiler/Asm/Transformation/Apply.hs | 104 ---------- src/Juvix/Compiler/Nockma/EvalCompiled.hs | 34 +++ .../Compiler/Nockma/Translation/FromAsm.hs | 34 --- src/Juvix/Compiler/Pipeline.hs | 3 + test/Asm/Transformation.hs | 3 +- test/Asm/Transformation/Apply.hs | 35 ---- test/Nockma/Compile.hs | 4 +- test/Nockma/Compile/Positive.hs | 1 + test/Nockma/Compile/{Asm => Tree}/Positive.hs | 29 +-- test/Tree/Eval/Base.hs | 14 +- 16 files changed, 74 insertions(+), 448 deletions(-) delete mode 100644 runtime/src/asm/apply.jva delete mode 100644 src/Juvix/Compiler/Asm/Extra/Apply.hs delete mode 100644 src/Juvix/Compiler/Asm/Transformation/Apply.hs create mode 100644 src/Juvix/Compiler/Nockma/EvalCompiled.hs delete mode 100644 test/Asm/Transformation/Apply.hs rename test/Nockma/Compile/{Asm => Tree}/Positive.hs (74%) diff --git a/app/Commands/Dev/Nockma/Eval.hs b/app/Commands/Dev/Nockma/Eval.hs index 3bd4a2774e..dc62357c0a 100644 --- a/app/Commands/Dev/Nockma/Eval.hs +++ b/app/Commands/Dev/Nockma/Eval.hs @@ -2,6 +2,7 @@ module Commands.Dev.Nockma.Eval where import Commands.Base hiding (Atom) import Commands.Dev.Nockma.Eval.Options +import Juvix.Compiler.Nockma.EvalCompiled import Juvix.Compiler.Nockma.Evaluator.Options import Juvix.Compiler.Nockma.Pretty import Juvix.Compiler.Nockma.Translation.FromAsm diff --git a/cntlines.sh b/cntlines.sh index 40c29236ae..ea310b9e20 100755 --- a/cntlines.sh +++ b/cntlines.sh @@ -10,9 +10,9 @@ function count_ext () { RUNTIME_C=$(count runtime/src/juvix) RUNTIME_VAMPIR=$(count_ext '*.pir' runtime/src/vampir) -RUNTIME_JVA=$(count_ext '*.jva' runtime/src/asm) +RUNTIME_JVT=$(count_ext '*.jvt' runtime/src/tree) -RUNTIME=$((RUNTIME_C+RUNTIME_VAMPIR+RUNTIME_JVA)) +RUNTIME=$((RUNTIME_C+RUNTIME_VAMPIR+RUNTIME_JVT)) BACKENDC=$(count src/Juvix/Compiler/Backend/C/) CAIRO=$(count src/Juvix/Compiler/Backend/Cairo/) @@ -61,7 +61,7 @@ echo " JuvixTree: $TREE LOC" echo " JuvixCore: $CORE LOC" echo "Runtime: $RUNTIME LOC" echo " C runtime: $RUNTIME_C LOC" -echo " JuvixAsm runtime: $RUNTIME_JVA LOC" +echo " JuvixTree runtime: $RUNTIME_JVT LOC" echo " VampIR runtime: $RUNTIME_VAMPIR LOC" echo "Other: $OTHER LOC" echo " Application: $APP LOC" @@ -72,4 +72,4 @@ echo " Data: $DATA LOC" echo " Prelude: $PRELUDE LOC" echo "Tests: $TESTS LOC" echo "" -echo "Total: $TOTAL Haskell LOC + $RUNTIME_C C LOC + $RUNTIME_JVA JuvixAsm LOC + $RUNTIME_VAMPIR VampIR LOC" +echo "Total: $TOTAL Haskell LOC + $RUNTIME_C C LOC + $RUNTIME_JVT JuvixTree LOC + $RUNTIME_VAMPIR VampIR LOC" diff --git a/runtime/src/asm/apply.jva b/runtime/src/asm/apply.jva deleted file mode 100644 index 9c7588cdfd..0000000000 --- a/runtime/src/asm/apply.jva +++ /dev/null @@ -1,193 +0,0 @@ - -function juvix_apply_1(*, *) : * { - push arg[0]; - argsnum; - push 1; - eq; - br { - true: { -- argsnum = 1 - push arg[1]; - push arg[0]; - tcall $ 1; - }; - false: { -- argsnum > 1 - push arg[1]; - push arg[0]; - cextend 1; - ret; - }; - }; -} - -function juvix_apply_2(*, *, *) : * { - push arg[0]; - argsnum; - tsave n { - push n; - push 2; - eq; - br { - true: { -- argsnum = 2 - push arg[2]; - push arg[1]; - push arg[0]; - tcall $ 2; - }; - false: { - push n; - push 1; - eq; - br { - true: { -- argsnum = 1 - push arg[2]; - push arg[1]; - push arg[0]; - call $ 1; - tcall juvix_apply_1; - }; - false: { -- argsnum > 2 - push arg[2]; - push arg[1]; - push arg[0]; - cextend 2; - ret; - }; - }; - }; - }; - }; -} - -function juvix_apply_3(*, *, *, *) : * { - push arg[0]; - argsnum; - tsave n { - push n; - push 3; - eq; - br { - true: { -- argsnum = 3 - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - tcall $ 3; - }; - false: { - push n; - push 3; - lt; - br { - true: { -- argsnum > 3 - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - cextend 3; - ret; - }; - false: { -- argsnum <= 2 - push n; - push 2; - eq; - br { - true: { -- argsnum = 2 - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - call $ 2; - tcall juvix_apply_1; - }; - false: { -- argsnum = 1 - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - call $ 1; - tcall juvix_apply_2; - }; - }; - }; - }; - }; - }; - }; -} - -function juvix_apply_4(*, *, *, *, *) : * { - push arg[0]; - argsnum; - tsave n { - push n; - push 4; - eq; - br { - true: { -- argsnum = 4 - push arg[4]; - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - tcall $ 4; - }; - false: { - push n; - push 4; - lt; - br { - true: { -- argsnum > 4 - push arg[4]; - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - cextend 4; - ret; - }; - false: { -- argsnum <= 3 - push n; - push 3; - eq; - br { - true: { -- argsnum = 3 - push arg[4]; - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - call $ 3; - tcall juvix_apply_1; - }; - false: { - push n; - push 2; - eq; - br { - true: { -- argsnum = 2 - push arg[4]; - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - call $ 2; - tcall juvix_apply_2; - }; - false: { -- argsnum = 1 - push arg[4]; - push arg[3]; - push arg[2]; - push arg[1]; - push arg[0]; - call $ 1; - tcall juvix_apply_3; - }; - }; - }; - }; - }; - }; - }; - }; - }; -} diff --git a/src/Juvix/Compiler/Asm/Extra/Apply.hs b/src/Juvix/Compiler/Asm/Extra/Apply.hs deleted file mode 100644 index 840867487d..0000000000 --- a/src/Juvix/Compiler/Asm/Extra/Apply.hs +++ /dev/null @@ -1,55 +0,0 @@ -module Juvix.Compiler.Asm.Extra.Apply where - -import Data.FileEmbed qualified as FE -import Data.HashMap.Strict qualified as HashMap -import Data.Text.Encoding -import Juvix.Compiler.Asm.Data.InfoTable -import Juvix.Compiler.Asm.Data.InfoTableBuilder -import Juvix.Compiler.Asm.Language -import Juvix.Compiler.Asm.Translation.FromSource - -data ApplyBuiltins = ApplyBuiltins - { -- | The number of `juvix_apply_n` functions. - _applyBuiltinsNum :: Int, - -- | Maps `n` to the function `juvix_apply_n`. - _applyBuiltinsMap :: HashMap Int Symbol - } - -makeLenses ''ApplyBuiltins - -addApplyBuiltins :: InfoTable -> (ApplyBuiltins, InfoTable) -addApplyBuiltins tab = (blts, bs' ^. stateInfoTable) - where - nextSymbolId = maximum (0 : map (^. symbolId) (HashMap.keys (tab ^. infoFunctions) ++ HashMap.keys (tab ^. infoInductives))) + 1 - nextUserId = maximum (0 : mapMaybe getUserTagId (HashMap.keys (tab ^. infoConstrs))) + 1 - - bs :: BuilderState - bs = - BuilderState - { _stateNextSymbolId = nextSymbolId, - _stateNextUserTag = nextUserId, - _stateInfoTable = tab, - _stateIdents = mempty - } - - bs' :: BuilderState - bs' = - fromRight impossible $ - parseText' bs $ - decodeUtf8 $(FE.makeRelativeToProject "runtime/src/asm/apply.jva" >>= FE.embedFile) - - blts :: ApplyBuiltins - blts = - ApplyBuiltins - { _applyBuiltinsNum = 4, - _applyBuiltinsMap = - HashMap.fromList $ map mkApply [1 .. 4] - } - - mkApply :: Int -> (Int, Symbol) - mkApply x = (x, f) - where - idt = "juvix_apply_" <> show x - f = case fromJust $ HashMap.lookup idt (bs' ^. stateIdents) of - IdentFun s -> s - _ -> impossible diff --git a/src/Juvix/Compiler/Asm/Pipeline.hs b/src/Juvix/Compiler/Asm/Pipeline.hs index 1c13a041c2..321a056da2 100644 --- a/src/Juvix/Compiler/Asm/Pipeline.hs +++ b/src/Juvix/Compiler/Asm/Pipeline.hs @@ -20,7 +20,7 @@ toReg' = validate >=> filterUnreachable >=> computeStackUsage >=> computePreallo -- | Perform transformations on JuvixAsm necessary before the translation to -- Nockma toNockma' :: (Members '[Error AsmError, Reader Options] r) => InfoTable -> Sem r InfoTable -toNockma' = validate >=> computeApply >=> filterUnreachable >=> computeTempHeight +toNockma' = validate >=> filterUnreachable >=> computeTempHeight toReg :: (Members '[Error JuvixError, Reader EntryPoint] r) => InfoTable -> Sem r InfoTable toReg = mapReader fromEntryPoint . mapError (JuvixError @AsmError) . toReg' diff --git a/src/Juvix/Compiler/Asm/Transformation.hs b/src/Juvix/Compiler/Asm/Transformation.hs index f1c6196166..b0771b3359 100644 --- a/src/Juvix/Compiler/Asm/Transformation.hs +++ b/src/Juvix/Compiler/Asm/Transformation.hs @@ -2,13 +2,11 @@ module Juvix.Compiler.Asm.Transformation ( module Juvix.Compiler.Asm.Transformation.StackUsage, module Juvix.Compiler.Asm.Transformation.Prealloc, module Juvix.Compiler.Asm.Transformation.Validate, - module Juvix.Compiler.Asm.Transformation.Apply, module Juvix.Compiler.Asm.Transformation.FilterUnreachable, module Juvix.Compiler.Asm.Transformation.TempHeight, ) where -import Juvix.Compiler.Asm.Transformation.Apply import Juvix.Compiler.Asm.Transformation.FilterUnreachable import Juvix.Compiler.Asm.Transformation.Prealloc import Juvix.Compiler.Asm.Transformation.StackUsage diff --git a/src/Juvix/Compiler/Asm/Transformation/Apply.hs b/src/Juvix/Compiler/Asm/Transformation/Apply.hs deleted file mode 100644 index 69ee88b5d1..0000000000 --- a/src/Juvix/Compiler/Asm/Transformation/Apply.hs +++ /dev/null @@ -1,104 +0,0 @@ -module Juvix.Compiler.Asm.Transformation.Apply where - -import Data.HashMap.Strict qualified as HashMap -import Juvix.Compiler.Asm.Extra.Apply -import Juvix.Compiler.Asm.Options -import Juvix.Compiler.Asm.Transformation.Base - -computeFunctionApply :: (Member (Error AsmError) r) => ApplyBuiltins -> InfoTable -> FunctionInfo -> Sem r FunctionInfo -computeFunctionApply blts tab fi = do - cs <- recurseS sig (fi ^. functionCode) - return fi {_functionCode = concat cs} - where - sig :: RecursorSig StackInfo r Code - sig = - RecursorSig - { _recursorInfoTable = tab, - _recurseInstr = \_ cmd -> goInstr cmd, - _recurseBranch = \_ cmd l r -> goBranch cmd l r, - _recurseCase = \_ cmd cs md -> goCase cmd cs md, - _recurseSave = \_ cmd b -> goSave cmd b - } - - goInstr :: CmdInstr -> Sem r Code - goInstr cmd = case cmd ^. cmdInstrInstruction of - CallClosures InstrCallClosures {..} -> return $ goApply False _callClosuresArgsNum - TailCallClosures InstrCallClosures {..} -> return $ goApply True _callClosuresArgsNum - _ -> return [Instr cmd] - - goApply :: Bool -> Int -> Code - goApply isTail n = replicate m (mkApply False (blts ^. applyBuiltinsNum)) ++ [mkApply isTail r] - where - (m, r) = n `divMod` (blts ^. applyBuiltinsNum) - - mkApply :: Bool -> Int -> Command - mkApply isTail k = - Instr $ - CmdInstr emptyInfo $ - (if isTail then TailCall else Call) - InstrCall - { _callType = CallFun sym, - _callArgsNum = k + 1 - } - where - sym = fromJust $ HashMap.lookup k (blts ^. applyBuiltinsMap) - - goBranch :: CmdBranch -> [Code] -> [Code] -> Sem r Code - goBranch cmd l r = - return - [ Branch - cmd - { _cmdBranchTrue = concat l, - _cmdBranchFalse = concat r - } - ] - - goCase :: CmdCase -> [[Code]] -> Maybe [Code] -> Sem r Code - goCase cmd cs md = - return - [ Case - cmd - { _cmdCaseBranches = - zipWith - (\br c -> CaseBranch (br ^. caseBranchTag) (concat c)) - (cmd ^. cmdCaseBranches) - cs, - _cmdCaseDefault = fmap concat md - } - ] - - goSave :: CmdSave -> [Code] -> Sem r Code - goSave cmd c = return [Save cmd {_cmdSaveCode = concat c}] - -computeApply :: (Member (Error AsmError) r) => InfoTable -> Sem r InfoTable -computeApply tab = liftFunctionTransformation (computeFunctionApply blts tab') tab' - where - (blts, tab') = addApplyBuiltins tab - -checkNoCallClosures :: Options -> InfoTable -> Bool -checkNoCallClosures opts tab = - case run $ runError $ runReader opts sb of - Left err -> error (show err) - Right b -> b - where - sb :: Sem '[Reader Options, Error AsmError] Bool - sb = allM (check . (^. functionCode)) (HashMap.elems (tab ^. infoFunctions)) - - check :: Code -> Sem '[Reader Options, Error AsmError] Bool - check c = foldS sig c True - where - sig = - FoldSig - { _foldInfoTable = tab, - _foldAdjust = id, - _foldInstr = \_ cmd b -> return $ b && goInstr (cmd ^. cmdInstrInstruction), - _foldBranch = \_ _ b1 b2 b3 -> return $ b1 && b2 && b3, - _foldCase = \_ _ bs bd b -> return $ and bs && fromMaybe True bd && b, - _foldSave = \_ _ b1 b2 -> return $ b1 && b2 - } - - goInstr :: Instruction -> Bool - goInstr = \case - CallClosures {} -> False - TailCallClosures {} -> False - _ -> True diff --git a/src/Juvix/Compiler/Nockma/EvalCompiled.hs b/src/Juvix/Compiler/Nockma/EvalCompiled.hs new file mode 100644 index 0000000000..9ca79a5376 --- /dev/null +++ b/src/Juvix/Compiler/Nockma/EvalCompiled.hs @@ -0,0 +1,34 @@ +module Juvix.Compiler.Nockma.EvalCompiled where + +import Juvix.Compiler.Nockma.Evaluator +import Juvix.Compiler.Nockma.Language +import Juvix.Compiler.Nockma.Pretty (ppTrace) +import Juvix.Compiler.Nockma.Translation.FromAsm +import Juvix.Prelude + +compileAndRunNock' :: (Members '[Reader EvalOptions, Output (Term Natural)] r) => CompilerOptions -> ConstructorInfos -> [CompilerFunction] -> CompilerFunction -> Sem r (Term Natural) +compileAndRunNock' opts constrs funs mainfun = + let Cell nockSubject t = runCompilerWith opts constrs funs mainfun + in evalCompiledNock' nockSubject t + +evalCompiledNock' :: (Members '[Reader EvalOptions, Output (Term Natural)] r) => Term Natural -> Term Natural -> Sem r (Term Natural) +evalCompiledNock' stack mainTerm = do + evalT <- + runError @(ErrNockNatural Natural) + . runError @(NockEvalError Natural) + $ eval stack mainTerm + case evalT of + Left e -> error (show e) + Right ev -> case ev of + Left e -> error (ppTrace e) + Right res -> return res + +-- | Used in testing and app +getStack :: StackId -> Term Natural -> Term Natural +getStack st m = + fromRight' + . run + . runError @(NockEvalError Natural) + . topEvalCtx + . subTerm m + $ stackPath st diff --git a/src/Juvix/Compiler/Nockma/Translation/FromAsm.hs b/src/Juvix/Compiler/Nockma/Translation/FromAsm.hs index f553c13d8c..0862df46ab 100644 --- a/src/Juvix/Compiler/Nockma/Translation/FromAsm.hs +++ b/src/Juvix/Compiler/Nockma/Translation/FromAsm.hs @@ -2,7 +2,6 @@ module Juvix.Compiler.Nockma.Translation.FromAsm where import Juvix.Compiler.Asm.Data.InfoTable qualified as Asm import Juvix.Compiler.Asm.Language qualified as Asm -import Juvix.Compiler.Nockma.Evaluator import Juvix.Compiler.Nockma.Pretty import Juvix.Compiler.Nockma.Stdlib import Juvix.Compiler.Pipeline.EntryPoint @@ -1180,36 +1179,3 @@ pushNat = pushNatOnto ValueStack pushNatOnto :: (Member Compiler r) => StackId -> Natural -> Sem r () pushNatOnto s n = pushOnto s (OpQuote # toNock n) - -compileAndRunNock' :: - (Members '[Reader EvalOptions, Output (Term Natural)] r) => - CompilerOptions -> - ConstructorInfos -> - [CompilerFunction] -> - CompilerFunction -> - Sem r (Term Natural) -compileAndRunNock' opts constrs funs mainfun = - let Cell nockSubject t = runCompilerWith opts constrs funs mainfun - in evalCompiledNock' nockSubject t - -evalCompiledNock' :: (Members '[Reader EvalOptions, Output (Term Natural)] r) => Term Natural -> Term Natural -> Sem r (Term Natural) -evalCompiledNock' stack mainTerm = do - evalT <- - runError @(ErrNockNatural Natural) - . runError @(NockEvalError Natural) - $ eval stack mainTerm - case evalT of - Left e -> error (show e) - Right ev -> case ev of - Left e -> error (ppTrace e) - Right res -> return res - --- | Used in testing and app -getStack :: StackId -> Term Natural -> Term Natural -getStack st m = - fromRight' - . run - . runError @(NockEvalError Natural) - . topEvalCtx - . subTerm m - $ stackPath st diff --git a/src/Juvix/Compiler/Pipeline.hs b/src/Juvix/Compiler/Pipeline.hs index 15b6a8c635..5666584af0 100644 --- a/src/Juvix/Compiler/Pipeline.hs +++ b/src/Juvix/Compiler/Pipeline.hs @@ -196,6 +196,9 @@ regToMiniC tab = do e <- ask return $ C.fromReg (Backend.getLimits (e ^. entryPointTarget) (e ^. entryPointDebug)) tab +treeToNockma' :: (Members '[Error JuvixError, Reader Asm.Options, Reader Nockma.CompilerOptions] r) => Tree.InfoTable -> Sem r (Nockma.Cell Natural) +treeToNockma' = Tree.toNockma >=> treeToAsm >=> asmToNockma' + asmToNockma' :: (Members '[Error JuvixError, Reader Asm.Options, Reader Nockma.CompilerOptions] r) => Asm.InfoTable -> Sem r (Nockma.Cell Natural) asmToNockma' = mapError (JuvixError @Asm.AsmError) . Asm.toNockma' >=> Nockma.fromAsmTable diff --git a/test/Asm/Transformation.hs b/test/Asm/Transformation.hs index 5a2d087c04..11356b1e52 100644 --- a/test/Asm/Transformation.hs +++ b/test/Asm/Transformation.hs @@ -1,9 +1,8 @@ module Asm.Transformation where -import Asm.Transformation.Apply qualified as Apply import Asm.Transformation.Prealloc qualified as Prealloc import Asm.Transformation.Reachability qualified as Reachability import Base allTests :: TestTree -allTests = testGroup "JuvixAsm transformations" [Prealloc.allTests, Apply.allTests, Reachability.allTests] +allTests = testGroup "JuvixAsm transformations" [Prealloc.allTests, Reachability.allTests] diff --git a/test/Asm/Transformation/Apply.hs b/test/Asm/Transformation/Apply.hs deleted file mode 100644 index 77168363ed..0000000000 --- a/test/Asm/Transformation/Apply.hs +++ /dev/null @@ -1,35 +0,0 @@ -module Asm.Transformation.Apply (allTests) where - -import Asm.Run.Positive qualified as Run -import Asm.Transformation.Base -import Base -import Juvix.Compiler.Asm.Options -import Juvix.Compiler.Asm.Transformation -import Juvix.Compiler.Asm.Transformation.Base - -allTests :: TestTree -allTests = - testGroup "Apply" $ - map liftTest $ - Run.filterTests - [ "Test007: Higher-order functions", - "Test022: Self-application", - "Test025: Dynamic closure extension", - "Test032: Church numerals" - ] - Run.tests - -liftTest :: Run.PosTest -> TestTree -liftTest _testEval = - fromTest - Test - { _testTransformation = runTransformation (runReader opts . computeApply), - _testAssertion = \tab -> unless (checkNoCallClosures opts tab) (error "check apply"), - _testEval - } - where - opts = - Options - { _optDebug = True, - _optLimits = getLimits TargetCWasm32Wasi True - } diff --git a/test/Nockma/Compile.hs b/test/Nockma/Compile.hs index 88a56939be..5eadc34205 100644 --- a/test/Nockma/Compile.hs +++ b/test/Nockma/Compile.hs @@ -1,8 +1,8 @@ module Nockma.Compile where import Base -import Nockma.Compile.Asm.Positive qualified as Asm import Nockma.Compile.Positive qualified as P +import Nockma.Compile.Tree.Positive qualified as Tree allTests :: TestTree -allTests = testGroup "Nockma compile" [P.allTests, Asm.allTests] +allTests = testGroup "Nockma compile" [P.allTests, Tree.allTests] diff --git a/test/Nockma/Compile/Positive.hs b/test/Nockma/Compile/Positive.hs index 10437e1e55..57d6a52d94 100644 --- a/test/Nockma/Compile/Positive.hs +++ b/test/Nockma/Compile/Positive.hs @@ -3,6 +3,7 @@ module Nockma.Compile.Positive where import Base hiding (Path) import Data.List.NonEmpty qualified as NonEmpty import Juvix.Compiler.Asm.Language qualified as Asm +import Juvix.Compiler.Nockma.EvalCompiled import Juvix.Compiler.Nockma.Evaluator import Juvix.Compiler.Nockma.Language import Juvix.Compiler.Nockma.Pretty diff --git a/test/Nockma/Compile/Asm/Positive.hs b/test/Nockma/Compile/Tree/Positive.hs similarity index 74% rename from test/Nockma/Compile/Asm/Positive.hs rename to test/Nockma/Compile/Tree/Positive.hs index 53923d092b..a6cb7fb04d 100644 --- a/test/Nockma/Compile/Asm/Positive.hs +++ b/test/Nockma/Compile/Tree/Positive.hs @@ -1,15 +1,16 @@ -module Nockma.Compile.Asm.Positive where +module Nockma.Compile.Tree.Positive where -import Asm.Run.Base -import Asm.Run.Positive qualified as Asm import Base -import Juvix.Compiler.Asm import Juvix.Compiler.Asm.Options qualified as Asm +import Juvix.Compiler.Backend +import Juvix.Compiler.Nockma.EvalCompiled import Juvix.Compiler.Nockma.Evaluator qualified as NockmaEval import Juvix.Compiler.Nockma.Language import Juvix.Compiler.Nockma.Pretty qualified as Nockma -import Juvix.Compiler.Nockma.Translation.FromAsm import Juvix.Compiler.Nockma.Translation.FromAsm qualified as Nockma +import Juvix.Compiler.Tree +import Tree.Eval.Base +import Tree.Eval.Positive qualified as Tree runNockmaAssertion :: Handle -> Symbol -> InfoTable -> IO () runNockmaAssertion hout _main tab = do @@ -20,7 +21,7 @@ runNockmaAssertion hout _main tab = do . runReader (Nockma.CompilerOptions {_compilerOptionsEnableTrace = True}) . runErrorIO' @JuvixError - $ asmToNockma' tab + $ treeToNockma' tab res <- runM . runOutputSem @(Term Natural) @@ -33,20 +34,20 @@ runNockmaAssertion hout _main tab = do where getReturn :: Term Natural -> Term Natural getReturn res = - let valStack = getStack ValueStack res + let valStack = getStack Nockma.ValueStack res in case valStack of TermCell c -> c ^. cellLeft TermAtom {} -> error "should be a cell" -testDescr :: Asm.PosTest -> TestDescr -testDescr Asm.PosTest {..} = - let tRoot = Asm.root _relDir +testDescr :: Tree.PosTest -> TestDescr +testDescr Tree.PosTest {..} = + let tRoot = Tree.root _relDir file' = tRoot _file expected' = tRoot _expectedFile in TestDescr { _testName = _name, _testRoot = tRoot, - _testAssertion = Steps $ asmRunAssertionParam runNockmaAssertion file' expected' return (const (return ())) + _testAssertion = Steps $ treeEvalAssertionParam runNockmaAssertion file' expected' [] (const (return ())) } testsSlow :: [Int] @@ -74,8 +75,8 @@ testsBugged = testsToIgnore :: [Int] testsToIgnore = testsHopeless ++ testsBugged ++ testsSlow ++ testsAdt ++ testsNegativeInteger -shouldRun :: Asm.PosTest -> Bool -shouldRun Asm.PosTest {..} = testNum `notElem` map to3DigitString testsToIgnore +shouldRun :: Tree.PosTest -> Bool +shouldRun Tree.PosTest {..} = testNum `notElem` map to3DigitString testsToIgnore where testNum :: String testNum = take 3 (drop 4 _name) @@ -90,4 +91,4 @@ allTests :: TestTree allTests = testGroup "Nockma Asm compile positive tests" - (map (mkTest . testDescr) (filter shouldRun Asm.tests)) + (map (mkTest . testDescr) (filter shouldRun Tree.tests)) diff --git a/test/Tree/Eval/Base.hs b/test/Tree/Eval/Base.hs index e6b46c8254..ffa4f28d59 100644 --- a/test/Tree/Eval/Base.hs +++ b/test/Tree/Eval/Base.hs @@ -19,7 +19,17 @@ treeEvalAssertion :: (InfoTable -> Assertion) -> (String -> IO ()) -> Assertion -treeEvalAssertion mainFile expectedFile trans testTrans step = do +treeEvalAssertion = treeEvalAssertionParam evalAssertion + +treeEvalAssertionParam :: + (Handle -> Symbol -> InfoTable -> IO ()) -> + Path Abs File -> + Path Abs File -> + [TransformationId] -> + (InfoTable -> Assertion) -> + (String -> IO ()) -> + Assertion +treeEvalAssertionParam evalParam mainFile expectedFile trans testTrans step = do step "Parse" s <- readFile (toFilePath mainFile) case runParser (toFilePath mainFile) s of @@ -36,7 +46,7 @@ treeEvalAssertion mainFile expectedFile trans testTrans step = do let outputFile = dirPath $(mkRelFile "out.out") hout <- openFile (toFilePath outputFile) WriteMode step "Evaluate" - evalAssertion hout sym tab + evalParam hout sym tab hClose hout actualOutput <- readFile (toFilePath outputFile) step "Compare expected and actual program output"