package org.graalvm.wasm.nodes;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.CompilerAsserts;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.HostCompilerDirectives.BytecodeInterpreterSwitch;
import com.oracle.truffle.api.HostCompilerDirectives.BytecodeInterpreterSwitchBoundary;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage.ContextReference;
import com.oracle.truffle.api.frame.FrameSlotTypeException;
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.LoopNode;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RepeatingNode;
import org.graalvm.wasm.Assert;
import org.graalvm.wasm.BinaryStreamParser;
import org.graalvm.wasm.SymbolTable;
import org.graalvm.wasm.WasmCodeEntry;
import org.graalvm.wasm.WasmContext;
import org.graalvm.wasm.WasmFunction;
import org.graalvm.wasm.WasmFunctionInstance;
import org.graalvm.wasm.WasmInstance;
import org.graalvm.wasm.WasmLanguage;
import org.graalvm.wasm.WasmTable;
import org.graalvm.wasm.WasmType;
import org.graalvm.wasm.exception.Failure;
import org.graalvm.wasm.exception.WasmException;
import org.graalvm.wasm.memory.WasmMemory;
import org.graalvm.wasm.memory.WasmMemoryException;
import static org.graalvm.wasm.constants.Instructions.BLOCK;
import static org.graalvm.wasm.constants.Instructions.BR;
import static org.graalvm.wasm.constants.Instructions.BR_IF;
import static org.graalvm.wasm.constants.Instructions.BR_TABLE;
import static org.graalvm.wasm.constants.Instructions.CALL;
import static org.graalvm.wasm.constants.Instructions.CALL_INDIRECT;
import static org.graalvm.wasm.constants.Instructions.DROP;
import static org.graalvm.wasm.constants.Instructions.ELSE;
import static org.graalvm.wasm.constants.Instructions.END;
import static org.graalvm.wasm.constants.Instructions.F32_ABS;
import static org.graalvm.wasm.constants.Instructions.F32_ADD;
import static org.graalvm.wasm.constants.Instructions.F32_CEIL;
import static org.graalvm.wasm.constants.Instructions.F32_CONST;
import static org.graalvm.wasm.constants.Instructions.F32_CONVERT_I32_S;
import static org.graalvm.wasm.constants.Instructions.F32_CONVERT_I32_U;
import static org.graalvm.wasm.constants.Instructions.F32_CONVERT_I64_S;
import static org.graalvm.wasm.constants.Instructions.F32_CONVERT_I64_U;
import static org.graalvm.wasm.constants.Instructions.F32_COPYSIGN;
import static org.graalvm.wasm.constants.Instructions.F32_DEMOTE_F64;
import static org.graalvm.wasm.constants.Instructions.F32_DIV;
import static org.graalvm.wasm.constants.Instructions.F32_EQ;
import static org.graalvm.wasm.constants.Instructions.F32_FLOOR;
import static org.graalvm.wasm.constants.Instructions.F32_GE;
import static org.graalvm.wasm.constants.Instructions.F32_GT;
import static org.graalvm.wasm.constants.Instructions.F32_LE;
import static org.graalvm.wasm.constants.Instructions.F32_LOAD;
import static org.graalvm.wasm.constants.Instructions.F32_LT;
import static org.graalvm.wasm.constants.Instructions.F32_MAX;
import static org.graalvm.wasm.constants.Instructions.F32_MIN;
import static org.graalvm.wasm.constants.Instructions.F32_MUL;
import static org.graalvm.wasm.constants.Instructions.F32_NE;
import static org.graalvm.wasm.constants.Instructions.F32_NEAREST;
import static org.graalvm.wasm.constants.Instructions.F32_NEG;
import static org.graalvm.wasm.constants.Instructions.F32_REINTERPRET_I32;
import static org.graalvm.wasm.constants.Instructions.F32_SQRT;
import static org.graalvm.wasm.constants.Instructions.F32_STORE;
import static org.graalvm.wasm.constants.Instructions.F32_SUB;
import static org.graalvm.wasm.constants.Instructions.F32_TRUNC;
import static org.graalvm.wasm.constants.Instructions.F64_ABS;
import static org.graalvm.wasm.constants.Instructions.F64_ADD;
import static org.graalvm.wasm.constants.Instructions.F64_CEIL;
import static org.graalvm.wasm.constants.Instructions.F64_CONST;
import static org.graalvm.wasm.constants.Instructions.F64_CONVERT_I32_S;
import static org.graalvm.wasm.constants.Instructions.F64_CONVERT_I32_U;
import static org.graalvm.wasm.constants.Instructions.F64_CONVERT_I64_S;
import static org.graalvm.wasm.constants.Instructions.F64_CONVERT_I64_U;
import static org.graalvm.wasm.constants.Instructions.F64_COPYSIGN;
import static org.graalvm.wasm.constants.Instructions.F64_DIV;
import static org.graalvm.wasm.constants.Instructions.F64_EQ;
import static org.graalvm.wasm.constants.Instructions.F64_FLOOR;
import static org.graalvm.wasm.constants.Instructions.F64_GE;
import static org.graalvm.wasm.constants.Instructions.F64_GT;
import static org.graalvm.wasm.constants.Instructions.F64_LE;
import static org.graalvm.wasm.constants.Instructions.F64_LOAD;
import static org.graalvm.wasm.constants.Instructions.F64_LT;
import static org.graalvm.wasm.constants.Instructions.F64_MAX;
import static org.graalvm.wasm.constants.Instructions.F64_MIN;
import static org.graalvm.wasm.constants.Instructions.F64_MUL;
import static org.graalvm.wasm.constants.Instructions.F64_NE;
import static org.graalvm.wasm.constants.Instructions.F64_NEAREST;
import static org.graalvm.wasm.constants.Instructions.F64_NEG;
import static org.graalvm.wasm.constants.Instructions.F64_PROMOTE_F32;
import static org.graalvm.wasm.constants.Instructions.F64_REINTERPRET_I64;
import static org.graalvm.wasm.constants.Instructions.F64_SQRT;
import static org.graalvm.wasm.constants.Instructions.F64_STORE;
import static org.graalvm.wasm.constants.Instructions.F64_SUB;
import static org.graalvm.wasm.constants.Instructions.F64_TRUNC;
import static org.graalvm.wasm.constants.Instructions.GLOBAL_GET;
import static org.graalvm.wasm.constants.Instructions.GLOBAL_SET;
import static org.graalvm.wasm.constants.Instructions.I32_ADD;
import static org.graalvm.wasm.constants.Instructions.I32_AND;
import static org.graalvm.wasm.constants.Instructions.I32_CLZ;
import static org.graalvm.wasm.constants.Instructions.I32_CONST;
import static org.graalvm.wasm.constants.Instructions.I32_CTZ;
import static org.graalvm.wasm.constants.Instructions.I32_DIV_S;
import static org.graalvm.wasm.constants.Instructions.I32_DIV_U;
import static org.graalvm.wasm.constants.Instructions.I32_EQ;
import static org.graalvm.wasm.constants.Instructions.I32_EQZ;
import static org.graalvm.wasm.constants.Instructions.I32_GE_S;
import static org.graalvm.wasm.constants.Instructions.I32_GE_U;
import static org.graalvm.wasm.constants.Instructions.I32_GT_S;
import static org.graalvm.wasm.constants.Instructions.I32_GT_U;
import static org.graalvm.wasm.constants.Instructions.I32_LE_S;
import static org.graalvm.wasm.constants.Instructions.I32_LE_U;
import static org.graalvm.wasm.constants.Instructions.I32_LOAD;
import static org.graalvm.wasm.constants.Instructions.I32_LOAD16_S;
import static org.graalvm.wasm.constants.Instructions.I32_LOAD16_U;
import static org.graalvm.wasm.constants.Instructions.I32_LOAD8_S;
import static org.graalvm.wasm.constants.Instructions.I32_LOAD8_U;
import static org.graalvm.wasm.constants.Instructions.I32_LT_S;
import static org.graalvm.wasm.constants.Instructions.I32_LT_U;
import static org.graalvm.wasm.constants.Instructions.I32_MUL;
import static org.graalvm.wasm.constants.Instructions.I32_NE;
import static org.graalvm.wasm.constants.Instructions.I32_OR;
import static org.graalvm.wasm.constants.Instructions.I32_POPCNT;
import static org.graalvm.wasm.constants.Instructions.I32_REINTERPRET_F32;
import static org.graalvm.wasm.constants.Instructions.I32_REM_S;
import static org.graalvm.wasm.constants.Instructions.I32_REM_U;
import static org.graalvm.wasm.constants.Instructions.I32_ROTL;
import static org.graalvm.wasm.constants.Instructions.I32_ROTR;
import static org.graalvm.wasm.constants.Instructions.I32_SHL;
import static org.graalvm.wasm.constants.Instructions.I32_SHR_S;
import static org.graalvm.wasm.constants.Instructions.I32_SHR_U;
import static org.graalvm.wasm.constants.Instructions.I32_STORE;
import static org.graalvm.wasm.constants.Instructions.I32_STORE_16;
import static org.graalvm.wasm.constants.Instructions.I32_STORE_8;
import static org.graalvm.wasm.constants.Instructions.I32_SUB;
import static org.graalvm.wasm.constants.Instructions.I32_TRUNC_F32_S;
import static org.graalvm.wasm.constants.Instructions.I32_TRUNC_F32_U;
import static org.graalvm.wasm.constants.Instructions.I32_TRUNC_F64_S;
import static org.graalvm.wasm.constants.Instructions.I32_TRUNC_F64_U;
import static org.graalvm.wasm.constants.Instructions.I32_WRAP_I64;
import static org.graalvm.wasm.constants.Instructions.I32_XOR;
import static org.graalvm.wasm.constants.Instructions.I64_ADD;
import static org.graalvm.wasm.constants.Instructions.I64_AND;
import static org.graalvm.wasm.constants.Instructions.I64_CLZ;
import static org.graalvm.wasm.constants.Instructions.I64_CONST;
import static org.graalvm.wasm.constants.Instructions.I64_CTZ;
import static org.graalvm.wasm.constants.Instructions.I64_DIV_S;
import static org.graalvm.wasm.constants.Instructions.I64_DIV_U;
import static org.graalvm.wasm.constants.Instructions.I64_EQ;
import static org.graalvm.wasm.constants.Instructions.I64_EQZ;
import static org.graalvm.wasm.constants.Instructions.I64_EXTEND_I32_S;
import static org.graalvm.wasm.constants.Instructions.I64_EXTEND_I32_U;
import static org.graalvm.wasm.constants.Instructions.I64_GE_S;
import static org.graalvm.wasm.constants.Instructions.I64_GE_U;
import static org.graalvm.wasm.constants.Instructions.I64_GT_S;
import static org.graalvm.wasm.constants.Instructions.I64_GT_U;
import static org.graalvm.wasm.constants.Instructions.I64_LE_S;
import static org.graalvm.wasm.constants.Instructions.I64_LE_U;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD16_S;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD16_U;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD32_S;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD32_U;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD8_S;
import static org.graalvm.wasm.constants.Instructions.I64_LOAD8_U;
import static org.graalvm.wasm.constants.Instructions.I64_LT_S;
import static org.graalvm.wasm.constants.Instructions.I64_LT_U;
import static org.graalvm.wasm.constants.Instructions.I64_MUL;
import static org.graalvm.wasm.constants.Instructions.I64_NE;
import static org.graalvm.wasm.constants.Instructions.I64_OR;
import static org.graalvm.wasm.constants.Instructions.I64_POPCNT;
import static org.graalvm.wasm.constants.Instructions.I64_REINTERPRET_F64;
import static org.graalvm.wasm.constants.Instructions.I64_REM_S;
import static org.graalvm.wasm.constants.Instructions.I64_REM_U;
import static org.graalvm.wasm.constants.Instructions.I64_ROTL;
import static org.graalvm.wasm.constants.Instructions.I64_ROTR;
import static org.graalvm.wasm.constants.Instructions.I64_SHL;
import static org.graalvm.wasm.constants.Instructions.I64_SHR_S;
import static org.graalvm.wasm.constants.Instructions.I64_SHR_U;
import static org.graalvm.wasm.constants.Instructions.I64_STORE;
import static org.graalvm.wasm.constants.Instructions.I64_STORE_16;
import static org.graalvm.wasm.constants.Instructions.I64_STORE_32;
import static org.graalvm.wasm.constants.Instructions.I64_STORE_8;
import static org.graalvm.wasm.constants.Instructions.I64_SUB;
import static org.graalvm.wasm.constants.Instructions.I64_TRUNC_F32_S;
import static org.graalvm.wasm.constants.Instructions.I64_TRUNC_F32_U;
import static org.graalvm.wasm.constants.Instructions.I64_TRUNC_F64_S;
import static org.graalvm.wasm.constants.Instructions.I64_TRUNC_F64_U;
import static org.graalvm.wasm.constants.Instructions.I64_XOR;
import static org.graalvm.wasm.constants.Instructions.IF;
import static org.graalvm.wasm.constants.Instructions.LOCAL_GET;
import static org.graalvm.wasm.constants.Instructions.LOCAL_SET;
import static org.graalvm.wasm.constants.Instructions.LOCAL_TEE;
import static org.graalvm.wasm.constants.Instructions.LOOP;
import static org.graalvm.wasm.constants.Instructions.MEMORY_GROW;
import static org.graalvm.wasm.constants.Instructions.MEMORY_SIZE;
import static org.graalvm.wasm.constants.Instructions.NOP;
import static org.graalvm.wasm.constants.Instructions.RETURN;
import static org.graalvm.wasm.constants.Instructions.SELECT;
import static org.graalvm.wasm.constants.Instructions.UNREACHABLE;
public final class WasmBlockNode extends WasmNode implements RepeatingNode {
@CompilationFinal private int intConstantLength;
@CompilationFinal private int branchTableLength;
@CompilationFinal private final int startOffset;
@CompilationFinal private final byte returnTypeId;
@CompilationFinal private final int initialStackPointer;
@CompilationFinal private final int initialIntConstantOffset;
@CompilationFinal private final int initialBranchTableOffset;
@CompilationFinal private final int initialProfileOffset;
@CompilationFinal private int profileCount;
@CompilationFinal private ContextReference<WasmContext> rawContextReference;
@Children private Node[] children;
public WasmBlockNode(WasmInstance wasmInstance, WasmCodeEntry codeEntry, int startOffset, byte returnTypeId, int initialStackPointer, int initialIntConstantOffset,
int initialBranchTableOffset, int initialProfileOffset) {
super(wasmInstance, codeEntry, -1);
this.startOffset = startOffset;
this.returnTypeId = returnTypeId;
this.initialStackPointer = initialStackPointer;
this.initialIntConstantOffset = initialIntConstantOffset;
this.initialBranchTableOffset = initialBranchTableOffset;
this.initialProfileOffset = initialProfileOffset;
}
private ContextReference<WasmContext> contextReference() {
if (rawContextReference == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
rawContextReference = lookupContextReference(WasmLanguage.class);
}
return rawContextReference;
}
@SuppressWarnings("hiding")
public void initialize(Node[] children, int byteLength, int intConstantLength, int branchTableLength, int profileCount) {
initialize(byteLength);
this.intConstantLength = intConstantLength;
this.branchTableLength = branchTableLength;
this.profileCount = profileCount;
this.children = children;
}
@Override
int intConstantLength() {
return intConstantLength;
}
@Override
int branchTableLength() {
return branchTableLength;
}
@Override
int profileCount() {
return profileCount;
}
public int startOfset() {
return startOffset;
}
@Override
public Object initialLoopStatus() {
return 0;
}
@Override
public boolean executeRepeating(VirtualFrame frame) {
throw WasmException.create(Failure.UNSPECIFIED_INTERNAL, this, "This method should never have been called.");
}
@Override
public Integer executeRepeatingWithValue(VirtualFrame frame) {
final WasmCodeEntry codeEntry = codeEntry();
final long[] stacklocals;
try {
stacklocals = (long[]) frame.getObject(codeEntry.stackLocalsSlot());
} catch (FrameSlotTypeException e) {
throw WasmException.create(Failure.UNSPECIFIED_INTERNAL, this, "Invalid object type in the stack slot.");
}
return execute(contextReference().get(), frame, stacklocals);
}
@Override
@BytecodeInterpreterSwitch
@BytecodeInterpreterSwitchBoundary
@ExplodeLoop(kind = ExplodeLoop.LoopExplosionKind.FULL_EXPLODE_UNTIL_RETURN)
public int execute(WasmContext context, VirtualFrame frame, long[] stacklocals) {
final WasmCodeEntry codeEntry = codeEntry();
final int numLocals = codeEntry.numLocals();
final byte[] data = codeEntry.data();
final int[] intConstants = codeEntry.intConstants();
final int[] profileCounters = codeEntry.profileCounters();
final int blockByteLength = byteLength();
final int offsetLimit = startOffset + blockByteLength;
int childrenOffset = 0;
int intConstantOffset = initialIntConstantOffset;
int branchTableOffset = initialBranchTableOffset;
int stackPointer = numLocals + initialStackPointer;
int profileOffset = initialProfileOffset;
int offset = startOffset;
WasmMemory memory = instance().memory();
check(data.length, (1 << 31) - 1);
check(intConstants.length, (1 << 31) - 1);
check(profileCounters.length, (1 << 31) - 1);
check(stacklocals.length, (1 << 31) - 1);
try {
while (offset < offsetLimit) {
byte byteOpcode = BinaryStreamParser.rawPeek1(data, offset);
int opcode = byteOpcode & 0xFF;
offset++;
CompilerAsserts.partialEvaluationConstant(offset);
switch (opcode) {
case UNREACHABLE:
throw WasmException.create(Failure.UNSPECIFIED_TRAP, this, "unreachable");
case NOP:
break;
case BLOCK: {
WasmBlockNode block = (WasmBlockNode) children[childrenOffset];
int unwindCounter = block.execute(context, frame, stacklocals);
if (unwindCounter > 0) {
return unwindCounter - 1;
}
childrenOffset++;
offset += block.byteLength();
stackPointer += block.returnLength();
intConstantOffset += block.intConstantLength();
branchTableOffset += block.branchTableLength();
profileOffset += block.profileCount();
break;
}
case LOOP: {
LoopNode loopNode = (LoopNode) children[childrenOffset];
final WasmBlockNode loopBody = (WasmBlockNode) loopNode.getRepeatingNode();
int unwindCounter = executeLoopNode(childrenOffset, frame);
if (unwindCounter > 0) {
return unwindCounter - 1;
}
assert unwindCounter == -1 : "Unwind counter after loop exit: " + unwindCounter;
childrenOffset++;
offset += loopBody.byteLength();
stackPointer += loopBody.returnLength();
intConstantOffset += loopBody.intConstantLength();
branchTableOffset += loopBody.branchTableLength();
profileOffset += loopBody.profileCount();
break;
}
case IF: {
WasmIfNode ifNode = (WasmIfNode) children[childrenOffset];
stackPointer--;
int unwindCounter = ifNode.execute(context, frame, stacklocals);
if (unwindCounter > 0) {
return unwindCounter - 1;
}
childrenOffset++;
offset += ifNode.byteLength();
stackPointer += ifNode.returnLength();
intConstantOffset += ifNode.intConstantLength();
branchTableOffset += ifNode.branchTableLength();
profileOffset += ifNode.profileCount();
break;
}
case ELSE:
break;
case END:
break;
case BR: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int unwindCounter = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
int continuationStackPointer = intConstants[intConstantOffset];
intConstantOffset++;
int targetBlockReturnLength = intConstants[intConstantOffset];
intConstantOffset++;
unwindStack(stacklocals, stackPointer, numLocals + continuationStackPointer, targetBlockReturnLength);
return unwindCounter;
}
case BR_IF: {
stackPointer--;
long valueLength = unsignedIntConstantAndLength(data, offset);
int unwindCounter = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
int continuationStackPointer = intConstants[intConstantOffset];
intConstantOffset++;
int targetBlockReturnLength = intConstants[intConstantOffset];
intConstantOffset++;
boolean condition = WasmCodeEntry.profileCondition(profileCounters, profileOffset, popBoolean(stacklocals, stackPointer));
++profileOffset;
if (condition) {
unwindStack(stacklocals, stackPointer, numLocals + continuationStackPointer, targetBlockReturnLength);
return unwindCounter;
}
break;
}
case BR_TABLE: {
stackPointer--;
int index = popInt(stacklocals, stackPointer);
int[] table = codeEntry.branchTable(branchTableOffset);
index = index < 0 || index >= (table.length - 1) / 2 ? (table.length - 1) / 2 - 1 : index;
int returnTypeLength = table[0];
for (int i = 0; i < (table.length - 1) / 2; ++i) {
if (i == index) {
int unwindCounter = table[1 + 2 * i];
int continuationStackPointer = table[1 + 2 * i + 1];
unwindStack(stacklocals, stackPointer, numLocals + continuationStackPointer, returnTypeLength);
return unwindCounter;
}
}
throw WasmException.create(Failure.UNSPECIFIED_INTERNAL, this, "Should not reach here");
}
case RETURN: {
int unwindCounter = intConstants[intConstantOffset];
intConstantOffset++;
int rootBlockReturnLength = intConstants[intConstantOffset];
intConstantOffset++;
unwindStack(stacklocals, stackPointer, numLocals, rootBlockReturnLength);
return unwindCounter;
}
case CALL: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int functionIndex = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
WasmFunction function = instance().symbolTable().function(functionIndex);
byte returnType = function.returnType();
int numArgs = function.numArguments();
Object[] args = createArgumentsForCall(stacklocals, function.typeIndex(), numArgs, stackPointer);
stackPointer -= args.length;
Object result = executeDirectCall(childrenOffset, args);
childrenOffset++;
switch (returnType) {
case WasmType.I32_TYPE: {
pushInt(stacklocals, stackPointer, (int) result);
stackPointer++;
break;
}
case WasmType.I64_TYPE: {
push(stacklocals, stackPointer, (long) result);
stackPointer++;
break;
}
case WasmType.F32_TYPE: {
pushFloat(stacklocals, stackPointer, (float) result);
stackPointer++;
break;
}
case WasmType.F64_TYPE: {
pushDouble(stacklocals, stackPointer, (double) result);
stackPointer++;
break;
}
case WasmType.VOID_TYPE: {
break;
}
default: {
throw formatException("Unknown return type: %d", returnType);
}
}
break;
}
case CALL_INDIRECT: {
stackPointer--;
final SymbolTable symtab = instance().symbolTable();
final WasmTable table = instance().table();
final Object[] elements = table.elements();
final int elementIndex = popInt(stacklocals, stackPointer);
if (elementIndex < 0 || elementIndex >= elements.length) {
throw WasmException.format(Failure.UNSPECIFIED_TRAP, this, "Element index '%d' out of table bounds.", elementIndex);
}
final Object element = elements[elementIndex];
if (element == null) {
throw WasmException.format(Failure.UNSPECIFIED_TRAP, this, "Table element at index %d is uninitialized.", elementIndex);
}
final WasmFunction function;
final CallTarget target;
if (element instanceof WasmFunctionInstance) {
final WasmFunctionInstance functionInstance = (WasmFunctionInstance) element;
function = functionInstance.function();
target = functionInstance.target();
} else {
throw WasmException.format(Failure.UNSPECIFIED_TRAP, this, "Unknown table element type: %s", element);
}
long valueLength = unsignedIntConstantAndLength(data, offset);
int expectedFunctionTypeIndex = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
int expectedTypeEquivalenceClass = symtab.equivalenceClass(expectedFunctionTypeIndex);
offset += 1;
if (function != null && expectedTypeEquivalenceClass != function.typeEquivalenceClass()) {
throw WasmException.format(Failure.UNSPECIFIED_TRAP, this, "Actual (type %d of function %s) and expected (type %d in module %s) types differ in the indirect call.",
function.typeIndex(), function.name(), expectedFunctionTypeIndex, instance().name());
}
int numArgs = instance().symbolTable().functionTypeArgumentCount(expectedFunctionTypeIndex);
Object[] args = createArgumentsForCall(stacklocals, expectedFunctionTypeIndex, numArgs, stackPointer);
stackPointer -= args.length;
final Object result = executeIndirectCallNode(childrenOffset, target, args);
childrenOffset++;
byte returnType = instance().symbolTable().functionTypeReturnType(expectedFunctionTypeIndex);
switch (returnType) {
case WasmType.I32_TYPE: {
pushInt(stacklocals, stackPointer, (int) result);
stackPointer++;
break;
}
case WasmType.I64_TYPE: {
push(stacklocals, stackPointer, (long) result);
stackPointer++;
break;
}
case WasmType.F32_TYPE: {
pushFloat(stacklocals, stackPointer, (float) result);
stackPointer++;
break;
}
case WasmType.F64_TYPE: {
pushDouble(stacklocals, stackPointer, (double) result);
stackPointer++;
break;
}
case WasmType.VOID_TYPE: {
break;
}
default: {
throw formatException("Unknown return type: %d", returnType);
}
}
break;
}
case DROP: {
stackPointer--;
pop(stacklocals, stackPointer);
break;
}
case SELECT: {
stackPointer--;
int cond = popInt(stacklocals, stackPointer);
stackPointer--;
long val2 = pop(stacklocals, stackPointer);
stackPointer--;
long val1 = pop(stacklocals, stackPointer);
push(stacklocals, stackPointer, cond != 0 ? val1 : val2);
stackPointer++;
break;
}
case LOCAL_GET: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int index = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
local_get(stacklocals, stackPointer, index);
stackPointer++;
break;
}
case LOCAL_SET: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int index = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
stackPointer--;
local_set(stacklocals, stackPointer, index);
break;
}
case LOCAL_TEE: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int index = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
local_tee(stacklocals, stackPointer - 1, index);
break;
}
case GLOBAL_GET: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int index = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
global_get(context, stacklocals, stackPointer, index);
stackPointer++;
break;
}
case GLOBAL_SET: {
long valueLength = unsignedIntConstantAndLength(data, offset);
int index = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
stackPointer--;
global_set(context, stacklocals, stackPointer, index);
break;
}
case I32_LOAD: {
int memAlignOffsetDelta = offsetDelta(data, offset);
offset += memAlignOffsetDelta;
long valueLength = unsignedIntConstantAndLength(data, offset);
int memOffset = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
int baseAddress = popInt(stacklocals, stackPointer - 1);
int address = baseAddress + memOffset;
int value = memory.load_i32(this, address);
pushInt(stacklocals, stackPointer - 1, value);
break;
}
case I64_LOAD:
case F32_LOAD:
case F64_LOAD:
case I32_LOAD8_S:
case I32_LOAD8_U:
case I32_LOAD16_S:
case I32_LOAD16_U:
case I64_LOAD8_S:
case I64_LOAD8_U:
case I64_LOAD16_S:
case I64_LOAD16_U:
case I64_LOAD32_S:
case I64_LOAD32_U: {
int memAlignOffsetDelta = offsetDelta(data, offset);
offset += memAlignOffsetDelta;
long valueLength = unsignedIntConstantAndLength(data, offset);
int memOffset = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
load(memory, stacklocals, stackPointer - 1, opcode, memOffset);
break;
}
case I32_STORE:
case I64_STORE:
case F32_STORE:
case F64_STORE:
case I32_STORE_8:
case I32_STORE_16:
case I64_STORE_8:
case I64_STORE_16:
case I64_STORE_32: {
int memAlignOffsetDelta = offsetDelta(data, offset);
offset += memAlignOffsetDelta;
long valueLength = unsignedIntConstantAndLength(data, offset);
int memOffset = (int) loBits(valueLength);
int offsetDelta = hiBits(valueLength);
offset += offsetDelta;
store(memory, stacklocals, stackPointer, opcode, memOffset);
stackPointer -= 2;
break;
}
case MEMORY_SIZE: {
offset++;
int pageSize = memory.pageSize();
pushInt(stacklocals, stackPointer, pageSize);
stackPointer++;
break;
}
case MEMORY_GROW: {
offset++;
stackPointer--;
int extraSize = popInt(stacklocals, stackPointer);
int pageSize = memory.pageSize();
if (memory.grow(extraSize)) {
pushInt(stacklocals, stackPointer, pageSize);
stackPointer++;
} else {
pushInt(stacklocals, stackPointer, -1);
stackPointer++;
}
break;
}
case I32_CONST: {
long valueAndLength = signedIntConstantAndLength(data, offset);
int offsetDelta = hiBits(valueAndLength);
offset += offsetDelta;
push(stacklocals, stackPointer, loBits(valueAndLength));
stackPointer++;
break;
}
case I64_CONST: {
long value = signedLongConstant(data, offset);
int offsetDelta = offsetDelta(data, offset);
offset += offsetDelta;
push(stacklocals, stackPointer, value);
stackPointer++;
break;
}
case I32_EQZ:
i32_eqz(stacklocals, stackPointer);
break;
case I32_EQ:
i32_eq(stacklocals, stackPointer);
stackPointer--;
break;
case I32_NE:
i32_ne(stacklocals, stackPointer);
stackPointer--;
break;
case I32_LT_S:
i32_lt_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_LT_U:
i32_lt_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_GT_S:
i32_gt_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_GT_U:
i32_gt_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_LE_S:
i32_le_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_LE_U:
i32_le_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_GE_S:
i32_ge_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_GE_U:
i32_ge_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_EQZ:
i64_eqz(stacklocals, stackPointer);
break;
case I64_EQ:
i64_eq(stacklocals, stackPointer);
stackPointer--;
break;
case I64_NE:
i64_ne(stacklocals, stackPointer);
stackPointer--;
break;
case I64_LT_S:
i64_lt_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_LT_U:
i64_lt_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_GT_S:
i64_gt_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_GT_U:
i64_gt_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_LE_S:
i64_le_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_LE_U:
i64_le_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_GE_S:
i64_ge_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_GE_U:
i64_ge_u(stacklocals, stackPointer);
stackPointer--;
break;
case F32_EQ:
f32_eq(stacklocals, stackPointer);
stackPointer--;
break;
case F32_NE:
f32_ne(stacklocals, stackPointer);
stackPointer--;
break;
case F32_LT:
f32_lt(stacklocals, stackPointer);
stackPointer--;
break;
case F32_GT:
f32_gt(stacklocals, stackPointer);
stackPointer--;
break;
case F32_LE:
f32_le(stacklocals, stackPointer);
stackPointer--;
break;
case F32_GE:
f32_ge(stacklocals, stackPointer);
stackPointer--;
break;
case F64_EQ:
f64_eq(stacklocals, stackPointer);
stackPointer--;
break;
case F64_NE:
f64_ne(stacklocals, stackPointer);
stackPointer--;
break;
case F64_LT:
f64_lt(stacklocals, stackPointer);
stackPointer--;
break;
case F64_GT:
f64_gt(stacklocals, stackPointer);
stackPointer--;
break;
case F64_LE:
f64_le(stacklocals, stackPointer);
stackPointer--;
break;
case F64_GE:
f64_ge(stacklocals, stackPointer);
stackPointer--;
break;
case I32_CLZ:
i32_clz(stacklocals, stackPointer);
break;
case I32_CTZ:
i32_ctz(stacklocals, stackPointer);
break;
case I32_POPCNT:
i32_popcnt(stacklocals, stackPointer);
break;
case I32_ADD:
i32_add(stacklocals, stackPointer);
stackPointer--;
break;
case I32_SUB:
i32_sub(stacklocals, stackPointer);
stackPointer--;
break;
case I32_MUL:
i32_mul(stacklocals, stackPointer);
stackPointer--;
break;
case I32_DIV_S:
i32_div_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_DIV_U:
i32_div_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_REM_S:
i32_rem_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_REM_U:
i32_rem_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_AND:
i32_and(stacklocals, stackPointer);
stackPointer--;
break;
case I32_OR:
i32_or(stacklocals, stackPointer);
stackPointer--;
break;
case I32_XOR:
i32_xor(stacklocals, stackPointer);
stackPointer--;
break;
case I32_SHL:
i32_shl(stacklocals, stackPointer);
stackPointer--;
break;
case I32_SHR_S:
i32_shr_s(stacklocals, stackPointer);
stackPointer--;
break;
case I32_SHR_U:
i32_shr_u(stacklocals, stackPointer);
stackPointer--;
break;
case I32_ROTL:
i32_rotl(stacklocals, stackPointer);
stackPointer--;
break;
case I32_ROTR:
i32_rotr(stacklocals, stackPointer);
stackPointer--;
break;
case I64_CLZ:
i64_clz(stacklocals, stackPointer);
break;
case I64_CTZ:
i64_ctz(stacklocals, stackPointer);
break;
case I64_POPCNT:
i64_popcnt(stacklocals, stackPointer);
break;
case I64_ADD:
i64_add(stacklocals, stackPointer);
stackPointer--;
break;
case I64_SUB:
i64_sub(stacklocals, stackPointer);
stackPointer--;
break;
case I64_MUL:
i64_mul(stacklocals, stackPointer);
stackPointer--;
break;
case I64_DIV_S:
i64_div_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_DIV_U:
i64_div_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_REM_S:
i64_rem_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_REM_U:
i64_rem_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_AND:
i64_and(stacklocals, stackPointer);
stackPointer--;
break;
case I64_OR:
i64_or(stacklocals, stackPointer);
stackPointer--;
break;
case I64_XOR:
i64_xor(stacklocals, stackPointer);
stackPointer--;
break;
case I64_SHL:
i64_shl(stacklocals, stackPointer);
stackPointer--;
break;
case I64_SHR_S:
i64_shr_s(stacklocals, stackPointer);
stackPointer--;
break;
case I64_SHR_U:
i64_shr_u(stacklocals, stackPointer);
stackPointer--;
break;
case I64_ROTL:
i64_rotl(stacklocals, stackPointer);
stackPointer--;
break;
case I64_ROTR:
i64_rotr(stacklocals, stackPointer);
stackPointer--;
break;
case F32_CONST: {
int value = BinaryStreamParser.peek4(data, offset);
offset += 4;
pushInt(stacklocals, stackPointer, value);
stackPointer++;
break;
}
case F32_ABS:
f32_abs(stacklocals, stackPointer);
break;
case F32_NEG:
f32_neg(stacklocals, stackPointer);
break;
case F32_CEIL:
f32_ceil(stacklocals, stackPointer);
break;
case F32_FLOOR:
f32_floor(stacklocals, stackPointer);
break;
case F32_TRUNC:
f32_trunc(stacklocals, stackPointer);
break;
case F32_NEAREST:
f32_nearest(stacklocals, stackPointer);
break;
case F32_SQRT:
f32_sqrt(stacklocals, stackPointer);
break;
case F32_ADD:
f32_add(stacklocals, stackPointer);
stackPointer--;
break;
case F32_SUB:
f32_sub(stacklocals, stackPointer);
stackPointer--;
break;
case F32_MUL:
f32_mul(stacklocals, stackPointer);
stackPointer--;
break;
case F32_DIV:
f32_div(stacklocals, stackPointer);
stackPointer--;
break;
case F32_MIN:
f32_min(stacklocals, stackPointer);
stackPointer--;
break;
case F32_MAX:
f32_max(stacklocals, stackPointer);
stackPointer--;
break;
case F32_COPYSIGN:
f32_copysign(stacklocals, stackPointer);
stackPointer--;
break;
case F64_CONST: {
long value = BinaryStreamParser.peek8(data, offset);
offset += 8;
push(stacklocals, stackPointer, value);
stackPointer++;
break;
}
case F64_ABS:
f64_abs(stacklocals, stackPointer);
break;
case F64_NEG:
f64_neg(stacklocals, stackPointer);
break;
case F64_CEIL:
f64_ceil(stacklocals, stackPointer);
break;
case F64_FLOOR:
f64_floor(stacklocals, stackPointer);
break;
case F64_TRUNC:
f64_trunc(stacklocals, stackPointer);
break;
case F64_NEAREST:
f64_nearest(stacklocals, stackPointer);
break;
case F64_SQRT:
f64_sqrt(stacklocals, stackPointer);
break;
case F64_ADD:
f64_add(stacklocals, stackPointer);
stackPointer--;
break;
case F64_SUB:
f64_sub(stacklocals, stackPointer);
stackPointer--;
break;
case F64_MUL:
f64_mul(stacklocals, stackPointer);
stackPointer--;
break;
case F64_DIV:
f64_div(stacklocals, stackPointer);
stackPointer--;
break;
case F64_MIN:
f64_min(stacklocals, stackPointer);
stackPointer--;
break;
case F64_MAX:
f64_max(stacklocals, stackPointer);
stackPointer--;
break;
case F64_COPYSIGN:
f64_copysign(stacklocals, stackPointer);
stackPointer--;
break;
case I32_WRAP_I64:
i32_wrap_i64(stacklocals, stackPointer);
break;
case I32_TRUNC_F32_S:
i32_trunc_f32_s(stacklocals, stackPointer);
break;
case I32_TRUNC_F32_U:
i32_trunc_f32_u(stacklocals, stackPointer);
break;
case I32_TRUNC_F64_S:
i32_trunc_f64_s(stacklocals, stackPointer);
break;
case I32_TRUNC_F64_U:
i32_trunc_f64_u(stacklocals, stackPointer);
break;
case I64_EXTEND_I32_S:
i64_extend_i32_s(stacklocals, stackPointer);
break;
case I64_EXTEND_I32_U:
i64_extend_i32_u(stacklocals, stackPointer);
break;
case I64_TRUNC_F32_S:
i64_trunc_f32_s(stacklocals, stackPointer);
break;
case I64_TRUNC_F32_U:
i64_trunc_f32_u(stacklocals, stackPointer);
break;
case I64_TRUNC_F64_S:
i64_trunc_f64_s(stacklocals, stackPointer);
break;
case I64_TRUNC_F64_U:
i64_trunc_f64_u(stacklocals, stackPointer);
break;
case F32_CONVERT_I32_S:
f32_convert_i32_s(stacklocals, stackPointer);
break;
case F32_CONVERT_I32_U:
f32_convert_i32_u(stacklocals, stackPointer);
break;
case F32_CONVERT_I64_S:
f32_convert_i64_s(stacklocals, stackPointer);
break;
case F32_CONVERT_I64_U:
f32_convert_i64_u(stacklocals, stackPointer);
break;
case F32_DEMOTE_F64:
f32_demote_f64(stacklocals, stackPointer);
break;
case F64_CONVERT_I32_S:
f64_convert_i32_s(stacklocals, stackPointer);
break;
case F64_CONVERT_I32_U:
f64_convert_i32_u(stacklocals, stackPointer);
break;
case F64_CONVERT_I64_S:
f64_convert_i64_s(stacklocals, stackPointer);
break;
case F64_CONVERT_I64_U:
f64_convert_i64_u(stacklocals, stackPointer);
break;
case F64_PROMOTE_F32:
f64_promote_f32(stacklocals, stackPointer);
break;
case I32_REINTERPRET_F32:
break;
case I64_REINTERPRET_F64:
break;
case F32_REINTERPRET_I32:
break;
case F64_REINTERPRET_I64:
break;
default:
throw createUnknownOpcodeError(opcode, "Unknown opcode: 0x%02X", Failure.UNSPECIFIED_MALFORMED);
}
}
} catch (ArithmeticException e) {
throw WasmException.fromArithmeticException(this, e);
}
return -1;
}
@SuppressWarnings("unused")
private void check(int v, int limit) {
if (v >= limit) {
throw WasmException.create(Failure.UNSPECIFIED_INTERNAL, this, "array length too large");
}
}
@BytecodeInterpreterSwitchBoundary
private int executeLoopNode(int childrenOffset, VirtualFrame frame) {
final LoopNode loopNode = (LoopNode) children[childrenOffset];
int unwindCounter = (Integer) loopNode.execute(frame);
return unwindCounter;
}
@BytecodeInterpreterSwitchBoundary
private Object executeDirectCall(int childrenOffset, Object[] args) {
DirectCallNode callNode = (DirectCallNode) children[childrenOffset];
return callNode.call(args);
}
@BytecodeInterpreterSwitchBoundary
private Object executeIndirectCallNode(int childrenOffset, CallTarget target, Object[] args) {
WasmIndirectCallNode callNode = (WasmIndirectCallNode) children[childrenOffset];
return callNode.execute(target, args);
}
private static long loBits(long bits) {
return bits & 0xffff_ffffL;
}
private static int hiBits(long bits) {
return (int) ((bits >>> 32) & 0xffff_ffffL);
}
@BytecodeInterpreterSwitchBoundary
private static RuntimeException createUnknownOpcodeError(int opcode, String s, Failure failure) {
return Assert.fail(Assert.format(s, opcode), failure);
}
private void load(WasmMemory memory, long[] stack, int stackPointer, int opcode, int memOffset) {
int baseAddress = popInt(stack, stackPointer);
int address = baseAddress + memOffset;
try {
switch (opcode) {
case I32_LOAD: {
int value = memory.load_i32(this, address);
pushInt(stack, stackPointer, value);
break;
}
case I64_LOAD: {
long value = memory.load_i64(this, address);
push(stack, stackPointer, value);
break;
}
case F32_LOAD: {
float value = memory.load_f32(this, address);
pushFloat(stack, stackPointer, value);
break;
}
case F64_LOAD: {
double value = memory.load_f64(this, address);
pushDouble(stack, stackPointer, value);
break;
}
case I32_LOAD8_S: {
int value = memory.load_i32_8s(this, address);
pushInt(stack, stackPointer, value);
break;
}
case I32_LOAD8_U: {
int value = memory.load_i32_8u(this, address);
pushInt(stack, stackPointer, value);
break;
}
case I32_LOAD16_S: {
int value = memory.load_i32_16s(this, address);
pushInt(stack, stackPointer, value);
break;
}
case I32_LOAD16_U: {
int value = memory.load_i32_16u(this, address);
pushInt(stack, stackPointer, value);
break;
}
case I64_LOAD8_S: {
long value = memory.load_i64_8s(this, address);
push(stack, stackPointer, value);
break;
}
case I64_LOAD8_U: {
long value = memory.load_i64_8u(this, address);
push(stack, stackPointer, value);
break;
}
case I64_LOAD16_S: {
long value = memory.load_i64_16s(this, address);
push(stack, stackPointer, value);
break;
}
case I64_LOAD16_U: {
long value = memory.load_i64_16u(this, address);
push(stack, stackPointer, value);
break;
}
case I64_LOAD32_S: {
long value = memory.load_i64_32s(this, address);
push(stack, stackPointer, value);
break;
}
case I64_LOAD32_U: {
long value = memory.load_i64_32u(this, address);
push(stack, stackPointer, value);
break;
}
default: {
throw formatException("Unknown load opcode: %d", opcode);
}
}
} catch (WasmMemoryException e) {
throw WasmException.create(Failure.UNSPECIFIED_TRAP, this, "memory address out-of-bounds");
}
}
private void store(WasmMemory memory, long[] stack, int stackPointer, int opcode, int memOffset) {
try {
switch (opcode) {
case I32_STORE: {
int value = popInt(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i32(this, address, value);
break;
}
case I64_STORE: {
long value = pop(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i64(this, address, value);
break;
}
case F32_STORE: {
float value = popAsFloat(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_f32(this, address, value);
break;
}
case F64_STORE: {
double value = popAsDouble(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_f64(this, address, value);
break;
}
case I32_STORE_8: {
int value = popInt(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i32_8(this, address, (byte) value);
break;
}
case I32_STORE_16: {
int value = popInt(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i32_16(this, address, (short) value);
break;
}
case I64_STORE_8: {
long value = pop(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i64_8(this, address, (byte) value);
break;
}
case I64_STORE_16: {
long value = pop(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i64_16(this, address, (short) value);
break;
}
case I64_STORE_32: {
long value = pop(stack, stackPointer - 1);
int baseAddress = popInt(stack, stackPointer - 2);
int address = baseAddress + memOffset;
memory.store_i64_32(this, address, (int) value);
break;
}
default: {
throw formatException("Unknown store opcode: %d", opcode);
}
}
} catch (WasmMemoryException e) {
throw WasmException.create(Failure.UNSPECIFIED_TRAP, this, "memory address out-of-bounds");
}
}
private void global_set(WasmContext context, long[] stack, int stackPointer, int index) {
byte type = instance().symbolTable().globalValueType(index);
switch (type) {
case WasmType.I32_TYPE: {
int value = popInt(stack, stackPointer);
int address = instance().globalAddress(index);
context.globals().storeInt(address, value);
break;
}
case WasmType.I64_TYPE: {
long value = pop(stack, stackPointer);
int address = instance().globalAddress(index);
context.globals().storeLong(address, value);
break;
}
case WasmType.F32_TYPE: {
int value = popInt(stack, stackPointer);
int address = instance().globalAddress(index);
context.globals().storeFloatWithInt(address, value);
break;
}
case WasmType.F64_TYPE: {
long value = pop(stack, stackPointer);
int address = instance().globalAddress(index);
context.globals().storeDoubleWithLong(address, value);
break;
}
default: {
throw WasmException.create(Failure.UNSPECIFIED_TRAP, this, "Local variable cannot have the void type.");
}
}
}
private void global_get(WasmContext context, long[] stack, int stackPointer, int index) {
byte type = instance().symbolTable().globalValueType(index);
switch (type) {
case WasmType.I32_TYPE: {
int address = instance().globalAddress(index);
int value = context.globals().loadAsInt(address);
pushInt(stack, stackPointer, value);
break;
}
case WasmType.I64_TYPE: {
int address = instance().globalAddress(index);
long value = context.globals().loadAsLong(address);
push(stack, stackPointer, value);
break;
}
case WasmType.F32_TYPE: {
int address = instance().globalAddress(index);
int value = context.globals().loadAsInt(address);
pushInt(stack, stackPointer, value);
break;
}
case WasmType.F64_TYPE: {
int address = instance().globalAddress(index);
long value = context.globals().loadAsLong(address);
push(stack, stackPointer, value);
break;
}
default: {
throw WasmException.create(Failure.UNSPECIFIED_TRAP, this, "Local variable cannot have the void type.");
}
}
}
private void local_tee(long[] stacklocals, int stackPointer, int index) {
final long value = pop(stacklocals, stackPointer);
if (CompilerDirectives.inCompiledCode()) {
push(stacklocals, stackPointer, value);
}
stacklocals[index] = value;
}
private void local_set(long[] stacklocals, int stackPointer, int index) {
final long value = pop(stacklocals, stackPointer);
stacklocals[index] = value;
}
private void local_get(long[] stacklocals, int stackPointer, int index) {
long value = stacklocals[index];
push(stacklocals, stackPointer, value);
}
@SuppressWarnings("unused")
private void unary_op(int opcode, long[] stack, int stackPointer) {
switch (opcode) {
case I32_EQZ:
i32_eqz(stack, stackPointer);
break;
case I64_EQZ:
i64_eqz(stack, stackPointer);
break;
case I32_CLZ:
i32_clz(stack, stackPointer);
break;
case I32_CTZ:
i32_ctz(stack, stackPointer);
break;
case I32_POPCNT:
i32_popcnt(stack, stackPointer);
break;
case I64_CLZ:
i64_clz(stack, stackPointer);
break;
case I64_CTZ:
i64_ctz(stack, stackPointer);
break;
case I64_POPCNT:
i64_popcnt(stack, stackPointer);
break;
case F32_ABS:
f32_abs(stack, stackPointer);
break;
case F32_NEG:
f32_neg(stack, stackPointer);
break;
case F32_CEIL:
f32_ceil(stack, stackPointer);
break;
case F32_FLOOR:
f32_floor(stack, stackPointer);
break;
case F32_TRUNC:
f32_trunc(stack, stackPointer);
break;
case F32_NEAREST:
f32_nearest(stack, stackPointer);
break;
case F32_SQRT:
f32_sqrt(stack, stackPointer);
break;
case F64_ABS:
f64_abs(stack, stackPointer);
break;
case F64_NEG:
f64_neg(stack, stackPointer);
break;
case F64_CEIL:
f64_ceil(stack, stackPointer);
break;
case F64_FLOOR:
f64_floor(stack, stackPointer);
break;
case F64_TRUNC:
f64_trunc(stack, stackPointer);
break;
case F64_NEAREST:
f64_nearest(stack, stackPointer);
break;
case F64_SQRT:
f64_sqrt(stack, stackPointer);
break;
case I32_WRAP_I64:
i32_wrap_i64(stack, stackPointer);
break;
case I32_TRUNC_F32_S:
i32_trunc_f32_s(stack, stackPointer);
break;
case I32_TRUNC_F32_U:
i32_trunc_f32_u(stack, stackPointer);
break;
case I32_TRUNC_F64_S:
i32_trunc_f64_s(stack, stackPointer);
break;
case I32_TRUNC_F64_U:
i32_trunc_f64_u(stack, stackPointer);
break;
case I64_EXTEND_I32_S:
i64_extend_i32_s(stack, stackPointer);
break;
case I64_EXTEND_I32_U:
i64_extend_i32_u(stack, stackPointer);
break;
case I64_TRUNC_F32_S:
i64_trunc_f32_s(stack, stackPointer);
break;
case I64_TRUNC_F32_U:
i64_trunc_f32_u(stack, stackPointer);
break;
case I64_TRUNC_F64_S:
i64_trunc_f64_s(stack, stackPointer);
break;
case I64_TRUNC_F64_U:
i64_trunc_f64_u(stack, stackPointer);
break;
case F32_CONVERT_I32_S:
f32_convert_i32_s(stack, stackPointer);
break;
case F32_CONVERT_I32_U:
f32_convert_i32_u(stack, stackPointer);
break;
case F32_CONVERT_I64_S:
f32_convert_i64_s(stack, stackPointer);
break;
case F32_CONVERT_I64_U:
f32_convert_i64_u(stack, stackPointer);
break;
case F32_DEMOTE_F64:
f32_demote_f64(stack, stackPointer);
break;
case F64_CONVERT_I32_S:
f64_convert_i32_s(stack, stackPointer);
break;
case F64_CONVERT_I32_U:
f64_convert_i32_u(stack, stackPointer);
break;
case F64_CONVERT_I64_S:
f64_convert_i64_s(stack, stackPointer);
break;
case F64_CONVERT_I64_U:
f64_convert_i64_u(stack, stackPointer);
break;
case F64_PROMOTE_F32:
f64_promote_f32(stack, stackPointer);
break;
default:
createUnknownOpcodeError(opcode, "Unexpected opcode: 0x%02X", Failure.UNSPECIFIED_INTERNAL);
}
}
private void i32_eqz(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
pushInt(stack, stackPointer - 1, x == 0 ? 1 : 0);
}
private void i64_eqz(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
pushInt(stack, stackPointer - 1, x == 0 ? 1 : 0);
}
@SuppressWarnings("unused")
private void binary_op(int opcode, long[] stack, int stackPointer) {
switch (opcode) {
case I32_EQ:
i32_eq(stack, stackPointer);
break;
case I32_NE:
i32_ne(stack, stackPointer);
break;
case I32_LT_S:
i32_lt_s(stack, stackPointer);
break;
case I32_LT_U:
i32_lt_u(stack, stackPointer);
break;
case I32_GT_S:
i32_gt_s(stack, stackPointer);
break;
case I32_GT_U:
i32_gt_u(stack, stackPointer);
break;
case I32_LE_S:
i32_le_s(stack, stackPointer);
break;
case I32_LE_U:
i32_le_u(stack, stackPointer);
break;
case I32_GE_S:
i32_ge_s(stack, stackPointer);
break;
case I32_GE_U:
i32_ge_u(stack, stackPointer);
break;
case I64_EQ:
i64_eq(stack, stackPointer);
break;
case I64_NE:
i64_ne(stack, stackPointer);
break;
case I64_LT_S:
i64_lt_s(stack, stackPointer);
break;
case I64_LT_U:
i64_lt_u(stack, stackPointer);
break;
case I64_GT_S:
i64_gt_s(stack, stackPointer);
break;
case I64_GT_U:
i64_gt_u(stack, stackPointer);
break;
case I64_LE_S:
i64_le_s(stack, stackPointer);
break;
case I64_LE_U:
i64_le_u(stack, stackPointer);
break;
case I64_GE_S:
i64_ge_s(stack, stackPointer);
break;
case I64_GE_U:
i64_ge_u(stack, stackPointer);
break;
case F32_EQ:
f32_eq(stack, stackPointer);
break;
case F32_NE:
f32_ne(stack, stackPointer);
break;
case F32_LT:
f32_lt(stack, stackPointer);
break;
case F32_GT:
f32_gt(stack, stackPointer);
break;
case F32_LE:
f32_le(stack, stackPointer);
break;
case F32_GE:
f32_ge(stack, stackPointer);
break;
case F64_EQ:
f64_eq(stack, stackPointer);
break;
case F64_NE:
f64_ne(stack, stackPointer);
break;
case F64_LT:
f64_lt(stack, stackPointer);
break;
case F64_GT:
f64_gt(stack, stackPointer);
break;
case F64_LE:
f64_le(stack, stackPointer);
break;
case F64_GE:
f64_ge(stack, stackPointer);
break;
case I32_ADD:
i32_add(stack, stackPointer);
break;
case I32_SUB:
i32_sub(stack, stackPointer);
break;
case I32_MUL:
i32_mul(stack, stackPointer);
break;
case I32_DIV_S:
i32_div_s(stack, stackPointer);
break;
case I32_DIV_U:
i32_div_u(stack, stackPointer);
break;
case I32_REM_S:
i32_rem_s(stack, stackPointer);
break;
case I32_REM_U:
i32_rem_u(stack, stackPointer);
break;
case I32_AND:
i32_and(stack, stackPointer);
break;
case I32_OR:
i32_or(stack, stackPointer);
break;
case I32_XOR:
i32_xor(stack, stackPointer);
break;
case I32_SHL:
i32_shl(stack, stackPointer);
break;
case I32_SHR_S:
i32_shr_s(stack, stackPointer);
break;
case I32_SHR_U:
i32_shr_u(stack, stackPointer);
break;
case I32_ROTL:
i32_rotl(stack, stackPointer);
break;
case I32_ROTR:
i32_rotr(stack, stackPointer);
break;
case I64_ADD:
i64_add(stack, stackPointer);
break;
case I64_SUB:
i64_sub(stack, stackPointer);
break;
case I64_MUL:
i64_mul(stack, stackPointer);
break;
case I64_DIV_S:
i64_div_s(stack, stackPointer);
break;
case I64_DIV_U:
i64_div_u(stack, stackPointer);
break;
case I64_REM_S:
i64_rem_s(stack, stackPointer);
break;
case I64_REM_U:
i64_rem_u(stack, stackPointer);
break;
case I64_AND:
i64_and(stack, stackPointer);
break;
case I64_OR:
i64_or(stack, stackPointer);
break;
case I64_XOR:
i64_xor(stack, stackPointer);
break;
case I64_SHL:
i64_shl(stack, stackPointer);
break;
case I64_SHR_S:
i64_shr_s(stack, stackPointer);
break;
case I64_SHR_U:
i64_shr_u(stack, stackPointer);
break;
case I64_ROTL:
i64_rotl(stack, stackPointer);
break;
case I64_ROTR:
i64_rotr(stack, stackPointer);
break;
case F32_ADD:
f32_add(stack, stackPointer);
break;
case F32_SUB:
f32_sub(stack, stackPointer);
break;
case F32_MUL:
f32_mul(stack, stackPointer);
break;
case F32_DIV:
f32_div(stack, stackPointer);
break;
case F32_MIN:
f32_min(stack, stackPointer);
break;
case F32_MAX:
f32_max(stack, stackPointer);
break;
case F32_COPYSIGN:
f32_copysign(stack, stackPointer);
break;
case F64_ADD:
f64_add(stack, stackPointer);
break;
case F64_SUB:
f64_sub(stack, stackPointer);
break;
case F64_MUL:
f64_mul(stack, stackPointer);
break;
case F64_DIV:
f64_div(stack, stackPointer);
break;
case F64_MIN:
f64_min(stack, stackPointer);
break;
case F64_MAX:
f64_max(stack, stackPointer);
break;
case F64_COPYSIGN:
f64_copysign(stack, stackPointer);
break;
default:
createUnknownOpcodeError(opcode, "Unexpected opcode: 0x%02X", Failure.UNSPECIFIED_INTERNAL);
}
}
private void i32_eq(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y == x ? 1 : 0);
}
private void i32_ne(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y != x ? 1 : 0);
}
private void i32_lt_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y < x ? 1 : 0);
}
private void i32_lt_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Integer.compareUnsigned(y, x) < 0 ? 1 : 0);
}
private void i32_gt_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y > x ? 1 : 0);
}
private void i32_gt_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Integer.compareUnsigned(y, x) > 0 ? 1 : 0);
}
private void i32_le_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y <= x ? 1 : 0);
}
private void i32_le_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Integer.compareUnsigned(y, x) <= 0 ? 1 : 0);
}
private void i32_ge_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y >= x ? 1 : 0);
}
private void i32_ge_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Integer.compareUnsigned(y, x) >= 0 ? 1 : 0);
}
private void i64_eq(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y == x ? 1 : 0);
}
private void i64_ne(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y != x ? 1 : 0);
}
private void i64_lt_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y < x ? 1 : 0);
}
private void i64_lt_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Long.compareUnsigned(y, x) < 0 ? 1 : 0);
}
private void i64_gt_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y > x ? 1 : 0);
}
private void i64_gt_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Long.compareUnsigned(y, x) > 0 ? 1 : 0);
}
private void i64_le_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y <= x ? 1 : 0);
}
private void i64_le_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Long.compareUnsigned(y, x) <= 0 ? 1 : 0);
}
private void i64_ge_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y >= x ? 1 : 0);
}
private void i64_ge_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, Long.compareUnsigned(y, x) >= 0 ? 1 : 0);
}
private void f32_eq(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y == x ? 1 : 0);
}
private void f32_ne(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y != x ? 1 : 0);
}
private void f32_lt(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y < x ? 1 : 0);
}
private void f32_gt(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y > x ? 1 : 0);
}
private void f32_le(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y <= x ? 1 : 0);
}
private void f32_ge(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y >= x ? 1 : 0);
}
private void f64_eq(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y == x ? 1 : 0);
}
private void f64_ne(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y != x ? 1 : 0);
}
private void f64_lt(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y < x ? 1 : 0);
}
private void f64_gt(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y > x ? 1 : 0);
}
private void f64_le(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y <= x ? 1 : 0);
}
private void f64_ge(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
pushInt(stack, stackPointer - 2, y >= x ? 1 : 0);
}
private void i32_clz(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int result = Integer.numberOfLeadingZeros(x);
pushInt(stack, stackPointer - 1, result);
}
private void i32_ctz(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int result = Integer.numberOfTrailingZeros(x);
pushInt(stack, stackPointer - 1, result);
}
private void i32_popcnt(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int result = Integer.bitCount(x);
pushInt(stack, stackPointer - 1, result);
}
private void i32_add(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y + x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_sub(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y - x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_mul(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y * x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_div_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
if (x == -1 && y == Integer.MIN_VALUE) {
throw WasmException.create(Failure.INT_OVERFLOW, this);
}
int result = y / x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_div_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = Integer.divideUnsigned(y, x);
pushInt(stack, stackPointer - 2, result);
}
private void i32_rem_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y % x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_rem_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = Integer.remainderUnsigned(y, x);
pushInt(stack, stackPointer - 2, result);
}
private void i32_and(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y & x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_or(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y | x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_xor(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y ^ x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_shl(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y << x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_shr_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y >> x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_shr_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = y >>> x;
pushInt(stack, stackPointer - 2, result);
}
private void i32_rotl(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = Integer.rotateLeft(y, x);
pushInt(stack, stackPointer - 2, result);
}
private void i32_rotr(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
int y = popInt(stack, stackPointer - 2);
int result = Integer.rotateRight(y, x);
pushInt(stack, stackPointer - 2, result);
}
private void i64_clz(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long result = Long.numberOfLeadingZeros(x);
push(stack, stackPointer - 1, result);
}
private void i64_ctz(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long result = Long.numberOfTrailingZeros(x);
push(stack, stackPointer - 1, result);
}
private void i64_popcnt(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long result = Long.bitCount(x);
push(stack, stackPointer - 1, result);
}
private void i64_add(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y + x;
push(stack, stackPointer - 2, result);
}
private void i64_sub(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y - x;
push(stack, stackPointer - 2, result);
}
private void i64_mul(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y * x;
push(stack, stackPointer - 2, result);
}
private void i64_div_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
if (x == -1 && y == Long.MIN_VALUE) {
throw WasmException.create(Failure.INT_OVERFLOW, this);
}
final long result = y / x;
push(stack, stackPointer - 2, result);
}
private void i64_div_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = Long.divideUnsigned(y, x);
push(stack, stackPointer - 2, result);
}
private void i64_rem_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y % x;
push(stack, stackPointer - 2, result);
}
private void i64_rem_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = Long.remainderUnsigned(y, x);
push(stack, stackPointer - 2, result);
}
private void i64_and(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y & x;
push(stack, stackPointer - 2, result);
}
private void i64_or(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y | x;
push(stack, stackPointer - 2, result);
}
private void i64_xor(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y ^ x;
push(stack, stackPointer - 2, result);
}
private void i64_shl(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y << x;
push(stack, stackPointer - 2, result);
}
private void i64_shr_s(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y >> x;
push(stack, stackPointer - 2, result);
}
private void i64_shr_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = y >>> x;
push(stack, stackPointer - 2, result);
}
private void i64_rotl(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = Long.rotateLeft(y, (int) x);
push(stack, stackPointer - 2, result);
}
private void i64_rotr(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
long y = pop(stack, stackPointer - 2);
long result = Long.rotateRight(y, (int) x);
push(stack, stackPointer - 2, result);
}
private void f32_abs(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = Math.abs(x);
pushFloat(stack, stackPointer - 1, result);
}
private void f32_neg(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = -x;
pushFloat(stack, stackPointer - 1, result);
}
private void f32_ceil(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = (float) Math.ceil(x);
pushFloat(stack, stackPointer - 1, result);
}
private void f32_floor(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = (float) Math.floor(x);
pushFloat(stack, stackPointer - 1, result);
}
private void f32_trunc(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = (float) (x < 0.0 ? Math.ceil(x) : Math.floor(x));
pushFloat(stack, stackPointer - 1, result);
}
private void f32_nearest(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = (float) Math.rint(x);
pushFloat(stack, stackPointer - 1, result);
}
private void f32_sqrt(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float result = (float) Math.sqrt(x);
pushFloat(stack, stackPointer - 1, result);
}
private void f32_add(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = y + x;
pushFloat(stack, stackPointer - 2, result);
}
private void f32_sub(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = y - x;
pushFloat(stack, stackPointer - 2, result);
}
private void f32_mul(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = y * x;
pushFloat(stack, stackPointer - 2, result);
}
private void f32_div(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = y / x;
pushFloat(stack, stackPointer - 2, result);
}
private void f32_min(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = Math.min(y, x);
pushFloat(stack, stackPointer - 2, result);
}
private void f32_max(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = Math.max(y, x);
pushFloat(stack, stackPointer - 2, result);
}
private void f32_copysign(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
float y = popAsFloat(stack, stackPointer - 2);
float result = Math.copySign(y, x);
pushFloat(stack, stackPointer - 2, result);
}
private void f64_abs(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = Math.abs(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_neg(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = -x;
pushDouble(stack, stackPointer - 1, result);
}
private void f64_ceil(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = Math.ceil(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_floor(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = Math.floor(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_trunc(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = x < 0.0 ? Math.ceil(x) : Math.floor(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_nearest(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = Math.rint(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_sqrt(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double result = Math.sqrt(x);
pushDouble(stack, stackPointer - 1, result);
}
private void f64_add(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = y + x;
pushDouble(stack, stackPointer - 2, result);
}
private void f64_sub(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = y - x;
pushDouble(stack, stackPointer - 2, result);
}
private void f64_mul(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = y * x;
pushDouble(stack, stackPointer - 2, result);
}
private void f64_div(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = y / x;
pushDouble(stack, stackPointer - 2, result);
}
private void f64_min(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = Math.min(y, x);
pushDouble(stack, stackPointer - 2, result);
}
private void f64_max(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = Math.max(y, x);
pushDouble(stack, stackPointer - 2, result);
}
private void f64_copysign(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
double y = popAsDouble(stack, stackPointer - 2);
double result = Math.copySign(y, x);
pushDouble(stack, stackPointer - 2, result);
}
private void i32_wrap_i64(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
int result = (int) (x & 0xFFFF_FFFFL);
pushInt(stack, stackPointer - 1, result);
}
private void i32_trunc_f32_u(long[] stack, int stackPointer) {
i32_trunc_f32_s(stack, stackPointer);
}
private void i32_trunc_f32_s(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
int result = (int) x;
pushInt(stack, stackPointer - 1, result);
}
private void i32_trunc_f64_s(long[] stack, int stackPointer) {
i32_trunc_f64_u(stack, stackPointer);
}
private void i32_trunc_f64_u(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
int result = (int) x;
pushInt(stack, stackPointer - 1, result);
}
private void i64_extend_i32_s(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
long result = x;
push(stack, stackPointer - 1, result);
}
private void i64_extend_i32_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
long result = x & 0xFFFF_FFFFL;
push(stack, stackPointer - 1, result);
}
private void i64_trunc_f32_s(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
long result = (long) x;
push(stack, stackPointer - 1, result);
}
private void i64_trunc_f32_u(long[] stack, int stackPointer) {
i64_trunc_f32_s(stack, stackPointer);
}
private void i64_trunc_f64_s(long[] stack, int stackPointer) {
i64_trunc_f64_u(stack, stackPointer);
}
private void i64_trunc_f64_u(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
long result = (long) x;
push(stack, stackPointer - 1, result);
}
private void f32_convert_i32_s(long[] stack, int stackPointer) {
f32_convert_i32_u(stack, stackPointer);
}
private void f32_convert_i32_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
float result = x;
pushFloat(stack, stackPointer - 1, result);
}
private void f32_convert_i64_s(long[] stack, int stackPointer) {
f32_convert_i64_u(stack, stackPointer);
}
private void f32_convert_i64_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
float result = x;
pushFloat(stack, stackPointer - 1, result);
}
private void f32_demote_f64(long[] stack, int stackPointer) {
double x = popAsDouble(stack, stackPointer - 1);
float result = (float) x;
pushFloat(stack, stackPointer - 1, result);
}
private void f64_convert_i32_s(long[] stack, int stackPointer) {
f64_convert_i32_u(stack, stackPointer);
}
private void f64_convert_i32_u(long[] stack, int stackPointer) {
int x = popInt(stack, stackPointer - 1);
double result = x;
pushDouble(stack, stackPointer - 1, result);
}
private void f64_convert_i64_s(long[] stack, int stackPointer) {
f64_convert_i64_u(stack, stackPointer);
}
private void f64_convert_i64_u(long[] stack, int stackPointer) {
long x = pop(stack, stackPointer - 1);
double result = x;
pushDouble(stack, stackPointer - 1, result);
}
private void f64_promote_f32(long[] stack, int stackPointer) {
float x = popAsFloat(stack, stackPointer - 1);
double result = x;
pushDouble(stack, stackPointer - 1, result);
}
private boolean popBoolean(long[] stack, int stackPointer) {
int condition = popInt(stack, stackPointer);
return condition != 0;
}
@TruffleBoundary
public void resolveCallNode(int childOffset) {
final WasmFunction function = ((WasmCallStubNode) children[childOffset]).function();
final CallTarget target = instance().target(function.index());
children[childOffset] = Truffle.getRuntime().createDirectCallNode(target);
}
@ExplodeLoop
private Object[] createArgumentsForCall(long[] stack, int functionTypeIndex, int numArgs, int stackPointerOffset) {
CompilerAsserts.partialEvaluationConstant(numArgs);
Object[] args = new Object[numArgs];
int stackPointer = stackPointerOffset;
for (int i = numArgs - 1; i >= 0; --i) {
stackPointer--;
byte type = instance().symbolTable().functionTypeArgumentTypeAt(functionTypeIndex, i);
switch (type) {
case WasmType.I32_TYPE:
args[i] = popInt(stack, stackPointer);
break;
case WasmType.I64_TYPE:
args[i] = pop(stack, stackPointer);
break;
case WasmType.F32_TYPE:
args[i] = popAsFloat(stack, stackPointer);
break;
case WasmType.F64_TYPE:
args[i] = popAsDouble(stack, stackPointer);
break;
default: {
throw formatException("Unknown type: %d", type);
}
}
}
return args;
}
@BytecodeInterpreterSwitchBoundary
private WasmException formatException(String formatString, int type) {
return WasmException.format(Failure.UNSPECIFIED_TRAP, this, formatString, type);
}
@ExplodeLoop
private void unwindStack(long[] stack, int stackPointer, int continuationStackPointer, int returnLength) {
CompilerAsserts.partialEvaluationConstant(stackPointer);
CompilerAsserts.partialEvaluationConstant(returnLength);
for (int i = 0; i < returnLength; ++i) {
long value = pop(stack, stackPointer + i - 1);
push(stack, continuationStackPointer + i, value);
}
for (int i = continuationStackPointer + returnLength; i < stackPointer; ++i) {
pop(stack, i);
}
}
@Override
public byte returnTypeId() {
return returnTypeId;
}
private static long unsignedIntConstantAndLength(byte[] data, int offset) {
return BinaryStreamParser.rawPeekUnsignedInt32AndLength(data, offset);
}
private static long signedIntConstantAndLength(byte[] data, int offset) {
return BinaryStreamParser.rawPeekSignedInt32AndLength(data, offset);
}
private static long signedLongConstant(byte[] data, int offset) {
return BinaryStreamParser.peekSignedInt64(data, offset);
}
private static int offsetDelta(byte[] data, int offset) {
return BinaryStreamParser.peekLeb128Length(data, offset);
}
}