diff --git a/src/hotspot/share/opto/memnode.cpp b/src/hotspot/share/opto/memnode.cpp index a7cf598cb3d..55a65042584 100644 --- a/src/hotspot/share/opto/memnode.cpp +++ b/src/hotspot/share/opto/memnode.cpp @@ -2774,12 +2774,33 @@ class MergePrimitiveStores : public StackObj { private: PhaseGVN* const _phase; StoreNode* const _store; + // State machine with initial state Unknown + // Allowed transitions: + // Unknown -> Const + // Unknown -> Platform + // Unknown -> Reverse + // Unknown -> NotAdjacent + // Const -> Const + // Const -> NotAdjacent + // Platform -> Platform + // Platform -> NotAdjacent + // Reverse -> Reverse + // Reverse -> NotAdjacent + // NotAdjacent -> NotAdjacent + enum ValueOrder : uint8_t { + Unknown, // Initial state + Const, // Input values are const + Platform, // Platform order + Reverse, // Reverse platform order + NotAdjacent // Not adjacent + }; + ValueOrder _value_order; NOT_PRODUCT( const CHeapBitMap &_trace_tags; ) public: MergePrimitiveStores(PhaseGVN* phase, StoreNode* store) : - _phase(phase), _store(store) + _phase(phase), _store(store), _value_order(ValueOrder::Unknown) NOT_PRODUCT( COMMA _trace_tags(Compile::current()->directive()->trace_merge_stores_tags()) ) {} @@ -2789,7 +2810,7 @@ private: bool is_compatible_store(const StoreNode* other_store) const; bool is_adjacent_pair(const StoreNode* use_store, const StoreNode* def_store) const; bool is_adjacent_input_pair(const Node* n1, const Node* n2, const int memory_size) const; - static bool is_con_RShift(const Node* n, Node const*& base_out, jint& shift_out); + static bool is_con_RShift(const Node* n, Node const*& base_out, jint& shift_out, PhaseGVN* phase); enum CFGStatus { SuccessNoRangeCheck, SuccessWithRangeCheck, Failure }; static CFGStatus cfg_status_for_pair(const StoreNode* use_store, const StoreNode* def_store); @@ -2825,6 +2846,7 @@ private: #endif }; + enum ValueOrder find_adjacent_input_value_order(const Node* n1, const Node* n2, const int memory_size) const; Status find_adjacent_use_store(const StoreNode* def_store) const; Status find_adjacent_def_store(const StoreNode* use_store) const; Status find_use_store(const StoreNode* def_store) const; @@ -2886,10 +2908,17 @@ StoreNode* MergePrimitiveStores::run() { // Check if we can merge with at least one def, so that we have at least 2 stores to merge. Status status_def = find_adjacent_def_store(_store); NOT_PRODUCT( if (is_trace_basic()) { tty->print("[TraceMergeStores] expect def: "); status_def.print_on(tty); }) - if (status_def.found_store() == nullptr) { + Node* def_store = status_def.found_store(); + if (def_store == nullptr) { return nullptr; } + // Initialize value order + _value_order = find_adjacent_input_value_order(def_store->in(MemNode::ValueIn), + _store->in(MemNode::ValueIn), + _store->memory_size()); + assert(_value_order != ValueOrder::NotAdjacent && _value_order != ValueOrder::Unknown, "Order should be checked"); + ResourceMark rm; Node_List merge_list; collect_merge_list(merge_list); @@ -2936,49 +2965,75 @@ bool MergePrimitiveStores::is_adjacent_pair(const StoreNode* use_store, const St return pointer_def.is_adjacent_to_and_before(pointer_use); } -bool MergePrimitiveStores::is_adjacent_input_pair(const Node* n1, const Node* n2, const int memory_size) const { +// Check input values n1 and n2 can be merged and return the value order +MergePrimitiveStores::ValueOrder MergePrimitiveStores::find_adjacent_input_value_order(const Node* n1, const Node* n2, + const int memory_size) const { // Pattern: [n1 = ConI, n2 = ConI] - if (n1->Opcode() == Op_ConI) { - return n2->Opcode() == Op_ConI; + if (n1->Opcode() == Op_ConI && n2->Opcode() == Op_ConI) { + return ValueOrder::Const; } - // 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; + Node const *base_n2; jint shift_n2; - if (!is_con_RShift(n2, base_n2, shift_n2)) { - return false; + if (!is_con_RShift(n2, base_n2, shift_n2, _phase)) { + return ValueOrder::NotAdjacent; } - if (n1->Opcode() == Op_ConvL2I) { - // look through - n1 = n1->in(1); - } - Node const* base_n1; + Node const *base_n1; jint shift_n1; - if (n1 == base_n2) { - // n1 = base = base >> 0 - base_n1 = n1; - shift_n1 = 0; - } else if (!is_con_RShift(n1, base_n1, shift_n1)) { - return false; + if (!is_con_RShift(n1, base_n1, shift_n1, _phase)) { + return ValueOrder::NotAdjacent; } + int bits_per_store = memory_size * 8; if (base_n1 != base_n2 || - shift_n1 + bits_per_store != shift_n2 || + abs(shift_n1 - shift_n2) != bits_per_store || shift_n1 % bits_per_store != 0) { - return false; + // Values are not adjacent + return ValueOrder::NotAdjacent; } - // both load from same value with correct shift - return true; + // Detect value order +#ifdef VM_LITTLE_ENDIAN + return shift_n1 < shift_n2 ? ValueOrder::Platform // Pattern: [n1 = base >> shift, n2 = base >> (shift + memory_size)] + : ValueOrder::Reverse; // Pattern: [n1 = base >> (shift + memory_size), n2 = base >> shift] +#else + return shift_n1 > shift_n2 ? ValueOrder::Platform // Pattern: [n1 = base >> (shift + memory_size), n2 = base >> shift] + : ValueOrder::Reverse; // Pattern: [n1 = base >> shift, n2 = base >> (shift + memory_size)] +#endif +} + +bool MergePrimitiveStores::is_adjacent_input_pair(const Node* n1, const Node* n2, const int memory_size) const { + ValueOrder input_value_order = find_adjacent_input_value_order(n1, n2, memory_size); + + switch (input_value_order) { + case ValueOrder::NotAdjacent: + return false; + case ValueOrder::Reverse: + if (memory_size != 1 || + !Matcher::match_rule_supported(Op_ReverseBytesS) || + !Matcher::match_rule_supported(Op_ReverseBytesI) || + !Matcher::match_rule_supported(Op_ReverseBytesL)) { + // ReverseBytes are not supported by platform + return false; + } + // fall-through. + case ValueOrder::Const: + case ValueOrder::Platform: + if (_value_order == ValueOrder::Unknown) { + // Initial state is Unknown, and we find a valid input value order + return true; + } + // The value order can not be changed + return _value_order == input_value_order; + case ValueOrder::Unknown: + default: + ShouldNotReachHere(); + } + return false; } // Detect pattern: n = base_out >> shift_out -bool MergePrimitiveStores::is_con_RShift(const Node* n, Node const*& base_out, jint& shift_out) { +bool MergePrimitiveStores::is_con_RShift(const Node* n, Node const*& base_out, jint& shift_out, PhaseGVN* phase) { assert(n != nullptr, "precondition"); int opc = n->Opcode(); @@ -2997,6 +3052,14 @@ bool MergePrimitiveStores::is_con_RShift(const Node* n, Node const*& base_out, j // The shift must be positive: return shift_out >= 0; } + + if (phase->type(n)->isa_int() != nullptr || + phase->type(n)->isa_long() != nullptr) { + // (base >> 0) + base_out = n; + shift_out = 0; + return true; + } return false; } @@ -3163,6 +3226,7 @@ Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) Node* first = merge_list.at(merge_list.size()-1); Node* merged_input_value = nullptr; if (_store->in(MemNode::ValueIn)->Opcode() == Op_ConI) { + assert(_value_order == ValueOrder::Const, "must match"); // Pattern: [ConI, ConI, ...] -> new constant jlong con = 0; jlong bits_per_store = _store->memory_size() * 8; @@ -3179,6 +3243,7 @@ Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) } merged_input_value = _phase->longcon(con); } else { + assert(_value_order == ValueOrder::Platform || _value_order == ValueOrder::Reverse, "must match"); // Pattern: [base >> 24, base >> 16, base >> 8, base] -> base // | | // _store first @@ -3189,10 +3254,13 @@ Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) // `_store` and `first` are swapped in the diagram above swap(hi, lo); #endif // !VM_LITTLE_ENDIAN + if (_value_order == ValueOrder::Reverse) { + swap(hi, lo); + } Node const* hi_base; jint hi_shift; merged_input_value = lo; - bool is_true = is_con_RShift(hi, hi_base, hi_shift); + bool is_true = is_con_RShift(hi, hi_base, hi_shift, _phase); assert(is_true, "must detect con RShift"); if (merged_input_value != hi_base && merged_input_value->Opcode() == Op_ConvL2I) { // look through @@ -3218,6 +3286,17 @@ Node* MergePrimitiveStores::make_merged_input_value(const Node_List& merge_list) (_phase->type(merged_input_value)->isa_long() != nullptr && new_memory_size == 8), "merged_input_value is either int or long, and new_memory_size is small enough"); + if (_value_order == ValueOrder::Reverse) { + assert(_store->memory_size() == 1, "only implemented for bytes"); + if (new_memory_size == 8) { + merged_input_value = _phase->transform(new ReverseBytesLNode(merged_input_value)); + } else if (new_memory_size == 4) { + merged_input_value = _phase->transform(new ReverseBytesINode(merged_input_value)); + } else { + assert(new_memory_size == 2, "sanity check"); + merged_input_value = _phase->transform(new ReverseBytesSNode(merged_input_value)); + } + } return merged_input_value; } diff --git a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java index 3598b8eae71..99e714d8c5f 100644 --- a/test/hotspot/jtreg/compiler/c2/TestMergeStores.java +++ b/test/hotspot/jtreg/compiler/c2/TestMergeStores.java @@ -33,7 +33,7 @@ import java.util.Random; /* * @test - * @bug 8318446 8331054 8331311 8335392 + * @bug 8318446 8331054 8331311 8335392 8347405 * @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 8331311 8335392 + * @bug 8318446 8331054 8331311 8335392 8347405 * @summary Test merging of consecutive stores * @modules java.base/jdk.internal.misc * @library /test/lib / @@ -724,10 +724,12 @@ public class TestMergeStores { @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, 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"}, + @IR(counts = { IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + 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", + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) static Object[] test2a(byte[] a, int offset, long v) { a[offset + 0] = (byte)(v >> 0); @@ -755,10 +757,12 @@ public class TestMergeStores { @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"little-endian", "true"}) - @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", 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"}, + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) static Object[] test2c(byte[] a, int offset, long v) { storeLongLE(a, offset, v); @@ -797,11 +801,13 @@ public class TestMergeStores { } @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", + 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", + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) @@ -828,11 +834,13 @@ public class TestMergeStores { } @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", + 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", + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @IR(counts = {IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) @@ -873,13 +881,18 @@ public class TestMergeStores { } @Test - @IR(counts = {IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", + 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\\\\)", "2", + 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\\\\)", "8", + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", 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"}, + 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", + IRNode.REVERSE_BYTES_I, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) static Object[] test3a(byte[] a, int offset, long v) { a[offset + 0] = (byte)(v >> 0); @@ -907,11 +920,13 @@ public class TestMergeStores { } @Test - @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", 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"}) + 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", + IRNode.REVERSE_BYTES_I, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @IR(counts = {IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2"}, applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) @@ -956,10 +971,12 @@ public class TestMergeStores { 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\\\\)", "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"}, + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4", // Unmerged stores: offset + (2,3,16) , and 1 for uncommon trap + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", // 1([+0,+1]) for platform order and 2([+4,+5], [+14,+15]) for reverse order + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2", // 1([+6,+9]) for platform order and 1([+10,+13]) for reverse order + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.REVERSE_BYTES_I, "1", + IRNode.REVERSE_BYTES_S, "2"}, applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) static Object[] test4a(byte[] a, int offset, long v1, int v2, short v3, byte v4) { @@ -1006,12 +1023,14 @@ public class TestMergeStores { } @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"}, + @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "4", // Unmerged stores: offset + (2,3,16) , and 1 for uncommon trap + IRNode.STORE_C_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "3", // 1([+0,+1]) for platform order and 2([+4,+5], [+14,+15]) for reverse order + IRNode.STORE_I_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "2", // 1([+6,+9]) for platform order and 1([+10,+13]) for reverse order + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.REVERSE_BYTES_I, "1", + IRNode.REVERSE_BYTES_S, "2"}, applyIf = {"UseUnalignedAccesses", "true"}, - applyIfPlatform = {"little-endian", "true"}) + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @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", @@ -1783,10 +1802,12 @@ public class TestMergeStores { IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1"}, // expect merged applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"little-endian", "true"}) - @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + @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\\\\)", "0"}, + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // expect merged + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, applyIfPlatform = {"big-endian", "true"}) static Object[] test500a(byte[] a, int offset, long v) { int idx = 0; @@ -1897,11 +1918,13 @@ public class TestMergeStores { } @Test - @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "8", + @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\\\\)", "0"}, - applyIfPlatform = {"little-endian", "true"}) + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "1", // expect merged + IRNode.REVERSE_BYTES_L, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @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", @@ -1932,11 +1955,13 @@ public class TestMergeStores { } @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", + @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"}, - applyIfPlatform = {"little-endian", "true"}) + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.REVERSE_BYTES_S, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @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", @@ -1967,11 +1992,13 @@ public class TestMergeStores { } @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", + @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"}, - applyIfPlatform = {"little-endian", "true"}) + IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0", + IRNode.REVERSE_BYTES_S, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @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", @@ -2168,11 +2195,13 @@ public class TestMergeStores { } @Test - @IR(counts = {IRNode.STORE_B_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "6", + @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\\\\)", "0", - IRNode.STORE_L_OF_CLASS, "byte\\\\[int:>=0] \\\\(java/lang/Cloneable,java/io/Serializable\\\\)", "0"}, - applyIfPlatform = {"little-endian", "true"}) + 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", + IRNode.REVERSE_BYTES_I, "1"}, + applyIf = {"UseUnalignedAccesses", "true"}, + applyIfPlatformAnd = {"little-endian", "true", "riscv64", "false"}) // Exclude riscv64 where ReverseBytes is only conditionally supported @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", diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index 20a0af1f111..0a5f6c63c76 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -1520,6 +1520,26 @@ public class IRNode { vectorNode(REPLICATE_D, "Replicate", TYPE_DOUBLE); } + public static final String REVERSE_BYTES_I = PREFIX + "REVERSE_BYTES_I" + POSTFIX; + static { + beforeMatchingNameRegex(REVERSE_BYTES_I, "ReverseBytesI"); + } + + public static final String REVERSE_BYTES_L = PREFIX + "REVERSE_BYTES_L" + POSTFIX; + static { + beforeMatchingNameRegex(REVERSE_BYTES_L, "ReverseBytesL"); + } + + public static final String REVERSE_BYTES_S = PREFIX + "REVERSE_BYTES_S" + POSTFIX; + static { + beforeMatchingNameRegex(REVERSE_BYTES_S, "ReverseBytesS"); + } + + public static final String REVERSE_BYTES_US = PREFIX + "REVERSE_BYTES_US" + POSTFIX; + static { + beforeMatchingNameRegex(REVERSE_BYTES_US, "ReverseBytesUS"); + } + public static final String REVERSE_BYTES_VB = VECTOR_PREFIX + "REVERSE_BYTES_VB" + POSTFIX; static { vectorNode(REVERSE_BYTES_VB, "ReverseBytesV", TYPE_BYTE);