Skip to content

Commit

Permalink
Check for interrupts in trivial loops
Browse files Browse the repository at this point in the history
With "trivial loop" we mean loops that are empty or do not contain actual sends (e.g., only primitive calls). An actual send would trigger a check for interrupts.

fixes #104, also fixes #167
  • Loading branch information
fniephaus committed Dec 17, 2023
1 parent 1daead2 commit 6ca99a5
Show file tree
Hide file tree
Showing 8 changed files with 111 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,8 @@ public AbstractBytecodeNode[] asBytecodeNodesEmpty() {
return new AbstractBytecodeNode[AbstractSqueakBytecodeDecoder.trailerPosition(this)];
}

public AbstractBytecodeNode bytecodeNodeAt(final VirtualFrame frame, final int pc) {
return getDecoder().decodeBytecode(frame, this, pc);
public AbstractBytecodeNode bytecodeNodeAt(final VirtualFrame frame, final AbstractBytecodeNode[] bytecodeNodes, final int pc) {
return getDecoder().decodeBytecode(frame, this, bytecodeNodes, pc);
}

public int findLineNumber(final int index) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
import de.hpi.swa.trufflesqueak.exceptions.Returns.NonLocalReturn;
import de.hpi.swa.trufflesqueak.model.CompiledCodeObject;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.AbstractBytecodeNode;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.JumpBytecodes.AbstractUnconditionalJumpNode;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.JumpBytecodes.ConditionalJumpNode;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.JumpBytecodes.UnconditionalJumpNode;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.ReturnBytecodes.AbstractReturnNode;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.SendBytecodes.AbstractSendNode;
import de.hpi.swa.trufflesqueak.nodes.primitives.AbstractPrimitiveNode;
Expand Down Expand Up @@ -127,7 +127,8 @@ private Object interpretBytecode(final VirtualFrame frame, final int startPC) {
pc = jumpNode.getSuccessorIndex();
continue bytecode_loop;
}
} else if (node instanceof final UnconditionalJumpNode jumpNode) {
} else if (node instanceof final AbstractUnconditionalJumpNode jumpNode) {
jumpNode.executeCheck(frame);
final int successor = jumpNode.getSuccessorIndex();
if (CompilerDirectives.hasNextTier() && successor <= pc) {
backJumpCounter.value++;
Expand Down Expand Up @@ -173,7 +174,7 @@ private static final class Counter {
private AbstractBytecodeNode fetchNextBytecodeNode(final VirtualFrame frame, final int pcZeroBased) {
if (bytecodeNodes[pcZeroBased] == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
bytecodeNodes[pcZeroBased] = insert(code.bytecodeNodeAt(frame, pcZeroBased));
bytecodeNodes[pcZeroBased] = insert(code.bytecodeNodeAt(frame, bytecodeNodes, pcZeroBased));
notifyInserted(bytecodeNodes[pcZeroBased]);
}
return bytecodeNodes[pcZeroBased];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

public abstract class AbstractSqueakBytecodeDecoder {

public abstract AbstractBytecodeNode decodeBytecode(VirtualFrame frame, CompiledCodeObject code, int index);
public abstract AbstractBytecodeNode decodeBytecode(VirtualFrame frame, CompiledCodeObject code, AbstractBytecodeNode[] bytecodeNodes, int index);

public abstract boolean hasStoreIntoTemp1AfterCallPrimitive(CompiledCodeObject code);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
import com.oracle.truffle.api.nodes.IndirectCallNode;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.profiles.CountingConditionProfile;

import de.hpi.swa.trufflesqueak.exceptions.ProcessSwitch;
import de.hpi.swa.trufflesqueak.exceptions.SqueakExceptions.SqueakException;
import de.hpi.swa.trufflesqueak.model.CompiledCodeObject;
import de.hpi.swa.trufflesqueak.nodes.bytecodes.SendBytecodes.AbstractSendNode;
import de.hpi.swa.trufflesqueak.nodes.context.frame.FrameStackPopNode;
import de.hpi.swa.trufflesqueak.nodes.interrupts.CheckForInterruptsNode;
import de.hpi.swa.trufflesqueak.util.FrameAccess;
import de.hpi.swa.trufflesqueak.util.LogUtils;

public final class JumpBytecodes {

Expand Down Expand Up @@ -113,33 +120,100 @@ protected boolean check(final boolean value) {
}
}

public static final class UnconditionalJumpNode extends AbstractBytecodeNode {
private UnconditionalJumpNode(final CompiledCodeObject code, final int index, final int numBytecodes, final int offset) {
public abstract static class AbstractUnconditionalJumpNode extends AbstractBytecodeNode {
private AbstractUnconditionalJumpNode(final CompiledCodeObject code, final int index, final int numBytecodes, final int offset) {
super(code, index, numBytecodes + offset);
}

public static UnconditionalJumpNode createShort(final CompiledCodeObject code, final int index, final int bytecode) {
return new UnconditionalJumpNode(code, index, 1, calculateShortOffset(bytecode));
@Override
public void executeVoid(final VirtualFrame frame) {
throw CompilerDirectives.shouldNotReachHere();
}

public static UnconditionalJumpNode createLong(final CompiledCodeObject code, final int index, final int bytecode, final byte parameter) {
return new UnconditionalJumpNode(code, index, 2, ((bytecode & 7) - 4 << 8) + Byte.toUnsignedInt(parameter));
public abstract void executeCheck(VirtualFrame frame);

@Override
public String toString() {
CompilerAsserts.neverPartOfCompilation();
return "jumpTo: " + getSuccessorIndex();
}
}

public static UnconditionalJumpNode createLongExtended(final CompiledCodeObject code, final int index, final int numBytecodes, final byte bytecode, final int extB) {
return new UnconditionalJumpNode(code, index, numBytecodes, calculateLongExtendedOffset(bytecode, extB));
public static final class UnconditionalJumpWithoutCheckNode extends AbstractUnconditionalJumpNode {
protected UnconditionalJumpWithoutCheckNode(final CompiledCodeObject code, final int index, final int numBytecodes, final int offset) {
super(code, index, numBytecodes, offset);
}

@Override
public void executeVoid(final VirtualFrame frame) {
throw CompilerDirectives.shouldNotReachHere();
public void executeCheck(final VirtualFrame frame) {
// nothing to do
}
}

protected static final class UnconditionalJumpWithCheckNode extends AbstractUnconditionalJumpNode {
@Child private CheckForInterruptsNode interruptHandlerNode = CheckForInterruptsNode.create();

protected UnconditionalJumpWithCheckNode(final CompiledCodeObject code, final int index, final int numBytecodes, final int offset) {
super(code, index, numBytecodes, offset);
LogUtils.INTERRUPTS.fine(() -> "Added interrupt check to backjump in " + code);
}

@Override
public String toString() {
CompilerAsserts.neverPartOfCompilation();
return "jumpTo: " + getSuccessorIndex();
public void executeCheck(final VirtualFrame frame) {
try {
interruptHandlerNode.execute(frame);
} catch (ProcessSwitch ps) {
// persist PC
FrameAccess.setInstructionPointer(frame, getSuccessorIndex());
throw ps;
}
}
}

public static AbstractUnconditionalJumpNode createUnconditionalShortJump(final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index, final int bytecode) {
final int offset = calculateShortOffset(bytecode);
if (needsCheck(bytecodeNodes, index, 1, offset)) {
return new UnconditionalJumpWithCheckNode(code, index, 1, offset);
} else {
return new UnconditionalJumpWithoutCheckNode(code, index, 1, offset);
}
}

public static AbstractUnconditionalJumpNode createUnconditionalLongJump(final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index, final int bytecode,
final byte parameter) {
final int offset = ((bytecode & 7) - 4 << 8) + Byte.toUnsignedInt(parameter);
if (needsCheck(bytecodeNodes, index, 2, offset)) {
return new UnconditionalJumpWithCheckNode(code, index, 2, offset);
} else {
return new UnconditionalJumpWithoutCheckNode(code, index, 2, offset);
}
}

public static AbstractUnconditionalJumpNode createUnconditionalLongExtendedJump(final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index, final int numBytecodes,
final byte bytecode, final int extB) {
final int offset = calculateLongExtendedOffset(bytecode, extB);
if (needsCheck(bytecodeNodes, index, numBytecodes, offset)) {
return new UnconditionalJumpWithCheckNode(code, index, numBytecodes, offset);
} else {
return new UnconditionalJumpWithoutCheckNode(code, index, numBytecodes, offset);
}
}

private static boolean needsCheck(final AbstractBytecodeNode[] bytecodeNodes, final int index, final int numBytecodes, final int offset) {
CompilerAsserts.neverPartOfCompilation();
if (offset < 0) { // backjumps only
final int backJumpIndex = index + numBytecodes + offset;
for (int i = backJumpIndex; i < index; i++) {
if (bytecodeNodes[i] instanceof final AbstractSendNode abs) {
// NodeUtil.printTree(System.out, abs);
if (NodeUtil.findFirstNodeInstance(abs, DirectCallNode.class) != null || NodeUtil.findFirstNodeInstance(abs, IndirectCallNode.class) != null) {
return false;
}
}
}
return true;
}
return false;
}

public static int calculateShortOffset(final int bytecode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ public boolean hasStoreIntoTemp1AfterCallPrimitive(final CompiledCodeObject code
}

@Override
public AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final int index) {
return decodeBytecode(frame, code, index, 0, 0, 0, 0);
public AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index) {
return decodeBytecode(frame, code, bytecodeNodes, index, 0, 0, 0, 0);
}

private static AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final int index, final int extBytes, final int extA, final int extB,
final int numExtB) {
private static AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index, final int extBytes,
final int extA, final int extB, final int numExtB) {
CompilerAsserts.neverPartOfCompilation();
final byte[] bytecode = code.getBytes();
final int indexWithExt = index + extBytes;
Expand Down Expand Up @@ -86,7 +86,7 @@ private static AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, fin
-> SendBytecodes.SelfSendNode.create(code, index, 1, (NativeObject) code.getLiteral(b & 0xF), 1);
case 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF //
-> SendBytecodes.SelfSendNode.create(code, index, 1, (NativeObject) code.getLiteral(b & 0xF), 2);
case 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7 -> JumpBytecodes.UnconditionalJumpNode.createShort(code, index, b);
case 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7 -> JumpBytecodes.createUnconditionalShortJump(code, bytecodeNodes, index, b);
case 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF -> JumpBytecodes.ConditionalJumpOnTrueNode.createShort(code, index, b);
case 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7 -> JumpBytecodes.ConditionalJumpOnFalseNode.createShort(code, index, b);
case 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF -> new StoreBytecodes.PopIntoReceiverVariableNode(code, index, 1, b & 7);
Expand All @@ -95,10 +95,10 @@ private static AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, fin
// FIXME: Unconditional trap
case 0xD9 -> SendBytecodes.SelfSendNode.create(code, index, 1, code.getSqueakClass().getImage().getSpecialSelector(SPECIAL_OBJECT.SELECTOR_SISTA_TRAP), 1);
case 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF -> new MiscellaneousBytecodes.UnknownBytecodeNode(code, index, 1, b);
case 0xE0 -> decodeBytecode(frame, code, index, extBytes + 2, (extA << 8) + Byte.toUnsignedInt(bytecode[indexWithExt + 1]), extB, numExtB);
case 0xE0 -> decodeBytecode(frame, code, bytecodeNodes, index, extBytes + 2, (extA << 8) + Byte.toUnsignedInt(bytecode[indexWithExt + 1]), extB, numExtB);
case 0xE1 -> {
final int byteValue = Byte.toUnsignedInt(bytecode[indexWithExt + 1]);
yield decodeBytecode(frame, code, index, extBytes + 2, extA, numExtB == 0 && byteValue > 127 ? byteValue - 256 : (extB << 8) + byteValue, numExtB + 1);
yield decodeBytecode(frame, code, bytecodeNodes, index, extBytes + 2, extA, numExtB == 0 && byteValue > 127 ? byteValue - 256 : (extB << 8) + byteValue, numExtB + 1);
}
case 0xE2 -> PushBytecodes.PushReceiverVariableNode.create(code, index, 2 + extBytes, Byte.toUnsignedInt(bytecode[indexWithExt + 1]) + (extA << 8));
case 0xE3 -> PushBytecodes.PushLiteralVariableNode.create(code, index, 2 + extBytes, Byte.toUnsignedInt(bytecode[indexWithExt + 1]) + (extA << 8));
Expand Down Expand Up @@ -132,7 +132,7 @@ private static AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, fin
}
}
case 0xEC -> new MiscellaneousBytecodes.UnknownBytecodeNode(code, index, 1, b);
case 0xED -> JumpBytecodes.UnconditionalJumpNode.createLongExtended(code, index, 2 + extBytes, bytecode[indexWithExt + 1], extB);
case 0xED -> JumpBytecodes.createUnconditionalLongExtendedJump(code, bytecodeNodes, index, 2 + extBytes, bytecode[indexWithExt + 1], extB);
case 0xEE -> JumpBytecodes.ConditionalJumpOnTrueNode.createLongExtended(code, index, 2 + extBytes, bytecode[indexWithExt + 1], extB);
case 0xEF -> JumpBytecodes.ConditionalJumpOnFalseNode.createLongExtended(code, index, 2 + extBytes, bytecode[indexWithExt + 1], extB);
case 0xF0 -> new StoreBytecodes.PopIntoReceiverVariableNode(code, index, 2 + extBytes, Byte.toUnsignedInt(bytecode[indexWithExt + 1]) + (extA << 8));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public boolean hasStoreIntoTemp1AfterCallPrimitive(final CompiledCodeObject code
}

@Override
public AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final int index) {
public AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final CompiledCodeObject code, final AbstractBytecodeNode[] bytecodeNodes, final int index) {
CompilerAsserts.neverPartOfCompilation();
final byte[] bytecode = code.getBytes();
final int b = Byte.toUnsignedInt(bytecode[index]);
Expand Down Expand Up @@ -82,9 +82,9 @@ public AbstractBytecodeNode decodeBytecode(final VirtualFrame frame, final Compi
case 0x8D -> new StoreBytecodes.StoreIntoRemoteTempNode(code, index, 3, bytecode[index + 1], bytecode[index + 2]);
case 0x8E -> new StoreBytecodes.PopIntoRemoteTempNode(code, index, 3, bytecode[index + 1], bytecode[index + 2]);
case 0x8F -> PushBytecodes.PushClosureNode.create(code, index, bytecode[index + 1], bytecode[index + 2], bytecode[index + 3]);
case 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97 -> JumpBytecodes.UnconditionalJumpNode.createShort(code, index, b);
case 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97 -> JumpBytecodes.createUnconditionalShortJump(code, bytecodeNodes, index, b);
case 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F -> JumpBytecodes.ConditionalJumpOnFalseNode.createShort(code, index, b);
case 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7 -> JumpBytecodes.UnconditionalJumpNode.createLong(code, index, b, bytecode[index + 1]);
case 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7 -> JumpBytecodes.createUnconditionalLongJump(code, bytecodeNodes, index, b, bytecode[index + 1]);
case 0xA8, 0xA9, 0xAA, 0xAB -> JumpBytecodes.ConditionalJumpOnTrueNode.createLong(code, index, b, bytecode[index + 1]);
case 0xAC, 0xAD, 0xAE, 0xAF -> JumpBytecodes.ConditionalJumpOnFalseNode.createLong(code, index, b, bytecode[index + 1]);
case 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import com.oracle.truffle.api.dsl.NeverDefault;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;

import de.hpi.swa.trufflesqueak.image.SqueakImageContext;
import de.hpi.swa.trufflesqueak.model.ArrayObject;
Expand All @@ -24,11 +23,6 @@ public final class CheckForInterruptsNode extends Node {
private final Object[] specialObjects;
private final CheckForInterruptsState istate;

private final BranchProfile isActiveProfile = BranchProfile.create();
private final BranchProfile nextWakeupTickProfile = BranchProfile.create();
private final BranchProfile pendingFinalizationSignalsProfile = BranchProfile.create();
private final BranchProfile hasSemaphoresToSignalProfile = BranchProfile.create();

private CheckForInterruptsNode(final SqueakImageContext image) {
specialObjects = image.specialObjectsArray.getObjectStorage();
istate = image.interrupt;
Expand All @@ -44,28 +38,24 @@ public void execute(final VirtualFrame frame) {
if (!istate.shouldTrigger()) {
return;
}
isActiveProfile.enter();
/* Exclude interrupts case from compilation. */
CompilerDirectives.transferToInterpreter();
if (istate.interruptPending()) {
/* Exclude user interrupt case from compilation. */
CompilerDirectives.transferToInterpreter();
LogUtils.INTERRUPTS.fine("User interrupt");
istate.interruptPending = false; // reset interrupt flag
signalSemaporeNode.executeSignal(frame, this, istate.getInterruptSemaphore());
}
if (istate.nextWakeUpTickTrigger()) {
nextWakeupTickProfile.enter();
LogUtils.INTERRUPTS.fine("Timer interrupt");
istate.nextWakeupTick = 0; // reset timer interrupt
signalSemaporeNode.executeSignal(frame, this, istate.getTimerSemaphore());
}
if (istate.pendingFinalizationSignals()) { // signal any pending finalizations
pendingFinalizationSignalsProfile.enter();
LogUtils.INTERRUPTS.fine("Finalization interrupt");
istate.setPendingFinalizations(false);
signalSemaporeNode.executeSignal(frame, this, specialObjects[SPECIAL_OBJECT.THE_FINALIZATION_SEMAPHORE]);
}
if (istate.hasSemaphoresToSignal()) {
hasSemaphoresToSignalProfile.enter();
LogUtils.INTERRUPTS.fine("Semaphore interrupt");
final ArrayObject externalObjects = (ArrayObject) specialObjects[SPECIAL_OBJECT.EXTERNAL_OBJECTS_ARRAY];
if (!externalObjects.isEmptyType()) { // signal external semaphores
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public static CheckForInterruptsQuickNode create(final CompiledCodeObject code)
}
}

public static CheckForInterruptsQuickNode create() {
return new CheckForInterruptsQuickImplNode(SqueakImageContext.getSlow());
}

public abstract void execute(VirtualFrame frame);

@DenyReplace
Expand Down

0 comments on commit 6ca99a5

Please sign in to comment.