diff --git a/impls/java-truffle/README.md b/impls/java-truffle/README.md index a4d34f2fef..932b742207 100644 --- a/impls/java-truffle/README.md +++ b/impls/java-truffle/README.md @@ -677,3 +677,23 @@ It's also worth observing that the Truffle/GraalVM provide _other_ interesting b that are not performance-related. I won't cover them here. I think the most interesting non-performance benefit is the promise of interoperability with other Truffle languages. +## Bonus: AOT-compiled Mal + +GraalVM can ahead-of-time compile Java into a stand-alone executable (with some caveats) +called a _native image_. +This works even for Truffle interpreters! With AOT-compiled Mal, we get all the JIT compilation +goodness of Truffle, _and_ we ditch the need for a Java runtime, **and** we skip the long JVM +start-up time! A GraalVM native image of our Mal interpreter is well suited for scripts and +command line applications. + +The `make-native.sh` script can be used to compile a native image of any Mal step. +To run it, though, you'll need some additional +[prerequisites](https://www.graalvm.org/reference-manual/native-image/#prerequisites). + +The `make-native.sh` script + +* assumes you've already run `gradle build` to compile all Java classes +* takes as its only argument a step name, e.g. `step3_env` +** when no argument is supplied, `stepE_macros` is selected by default +* produces a `build/${STEP}` native image + diff --git a/impls/java-truffle/build.gradle b/impls/java-truffle/build.gradle index 5e6714e74d..a5d5c9c62f 100644 --- a/impls/java-truffle/build.gradle +++ b/impls/java-truffle/build.gradle @@ -14,9 +14,9 @@ repositories { } dependencies { - implementation 'org.graalvm.truffle:truffle-api:20.1.0' + implementation 'org.graalvm.truffle:truffle-api:21.1.0' implementation 'org.organicdesign:Paguro:3.2.0' - annotationProcessor 'org.graalvm.truffle:truffle-dsl-processor:20.1.0' + annotationProcessor 'org.graalvm.truffle:truffle-dsl-processor:21.1.0' } group = 'com.github.mmcgill' diff --git a/impls/java-truffle/make-native.sh b/impls/java-truffle/make-native.sh new file mode 100755 index 0000000000..db86494e4d --- /dev/null +++ b/impls/java-truffle/make-native.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +STEP=${1:-stepE_macros} + +CP=$(gradle -q --console plain printClasspath) +native-image --macro:truffle --no-fallback --initialize-at-build-time \ + -H:+TruffleCheckBlackListedMethods \ + -cp "$CP" truffle.mal.$STEP build/$STEP diff --git a/impls/java-truffle/src/main/java/truffle/mal/Core.java b/impls/java-truffle/src/main/java/truffle/mal/Core.java index b798f6e879..294049e131 100644 --- a/impls/java-truffle/src/main/java/truffle/mal/Core.java +++ b/impls/java-truffle/src/main/java/truffle/mal/Core.java @@ -1393,6 +1393,7 @@ abstract class EvalBuiltin extends BuiltinNode { protected EvalBuiltin() { super("eval"); } @Specialization + @TruffleBoundary protected Object eval(Object ast) { return language.evalForm(ast).call(); } @@ -1404,11 +1405,13 @@ abstract class ThrowBuiltin extends BuiltinNode { protected ThrowBuiltin() { super("throw"); } + @TruffleBoundary @Specialization protected Object throwException(String obj) { throw new MalException(obj); } + @TruffleBoundary @Fallback protected Object throwException(Object obj) { throw new MalException(obj); diff --git a/impls/java-truffle/src/main/java/truffle/mal/MalEnv.java b/impls/java-truffle/src/main/java/truffle/mal/MalEnv.java index aa7a381fd6..3678b679bd 100644 --- a/impls/java-truffle/src/main/java/truffle/mal/MalEnv.java +++ b/impls/java-truffle/src/main/java/truffle/mal/MalEnv.java @@ -277,6 +277,7 @@ class LexicalScope { final LexicalScope parent; final int depth; final Map slots; + private int staticBindingCount; final Map notDynamicallyBound; LexicalScope() { @@ -287,6 +288,7 @@ class LexicalScope { this.parent = parent; this.depth = parent == null? 0 : parent.depth+1; this.slots = new HashMap<>(); + this.staticBindingCount = 0; this.notDynamicallyBound = new HashMap<>(); } @@ -309,6 +311,7 @@ private Assumption getNotDynamicallyBound(MalSymbol symbol) { public EnvSlot allocateSlot(MalSymbol symbol) { var slot = new EnvSlot(0, slots.size(), getNotDynamicallyBound(symbol)); slots.put(symbol, slot); + staticBindingCount++; return slot; } @@ -354,7 +357,7 @@ public void wasDynamicallyBound(MalSymbol sym) { } public int getStaticBindingCount() { - return slots.size(); + return staticBindingCount; } static class EnvSlot { diff --git a/impls/java-truffle/src/main/java/truffle/mal/Types.java b/impls/java-truffle/src/main/java/truffle/mal/Types.java index f093e69cfd..11a65c7659 100644 --- a/impls/java-truffle/src/main/java/truffle/mal/Types.java +++ b/impls/java-truffle/src/main/java/truffle/mal/Types.java @@ -241,6 +241,15 @@ public MalVector append(Object obj) { return new MalVector(vector.append(obj), this.meta); } + @TruffleBoundary + public MalVector concat(Object[] objs) { + var v = vector.mutable(); + for (int i=0; i < objs.length; ++i) { + v.append(objs[i]); + } + return new MalVector(v.immutable(), meta); + } + @TruffleBoundary public MalVector concat(Iterable objs) { return new MalVector(vector.concat(objs), meta); diff --git a/impls/java-truffle/src/main/java/truffle/mal/step2_eval.java b/impls/java-truffle/src/main/java/truffle/mal/step2_eval.java index a90807e5fb..5f9068af94 100644 --- a/impls/java-truffle/src/main/java/truffle/mal/step2_eval.java +++ b/impls/java-truffle/src/main/java/truffle/mal/step2_eval.java @@ -3,7 +3,6 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @@ -15,6 +14,7 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleLanguage; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.TruffleObject; @@ -137,9 +137,9 @@ static class VectorNode extends MalNode { @Override public Object executeGeneric(VirtualFrame frame) { - var elements = new ArrayList<>(elementNodes.length); + var elements = new Object[elementNodes.length]; for (int i=0; i < elementNodes.length; i++) { - elements.add(elementNodes[i].executeGeneric(frame)); + elements[i] = elementNodes[i].executeGeneric(frame); } return MalVector.EMPTY.concat(elements); } @@ -174,14 +174,19 @@ static class LookupNode extends MalNode { this.symbol = symbol; } - @Override - public Object executeGeneric(VirtualFrame frame) { + @TruffleBoundary + private Object lookup() { var result = replEnv.get(symbol); if (result == null) { throw new MalException(symbol+" not found"); } return result; } + + @Override + public Object executeGeneric(VirtualFrame frame) { + return lookup(); + } } static class ApplyNode extends MalNode { diff --git a/impls/java-truffle/src/main/java/truffle/mal/stepE_macros.java b/impls/java-truffle/src/main/java/truffle/mal/stepE_macros.java index 880d9b9080..b0f7f5202a 100644 --- a/impls/java-truffle/src/main/java/truffle/mal/stepE_macros.java +++ b/impls/java-truffle/src/main/java/truffle/mal/stepE_macros.java @@ -204,9 +204,9 @@ static class VectorNode extends MalNode { @ExplodeLoop @Override public Object executeGeneric(VirtualFrame frame, MalEnv env) { - var elements = new ArrayList<>(elementNodes.length); + var elements = new Object[elementNodes.length]; for (int i=0; i < elementNodes.length; i++) { - elements.add(elementNodes[i].executeGeneric(frame, env)); + elements[i] = elementNodes[i].executeGeneric(frame, env); } return MalVector.EMPTY.concat(elements); }