aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAkira Baruah <akirabaruah@google.com>2024-05-08 23:20:22 +0000
committerAkira Baruah <akirabaruah@google.com>2024-05-09 23:31:21 +0000
commit952ff8acf1f0477bccca050afa12eaff5614c2fd (patch)
tree51af08f7d8fd0b54e1b4de3815d8ac85bfdd5072
parent0d048372c919d21b949347fc59f33b6145c832e4 (diff)
downloadbinary_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
-rw-r--r--interpreter/riscv64/interpreter_test.cc72
-rw-r--r--intrinsics/include/berberis/intrinsics/common/intrinsics_float.h30
-rw-r--r--intrinsics/riscv64_to_x86_64/include/berberis/intrinsics/intrinsics_float.h30
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;