From f7231c392f73e2acb24a1456a021dd39216955a6 Mon Sep 17 00:00:00 2001 From: Matthew Hall Date: Wed, 18 Sep 2024 14:23:11 -0400 Subject: [PATCH] Added support for java behaviour of fmin/fmax/etc + fixed tests - separate evaluators for J9 vs omr to return the NaN unchanged - compare int/long bits instead of float bits to check behaviour for +-0 and NaNs Signed-off-by: Matthew Hall --- .../compiler/z/codegen/J9CodeGenerator.cpp | 18 +- .../compiler/z/codegen/J9TreeEvaluator.cpp | 136 ++++++++-- .../compiler/z/codegen/J9TreeEvaluator.hpp | 6 +- .../recognizedMethod/TestJavaLangMath.java | 237 ++++++++++++++---- .../test/recognizedMethod/TestMathUtils.java | 232 +++++++++++++++++ 5 files changed, 549 insertions(+), 80 deletions(-) create mode 100644 test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java diff --git a/runtime/compiler/z/codegen/J9CodeGenerator.cpp b/runtime/compiler/z/codegen/J9CodeGenerator.cpp index fd1e832f9c4..cf90fecc1c0 100644 --- a/runtime/compiler/z/codegen/J9CodeGenerator.cpp +++ b/runtime/compiler/z/codegen/J9CodeGenerator.cpp @@ -4079,20 +4079,24 @@ J9::Z::CodeGenerator::inlineDirectCall( } } - if (!comp->getOption(TR_DisableSIMDDoubleMaxMin) && cg->getSupportsVectorRegisters()) - { - switch (methodSymbol->getRecognizedMethod()) - { + if (cg->getSupportsInlineMath_MaxMin_FD()) { + switch (methodSymbol->getRecognizedMethod()) { case TR::java_lang_Math_max_D: - resultReg = TR::TreeEvaluator::inlineDoubleMax(node, cg); + resultReg = J9::Z::TreeEvaluator::dmaxEvaluator(node, cg); return true; case TR::java_lang_Math_min_D: - resultReg = TR::TreeEvaluator::inlineDoubleMin(node, cg); + resultReg = J9::Z::TreeEvaluator::dminEvaluator(node, cg); + return true; + case TR::java_lang_Math_max_F: + resultReg = J9::Z::TreeEvaluator::fmaxEvaluator(node, cg); + return true; + case TR::java_lang_Math_min_F: + resultReg = J9::Z::TreeEvaluator::fminEvaluator(node, cg); return true; default: break; - } } + } switch (methodSymbol->getRecognizedMethod()) { diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp index 482c8c0ed4e..4507e1f711a 100644 --- a/runtime/compiler/z/codegen/J9TreeEvaluator.cpp +++ b/runtime/compiler/z/codegen/J9TreeEvaluator.cpp @@ -906,10 +906,13 @@ allocateWriteBarrierInternalPointerRegister(TR::CodeGenerator * cg, TR::Node * s } -extern TR::Register * -doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) +static TR::Register* +fpMinMaxVectorHelper(TR::Node* node, TR::CodeGenerator* cg, TR::DataTypes dataType, bool isMaxOp) { TR_ASSERT(node->getNumChildren() >= 1 || node->getNumChildren() <= 2, "node has incorrect number of children"); + TR_ASSERT(dataType == TR::DataTypes::Double || dataType == TR::DataTypes::Float, "incorrect dataType"); + + int type = dataType == TR::DataTypes::Double ? 3 : 2; //for type mask /* ===================== Allocating Registers ===================== */ @@ -926,17 +929,17 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) TR::Register * v2 = cg->evaluate(node->getSecondChild()); /* ====== WFTCIDB V16,V0,X'F' a == NaN ====== */ - generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v16, v0, 0xF, 8, 3); + generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v16, v0, 0xF, 8, type); /* ====== For Max: WFCHE V17,V0,V2 Compare a >= b ====== */ if(isMaxOp) { - generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v0, v2, 0, 8, 3); + generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v0, v2, 0, 8, type); } /* ====== For Min: WFCHE V17,V0,V2 Compare a <= b ====== */ else { - generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v2, v0, 0, 8, 3); + generateVRRcInstruction(cg, TR::InstOpCode::VFCH, node, v17, v2, v0, 0, 8, type); } /* ====== VO V16,V16,V17 (a >= b) || (a == NaN) ====== */ @@ -945,15 +948,15 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) /* ====== For Max: WFTCIDB V17,V0,X'800' a == +0 ====== */ if(isMaxOp) { - generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x800, 8, 3); + generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x800, 8, type); } /* ====== For Min: WFTCIDB V17,V0,X'400' a == -0 ====== */ else { - generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x400, 8, 3); + generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v17, v0, 0x400, 8, type); } /* ====== WFTCIDB V18,V2,X'C00' b == 0 ====== */ - generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v18, v2, 0xC00, 8, 3); + generateVRIeInstruction(cg, TR::InstOpCode::VFTCI, node, v18, v2, 0xC00, 8, type); /* ====== VN V17,V17,V18 (a == -0) && (b == 0) ====== */ generateVRRcInstruction(cg, TR::InstOpCode::VN, node, v17, v17, v18, 0, 0, 0); @@ -964,6 +967,7 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) /* ====== VSEL V0,V0,V2,V16 ====== */ generateVRReInstruction(cg, TR::InstOpCode::VSEL, node, v0, v0, v2, v16); + /* ===================== Deallocating Registers ===================== */ cg->stopUsingRegister(v2); cg->stopUsingRegister(v16); @@ -978,6 +982,110 @@ doubleMaxMinHelper(TR::Node *node, TR::CodeGenerator *cg, bool isMaxOp) return node->getRegister(); } +static TR::Register* +fpMinMaxHelper(TR::Node* node, TR::CodeGenerator* cg, TR::InstOpCode::Mnemonic compareRROp, TR::InstOpCode::S390BranchCondition branchCond, TR::InstOpCode::Mnemonic moveRROp) + { + TR::Node* lhsNode = node->getChild(0); + TR::Node* rhsNode = node->getChild(1); + + TR::Register* lhsReg = cg->gprClobberEvaluate(lhsNode); + TR::Register* rhsReg = cg->evaluate(rhsNode); + + TR::LabelSymbol* cFlowRegionStart = generateLabelSymbol(cg); + TR::LabelSymbol* cFlowRegionEnd = generateLabelSymbol(cg); + + TR::LabelSymbol* swap = generateLabelSymbol(cg); + TR::LabelSymbol* equalRegion = generateLabelSymbol(cg); + + generateS390LabelInstruction(cg, TR::InstOpCode::label, node, cFlowRegionStart); + cFlowRegionStart->setStartInternalControlFlow(); + + generateRREInstruction(cg, compareRROp, node, lhsReg, rhsReg); + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, branchCond, node, cFlowRegionEnd); + //Check for NaN operands for float and double + //Support float and double +0/-0 comparisons adhering to IEEE 754 standard + //Checking if operands are equal, then branching to equalRegion, otherwise fall through for NaN case handling + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_MASK8, node, equalRegion); + + // If first operand is NaN, then we are done, otherwise fallthrough to move second operand as result + generateRXEInstruction(cg, node->getOpCode().isDouble() ? TR::InstOpCode::TCDB : TR::InstOpCode::TCEB, node, lhsReg, generateS390MemoryReference(0x00F, cg),0); + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_MASK4, node, cFlowRegionEnd); + //branch to swap label, since either second operand is NaN, or entire satisfies the alternate condition code + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_BRC, node, swap); + + //code for handling +0/-0 comparisons when operands are equal + generateS390LabelInstruction(cg, TR::InstOpCode::label, node, equalRegion); + if (node->getOpCode().isMax()) + { + //For Max calls, checking if first operand is +0, then we are done, otherwise fall through for swap + generateRXEInstruction(cg, node->getOpCode().isDouble() ? TR::InstOpCode::TCDB : TR::InstOpCode::TCEB, node, lhsReg, generateS390MemoryReference(0x800, cg), 0); // lhsReg is +0 ? + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_MASK4, node, cFlowRegionEnd); // it is +0 + } + else if (node->getOpCode().isMin()) + { + //For Min calls, checking if first operand is not +0, then we are done, otherwise fall through for swap + generateRXEInstruction(cg, node->getOpCode().isDouble() ? TR::InstOpCode::TCDB : TR::InstOpCode::TCEB, node, lhsReg, generateS390MemoryReference(0x400, cg), 0); // lhsReg is -0 ? + generateS390BranchInstruction(cg, TR::InstOpCode::BRC, TR::InstOpCode::COND_MASK4, node, cFlowRegionEnd); + } + + generateS390LabelInstruction(cg, TR::InstOpCode::label, node, swap); + //Move resulting operand to lhsReg as fallthrough for alternate Condition Code + generateRREInstruction(cg, moveRROp, node, lhsReg, rhsReg); + + TR::RegisterDependencyConditions* deps = new (cg->trHeapMemory()) TR::RegisterDependencyConditions(0, 2, cg); + deps->addPostConditionIfNotAlreadyInserted(lhsReg, TR::RealRegister::AssignAny); + deps->addPostConditionIfNotAlreadyInserted(rhsReg, TR::RealRegister::AssignAny); + + generateS390LabelInstruction(cg, TR::InstOpCode::label, node, cFlowRegionEnd, deps); + cFlowRegionEnd->setEndInternalControlFlow(); + + node->setRegister(lhsReg); + cg->decReferenceCount(lhsNode); + cg->decReferenceCount(rhsNode); + + return lhsReg; + } + +TR::Register* +J9::Z::TreeEvaluator::fminEvaluator(TR::Node *node, TR::CodeGenerator *cg) + { + if (cg->getSupportsVectorRegisters()) + { + return fpMinMaxVectorHelper(node, cg, TR::DataTypes::Float, false); + } + return fpMinMaxHelper(node, cg, TR::InstOpCode::CEBR, TR::InstOpCode::COND_MASK4, TR::InstOpCode::LER); + } + +TR::Register* +J9::Z::TreeEvaluator::dminEvaluator(TR::Node *node, TR::CodeGenerator *cg) + { + if (cg->getSupportsVectorRegisters()) + { + return fpMinMaxVectorHelper(node, cg, TR::DataTypes::Double, false); + } + return fpMinMaxHelper(node, cg, TR::InstOpCode::CDBR, TR::InstOpCode::COND_MASK4, TR::InstOpCode::LDR); + } + +TR::Register* +J9::Z::TreeEvaluator::fmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg) + { + if (cg->getSupportsVectorRegisters()) + { + return fpMinMaxVectorHelper(node, cg, TR::DataTypes::Float, true); + } + return fpMinMaxHelper(node, cg, TR::InstOpCode::CEBR, TR::InstOpCode::COND_MASK2, TR::InstOpCode::LER); + } + +TR::Register* +J9::Z::TreeEvaluator::dmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg) + { + if (cg->getSupportsVectorRegisters()) + { + return fpMinMaxVectorHelper(node, cg, TR::DataTypes::Double, true); + } + return fpMinMaxHelper(node, cg, TR::InstOpCode::CDBR, TR::InstOpCode::COND_MASK2, TR::InstOpCode::LDR); + } + TR::Register* J9::Z::TreeEvaluator::inlineVectorizedStringIndexOf(TR::Node* node, TR::CodeGenerator* cg, bool isUTF16) { @@ -2945,19 +3053,7 @@ J9::Z::TreeEvaluator::toLowerIntrinsic(TR::Node *node, TR::CodeGenerator *cg, bo return caseConversionHelper(node, cg, false, isCompressedString); } -TR::Register* -J9::Z::TreeEvaluator::inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg) - { - cg->generateDebugCounter("z13/simd/doubleMax", 1, TR::DebugCounter::Free); - return doubleMaxMinHelper(node, cg, true); - } -TR::Register* -J9::Z::TreeEvaluator::inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg) - { - cg->generateDebugCounter("z13/simd/doubleMin", 1, TR::DebugCounter::Free); - return doubleMaxMinHelper(node, cg, false); - } TR::Register * J9::Z::TreeEvaluator::inlineMathFma(TR::Node *node, TR::CodeGenerator *cg) diff --git a/runtime/compiler/z/codegen/J9TreeEvaluator.hpp b/runtime/compiler/z/codegen/J9TreeEvaluator.hpp index da2286d3b73..c21264611ae 100644 --- a/runtime/compiler/z/codegen/J9TreeEvaluator.hpp +++ b/runtime/compiler/z/codegen/J9TreeEvaluator.hpp @@ -126,8 +126,10 @@ class OMR_EXTENSIBLE TreeEvaluator: public J9::TreeEvaluator */ static TR::Register *inlineVectorizedStringIndexOf(TR::Node *node, TR::CodeGenerator *cg, bool isCompressed); static TR::Register *inlineIntrinsicIndexOf(TR::Node *node, TR::CodeGenerator *cg, bool isLatin1); - static TR::Register *inlineDoubleMax(TR::Node *node, TR::CodeGenerator *cg); - static TR::Register *inlineDoubleMin(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *fminEvaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *dminEvaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *fmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg); + static TR::Register *dmaxEvaluator(TR::Node *node, TR::CodeGenerator *cg); static TR::Register *inlineMathFma(TR::Node *node, TR::CodeGenerator *cg); /* This Evaluator generates the SIMD routine for methods diff --git a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java index e8ed06e2980..2593acd1653 100644 --- a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java +++ b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestJavaLangMath.java @@ -24,6 +24,9 @@ import org.testng.AssertJUnit; import org.testng.annotations.Test; import java.util.Random; +import java.io.*; +import java.lang.Math.*; +import org.testng.asserts.SoftAssert; public class TestJavaLangMath { @@ -58,27 +61,94 @@ public void test_java_lang_Math_sqrt() { } /** - * Tests all execution paths defined by the {@link Math.max} method, for float and double data types. + * Tests all execution paths defined by the {@link Math.max} methods, for float and double data types. */ @Test(groups = {"level.sanity"}, invocationCount=2) public void test_java_lang_Math_max() { - // Test Math.max for double type with NaN & +0/-0 values - AssertJUnit.assertTrue(Double.isNaN(Math.max(Double.NaN, Double.NaN))); - AssertJUnit.assertTrue(Double.isNaN(Math.max(Double.NaN, 0.0))); - AssertJUnit.assertTrue(Double.isNaN(Math.max(0.0, Double.NaN))); - AssertJUnit.assertTrue(Double.isNaN(Math.max(Double.NaN, -0.0))); - AssertJUnit.assertTrue(Double.isNaN(Math.max(-0.0, Double.NaN))); - AssertJUnit.assertEquals(0.0, Math.max(0.0, -0.0), 0.0); - AssertJUnit.assertEquals(0.0, Math.max(-0.0, 0.0), 0.0); - - // Test Math.max for float type with NaN & +0/-0 values - AssertJUnit.assertTrue(Float.isNaN(Math.max(Float.NaN, Float.NaN))); - AssertJUnit.assertTrue(Float.isNaN(Math.max(Float.NaN, 0.0f))); - AssertJUnit.assertTrue(Float.isNaN(Math.max(0.0f, Float.NaN))); - AssertJUnit.assertTrue(Float.isNaN(Math.max(Float.NaN, -0.0f))); - AssertJUnit.assertTrue(Float.isNaN(Math.max(-0.0f, Float.NaN))); - AssertJUnit.assertEquals(0.0f, Math.max(0.0f, -0.0f), 0.0f); - AssertJUnit.assertEquals(0.0f, Math.max(-0.0f, 0.0f), 0.0f); + double count = 0; + float countf = 0; + Random r = new Random(); + //ensures jit compilation + for (int i = 0; i < 10; i++){ + countf += TestMathUtils.fmax(r.nextFloat(), r.nextFloat()); + countf += TestMathUtils.fmin(r.nextFloat(), r.nextFloat()); + count += TestMathUtils.dmin(Math.random(), Math.random()); + count += TestMathUtils.dmax(Math.random(), Math.random()); + } + //sanity check. If this fails we know the system converts NaNs to some canonical form automatically upon copy + AssertJUnit.assertEquals("failed sanity check", TestMathUtils.fqNaNBits, Float.floatToRawIntBits(TestMathUtils.fqNaN)); + + SoftAssert softAssert = new SoftAssert(); + for (int i = 0; i < 100; i++){ + TestMathUtils.NaNTestPair fp = TestMathUtils.getFloatNaNPair(); + if (fp == null) continue; + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(fp.first.floatValue(), fp.second.floatValue())), fp.expected.intValue(), "failed fmax" + fp.message); + } + TestMathUtils.formatErrorFloat(softAssert, "max Float NaNs"); + softAssert = new SoftAssert(); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(+0.0f, -0.0f)), TestMathUtils.pfZeroBits, "failed fmax(+0, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(-0.0f, +0.0f)), TestMathUtils.pfZeroBits, "failed fmax(-0, +0)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fqNaN, +0.0f)), TestMathUtils.fqNaNBits, "failed fmax(qNaN, +0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(+0.0f, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmax(+0, qNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fqNaN, -0.0f)), TestMathUtils.fqNaNBits, "failed fmax(qNaN, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(-0.0f, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmax(-0, qNaN)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fsNaN, +0.0f)), TestMathUtils.fsNaNBits, "failed fmax(sNaN, +0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(+0.0f, TestMathUtils.fsNaN)), TestMathUtils.fsNaNBits, "failed fmax(+0, sNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fsNaN, -0.0f)), TestMathUtils.fsNaNBits, "failed fmax(sNaN, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(-0.0f, TestMathUtils.fsNaN)), TestMathUtils.fsNaNBits, "failed fmax(-0, sNaN)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(-0.0f, TestMathUtils.fpInf)), TestMathUtils.fpInfBits, "failed fmax(-0, +Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(-0.0f, TestMathUtils.fnInf)), TestMathUtils.nfZeroBits, "failed fmax(-0, -Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(+0.0f, TestMathUtils.fpInf)), TestMathUtils.fpInfBits, "failed fmax(+0, +Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(+0.0f, TestMathUtils.fnInf)), TestMathUtils.pfZeroBits, "failed fmax(+0, -Inf)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fpInf, -0.0f)), TestMathUtils.fpInfBits, "failed fmax(+Inf, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fnInf, -0.0f)), TestMathUtils.nfZeroBits, "failed fmax(-Inf, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fpInf, +0.0f)), TestMathUtils.fpInfBits, "failed fmax(+Inf, +0))"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fnInf, +0.0f)), TestMathUtils.pfZeroBits, "failed fmax(-Inf, +0)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fpInf, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmax(+inf, qNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmax(TestMathUtils.fqNaN, TestMathUtils.fpInf)), TestMathUtils.fqNaNBits, "failed fmax(qNaN, +Inf"); + + TestMathUtils.formatErrorFloat(softAssert, "max +0/-0 floats"); + + softAssert = new SoftAssert(); + for (int i = 0; i < 100; i++){ + TestMathUtils.NaNTestPair fp = TestMathUtils.getDoubleNaNPair(); + if (fp == null) continue; + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(fp.first.doubleValue(), fp.second.doubleValue())), fp.expected.longValue(), "failed dmax" + fp.message); + } + TestMathUtils.formatErrorDouble(softAssert, "max Double NaNs"); + + softAssert = new SoftAssert(); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(+0.0f, -0.0f)), TestMathUtils.pdZeroBits, "failed dmax(+0, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(-0.0f, +0.0f)), TestMathUtils.pdZeroBits, "failed dmax(-0, +0)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dqNaN, +0.0f)), TestMathUtils.dqNaNBits, "failed dmax(qNaN, +0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(+0.0f, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmax(+0, qNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dqNaN, -0.0f)), TestMathUtils.dqNaNBits, "failed dmax(qNaN, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(-0.0f, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmax(-0, qNaN)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dsNaN, +0.0f)), TestMathUtils.dsNaNBits, "failed dmax(sNaN, +0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(+0.0f, TestMathUtils.dsNaN)), TestMathUtils.dsNaNBits, "failed dmax(+0, sNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dsNaN, -0.0f)), TestMathUtils.dsNaNBits, "failed dmax(sNaN, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(-0.0f, TestMathUtils.dsNaN)), TestMathUtils.dsNaNBits, "failed dmax(-0, sNaN)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(-0.0f, TestMathUtils.dpInf)), TestMathUtils.dpInfBits, "failed dmax(-0, +Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(-0.0f, TestMathUtils.dnInf)), TestMathUtils.ndZeroBits, "failed dmax(-0, -Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(+0.0f, TestMathUtils.dpInf)), TestMathUtils.dpInfBits, "failed dmax(+0, +Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(+0.0f, TestMathUtils.dnInf)), TestMathUtils.pdZeroBits, "failed dmax(+0, -Inf)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dpInf, -0.0f)), TestMathUtils.dpInfBits, "failed dmax(+Inf, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dnInf, -0.0f)), TestMathUtils.ndZeroBits, "failed dmax(-Inf, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dpInf, +0.0f)), TestMathUtils.dpInfBits, "failed dmax(+Inf, +0))"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dnInf, +0.0f)), TestMathUtils.pdZeroBits, "failed dmax(-Inf, +0)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dpInf, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmax(+inf, qNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmax(TestMathUtils.dqNaN, TestMathUtils.dpInf)), TestMathUtils.dqNaNBits, "failed dmax(qNaN, +Inf"); + TestMathUtils.formatErrorDouble(softAssert, "max +0/-0 doubles"); //Test Math.max with variation of random negative & positive doubles Random random = new Random(); @@ -86,20 +156,20 @@ public void test_java_lang_Math_max() { double d2 = -random.nextDouble() * 100; double d3 = random.nextDouble() * 100; // ensures number is positive and within a reasonable range double d4 = random.nextDouble() * 100; - AssertJUnit.assertEquals(Math.max(d1, d2), (d1 > d2) ? d1 : d2, 0.0); - AssertJUnit.assertEquals(Math.max(d2, d3), (d2 > d3) ? d2 : d3, 0.0); - AssertJUnit.assertEquals(Math.max(d3, d4), (d3 > d4) ? d3 : d4, 0.0); - AssertJUnit.assertEquals(Math.max(d1, d4), (d1 > d4) ? d1 : d4, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmax(d1, d2), (d1 > d2) ? d1 : d2, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmax(d2, d3), (d2 > d3) ? d2 : d3, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmax(d3, d4), (d3 > d4) ? d3 : d4, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmax(d1, d4), (d1 > d4) ? d1 : d4, 0.0); //Test Math.max with variation of random negative & positive floats float f1 = -random.nextFloat() * 100; // ensures number is negative and within a reasonable range float f2 = -random.nextFloat() * 100; float f3 = random.nextFloat() * 100; // ensures number is positive and within a reasonable range float f4 = random.nextFloat() * 100; - AssertJUnit.assertEquals(Math.max(f1, f2), (f1 > f2) ? f1 : f2, 0.0f); - AssertJUnit.assertEquals(Math.max(f2, f3), (f2 > f3) ? f2 : f3, 0.0f); - AssertJUnit.assertEquals(Math.max(f3, f4), (f3 > f4) ? f3 : f4, 0.0f); - AssertJUnit.assertEquals(Math.max(f1, f4), (f1 > f4) ? f1 : f4, 0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmax(f1, f2), (f1 > f2) ? f1 : f2, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmax(f2, f3), (f2 > f3) ? f2 : f3, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmax(f3, f4), (f3 > f4) ? f3 : f4, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmax(f1, f4), (f1 > f4) ? f1 : f4, +0.0f); } /** @@ -107,23 +177,88 @@ public void test_java_lang_Math_max() { */ @Test(groups = {"level.sanity"}, invocationCount=2) public void test_java_lang_Math_min() { - // Test Math.min for double type with NaN & +0/-0 values - AssertJUnit.assertTrue(Double.isNaN(Math.min(Double.NaN, Double.NaN))); - AssertJUnit.assertTrue(Double.isNaN(Math.min(Double.NaN, 0.0))); - AssertJUnit.assertTrue(Double.isNaN(Math.min(0.0, Double.NaN))); - AssertJUnit.assertTrue(Double.isNaN(Math.min(Double.NaN, -0.0))); - AssertJUnit.assertTrue(Double.isNaN(Math.min(-0.0, Double.NaN))); - AssertJUnit.assertEquals(-0.0, Math.min(0.0, -0.0), 0.0); - AssertJUnit.assertEquals(-0.0, Math.min(-0.0, 0.0), 0.0); - - // Test Math.min for float type with NaN & +0/-0 values - AssertJUnit.assertTrue(Float.isNaN(Math.min(Float.NaN, Float.NaN))); - AssertJUnit.assertTrue(Float.isNaN(Math.min(Float.NaN, 0.0f))); - AssertJUnit.assertTrue(Float.isNaN(Math.min(0.0f, Float.NaN))); - AssertJUnit.assertTrue(Float.isNaN(Math.min(Float.NaN, -0.0f))); - AssertJUnit.assertTrue(Float.isNaN(Math.min(-0.0f, Float.NaN))); - AssertJUnit.assertEquals(-0.0f, Math.min(0.0f, -0.0f), 0.0f); - AssertJUnit.assertEquals(-0.0f, Math.min(-0.0f, 0.0f), 0.0f); + double count = 0; + float countf = 0; + Random r = new Random(); + //ensures jit compilation + for (int i = 0; i < 10; i++){ + countf += TestMathUtils.fmax(r.nextFloat(), r.nextFloat()); + countf += TestMathUtils.fmin(r.nextFloat(), r.nextFloat()); + count += TestMathUtils.dmin(Math.random(), Math.random()); + count += TestMathUtils.dmax(Math.random(), Math.random()); + } + //sanity check. If this fails we know the system converts NaNs to some canonical form automatically upon copy + AssertJUnit.assertEquals("failed sanity check", TestMathUtils.fqNaNBits, Float.floatToRawIntBits(TestMathUtils.fqNaN)); + + SoftAssert softAssert = new SoftAssert(); + for (int i = 0; i < 100; i++){ + TestMathUtils.NaNTestPair fp = TestMathUtils.getFloatNaNPair(); + if (fp == null) continue; + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(fp.first.floatValue(), fp.second.floatValue())), fp.expected.intValue(), "failed fmin" + fp.message); + } + TestMathUtils.formatErrorFloat(softAssert, "min Float NaNs"); + softAssert = new SoftAssert(); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(+0.0f, -0.0f)), TestMathUtils.nfZeroBits, "failed fmin(+0, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(-0.0f, +0.0f)), TestMathUtils.nfZeroBits, "failed fmin(-0, +0)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fqNaN, +0.0f)), TestMathUtils.fqNaNBits, "failed fmin(qNaN, +0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(+0.0f, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmin(+0, qNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fqNaN, -0.0f)), TestMathUtils.fqNaNBits, "failed fmin(qNaN, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(-0.0f, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmin(-0, qNaN)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fsNaN, +0.0f)), TestMathUtils.fsNaNBits, "failed fmin(sNaN, +0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(+0.0f, TestMathUtils.fsNaN)), TestMathUtils.fsNaNBits, "failed fmin(+0, sNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fsNaN, -0.0f)), TestMathUtils.fsNaNBits, "failed fmin(sNaN, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(-0.0f, TestMathUtils.fsNaN)), TestMathUtils.fsNaNBits, "failed fmin(-0, sNaN)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(-0.0f, TestMathUtils.fpInf)), TestMathUtils.nfZeroBits, "failed fmin(-0, +Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(-0.0f, TestMathUtils.fnInf)), TestMathUtils.fnInfBits, "failed fmin(-0, -Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(+0.0f, TestMathUtils.fpInf)), TestMathUtils.pfZeroBits, "failed fmin(+0, +Inf)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(+0.0f, TestMathUtils.fnInf)), TestMathUtils.fnInfBits, "failed fmin(+0, -Inf)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fpInf, -0.0f)), TestMathUtils.nfZeroBits, "failed fmin(+Inf, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fnInf, -0.0f)), TestMathUtils.fnInfBits, "failed fmin(-Inf, -0)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fpInf, +0.0f)), TestMathUtils.pfZeroBits, "failed fmin(+Inf, +0))"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fnInf, +0.0f)), TestMathUtils.fnInfBits, "failed fmin(-Inf, +0)"); + + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fpInf, TestMathUtils.fqNaN)), TestMathUtils.fqNaNBits, "failed fmin(+inf, qNaN)"); + softAssert.assertEquals(Float.floatToRawIntBits(TestMathUtils.fmin(TestMathUtils.fqNaN, TestMathUtils.fpInf)), TestMathUtils.fqNaNBits, "failed fmin(qNaN, +Inf"); + TestMathUtils.formatErrorFloat(softAssert, "min +0/-0 floats"); + + softAssert = new SoftAssert(); + for (int i = 0; i < 100; i++){ + TestMathUtils.NaNTestPair fp = TestMathUtils.getDoubleNaNPair(); + if (fp == null) continue; + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(fp.first.doubleValue(), fp.second.doubleValue())), fp.expected.longValue(), "failed dmin" + fp.message); + } + TestMathUtils.formatErrorDouble(softAssert, "min Double NaNs"); + softAssert = new SoftAssert(); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(+0.0f, -0.0f)), TestMathUtils.ndZeroBits, "failed dmin(+0, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(-0.0f, +0.0f)), TestMathUtils.ndZeroBits, "failed dmin(-0, +0)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dqNaN, +0.0f)), TestMathUtils.dqNaNBits, "failed dmin(qNaN, +0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(+0.0f, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmin(+0, qNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dqNaN, -0.0f)), TestMathUtils.dqNaNBits, "failed dmin(q, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(-0.0f, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmin(-0, qNaN)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dsNaN, +0.0f)), TestMathUtils.dsNaNBits, "failed dmin(sNaN, +0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(+0.0f, TestMathUtils.dsNaN)), TestMathUtils.dsNaNBits, "failed dmin(+0, sNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dsNaN, -0.0f)), TestMathUtils.dsNaNBits, "failed dmin(sNaN, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(-0.0f, TestMathUtils.dsNaN)), TestMathUtils.dsNaNBits, "failed dmin(-0, sNaN)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(-0.0f, TestMathUtils.dpInf)), TestMathUtils.ndZeroBits, "failed dmin(-0, +Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(-0.0f, TestMathUtils.dnInf)), TestMathUtils.dnInfBits, "failed dmin(-0, -Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(+0.0f, TestMathUtils.dpInf)), TestMathUtils.pdZeroBits, "failed dmin(+0, +Inf)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(+0.0f, TestMathUtils.dnInf)), TestMathUtils.dnInfBits, "failed dmin(+0, -Inf)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dpInf, -0.0f)), TestMathUtils.ndZeroBits, "failed dmin(+Inf, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dnInf, -0.0f)), TestMathUtils.dnInfBits, "failed dmin(-Inf, -0)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dpInf, +0.0f)), TestMathUtils.pdZeroBits, "failed dmin(+Inf, +0))"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dnInf, +0.0f)), TestMathUtils.dnInfBits, "failed dmin(-Inf, +0)"); + + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dpInf, TestMathUtils.dqNaN)), TestMathUtils.dqNaNBits, "failed dmin(+inf, qNaN)"); + softAssert.assertEquals(Double.doubleToRawLongBits(TestMathUtils.dmin(TestMathUtils.dqNaN, TestMathUtils.dpInf)), TestMathUtils.dqNaNBits, "failed dmin(qNaN, +Inf"); + TestMathUtils.formatErrorDouble(softAssert, "min +0/-0 doubles"); //Test Math.min with variation of random negative & positive doubles Random random = new Random(); @@ -131,19 +266,19 @@ public void test_java_lang_Math_min() { double d2 = -random.nextDouble() * 100; double d3 = random.nextDouble() * 100; // ensures number is positive and within a reasonable range double d4 = random.nextDouble() * 100; - AssertJUnit.assertEquals(Math.min(d1, d2), (d1 < d2) ? d1 : d2, 0.0); - AssertJUnit.assertEquals(Math.min(d2, d3), (d2 < d3) ? d2 : d3, 0.0); - AssertJUnit.assertEquals(Math.min(d3, d4), (d3 < d4) ? d3 : d4, 0.0); - AssertJUnit.assertEquals(Math.min(d1, d4), (d1 < d4) ? d1 : d4, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmin(d1, d2), (d1 < d2) ? d1 : d2, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmin(d2, d3), (d2 < d3) ? d2 : d3, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmin(d3, d4), (d3 < d4) ? d3 : d4, 0.0); + AssertJUnit.assertEquals(TestMathUtils.dmin(d1, d4), (d1 < d4) ? d1 : d4, 0.0); //Test Math.min with variation of random negative & positive floats float f1 = -random.nextFloat() * 100; // ensures number is negative and within a reasonable range float f2 = -random.nextFloat() * 100; float f3 = random.nextFloat() * 100; // ensures number is positive and within a reasonable range float f4 = random.nextFloat() * 100; - AssertJUnit.assertEquals(Math.min(f1, f2), (f1 < f2) ? f1 : f2, 0.0f); - AssertJUnit.assertEquals(Math.min(f2, f3), (f2 < f3) ? f2 : f3, 0.0f); - AssertJUnit.assertEquals(Math.min(f3, f4), (f3 < f4) ? f3 : f4, 0.0f); - AssertJUnit.assertEquals(Math.min(f1, f4), (f1 < f4) ? f1 : f4, 0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmin(f1, f2), (f1 < f2) ? f1 : f2, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmin(f2, f3), (f2 < f3) ? f2 : f3, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmin(f3, f4), (f3 < f4) ? f3 : f4, +0.0f); + AssertJUnit.assertEquals(TestMathUtils.fmin(f1, f4), (f1 < f4) ? f1 : f4, +0.0f); } } diff --git a/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java new file mode 100644 index 00000000000..73b47b3b083 --- /dev/null +++ b/test/functional/JIT_Test/src/jit/test/recognizedMethod/TestMathUtils.java @@ -0,0 +1,232 @@ +package jit.test.recognizedMethod; +import java.util.Random; +import org.testng.asserts.SoftAssert; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.lang.Math.*; + +public class TestMathUtils { + // constants used to various min/max tests + public static final int fqNaNBits = 0x7fcabdef; + public static final float fqNaN = Float.intBitsToFloat(fqNaNBits); + public static final int fsNaNBits = 0x7faedbaf; + public static float fsNaN = Float.intBitsToFloat(fsNaNBits); + public static final int pfZeroBits = 0; + public static final int nfZeroBits = 0x80000000; + public static final float fpInf = Float.POSITIVE_INFINITY; + public static final float fnInf = Float.NEGATIVE_INFINITY; + public static final int fpInfBits = Float.floatToRawIntBits(fpInf); + public static final int fnInfBits = Float.floatToRawIntBits(fnInf); + private static final int fquietBit = 0x00400000; + private static final int fNaNExpStart = 0x7f800000; + private static final int fNaNMantisaMax = 0x00400000; + + public static final long dqNaNBits = 0x7ff800000000000fL; + public static final double dqNaN = Double.longBitsToDouble(dqNaNBits); + public static final long dsNaNBits = 0x7ff400000000000fL; + public static final double dsNaN = Double.longBitsToDouble(dsNaNBits); + public static final long pdZeroBits = 0L; + public static final long ndZeroBits = 0x8000000000000000L; + public static final double dpInf = Double.POSITIVE_INFINITY; + public static final double dnInf = Double.NEGATIVE_INFINITY; + public static final long dpInfBits = Double.doubleToRawLongBits(dpInf); + public static final long dnInfBits = Double.doubleToRawLongBits(dnInf); + private static final long dNaNMantisaMax = 0x0008000000000000L; + private static final long dNaNExpStart = 0x7ff0000000000000L; + private static final long dquietBit = 0x0008000000000000L; + + public static class NaNTestPair { + T first; + T second; + BitType bFirst; + BitType bSecond; + String message; + BitType expected; + } + + public static NaNTestPair getDoubleNaNPair(){ + Random r = new Random(); + NaNTestPair p = new NaNTestPair(); + // 0->quiet, 1->signalling + String message = ""; + double farg1, farg2, farg3; + farg1 = 0.0f; + farg2 = 0.0f; + farg3 = 0.0f; + long iarg1, iarg2; + iarg1 = r.nextLong() >> 13; // shift right to only mantisa has 1s, and quiet bit is not set + int arg1Type = r.nextInt(3); + + if (arg1Type == 0) { // qNaN + message = message + "(qNaN, "; + iarg1 = iarg1 | dNaNExpStart | dquietBit; + farg1 = Double.longBitsToDouble(iarg1); + }else if (arg1Type == 1) { // sNaN + message = message + "(sNaN, "; + iarg1 = iarg1 | dNaNExpStart; + farg1 = Double.longBitsToDouble(iarg1); + }else { // Normal number + farg1 = r.nextDouble() * 1000000.0 + 1.0; + message = message + "(" + String.valueOf(farg1) + ", "; + } + + //arg 2 + int arg2Type = r.nextInt(3); + iarg2 = r.nextLong() >> 13; + if (arg2Type == 0) + { + message = message + " qNaN)"; + iarg2 = iarg2 | dNaNExpStart | dquietBit; + farg2 = Double.longBitsToDouble(iarg2); + } + else if (arg2Type == 1) + { + message = message + " sNaN)"; + iarg2 = iarg2 | dNaNExpStart; + farg2 = Double.longBitsToDouble(iarg2); + } + else + { + if (arg1Type != 2) + { + farg2 = r.nextDouble() * 1000000.0 + 1; + message = message + String.valueOf(farg2) + ")"; + iarg2 = Double.doubleToRawLongBits(farg2); + } + else + { + return null; + } + } + + p.first = farg1; + p.bFirst = iarg1; + p.second = farg2; + p.bSecond = iarg2; + p.message = message; + p.expected = (arg1Type != 2 ? iarg1 : iarg2); + p.message = p.message + String.format("[0x%x, 0x%x]", p.bFirst, p.bSecond); + return p; + } + + public static NaNTestPair getFloatNaNPair(){ // 0: max, 1: min + Random r = new Random(); + NaNTestPair p = new NaNTestPair(); + // 0->quiet, 1->signalling + String message = ""; + float farg1, farg2, farg3; + farg1 = farg2 = farg3 = 0.0f; + int iarg1, iarg2; + iarg1 = r.nextInt(fNaNMantisaMax - 1) + 1; + int arg1Type = r.nextInt(3); + if (arg1Type == 0) { // qNaN + message = message + "(qNaN, "; + iarg1 = iarg1 | fNaNExpStart | fquietBit; + farg1 = Float.intBitsToFloat(iarg1); + }else if (arg1Type == 1) { // sNaN + message = message + "(sNaN, "; + iarg1 = iarg1 | fNaNExpStart; + farg1 = Float.intBitsToFloat(iarg1); + }else{ // Normal number + farg1 = r.nextFloat() * 1000000.0f + 1.0f; + message = message + "(" + String.valueOf(farg1) + ", "; + } + + //arg 2 + int arg2Type = r.nextInt(3); + iarg2 = r.nextInt(fNaNMantisaMax - 1) + 1; + if (arg2Type == 0) { + message = message + " qNaN)"; + iarg2 = (r.nextInt(fNaNMantisaMax - 1) + 1) | fNaNExpStart | fquietBit; + farg2 = Float.intBitsToFloat(iarg2); + }else if (arg2Type == 1){ + message = message + " sNaN)"; + iarg2 = (r.nextInt(fNaNMantisaMax - 1) + 1) | fNaNExpStart; + farg2 = Float.intBitsToFloat(iarg2); + }else { + if (arg1Type != 2) { + farg2 = r.nextFloat() * 1000000.0f + 1f; + message = message + String.valueOf(farg2) + ")"; + iarg2 = Float.floatToRawIntBits(farg2); + }else{ + return null; + } + } + p.first = farg1; + p.second = farg2; + p.bFirst = iarg1; + p.bSecond = iarg2; + p.message = message; + p.expected = (arg1Type != 2 ? iarg1 : iarg2); + p.message = p.message + String.format("[0x%x, 0x%x]", p.bFirst, p.bSecond); + return p; + } + +// these 4 methods used as wrappers to ensure jit compilation + public static float fmax(float x, float y) { + return Math.max(x, y); + } + + public static float fmin(float x, float y){ + return Math.min(x, y); + } + + public static double dmin(double x, double y){ + return Math.min(x, y); + } + + public static double dmax(double x, double y){ + return Math.max(x, y); + } + + public static boolean isQuietNaN(float f){ + int intBits = Float.floatToRawIntBits(f); + return (intBits & fquietBit) == fquietBit; + } + public static boolean isQuietNaN(double d){ + long longBits = Double.doubleToRawLongBits(d); + return (longBits & dquietBit) == dquietBit; + } + + public static void formatErrorFloat(SoftAssert s, String test) { + try{ + s.assertAll(); + System.out.println("Passed " + test); + }catch(AssertionError e){ + String[] lines = e.toString().split("\n"); + for (String line: lines) { + Pattern p = Pattern.compile("(?<=\\[)[-]?\\d+"); + Matcher m = p.matcher(line); + StringBuffer result = new StringBuffer(); + while (m.find()) { + String hex = String.format("0x%x", Integer.parseInt(m.group())); + m.appendReplacement(result, hex); + } + m.appendTail(result); + System.out.println(result); + } + throw new AssertionError("Failed " + test); + } + } + + public static void formatErrorDouble(SoftAssert s, String test) { + try{ + s.assertAll(); + System.out.println("Passed " + test); + }catch(AssertionError e){ + String[] lines = e.toString().split("\n"); + for (String line: lines) { + Pattern p = Pattern.compile("(?<=\\[)[-]?\\d+"); + Matcher m = p.matcher(line); + StringBuffer result = new StringBuffer(); + while (m.find()) { + String hex = String.format("0x%x", Long.parseLong(m.group())); + m.appendReplacement(result, hex); + } + m.appendTail(result); + System.out.println(result); + } + throw new AssertionError("Failed " + test); + } + } +}