|
1 | 1 | /*
|
2 |
| - * Copyright (c) 2018, Red Hat Inc. All rights reserved. |
| 2 | + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. |
3 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
4 | 4 | *
|
5 | 5 | * This code is free software; you can redistribute it and/or modify it
|
|
23 | 23 |
|
24 | 24 | /*
|
25 | 25 | * @test
|
26 |
| - * @bug 8204479 |
27 |
| - * @summary Bitwise AND on byte value sometimes produces wrong result |
| 26 | + * @bug 8204479 8253191 |
28 | 27 | *
|
29 |
| - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation |
30 |
| - * -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -Xcomp -XX:-Inline |
31 |
| - * compiler.c2.TestUnsignedByteCompare |
| 28 | + * @library /test/lib |
| 29 | + * @modules java.base/jdk.internal.vm.annotation |
| 30 | + * |
| 31 | + * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation compiler.c2.TestUnsignedByteCompare |
32 | 32 | */
|
33 |
| - |
34 | 33 | package compiler.c2;
|
35 | 34 |
|
| 35 | +import java.lang.invoke.*; |
| 36 | +import jdk.internal.vm.annotation.DontInline; |
| 37 | +import jdk.test.lib.Asserts; |
| 38 | + |
36 | 39 | public class TestUnsignedByteCompare {
|
37 | 40 |
|
38 |
| - static int p, n; |
| 41 | + @DontInline static boolean testByteGT0(byte[] val) { return (val[0] & mask()) > 0; } |
| 42 | + @DontInline static boolean testByteGE0(byte[] val) { return (val[0] & mask()) >= 0; } |
| 43 | + @DontInline static boolean testByteEQ0(byte[] val) { return (val[0] & mask()) == 0; } |
| 44 | + @DontInline static boolean testByteNE0(byte[] val) { return (val[0] & mask()) != 0; } |
| 45 | + @DontInline static boolean testByteLE0(byte[] val) { return (val[0] & mask()) <= 0; } |
| 46 | + @DontInline static boolean testByteLT0(byte[] val) { return (val[0] & mask()) < 0; } |
39 | 47 |
|
40 |
| - static void report(byte[] ba, int i, boolean failed) { |
41 |
| - // Enable for debugging: |
42 |
| - // System.out.println((failed ? "Failed" : "Passed") + " with: " + ba[i] + " at " + i); |
| 48 | + static void testValue(byte b) { |
| 49 | + byte[] bs = new byte[] { b }; |
| 50 | + Asserts.assertEquals(((b & mask()) > 0), testByteGT0(bs), errorMessage(b, "GT0")); |
| 51 | + Asserts.assertEquals(((b & mask()) >= 0), testByteGE0(bs), errorMessage(b, "GE0")); |
| 52 | + Asserts.assertEquals(((b & mask()) == 0), testByteEQ0(bs), errorMessage(b, "EQ0")); |
| 53 | + Asserts.assertEquals(((b & mask()) != 0), testByteNE0(bs), errorMessage(b, "NE0")); |
| 54 | + Asserts.assertEquals(((b & mask()) <= 0), testByteLE0(bs), errorMessage(b, "LE0")); |
| 55 | + Asserts.assertEquals(((b & mask()) < 0), testByteLT0(bs), errorMessage(b, "LT0")); |
43 | 56 | }
|
44 | 57 |
|
45 |
| - static void m1(byte[] ba) { |
46 |
| - for (int i = 0; i < ba.length; i++) { |
47 |
| - if ((ba[i] & 0xFF) < 0x10) { |
48 |
| - p++; |
49 |
| - report(ba, i, true); |
50 |
| - } else { |
51 |
| - n++; |
52 |
| - report(ba, i, false); |
| 58 | + public static void main(String[] args) { |
| 59 | + for (int mask = 0; mask <= 0xFF; mask++) { |
| 60 | + setMask(mask); |
| 61 | + for (int i = 0; i < 20_000; i++) { |
| 62 | + testValue((byte) i); |
53 | 63 | }
|
54 | 64 | }
|
| 65 | + System.out.println("TEST PASSED"); |
55 | 66 | }
|
56 | 67 |
|
57 |
| - static void m2(byte[] ba) { |
58 |
| - for (int i = 0; i < ba.length; i++) { |
59 |
| - if (((ba[i] & 0xFF) & 0x80) < 0) { |
60 |
| - p++; |
61 |
| - report(ba, i, true); |
62 |
| - } else { |
63 |
| - n++; |
64 |
| - report(ba, i, false); |
65 |
| - } |
66 |
| - } |
| 68 | + static String errorMessage(byte b, String type) { |
| 69 | + return String.format("%s: val=0x%x mask=0x%x", type, b, mask()); |
67 | 70 | }
|
68 | 71 |
|
69 |
| - static public void main(String[] args) { |
70 |
| - final int tries = 1_000; |
71 |
| - final int count = 1_000; |
| 72 | + // Mutable mask as a compile-time constant. |
72 | 73 |
|
73 |
| - byte[] ba = new byte[count]; |
| 74 | + private static final CallSite MASK_CS = new MutableCallSite(MethodType.methodType(int.class)); |
| 75 | + private static final MethodHandle MASK_MH = MASK_CS.dynamicInvoker(); |
74 | 76 |
|
75 |
| - for (int i = 0; i < count; i++) { |
76 |
| - int v = -(i % 126 + 1); |
77 |
| - ba[i] = (byte)v; |
78 |
| - } |
79 |
| - |
80 |
| - for (int t = 0; t < tries; t++) { |
81 |
| - m1(ba); |
82 |
| - if (p != 0) { |
83 |
| - throw new IllegalStateException("m1 error: p = " + p + ", n = " + n); |
84 |
| - } |
| 77 | + static int mask() { |
| 78 | + try { |
| 79 | + return (int) MASK_MH.invokeExact(); |
| 80 | + } catch (Throwable t) { |
| 81 | + throw new InternalError(t); // should NOT happen |
85 | 82 | }
|
| 83 | + } |
86 | 84 |
|
87 |
| - for (int t = 0; t < tries; t++) { |
88 |
| - m2(ba); |
89 |
| - if (p != 0) { |
90 |
| - throw new IllegalStateException("m2 error: p = " + p + ", n = " + n); |
91 |
| - } |
92 |
| - } |
| 85 | + static void setMask(int mask) { |
| 86 | + MethodHandle constant = MethodHandles.constant(int.class, mask); |
| 87 | + MASK_CS.setTarget(constant); |
93 | 88 | }
|
94 | 89 | }
|
0 commit comments