Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support compilation to Anoma compatible functions #2652

Merged
merged 26 commits into from
Feb 23, 2024
Merged

Conversation

paulcadman
Copy link
Collaborator

@paulcadman paulcadman commented Feb 14, 2024

This PR adds a anoma target to the juvix compile. This target compiles a Juvix main function to a Nockma/Anoma "function". Unlike the native, wasm, and nockma targets the main function may have any type signature.

Anoma calling convention

Anoma calls functions by evaluating the formula [call L replace [RL args] @ S] against a subject equal to the function. Here args is a Nockma term that evaluates to a tuple of arguments that should be passed to the function.

The anoma target compiles the main function to Nockma in the same way as the nockma target. The main function is then wrapped to make it compatible with the Anoma calling convention.

Testing

The anoma calling convention is unit tested and smoke tested.

This PR also adds versions of the end-to-end compilation tests. Most tests are included, tests for builtin IO operations and string builtins are omitted. Other tests that use Strings have been adapted to use other types that are compatible with this backend.

Nockma REPL

To facilitate testing the Nockma REPL can now load a nockma file as an initial subject.

@paulcadman paulcadman marked this pull request as draft February 15, 2024 08:58
@paulcadman paulcadman force-pushed the nockma-anoma branch 3 times, most recently from c11d971 to 92a86e4 Compare February 20, 2024 17:10
@paulcadman paulcadman marked this pull request as ready for review February 20, 2024 17:13
@paulcadman paulcadman requested review from lukaszcz and janmasrovira and removed request for lukaszcz February 20, 2024 17:13
@paulcadman paulcadman added this to the 0.6.0 milestone Feb 20, 2024
@@ -69,7 +69,10 @@ toVampIRTransformations = [CombineInfoTables, FilterUnreachable, CheckVampIR, Le

toStrippedTransformations :: [TransformationId]
toStrippedTransformations =
[CombineInfoTables, FilterUnreachable, CheckExec, LambdaLetRecLifting, TopEtaExpand, OptPhaseExec, MoveApps, RemoveTypeArgs]
[CombineInfoTables, FilterUnreachable, LambdaLetRecLifting, TopEtaExpand, OptPhaseExec, MoveApps, RemoveTypeArgs]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order was significant here -- "illegal" things (for CheckExec - axioms) may get filtered out or brought in from imported modules. Plus, there should be similar checks for Anoma/Nockma and later Cairo. I'll make a commit to this PR to refactor the transformation pipeline to allow for different kinds of checks.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see - we moved the CheckExec to a toExec transformation (

coreToMiniC = Core.toExec >=> coreToAsm >=> asmToMiniC
) to allow Anoma main functions to have arbitrary signatures. But in the case of Anoma we want to check that axioms are not present so I think your refactor is a good idea, thanks.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I commited a refactor.

return Test {..}

withRootCopy :: (Prelude.Path Abs Dir -> IO a) -> IO a
withRootCopy action = withSystemTempDir "test" $ \tmpRootDir -> do
Copy link
Collaborator

@lukaszcz lukaszcz Feb 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stored standard library is not shared between different tests, which defeats per-module compilation and makes the tests run around 4 times slower than they otherwise would.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running the tests concurrently in the same directory triggers the leaking IO bypassing the TaggedLock that we've seen before. It's possible that using Effectful will fix this. An alternative would be to synchronously build the standard library somehow before running the test suite and copy that to each temporary location.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Somehow, it works for the Juvix->Native/Wasm compilation tests (in tests/Compilation/positive). What's different?

I remember I've been dealing specifically with this issue when implementing per-module-compilation, and ultimately used file-level locking which solved the problem.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't remember what's happening exactly for the main exec pipeline compilation tests, but the stored standard library modules seem to be reused judging by decrease in runtime after first test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The anoma compilation tests are using the same functions to run the pipeline as the main exec compilation tests, yet they fail for the leaking IO / tagged lock issue. TaggedLock in general is too unreliable and I'm thinking about a plan to remove it. I propose to leave the anoma compilation tests as they are (using separate directories) for now until we work out how we will replace tagged lock.


terminating
map'' {A B} {{Partial}} (f : A → B) (x : List A) : List B :=
if (null x) nil (f (phead x) :: map'' f (tail x));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compiling this to Nockma relies on Core-level optimizations. The function phead can internally call a fail function which takes a string as argument. If Partial is not specialised and this internal call to fail is not inlined to directly call the builtin, you'll be translating strings and get a crash.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd like to keep support for Strings so we can intercept messages from builtin fail: #2665

We've re-enabled support for PrimString in the Anoma target and refactored the failing tests to use failwith (i.e builtin fail) directly instead of via the Partial trait.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't completely understand the rationale behind this replacement -- if you have strings in the Nockma backend you can use Partial, if you don't you can't use failwith either.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I misunderstood something. Our rationale is: we don't support Strings in the Nockma backend yet, but we can support builtin fail because it's compiled directly to OpFail in Tree and we can ignore the String argument in the compilation:

Tree.OpFail -> return crash

The proposal in #2665 is to intercept the Strings from OpFail and add them to the CellInfo. This String can be used in the Juvix/Nockma evaluator in the error message of the crash but is not serialised when we output Nockma code for consumption by Anoma/Nockma evaluator.

Copy link
Collaborator

@lukaszcz lukaszcz Feb 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but we can support builtin fail because it's compiled directly to OpFail in Tree

I think that's not an entirely correct assumption, because of eta-expansions in the pipeline or how failwith is translated from Internal to Core. So lambda-abstractions with string argument type might be added on top of the builtin, and the string argument would be applied to a lambda-function - not to the builtin directly. Otherwise my checks for strings in checkBuiltins' wouldn't fail with only failwith because OpFail is already handled specially there. It might work because of subsequent optimisation which inlines the eta-expanded functions.

Copy link
Collaborator Author

@paulcadman paulcadman Feb 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. I'll restore the PrimString check and modify the tests to avoid using Partial/failwith.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've now modified the tests to avoid using Partial/failwith and restored the Strings check in the anoma backend.

@lukaszcz
Copy link
Collaborator

I added checks for strings in the Anoma pipeline, which makes the 4 tests indirectly depending on them fail. They compiled before because you had Core-level optimizations turned on, which inlined the functions taking strings, but we shouldn't rely on this.

@paulcadman
Copy link
Collaborator Author

I added checks for strings in the Anoma pipeline, which makes the 4 tests indirectly depending on them fail. They compiled before because you had Core-level optimizations turned on, which inlined the functions taking strings, but we shouldn't rely on this.

Should these tests be removed then?

@lukaszcz
Copy link
Collaborator

I added checks for strings in the Anoma pipeline, which makes the 4 tests indirectly depending on them fail. They compiled before because you had Core-level optimizations turned on, which inlined the functions taking strings, but we shouldn't rely on this.

Should these tests be removed then?

I don't know, maybe they can be fixed, e.g., by removing functions depending on Partial (test 007 definitely can be). I didn't look too much into them, leaving this decision to you.

upToAnoma ::
(Members '[HighlightBuilder, Reader Parser.ParserResult, Reader EntryPoint, Reader Store.ModuleTable, Files, NameIdGen, Error JuvixError, GitClone, PathResolver] r) =>
Sem r (Nockma.Term Natural)
upToAnoma = upToStoredCore >>= \Core.CoreResult {..} -> Nockma.TermCell <$> coreToAnoma _coreResultModule
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be storedCoreToAnoma (using storedCoreToTree) instead of coreToAnoma. Otherwise, we're doing the Core.toStored transformations twice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Done

Copy link
Collaborator

@lukaszcz lukaszcz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change src/Juvix/Compiler/Pipeline.hs as discussed in the comment.

@paulcadman paulcadman merged commit be24abb into main Feb 23, 2024
4 checks passed
@paulcadman paulcadman deleted the nockma-anoma branch February 23, 2024 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants