diff options
author | 2024-07-22 18:16:20 +0000 | |
---|---|---|
committer | 2024-08-12 15:17:18 +0000 | |
commit | bda29056665961578f2b97cd6d40daca2058694d (patch) | |
tree | ff3df2723b2c19ac393dbbdb6137e296e94e2868 | |
parent | 3244be57e9c170696b0a17369bcf3e77f82ee9ec (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.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_riscv64.cc | 86 | ||||
-rw-r--r-- | runtime/intrinsics_list.h | 4 | ||||
-rw-r--r-- | runtime/oat/image.cc | 4 | ||||
-rw-r--r-- | test/057-math-intrinsics/expected-stderr.txt | 0 | ||||
-rw-r--r-- | test/057-math-intrinsics/expected-stdout.txt | 0 | ||||
-rw-r--r-- | test/057-math-intrinsics/info.txt | 3 | ||||
-rw-r--r-- | test/057-math-intrinsics/src/CopySignTest.java | 162 | ||||
-rw-r--r-- | test/057-math-intrinsics/src/Main.java | 22 | ||||
-rw-r--r-- | test/057-math-intrinsics/src/SignumTest.java | 130 |
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); + } + } + +} |