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