Index: src/wasm/ast-decoder.cc |
diff --git a/src/wasm/ast-decoder.cc b/src/wasm/ast-decoder.cc |
index 3721ff1cfaf4d07f46d171ddd8369314ad622bce..a56510db1da303256e4ee5a7ad418d2c3edbfcbf 100644 |
--- a/src/wasm/ast-decoder.cc |
+++ b/src/wasm/ast-decoder.cc |
@@ -5,6 +5,7 @@ |
#include "src/base/platform/elapsed-timer.h" |
#include "src/signature.h" |
+#include "src/bit-vector.h" |
#include "src/flags.h" |
#include "src/handles.h" |
#include "src/zone-containers.h" |
@@ -97,13 +98,111 @@ struct IfEnv { |
#define BUILD0(func) (build() ? builder_->func() : nullptr) |
+// Generic Wasm bytecode decoder with utilities for decoding operands, |
+// lengths, etc. |
+class WasmDecoder : public Decoder { |
+ public: |
+ WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {} |
+ |
+ protected: |
+ FunctionEnv* function_env_; |
+ |
+ void Reset(FunctionEnv* function_env, const byte* start, const byte* end) { |
+ Decoder::Reset(start, end); |
+ function_env_ = function_env; |
+ } |
+ |
+ // Load an operand at [pc + 1]. |
+ template <typename V> |
+ V Operand(const byte* pc) { |
+ if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) { |
+ const char* msg = "Expected operand following opcode"; |
+ switch (sizeof(V)) { |
+ case 1: |
+ msg = "Expected 1-byte operand following opcode"; |
+ break; |
+ case 2: |
+ msg = "Expected 2-byte operand following opcode"; |
+ break; |
+ case 4: |
+ msg = "Expected 4-byte operand following opcode"; |
+ break; |
+ default: |
+ break; |
+ } |
+ error(pc, msg); |
+ return -1; |
+ } |
+ return *reinterpret_cast<const V*>(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); |
+ } |
+ error(pc, "invalid local variable index"); |
+ return kAstStmt; |
+ } |
+ |
+ 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)); |
+ } |
+ error(pc, "invalid global variable index"); |
+ return kAstStmt; |
+ } |
+ |
+ 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); |
+ } |
+ error(pc, "invalid function index"); |
+ return nullptr; |
+ } |
+ |
+ 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); |
+ } |
+ error(pc, "invalid signature index"); |
+ return nullptr; |
+ } |
+ |
+ 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; |
+ } |
+ |
+ void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) { |
+ byte bitfield = Operand<uint8_t>(pc); |
+ if (MemoryAccess::OffsetField::decode(bitfield)) { |
+ *offset = UnsignedLEB128Operand(pc + 1, length); |
+ (*length)++; // to account for the memory access byte |
+ } else { |
+ *offset = 0; |
+ *length = 2; |
+ } |
+ } |
+}; |
+ |
+ |
// A shift-reduce-parser strategy for decoding Wasm code that uses an explicit |
// shift-reduce strategy with multiple internal stacks. |
-class LR_WasmDecoder : public Decoder { |
+class LR_WasmDecoder : public WasmDecoder { |
public: |
LR_WasmDecoder(Zone* zone, TFBuilder* builder) |
- : Decoder(nullptr, nullptr), |
- zone_(zone), |
+ : zone_(zone), |
builder_(builder), |
trees_(zone), |
stack_(zone), |
@@ -127,8 +226,7 @@ class LR_WasmDecoder : public Decoder { |
} |
base_ = base; |
- Reset(pc, end); |
- function_env_ = function_env; |
+ Reset(function_env, pc, end); |
InitSsaEnv(); |
DecodeFunctionBody(); |
@@ -177,7 +275,6 @@ class LR_WasmDecoder : public Decoder { |
TreeResult result_; |
SsaEnv* ssa_env_; |
- FunctionEnv* function_env_; |
ZoneVector<Tree*> trees_; |
ZoneVector<Production> stack_; |
@@ -1291,94 +1388,11 @@ class LR_WasmDecoder : public Decoder { |
return result; |
} |
- // Load an operand at [pc + 1]. |
- template <typename V> |
- V Operand(const byte* pc) { |
- if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) { |
- const char* msg = "Expected operand following opcode"; |
- switch (sizeof(V)) { |
- case 1: |
- msg = "Expected 1-byte operand following opcode"; |
- break; |
- case 2: |
- msg = "Expected 2-byte operand following opcode"; |
- break; |
- case 4: |
- msg = "Expected 4-byte operand following opcode"; |
- break; |
- default: |
- break; |
- } |
- error(pc, msg); |
- return -1; |
- } |
- return *reinterpret_cast<const V*>(pc + 1); |
- } |
- |
int EnvironmentCount() { |
if (builder_) return static_cast<int>(function_env_->GetLocalCount()); |
return 0; // if we aren't building a graph, don't bother with SSA renaming. |
} |
- LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) { |
- *index = UnsignedLEB128Operand(pc, length); |
- if (function_env_->IsValidLocal(*index)) { |
- return function_env_->GetLocalType(*index); |
- } |
- error(pc, "invalid local variable index"); |
- return kAstStmt; |
- } |
- |
- 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)); |
- } |
- error(pc, "invalid global variable index"); |
- return kAstStmt; |
- } |
- |
- 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); |
- } |
- error(pc, "invalid function index"); |
- return nullptr; |
- } |
- |
- 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); |
- } |
- error(pc, "invalid signature index"); |
- return nullptr; |
- } |
- |
- 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; |
- } |
- |
- void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) { |
- byte bitfield = Operand<uint8_t>(pc); |
- if (MemoryAccess::OffsetField::decode(bitfield)) { |
- *offset = UnsignedLEB128Operand(pc + 1, length); |
- (*length)++; // to account for the memory access byte |
- } else { |
- *offset = 0; |
- *length = 2; |
- } |
- } |
- |
virtual void onFirstError() { |
limit_ = start_; // Terminate decoding loop. |
builder_ = nullptr; // Don't build any more nodes. |
@@ -1476,6 +1490,7 @@ ReadUnsignedLEB128ErrorCode ReadUnsignedLEB128Operand(const byte* pc, |
} |
+// 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: |
@@ -1527,6 +1542,7 @@ int OpcodeLength(const byte* 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__}; \ |
@@ -1595,6 +1611,7 @@ int OpcodeArity(FunctionEnv* env, const byte* pc) { |
} |
+ |
void PrintAst(FunctionEnv* env, const byte* start, const byte* end) { |
const byte* pc = start; |
std::vector<int> arity_stack; |
@@ -1624,6 +1641,69 @@ 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 { |
+ public: |
+ LoopAssignmentAnalyzer(Zone* zone, FunctionEnv* function_env) : zone_(zone) { |
+ function_env_ = function_env; |
+ } |
+ |
+ BitVector* Analyze(const byte* pc, const byte* limit) { |
+ Decoder::Reset(pc, limit); |
+ if (pc_ >= limit_) return nullptr; |
+ if (*pc_ != kExprLoop) return nullptr; |
+ |
+ BitVector* assigned = |
+ 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_)); |
+ pc_ += OpcodeLength(pc_); |
+ |
+ // Iteratively process all AST nodes nested inside the loop. |
+ while (pc_ < limit_) { |
+ WasmOpcode opcode = static_cast<WasmOpcode>(*pc_); |
+ int arity = 0; |
+ int length = 1; |
+ if (opcode == kExprSetLocal) { |
+ uint32_t index; |
+ LocalOperand(pc_, &index, &length); |
+ if (assigned->length() > 0 && |
+ static_cast<int>(index) < assigned->length()) { |
+ // Unverified code might have an out-of-bounds index. |
+ assigned->Add(index); |
+ } |
+ arity = 1; |
+ } else { |
+ arity = OpcodeArity(function_env_, pc_); |
+ length = OpcodeLength(pc_); |
+ } |
+ |
+ pc_ += length; |
+ arity_stack.push_back(arity); |
+ while (arity_stack.back() == 0) { |
+ arity_stack.pop_back(); |
+ if (arity_stack.empty()) return assigned; // reached end of loop |
+ arity_stack.back()--; |
+ } |
+ } |
+ return assigned; |
+ } |
+ |
+ private: |
+ Zone* zone_; |
+}; |
+ |
+ |
+BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env, |
+ const byte* start, const byte* end) { |
+ LoopAssignmentAnalyzer analyzer(zone, env); |
+ return analyzer.Analyze(start, end); |
+} |
+ |
} // namespace wasm |
} // namespace internal |
} // namespace v8 |