wasm-external-refs.cc 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <limits>

#include "include/v8config.h"

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 18 19 20 21 22
#include "src/wasm/wasm-external-refs.h"

namespace v8 {
namespace internal {
namespace wasm {

23 24
void f32_trunc_wrapper(Address data) {
  WriteUnalignedValue<float>(data, truncf(ReadUnalignedValue<float>(data)));
25 26
}

27 28
void f32_floor_wrapper(Address data) {
  WriteUnalignedValue<float>(data, floorf(ReadUnalignedValue<float>(data)));
29 30
}

31 32
void f32_ceil_wrapper(Address data) {
  WriteUnalignedValue<float>(data, ceilf(ReadUnalignedValue<float>(data)));
33 34
}

35 36
void f32_nearest_int_wrapper(Address data) {
  WriteUnalignedValue<float>(data, nearbyintf(ReadUnalignedValue<float>(data)));
37 38
}

39 40
void f64_trunc_wrapper(Address data) {
  WriteUnalignedValue<double>(data, trunc(ReadUnalignedValue<double>(data)));
41
}
42

43 44
void f64_floor_wrapper(Address data) {
  WriteUnalignedValue<double>(data, floor(ReadUnalignedValue<double>(data)));
45
}
46

47 48
void f64_ceil_wrapper(Address data) {
  WriteUnalignedValue<double>(data, ceil(ReadUnalignedValue<double>(data)));
49
}
50

51 52 53
void f64_nearest_int_wrapper(Address data) {
  WriteUnalignedValue<double>(data,
                              nearbyint(ReadUnalignedValue<double>(data)));
54
}
55

56 57 58
void int64_to_float32_wrapper(Address data) {
  int64_t input = ReadUnalignedValue<int64_t>(data);
  WriteUnalignedValue<float>(data, static_cast<float>(input));
59 60
}

61 62 63 64
void uint64_to_float32_wrapper(Address data) {
  uint64_t input = ReadUnalignedValue<uint64_t>(data);
  float result = static_cast<float>(input);

65 66 67 68 69 70 71 72
#if V8_CC_MSVC
  // With MSVC we use static_cast<float>(uint32_t) instead of
  // static_cast<float>(uint64_t) to achieve round-to-nearest-ties-even
  // semantics. The idea is to calculate
  // static_cast<float>(high_word) * 2^32 + static_cast<float>(low_word). To
  // achieve proper rounding in all cases we have to adjust the high_word
  // with a "rounding bit" sometimes. The rounding bit is stored in the LSB of
  // the high_word if the low_word may affect the rounding of the high_word.
73 74
  uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
  uint32_t high_word = static_cast<uint32_t>(input >> 32);
75 76 77 78 79 80 81 82

  float shift = static_cast<float>(1ull << 32);
  // If the MSB of the high_word is set, then we make space for a rounding bit.
  if (high_word < 0x80000000) {
    high_word <<= 1;
    shift = static_cast<float>(1ull << 31);
  }

83
  if ((high_word & 0xFE000000) && low_word) {
84 85 86 87
    // Set the rounding bit.
    high_word |= 1;
  }

88
  result = static_cast<float>(high_word);
89 90 91
  result *= shift;
  result += static_cast<float>(low_word);
#endif
92 93

  WriteUnalignedValue<float>(data, result);
94 95
}

96 97 98
void int64_to_float64_wrapper(Address data) {
  int64_t input = ReadUnalignedValue<int64_t>(data);
  WriteUnalignedValue<double>(data, static_cast<double>(input));
99 100
}

101 102 103 104
void uint64_to_float64_wrapper(Address data) {
  uint64_t input = ReadUnalignedValue<uint64_t>(data);
  double result = static_cast<double>(input);

105 106 107 108 109
#if V8_CC_MSVC
  // With MSVC we use static_cast<double>(uint32_t) instead of
  // static_cast<double>(uint64_t) to achieve round-to-nearest-ties-even
  // semantics. The idea is to calculate
  // static_cast<double>(high_word) * 2^32 + static_cast<double>(low_word).
110 111
  uint32_t low_word = static_cast<uint32_t>(input & 0xFFFFFFFF);
  uint32_t high_word = static_cast<uint32_t>(input >> 32);
112 113 114

  double shift = static_cast<double>(1ull << 32);

115
  result = static_cast<double>(high_word);
116 117 118
  result *= shift;
  result += static_cast<double>(low_word);
#endif
119 120

  WriteUnalignedValue<double>(data, result);
121 122
}

123
int32_t float32_to_int64_wrapper(Address data) {
124 125 126
  // We use "<" here to check the upper bound because of rounding problems: With
  // "<=" some inputs would be considered within int64 range which are actually
  // not within int64 range.
127 128 129 130
  float input = ReadUnalignedValue<float>(data);
  if (input >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
      input < static_cast<float>(std::numeric_limits<int64_t>::max())) {
    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
131 132 133 134 135
    return 1;
  }
  return 0;
}

136 137
int32_t float32_to_uint64_wrapper(Address data) {
  float input = ReadUnalignedValue<float>(data);
138 139 140
  // We use "<" here to check the upper bound because of rounding problems: With
  // "<=" some inputs would be considered within uint64 range which are actually
  // not within uint64 range.
141 142 143
  if (input > -1.0 &&
      input < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
144 145 146 147 148
    return 1;
  }
  return 0;
}

149
int32_t float64_to_int64_wrapper(Address data) {
150 151 152
  // We use "<" here to check the upper bound because of rounding problems: With
  // "<=" some inputs would be considered within int64 range which are actually
  // not within int64 range.
153 154 155 156
  double input = ReadUnalignedValue<double>(data);
  if (input >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
      input < static_cast<double>(std::numeric_limits<int64_t>::max())) {
    WriteUnalignedValue<int64_t>(data, static_cast<int64_t>(input));
157 158 159 160 161
    return 1;
  }
  return 0;
}

162
int32_t float64_to_uint64_wrapper(Address data) {
163 164 165
  // We use "<" here to check the upper bound because of rounding problems: With
  // "<=" some inputs would be considered within uint64 range which are actually
  // not within uint64 range.
166 167 168 169
  double input = ReadUnalignedValue<double>(data);
  if (input > -1.0 &&
      input < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
    WriteUnalignedValue<uint64_t>(data, static_cast<uint64_t>(input));
170 171 172 173 174
    return 1;
  }
  return 0;
}

175 176 177 178
int32_t int64_div_wrapper(Address data) {
  int64_t dividend = ReadUnalignedValue<int64_t>(data);
  int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
  if (divisor == 0) {
179 180
    return 0;
  }
181
  if (divisor == -1 && dividend == std::numeric_limits<int64_t>::min()) {
182 183
    return -1;
  }
184
  WriteUnalignedValue<int64_t>(data, dividend / divisor);
185 186 187
  return 1;
}

188 189 190 191
int32_t int64_mod_wrapper(Address data) {
  int64_t dividend = ReadUnalignedValue<int64_t>(data);
  int64_t divisor = ReadUnalignedValue<int64_t>(data + sizeof(dividend));
  if (divisor == 0) {
192 193
    return 0;
  }
194
  WriteUnalignedValue<int64_t>(data, dividend % divisor);
195 196 197
  return 1;
}

198 199 200 201
int32_t uint64_div_wrapper(Address data) {
  uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
  uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
  if (divisor == 0) {
202 203
    return 0;
  }
204
  WriteUnalignedValue<uint64_t>(data, dividend / divisor);
205 206 207
  return 1;
}

208 209 210 211
int32_t uint64_mod_wrapper(Address data) {
  uint64_t dividend = ReadUnalignedValue<uint64_t>(data);
  uint64_t divisor = ReadUnalignedValue<uint64_t>(data + sizeof(dividend));
  if (divisor == 0) {
212 213
    return 0;
  }
214
  WriteUnalignedValue<uint64_t>(data, dividend % divisor);
215 216
  return 1;
}
217

218 219
uint32_t word32_ctz_wrapper(Address data) {
  return base::bits::CountTrailingZeros(ReadUnalignedValue<uint32_t>(data));
220 221
}

222 223
uint32_t word64_ctz_wrapper(Address data) {
  return base::bits::CountTrailingZeros(ReadUnalignedValue<uint64_t>(data));
224 225
}

226 227
uint32_t word32_popcnt_wrapper(Address data) {
  return base::bits::CountPopulation(ReadUnalignedValue<uint32_t>(data));
228 229
}

230 231
uint32_t word64_popcnt_wrapper(Address data) {
  return base::bits::CountPopulation(ReadUnalignedValue<uint64_t>(data));
232 233
}

234 235 236
uint32_t word32_rol_wrapper(Address data) {
  uint32_t input = ReadUnalignedValue<uint32_t>(data);
  uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
237
  return (input << shift) | (input >> ((32 - shift) & 31));
238 239
}

240 241 242
uint32_t word32_ror_wrapper(Address data) {
  uint32_t input = ReadUnalignedValue<uint32_t>(data);
  uint32_t shift = ReadUnalignedValue<uint32_t>(data + sizeof(input)) & 31;
243
  return (input >> shift) | (input << ((32 - shift) & 31));
244 245
}

246 247 248
void float64_pow_wrapper(Address data) {
  double x = ReadUnalignedValue<double>(data);
  double y = ReadUnalignedValue<double>(data + sizeof(x));
249
  WriteUnalignedValue<double>(data, base::ieee754::pow(x, y));
250
}
251

252 253 254 255 256 257 258 259 260
// Asan on Windows triggers exceptions in this function to allocate
// shadow memory lazily. When this function is called from WebAssembly,
// these exceptions would be handled by the trap handler before they get
// handled by Asan, and thereby confuse the thread-in-wasm flag.
// Therefore we disable ASAN for this function. Alternatively we could
// reset the thread-in-wasm flag before calling this function. However,
// as this is only a problem with Asan on Windows, we did not consider
// it worth the overhead.
DISABLE_ASAN void memory_copy_wrapper(Address dst, Address src, uint32_t size) {
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
  // Use explicit forward and backward copy to match the required semantics for
  // the memory.copy instruction. It is assumed that the caller of this
  // function has already performed bounds checks, so {src + size} and
  // {dst + size} should not overflow.
  DCHECK(src + size >= src && dst + size >= dst);
  uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
  uint8_t* src8 = reinterpret_cast<uint8_t*>(src);
  if (src < dst && src + size > dst && dst + size > src) {
    dst8 += size - 1;
    src8 += size - 1;
    for (; size > 0; size--) {
      *dst8-- = *src8--;
    }
  } else {
    for (; size > 0; size--) {
      *dst8++ = *src8++;
    }
  }
279 280
}

281 282 283 284 285
// Asan on Windows triggers exceptions in this function that confuse the
// WebAssembly trap handler, so Asan is disabled. See the comment on
// memory_copy_wrapper above for more info.
DISABLE_ASAN void memory_fill_wrapper(Address dst, uint32_t value,
                                      uint32_t size) {
286 287 288 289 290 291 292 293 294
  // Use an explicit forward copy to match the required semantics for the
  // memory.fill instruction. It is assumed that the caller of this function
  // has already performed bounds checks, so {dst + size} should not overflow.
  DCHECK(dst + size >= dst);
  uint8_t* dst8 = reinterpret_cast<uint8_t*>(dst);
  uint8_t value8 = static_cast<uint8_t>(value);
  for (; size > 0; size--) {
    *dst8++ = value8;
  }
295 296
}

297 298 299 300 301 302 303 304 305 306 307 308
static WasmTrapCallbackForTesting wasm_trap_callback_for_testing = nullptr;

void set_trap_callback_for_testing(WasmTrapCallbackForTesting callback) {
  wasm_trap_callback_for_testing = callback;
}

void call_trap_callback_for_testing() {
  if (wasm_trap_callback_for_testing) {
    wasm_trap_callback_for_testing();
  }
}

309 310 311
}  // namespace wasm
}  // namespace internal
}  // namespace v8