mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
Speed cascade of if statements up (#6255)
Fixes #5709. We have a test and a generic fix that improves inlining of every builtin. Everything seems to be faster.
This commit is contained in:
parent
9ebda56385
commit
a74933d10f
@ -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
|
||||
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
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 @@ public abstract class ExecuteCallNode extends Node {
|
||||
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.
|
||||
*
|
||||
|
@ -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 @@ public abstract class BuiltinRootNode extends RootNode {
|
||||
*/
|
||||
@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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,13 @@ import org.enso.interpreter.runtime.state.State;
|
||||
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 {
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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 @@ import org.enso.interpreter.runtime.error.DataflowError;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.builtin.error;
|
||||
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 @@ public abstract class ThrowPanicNode extends Node {
|
||||
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 @@ public abstract class ThrowPanicNode extends Node {
|
||||
"payload.getConstructor().getType() == getContext().getBuiltins().caughtPanic().getType()"
|
||||
})
|
||||
Object doCaughtPanic(
|
||||
VirtualFrame frame,
|
||||
Atom payload,
|
||||
@CachedLibrary(limit = "5") InteropLibrary interopLibrary,
|
||||
@CachedLibrary(limit = "5") StructsLibrary structs,
|
||||
|
@ -2,6 +2,7 @@ package org.enso.interpreter.node.expression.builtin.runtime;
|
||||
|
||||
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 @@ import org.enso.interpreter.runtime.error.PanicException;
|
||||
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);
|
||||
|
@ -159,7 +159,11 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
|
||||
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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
if (arg.shouldCheckErrors()) {
|
||||
String condProfile = mkArgumentInternalVarName(arg) + DATAFLOW_ERROR_PROFILE;
|
||||
out.println(
|
||||
" if ("
|
||||
" if (internals."
|
||||
+ condProfile
|
||||
+ ".profile(TypesGen.isDataflowError("
|
||||
+ argReference
|
||||
@ -350,7 +374,7 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
" if (TypesGen.isPanicSentinel("
|
||||
+ argReference
|
||||
+ ")) {\n"
|
||||
+ " "
|
||||
+ " internals."
|
||||
+ branchProfile
|
||||
+ ".enter();\n"
|
||||
+ " throw TypesGen.asPanicSentinel("
|
||||
@ -432,7 +456,7 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
" " + 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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
+ " expected a "
|
||||
+ builtinName
|
||||
+ "\");");
|
||||
out.println(" throw new PanicException(error,this);");
|
||||
out.println(" throw new PanicException(error, bodyNode);");
|
||||
out.println(" }");
|
||||
}
|
||||
|
||||
@ -467,7 +491,7 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
" 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 @@ public class MethodProcessor extends BuiltinsMetadataProcessor<MethodProcessor.M
|
||||
+ arrayRead(argumentsArray, arg.getPosition())
|
||||
+ " = withWarnings.getValue();");
|
||||
out.println(
|
||||
" gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(this));");
|
||||
" gatheredWarnings = gatheredWarnings.prepend(withWarnings.getReassignedWarnings(bodyNode));");
|
||||
out.println(" }");
|
||||
}
|
||||
return true;
|
||||
|
@ -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 class MethodDefinition {
|
||||
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 class MethodDefinition {
|
||||
return needsCallerInfo;
|
||||
}
|
||||
|
||||
/** @return whether this method requires virtual frame. */
|
||||
public boolean needsFrame() {
|
||||
return needsFrame;
|
||||
}
|
||||
|
||||
public String getConstructorExpression() {
|
||||
return constructorExpression;
|
||||
}
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user