From e3a7021806e72a1d5614794500599bc08d048cbc Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Mon, 26 Apr 2021 22:36:35 +0100 Subject: [PATCH 01/12] Make dispatch chain walk non-recursive Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 230 ++++++++++++------ .../nodes/dispatch/AbstractDispatchNode.java | 62 +++-- .../nodes/dispatch/CachedDispatchNode.java | 36 --- .../nodes/dispatch/CachedDnuNode.java | 49 ++-- .../nodes/dispatch/CachedExprNode.java | 45 ---- .../nodes/dispatch/GenericDispatchNode.java | 50 ++-- .../dispatch/UninitializedDispatchNode.java | 96 -------- 7 files changed, 238 insertions(+), 330 deletions(-) delete mode 100644 src/trufflesom/interpreter/nodes/dispatch/CachedDispatchNode.java delete mode 100644 src/trufflesom/interpreter/nodes/dispatch/CachedExprNode.java delete mode 100644 src/trufflesom/interpreter/nodes/dispatch/UninitializedDispatchNode.java diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 84b676c00..45ba96f8d 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -1,26 +1,33 @@ package trufflesom.interpreter.nodes; -import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CallTarget; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.source.SourceSection; import bd.primitives.Specializer; import bd.primitives.nodes.PreevaluatedExpression; import bd.tools.nodes.Invocation; -import trufflesom.interpreter.TruffleCompiler; +import trufflesom.interpreter.Types; import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode; -import trufflesom.interpreter.nodes.dispatch.DispatchChain.Cost; +import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedDispatchNode; +import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedExprNode; +import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; +import trufflesom.interpreter.nodes.dispatch.CachedDnuNode; +import trufflesom.interpreter.nodes.dispatch.DispatchGuard; import trufflesom.interpreter.nodes.dispatch.GenericDispatchNode; -import trufflesom.interpreter.nodes.dispatch.UninitializedDispatchNode; import trufflesom.primitives.Primitives; import trufflesom.vm.NotYetImplementedException; import trufflesom.vm.Universe; import trufflesom.vmobjects.SClass; import trufflesom.vmobjects.SInvokable; +import trufflesom.vmobjects.SObject; import trufflesom.vmobjects.SSymbol; @@ -32,8 +39,7 @@ public static ExpressionNode create(final SSymbol selector, Specializer specializer = prims.getParserSpecializer(selector, arguments); if (specializer == null) { - return new UninitializedMessageSendNode( - selector, arguments, universe).initialize(source); + return new GenericMessageSendNode(selector, arguments, universe).initialize(source); } ExpressionNode newNode = specializer.create(null, arguments, source, universe); @@ -44,14 +50,14 @@ public static ExpressionNode create(final SSymbol selector, public static AbstractMessageSendNode createForPerformNodes(final SSymbol selector, final SourceSection source, final Universe universe) { - return new UninitializedMessageSendNode(selector, NO_ARGS, universe).initialize(source); + return new GenericMessageSendNode(selector, NO_ARGS, universe).initialize(source); } public static GenericMessageSendNode createGeneric(final SSymbol selector, final ExpressionNode[] argumentNodes, final SourceSection source, final Universe universe) { - return new GenericMessageSendNode(selector, argumentNodes, - new UninitializedDispatchNode(selector, universe)).initialize(source); + return new GenericMessageSendNode( + selector, argumentNodes, universe, true).initialize(source); } public static AbstractMessageSendNode createSuperSend(final SClass superClass, @@ -102,109 +108,171 @@ private Object[] evaluateArguments(final VirtualFrame frame) { public abstract int getNumberOfArguments(); } - public static final class UninitializedMessageSendNode extends AbstractMessageSendNode { + public static final class GenericMessageSendNode + extends AbstractMessageSendNode { + + private final SSymbol selector; + private final Universe universe; + + private final int numberOfSignatureArguments; + + @CompilationFinal private boolean triedEager; - protected final SSymbol selector; - protected final Universe universe; + @Child private GuardedDispatchNode dispatchCache; - protected UninitializedMessageSendNode(final SSymbol selector, - final ExpressionNode[] arguments, final Universe universe) { + private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, + final Universe universe, final boolean triedEager) { super(arguments); this.selector = selector; this.universe = universe; + this.triedEager = triedEager; + this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments(); } - @Override - public String toString() { - return getClass().getSimpleName() + "(" + selector.getString() + ")"; + private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, + final Universe universe) { + this(selector, arguments, universe, false); } @Override - public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { - return specialize(arguments).doPreEvaluated(frame, arguments); - } - - private PreevaluatedExpression specialize(final Object[] arguments) { - TruffleCompiler.transferToInterpreterAndInvalidate("Specialize Message Node"); - - // We treat super sends separately for simplicity, might not be the - // optimal solution, especially in cases were the knowledge of the - // receiver class also allows us to do more specific things, but for the - // moment we will leave it at this. - // TODO: revisit, and also do more specific optimizations for super sends. - - Primitives prims = universe.getPrimitives(); - - Specializer specializer = - prims.getEagerSpecializer(selector, arguments, argumentNodes); - - if (specializer != null) { - PreevaluatedExpression newNode = - (PreevaluatedExpression) specializer.create(arguments, argumentNodes, - sourceSection, universe); + @ExplodeLoop + public Object doPreEvaluated(final VirtualFrame frame, + final Object[] arguments) { - return (PreevaluatedExpression) replace((ExpressionNode) newNode); + GuardedDispatchNode cache = dispatchCache; + + Object rcvr = arguments[0]; + + while (cache != null) { + try { + if (cache.entryMatches(rcvr)) { + return cache.doPreEvaluated(frame, arguments); + } + } catch (InvalidAssumptionException e) { + // Remove invalid node from dispatch chain + CompilerDirectives.transferToInterpreterAndInvalidate(); + if (cache.getParent() == this) { + if (cache.next == null) { + dispatchCache = null; + cache = null; + } else { + dispatchCache = insert(cache.next); + cache = cache.next; + } + } else { + GuardedDispatchNode parent = (GuardedDispatchNode) cache.getParent(); + if (cache.next == null) { + parent.next = null; + cache = null; + } else { + parent.next = insert(cache.next); + cache = cache.next; + } + } + continue; + } + // cache guard + // --> apply cache + cache = cache.next; } - return makeGenericSend(); - } + CompilerDirectives.transferToInterpreterAndInvalidate(); - private GenericMessageSendNode makeGenericSend() { - GenericMessageSendNode send = new GenericMessageSendNode(selector, argumentNodes, - new UninitializedDispatchNode(selector, universe)).initialize(sourceSection); - return replace(send); + return specialize(arguments).doPreEvaluated(frame, arguments); } @Override - public SSymbol getInvocationIdentifier() { - return selector; + public String toString() { + return "GMsgSend(" + selector.getString() + ")"; } @Override - public int getNumberOfArguments() { - return selector.getNumberOfSignatureArguments(); + public NodeCost getCost() { + return NodeCost.NONE; } - } - // TODO: currently, we do not only specialize the given stuff above, but also what has been - // classified as 'value' sends in the OMOP branch. Is that a problem? + private PreevaluatedExpression specialize(final Object[] arguments) { + if (!triedEager) { + triedEager = true; + PreevaluatedExpression eager = attemptEagerSpecialization(arguments); + if (eager != null) { + return eager; + } + } - public static final class GenericMessageSendNode - extends AbstractMessageSendNode { + final GuardedDispatchNode first = dispatchCache; + GuardedDispatchNode cache = first; + int cacheSize = 0; + while (cache != null) { + cache = cache.next; + cacheSize += 1; + } - private final SSymbol selector; - private final int numberOfSignatureArguments; + Object rcvr = arguments[0]; + assert rcvr != null; - @Child private AbstractDispatchNode dispatchNode; + if (rcvr instanceof SObject) { + SObject r = (SObject) rcvr; + if (r.updateLayoutToMatchClass() && first != null) { + // if the dispatchCache is null, we end up here, so continue directly below instead + // otherwise, let's retry the cache! + return this; + } + } - private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, - final AbstractDispatchNode dispatchNode) { - super(arguments); - this.selector = selector; - this.dispatchNode = dispatchNode; - this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments(); - } + if (cacheSize < AbstractDispatchNode.INLINE_CACHE_SIZE) { + SClass rcvrClass = Types.getClassOf(rcvr, universe); + SInvokable method = rcvrClass.lookupInvokable(selector); + CallTarget callTarget = null; + PreevaluatedExpression expr = null; + if (method != null) { + if (method.isTrivial()) { + expr = method.copyTrivialNode(); + assert expr != null; + } else { + callTarget = method.getCallTarget(); + } + } + + DispatchGuard guard = DispatchGuard.create(rcvr); + + GuardedDispatchNode node; + if (expr != null) { + node = new CachedExprNode(guard, expr); + } else if (method != null) { + node = new CachedDispatchNode(guard, callTarget); + } else { + node = new CachedDnuNode(rcvrClass, guard, selector); + } + + if (first != null) { + node.next = node.insertHere(first); + } + dispatchCache = insert(node); + return node; + } - @Override - public Object doPreEvaluated(final VirtualFrame frame, - final Object[] arguments) { - return dispatchNode.executeDispatch(frame, arguments); + // the chain is longer than the maximum defined by INLINE_CACHE_SIZE and + // thus, this callsite is considered to be megaprophic, and we generalize it. + GenericDispatchNode generic = new GenericDispatchNode(selector, universe); + dispatchCache = insert(generic); + return generic; } - public void replaceDispatchListHead( - final GenericDispatchNode replacement) { - CompilerAsserts.neverPartOfCompilation(); - dispatchNode.replace(replacement); - } + private PreevaluatedExpression attemptEagerSpecialization(final Object[] arguments) { + Primitives prims = universe.getPrimitives(); - @Override - public String toString() { - return "GMsgSend(" + selector.getString() + ")"; - } + Specializer specializer = + prims.getEagerSpecializer(selector, arguments, argumentNodes); - @Override - public NodeCost getCost() { - return Cost.getCost(dispatchNode); + if (specializer != null) { + PreevaluatedExpression newNode = + (PreevaluatedExpression) specializer.create(arguments, argumentNodes, + sourceSection, universe); + + return (PreevaluatedExpression) replace((ExpressionNode) newNode); + } + return null; } @Override diff --git a/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java index f04711087..9e3b12a8d 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java @@ -4,32 +4,66 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.DirectCallNode; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; import com.oracle.truffle.api.nodes.Node; +import bd.primitives.nodes.PreevaluatedExpression; -public abstract class AbstractDispatchNode extends Node implements DispatchChain { + +public abstract class AbstractDispatchNode extends Node implements PreevaluatedExpression { public static final int INLINE_CACHE_SIZE = 6; - public abstract Object executeDispatch(VirtualFrame frame, Object[] arguments); + @Override + public abstract Object doPreEvaluated(VirtualFrame frame, Object[] args); + + public abstract boolean entryMatches(Object rcvr) throws InvalidAssumptionException; + + public abstract static class GuardedDispatchNode extends AbstractDispatchNode { + private final DispatchGuard guard; + + @Child public GuardedDispatchNode next; + + protected GuardedDispatchNode(final DispatchGuard guard) { + this.guard = guard; + } - public abstract static class AbstractCachedDispatchNode - extends AbstractDispatchNode { + @Override + public boolean entryMatches(final Object rcvr) throws InvalidAssumptionException { + return guard.entryMatches(rcvr); + } + + public final T insertHere(final T newChild) { + return super.insert(newChild); + } + } + + public static final class CachedDispatchNode extends GuardedDispatchNode { + + @Child protected DirectCallNode cachedMethod; + + public CachedDispatchNode(final DispatchGuard guard, final CallTarget callTarget) { + super(guard); + cachedMethod = insert(Truffle.getRuntime().createDirectCallNode(callTarget)); + } + + @Override + public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { + return cachedMethod.call(arguments); + } + } - @Child protected DirectCallNode cachedMethod; - @Child protected AbstractDispatchNode nextInCache; + public static final class CachedExprNode extends GuardedDispatchNode { - public AbstractCachedDispatchNode(final CallTarget methodCallTarget, - final AbstractDispatchNode nextInCache) { - DirectCallNode cachedMethod = - Truffle.getRuntime().createDirectCallNode(methodCallTarget); + @Child protected PreevaluatedExpression expr; - this.cachedMethod = cachedMethod; - this.nextInCache = nextInCache; + public CachedExprNode(final DispatchGuard guard, final PreevaluatedExpression expr) { + super(guard); + this.expr = expr; } @Override - public final int lengthOfDispatchChain() { - return 1 + nextInCache.lengthOfDispatchChain(); + public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { + return expr.doPreEvaluated(frame, arguments); } } } diff --git a/src/trufflesom/interpreter/nodes/dispatch/CachedDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/CachedDispatchNode.java deleted file mode 100644 index 6b6c36a2f..000000000 --- a/src/trufflesom/interpreter/nodes/dispatch/CachedDispatchNode.java +++ /dev/null @@ -1,36 +0,0 @@ -package trufflesom.interpreter.nodes.dispatch; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; - -import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.AbstractCachedDispatchNode; - - -public final class CachedDispatchNode extends AbstractCachedDispatchNode { - - private final DispatchGuard guard; - - public CachedDispatchNode(final DispatchGuard guard, - final CallTarget callTarget, final AbstractDispatchNode nextInCache) { - super(callTarget, nextInCache); - this.guard = guard; - } - - @Override - public Object executeDispatch( - final VirtualFrame frame, final Object[] arguments) { - Object rcvr = arguments[0]; - try { - if (guard.entryMatches(rcvr)) { - return cachedMethod.call(arguments); - } else { - return nextInCache.executeDispatch(frame, arguments); - } - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreter(); - return replace(nextInCache).executeDispatch(frame, arguments); - } - } -} diff --git a/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java b/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java index 293e117aa..01096fe13 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java @@ -4,13 +4,14 @@ import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import com.oracle.truffle.api.nodes.DirectCallNode; import trufflesom.interpreter.SArguments; import trufflesom.interpreter.SomLanguage; import trufflesom.interpreter.Types; -import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.AbstractCachedDispatchNode; +import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; import trufflesom.primitives.basics.SystemPrims.PrintStackTracePrim; import trufflesom.vm.Universe; import trufflesom.vm.VmSettings; @@ -18,48 +19,38 @@ import trufflesom.vmobjects.SSymbol; -public final class CachedDnuNode extends AbstractCachedDispatchNode { - private final SSymbol selector; - private final DispatchGuard guard; +public final class CachedDnuNode extends GuardedDispatchNode { + private final SSymbol selector; + + @Child protected DirectCallNode cachedMethod; public CachedDnuNode(final SClass rcvrClass, final DispatchGuard guard, - final SSymbol selector, final AbstractDispatchNode nextInCache, - final Universe universe) { - super(getDnuCallTarget(rcvrClass, universe), nextInCache); + final SSymbol selector) { + super(guard); this.selector = selector; - this.guard = guard; + + cachedMethod = insert(Truffle.getRuntime().createDirectCallNode( + getDnuCallTarget(rcvrClass))); } @Override - public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { + public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { Object rcvr = arguments[0]; - try { - if (guard.entryMatches(rcvr)) { - return performDnu(arguments, rcvr); - } else { - return nextInCache.executeDispatch(frame, arguments); - } - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreter(); - return replace(nextInCache).executeDispatch(frame, arguments); - } - } - - public static CallTarget getDnuCallTarget(final SClass rcvrClass, final Universe universe) { - return rcvrClass.lookupInvokable( - symbolFor("doesNotUnderstand:arguments:")).getCallTarget(); - } - - protected Object performDnu(final Object[] arguments, final Object rcvr) { if (VmSettings.PrintStackTraceOnDNU) { CompilerDirectives.transferToInterpreter(); PrintStackTracePrim.printStackTrace(0, getSourceSection()); Universe.errorPrintln("Lookup of " + selector + " failed in " - + Types.getClassOf(rcvr, SomLanguage.getCurrentContext()).getName().getString()); + + Types.getClassOf(rcvr, SomLanguage.getCurrentContext()).getName() + .getString()); } Object[] argsArr = new Object[] { rcvr, selector, SArguments.getArgumentsWithoutReceiver(arguments)}; return cachedMethod.call(argsArr); } + + public static CallTarget getDnuCallTarget(final SClass rcvrClass) { + return rcvrClass.lookupInvokable( + symbolFor("doesNotUnderstand:arguments:")).getCallTarget(); + } } diff --git a/src/trufflesom/interpreter/nodes/dispatch/CachedExprNode.java b/src/trufflesom/interpreter/nodes/dispatch/CachedExprNode.java deleted file mode 100644 index 013138c9a..000000000 --- a/src/trufflesom/interpreter/nodes/dispatch/CachedExprNode.java +++ /dev/null @@ -1,45 +0,0 @@ -package trufflesom.interpreter.nodes.dispatch; - -import com.oracle.truffle.api.CompilerDirectives; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.InvalidAssumptionException; - -import bd.primitives.nodes.PreevaluatedExpression; - - -public class CachedExprNode extends AbstractDispatchNode { - - private final DispatchGuard guard; - - @Child protected AbstractDispatchNode nextInCache; - - @Child protected PreevaluatedExpression expr; - - public CachedExprNode(final DispatchGuard guard, final PreevaluatedExpression expr, - final AbstractDispatchNode nextInCache) { - this.guard = guard; - this.expr = expr; - this.nextInCache = nextInCache; - } - - @Override - public Object executeDispatch( - final VirtualFrame frame, final Object[] arguments) { - Object rcvr = arguments[0]; - try { - if (guard.entryMatches(rcvr)) { - return expr.doPreEvaluated(frame, arguments); - } else { - return nextInCache.executeDispatch(frame, arguments); - } - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreter(); - return replace(nextInCache).executeDispatch(frame, arguments); - } - } - - @Override - public final int lengthOfDispatchChain() { - return 1 + nextInCache.lengthOfDispatchChain(); - } -} diff --git a/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java index 66b26955c..a594cffbb 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java @@ -1,13 +1,14 @@ package trufflesom.interpreter.nodes.dispatch; import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.IndirectCallNode; +import com.oracle.truffle.api.nodes.InvalidAssumptionException; import trufflesom.interpreter.SArguments; +import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; import trufflesom.primitives.reflection.ObjectPrims.ClassPrim; import trufflesom.primitives.reflection.ObjectPrimsFactory.ClassPrimFactory; import trufflesom.vm.Universe; @@ -17,52 +18,43 @@ import trufflesom.vmobjects.SSymbol; -public final class GenericDispatchNode extends AbstractDispatchNode { +public final class GenericDispatchNode extends GuardedDispatchNode { @Child private IndirectCallNode call; @Child private ClassPrim classNode; protected final SSymbol selector; - private final Universe universe; public GenericDispatchNode(final SSymbol selector, final Universe universe) { + super(null); this.selector = selector; - this.universe = universe; - call = Truffle.getRuntime().createIndirectCallNode(); - classNode = ClassPrimFactory.create(null); + call = insert(Truffle.getRuntime().createIndirectCallNode()); + classNode = insert(ClassPrimFactory.create(null)); classNode.initialize(universe); } @TruffleBoundary - private Object dispatch(final Object[] arguments) { - Object rcvr = arguments[0]; - SClass rcvrClass = classNode.executeEvaluated(rcvr); - SInvokable method = rcvrClass.lookupInvokable(selector); + private Object sendDnu(final SClass rcvrClass, final Object[] arguments) { + // Won't use DNU caching here, because it is already a megamorphic node + SArray argumentsArray = SArguments.getArgumentsWithoutReceiver(arguments); + Object[] args = new Object[] {arguments[0], selector, argumentsArray}; + CallTarget target = CachedDnuNode.getDnuCallTarget(rcvrClass); - CallTarget target; - Object[] args; - - if (method != null) { - target = method.getCallTarget(); - args = arguments; - } else { - // TODO: actually do use node - CompilerDirectives.transferToInterpreter(); - // Won't use DNU caching here, because it is already a megamorphic node - SArray argumentsArray = SArguments.getArgumentsWithoutReceiver(arguments); - args = new Object[] {arguments[0], selector, argumentsArray}; - target = CachedDnuNode.getDnuCallTarget(rcvrClass, universe); - } return call.call(target, args); } @Override - public Object executeDispatch( - final VirtualFrame frame, final Object[] arguments) { - return dispatch(arguments); + public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { + Object rcvr = arguments[0]; + SClass rcvrClass = classNode.executeEvaluated(rcvr); + SInvokable method = rcvrClass.lookupInvokable(selector); + if (method != null) { + return call.call(method.getCallTarget(), arguments); + } + return sendDnu(rcvrClass, arguments); } @Override - public int lengthOfDispatchChain() { - return 1000; + public boolean entryMatches(final Object rcvr) throws InvalidAssumptionException { + return true; } } diff --git a/src/trufflesom/interpreter/nodes/dispatch/UninitializedDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/UninitializedDispatchNode.java deleted file mode 100644 index a5bd909f0..000000000 --- a/src/trufflesom/interpreter/nodes/dispatch/UninitializedDispatchNode.java +++ /dev/null @@ -1,96 +0,0 @@ -package trufflesom.interpreter.nodes.dispatch; - -import static trufflesom.interpreter.TruffleCompiler.transferToInterpreterAndInvalidate; - -import com.oracle.truffle.api.CallTarget; -import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.Node; - -import bd.primitives.nodes.PreevaluatedExpression; -import trufflesom.interpreter.Types; -import trufflesom.interpreter.nodes.MessageSendNode.GenericMessageSendNode; -import trufflesom.vm.Universe; -import trufflesom.vmobjects.SClass; -import trufflesom.vmobjects.SInvokable; -import trufflesom.vmobjects.SObject; -import trufflesom.vmobjects.SSymbol; - - -public final class UninitializedDispatchNode extends AbstractDispatchNode { - private final SSymbol selector; - private final Universe universe; - - public UninitializedDispatchNode(final SSymbol selector, final Universe universe) { - this.selector = selector; - this.universe = universe; - } - - private AbstractDispatchNode specialize(final Object[] arguments) { - // Determine position in dispatch node chain, i.e., size of inline cache - Node i = this; - int chainDepth = 0; - while (i.getParent() instanceof AbstractDispatchNode) { - i = i.getParent(); - chainDepth++; - } - AbstractDispatchNode first = (AbstractDispatchNode) i; - - Object rcvr = arguments[0]; - assert rcvr != null; - - if (rcvr instanceof SObject) { - SObject r = (SObject) rcvr; - if (r.updateLayoutToMatchClass() && first != this) { // if first is this, short cut and - // directly continue... - return first; - } - } - - if (chainDepth < INLINE_CACHE_SIZE) { - SClass rcvrClass = Types.getClassOf(rcvr, universe); - SInvokable method = rcvrClass.lookupInvokable(selector); - CallTarget callTarget = null; - PreevaluatedExpression expr = null; - if (method != null) { - if (method.isTrivial()) { - expr = method.copyTrivialNode(); - assert expr != null; - } else { - callTarget = method.getCallTarget(); - } - } - - UninitializedDispatchNode newChainEnd = - new UninitializedDispatchNode(selector, universe); - DispatchGuard guard = DispatchGuard.create(rcvr); - AbstractDispatchNode node; - if (expr != null) { - node = new CachedExprNode(guard, expr, newChainEnd); - } else if (method != null) { - node = new CachedDispatchNode(guard, callTarget, newChainEnd); - } else { - node = new CachedDnuNode(rcvrClass, guard, selector, newChainEnd, universe); - } - return replace(node); - } - - // the chain is longer than the maximum defined by INLINE_CACHE_SIZE and - // thus, this callsite is considered to be megaprophic, and we generalize - // it. - GenericDispatchNode genericReplacement = new GenericDispatchNode(selector, universe); - GenericMessageSendNode sendNode = (GenericMessageSendNode) first.getParent(); - sendNode.replaceDispatchListHead(genericReplacement); - return genericReplacement; - } - - @Override - public Object executeDispatch(final VirtualFrame frame, final Object[] arguments) { - transferToInterpreterAndInvalidate("Initialize a dispatch node."); - return specialize(arguments).executeDispatch(frame, arguments); - } - - @Override - public int lengthOfDispatchChain() { - return 0; - } -} From 9efd2cde5d25da3c81cbc4de98991128a42e9e9d Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 27 Apr 2021 19:04:04 +0100 Subject: [PATCH 02/12] Added node cost and reportPolymorphicSpecialize to MessageSendNode Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 45ba96f8d..7ea3566ae 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -171,8 +171,6 @@ public Object doPreEvaluated(final VirtualFrame frame, } continue; } - // cache guard - // --> apply cache cache = cache.next; } @@ -188,7 +186,31 @@ public String toString() { @Override public NodeCost getCost() { - return NodeCost.NONE; + if (!triedEager) { + return NodeCost.UNINITIALIZED; + } + + GuardedDispatchNode cache = dispatchCache; + + if (cache instanceof GenericDispatchNode) { + return NodeCost.MEGAMORPHIC; + } + + int cacheSize = 0; + while (cache != null) { + cache = cache.next; + cacheSize += 1; + } + + if (cacheSize == 0) { + return NodeCost.UNINITIALIZED; + } + + if (cacheSize == 1) { + return NodeCost.MONOMORPHIC; + } + + return NodeCost.POLYMORPHIC; } private PreevaluatedExpression specialize(final Object[] arguments) { @@ -246,6 +268,7 @@ private PreevaluatedExpression specialize(final Object[] arguments) { } if (first != null) { + reportPolymorphicSpecialize(); node.next = node.insertHere(first); } dispatchCache = insert(node); @@ -256,6 +279,7 @@ private PreevaluatedExpression specialize(final Object[] arguments) { // thus, this callsite is considered to be megaprophic, and we generalize it. GenericDispatchNode generic = new GenericDispatchNode(selector, universe); dispatchCache = insert(generic); + reportPolymorphicSpecialize(); return generic; } From 29964b42b39cd4e64a7d5e753ebfd2b66959608c Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Tue, 27 Apr 2021 22:52:29 +0100 Subject: [PATCH 03/12] Fix some of the issues we have with setting the holder This is only a partial solution, there are still cases where we do not have the correct holder. Signed-off-by: Stefan Marr --- src/trufflesom/compiler/MethodGenerationContext.java | 2 +- src/trufflesom/compiler/bc/BytecodeMethodGenContext.java | 6 ++++++ src/trufflesom/interpreter/Invokable.java | 3 ++- src/trufflesom/interpreter/Method.java | 1 + src/trufflesom/interpreter/Primitive.java | 4 +++- src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java | 1 + 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/trufflesom/compiler/MethodGenerationContext.java b/src/trufflesom/compiler/MethodGenerationContext.java index 0b1226267..7ec0efd10 100644 --- a/src/trufflesom/compiler/MethodGenerationContext.java +++ b/src/trufflesom/compiler/MethodGenerationContext.java @@ -90,7 +90,7 @@ public class MethodGenerationContext private Internal frameOnStack; protected final LexicalScope currentScope; - private final List embeddedBlockMethods; + protected final List embeddedBlockMethods; public final StructuralProbe structuralProbe; diff --git a/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java b/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java index bc69b3b6a..72ebf2c1d 100644 --- a/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java +++ b/src/trufflesom/compiler/bc/BytecodeMethodGenContext.java @@ -182,6 +182,12 @@ public void addBytecodeArgument(final byte code) { bytecode.add(code); } + public void replaceWith(final SMethod oldBlock, final SMethod newBlock) { + boolean wasInList = embeddedBlockMethods.remove(oldBlock); + assert wasInList : "The block to be removed is expected to be in the list of embedded methods"; + embeddedBlockMethods.add(newBlock); + } + public void patchJumpOffsetToPointToNextInstruction(final int idxOfOffset, final ParserBc parser) throws ParseError { int instructionStart = idxOfOffset - 1; diff --git a/src/trufflesom/interpreter/Invokable.java b/src/trufflesom/interpreter/Invokable.java index c2bbac272..6dd7c88f3 100644 --- a/src/trufflesom/interpreter/Invokable.java +++ b/src/trufflesom/interpreter/Invokable.java @@ -1,5 +1,6 @@ package trufflesom.interpreter; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.RootNode; @@ -20,7 +21,7 @@ public abstract class Invokable extends RootNode { protected final ExpressionNode uninitializedBody; - protected SClass holder; + @CompilationFinal protected SClass holder; protected Invokable(final String name, final SourceSection sourceSection, final FrameDescriptor frameDescriptor, diff --git a/src/trufflesom/interpreter/Method.java b/src/trufflesom/interpreter/Method.java index c60df3d95..7a9ee76c4 100644 --- a/src/trufflesom/interpreter/Method.java +++ b/src/trufflesom/interpreter/Method.java @@ -99,6 +99,7 @@ public Method cloneAndAdaptAfterScopeChange(final BytecodeMethodGenContext mgenc Method clone = new Method(name, sourceSection, adaptedBody, adaptedScope, uninit, getLanguage(SomLanguage.class)); adaptedScope.setMethod(clone); + clone.setHolder(holder); return clone; } diff --git a/src/trufflesom/interpreter/Primitive.java b/src/trufflesom/interpreter/Primitive.java index c9eb14be2..365901b5c 100644 --- a/src/trufflesom/interpreter/Primitive.java +++ b/src/trufflesom/interpreter/Primitive.java @@ -26,8 +26,10 @@ public Primitive(final String name, final SourceSection sourceSection, @Override public Node deepCopy() { assert getFrameDescriptor().getSize() == 0 : "Make sure there are no slots to be taken care off"; - return new Primitive(name, sourceSection, NodeUtil.cloneNode(uninitializedBody), + Primitive p = new Primitive(name, sourceSection, NodeUtil.cloneNode(uninitializedBody), getFrameDescriptor(), uninitializedBody, getLanguage(SomLanguage.class)); + p.setHolder(holder); + return p; } @Override diff --git a/src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java b/src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java index cad76f445..9cb347d49 100644 --- a/src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java +++ b/src/trufflesom/interpreter/nodes/bc/BytecodeLoopNode.java @@ -1334,6 +1334,7 @@ private void inlineInto(final BytecodeMethodGenContext mgenc, final int targetCo SMethod newMethod = new SMethod(blockMethod.getSignature(), adapted, blockMethod.getEmbeddedBlocks(), blockIvk.getSourceSection()); newMethod.setHolder(blockMethod.getHolder()); + mgenc.replaceWith(blockMethod, newMethod); mgenc.addLiteralIfAbsent(newMethod, null); emitPUSHBLOCK(mgenc, newMethod, bytecodes[i] == PUSH_BLOCK); break; From 4624fa46ac80cf46b770e04e5dfdc373f2138cb0 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 28 Apr 2021 11:40:13 +0100 Subject: [PATCH 04/12] Fix bug in dispatch chain element removal (adopt/insert node at with right parent) Also extract code to reduce duplication. Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 66 ++++++++++--------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 7ea3566ae..a4ca8b67b 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -149,26 +149,8 @@ public Object doPreEvaluated(final VirtualFrame frame, return cache.doPreEvaluated(frame, arguments); } } catch (InvalidAssumptionException e) { - // Remove invalid node from dispatch chain CompilerDirectives.transferToInterpreterAndInvalidate(); - if (cache.getParent() == this) { - if (cache.next == null) { - dispatchCache = null; - cache = null; - } else { - dispatchCache = insert(cache.next); - cache = cache.next; - } - } else { - GuardedDispatchNode parent = (GuardedDispatchNode) cache.getParent(); - if (cache.next == null) { - parent.next = null; - cache = null; - } else { - parent.next = insert(cache.next); - cache = cache.next; - } - } + cache = removeInvalidEntryAndReturnNext(cache); continue; } cache = cache.next; @@ -179,11 +161,43 @@ public Object doPreEvaluated(final VirtualFrame frame, return specialize(arguments).doPreEvaluated(frame, arguments); } + private GuardedDispatchNode removeInvalidEntryAndReturnNext( + final GuardedDispatchNode cache) { + if (cache.getParent() == this) { + if (cache.next == null) { + dispatchCache = null; + return null; + } else { + dispatchCache = insert(cache.next); + return cache.next; + } + } else { + GuardedDispatchNode parent = (GuardedDispatchNode) cache.getParent(); + if (cache.next == null) { + parent.next = null; + return null; + } else { + parent.next = parent.insertHere(cache.next); + return cache.next; + } + } + } + @Override public String toString() { return "GMsgSend(" + selector.getString() + ")"; } + private int getCacheSize(GuardedDispatchNode cache) { + int cacheSize = 0; + while (cache != null) { + cache = cache.next; + cacheSize += 1; + } + + return cacheSize; + } + @Override public NodeCost getCost() { if (!triedEager) { @@ -196,11 +210,7 @@ public NodeCost getCost() { return NodeCost.MEGAMORPHIC; } - int cacheSize = 0; - while (cache != null) { - cache = cache.next; - cacheSize += 1; - } + int cacheSize = getCacheSize(cache); if (cacheSize == 0) { return NodeCost.UNINITIALIZED; @@ -223,12 +233,8 @@ private PreevaluatedExpression specialize(final Object[] arguments) { } final GuardedDispatchNode first = dispatchCache; - GuardedDispatchNode cache = first; - int cacheSize = 0; - while (cache != null) { - cache = cache.next; - cacheSize += 1; - } + + int cacheSize = getCacheSize(first); Object rcvr = arguments[0]; assert rcvr != null; From 6ec1886a1902b8453af211badfc9447fda2dd9d3 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 19 Nov 2021 22:02:32 +0000 Subject: [PATCH 05/12] Turn while to do/while to do first check and then read argument only when needed Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index a4ca8b67b..c9cf34e57 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -141,19 +141,21 @@ public Object doPreEvaluated(final VirtualFrame frame, GuardedDispatchNode cache = dispatchCache; - Object rcvr = arguments[0]; - - while (cache != null) { - try { - if (cache.entryMatches(rcvr)) { - return cache.doPreEvaluated(frame, arguments); + if (cache != null) { + Object rcvr = arguments[0]; + + do { + try { + if (cache.entryMatches(rcvr)) { + return cache.doPreEvaluated(frame, arguments); + } + } catch (InvalidAssumptionException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + cache = removeInvalidEntryAndReturnNext(cache); + continue; } - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - cache = removeInvalidEntryAndReturnNext(cache); - continue; - } - cache = cache.next; + cache = cache.next; + } while (cache != null); } CompilerDirectives.transferToInterpreterAndInvalidate(); From bc156f6b2c5d20e3a717e6071e750d1ff24c8f15 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 24 Nov 2021 01:23:21 +0000 Subject: [PATCH 06/12] Avoid traversing cache to determine size, instead keep it explicitly Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index c9cf34e57..577087977 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -57,7 +57,7 @@ public static GenericMessageSendNode createGeneric(final SSymbol selector, final ExpressionNode[] argumentNodes, final SourceSection source, final Universe universe) { return new GenericMessageSendNode( - selector, argumentNodes, universe, true).initialize(source); + selector, argumentNodes, universe, 0).initialize(source); } public static AbstractMessageSendNode createSuperSend(final SClass superClass, @@ -116,22 +116,22 @@ public static final class GenericMessageSendNode private final int numberOfSignatureArguments; - @CompilationFinal private boolean triedEager; + @CompilationFinal private int numCacheNodes; @Child private GuardedDispatchNode dispatchCache; private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, - final Universe universe, final boolean triedEager) { + final Universe universe, final int numCacheNodes) { super(arguments); this.selector = selector; this.universe = universe; - this.triedEager = triedEager; + this.numCacheNodes = numCacheNodes; this.numberOfSignatureArguments = selector.getNumberOfSignatureArguments(); } private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, final Universe universe) { - this(selector, arguments, universe, false); + this(selector, arguments, universe, -1); } @Override @@ -190,19 +190,9 @@ public String toString() { return "GMsgSend(" + selector.getString() + ")"; } - private int getCacheSize(GuardedDispatchNode cache) { - int cacheSize = 0; - while (cache != null) { - cache = cache.next; - cacheSize += 1; - } - - return cacheSize; - } - @Override public NodeCost getCost() { - if (!triedEager) { + if (numCacheNodes < 0) { return NodeCost.UNINITIALIZED; } @@ -212,7 +202,7 @@ public NodeCost getCost() { return NodeCost.MEGAMORPHIC; } - int cacheSize = getCacheSize(cache); + int cacheSize = numCacheNodes; if (cacheSize == 0) { return NodeCost.UNINITIALIZED; @@ -226,8 +216,9 @@ public NodeCost getCost() { } private PreevaluatedExpression specialize(final Object[] arguments) { - if (!triedEager) { - triedEager = true; + int cacheSize = numCacheNodes; + if (cacheSize < 0) { + cacheSize = numCacheNodes = 0; PreevaluatedExpression eager = attemptEagerSpecialization(arguments); if (eager != null) { return eager; @@ -236,8 +227,6 @@ private PreevaluatedExpression specialize(final Object[] arguments) { final GuardedDispatchNode first = dispatchCache; - int cacheSize = getCacheSize(first); - Object rcvr = arguments[0]; assert rcvr != null; @@ -280,6 +269,7 @@ private PreevaluatedExpression specialize(final Object[] arguments) { node.next = node.insertHere(first); } dispatchCache = insert(node); + numCacheNodes = cacheSize + 1; return node; } @@ -288,6 +278,7 @@ private PreevaluatedExpression specialize(final Object[] arguments) { GenericDispatchNode generic = new GenericDispatchNode(selector, universe); dispatchCache = insert(generic); reportPolymorphicSpecialize(); + numCacheNodes = cacheSize + 1; return generic; } From a5da33f2f601f87b1788e83a85273cd2ff15558e Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 21:02:03 +0000 Subject: [PATCH 07/12] Fold Guarded and Abstract dispatch nodes Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 7 ++-- .../nodes/dispatch/AbstractDispatchNode.java | 33 ++++++++----------- .../nodes/dispatch/CachedDnuNode.java | 3 +- .../nodes/dispatch/GenericDispatchNode.java | 3 +- 4 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 577087977..173902bf7 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -18,7 +18,6 @@ import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode; import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedDispatchNode; import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.CachedExprNode; -import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; import trufflesom.interpreter.nodes.dispatch.CachedDnuNode; import trufflesom.interpreter.nodes.dispatch.DispatchGuard; import trufflesom.interpreter.nodes.dispatch.GenericDispatchNode; @@ -118,7 +117,7 @@ public static final class GenericMessageSendNode @CompilationFinal private int numCacheNodes; - @Child private GuardedDispatchNode dispatchCache; + @Child private AbstractDispatchNode dispatchCache; private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] arguments, final Universe universe, final int numCacheNodes) { @@ -174,7 +173,6 @@ private GuardedDispatchNode removeInvalidEntryAndReturnNext( return cache.next; } } else { - GuardedDispatchNode parent = (GuardedDispatchNode) cache.getParent(); if (cache.next == null) { parent.next = null; return null; @@ -182,6 +180,7 @@ private GuardedDispatchNode removeInvalidEntryAndReturnNext( parent.next = parent.insertHere(cache.next); return cache.next; } + AbstractDispatchNode parent = (AbstractDispatchNode) prev; } } @@ -255,7 +254,7 @@ private PreevaluatedExpression specialize(final Object[] arguments) { DispatchGuard guard = DispatchGuard.create(rcvr); - GuardedDispatchNode node; + AbstractDispatchNode node; if (expr != null) { node = new CachedExprNode(guard, expr); } else if (method != null) { diff --git a/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java index 9e3b12a8d..0b2b21c44 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/AbstractDispatchNode.java @@ -13,31 +13,26 @@ public abstract class AbstractDispatchNode extends Node implements PreevaluatedExpression { public static final int INLINE_CACHE_SIZE = 6; - @Override - public abstract Object doPreEvaluated(VirtualFrame frame, Object[] args); - - public abstract boolean entryMatches(Object rcvr) throws InvalidAssumptionException; + private final DispatchGuard guard; - public abstract static class GuardedDispatchNode extends AbstractDispatchNode { - private final DispatchGuard guard; + @Child public AbstractDispatchNode next; - @Child public GuardedDispatchNode next; + protected AbstractDispatchNode(final DispatchGuard guard) { + this.guard = guard; + } - protected GuardedDispatchNode(final DispatchGuard guard) { - this.guard = guard; - } + @Override + public abstract Object doPreEvaluated(VirtualFrame frame, Object[] args); - @Override - public boolean entryMatches(final Object rcvr) throws InvalidAssumptionException { - return guard.entryMatches(rcvr); - } + public boolean entryMatches(final Object rcvr) throws InvalidAssumptionException { + return guard.entryMatches(rcvr); + } - public final T insertHere(final T newChild) { - return super.insert(newChild); - } + public final T insertHere(final T newChild) { + return super.insert(newChild); } - public static final class CachedDispatchNode extends GuardedDispatchNode { + public static final class CachedDispatchNode extends AbstractDispatchNode { @Child protected DirectCallNode cachedMethod; @@ -52,7 +47,7 @@ public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) } } - public static final class CachedExprNode extends GuardedDispatchNode { + public static final class CachedExprNode extends AbstractDispatchNode { @Child protected PreevaluatedExpression expr; diff --git a/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java b/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java index 01096fe13..84813d597 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/CachedDnuNode.java @@ -11,7 +11,6 @@ import trufflesom.interpreter.SArguments; import trufflesom.interpreter.SomLanguage; import trufflesom.interpreter.Types; -import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; import trufflesom.primitives.basics.SystemPrims.PrintStackTracePrim; import trufflesom.vm.Universe; import trufflesom.vm.VmSettings; @@ -19,7 +18,7 @@ import trufflesom.vmobjects.SSymbol; -public final class CachedDnuNode extends GuardedDispatchNode { +public final class CachedDnuNode extends AbstractDispatchNode { private final SSymbol selector; @Child protected DirectCallNode cachedMethod; diff --git a/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java b/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java index a594cffbb..24ea769d6 100644 --- a/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java +++ b/src/trufflesom/interpreter/nodes/dispatch/GenericDispatchNode.java @@ -8,7 +8,6 @@ import com.oracle.truffle.api.nodes.InvalidAssumptionException; import trufflesom.interpreter.SArguments; -import trufflesom.interpreter.nodes.dispatch.AbstractDispatchNode.GuardedDispatchNode; import trufflesom.primitives.reflection.ObjectPrims.ClassPrim; import trufflesom.primitives.reflection.ObjectPrimsFactory.ClassPrimFactory; import trufflesom.vm.Universe; @@ -18,7 +17,7 @@ import trufflesom.vmobjects.SSymbol; -public final class GenericDispatchNode extends GuardedDispatchNode { +public final class GenericDispatchNode extends AbstractDispatchNode { @Child private IndirectCallNode call; @Child private ClassPrim classNode; From 93a7284b22562a813d1fc6fd5b48eafdab1ecf46 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 21:03:14 +0000 Subject: [PATCH 08/12] Move try out of dispatch loop, and instead recurse on exception - also simplify the code to remove invalid node Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 49 +++++++------------ 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 173902bf7..2a141b9ee 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.nodes.ExplodeLoop; import com.oracle.truffle.api.nodes.InvalidAssumptionException; +import com.oracle.truffle.api.nodes.Node; import com.oracle.truffle.api.nodes.NodeCost; import com.oracle.truffle.api.source.SourceSection; @@ -135,52 +136,38 @@ private GenericMessageSendNode(final SSymbol selector, final ExpressionNode[] ar @Override @ExplodeLoop - public Object doPreEvaluated(final VirtualFrame frame, - final Object[] arguments) { - - GuardedDispatchNode cache = dispatchCache; + public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) { + AbstractDispatchNode cache = dispatchCache; if (cache != null) { Object rcvr = arguments[0]; - do { - try { + try { + do { if (cache.entryMatches(rcvr)) { return cache.doPreEvaluated(frame, arguments); } - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - cache = removeInvalidEntryAndReturnNext(cache); - continue; - } - cache = cache.next; - } while (cache != null); + cache = cache.next; + } while (cache != null); + } catch (InvalidAssumptionException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + removeInvalidEntryAndReturnNext(cache); + return doPreEvaluated(frame, arguments); + } } CompilerDirectives.transferToInterpreterAndInvalidate(); - return specialize(arguments).doPreEvaluated(frame, arguments); } - private GuardedDispatchNode removeInvalidEntryAndReturnNext( - final GuardedDispatchNode cache) { - if (cache.getParent() == this) { - if (cache.next == null) { - dispatchCache = null; - return null; - } else { - dispatchCache = insert(cache.next); - return cache.next; - } + private void removeInvalidEntryAndReturnNext( + final AbstractDispatchNode cache) { + Node prev = cache.getParent(); + if (prev == this) { + dispatchCache = insert(cache.next); } else { - if (cache.next == null) { - parent.next = null; - return null; - } else { - parent.next = parent.insertHere(cache.next); - return cache.next; - } AbstractDispatchNode parent = (AbstractDispatchNode) prev; + parent.next = parent.insertHere(cache.next); } } From 0b3c03829fd745d5858dba8b5b65ec6d4c68a255 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 21:03:47 +0000 Subject: [PATCH 09/12] Rely on solely on numCacheNodes for node cost Signed-off-by: Stefan Marr --- src/trufflesom/interpreter/nodes/MessageSendNode.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 2a141b9ee..f3b1f5cfb 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -182,12 +182,6 @@ public NodeCost getCost() { return NodeCost.UNINITIALIZED; } - GuardedDispatchNode cache = dispatchCache; - - if (cache instanceof GenericDispatchNode) { - return NodeCost.MEGAMORPHIC; - } - int cacheSize = numCacheNodes; if (cacheSize == 0) { @@ -198,7 +192,10 @@ public NodeCost getCost() { return NodeCost.MONOMORPHIC; } - return NodeCost.POLYMORPHIC; + if (cacheSize < AbstractDispatchNode.INLINE_CACHE_SIZE) { + return NodeCost.POLYMORPHIC; + } + return NodeCost.MEGAMORPHIC; } private PreevaluatedExpression specialize(final Object[] arguments) { From a64fee06dda530835ee650f75d509da7503d0f22 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 21:04:14 +0000 Subject: [PATCH 10/12] Use cache size and reorder a bit to avoid redundant accesses Signed-off-by: Stefan Marr --- src/trufflesom/interpreter/nodes/MessageSendNode.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index f3b1f5cfb..7cf1b7f30 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -201,21 +201,20 @@ public NodeCost getCost() { private PreevaluatedExpression specialize(final Object[] arguments) { int cacheSize = numCacheNodes; if (cacheSize < 0) { - cacheSize = numCacheNodes = 0; PreevaluatedExpression eager = attemptEagerSpecialization(arguments); if (eager != null) { return eager; } - } - final GuardedDispatchNode first = dispatchCache; + cacheSize = numCacheNodes = 0; + } Object rcvr = arguments[0]; assert rcvr != null; if (rcvr instanceof SObject) { SObject r = (SObject) rcvr; - if (r.updateLayoutToMatchClass() && first != null) { + if (r.updateLayoutToMatchClass() && cacheSize > 0) { // if the dispatchCache is null, we end up here, so continue directly below instead // otherwise, let's retry the cache! return this; @@ -247,8 +246,9 @@ private PreevaluatedExpression specialize(final Object[] arguments) { node = new CachedDnuNode(rcvrClass, guard, selector); } - if (first != null) { + if (cacheSize > 0) { reportPolymorphicSpecialize(); + final AbstractDispatchNode first = dispatchCache; node.next = node.insertHere(first); } dispatchCache = insert(node); From 1eabd67092e41de52453d308a2b8a7fe4826af3f Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 21:53:40 +0000 Subject: [PATCH 11/12] Move try back into lookup loop Signed-off-by: Stefan Marr --- .../interpreter/nodes/MessageSendNode.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/trufflesom/interpreter/nodes/MessageSendNode.java b/src/trufflesom/interpreter/nodes/MessageSendNode.java index 7cf1b7f30..6fc752e12 100644 --- a/src/trufflesom/interpreter/nodes/MessageSendNode.java +++ b/src/trufflesom/interpreter/nodes/MessageSendNode.java @@ -142,33 +142,33 @@ public Object doPreEvaluated(final VirtualFrame frame, final Object[] arguments) if (cache != null) { Object rcvr = arguments[0]; - try { - do { + do { + try { if (cache.entryMatches(rcvr)) { return cache.doPreEvaluated(frame, arguments); } - cache = cache.next; - } while (cache != null); - } catch (InvalidAssumptionException e) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - removeInvalidEntryAndReturnNext(cache); - return doPreEvaluated(frame, arguments); - } + } catch (InvalidAssumptionException e) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + cache = removeInvalidEntryAndReturnNext(cache); + continue; + } + cache = cache.next; + } while (cache != null); } CompilerDirectives.transferToInterpreterAndInvalidate(); return specialize(arguments).doPreEvaluated(frame, arguments); } - private void removeInvalidEntryAndReturnNext( + private AbstractDispatchNode removeInvalidEntryAndReturnNext( final AbstractDispatchNode cache) { Node prev = cache.getParent(); if (prev == this) { - dispatchCache = insert(cache.next); - } else { - AbstractDispatchNode parent = (AbstractDispatchNode) prev; - parent.next = parent.insertHere(cache.next); + return dispatchCache = insert(cache.next); } + + AbstractDispatchNode parent = (AbstractDispatchNode) prev; + return parent.next = parent.insertHere(cache.next); } @Override From 8180e2bd76ce67d711e1112efe6b68737bbec9a3 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sun, 5 Dec 2021 23:16:17 +0000 Subject: [PATCH 12/12] Remove broken path element Signed-off-by: Stefan Marr --- build.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.xml b/build.xml index 94ab26eef..e4aac539f 100644 --- a/build.xml +++ b/build.xml @@ -58,8 +58,6 @@ - -