Index: src/wasm/ast-decoder.cc |
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc |
index cfee4f3cf14171df7815cc7a9465bf66c8d53a25..6d847b8e3ff4fb266fae060aeadb44807c3518ea 100644 |
--- a/src/wasm/ast-decoder.cc |
+++ b/src/wasm/ast-decoder.cc |
@@ -41,7 +41,6 @@ struct Tree { |
WasmOpcode opcode() const { return static_cast<WasmOpcode>(*pc); } |
}; |
- |
// A production represents an incomplete decoded tree in the LR decoder. |
struct Production { |
Tree* tree; // the root of the syntax tree. |
@@ -103,8 +102,8 @@ struct IfEnv { |
class WasmDecoder : public Decoder { |
public: |
WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {} |
- |
- protected: |
+ WasmDecoder(FunctionEnv* env, const byte* start, const byte* end) |
+ : Decoder(start, end), function_env_(env) {} |
FunctionEnv* function_env_; |
void Reset(FunctionEnv* function_env, const byte* start, const byte* end) { |
@@ -136,62 +135,209 @@ class WasmDecoder : public Decoder { |
return read_u64(pc + 1); |
} |
- LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) { |
- *index = UnsignedLEB128Operand(pc, length); |
- if (function_env_->IsValidLocal(*index)) { |
- return function_env_->GetLocalType(*index); |
+ inline bool Validate(const byte* pc, LocalIndexOperand& operand) { |
+ if (operand.index < function_env_->total_locals) { |
+ operand.type = function_env_->GetLocalType(operand.index); |
+ return true; |
} |
- error(pc, "invalid local variable index"); |
- return kAstStmt; |
+ error(pc, pc + 1, "invalid local index"); |
+ return false; |
} |
- LocalType GlobalOperand(const byte* pc, uint32_t* index, int* length) { |
- *index = UnsignedLEB128Operand(pc, length); |
- if (function_env_->module->IsValidGlobal(*index)) { |
- return WasmOpcodes::LocalTypeFor( |
- function_env_->module->GetGlobalType(*index)); |
+ inline bool Validate(const byte* pc, GlobalIndexOperand& operand) { |
+ ModuleEnv* m = function_env_->module; |
+ if (m && m->module && operand.index < m->module->globals->size()) { |
+ operand.machine_type = m->module->globals->at(operand.index).type; |
+ operand.type = WasmOpcodes::LocalTypeFor(operand.machine_type); |
+ return true; |
} |
- error(pc, "invalid global variable index"); |
- return kAstStmt; |
+ error(pc, pc + 1, "invalid global index"); |
+ return false; |
} |
- FunctionSig* FunctionSigOperand(const byte* pc, uint32_t* index, |
- int* length) { |
- *index = UnsignedLEB128Operand(pc, length); |
- if (function_env_->module->IsValidFunction(*index)) { |
- return function_env_->module->GetFunctionSignature(*index); |
+ inline bool Validate(const byte* pc, FunctionIndexOperand& operand) { |
+ ModuleEnv* m = function_env_->module; |
+ if (m && m->module && operand.index < m->module->functions->size()) { |
+ operand.sig = m->module->functions->at(operand.index).sig; |
+ return true; |
} |
- error(pc, "invalid function index"); |
- return nullptr; |
+ error(pc, pc + 1, "invalid function index"); |
+ return false; |
} |
- FunctionSig* SigOperand(const byte* pc, uint32_t* index, int* length) { |
- *index = UnsignedLEB128Operand(pc, length); |
- if (function_env_->module->IsValidSignature(*index)) { |
- return function_env_->module->GetSignature(*index); |
+ inline bool Validate(const byte* pc, SignatureIndexOperand& operand) { |
+ ModuleEnv* m = function_env_->module; |
+ if (m && m->module && operand.index < m->module->signatures->size()) { |
+ operand.sig = m->module->signatures->at(operand.index); |
+ return true; |
} |
- error(pc, "invalid signature index"); |
- return nullptr; |
+ error(pc, pc + 1, "invalid signature index"); |
+ return false; |
} |
- uint32_t UnsignedLEB128Operand(const byte* pc, int* length) { |
- uint32_t result = 0; |
- ReadUnsignedLEB128ErrorCode error_code = |
- ReadUnsignedLEB128Operand(pc + 1, limit_, length, &result); |
- if (error_code == kInvalidLEB128) error(pc, "invalid LEB128 varint"); |
- if (error_code == kMissingLEB128) error(pc, "expected LEB128 varint"); |
- (*length)++; |
- return result; |
+ inline bool Validate(const byte* pc, BreakDepthOperand& operand, |
+ ZoneVector<Block>& blocks) { |
+ if (operand.depth < blocks.size()) { |
+ operand.target = &blocks[blocks.size() - operand.depth - 1]; |
+ return true; |
+ } |
+ error(pc, pc + 1, "invalid break depth"); |
+ return false; |
} |
- void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) { |
- byte bitfield = ByteOperand(pc, "missing memory access operand"); |
- if (MemoryAccess::OffsetField::decode(bitfield)) { |
- *offset = UnsignedLEB128Operand(pc + 1, length); |
- (*length)++; // to account for the memory access byte |
- } else { |
- *offset = 0; |
- *length = 2; |
+ bool Validate(const byte* pc, TableSwitchOperand& operand, |
+ size_t block_depth) { |
+ if (operand.table_count == 0) { |
+ error(pc, "tableswitch with 0 entries"); |
+ return false; |
+ } |
+ // Verify table. |
+ for (uint32_t i = 0; i < operand.table_count; i++) { |
+ uint16_t target = operand.read_entry(this, i); |
+ if (target >= 0x8000) { |
+ size_t depth = target - 0x8000; |
+ if (depth > block_depth) { |
+ error(operand.table + i * 2, "improper branch in tableswitch"); |
+ return false; |
+ } |
+ } else { |
+ if (target >= operand.case_count) { |
+ error(operand.table + i * 2, "invalid case target in tableswitch"); |
+ return false; |
+ } |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ int OpcodeArity(const byte* pc) { |
+#define DECLARE_ARITY(name, ...) \ |
+ static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ |
+ static const int kArity_##name = \ |
+ static_cast<int>(arraysize(kTypes_##name) - 1); |
+ |
+ FOREACH_SIGNATURE(DECLARE_ARITY); |
+#undef DECLARE_ARITY |
+ |
+ switch (static_cast<WasmOpcode>(*pc)) { |
+ case kExprI8Const: |
+ case kExprI32Const: |
+ case kExprI64Const: |
+ case kExprF64Const: |
+ case kExprF32Const: |
+ case kExprGetLocal: |
+ case kExprLoadGlobal: |
+ case kExprNop: |
+ case kExprUnreachable: |
+ return 0; |
+ |
+ case kExprBr: |
+ case kExprStoreGlobal: |
+ case kExprSetLocal: |
+ return 1; |
+ |
+ case kExprIf: |
+ case kExprBrIf: |
+ return 2; |
+ case kExprIfElse: |
+ case kExprSelect: |
+ return 3; |
+ |
+ case kExprBlock: |
+ case kExprLoop: { |
+ BlockCountOperand operand(this, pc); |
+ return operand.count; |
+ } |
+ |
+ case kExprCallFunction: { |
+ FunctionIndexOperand operand(this, pc); |
+ return static_cast<int>( |
+ function_env_->module->GetFunctionSignature(operand.index) |
+ ->parameter_count()); |
+ } |
+ case kExprCallIndirect: { |
+ SignatureIndexOperand operand(this, pc); |
+ return 1 + static_cast<int>( |
+ function_env_->module->GetSignature(operand.index) |
+ ->parameter_count()); |
+ } |
+ case kExprReturn: { |
+ return static_cast<int>(function_env_->sig->return_count()); |
+ } |
+ case kExprTableSwitch: { |
+ TableSwitchOperand operand(this, pc); |
+ return 1 + operand.case_count; |
+ } |
+ |
+#define DECLARE_OPCODE_CASE(name, opcode, sig) \ |
+ case kExpr##name: \ |
+ return kArity_##sig; |
+ |
+ FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) |
+ FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) |
+ FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) |
+ FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) |
+#undef DECLARE_OPCODE_CASE |
+ } |
+ UNREACHABLE(); |
+ return 0; |
+ } |
+ |
+ int OpcodeLength(const byte* pc) { |
+ switch (static_cast<WasmOpcode>(*pc)) { |
+#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: |
+ FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) |
+ FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) |
+#undef DECLARE_OPCODE_CASE |
+ { |
+ MemoryAccessOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprBlock: |
+ case kExprLoop: { |
+ BlockCountOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprBr: |
+ case kExprBrIf: { |
+ BreakDepthOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprStoreGlobal: |
+ case kExprLoadGlobal: { |
+ GlobalIndexOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ |
+ case kExprCallFunction: { |
+ FunctionIndexOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprCallIndirect: { |
+ SignatureIndexOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ |
+ case kExprSetLocal: |
+ case kExprGetLocal: { |
+ LocalIndexOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprTableSwitch: { |
+ TableSwitchOperand operand(this, pc); |
+ return 1 + operand.length; |
+ } |
+ case kExprI8Const: |
+ return 2; |
+ case kExprI32Const: |
+ case kExprF32Const: |
+ return 5; |
+ case kExprI64Const: |
+ case kExprF64Const: |
+ return 9; |
+ |
+ default: |
+ return 1; |
} |
} |
}; |
@@ -431,25 +577,25 @@ class LR_WasmDecoder : public WasmDecoder { |
Leaf(kAstStmt); |
break; |
case kExprBlock: { |
- int length = ByteOperand(pc_); |
- if (length < 1) { |
+ BlockCountOperand operand(this, pc_); |
+ if (operand.count < 1) { |
Leaf(kAstStmt); |
} else { |
- Shift(kAstEnd, length); |
+ Shift(kAstEnd, operand.count); |
// The break environment is the outer environment. |
SsaEnv* break_env = ssa_env_; |
PushBlock(break_env); |
SetEnv("block:start", Steal(break_env)); |
} |
- len = 2; |
+ len = 1 + operand.length; |
break; |
} |
case kExprLoop: { |
- int length = ByteOperand(pc_); |
- if (length < 1) { |
+ BlockCountOperand operand(this, pc_); |
+ if (operand.count < 1) { |
Leaf(kAstStmt); |
} else { |
- Shift(kAstEnd, length); |
+ Shift(kAstEnd, operand.count); |
// The break environment is the outer environment. |
SsaEnv* break_env = ssa_env_; |
PushBlock(break_env); |
@@ -461,7 +607,7 @@ class LR_WasmDecoder : public WasmDecoder { |
PushBlock(cont_env); |
blocks_.back().stack_depth = -1; // no production for inner block. |
} |
- len = 2; |
+ len = 1 + operand.length; |
break; |
} |
case kExprIf: |
@@ -474,58 +620,27 @@ class LR_WasmDecoder : public WasmDecoder { |
Shift(kAstStmt, 3); // Result type is typeof(x) in {c ? x : y}. |
break; |
case kExprBr: { |
- uint32_t depth = ByteOperand(pc_); |
- Shift(kAstEnd, 1); |
- if (depth >= blocks_.size()) { |
- error("improperly nested branch"); |
+ BreakDepthOperand operand(this, pc_); |
+ if (Validate(pc_, operand, blocks_)) { |
+ Shift(kAstEnd, 1); |
} |
- len = 2; |
+ len = 1 + operand.length; |
break; |
} |
case kExprBrIf: { |
- uint32_t depth = ByteOperand(pc_); |
- Shift(kAstStmt, 2); |
- if (depth >= blocks_.size()) { |
- error("improperly nested conditional branch"); |
+ BreakDepthOperand operand(this, pc_); |
+ if (Validate(pc_, operand, blocks_)) { |
+ Shift(kAstStmt, 2); |
} |
- len = 2; |
+ len = 1 + operand.length; |
break; |
} |
case kExprTableSwitch: { |
- if (!checkAvailable(5)) { |
- error("expected #tableswitch <cases> <table>, fell off end"); |
- break; |
- } |
- uint16_t case_count = read_u16(pc_ + 1); |
- uint16_t table_count = read_u16(pc_ + 3); |
- len = 5 + table_count * 2; |
- |
- if (table_count == 0) { |
- error("tableswitch with 0 entries"); |
- break; |
- } |
- |
- if (!checkAvailable(len)) { |
- error("expected #tableswitch <cases> <table>, fell off end"); |
- break; |
- } |
- |
- Shift(kAstEnd, 1 + case_count); |
- |
- // Verify table. |
- for (int i = 0; i < table_count; i++) { |
- uint16_t target = read_u16(pc_ + 5 + i * 2); |
- if (target >= 0x8000) { |
- size_t depth = target - 0x8000; |
- if (depth > blocks_.size()) { |
- error(pc_ + 5 + i * 2, "improper branch in tableswitch"); |
- } |
- } else { |
- if (target >= case_count) { |
- error(pc_ + 5 + i * 2, "invalid case target in tableswitch"); |
- } |
- } |
+ TableSwitchOperand operand(this, pc_); |
+ if (Validate(pc_, operand, blocks_.size())) { |
+ Shift(kAstEnd, 1 + operand.case_count); |
} |
+ len = 1 + operand.length; |
break; |
} |
case kExprReturn: { |
@@ -546,59 +661,66 @@ class LR_WasmDecoder : public WasmDecoder { |
break; |
} |
case kExprI8Const: { |
- int32_t value = bit_cast<int8_t>(ByteOperand(pc_)); |
- Leaf(kAstI32, BUILD(Int32Constant, value)); |
- len = 2; |
+ ImmI8Operand operand(this, pc_); |
+ Leaf(kAstI32, BUILD(Int32Constant, operand.value)); |
+ len = 1 + operand.length; |
break; |
} |
case kExprI32Const: { |
- uint32_t value = Uint32Operand(pc_); |
- Leaf(kAstI32, BUILD(Int32Constant, value)); |
- len = 5; |
+ ImmI32Operand operand(this, pc_); |
+ Leaf(kAstI32, BUILD(Int32Constant, operand.value)); |
+ len = 1 + operand.length; |
break; |
} |
case kExprI64Const: { |
- uint64_t value = Uint64Operand(pc_); |
- Leaf(kAstI64, BUILD(Int64Constant, value)); |
- len = 9; |
+ ImmI64Operand operand(this, pc_); |
+ Leaf(kAstI64, BUILD(Int64Constant, operand.value)); |
+ len = 1 + operand.length; |
break; |
} |
case kExprF32Const: { |
- float value = bit_cast<float>(Uint32Operand(pc_)); |
- Leaf(kAstF32, BUILD(Float32Constant, value)); |
- len = 5; |
+ ImmF32Operand operand(this, pc_); |
+ Leaf(kAstF32, BUILD(Float32Constant, operand.value)); |
+ len = 1 + operand.length; |
break; |
} |
case kExprF64Const: { |
- double value = bit_cast<double>(Uint64Operand(pc_)); |
- Leaf(kAstF64, BUILD(Float64Constant, value)); |
- len = 9; |
+ ImmF64Operand operand(this, pc_); |
+ Leaf(kAstF64, BUILD(Float64Constant, operand.value)); |
+ len = 1 + operand.length; |
break; |
} |
case kExprGetLocal: { |
- uint32_t index; |
- LocalType type = LocalOperand(pc_, &index, &len); |
- TFNode* val = |
- build() && type != kAstStmt ? ssa_env_->locals[index] : nullptr; |
- Leaf(type, val); |
+ LocalIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ TFNode* val = build() ? ssa_env_->locals[operand.index] : nullptr; |
+ Leaf(operand.type, val); |
+ } |
+ len = 1 + operand.length; |
break; |
} |
case kExprSetLocal: { |
- uint32_t index; |
- LocalType type = LocalOperand(pc_, &index, &len); |
- Shift(type, 1); |
+ LocalIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ Shift(operand.type, 1); |
+ } |
+ len = 1 + operand.length; |
break; |
} |
case kExprLoadGlobal: { |
- uint32_t index; |
- LocalType type = GlobalOperand(pc_, &index, &len); |
- Leaf(type, BUILD(LoadGlobal, index)); |
+ GlobalIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ Leaf(operand.type, BUILD(LoadGlobal, operand.index)); |
+ } |
+ len = 1 + operand.length; |
break; |
} |
case kExprStoreGlobal: { |
- uint32_t index; |
- LocalType type = GlobalOperand(pc_, &index, &len); |
- Shift(type, 1); |
+ GlobalIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ Shift(operand.type, 1); |
+ } |
+ len = 1 + operand.length; |
break; |
} |
case kExprI32LoadMem8S: |
@@ -647,27 +769,25 @@ class LR_WasmDecoder : public WasmDecoder { |
Shift(kAstI32, 1); |
break; |
case kExprCallFunction: { |
- uint32_t unused; |
- FunctionSig* sig = FunctionSigOperand(pc_, &unused, &len); |
- if (sig) { |
- LocalType type = |
- sig->return_count() == 0 ? kAstStmt : sig->GetReturn(); |
- Shift(type, static_cast<int>(sig->parameter_count())); |
- } else { |
- Leaf(kAstI32); // error |
+ FunctionIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ LocalType type = operand.sig->return_count() == 0 |
+ ? kAstStmt |
+ : operand.sig->GetReturn(); |
+ Shift(type, static_cast<int>(operand.sig->parameter_count())); |
} |
+ len = 1 + operand.length; |
break; |
} |
case kExprCallIndirect: { |
- uint32_t unused; |
- FunctionSig* sig = SigOperand(pc_, &unused, &len); |
- if (sig) { |
- LocalType type = |
- sig->return_count() == 0 ? kAstStmt : sig->GetReturn(); |
- Shift(type, static_cast<int>(1 + sig->parameter_count())); |
- } else { |
- Leaf(kAstI32); // error |
+ SignatureIndexOperand operand(this, pc_); |
+ if (Validate(pc_, operand)) { |
+ LocalType type = operand.sig->return_count() == 0 |
+ ? kAstStmt |
+ : operand.sig->GetReturn(); |
+ Shift(type, static_cast<int>(1 + operand.sig->parameter_count())); |
} |
+ len = 1 + operand.length; |
break; |
} |
default: |
@@ -690,19 +810,15 @@ class LR_WasmDecoder : public WasmDecoder { |
} |
int DecodeLoadMem(const byte* pc, LocalType type) { |
- int length = 2; |
- uint32_t offset; |
- MemoryAccessOperand(pc, &length, &offset); |
+ MemoryAccessOperand operand(this, pc); |
Shift(type, 1); |
- return length; |
+ return 1 + operand.length; |
} |
int DecodeStoreMem(const byte* pc, LocalType type) { |
- int length = 2; |
- uint32_t offset; |
- MemoryAccessOperand(pc, &length, &offset); |
+ MemoryAccessOperand operand(this, pc); |
Shift(type, 2); |
- return length; |
+ return 1 + operand.length; |
} |
void AddImplicitReturnAtEnd() { |
@@ -876,31 +992,23 @@ class LR_WasmDecoder : public WasmDecoder { |
break; |
} |
case kExprBr: { |
- uint32_t depth = ByteOperand(p->pc()); |
- if (depth >= blocks_.size()) { |
- error("improperly nested branch"); |
- break; |
- } |
- Block* block = &blocks_[blocks_.size() - depth - 1]; |
- ReduceBreakToExprBlock(p, block); |
+ BreakDepthOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand, blocks_)); |
+ ReduceBreakToExprBlock(p, operand.target); |
break; |
} |
case kExprBrIf: { |
if (p->index == 1) { |
TypeCheckLast(p, kAstI32); |
} else if (p->done()) { |
- uint32_t depth = ByteOperand(p->pc()); |
- if (depth >= blocks_.size()) { |
- error("improperly nested branch"); |
- break; |
- } |
- Block* block = &blocks_[blocks_.size() - depth - 1]; |
+ BreakDepthOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand, blocks_)); |
SsaEnv* fenv = ssa_env_; |
SsaEnv* tenv = Split(fenv); |
BUILD(Branch, p->tree->children[0]->node, &tenv->control, |
&fenv->control); |
ssa_env_ = tenv; |
- ReduceBreakToExprBlock(p, block); |
+ ReduceBreakToExprBlock(p, operand.target); |
ssa_env_ = fenv; |
} |
break; |
@@ -909,18 +1017,22 @@ class LR_WasmDecoder : public WasmDecoder { |
if (p->index == 1) { |
// Switch key finished. |
TypeCheckLast(p, kAstI32); |
+ if (failed()) break; |
- uint16_t table_count = read_u16(p->pc() + 3); |
+ TableSwitchOperand operand(this, p->pc()); |
+ DCHECK(Validate(p->pc(), operand, blocks_.size())); |
// Build the switch only if it has more than just a default target. |
- bool build_switch = table_count > 1; |
+ bool build_switch = operand.table_count > 1; |
TFNode* sw = nullptr; |
- if (build_switch) sw = BUILD(Switch, table_count, p->last()->node); |
+ if (build_switch) |
+ sw = BUILD(Switch, operand.table_count, p->last()->node); |
// Allocate environments for each case. |
- uint16_t case_count = read_u16(p->pc() + 1); |
- SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(case_count); |
- for (int i = 0; i < case_count; i++) case_envs[i] = UnreachableEnv(); |
+ SsaEnv** case_envs = zone_->NewArray<SsaEnv*>(operand.case_count); |
+ for (uint32_t i = 0; i < operand.case_count; i++) { |
+ case_envs[i] = UnreachableEnv(); |
+ } |
ifs_.push_back({nullptr, nullptr, case_envs}); |
SsaEnv* break_env = ssa_env_; |
@@ -929,13 +1041,14 @@ class LR_WasmDecoder : public WasmDecoder { |
ssa_env_ = copy; |
// Build the environments for each case based on the table. |
- for (int i = 0; i < table_count; i++) { |
- uint16_t target = read_u16(p->pc() + 5 + i * 2); |
+ for (uint32_t i = 0; i < operand.table_count; i++) { |
+ uint16_t target = operand.read_entry(this, i); |
SsaEnv* env = copy; |
if (build_switch) { |
env = Split(env); |
- env->control = (i == table_count - 1) ? BUILD(IfDefault, sw) |
- : BUILD(IfValue, i, sw); |
+ env->control = (i == operand.table_count - 1) |
+ ? BUILD(IfDefault, sw) |
+ : BUILD(IfValue, i, sw); |
} |
if (target >= 0x8000) { |
// Targets an outer block. |
@@ -981,12 +1094,11 @@ class LR_WasmDecoder : public WasmDecoder { |
break; |
} |
case kExprSetLocal: { |
- int unused = 0; |
- uint32_t index; |
- LocalType type = LocalOperand(p->pc(), &index, &unused); |
+ LocalIndexOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand)); |
Tree* val = p->last(); |
- if (type == val->type) { |
- if (build()) ssa_env_->locals[index] = val->node; |
+ if (operand.type == val->type) { |
+ if (build()) ssa_env_->locals[operand.index] = val->node; |
p->tree->node = val->node; |
} else { |
error(p->pc(), val->pc, "Typecheck failed in SetLocal"); |
@@ -994,12 +1106,11 @@ class LR_WasmDecoder : public WasmDecoder { |
break; |
} |
case kExprStoreGlobal: { |
- int unused = 0; |
- uint32_t index; |
- LocalType type = GlobalOperand(p->pc(), &index, &unused); |
+ GlobalIndexOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand)); |
Tree* val = p->last(); |
- if (type == val->type) { |
- BUILD(StoreGlobal, index, val->node); |
+ if (operand.type == val->type) { |
+ BUILD(StoreGlobal, operand.index, val->node); |
p->tree->node = val->node; |
} else { |
error(p->pc(), val->pc, "Typecheck failed in StoreGlobal"); |
@@ -1068,34 +1179,29 @@ class LR_WasmDecoder : public WasmDecoder { |
return; |
case kExprCallFunction: { |
- int len; |
- uint32_t index; |
- FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len); |
- if (!sig) break; |
+ FunctionIndexOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand)); |
if (p->index > 0) { |
- TypeCheckLast(p, sig->GetParam(p->index - 1)); |
+ TypeCheckLast(p, operand.sig->GetParam(p->index - 1)); |
} |
if (p->done() && build()) { |
uint32_t count = p->tree->count + 1; |
TFNode** buffer = builder_->Buffer(count); |
- FunctionSig* sig = FunctionSigOperand(p->pc(), &index, &len); |
- USE(sig); |
buffer[0] = nullptr; // reserved for code object. |
for (uint32_t i = 1; i < count; i++) { |
buffer[i] = p->tree->children[i - 1]->node; |
} |
- p->tree->node = builder_->CallDirect(index, buffer); |
+ p->tree->node = builder_->CallDirect(operand.index, buffer); |
} |
break; |
} |
case kExprCallIndirect: { |
- int len; |
- uint32_t index; |
- FunctionSig* sig = SigOperand(p->pc(), &index, &len); |
+ SignatureIndexOperand operand(this, p->pc()); |
+ CHECK(Validate(p->pc(), operand)); |
if (p->index == 1) { |
TypeCheckLast(p, kAstI32); |
} else { |
- TypeCheckLast(p, sig->GetParam(p->index - 2)); |
+ TypeCheckLast(p, operand.sig->GetParam(p->index - 2)); |
} |
if (p->done() && build()) { |
uint32_t count = p->tree->count; |
@@ -1103,7 +1209,7 @@ class LR_WasmDecoder : public WasmDecoder { |
for (uint32_t i = 0; i < count; i++) { |
buffer[i] = p->tree->children[i]->node; |
} |
- p->tree->node = builder_->CallIndirect(index, buffer); |
+ p->tree->node = builder_->CallIndirect(operand.index, buffer); |
} |
break; |
} |
@@ -1152,11 +1258,9 @@ class LR_WasmDecoder : public WasmDecoder { |
DCHECK_EQ(1, p->index); |
TypeCheckLast(p, kAstI32); // index |
if (build()) { |
- int length = 0; |
- uint32_t offset = 0; |
- MemoryAccessOperand(p->pc(), &length, &offset); |
+ MemoryAccessOperand operand(this, p->pc()); |
p->tree->node = |
- builder_->LoadMem(type, mem_type, p->last()->node, offset); |
+ builder_->LoadMem(type, mem_type, p->last()->node, operand.offset); |
} |
} |
@@ -1167,11 +1271,10 @@ class LR_WasmDecoder : public WasmDecoder { |
DCHECK_EQ(2, p->index); |
TypeCheckLast(p, type); |
if (build()) { |
- int length = 0; |
- uint32_t offset = 0; |
- MemoryAccessOperand(p->pc(), &length, &offset); |
+ MemoryAccessOperand operand(this, p->pc()); |
TFNode* val = p->tree->children[1]->node; |
- builder_->StoreMem(mem_type, p->tree->children[0]->node, offset, val); |
+ builder_->StoreMem(mem_type, p->tree->children[0]->node, operand.offset, |
+ val); |
p->tree->node = val; |
} |
} |
@@ -1194,7 +1297,7 @@ class LR_WasmDecoder : public WasmDecoder { |
void SetEnv(const char* reason, SsaEnv* env) { |
TRACE(" env = %p, block depth = %d, reason = %s", static_cast<void*>(env), |
static_cast<int>(blocks_.size()), reason); |
- if (env->control != nullptr && FLAG_trace_wasm_decoder) { |
+ if (FLAG_trace_wasm_decoder && env && env->control) { |
TRACE(", control = "); |
compiler::WasmGraphBuilder::PrintDebugName(env->control); |
} |
@@ -1447,158 +1550,29 @@ ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte* pc, |
const byte* limit, |
int* length, |
uint32_t* result) { |
- *result = 0; |
- const byte* ptr = pc; |
- const byte* end = pc + 5; // maximum 5 bytes. |
- if (end > limit) end = limit; |
- int shift = 0; |
- byte b = 0; |
- while (ptr < end) { |
- b = *ptr++; |
- *result = *result | ((b & 0x7F) << shift); |
- if ((b & 0x80) == 0) break; |
- shift += 7; |
- } |
- DCHECK_LE(ptr - pc, 5); |
- *length = static_cast<int>(ptr - pc); |
- if (ptr == end && (b & 0x80)) { |
- return kInvalidLEB128; |
- } else if (*length == 0) { |
- return kMissingLEB128; |
- } else { |
- return kNoError; |
- } |
+ Decoder decoder(pc, limit); |
+ *result = decoder.checked_read_u32v(pc, 0, length); |
+ if (decoder.ok()) return kNoError; |
+ return (limit - pc) > 1 ? kInvalidLEB128 : kMissingLEB128; |
} |
- |
-// TODO(titzer): move this into WasmDecoder and bounds check accesses. |
-int OpcodeLength(const byte* pc) { |
- switch (static_cast<WasmOpcode>(*pc)) { |
-#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: |
- FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) |
- FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) |
-#undef DECLARE_OPCODE_CASE |
- { |
- // Loads and stores have an optional offset. |
- byte bitfield = pc[1]; |
- if (MemoryAccess::OffsetField::decode(bitfield)) { |
- int length; |
- uint32_t result = 0; |
- ReadUnsignedLEB128Operand(pc + 2, pc + 7, &length, &result); |
- return 2 + length; |
- } |
- return 2; |
- } |
- case kExprI8Const: |
- case kExprBlock: |
- case kExprLoop: |
- case kExprBr: |
- case kExprBrIf: |
- return 2; |
- case kExprI32Const: |
- case kExprF32Const: |
- return 5; |
- case kExprI64Const: |
- case kExprF64Const: |
- return 9; |
- case kExprStoreGlobal: |
- case kExprSetLocal: |
- case kExprLoadGlobal: |
- case kExprCallFunction: |
- case kExprCallIndirect: |
- case kExprGetLocal: { |
- int length; |
- uint32_t result = 0; |
- ReadUnsignedLEB128Operand(pc + 1, pc + 6, &length, &result); |
- return 1 + length; |
- } |
- case kExprTableSwitch: { |
- uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc + 3); |
- return 5 + table_count * 2; |
- } |
- |
- default: |
- return 1; |
- } |
+int OpcodeLength(const byte* pc, const byte* end) { |
+ WasmDecoder decoder(nullptr, pc, end); |
+ return decoder.OpcodeLength(pc); |
} |
- |
-// TODO(titzer): move this into WasmDecoder and bounds check accesses. |
-int OpcodeArity(FunctionEnv* env, const byte* pc) { |
-#define DECLARE_ARITY(name, ...) \ |
- static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ |
- static const int kArity_##name = \ |
- static_cast<int>(arraysize(kTypes_##name) - 1); |
- |
- FOREACH_SIGNATURE(DECLARE_ARITY); |
-#undef DECLARE_ARITY |
- |
- switch (static_cast<WasmOpcode>(*pc)) { |
- case kExprI8Const: |
- case kExprI32Const: |
- case kExprI64Const: |
- case kExprF64Const: |
- case kExprF32Const: |
- case kExprGetLocal: |
- case kExprLoadGlobal: |
- case kExprNop: |
- case kExprUnreachable: |
- return 0; |
- |
- case kExprBr: |
- case kExprStoreGlobal: |
- case kExprSetLocal: |
- return 1; |
- |
- case kExprIf: |
- case kExprBrIf: |
- return 2; |
- case kExprIfElse: |
- case kExprSelect: |
- return 3; |
- case kExprBlock: |
- case kExprLoop: |
- return *(pc + 1); |
- |
- case kExprCallFunction: { |
- int index = *(pc + 1); |
- return static_cast<int>( |
- env->module->GetFunctionSignature(index)->parameter_count()); |
- } |
- case kExprCallIndirect: { |
- int index = *(pc + 1); |
- return 1 + static_cast<int>( |
- env->module->GetSignature(index)->parameter_count()); |
- } |
- case kExprReturn: |
- return static_cast<int>(env->sig->return_count()); |
- case kExprTableSwitch: { |
- uint16_t case_count = *reinterpret_cast<const uint16_t*>(pc + 1); |
- return 1 + case_count; |
- } |
- |
-#define DECLARE_OPCODE_CASE(name, opcode, sig) \ |
- case kExpr##name: \ |
- return kArity_##sig; |
- |
- FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) |
- FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) |
- FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) |
- FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) |
-#undef DECLARE_OPCODE_CASE |
- } |
- UNREACHABLE(); |
- return 0; |
+int OpcodeArity(FunctionEnv* env, const byte* pc, const byte* end) { |
+ WasmDecoder decoder(env, pc, end); |
+ return decoder.OpcodeArity(pc); |
} |
- |
- |
void PrintAst(FunctionEnv* env, const byte* start, const byte* end) { |
+ WasmDecoder decoder(env, start, end); |
const byte* pc = start; |
std::vector<int> arity_stack; |
while (pc < end) { |
- int arity = OpcodeArity(env, pc); |
- size_t length = OpcodeLength(pc); |
+ int arity = decoder.OpcodeArity(pc); |
+ size_t length = decoder.OpcodeLength(pc); |
for (auto arity : arity_stack) { |
printf(" "); |
@@ -1623,7 +1597,6 @@ void PrintAst(FunctionEnv* env, const byte* start, const byte* end) { |
} |
} |
- |
// Analyzes loop bodies for static assignments to locals, which helps in |
// reducing the number of phis introduced at loop headers. |
class LoopAssignmentAnalyzer : public WasmDecoder { |
@@ -1641,7 +1614,7 @@ class LoopAssignmentAnalyzer : public WasmDecoder { |
new (zone_) BitVector(function_env_->total_locals, zone_); |
// Keep a stack to model the nesting of expressions. |
std::vector<int> arity_stack; |
- arity_stack.push_back(OpcodeArity(function_env_, pc_)); |
+ arity_stack.push_back(OpcodeArity(pc_)); |
pc_ += OpcodeLength(pc_); |
// Iteratively process all AST nodes nested inside the loop. |
@@ -1650,16 +1623,16 @@ class LoopAssignmentAnalyzer : public WasmDecoder { |
int arity = 0; |
int length = 1; |
if (opcode == kExprSetLocal) { |
- uint32_t index; |
- LocalOperand(pc_, &index, &length); |
+ LocalIndexOperand operand(this, pc_); |
if (assigned->length() > 0 && |
- static_cast<int>(index) < assigned->length()) { |
+ static_cast<int>(operand.index) < assigned->length()) { |
// Unverified code might have an out-of-bounds index. |
- assigned->Add(index); |
+ assigned->Add(operand.index); |
} |
arity = 1; |
+ length = 1 + operand.length; |
} else { |
- arity = OpcodeArity(function_env_, pc_); |
+ arity = OpcodeArity(pc_); |
length = OpcodeLength(pc_); |
} |