Skip to content

Commit

Permalink
Introduce nodes for indirect dispatch
Browse files Browse the repository at this point in the history
  • Loading branch information
fniephaus committed Jan 7, 2025
1 parent 91da9f4 commit d07ded7
Show file tree
Hide file tree
Showing 13 changed files with 771 additions and 473 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ protected static final Object doSendGeneric(final Node node, final AbstractSquea
arguments[i] = wrapNode.executeWrap(node, rawArguments[i]);
}
if (method.hasPrimitive()) {
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNode();
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNodeOrNull();
if (primitiveNode != null) {
final VirtualFrame frame = null; // FIXME?
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ public final Object send(final SqueakImageContext image, final String selector,
image.interrupt.deactivate();
try {
if (method.hasPrimitive()) {
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNode();
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNodeOrNull();
if (primitiveNode != null) {
try {
return primitiveNode.executeWithArguments(image.externalSenderFrame, this, arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ public final class CompiledCodeObject extends AbstractSqueakObjectWithClassAndHa
private static final String SOURCE_UNAVAILABLE_NAME = "<unavailable>";
public static final String SOURCE_UNAVAILABLE_CONTENTS = "Source unavailable";

private static final AbstractPrimitiveNode NULL_PRIMITIVE = new AbstractPrimitiveNode() {
};

// header info and data
@CompilationFinal private int header;
/*
Expand Down Expand Up @@ -93,7 +96,7 @@ static final class ExecutionData {
@CompilationFinal private RootCallTarget resumptionCallTarget;
}

private AbstractPrimitiveNode primitiveNode;
@CompilationFinal private AbstractPrimitiveNode primitiveNode;

@TruffleBoundary
public CompiledCodeObject(final long header, final ClassObject classObject) {
Expand Down Expand Up @@ -198,13 +201,6 @@ public Source getSource() {
return executionData.source;
}

public AbstractPrimitiveNode getPrimitiveNode() {
if (primitiveNode == null) {
primitiveNode = PrimitiveNodeFactory.getOrCreateIndexedOrNamed(this);
}
return primitiveNode;
}

// used by CompiledMethod>>callTarget
public RootCallTarget getCallTargetOrNull() {
if (hasExecutionData()) {
Expand Down Expand Up @@ -439,6 +435,20 @@ public int primitiveIndex() {
return (Byte.toUnsignedInt(bytes[2]) << 8) + Byte.toUnsignedInt(bytes[1]);
}

@Idempotent
public AbstractPrimitiveNode getPrimitiveNodeOrNull() {
if (primitiveNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
if (hasPrimitive()) {
primitiveNode = PrimitiveNodeFactory.getOrCreateIndexedOrNamed(this);
}
if (primitiveNode == null) {
primitiveNode = NULL_PRIMITIVE;
}
}
return primitiveNode == NULL_PRIMITIVE ? null : primitiveNode;
}

public boolean isUnwindMarked() {
return hasPrimitive() && primitiveIndex() == PrimitiveNodeFactory.PRIMITIVE_ENSURE_MARKER_INDEX;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ public Object executeAsSymbolSlow(final SqueakImageContext image, final VirtualF
final Object lookupResult = LookupMethodNode.executeUncached(SqueakObjectClassNode.executeUncached(receiver), this);
if (lookupResult instanceof CompiledCodeObject method) {
if (method.hasPrimitive()) {
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNode();
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNodeOrNull();
if (primitiveNode != null) {
try {
return primitiveNode.executeWithArguments(image.externalSenderFrame, receiver, arguments);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public final class CacheLimits {
public static final int INLINE_METHOD_CACHE_LIMIT = 6;
public static final int INLINE_BLOCK_CACHE_LIMIT = 3;
public static final int PERFORM_SELECTOR_CACHE_LIMIT = 4;
public static final int INDIRECT_PRIMITIVE_CACHE_LIMIT = 2;
}
Original file line number Diff line number Diff line change
Expand Up @@ -115,35 +115,9 @@ protected static final Object doDirect(final VirtualFrame frame, final Object re
@SuppressWarnings("truffle-static-method")
protected final Object doIndirect(final VirtualFrame frame, final Object receiver,
@Bind("this") final Node node,
@Cached final SqueakObjectClassNode classNode,
@Cached final ResolveMethodNode methodNode,
@Cached final InlinedBranchProfile hasPrimitiveProfile,
@Cached final CreateFrameArgumentsForIndirectCall0Node argumentsNode,
@Cached final DispatchIndirect0Node dispatchNode,
@Cached final IndirectCallNode callNode) {
final ClassObject receiverClass = classNode.executeLookup(node, receiver);
final Object lookupResult = getContext(node).lookup(receiverClass, selector);
final CompiledCodeObject method = methodNode.execute(node, getContext(node), receiverClass, lookupResult);
if (method.hasPrimitive()) {
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNode();
if (primitiveNode != null) {
hasPrimitiveProfile.enter(node);
final Object result = tryPrimitive(primitiveNode, frame.materialize(), node, method, receiver);
if (result != null) {
return result;
}
}
}
return callNode.call(method.getCallTarget(), argumentsNode.execute(frame, node, receiver, receiverClass, lookupResult, method, selector));
}

@TruffleBoundary
private static Object tryPrimitive(final AbstractPrimitiveNode primitiveNode, final MaterializedFrame frame, final Node node, final CompiledCodeObject method, final Object receiver) {
try {
return ((Primitive0) primitiveNode).execute(frame, receiver);
} catch (final PrimitiveFailed pf) {
DispatchUtils.handlePrimitiveFailedIndirect(node, method, pf);
return null;
}
return dispatchNode.execute(frame, node, callNode, selector, receiver);
}
}

Expand Down Expand Up @@ -361,33 +335,102 @@ public Object execute(final VirtualFrame frame, final Object receiver) {

@GenerateInline
@GenerateCached(false)
public abstract static class CreateFrameArgumentsForIndirectCall0Node extends AbstractNode {
public abstract Object[] execute(VirtualFrame frame, Node node, Object receiver, ClassObject receiverClass, Object lookupResult, CompiledCodeObject method, NativeObject selector);
public abstract static class DispatchIndirect0Node extends AbstractNode {
public abstract Object execute(VirtualFrame frame, Node node, IndirectCallNode callNode, NativeObject selector, Object receiver);

@Specialization
@SuppressWarnings("unused")
protected static final Object[] doMethod(final VirtualFrame frame, final Node node, final Object receiver, final ClassObject receiverClass,
final CompiledCodeObject lookupResult, final CompiledCodeObject method, final NativeObject selector,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
return FrameAccess.newWith(senderNode.execute(frame, node, method), null, receiver);
protected static final Object doIndirect(final VirtualFrame frame, final Node node, final IndirectCallNode callNode, final NativeObject selector, final Object receiver,
@Cached final SqueakObjectClassNode classNode,
@Cached final ResolveMethodNode methodNode,
@Cached final TryPrimitive0Node tryPrimitiveNode,
@Cached final CreateFrameArgumentsForIndirectCall0Node argumentsNode) {
final ClassObject receiverClass = classNode.executeLookup(node, receiver);
final Object lookupResult = getContext(node).lookup(receiverClass, selector);
final CompiledCodeObject method = methodNode.execute(node, getContext(node), receiverClass, lookupResult);
final Object result = tryPrimitiveNode.execute(frame, node, method, receiver);
if (result != null) {
return result;
} else {
return callNode.call(method.getCallTarget(), argumentsNode.execute(frame, node, receiver, receiverClass, lookupResult, method, selector));
}
}

@Specialization(guards = "lookupResult == null")
protected static final Object[] doDoesNotUnderstand(final VirtualFrame frame, final Node node, final Object receiver, final ClassObject receiverClass,
@SuppressWarnings("unused") final Object lookupResult, final CompiledCodeObject method, final NativeObject selector,
@Cached final AbstractPointersObjectWriteNode writeNode,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
final Object[] arguments = ArrayUtils.EMPTY_ARRAY;
final PointersObject message = getContext(node).newMessage(writeNode, node, selector, receiverClass, arguments);
return FrameAccess.newDNUWith(senderNode.execute(frame, node, method), receiver, message);
@GenerateInline
@GenerateCached(false)
protected abstract static class TryPrimitive0Node extends AbstractNode {
abstract Object execute(VirtualFrame frame, Node node, CompiledCodeObject method, Object receiver);

@SuppressWarnings("unused")
@Specialization(guards = "method.getPrimitiveNodeOrNull() == null")
protected static final Object doNoPrimitive(final CompiledCodeObject method, final Object receiver) {
return null;
}

@Specialization(guards = {"method == cachedMethod", "primitiveNode != null"}, limit = "INDIRECT_PRIMITIVE_CACHE_LIMIT")
protected static final Object doCached(final VirtualFrame frame, final Node node, @SuppressWarnings("unused") final CompiledCodeObject method, final Object receiver,
@SuppressWarnings("unused") @Cached("method") final CompiledCodeObject cachedMethod,
@Bind("cachedMethod.getPrimitiveNodeOrNull()") final AbstractPrimitiveNode primitiveNode,
@Cached final InlinedBranchProfile primitiveFailedProfile) {
try {
return ((Primitive0) primitiveNode).execute(frame, receiver);
} catch (final PrimitiveFailed pf) {
primitiveFailedProfile.enter(node);
DispatchUtils.handlePrimitiveFailedIndirect(node, method, pf);
return null;
}
}

@Specialization(replaces = {"doNoPrimitive", "doCached"})
protected static final Object doUncached(final VirtualFrame frame, final Node node, final CompiledCodeObject method, final Object receiver) {
final AbstractPrimitiveNode primitiveNode = method.getPrimitiveNodeOrNull();
if (primitiveNode != null) {
return tryPrimitive(primitiveNode, frame.materialize(), node, method, receiver);
} else {
return null;
}
}

@TruffleBoundary
private static Object tryPrimitive(final AbstractPrimitiveNode primitiveNode, final MaterializedFrame frame, final Node node, final CompiledCodeObject method, final Object receiver) {
try {
return ((Primitive0) primitiveNode).execute(frame, receiver);
} catch (final PrimitiveFailed pf) {
DispatchUtils.handlePrimitiveFailedIndirect(node, method, pf);
return null;
}
}
}

@Specialization(guards = {"targetObject != null", "!isCompiledCodeObject(targetObject)"})
protected static final Object[] doObjectAsMethod(final VirtualFrame frame, final Node node, final Object receiver, @SuppressWarnings("unused") final ClassObject receiverClass,
final Object targetObject, final CompiledCodeObject method, final NativeObject selector,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
final Object[] arguments = ArrayUtils.EMPTY_ARRAY;
return FrameAccess.newOAMWith(senderNode.execute(frame, node, method), targetObject, selector, getContext(node).asArrayOfObjects(arguments), receiver);
@GenerateInline
@GenerateCached(false)
protected abstract static class CreateFrameArgumentsForIndirectCall0Node extends AbstractNode {
abstract Object[] execute(VirtualFrame frame, Node node, Object receiver, ClassObject receiverClass, Object lookupResult, CompiledCodeObject method, NativeObject selector);

@Specialization
@SuppressWarnings("unused")
protected static final Object[] doMethod(final VirtualFrame frame, final Node node, final Object receiver, final ClassObject receiverClass,
final CompiledCodeObject lookupResult, final CompiledCodeObject method, final NativeObject selector,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
return FrameAccess.newWith(senderNode.execute(frame, node, method), null, receiver);
}

@Specialization(guards = "lookupResult == null")
protected static final Object[] doDoesNotUnderstand(final VirtualFrame frame, final Node node, final Object receiver, final ClassObject receiverClass,
@SuppressWarnings("unused") final Object lookupResult, final CompiledCodeObject method, final NativeObject selector,
@Cached final AbstractPointersObjectWriteNode writeNode,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
final Object[] arguments = ArrayUtils.EMPTY_ARRAY;
final PointersObject message = getContext(node).newMessage(writeNode, node, selector, receiverClass, arguments);
return FrameAccess.newDNUWith(senderNode.execute(frame, node, method), receiver, message);
}

@Specialization(guards = {"targetObject != null", "!isCompiledCodeObject(targetObject)"})
protected static final Object[] doObjectAsMethod(final VirtualFrame frame, final Node node, final Object receiver, @SuppressWarnings("unused") final ClassObject receiverClass,
final Object targetObject, final CompiledCodeObject method, final NativeObject selector,
@Shared("senderNode") @Cached final GetOrCreateContextOrMarkerNode senderNode) {
final Object[] arguments = ArrayUtils.EMPTY_ARRAY;
return FrameAccess.newOAMWith(senderNode.execute(frame, node, method), targetObject, selector, getContext(node).asArrayOfObjects(arguments), receiver);
}
}
}
}
Loading

0 comments on commit d07ded7

Please sign in to comment.