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 <math.h>
6#include <stdint.h>
7#include <stdlib.h>
8
9#include <limits>
10
11#include "include/v8config.h"
12#include "src/base/bits.h"
13#include "src/base/ieee754.h"
14#include "src/base/safe_conversions.h"
15#include "src/common/assert-scope.h"
16#include "src/utils/memcopy.h"
17#include "src/wasm/wasm-objects-inl.h"
18
19#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
20    defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) ||    \
21    defined(UNDEFINED_SANITIZER)
22#define V8_WITH_SANITIZER
23#endif
24
25#if defined(V8_OS_WIN) && defined(V8_WITH_SANITIZER)
26// With ASAN on Windows we have to reset the thread-in-wasm flag. Exceptions
27// caused by ASAN let the thread-in-wasm flag get out of sync. Even marking
28// functions with DISABLE_ASAN is not sufficient when the compiler produces
29// calls to memset. Therefore we add test-specific code for ASAN on
30// Windows.
31#define RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS
32#include "src/trap-handler/trap-handler.h"
33#endif
34
35#include "src/base/memory.h"
36#include "src/utils/utils.h"
37#include "src/wasm/wasm-external-refs.h"
38
39namespace v8 {
40namespace internal {
41namespace wasm {
42
43using base::ReadUnalignedValue;
44using base::WriteUnalignedValue;
45
46void f32_trunc_wrapper(Address data) {
47  WriteUnalignedValue<float>(data, truncf(ReadUnalignedValue<float>(data)));
48}
49
50void f32_floor_wrapper(Address data) {
51  WriteUnalignedValue<float>(data, floorf(ReadUnalignedValue<float>(data)));
52}
53
54void f32_ceil_wrapper(Address data) {
55  WriteUnalignedValue<float>(data, ceilf(ReadUnalignedValue<float>(data)));
56}
57
58void f32_nearest_int_wrapper(Address data) {
59  float input = ReadUnalignedValue<float>(data);
60  float value = nearbyintf(input);
61#if V8_OS_AIX
62  value = FpOpWorkaround<float>(input, value);
63#endif
64  WriteUnalignedValue<float>(data, value);
65}
66
67void f64_trunc_wrapper(Address data) {
68  WriteUnalignedValue<double>(data, trunc(ReadUnalignedValue<double>(data)));
69}
70
71void f64_floor_wrapper(Address data) {
72  WriteUnalignedValue<double>(data, floor(ReadUnalignedValue<double>(data)));
73}
74
75void f64_ceil_wrapper(Address data) {
76  WriteUnalignedValue<double>(data, ceil(ReadUnalignedValue<double>(data)));
77}
78
79void f64_nearest_int_wrapper(Address data) {
80  double input = ReadUnalignedValue<double>(data);
81  double value = nearbyint(input);
82#if V8_OS_AIX
83  value = FpOpWorkaround<double>(input, value);
84#endif
85  WriteUnalignedValue<double>(data, value);
86}
87
88void int64_to_float32_wrapper(Address data) {
89  int64_t input = ReadUnalignedValue<int64_t>(data);
90  WriteUnalignedValue<float>(data, static_cast<float>(input));
91}
92
93void uint64_to_float32_wrapper(Address data) {
94  uint64_t input = ReadUnalignedValue<uint64_t>(data);
95#if defined(V8_OS_WIN)
96  // On Windows, the FP stack registers calculate with less precision, which
97  // leads to a uint64_t to float32 conversion which does not satisfy the
98  // WebAssembly specification. Therefore we do a different approach here:
99  //
100  // / leading 0 \/  24 float data bits  \/  for rounding \/ trailing 0 \
101  // 00000000000001XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX100000000000000
102  //
103  // Float32 can only represent 24 data bit (1 implicit 1 bit + 23 mantissa
104  // bits). Starting from the most significant 1 bit, we can therefore extract
105  // 24 bits and do the conversion only on them. The other bits can affect the
106  // result only through rounding. Rounding works as follows:
107  // * If the most significant rounding bit is not set, then round down.
108  // * If the most significant rounding bit is set, and at least one of the
109  //   other rounding bits is set, then round up.
110  // * If the most significant rounding bit is set, but all other rounding bits
111  //   are not set, then round to even.
112  // We can aggregate 'all other rounding bits' in the second-most significant
113  // rounding bit.
114  // The resulting algorithm is therefore as follows:
115  // * Check if the distance between the most significant bit (MSB) and the
116  //   least significant bit (LSB) is greater than 25 bits. If the distance is
117  //   less or equal to 25 bits, the uint64 to float32 conversion is anyways
118  //   exact, and we just use the C++ conversion.
119  // * Find the most significant bit (MSB).
120  // * Starting from the MSB, extract 25 bits (24 data bits + the first rounding
121  //   bit).
122  // * The remaining rounding bits are guaranteed to contain at least one 1 bit,
123  //   due to the check we did above.
124  // * Store the 25 bits + 1 aggregated bit in an uint32_t.
125  // * Convert this uint32_t to float. The conversion does the correct rounding
126  //   now.
127  // * Shift the result back to the original magnitude.
128  uint32_t leading_zeros = base::bits::CountLeadingZeros(input);
129  uint32_t trailing_zeros = base::bits::CountTrailingZeros(input);
130  constexpr uint32_t num_extracted_bits = 25;
131  // Check if there are any rounding bits we have to aggregate.
132  if (leading_zeros + trailing_zeros + num_extracted_bits < 64) {
133    // Shift to extract the data bits.
134    uint32_t num_aggregation_bits = 64 - num_extracted_bits - leading_zeros;
135    // We extract the bits we want to convert. Note that we convert one bit more
136    // than necessary. This bit is a placeholder where we will store the
137    // aggregation bit.
138    int32_t extracted_bits =
139        static_cast<int32_t>(input >> (num_aggregation_bits - 1));
140    // Set the aggregation bit. We don't have to clear the slot first, because
141    // the bit there is also part of the aggregation.
142    extracted_bits |= 1;
143    float result = static_cast<float>(extracted_bits);
144    // We have to shift the result back. The shift amount is
145    // (num_aggregation_bits - 1), which is the shift amount we did originally,
146    // and (-2), which is for the two additional bits we kept originally for
147    // rounding.
148    int32_t shift_back = static_cast<int32_t>(num_aggregation_bits) - 1 - 2;
149    // Calculate the multiplier to shift the extracted bits back to the original
150    // magnitude. This multiplier is a power of two, so in the float32 bit
151    // representation we just have to construct the correct exponent and put it
152    // at the correct bit offset. The exponent consists of 8 bits, starting at
153    // the second MSB (a.k.a '<< 23'). The encoded exponent itself is
154    // ('actual exponent' - 127).
155    int32_t multiplier_bits = ((shift_back - 127) & 0xff) << 23;
156    result *= bit_cast<float>(multiplier_bits);
157    WriteUnalignedValue<float>(data, result);
158    return;
159  }
160#endif  // defined(V8_OS_WIN)
161  WriteUnalignedValue<float>(data, static_cast<float>(input));
162}
163
164void int64_to_float64_wrapper(Address data) {
165  int64_t input = ReadUnalignedValue<int64_t>(data);
166  WriteUnalignedValue<double>(data, static_cast<double>(input));
167}
168
169void uint64_to_float64_wrapper(Address data) {
170  uint64_t input = ReadUnalignedValue<uint64_t>(data);
171  double result = static_cast<double>(input);
172
173#if V8_CC_MSVC
174  // With MSVC we use static_cast<double>(uint32_t) instead of
175  // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
176  // semantics. The idea is to calculate
177  // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
178  uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
179  uint32_t high_word = static_cast<uint32_t>(input >> 32);
180
181  double shift = static_cast<double>(1ull << 32);
182
183  result = static_cast<double>(high_word);
184  result *= shift;
185  result += static_cast<double>(low_word);
186#endif
187
188  WriteUnalignedValue<double>(data, result);
189}
190
191int32_t float32_to_int64_wrapper(Address data) {
192  float input = ReadUnalignedValue<float>(data);
193  if (base::IsValueInRangeForNumericType<int64_t>(input)) {
194    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
195    return 1;
196  }
197  return 0;
198}
199
200int32_t float32_to_uint64_wrapper(Address data) {
201  float input = ReadUnalignedValue<float>(data);
202  if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
203    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
204    return 1;
205  }
206  return 0;
207}
208
209int32_t float64_to_int64_wrapper(Address data) {
210  double input = ReadUnalignedValue<double>(data);
211  if (base::IsValueInRangeForNumericType<int64_t>(input)) {
212    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
213    return 1;
214  }
215  return 0;
216}
217
218int32_t float64_to_uint64_wrapper(Address data) {
219  double input = ReadUnalignedValue<double>(data);
220  if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
221    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
222    return 1;
223  }
224  return 0;
225}
226
227void float32_to_int64_sat_wrapper(Address data) {
228  float input = ReadUnalignedValue<float>(data);
229  if (base::IsValueInRangeForNumericType<int64_t>(input)) {
230    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
231    return;
232  }
233  if (std::isnan(input)) {
234    WriteUnalignedValue<int64_t>(data, 0);
235    return;
236  }
237  if (input < 0.0) {
238    WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::min());
239    return;
240  }
241  WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::max());
242}
243
244void float32_to_uint64_sat_wrapper(Address data) {
245  float input = ReadUnalignedValue<float>(data);
246  if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
247    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
248    return;
249  }
250  if (input >= static_cast<float>(std::numeric_limits<uint64_t>::max())) {
251    WriteUnalignedValue<uint64_t>(data, std::numeric_limits<uint64_t>::max());
252    return;
253  }
254  WriteUnalignedValue<uint64_t>(data, 0);
255}
256
257void float64_to_int64_sat_wrapper(Address data) {
258  double input = ReadUnalignedValue<double>(data);
259  if (base::IsValueInRangeForNumericType<int64_t>(input)) {
260    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
261    return;
262  }
263  if (std::isnan(input)) {
264    WriteUnalignedValue<int64_t>(data, 0);
265    return;
266  }
267  if (input < 0.0) {
268    WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::min());
269    return;
270  }
271  WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::max());
272}
273
274void float64_to_uint64_sat_wrapper(Address data) {
275  double input = ReadUnalignedValue<double>(data);
276  if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
277    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
278    return;
279  }
280  if (input >= static_cast<double>(std::numeric_limits<uint64_t>::max())) {
281    WriteUnalignedValue<uint64_t>(data, std::numeric_limits<uint64_t>::max());
282    return;
283  }
284  WriteUnalignedValue<uint64_t>(data, 0);
285}
286
287int32_t int64_div_wrapper(Address data) {
288  int64_t dividend = ReadUnalignedValue<int64_t>(data);
289  int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
290  if (divisor == 0) {
291    return 0;
292  }
293  if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
294    return -1;
295  }
296  WriteUnalignedValue<int64_t>(data, dividend / divisor);
297  return 1;
298}
299
300int32_t int64_mod_wrapper(Address data) {
301  int64_t dividend = ReadUnalignedValue<int64_t>(data);
302  int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
303  if (divisor == 0) {
304    return 0;
305  }
306  if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
307    WriteUnalignedValue<int64_t>(data, 0);
308    return 1;
309  }
310  WriteUnalignedValue<int64_t>(data, dividend % divisor);
311  return 1;
312}
313
314int32_t uint64_div_wrapper(Address data) {
315  uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
316  uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
317  if (divisor == 0) {
318    return 0;
319  }
320  WriteUnalignedValue<uint64_t>(data, dividend / divisor);
321  return 1;
322}
323
324int32_t uint64_mod_wrapper(Address data) {
325  uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
326  uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
327  if (divisor == 0) {
328    return 0;
329  }
330  WriteUnalignedValue<uint64_t>(data, dividend % divisor);
331  return 1;
332}
333
334uint32_t word32_ctz_wrapper(Address data) {
335  return base::bits::CountTrailingZeros(ReadUnalignedValue<uint32_t>(data));
336}
337
338uint32_t word64_ctz_wrapper(Address data) {
339  return base::bits::CountTrailingZeros(ReadUnalignedValue<uint64_t>(data));
340}
341
342uint32_t word32_popcnt_wrapper(Address data) {
343  return base::bits::CountPopulation(ReadUnalignedValue<uint32_t>(data));
344}
345
346uint32_t word64_popcnt_wrapper(Address data) {
347  return base::bits::CountPopulation(ReadUnalignedValue<uint64_t>(data));
348}
349
350uint32_t word32_rol_wrapper(Address data) {
351  uint32_t input = ReadUnalignedValue<uint32_t>(data);
352  uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
353  return (input << shift) | (input >> ((32 - shift) & 31));
354}
355
356uint32_t word32_ror_wrapper(Address data) {
357  uint32_t input = ReadUnalignedValue<uint32_t>(data);
358  uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
359  return (input >> shift) | (input << ((32 - shift) & 31));
360}
361
362void word64_rol_wrapper(Address data) {
363  uint64_t input = ReadUnalignedValue<uint64_t>(data);
364  uint64_t shift = ReadUnalignedValue<uint64_t>(data + sizeof(input)) & 63;
365  uint64_t result = (input << shift) | (input >> ((64 - shift) & 63));
366  WriteUnalignedValue<uint64_t>(data, result);
367}
368
369void word64_ror_wrapper(Address data) {
370  uint64_t input = ReadUnalignedValue<uint64_t>(data);
371  uint64_t shift = ReadUnalignedValue<uint64_t>(data + sizeof(input)) & 63;
372  uint64_t result = (input >> shift) | (input << ((64 - shift) & 63));
373  WriteUnalignedValue<uint64_t>(data, result);
374}
375
376void float64_pow_wrapper(Address data) {
377  double x = ReadUnalignedValue<double>(data);
378  double y = ReadUnalignedValue<double>(data + sizeof(x));
379  WriteUnalignedValue<double>(data, base::ieee754::pow(x, y));
380}
381
382template <typename T, T (*float_round_op)(T)>
383void simd_float_round_wrapper(Address data) {
384  constexpr int n = kSimd128Size / sizeof(T);
385  for (int i = 0; i < n; i++) {
386    T input = ReadUnalignedValue<T>(data + (i * sizeof(T)));
387    T value = float_round_op(input);
388#if V8_OS_AIX
389    value = FpOpWorkaround<T>(input, value);
390#endif
391    WriteUnalignedValue<T>(data + (i * sizeof(T)), value);
392  }
393}
394
395void f64x2_ceil_wrapper(Address data) {
396  simd_float_round_wrapper<double, &ceil>(data);
397}
398
399void f64x2_floor_wrapper(Address data) {
400  simd_float_round_wrapper<double, &floor>(data);
401}
402
403void f64x2_trunc_wrapper(Address data) {
404  simd_float_round_wrapper<double, &trunc>(data);
405}
406
407void f64x2_nearest_int_wrapper(Address data) {
408  simd_float_round_wrapper<double, &nearbyint>(data);
409}
410
411void f32x4_ceil_wrapper(Address data) {
412  simd_float_round_wrapper<float, &ceilf>(data);
413}
414
415void f32x4_floor_wrapper(Address data) {
416  simd_float_round_wrapper<float, &floorf>(data);
417}
418
419void f32x4_trunc_wrapper(Address data) {
420  simd_float_round_wrapper<float, &truncf>(data);
421}
422
423void f32x4_nearest_int_wrapper(Address data) {
424  simd_float_round_wrapper<float, &nearbyintf>(data);
425}
426
427namespace {
428class V8_NODISCARD ThreadNotInWasmScope {
429// Asan on Windows triggers exceptions to allocate shadow memory lazily. When
430// this function is called from WebAssembly, these exceptions would be handled
431// by the trap handler before they get handled by Asan, and thereby confuse the
432// thread-in-wasm flag. Therefore we disable ASAN for this function.
433// Alternatively we could reset the thread-in-wasm flag before calling this
434// function. However, as this is only a problem with Asan on Windows, we did not
435// consider it worth the overhead.
436#if defined(RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS)
437
438 public:
439  ThreadNotInWasmScope() : thread_was_in_wasm_(trap_handler::IsThreadInWasm()) {
440    if (thread_was_in_wasm_) {
441      trap_handler::ClearThreadInWasm();
442    }
443  }
444
445  ~ThreadNotInWasmScope() {
446    if (thread_was_in_wasm_) {
447      trap_handler::SetThreadInWasm();
448    }
449  }
450
451 private:
452  bool thread_was_in_wasm_;
453#else
454
455 public:
456  ThreadNotInWasmScope() {
457    // This is needed to avoid compilation errors (unused variable).
458    USE(this);
459  }
460#endif
461};
462
463inline byte* EffectiveAddress(WasmInstanceObject instance, uintptr_t index) {
464  return instance.memory_start() + index;
465}
466
467template <typename V>
468V ReadAndIncrementOffset(Address data, size_t* offset) {
469  V result = ReadUnalignedValue<V>(data + *offset);
470  *offset += sizeof(V);
471  return result;
472}
473
474constexpr int32_t kSuccess = 1;
475constexpr int32_t kOutOfBounds = 0;
476}  // namespace
477
478int32_t memory_init_wrapper(Address data) {
479  ThreadNotInWasmScope thread_not_in_wasm_scope;
480  DisallowGarbageCollection no_gc;
481  size_t offset = 0;
482  Object raw_instance = ReadAndIncrementOffset<Object>(data, &offset);
483  WasmInstanceObject instance = WasmInstanceObject::cast(raw_instance);
484  uintptr_t dst = ReadAndIncrementOffset<uintptr_t>(data, &offset);
485  uint32_t src = ReadAndIncrementOffset<uint32_t>(data, &offset);
486  uint32_t seg_index = ReadAndIncrementOffset<uint32_t>(data, &offset);
487  uint32_t size = ReadAndIncrementOffset<uint32_t>(data, &offset);
488
489  uint64_t mem_size = instance.memory_size();
490  if (!base::IsInBounds<uint64_t>(dst, size, mem_size)) return kOutOfBounds;
491
492  uint32_t seg_size = instance.data_segment_sizes()[seg_index];
493  if (!base::IsInBounds<uint32_t>(src, size, seg_size)) return kOutOfBounds;
494
495  byte* seg_start =
496      reinterpret_cast<byte*>(instance.data_segment_starts()[seg_index]);
497  std::memcpy(EffectiveAddress(instance, dst), seg_start + src, size);
498  return kSuccess;
499}
500
501int32_t memory_copy_wrapper(Address data) {
502  ThreadNotInWasmScope thread_not_in_wasm_scope;
503  DisallowGarbageCollection no_gc;
504  size_t offset = 0;
505  Object raw_instance = ReadAndIncrementOffset<Object>(data, &offset);
506  WasmInstanceObject instance = WasmInstanceObject::cast(raw_instance);
507  uintptr_t dst = ReadAndIncrementOffset<uintptr_t>(data, &offset);
508  uintptr_t src = ReadAndIncrementOffset<uintptr_t>(data, &offset);
509  uintptr_t size = ReadAndIncrementOffset<uintptr_t>(data, &offset);
510
511  uint64_t mem_size = instance.memory_size();
512  if (!base::IsInBounds<uint64_t>(dst, size, mem_size)) return kOutOfBounds;
513  if (!base::IsInBounds<uint64_t>(src, size, mem_size)) return kOutOfBounds;
514
515  // Use std::memmove, because the ranges can overlap.
516  std::memmove(EffectiveAddress(instance, dst), EffectiveAddress(instance, src),
517               size);
518  return kSuccess;
519}
520
521int32_t memory_fill_wrapper(Address data) {
522  ThreadNotInWasmScope thread_not_in_wasm_scope;
523  DisallowGarbageCollection no_gc;
524
525  size_t offset = 0;
526  Object raw_instance = ReadAndIncrementOffset<Object>(data, &offset);
527  WasmInstanceObject instance = WasmInstanceObject::cast(raw_instance);
528  uintptr_t dst = ReadAndIncrementOffset<uintptr_t>(data, &offset);
529  uint8_t value =
530      static_cast<uint8_t>(ReadAndIncrementOffset<uint32_t>(data, &offset));
531  uintptr_t size = ReadAndIncrementOffset<uintptr_t>(data, &offset);
532
533  uint64_t mem_size = instance.memory_size();
534  if (!base::IsInBounds<uint64_t>(dst, size, mem_size)) return kOutOfBounds;
535
536  std::memset(EffectiveAddress(instance, dst), value, size);
537  return kSuccess;
538}
539
540namespace {
541inline void* ArrayElementAddress(WasmArray array, uint32_t index,
542                                 int element_size_bytes) {
543  return reinterpret_cast<void*>(array.ptr() + WasmArray::kHeaderSize -
544                                 kHeapObjectTag + index * element_size_bytes);
545}
546}  // namespace
547
548void array_copy_wrapper(Address raw_instance, Address raw_dst_array,
549                        uint32_t dst_index, Address raw_src_array,
550                        uint32_t src_index, uint32_t length) {
551  DCHECK_GT(length, 0);
552  ThreadNotInWasmScope thread_not_in_wasm_scope;
553  DisallowGarbageCollection no_gc;
554  WasmArray dst_array = WasmArray::cast(Object(raw_dst_array));
555  WasmArray src_array = WasmArray::cast(Object(raw_src_array));
556
557  bool overlapping_ranges =
558      dst_array.ptr() == src_array.ptr() &&
559      (dst_index < src_index ? dst_index + length > src_index
560                             : src_index + length > dst_index);
561  wasm::ValueType element_type = src_array.type()->element_type();
562  if (element_type.is_reference()) {
563    WasmInstanceObject instance =
564        WasmInstanceObject::cast(Object(raw_instance));
565    Isolate* isolate = Isolate::FromRootAddress(instance.isolate_root());
566    ObjectSlot dst_slot = dst_array.ElementSlot(dst_index);
567    ObjectSlot src_slot = src_array.ElementSlot(src_index);
568    if (overlapping_ranges) {
569      isolate->heap()->MoveRange(dst_array, dst_slot, src_slot, length,
570                                 UPDATE_WRITE_BARRIER);
571    } else {
572      isolate->heap()->CopyRange(dst_array, dst_slot, src_slot, length,
573                                 UPDATE_WRITE_BARRIER);
574    }
575  } else {
576    int element_size_bytes = element_type.value_kind_size();
577    void* dst = ArrayElementAddress(dst_array, dst_index, element_size_bytes);
578    void* src = ArrayElementAddress(src_array, src_index, element_size_bytes);
579    size_t copy_size = length * element_size_bytes;
580    if (overlapping_ranges) {
581      MemMove(dst, src, copy_size);
582    } else {
583      MemCopy(dst, src, copy_size);
584    }
585  }
586}
587
588static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;
589
590void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
591  wasm_trap_callback_for_testing = callback;
592}
593
594void call_trap_callback_for_testing() {
595  if (wasm_trap_callback_for_testing) {
596    wasm_trap_callback_for_testing();
597  }
598}
599
600}  // namespace wasm
601}  // namespace internal
602}  // namespace v8
603
604#undef V8_WITH_SANITIZER
605#undef RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS
606