summaryrefslogtreecommitdiff
path: root/src/wasm-interpreter.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm-interpreter.h')
-rw-r--r--src/wasm-interpreter.h136
1 files changed, 83 insertions, 53 deletions
diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h
index 2a841f6fd..e7a5d6d14 100644
--- a/src/wasm-interpreter.h
+++ b/src/wasm-interpreter.h
@@ -29,6 +29,10 @@
#include "support/bits.h"
#include "wasm.h"
+#ifdef WASM_INTERPRETER_DEBUG
+#include "wasm-printing.h"
+#endif
+
namespace wasm {
using namespace cashew;
@@ -39,7 +43,6 @@ IString WASM("wasm"),
RETURN_FLOW("*return:)*");
enum {
- pageSize = 64*1024,
maxCallDepth = 250
};
@@ -112,6 +115,15 @@ public:
return callFunction(export_->value, arguments);
}
+ std::string printFunctionStack() {
+ std::string ret = "/== (binaryen interpreter stack trace)\n";
+ for (int i = int(functionStack.size()) - 1; i >= 0; i--) {
+ ret += std::string("|: ") + functionStack[i].str + "\n";
+ }
+ ret += std::string("\\==\n");
+ return ret;
+ }
+
private:
size_t callDepth = 0;
@@ -120,6 +132,10 @@ private:
int indent = 0;
#endif
+ // Function stack. We maintain this explicitly to allow printing of
+ // stack traces.
+ std::vector<Name> functionStack;
+
//
// Calls a function. This can be used both internally (calls from
// the interpreter to another method), or when you want to call into
@@ -166,7 +182,7 @@ private:
indent++;
#if WASM_INTERPRETER_DEBUG == 2
doIndent(std::cout, indent);
- expression->print(std::cout, indent) << '\n';
+ std::cout << "\n" << expression << '\n';
indent++;
#endif
}
@@ -200,12 +216,33 @@ private:
Flow visitBlock(Block *curr) {
NOTE_ENTER("Block");
+ // special-case Block, because Block nesting (in their first element) can be incredibly deep
+ std::vector<Block*> stack;
+ stack.push_back(curr);
+ while (curr->list.size() > 0 && curr->list[0]->is<Block>()) {
+ curr = curr->list[0]->cast<Block>();
+ stack.push_back(curr);
+ }
Flow flow;
- for (auto expression : curr->list) {
- flow = visit(expression);
+ auto* top = stack.back();
+ while (stack.size() > 0) {
+ curr = stack.back();
+ stack.pop_back();
if (flow.breaking()) {
flow.clearIf(curr->name);
- return flow;
+ continue;
+ }
+ auto& list = curr->list;
+ for (size_t i = 0; i < list.size(); i++) {
+ if (curr != top && i == 0) {
+ // one of the block recursions we already handled
+ continue;
+ }
+ flow = visit(list[i]);
+ if (flow.breaking()) {
+ flow.clearIf(curr->name);
+ break;
+ }
}
}
return flow;
@@ -246,46 +283,30 @@ private:
if (curr->condition) {
Flow conditionFlow = visit(curr->condition);
if (conditionFlow.breaking()) return conditionFlow;
- condition = conditionFlow.value.getInteger();
+ condition = conditionFlow.value.getInteger() != 0;
}
return condition ? flow : Flow();
}
Flow visitSwitch(Switch *curr) {
NOTE_ENTER("Switch");
- Flow flow = visit(curr->value);
- if (flow.breaking()) {
- flow.clearIf(curr->name);
- return flow;
- }
+ Flow flow = visit(curr->condition);
+ if (flow.breaking()) return flow;
NOTE_EVAL1(flow.value);
int64_t index = flow.value.getInteger();
+
+ if (curr->value) {
+ flow = visit(curr->value);
+ if (flow.breaking()) return flow;
+ NOTE_EVAL1(flow.value);
+ } else {
+ flow = Flow();
+ }
+
Name target = curr->default_;
if (index >= 0 && (size_t)index < curr->targets.size()) {
- target = curr->targets[index];
- }
- // This is obviously very inefficient. This should be a cached data structure
- std::map<Name, size_t> caseMap; // name => index in cases
- for (size_t i = 0; i < curr->cases.size(); i++) {
- caseMap[curr->cases[i].name] = i;
- }
- auto iter = caseMap.find(target);
- if (iter == caseMap.end()) {
- // not in the cases, so this is a break
- Flow flow(target);
- flow.clearIf(curr->name);
- return flow;
- }
- size_t caseIndex = iter->second;
- assert(caseIndex < curr->cases.size());
- while (caseIndex < curr->cases.size()) {
- Switch::Case& c = curr->cases[caseIndex];
- flow = visit(c.body);
- if (flow.breaking()) {
- flow.clearIf(curr->name);
- break;
- }
- caseIndex++;
+ target = curr->targets[(size_t)index];
}
+ flow.breakTo = target;
return flow;
}
@@ -382,6 +403,7 @@ private:
case Clz: return value.countLeadingZeroes();
case Ctz: return value.countTrailingZeroes();
case Popcnt: return value.popCount();
+ case EqZ: return Literal(int32_t(value == Literal(int32_t(0))));
case ReinterpretInt: return value.castToF32();
case ExtendSInt32: return value.extendToSI64();
case ExtendUInt32: return value.extendToUI64();
@@ -395,6 +417,7 @@ private:
case Clz: return value.countLeadingZeroes();
case Ctz: return value.countTrailingZeroes();
case Popcnt: return value.popCount();
+ case EqZ: return Literal(int32_t(value == Literal(int64_t(0))));
case WrapInt64: return value.truncateToI32();
case ReinterpretInt: return value.castToF64();
case ConvertUInt64: return curr->type == f32 ? value.convertUToF32() : value.convertUToF64();
@@ -445,8 +468,8 @@ private:
if (flow.breaking()) return flow;
Literal right = flow.value;
NOTE_EVAL2(left, right);
- assert(left.type == curr->left->type);
- assert(right.type == curr->right->type);
+ assert(isConcreteWasmType(curr->left->type) ? left.type == curr->left->type : true);
+ assert(isConcreteWasmType(curr->right->type) ? right.type == curr->right->type : true);
if (left.type == i32) {
switch (curr->op) {
case Add: return left.add(right);
@@ -476,6 +499,8 @@ private:
case Shl: return left.shl(right.and_(Literal(int32_t(31))));
case ShrU: return left.shrU(right.and_(Literal(int32_t(31))));
case ShrS: return left.shrS(right.and_(Literal(int32_t(31))));
+ case RotL: return left.rotL(right);
+ case RotR: return left.rotR(right);
case Eq: return left.eq(right);
case Ne: return left.ne(right);
case LtS: return left.ltS(right);
@@ -517,6 +542,8 @@ private:
case Shl: return left.shl(right.and_(Literal(int64_t(63))));
case ShrU: return left.shrU(right.and_(Literal(int64_t(63))));
case ShrS: return left.shrS(right.and_(Literal(int64_t(63))));
+ case RotL: return left.rotL(right);
+ case RotR: return left.rotR(right);
case Eq: return left.eq(right);
case Ne: return left.ne(right);
case LtS: return left.ltS(right);
@@ -574,21 +601,20 @@ private:
Flow visitHost(Host *curr) {
NOTE_ENTER("Host");
switch (curr->op) {
- case PageSize: return Literal((int32_t)pageSize);
- case MemorySize: return Literal((int32_t)instance.memorySize);
+ case PageSize: return Literal((int32_t)Memory::kPageSize);
+ case MemorySize: return Literal(int32_t(instance.memorySize * Memory::kPageSize));
case GrowMemory: {
Flow flow = visit(curr->operands[0]);
if (flow.breaking()) return flow;
int32_t ret = instance.memorySize;
uint32_t delta = flow.value.geti32();
- if (delta % pageSize != 0) trap("growMemory: delta not multiple");
- if (delta > uint32_t(-1) - pageSize) trap("growMemory: delta relatively too big");
+ if (delta > uint32_t(-1) /Memory::kPageSize) trap("growMemory: delta relatively too big");
if (instance.memorySize >= uint32_t(-1) - delta) trap("growMemory: delta objectively too big");
uint32_t newSize = instance.memorySize + delta;
if (newSize > instance.wasm.memory.max) trap("growMemory: exceeds max");
- instance.externalInterface->growMemory(instance.memorySize, newSize);
+ instance.externalInterface->growMemory(instance.memorySize * Memory::kPageSize, newSize * Memory::kPageSize);
instance.memorySize = newSize;
- return Literal(ret);
+ return Literal(int32_t(ret * Memory::kPageSize));
}
case HasFeature: {
IString id = curr->nameOperand;
@@ -615,8 +641,8 @@ private:
if (val > (double)std::numeric_limits<int32_t>::max() || val < (double)std::numeric_limits<int32_t>::min()) trap("i32.truncSFloat overflow");
return Literal(int32_t(val));
} else {
- int64_t converted = val;
- if ((val >= 1 && converted <= 0) || val < (double)LLONG_MIN) trap("i32.truncSFloat overflow");
+ int64_t converted = (int64_t)val;
+ if ((val >= 1 && converted <= 0) || val < (double)LLONG_MIN) trap("i64.truncSFloat overflow");
return Literal(converted);
}
}
@@ -625,10 +651,10 @@ private:
double val = value.getFloat();
if (isnan(val)) trap("truncUFloat of nan");
if (curr->type == i32) {
- if (val > (double)std::numeric_limits<uint32_t>::max() || val <= (double)-1) trap("i64.truncUFloat overflow");
+ if (val > (double)std::numeric_limits<uint32_t>::max() || val <= (double)-1) trap("i32.truncUFloat overflow");
return Literal(uint32_t(val));
} else {
- uint64_t converted = val;
+ uint64_t converted = (uint64_t)val;
if (converted < val - 1 || val <= (double)-1) trap("i64.truncUFloat overflow");
return Literal(converted);
}
@@ -641,6 +667,7 @@ private:
if (callDepth > maxCallDepth) externalInterface->trap("stack limit");
callDepth++;
+ functionStack.push_back(name);
Function *function = wasm.functionsMap[name];
assert(function);
@@ -656,29 +683,32 @@ private:
if (function->result == none) ret = Literal();
assert(function->result == ret.type);
callDepth--;
+ assert(functionStack.back() == name);
+ functionStack.pop_back();
#ifdef WASM_INTERPRETER_DEBUG
std::cout << "exiting " << function->name << " with " << ret << '\n';
#endif
return ret;
}
- size_t memorySize;
+ size_t memorySize; // in pages
template <class LS>
size_t getFinalAddress(LS* curr, Literal ptr) {
- auto trapIfGt = [this](size_t lhs, size_t rhs, const char* msg) {
+ auto trapIfGt = [this](uint64_t lhs, uint64_t rhs, const char* msg) {
if (lhs > rhs) {
std::stringstream ss;
ss << msg << ": " << lhs << " > " << rhs;
externalInterface->trap(ss.str().c_str());
}
};
+ uint32_t memorySizeBytes = memorySize * Memory::kPageSize;
uint64_t addr = ptr.type == i32 ? ptr.geti32() : ptr.geti64();
- trapIfGt(curr->offset, memorySize, "offset > memory");
- trapIfGt(addr, memorySize - curr->offset, "final > memory");
+ trapIfGt(curr->offset, memorySizeBytes, "offset > memory");
+ trapIfGt(addr, memorySizeBytes - curr->offset, "final > memory");
addr += curr->offset;
- trapIfGt(curr->bytes, memorySize, "bytes > memory");
- trapIfGt(addr, memorySize - curr->bytes, "highest > memory");
+ trapIfGt(curr->bytes, memorySizeBytes, "bytes > memory");
+ trapIfGt(addr, memorySizeBytes - curr->bytes, "highest > memory");
return addr;
}