diff options
author | Akira Baruah <akirabaruah@google.com> | 2024-05-08 23:20:22 +0000 |
---|---|---|
committer | Akira Baruah <akirabaruah@google.com> | 2024-05-09 23:31:21 +0000 |
commit | 952ff8acf1f0477bccca050afa12eaff5614c2fd (patch) | |
tree | 51af08f7d8fd0b54e1b4de3815d8ac85bfdd5072 | |
parent | 0d048372c919d21b949347fc59f33b6145c832e4 (diff) | |
download | binary_translation-952ff8acf1f0477bccca050afa12eaff5614c2fd.tar.gz |
Specify FMax/FMin float intrinsics for riscv64
Removes the generalized "common" implementations for FMax and FMin and
defines them specifically for riscv64 according to IEEE 754-2019.
Updates vector interpreter tests to check this behavior explicitly.
Bug: 330396676
Test: Bionic unit tests: math_h.{fmax,fmin,fmaxf,fminf}
Test: berberis_host_tests
Change-Id: Ia82e441432e22512686b632bfc4a0179d521e46a
3 files changed, 71 insertions, 61 deletions
diff --git a/interpreter/riscv64/interpreter_test.cc b/interpreter/riscv64/interpreter_test.cc index 1cbdfcb2..5ec5d411 100644 --- a/interpreter/riscv64/interpreter_test.cc +++ b/interpreter/riscv64/interpreter_test.cc @@ -10042,39 +10042,42 @@ TEST_F(Riscv64InterpreterTest, TestVmin) { {0xaaaa'aaaa'aaaa'aaaa, 0xaaaa'aaaa'aaaa'aaaa}, {0xaaaa'aaaa'aaaa'aaaa, 0xaaaa'aaaa'aaaa'aaaa}}, kVectorCalculationsSourceLegacy); +} + +TEST_F(Riscv64InterpreterTest, TestVfmin) { TestVectorFloatInstruction(0x1100d457, // vfmin.vf v8, v16, f1, v0.t - {{0xf005'f005, 0xf005'f005, 0x4040'4040, 0x7fc0'0000}, - {0x40b4'0000, 0x7fc0'0000, 0x40b4'0000, 0x7fc0'0000}, + {{0xf005'f005, 0xf005'f005, 0x4040'4040, 0x40b4'0000}, + {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, {0x4016'4016, 0x4016'4016, 0x0000'0000, 0x4016'8000}, {0xaaaa'aaaa, 0xaaaa'aaaa, 0x1111'1111, 0x1111'1111}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, + {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, + {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, {0xa9bb'bbbb, 0xa9bb'bbbb, 0xa9bb'bbbb, 0xa9bb'bbbb}, {0xa9a9'a9a9, 0xa9a9'a9a9, 0xa9a9'a9a9, 0xa9a9'a9a9}}, - {{0xf005'f005'f005'f005, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x40164'016'4016'4016, 0x4016'8000'0000'0000}, + {{0xf005'f005'f005'f005, 0x4016'8000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, + {0x4016'4016'4016'4016, 0x4016'8000'0000'0000}, {0xaaaa'aaaa'aaaa'aaaa, 0x1111'1111'1111'1111}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, {0xa9bb'bbbb'a9bb'bbbb, 0xa9bb'bbbb'a9bb'bbbb}, {0xa9a9'a9a9'a9a9'a9a9, 0xa9a9'a9a9'a9a9'a9a9}}, kVectorComparisonSource); TestVectorFloatInstruction(0x110c1457, // vfmin.vv v8,v16,v24,v0.t {{0xf005'f005, 0xf005'f005, 0x4040'4040, 0x7fc0'0000}, - {0x1111'1111, 0x7fc0'0000, 0x1111'1111, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, + {0x1111'1111, 0x1111'1111, 0x1111'1111, 0x1111'1111}, + {0x4016'4016, 0x4016'4016, 0x0000'0000, 0x4016'8000}, {0xaaaa'aaaa, 0xaaaa'aaaa, 0x1111'1111, 0x1111'1111}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, + {0x8684'8280, 0x8e8c'8a89, 0x9694'9291, 0x9e9c'9a98}, + {0xa6a4'a2a0, 0xaeac'aaa9, 0xb6b4'b2b1, 0xbebc'bab8}, {0xc6c4'c2c0, 0xcecc'cac9, 0xd6d4'd2d1, 0xdedc'dad8}, {0xe6e4'e2e0, 0xeeec'eae9, 0xf6f4'f2f1, 0xfefc'faf8}}, {{0xf005'f005'f005'f005, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {0x1111'1111'1111'1111, 0x1111'1111'1111'1111}, + {0x4016'4016'4016'4016, 0x4016'8000'0000'0000}, {0xaaaa'aaaa'aaaa'aaaa, 0x1111'1111'1111'1111}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {0x8e8c'8a89'8684'8280, 0x9e9c'9a98'9694'9291}, + {0xaeac'aaa9'a6a4'a2a0, 0xbebc'bab8'b6b4'b2b1}, {0xcecc'cac9'c6c4'c2c0, 0xdedc'dad8'd6d4'd2d1}, {0xeeec'eae9'e6e4'e2e0, 0xfefc'faf8'f6f4'f2f1}}, kVectorComparisonSource); @@ -10224,39 +10227,42 @@ TEST_F(Riscv64InterpreterTest, TestVmax) { {0xe766'e564'e362'e160, 0xef6e'ed6c'eb6a'e968}, {0xf776'f574'f372'f170, 0xff7e'fd7c'fb7a'f978}}, kVectorCalculationsSourceLegacy); +} + +TEST_F(Riscv64InterpreterTest, TestVfmax) { TestVectorFloatInstruction(0x1900d457, // vfmax.vf v8, v16, f1, v0.t - {{0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x7fc0'0000}, - {0x40b4'40b4, 0x7fc0'0000, 0x40b4'0000, 0x7fc0'0000}, + {{0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, + {0x40b4'40b4, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, + {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, + {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}, {0x40b4'0000, 0x40b4'0000, 0x40b4'0000, 0x40b4'0000}}, - {{0x4016'8000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {{0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, + {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}, {0x4016'8000'0000'0000, 0x4016'8000'0000'0000}}, kVectorComparisonSource); TestVectorFloatInstruction(0x190c1457, // vfmax.vv v8,v16,v24,v0.t {{0xf005'f005, 0xf005'f005, 0x4040'4040, 0x7fc0'0000}, - {0x40b4'40b4, 0x7fc0'0000, 0x40b4'0000, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, + {0x40b4'40b4, 0x1111'1111, 0x40b4'0000, 0x1111'1111}, + {0x4016'4016, 0x4016'4016, 0x0000'0000, 0x4016'8000}, {0x6664'6260, 0x6e6c'6a69, 0x7674'7271, 0x7e7c'7a78}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, - {0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000, 0x7fc0'0000}, + {0x8684'8280, 0x8e8c'8a89, 0x9694'9291, 0x9e9c'9a98}, + {0xa6a4'a2a0, 0xaeac'aaa9, 0xb6b4'b2b1, 0xbebc'bab8}, {0xa9bb'bbbb, 0xa9bb'bbbb, 0xa9bb'bbbb, 0xa9bb'bbbb}, {0xa9a9'a9a9, 0xa9a9'a9a9, 0xa9a9'a9a9, 0xa9a9'a9a9}}, {{0xf005'f005'f005'f005, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {0x1111'1111'1111'1111, 0x1111'1111'1111'1111}, + {0x4016'4016'4016'4016, 0x4016'8000'0000'0000}, {0x6e6c'6a69'6664'6260, 0x7e7c'7a78'7674'7271}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, - {0x7ff8'0000'0000'0000, 0x7ff8'0000'0000'0000}, + {0x8e8c'8a89'8684'8280, 0x9e9c'9a98'9694'9291}, + {0xaeac'aaa9'a6a4'a2a0, 0xbebc'bab8'b6b4'b2b1}, {0xa9bb'bbbb'a9bb'bbbb, 0xa9bb'bbbb'a9bb'bbbb}, {0xa9a9'a9a9'a9a9'a9a9, 0xa9a9'a9a9'a9a9'a9a9}}, kVectorComparisonSource); diff --git a/intrinsics/include/berberis/intrinsics/common/intrinsics_float.h b/intrinsics/include/berberis/intrinsics/common/intrinsics_float.h index c7e27b20..a227b6b4 100644 --- a/intrinsics/include/berberis/intrinsics/common/intrinsics_float.h +++ b/intrinsics/include/berberis/intrinsics/common/intrinsics_float.h @@ -110,34 +110,8 @@ class WrappedFloatType { friend inline WrappedFloatType MulAdd(const WrappedFloatType& v1, const WrappedFloatType& v2, const WrappedFloatType& v3); - - friend inline WrappedFloatType Max(WrappedFloatType op1, WrappedFloatType op2) { - if (FPClassify(op1) == FPInfo::kZero && FPClassify(op2) == FPInfo::kZero && - SignBit(op1) != SignBit(op2)) { - return WrappedFloatType(BaseType(+0.f)); - } - // If either argument is NaN - return default NaN (fmax() may return other). - if (IsNan(op1) || IsNan(op2)) { - return std::numeric_limits<WrappedFloatType>::quiet_NaN(); - } - // Note: fmax is not needed here - it differs from std::max based on operator< only if NANs are - // involved - and does wrong thing there. We have no NANs at this point thus could use std::max - return std::max(op1, op2); - } - - friend inline WrappedFloatType Min(WrappedFloatType op1, WrappedFloatType op2) { - if (FPClassify(op1) == FPInfo::kZero && FPClassify(op2) == FPInfo::kZero && - SignBit(op1) != SignBit(op2)) { - return WrappedFloatType(BaseType(-0.f)); - } - // If either argument is NaN - return default NaN (fmin() may return other). - if (IsNan(op1) || IsNan(op2)) { - return std::numeric_limits<WrappedFloatType>::quiet_NaN(); - } - // Note: fmin is not needed here - it differs from std::min based on operator< only if NANs are - // involved - and does wrong thing there. We have no NANs at this point thus could use std::min - return std::min(op1, op2); - } + friend inline WrappedFloatType Max(WrappedFloatType op1, WrappedFloatType op2); + friend inline WrappedFloatType Min(WrappedFloatType op1, WrappedFloatType op2); private: static_assert(!std::numeric_limits<BaseType>::is_exact, diff --git a/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/intrinsics_float.h b/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/intrinsics_float.h index 98e3313d..05152748 100644 --- a/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/intrinsics_float.h +++ b/intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/intrinsics_float.h @@ -17,6 +17,7 @@ #ifndef BERBERIS_INTRINSICS_RISCV64_TO_X86_64_INTRINSICS_FLOAT_H_ #define BERBERIS_INTRINSICS_RISCV64_TO_X86_64_INTRINSICS_FLOAT_H_ +#include <cmath> #include <limits> #include "berberis/base/bit_util.h" @@ -96,6 +97,35 @@ inline FloatType ExecuteFloatOperation(uint8_t requested_rm, return operation(args...); } +// From RISC-V ISA manual: Single-Precision Floating-Point Computational Instructions. +// Covers behavior for both single and double precision floating point comparisons. +#define DEFINE_FLOAT_COMPARE_FUNC(FuncName, FloatType, ZeroVal, Intrinsic) \ + inline FloatType FuncName(FloatType op1, FloatType op2) { \ + FPInfo op1_class = FPClassify(op1); \ + FPInfo op2_class = FPClassify(op2); \ + if (op1_class == FPInfo::kZero && op2_class == FPInfo::kZero && \ + SignBit(op1) != SignBit(op2)) { \ + return FloatType(ZeroVal); \ + } \ + /* If both inputs are NaNs, the result is the canonical NaN. */ \ + if (op1_class == FPInfo::kNaN && op2_class == FPInfo::kNaN) { \ + return std::numeric_limits<FloatType>::quiet_NaN(); \ + } \ + /* If only one operand is a NaN, the result is the non-NaN operand. */ \ + if (op1_class == FPInfo::kNaN) { \ + return op2; \ + } \ + if (op2_class == FPInfo::kNaN) { \ + return op1; \ + } \ + return FloatType(Intrinsic(op1.value_, op2.value_)); \ + } +DEFINE_FLOAT_COMPARE_FUNC(Max, Float32, +0.f, std::fmax); +DEFINE_FLOAT_COMPARE_FUNC(Max, Float64, +0.f, std::fmax); +DEFINE_FLOAT_COMPARE_FUNC(Min, Float32, -0.f, std::fmin); +DEFINE_FLOAT_COMPARE_FUNC(Min, Float64, -0.f, std::fmin); +#undef DEFINE_FLOAT_COMPARE_FUNC + // We only need Negative(long double) for FMA, b/120563432 doesn't affect this function. inline long double Negative(const long double& v) { return -v; |