Line data Source code
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
3 : // found in the LICENSE file.
4 :
5 : #include <type_traits>
6 :
7 : #include "src/wasm/wasm-interpreter.h"
8 :
9 : #include "src/assembler-inl.h"
10 : #include "src/compiler/wasm-compiler.h"
11 : #include "src/conversions.h"
12 : #include "src/identity-map.h"
13 : #include "src/objects-inl.h"
14 : #include "src/utils.h"
15 : #include "src/wasm/decoder.h"
16 : #include "src/wasm/function-body-decoder-impl.h"
17 : #include "src/wasm/function-body-decoder.h"
18 : #include "src/wasm/memory-tracing.h"
19 : #include "src/wasm/wasm-external-refs.h"
20 : #include "src/wasm/wasm-limits.h"
21 : #include "src/wasm/wasm-module.h"
22 : #include "src/wasm/wasm-objects-inl.h"
23 :
24 : #include "src/zone/accounting-allocator.h"
25 : #include "src/zone/zone-containers.h"
26 :
27 : namespace v8 {
28 : namespace internal {
29 : namespace wasm {
30 :
31 : #if DEBUG
32 : #define TRACE(...) \
33 : do { \
34 : if (FLAG_trace_wasm_interpreter) PrintF(__VA_ARGS__); \
35 : } while (false)
36 : #else
37 : #define TRACE(...)
38 : #endif
39 :
40 : #define FOREACH_INTERNAL_OPCODE(V) V(Breakpoint, 0xFF)
41 :
42 : #define WASM_CTYPES(V) \
43 : V(I32, int32_t) V(I64, int64_t) V(F32, float) V(F64, double)
44 :
45 : #define FOREACH_SIMPLE_BINOP(V) \
46 : V(I32Add, uint32_t, +) \
47 : V(I32Sub, uint32_t, -) \
48 : V(I32Mul, uint32_t, *) \
49 : V(I32And, uint32_t, &) \
50 : V(I32Ior, uint32_t, |) \
51 : V(I32Xor, uint32_t, ^) \
52 : V(I32Eq, uint32_t, ==) \
53 : V(I32Ne, uint32_t, !=) \
54 : V(I32LtU, uint32_t, <) \
55 : V(I32LeU, uint32_t, <=) \
56 : V(I32GtU, uint32_t, >) \
57 : V(I32GeU, uint32_t, >=) \
58 : V(I32LtS, int32_t, <) \
59 : V(I32LeS, int32_t, <=) \
60 : V(I32GtS, int32_t, >) \
61 : V(I32GeS, int32_t, >=) \
62 : V(I64Add, uint64_t, +) \
63 : V(I64Sub, uint64_t, -) \
64 : V(I64Mul, uint64_t, *) \
65 : V(I64And, uint64_t, &) \
66 : V(I64Ior, uint64_t, |) \
67 : V(I64Xor, uint64_t, ^) \
68 : V(I64Eq, uint64_t, ==) \
69 : V(I64Ne, uint64_t, !=) \
70 : V(I64LtU, uint64_t, <) \
71 : V(I64LeU, uint64_t, <=) \
72 : V(I64GtU, uint64_t, >) \
73 : V(I64GeU, uint64_t, >=) \
74 : V(I64LtS, int64_t, <) \
75 : V(I64LeS, int64_t, <=) \
76 : V(I64GtS, int64_t, >) \
77 : V(I64GeS, int64_t, >=) \
78 : V(F32Add, float, +) \
79 : V(F32Sub, float, -) \
80 : V(F32Eq, float, ==) \
81 : V(F32Ne, float, !=) \
82 : V(F32Lt, float, <) \
83 : V(F32Le, float, <=) \
84 : V(F32Gt, float, >) \
85 : V(F32Ge, float, >=) \
86 : V(F64Add, double, +) \
87 : V(F64Sub, double, -) \
88 : V(F64Eq, double, ==) \
89 : V(F64Ne, double, !=) \
90 : V(F64Lt, double, <) \
91 : V(F64Le, double, <=) \
92 : V(F64Gt, double, >) \
93 : V(F64Ge, double, >=) \
94 : V(F32Mul, float, *) \
95 : V(F64Mul, double, *) \
96 : V(F32Div, float, /) \
97 : V(F64Div, double, /)
98 :
99 : #define FOREACH_OTHER_BINOP(V) \
100 : V(I32DivS, int32_t) \
101 : V(I32DivU, uint32_t) \
102 : V(I32RemS, int32_t) \
103 : V(I32RemU, uint32_t) \
104 : V(I32Shl, uint32_t) \
105 : V(I32ShrU, uint32_t) \
106 : V(I32ShrS, int32_t) \
107 : V(I64DivS, int64_t) \
108 : V(I64DivU, uint64_t) \
109 : V(I64RemS, int64_t) \
110 : V(I64RemU, uint64_t) \
111 : V(I64Shl, uint64_t) \
112 : V(I64ShrU, uint64_t) \
113 : V(I64ShrS, int64_t) \
114 : V(I32Ror, int32_t) \
115 : V(I32Rol, int32_t) \
116 : V(I64Ror, int64_t) \
117 : V(I64Rol, int64_t) \
118 : V(F32Min, float) \
119 : V(F32Max, float) \
120 : V(F64Min, double) \
121 : V(F64Max, double) \
122 : V(I32AsmjsDivS, int32_t) \
123 : V(I32AsmjsDivU, uint32_t) \
124 : V(I32AsmjsRemS, int32_t) \
125 : V(I32AsmjsRemU, uint32_t)
126 :
127 : #define FOREACH_OTHER_UNOP(V) \
128 : V(I32Clz, uint32_t) \
129 : V(I32Ctz, uint32_t) \
130 : V(I32Popcnt, uint32_t) \
131 : V(I32Eqz, uint32_t) \
132 : V(I64Clz, uint64_t) \
133 : V(I64Ctz, uint64_t) \
134 : V(I64Popcnt, uint64_t) \
135 : V(I64Eqz, uint64_t) \
136 : V(F32Abs, float) \
137 : V(F32Neg, float) \
138 : V(F32Ceil, float) \
139 : V(F32Floor, float) \
140 : V(F32Trunc, float) \
141 : V(F32NearestInt, float) \
142 : V(F64Abs, double) \
143 : V(F64Neg, double) \
144 : V(F64Ceil, double) \
145 : V(F64Floor, double) \
146 : V(F64Trunc, double) \
147 : V(F64NearestInt, double) \
148 : V(I32SConvertF32, float) \
149 : V(I32SConvertF64, double) \
150 : V(I32UConvertF32, float) \
151 : V(I32UConvertF64, double) \
152 : V(I32ConvertI64, int64_t) \
153 : V(I64SConvertF32, float) \
154 : V(I64SConvertF64, double) \
155 : V(I64UConvertF32, float) \
156 : V(I64UConvertF64, double) \
157 : V(I64SConvertI32, int32_t) \
158 : V(I64UConvertI32, uint32_t) \
159 : V(F32SConvertI32, int32_t) \
160 : V(F32UConvertI32, uint32_t) \
161 : V(F32SConvertI64, int64_t) \
162 : V(F32UConvertI64, uint64_t) \
163 : V(F32ConvertF64, double) \
164 : V(F32ReinterpretI32, int32_t) \
165 : V(F64SConvertI32, int32_t) \
166 : V(F64UConvertI32, uint32_t) \
167 : V(F64SConvertI64, int64_t) \
168 : V(F64UConvertI64, uint64_t) \
169 : V(F64ConvertF32, float) \
170 : V(F64ReinterpretI64, int64_t) \
171 : V(I32AsmjsSConvertF32, float) \
172 : V(I32AsmjsUConvertF32, float) \
173 : V(I32AsmjsSConvertF64, double) \
174 : V(I32AsmjsUConvertF64, double) \
175 : V(F32Sqrt, float) \
176 : V(F64Sqrt, double)
177 :
178 : namespace {
179 :
180 : // CachedInstanceInfo encapsulates globals and memory buffer runtime information
181 : // for a wasm instance. The interpreter caches that information when
182 : // constructed, copying it from the {WasmInstanceObject}. It expects it be
183 : // notified on changes to it, e.g. {GrowMemory}. We cache it because interpreter
184 : // perf is sensitive to accesses to this information.
185 : //
186 : // TODO(wasm): other runtime information, such as indirect function table, or
187 : // code table (incl. imports) is currently handled separately. Consider
188 : // unifying, if possible, with {ModuleEnv}.
189 :
190 : struct CachedInstanceInfo {
191 : CachedInstanceInfo(byte* globals, byte* mem, uint32_t size)
192 : : globals_start(globals), mem_start(mem), mem_size(size) {}
193 : // We do not expect the location of the globals buffer to
194 : // change for an instance.
195 : byte* const globals_start = nullptr;
196 : // The memory buffer may change because of GrowMemory
197 : byte* mem_start = nullptr;
198 : uint32_t mem_size = 0;
199 : };
200 :
201 : inline int32_t ExecuteI32DivS(int32_t a, int32_t b, TrapReason* trap) {
202 972 : if (b == 0) {
203 : *trap = kTrapDivByZero;
204 : return 0;
205 : }
206 846 : if (b == -1 && a == std::numeric_limits<int32_t>::min()) {
207 : *trap = kTrapDivUnrepresentable;
208 : return 0;
209 : }
210 840 : return a / b;
211 : }
212 :
213 : inline uint32_t ExecuteI32DivU(uint32_t a, uint32_t b, TrapReason* trap) {
214 42 : if (b == 0) {
215 : *trap = kTrapDivByZero;
216 : return 0;
217 : }
218 24 : return a / b;
219 : }
220 :
221 : inline int32_t ExecuteI32RemS(int32_t a, int32_t b, TrapReason* trap) {
222 84 : if (b == 0) {
223 : *trap = kTrapRemByZero;
224 : return 0;
225 : }
226 48 : if (b == -1) return 0;
227 36 : return a % b;
228 : }
229 :
230 : inline uint32_t ExecuteI32RemU(uint32_t a, uint32_t b, TrapReason* trap) {
231 42 : if (b == 0) {
232 : *trap = kTrapRemByZero;
233 : return 0;
234 : }
235 24 : return a % b;
236 : }
237 :
238 : inline uint32_t ExecuteI32Shl(uint32_t a, uint32_t b, TrapReason* trap) {
239 20196 : return a << (b & 0x1f);
240 : }
241 :
242 : inline uint32_t ExecuteI32ShrU(uint32_t a, uint32_t b, TrapReason* trap) {
243 20196 : return a >> (b & 0x1f);
244 : }
245 :
246 : inline int32_t ExecuteI32ShrS(int32_t a, int32_t b, TrapReason* trap) {
247 20196 : return a >> (b & 0x1f);
248 : }
249 :
250 : inline int64_t ExecuteI64DivS(int64_t a, int64_t b, TrapReason* trap) {
251 40314 : if (b == 0) {
252 : *trap = kTrapDivByZero;
253 : return 0;
254 : }
255 39228 : if (b == -1 && a == std::numeric_limits<int64_t>::min()) {
256 : *trap = kTrapDivUnrepresentable;
257 : return 0;
258 : }
259 39222 : return a / b;
260 : }
261 :
262 : inline uint64_t ExecuteI64DivU(uint64_t a, uint64_t b, TrapReason* trap) {
263 39402 : if (b == 0) {
264 : *trap = kTrapDivByZero;
265 : return 0;
266 : }
267 38412 : return a / b;
268 : }
269 :
270 : inline int64_t ExecuteI64RemS(int64_t a, int64_t b, TrapReason* trap) {
271 39408 : if (b == 0) {
272 : *trap = kTrapRemByZero;
273 : return 0;
274 : }
275 38418 : if (b == -1) return 0;
276 37926 : return a % b;
277 : }
278 :
279 : inline uint64_t ExecuteI64RemU(uint64_t a, uint64_t b, TrapReason* trap) {
280 39402 : if (b == 0) {
281 : *trap = kTrapRemByZero;
282 : return 0;
283 : }
284 38412 : return a % b;
285 : }
286 :
287 : inline uint64_t ExecuteI64Shl(uint64_t a, uint64_t b, TrapReason* trap) {
288 80688 : return a << (b & 0x3f);
289 : }
290 :
291 : inline uint64_t ExecuteI64ShrU(uint64_t a, uint64_t b, TrapReason* trap) {
292 80688 : return a >> (b & 0x3f);
293 : }
294 :
295 : inline int64_t ExecuteI64ShrS(int64_t a, int64_t b, TrapReason* trap) {
296 80688 : return a >> (b & 0x3f);
297 : }
298 :
299 : inline uint32_t ExecuteI32Ror(uint32_t a, uint32_t b, TrapReason* trap) {
300 24 : uint32_t shift = (b & 0x1f);
301 24 : return (a >> shift) | (a << (32 - shift));
302 : }
303 :
304 : inline uint32_t ExecuteI32Rol(uint32_t a, uint32_t b, TrapReason* trap) {
305 24 : uint32_t shift = (b & 0x1f);
306 24 : return (a << shift) | (a >> (32 - shift));
307 : }
308 :
309 : inline uint64_t ExecuteI64Ror(uint64_t a, uint64_t b, TrapReason* trap) {
310 39390 : uint32_t shift = (b & 0x3f);
311 39390 : return (a >> shift) | (a << (64 - shift));
312 : }
313 :
314 : inline uint64_t ExecuteI64Rol(uint64_t a, uint64_t b, TrapReason* trap) {
315 39390 : uint32_t shift = (b & 0x3f);
316 39390 : return (a << shift) | (a >> (64 - shift));
317 : }
318 :
319 : inline float ExecuteF32Min(float a, float b, TrapReason* trap) {
320 79350 : return JSMin(a, b);
321 : }
322 :
323 : inline float ExecuteF32Max(float a, float b, TrapReason* trap) {
324 79350 : return JSMax(a, b);
325 : }
326 :
327 : inline float ExecuteF32CopySign(float a, float b, TrapReason* trap) {
328 79362 : return copysignf(a, b);
329 : }
330 :
331 : inline double ExecuteF64Min(double a, double b, TrapReason* trap) {
332 14406 : return JSMin(a, b);
333 : }
334 :
335 : inline double ExecuteF64Max(double a, double b, TrapReason* trap) {
336 14406 : return JSMax(a, b);
337 : }
338 :
339 : inline double ExecuteF64CopySign(double a, double b, TrapReason* trap) {
340 14418 : return copysign(a, b);
341 : }
342 :
343 : inline int32_t ExecuteI32AsmjsDivS(int32_t a, int32_t b, TrapReason* trap) {
344 3510 : if (b == 0) return 0;
345 3144 : if (b == -1 && a == std::numeric_limits<int32_t>::min()) {
346 : return std::numeric_limits<int32_t>::min();
347 : }
348 3132 : return a / b;
349 : }
350 :
351 : inline uint32_t ExecuteI32AsmjsDivU(uint32_t a, uint32_t b, TrapReason* trap) {
352 30 : if (b == 0) return 0;
353 12 : return a / b;
354 : }
355 :
356 : inline int32_t ExecuteI32AsmjsRemS(int32_t a, int32_t b, TrapReason* trap) {
357 3510 : if (b == 0) return 0;
358 3144 : if (b == -1) return 0;
359 2790 : return a % b;
360 : }
361 :
362 : inline uint32_t ExecuteI32AsmjsRemU(uint32_t a, uint32_t b, TrapReason* trap) {
363 30 : if (b == 0) return 0;
364 12 : return a % b;
365 : }
366 :
367 : inline int32_t ExecuteI32AsmjsSConvertF32(float a, TrapReason* trap) {
368 690 : return DoubleToInt32(a);
369 : }
370 :
371 : inline uint32_t ExecuteI32AsmjsUConvertF32(float a, TrapReason* trap) {
372 690 : return DoubleToUint32(a);
373 : }
374 :
375 : inline int32_t ExecuteI32AsmjsSConvertF64(double a, TrapReason* trap) {
376 294 : return DoubleToInt32(a);
377 : }
378 :
379 : inline uint32_t ExecuteI32AsmjsUConvertF64(double a, TrapReason* trap) {
380 : return DoubleToUint32(a);
381 : }
382 :
383 : int32_t ExecuteI32Clz(uint32_t val, TrapReason* trap) {
384 396 : return base::bits::CountLeadingZeros32(val);
385 : }
386 :
387 : uint32_t ExecuteI32Ctz(uint32_t val, TrapReason* trap) {
388 : return base::bits::CountTrailingZeros32(val);
389 : }
390 :
391 : uint32_t ExecuteI32Popcnt(uint32_t val, TrapReason* trap) {
392 60 : return word32_popcnt_wrapper(&val);
393 : }
394 :
395 : inline uint32_t ExecuteI32Eqz(uint32_t val, TrapReason* trap) {
396 744 : return val == 0 ? 1 : 0;
397 : }
398 :
399 : int64_t ExecuteI64Clz(uint64_t val, TrapReason* trap) {
400 390 : return base::bits::CountLeadingZeros64(val);
401 : }
402 :
403 : inline uint64_t ExecuteI64Ctz(uint64_t val, TrapReason* trap) {
404 390 : return base::bits::CountTrailingZeros64(val);
405 : }
406 :
407 : inline int64_t ExecuteI64Popcnt(uint64_t val, TrapReason* trap) {
408 60 : return word64_popcnt_wrapper(&val);
409 : }
410 :
411 : inline int32_t ExecuteI64Eqz(uint64_t val, TrapReason* trap) {
412 498 : return val == 0 ? 1 : 0;
413 : }
414 :
415 : inline float ExecuteF32Abs(float a, TrapReason* trap) {
416 24 : return bit_cast<float>(bit_cast<uint32_t>(a) & 0x7fffffff);
417 : }
418 :
419 : inline float ExecuteF32Neg(float a, TrapReason* trap) {
420 702 : return bit_cast<float>(bit_cast<uint32_t>(a) ^ 0x80000000);
421 : }
422 :
423 690 : inline float ExecuteF32Ceil(float a, TrapReason* trap) { return ceilf(a); }
424 :
425 690 : inline float ExecuteF32Floor(float a, TrapReason* trap) { return floorf(a); }
426 :
427 690 : inline float ExecuteF32Trunc(float a, TrapReason* trap) { return truncf(a); }
428 :
429 : inline float ExecuteF32NearestInt(float a, TrapReason* trap) {
430 690 : return nearbyintf(a);
431 : }
432 :
433 : inline float ExecuteF32Sqrt(float a, TrapReason* trap) {
434 12 : float result = sqrtf(a);
435 : return result;
436 : }
437 :
438 : inline double ExecuteF64Abs(double a, TrapReason* trap) {
439 24 : return bit_cast<double>(bit_cast<uint64_t>(a) & 0x7fffffffffffffff);
440 : }
441 :
442 : inline double ExecuteF64Neg(double a, TrapReason* trap) {
443 306 : return bit_cast<double>(bit_cast<uint64_t>(a) ^ 0x8000000000000000);
444 : }
445 :
446 294 : inline double ExecuteF64Ceil(double a, TrapReason* trap) { return ceil(a); }
447 :
448 294 : inline double ExecuteF64Floor(double a, TrapReason* trap) { return floor(a); }
449 :
450 294 : inline double ExecuteF64Trunc(double a, TrapReason* trap) { return trunc(a); }
451 :
452 : inline double ExecuteF64NearestInt(double a, TrapReason* trap) {
453 294 : return nearbyint(a);
454 : }
455 :
456 12 : inline double ExecuteF64Sqrt(double a, TrapReason* trap) { return sqrt(a); }
457 :
458 : int32_t ExecuteI32SConvertF32(float a, TrapReason* trap) {
459 : // The upper bound is (INT32_MAX + 1), which is the lowest float-representable
460 : // number above INT32_MAX which cannot be represented as int32.
461 : float upper_bound = 2147483648.0f;
462 : // We use INT32_MIN as a lower bound because (INT32_MIN - 1) is not
463 : // representable as float, and no number between (INT32_MIN - 1) and INT32_MIN
464 : // is.
465 : float lower_bound = static_cast<float>(INT32_MIN);
466 792 : if (a < upper_bound && a >= lower_bound) {
467 528 : return static_cast<int32_t>(a);
468 : }
469 : *trap = kTrapFloatUnrepresentable;
470 : return 0;
471 : }
472 :
473 : int32_t ExecuteI32SConvertF64(double a, TrapReason* trap) {
474 : // The upper bound is (INT32_MAX + 1), which is the lowest double-
475 : // representable number above INT32_MAX which cannot be represented as int32.
476 : double upper_bound = 2147483648.0;
477 : // The lower bound is (INT32_MIN - 1), which is the greatest double-
478 : // representable number below INT32_MIN which cannot be represented as int32.
479 : double lower_bound = -2147483649.0;
480 396 : if (a < upper_bound && a > lower_bound) {
481 300 : return static_cast<int32_t>(a);
482 : }
483 : *trap = kTrapFloatUnrepresentable;
484 : return 0;
485 : }
486 :
487 : uint32_t ExecuteI32UConvertF32(float a, TrapReason* trap) {
488 : // The upper bound is (UINT32_MAX + 1), which is the lowest
489 : // float-representable number above UINT32_MAX which cannot be represented as
490 : // uint32.
491 : double upper_bound = 4294967296.0f;
492 : double lower_bound = -1.0f;
493 690 : if (a < upper_bound && a > lower_bound) {
494 276 : return static_cast<uint32_t>(a);
495 : }
496 : *trap = kTrapFloatUnrepresentable;
497 : return 0;
498 : }
499 :
500 : uint32_t ExecuteI32UConvertF64(double a, TrapReason* trap) {
501 : // The upper bound is (UINT32_MAX + 1), which is the lowest
502 : // double-representable number above UINT32_MAX which cannot be represented as
503 : // uint32.
504 : double upper_bound = 4294967296.0;
505 : double lower_bound = -1.0;
506 294 : if (a < upper_bound && a > lower_bound) {
507 162 : return static_cast<uint32_t>(a);
508 : }
509 : *trap = kTrapFloatUnrepresentable;
510 : return 0;
511 : }
512 :
513 : inline uint32_t ExecuteI32ConvertI64(int64_t a, TrapReason* trap) {
514 236892 : return static_cast<uint32_t>(a & 0xFFFFFFFF);
515 : }
516 :
517 : int64_t ExecuteI64SConvertF32(float a, TrapReason* trap) {
518 : int64_t output;
519 1380 : if (!float32_to_int64_wrapper(&a, &output)) {
520 : *trap = kTrapFloatUnrepresentable;
521 : }
522 1380 : return output;
523 : }
524 :
525 : int64_t ExecuteI64SConvertF64(double a, TrapReason* trap) {
526 : int64_t output;
527 1084 : if (!float64_to_int64_wrapper(&a, &output)) {
528 : *trap = kTrapFloatUnrepresentable;
529 : }
530 1084 : return output;
531 : }
532 :
533 : uint64_t ExecuteI64UConvertF32(float a, TrapReason* trap) {
534 : uint64_t output;
535 1380 : if (!float32_to_uint64_wrapper(&a, &output)) {
536 : *trap = kTrapFloatUnrepresentable;
537 : }
538 1380 : return output;
539 : }
540 :
541 : uint64_t ExecuteI64UConvertF64(double a, TrapReason* trap) {
542 : uint64_t output;
543 588 : if (!float64_to_uint64_wrapper(&a, &output)) {
544 : *trap = kTrapFloatUnrepresentable;
545 : }
546 588 : return output;
547 : }
548 :
549 : inline int64_t ExecuteI64SConvertI32(int32_t a, TrapReason* trap) {
550 372 : return static_cast<int64_t>(a);
551 : }
552 :
553 : inline int64_t ExecuteI64UConvertI32(uint32_t a, TrapReason* trap) {
554 348 : return static_cast<uint64_t>(a);
555 : }
556 :
557 : inline float ExecuteF32SConvertI32(int32_t a, TrapReason* trap) {
558 30 : return static_cast<float>(a);
559 : }
560 :
561 : inline float ExecuteF32UConvertI32(uint32_t a, TrapReason* trap) {
562 10 : return static_cast<float>(a);
563 : }
564 :
565 : inline float ExecuteF32SConvertI64(int64_t a, TrapReason* trap) {
566 : float output;
567 486 : int64_to_float32_wrapper(&a, &output);
568 486 : return output;
569 : }
570 :
571 : inline float ExecuteF32UConvertI64(uint64_t a, TrapReason* trap) {
572 : float output;
573 456 : uint64_to_float32_wrapper(&a, &output);
574 456 : return output;
575 : }
576 :
577 : inline float ExecuteF32ConvertF64(double a, TrapReason* trap) {
578 10 : return static_cast<float>(a);
579 : }
580 :
581 : inline float ExecuteF32ReinterpretI32(int32_t a, TrapReason* trap) {
582 : return bit_cast<float>(a);
583 : }
584 :
585 : inline double ExecuteF64SConvertI32(int32_t a, TrapReason* trap) {
586 1404 : return static_cast<double>(a);
587 : }
588 :
589 : inline double ExecuteF64UConvertI32(uint32_t a, TrapReason* trap) {
590 10 : return static_cast<double>(a);
591 : }
592 :
593 : inline double ExecuteF64SConvertI64(int64_t a, TrapReason* trap) {
594 : double output;
595 23048 : int64_to_float64_wrapper(&a, &output);
596 23048 : return output;
597 : }
598 :
599 : inline double ExecuteF64UConvertI64(uint64_t a, TrapReason* trap) {
600 : double output;
601 450 : uint64_to_float64_wrapper(&a, &output);
602 450 : return output;
603 : }
604 :
605 : inline double ExecuteF64ConvertF32(float a, TrapReason* trap) {
606 2090 : return static_cast<double>(a);
607 : }
608 :
609 : inline double ExecuteF64ReinterpretI64(int64_t a, TrapReason* trap) {
610 : return bit_cast<double>(a);
611 : }
612 :
613 : inline int32_t ExecuteI32ReinterpretF32(WasmValue a) {
614 : return a.to_unchecked<int32_t>();
615 : }
616 :
617 : inline int64_t ExecuteI64ReinterpretF64(WasmValue a) {
618 : return a.to_unchecked<int64_t>();
619 : }
620 :
621 : enum InternalOpcode {
622 : #define DECL_INTERNAL_ENUM(name, value) kInternal##name = value,
623 : FOREACH_INTERNAL_OPCODE(DECL_INTERNAL_ENUM)
624 : #undef DECL_INTERNAL_ENUM
625 : };
626 :
627 : const char* OpcodeName(uint32_t val) {
628 0 : switch (val) {
629 : #define DECL_INTERNAL_CASE(name, value) \
630 : case kInternal##name: \
631 : return "Internal" #name;
632 : FOREACH_INTERNAL_OPCODE(DECL_INTERNAL_CASE)
633 : #undef DECL_INTERNAL_CASE
634 : }
635 0 : return WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(val));
636 : }
637 :
638 : // Unwrap a wasm to js wrapper, return the callable heap object.
639 : // If the wrapper would throw a TypeError, return a null handle.
640 6220 : Handle<HeapObject> UnwrapWasmToJSWrapper(Isolate* isolate,
641 : Handle<Code> js_wrapper) {
642 : DCHECK_EQ(Code::WASM_TO_JS_FUNCTION, js_wrapper->kind());
643 : Handle<FixedArray> deopt_data(js_wrapper->deoptimization_data(), isolate);
644 : DCHECK_EQ(2, deopt_data->length());
645 : intptr_t js_imports_table_loc = static_cast<intptr_t>(
646 : HeapNumber::cast(deopt_data->get(0))->value_as_bits());
647 : Handle<FixedArray> js_imports_table(
648 6220 : reinterpret_cast<FixedArray**>(js_imports_table_loc));
649 6220 : int index = 0;
650 6220 : CHECK(deopt_data->get(1)->ToInt32(&index));
651 : DCHECK_GT(js_imports_table->length(), index);
652 6220 : Handle<Object> obj(js_imports_table->get(index), isolate);
653 6220 : if (obj->IsCallable()) {
654 : return Handle<HeapObject>::cast(obj);
655 : } else {
656 : // If we did not find a callable object, this is an illegal JS import and
657 : // obj must be undefined.
658 : DCHECK(obj->IsUndefined(isolate));
659 : return Handle<HeapObject>::null();
660 : }
661 : }
662 :
663 : class SideTable;
664 :
665 : // Code and metadata needed to execute a function.
666 59186 : struct InterpreterCode {
667 : const WasmFunction* function; // wasm function
668 : BodyLocalDecls locals; // local declarations
669 : const byte* orig_start; // start of original code
670 : const byte* orig_end; // end of original code
671 : byte* start; // start of (maybe altered) code
672 : byte* end; // end of (maybe altered) code
673 : SideTable* side_table; // precomputed side table for control flow.
674 :
675 9226304 : const byte* at(pc_t pc) { return start + pc; }
676 : };
677 :
678 : // A helper class to compute the control transfers for each bytecode offset.
679 : // Control transfers allow Br, BrIf, BrTable, If, Else, and End bytecodes to
680 : // be directly executed without the need to dynamically track blocks.
681 : class SideTable : public ZoneObject {
682 : public:
683 : ControlTransferMap map_;
684 : uint32_t max_stack_height_;
685 :
686 40546 : SideTable(Zone* zone, const WasmModule* module, InterpreterCode* code)
687 20273 : : map_(zone), max_stack_height_(0) {
688 : // Create a zone for all temporary objects.
689 20273 : Zone control_transfer_zone(zone->allocator(), ZONE_NAME);
690 :
691 : // Represents a control flow label.
692 : class CLabel : public ZoneObject {
693 : explicit CLabel(Zone* zone, uint32_t target_stack_height, uint32_t arity)
694 : : target(nullptr),
695 : target_stack_height(target_stack_height),
696 : arity(arity),
697 53185 : refs(zone) {}
698 :
699 : public:
700 : struct Ref {
701 : const byte* from_pc;
702 : const uint32_t stack_height;
703 : };
704 : const byte* target;
705 : uint32_t target_stack_height;
706 : // Arity when branching to this label.
707 : const uint32_t arity;
708 : ZoneVector<Ref> refs;
709 :
710 53185 : static CLabel* New(Zone* zone, uint32_t stack_height, uint32_t arity) {
711 53185 : return new (zone) CLabel(zone, stack_height, arity);
712 : }
713 :
714 : // Bind this label to the given PC.
715 : void Bind(const byte* pc) {
716 : DCHECK_NULL(target);
717 53185 : target = pc;
718 : }
719 :
720 : // Reference this label from the given location.
721 : void Ref(const byte* from_pc, uint32_t stack_height) {
722 : // Target being bound before a reference means this is a loop.
723 : DCHECK_IMPLIES(target, *target == kExprLoop);
724 52646 : refs.push_back({from_pc, stack_height});
725 : }
726 :
727 53185 : void Finish(ControlTransferMap* map, const byte* start) {
728 : DCHECK_NOT_NULL(target);
729 132693 : for (auto ref : refs) {
730 26323 : size_t offset = static_cast<size_t>(ref.from_pc - start);
731 26323 : auto pcdiff = static_cast<pcdiff_t>(target - ref.from_pc);
732 : DCHECK_GE(ref.stack_height, target_stack_height);
733 : spdiff_t spdiff =
734 26323 : static_cast<spdiff_t>(ref.stack_height - target_stack_height);
735 : TRACE("control transfer @%zu: Δpc %d, stack %u->%u = -%u\n", offset,
736 : pcdiff, ref.stack_height, target_stack_height, spdiff);
737 26323 : ControlTransferEntry& entry = (*map)[offset];
738 26323 : entry.pc_diff = pcdiff;
739 26323 : entry.sp_diff = spdiff;
740 26323 : entry.target_arity = arity;
741 : }
742 53185 : }
743 : };
744 :
745 : // An entry in the control stack.
746 : struct Control {
747 : const byte* pc;
748 : CLabel* end_label;
749 : CLabel* else_label;
750 : // Arity (number of values on the stack) when exiting this control
751 : // structure via |end|.
752 : uint32_t exit_arity;
753 : // Track whether this block was already left, i.e. all further
754 : // instructions are unreachable.
755 : bool unreachable = false;
756 :
757 : Control(const byte* pc, CLabel* end_label, CLabel* else_label,
758 : uint32_t exit_arity)
759 : : pc(pc),
760 : end_label(end_label),
761 : else_label(else_label),
762 52833 : exit_arity(exit_arity) {}
763 : Control(const byte* pc, CLabel* end_label, uint32_t exit_arity)
764 : : Control(pc, end_label, nullptr, exit_arity) {}
765 :
766 52833 : void Finish(ControlTransferMap* map, const byte* start) {
767 52833 : end_label->Finish(map, start);
768 52833 : if (else_label) else_label->Finish(map, start);
769 52833 : }
770 : };
771 :
772 : // Compute the ControlTransfer map.
773 : // This algorithm maintains a stack of control constructs similar to the
774 : // AST decoder. The {control_stack} allows matching {br,br_if,br_table}
775 : // bytecodes with their target, as well as determining whether the current
776 : // bytecodes are within the true or false block of an else.
777 : ZoneVector<Control> control_stack(&control_transfer_zone);
778 : uint32_t stack_height = 0;
779 : uint32_t func_arity =
780 20273 : static_cast<uint32_t>(code->function->sig->return_count());
781 : CLabel* func_label =
782 20273 : CLabel::New(&control_transfer_zone, stack_height, func_arity);
783 20273 : control_stack.emplace_back(code->orig_start, func_label, func_arity);
784 : auto control_parent = [&]() -> Control& {
785 : DCHECK_LE(2, control_stack.size());
786 65724 : return control_stack[control_stack.size() - 2];
787 20273 : };
788 : auto copy_unreachable = [&] {
789 32711 : control_stack.back().unreachable = control_parent().unreachable;
790 : };
791 253857 : for (BytecodeIterator i(code->orig_start, code->orig_end, &code->locals);
792 213311 : i.has_next(); i.next()) {
793 : WasmOpcode opcode = i.current();
794 213311 : bool unreachable = control_stack.back().unreachable;
795 213311 : if (unreachable) {
796 : TRACE("@%u: %s (is unreachable)\n", i.pc_offset(),
797 : WasmOpcodes::OpcodeName(opcode));
798 : } else {
799 : auto stack_effect =
800 179323 : StackEffect(module, code->function->sig, i.pc(), i.end());
801 : TRACE("@%u: %s (sp %d - %d + %d)\n", i.pc_offset(),
802 : WasmOpcodes::OpcodeName(opcode), stack_height, stack_effect.first,
803 : stack_effect.second);
804 : DCHECK_GE(stack_height, stack_effect.first);
805 : DCHECK_GE(kMaxUInt32, static_cast<uint64_t>(stack_height) -
806 : stack_effect.first + stack_effect.second);
807 179323 : stack_height = stack_height - stack_effect.first + stack_effect.second;
808 179323 : if (stack_height > max_stack_height_) max_stack_height_ = stack_height;
809 : }
810 213311 : switch (opcode) {
811 : case kExprBlock:
812 : case kExprLoop: {
813 32208 : bool is_loop = opcode == kExprLoop;
814 32208 : BlockTypeOperand<Decoder::kNoValidate> operand(&i, i.pc());
815 32208 : if (operand.type == kWasmVar) {
816 30 : operand.sig = module->signatures[operand.sig_index];
817 : }
818 : TRACE("control @%u: %s, arity %d->%d\n", i.pc_offset(),
819 : is_loop ? "Loop" : "Block",
820 : operand.in_arity(), operand.out_arity());
821 : CLabel* label = CLabel::New(&control_transfer_zone, stack_height,
822 : is_loop ? operand.in_arity()
823 64416 : : operand.out_arity());
824 64416 : control_stack.emplace_back(i.pc(), label, operand.out_arity());
825 : copy_unreachable();
826 32208 : if (is_loop) label->Bind(i.pc());
827 : break;
828 : }
829 : case kExprIf: {
830 352 : BlockTypeOperand<Decoder::kNoValidate> operand(&i, i.pc());
831 352 : if (operand.type == kWasmVar) {
832 12 : operand.sig = module->signatures[operand.sig_index];
833 : }
834 : TRACE("control @%u: If, arity %d->%d\n", i.pc_offset(),
835 : operand.in_arity(), operand.out_arity());
836 : CLabel* end_label =
837 : CLabel::New(&control_transfer_zone, stack_height,
838 352 : operand.out_arity());
839 : CLabel* else_label =
840 352 : CLabel::New(&control_transfer_zone, stack_height, 0);
841 352 : control_stack.emplace_back(i.pc(), end_label, else_label,
842 1056 : operand.out_arity());
843 : copy_unreachable();
844 352 : if (!unreachable) else_label->Ref(i.pc(), stack_height);
845 : break;
846 : }
847 : case kExprElse: {
848 : Control* c = &control_stack.back();
849 : copy_unreachable();
850 : TRACE("control @%u: Else\n", i.pc_offset());
851 151 : if (!control_parent().unreachable) {
852 144 : c->end_label->Ref(i.pc(), stack_height);
853 : }
854 : DCHECK_NOT_NULL(c->else_label);
855 151 : c->else_label->Bind(i.pc() + 1);
856 151 : c->else_label->Finish(&map_, code->orig_start);
857 151 : c->else_label = nullptr;
858 : DCHECK_GE(stack_height, c->end_label->target_stack_height);
859 151 : stack_height = c->end_label->target_stack_height;
860 151 : break;
861 : }
862 : case kExprEnd: {
863 52833 : Control* c = &control_stack.back();
864 : TRACE("control @%u: End\n", i.pc_offset());
865 : // Only loops have bound labels.
866 : DCHECK_IMPLIES(c->end_label->target, *c->pc == kExprLoop);
867 52833 : if (!c->end_label->target) {
868 52613 : if (c->else_label) c->else_label->Bind(i.pc());
869 52613 : c->end_label->Bind(i.pc() + 1);
870 : }
871 105666 : c->Finish(&map_, code->orig_start);
872 : DCHECK_GE(stack_height, c->end_label->target_stack_height);
873 52833 : stack_height = c->end_label->target_stack_height + c->exit_arity;
874 : control_stack.pop_back();
875 : break;
876 : }
877 : case kExprBr: {
878 603 : BreakDepthOperand<Decoder::kNoValidate> operand(&i, i.pc());
879 : TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth);
880 1206 : Control* c = &control_stack[control_stack.size() - operand.depth - 1];
881 603 : if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
882 : break;
883 : }
884 : case kExprBrIf: {
885 139 : BreakDepthOperand<Decoder::kNoValidate> operand(&i, i.pc());
886 : TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth);
887 278 : Control* c = &control_stack[control_stack.size() - operand.depth - 1];
888 139 : if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
889 : break;
890 : }
891 : case kExprBrTable: {
892 6305 : BranchTableOperand<Decoder::kNoValidate> operand(&i, i.pc());
893 : BranchTableIterator<Decoder::kNoValidate> iterator(&i, operand);
894 : TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(),
895 : operand.table_count);
896 6305 : if (!unreachable) {
897 31411 : while (iterator.has_next()) {
898 25106 : uint32_t j = iterator.cur_index();
899 25106 : uint32_t target = iterator.next();
900 50212 : Control* c = &control_stack[control_stack.size() - target - 1];
901 25106 : c->end_label->Ref(i.pc() + j, stack_height);
902 : }
903 : }
904 : break;
905 : }
906 : default:
907 : break;
908 : }
909 213311 : if (WasmOpcodes::IsUnconditionalJump(opcode)) {
910 32151 : control_stack.back().unreachable = true;
911 : }
912 : }
913 : DCHECK_EQ(0, control_stack.size());
914 20273 : DCHECK_EQ(func_arity, stack_height);
915 20273 : }
916 :
917 : ControlTransferEntry& Lookup(pc_t from) {
918 : auto result = map_.find(from);
919 : DCHECK(result != map_.end());
920 : return result->second;
921 : }
922 : };
923 :
924 : struct ExternalCallResult {
925 : enum Type {
926 : // The function should be executed inside this interpreter.
927 : INTERNAL,
928 : // For indirect calls: Table or function does not exist.
929 : INVALID_FUNC,
930 : // For indirect calls: Signature does not match expected signature.
931 : SIGNATURE_MISMATCH,
932 : // The function was executed and returned normally.
933 : EXTERNAL_RETURNED,
934 : // The function was executed, threw an exception, and the stack was unwound.
935 : EXTERNAL_UNWOUND
936 : };
937 : Type type;
938 : // If type is INTERNAL, this field holds the function to call internally.
939 : InterpreterCode* interpreter_code;
940 :
941 : ExternalCallResult(Type type) : type(type) { // NOLINT
942 : DCHECK_NE(INTERNAL, type);
943 : }
944 : ExternalCallResult(Type type, InterpreterCode* code)
945 : : type(type), interpreter_code(code) {
946 : DCHECK_EQ(INTERNAL, type);
947 : }
948 : };
949 :
950 : // The main storage for interpreter code. It maps {WasmFunction} to the
951 : // metadata needed to execute each function.
952 : class CodeMap {
953 : Zone* zone_;
954 : const WasmModule* module_;
955 : ZoneVector<InterpreterCode> interpreter_code_;
956 : // This handle is set and reset by the SetInstanceObject() /
957 : // ClearInstanceObject() method, which is used by the HeapObjectsScope.
958 : Handle<WasmInstanceObject> instance_;
959 :
960 : public:
961 18637 : CodeMap(Isolate* isolate, const WasmModule* module,
962 : const uint8_t* module_start, Zone* zone)
963 18637 : : zone_(zone), module_(module), interpreter_code_(zone) {
964 37274 : if (module == nullptr) return;
965 37274 : interpreter_code_.reserve(module->functions.size());
966 39278 : for (const WasmFunction& function : module->functions) {
967 2004 : if (function.imported) {
968 : DCHECK(!function.code.is_set());
969 800 : AddFunction(&function, nullptr, nullptr);
970 : } else {
971 : AddFunction(&function, module_start + function.code.offset(),
972 1204 : module_start + function.code.end_offset());
973 : }
974 : }
975 : }
976 :
977 : void SetInstanceObject(Handle<WasmInstanceObject> instance) {
978 : DCHECK(instance_.is_null());
979 2558780 : instance_ = instance;
980 : }
981 :
982 2558780 : void ClearInstanceObject() { instance_ = Handle<WasmInstanceObject>::null(); }
983 :
984 : const WasmModule* module() const { return module_; }
985 : bool has_instance() const { return !instance_.is_null(); }
986 : WasmInstanceObject* instance() const {
987 : DCHECK(has_instance());
988 : return *instance_;
989 : }
990 64 : MaybeHandle<WasmInstanceObject> maybe_instance() const {
991 : return has_instance() ? handle(instance())
992 128 : : MaybeHandle<WasmInstanceObject>();
993 : }
994 :
995 6260 : Code* GetImportedFunction(uint32_t function_index) {
996 : DCHECK(has_instance());
997 : DCHECK_GT(module_->num_imported_functions, function_index);
998 : FixedArray* code_table = instance()->compiled_module()->ptr_to_code_table();
999 12520 : return Code::cast(code_table->get(static_cast<int>(function_index)));
1000 : }
1001 :
1002 : InterpreterCode* GetCode(const WasmFunction* function) {
1003 : InterpreterCode* code = GetCode(function->func_index);
1004 : DCHECK_EQ(function, code->function);
1005 : return code;
1006 : }
1007 :
1008 : InterpreterCode* GetCode(uint32_t function_index) {
1009 : DCHECK_LT(function_index, interpreter_code_.size());
1010 6746248 : return Preprocess(&interpreter_code_[function_index]);
1011 : }
1012 :
1013 156 : InterpreterCode* GetIndirectCode(uint32_t table_index, uint32_t entry_index) {
1014 312 : if (table_index >= module_->function_tables.size()) return nullptr;
1015 : const WasmIndirectFunctionTable* table =
1016 : &module_->function_tables[table_index];
1017 312 : if (entry_index >= table->values.size()) return nullptr;
1018 102 : uint32_t index = table->values[entry_index];
1019 204 : if (index >= interpreter_code_.size()) return nullptr;
1020 102 : return GetCode(index);
1021 : }
1022 :
1023 3392370 : InterpreterCode* Preprocess(InterpreterCode* code) {
1024 : DCHECK_EQ(code->function->imported, code->start == nullptr);
1025 3392370 : if (!code->side_table && code->start) {
1026 : // Compute the control targets map and the local declarations.
1027 40486 : code->side_table = new (zone_) SideTable(zone_, module_, code);
1028 : }
1029 3392370 : return code;
1030 : }
1031 :
1032 24151 : void AddFunction(const WasmFunction* function, const byte* code_start,
1033 : const byte* code_end) {
1034 : InterpreterCode code = {
1035 : function, BodyLocalDecls(zone_), code_start,
1036 : code_end, const_cast<byte*>(code_start), const_cast<byte*>(code_end),
1037 48302 : nullptr};
1038 :
1039 : DCHECK_EQ(interpreter_code_.size(), function->func_index);
1040 24151 : interpreter_code_.push_back(code);
1041 24151 : }
1042 :
1043 19195 : void SetFunctionCode(const WasmFunction* function, const byte* start,
1044 : const byte* end) {
1045 : DCHECK_LT(function->func_index, interpreter_code_.size());
1046 19195 : InterpreterCode* code = &interpreter_code_[function->func_index];
1047 : DCHECK_EQ(function, code->function);
1048 19195 : code->orig_start = start;
1049 19195 : code->orig_end = end;
1050 19195 : code->start = const_cast<byte*>(start);
1051 19195 : code->end = const_cast<byte*>(end);
1052 19195 : code->side_table = nullptr;
1053 19195 : Preprocess(code);
1054 19195 : }
1055 : };
1056 :
1057 6140 : Handle<Object> WasmValueToNumber(Factory* factory, WasmValue val,
1058 : wasm::ValueType type) {
1059 6140 : switch (type) {
1060 : case kWasmI32:
1061 6130 : return factory->NewNumberFromInt(val.to<int32_t>());
1062 : case kWasmI64:
1063 : // wasm->js and js->wasm is illegal for i64 type.
1064 0 : UNREACHABLE();
1065 : case kWasmF32:
1066 0 : return factory->NewNumber(val.to<float>());
1067 : case kWasmF64:
1068 10 : return factory->NewNumber(val.to<double>());
1069 : default:
1070 : // TODO(wasm): Implement simd.
1071 0 : UNIMPLEMENTED();
1072 : return Handle<Object>::null();
1073 : }
1074 : }
1075 :
1076 : // Convert JS value to WebAssembly, spec here:
1077 : // https://github.com/WebAssembly/design/blob/master/JS.md#towebassemblyvalue
1078 6040 : WasmValue ToWebAssemblyValue(Isolate* isolate, Handle<Object> value,
1079 : wasm::ValueType type) {
1080 6040 : switch (type) {
1081 : case kWasmI32: {
1082 : MaybeHandle<Object> maybe_i32 = Object::ToInt32(isolate, value);
1083 : // TODO(clemensh): Handle failure here (unwind).
1084 : int32_t value;
1085 6030 : CHECK(maybe_i32.ToHandleChecked()->ToInt32(&value));
1086 6030 : return WasmValue(value);
1087 : }
1088 : case kWasmI64:
1089 : // If the signature contains i64, a type error was thrown before.
1090 0 : UNREACHABLE();
1091 : case kWasmF32: {
1092 10 : MaybeHandle<Object> maybe_number = Object::ToNumber(value);
1093 : // TODO(clemensh): Handle failure here (unwind).
1094 : return WasmValue(
1095 10 : static_cast<float>(maybe_number.ToHandleChecked()->Number()));
1096 : }
1097 : case kWasmF64: {
1098 0 : MaybeHandle<Object> maybe_number = Object::ToNumber(value);
1099 : // TODO(clemensh): Handle failure here (unwind).
1100 0 : return WasmValue(maybe_number.ToHandleChecked()->Number());
1101 : }
1102 : default:
1103 : // TODO(wasm): Handle simd.
1104 0 : UNIMPLEMENTED();
1105 : return WasmValue();
1106 : }
1107 : }
1108 :
1109 : // Responsible for executing code directly.
1110 0 : class ThreadImpl {
1111 : struct Activation {
1112 : uint32_t fp;
1113 : sp_t sp;
1114 45156 : Activation(uint32_t fp, sp_t sp) : fp(fp), sp(sp) {}
1115 : };
1116 :
1117 : public:
1118 : ThreadImpl(Zone* zone, CodeMap* codemap, WasmContext* wasm_context)
1119 : : codemap_(codemap),
1120 : wasm_context_(wasm_context),
1121 : zone_(zone),
1122 : frames_(zone),
1123 37274 : activations_(zone) {}
1124 :
1125 : //==========================================================================
1126 : // Implementation of public interface for WasmInterpreter::Thread.
1127 : //==========================================================================
1128 :
1129 : WasmInterpreter::State state() { return state_; }
1130 :
1131 2563038 : void InitFrame(const WasmFunction* function, WasmValue* args) {
1132 : DCHECK_EQ(current_activation().fp, frames_.size());
1133 : InterpreterCode* code = codemap()->GetCode(function);
1134 2563038 : size_t num_params = function->sig->parameter_count();
1135 2563038 : EnsureStackSpace(num_params);
1136 : Push(args, num_params);
1137 2563038 : PushFrame(code);
1138 2563038 : }
1139 :
1140 0 : WasmInterpreter::State Run(int num_steps = -1) {
1141 : DCHECK(state_ == WasmInterpreter::STOPPED ||
1142 : state_ == WasmInterpreter::PAUSED);
1143 : DCHECK(num_steps == -1 || num_steps > 0);
1144 : if (num_steps == -1) {
1145 : TRACE(" => Run()\n");
1146 : } else if (num_steps == 1) {
1147 : TRACE(" => Step()\n");
1148 : } else {
1149 : TRACE(" => Run(%d)\n", num_steps);
1150 : }
1151 2570688 : state_ = WasmInterpreter::RUNNING;
1152 2570688 : Execute(frames_.back().code, frames_.back().pc, num_steps);
1153 : // If state_ is STOPPED, the current activation must be fully unwound.
1154 : DCHECK_IMPLIES(state_ == WasmInterpreter::STOPPED,
1155 : current_activation().fp == frames_.size());
1156 2570688 : return state_;
1157 : }
1158 :
1159 0 : void Pause() { UNIMPLEMENTED(); }
1160 :
1161 : void Reset() {
1162 : TRACE("----- RESET -----\n");
1163 2517882 : sp_ = stack_start_;
1164 : frames_.clear();
1165 2517882 : state_ = WasmInterpreter::STOPPED;
1166 2517882 : trap_reason_ = kTrapCount;
1167 2517882 : possible_nondeterminism_ = false;
1168 : }
1169 :
1170 : int GetFrameCount() {
1171 : DCHECK_GE(kMaxInt, frames_.size());
1172 9488 : return static_cast<int>(frames_.size());
1173 : }
1174 :
1175 5103388 : WasmValue GetReturnValue(uint32_t index) {
1176 2551694 : if (state_ == WasmInterpreter::TRAPPED) return WasmValue(0xdeadbeef);
1177 : DCHECK_EQ(WasmInterpreter::FINISHED, state_);
1178 : Activation act = current_activation();
1179 : // Current activation must be finished.
1180 : DCHECK_EQ(act.fp, frames_.size());
1181 2551694 : return GetStackValue(act.sp + index);
1182 : }
1183 :
1184 : WasmValue GetStackValue(sp_t index) {
1185 : DCHECK_GT(StackHeight(), index);
1186 7998358 : return stack_start_[index];
1187 : }
1188 :
1189 : void SetStackValue(sp_t index, WasmValue value) {
1190 : DCHECK_GT(StackHeight(), index);
1191 15778 : stack_start_[index] = value;
1192 : }
1193 :
1194 : TrapReason GetTrapReason() { return trap_reason_; }
1195 :
1196 : pc_t GetBreakpointPc() { return break_pc_; }
1197 :
1198 : bool PossibleNondeterminism() { return possible_nondeterminism_; }
1199 :
1200 : uint64_t NumInterpretedCalls() { return num_interpreted_calls_; }
1201 :
1202 26 : void AddBreakFlags(uint8_t flags) { break_flags_ |= flags; }
1203 :
1204 0 : void ClearBreakFlags() { break_flags_ = WasmInterpreter::BreakFlag::None; }
1205 :
1206 : uint32_t NumActivations() {
1207 72 : return static_cast<uint32_t>(activations_.size());
1208 : }
1209 :
1210 90312 : uint32_t StartActivation() {
1211 : TRACE("----- START ACTIVATION %zu -----\n", activations_.size());
1212 : // If you use activations, use them consistently:
1213 : DCHECK_IMPLIES(activations_.empty(), frames_.empty());
1214 : DCHECK_IMPLIES(activations_.empty(), StackHeight() == 0);
1215 90312 : uint32_t activation_id = static_cast<uint32_t>(activations_.size());
1216 45156 : activations_.emplace_back(static_cast<uint32_t>(frames_.size()),
1217 90312 : StackHeight());
1218 45156 : state_ = WasmInterpreter::STOPPED;
1219 45156 : return activation_id;
1220 : }
1221 :
1222 : void FinishActivation(uint32_t id) {
1223 : TRACE("----- FINISH ACTIVATION %zu -----\n", activations_.size() - 1);
1224 : DCHECK_LT(0, activations_.size());
1225 : DCHECK_EQ(activations_.size() - 1, id);
1226 : // Stack height must match the start of this activation (otherwise unwind
1227 : // first).
1228 : DCHECK_EQ(activations_.back().fp, frames_.size());
1229 : DCHECK_LE(activations_.back().sp, StackHeight());
1230 90312 : sp_ = stack_start_ + activations_.back().sp;
1231 : activations_.pop_back();
1232 : }
1233 :
1234 : uint32_t ActivationFrameBase(uint32_t id) {
1235 : DCHECK_GT(activations_.size(), id);
1236 8200 : return activations_[id].fp;
1237 : }
1238 :
1239 : // Handle a thrown exception. Returns whether the exception was handled inside
1240 : // the current activation. Unwinds the interpreted stack accordingly.
1241 222 : WasmInterpreter::Thread::ExceptionHandlingResult HandleException(
1242 : Isolate* isolate) {
1243 : DCHECK(isolate->has_pending_exception());
1244 : // TODO(wasm): Add wasm exception handling (would return true).
1245 : USE(isolate->pending_exception());
1246 : TRACE("----- UNWIND -----\n");
1247 : DCHECK_LT(0, activations_.size());
1248 : Activation& act = activations_.back();
1249 : DCHECK_LE(act.fp, frames_.size());
1250 222 : frames_.resize(act.fp);
1251 : DCHECK_LE(act.sp, StackHeight());
1252 222 : sp_ = stack_start_ + act.sp;
1253 222 : state_ = WasmInterpreter::STOPPED;
1254 222 : return WasmInterpreter::Thread::UNWOUND;
1255 : }
1256 :
1257 : private:
1258 : // Entries on the stack of functions being evaluated.
1259 : struct Frame {
1260 : InterpreterCode* code;
1261 : pc_t pc;
1262 : sp_t sp;
1263 :
1264 : // Limit of parameters.
1265 : sp_t plimit() { return sp + code->function->sig->parameter_count(); }
1266 : // Limit of locals.
1267 : sp_t llimit() { return plimit() + code->locals.type_list.size(); }
1268 : };
1269 :
1270 : struct Block {
1271 : pc_t pc;
1272 : sp_t sp;
1273 : size_t fp;
1274 : unsigned arity;
1275 : };
1276 :
1277 : friend class InterpretedFrameImpl;
1278 :
1279 : CodeMap* codemap_;
1280 : WasmContext* wasm_context_;
1281 : Zone* zone_;
1282 : WasmValue* stack_start_ = nullptr; // Start of allocated stack space.
1283 : WasmValue* stack_limit_ = nullptr; // End of allocated stack space.
1284 : WasmValue* sp_ = nullptr; // Current stack pointer.
1285 : ZoneVector<Frame> frames_;
1286 : WasmInterpreter::State state_ = WasmInterpreter::STOPPED;
1287 : pc_t break_pc_ = kInvalidPc;
1288 : TrapReason trap_reason_ = kTrapCount;
1289 : bool possible_nondeterminism_ = false;
1290 : uint8_t break_flags_ = 0; // a combination of WasmInterpreter::BreakFlag
1291 : uint64_t num_interpreted_calls_ = 0;
1292 : // Store the stack height of each activation (for unwind and frame
1293 : // inspection).
1294 : ZoneVector<Activation> activations_;
1295 :
1296 : CodeMap* codemap() const { return codemap_; }
1297 650 : const WasmModule* module() const { return codemap_->module(); }
1298 :
1299 : void DoTrap(TrapReason trap, pc_t pc) {
1300 10975 : state_ = WasmInterpreter::TRAPPED;
1301 10975 : trap_reason_ = trap;
1302 : CommitPc(pc);
1303 : }
1304 :
1305 : // Push a frame with arguments already on the stack.
1306 6729418 : void PushFrame(InterpreterCode* code) {
1307 : DCHECK_NOT_NULL(code);
1308 : DCHECK_NOT_NULL(code->side_table);
1309 : EnsureStackSpace(code->side_table->max_stack_height_ +
1310 6729418 : code->locals.type_list.size());
1311 :
1312 3364709 : ++num_interpreted_calls_;
1313 3364709 : size_t arity = code->function->sig->parameter_count();
1314 : // The parameters will overlap the arguments already on the stack.
1315 : DCHECK_GE(StackHeight(), arity);
1316 10094127 : frames_.push_back({code, 0, StackHeight() - arity});
1317 3364709 : frames_.back().pc = InitLocals(code);
1318 : TRACE(" => PushFrame #%zu (#%u @%zu)\n", frames_.size() - 1,
1319 : code->function->func_index, frames_.back().pc);
1320 3364709 : }
1321 :
1322 3364709 : pc_t InitLocals(InterpreterCode* code) {
1323 6736196 : for (auto p : code->locals.type_list) {
1324 : WasmValue val;
1325 6778 : switch (p) {
1326 : #define CASE_TYPE(wasm, ctype) \
1327 : case kWasm##wasm: \
1328 : val = WasmValue(static_cast<ctype>(0)); \
1329 : break;
1330 3485 : WASM_CTYPES(CASE_TYPE)
1331 : #undef CASE_TYPE
1332 : default:
1333 0 : UNREACHABLE();
1334 : break;
1335 : }
1336 : Push(val);
1337 : }
1338 3364709 : return code->locals.encoded_size;
1339 : }
1340 :
1341 : void CommitPc(pc_t pc) {
1342 : DCHECK(!frames_.empty());
1343 24898 : frames_.back().pc = pc;
1344 : }
1345 :
1346 : bool SkipBreakpoint(InterpreterCode* code, pc_t pc) {
1347 8570 : if (pc == break_pc_) {
1348 : // Skip the previously hit breakpoint when resuming.
1349 4285 : break_pc_ = kInvalidPc;
1350 : return true;
1351 : }
1352 : return false;
1353 : }
1354 :
1355 : int LookupTargetDelta(InterpreterCode* code, pc_t pc) {
1356 24593 : return static_cast<int>(code->side_table->Lookup(pc).pc_diff);
1357 : }
1358 :
1359 381308 : int DoBreak(InterpreterCode* code, pc_t pc, size_t depth) {
1360 381308 : ControlTransferEntry& control_transfer_entry = code->side_table->Lookup(pc);
1361 381308 : DoStackTransfer(sp_ - control_transfer_entry.sp_diff,
1362 762616 : control_transfer_entry.target_arity);
1363 381308 : return control_transfer_entry.pc_diff;
1364 : }
1365 :
1366 292522 : pc_t ReturnPc(Decoder* decoder, InterpreterCode* code, pc_t pc) {
1367 146261 : switch (code->orig_start[pc]) {
1368 : case kExprCallFunction: {
1369 : CallFunctionOperand<Decoder::kNoValidate> operand(decoder,
1370 : code->at(pc));
1371 146155 : return pc + 1 + operand.length;
1372 : }
1373 : case kExprCallIndirect: {
1374 : CallIndirectOperand<Decoder::kNoValidate> operand(decoder,
1375 106 : code->at(pc));
1376 106 : return pc + 1 + operand.length;
1377 : }
1378 : default:
1379 0 : UNREACHABLE();
1380 : }
1381 : }
1382 :
1383 2698201 : bool DoReturn(Decoder* decoder, InterpreterCode** code, pc_t* pc, pc_t* limit,
1384 : size_t arity) {
1385 : DCHECK_GT(frames_.size(), 0);
1386 8094603 : WasmValue* sp_dest = stack_start_ + frames_.back().sp;
1387 : frames_.pop_back();
1388 2698201 : if (frames_.size() == current_activation().fp) {
1389 : // A return from the last frame terminates the execution.
1390 2551940 : state_ = WasmInterpreter::FINISHED;
1391 2551940 : DoStackTransfer(sp_dest, arity);
1392 : TRACE(" => finish\n");
1393 2551940 : return false;
1394 : } else {
1395 : // Return to caller frame.
1396 : Frame* top = &frames_.back();
1397 146261 : *code = top->code;
1398 146261 : decoder->Reset((*code)->start, (*code)->end);
1399 146261 : *pc = ReturnPc(decoder, *code, top->pc);
1400 146261 : *limit = top->code->end - top->code->start;
1401 : TRACE(" => Return to #%zu (#%u @%zu)\n", frames_.size() - 1,
1402 : (*code)->function->func_index, *pc);
1403 146261 : DoStackTransfer(sp_dest, arity);
1404 146261 : return true;
1405 : }
1406 : }
1407 :
1408 : // Returns true if the call was successful, false if the stack check failed
1409 : // and the current activation was fully unwound.
1410 801671 : bool DoCall(Decoder* decoder, InterpreterCode* target, pc_t* pc,
1411 : pc_t* limit) WARN_UNUSED_RESULT {
1412 801671 : frames_.back().pc = *pc;
1413 801671 : PushFrame(target);
1414 801671 : if (!DoStackCheck()) return false;
1415 801661 : *pc = frames_.back().pc;
1416 801661 : *limit = target->end - target->start;
1417 801661 : decoder->Reset(target->start, target->end);
1418 801661 : return true;
1419 : }
1420 :
1421 : // Copies {arity} values on the top of the stack down the stack to {dest},
1422 : // dropping the values in-between.
1423 3079509 : void DoStackTransfer(WasmValue* dest, size_t arity) {
1424 : // before: |---------------| pop_count | arity |
1425 : // ^ 0 ^ dest ^ sp_
1426 : //
1427 : // after: |---------------| arity |
1428 : // ^ 0 ^ sp_
1429 : DCHECK_LE(dest, sp_);
1430 : DCHECK_LE(dest + arity, sp_);
1431 3079509 : if (arity) memcpy(dest, sp_ - arity, arity * sizeof(*sp_));
1432 3079509 : sp_ = dest + arity;
1433 3079509 : }
1434 :
1435 : template <typename mtype>
1436 : inline bool BoundsCheck(uint32_t mem_size, uint32_t offset, uint32_t index) {
1437 : return sizeof(mtype) <= mem_size && offset <= mem_size - sizeof(mtype) &&
1438 121830 : index <= mem_size - sizeof(mtype) - offset;
1439 : }
1440 :
1441 : template <typename ctype, typename mtype>
1442 97380 : bool ExecuteLoad(Decoder* decoder, InterpreterCode* code, pc_t pc, int& len,
1443 : MachineRepresentation rep) {
1444 : MemoryAccessOperand<Decoder::kNoValidate> operand(decoder, code->at(pc),
1445 97380 : sizeof(ctype));
1446 97380 : uint32_t index = Pop().to<uint32_t>();
1447 194760 : if (!BoundsCheck<mtype>(wasm_context_->mem_size, operand.offset, index)) {
1448 : DoTrap(kTrapMemOutOfBounds, pc);
1449 2501 : return false;
1450 : }
1451 94879 : byte* addr = wasm_context_->mem_start + operand.offset + index;
1452 1512 : WasmValue result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr)));
1453 :
1454 : Push(result);
1455 94879 : len = 1 + operand.length;
1456 :
1457 94879 : if (FLAG_wasm_trace_memory) {
1458 42 : tracing::TraceMemoryOperation(
1459 : tracing::kWasmInterpreted, false, rep, operand.offset + index,
1460 : code->function->func_index, static_cast<int>(pc),
1461 42 : wasm_context_->mem_start);
1462 : }
1463 :
1464 : return true;
1465 : }
1466 :
1467 : template <typename ctype, typename mtype>
1468 22548 : bool ExecuteStore(Decoder* decoder, InterpreterCode* code, pc_t pc, int& len,
1469 : MachineRepresentation rep) {
1470 : MemoryAccessOperand<Decoder::kNoValidate> operand(decoder, code->at(pc),
1471 22548 : sizeof(ctype));
1472 22548 : WasmValue val = Pop();
1473 :
1474 22548 : uint32_t index = Pop().to<uint32_t>();
1475 45096 : if (!BoundsCheck<mtype>(wasm_context_->mem_size, operand.offset, index)) {
1476 : DoTrap(kTrapMemOutOfBounds, pc);
1477 1620 : return false;
1478 : }
1479 20928 : byte* addr = wasm_context_->mem_start + operand.offset + index;
1480 70 : WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>()));
1481 20928 : len = 1 + operand.length;
1482 :
1483 : if (std::is_same<float, ctype>::value) {
1484 1234 : possible_nondeterminism_ |= std::isnan(val.to<float>());
1485 : } else if (std::is_same<double, ctype>::value) {
1486 16168 : possible_nondeterminism_ |= std::isnan(val.to<double>());
1487 : }
1488 :
1489 20928 : if (FLAG_wasm_trace_memory) {
1490 12 : tracing::TraceMemoryOperation(
1491 : tracing::kWasmInterpreted, true, rep, operand.offset + index,
1492 : code->function->func_index, static_cast<int>(pc),
1493 12 : wasm_context_->mem_start);
1494 : }
1495 :
1496 : return true;
1497 : }
1498 :
1499 : // Check if our control stack (frames_) exceeds the limit. Trigger stack
1500 : // overflow if it does, and unwinding the current frame.
1501 : // Returns true if execution can continue, false if the current activation was
1502 : // fully unwound.
1503 : // Do call this function immediately *after* pushing a new frame. The pc of
1504 : // the top frame will be reset to 0 if the stack check fails.
1505 801691 : bool DoStackCheck() WARN_UNUSED_RESULT {
1506 : // Sum up the size of all dynamically growing structures.
1507 1603342 : if (V8_LIKELY(frames_.size() <= kV8MaxWasmInterpretedStackSize)) {
1508 : return true;
1509 : }
1510 10 : if (!codemap()->has_instance()) {
1511 : // In test mode: Just abort.
1512 0 : FATAL("wasm interpreter: stack overflow");
1513 : }
1514 : // The pc of the top frame is initialized to the first instruction. We reset
1515 : // it to 0 here such that we report the same position as in compiled code.
1516 10 : frames_.back().pc = 0;
1517 : Isolate* isolate = codemap()->instance()->GetIsolate();
1518 : HandleScope handle_scope(isolate);
1519 10 : isolate->StackOverflow();
1520 10 : return HandleException(isolate) == WasmInterpreter::Thread::HANDLED;
1521 : }
1522 :
1523 8840784 : void Execute(InterpreterCode* code, pc_t pc, int max) {
1524 : DCHECK_NOT_NULL(code->side_table);
1525 : DCHECK(!frames_.empty());
1526 : // There must be enough space on the stack to hold the arguments, locals,
1527 : // and the value stack.
1528 : DCHECK_LE(code->function->sig->parameter_count() +
1529 : code->locals.type_list.size() +
1530 : code->side_table->max_stack_height_,
1531 : stack_limit_ - stack_start_ - frames_.back().sp);
1532 :
1533 11936704 : Decoder decoder(code->start, code->end);
1534 2570688 : pc_t limit = code->end - code->start;
1535 : bool hit_break = false;
1536 :
1537 : while (true) {
1538 : #define PAUSE_IF_BREAK_FLAG(flag) \
1539 : if (V8_UNLIKELY(break_flags_ & WasmInterpreter::BreakFlag::flag)) { \
1540 : hit_break = true; \
1541 : max = 0; \
1542 : }
1543 :
1544 : DCHECK_GT(limit, pc);
1545 : DCHECK_NOT_NULL(code->start);
1546 :
1547 : // Do first check for a breakpoint, in order to set hit_break correctly.
1548 : const char* skip = " ";
1549 15358682 : int len = 1;
1550 15358682 : byte opcode = code->start[pc];
1551 : byte orig = opcode;
1552 15358682 : if (V8_UNLIKELY(opcode == kInternalBreakpoint)) {
1553 8570 : orig = code->orig_start[pc];
1554 8570 : if (SkipBreakpoint(code, pc)) {
1555 : // skip breakpoint by switching on original code.
1556 : skip = "[skip] ";
1557 : } else {
1558 : TRACE("@%-3zu: [break] %-24s:", pc,
1559 : WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(orig)));
1560 : TraceValueStack();
1561 : TRACE("\n");
1562 : hit_break = true;
1563 7663 : break;
1564 : }
1565 : }
1566 :
1567 : // If max is 0, break. If max is positive (a limit is set), decrement it.
1568 15354397 : if (max == 0) break;
1569 15351019 : if (max > 0) --max;
1570 :
1571 : USE(skip);
1572 : TRACE("@%-3zu: %s%-24s:", pc, skip,
1573 : WasmOpcodes::OpcodeName(static_cast<WasmOpcode>(orig)));
1574 : TraceValueStack();
1575 : TRACE("\n");
1576 :
1577 : #ifdef DEBUG
1578 : // Compute the stack effect of this opcode, and verify later that the
1579 : // stack was modified accordingly.
1580 : std::pair<uint32_t, uint32_t> stack_effect = wasm::StackEffect(
1581 : codemap_->module(), frames_.back().code->function->sig,
1582 : code->orig_start + pc, code->orig_end);
1583 : sp_t expected_new_stack_height =
1584 : StackHeight() - stack_effect.first + stack_effect.second;
1585 : #endif
1586 :
1587 15351019 : switch (orig) {
1588 : case kExprNop:
1589 : break;
1590 : case kExprBlock: {
1591 : BlockTypeOperand<Decoder::kNoValidate> operand(&decoder,
1592 1703628 : code->at(pc));
1593 1703628 : len = 1 + operand.length;
1594 : break;
1595 : }
1596 : case kExprLoop: {
1597 : BlockTypeOperand<Decoder::kNoValidate> operand(&decoder,
1598 47954 : code->at(pc));
1599 47954 : len = 1 + operand.length;
1600 : break;
1601 : }
1602 : case kExprIf: {
1603 : BlockTypeOperand<Decoder::kNoValidate> operand(&decoder,
1604 30786 : code->at(pc));
1605 30786 : WasmValue cond = Pop();
1606 : bool is_true = cond.to<uint32_t>() != 0;
1607 30786 : if (is_true) {
1608 : // fall through to the true block.
1609 28159 : len = 1 + operand.length;
1610 : TRACE(" true => fallthrough\n");
1611 : } else {
1612 5254 : len = LookupTargetDelta(code, pc);
1613 : TRACE(" false => @%zu\n", pc + len);
1614 : }
1615 : break;
1616 : }
1617 : case kExprElse: {
1618 21966 : len = LookupTargetDelta(code, pc);
1619 : TRACE(" end => @%zu\n", pc + len);
1620 21966 : break;
1621 : }
1622 : case kExprSelect: {
1623 1398 : WasmValue cond = Pop();
1624 1398 : WasmValue fval = Pop();
1625 1398 : WasmValue tval = Pop();
1626 1398 : Push(cond.to<int32_t>() != 0 ? tval : fval);
1627 : break;
1628 : }
1629 : case kExprBr: {
1630 : BreakDepthOperand<Decoder::kNoValidate> operand(&decoder,
1631 : code->at(pc));
1632 52804 : len = DoBreak(code, pc, operand.depth);
1633 : TRACE(" br => @%zu\n", pc + len);
1634 : break;
1635 : }
1636 : case kExprBrIf: {
1637 : BreakDepthOperand<Decoder::kNoValidate> operand(&decoder,
1638 : code->at(pc));
1639 43098 : WasmValue cond = Pop();
1640 : bool is_true = cond.to<uint32_t>() != 0;
1641 43098 : if (is_true) {
1642 44040 : len = DoBreak(code, pc, operand.depth);
1643 : TRACE(" br_if => @%zu\n", pc + len);
1644 : } else {
1645 : TRACE(" false => fallthrough\n");
1646 21078 : len = 1 + operand.length;
1647 : }
1648 : break;
1649 : }
1650 : case kExprBrTable: {
1651 : BranchTableOperand<Decoder::kNoValidate> operand(&decoder,
1652 332886 : code->at(pc));
1653 : BranchTableIterator<Decoder::kNoValidate> iterator(&decoder, operand);
1654 332886 : uint32_t key = Pop().to<uint32_t>();
1655 : uint32_t depth = 0;
1656 332886 : if (key >= operand.table_count) key = operand.table_count;
1657 1622604 : for (uint32_t i = 0; i <= key; i++) {
1658 : DCHECK(iterator.has_next());
1659 1289718 : depth = iterator.next();
1660 : }
1661 665772 : len = key + DoBreak(code, pc + key, static_cast<size_t>(depth));
1662 : TRACE(" br[%u] => @%zu\n", key, pc + key + len);
1663 : break;
1664 : }
1665 : case kExprReturn: {
1666 2698201 : size_t arity = code->function->sig->return_count();
1667 2896391 : if (!DoReturn(&decoder, &code, &pc, &limit, arity)) return;
1668 12 : PAUSE_IF_BREAK_FLAG(AfterReturn);
1669 801673 : continue;
1670 : }
1671 : case kExprUnreachable: {
1672 : return DoTrap(kTrapUnreachable, pc);
1673 : }
1674 : case kExprEnd: {
1675 : break;
1676 : }
1677 : case kExprI32Const: {
1678 : ImmI32Operand<Decoder::kNoValidate> operand(&decoder, code->at(pc));
1679 : Push(WasmValue(operand.value));
1680 492765 : len = 1 + operand.length;
1681 : break;
1682 : }
1683 : case kExprI64Const: {
1684 : ImmI64Operand<Decoder::kNoValidate> operand(&decoder, code->at(pc));
1685 : Push(WasmValue(operand.value));
1686 10290 : len = 1 + operand.length;
1687 : break;
1688 : }
1689 : case kExprF32Const: {
1690 : ImmF32Operand<Decoder::kNoValidate> operand(&decoder, code->at(pc));
1691 : Push(WasmValue(operand.value));
1692 294 : len = 1 + operand.length;
1693 : break;
1694 : }
1695 : case kExprF64Const: {
1696 : ImmF64Operand<Decoder::kNoValidate> operand(&decoder, code->at(pc));
1697 : Push(WasmValue(operand.value));
1698 1680 : len = 1 + operand.length;
1699 : break;
1700 : }
1701 : case kExprGetLocal: {
1702 : LocalIndexOperand<Decoder::kNoValidate> operand(&decoder,
1703 : code->at(pc));
1704 5445897 : Push(GetStackValue(frames_.back().sp + operand.index));
1705 5445897 : len = 1 + operand.length;
1706 : break;
1707 : }
1708 : case kExprSetLocal: {
1709 : LocalIndexOperand<Decoder::kNoValidate> operand(&decoder,
1710 : code->at(pc));
1711 11998 : WasmValue val = Pop();
1712 11998 : SetStackValue(frames_.back().sp + operand.index, val);
1713 11998 : len = 1 + operand.length;
1714 : break;
1715 : }
1716 : case kExprTeeLocal: {
1717 : LocalIndexOperand<Decoder::kNoValidate> operand(&decoder,
1718 : code->at(pc));
1719 3780 : WasmValue val = Pop();
1720 3780 : SetStackValue(frames_.back().sp + operand.index, val);
1721 : Push(val);
1722 3780 : len = 1 + operand.length;
1723 : break;
1724 : }
1725 : case kExprDrop: {
1726 : Pop();
1727 : break;
1728 : }
1729 : case kExprCallFunction: {
1730 : CallFunctionOperand<Decoder::kNoValidate> operand(&decoder,
1731 : code->at(pc));
1732 : InterpreterCode* target = codemap()->GetCode(operand.index);
1733 807805 : if (target->function->imported) {
1734 6260 : CommitPc(pc);
1735 : ExternalCallResult result =
1736 6260 : CallImportedFunction(target->function->func_index);
1737 6260 : switch (result.type) {
1738 : case ExternalCallResult::INTERNAL:
1739 : // The import is a function of this instance. Call it directly.
1740 0 : target = result.interpreter_code;
1741 : DCHECK(!target->function->imported);
1742 0 : break;
1743 : case ExternalCallResult::INVALID_FUNC:
1744 : case ExternalCallResult::SIGNATURE_MISMATCH:
1745 : // Direct calls are checked statically.
1746 0 : UNREACHABLE();
1747 : case ExternalCallResult::EXTERNAL_RETURNED:
1748 6170 : PAUSE_IF_BREAK_FLAG(AfterCall);
1749 6170 : len = 1 + operand.length;
1750 6170 : break;
1751 : case ExternalCallResult::EXTERNAL_UNWOUND:
1752 90 : return;
1753 : }
1754 6170 : if (result.type != ExternalCallResult::INTERNAL) break;
1755 : }
1756 : // Execute an internal call.
1757 801545 : if (!DoCall(&decoder, target, &pc, &limit)) return;
1758 801535 : code = target;
1759 801535 : PAUSE_IF_BREAK_FLAG(AfterCall);
1760 801535 : continue; // don't bump pc
1761 : } break;
1762 : case kExprCallIndirect: {
1763 : CallIndirectOperand<Decoder::kNoValidate> operand(&decoder,
1764 236 : code->at(pc));
1765 236 : uint32_t entry_index = Pop().to<uint32_t>();
1766 : // Assume only one table for now.
1767 : DCHECK_LE(module()->function_tables.size(), 1u);
1768 : ExternalCallResult result =
1769 236 : CallIndirectFunction(0, entry_index, operand.index);
1770 236 : switch (result.type) {
1771 : case ExternalCallResult::INTERNAL:
1772 : // The import is a function of this instance. Call it directly.
1773 126 : if (!DoCall(&decoder, result.interpreter_code, &pc, &limit))
1774 100 : return;
1775 126 : code = result.interpreter_code;
1776 126 : PAUSE_IF_BREAK_FLAG(AfterCall);
1777 126 : continue; // don't bump pc
1778 : case ExternalCallResult::INVALID_FUNC:
1779 64 : return DoTrap(kTrapFuncInvalid, pc);
1780 : case ExternalCallResult::SIGNATURE_MISMATCH:
1781 26 : return DoTrap(kTrapFuncSigMismatch, pc);
1782 : case ExternalCallResult::EXTERNAL_RETURNED:
1783 10 : PAUSE_IF_BREAK_FLAG(AfterCall);
1784 10 : len = 1 + operand.length;
1785 10 : break;
1786 : case ExternalCallResult::EXTERNAL_UNWOUND:
1787 : return;
1788 : }
1789 10 : } break;
1790 : case kExprGetGlobal: {
1791 : GlobalIndexOperand<Decoder::kNoValidate> operand(&decoder,
1792 : code->at(pc));
1793 640 : const WasmGlobal* global = &module()->globals[operand.index];
1794 320 : byte* ptr = wasm_context_->globals_start + global->offset;
1795 : WasmValue val;
1796 320 : switch (global->type) {
1797 : #define CASE_TYPE(wasm, ctype) \
1798 : case kWasm##wasm: \
1799 : val = WasmValue(*reinterpret_cast<ctype*>(ptr)); \
1800 : break;
1801 188 : WASM_CTYPES(CASE_TYPE)
1802 : #undef CASE_TYPE
1803 : default:
1804 0 : UNREACHABLE();
1805 : }
1806 : Push(val);
1807 320 : len = 1 + operand.length;
1808 : break;
1809 : }
1810 : case kExprSetGlobal: {
1811 : GlobalIndexOperand<Decoder::kNoValidate> operand(&decoder,
1812 : code->at(pc));
1813 464 : const WasmGlobal* global = &module()->globals[operand.index];
1814 232 : byte* ptr = wasm_context_->globals_start + global->offset;
1815 232 : WasmValue val = Pop();
1816 232 : switch (global->type) {
1817 : #define CASE_TYPE(wasm, ctype) \
1818 : case kWasm##wasm: \
1819 : *reinterpret_cast<ctype*>(ptr) = val.to<ctype>(); \
1820 : break;
1821 232 : WASM_CTYPES(CASE_TYPE)
1822 : #undef CASE_TYPE
1823 : default:
1824 0 : UNREACHABLE();
1825 : }
1826 232 : len = 1 + operand.length;
1827 : break;
1828 : }
1829 :
1830 : #define LOAD_CASE(name, ctype, mtype, rep) \
1831 : case kExpr##name: { \
1832 : if (!ExecuteLoad<ctype, mtype>(&decoder, code, pc, len, \
1833 : MachineRepresentation::rep)) \
1834 : return; \
1835 : break; \
1836 : }
1837 :
1838 381 : LOAD_CASE(I32LoadMem8S, int32_t, int8_t, kWord8);
1839 393 : LOAD_CASE(I32LoadMem8U, int32_t, uint8_t, kWord8);
1840 333 : LOAD_CASE(I32LoadMem16S, int32_t, int16_t, kWord16);
1841 333 : LOAD_CASE(I32LoadMem16U, int32_t, uint16_t, kWord16);
1842 144 : LOAD_CASE(I64LoadMem8S, int64_t, int8_t, kWord8);
1843 0 : LOAD_CASE(I64LoadMem8U, int64_t, uint8_t, kWord16);
1844 144 : LOAD_CASE(I64LoadMem16S, int64_t, int16_t, kWord16);
1845 0 : LOAD_CASE(I64LoadMem16U, int64_t, uint16_t, kWord16);
1846 144 : LOAD_CASE(I64LoadMem32S, int64_t, int32_t, kWord32);
1847 0 : LOAD_CASE(I64LoadMem32U, int64_t, uint32_t, kWord32);
1848 27580 : LOAD_CASE(I32LoadMem, int32_t, int32_t, kWord32);
1849 9302 : LOAD_CASE(I64LoadMem, int64_t, int64_t, kWord64);
1850 13773 : LOAD_CASE(F32LoadMem, float, float, kFloat32);
1851 44853 : LOAD_CASE(F64LoadMem, double, double, kFloat64);
1852 : #undef LOAD_CASE
1853 :
1854 : #define STORE_CASE(name, ctype, mtype, rep) \
1855 : case kExpr##name: { \
1856 : if (!ExecuteStore<ctype, mtype>(&decoder, code, pc, len, \
1857 : MachineRepresentation::rep)) \
1858 : return; \
1859 : break; \
1860 : }
1861 :
1862 410 : STORE_CASE(I32StoreMem8, int32_t, int8_t, kWord8);
1863 380 : STORE_CASE(I32StoreMem16, int32_t, int16_t, kWord16);
1864 0 : STORE_CASE(I64StoreMem8, int64_t, int8_t, kWord8);
1865 0 : STORE_CASE(I64StoreMem16, int64_t, int16_t, kWord16);
1866 0 : STORE_CASE(I64StoreMem32, int64_t, int32_t, kWord32);
1867 2822 : STORE_CASE(I32StoreMem, int32_t, int32_t, kWord32);
1868 1174 : STORE_CASE(I64StoreMem, int64_t, int64_t, kWord64);
1869 1414 : STORE_CASE(F32StoreMem, float, float, kFloat32);
1870 16348 : STORE_CASE(F64StoreMem, double, double, kFloat64);
1871 : #undef STORE_CASE
1872 :
1873 : #define ASMJS_LOAD_CASE(name, ctype, mtype, defval) \
1874 : case kExpr##name: { \
1875 : uint32_t index = Pop().to<uint32_t>(); \
1876 : ctype result; \
1877 : if (!BoundsCheck<mtype>(wasm_context_->mem_size, 0, index)) { \
1878 : result = defval; \
1879 : } else { \
1880 : byte* addr = wasm_context_->mem_start + index; \
1881 : /* TODO(titzer): alignment for asmjs load mem? */ \
1882 : result = static_cast<ctype>(*reinterpret_cast<mtype*>(addr)); \
1883 : } \
1884 : Push(WasmValue(result)); \
1885 : break; \
1886 : }
1887 0 : ASMJS_LOAD_CASE(I32AsmjsLoadMem8S, int32_t, int8_t, 0);
1888 0 : ASMJS_LOAD_CASE(I32AsmjsLoadMem8U, int32_t, uint8_t, 0);
1889 0 : ASMJS_LOAD_CASE(I32AsmjsLoadMem16S, int32_t, int16_t, 0);
1890 0 : ASMJS_LOAD_CASE(I32AsmjsLoadMem16U, int32_t, uint16_t, 0);
1891 450 : ASMJS_LOAD_CASE(I32AsmjsLoadMem, int32_t, int32_t, 0);
1892 450 : ASMJS_LOAD_CASE(F32AsmjsLoadMem, float, float,
1893 : std::numeric_limits<float>::quiet_NaN());
1894 612 : ASMJS_LOAD_CASE(F64AsmjsLoadMem, double, double,
1895 : std::numeric_limits<double>::quiet_NaN());
1896 : #undef ASMJS_LOAD_CASE
1897 :
1898 : #define ASMJS_STORE_CASE(name, ctype, mtype) \
1899 : case kExpr##name: { \
1900 : WasmValue val = Pop(); \
1901 : uint32_t index = Pop().to<uint32_t>(); \
1902 : if (BoundsCheck<mtype>(wasm_context_->mem_size, 0, index)) { \
1903 : byte* addr = wasm_context_->mem_start + index; \
1904 : /* TODO(titzer): alignment for asmjs store mem? */ \
1905 : *(reinterpret_cast<mtype*>(addr)) = static_cast<mtype>(val.to<ctype>()); \
1906 : } \
1907 : Push(val); \
1908 : break; \
1909 : }
1910 :
1911 0 : ASMJS_STORE_CASE(I32AsmjsStoreMem8, int32_t, int8_t);
1912 0 : ASMJS_STORE_CASE(I32AsmjsStoreMem16, int32_t, int16_t);
1913 4194 : ASMJS_STORE_CASE(I32AsmjsStoreMem, int32_t, int32_t);
1914 0 : ASMJS_STORE_CASE(F32AsmjsStoreMem, float, float);
1915 0 : ASMJS_STORE_CASE(F64AsmjsStoreMem, double, double);
1916 : #undef ASMJS_STORE_CASE
1917 : case kExprGrowMemory: {
1918 : MemoryIndexOperand<Decoder::kNoValidate> operand(&decoder,
1919 : code->at(pc));
1920 64 : uint32_t delta_pages = Pop().to<uint32_t>();
1921 : Handle<WasmInstanceObject> instance =
1922 128 : codemap()->maybe_instance().ToHandleChecked();
1923 : DCHECK_EQ(wasm_context_, instance->wasm_context()->get());
1924 : Isolate* isolate = instance->GetIsolate();
1925 : int32_t result =
1926 64 : WasmInstanceObject::GrowMemory(isolate, instance, delta_pages);
1927 : Push(WasmValue(result));
1928 64 : len = 1 + operand.length;
1929 : break;
1930 : }
1931 : case kExprMemorySize: {
1932 : MemoryIndexOperand<Decoder::kNoValidate> operand(&decoder,
1933 : code->at(pc));
1934 : Push(WasmValue(static_cast<uint32_t>(wasm_context_->mem_size /
1935 0 : WasmModule::kPageSize)));
1936 0 : len = 1 + operand.length;
1937 : break;
1938 : }
1939 : // We need to treat kExprI32ReinterpretF32 and kExprI64ReinterpretF64
1940 : // specially to guarantee that the quiet bit of a NaN is preserved on
1941 : // ia32 by the reinterpret casts.
1942 : case kExprI32ReinterpretF32: {
1943 18 : WasmValue val = Pop();
1944 : Push(WasmValue(ExecuteI32ReinterpretF32(val)));
1945 18 : possible_nondeterminism_ |= std::isnan(val.to<float>());
1946 : break;
1947 : }
1948 : case kExprI64ReinterpretF64: {
1949 366 : WasmValue val = Pop();
1950 : Push(WasmValue(ExecuteI64ReinterpretF64(val)));
1951 366 : possible_nondeterminism_ |= std::isnan(val.to<double>());
1952 : break;
1953 : }
1954 : #define EXECUTE_SIMPLE_BINOP(name, ctype, op) \
1955 : case kExpr##name: { \
1956 : WasmValue rval = Pop(); \
1957 : WasmValue lval = Pop(); \
1958 : WasmValue result(lval.to<ctype>() op rval.to<ctype>()); \
1959 : Push(result); \
1960 : break; \
1961 : }
1962 3450434 : FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP)
1963 : #undef EXECUTE_SIMPLE_BINOP
1964 :
1965 : #define EXECUTE_OTHER_BINOP(name, ctype) \
1966 : case kExpr##name: { \
1967 : TrapReason trap = kTrapCount; \
1968 : volatile ctype rval = Pop().to<ctype>(); \
1969 : volatile ctype lval = Pop().to<ctype>(); \
1970 : WasmValue result(Execute##name(lval, rval, &trap)); \
1971 : if (trap != kTrapCount) return DoTrap(trap, pc); \
1972 : Push(result); \
1973 : break; \
1974 : }
1975 1631142 : FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP)
1976 : #undef EXECUTE_OTHER_BINOP
1977 :
1978 : case kExprF32CopySign: {
1979 : // Handle kExprF32CopySign separately because it may introduce
1980 : // observable non-determinism.
1981 : TrapReason trap = kTrapCount;
1982 79362 : volatile float rval = Pop().to<float>();
1983 79362 : volatile float lval = Pop().to<float>();
1984 79362 : WasmValue result(ExecuteF32CopySign(lval, rval, &trap));
1985 : Push(result);
1986 158724 : possible_nondeterminism_ |= std::isnan(rval);
1987 : break;
1988 : }
1989 : case kExprF64CopySign: {
1990 : // Handle kExprF32CopySign separately because it may introduce
1991 : // observable non-determinism.
1992 : TrapReason trap = kTrapCount;
1993 14418 : volatile double rval = Pop().to<double>();
1994 14418 : volatile double lval = Pop().to<double>();
1995 14418 : WasmValue result(ExecuteF64CopySign(lval, rval, &trap));
1996 : Push(result);
1997 28836 : possible_nondeterminism_ |= std::isnan(rval);
1998 : break;
1999 : }
2000 : #define EXECUTE_OTHER_UNOP(name, ctype) \
2001 : case kExpr##name: { \
2002 : TrapReason trap = kTrapCount; \
2003 : volatile ctype val = Pop().to<ctype>(); \
2004 : WasmValue result(Execute##name(val, &trap)); \
2005 : if (trap != kTrapCount) return DoTrap(trap, pc); \
2006 : Push(result); \
2007 : break; \
2008 : }
2009 289080 : FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP)
2010 : #undef EXECUTE_OTHER_UNOP
2011 :
2012 : default:
2013 : V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
2014 0 : code->start[pc], OpcodeName(code->start[pc]));
2015 : UNREACHABLE();
2016 : }
2017 :
2018 : #ifdef DEBUG
2019 : if (!WasmOpcodes::IsControlOpcode(static_cast<WasmOpcode>(opcode))) {
2020 : DCHECK_EQ(expected_new_stack_height, StackHeight());
2021 : }
2022 : #endif
2023 :
2024 14204907 : pc += len;
2025 14204907 : if (pc == limit) {
2026 : // Fell off end of code; do an implicit return.
2027 : TRACE("@%-3zu: ImplicitReturn\n", pc);
2028 2364835 : if (!DoReturn(&decoder, &code, &pc, &limit,
2029 4729670 : code->function->sig->return_count()))
2030 : return;
2031 146249 : PAUSE_IF_BREAK_FLAG(AfterReturn);
2032 : }
2033 : #undef PAUSE_IF_BREAK_FLAG
2034 : }
2035 :
2036 7663 : state_ = WasmInterpreter::PAUSED;
2037 11974 : break_pc_ = hit_break ? pc : kInvalidPc;
2038 7663 : CommitPc(pc);
2039 : }
2040 :
2041 : WasmValue Pop() {
2042 : DCHECK_GT(frames_.size(), 0);
2043 : DCHECK_GT(StackHeight(), frames_.back().llimit()); // can't pop into locals
2044 5986988 : return *--sp_;
2045 : }
2046 :
2047 : void PopN(int n) {
2048 : DCHECK_GE(StackHeight(), n);
2049 : DCHECK_GT(frames_.size(), 0);
2050 : // Check that we don't pop into locals.
2051 : DCHECK_GE(StackHeight() - n, frames_.back().llimit());
2052 : sp_ -= n;
2053 : }
2054 :
2055 : WasmValue PopArity(size_t arity) {
2056 : if (arity == 0) return WasmValue();
2057 : CHECK_EQ(1, arity);
2058 : return Pop();
2059 : }
2060 :
2061 : void Push(WasmValue val) {
2062 : DCHECK_NE(kWasmStmt, val.type());
2063 : DCHECK_LE(1, stack_limit_ - sp_);
2064 8897006 : *sp_++ = val;
2065 : }
2066 :
2067 : void Push(WasmValue* vals, size_t arity) {
2068 : DCHECK_LE(arity, stack_limit_ - sp_);
2069 2563038 : for (WasmValue *val = vals, *end = vals + arity; val != end; ++val) {
2070 : DCHECK_NE(kWasmStmt, val->type());
2071 : }
2072 2563038 : memcpy(sp_, vals, arity * sizeof(*sp_));
2073 2563038 : sp_ += arity;
2074 : }
2075 :
2076 5927747 : void EnsureStackSpace(size_t size) {
2077 11855494 : if (V8_LIKELY(static_cast<size_t>(stack_limit_ - sp_) >= size)) return;
2078 17250 : size_t old_size = stack_limit_ - stack_start_;
2079 : size_t requested_size =
2080 17250 : base::bits::RoundUpToPowerOfTwo64((sp_ - stack_start_) + size);
2081 17250 : size_t new_size = Max(size_t{8}, Max(2 * old_size, requested_size));
2082 17250 : WasmValue* new_stack = zone_->NewArray<WasmValue>(new_size);
2083 17250 : memcpy(new_stack, stack_start_, old_size * sizeof(*sp_));
2084 17250 : sp_ = new_stack + (sp_ - stack_start_);
2085 17250 : stack_start_ = new_stack;
2086 17250 : stack_limit_ = new_stack + new_size;
2087 : }
2088 :
2089 3410280 : sp_t StackHeight() { return sp_ - stack_start_; }
2090 :
2091 : void TraceValueStack() {
2092 : #ifdef DEBUG
2093 : if (!FLAG_trace_wasm_interpreter) return;
2094 : Frame* top = frames_.size() > 0 ? &frames_.back() : nullptr;
2095 : sp_t sp = top ? top->sp : 0;
2096 : sp_t plimit = top ? top->plimit() : 0;
2097 : sp_t llimit = top ? top->llimit() : 0;
2098 : for (size_t i = sp; i < StackHeight(); ++i) {
2099 : if (i < plimit)
2100 : PrintF(" p%zu:", i);
2101 : else if (i < llimit)
2102 : PrintF(" l%zu:", i);
2103 : else
2104 : PrintF(" s%zu:", i);
2105 : WasmValue val = GetStackValue(i);
2106 : switch (val.type()) {
2107 : case kWasmI32:
2108 : PrintF("i32:%d", val.to<int32_t>());
2109 : break;
2110 : case kWasmI64:
2111 : PrintF("i64:%" PRId64 "", val.to<int64_t>());
2112 : break;
2113 : case kWasmF32:
2114 : PrintF("f32:%f", val.to<float>());
2115 : break;
2116 : case kWasmF64:
2117 : PrintF("f64:%lf", val.to<double>());
2118 : break;
2119 : case kWasmStmt:
2120 : PrintF("void");
2121 : break;
2122 : default:
2123 : UNREACHABLE();
2124 : break;
2125 : }
2126 : }
2127 : #endif // DEBUG
2128 : }
2129 :
2130 : ExternalCallResult TryHandleException(Isolate* isolate) {
2131 100 : if (HandleException(isolate) == WasmInterpreter::Thread::UNWOUND) {
2132 : return {ExternalCallResult::EXTERNAL_UNWOUND};
2133 : }
2134 : return {ExternalCallResult::EXTERNAL_RETURNED};
2135 : }
2136 :
2137 : // TODO(clemensh): Remove this, call JS via existing wasm-to-js wrapper, using
2138 : // CallExternalWasmFunction.
2139 6220 : ExternalCallResult CallExternalJSFunction(Isolate* isolate, Handle<Code> code,
2140 24540 : FunctionSig* signature) {
2141 6220 : Handle<HeapObject> target = UnwrapWasmToJSWrapper(isolate, code);
2142 :
2143 6220 : if (target.is_null()) {
2144 : isolate->Throw(*isolate->factory()->NewTypeError(
2145 40 : MessageTemplate::kWasmTrapTypeError));
2146 : return TryHandleException(isolate);
2147 : }
2148 :
2149 : #if DEBUG
2150 : std::ostringstream oss;
2151 : target->HeapObjectShortPrint(oss);
2152 : TRACE(" => Calling imported function %s\n", oss.str().c_str());
2153 : #endif
2154 :
2155 6200 : int num_args = static_cast<int>(signature->parameter_count());
2156 :
2157 : // Get all arguments as JS values.
2158 : std::vector<Handle<Object>> args;
2159 6200 : args.reserve(num_args);
2160 6200 : WasmValue* wasm_args = sp_ - num_args;
2161 12340 : for (int i = 0; i < num_args; ++i) {
2162 : args.push_back(WasmValueToNumber(isolate->factory(), wasm_args[i],
2163 18420 : signature->GetParam(i)));
2164 : }
2165 :
2166 : // The receiver is the global proxy if in sloppy mode (default), undefined
2167 : // if in strict mode.
2168 6200 : Handle<Object> receiver = isolate->global_proxy();
2169 12400 : if (target->IsJSFunction() &&
2170 : is_strict(JSFunction::cast(*target)->shared()->language_mode())) {
2171 0 : receiver = isolate->factory()->undefined_value();
2172 : }
2173 :
2174 : MaybeHandle<Object> maybe_retval =
2175 6200 : Execution::Call(isolate, target, receiver, num_args, args.data());
2176 6200 : if (maybe_retval.is_null()) return TryHandleException(isolate);
2177 :
2178 6160 : Handle<Object> retval = maybe_retval.ToHandleChecked();
2179 : // Pop arguments off the stack.
2180 6160 : sp_ -= num_args;
2181 : // Push return values.
2182 6160 : if (signature->return_count() > 0) {
2183 : // TODO(wasm): Handle multiple returns.
2184 : DCHECK_EQ(1, signature->return_count());
2185 6040 : Push(ToWebAssemblyValue(isolate, retval, signature->GetReturn()));
2186 : }
2187 6160 : return {ExternalCallResult::EXTERNAL_RETURNED};
2188 : }
2189 :
2190 60 : ExternalCallResult CallExternalWasmFunction(Isolate* isolate,
2191 : Handle<Code> code,
2192 280 : FunctionSig* sig) {
2193 : Handle<WasmDebugInfo> debug_info(codemap()->instance()->debug_info(),
2194 : isolate);
2195 : Handle<JSFunction> wasm_entry =
2196 60 : WasmDebugInfo::GetCWasmEntry(debug_info, sig);
2197 :
2198 : TRACE(" => Calling external wasm function\n");
2199 :
2200 : // Copy the arguments to one buffer.
2201 : // TODO(clemensh): Introduce a helper for all argument buffer
2202 : // con-/destruction.
2203 60 : int num_args = static_cast<int>(sig->parameter_count());
2204 60 : std::vector<uint8_t> arg_buffer(num_args * 8);
2205 : size_t offset = 0;
2206 60 : WasmValue* wasm_args = sp_ - num_args;
2207 120 : for (int i = 0; i < num_args; ++i) {
2208 120 : uint32_t param_size = 1 << ElementSizeLog2Of(sig->GetParam(i));
2209 120 : if (arg_buffer.size() < offset + param_size) {
2210 0 : arg_buffer.resize(std::max(2 * arg_buffer.size(), offset + param_size));
2211 : }
2212 60 : switch (sig->GetParam(i)) {
2213 : case kWasmI32:
2214 60 : WriteUnalignedValue(arg_buffer.data() + offset,
2215 60 : wasm_args[i].to<uint32_t>());
2216 : break;
2217 : case kWasmI64:
2218 0 : WriteUnalignedValue(arg_buffer.data() + offset,
2219 0 : wasm_args[i].to<uint64_t>());
2220 : break;
2221 : case kWasmF32:
2222 0 : WriteUnalignedValue(arg_buffer.data() + offset,
2223 0 : wasm_args[i].to<float>());
2224 : break;
2225 : case kWasmF64:
2226 0 : WriteUnalignedValue(arg_buffer.data() + offset,
2227 0 : wasm_args[i].to<double>());
2228 : break;
2229 : default:
2230 0 : UNIMPLEMENTED();
2231 : }
2232 : offset += param_size;
2233 : }
2234 :
2235 : // Wrap the arg_buffer data pointer in a handle. As this is an aligned
2236 : // pointer, to the GC it will look like a Smi.
2237 : Handle<Object> arg_buffer_obj(reinterpret_cast<Object*>(arg_buffer.data()),
2238 : isolate);
2239 : DCHECK(!arg_buffer_obj->IsHeapObject());
2240 :
2241 180 : Handle<Object> args[compiler::CWasmEntryParameters::kNumParameters];
2242 60 : args[compiler::CWasmEntryParameters::kCodeObject] = code;
2243 60 : args[compiler::CWasmEntryParameters::kArgumentsBuffer] = arg_buffer_obj;
2244 :
2245 : Handle<Object> receiver = isolate->factory()->undefined_value();
2246 : trap_handler::SetThreadInWasm();
2247 : MaybeHandle<Object> maybe_retval =
2248 60 : Execution::Call(isolate, wasm_entry, receiver, arraysize(args), args);
2249 : TRACE(" => External wasm function returned%s\n",
2250 : maybe_retval.is_null() ? " with exception" : "");
2251 :
2252 60 : if (maybe_retval.is_null()) {
2253 : DCHECK(!trap_handler::IsThreadInWasm());
2254 : return TryHandleException(isolate);
2255 : }
2256 :
2257 : trap_handler::ClearThreadInWasm();
2258 :
2259 : // Pop arguments off the stack.
2260 20 : sp_ -= num_args;
2261 : // Push return values.
2262 20 : if (sig->return_count() > 0) {
2263 : // TODO(wasm): Handle multiple returns.
2264 : DCHECK_EQ(1, sig->return_count());
2265 20 : switch (sig->GetReturn()) {
2266 : case kWasmI32:
2267 : Push(WasmValue(ReadUnalignedValue<uint32_t>(arg_buffer.data())));
2268 20 : break;
2269 : case kWasmI64:
2270 : Push(WasmValue(ReadUnalignedValue<uint64_t>(arg_buffer.data())));
2271 0 : break;
2272 : case kWasmF32:
2273 : Push(WasmValue(ReadUnalignedValue<float>(arg_buffer.data())));
2274 0 : break;
2275 : case kWasmF64:
2276 : Push(WasmValue(ReadUnalignedValue<double>(arg_buffer.data())));
2277 0 : break;
2278 : default:
2279 0 : UNIMPLEMENTED();
2280 : }
2281 : }
2282 20 : return {ExternalCallResult::EXTERNAL_RETURNED};
2283 : }
2284 :
2285 6310 : ExternalCallResult CallCodeObject(Isolate* isolate, Handle<Code> code,
2286 120 : FunctionSig* signature) {
2287 : DCHECK(AllowHandleAllocation::IsAllowed());
2288 : DCHECK(AllowHeapAllocation::IsAllowed());
2289 :
2290 6310 : if (code->kind() == Code::WASM_FUNCTION) {
2291 : FixedArray* deopt_data = code->deoptimization_data();
2292 : DCHECK_EQ(2, deopt_data->length());
2293 : WasmInstanceObject* target_instance =
2294 : WasmInstanceObject::cast(WeakCell::cast(deopt_data->get(0))->value());
2295 90 : if (target_instance != codemap()->instance()) {
2296 60 : return CallExternalWasmFunction(isolate, code, signature);
2297 : }
2298 : int target_func_idx = Smi::ToInt(deopt_data->get(1));
2299 : DCHECK_LE(0, target_func_idx);
2300 : return {ExternalCallResult::INTERNAL,
2301 60 : codemap()->GetCode(target_func_idx)};
2302 : }
2303 :
2304 6220 : return CallExternalJSFunction(isolate, code, signature);
2305 : }
2306 :
2307 18780 : ExternalCallResult CallImportedFunction(uint32_t function_index) {
2308 : // Use a new HandleScope to avoid leaking / accumulating handles in the
2309 : // outer scope.
2310 : Isolate* isolate = codemap()->instance()->GetIsolate();
2311 : HandleScope handle_scope(isolate);
2312 :
2313 : Handle<Code> target(codemap()->GetImportedFunction(function_index),
2314 6260 : isolate);
2315 : return CallCodeObject(isolate, target,
2316 25040 : codemap()->module()->functions[function_index].sig);
2317 : }
2318 :
2319 236 : ExternalCallResult CallIndirectFunction(uint32_t table_index,
2320 : uint32_t entry_index,
2321 540 : uint32_t sig_index) {
2322 472 : if (!codemap()->has_instance() ||
2323 236 : !codemap()->instance()->compiled_module()->has_function_tables()) {
2324 : // No instance. Rely on the information stored in the WasmModule.
2325 : // TODO(wasm): This is only needed for testing. Refactor testing to use
2326 : // the same paths as production.
2327 : InterpreterCode* code =
2328 156 : codemap()->GetIndirectCode(table_index, entry_index);
2329 156 : if (!code) return {ExternalCallResult::INVALID_FUNC};
2330 102 : if (code->function->sig_index != sig_index) {
2331 : // If not an exact match, we have to do a canonical check.
2332 : int function_canonical_id =
2333 54 : module()->signature_ids[code->function->sig_index];
2334 36 : int expected_canonical_id = module()->signature_ids[sig_index];
2335 : DCHECK_EQ(function_canonical_id,
2336 : module()->signature_map.Find(code->function->sig));
2337 18 : if (function_canonical_id != expected_canonical_id) {
2338 6 : return {ExternalCallResult::SIGNATURE_MISMATCH};
2339 : }
2340 : }
2341 96 : return {ExternalCallResult::INTERNAL, code};
2342 : }
2343 :
2344 : WasmCompiledModule* compiled_module =
2345 : codemap()->instance()->compiled_module();
2346 : Isolate* isolate = compiled_module->GetIsolate();
2347 :
2348 : Code* target;
2349 : {
2350 : DisallowHeapAllocation no_gc;
2351 : // Get function to be called directly from the live instance to see latest
2352 : // changes to the tables.
2353 :
2354 : // Canonicalize signature index.
2355 240 : uint32_t canonical_sig_index = module()->signature_ids[sig_index];
2356 : DCHECK_EQ(canonical_sig_index,
2357 : module()->signature_map.Find(module()->signatures[sig_index]));
2358 :
2359 : // Check signature.
2360 : FixedArray* sig_tables = compiled_module->ptr_to_signature_tables();
2361 80 : if (table_index >= static_cast<uint32_t>(sig_tables->length())) {
2362 0 : return {ExternalCallResult::INVALID_FUNC};
2363 : }
2364 : // Reconstitute the global handle to sig_table, and, further below,
2365 : // to the function table, from the address stored in the
2366 : // respective table of tables.
2367 80 : int table_index_as_int = static_cast<int>(table_index);
2368 : Handle<FixedArray> sig_table(reinterpret_cast<FixedArray**>(
2369 80 : WasmCompiledModule::GetTableValue(sig_tables, table_index_as_int)));
2370 80 : if (entry_index >= static_cast<uint32_t>(sig_table->length())) {
2371 10 : return {ExternalCallResult::INVALID_FUNC};
2372 : }
2373 70 : int found_sig = Smi::ToInt(sig_table->get(static_cast<int>(entry_index)));
2374 70 : if (static_cast<uint32_t>(found_sig) != canonical_sig_index) {
2375 20 : return {ExternalCallResult::SIGNATURE_MISMATCH};
2376 : }
2377 :
2378 : // Get code object.
2379 : FixedArray* fun_tables = compiled_module->ptr_to_function_tables();
2380 : DCHECK_EQ(sig_tables->length(), fun_tables->length());
2381 : Handle<FixedArray> fun_table(reinterpret_cast<FixedArray**>(
2382 50 : WasmCompiledModule::GetTableValue(fun_tables, table_index_as_int)));
2383 : DCHECK_EQ(sig_table->length(), fun_table->length());
2384 : target = Code::cast(fun_table->get(static_cast<int>(entry_index)));
2385 : }
2386 :
2387 : // Call the code object. Use a new HandleScope to avoid leaking /
2388 : // accumulating handles in the outer scope.
2389 : HandleScope handle_scope(isolate);
2390 : FunctionSig* signature =
2391 150 : &codemap()->module()->signatures[table_index][sig_index];
2392 50 : return CallCodeObject(isolate, handle(target, isolate), signature);
2393 : }
2394 :
2395 : inline Activation current_activation() {
2396 5249895 : return activations_.empty() ? Activation(0, 0) : activations_.back();
2397 : }
2398 : };
2399 :
2400 : class InterpretedFrameImpl {
2401 : public:
2402 : InterpretedFrameImpl(ThreadImpl* thread, int index)
2403 1315720 : : thread_(thread), index_(index) {
2404 : DCHECK_LE(0, index);
2405 : }
2406 :
2407 2632858 : const WasmFunction* function() const { return frame()->code->function; }
2408 :
2409 1314799 : int pc() const {
2410 : DCHECK_LE(0, frame()->pc);
2411 : DCHECK_GE(kMaxInt, frame()->pc);
2412 1314799 : return static_cast<int>(frame()->pc);
2413 : }
2414 :
2415 : int GetParameterCount() const {
2416 : DCHECK_GE(kMaxInt, function()->sig->parameter_count());
2417 285 : return static_cast<int>(function()->sig->parameter_count());
2418 : }
2419 :
2420 : int GetLocalCount() const {
2421 1289 : size_t num_locals = function()->sig->parameter_count() +
2422 2578 : frame()->code->locals.type_list.size();
2423 : DCHECK_GE(kMaxInt, num_locals);
2424 1289 : return static_cast<int>(num_locals);
2425 : }
2426 :
2427 465 : int GetStackHeight() const {
2428 : bool is_top_frame =
2429 880 : static_cast<size_t>(index_) + 1 == thread_->frames_.size();
2430 : size_t stack_limit =
2431 565 : is_top_frame ? thread_->StackHeight() : thread_->frames_[index_ + 1].sp;
2432 : DCHECK_LE(frame()->sp, stack_limit);
2433 465 : size_t frame_size = stack_limit - frame()->sp;
2434 : DCHECK_LE(GetLocalCount(), frame_size);
2435 930 : return static_cast<int>(frame_size) - GetLocalCount();
2436 : }
2437 :
2438 624 : WasmValue GetLocalValue(int index) const {
2439 : DCHECK_LE(0, index);
2440 : DCHECK_GT(GetLocalCount(), index);
2441 1248 : return thread_->GetStackValue(static_cast<int>(frame()->sp) + index);
2442 : }
2443 :
2444 143 : WasmValue GetStackValue(int index) const {
2445 : DCHECK_LE(0, index);
2446 : // Index must be within the number of stack values of this frame.
2447 : DCHECK_GT(GetStackHeight(), index);
2448 286 : return thread_->GetStackValue(static_cast<int>(frame()->sp) +
2449 286 : GetLocalCount() + index);
2450 : }
2451 :
2452 : private:
2453 : ThreadImpl* thread_;
2454 : int index_;
2455 :
2456 : ThreadImpl::Frame* frame() const {
2457 : DCHECK_GT(thread_->frames_.size(), index_);
2458 2631691 : return &thread_->frames_[index_];
2459 : }
2460 : };
2461 :
2462 : // Converters between WasmInterpreter::Thread and WasmInterpreter::ThreadImpl.
2463 : // Thread* is the public interface, without knowledge of the object layout.
2464 : // This cast is potentially risky, but as long as we always cast it back before
2465 : // accessing any data, it should be fine. UBSan is not complaining.
2466 : WasmInterpreter::Thread* ToThread(ThreadImpl* impl) {
2467 : return reinterpret_cast<WasmInterpreter::Thread*>(impl);
2468 : }
2469 : ThreadImpl* ToImpl(WasmInterpreter::Thread* thread) {
2470 : return reinterpret_cast<ThreadImpl*>(thread);
2471 : }
2472 :
2473 : // Same conversion for InterpretedFrame and InterpretedFrameImpl.
2474 : InterpretedFrame* ToFrame(InterpretedFrameImpl* impl) {
2475 : return reinterpret_cast<InterpretedFrame*>(impl);
2476 : }
2477 : const InterpretedFrameImpl* ToImpl(const InterpretedFrame* frame) {
2478 : return reinterpret_cast<const InterpretedFrameImpl*>(frame);
2479 : }
2480 :
2481 : //============================================================================
2482 : // Implementation details of the heap objects scope.
2483 : //============================================================================
2484 : class HeapObjectsScopeImpl {
2485 : public:
2486 2558850 : HeapObjectsScopeImpl(CodeMap* codemap, Handle<WasmInstanceObject> instance)
2487 5117700 : : codemap_(codemap), needs_reset(!codemap_->has_instance()) {
2488 2558850 : if (needs_reset) {
2489 2558780 : instance_ = handle(*instance);
2490 2558780 : codemap_->SetInstanceObject(instance_);
2491 : } else {
2492 : DCHECK_EQ(*instance, codemap_->instance());
2493 2558850 : return;
2494 : }
2495 : }
2496 :
2497 : ~HeapObjectsScopeImpl() {
2498 2558850 : if (!needs_reset) return;
2499 : DCHECK_EQ(*instance_, codemap_->instance());
2500 2558780 : codemap_->ClearInstanceObject();
2501 : // Clear the handle, such that anyone who accidentally copied them will
2502 : // notice.
2503 2558780 : *instance_.location() = nullptr;
2504 : }
2505 :
2506 : private:
2507 : CodeMap* codemap_;
2508 : Handle<WasmInstanceObject> instance_;
2509 : bool needs_reset;
2510 : };
2511 :
2512 : } // namespace
2513 :
2514 : //============================================================================
2515 : // Implementation of the pimpl idiom for WasmInterpreter::Thread.
2516 : // Instead of placing a pointer to the ThreadImpl inside of the Thread object,
2517 : // we just reinterpret_cast them. ThreadImpls are only allocated inside this
2518 : // translation unit anyway.
2519 : //============================================================================
2520 22359 : WasmInterpreter::State WasmInterpreter::Thread::state() {
2521 22359 : return ToImpl(this)->state();
2522 : }
2523 2563038 : void WasmInterpreter::Thread::InitFrame(const WasmFunction* function,
2524 : WasmValue* args) {
2525 2563038 : ToImpl(this)->InitFrame(function, args);
2526 2563038 : }
2527 2570688 : WasmInterpreter::State WasmInterpreter::Thread::Run(int num_steps) {
2528 2570688 : return ToImpl(this)->Run(num_steps);
2529 : }
2530 0 : void WasmInterpreter::Thread::Pause() { return ToImpl(this)->Pause(); }
2531 5035764 : void WasmInterpreter::Thread::Reset() { return ToImpl(this)->Reset(); }
2532 : WasmInterpreter::Thread::ExceptionHandlingResult
2533 112 : WasmInterpreter::Thread::HandleException(Isolate* isolate) {
2534 112 : return ToImpl(this)->HandleException(isolate);
2535 : }
2536 4176 : pc_t WasmInterpreter::Thread::GetBreakpointPc() {
2537 4176 : return ToImpl(this)->GetBreakpointPc();
2538 : }
2539 4744 : int WasmInterpreter::Thread::GetFrameCount() {
2540 4744 : return ToImpl(this)->GetFrameCount();
2541 : }
2542 1315720 : std::unique_ptr<InterpretedFrame> WasmInterpreter::Thread::GetFrame(int index) {
2543 : DCHECK_LE(0, index);
2544 : DCHECK_GT(GetFrameCount(), index);
2545 : return std::unique_ptr<InterpretedFrame>(
2546 2631440 : ToFrame(new InterpretedFrameImpl(ToImpl(this), index)));
2547 : }
2548 2551694 : WasmValue WasmInterpreter::Thread::GetReturnValue(int index) {
2549 2551694 : return ToImpl(this)->GetReturnValue(index);
2550 : }
2551 100 : TrapReason WasmInterpreter::Thread::GetTrapReason() {
2552 100 : return ToImpl(this)->GetTrapReason();
2553 : }
2554 2502830 : bool WasmInterpreter::Thread::PossibleNondeterminism() {
2555 2502830 : return ToImpl(this)->PossibleNondeterminism();
2556 : }
2557 77252 : uint64_t WasmInterpreter::Thread::NumInterpretedCalls() {
2558 77252 : return ToImpl(this)->NumInterpretedCalls();
2559 : }
2560 26 : void WasmInterpreter::Thread::AddBreakFlags(uint8_t flags) {
2561 : ToImpl(this)->AddBreakFlags(flags);
2562 26 : }
2563 0 : void WasmInterpreter::Thread::ClearBreakFlags() {
2564 : ToImpl(this)->ClearBreakFlags();
2565 0 : }
2566 36 : uint32_t WasmInterpreter::Thread::NumActivations() {
2567 36 : return ToImpl(this)->NumActivations();
2568 : }
2569 45156 : uint32_t WasmInterpreter::Thread::StartActivation() {
2570 45156 : return ToImpl(this)->StartActivation();
2571 : }
2572 45156 : void WasmInterpreter::Thread::FinishActivation(uint32_t id) {
2573 : ToImpl(this)->FinishActivation(id);
2574 45156 : }
2575 4100 : uint32_t WasmInterpreter::Thread::ActivationFrameBase(uint32_t id) {
2576 4100 : return ToImpl(this)->ActivationFrameBase(id);
2577 : }
2578 :
2579 : //============================================================================
2580 : // The implementation details of the interpreter.
2581 : //============================================================================
2582 : class WasmInterpreterInternals : public ZoneObject {
2583 : public:
2584 : // Create a copy of the module bytes for the interpreter, since the passed
2585 : // pointer might be invalidated after constructing the interpreter.
2586 : const ZoneVector<uint8_t> module_bytes_;
2587 : CodeMap codemap_;
2588 : ZoneVector<ThreadImpl> threads_;
2589 :
2590 18637 : WasmInterpreterInternals(Isolate* isolate, Zone* zone,
2591 : const WasmModule* module,
2592 : const ModuleWireBytes& wire_bytes,
2593 : WasmContext* wasm_context)
2594 : : module_bytes_(wire_bytes.start(), wire_bytes.end(), zone),
2595 : codemap_(isolate, module, module_bytes_.data(), zone),
2596 55911 : threads_(zone) {
2597 18637 : threads_.emplace_back(zone, &codemap_, wasm_context);
2598 18637 : }
2599 : };
2600 :
2601 : //============================================================================
2602 : // Implementation of the public interface of the interpreter.
2603 : //============================================================================
2604 18637 : WasmInterpreter::WasmInterpreter(Isolate* isolate, const WasmModule* module,
2605 : const ModuleWireBytes& wire_bytes,
2606 : WasmContext* wasm_context)
2607 : : zone_(isolate->allocator(), ZONE_NAME),
2608 : internals_(new (&zone_) WasmInterpreterInternals(
2609 37274 : isolate, &zone_, module, wire_bytes, wasm_context)) {}
2610 :
2611 18617 : WasmInterpreter::~WasmInterpreter() { internals_->~WasmInterpreterInternals(); }
2612 :
2613 0 : void WasmInterpreter::Run() { internals_->threads_[0].Run(); }
2614 :
2615 0 : void WasmInterpreter::Pause() { internals_->threads_[0].Pause(); }
2616 :
2617 4400 : bool WasmInterpreter::SetBreakpoint(const WasmFunction* function, pc_t pc,
2618 : bool enabled) {
2619 2200 : InterpreterCode* code = internals_->codemap_.GetCode(function);
2620 2200 : size_t size = static_cast<size_t>(code->end - code->start);
2621 : // Check bounds for {pc}.
2622 2200 : if (pc < code->locals.encoded_size || pc >= size) return false;
2623 : // Make a copy of the code before enabling a breakpoint.
2624 2200 : if (enabled && code->orig_start == code->start) {
2625 61 : code->start = reinterpret_cast<byte*>(zone_.New(size));
2626 61 : memcpy(code->start, code->orig_start, size);
2627 61 : code->end = code->start + size;
2628 : }
2629 2200 : bool prev = code->start[pc] == kInternalBreakpoint;
2630 2200 : if (enabled) {
2631 1156 : code->start[pc] = kInternalBreakpoint;
2632 : } else {
2633 1044 : code->start[pc] = code->orig_start[pc];
2634 : }
2635 2200 : return prev;
2636 : }
2637 :
2638 0 : bool WasmInterpreter::GetBreakpoint(const WasmFunction* function, pc_t pc) {
2639 0 : InterpreterCode* code = internals_->codemap_.GetCode(function);
2640 0 : size_t size = static_cast<size_t>(code->end - code->start);
2641 : // Check bounds for {pc}.
2642 0 : if (pc < code->locals.encoded_size || pc >= size) return false;
2643 : // Check if a breakpoint is present at that place in the code.
2644 0 : return code->start[pc] == kInternalBreakpoint;
2645 : }
2646 :
2647 0 : bool WasmInterpreter::SetTracing(const WasmFunction* function, bool enabled) {
2648 0 : UNIMPLEMENTED();
2649 : return false;
2650 : }
2651 :
2652 0 : int WasmInterpreter::GetThreadCount() {
2653 0 : return 1; // only one thread for now.
2654 : }
2655 :
2656 2731039 : WasmInterpreter::Thread* WasmInterpreter::GetThread(int id) {
2657 2731039 : CHECK_EQ(0, id); // only one thread for now.
2658 5462078 : return ToThread(&internals_->threads_[id]);
2659 : }
2660 :
2661 22147 : void WasmInterpreter::AddFunctionForTesting(const WasmFunction* function) {
2662 22147 : internals_->codemap_.AddFunction(function, nullptr, nullptr);
2663 22147 : }
2664 :
2665 38390 : void WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
2666 : const byte* start,
2667 : const byte* end) {
2668 38390 : internals_->codemap_.SetFunctionCode(function, start, end);
2669 19195 : }
2670 :
2671 30 : ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
2672 : Zone* zone, const WasmModule* module, const byte* start, const byte* end) {
2673 : // Create some dummy structures, to avoid special-casing the implementation
2674 : // just for testing.
2675 30 : FunctionSig sig(0, 0, nullptr);
2676 30 : WasmFunction function{&sig, 0, 0, {0, 0}, {0, 0}, false, false};
2677 : InterpreterCode code{
2678 60 : &function, BodyLocalDecls(zone), start, end, nullptr, nullptr, nullptr};
2679 :
2680 : // Now compute and return the control transfers.
2681 30 : SideTable side_table(zone, module, &code);
2682 30 : return side_table.map_;
2683 : }
2684 :
2685 : //============================================================================
2686 : // Implementation of the frame inspection interface.
2687 : //============================================================================
2688 1315159 : const WasmFunction* InterpretedFrame::function() const {
2689 1315159 : return ToImpl(this)->function();
2690 : }
2691 2629598 : int InterpretedFrame::pc() const { return ToImpl(this)->pc(); }
2692 285 : int InterpretedFrame::GetParameterCount() const {
2693 285 : return ToImpl(this)->GetParameterCount();
2694 : }
2695 681 : int InterpretedFrame::GetLocalCount() const {
2696 681 : return ToImpl(this)->GetLocalCount();
2697 : }
2698 465 : int InterpretedFrame::GetStackHeight() const {
2699 465 : return ToImpl(this)->GetStackHeight();
2700 : }
2701 624 : WasmValue InterpretedFrame::GetLocalValue(int index) const {
2702 624 : return ToImpl(this)->GetLocalValue(index);
2703 : }
2704 143 : WasmValue InterpretedFrame::GetStackValue(int index) const {
2705 143 : return ToImpl(this)->GetStackValue(index);
2706 : }
2707 :
2708 : //============================================================================
2709 : // Public API of the heap objects scope.
2710 : //============================================================================
2711 2558850 : WasmInterpreter::HeapObjectsScope::HeapObjectsScope(
2712 : WasmInterpreter* interpreter, Handle<WasmInstanceObject> instance) {
2713 : static_assert(sizeof(data) == sizeof(HeapObjectsScopeImpl), "Size mismatch");
2714 2558850 : new (data) HeapObjectsScopeImpl(&interpreter->internals_->codemap_, instance);
2715 2558850 : }
2716 :
2717 2558850 : WasmInterpreter::HeapObjectsScope::~HeapObjectsScope() {
2718 : reinterpret_cast<HeapObjectsScopeImpl*>(data)->~HeapObjectsScopeImpl();
2719 2558850 : }
2720 :
2721 : #undef TRACE
2722 : #undef FOREACH_INTERNAL_OPCODE
2723 : #undef WASM_CTYPES
2724 : #undef FOREACH_SIMPLE_BINOP
2725 : #undef FOREACH_OTHER_BINOP
2726 : #undef FOREACH_OTHER_UNOP
2727 :
2728 : } // namespace wasm
2729 : } // namespace internal
2730 : } // namespace v8
|