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:
Jaroslav Tulach 2023-04-14 15:27:23 +02:00 committed by GitHub
parent 9ebda56385
commit a74933d10f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 386 additions and 37 deletions

View File

@ -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

View File

@ -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)
)
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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.
*

View File

@ -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;
}
}
}

View File

@ -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 {

View File

@ -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));
}
}

View File

@ -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);
}
}

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -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"