summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Olga Mikhaltsova <olga.mikhaltsova@syntacore.com> 2024-07-22 18:16:20 +0000
committer Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-08-12 15:17:18 +0000
commitbda29056665961578f2b97cd6d40daca2058694d (patch)
treeff3df2723b2c19ac393dbbdb6137e296e94e2868
parent3244be57e9c170696b0a17369bcf3e77f82ee9ec (diff)
riscv64: implement signum{float|double} and copySign{float|double} intrinsics
Performance improvement: copySign(double) -29% copySign(float) -33% signum(double) -20% signum(float) -22% Test: testrunner.py --target --64 --ndebug --optimizing Change-Id: I3b69a4d35a0b37e5debed9f16c3a757a4204387e
-rw-r--r--compiler/optimizing/code_generator_arm64.h4
-rw-r--r--compiler/optimizing/code_generator_arm_vixl.h4
-rw-r--r--compiler/optimizing/code_generator_x86.h4
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/intrinsics_riscv64.cc86
-rw-r--r--runtime/intrinsics_list.h4
-rw-r--r--runtime/oat/image.cc4
-rw-r--r--test/057-math-intrinsics/expected-stderr.txt0
-rw-r--r--test/057-math-intrinsics/expected-stdout.txt0
-rw-r--r--test/057-math-intrinsics/info.txt3
-rw-r--r--test/057-math-intrinsics/src/CopySignTest.java162
-rw-r--r--test/057-math-intrinsics/src/Main.java22
-rw-r--r--test/057-math-intrinsics/src/SignumTest.java130
13 files changed, 425 insertions, 2 deletions
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 0a984c637b..a64615fc31 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -125,6 +125,10 @@ const vixl::aarch64::CPURegList callee_saved_fp_registers(vixl::aarch64::CPURegi
Location ARM64ReturnLocation(DataType::Type return_type);
#define UNIMPLEMENTED_INTRINSIC_LIST_ARM64(V) \
+ V(MathSignumFloat) \
+ V(MathSignumDouble) \
+ V(MathCopySignFloat) \
+ V(MathCopySignDouble) \
V(IntegerRemainderUnsigned) \
V(LongRemainderUnsigned) \
V(StringStringIndexOf) \
diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h
index aac823627d..02f58dc178 100644
--- a/compiler/optimizing/code_generator_arm_vixl.h
+++ b/compiler/optimizing/code_generator_arm_vixl.h
@@ -121,6 +121,10 @@ using VIXLInt32Literal = vixl::aarch32::Literal<int32_t>;
using VIXLUInt32Literal = vixl::aarch32::Literal<uint32_t>;
#define UNIMPLEMENTED_INTRINSIC_LIST_ARM(V) \
+ V(MathSignumFloat) \
+ V(MathSignumDouble) \
+ V(MathCopySignFloat) \
+ V(MathCopySignDouble) \
V(MathRoundDouble) /* Could be done by changing rounding mode, maybe? */ \
V(UnsafeCASLong) /* High register pressure */ \
V(SystemArrayCopyChar) \
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 6ce0c506a0..456ce51bbf 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -49,6 +49,10 @@ static constexpr size_t kRuntimeParameterFpuRegistersLength =
arraysize(kRuntimeParameterFpuRegisters);
#define UNIMPLEMENTED_INTRINSIC_LIST_X86(V) \
+ V(MathSignumFloat) \
+ V(MathSignumDouble) \
+ V(MathCopySignFloat) \
+ V(MathCopySignDouble) \
V(MathRoundDouble) \
V(FloatIsInfinite) \
V(DoubleIsInfinite) \
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index ddeb33a261..cbb4b17fe5 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -54,6 +54,10 @@ static constexpr size_t kRuntimeParameterFpuRegistersLength =
static constexpr FloatRegister non_volatile_xmm_regs[] = { XMM12, XMM13, XMM14, XMM15 };
#define UNIMPLEMENTED_INTRINSIC_LIST_X86_64(V) \
+ V(MathSignumFloat) \
+ V(MathSignumDouble) \
+ V(MathCopySignFloat) \
+ V(MathCopySignDouble) \
V(CRC32Update) \
V(CRC32UpdateBytes) \
V(CRC32UpdateByteBuffer) \
diff --git a/compiler/optimizing/intrinsics_riscv64.cc b/compiler/optimizing/intrinsics_riscv64.cc
index 9f1ac08a26..eec73ea052 100644
--- a/compiler/optimizing/intrinsics_riscv64.cc
+++ b/compiler/optimizing/intrinsics_riscv64.cc
@@ -5526,6 +5526,92 @@ void IntrinsicCodeGeneratorRISCV64::VisitStringGetCharsNoCheck(HInvoke* invoke)
__ Bind(&done);
}
+void GenMathSignum(CodeGeneratorRISCV64* codegen, HInvoke* invoke, DataType::Type type) {
+ LocationSummary* locations = invoke->GetLocations();
+ DCHECK(locations->InAt(0).Equals(locations->Out()));
+ FRegister in = locations->InAt(0).AsFpuRegister<FRegister>();
+ Riscv64Assembler* assembler = codegen->GetAssembler();
+ ScratchRegisterScope srs(assembler);
+ XRegister tmp = srs.AllocateXRegister();
+ FRegister ftmp = srs.AllocateFRegister();
+ Riscv64Label done;
+
+ if (type == DataType::Type::kFloat64) {
+ // 0x3FF0000000000000L = 1.0
+ __ Li(tmp, 0x3FF0000000000000L);
+ __ FMvDX(ftmp, tmp);
+ __ FClassD(tmp, in);
+ } else {
+ // 0x3f800000 = 1.0f
+ __ Li(tmp, 0x3F800000);
+ __ FMvWX(ftmp, tmp);
+ __ FClassS(tmp, in);
+ }
+
+ __ Andi(tmp, tmp, kPositiveZero | kNegativeZero | kSignalingNaN | kQuietNaN);
+ __ Bnez(tmp, &done);
+
+ if (type == DataType::Type::kFloat64) {
+ __ FSgnjD(in, ftmp, in);
+ } else {
+ __ FSgnjS(in, ftmp, in);
+ }
+
+ __ Bind(&done);
+}
+
+void IntrinsicLocationsBuilderRISCV64::VisitMathSignumDouble(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void IntrinsicCodeGeneratorRISCV64::VisitMathSignumDouble(HInvoke* invoke) {
+ GenMathSignum(codegen_, invoke, DataType::Type::kFloat64);
+}
+
+void IntrinsicLocationsBuilderRISCV64::VisitMathSignumFloat(HInvoke* invoke) {
+ LocationSummary* locations =
+ new (allocator_) LocationSummary(invoke, LocationSummary::kNoCall, kIntrinsified);
+ locations->SetInAt(0, Location::RequiresFpuRegister());
+ locations->SetOut(Location::SameAsFirstInput());
+}
+
+void IntrinsicCodeGeneratorRISCV64::VisitMathSignumFloat(HInvoke* invoke) {
+ GenMathSignum(codegen_, invoke, DataType::Type::kFloat32);
+}
+
+void GenMathCopySign(CodeGeneratorRISCV64* codegen, HInvoke* invoke, DataType::Type type) {
+ Riscv64Assembler* assembler = codegen->GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+ FRegister in0 = locations->InAt(0).AsFpuRegister<FRegister>();
+ FRegister in1 = locations->InAt(1).AsFpuRegister<FRegister>();
+ FRegister out = locations->Out().AsFpuRegister<FRegister>();
+
+ if (type == DataType::Type::kFloat64) {
+ __ FSgnjD(out, in0, in1);
+ } else {
+ __ FSgnjS(out, in0, in1);
+ }
+}
+
+void IntrinsicLocationsBuilderRISCV64::VisitMathCopySignDouble(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(allocator_, invoke);
+}
+
+void IntrinsicCodeGeneratorRISCV64::VisitMathCopySignDouble(HInvoke* invoke) {
+ GenMathCopySign(codegen_, invoke, DataType::Type::kFloat64);
+}
+
+void IntrinsicLocationsBuilderRISCV64::VisitMathCopySignFloat(HInvoke* invoke) {
+ CreateFPFPToFPCallLocations(allocator_, invoke);
+}
+
+void IntrinsicCodeGeneratorRISCV64::VisitMathCopySignFloat(HInvoke* invoke) {
+ GenMathCopySign(codegen_, invoke, DataType::Type::kFloat32);
+}
+
#define MARK_UNIMPLEMENTED(Name) UNIMPLEMENTED_INTRINSIC(RISCV64, Name)
UNIMPLEMENTED_INTRINSIC_LIST_RISCV64(MARK_UNIMPLEMENTED);
#undef MARK_UNIMPLEMENTED
diff --git a/runtime/intrinsics_list.h b/runtime/intrinsics_list.h
index 862c3cbd52..c079839fca 100644
--- a/runtime/intrinsics_list.h
+++ b/runtime/intrinsics_list.h
@@ -179,6 +179,10 @@
V(MathRoundDouble, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "round", "(D)J") \
V(MathRoundFloat, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "round", "(F)I") \
V(MathMultiplyHigh, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "multiplyHigh", "(JJ)J") \
+ V(MathSignumDouble, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "signum", "(D)D") \
+ V(MathSignumFloat, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "signum", "(F)F") \
+ V(MathCopySignDouble, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "copySign", "(DD)D") \
+ V(MathCopySignFloat, kStatic, kNeedsEnvironment, kNoSideEffects, kNoThrow, "Ljava/lang/Math;", "copySign", "(FF)F") \
V(SystemArrayCopyByte, kStatic, kNeedsEnvironment, kAllSideEffects, kCanThrow, "Ljava/lang/System;", "arraycopy", "([BI[BII)V") \
V(SystemArrayCopyChar, kStatic, kNeedsEnvironment, kAllSideEffects, kCanThrow, "Ljava/lang/System;", "arraycopy", "([CI[CII)V") \
V(SystemArrayCopyInt, kStatic, kNeedsEnvironment, kAllSideEffects, kCanThrow, "Ljava/lang/System;", "arraycopy", "([II[III)V") \
diff --git a/runtime/oat/image.cc b/runtime/oat/image.cc
index f69989b592..7e94c82c85 100644
--- a/runtime/oat/image.cc
+++ b/runtime/oat/image.cc
@@ -34,8 +34,8 @@
namespace art HIDDEN {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-// Last change: Add unsignedRemainder intrinsics.
-const uint8_t ImageHeader::kImageVersion[] = { '1', '1', '1', '\0' };
+// Last change: Add signum, copySign intrinsics.
+const uint8_t ImageHeader::kImageVersion[] = { '1', '1', '2', '\0' };
ImageHeader::ImageHeader(uint32_t image_reservation_size,
uint32_t component_count,
diff --git a/test/057-math-intrinsics/expected-stderr.txt b/test/057-math-intrinsics/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/057-math-intrinsics/expected-stderr.txt
diff --git a/test/057-math-intrinsics/expected-stdout.txt b/test/057-math-intrinsics/expected-stdout.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/057-math-intrinsics/expected-stdout.txt
diff --git a/test/057-math-intrinsics/info.txt b/test/057-math-intrinsics/info.txt
new file mode 100644
index 0000000000..f96b45f6f7
--- /dev/null
+++ b/test/057-math-intrinsics/info.txt
@@ -0,0 +1,3 @@
+Tests for math intrinsics:
+ - signum {float|double},
+ - copySign {float|double}.
diff --git a/test/057-math-intrinsics/src/CopySignTest.java b/test/057-math-intrinsics/src/CopySignTest.java
new file mode 100644
index 0000000000..3f7d6ecfe2
--- /dev/null
+++ b/test/057-math-intrinsics/src/CopySignTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to test
+// it does compile the method, and that $noinline$ is a marker to
+// test that it does not inline it.
+
+public class CopySignTest {
+
+ public static void main() {
+ copySignFloat();
+ copySignDouble();
+ }
+
+ private static float $opt$noinline$copySignFloat(float a, float b) {
+ return Math.copySign(a, b);
+ }
+
+ private static void copySignFloat() {
+ float testCases [][] = {
+ { +0.0f,
+ Float.MIN_VALUE,
+ 0x0.fffffcP-126f,
+ 0x0.fffffeP-126f,
+ Float.MIN_NORMAL,
+ 1.0f,
+ 3.0f,
+ 0x1.fffffcP+127f,
+ Float.MAX_VALUE,
+ Float.POSITIVE_INFINITY,
+ Float.NaN
+ },
+ { Float.NEGATIVE_INFINITY,
+ -Float.MAX_VALUE,
+ -3.0f,
+ -1.0f,
+ -Float.MIN_NORMAL,
+ -0x0.fffffcP-126f,
+ -0x0.fffffeP-126f,
+ -Float.MIN_VALUE,
+ -0.0f,
+ Float.intBitsToFloat(0xFFC00000), // "negative" NaN
+ -0x1.fffffcP+127f
+ }
+ };
+
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int m = 0; m < testCases[i].length; m++) {
+ for (int n = 0; n < testCases[j].length; n++) {
+ float expected = (j == 0 ? 1.0f : -1.0f) * Math.abs(testCases[i][m]);
+
+ float calculated = Math.copySign(testCases[i][m], testCases[j][n]);
+ if (Float.isNaN(testCases[i][m])) {
+ assertEquals(calculated, Float.NaN);
+ } else if (Float.isNaN(testCases[j][n])) {
+ assertEquals(Math.abs(calculated), Math.abs(testCases[i][m]));
+ } else {
+ assertEquals(calculated, expected);
+ }
+
+ calculated = $opt$noinline$copySignFloat(testCases[i][m], testCases[j][n]);
+ if (Float.isNaN(testCases[i][m])) {
+ assertEquals(calculated, Float.NaN);
+ } else if (Float.isNaN(testCases[j][n])) {
+ assertEquals(Math.abs(calculated), Math.abs(testCases[i][m]));
+ } else {
+ assertEquals(calculated, expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static double $opt$noinline$copySignDouble(double a, double b) {
+ return Math.copySign(a, b);
+ }
+
+ private static void copySignDouble() {
+ double testCases [][] = {
+ { +0.0d,
+ Double.MIN_VALUE,
+ 0x0.ffffffffffffeP-1022,
+ 0x0.fffffffffffffP-1022,
+ Double.MIN_NORMAL,
+ 1.0d,
+ 3.0d,
+ 0x1.ffffffffffffeP+1023,
+ Double.MAX_VALUE,
+ Double.POSITIVE_INFINITY,
+ Double.NaN
+ },
+ { Double.NEGATIVE_INFINITY,
+ -Double.MAX_VALUE,
+ -3.0d,
+ -1.0d,
+ -Double.MIN_NORMAL,
+ -0x0.ffffffffffffeP-1022,
+ -0x0.fffffffffffffP-1022,
+ -Double.MIN_VALUE,
+ -0.0d,
+ Double.longBitsToDouble(0xfff8000000000000L), // "negative" NaN
+ -0x1.ffffffffffffeP+1023
+ }
+ };
+
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 2; j++) {
+ for (int m = 0; m < testCases[i].length; m++) {
+ for (int n = 0; n < testCases[j].length; n++) {
+ double expected = (j == 0 ? 1.0f : -1.0f) * Math.abs(testCases[i][m]);
+
+ double calculated = Math.copySign(testCases[i][m], testCases[j][n]);
+ if (Double.isNaN(testCases[i][m])) {
+ assertEquals(calculated, Double.NaN);
+ } else if (Double.isNaN(testCases[j][n])) {
+ assertEquals(Math.abs(calculated), Math.abs(testCases[i][m]));
+ } else {
+ assertEquals(calculated, expected);
+ }
+
+ calculated = $opt$noinline$copySignDouble(testCases[i][m], testCases[j][n]);
+ if (Double.isNaN(testCases[i][m])) {
+ assertEquals(calculated, Double.NaN);
+ } else if (Double.isNaN(testCases[j][n])) {
+ assertEquals(Math.abs(calculated), Math.abs(testCases[i][m]));
+ } else {
+ assertEquals(calculated, expected);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private static void assertEquals(float calculated, float expected) {
+ if (0 != Float.compare(calculated, expected)) {
+ throw new Error("Expected: " + expected + ", found: " + calculated);
+ }
+ }
+
+ private static void assertEquals(double calculated, double expected) {
+ if (0 != Double.compare(calculated, expected)) {
+ throw new Error("Expected: " + expected + ", found: " + calculated);
+ }
+ }
+
+}
diff --git a/test/057-math-intrinsics/src/Main.java b/test/057-math-intrinsics/src/Main.java
new file mode 100644
index 0000000000..32f90ef79e
--- /dev/null
+++ b/test/057-math-intrinsics/src/Main.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String args[]) {
+ SignumTest.main();
+ CopySignTest.main();
+ }
+}
diff --git a/test/057-math-intrinsics/src/SignumTest.java b/test/057-math-intrinsics/src/SignumTest.java
new file mode 100644
index 0000000000..eff200bc51
--- /dev/null
+++ b/test/057-math-intrinsics/src/SignumTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Note that $opt$ is a marker for the optimizing compiler to test
+// it does compile the method, and that $noinline$ is a marker to
+// test that it does not inline it.
+
+public class SignumTest {
+
+ public static void main() {
+ signumFloat();
+ signumFloat_noinline();
+ signumDouble();
+ signumDouble_noinline();
+ }
+
+ private static void signumFloat() {
+ assertEquals(Math.signum(123.4f), 1.0f);
+ assertEquals(Math.signum(-56.7f), -1.0f);
+ assertEquals(Math.signum(7e30f), 1.0f);
+ assertEquals(Math.signum(-0.3e30f), -1.0f);
+ assertEquals(Math.signum(Float.MAX_VALUE), 1.0f);
+ assertEquals(Math.signum(-Float.MAX_VALUE), -1.0f);
+ assertEquals(Math.signum(Float.MIN_VALUE), 1.0f);
+ assertEquals(Math.signum(-Float.MIN_VALUE), -1.0f);
+ assertEquals(Math.signum(0.0f), 0.0f);
+ assertEquals(Math.signum(-0.0f), -0.0f);
+ assertEquals(Math.signum(Float.POSITIVE_INFINITY), 1.0f);
+ assertEquals(Math.signum(Float.NEGATIVE_INFINITY), -1.0f);
+ assertEquals(Math.signum(Float.NaN), Float.NaN);
+ assertEquals(Math.signum(Float.MIN_NORMAL), 1.0f);
+ assertEquals(Math.signum(-Float.MIN_NORMAL), -1.0f);
+ assertEquals(Math.signum(0x0.0002P-126f), 1.0f);
+ assertEquals(Math.signum(-0x0.0002P-126f), -1.0f);
+ }
+
+ private static float $opt$noinline$signumFloat(float a) {
+ return Math.signum(a);
+ }
+
+ private static void signumFloat_noinline() {
+ assertEquals($opt$noinline$signumFloat(123.4f), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-56.7f), -1.0f);
+ assertEquals($opt$noinline$signumFloat(7e30f), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-0.3e30f), -1.0f);
+ assertEquals($opt$noinline$signumFloat(Float.MAX_VALUE), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-Float.MAX_VALUE), -1.0f);
+ assertEquals($opt$noinline$signumFloat(Float.MIN_VALUE), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-Float.MIN_VALUE), -1.0f);
+ assertEquals($opt$noinline$signumFloat(0.0f), 0.0f);
+ assertEquals($opt$noinline$signumFloat(-0.0f), -0.0f);
+ assertEquals($opt$noinline$signumFloat(Float.POSITIVE_INFINITY), 1.0f);
+ assertEquals($opt$noinline$signumFloat(Float.NEGATIVE_INFINITY), -1.0f);
+ assertEquals($opt$noinline$signumFloat(Float.NaN), Float.NaN);
+ assertEquals($opt$noinline$signumFloat(Float.MIN_NORMAL), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-Float.MIN_NORMAL), -1.0f);
+ assertEquals($opt$noinline$signumFloat(0x0.0002P-126f), 1.0f);
+ assertEquals($opt$noinline$signumFloat(-0x0.0002P-126f), -1.0f);
+ }
+
+ private static void signumDouble() {
+ assertEquals(Math.signum(123.4d), 1.0d);
+ assertEquals(Math.signum(-56.7d), -1.0d);
+ assertEquals(Math.signum(7e30d), 1.0d);
+ assertEquals(Math.signum(-0.3e30d), -1.0d);
+ assertEquals(Math.signum(Double.MAX_VALUE), 1.0d);
+ assertEquals(Math.signum(-Double.MAX_VALUE), -1.0d);
+ assertEquals(Math.signum(Double.MIN_VALUE), 1.0d);
+ assertEquals(Math.signum(-Double.MIN_VALUE), -1.0d);
+ assertEquals(Math.signum(0.0d), 0.0d);
+ assertEquals(Math.signum(-0.0d), -0.0d);
+ assertEquals(Math.signum(Double.POSITIVE_INFINITY), 1.0d);
+ assertEquals(Math.signum(Double.NEGATIVE_INFINITY), -1.0d);
+ assertEquals(Math.signum(Double.NaN), Double.NaN);
+ assertEquals(Math.signum(Double.MIN_NORMAL), 1.0d);
+ assertEquals(Math.signum(-Double.MIN_NORMAL), -1.0d);
+ assertEquals(Math.signum(0x0.00000001P-1022), 1.0d);
+ assertEquals(Math.signum(-0x0.00000001P-1022), -1.0d);
+ }
+
+ private static double $opt$noinline$signumDouble(double a) {
+ return Math.signum(a);
+ }
+
+ private static void signumDouble_noinline() {
+ assertEquals($opt$noinline$signumDouble(123.4d), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-56.7d), -1.0d);
+ assertEquals($opt$noinline$signumDouble(7e30d), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-0.3e30d), -1.0d);
+ assertEquals($opt$noinline$signumDouble(Double.MAX_VALUE), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-Double.MAX_VALUE), -1.0d);
+ assertEquals($opt$noinline$signumDouble(Double.MIN_VALUE), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-Double.MIN_VALUE), -1.0d);
+ assertEquals($opt$noinline$signumDouble(0.0d), 0.0d);
+ assertEquals($opt$noinline$signumDouble(-0.0d), -0.0d);
+ assertEquals($opt$noinline$signumDouble(Double.POSITIVE_INFINITY), 1.0d);
+ assertEquals($opt$noinline$signumDouble(Double.NEGATIVE_INFINITY), -1.0d);
+ assertEquals($opt$noinline$signumDouble(Double.NaN), Double.NaN);
+ assertEquals($opt$noinline$signumDouble(Double.MIN_NORMAL), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-Double.MIN_NORMAL), -1.0d);
+ assertEquals($opt$noinline$signumDouble(0x0.00000001P-1022), 1.0d);
+ assertEquals($opt$noinline$signumDouble(-0x0.00000001P-1022), -1.0d);
+ }
+
+ private static void assertEquals(float calculated, float expected) {
+ if (0 != Float.compare(calculated, expected)) {
+ throw new Error("Expected: " + expected + ", found: " + calculated);
+ }
+ }
+
+ private static void assertEquals(double calculated, double expected) {
+ if (0 != Double.compare(calculated, expected)) {
+ throw new Error("Expected: " + expected + ", found: " + calculated);
+ }
+ }
+
+}