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 <math.h>
6 : #include <stdint.h>
7 : #include <stdlib.h>
8 : #include <limits>
9 :
10 : #include "include/v8config.h"
11 :
12 : #include "src/base/bits.h"
13 : #include "src/base/ieee754.h"
14 : #include "src/memcopy.h"
15 : #include "src/utils.h"
16 : #include "src/v8memory.h"
17 : #include "src/wasm/wasm-external-refs.h"
18 :
19 : namespace v8 {
20 : namespace internal {
21 : namespace wasm {
22 :
23 920 : void f32_trunc_wrapper(Address data) {
24 920 : WriteUnalignedValue<float>(data, truncf(ReadUnalignedValue<float>(data)));
25 920 : }
26 :
27 920 : void f32_floor_wrapper(Address data) {
28 920 : WriteUnalignedValue<float>(data, floorf(ReadUnalignedValue<float>(data)));
29 920 : }
30 :
31 920 : void f32_ceil_wrapper(Address data) {
32 : WriteUnalignedValue<float>(data, ceilf(ReadUnalignedValue<float>(data)));
33 920 : }
34 :
35 920 : void f32_nearest_int_wrapper(Address data) {
36 920 : WriteUnalignedValue<float>(data, nearbyintf(ReadUnalignedValue<float>(data)));
37 920 : }
38 :
39 392 : void f64_trunc_wrapper(Address data) {
40 392 : WriteUnalignedValue<double>(data, trunc(ReadUnalignedValue<double>(data)));
41 392 : }
42 :
43 392 : void f64_floor_wrapper(Address data) {
44 392 : WriteUnalignedValue<double>(data, floor(ReadUnalignedValue<double>(data)));
45 392 : }
46 :
47 392 : void f64_ceil_wrapper(Address data) {
48 392 : WriteUnalignedValue<double>(data, ceil(ReadUnalignedValue<double>(data)));
49 392 : }
50 :
51 392 : void f64_nearest_int_wrapper(Address data) {
52 392 : WriteUnalignedValue<double>(data,
53 : nearbyint(ReadUnalignedValue<double>(data)));
54 392 : }
55 :
56 648 : void int64_to_float32_wrapper(Address data) {
57 : int64_t input = ReadUnalignedValue<int64_t>(data);
58 648 : WriteUnalignedValue<float>(data, static_cast<float>(input));
59 648 : }
60 :
61 952 : void uint64_to_float32_wrapper(Address data) {
62 : uint64_t input = ReadUnalignedValue<uint64_t>(data);
63 952 : float result = static_cast<float>(input);
64 :
65 : #if V8_CC_MSVC
66 : // With MSVC we use static_cast<float>(uint32_t) instead of
67 : // static_cast<float>(uint64_t) to achieve round-to-nearest-ties-even
68 : // semantics. The idea is to calculate
69 : // static_cast<float>(high_word) * 2^32 + static_cast<float>(low_word). To
70 : // achieve proper rounding in all cases we have to adjust the high_word
71 : // with a "rounding bit" sometimes. The rounding bit is stored in the LSB of
72 : // the high_word if the low_word may affect the rounding of the high_word.
73 : uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
74 : uint32_t high_word = static_cast<uint32_t>(input >> 32);
75 :
76 : float shift = static_cast<float>(1ull << 32);
77 : // If the MSB of the high_word is set, then we make space for a rounding bit.
78 : if (high_word < 0x80000000) {
79 : high_word <<= 1;
80 : shift = static_cast<float>(1ull << 31);
81 : }
82 :
83 : if ((high_word & 0xFE000000) && low_word) {
84 : // Set the rounding bit.
85 : high_word |= 1;
86 : }
87 :
88 : result = static_cast<float>(high_word);
89 : result *= shift;
90 : result += static_cast<float>(low_word);
91 : #endif
92 :
93 : WriteUnalignedValue<float>(data, result);
94 952 : }
95 :
96 648 : void int64_to_float64_wrapper(Address data) {
97 : int64_t input = ReadUnalignedValue<int64_t>(data);
98 648 : WriteUnalignedValue<double>(data, static_cast<double>(input));
99 648 : }
100 :
101 948 : void uint64_to_float64_wrapper(Address data) {
102 : uint64_t input = ReadUnalignedValue<uint64_t>(data);
103 948 : double result = static_cast<double>(input);
104 :
105 : #if V8_CC_MSVC
106 : // With MSVC we use static_cast<double>(uint32_t) instead of
107 : // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
108 : // semantics. The idea is to calculate
109 : // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
110 : uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
111 : uint32_t high_word = static_cast<uint32_t>(input >> 32);
112 :
113 : double shift = static_cast<double>(1ull << 32);
114 :
115 : result = static_cast<double>(high_word);
116 : result *= shift;
117 : result += static_cast<double>(low_word);
118 : #endif
119 :
120 : WriteUnalignedValue<double>(data, result);
121 948 : }
122 :
123 1840 : int32_t float32_to_int64_wrapper(Address data) {
124 : // We use "<" here to check the upper bound because of rounding problems: With
125 : // "<=" some inputs would be considered within int64 range which are actually
126 : // not within int64 range.
127 : float input = ReadUnalignedValue<float>(data);
128 1840 : if (input >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
129 : input < static_cast<float>(std::numeric_limits<int64_t>::max())) {
130 1328 : WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
131 1328 : return 1;
132 : }
133 : return 0;
134 : }
135 :
136 1840 : int32_t float32_to_uint64_wrapper(Address data) {
137 : float input = ReadUnalignedValue<float>(data);
138 : // We use "<" here to check the upper bound because of rounding problems: With
139 : // "<=" some inputs would be considered within uint64 range which are actually
140 : // not within uint64 range.
141 1840 : if (input > -1.0 &&
142 : input < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
143 816 : WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
144 816 : return 1;
145 : }
146 : return 0;
147 : }
148 :
149 1116 : int32_t float64_to_int64_wrapper(Address data) {
150 : // We use "<" here to check the upper bound because of rounding problems: With
151 : // "<=" some inputs would be considered within int64 range which are actually
152 : // not within int64 range.
153 : double input = ReadUnalignedValue<double>(data);
154 1116 : if (input >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
155 : input < static_cast<double>(std::numeric_limits<int64_t>::max())) {
156 940 : WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
157 940 : return 1;
158 : }
159 : return 0;
160 : }
161 :
162 784 : int32_t float64_to_uint64_wrapper(Address data) {
163 : // We use "<" here to check the upper bound because of rounding problems: With
164 : // "<=" some inputs would be considered within uint64 range which are actually
165 : // not within uint64 range.
166 : double input = ReadUnalignedValue<double>(data);
167 784 : if (input > -1.0 &&
168 : input < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
169 464 : WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
170 464 : return 1;
171 : }
172 : return 0;
173 : }
174 :
175 52488 : int32_t int64_div_wrapper(Address data) {
176 : int64_t dividend = ReadUnalignedValue<int64_t>(data);
177 52488 : int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
178 52488 : if (divisor == 0) {
179 : return 0;
180 : }
181 51192 : if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
182 : return -1;
183 : }
184 51192 : WriteUnalignedValue<int64_t>(data, dividend / divisor);
185 51192 : return 1;
186 : }
187 :
188 52488 : int32_t int64_mod_wrapper(Address data) {
189 : int64_t dividend = ReadUnalignedValue<int64_t>(data);
190 52488 : int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
191 52488 : if (divisor == 0) {
192 : return 0;
193 : }
194 51192 : WriteUnalignedValue<int64_t>(data, dividend % divisor);
195 51192 : return 1;
196 : }
197 :
198 52488 : int32_t uint64_div_wrapper(Address data) {
199 : uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
200 52488 : uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
201 52488 : if (divisor == 0) {
202 : return 0;
203 : }
204 51192 : WriteUnalignedValue<uint64_t>(data, dividend / divisor);
205 51192 : return 1;
206 : }
207 :
208 52488 : int32_t uint64_mod_wrapper(Address data) {
209 : uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
210 52488 : uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
211 52488 : if (divisor == 0) {
212 : return 0;
213 : }
214 51192 : WriteUnalignedValue<uint64_t>(data, dividend % divisor);
215 51192 : return 1;
216 : }
217 :
218 464 : uint32_t word32_ctz_wrapper(Address data) {
219 464 : return base::bits::CountTrailingZeros(ReadUnalignedValue<uint32_t>(data));
220 : }
221 :
222 648 : uint32_t word64_ctz_wrapper(Address data) {
223 648 : return base::bits::CountTrailingZeros(ReadUnalignedValue<uint64_t>(data));
224 : }
225 :
226 464 : uint32_t word32_popcnt_wrapper(Address data) {
227 464 : return base::bits::CountPopulation(ReadUnalignedValue<uint32_t>(data));
228 : }
229 :
230 648 : uint32_t word64_popcnt_wrapper(Address data) {
231 648 : return base::bits::CountPopulation(ReadUnalignedValue<uint64_t>(data));
232 : }
233 :
234 26912 : uint32_t word32_rol_wrapper(Address data) {
235 : uint32_t input = ReadUnalignedValue<uint32_t>(data);
236 53824 : uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
237 26912 : return (input << shift) | (input >> ((32 - shift) & 31));
238 : }
239 :
240 26912 : uint32_t word32_ror_wrapper(Address data) {
241 : uint32_t input = ReadUnalignedValue<uint32_t>(data);
242 53824 : uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
243 26912 : return (input >> shift) | (input << ((32 - shift) & 31));
244 : }
245 :
246 19243 : void float64_pow_wrapper(Address data) {
247 : double x = ReadUnalignedValue<double>(data);
248 19243 : double y = ReadUnalignedValue<double>(data + sizeof(x));
249 19243 : WriteUnalignedValue<double>(data, base::ieee754::pow(x, y));
250 19243 : }
251 :
252 1312 : void memory_copy_wrapper(Address dst, Address src, uint32_t size) {
253 : // Use explicit forward and backward copy to match the required semantics for
254 : // the memory.copy instruction. It is assumed that the caller of this
255 : // function has already performed bounds checks, so {src + size} and
256 : // {dst + size} should not overflow.
257 : DCHECK(src + size >= src && dst + size >= dst);
258 1312 : uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
259 1312 : uint8_t* src8 = reinterpret_cast<uint8_t*>(src);
260 1312 : if (src < dst && src + size > dst && dst + size > src) {
261 12 : dst8 += size - 1;
262 12 : src8 += size - 1;
263 84 : for (; size > 0; size--) {
264 36 : *dst8-- = *src8--;
265 : }
266 : } else {
267 17844372 : for (; size > 0; size--) {
268 8921536 : *dst8++ = *src8++;
269 : }
270 : }
271 1312 : }
272 :
273 140 : void memory_fill_wrapper(Address dst, uint32_t value, uint32_t size) {
274 : // Use an explicit forward copy to match the required semantics for the
275 : // memory.fill instruction. It is assumed that the caller of this function
276 : // has already performed bounds checks, so {dst + size} should not overflow.
277 : DCHECK(dst + size >= dst);
278 140 : uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
279 140 : uint8_t value8 = static_cast<uint8_t>(value);
280 5759652 : for (; size > 0; size--) {
281 2879756 : *dst8++ = value8;
282 : }
283 140 : }
284 :
285 : static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;
286 :
287 9693368 : void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
288 9693368 : wasm_trap_callback_for_testing = callback;
289 9693368 : }
290 :
291 72760 : void call_trap_callback_for_testing() {
292 72760 : if (wasm_trap_callback_for_testing) {
293 72760 : wasm_trap_callback_for_testing();
294 : }
295 72760 : }
296 :
297 : } // namespace wasm
298 : } // namespace internal
299 122004 : } // namespace v8
|