| 
 | 1 | +/*  | 
 | 2 | + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.  | 
 | 3 | + * Copyright (c) 2025, Rivos Inc. All rights reserved.  | 
 | 4 | + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.  | 
 | 5 | + *  | 
 | 6 | + * This code is free software; you can redistribute it and/or modify it  | 
 | 7 | + * under the terms of the GNU General Public License version 2 only, as  | 
 | 8 | + * published by the Free Software Foundation.  | 
 | 9 | + *  | 
 | 10 | + * This code is distributed in the hope that it will be useful, but WITHOUT  | 
 | 11 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or  | 
 | 12 | + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License  | 
 | 13 | + * version 2 for more details (a copy is included in the LICENSE file that  | 
 | 14 | + * accompanied this code).  | 
 | 15 | + *  | 
 | 16 | + * You should have received a copy of the GNU General Public License version  | 
 | 17 | + * 2 along with this work; if not, write to the Free Software Foundation,  | 
 | 18 | + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.  | 
 | 19 | + *  | 
 | 20 | + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA  | 
 | 21 | + * or visit www.oracle.com if you need additional information or have any  | 
 | 22 | + * questions.  | 
 | 23 | + */  | 
 | 24 | + | 
 | 25 | +/*  | 
 | 26 | + * @test  | 
 | 27 | + * @bug 8365206  | 
 | 28 | + * @summary Verify NaN sign and significand bits are preserved across conversions,  | 
 | 29 | + *          float -> float16 -> float  | 
 | 30 | + * @requires (os.arch == "riscv64" & vm.cpu.features ~= ".*zfh.*")  | 
 | 31 | + * @requires vm.compiler1.enabled & vm.compiler2.enabled  | 
 | 32 | + * @requires vm.compMode != "Xcomp"  | 
 | 33 | + * @library /test/lib /  | 
 | 34 | + *  | 
 | 35 | + * @build jdk.test.whitebox.WhiteBox  | 
 | 36 | + * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox  | 
 | 37 | + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI  | 
 | 38 | + *                   -Xmixed -XX:-BackgroundCompilation -XX:-UseOnStackReplacement  | 
 | 39 | + *                   -XX:CompileThresholdScaling=1000.0 Binary16ConversionNaN_2  | 
 | 40 | + */  | 
 | 41 | + | 
 | 42 | +/*  | 
 | 43 | + * The behavior tested below is an implementation property not  | 
 | 44 | + * required by the specification. It would be acceptable for this  | 
 | 45 | + * information to not be preserved (as long as a NaN is returned) if,  | 
 | 46 | + * say, a intrinsified version using native hardware instructions  | 
 | 47 | + * behaved differently.  | 
 | 48 | + *  | 
 | 49 | + * If that is the case, this test should be modified to disable  | 
 | 50 | + * intrinsics or to otherwise not run on platforms with an differently  | 
 | 51 | + * behaving intrinsic.  | 
 | 52 | + */  | 
 | 53 | + | 
 | 54 | +import compiler.whitebox.CompilerWhiteBoxTest;  | 
 | 55 | +import jdk.test.whitebox.WhiteBox;  | 
 | 56 | +import java.lang.reflect.Method;  | 
 | 57 | +import java.util.Random;  | 
 | 58 | + | 
 | 59 | +public class Binary16ConversionNaN_2 {  | 
 | 60 | + | 
 | 61 | +    private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox();  | 
 | 62 | + | 
 | 63 | +    /*  | 
 | 64 | +     * Put all 16-bit NaN values through a conversion loop and make  | 
 | 65 | +     * sure the significand, sign, and exponent are all preserved.  | 
 | 66 | +     */  | 
 | 67 | +    public static void main(String... argv) throws NoSuchMethodException {  | 
 | 68 | +        int errors = 0;  | 
 | 69 | +        final int NAN_EXPONENT = 0x7f80_0000;  | 
 | 70 | +        final int SIGN_BIT     = 0x8000_0000;  | 
 | 71 | + | 
 | 72 | +        // First, run with Interpreter only to  collect "gold" data.  | 
 | 73 | +        // Glags -Xmixed -XX:CompileThresholdScaling=1000.0 are used  | 
 | 74 | +        // to prevent compilation during this phase.  | 
 | 75 | +        float[] pVal = new float[1024];  | 
 | 76 | +        float[] pRes = new float[1024];  | 
 | 77 | +        float[] nVal = new float[1024];  | 
 | 78 | +        float[] nRes = new float[1024];  | 
 | 79 | + | 
 | 80 | +        Random rand = new Random();  | 
 | 81 | + | 
 | 82 | +        // A NaN has a nonzero significand  | 
 | 83 | +        for (int i = 1; i <= 0x3ff; i++) {  | 
 | 84 | +            int shift = rand.nextInt(13+1);  | 
 | 85 | +            int binaryNaN = (NAN_EXPONENT | (i << shift));  | 
 | 86 | +            assert isNaN(binaryNaN);  | 
 | 87 | +            // the payloads of non-canonical NaNs are preserved.  | 
 | 88 | +            float f1 = Float.intBitsToFloat(binaryNaN);  | 
 | 89 | +            float f2 = testRoundTrip(f1);  | 
 | 90 | +            errors  += verify(f1, f2);  | 
 | 91 | +            pVal[i] = f1;  | 
 | 92 | +            pRes[i] = f2;  | 
 | 93 | + | 
 | 94 | +            int binaryNegNaN = (SIGN_BIT | binaryNaN);  | 
 | 95 | +            float f3 = Float.intBitsToFloat(binaryNegNaN);  | 
 | 96 | +            float f4 = testRoundTrip(f3);  | 
 | 97 | +            errors  += verify(f3, f4);  | 
 | 98 | +            nVal[i] = f3;  | 
 | 99 | +            nRes[i] = f4;  | 
 | 100 | +        }  | 
 | 101 | +        if (errors > 0) { // Exit if Interpreter failed  | 
 | 102 | +            throw new RuntimeException(errors + " errors");  | 
 | 103 | +        }  | 
 | 104 | + | 
 | 105 | +        Method test_method = Binary16ConversionNaN_2.class.getDeclaredMethod("testRoundTrip", float.class);  | 
 | 106 | + | 
 | 107 | +        // Compile with C1 and compare results  | 
 | 108 | +        WHITE_BOX.enqueueMethodForCompilation(test_method, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE);  | 
 | 109 | +        if (!WHITE_BOX.isMethodCompiled(test_method)) {  | 
 | 110 | +            throw new RuntimeException("test is not compiled by C1");  | 
 | 111 | +        }  | 
 | 112 | +        for (int i = 1; i <= 0x3ff; i++) {  | 
 | 113 | +            float f1 = testRoundTrip(pVal[i]);  | 
 | 114 | +            errors  += verifyCompiler(pRes[i], f1, "C1");  | 
 | 115 | +            float f2 = testRoundTrip(nVal[i]);  | 
 | 116 | +            errors  += verifyCompiler(nRes[i], f2, "C1");  | 
 | 117 | +        }  | 
 | 118 | + | 
 | 119 | +        WHITE_BOX.deoptimizeMethod(test_method);  | 
 | 120 | + | 
 | 121 | +        // Compile with C2 and compare results  | 
 | 122 | +        WHITE_BOX.enqueueMethodForCompilation(test_method, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION);  | 
 | 123 | +        if (!WHITE_BOX.isMethodCompiled(test_method)) {  | 
 | 124 | +            throw new RuntimeException("test is not compiled by C2");  | 
 | 125 | +        }  | 
 | 126 | +        for (int i = 1; i <= 0x3ff; i++) {  | 
 | 127 | +            float f1 = testRoundTrip(pVal[i]);  | 
 | 128 | +            errors  += verifyCompiler(pRes[i], f1, "C2");  | 
 | 129 | +            float f2 = testRoundTrip(nVal[i]);  | 
 | 130 | +            errors  += verifyCompiler(nRes[i], f2, "C2");  | 
 | 131 | +        }  | 
 | 132 | + | 
 | 133 | +        if (errors > 0) {  | 
 | 134 | +            throw new RuntimeException(errors + " errors");  | 
 | 135 | +        }  | 
 | 136 | +    }  | 
 | 137 | + | 
 | 138 | +    private static boolean isNaN(int binary) {  | 
 | 139 | +        return ((binary & 0x7f80_0000) == 0x7f80_0000) // Max exponent and...  | 
 | 140 | +            && ((binary & 0x007f_ffff) != 0 );         // significand nonzero.  | 
 | 141 | +    }  | 
 | 142 | + | 
 | 143 | +    private static float testRoundTrip(float f) {  | 
 | 144 | +        short s = Float.floatToFloat16(f);  | 
 | 145 | +        return Float.float16ToFloat(s);  | 
 | 146 | +    }  | 
 | 147 | + | 
 | 148 | +    private static int verify(float f1, float f2) {  | 
 | 149 | +        int errors = 0;  | 
 | 150 | +        int i1 = Float.floatToRawIntBits(f1);  | 
 | 151 | +        int i2 = Float.floatToRawIntBits(f2);  | 
 | 152 | +        assert Float.isNaN(f1);  | 
 | 153 | +        if (!Float.isNaN(f2) ||  | 
 | 154 | +            ((i1 & 0x8000_0000) != (i2 & 0x8000_0000))) {  | 
 | 155 | +            errors++;  | 
 | 156 | +            System.out.println("Roundtrip failure on NaN value " +  | 
 | 157 | +                               Integer.toHexString(i1) +  | 
 | 158 | +                               "\t got back " + Integer.toHexString(i2));  | 
 | 159 | +        }  | 
 | 160 | +        return errors;  | 
 | 161 | +    }  | 
 | 162 | + | 
 | 163 | +    private static int verifyCompiler(float f1, float f2, String name) {  | 
 | 164 | +        int errors = 0;  | 
 | 165 | +        int i1 = Float.floatToRawIntBits(f1);  | 
 | 166 | +        int i2 = Float.floatToRawIntBits(f2);  | 
 | 167 | +        assert Float.isNaN(f1);  | 
 | 168 | +        if (!Float.isNaN(f2) ||  | 
 | 169 | +            ((i1 & 0x8000_0000) != (i2 & 0x8000_0000))) {  | 
 | 170 | +            errors++;  | 
 | 171 | +            System.out.println("Roundtrip failure on NaN value " +  | 
 | 172 | +                               Integer.toHexString(i1) +  | 
 | 173 | +                               "\t got back " + Integer.toHexString(i2) +  | 
 | 174 | +                               "\t from " + name + " code");  | 
 | 175 | +        }  | 
 | 176 | +        return errors;  | 
 | 177 | +    }  | 
 | 178 | +}  | 
0 commit comments