8331311: C2: Big Endian Port of 8318446: optimize stores into primitive arrays by combining values into larger store

Reviewed-by: epeter, kvn
This commit is contained in:
Richard Reingruber 2024-06-07 06:16:03 +00:00
parent b4beda21b4
commit f7862bd6b9
3 changed files with 693 additions and 29 deletions
src/hotspot/share/opto
test/hotspot/jtreg/compiler

@ -2881,14 +2881,14 @@ public:
//
// 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 @@ public:
// 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
}

@ -33,7 +33,7 @@ import java.util.Random;
/*
* @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 @@ import java.util.Random;
/*
* @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 class 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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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 class 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<String,TestFunction>());
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<String,TestFunction>());
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 class 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<String,TestFunction>());
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<String,TestFunction>());
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 class 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<String,TestFunction>());
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<String,TestFunction>());
testGroups.get("test300").put("test300R", (_,_) -> { return test300R(aI.clone()); });
testGroups.get("test300").put("test300a", (_,_) -> { return test300a(aI.clone()); });
@ -184,6 +212,24 @@ public class 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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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 class TestMergeStores {
testGroups.put("test700", new HashMap<String,TestFunction>());
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<String,TestFunction>());
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<String,TestFunction>());
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 class 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 @@ public class TestMergeStores {
(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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
@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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
@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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 @@ public class TestMergeStores {
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 };
}
}

@ -31,6 +31,7 @@ import jdk.test.lib.Platform;
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 @@ public class IREncodingPrinter {
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));
}