Skip to content

Commit

Permalink
Introduce TransferToNode
Browse files Browse the repository at this point in the history
and align `WakeHighestPriorityNode` with SqueakJS.
  • Loading branch information
fniephaus committed Dec 26, 2023
1 parent cc9a61e commit 3545a26
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,12 @@
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.InlinedConditionProfile;

import de.hpi.swa.trufflesqueak.exceptions.ProcessSwitch;
import de.hpi.swa.trufflesqueak.exceptions.SqueakExceptions.SqueakException;
import de.hpi.swa.trufflesqueak.image.SqueakImageChunk;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.image.SqueakImageReader;
import de.hpi.swa.trufflesqueak.image.SqueakImageWriter;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.CONTEXT;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS_SCHEDULER;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectWriteNode;
import de.hpi.swa.trufflesqueak.util.FrameAccess;
import de.hpi.swa.trufflesqueak.util.MiscUtils;
import de.hpi.swa.trufflesqueak.util.ObjectGraphUtils.ObjectTracer;
Expand Down Expand Up @@ -505,25 +501,6 @@ private Object[] getReceiverAndNArguments(final int numArgs) {
return arguments;
}

public void transferTo(final SqueakImageContext image, final PointersObject newProcess, final PointersObject activeProcess, final ContextObject newActiveContext,
final AbstractPointersObjectWriteNode writeNode, final Node inlineTarget) {
// Record a process to be awakened on the next interpreter cycle.
final PointersObject scheduler = image.getScheduler();
assert newProcess != activeProcess : "trying to switch to already active process";
// overwritten in next line.
final PointersObject oldProcess = activeProcess;
writeNode.execute(inlineTarget, scheduler, PROCESS_SCHEDULER.ACTIVE_PROCESS, newProcess);
writeNode.execute(inlineTarget, oldProcess, PROCESS.SUSPENDED_CONTEXT, this);
writeNode.executeNil(inlineTarget, newProcess, PROCESS.LIST);
writeNode.executeNil(inlineTarget, newProcess, PROCESS.SUSPENDED_CONTEXT);
if (CompilerDirectives.isPartialEvaluationConstant(newActiveContext)) {
throw ProcessSwitch.create(newActiveContext);
} else {
// Avoid further PE if newActiveContext is not a PE constant.
throw ProcessSwitch.createWithBoundary(newActiveContext);
}
}

/**
* Since {@link MaterializedFrame} is an interface, the Graal compiler needs help to find the
* concrete class, and which concrete implementation is used depends on the GraalVM edition (CE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,65 +6,52 @@
*/
package de.hpi.swa.trufflesqueak.nodes.process;

import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Cached.Shared;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;

import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.model.ContextObject;
import de.hpi.swa.trufflesqueak.model.PointersObject;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS;
import de.hpi.swa.trufflesqueak.nodes.AbstractNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectReadNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectWriteNode;
import de.hpi.swa.trufflesqueak.nodes.context.frame.GetOrCreateContextNode;

@GenerateInline
@GenerateCached(false)
public abstract class ResumeProcessNode extends AbstractNode {

public static void executeUncached(final VirtualFrame frame, final SqueakImageContext image, final PointersObject newProcess) {
final AbstractPointersObjectReadNode readNode = AbstractPointersObjectReadNode.getUncached();
public static final void executeUncached(final VirtualFrame frame, final SqueakImageContext image, final PointersObject newProcess) {
final PointersObject activeProcess = image.getActiveProcessSlow();
if (hasHigherPriority(newProcess, activeProcess, readNode, null)) {
final AbstractPointersObjectReadNode readNode = AbstractPointersObjectReadNode.getUncached();
final long activePriority = readNode.executeLong(null, activeProcess, PROCESS.PRIORITY);
final long newPriority = readNode.executeLong(null, newProcess, PROCESS.PRIORITY);
if (newPriority > activePriority) {
PutToSleepNode.executeUncached(image, activeProcess);
final ContextObject newActiveContext = (ContextObject) readNode.execute(null, newProcess, PROCESS.SUSPENDED_CONTEXT);
final AbstractPointersObjectWriteNode writeNode = AbstractPointersObjectWriteNode.getUncached();
GetOrCreateContextNode.getOrCreateUncached(frame).transferTo(image, newProcess, activeProcess, newActiveContext, writeNode, null);
throw TransferToNode.executeUncached(frame, newProcess);
} else {
PutToSleepNode.executeUncached(image, newProcess);
}
}

public abstract void executeResume(VirtualFrame frame, Node node, PointersObject newProcess);

@Specialization(guards = "hasHigherPriority(newProcess, activeProcess, pointersReadNode, node)")
protected static final void doTransferTo(final VirtualFrame frame, final Node node, final PointersObject newProcess,
@Shared("putToSleepNode") @Cached final PutToSleepNode putToSleepNode,
@Shared("pointersReadNode") @Cached final AbstractPointersObjectReadNode pointersReadNode,
@SuppressWarnings("unused") @Shared("getActiveProcessNode") @Cached final GetActiveProcessNode getActiveProcessNode,
@Bind("getActiveProcessNode.execute(node)") final PointersObject activeProcess,
@Cached final AbstractPointersObjectWriteNode pointersWriteNode,
@Cached final GetOrCreateContextNode contextNode) {
putToSleepNode.executePutToSleep(node, activeProcess);
final ContextObject newActiveContext = (ContextObject) pointersReadNode.execute(node, newProcess, PROCESS.SUSPENDED_CONTEXT);
contextNode.executeGet(frame, node).transferTo(getContext(node), newProcess, activeProcess, newActiveContext, pointersWriteNode, node);
}

@Specialization(guards = "!hasHigherPriority(newProcess, getActiveProcessNode.execute(node), pointersReadNode, node)")
protected static final void doSleep(final Node node, final PointersObject newProcess,
@SuppressWarnings("unused") @Shared("pointersReadNode") @Cached final AbstractPointersObjectReadNode pointersReadNode,
@SuppressWarnings("unused") @Shared("getActiveProcessNode") @Cached final GetActiveProcessNode getActiveProcessNode,
@Shared("putToSleepNode") @Cached final PutToSleepNode putToSleepNode) {
putToSleepNode.executePutToSleep(node, newProcess);
}

protected static final boolean hasHigherPriority(final PointersObject newProcess, final PointersObject activeProcess, final AbstractPointersObjectReadNode readNode, final Node inlineTarget) {
return readNode.executeLong(inlineTarget, newProcess, PROCESS.PRIORITY) > readNode.executeLong(inlineTarget, activeProcess, PROCESS.PRIORITY);
@Specialization
protected static final void resumeProcess(final VirtualFrame frame, final Node node, final PointersObject newProcess,
@Cached final AbstractPointersObjectReadNode readNode,
@Cached final GetActiveProcessNode getActiveProcessNode,
@Cached final PutToSleepNode putToSleepNode,
@Cached final TransferToNode transferToNode) {
final PointersObject activeProcess = getActiveProcessNode.execute(node);
final long activePriority = readNode.executeLong(node, activeProcess, PROCESS.PRIORITY);
final long newPriority = readNode.executeLong(node, newProcess, PROCESS.PRIORITY);
if (newPriority > activePriority) {
putToSleepNode.executePutToSleep(node, activeProcess);
throw transferToNode.execute(frame, node, newProcess);
} else {
putToSleepNode.executePutToSleep(node, newProcess);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright (c) 2023 Software Architecture Group, Hasso Plattner Institute
* Copyright (c) 2023 Oracle and/or its affiliates
*
* Licensed under the MIT License.
*/
package de.hpi.swa.trufflesqueak.nodes.process;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.GenerateCached;
import com.oracle.truffle.api.dsl.GenerateInline;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;

import de.hpi.swa.trufflesqueak.exceptions.ProcessSwitch;
import de.hpi.swa.trufflesqueak.exceptions.SqueakExceptions.SqueakException;
import de.hpi.swa.trufflesqueak.model.ContextObject;
import de.hpi.swa.trufflesqueak.model.PointersObject;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS_SCHEDULER;
import de.hpi.swa.trufflesqueak.nodes.AbstractNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectReadNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectWriteNode;
import de.hpi.swa.trufflesqueak.nodes.context.frame.GetOrCreateContextNode;

@GenerateInline
@GenerateCached(false)
public abstract class TransferToNode extends AbstractNode {

public abstract ProcessSwitch execute(VirtualFrame frame, Node node, PointersObject newProcess);

public static final ProcessSwitch executeUncached(VirtualFrame frame, PointersObject newProcess) {
// Record a process to be awakened on the next interpreter cycle.
final PointersObject scheduler = getContext(null).getScheduler();
final AbstractPointersObjectReadNode readNode = AbstractPointersObjectReadNode.getUncached();
final AbstractPointersObjectWriteNode writeNode = AbstractPointersObjectWriteNode.getUncached();
final PointersObject oldProcess = readNode.executePointers(null, scheduler, PROCESS_SCHEDULER.ACTIVE_PROCESS);
writeNode.execute(null, scheduler, PROCESS_SCHEDULER.ACTIVE_PROCESS, newProcess);
ContextObject activeContext = GetOrCreateContextNode.getOrCreateUncached(frame);
writeNode.execute(null, oldProcess, PROCESS.SUSPENDED_CONTEXT, activeContext);
final Object newActiveContext = readNode.execute(null, newProcess, PROCESS.SUSPENDED_CONTEXT);
writeNode.executeNil(null, newProcess, PROCESS.SUSPENDED_CONTEXT);
writeNode.executeNil(null, newProcess, PROCESS.LIST);
if (!(newActiveContext instanceof final ContextObject newActiveContextObject)) {
throw SqueakException.create("new process not runnable");
}
// Avoid further PE if newActiveContext is not a PE constant.
throw ProcessSwitch.createWithBoundary(newActiveContextObject);
}

@Specialization
protected static final ProcessSwitch transferTo(final VirtualFrame frame, final Node node, final PointersObject newProcess,
@Cached final GetOrCreateContextNode contextNode,
@Cached final AbstractPointersObjectReadNode readOldProcessNode,
@Cached final AbstractPointersObjectReadNode readNewActiveContextNode,
@Cached final AbstractPointersObjectWriteNode writeActiveProcessNode,
@Cached final AbstractPointersObjectWriteNode writeSuspendedContextNode,
@Cached final AbstractPointersObjectWriteNode writeNilContextNode,
@Cached final AbstractPointersObjectWriteNode writeListNode) {
// Record a process to be awakened on the next interpreter cycle.
final PointersObject scheduler = getContext(node).getScheduler();
final PointersObject oldProcess = readOldProcessNode.executePointers(node, scheduler, PROCESS_SCHEDULER.ACTIVE_PROCESS);
writeActiveProcessNode.execute(node, scheduler, PROCESS_SCHEDULER.ACTIVE_PROCESS, newProcess);
writeSuspendedContextNode.execute(node, oldProcess, PROCESS.SUSPENDED_CONTEXT, contextNode.executeGet(frame, node));
final Object newActiveContext = readNewActiveContextNode.execute(node, newProcess, PROCESS.SUSPENDED_CONTEXT);
writeNilContextNode.executeNil(node, newProcess, PROCESS.SUSPENDED_CONTEXT);
writeListNode.executeNil(node, newProcess, PROCESS.LIST);
if (!(newActiveContext instanceof final ContextObject newActiveContextObject)) {
throw SqueakException.create("new process not runnable");
}
if (CompilerDirectives.isPartialEvaluationConstant(newActiveContextObject)) {
throw ProcessSwitch.create(newActiveContextObject);
} else {
// Avoid further PE if newActiveContext is not a PE constant.
throw ProcessSwitch.createWithBoundary(newActiveContextObject);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,14 @@

import de.hpi.swa.trufflesqueak.exceptions.ProcessSwitch;
import de.hpi.swa.trufflesqueak.exceptions.SqueakExceptions.SqueakException;
import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.model.ArrayObject;
import de.hpi.swa.trufflesqueak.model.ContextObject;
import de.hpi.swa.trufflesqueak.model.PointersObject;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS;
import de.hpi.swa.trufflesqueak.model.layout.ObjectLayouts.PROCESS_SCHEDULER;
import de.hpi.swa.trufflesqueak.nodes.AbstractNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectReadNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.AbstractPointersObjectNodes.AbstractPointersObjectWriteNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.ArrayObjectNodes.ArrayObjectReadNode;
import de.hpi.swa.trufflesqueak.nodes.accessing.ArrayObjectNodes.ArrayObjectSizeNode;
import de.hpi.swa.trufflesqueak.nodes.context.frame.GetOrCreateContextNode;

@GenerateInline
@GenerateCached(false)
Expand All @@ -40,25 +36,19 @@ protected static final ProcessSwitch doWake(final VirtualFrame frame, final Node
@Cached final ArrayObjectSizeNode arraySizeNode,
@Cached final AbstractPointersObjectReadNode pointersReadNode,
@Cached final AbstractPointersObjectWriteNode pointersWriteNode,
@Cached final GetOrCreateContextNode contextNode,
@Cached final GetActiveProcessNode getActiveProcessNode) {
final SqueakImageContext image = getContext(node);
@Cached final TransferToNode transferToNode) {
// Return the highest priority process that is ready to run.
// Note: It is a fatal VM error if there is no runnable process.
final ArrayObject schedLists = pointersReadNode.executeArray(node, image.getScheduler(), PROCESS_SCHEDULER.PROCESS_LISTS);
for (long p = arraySizeNode.execute(node, schedLists) - 1; p >= 0; p--) {
final PointersObject processList = (PointersObject) arrayReadNode.execute(node, schedLists, p);
while (!processList.isEmptyList(pointersReadNode, node)) {
final PointersObject newProcess = processList.removeFirstLinkOfList(pointersReadNode, pointersWriteNode, node);
final Object newContext = pointersReadNode.execute(node, newProcess, PROCESS.SUSPENDED_CONTEXT);
if (newContext instanceof final ContextObject newActiveContext) {
contextNode.executeGet(frame, node).transferTo(image, newProcess, getActiveProcessNode.execute(node), newActiveContext, pointersWriteNode, node);
throw SqueakException.create("Should not be reached");
} else {
assert false : "evicted zombie process from run queue";
}
final ArrayObject schedLists = pointersReadNode.executeArray(node, getContext(node).getScheduler(), PROCESS_SCHEDULER.PROCESS_LISTS);
long p = arraySizeNode.execute(node, schedLists) - 1; // index of last indexable field
PointersObject processList;
do {
if (p < 0) {
throw SqueakException.create("scheduler could not find a runnable process");
}
}
throw SqueakException.create("scheduler could not find a runnable process");
processList = (PointersObject) arrayReadNode.execute(node, schedLists, p--);
} while (processList.isEmptyList(pointersReadNode, node));
final PointersObject newProcess = processList.removeFirstLinkOfList(pointersReadNode, pointersWriteNode, node);
throw transferToNode.execute(frame, node, newProcess);
}
}

1 comment on commit 3545a26

@TruffleSqueak-Bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance Report (3545a26)

Benchmarks ran on graalvm-jdk-21+35.1.

Steady (after 100 iterations)

Benchmark Name Min Geomean Median Mean Max Total (ms) Total (min)
Bounce 558 568 560.98 559 560.97 112196 1.87
CD 479 491 482.76 481 482.75 96552 1.61
DeltaBlue 288 462 417.83 418 416.56 83566 1.39
Havlak 1169 1220 1198.64 1203 1198.58 239727 4
Json 365 374 367.44 366 367.42 73487 1.22
List 377 389 378.29 378 378.28 75658 1.26
Mandelbrot 231 241 234.94 236 234.92 46987 0.78
NBody 256 291 278.32 281 278.14 55663 0.93
Permute 155 167 156.9 156 156.89 31380 0.52
Queens 245 260 245.78 245 245.77 49156 0.82
Richards 1245 1258 1249.72 1250 1249.72 249944 4.17
Sieve 177 191 178.14 178 178.12 35627 0.59
Storage 142 151 144.39 143 144.37 28878 0.48
Towers 195 207 197.38 198 197.37 39476 0.66
5882 6270 6091.49 6092 6089.87 1218297 20.3

3545a26-2-steady.svg

Warmup (first 100 iterations)

3545a26-3-warmup.svg

Please sign in to comment.