c3f346b7ac
Relaxed f32x4 and f64x2 min and max. These instructions only guarantee results when the inputs are non nans, and when the inputs are not 0s of opposite signs. Reuse existing float binop testing harnesses and add special checks for such constants when relaxed operations are being tested. Drive-by rename of x64 instruction codes to be Minps/Maxps/Minpd/Maxpd since they map down exactly to a single instruction. Bug: v8:12284 Change-Id: I1449dbfa87935a96d7d260db22667ab7b9e86601 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3218196 Reviewed-by: Deepti Gandluri <gdeepti@chromium.org> Commit-Queue: Zhi An Ng <zhin@chromium.org> Cr-Commit-Position: refs/heads/main@{#77484}
180 lines
6.7 KiB
C++
180 lines
6.7 KiB
C++
// Copyright 2021 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 <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "src/base/macros.h"
|
|
#include "src/wasm/compilation-environment.h"
|
|
#include "src/wasm/wasm-opcodes.h"
|
|
#include "test/cctest/wasm/wasm-run-utils.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
using Int8UnOp = int8_t (*)(int8_t);
|
|
using Int8BinOp = int8_t (*)(int8_t, int8_t);
|
|
using Uint8BinOp = uint8_t (*)(uint8_t, uint8_t);
|
|
using Int8CompareOp = int (*)(int8_t, int8_t);
|
|
using Int8ShiftOp = int8_t (*)(int8_t, int);
|
|
|
|
using Int16UnOp = int16_t (*)(int16_t);
|
|
using Int16BinOp = int16_t (*)(int16_t, int16_t);
|
|
using Uint16BinOp = uint16_t (*)(uint16_t, uint16_t);
|
|
using Int16ShiftOp = int16_t (*)(int16_t, int);
|
|
using Int32UnOp = int32_t (*)(int32_t);
|
|
using Int32BinOp = int32_t (*)(int32_t, int32_t);
|
|
using Int32ShiftOp = int32_t (*)(int32_t, int);
|
|
using Int64UnOp = int64_t (*)(int64_t);
|
|
using Int64BinOp = int64_t (*)(int64_t, int64_t);
|
|
using Int64ShiftOp = int64_t (*)(int64_t, int);
|
|
using FloatUnOp = float (*)(float);
|
|
using FloatBinOp = float (*)(float, float);
|
|
using FloatCompareOp = int32_t (*)(float, float);
|
|
using DoubleUnOp = double (*)(double);
|
|
using DoubleBinOp = double (*)(double, double);
|
|
using DoubleCompareOp = int64_t (*)(double, double);
|
|
|
|
void RunI8x16UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int8UnOp expected_op);
|
|
|
|
template <typename T = int8_t, typename OpType = T (*)(T, T)>
|
|
void RunI8x16BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
OpType expected_op);
|
|
|
|
void RunI8x16ShiftOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int8ShiftOp expected_op);
|
|
void RunI8x16MixedRelationalOpTest(TestExecutionTier execution_tier,
|
|
WasmOpcode opcode, Int8BinOp expected_op);
|
|
|
|
void RunI16x8UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int16UnOp expected_op);
|
|
template <typename T = int16_t, typename OpType = T (*)(T, T)>
|
|
void RunI16x8BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
OpType expected_op);
|
|
void RunI16x8ShiftOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int16ShiftOp expected_op);
|
|
void RunI16x8MixedRelationalOpTest(TestExecutionTier execution_tier,
|
|
WasmOpcode opcode, Int16BinOp expected_op);
|
|
|
|
void RunI32x4UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int32UnOp expected_op);
|
|
void RunI32x4BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int32BinOp expected_op);
|
|
void RunI32x4ShiftOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int32ShiftOp expected_op);
|
|
|
|
void RunI64x2UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int64UnOp expected_op);
|
|
void RunI64x2BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int64BinOp expected_op);
|
|
void RunI64x2ShiftOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
Int64ShiftOp expected_op);
|
|
|
|
// Generic expected value functions.
|
|
template <typename T, typename = typename std::enable_if<
|
|
std::is_floating_point<T>::value>::type>
|
|
T Negate(T a) {
|
|
return -a;
|
|
}
|
|
|
|
template <typename T>
|
|
T Minimum(T a, T b) {
|
|
return std::min(a, b);
|
|
}
|
|
|
|
template <typename T>
|
|
T Maximum(T a, T b) {
|
|
return std::max(a, b);
|
|
}
|
|
|
|
#if V8_OS_AIX
|
|
template <typename T>
|
|
bool MightReverseSign(T float_op) {
|
|
return float_op == static_cast<T>(Negate) ||
|
|
float_op == static_cast<T>(std::abs);
|
|
}
|
|
#endif
|
|
|
|
// Test some values not included in the float inputs from value_helper. These
|
|
// tests are useful for opcodes that are synthesized during code gen, like Min
|
|
// and Max on ia32 and x64.
|
|
static constexpr uint32_t nan_test_array[] = {
|
|
// Bit patterns of quiet NaNs and signaling NaNs, with or without
|
|
// additional payload.
|
|
0x7FC00000, 0xFFC00000, 0x7FFFFFFF, 0xFFFFFFFF, 0x7F876543, 0xFF876543,
|
|
// NaN with top payload bit unset.
|
|
0x7FA00000,
|
|
// Both Infinities.
|
|
0x7F800000, 0xFF800000,
|
|
// Some "normal" numbers, 1 and -1.
|
|
0x3F800000, 0xBF800000};
|
|
|
|
#define FOR_FLOAT32_NAN_INPUTS(i) \
|
|
for (size_t i = 0; i < arraysize(nan_test_array); ++i)
|
|
|
|
// Test some values not included in the double inputs from value_helper. These
|
|
// tests are useful for opcodes that are synthesized during code gen, like Min
|
|
// and Max on ia32 and x64.
|
|
static constexpr uint64_t double_nan_test_array[] = {
|
|
// quiet NaNs, + and -
|
|
0x7FF8000000000001, 0xFFF8000000000001,
|
|
// with payload
|
|
0x7FF8000000000011, 0xFFF8000000000011,
|
|
// signaling NaNs, + and -
|
|
0x7FF0000000000001, 0xFFF0000000000001,
|
|
// with payload
|
|
0x7FF0000000000011, 0xFFF0000000000011,
|
|
// Both Infinities.
|
|
0x7FF0000000000000, 0xFFF0000000000000,
|
|
// Some "normal" numbers, 1 and -1.
|
|
0x3FF0000000000000, 0xBFF0000000000000};
|
|
|
|
#define FOR_FLOAT64_NAN_INPUTS(i) \
|
|
for (size_t i = 0; i < arraysize(double_nan_test_array); ++i)
|
|
|
|
// Returns true if the platform can represent the result.
|
|
template <typename T>
|
|
bool PlatformCanRepresent(T x) {
|
|
#if V8_TARGET_ARCH_ARM
|
|
return std::fpclassify(x) != FP_SUBNORMAL;
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
// Returns true for very small and very large numbers. We skip these test
|
|
// values for the approximation instructions, which don't work at the extremes.
|
|
bool IsExtreme(float x);
|
|
bool IsCanonical(float actual);
|
|
void CheckFloatResult(float x, float y, float expected, float actual,
|
|
bool exact = true);
|
|
|
|
bool IsExtreme(double x);
|
|
bool IsCanonical(double actual);
|
|
void CheckDoubleResult(double x, double y, double expected, double actual,
|
|
bool exact = true);
|
|
|
|
void RunF32x4UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
FloatUnOp expected_op, bool exact = true);
|
|
|
|
void RunF32x4BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
FloatBinOp expected_op);
|
|
|
|
void RunF32x4CompareOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
FloatCompareOp expected_op);
|
|
|
|
void RunF64x2UnOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
DoubleUnOp expected_op, bool exact = true);
|
|
void RunF64x2BinOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
DoubleBinOp expected_op);
|
|
void RunF64x2CompareOpTest(TestExecutionTier execution_tier, WasmOpcode opcode,
|
|
DoubleCompareOp expected_op);
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|