diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index df85c99c32bc..af4675d0fdd3 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -474,7 +474,7 @@ is_unresolved_symbol value = @Builtin_Method "Meta.is_unresolved_symbol" used carefully. get_source_location : Integer -> Text get_source_location skip_frames = - get_source_location_builtin skip_frames+1 + get_source_location_builtin skip_frames ## PRIVATE diff --git a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala index 1c1e39010524..32f36c73476b 100644 --- a/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala +++ b/engine/runtime-with-instruments/src/test/scala/org/enso/interpreter/test/instrument/RuntimeServerTest.scala @@ -3058,7 +3058,6 @@ class RuntimeServerTest ) ) context.receiveN(4) should contain theSameElementsAs Seq( - Api.Response(Api.BackgroundJobsStartedNotification()), Api.Response(requestId, Api.PushContextResponse(contextId)), Api.Response( Api.ExecutionUpdate( @@ -3066,11 +3065,10 @@ class RuntimeServerTest Seq( Api.ExecutionResult.Diagnostic.error( "Type error: expected `str` to be Text, but got 2 (Integer).", - None, - None, + Some(mainFile), + Some(model.Range(model.Position(2, 10), model.Position(2, 15))), None, Vector( - Api.StackTraceElement("Text.+", None, None, None), Api.StackTraceElement( "Main.bar", Some(mainFile), @@ -3092,6 +3090,7 @@ class RuntimeServerTest ) ) ), + Api.Response(Api.BackgroundJobsStartedNotification()), context.executionComplete(contextId) ) } @@ -3216,7 +3215,6 @@ class RuntimeServerTest ) ) context.receiveN(4) should contain theSameElementsAs Seq( - Api.Response(Api.BackgroundJobsStartedNotification()), Api.Response(requestId, Api.PushContextResponse(contextId)), Api.Response( Api.ExecutionUpdate( @@ -3224,11 +3222,10 @@ class RuntimeServerTest Seq( Api.ExecutionResult.Diagnostic.error( "Type error: expected `that` to be Number, but got quux (Unresolved_Symbol).", - None, - None, + Some(mainFile), + Some(model.Range(model.Position(10, 8), model.Position(10, 17))), None, Vector( - Api.StackTraceElement("Small_Integer.+", None, None, None), Api.StackTraceElement( "Main.baz", Some(mainFile), @@ -3266,6 +3263,7 @@ class RuntimeServerTest ) ) ), + Api.Response(Api.BackgroundJobsStartedNotification()), context.executionComplete(contextId) ) } diff --git a/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/IfVsCaseBenchmarks.java b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/IfVsCaseBenchmarks.java new file mode 100644 index 000000000000..37a69a779151 --- /dev/null +++ b/engine/runtime/src/bench/java/org/enso/interpreter/bench/benchmarks/semantic/IfVsCaseBenchmarks.java @@ -0,0 +1,187 @@ +package org.enso.interpreter.bench.benchmarks.semantic; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; +import org.enso.interpreter.test.TestBase; +import org.enso.polyglot.MethodNames.Module; +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Source; +import org.graalvm.polyglot.Value; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.BenchmarkMode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Mode; +import org.openjdk.jmh.annotations.OutputTimeUnit; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.TearDown; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.BenchmarkParams; + +@BenchmarkMode(Mode.AverageTime) +@Fork(1) +@Warmup(iterations = 5, time = 1) +@Measurement(iterations = 3, time = 3) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +@State(Scope.Benchmark) +public class IfVsCaseBenchmarks extends TestBase { + private static final int INPUT_VEC_SIZE = 100_000; + private Context ctx; + private Value ifBench3; + private Value caseBench3; + private Value ifBench6; + private Value caseBench6; + private Value createVec; + private Value inputVec; + private OutputStream out = new ByteArrayOutputStream(); + + @Setup + public void initializeBench(BenchmarkParams params) throws IOException { + ctx = Context.newBuilder("enso") + .allowAllAccess(true) + .logHandler(out) + .out(out) + .err(out) + .allowIO(true) + .allowExperimentalOptions(true) + .option( + "enso.languageHomeOverride", + Paths.get("../../distribution/component").toFile().getAbsolutePath() + ) + .option("engine.MultiTier", "true") + .option("engine.BackgroundCompilation", "true") + .build(); + + var code = """ + from Standard.Base import all + + type My_Type + Value f1 f2 f3 f4 f5 f6 + + if_bench_3 : Vector My_Type -> Integer + if_bench_3 vec = + vec.fold 0 acc-> curr-> + if curr.f1.not then acc else + if curr.f2.not then acc else + if curr.f3.not then acc else + acc + 1 + + case_bench_3 : Vector My_Type -> Integer + case_bench_3 vec = + vec.fold 0 acc-> curr-> + case curr.f1 of + False -> acc + True -> case curr.f2 of + False -> acc + True -> case curr.f3 of + False -> acc + True -> acc + 1 + + if_bench_6 : Vector My_Type -> Integer + if_bench_6 vec = + vec.fold 0 acc-> curr-> + if curr.f1.not then acc else + if curr.f2.not then acc else + if curr.f3.not then acc else + if curr.f4.not then acc else + if curr.f5.not then acc else + if curr.f6.not then acc else + acc + 1 + + case_bench_6 : Vector My_Type -> Integer + case_bench_6 vec = + vec.fold 0 acc-> curr-> + case curr.f1 of + False -> acc + True -> case curr.f2 of + False -> acc + True -> case curr.f3 of + False -> acc + True -> case curr.f4 of + False -> acc + True -> case curr.f5 of + False -> acc + True -> case curr.f6 of + False -> acc + True -> acc + 1 + + create_vec polyglot_vec = + Vector.from_polyglot_array polyglot_vec . map elem-> + My_Type.Value (elem.at 0) (elem.at 1) (elem.at 2) (elem.at 3) (elem.at 4) (elem.at 5) + + """; + + var file = File.createTempFile("if_case", ".enso"); + try (var w = new FileWriter(file)) { + w.write(code); + } + var src = Source.newBuilder("enso", file).build(); + Value module = ctx.eval(src); + ifBench3 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "if_bench_3")); + caseBench3 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "case_bench_3")); + ifBench6 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "if_bench_6")); + caseBench6 = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "case_bench_6")); + createVec = Objects.requireNonNull(module.invokeMember(Module.EVAL_EXPRESSION, "create_vec")); + // So far, input is a vector of My_Type.Value with all fields set to True + inputVec = createMyTypeAllTrue(INPUT_VEC_SIZE); + } + + @TearDown + public void tearDown() { + ctx.close(); + } + + /** + * Iterates over a vector of {@code My_Type} values with True only fields. + */ + @Benchmark + public void ifBench3() { + Value res = ifBench3.execute(inputVec); + checkResult(res); + } + + @Benchmark + public void ifBench6() { + Value res = ifBench6.execute(inputVec); + checkResult(res); + } + + @Benchmark + public void caseBench3() { + Value res = caseBench3.execute(inputVec); + checkResult(res); + } + + @Benchmark + public void caseBench6() { + Value res = caseBench6.execute(inputVec); + checkResult(res); + } + + private static void checkResult(Value res) { + if (res.asInt() != INPUT_VEC_SIZE) { + throw new AssertionError("Expected result: " + INPUT_VEC_SIZE + ", got: " + res.asInt()); + } + } + + /** + * Creates a vector of {@code My_Type} with all True fields + */ + private Value createMyTypeAllTrue(int size) { + List<List<Boolean>> inputPolyVec = new ArrayList<>(); + for (int i = 0; i < size; i++) { + inputPolyVec.add(List.of(true, true, true, true, true, true)); + } + return createVec.execute(inputPolyVec); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/InlineableRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/InlineableRootNode.java new file mode 100644 index 000000000000..d660aad64bfe --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/InlineableRootNode.java @@ -0,0 +1,56 @@ +package org.enso.interpreter.node; + +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.RootNode; + +/** + * Special interface that allows various {@link RootNode} subclasses to provide + * more effective implementation of {@link DirectCallNode}. Used by for example + * by {@code BuiltinRootNode}. + */ +public interface InlineableRootNode { + /** + * Provides access to {@link RootNode}. Usually the object shall inherit from + * {link RootNode} as well as implement the {@link InlineableRootNode} + * interface. This method thus usually returns {@code this}. + * + * @return {@code this} types as {link RootNode} + */ + public RootNode getRootNode(); + + /** + * Name of the {@link RootNode}. + * + * @return root node name + */ + public String getName(); + + /** + * Override to provide more effective implementation of {@link DirectCallNode} + * suited more for Enso aggressive inlining. + * + * @return a node to {@link DirectCallNode#call(java.lang.Object...) call} the + * associated {@link RootNode} - may return {@code null} + */ + public DirectCallNode createDirectCallNode(); + + /** + * * Obtain a {@link DirectCallNode} for given {@link CallTarget}.Either + * delegates to {@link #createDirectCallNode} or uses regular + * {@link DirectCallNode#create(com.oracle.truffle.api.CallTarget)} method. + * Use for example by {@code ExecuteCallNode}. + * + * @param target call target with regular or + * {@link InlineableRootNode} {@link RootCallTarget#getRootNode()} + * @return instance of {@link DirectCallNode} to use to invoke the + * {@link RootNode#execute(com.oracle.truffle.api.frame.VirtualFrame)}. + */ + public static DirectCallNode create(RootCallTarget target) { + if (target.getRootNode() instanceof InlineableRootNode inRoot && inRoot.createDirectCallNode() instanceof DirectCallNode node) { + return node; + } + return DirectCallNode.create(target); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/ExecuteCallNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/ExecuteCallNode.java index 35c2624f0682..c734cbd3a5c1 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/ExecuteCallNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/ExecuteCallNode.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.IndirectCallNode; import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; +import org.enso.interpreter.node.InlineableRootNode; import org.enso.interpreter.runtime.callable.CallerInfo; import org.enso.interpreter.runtime.callable.function.Function; @@ -52,11 +53,15 @@ protected Object callDirect( Object state, Object[] arguments, @Cached("function.getCallTarget()") RootCallTarget cachedTarget, - @Cached("create(cachedTarget)") DirectCallNode callNode) { + @Cached("createCallNode(cachedTarget)") DirectCallNode callNode) { return callNode.call( Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments)); } + static DirectCallNode createCallNode(RootCallTarget t) { + return InlineableRootNode.create(t); + } + /** * Calls the function with a lookup. * diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/BuiltinRootNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/BuiltinRootNode.java index 4d7fd660506a..c36cd40d4d88 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/BuiltinRootNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/BuiltinRootNode.java @@ -1,13 +1,18 @@ package org.enso.interpreter.node.expression.builtin; +import com.oracle.truffle.api.CallTarget; +import org.enso.interpreter.EnsoLanguage; + import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; -import org.enso.interpreter.EnsoLanguage; +import org.enso.interpreter.node.InlineableRootNode; /** Root node for use by all the builtin functions. */ @NodeInfo(shortName = "BuiltinRoot", description = "Root node for builtin functions.") -public abstract class BuiltinRootNode extends RootNode { +public abstract class BuiltinRootNode extends RootNode implements InlineableRootNode { protected BuiltinRootNode(EnsoLanguage language) { super(language); } @@ -28,4 +33,64 @@ protected BuiltinRootNode(EnsoLanguage language) { */ @Override public abstract String getName(); + + /** + * Factory method creating a {@link DirectCallNode} to invoke this builtin.Defaults to standard + * {@link DirectCallNode#create(com.oracle.truffle.api.CallTarget)} implementation. Subclasses may + * override this with the help of {@link InlinedCallNode}. + * + * @return new node to use to call this builtin + */ + public DirectCallNode createDirectCallNode() { + return DirectCallNode.create(getCallTarget()); + } + + /** + * Helper class allowing better implementation of {@link #createDirectCallNode}. Subclass, pass in + * {@code extra} and {@code body} and override {@code call} method to do what has to be done. + * + * @param <E> extra data to keep in the node + * @param <N> node to delegate to from {@link #call(java.lang.Object...)} method + */ + protected abstract static class InlinedCallNode<E, N extends Node> extends DirectCallNode { + protected final E extra; + @Child protected N body; + + protected InlinedCallNode(E extra, N body) { + super(null); + this.extra = extra; + this.body = body; + } + + @Override + public abstract Object call(Object... arguments); + + @Override + public final boolean isInlinable() { + return true; + } + + @Override + public final boolean isInliningForced() { + return true; + } + + @Override + public final void forceInlining() {} + + @Override + public final boolean isCallTargetCloningAllowed() { + return false; + } + + @Override + public final boolean cloneCallTarget() { + return false; + } + + @Override + public final CallTarget getClonedCallTarget() { + return null; + } + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java index f6150ee5ac2c..6f29ff4edddb 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/bool/IfThenElseNode.java @@ -12,12 +12,13 @@ type = "Boolean", name = "if_then_else", description = "Performs the standard if-then-else control flow operation.") -public class IfThenElseNode extends Node { +public final class IfThenElseNode extends Node { private @Child ThunkExecutorNode leftThunkExecutorNode = ThunkExecutorNode.build(); private @Child ThunkExecutorNode rightThunkExecutorNode = ThunkExecutorNode.build(); private final ConditionProfile condProfile = ConditionProfile.createCountingProfile(); - Object execute(State state, boolean self, @Suspend Object if_true, @Suspend Object if_false) { + public Object execute( + State state, boolean self, @Suspend Object if_true, @Suspend Object if_false) { if (condProfile.profile(self)) { return leftThunkExecutorNode.executeThunk(if_true, state, BaseNode.TailStatus.TAIL_DIRECT); } else { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/debug/DebugEvalNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/debug/DebugEvalNode.java index 8437f4c18ea6..bcf9a54e849c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/debug/DebugEvalNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/debug/DebugEvalNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.debug; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.node.BaseNode; @@ -22,7 +23,8 @@ public class DebugEvalNode extends Node { evalNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT); } - Object execute(CallerInfo callerInfo, State state, Object expression) { + Object execute( + VirtualFrame requestOwnStackFrame, CallerInfo callerInfo, State state, Object expression) { return evalNode.execute(callerInfo, state, expectTextNode.execute(expression)); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowErrorNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowErrorNode.java index 6d11c466f2ce..12ebeb6d4143 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowErrorNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowErrorNode.java @@ -1,5 +1,6 @@ package org.enso.interpreter.node.expression.builtin.error; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.error.DataflowError; @@ -9,7 +10,7 @@ name = "throw", description = "Returns a new value error with given payload.") public class ThrowErrorNode extends Node { - public Object execute(Object payload) { + public Object execute(VirtualFrame giveMeAStackFrame, Object payload) { return DataflowError.withoutTrace(payload, this); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowPanicNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowPanicNode.java index e04a1e5ad174..be8726355ec3 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowPanicNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/error/ThrowPanicNode.java @@ -3,6 +3,7 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; @@ -24,7 +25,7 @@ static ThrowPanicNode build() { return ThrowPanicNodeGen.create(); } - abstract Object execute(Object payload); + abstract Object execute(VirtualFrame giveMeAStackFrame, Object payload); EnsoContext getContext() { return EnsoContext.get(this); @@ -35,6 +36,7 @@ EnsoContext getContext() { "payload.getConstructor().getType() == getContext().getBuiltins().caughtPanic().getType()" }) Object doCaughtPanic( + VirtualFrame frame, Atom payload, @CachedLibrary(limit = "5") InteropLibrary interopLibrary, @CachedLibrary(limit = "5") StructsLibrary structs, diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java index e9b9ed0d44d3..17f1d7ad17a6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/runtime/GetStackTraceNode.java @@ -2,6 +2,7 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.TruffleStackTrace; +import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.BuiltinMethod; import org.enso.interpreter.runtime.data.Array; @@ -13,7 +14,7 @@ description = "Gets the current execution stacktrace.", autoRegister = false) public class GetStackTraceNode extends Node { - Array execute() { + Array execute(VirtualFrame requestOwnStackFrame) { var exception = new PanicException("Stacktrace", this); TruffleStackTrace.fillIn(exception); return stackTraceToArray(exception); diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java index a5589b7dbc93..734a644484ee 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java @@ -159,7 +159,11 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException out.println(" description = \"\"\"\n" + methodDefinition.getDescription() + "\"\"\")"); out.println("public class " + methodDefinition.getClassName() + " extends BuiltinRootNode {"); out.println(" private @Child " + methodDefinition.getOriginalClassName() + " bodyNode;"); - out.println(" private final boolean staticOfInstanceMethod;"); + out.println(" private static final class Internals {"); + out.println(" Internals(boolean s) {"); + out.println(" this.staticOfInstanceMethod = s;"); + out.println(" }"); + out.println(" private final boolean staticOfInstanceMethod;"); out.println(); @@ -167,28 +171,30 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException if (arg.shouldCheckErrors()) { String condName = mkArgumentInternalVarName(arg) + DATAFLOW_ERROR_PROFILE; out.println( - " private final ConditionProfile " + " private final ConditionProfile " + condName + " = ConditionProfile.createCountingProfile();"); } if (arg.isPositional() && !arg.isSelf()) { String branchName = mkArgumentInternalVarName(arg) + PANIC_SENTINEL_PROFILE; - out.println(" private final BranchProfile " + branchName + " = BranchProfile.create();"); + out.println(" private final BranchProfile " + branchName + " = BranchProfile.create();"); } if (arg.shouldCheckWarnings()) { String warningName = mkArgumentInternalVarName(arg) + WARNING_PROFILE; out.println( - " private final BranchProfile " + warningName + " = BranchProfile.create();"); + " private final BranchProfile " + warningName + " = BranchProfile.create();"); } } - out.println(" private final BranchProfile anyWarningsProfile = BranchProfile.create();"); + out.println(" private final BranchProfile anyWarningsProfile = BranchProfile.create();"); + out.println(" }"); + out.println(" private final Internals internals;"); out.println(" private " + methodDefinition.getClassName() + "(EnsoLanguage language, boolean staticOfInstanceMethod) {"); out.println(" super(language);"); out.println(" this.bodyNode = " + methodDefinition.getConstructorExpression() + ";"); - out.println(" this.staticOfInstanceMethod = staticOfInstanceMethod;"); + out.println(" this.internals = new Internals(staticOfInstanceMethod);"); out.println(" }"); out.println(); @@ -223,17 +229,35 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException out.println(" }"); out.println(); + if (!methodDefinition.needsFrame()) { + out.println(" @Override"); + out.println(" public final InlinedCallNode createDirectCallNode() {"); + out.println(" var n = " + methodDefinition.getConstructorExpression() + ";"); + out.println(" return new InlinedCallNode<>(new Internals(internals.staticOfInstanceMethod), n) {"); + out.println(" public Object call(Object[] args) {"); + out.println(" return handleExecute(extra, body, args);"); + out.println(" }"); + out.println(" };"); + out.println(" }"); + } out.println(" @Override"); out.println(" public Object execute(VirtualFrame frame) {"); - out.println(" State state = Function.ArgumentsHelper.getState(frame.getArguments());"); + if (methodDefinition.needsFrame()) { + out.println(" var args = frame.getArguments();"); + } else { + out.println(" return handleExecute(this.internals, bodyNode, frame.getArguments());"); + out.println(" }"); + out.println(" private static Object handleExecute(Internals internals, " + methodDefinition.getOriginalClassName() + " bodyNode, Object[] args) {"); + } + out.println(" var prefix = internals.staticOfInstanceMethod ? 1 : 0;"); + out.println(" State state = Function.ArgumentsHelper.getState(args);"); if (methodDefinition.needsCallerInfo()) { out.println( - " CallerInfo callerInfo = Function.ArgumentsHelper.getCallerInfo(frame.getArguments());"); + " CallerInfo callerInfo = Function.ArgumentsHelper.getCallerInfo(args);"); } out.println( - " Object[] arguments = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());"); - out.println(" int prefix = this.staticOfInstanceMethod ? 1 : 0;"); + " Object[] arguments = Function.ArgumentsHelper.getPositionalArguments(args);"); List<String> callArgNames = new ArrayList<>(); for (MethodDefinition.ArgumentDefinition arg : methodDefinition.getArguments()) { @@ -260,7 +284,7 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException String executeCall = "bodyNode.execute(" + String.join(", ", callArgNames) + ")"; if (warningsPossible) { out.println(" if (anyWarnings) {"); - out.println(" anyWarningsProfile.enter();"); + out.println(" internals.anyWarningsProfile.enter();"); out.println(" Object result = " + executeCall + ";"); out.println(" return WithWarnings.appendTo(result, gatheredWarnings);"); out.println(" } else {"); @@ -296,7 +320,7 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException out.println(" @Override"); out.println(" protected RootNode cloneUninitialized() {"); - out.println(" return new " + methodDefinition.getClassName() + "(EnsoLanguage.get(this), this.staticOfInstanceMethod);"); + out.println(" return new " + methodDefinition.getClassName() + "(EnsoLanguage.get(this), internals.staticOfInstanceMethod);"); out.println(" }"); out.println(); @@ -334,7 +358,7 @@ private void generateArgumentRead( if (arg.shouldCheckErrors()) { String condProfile = mkArgumentInternalVarName(arg) + DATAFLOW_ERROR_PROFILE; out.println( - " if (" + " if (internals." + condProfile + ".profile(TypesGen.isDataflowError(" + argReference @@ -350,7 +374,7 @@ private void generateArgumentRead( " if (TypesGen.isPanicSentinel(" + argReference + ")) {\n" - + " " + + " internals." + branchProfile + ".enter();\n" + " throw TypesGen.asPanicSentinel(" @@ -432,7 +456,7 @@ private void generateCheckedArgumentRead( " " + varName + " = " + castName + "(" + argsArray + "[arg" + arg.getPosition() + "Idx]);"); out.println(" } catch (UnexpectedResultException e) {"); out.println(" com.oracle.truffle.api.CompilerDirectives.transferToInterpreter();"); - out.println(" var builtins = EnsoContext.get(this).getBuiltins();"); + out.println(" var builtins = EnsoContext.get(bodyNode).getBuiltins();"); out.println(" var ensoTypeName = org.enso.interpreter.runtime.type.ConstantsGen.getEnsoTypeName(\"" + builtinName + "\");"); out.println(" var error = (ensoTypeName != null)"); out.println(" ? builtins.error().makeTypeError(builtins.fromTypeSystem(ensoTypeName), arguments[arg" @@ -447,7 +471,7 @@ private void generateCheckedArgumentRead( + " expected a " + builtinName + "\");"); - out.println(" throw new PanicException(error,this);"); + out.println(" throw new PanicException(error, bodyNode);"); out.println(" }"); } @@ -467,7 +491,7 @@ private boolean generateWarningsCheck( " if (" + arrayRead(argumentsArray, arg.getPosition()) + " instanceof WithWarnings) {"); - out.println(" " + mkArgumentInternalVarName(arg) + WARNING_PROFILE + ".enter();"); + out.println(" internals." + mkArgumentInternalVarName(arg) + WARNING_PROFILE + ".enter();"); out.println(" anyWarnings = true;"); out.println( " WithWarnings withWarnings = (WithWarnings) " @@ -478,7 +502,7 @@ private boolean generateWarningsCheck( + arrayRead(argumentsArray, arg.getPosition()) + " = withWarnings.getValue();"); out.println( - " gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(this));"); + " gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(bodyNode));"); out.println(" }"); } return true; diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java index b5faa71b379b..812a2bd5388e 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/model/MethodDefinition.java @@ -25,6 +25,7 @@ public class MethodDefinition { private final List<ArgumentDefinition> arguments; private final Set<String> imports; private final boolean needsCallerInfo; + private final boolean needsFrame; private final String constructorExpression; /** @@ -45,6 +46,7 @@ public MethodDefinition(String packageName, TypeElement element, ExecutableEleme this.arguments = initArguments(execute); this.imports = initImports(); this.needsCallerInfo = arguments.stream().anyMatch(ArgumentDefinition::isCallerInfo); + this.needsFrame = arguments.stream().anyMatch(ArgumentDefinition::isFrame); this.constructorExpression = initConstructor(element); } @@ -194,6 +196,11 @@ public boolean needsCallerInfo() { return needsCallerInfo; } + /** @return whether this method requires virtual frame. */ + public boolean needsFrame() { + return needsFrame; + } + public String getConstructorExpression() { return constructorExpression; } diff --git a/test/Tests/src/Semantic/Warnings_Spec.enso b/test/Tests/src/Semantic/Warnings_Spec.enso index d94fa98f64ae..e9096c541b63 100644 --- a/test/Tests/src/Semantic/Warnings_Spec.enso +++ b/test/Tests/src/Semantic/Warnings_Spec.enso @@ -154,7 +154,7 @@ spec = Test.group "Dataflow Warnings" <| r = reassign_test x warn = Warning.get_all r . first reassignments = warn.reassignments.map .name - reassignments.should_equal ['Warnings_Spec.poly_sum', 'Small_Integer.+', 'Warnings_Spec.get_foo', 'Wrap.Value', 'Warnings_Spec.unwrap', 'Warnings_Spec.rewrap', 'Wrap.Value'] + reassignments.should_equal ['Warnings_Spec.poly_sum', 'Warnings_Spec.reassign_test', 'Warnings_Spec.get_foo', 'Wrap.Value', 'Warnings_Spec.unwrap', 'Warnings_Spec.rewrap', 'Wrap.Value'] Test.specify "should allow to set all warnings" <| warned = Warning.attach 1 <| Warning.attach 2 <| Warning.attach 3 <| Warning.attach 4 "foo"