v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
wasm-external-refs.cc
Go to the documentation of this file.
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 "src/base/bits.h"
12#include "src/base/ieee754.h"
17#include "src/numbers/ieee754.h"
18#include "src/roots/roots-inl.h"
19#include "src/utils/memcopy.h"
20#include "src/wasm/float16.h"
23
24#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
25 defined(THREAD_SANITIZER) || defined(LEAK_SANITIZER) || \
26 defined(UNDEFINED_SANITIZER)
27#define V8_WITH_SANITIZER
28#endif
29
30#if defined(V8_OS_WIN) && defined(V8_WITH_SANITIZER)
31// With ASAN on Windows we have to reset the thread-in-wasm flag. Exceptions
32// caused by ASAN let the thread-in-wasm flag get out of sync. Even marking
33// functions with DISABLE_ASAN is not sufficient when the compiler produces
34// calls to memset. Therefore we add test-specific code for ASAN on
35// Windows.
36#define RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS
38#endif
39
40#include "src/base/memory.h"
42#include "src/utils/utils.h"
44
45namespace v8::internal::wasm {
46
49
51 WriteUnalignedValue<float>(data, truncf(ReadUnalignedValue<float>(data)));
52}
53
55 WriteUnalignedValue<float>(data, floorf(ReadUnalignedValue<float>(data)));
56}
57
59 WriteUnalignedValue<float>(data, ceilf(ReadUnalignedValue<float>(data)));
60}
61
63 float input = ReadUnalignedValue<float>(data);
64 float value = nearbyintf(input);
65#if V8_OS_AIX
66 value = FpOpWorkaround<float>(input, value);
67#endif
68 WriteUnalignedValue<float>(data, value);
69}
70
72 WriteUnalignedValue<double>(data, trunc(ReadUnalignedValue<double>(data)));
73}
74
76 WriteUnalignedValue<double>(data, floor(ReadUnalignedValue<double>(data)));
77}
78
80 WriteUnalignedValue<double>(data, ceil(ReadUnalignedValue<double>(data)));
81}
82
84 double input = ReadUnalignedValue<double>(data);
85 double value = nearbyint(input);
86#if V8_OS_AIX
87 value = FpOpWorkaround<double>(input, value);
88#endif
89 WriteUnalignedValue<double>(data, value);
90}
91
93 int64_t input = ReadUnalignedValue<int64_t>(data);
94 WriteUnalignedValue<float>(data, static_cast<float>(input));
95}
96
98 uint64_t input = ReadUnalignedValue<uint64_t>(data);
99#if defined(V8_OS_WIN)
100 // On Windows, the FP stack registers calculate with less precision, which
101 // leads to a uint64_t to float32 conversion which does not satisfy the
102 // WebAssembly specification. Therefore we do a different approach here:
103 //
104 // / leading 0 \/ 24 float data bits \/ for rounding \/ trailing 0 \
105 // 00000000000001XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX100000000000000
106 //
107 // Float32 can only represent 24 data bit (1 implicit 1 bit + 23 mantissa
108 // bits). Starting from the most significant 1 bit, we can therefore extract
109 // 24 bits and do the conversion only on them. The other bits can affect the
110 // result only through rounding. Rounding works as follows:
111 // * If the most significant rounding bit is not set, then round down.
112 // * If the most significant rounding bit is set, and at least one of the
113 // other rounding bits is set, then round up.
114 // * If the most significant rounding bit is set, but all other rounding bits
115 // are not set, then round to even.
116 // We can aggregate 'all other rounding bits' in the second-most significant
117 // rounding bit.
118 // The resulting algorithm is therefore as follows:
119 // * Check if the distance between the most significant bit (MSB) and the
120 // least significant bit (LSB) is greater than 25 bits. If the distance is
121 // less or equal to 25 bits, the uint64 to float32 conversion is anyways
122 // exact, and we just use the C++ conversion.
123 // * Find the most significant bit (MSB).
124 // * Starting from the MSB, extract 25 bits (24 data bits + the first rounding
125 // bit).
126 // * The remaining rounding bits are guaranteed to contain at least one 1 bit,
127 // due to the check we did above.
128 // * Store the 25 bits + 1 aggregated bit in an uint32_t.
129 // * Convert this uint32_t to float. The conversion does the correct rounding
130 // now.
131 // * Shift the result back to the original magnitude.
132 uint32_t leading_zeros = base::bits::CountLeadingZeros(input);
133 uint32_t trailing_zeros = base::bits::CountTrailingZeros(input);
134 constexpr uint32_t num_extracted_bits = 25;
135 // Check if there are any rounding bits we have to aggregate.
136 if (leading_zeros + trailing_zeros + num_extracted_bits < 64) {
137 // Shift to extract the data bits.
138 uint32_t num_aggregation_bits = 64 - num_extracted_bits - leading_zeros;
139 // We extract the bits we want to convert. Note that we convert one bit more
140 // than necessary. This bit is a placeholder where we will store the
141 // aggregation bit.
142 int32_t extracted_bits =
143 static_cast<int32_t>(input >> (num_aggregation_bits - 1));
144 // Set the aggregation bit. We don't have to clear the slot first, because
145 // the bit there is also part of the aggregation.
146 extracted_bits |= 1;
147 float result = static_cast<float>(extracted_bits);
148 // We have to shift the result back. The shift amount is
149 // (num_aggregation_bits - 1), which is the shift amount we did originally,
150 // and (-2), which is for the two additional bits we kept originally for
151 // rounding.
152 int32_t shift_back = static_cast<int32_t>(num_aggregation_bits) - 1 - 2;
153 // Calculate the multiplier to shift the extracted bits back to the original
154 // magnitude. This multiplier is a power of two, so in the float32 bit
155 // representation we just have to construct the correct exponent and put it
156 // at the correct bit offset. The exponent consists of 8 bits, starting at
157 // the second MSB (a.k.a '<< 23'). The encoded exponent itself is
158 // ('actual exponent' - 127).
159 int32_t multiplier_bits = ((shift_back - 127) & 0xff) << 23;
160 result *= base::bit_cast<float>(multiplier_bits);
161 WriteUnalignedValue<float>(data, result);
162 return;
163 }
164#endif // defined(V8_OS_WIN)
165 WriteUnalignedValue<float>(data, static_cast<float>(input));
166}
167
169 int64_t input = ReadUnalignedValue<int64_t>(data);
170 WriteUnalignedValue<double>(data, static_cast<double>(input));
171}
172
174 uint64_t input = ReadUnalignedValue<uint64_t>(data);
175 double result = static_cast<double>(input);
176
177#if V8_CC_MSVC
178 // With MSVC we use static_cast<double>(uint32_t) instead of
179 // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
180 // semantics. The idea is to calculate
181 // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
182 uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
183 uint32_t high_word = static_cast<uint32_t>(input >> 32);
184
185 double shift = static_cast<double>(1ull << 32);
186
187 result = static_cast<double>(high_word);
188 result *= shift;
189 result += static_cast<double>(low_word);
190#endif
191
192 WriteUnalignedValue<double>(data, result);
193}
194
196 float input = ReadUnalignedValue<float>(data);
197 if (base::IsValueInRangeForNumericType<int64_t>(input)) {
198 WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
199 return 1;
200 }
201 return 0;
202}
203
205 float input = ReadUnalignedValue<float>(data);
206 if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
207 WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
208 return 1;
209 }
210 return 0;
211}
212
214 double input = ReadUnalignedValue<double>(data);
215 if (base::IsValueInRangeForNumericType<int64_t>(input)) {
216 WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
217 return 1;
218 }
219 return 0;
220}
221
223 double input = ReadUnalignedValue<double>(data);
224 if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
225 WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
226 return 1;
227 }
228 return 0;
229}
230
232 float input = ReadUnalignedValue<float>(data);
233 if (base::IsValueInRangeForNumericType<int64_t>(input)) {
234 WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
235 return;
236 }
237 if (std::isnan(input)) {
238 WriteUnalignedValue<int64_t>(data, 0);
239 return;
240 }
241 if (input < 0.0) {
242 WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::min());
243 return;
244 }
245 WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::max());
246}
247
249 float input = ReadUnalignedValue<float>(data);
250 if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
251 WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
252 return;
253 }
254 if (input >= static_cast<float>(std::numeric_limits<uint64_t>::max())) {
255 WriteUnalignedValue<uint64_t>(data, std::numeric_limits<uint64_t>::max());
256 return;
257 }
258 WriteUnalignedValue<uint64_t>(data, 0);
259}
260
262 double input = ReadUnalignedValue<double>(data);
263 if (base::IsValueInRangeForNumericType<int64_t>(input)) {
264 WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
265 return;
266 }
267 if (std::isnan(input)) {
268 WriteUnalignedValue<int64_t>(data, 0);
269 return;
270 }
271 if (input < 0.0) {
272 WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::min());
273 return;
274 }
275 WriteUnalignedValue<int64_t>(data, std::numeric_limits<int64_t>::max());
276}
277
279 double input = ReadUnalignedValue<double>(data);
280 if (base::IsValueInRangeForNumericType<uint64_t>(input)) {
281 WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
282 return;
283 }
284 if (input >= static_cast<double>(std::numeric_limits<uint64_t>::max())) {
285 WriteUnalignedValue<uint64_t>(data, std::numeric_limits<uint64_t>::max());
286 return;
287 }
288 WriteUnalignedValue<uint64_t>(data, 0);
289}
290
292 WriteUnalignedValue<float>(data, Float16::Read(data).ToFloat32());
293}
294
296 Float16::FromFloat32(ReadUnalignedValue<float>(data)).Write(data);
297}
298
300 int64_t dividend = ReadUnalignedValue<int64_t>(data);
301 int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
302 if (divisor == 0) {
303 return 0;
304 }
305 if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
306 return -1;
307 }
308 WriteUnalignedValue<int64_t>(data, dividend / divisor);
309 return 1;
310}
311
313 int64_t dividend = ReadUnalignedValue<int64_t>(data);
314 int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
315 if (divisor == 0) {
316 return 0;
317 }
318 if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
319 WriteUnalignedValue<int64_t>(data, 0);
320 return 1;
321 }
322 WriteUnalignedValue<int64_t>(data, dividend % divisor);
323 return 1;
324}
325
327 uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
328 uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
329 if (divisor == 0) {
330 return 0;
331 }
332 WriteUnalignedValue<uint64_t>(data, dividend / divisor);
333 return 1;
334}
335
337 uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
338 uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
339 if (divisor == 0) {
340 return 0;
341 }
342 WriteUnalignedValue<uint64_t>(data, dividend % divisor);
343 return 1;
344}
345
346uint32_t word32_rol_wrapper(uint32_t input, uint32_t shift) {
347 return (input << (shift & 31)) | (input >> ((32 - shift) & 31));
348}
349
350uint32_t word32_ror_wrapper(uint32_t input, uint32_t shift) {
351 return (input >> (shift & 31)) | (input << ((32 - shift) & 31));
352}
353
354uint64_t word64_rol_wrapper(uint64_t input, uint32_t shift) {
355 return (input << (shift & 63)) | (input >> ((64 - shift) & 63));
356}
357
358uint64_t word64_ror_wrapper(uint64_t input, uint32_t shift) {
359 return (input >> (shift & 63)) | (input << ((64 - shift) & 63));
360}
361
363 double x = ReadUnalignedValue<double>(data);
364 double y = ReadUnalignedValue<double>(data + sizeof(x));
365 WriteUnalignedValue<double>(data, math::pow(x, y));
366}
367
368template <typename T, T (*float_round_op)(T)>
370 constexpr int n = kSimd128Size / sizeof(T);
371 for (int i = 0; i < n; i++) {
372 T input = ReadUnalignedValue<T>(data + (i * sizeof(T)));
373 T value = float_round_op(input);
374#if V8_OS_AIX
375 value = FpOpWorkaround<T>(input, value);
376#endif
377 WriteUnalignedValue<T>(data + (i * sizeof(T)), value);
378 }
379}
380
384
388
392
396
400
404
408
412
414 return Float16::FromFloat32(std::abs(a.ToFloat32()));
415}
416
420
421Float16 f16_neg(Float16 a) { return Float16::FromFloat32(-(a.ToFloat32())); }
422
426
428 return Float16::FromFloat32(std::sqrt(a.ToFloat32()));
429}
430
434
436 return Float16::FromFloat32(ceilf(a.ToFloat32()));
437}
438
442
444 return Float16::FromFloat32(floorf(a.ToFloat32()));
445}
446
450
452 return Float16::FromFloat32(truncf(a.ToFloat32()));
453}
454
458
460 return Float16::FromFloat32(nearbyintf(a.ToFloat32()));
461}
462
466
467template <typename R, R (*float_bin_op)(Float16, Float16)>
469 constexpr int n = kSimd128Size / sizeof(Float16);
470 for (int i = 0; i < n; i++) {
471 Float16 lhs = Float16::Read(data + (i * sizeof(Float16)));
472 Float16 rhs = Float16::Read(data + kSimd128Size + (i * sizeof(Float16)));
473 R value = float_bin_op(lhs, rhs);
474 WriteUnalignedValue<R>(data + (i * sizeof(R)), value);
475 }
476}
477
478int16_t f16_eq(Float16 a, Float16 b) {
479 return a.ToFloat32() == b.ToFloat32() ? -1 : 0;
480}
481
485
486int16_t f16_ne(Float16 a, Float16 b) {
487 return a.ToFloat32() != b.ToFloat32() ? -1 : 0;
488}
489
493
494int16_t f16_lt(Float16 a, Float16 b) {
495 return a.ToFloat32() < b.ToFloat32() ? -1 : 0;
496}
497
501
502int16_t f16_le(Float16 a, Float16 b) {
503 return a.ToFloat32() <= b.ToFloat32() ? -1 : 0;
504}
505
509
511 return Float16::FromFloat32(a.ToFloat32() + b.ToFloat32());
512}
513
517
519 return Float16::FromFloat32(a.ToFloat32() - b.ToFloat32());
520}
521
525
527 return Float16::FromFloat32(a.ToFloat32() * b.ToFloat32());
528}
529
533
535 return Float16::FromFloat32(base::Divide(a.ToFloat32(), b.ToFloat32()));
536}
537
541
543 return Float16::FromFloat32(JSMin(a.ToFloat32(), b.ToFloat32()));
544}
545
549
551 return Float16::FromFloat32(JSMax(a.ToFloat32(), b.ToFloat32()));
552}
553
557
559 return Float16::FromFloat32(std::min(a.ToFloat32(), b.ToFloat32()));
560}
561
565
567 return Float16::FromFloat32(std::max(a.ToFloat32(), b.ToFloat32()));
568}
569
573
574template <typename T, typename R, R (*float_un_op)(T)>
576 constexpr int n = kSimd128Size / sizeof(T);
577 for (int i = 0; i < n; i++) {
578 T input = ReadUnalignedValue<T>(data + (i * sizeof(T)));
579 R value = float_un_op(input);
580 WriteUnalignedValue<R>(data + (i * sizeof(T)), value);
581 }
582}
583
584int16_t ConvertToIntS(Float16 val) {
585 float f32 = val.ToFloat32();
586 if (std::isnan(f32)) return 0;
587 if (f32 > float{kMaxInt16}) return kMaxInt16;
588 if (f32 < float{kMinInt16}) return kMinInt16;
589 return static_cast<int16_t>(f32);
590}
591
592uint16_t ConvertToIntU(Float16 val) {
593 float f32 = val.ToFloat32();
594 if (std::isnan(f32)) return 0;
595 if (f32 > float{kMaxUInt16}) return kMaxUInt16;
596 if (f32 < float{0}) return 0;
597 return static_cast<uint16_t>(f32);
598}
599
603
607
608Float16 ConvertToF16S(int16_t val) { return Float16::FromFloat32(val); }
609
613
614Float16 ConvertToF16U(uint16_t val) { return Float16::FromFloat32(val); }
615
619
621 // Result is stored in the same buffer, so read all values to local
622 // stack variables first.
623 Float16 a = Float16::Read(data);
624 Float16 b = Float16::Read(data + sizeof(Float16));
625 Float16 c = Float16::Read(data + 2 * sizeof(Float16));
626 Float16 d = Float16::Read(data + 3 * sizeof(Float16));
627
628 WriteUnalignedValue<float>(data, a.ToFloat32());
629 WriteUnalignedValue<float>(data + sizeof(float), b.ToFloat32());
630 WriteUnalignedValue<float>(data + (2 * sizeof(float)), c.ToFloat32());
631 WriteUnalignedValue<float>(data + (3 * sizeof(float)), d.ToFloat32());
632}
633
635#if V8_TARGET_BIG_ENDIAN
636 for (int i = 3, j = 7; i >= 0; i--, j--) {
637 float input = ReadUnalignedValue<float>(data + (i * sizeof(float)));
638 Float16::FromFloat32(input).Write(data + (j * sizeof(Float16)));
639 }
640 for (int i = 0; i < 4; i++) {
641 WriteUnalignedValue<Float16>(data + (i * sizeof(Float16)),
643 }
644#else
645 for (int i = 0; i < 4; i++) {
646 float input = ReadUnalignedValue<float>(data + (i * sizeof(float)));
647 Float16::FromFloat32(input).Write(data + (i * sizeof(Float16)));
648 }
649 for (int i = 4; i < 8; i++) {
650 WriteUnalignedValue<Float16>(data + (i * sizeof(Float16)),
652 }
653#endif
654}
655
657#if V8_TARGET_BIG_ENDIAN
658 for (int i = 1, j = 7; i >= 0; i--, j--) {
659 double input = ReadUnalignedValue<double>(data + (i * sizeof(double)));
660 WriteUnalignedValue<uint16_t>(data + (j * sizeof(uint16_t)),
661 DoubleToFloat16(input));
662 }
663 for (int i = 0; i < 6; i++) {
664 WriteUnalignedValue<Float16>(data + (i * sizeof(Float16)),
666 }
667#else
668 for (int i = 0; i < 2; i++) {
669 double input = ReadUnalignedValue<double>(data + (i * sizeof(double)));
670 WriteUnalignedValue<uint16_t>(data + (i * sizeof(uint16_t)),
671 DoubleToFloat16(input));
672 }
673 for (int i = 2; i < 8; i++) {
674 WriteUnalignedValue<Float16>(data + (i * sizeof(Float16)),
676 }
677#endif
678}
679
680template <float (*float_fma_op)(float, float, float)>
682 constexpr int n = kSimd128Size / sizeof(Float16);
683 for (int i = 0; i < n; i++) {
684 Address offset = data + i * sizeof(Float16);
688 float value = float_fma_op(a.ToFloat32(), b.ToFloat32(), c.ToFloat32());
690 }
691}
692
693float Qfma(float a, float b, float c) { return a * b + c; }
694
698
699float Qfms(float a, float b, float c) { return -(a * b) + c; }
700
704
705namespace {
706class V8_NODISCARD ThreadNotInWasmScope {
707// Asan on Windows triggers exceptions to allocate shadow memory lazily. When
708// this function is called from WebAssembly, these exceptions would be handled
709// by the trap handler before they get handled by Asan, and thereby confuse the
710// thread-in-wasm flag. Therefore we disable ASAN for this function.
711// Alternatively we could reset the thread-in-wasm flag before calling this
712// function. However, as this is only a problem with Asan on Windows, we did not
713// consider it worth the overhead.
714#if defined(RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS)
715
716 public:
717 ThreadNotInWasmScope() : thread_was_in_wasm_(trap_handler::IsThreadInWasm()) {
718 if (thread_was_in_wasm_) {
719 trap_handler::ClearThreadInWasm();
720 }
721 }
722
723 ~ThreadNotInWasmScope() {
724 if (thread_was_in_wasm_) {
725 trap_handler::SetThreadInWasm();
726 }
727 }
728
729 private:
730 bool thread_was_in_wasm_;
731#else
732
733 public:
734 ThreadNotInWasmScope() {
735 // This is needed to avoid compilation errors (unused variable).
736 USE(this);
737 }
738#endif
739};
740
741inline uint8_t* EffectiveAddress(Tagged<WasmTrustedInstanceData> trusted_data,
742 uint32_t mem_index, uintptr_t index) {
743 return trusted_data->memory_base(mem_index) + index;
744}
745
746template <typename V>
747V ReadAndIncrementOffset(Address data, size_t* offset) {
748 V result = ReadUnalignedValue<V>(data + *offset);
749 *offset += sizeof(V);
750 return result;
751}
752
753constexpr int32_t kSuccess = 1;
754constexpr int32_t kOutOfBounds = 0;
755} // namespace
756
757int32_t memory_init_wrapper(Address trusted_data_addr, uint32_t mem_index,
758 uintptr_t dst, uint32_t src, uint32_t seg_index,
759 uint32_t size) {
760 ThreadNotInWasmScope thread_not_in_wasm_scope;
764
765 uint64_t mem_size = trusted_data->memory_size(mem_index);
766 if (!base::IsInBounds<uint64_t>(dst, size, mem_size)) return kOutOfBounds;
767
768 uint32_t seg_size = trusted_data->data_segment_sizes()->get(seg_index);
769 if (!base::IsInBounds<uint32_t>(src, size, seg_size)) return kOutOfBounds;
770
771 uint8_t* seg_start = reinterpret_cast<uint8_t*>(
772 trusted_data->data_segment_starts()->get(seg_index));
773 std::memcpy(EffectiveAddress(trusted_data, mem_index, dst), seg_start + src,
774 size);
775 return kSuccess;
776}
777
778int32_t memory_copy_wrapper(Address trusted_data_addr, uint32_t dst_mem_index,
779 uint32_t src_mem_index, uintptr_t dst,
780 uintptr_t src, uintptr_t size) {
781 ThreadNotInWasmScope thread_not_in_wasm_scope;
785
786 size_t dst_mem_size = trusted_data->memory_size(dst_mem_index);
787 size_t src_mem_size = trusted_data->memory_size(src_mem_index);
788 static_assert(std::is_same_v<size_t, uintptr_t>);
789 if (!base::IsInBounds<size_t>(dst, size, dst_mem_size)) return kOutOfBounds;
790 if (!base::IsInBounds<size_t>(src, size, src_mem_size)) return kOutOfBounds;
791
792 // Use std::memmove, because the ranges can overlap.
793 std::memmove(EffectiveAddress(trusted_data, dst_mem_index, dst),
794 EffectiveAddress(trusted_data, src_mem_index, src), size);
795 return kSuccess;
796}
797
798int32_t memory_fill_wrapper(Address trusted_data_addr, uint32_t mem_index,
799 uintptr_t dst, uint8_t value, uintptr_t size) {
800 ThreadNotInWasmScope thread_not_in_wasm_scope;
802
805
806 uint64_t mem_size = trusted_data->memory_size(mem_index);
807 if (!base::IsInBounds<uint64_t>(dst, size, mem_size)) return kOutOfBounds;
808
809 std::memset(EffectiveAddress(trusted_data, mem_index, dst), value, size);
810 return kSuccess;
811}
812
813namespace {
814inline void* ArrayElementAddress(Address array, uint32_t index,
815 int element_size_bytes) {
816 return reinterpret_cast<void*>(array + WasmArray::kHeaderSize -
817 kHeapObjectTag + index * element_size_bytes);
818}
819inline void* ArrayElementAddress(Tagged<WasmArray> array, uint32_t index,
820 int element_size_bytes) {
821 return ArrayElementAddress(array.ptr(), index, element_size_bytes);
822}
823} // namespace
824
825void array_copy_wrapper(Address raw_dst_array, uint32_t dst_index,
826 Address raw_src_array, uint32_t src_index,
827 uint32_t length) {
828 DCHECK_GT(length, 0);
829 ThreadNotInWasmScope thread_not_in_wasm_scope;
831 Tagged<WasmArray> dst_array = Cast<WasmArray>(Tagged<Object>(raw_dst_array));
832 Tagged<WasmArray> src_array = Cast<WasmArray>(Tagged<Object>(raw_src_array));
833
834 bool overlapping_ranges =
835 dst_array.ptr() == src_array.ptr() &&
836 (dst_index < src_index ? dst_index + length > src_index
837 : src_index + length > dst_index);
838 wasm::CanonicalValueType element_type =
839 src_array->map()->wasm_type_info()->element_type();
840 if (element_type.is_reference()) {
841 ObjectSlot dst_slot = dst_array->ElementSlot(dst_index);
842 ObjectSlot src_slot = src_array->ElementSlot(src_index);
844 if (overlapping_ranges) {
845 heap->MoveRange(dst_array, dst_slot, src_slot, length,
847 } else {
848 heap->CopyRange(dst_array, dst_slot, src_slot, length,
850 }
851 } else {
852 int element_size_bytes = element_type.value_kind_size();
853 void* dst = ArrayElementAddress(dst_array, dst_index, element_size_bytes);
854 void* src = ArrayElementAddress(src_array, src_index, element_size_bytes);
855 size_t copy_size = length * element_size_bytes;
856 if (overlapping_ranges) {
857 MemMove(dst, src, copy_size);
858 } else {
859 MemCopy(dst, src, copy_size);
860 }
861 }
862}
863
864void array_fill_wrapper(Address raw_array, uint32_t index, uint32_t length,
865 uint32_t emit_write_barrier, uint32_t raw_type,
866 Address initial_value_addr) {
867 ThreadNotInWasmScope thread_not_in_wasm_scope;
869 ValueType type = ValueType::FromRawBitField(raw_type);
870 int8_t* initial_element_address = reinterpret_cast<int8_t*>(
871 ArrayElementAddress(raw_array, index, type.value_kind_size()));
872 const int bytes_to_set = length * type.value_kind_size();
873
874 // We implement the general case by setting the first 8 bytes manually, then
875 // filling the rest by exponentially growing {memcpy}s.
876
877 CHECK_GE(static_cast<size_t>(bytes_to_set), sizeof(int64_t));
878
879 switch (type.kind()) {
880 case kI64:
881 case kF64: {
882 // Stack pointers are only aligned to 4 bytes.
883 int64_t initial_value =
884 base::ReadUnalignedValue<int64_t>(initial_value_addr);
885 if (initial_value == 0) {
886 std::memset(initial_element_address, 0, bytes_to_set);
887 return;
888 }
889 // Array elements are only aligned to 4 bytes, therefore
890 // `initial_element_address` may be misaligned as a 64-bit pointer.
892 reinterpret_cast<Address>(initial_element_address), initial_value);
893 break;
894 }
895 case kI32:
896 case kF32: {
897 int32_t initial_value = *reinterpret_cast<int32_t*>(initial_value_addr);
898 if (initial_value == 0) {
899 std::memset(initial_element_address, 0, bytes_to_set);
900 return;
901 }
902 int32_t* base = reinterpret_cast<int32_t*>(initial_element_address);
903 base[0] = base[1] = initial_value;
904 break;
905 }
906 case kF16:
907 case kI16: {
908 // The array.fill input is an i32!
909 int16_t initial_value = *reinterpret_cast<int32_t*>(initial_value_addr);
910 if (initial_value == 0) {
911 std::memset(initial_element_address, 0, bytes_to_set);
912 return;
913 }
914 int16_t* base = reinterpret_cast<int16_t*>(initial_element_address);
915 base[0] = base[1] = base[2] = base[3] = initial_value;
916 break;
917 }
918 case kI8: {
919 // The array.fill input is an i32!
920 int8_t initial_value = *reinterpret_cast<int32_t*>(initial_value_addr);
921 if (initial_value == 0) {
922 std::memset(initial_element_address, 0, bytes_to_set);
923 return;
924 }
925 int8_t* base = reinterpret_cast<int8_t*>(initial_element_address);
926 for (size_t i = 0; i < sizeof(int64_t); i++) {
927 base[i] = initial_value;
928 }
929 break;
930 }
931 case kRefNull:
932 case kRef: {
933 intptr_t uncompressed_pointer =
934 base::ReadUnalignedValue<intptr_t>(initial_value_addr);
935 if constexpr (kTaggedSize == 4) {
936 int32_t* base = reinterpret_cast<int32_t*>(initial_element_address);
937 base[0] = base[1] = static_cast<int32_t>(uncompressed_pointer);
938 } else {
940 reinterpret_cast<Address>(initial_element_address),
941 uncompressed_pointer);
942 }
943 break;
944 }
945 case kS128:
946 // S128 can only be filled with zeros.
947 DCHECK_EQ(base::ReadUnalignedValue<int64_t>(initial_value_addr), 0);
948 std::memset(initial_element_address, 0, bytes_to_set);
949 return;
950 case kVoid:
951 case kTop:
952 case kBottom:
953 UNREACHABLE();
954 }
955
956 int bytes_already_set = sizeof(int64_t);
957
958 while (bytes_already_set * 2 <= bytes_to_set) {
959 std::memcpy(initial_element_address + bytes_already_set,
960 initial_element_address, bytes_already_set);
961 bytes_already_set *= 2;
962 }
963
964 if (bytes_already_set < bytes_to_set) {
965 std::memcpy(initial_element_address + bytes_already_set,
966 initial_element_address, bytes_to_set - bytes_already_set);
967 }
968
969 if (emit_write_barrier) {
970 DCHECK(type.is_reference());
972 Isolate* isolate = Isolate::Current();
973 ObjectSlot start(reinterpret_cast<Address>(initial_element_address));
975 reinterpret_cast<Address>(initial_element_address + bytes_to_set));
976 WriteBarrier::ForRange(isolate->heap(), array, start, end);
977 }
978}
979
980double flat_string_to_f64(Address string_address) {
981 Tagged<String> s = Cast<String>(Tagged<Object>(string_address));
983 std::numeric_limits<double>::quiet_NaN());
984}
985
987 isolate->SwitchStacks(from, isolate->isolate_data()->active_stack());
988}
989
991 isolate->SwitchStacks(from, isolate->isolate_data()->active_stack());
992 isolate->RetireWasmStack(from);
993}
994
995intptr_t switch_to_the_central_stack(Isolate* isolate, uintptr_t current_sp) {
996 ThreadLocalTop* thread_local_top = isolate->thread_local_top();
997 StackGuard* stack_guard = isolate->stack_guard();
998
999 auto secondary_stack_limit = stack_guard->real_jslimit();
1000
1001 stack_guard->SetStackLimitForStackSwitching(
1002 thread_local_top->central_stack_limit_);
1003
1004 thread_local_top->secondary_stack_limit_ = secondary_stack_limit;
1005 thread_local_top->secondary_stack_sp_ = current_sp;
1006 thread_local_top->is_on_central_stack_flag_ = true;
1007
1008 auto counter = isolate->wasm_switch_to_the_central_stack_counter();
1009 isolate->set_wasm_switch_to_the_central_stack_counter(counter + 1);
1010
1011 return thread_local_top->central_stack_sp_;
1012}
1013
1015 ThreadLocalTop* thread_local_top = isolate->thread_local_top();
1016 CHECK_NE(thread_local_top->secondary_stack_sp_, 0);
1017 CHECK_NE(thread_local_top->secondary_stack_limit_, 0);
1018
1019 auto secondary_stack_limit = thread_local_top->secondary_stack_limit_;
1020 thread_local_top->secondary_stack_limit_ = 0;
1021 thread_local_top->secondary_stack_sp_ = 0;
1022 thread_local_top->is_on_central_stack_flag_ = false;
1023
1024 StackGuard* stack_guard = isolate->stack_guard();
1025 stack_guard->SetStackLimitForStackSwitching(secondary_stack_limit);
1026}
1027
1029 ThreadLocalTop* thread_local_top = isolate->thread_local_top();
1030 StackGuard* stack_guard = isolate->stack_guard();
1031 wasm::StackMemory* stack = isolate->isolate_data()->active_stack();
1032 Address central_stack_sp = thread_local_top->central_stack_sp_;
1033 stack->set_stack_switch_info(fp, central_stack_sp);
1034 stack_guard->SetStackLimitForStackSwitching(
1035 thread_local_top->central_stack_limit_);
1036 thread_local_top->is_on_central_stack_flag_ = true;
1037 return central_stack_sp;
1038}
1039
1041 // The stack only contains wasm frames after this JS call.
1042 wasm::StackMemory* stack = isolate->isolate_data()->active_stack();
1043 stack->clear_stack_switch_info();
1044 ThreadLocalTop* thread_local_top = isolate->thread_local_top();
1045 thread_local_top->is_on_central_stack_flag_ = false;
1046 StackGuard* stack_guard = isolate->stack_guard();
1047 stack_guard->SetStackLimitForStackSwitching(
1048 reinterpret_cast<uintptr_t>(stack->jslimit()));
1049}
1050
1051// frame_size includes param slots area and extra frame slots above FP.
1052Address grow_stack(Isolate* isolate, void* current_sp, size_t frame_size,
1053 size_t gap, Address current_fp) {
1054 // Check if this is a real stack overflow.
1055 StackLimitCheck check(isolate);
1056 if (check.WasmHasOverflowed(gap)) {
1057 // If there is no parent, then the current stack is the main isolate stack.
1058 wasm::StackMemory* active_stack = isolate->isolate_data()->active_stack();
1059 if (active_stack->jmpbuf()->parent == nullptr) {
1060 return 0;
1061 }
1062 DCHECK(active_stack->IsActive());
1063 if (!active_stack->Grow(current_fp)) {
1064 return 0;
1065 }
1066
1067 Address new_sp = active_stack->base() - frame_size;
1068 // Here we assume stack values don't refer other moved stack slots.
1069 // A stack grow event happens right in the beginning of the function
1070 // call so moved slots contain only incoming params and frame header.
1071 // So, it is reasonable to assume no self references.
1072 std::memcpy(reinterpret_cast<void*>(new_sp), current_sp, frame_size);
1073
1074#if V8_TARGET_ARCH_ARM64
1075 Address new_fp =
1076 new_sp + (current_fp - reinterpret_cast<Address>(current_sp));
1077 Address old_pc_address = current_fp + CommonFrameConstants::kCallerPCOffset;
1078 Address new_pc_address = new_fp + CommonFrameConstants::kCallerPCOffset;
1079 Address old_signed_pc = base::Memory<Address>(old_pc_address);
1081 isolate, old_signed_pc, new_pc_address + kSystemPointerSize,
1082 old_pc_address + kSystemPointerSize);
1083 WriteUnalignedValue<Address>(new_pc_address, new_signed_pc);
1084#endif
1085
1086 isolate->stack_guard()->SetStackLimitForStackSwitching(
1087 reinterpret_cast<uintptr_t>(active_stack->jslimit()));
1088 return new_sp;
1089 }
1090
1091 return 0;
1092}
1093
1095 // If there is no parent, then the current stack is the main isolate stack.
1096 wasm::StackMemory* active_stack = isolate->isolate_data()->active_stack();
1097 if (active_stack->jmpbuf()->parent == nullptr) {
1098 return 0;
1099 }
1100 DCHECK(active_stack->IsActive());
1101 Address old_fp = active_stack->Shrink();
1102
1103 isolate->stack_guard()->SetStackLimitForStackSwitching(
1104 reinterpret_cast<uintptr_t>(active_stack->jslimit()));
1105 return old_fp;
1106}
1107
1109 // If there is no parent, then the current stack is the main isolate stack.
1110 wasm::StackMemory* active_stack = isolate->isolate_data()->active_stack();
1111 if (active_stack->jmpbuf()->parent == nullptr) {
1112 return 0;
1113 }
1115 return active_stack->old_fp();
1116}
1117
1118} // namespace v8::internal::wasm
1119
1120#undef V8_WITH_SANITIZER
1121#undef RESET_THREAD_IN_WASM_FLAG_FOR_ASAN_ON_WINDOWS
#define T
void Write(base::Address destination)
Definition float16.h:22
float ToFloat32() const
Definition float16.h:30
static Float16 FromFloat32(float f32)
Definition float16.h:26
static Float16 Read(base::Address source)
Definition float16.h:18
static V8_INLINE Isolate * Current()
Definition isolate-inl.h:35
static V8_INLINE Address MoveSignedPC(Isolate *isolate, Address pc, Address new_sp, Address old_sp)
void SetStackLimitForStackSwitching(uintptr_t limit)
V8_INLINE constexpr StorageType ptr() const
static void ForRange(Heap *heap, Tagged< HeapObject > object, TSlot start, TSlot end)
bool Grow(Address current_fp)
Definition stacks.cc:85
constexpr int value_kind_size() const
Definition value-type.h:485
constexpr bool is_reference() const
Definition value-type.h:600
static constexpr ValueType FromRawBitField(uint32_t bits)
Definition value-type.h:913
int start
int end
int32_t offset
ZoneVector< RpoNumber > & result
int x
int n
Definition mul-fft.cc:296
int int32_t
Definition unicode.cc:40
constexpr unsigned CountLeadingZeros(T value)
Definition bits.h:100
constexpr unsigned CountTrailingZeros(T value)
Definition bits.h:144
static V ReadUnalignedValue(Address p)
Definition memory.h:28
constexpr bool IsInBounds(T index, T length, T max)
Definition bounds.h:49
V8_INLINE Dest bit_cast(Source const &source)
Definition macros.h:95
T & Memory(Address addr)
Definition memory.h:18
static void WriteUnalignedValue(Address p, V value)
Definition memory.h:41
T Divide(T x, T y)
double pow(double x, double y)
Definition ieee754.cc:14
void f32_trunc_wrapper(Address data)
void f16x8_eq_wrapper(Address data)
void int64_to_float32_wrapper(Address data)
void uint64_to_float32_wrapper(Address data)
void switch_stacks(Isolate *isolate, wasm::StackMemory *from)
void simd_float16_fma_wrapper(Address data)
void f16x8_min_wrapper(Address data)
int32_t memory_copy_wrapper(Address trusted_data_addr, uint32_t dst_mem_index, uint32_t src_mem_index, uintptr_t dst, uintptr_t src, uintptr_t size)
void f16x8_sqrt_wrapper(Address data)
void f16x8_qfma_wrapper(Address data)
int32_t uint64_div_wrapper(Address data)
void f64_nearest_int_wrapper(Address data)
Float16 f16_trunc(Float16 a)
void f16x8_floor_wrapper(Address data)
void f16x8_abs_wrapper(Address data)
Float16 f16_abs(Float16 a)
Float16 f16_floor(Float16 a)
Float16 ConvertToF16S(int16_t val)
uint32_t word32_rol_wrapper(uint32_t input, uint32_t shift)
Float16 f16_div(Float16 a, Float16 b)
void f32x4_floor_wrapper(Address data)
void f16x8_sub_wrapper(Address data)
void f16x8_sconvert_i16x8_wrapper(Address data)
Address shrink_stack(Isolate *isolate)
Float16 f16_pmax(Float16 a, Float16 b)
void float16_to_float32_wrapper(Address data)
void simd_float_un_wrapper(Address data)
uint32_t word32_ror_wrapper(uint32_t input, uint32_t shift)
Float16 f16_sub(Float16 a, Float16 b)
intptr_t switch_to_the_central_stack_for_js(Isolate *isolate, Address fp)
double flat_string_to_f64(Address string_address)
Address grow_stack(Isolate *isolate, void *current_sp, size_t frame_size, size_t gap, Address current_fp)
void f16x8_demote_f64x2_zero_wrapper(Address data)
int32_t memory_init_wrapper(Address trusted_data_addr, uint32_t mem_index, uintptr_t dst, uint32_t src, uint32_t seg_index, uint32_t size)
void f16x8_pmin_wrapper(Address data)
Float16 f16_min(Float16 a, Float16 b)
void f16x8_lt_wrapper(Address data)
void float64_to_uint64_sat_wrapper(Address data)
void switch_from_the_central_stack(Isolate *isolate)
void simd_float_round_wrapper(Address data)
void f64x2_ceil_wrapper(Address data)
int16_t f16_lt(Float16 a, Float16 b)
void f16x8_nearest_int_wrapper(Address data)
Float16 f16_neg(Float16 a)
float Qfma(float a, float b, float c)
int32_t float32_to_uint64_wrapper(Address data)
void f32_nearest_int_wrapper(Address data)
int16_t f16_eq(Float16 a, Float16 b)
int32_t float64_to_int64_wrapper(Address data)
int32_t float32_to_int64_wrapper(Address data)
Float16 ConvertToF16U(uint16_t val)
void f16x8_mul_wrapper(Address data)
intptr_t switch_to_the_central_stack(Isolate *isolate, uintptr_t current_sp)
void f64x2_trunc_wrapper(Address data)
uint64_t word64_rol_wrapper(uint64_t input, uint32_t shift)
float Qfms(float a, float b, float c)
void simd_float16_bin_wrapper(Address data)
int32_t memory_fill_wrapper(Address trusted_data_addr, uint32_t mem_index, uintptr_t dst, uint8_t value, uintptr_t size)
void f16x8_neg_wrapper(Address data)
void f16x8_pmax_wrapper(Address data)
int32_t int64_div_wrapper(Address data)
void f64x2_floor_wrapper(Address data)
void f32x4_ceil_wrapper(Address data)
uint16_t ConvertToIntU(Float16 val)
void f16x8_ceil_wrapper(Address data)
void float64_pow_wrapper(Address data)
void float32_to_int64_sat_wrapper(Address data)
void f32x4_trunc_wrapper(Address data)
Float16 f16_max(Float16 a, Float16 b)
uint64_t word64_ror_wrapper(uint64_t input, uint32_t shift)
void i16x8_uconvert_f16x8_wrapper(Address data)
void f16x8_div_wrapper(Address data)
void uint64_to_float64_wrapper(Address data)
void float64_to_int64_sat_wrapper(Address data)
void f16x8_trunc_wrapper(Address data)
Float16 f16_sqrt(Float16 a)
void i16x8_sconvert_f16x8_wrapper(Address data)
void array_copy_wrapper(Address raw_dst_array, uint32_t dst_index, Address raw_src_array, uint32_t src_index, uint32_t length)
Float16 f16_ceil(Float16 a)
void array_fill_wrapper(Address raw_array, uint32_t index, uint32_t length, uint32_t emit_write_barrier, uint32_t raw_type, Address initial_value_addr)
Float16 f16_nearest_int(Float16 a)
int16_t f16_le(Float16 a, Float16 b)
void f16x8_demote_f32x4_zero_wrapper(Address data)
void f16x8_add_wrapper(Address data)
int32_t uint64_mod_wrapper(Address data)
void f64_ceil_wrapper(Address data)
void f32x4_nearest_int_wrapper(Address data)
int16_t ConvertToIntS(Float16 val)
void return_switch(Isolate *isolate, wasm::StackMemory *from)
void f16x8_qfms_wrapper(Address data)
void f32_floor_wrapper(Address data)
int32_t float64_to_uint64_wrapper(Address data)
void f16x8_uconvert_i16x8_wrapper(Address data)
void switch_from_the_central_stack_for_js(Isolate *isolate)
Address load_old_fp(Isolate *isolate)
void f16x8_max_wrapper(Address data)
void float32_to_float16_wrapper(Address data)
Float16 f16_mul(Float16 a, Float16 b)
int32_t int64_mod_wrapper(Address data)
void f64_trunc_wrapper(Address data)
void f32x4_promote_low_f16x8_wrapper(Address data)
Float16 f16_add(Float16 a, Float16 b)
void f16x8_ne_wrapper(Address data)
int16_t f16_ne(Float16 a, Float16 b)
void f64x2_nearest_int_wrapper(Address data)
Float16 f16_pmin(Float16 a, Float16 b)
void f32_ceil_wrapper(Address data)
void f64_floor_wrapper(Address data)
void int64_to_float64_wrapper(Address data)
void float32_to_uint64_sat_wrapper(Address data)
void f16x8_le_wrapper(Address data)
constexpr int kTaggedSize
Definition globals.h:542
constexpr int kSimd128Size
Definition globals.h:706
@ UPDATE_WRITE_BARRIER
Definition objects.h:55
double FlatStringToDouble(Tagged< String > string, ConversionFlag flag, double empty_string_val)
T JSMax(T x, T y)
Definition utils.h:75
Tagged(T object) -> Tagged< T >
constexpr int kMaxUInt16
Definition globals.h:382
constexpr int kMinInt16
Definition globals.h:381
constexpr int kSystemPointerSize
Definition globals.h:410
V8_EXPORT_PRIVATE void MemMove(void *dest, const void *src, size_t size)
Definition memcopy.h:189
const int kHeapObjectTag
Definition v8-internal.h:72
constexpr int kMaxInt16
Definition globals.h:380
uint16_t DoubleToFloat16(double value)
void MemCopy(void *dest, const void *src, size_t size)
Definition memcopy.h:124
T JSMin(T x, T y)
Definition utils.h:84
Tagged< To > Cast(Tagged< From > value, const v8::SourceLocation &loc=INIT_SOURCE_LOCATION_IN_DEBUG)
Definition casting.h:150
#define CHECK_GE(lhs, rhs)
#define CHECK_NE(lhs, rhs)
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
#define USE(...)
Definition macros.h:293
#define V8_NODISCARD
Definition v8config.h:693