diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index b8da06af5d6bf..d0b6c59637f13 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -2881,14 +2881,14 @@ class ArrayPointer { // // RangeCheck[i+0] RangeCheck[i+0] // StoreB[i+0] -// RangeCheck[i+1] RangeCheck[i+1] +// RangeCheck[i+3] RangeCheck[i+3] // StoreB[i+1] --> pass: fail: // StoreB[i+2] StoreI[i+0] StoreB[i+0] // StoreB[i+3] // // The 4 StoreB are merged into a single StoreI node. We have to be careful with RangeCheck[i+1]: before // the optimization, if this RangeCheck[i+1] fails, then we execute only StoreB[i+0], and then trap. After -// the optimization, the new StoreI[i+0] is on the passing path of RangeCheck[i+1], and StoreB[i+0] on the +// the optimization, the new StoreI[i+0] is on the passing path of RangeCheck[i+3], and StoreB[i+0] on the // failing path. // // Note: For normal array stores, every store at first has a RangeCheck. But they can be removed with: @@ -2900,11 +2900,11 @@ class ArrayPointer { // RangeCheck[i+0] RangeCheck[i+0] <- before first store // StoreB[i+0] StoreB[i+0] <- first store // RangeCheck[i+1] --> smeared --> RangeCheck[i+3] <- only RC between first and last store -// StoreB[i+0] StoreB[i+1] <- second store +// StoreB[i+1] StoreB[i+1] <- second store // RangeCheck[i+2] --> removed -// StoreB[i+0] StoreB[i+2] +// StoreB[i+2] StoreB[i+2] // RangeCheck[i+3] --> removed -// StoreB[i+0] StoreB[i+3] <- last store +// StoreB[i+3] StoreB[i+3] <- last store // // Thus, it is a common pattern that between the first and last store in a chain // of adjacent stores there remains exactly one RangeCheck, located between the @@ -3067,6 +3067,11 @@ bool MergePrimitiveArrayStores::is_adjacent_input_pair(const Node* n1, const Nod } // Pattern: [n1 = base >> shift, n2 = base >> (shift + memory_size)] +#ifndef VM_LITTLE_ENDIAN + // Pattern: [n1 = base >> (shift + memory_size), n2 = base >> shift] + // Swapping n1 with n2 gives same pattern as on little endian platforms. + swap(n1, n2); +#endif // !VM_LITTLE_ENDIAN Node const* base_n2; jint shift_n2; if (!is_con_RShift(n2, base_n2, shift_n2)) { @@ -3281,8 +3286,13 @@ Node* MergePrimitiveArrayStores::make_merged_input_value(const Node_List& merge_ jlong mask = (((jlong)1) << bits_per_store) - 1; for (uint i = 0; i < merge_list.size(); i++) { jlong con_i = merge_list.at(i)->in(MemNode::ValueIn)->get_int(); +#ifdef VM_LITTLE_ENDIAN con = con << bits_per_store; con = con | (mask & con_i); +#else // VM_LITTLE_ENDIAN + con_i = (mask & con_i) << (i * bits_per_store); + con = con | con_i; +#endif // VM_LITTLE_ENDIAN } merged_input_value = _phase->longcon(con); } else { @@ -3290,16 +3300,22 @@ Node* MergePrimitiveArrayStores::make_merged_input_value(const Node_List& merge_ // | | // _store first // - merged_input_value = first->in(MemNode::ValueIn); - Node const* base_last; - jint shift_last; - bool is_true = is_con_RShift(_store->in(MemNode::ValueIn), base_last, shift_last); + Node* hi = _store->in(MemNode::ValueIn); + Node* lo = first->in(MemNode::ValueIn); +#ifndef VM_LITTLE_ENDIAN + // `_store` and `first` are swapped in the diagram above + swap(hi, lo); +#endif // !VM_LITTLE_ENDIAN + Node const* hi_base; + jint hi_shift; + merged_input_value = lo; + bool is_true = is_con_RShift(hi, hi_base, hi_shift); assert(is_true, "must detect con RShift"); - if (merged_input_value != base_last && merged_input_value->Opcode() == Op_ConvL2I) { + if (merged_input_value != hi_base && merged_input_value->Opcode() == Op_ConvL2I) { // look through merged_input_value = merged_input_value->in(1); } - if (merged_input_value != base_last) { + if (merged_input_value != hi_base) { // merged_input_value is not the base return nullptr; } @@ -3473,7 +3489,6 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { } } -#ifdef VM_LITTLE_ENDIAN if (MergeStores && UseUnalignedAccesses) { if (phase->C->post_loop_opts_phase()) { MergePrimitiveArrayStores merge(phase, this); @@ -3483,7 +3498,6 @@ Node *StoreNode::Ideal(PhaseGVN *phase, bool can_reshape) { phase->C->record_for_post_loop_opts_igvn(this); } } -#endif return nullptr; // No further progress } diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java index 555946502196b..0e86045618b9a 100644 --- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java +++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java @@ -33,7 +33,7 @@ /* * @test - * @bug 8318446 8331054 + * @bug 8318446 8331054 8331311 * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / @@ -42,7 +42,7 @@ /* * @test - * @bug 8318446 8331054 + * @bug 8318446 8331054 8331311 * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / @@ -112,16 +112,32 @@ public TestMergeStores() { testGroups.get("test2").put("test2b", (_,_) -> { return test2b(aB.clone(), offset1, vL1); }); testGroups.get("test2").put("test2c", (_,_) -> { return test2c(aB.clone(), offset1, vL1); }); testGroups.get("test2").put("test2d", (_,_) -> { return test2d(aB.clone(), offset1, vL1); }); - testGroups.get("test2").put("test2e", (_,_) -> { return test2d(aB.clone(), offset1, vL1); }); + testGroups.get("test2").put("test2e", (_,_) -> { return test2e(aB.clone(), offset1, vL1); }); + + testGroups.put("test2BE", new HashMap()); + testGroups.get("test2BE").put("test2RBE", (_,_) -> { return test2RBE(aB.clone(), offset1, vL1); }); + testGroups.get("test2BE").put("test2aBE", (_,_) -> { return test2aBE(aB.clone(), offset1, vL1); }); + testGroups.get("test2BE").put("test2bBE", (_,_) -> { return test2bBE(aB.clone(), offset1, vL1); }); + testGroups.get("test2BE").put("test2cBE", (_,_) -> { return test2cBE(aB.clone(), offset1, vL1); }); + testGroups.get("test2BE").put("test2dBE", (_,_) -> { return test2dBE(aB.clone(), offset1, vL1); }); + testGroups.get("test2BE").put("test2eBE", (_,_) -> { return test2eBE(aB.clone(), offset1, vL1); }); testGroups.put("test3", new HashMap()); testGroups.get("test3").put("test3R", (_,_) -> { return test3R(aB.clone(), offset1, vL1); }); testGroups.get("test3").put("test3a", (_,_) -> { return test3a(aB.clone(), offset1, vL1); }); + testGroups.put("test3BE", new HashMap()); + testGroups.get("test3BE").put("test3RBE", (_,_) -> { return test3RBE(aB.clone(), offset1, vL1); }); + testGroups.get("test3BE").put("test3aBE", (_,_) -> { return test3aBE(aB.clone(), offset1, vL1); }); + testGroups.put("test4", new HashMap()); testGroups.get("test4").put("test4R", (_,_) -> { return test4R(aB.clone(), offset1, vL1, vI1, vS1, vB1); }); testGroups.get("test4").put("test4a", (_,_) -> { return test4a(aB.clone(), offset1, vL1, vI1, vS1, vB1); }); + testGroups.put("test4BE", new HashMap()); + testGroups.get("test4BE").put("test4RBE", (_,_) -> { return test4RBE(aB.clone(), offset1, vL1, vI1, vS1, vB1); }); + testGroups.get("test4BE").put("test4aBE", (_,_) -> { return test4aBE(aB.clone(), offset1, vL1, vI1, vS1, vB1); }); + testGroups.put("test5", new HashMap()); testGroups.get("test5").put("test5R", (_,_) -> { return test5R(aB.clone(), offset1); }); testGroups.get("test5").put("test5a", (_,_) -> { return test5a(aB.clone(), offset1); }); @@ -134,6 +150,10 @@ public TestMergeStores() { testGroups.get("test7").put("test7R", (_,_) -> { return test7R(aB.clone(), offset1, vI1); }); testGroups.get("test7").put("test7a", (_,_) -> { return test7a(aB.clone(), offset1, vI1); }); + testGroups.put("test7BE", new HashMap()); + testGroups.get("test7BE").put("test7RBE", (_,_) -> { return test7RBE(aB.clone(), offset1, vI1); }); + testGroups.get("test7BE").put("test7aBE", (_,_) -> { return test7aBE(aB.clone(), offset1, vI1); }); + testGroups.put("test100", new HashMap()); testGroups.get("test100").put("test100R", (_,_) -> { return test100R(aS.clone(), offset1); }); testGroups.get("test100").put("test100a", (_,_) -> { return test100a(aS.clone(), offset1); }); @@ -146,6 +166,10 @@ public TestMergeStores() { testGroups.get("test102").put("test102R", (_,_) -> { return test102R(aS.clone(), offset1, vL1, vI1, vS1); }); testGroups.get("test102").put("test102a", (_,_) -> { return test102a(aS.clone(), offset1, vL1, vI1, vS1); }); + testGroups.put("test102BE", new HashMap()); + testGroups.get("test102BE").put("test102RBE", (_,_) -> { return test102RBE(aS.clone(), offset1, vL1, vI1, vS1); }); + testGroups.get("test102BE").put("test102aBE", (_,_) -> { return test102aBE(aS.clone(), offset1, vL1, vI1, vS1); }); + testGroups.put("test200", new HashMap()); testGroups.get("test200").put("test200R", (_,_) -> { return test200R(aI.clone(), offset1); }); testGroups.get("test200").put("test200a", (_,_) -> { return test200a(aI.clone(), offset1); }); @@ -158,6 +182,10 @@ public TestMergeStores() { testGroups.get("test202").put("test202R", (_,_) -> { return test202R(aI.clone(), offset1, vL1, vI1); }); testGroups.get("test202").put("test202a", (_,_) -> { return test202a(aI.clone(), offset1, vL1, vI1); }); + testGroups.put("test202BE", new HashMap()); + testGroups.get("test202BE").put("test202RBE", (_,_) -> { return test202RBE(aI.clone(), offset1, vL1, vI1); }); + testGroups.get("test202BE").put("test202aBE", (_,_) -> { return test202aBE(aI.clone(), offset1, vL1, vI1); }); + testGroups.put("test300", new HashMap()); testGroups.get("test300").put("test300R", (_,_) -> { return test300R(aI.clone()); }); testGroups.get("test300").put("test300a", (_,_) -> { return test300a(aI.clone()); }); @@ -184,6 +212,24 @@ public TestMergeStores() { // First use something in range, and after warmup randomize going outside the range. // Consequence: all RangeChecks stay in the final compilation. + testGroups.put("test500BE", new HashMap()); + testGroups.get("test500BE").put("test500RBE", (_,_) -> { return test500RBE(aB.clone(), offset1, vL1); }); + testGroups.get("test500BE").put("test500aBE", (_,_) -> { return test500aBE(aB.clone(), offset1, vL1); }); + + testGroups.put("test501BE", new HashMap()); + testGroups.get("test501BE").put("test500RBE", (_,i) -> { return test500RBE(aB.clone(), RANGE - 20 + (i % 30), vL1); }); + testGroups.get("test501BE").put("test501aBE", (_,i) -> { return test501aBE(aB.clone(), RANGE - 20 + (i % 30), vL1); }); + // +-------------------+ + // Create offsets that are sometimes going to pass all RangeChecks, and sometimes one, and sometimes none. + // Consequence: all RangeChecks stay in the final compilation. + + testGroups.put("test502BE", new HashMap()); + testGroups.get("test502BE").put("test500RBE", (w,i) -> { return test500RBE(aB.clone(), w ? offset1 : RANGE - 20 + (i % 30), vL1); }); + testGroups.get("test502BE").put("test502aBE", (w,i) -> { return test502aBE(aB.clone(), w ? offset1 : RANGE - 20 + (i % 30), vL1); }); + // +-----+ +-------------------+ + // First use something in range, and after warmup randomize going outside the range. + // Consequence: all RangeChecks stay in the final compilation. + testGroups.put("test600", new HashMap()); testGroups.get("test600").put("test600R", (_,i) -> { return test600R(aB.clone(), aI.clone(), i); }); testGroups.get("test600").put("test600a", (_,i) -> { return test600a(aB.clone(), aI.clone(), i); }); @@ -191,6 +237,14 @@ public TestMergeStores() { testGroups.put("test700", new HashMap()); testGroups.get("test700").put("test700R", (_,i) -> { return test700R(aI.clone(), i); }); testGroups.get("test700").put("test700a", (_,i) -> { return test700a(aI.clone(), i); }); + + testGroups.put("test800", new HashMap()); + testGroups.get("test800").put("test800R", (_,_) -> { return test800R(aB.clone(), offset1, vL1); }); + testGroups.get("test800").put("test800a", (_,_) -> { return test800a(aB.clone(), offset1, vL1); }); + + testGroups.put("test800BE", new HashMap()); + testGroups.get("test800BE").put("test800RBE", (_,_) -> { return test800RBE(aB.clone(), offset1, vL1); }); + testGroups.get("test800BE").put("test800aBE", (_,_) -> { return test800aBE(aB.clone(), offset1, vL1); }); } @Warmup(100) @@ -208,24 +262,39 @@ public TestMergeStores() { "test2c", "test2d", "test2e", + "test2aBE", + "test2bBE", + "test2cBE", + "test2dBE", + "test2eBE", "test3a", + "test3aBE", "test4a", + "test4aBE", "test5a", "test6a", "test7a", + "test7aBE", "test100a", "test101a", "test102a", + "test102aBE", "test200a", "test201a", "test202a", + "test202aBE", "test300a", "test400a", "test500a", "test501a", "test502a", + "test500aBE", + "test501aBE", + "test502aBE", "test600a", - "test700a"}) + "test700a", + "test800a", + "test800aBE"}) public void runTests(RunInfo info) { // Repeat many times, so that we also have multiple iterations for post-warmup to potentially recompile int iters = info.isWarmUp() ? 1_000 : 50_000; @@ -412,6 +481,39 @@ static void storeLongLE(byte[] bytes, int offset, long value) { (byte)(value >> 56)); } + // ------------------------------------------- + // ------- Big-Endian API ---------- + // ------------------------------------------- + + // Store a short BE into an array using store bytes in an array + @ForceInline + static void storeShortBE(byte[] bytes, int offset, short value) { + storeBytes(bytes, offset, (byte)(value >> 8), + (byte)(value >> 0)); + } + + // Store an int BE into an array using store bytes in an array + @ForceInline + static void storeIntBE(byte[] bytes, int offset, int value) { + storeBytes(bytes, offset, (byte)(value >> 24), + (byte)(value >> 16), + (byte)(value >> 8 ), + (byte)(value >> 0 )); + } + + // Store an int BE into an array using store bytes in an array + @ForceInline + static void storeLongBE(byte[] bytes, int offset, long value) { + storeBytes(bytes, offset, (byte)(value >> 56), + (byte)(value >> 48), + (byte)(value >> 40), + (byte)(value >> 32), + (byte)(value >> 24), + (byte)(value >> 16), + (byte)(value >> 8 ), + (byte)(value >> 0 )); + } + // Store 2 bytes into an array @ForceInline static void storeBytes(byte[] bytes, int offset, byte b0, byte b1) { @@ -476,7 +578,7 @@ static Object[] test1a(byte[] a) { static Object[] test1b(byte[] a) { // Add custom null check, to ensure the unsafe access always recognizes its type as an array store if (a == null) {return null;} - UNSAFE.putLongUnaligned(a, UNSAFE.ARRAY_BYTE_BASE_OFFSET, 0xdeadbeefbaadbabeL); + UNSAFE.putLongUnaligned(a, UNSAFE.ARRAY_BYTE_BASE_OFFSET, 0xdeadbeefbaadbabeL, false /* bigEndian */); return new Object[]{ a }; } @@ -589,7 +691,13 @@ static Object[] test2R(byte[] a, int offset, long v) { @Test @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test2a(byte[] a, int offset, long v) { a[offset + 0] = (byte)(v >> 0); a[offset + 1] = (byte)(v >> 8); @@ -608,13 +716,19 @@ static Object[] test2a(byte[] a, int offset, long v) { static Object[] test2b(byte[] a, int offset, long v) { // Add custom null check, to ensure the unsafe access always recognizes its type as an array store if (a == null) {return null;} - UNSAFE.putLongUnaligned(a, UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset, v); + UNSAFE.putLongUnaligned(a, UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset, v, false /* bigEndian */); return new Object[]{ a }; } @Test @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test2c(byte[] a, int offset, long v) { storeLongLE(a, offset, v); return new Object[]{ a }; @@ -638,6 +752,82 @@ static Object[] test2e(byte[] a, int offset, long v) { return new Object[]{ a }; } + @DontCompile + static Object[] test2RBE(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 56); + a[offset + 1] = (byte)(v >> 48); + a[offset + 2] = (byte)(v >> 40); + a[offset + 3] = (byte)(v >> 32); + a[offset + 4] = (byte)(v >> 24); + a[offset + 5] = (byte)(v >> 16); + a[offset + 6] = (byte)(v >> 8); + a[offset + 7] = (byte)(v >> 0); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test2aBE(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 56); + a[offset + 1] = (byte)(v >> 48); + a[offset + 2] = (byte)(v >> 40); + a[offset + 3] = (byte)(v >> 32); + a[offset + 4] = (byte)(v >> 24); + a[offset + 5] = (byte)(v >> 16); + a[offset + 6] = (byte)(v >> 8); + a[offset + 7] = (byte)(v >> 0); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, + applyIf = {"UseUnalignedAccesses", "true"}) + static Object[] test2bBE(byte[] a, int offset, long v) { + // Add custom null check, to ensure the unsafe access always recognizes its type as an array store + if (a == null) {return null;} + UNSAFE.putLongUnaligned(a, UNSAFE.ARRAY_BYTE_BASE_OFFSET + offset, v, true /* bigEndian */); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test2cBE(byte[] a, int offset, long v) { + storeLongBE(a, offset, v); + return new Object[]{ a }; + } + + @Test + // No optimization, casting long -> int -> byte does not work + static Object[] test2dBE(byte[] a, int offset, long v) { + storeIntBE(a, offset + 0, (int)(v >> 32)); + storeIntBE(a, offset + 4, (int)(v >> 0)); + return new Object[]{ a }; + } + + @Test + // No optimization, casting long -> short -> byte does not work + static Object[] test2eBE(byte[] a, int offset, long v) { + storeShortBE(a, offset + 0, (short)(v >> 48)); + storeShortBE(a, offset + 2, (short)(v >> 32)); + storeShortBE(a, offset + 4, (short)(v >> 16)); + storeShortBE(a, offset + 6, (short)(v >> 0)); + return new Object[]{ a }; + } + @DontCompile static Object[] test3R(byte[] a, int offset, long v) { a[offset + 0] = (byte)(v >> 0); @@ -653,7 +843,13 @@ static Object[] test3R(byte[] a, int offset, long v) { @Test @IR(counts = {IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test3a(byte[] a, int offset, long v) { a[offset + 0] = (byte)(v >> 0); a[offset + 1] = (byte)(v >> 8); @@ -666,6 +862,40 @@ static Object[] test3a(byte[] a, int offset, long v) { return new Object[]{ a }; } + @DontCompile + static Object[] test3RBE(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 24); + a[offset + 1] = (byte)(v >> 16); + a[offset + 2] = (byte)(v >> 8); + a[offset + 3] = (byte)(v >> 0); + a[offset + 4] = (byte)(v >> 24); + a[offset + 5] = (byte)(v >> 16); + a[offset + 6] = (byte)(v >> 8); + a[offset + 7] = (byte)(v >> 0); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test3aBE(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 24); + a[offset + 1] = (byte)(v >> 16); + a[offset + 2] = (byte)(v >> 8); + a[offset + 3] = (byte)(v >> 0); + a[offset + 4] = (byte)(v >> 24); + a[offset + 5] = (byte)(v >> 16); + a[offset + 6] = (byte)(v >> 8); + a[offset + 7] = (byte)(v >> 0); + return new Object[]{ a }; + } + @DontCompile static Object[] test4R(byte[] a, int offset, long v1, int v2, short v3, byte v4) { a[offset + 0] = (byte)0x00; @@ -693,7 +923,14 @@ static Object[] test4R(byte[] a, int offset, long v1, int v2, short v3, byte v4) IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2", IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "12", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // Stores of constants can be merged + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test4a(byte[] a, int offset, long v1, int v2, short v3, byte v4) { a[offset + 0] = (byte)0x00; // individual load expected to go into state of RC a[offset + 1] = (byte)0xFF; @@ -715,6 +952,62 @@ static Object[] test4a(byte[] a, int offset, long v1, int v2, short v3, byte v4) return new Object[]{ a }; } + @DontCompile + static Object[] test4RBE(byte[] a, int offset, long v1, int v2, short v3, byte v4) { + a[offset + 0] = (byte)0x00; + a[offset + 1] = (byte)0xFF; + a[offset + 2] = v4; + a[offset + 3] = (byte)0x42; + a[offset + 4] = (byte)(v1 >> 8); + a[offset + 5] = (byte)(v1 >> 0); + a[offset + 6] = (byte)0xAB; + a[offset + 7] = (byte)0xCD; + a[offset + 8] = (byte)0xEF; + a[offset + 9] = (byte)0x01; + a[offset + 10] = (byte)(v2 >> 24); + a[offset + 11] = (byte)(v2 >> 16); + a[offset + 12] = (byte)(v2 >> 8); + a[offset + 13] = (byte)(v2 >> 0); + a[offset + 14] = (byte)(v3 >> 8); + a[offset + 15] = (byte)(v3 >> 0); + a[offset + 16] = (byte)0xEF; + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "12", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // Stores of constants can be merged + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4", // 3 (+ 1 for uncommon trap) + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test4aBE(byte[] a, int offset, long v1, int v2, short v3, byte v4) { + a[offset + 0] = (byte)0x00; // individual load expected to go into state of RC + a[offset + 1] = (byte)0xFF; + a[offset + 2] = v4; + a[offset + 3] = (byte)0x42; + a[offset + 4] = (byte)(v1 >> 8); + a[offset + 5] = (byte)(v1 >> 0); + a[offset + 6] = (byte)0xAB; + a[offset + 7] = (byte)0xCD; + a[offset + 8] = (byte)0xEF; + a[offset + 9] = (byte)0x01; + a[offset + 10] = (byte)(v2 >> 24); + a[offset + 11] = (byte)(v2 >> 16); + a[offset + 12] = (byte)(v2 >> 8); + a[offset + 13] = (byte)(v2 >> 0); + a[offset + 14] = (byte)(v3 >> 8); + a[offset + 15] = (byte)(v3 >> 0); + a[offset + 16] = (byte)0xEF; + return new Object[]{ a }; + } + @DontCompile static Object[] test5R(byte[] a, int offset) { a[offset + 0] = (byte)0x01; @@ -810,6 +1103,26 @@ static Object[] test7a(byte[] a, int offset1, int v1) { return new Object[]{ a }; } + @DontCompile + static Object[] test7RBE(byte[] a, int offset1, int v1) { + a[offset1 + 1] = (byte)(v1 >> 24); + a[offset1 + 2] = (byte)(v1 >> 16); + a[offset1 + 3] = (byte)(v1 >> 8); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}) + static Object[] test7aBE(byte[] a, int offset1, int v1) { + a[offset1 + 1] = (byte)(v1 >> 24); + a[offset1 + 2] = (byte)(v1 >> 16); + a[offset1 + 3] = (byte)(v1 >> 8); + return new Object[]{ a }; + } + @DontCompile static Object[] test100R(short[] a, int offset) { a[offset + 0] = (short)0x0100; @@ -925,7 +1238,14 @@ static Object[] test102R(short[] a, int offset, long v1, int v2, short v3) { IRNode.STORE_C_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4", // 3 (+1 that goes into RC) IRNode.STORE_I_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", IRNode.STORE_L_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "12", + IRNode.STORE_I_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // Stores of constants can be merged + IRNode.STORE_L_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test102a(short[] a, int offset, long v1, int v2, short v3) { a[offset + 0] = (short)0x0000; // store goes into RC a[offset + 1] = (short)0xFFFF; @@ -947,6 +1267,62 @@ static Object[] test102a(short[] a, int offset, long v1, int v2, short v3) { return new Object[]{ a }; } + @DontCompile + static Object[] test102RBE(short[] a, int offset, long v1, int v2, short v3) { + a[offset + 0] = (short)0x0000; + a[offset + 1] = (short)0xFFFF; + a[offset + 2] = v3; + a[offset + 3] = (short)0x4242; + a[offset + 4] = (short)(v1 >> 16); + a[offset + 5] = (short)(v1 >> 0); + a[offset + 6] = (short)0xAB11; + a[offset + 7] = (short)0xCD36; + a[offset + 8] = (short)0xEF89; + a[offset + 9] = (short)0x0156; + a[offset + 10] = (short)(v1 >> 48); + a[offset + 11] = (short)(v1 >> 32); + a[offset + 12] = (short)(v1 >> 16); + a[offset + 13] = (short)(v1 >> 0); + a[offset + 14] = (short)(v2 >> 16); + a[offset + 15] = (short)(v2 >> 0); + a[offset + 16] = (short)0xEFEF; + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "12", + IRNode.STORE_I_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // Stores of constants can be merged + IRNode.STORE_L_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4", // 3 (+1 that goes into RC) + IRNode.STORE_I_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", + IRNode.STORE_L_OF_CLASS, "short\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test102aBE(short[] a, int offset, long v1, int v2, short v3) { + a[offset + 0] = (short)0x0000; // store goes into RC + a[offset + 1] = (short)0xFFFF; + a[offset + 2] = v3; + a[offset + 3] = (short)0x4242; + a[offset + 4] = (short)(v1 >> 16); + a[offset + 5] = (short)(v1 >> 0); + a[offset + 6] = (short)0xAB11; + a[offset + 7] = (short)0xCD36; + a[offset + 8] = (short)0xEF89; + a[offset + 9] = (short)0x0156; + a[offset + 10] = (short)(v1 >> 48); + a[offset + 11] = (short)(v1 >> 32); + a[offset + 12] = (short)(v1 >> 16); + a[offset + 13] = (short)(v1 >> 0); + a[offset + 14] = (short)(v2 >> 16); + a[offset + 15] = (short)(v2 >> 0); + a[offset + 16] = (short)0xEFEF; + return new Object[]{ a }; + } + @DontCompile static Object[] test200R(int[] a, int offset) { a[offset + 0] = 0x01001236; @@ -1062,7 +1438,14 @@ static Object[] test202R(int[] a, int offset, long v1, int v2) { IRNode.STORE_C_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", IRNode.STORE_I_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6", // 5 (+1 that goes into RC) IRNode.STORE_L_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6"}, - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "10", + IRNode.STORE_L_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4"}, // Stores of constants can be merged + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test202a(int[] a, int offset, long v1, int v2) { a[offset + 0] = 0x00000000; // merged with store below, but also kept unchanged for RC a[offset + 1] = 0xFFFFFFFF; @@ -1075,7 +1458,7 @@ static Object[] test202a(int[] a, int offset, long v1, int v2) { a[offset + 8] = 0xEF890173; a[offset + 9] = 0x01560124; a[offset + 10] = (int)(v1 >> 0); - a[offset + 11] = (int)(v1 >> 32); + a[offset + 11] = (int)(v1 >> 32); // Stores to +11 and +12 can be merged also on big-endian a[offset + 12] = (int)(v1 >> 0); a[offset + 13] = (int)(v1 >> 32); a[offset + 14] = v2; @@ -1084,6 +1467,62 @@ static Object[] test202a(int[] a, int offset, long v1, int v2) { return new Object[]{ a }; } + @DontCompile + static Object[] test202RBE(int[] a, int offset, long v1, int v2) { + a[offset + 0] = 0x00000000; + a[offset + 1] = 0xFFFFFFFF; + a[offset + 2] = v2; + a[offset + 3] = 0x42424242; + a[offset + 4] = (int)(v1 >> 32); + a[offset + 5] = (int)(v1 >> 0); + a[offset + 6] = 0xAB110129; + a[offset + 7] = 0xCD360183; + a[offset + 8] = 0xEF890173; + a[offset + 9] = 0x01560124; + a[offset + 10] = (int)(v1 >> 32); + a[offset + 11] = (int)(v1 >> 0); + a[offset + 12] = (int)(v1 >> 32); + a[offset + 13] = (int)(v1 >> 0); + a[offset + 14] = v2; + a[offset + 15] = v2; + a[offset + 16] = 0xEFEFEFEF; + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "10", + IRNode.STORE_L_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4"}, // Stores of constants can be merged + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_C_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6", // 5 (+1 that goes into RC) + IRNode.STORE_L_OF_CLASS, "int\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test202aBE(int[] a, int offset, long v1, int v2) { + a[offset + 0] = 0x00000000; // merged with store below, but also kept unchanged for RC + a[offset + 1] = 0xFFFFFFFF; + a[offset + 2] = v2; + a[offset + 3] = 0x42424242; + a[offset + 4] = (int)(v1 >> 32); + a[offset + 5] = (int)(v1 >> 0); + a[offset + 6] = 0xAB110129; + a[offset + 7] = 0xCD360183; + a[offset + 8] = 0xEF890173; + a[offset + 9] = 0x01560124; + a[offset + 10] = (int)(v1 >> 32); + a[offset + 11] = (int)(v1 >> 0); // Stores to +11 and +12 can be merged also on little-endian + a[offset + 12] = (int)(v1 >> 32); + a[offset + 13] = (int)(v1 >> 0); + a[offset + 14] = v2; + a[offset + 15] = v2; + a[offset + 16] = 0xEFEFEFEF; + return new Object[]{ a }; + } + @DontCompile static Object[] test300R(int[] a) { a[2] = 42; @@ -1175,7 +1614,13 @@ static Object[] test500R(byte[] a, int offset, long v) { IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, // expect merged - applyIf = {"UseUnalignedAccesses", "true"}) + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"big-endian", "true"}) static Object[] test500a(byte[] a, int offset, long v) { int idx = 0; try { @@ -1255,6 +1700,140 @@ static Object[] test502a(byte[] a, int offset, long v) { return new Object[]{ a, new int[]{ idx } }; } + @DontCompile + // The 500-series has all the same code, but is executed with different inputs: + // 500a: never violate a RangeCheck -> expect will always merge stores + // 501a: randomly violate RangeCheck, also during warmup -> never merge stores + // 502a: during warmup never violate RangeCheck -> compile once with merged stores + // but then after warmup violate RangeCheck -> recompile without merged stores + static Object[] test500RBE(byte[] a, int offset, long v) { + int idx = 0; + try { + a[offset + 0] = (byte)(v >> 56); + idx = 1; + a[offset + 1] = (byte)(v >> 48); + idx = 2; + a[offset + 2] = (byte)(v >> 40); + idx = 3; + a[offset + 3] = (byte)(v >> 32); + idx = 4; + a[offset + 4] = (byte)(v >> 24); + idx = 5; + a[offset + 5] = (byte)(v >> 16); + idx = 6; + a[offset + 6] = (byte)(v >> 8); + idx = 7; + a[offset + 7] = (byte)(v >> 0); + idx = 8; + } catch (ArrayIndexOutOfBoundsException _) {} + return new Object[]{ a, new int[]{ idx } }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // for RangeCheck trap + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, // expect merged + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test500aBE(byte[] a, int offset, long v) { + int idx = 0; + try { + a[offset + 0] = (byte)(v >> 56); + idx = 1; + a[offset + 1] = (byte)(v >> 48); + idx = 2; + a[offset + 2] = (byte)(v >> 40); + idx = 3; + a[offset + 3] = (byte)(v >> 32); + idx = 4; + a[offset + 4] = (byte)(v >> 24); + idx = 5; + a[offset + 5] = (byte)(v >> 16); + idx = 6; + a[offset + 6] = (byte)(v >> 8); + idx = 7; + a[offset + 7] = (byte)(v >> 0); + idx = 8; + } catch (ArrayIndexOutOfBoundsException _) {} + return new Object[]{ a, new int[]{ idx } }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "7", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test501aBE(byte[] a, int offset, long v) { + int idx = 0; + try { + a[offset + 0] = (byte)(v >> 56); + idx = 1; + a[offset + 1] = (byte)(v >> 48); + idx = 2; + a[offset + 2] = (byte)(v >> 40); + idx = 3; + a[offset + 3] = (byte)(v >> 32); + idx = 4; + a[offset + 4] = (byte)(v >> 24); + idx = 5; + a[offset + 5] = (byte)(v >> 16); + idx = 6; + a[offset + 6] = (byte)(v >> 8); + idx = 7; + a[offset + 7] = (byte)(v >> 0); + idx = 8; + } catch (ArrayIndexOutOfBoundsException _) {} + return new Object[]{ a, new int[]{ idx } }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "7", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test502aBE(byte[] a, int offset, long v) { + int idx = 0; + try { + a[offset + 0] = (byte)(v >> 56); + idx = 1; + a[offset + 1] = (byte)(v >> 48); + idx = 2; + a[offset + 2] = (byte)(v >> 40); + idx = 3; + a[offset + 3] = (byte)(v >> 32); + idx = 4; + a[offset + 4] = (byte)(v >> 24); + idx = 5; + a[offset + 5] = (byte)(v >> 16); + idx = 6; + a[offset + 6] = (byte)(v >> 8); + idx = 7; + a[offset + 7] = (byte)(v >> 0); + idx = 8; + } catch (ArrayIndexOutOfBoundsException _) {} + return new Object[]{ a, new int[]{ idx } }; + } + @DontCompile static Object[] test600R(byte[] aB, int[] aI, int i) { Object a = null; @@ -1319,4 +1898,69 @@ static Object[] test700a(int[] a, long v1) { a[1] = (int)(v1 >> -2); return new Object[]{ a }; } + + @DontCompile + static Object[] test800R(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 0); + a[offset + 1] = (byte)(v >> 8); + a[offset + 2] = (byte)(v >> 16); + a[offset + 3] = (byte)(v >> 24); + a[offset + 4] = (byte)(v >> 32); + a[offset + 5] = (byte)(v >> 40); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}) + static Object[] test800a(byte[] a, int offset, long v) { + // Merge attempts begin at the lowest store in the Memory chain. + // Candidates are found following the chain. The list is trimmed to a + // power of 2 length by removing higher stores. + a[offset + 0] = (byte)(v >> 0); // Removed from candidate list + a[offset + 1] = (byte)(v >> 8); // Removed from candidate list + a[offset + 2] = (byte)(v >> 16); // The 4 following stores are on the candidate list. + a[offset + 3] = (byte)(v >> 24); // The current logic does not merge them + a[offset + 4] = (byte)(v >> 32); // since it would require shifting the input. + a[offset + 5] = (byte)(v >> 40); + return new Object[]{ a }; + } + + @DontCompile + static Object[] test800RBE(byte[] a, int offset, long v) { + a[offset + 0] = (byte)(v >> 40); + a[offset + 1] = (byte)(v >> 32); + a[offset + 2] = (byte)(v >> 24); + a[offset + 3] = (byte)(v >> 16); + a[offset + 4] = (byte)(v >> 8); + a[offset + 5] = (byte)(v >> 0); + return new Object[]{ a }; + } + + @Test + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIfPlatform = {"little-endian", "true"}) + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2", + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatform = {"big-endian", "true"}) + static Object[] test800aBE(byte[] a, int offset, long v) { + // Merge attempts begin at the lowest store in the Memory chain. + // Candidates are found following the chain. The list is trimmed to a + // power of 2 length by removing higher stores. + a[offset + 0] = (byte)(v >> 40); // Removed from candidate list + a[offset + 1] = (byte)(v >> 32); // Removed from candidate list + a[offset + 2] = (byte)(v >> 24); // The 4 following stores are on the candidate list + a[offset + 3] = (byte)(v >> 16); // and they are successfully merged on big endian platforms. + a[offset + 4] = (byte)(v >> 8); + a[offset + 5] = (byte)(v >> 0); + return new Object[]{ a }; + } } diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java index 82d4a398ab698..fc46712bb43f1 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/test/IREncodingPrinter.java @@ -31,6 +31,7 @@ import jdk.test.whitebox.WhiteBox; import java.lang.reflect.Method; +import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -74,7 +75,10 @@ public class IREncodingPrinter { "x86", // corresponds to vm.bits "32-bit", - "64-bit" + "64-bit", + // java.nio.ByteOrder + "little-endian", + "big-endian" )); // Please verify new CPU features before adding them. If we allow non-existent features @@ -359,7 +363,9 @@ private boolean checkPlatform(String platform, String value) { arch = "x86"; } - String currentPlatform = os + " " + arch + " " + (Platform.is32bit() ? "32-bit" : "64-bit"); + String endianess = ByteOrder.nativeOrder().equals(ByteOrder.BIG_ENDIAN)? "big-endian" : "little-endian"; + + String currentPlatform = os + " " + arch + " " + (Platform.is32bit() ? "32-bit" : "64-bit") + " " + endianess; return (trueValue && currentPlatform.contains(platform)) || (falseValue && !currentPlatform.contains(platform)); }