mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 20:31:45 +03:00
Speedup DataflowError.withDefaultTrace (#11153)
Improves the speed of `ExecutionEnvironment.hasContextEnabled`. # Important Notes Local speedup of `Map_Error_Benchmark_Vector_ignore.Map_Id_All_Errors` benchmark is roughly ???.
This commit is contained in:
parent
33904912ee
commit
c8393095b5
@ -4,6 +4,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
|||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||||
import org.enso.interpreter.runtime.error.DataflowError;
|
import org.enso.interpreter.runtime.error.DataflowError;
|
||||||
|
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
|
||||||
import org.enso.interpreter.runtime.state.State;
|
import org.enso.interpreter.runtime.state.State;
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
@ -12,7 +13,9 @@ import org.enso.interpreter.runtime.state.State;
|
|||||||
description = "Returns a new value error with given payload.",
|
description = "Returns a new value error with given payload.",
|
||||||
inlineable = true)
|
inlineable = true)
|
||||||
public class ThrowErrorNode extends Node {
|
public class ThrowErrorNode extends Node {
|
||||||
|
private @Child HasContextEnabledNode hasContextEnabledNode = HasContextEnabledNode.create();
|
||||||
|
|
||||||
public Object execute(VirtualFrame giveMeAStackFrame, State state, Object payload) {
|
public Object execute(VirtualFrame giveMeAStackFrame, State state, Object payload) {
|
||||||
return DataflowError.withDefaultTrace(state, payload, this);
|
return DataflowError.withDefaultTrace(state, payload, this, hasContextEnabledNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import org.enso.interpreter.runtime.EnsoContext;
|
|||||||
import org.enso.interpreter.runtime.data.atom.Atom;
|
import org.enso.interpreter.runtime.data.atom.Atom;
|
||||||
import org.enso.interpreter.runtime.error.PanicException;
|
import org.enso.interpreter.runtime.error.PanicException;
|
||||||
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
||||||
|
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
|
||||||
|
|
||||||
@BuiltinMethod(
|
@BuiltinMethod(
|
||||||
type = "Context",
|
type = "Context",
|
||||||
@ -14,6 +15,7 @@ import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
|||||||
description = "Check if the context is enabled in the provided execution environment.")
|
description = "Check if the context is enabled in the provided execution environment.")
|
||||||
public class ContextIsEnabledNode extends Node {
|
public class ContextIsEnabledNode extends Node {
|
||||||
private @Child ExpectStringNode expectStringNode = ExpectStringNode.build();
|
private @Child ExpectStringNode expectStringNode = ExpectStringNode.build();
|
||||||
|
private @Child HasContextEnabledNode hasContextEnabledNode = HasContextEnabledNode.create();
|
||||||
|
|
||||||
Object execute(Atom self, Object environmentName) {
|
Object execute(Atom self, Object environmentName) {
|
||||||
String envName = expectStringNode.execute(environmentName);
|
String envName = expectStringNode.execute(environmentName);
|
||||||
@ -26,6 +28,6 @@ public class ContextIsEnabledNode extends Node {
|
|||||||
.makeUnimplemented("execution environment mismatch");
|
.makeUnimplemented("execution environment mismatch");
|
||||||
throw new PanicException(error, this);
|
throw new PanicException(error, this);
|
||||||
}
|
}
|
||||||
return currentEnv.hasContextEnabled(self.getConstructor().getName());
|
return hasContextEnabledNode.executeHasContextEnabled(currentEnv, self.getConstructor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ import org.enso.interpreter.runtime.instrument.NotificationHandler;
|
|||||||
import org.enso.interpreter.runtime.scope.TopLevelScope;
|
import org.enso.interpreter.runtime.scope.TopLevelScope;
|
||||||
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
import org.enso.interpreter.runtime.state.ExecutionEnvironment;
|
||||||
import org.enso.interpreter.runtime.state.State;
|
import org.enso.interpreter.runtime.state.State;
|
||||||
|
import org.enso.interpreter.runtime.state.WithContextNode;
|
||||||
import org.enso.interpreter.runtime.util.TruffleFileSystem;
|
import org.enso.interpreter.runtime.util.TruffleFileSystem;
|
||||||
import org.enso.librarymanager.ProjectLoadingFailure;
|
import org.enso.librarymanager.ProjectLoadingFailure;
|
||||||
import org.enso.librarymanager.resolved.LibraryRoot;
|
import org.enso.librarymanager.resolved.LibraryRoot;
|
||||||
@ -894,7 +895,9 @@ public final class EnsoContext {
|
|||||||
public ExecutionEnvironment enableExecutionEnvironment(Atom context, String environmentName) {
|
public ExecutionEnvironment enableExecutionEnvironment(Atom context, String environmentName) {
|
||||||
ExecutionEnvironment original = globalExecutionEnvironment;
|
ExecutionEnvironment original = globalExecutionEnvironment;
|
||||||
if (original.getName().equals(environmentName)) {
|
if (original.getName().equals(environmentName)) {
|
||||||
setExecutionEnvironment(original.withContextEnabled(context));
|
var newExecEnv =
|
||||||
|
WithContextNode.getUncached().executeEnvironmentUpdate(original, context, true);
|
||||||
|
setExecutionEnvironment(newExecEnv);
|
||||||
}
|
}
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
@ -909,7 +912,9 @@ public final class EnsoContext {
|
|||||||
public ExecutionEnvironment disableExecutionEnvironment(Atom context, String environmentName) {
|
public ExecutionEnvironment disableExecutionEnvironment(Atom context, String environmentName) {
|
||||||
ExecutionEnvironment original = globalExecutionEnvironment;
|
ExecutionEnvironment original = globalExecutionEnvironment;
|
||||||
if (original.getName().equals(environmentName)) {
|
if (original.getName().equals(environmentName)) {
|
||||||
setExecutionEnvironment(original.withContextDisabled(context));
|
var newExecEnv =
|
||||||
|
WithContextNode.getUncached().executeEnvironmentUpdate(original, context, false);
|
||||||
|
setExecutionEnvironment(newExecEnv);
|
||||||
}
|
}
|
||||||
return original;
|
return original;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ import org.enso.interpreter.runtime.data.EnsoObject;
|
|||||||
import org.enso.interpreter.runtime.data.Type;
|
import org.enso.interpreter.runtime.data.Type;
|
||||||
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
|
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
|
||||||
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
||||||
|
import org.enso.interpreter.runtime.state.HasContextEnabledNode;
|
||||||
import org.enso.interpreter.runtime.state.State;
|
import org.enso.interpreter.runtime.state.State;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,15 +52,18 @@ public final class DataflowError extends AbstractTruffleException implements Ens
|
|||||||
* @param location the node in which the error was created
|
* @param location the node in which the error was created
|
||||||
* @return a new dataflow error
|
* @return a new dataflow error
|
||||||
*/
|
*/
|
||||||
public static DataflowError withDefaultTrace(State state, Object payload, Node location) {
|
public static DataflowError withDefaultTrace(
|
||||||
|
State state, Object payload, Node location, HasContextEnabledNode hasContextEnabledNode) {
|
||||||
assert payload != null;
|
assert payload != null;
|
||||||
|
var ensoCtx = EnsoContext.get(location);
|
||||||
|
var dataflowStacktraceCtx = ensoCtx.getBuiltins().context().getDataflowStackTrace();
|
||||||
boolean attachFullStackTrace =
|
boolean attachFullStackTrace =
|
||||||
state == null
|
state == null
|
||||||
|| EnsoContext.get(location)
|
|| hasContextEnabledNode.executeHasContextEnabled(
|
||||||
.getExecutionEnvironment()
|
ensoCtx.getExecutionEnvironment(), dataflowStacktraceCtx);
|
||||||
.hasContextEnabled("Dataflow_Stack_Trace");
|
|
||||||
if (attachFullStackTrace) {
|
if (attachFullStackTrace) {
|
||||||
var result = new DataflowError(payload, UNLIMITED_STACK_TRACE, location);
|
var result =
|
||||||
|
new DataflowError(payload, AbstractTruffleException.UNLIMITED_STACK_TRACE, location);
|
||||||
TruffleStackTrace.fillIn(result);
|
TruffleStackTrace.fillIn(result);
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
@ -68,8 +72,9 @@ public final class DataflowError extends AbstractTruffleException implements Ens
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Slow version of {@link #withDefaultTrace(State, Object, Node, HasContextEnabledNode)}. */
|
||||||
public static DataflowError withDefaultTrace(Object payload, Node location) {
|
public static DataflowError withDefaultTrace(Object payload, Node location) {
|
||||||
return withDefaultTrace(null, payload, location);
|
return withDefaultTrace(null, payload, location, HasContextEnabledNode.getUncached());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
package org.enso.interpreter.runtime.state;
|
||||||
|
|
||||||
|
/** Fields correspond to the constructors of {@code Standard.Base.Runtime.Context} builtin type. */
|
||||||
|
record ContextPermissions(boolean input, boolean output, boolean dataflowStacktrace) {}
|
@ -1,18 +1,10 @@
|
|||||||
package org.enso.interpreter.runtime.state;
|
package org.enso.interpreter.runtime.state;
|
||||||
|
|
||||||
import org.enso.interpreter.node.expression.builtin.runtime.Context;
|
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
|
||||||
import org.enso.interpreter.runtime.data.atom.Atom;
|
|
||||||
|
|
||||||
public class ExecutionEnvironment {
|
public class ExecutionEnvironment {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
// Ideally we would "just" use a map here. But that leads
|
final ContextPermissions permissions;
|
||||||
// to native image build problems. This in turn leads to
|
|
||||||
// TruffleBoundary annotations which in turn leads to slow path.
|
|
||||||
private final String[] keys;
|
|
||||||
private final Boolean[] permissions;
|
|
||||||
|
|
||||||
public static final String LIVE_ENVIRONMENT_NAME = "live";
|
public static final String LIVE_ENVIRONMENT_NAME = "live";
|
||||||
|
|
||||||
@ -22,21 +14,18 @@ public class ExecutionEnvironment {
|
|||||||
public static final ExecutionEnvironment DESIGN =
|
public static final ExecutionEnvironment DESIGN =
|
||||||
new ExecutionEnvironment(DESIGN_ENVIRONMENT_NAME);
|
new ExecutionEnvironment(DESIGN_ENVIRONMENT_NAME);
|
||||||
|
|
||||||
private static final ExecutionEnvironment initLive(String name) {
|
private static ExecutionEnvironment initLive(String name) {
|
||||||
String[] keys = new String[] {Context.INPUT_NAME, Context.OUTPUT_NAME};
|
var permissions = new ContextPermissions(true, true, false);
|
||||||
Boolean[] permissions = new Boolean[] {true, true};
|
return new ExecutionEnvironment(name, permissions);
|
||||||
return new ExecutionEnvironment(name, keys, permissions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecutionEnvironment(String name) {
|
public ExecutionEnvironment(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.keys = new String[0];
|
this.permissions = new ContextPermissions(false, false, false);
|
||||||
this.permissions = new Boolean[0];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ExecutionEnvironment(String name, String[] keys, Boolean[] permissions) {
|
ExecutionEnvironment(String name, ContextPermissions permissions) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.keys = keys;
|
|
||||||
this.permissions = permissions;
|
this.permissions = permissions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,60 +33,6 @@ public class ExecutionEnvironment {
|
|||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExecutionEnvironment withContextEnabled(Atom context) {
|
|
||||||
return update(context, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExecutionEnvironment withContextDisabled(Atom context) {
|
|
||||||
return update(context, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ExecutionEnvironment update(Atom context, boolean value) {
|
|
||||||
assert context.getConstructor().getType()
|
|
||||||
== EnsoContext.get(null).getBuiltins().context().getType();
|
|
||||||
int keyFound = -1;
|
|
||||||
for (int i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i].equals(context.getConstructor().getName())) {
|
|
||||||
keyFound = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String[] keys1;
|
|
||||||
Boolean[] permissions1;
|
|
||||||
if (keyFound != -1) {
|
|
||||||
keys1 = cloneArray(keys, new String[keys.length]);
|
|
||||||
permissions1 = cloneArray(permissions, new Boolean[keys.length]);
|
|
||||||
permissions1[keyFound] = value;
|
|
||||||
} else {
|
|
||||||
keys1 = cloneArray(keys, new String[keys.length + 1]);
|
|
||||||
permissions1 = cloneArray(permissions, new Boolean[keys.length + 1]);
|
|
||||||
keyFound = keys.length;
|
|
||||||
keys1[keyFound] = context.getConstructor().getName();
|
|
||||||
permissions1[keyFound] = value;
|
|
||||||
}
|
|
||||||
return new ExecutionEnvironment(name, keys1, permissions1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T[] cloneArray(T[] fromArray, T[] toArray) {
|
|
||||||
for (int i = 0; i < fromArray.length; i++) {
|
|
||||||
toArray[i] = fromArray[i];
|
|
||||||
}
|
|
||||||
return toArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean hasContextEnabled(String context) {
|
|
||||||
int keyFound = -1;
|
|
||||||
for (int i = 0; i < keys.length; i++) {
|
|
||||||
if (keys[i].equals(context)) {
|
|
||||||
keyFound = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (keyFound != -1) {
|
|
||||||
return permissions[keyFound];
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ExecutionEnvironment forName(String name) {
|
public static ExecutionEnvironment forName(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case LIVE_ENVIRONMENT_NAME:
|
case LIVE_ENVIRONMENT_NAME:
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
package org.enso.interpreter.runtime.state;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
|
import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||||
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
|
import org.enso.interpreter.runtime.data.atom.AtomConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node representing the functionality done by {@code Standard.Base.Runtime.Context.is_enabled}.
|
||||||
|
*/
|
||||||
|
@GenerateUncached
|
||||||
|
public abstract class HasContextEnabledNode extends Node {
|
||||||
|
|
||||||
|
public static HasContextEnabledNode getUncached() {
|
||||||
|
return HasContextEnabledNodeGen.getUncached();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static HasContextEnabledNode create() {
|
||||||
|
return HasContextEnabledNodeGen.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the context represented by the given {@code runtimeCtxCtor} is enabled in the
|
||||||
|
* given {@code executionEnvironment}.
|
||||||
|
*
|
||||||
|
* @param runtimeCtxCtor Constructor of {@code Standard.Base.Runtime.Context}.
|
||||||
|
*/
|
||||||
|
public abstract boolean executeHasContextEnabled(
|
||||||
|
ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor);
|
||||||
|
|
||||||
|
@Specialization(guards = "executionEnvironment == cachedEnv", limit = "3")
|
||||||
|
boolean cachedHasContextEnabled(
|
||||||
|
ExecutionEnvironment executionEnvironment,
|
||||||
|
AtomConstructor runtimeCtxCtor,
|
||||||
|
@Cached("executionEnvironment") ExecutionEnvironment cachedEnv) {
|
||||||
|
return doIt(cachedEnv, runtimeCtxCtor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Specialization(replaces = "cachedHasContextEnabled")
|
||||||
|
boolean uncachedHasContextEnabled(
|
||||||
|
ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor) {
|
||||||
|
return doIt(executionEnvironment, runtimeCtxCtor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean doIt(ExecutionEnvironment executionEnvironment, AtomConstructor runtimeCtxCtor) {
|
||||||
|
var ensoCtx = EnsoContext.get(this);
|
||||||
|
var contextBuiltin = ensoCtx.getBuiltins().context();
|
||||||
|
if (runtimeCtxCtor == contextBuiltin.getInput()) {
|
||||||
|
return executionEnvironment.permissions.input();
|
||||||
|
} else if (runtimeCtxCtor == contextBuiltin.getOutput()) {
|
||||||
|
return executionEnvironment.permissions.output();
|
||||||
|
} else if (runtimeCtxCtor == contextBuiltin.getDataflowStackTrace()) {
|
||||||
|
return executionEnvironment.permissions.dataflowStacktrace();
|
||||||
|
} else {
|
||||||
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
throw ensoCtx.raiseAssertionPanic(this, "Unknown context: " + runtimeCtxCtor, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package org.enso.interpreter.runtime.state;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.dsl.GenerateUncached;
|
||||||
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
|
import org.enso.interpreter.runtime.data.atom.Atom;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node representing functionality done by {@code Standard.Base.Runtime.Context.with_enabled} and
|
||||||
|
* {@code Standard.Base.Runtime.Context.with_disabled}. That is, it enables or disables the given
|
||||||
|
* context in the current {@link ExecutionEnvironment execution environment}.
|
||||||
|
*/
|
||||||
|
@GenerateUncached
|
||||||
|
public abstract class WithContextNode extends Node {
|
||||||
|
public static WithContextNode getUncached() {
|
||||||
|
return WithContextNodeGen.getUncached();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WithContextNode create() {
|
||||||
|
return WithContextNodeGen.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link ExecutionEnvironment} with the given context enabled or disabled.
|
||||||
|
*
|
||||||
|
* @param current Current execution environment.
|
||||||
|
* @param context Atom of type {@code Standard.Base.Runtime.Context}.
|
||||||
|
* @param enabled Whether to enable or disable the context.
|
||||||
|
*/
|
||||||
|
public abstract ExecutionEnvironment executeEnvironmentUpdate(
|
||||||
|
ExecutionEnvironment current, Atom context, boolean enabled);
|
||||||
|
|
||||||
|
@Specialization
|
||||||
|
ExecutionEnvironment doIt(ExecutionEnvironment current, Atom context, boolean enabled) {
|
||||||
|
var ensoCtx = EnsoContext.get(this);
|
||||||
|
var contextBuiltin = ensoCtx.getBuiltins().context();
|
||||||
|
if (context.getConstructor().getType() != contextBuiltin.getType()) {
|
||||||
|
throw ensoCtx.raiseAssertionPanic(this, "Invalid context type", null);
|
||||||
|
}
|
||||||
|
var ctor = context.getConstructor();
|
||||||
|
ContextPermissions newPermissions;
|
||||||
|
if (ctor == contextBuiltin.getInput()) {
|
||||||
|
newPermissions =
|
||||||
|
new ContextPermissions(
|
||||||
|
enabled, current.permissions.output(), current.permissions.dataflowStacktrace());
|
||||||
|
} else if (ctor == contextBuiltin.getOutput()) {
|
||||||
|
newPermissions =
|
||||||
|
new ContextPermissions(
|
||||||
|
current.permissions.input(), enabled, current.permissions.dataflowStacktrace());
|
||||||
|
} else if (ctor == contextBuiltin.getDataflowStackTrace()) {
|
||||||
|
newPermissions =
|
||||||
|
new ContextPermissions(
|
||||||
|
current.permissions.input(), current.permissions.output(), enabled);
|
||||||
|
} else {
|
||||||
|
throw ensoCtx.raiseAssertionPanic(this, "Unknown context: " + ctor, null);
|
||||||
|
}
|
||||||
|
return new ExecutionEnvironment(current.getName(), newPermissions);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user