Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(57)

Side by Side Diff: src/wasm/ast-decoder.cc

Issue 1617723003: [wasm] Add loop assignment analysis. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/wasm/ast-decoder.h ('k') | test/unittests/unittests.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 the V8 project authors. All rights reserved. 1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "src/base/platform/elapsed-timer.h" 5 #include "src/base/platform/elapsed-timer.h"
6 #include "src/signature.h" 6 #include "src/signature.h"
7 7
8 #include "src/bit-vector.h"
8 #include "src/flags.h" 9 #include "src/flags.h"
9 #include "src/handles.h" 10 #include "src/handles.h"
10 #include "src/zone-containers.h" 11 #include "src/zone-containers.h"
11 12
12 #include "src/wasm/ast-decoder.h" 13 #include "src/wasm/ast-decoder.h"
13 #include "src/wasm/decoder.h" 14 #include "src/wasm/decoder.h"
14 #include "src/wasm/wasm-module.h" 15 #include "src/wasm/wasm-module.h"
15 #include "src/wasm/wasm-opcodes.h" 16 #include "src/wasm/wasm-opcodes.h"
16 17
17 #include "src/compiler/wasm-compiler.h" 18 #include "src/compiler/wasm-compiler.h"
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 }; 91 };
91 92
92 93
93 // Macros that build nodes only if there is a graph and the current SSA 94 // Macros that build nodes only if there is a graph and the current SSA
94 // environment is reachable from start. This avoids problems with malformed 95 // environment is reachable from start. This avoids problems with malformed
95 // TF graphs when decoding inputs that have unreachable code. 96 // TF graphs when decoding inputs that have unreachable code.
96 #define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr) 97 #define BUILD(func, ...) (build() ? builder_->func(__VA_ARGS__) : nullptr)
97 #define BUILD0(func) (build() ? builder_->func() : nullptr) 98 #define BUILD0(func) (build() ? builder_->func() : nullptr)
98 99
99 100
101 // Generic Wasm bytecode decoder with utilities for decoding operands,
102 // lengths, etc.
103 class WasmDecoder : public Decoder {
104 public:
105 WasmDecoder() : Decoder(nullptr, nullptr), function_env_(nullptr) {}
106
107 protected:
108 FunctionEnv* function_env_;
109
110 void Reset(FunctionEnv* function_env, const byte* start, const byte* end) {
111 Decoder::Reset(start, end);
112 function_env_ = function_env;
113 }
114
115 // Load an operand at [pc + 1].
116 template <typename V>
117 V Operand(const byte* pc) {
118 if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) {
119 const char* msg = "Expected operand following opcode";
120 switch (sizeof(V)) {
121 case 1:
122 msg = "Expected 1-byte operand following opcode";
123 break;
124 case 2:
125 msg = "Expected 2-byte operand following opcode";
126 break;
127 case 4:
128 msg = "Expected 4-byte operand following opcode";
129 break;
130 default:
131 break;
132 }
133 error(pc, msg);
134 return -1;
135 }
136 return *reinterpret_cast<const V*>(pc + 1);
137 }
138
139 LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) {
140 *index = UnsignedLEB128Operand(pc, length);
141 if (function_env_->IsValidLocal(*index)) {
142 return function_env_->GetLocalType(*index);
143 }
144 error(pc, "invalid local variable index");
145 return kAstStmt;
146 }
147
148 LocalType GlobalOperand(const byte* pc, uint32_t* index, int* length) {
149 *index = UnsignedLEB128Operand(pc, length);
150 if (function_env_->module->IsValidGlobal(*index)) {
151 return WasmOpcodes::LocalTypeFor(
152 function_env_->module->GetGlobalType(*index));
153 }
154 error(pc, "invalid global variable index");
155 return kAstStmt;
156 }
157
158 FunctionSig* FunctionSigOperand(const byte* pc, uint32_t* index,
159 int* length) {
160 *index = UnsignedLEB128Operand(pc, length);
161 if (function_env_->module->IsValidFunction(*index)) {
162 return function_env_->module->GetFunctionSignature(*index);
163 }
164 error(pc, "invalid function index");
165 return nullptr;
166 }
167
168 FunctionSig* SigOperand(const byte* pc, uint32_t* index, int* length) {
169 *index = UnsignedLEB128Operand(pc, length);
170 if (function_env_->module->IsValidSignature(*index)) {
171 return function_env_->module->GetSignature(*index);
172 }
173 error(pc, "invalid signature index");
174 return nullptr;
175 }
176
177 uint32_t UnsignedLEB128Operand(const byte* pc, int* length) {
178 uint32_t result = 0;
179 ReadUnsignedLEB128ErrorCode error_code =
180 ReadUnsignedLEB128Operand(pc + 1, limit_, length, &result);
181 if (error_code == kInvalidLEB128) error(pc, "invalid LEB128 varint");
182 if (error_code == kMissingLEB128) error(pc, "expected LEB128 varint");
183 (*length)++;
184 return result;
185 }
186
187 void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) {
188 byte bitfield = Operand<uint8_t>(pc);
189 if (MemoryAccess::OffsetField::decode(bitfield)) {
190 *offset = UnsignedLEB128Operand(pc + 1, length);
191 (*length)++; // to account for the memory access byte
192 } else {
193 *offset = 0;
194 *length = 2;
195 }
196 }
197 };
198
199
100 // A shift-reduce-parser strategy for decoding Wasm code that uses an explicit 200 // A shift-reduce-parser strategy for decoding Wasm code that uses an explicit
101 // shift-reduce strategy with multiple internal stacks. 201 // shift-reduce strategy with multiple internal stacks.
102 class LR_WasmDecoder : public Decoder { 202 class LR_WasmDecoder : public WasmDecoder {
103 public: 203 public:
104 LR_WasmDecoder(Zone* zone, TFBuilder* builder) 204 LR_WasmDecoder(Zone* zone, TFBuilder* builder)
105 : Decoder(nullptr, nullptr), 205 : zone_(zone),
106 zone_(zone),
107 builder_(builder), 206 builder_(builder),
108 trees_(zone), 207 trees_(zone),
109 stack_(zone), 208 stack_(zone),
110 blocks_(zone), 209 blocks_(zone),
111 ifs_(zone) {} 210 ifs_(zone) {}
112 211
113 TreeResult Decode(FunctionEnv* function_env, const byte* base, const byte* pc, 212 TreeResult Decode(FunctionEnv* function_env, const byte* base, const byte* pc,
114 const byte* end) { 213 const byte* end) {
115 base::ElapsedTimer decode_timer; 214 base::ElapsedTimer decode_timer;
116 if (FLAG_trace_wasm_decode_time) { 215 if (FLAG_trace_wasm_decode_time) {
117 decode_timer.Start(); 216 decode_timer.Start();
118 } 217 }
119 trees_.clear(); 218 trees_.clear();
120 stack_.clear(); 219 stack_.clear();
121 blocks_.clear(); 220 blocks_.clear();
122 ifs_.clear(); 221 ifs_.clear();
123 222
124 if (end < pc) { 223 if (end < pc) {
125 error(pc, "function body end < start"); 224 error(pc, "function body end < start");
126 return result_; 225 return result_;
127 } 226 }
128 227
129 base_ = base; 228 base_ = base;
130 Reset(pc, end); 229 Reset(function_env, pc, end);
131 function_env_ = function_env;
132 230
133 InitSsaEnv(); 231 InitSsaEnv();
134 DecodeFunctionBody(); 232 DecodeFunctionBody();
135 233
136 Tree* tree = nullptr; 234 Tree* tree = nullptr;
137 if (ok()) { 235 if (ok()) {
138 if (ssa_env_->go()) { 236 if (ssa_env_->go()) {
139 if (stack_.size() > 0) { 237 if (stack_.size() > 0) {
140 error(stack_.back().pc(), end, "fell off end of code"); 238 error(stack_.back().pc(), end, "fell off end of code");
141 } 239 }
(...skipping 28 matching lines...) Expand all
170 268
171 private: 269 private:
172 static const size_t kErrorMsgSize = 128; 270 static const size_t kErrorMsgSize = 128;
173 271
174 Zone* zone_; 272 Zone* zone_;
175 TFBuilder* builder_; 273 TFBuilder* builder_;
176 const byte* base_; 274 const byte* base_;
177 TreeResult result_; 275 TreeResult result_;
178 276
179 SsaEnv* ssa_env_; 277 SsaEnv* ssa_env_;
180 FunctionEnv* function_env_;
181 278
182 ZoneVector<Tree*> trees_; 279 ZoneVector<Tree*> trees_;
183 ZoneVector<Production> stack_; 280 ZoneVector<Production> stack_;
184 ZoneVector<Block> blocks_; 281 ZoneVector<Block> blocks_;
185 ZoneVector<IfEnv> ifs_; 282 ZoneVector<IfEnv> ifs_;
186 283
187 inline bool build() { return builder_ && ssa_env_->go(); } 284 inline bool build() { return builder_ && ssa_env_->go(); }
188 285
189 void InitSsaEnv() { 286 void InitSsaEnv() {
190 FunctionSig* sig = function_env_->sig; 287 FunctionSig* sig = function_env_->sig;
(...skipping 1093 matching lines...) Expand 10 before | Expand all | Expand 10 after
1284 // Create an unreachable environment. 1381 // Create an unreachable environment.
1285 SsaEnv* UnreachableEnv() { 1382 SsaEnv* UnreachableEnv() {
1286 SsaEnv* result = reinterpret_cast<SsaEnv*>(zone_->New(sizeof(SsaEnv))); 1383 SsaEnv* result = reinterpret_cast<SsaEnv*>(zone_->New(sizeof(SsaEnv)));
1287 result->state = SsaEnv::kUnreachable; 1384 result->state = SsaEnv::kUnreachable;
1288 result->control = nullptr; 1385 result->control = nullptr;
1289 result->effect = nullptr; 1386 result->effect = nullptr;
1290 result->locals = nullptr; 1387 result->locals = nullptr;
1291 return result; 1388 return result;
1292 } 1389 }
1293 1390
1294 // Load an operand at [pc + 1].
1295 template <typename V>
1296 V Operand(const byte* pc) {
1297 if ((limit_ - pc) < static_cast<int>(1 + sizeof(V))) {
1298 const char* msg = "Expected operand following opcode";
1299 switch (sizeof(V)) {
1300 case 1:
1301 msg = "Expected 1-byte operand following opcode";
1302 break;
1303 case 2:
1304 msg = "Expected 2-byte operand following opcode";
1305 break;
1306 case 4:
1307 msg = "Expected 4-byte operand following opcode";
1308 break;
1309 default:
1310 break;
1311 }
1312 error(pc, msg);
1313 return -1;
1314 }
1315 return *reinterpret_cast<const V*>(pc + 1);
1316 }
1317
1318 int EnvironmentCount() { 1391 int EnvironmentCount() {
1319 if (builder_) return static_cast<int>(function_env_->GetLocalCount()); 1392 if (builder_) return static_cast<int>(function_env_->GetLocalCount());
1320 return 0; // if we aren't building a graph, don't bother with SSA renaming. 1393 return 0; // if we aren't building a graph, don't bother with SSA renaming.
1321 } 1394 }
1322 1395
1323 LocalType LocalOperand(const byte* pc, uint32_t* index, int* length) {
1324 *index = UnsignedLEB128Operand(pc, length);
1325 if (function_env_->IsValidLocal(*index)) {
1326 return function_env_->GetLocalType(*index);
1327 }
1328 error(pc, "invalid local variable index");
1329 return kAstStmt;
1330 }
1331
1332 LocalType GlobalOperand(const byte* pc, uint32_t* index, int* length) {
1333 *index = UnsignedLEB128Operand(pc, length);
1334 if (function_env_->module->IsValidGlobal(*index)) {
1335 return WasmOpcodes::LocalTypeFor(
1336 function_env_->module->GetGlobalType(*index));
1337 }
1338 error(pc, "invalid global variable index");
1339 return kAstStmt;
1340 }
1341
1342 FunctionSig* FunctionSigOperand(const byte* pc, uint32_t* index,
1343 int* length) {
1344 *index = UnsignedLEB128Operand(pc, length);
1345 if (function_env_->module->IsValidFunction(*index)) {
1346 return function_env_->module->GetFunctionSignature(*index);
1347 }
1348 error(pc, "invalid function index");
1349 return nullptr;
1350 }
1351
1352 FunctionSig* SigOperand(const byte* pc, uint32_t* index, int* length) {
1353 *index = UnsignedLEB128Operand(pc, length);
1354 if (function_env_->module->IsValidSignature(*index)) {
1355 return function_env_->module->GetSignature(*index);
1356 }
1357 error(pc, "invalid signature index");
1358 return nullptr;
1359 }
1360
1361 uint32_t UnsignedLEB128Operand(const byte* pc, int* length) {
1362 uint32_t result = 0;
1363 ReadUnsignedLEB128ErrorCode error_code =
1364 ReadUnsignedLEB128Operand(pc + 1, limit_, length, &result);
1365 if (error_code == kInvalidLEB128) error(pc, "invalid LEB128 varint");
1366 if (error_code == kMissingLEB128) error(pc, "expected LEB128 varint");
1367 (*length)++;
1368 return result;
1369 }
1370
1371 void MemoryAccessOperand(const byte* pc, int* length, uint32_t* offset) {
1372 byte bitfield = Operand<uint8_t>(pc);
1373 if (MemoryAccess::OffsetField::decode(bitfield)) {
1374 *offset = UnsignedLEB128Operand(pc + 1, length);
1375 (*length)++; // to account for the memory access byte
1376 } else {
1377 *offset = 0;
1378 *length = 2;
1379 }
1380 }
1381
1382 virtual void onFirstError() { 1396 virtual void onFirstError() {
1383 limit_ = start_; // Terminate decoding loop. 1397 limit_ = start_; // Terminate decoding loop.
1384 builder_ = nullptr; // Don't build any more nodes. 1398 builder_ = nullptr; // Don't build any more nodes.
1385 #if DEBUG 1399 #if DEBUG
1386 PrintStackForDebugging(); 1400 PrintStackForDebugging();
1387 #endif 1401 #endif
1388 } 1402 }
1389 1403
1390 #if DEBUG 1404 #if DEBUG
1391 void PrintStackForDebugging() { PrintProduction(0); } 1405 void PrintStackForDebugging() { PrintProduction(0); }
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
1469 if (ptr == end && (b & 0x80)) { 1483 if (ptr == end && (b & 0x80)) {
1470 return kInvalidLEB128; 1484 return kInvalidLEB128;
1471 } else if (*length == 0) { 1485 } else if (*length == 0) {
1472 return kMissingLEB128; 1486 return kMissingLEB128;
1473 } else { 1487 } else {
1474 return kNoError; 1488 return kNoError;
1475 } 1489 }
1476 } 1490 }
1477 1491
1478 1492
1493 // TODO(titzer): move this into WasmDecoder and bounds check accesses.
1479 int OpcodeLength(const byte* pc) { 1494 int OpcodeLength(const byte* pc) {
1480 switch (static_cast<WasmOpcode>(*pc)) { 1495 switch (static_cast<WasmOpcode>(*pc)) {
1481 #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name: 1496 #define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
1482 FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE) 1497 FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
1483 FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) 1498 FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
1484 #undef DECLARE_OPCODE_CASE 1499 #undef DECLARE_OPCODE_CASE
1485 { 1500 {
1486 // Loads and stores have an optional offset. 1501 // Loads and stores have an optional offset.
1487 byte bitfield = pc[1]; 1502 byte bitfield = pc[1];
1488 if (MemoryAccess::OffsetField::decode(bitfield)) { 1503 if (MemoryAccess::OffsetField::decode(bitfield)) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1520 uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc + 3); 1535 uint16_t table_count = *reinterpret_cast<const uint16_t*>(pc + 3);
1521 return 5 + table_count * 2; 1536 return 5 + table_count * 2;
1522 } 1537 }
1523 1538
1524 default: 1539 default:
1525 return 1; 1540 return 1;
1526 } 1541 }
1527 } 1542 }
1528 1543
1529 1544
1545 // TODO(titzer): move this into WasmDecoder and bounds check accesses.
1530 int OpcodeArity(FunctionEnv* env, const byte* pc) { 1546 int OpcodeArity(FunctionEnv* env, const byte* pc) {
1531 #define DECLARE_ARITY(name, ...) \ 1547 #define DECLARE_ARITY(name, ...) \
1532 static const LocalType kTypes_##name[] = {__VA_ARGS__}; \ 1548 static const LocalType kTypes_##name[] = {__VA_ARGS__}; \
1533 static const int kArity_##name = \ 1549 static const int kArity_##name = \
1534 static_cast<int>(arraysize(kTypes_##name) - 1); 1550 static_cast<int>(arraysize(kTypes_##name) - 1);
1535 1551
1536 FOREACH_SIGNATURE(DECLARE_ARITY); 1552 FOREACH_SIGNATURE(DECLARE_ARITY);
1537 #undef DECLARE_ARITY 1553 #undef DECLARE_ARITY
1538 1554
1539 switch (static_cast<WasmOpcode>(*pc)) { 1555 switch (static_cast<WasmOpcode>(*pc)) {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
1588 FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE) 1604 FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
1589 FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE) 1605 FOREACH_MISC_MEM_OPCODE(DECLARE_OPCODE_CASE)
1590 FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE) 1606 FOREACH_SIMPLE_OPCODE(DECLARE_OPCODE_CASE)
1591 #undef DECLARE_OPCODE_CASE 1607 #undef DECLARE_OPCODE_CASE
1592 } 1608 }
1593 UNREACHABLE(); 1609 UNREACHABLE();
1594 return 0; 1610 return 0;
1595 } 1611 }
1596 1612
1597 1613
1614
1598 void PrintAst(FunctionEnv* env, const byte* start, const byte* end) { 1615 void PrintAst(FunctionEnv* env, const byte* start, const byte* end) {
1599 const byte* pc = start; 1616 const byte* pc = start;
1600 std::vector<int> arity_stack; 1617 std::vector<int> arity_stack;
1601 while (pc < end) { 1618 while (pc < end) {
1602 int arity = OpcodeArity(env, pc); 1619 int arity = OpcodeArity(env, pc);
1603 size_t length = OpcodeLength(pc); 1620 size_t length = OpcodeLength(pc);
1604 1621
1605 for (auto arity : arity_stack) { 1622 for (auto arity : arity_stack) {
1606 printf(" "); 1623 printf(" ");
1607 USE(arity); 1624 USE(arity);
1608 } 1625 }
1609 1626
1610 WasmOpcode opcode = static_cast<WasmOpcode>(*pc); 1627 WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
1611 printf("k%s,", WasmOpcodes::OpcodeName(opcode)); 1628 printf("k%s,", WasmOpcodes::OpcodeName(opcode));
1612 1629
1613 for (size_t i = 1; i < length; i++) { 1630 for (size_t i = 1; i < length; i++) {
1614 printf(" 0x%02x,", pc[i]); 1631 printf(" 0x%02x,", pc[i]);
1615 } 1632 }
1616 pc += length; 1633 pc += length;
1617 printf("\n"); 1634 printf("\n");
1618 1635
1619 arity_stack.push_back(arity); 1636 arity_stack.push_back(arity);
1620 while (arity_stack.back() == 0) { 1637 while (arity_stack.back() == 0) {
1621 arity_stack.pop_back(); 1638 arity_stack.pop_back();
1622 if (arity_stack.empty()) break; 1639 if (arity_stack.empty()) break;
1623 arity_stack.back()--; 1640 arity_stack.back()--;
1624 } 1641 }
1625 } 1642 }
1626 } 1643 }
1644
1645
1646 // Analyzes loop bodies for static assignments to locals, which helps in
1647 // reducing the number of phis introduced at loop headers.
1648 class LoopAssignmentAnalyzer : public WasmDecoder {
1649 public:
1650 LoopAssignmentAnalyzer(Zone* zone, FunctionEnv* function_env) : zone_(zone) {
1651 function_env_ = function_env;
1652 }
1653
1654 BitVector* Analyze(const byte* pc, const byte* limit) {
1655 Decoder::Reset(pc, limit);
1656 if (pc_ >= limit_) return nullptr;
1657 if (*pc_ != kExprLoop) return nullptr;
1658
1659 BitVector* assigned =
1660 new (zone_) BitVector(function_env_->total_locals, zone_);
1661 // Keep a stack to model the nesting of expressions.
1662 std::vector<int> arity_stack;
1663 arity_stack.push_back(OpcodeArity(function_env_, pc_));
1664 pc_ += OpcodeLength(pc_);
1665
1666 // Iteratively process all AST nodes nested inside the loop.
1667 while (pc_ < limit_) {
1668 WasmOpcode opcode = static_cast<WasmOpcode>(*pc_);
1669 int arity = 0;
1670 int length = 1;
1671 if (opcode == kExprSetLocal) {
1672 uint32_t index;
1673 LocalOperand(pc_, &index, &length);
1674 if (assigned->length() > 0 &&
1675 static_cast<int>(index) < assigned->length()) {
1676 // Unverified code might have an out-of-bounds index.
1677 assigned->Add(index);
1678 }
1679 arity = 1;
1680 } else {
1681 arity = OpcodeArity(function_env_, pc_);
1682 length = OpcodeLength(pc_);
1683 }
1684
1685 pc_ += length;
1686 arity_stack.push_back(arity);
1687 while (arity_stack.back() == 0) {
1688 arity_stack.pop_back();
1689 if (arity_stack.empty()) return assigned; // reached end of loop
1690 arity_stack.back()--;
1691 }
1692 }
1693 return assigned;
1694 }
1695
1696 private:
1697 Zone* zone_;
1698 };
1699
1700
1701 BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone, FunctionEnv* env,
1702 const byte* start, const byte* end) {
1703 LoopAssignmentAnalyzer analyzer(zone, env);
1704 return analyzer.Analyze(start, end);
1705 }
1706
1627 } // namespace wasm 1707 } // namespace wasm
1628 } // namespace internal 1708 } // namespace internal
1629 } // namespace v8 1709 } // namespace v8
OLDNEW
« no previous file with comments | « src/wasm/ast-decoder.h ('k') | test/unittests/unittests.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698