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

Side by Side Diff: src/wasm/wasm-interpreter.cc

Issue 2671803002: [wasm] Refactor the non-determinism detection in the interpreter. (Closed)
Patch Set: Created 3 years, 10 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
OLDNEW
1 // Copyright 2016 the V8 project authors. All rights reserved. 1 // Copyright 2016 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 <type_traits>
6
5 #include "src/wasm/wasm-interpreter.h" 7 #include "src/wasm/wasm-interpreter.h"
6 8
7 #include "src/utils.h" 9 #include "src/utils.h"
8 #include "src/wasm/decoder.h" 10 #include "src/wasm/decoder.h"
9 #include "src/wasm/function-body-decoder.h" 11 #include "src/wasm/function-body-decoder.h"
10 #include "src/wasm/wasm-external-refs.h" 12 #include "src/wasm/wasm-external-refs.h"
11 #include "src/wasm/wasm-limits.h" 13 #include "src/wasm/wasm-limits.h"
12 #include "src/wasm/wasm-module.h" 14 #include "src/wasm/wasm-module.h"
13 15
14 #include "src/zone/accounting-allocator.h" 16 #include "src/zone/accounting-allocator.h"
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 V(F32Le, float, <=) \ 72 V(F32Le, float, <=) \
71 V(F32Gt, float, >) \ 73 V(F32Gt, float, >) \
72 V(F32Ge, float, >=) \ 74 V(F32Ge, float, >=) \
73 V(F64Add, double, +) \ 75 V(F64Add, double, +) \
74 V(F64Sub, double, -) \ 76 V(F64Sub, double, -) \
75 V(F64Eq, double, ==) \ 77 V(F64Eq, double, ==) \
76 V(F64Ne, double, !=) \ 78 V(F64Ne, double, !=) \
77 V(F64Lt, double, <) \ 79 V(F64Lt, double, <) \
78 V(F64Le, double, <=) \ 80 V(F64Le, double, <=) \
79 V(F64Gt, double, >) \ 81 V(F64Gt, double, >) \
80 V(F64Ge, double, >=) 82 V(F64Ge, double, >=) \
81 83 V(F32Mul, float, *) \
82 #define FOREACH_SIMPLE_BINOP_NAN(V) \ 84 V(F64Mul, double, *) \
83 V(F32Mul, float, *) \ 85 V(F32Div, float, /) \
84 V(F64Mul, double, *) \
85 V(F32Div, float, /) \
86 V(F64Div, double, /) 86 V(F64Div, double, /)
87 87
88 #define FOREACH_OTHER_BINOP(V) \ 88 #define FOREACH_OTHER_BINOP(V) \
89 V(I32DivS, int32_t) \ 89 V(I32DivS, int32_t) \
90 V(I32DivU, uint32_t) \ 90 V(I32DivU, uint32_t) \
91 V(I32RemS, int32_t) \ 91 V(I32RemS, int32_t) \
92 V(I32RemU, uint32_t) \ 92 V(I32RemU, uint32_t) \
93 V(I32Shl, uint32_t) \ 93 V(I32Shl, uint32_t) \
94 V(I32ShrU, uint32_t) \ 94 V(I32ShrU, uint32_t) \
95 V(I32ShrS, int32_t) \ 95 V(I32ShrS, int32_t) \
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 V(F32ReinterpretI32, int32_t) \ 155 V(F32ReinterpretI32, int32_t) \
156 V(F64SConvertI32, int32_t) \ 156 V(F64SConvertI32, int32_t) \
157 V(F64UConvertI32, uint32_t) \ 157 V(F64UConvertI32, uint32_t) \
158 V(F64SConvertI64, int64_t) \ 158 V(F64SConvertI64, int64_t) \
159 V(F64UConvertI64, uint64_t) \ 159 V(F64UConvertI64, uint64_t) \
160 V(F64ConvertF32, float) \ 160 V(F64ConvertF32, float) \
161 V(F64ReinterpretI64, int64_t) \ 161 V(F64ReinterpretI64, int64_t) \
162 V(I32AsmjsSConvertF32, float) \ 162 V(I32AsmjsSConvertF32, float) \
163 V(I32AsmjsUConvertF32, float) \ 163 V(I32AsmjsUConvertF32, float) \
164 V(I32AsmjsSConvertF64, double) \ 164 V(I32AsmjsSConvertF64, double) \
165 V(I32AsmjsUConvertF64, double) 165 V(I32AsmjsUConvertF64, double) \
166 166 V(F32Sqrt, float) \
167 #define FOREACH_OTHER_UNOP_NAN(V) \
168 V(F32Sqrt, float) \
169 V(F64Sqrt, double) 167 V(F64Sqrt, double)
170 168
171 static inline int32_t ExecuteI32DivS(int32_t a, int32_t b, TrapReason* trap) { 169 static inline int32_t ExecuteI32DivS(int32_t a, int32_t b, TrapReason* trap) {
172 if (b == 0) { 170 if (b == 0) {
173 *trap = kTrapDivByZero; 171 *trap = kTrapDivByZero;
174 return 0; 172 return 0;
175 } 173 }
176 if (b == -1 && a == std::numeric_limits<int32_t>::min()) { 174 if (b == -1 && a == std::numeric_limits<int32_t>::min()) {
177 *trap = kTrapDivUnrepresentable; 175 *trap = kTrapDivUnrepresentable;
178 return 0; 176 return 0;
(...skipping 990 matching lines...) Expand 10 before | Expand all | Expand 10 after
1169 // ^ 0 ^ stack_.size() 1167 // ^ 0 ^ stack_.size()
1170 DCHECK_LE(dest, stack_.size()); 1168 DCHECK_LE(dest, stack_.size());
1171 DCHECK_LE(dest + arity, stack_.size()); 1169 DCHECK_LE(dest + arity, stack_.size());
1172 size_t pop_count = stack_.size() - dest - arity; 1170 size_t pop_count = stack_.size() - dest - arity;
1173 for (size_t i = 0; i < arity; i++) { 1171 for (size_t i = 0; i < arity; i++) {
1174 stack_[dest + i] = stack_[dest + pop_count + i]; 1172 stack_[dest + i] = stack_[dest + pop_count + i];
1175 } 1173 }
1176 stack_.resize(stack_.size() - pop_count); 1174 stack_.resize(stack_.size() - pop_count);
1177 } 1175 }
1178 1176
1177 template <typename ctype, typename mtype>
Eric Holk 2017/02/03 03:11:26 I like the template approach way better than prepr
1178 bool ExecuteLoad(Decoder* decoder, InterpreterCode* code, pc_t pc, int& len) {
1179 MemoryAccessOperand operand(decoder, code->at(pc), sizeof(ctype));
1180 uint32_t index = Pop().to<uint32_t>();
1181 size_t effective_mem_size = instance()->mem_size - sizeof(mtype);
1182 if (operand.offset > effective_mem_size ||
1183 index > (effective_mem_size - operand.offset)) {
1184 DoTrap(kTrapMemOutOfBounds, pc);
1185 return false;
1186 }
1187 byte* addr = instance()->mem_start + operand.offset + index;
1188 WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr)));
1189
1190 Push(pc, result);
1191 len = 1 + operand.length;
1192 return true;
1193 }
1194
1195 template <typename ctype, typename mtype>
1196 bool ExecuteStore(Decoder* decoder, InterpreterCode* code, pc_t pc,
1197 int& len) {
1198 MemoryAccessOperand operand(decoder, code->at(pc), sizeof(ctype));
1199 WasmVal val = Pop();
1200
1201 uint32_t index = Pop().to<uint32_t>();
1202 size_t effective_mem_size = instance()->mem_size - sizeof(mtype);
1203 if (operand.offset > effective_mem_size ||
1204 index > (effective_mem_size - operand.offset)) {
1205 DoTrap(kTrapMemOutOfBounds, pc);
1206 return false;
1207 }
1208 byte* addr = instance()->mem_start + operand.offset + index;
1209 WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>()));
1210 len = 1 + operand.length;
1211
1212 if (std::is_same<float, ctype>::value) {
1213 possible_nondeterminism_ |= std::isnan(val.to<float>());
1214 } else if (std::is_same<double, ctype>::value) {
1215 possible_nondeterminism_ |= std::isnan(val.to<double>());
1216 }
1217 return true;
1218 }
1219
1179 void Execute(InterpreterCode* code, pc_t pc, int max) { 1220 void Execute(InterpreterCode* code, pc_t pc, int max) {
1180 Decoder decoder(code->start, code->end); 1221 Decoder decoder(code->start, code->end);
1181 pc_t limit = code->end - code->start; 1222 pc_t limit = code->end - code->start;
1182 while (--max >= 0) { 1223 while (--max >= 0) {
1183 #define PAUSE_IF_BREAK_FLAG(flag) \ 1224 #define PAUSE_IF_BREAK_FLAG(flag) \
1184 if (V8_UNLIKELY(break_flags_ & WasmInterpreter::BreakFlag::flag)) max = 0; 1225 if (V8_UNLIKELY(break_flags_ & WasmInterpreter::BreakFlag::flag)) max = 0;
1185 1226
1186 DCHECK_GT(limit, pc); 1227 DCHECK_GT(limit, pc);
1187 1228
1188 const char* skip = " "; 1229 const char* skip = " ";
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after
1420 *reinterpret_cast<float*>(ptr) = val.to<float>(); 1461 *reinterpret_cast<float*>(ptr) = val.to<float>();
1421 } else if (type == kWasmF64) { 1462 } else if (type == kWasmF64) {
1422 *reinterpret_cast<double*>(ptr) = val.to<double>(); 1463 *reinterpret_cast<double*>(ptr) = val.to<double>();
1423 } else { 1464 } else {
1424 UNREACHABLE(); 1465 UNREACHABLE();
1425 } 1466 }
1426 len = 1 + operand.length; 1467 len = 1 + operand.length;
1427 break; 1468 break;
1428 } 1469 }
1429 1470
1430 #define LOAD_CASE(name, ctype, mtype) \ 1471 #define LOAD_CASE(name, ctype, mtype) \
1431 case kExpr##name: { \ 1472 case kExpr##name: { \
1432 MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \ 1473 if (!ExecuteLoad<ctype, mtype>(&decoder, code, pc, len)) return; \
1433 uint32_t index = Pop().to<uint32_t>(); \ 1474 break; \
1434 size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \
1435 if (operand.offset > effective_mem_size || \
1436 index > (effective_mem_size - operand.offset)) { \
1437 return DoTrap(kTrapMemOutOfBounds, pc); \
1438 } \
1439 byte* addr = instance()->mem_start + operand.offset + index; \
1440 WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr))); \
1441 Push(pc, result); \
1442 len = 1 + operand.length; \
1443 break; \
1444 } 1475 }
1445 1476
1446 LOAD_CASE(I32LoadMem8S, int32_t, int8_t); 1477 LOAD_CASE(I32LoadMem8S, int32_t, int8_t);
1447 LOAD_CASE(I32LoadMem8U, int32_t, uint8_t); 1478 LOAD_CASE(I32LoadMem8U, int32_t, uint8_t);
1448 LOAD_CASE(I32LoadMem16S, int32_t, int16_t); 1479 LOAD_CASE(I32LoadMem16S, int32_t, int16_t);
1449 LOAD_CASE(I32LoadMem16U, int32_t, uint16_t); 1480 LOAD_CASE(I32LoadMem16U, int32_t, uint16_t);
1450 LOAD_CASE(I64LoadMem8S, int64_t, int8_t); 1481 LOAD_CASE(I64LoadMem8S, int64_t, int8_t);
1451 LOAD_CASE(I64LoadMem8U, int64_t, uint8_t); 1482 LOAD_CASE(I64LoadMem8U, int64_t, uint8_t);
1452 LOAD_CASE(I64LoadMem16S, int64_t, int16_t); 1483 LOAD_CASE(I64LoadMem16S, int64_t, int16_t);
1453 LOAD_CASE(I64LoadMem16U, int64_t, uint16_t); 1484 LOAD_CASE(I64LoadMem16U, int64_t, uint16_t);
1454 LOAD_CASE(I64LoadMem32S, int64_t, int32_t); 1485 LOAD_CASE(I64LoadMem32S, int64_t, int32_t);
1455 LOAD_CASE(I64LoadMem32U, int64_t, uint32_t); 1486 LOAD_CASE(I64LoadMem32U, int64_t, uint32_t);
1456 LOAD_CASE(I32LoadMem, int32_t, int32_t); 1487 LOAD_CASE(I32LoadMem, int32_t, int32_t);
1457 LOAD_CASE(I64LoadMem, int64_t, int64_t); 1488 LOAD_CASE(I64LoadMem, int64_t, int64_t);
1458 LOAD_CASE(F32LoadMem, float, float); 1489 LOAD_CASE(F32LoadMem, float, float);
1459 LOAD_CASE(F64LoadMem, double, double); 1490 LOAD_CASE(F64LoadMem, double, double);
1460 #undef LOAD_CASE 1491 #undef LOAD_CASE
1461 1492
1462 #define STORE_CASE(name, ctype, mtype) \ 1493 #define STORE_CASE(name, ctype, mtype) \
1463 case kExpr##name: { \ 1494 case kExpr##name: { \
1464 MemoryAccessOperand operand(&decoder, code->at(pc), sizeof(ctype)); \ 1495 if (!ExecuteStore<ctype, mtype>(&decoder, code, pc, len)) return; \
1465 WasmVal val = Pop(); \ 1496 break; \
1466 uint32_t index = Pop().to<uint32_t>(); \
1467 size_t effective_mem_size = instance()->mem_size - sizeof(mtype); \
1468 if (operand.offset > effective_mem_size || \
1469 index > (effective_mem_size - operand.offset)) { \
1470 return DoTrap(kTrapMemOutOfBounds, pc); \
1471 } \
1472 byte* addr = instance()->mem_start + operand.offset + index; \
1473 WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \
1474 len = 1 + operand.length; \
1475 break; \
1476 } 1497 }
1477 1498
1478 STORE_CASE(I32StoreMem8, int32_t, int8_t); 1499 STORE_CASE(I32StoreMem8, int32_t, int8_t);
1479 STORE_CASE(I32StoreMem16, int32_t, int16_t); 1500 STORE_CASE(I32StoreMem16, int32_t, int16_t);
1480 STORE_CASE(I64StoreMem8, int64_t, int8_t); 1501 STORE_CASE(I64StoreMem8, int64_t, int8_t);
1481 STORE_CASE(I64StoreMem16, int64_t, int16_t); 1502 STORE_CASE(I64StoreMem16, int64_t, int16_t);
1482 STORE_CASE(I64StoreMem32, int64_t, int32_t); 1503 STORE_CASE(I64StoreMem32, int64_t, int32_t);
1483 STORE_CASE(I32StoreMem, int32_t, int32_t); 1504 STORE_CASE(I32StoreMem, int32_t, int32_t);
1484 STORE_CASE(I64StoreMem, int64_t, int64_t); 1505 STORE_CASE(I64StoreMem, int64_t, int64_t);
1485 STORE_CASE(F32StoreMem, float, float); 1506 STORE_CASE(F32StoreMem, float, float);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1541 MemoryIndexOperand operand(&decoder, code->at(pc)); 1562 MemoryIndexOperand operand(&decoder, code->at(pc));
1542 Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size / 1563 Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size /
1543 WasmModule::kPageSize))); 1564 WasmModule::kPageSize)));
1544 len = 1 + operand.length; 1565 len = 1 + operand.length;
1545 break; 1566 break;
1546 } 1567 }
1547 // We need to treat kExprI32ReinterpretF32 and kExprI64ReinterpretF64 1568 // We need to treat kExprI32ReinterpretF32 and kExprI64ReinterpretF64
1548 // specially to guarantee that the quiet bit of a NaN is preserved on 1569 // specially to guarantee that the quiet bit of a NaN is preserved on
1549 // ia32 by the reinterpret casts. 1570 // ia32 by the reinterpret casts.
1550 case kExprI32ReinterpretF32: { 1571 case kExprI32ReinterpretF32: {
1551 WasmVal result(ExecuteI32ReinterpretF32(Pop())); 1572 WasmVal val = Pop();
1573 WasmVal result(ExecuteI32ReinterpretF32(val));
1552 Push(pc, result); 1574 Push(pc, result);
1575 possible_nondeterminism_ |= std::isnan(val.to<float>());
1553 break; 1576 break;
1554 } 1577 }
1555 case kExprI64ReinterpretF64: { 1578 case kExprI64ReinterpretF64: {
1556 WasmVal result(ExecuteI64ReinterpretF64(Pop())); 1579 WasmVal val = Pop();
1580 WasmVal result(ExecuteI64ReinterpretF64(val));
1557 Push(pc, result); 1581 Push(pc, result);
1582 possible_nondeterminism_ |= std::isnan(val.to<double>());
1558 break; 1583 break;
1559 } 1584 }
1560 #define EXECUTE_SIMPLE_BINOP(name, ctype, op) \ 1585 #define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
1561 case kExpr##name: { \ 1586 case kExpr##name: { \
1562 WasmVal rval = Pop(); \ 1587 WasmVal rval = Pop(); \
1563 WasmVal lval = Pop(); \ 1588 WasmVal lval = Pop(); \
1564 WasmVal result(lval.to<ctype>() op rval.to<ctype>()); \ 1589 WasmVal result(lval.to<ctype>() op rval.to<ctype>()); \
1565 Push(pc, result); \ 1590 Push(pc, result); \
1566 break; \ 1591 break; \
1567 } 1592 }
1568 FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP) 1593 FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP)
1569 #undef EXECUTE_SIMPLE_BINOP 1594 #undef EXECUTE_SIMPLE_BINOP
1570 1595
1571 #define EXECUTE_SIMPLE_BINOP_NAN(name, ctype, op) \
1572 case kExpr##name: { \
1573 WasmVal rval = Pop(); \
1574 WasmVal lval = Pop(); \
1575 ctype result = lval.to<ctype>() op rval.to<ctype>(); \
1576 possible_nondeterminism_ |= std::isnan(result); \
1577 WasmVal result_val(result); \
1578 Push(pc, result_val); \
1579 break; \
1580 }
1581 FOREACH_SIMPLE_BINOP_NAN(EXECUTE_SIMPLE_BINOP_NAN)
1582 #undef EXECUTE_SIMPLE_BINOP_NAN
1583
1584 #define EXECUTE_OTHER_BINOP(name, ctype) \ 1596 #define EXECUTE_OTHER_BINOP(name, ctype) \
1585 case kExpr##name: { \ 1597 case kExpr##name: { \
1586 TrapReason trap = kTrapCount; \ 1598 TrapReason trap = kTrapCount; \
1587 volatile ctype rval = Pop().to<ctype>(); \ 1599 volatile ctype rval = Pop().to<ctype>(); \
1588 volatile ctype lval = Pop().to<ctype>(); \ 1600 volatile ctype lval = Pop().to<ctype>(); \
1589 WasmVal result(Execute##name(lval, rval, &trap)); \ 1601 WasmVal result(Execute##name(lval, rval, &trap)); \
1590 if (trap != kTrapCount) return DoTrap(trap, pc); \ 1602 if (trap != kTrapCount) return DoTrap(trap, pc); \
1591 Push(pc, result); \ 1603 Push(pc, result); \
1592 break; \ 1604 break; \
1593 } 1605 }
1594 FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP) 1606 FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP)
1595 #undef EXECUTE_OTHER_BINOP 1607 #undef EXECUTE_OTHER_BINOP
1596 1608
1597 #define EXECUTE_OTHER_UNOP(name, ctype) \ 1609 #define EXECUTE_OTHER_UNOP(name, ctype) \
1598 case kExpr##name: { \ 1610 case kExpr##name: { \
1599 TrapReason trap = kTrapCount; \ 1611 TrapReason trap = kTrapCount; \
1600 volatile ctype val = Pop().to<ctype>(); \ 1612 volatile ctype val = Pop().to<ctype>(); \
1601 WasmVal result(Execute##name(val, &trap)); \ 1613 WasmVal result(Execute##name(val, &trap)); \
1602 if (trap != kTrapCount) return DoTrap(trap, pc); \ 1614 if (trap != kTrapCount) return DoTrap(trap, pc); \
1603 Push(pc, result); \ 1615 Push(pc, result); \
1604 break; \ 1616 break; \
1605 } 1617 }
1606 FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP) 1618 FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP)
1607 #undef EXECUTE_OTHER_UNOP 1619 #undef EXECUTE_OTHER_UNOP
1608 1620
1609 #define EXECUTE_OTHER_UNOP_NAN(name, ctype) \
1610 case kExpr##name: { \
1611 TrapReason trap = kTrapCount; \
1612 volatile ctype val = Pop().to<ctype>(); \
1613 ctype result = Execute##name(val, &trap); \
1614 possible_nondeterminism_ |= std::isnan(result); \
1615 WasmVal result_val(result); \
1616 if (trap != kTrapCount) return DoTrap(trap, pc); \
1617 Push(pc, result_val); \
1618 break; \
1619 }
1620 FOREACH_OTHER_UNOP_NAN(EXECUTE_OTHER_UNOP_NAN)
1621 #undef EXECUTE_OTHER_UNOP_NAN
1622
1623 default: 1621 default:
1624 V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s", 1622 V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
1625 code->start[pc], OpcodeName(code->start[pc])); 1623 code->start[pc], OpcodeName(code->start[pc]));
1626 UNREACHABLE(); 1624 UNREACHABLE();
1627 } 1625 }
1628 1626
1629 pc += len; 1627 pc += len;
1630 if (pc == limit) { 1628 if (pc == limit) {
1631 // Fell off end of code; do an implicit return. 1629 // Fell off end of code; do an implicit return.
1632 TRACE("@%-3zu: ImplicitReturn\n", pc); 1630 TRACE("@%-3zu: ImplicitReturn\n", pc);
(...skipping 289 matching lines...) Expand 10 before | Expand all | Expand 10 after
1922 return none; 1920 return none;
1923 } 1921 }
1924 1922
1925 void InterpretedFrame::SetLocalVal(int index, WasmVal val) { UNIMPLEMENTED(); } 1923 void InterpretedFrame::SetLocalVal(int index, WasmVal val) { UNIMPLEMENTED(); }
1926 1924
1927 void InterpretedFrame::SetExprVal(int pc, WasmVal val) { UNIMPLEMENTED(); } 1925 void InterpretedFrame::SetExprVal(int pc, WasmVal val) { UNIMPLEMENTED(); }
1928 1926
1929 } // namespace wasm 1927 } // namespace wasm
1930 } // namespace internal 1928 } // namespace internal
1931 } // namespace v8 1929 } // namespace v8
OLDNEW
« no previous file with comments | « no previous file | test/cctest/wasm/test-run-wasm-interpreter.cc » ('j') | test/cctest/wasm/test-run-wasm-interpreter.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698