From 3ffc5b9ef720a07143ef5728d2597afdf2f2c251 Mon Sep 17 00:00:00 2001 From: Marc Chevalier Date: Fri, 11 Jul 2025 07:07:27 +0000 Subject: [PATCH] 8359344: C2: Malformed control flow after intrinsic bailout Reviewed-by: thartmann, kvn --- src/hotspot/share/opto/callnode.hpp | 1 + src/hotspot/share/opto/library_call.cpp | 69 ++++--- src/hotspot/share/opto/library_call.hpp | 23 +++ src/hotspot/share/opto/vectorIntrinsics.cpp | 53 +----- .../VectorIntoArrayInvalidControlFlow.java | 69 +++++++ .../compiler/lib/ir_framework/IRNode.java | 6 +- .../jtreg/compiler/unsafe/OpaqueAccesses.java | 178 +++++++++++++----- 7 files changed, 282 insertions(+), 117 deletions(-) create mode 100644 test/hotspot/jtreg/compiler/intrinsics/VectorIntoArrayInvalidControlFlow.java diff --git a/src/hotspot/share/opto/callnode.hpp b/src/hotspot/share/opto/callnode.hpp index db857d4c6d1..519524826bd 100644 --- a/src/hotspot/share/opto/callnode.hpp +++ b/src/hotspot/share/opto/callnode.hpp @@ -330,6 +330,7 @@ public: class SafePointNode : public MultiNode { friend JVMState; friend class GraphKit; + friend class LibraryCallKit; virtual bool cmp( const Node &n ) const; virtual uint size_of() const; // Size is bigger diff --git a/src/hotspot/share/opto/library_call.cpp b/src/hotspot/share/opto/library_call.cpp index 94f047dc115..266706cdfe8 100644 --- a/src/hotspot/share/opto/library_call.cpp +++ b/src/hotspot/share/opto/library_call.cpp @@ -135,6 +135,7 @@ JVMState* LibraryIntrinsic::generate(JVMState* jvms) { // The intrinsic bailed out assert(ctrl == kit.control(), "Control flow was added although the intrinsic bailed out"); + assert(jvms->map() == kit.map(), "Out of sync JVM state"); if (jvms->has_method()) { // Not a root compile. const char* msg; @@ -1724,18 +1725,15 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) { } // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); value = must_be_not_null(value, true); Node* adr = array_element_address(value, index, T_CHAR); if (adr->is_top()) { - set_map(old_map); - set_sp(old_sp); return false; } - destruct_map_clone(old_map); + old_state.discard(); if (is_store) { access_store_at(value, adr, TypeAryPtr::BYTES, ch, TypeInt::CHAR, T_CHAR, IN_HEAP | MO_UNORDERED | C2_MISMATCHED); } else { @@ -2373,6 +2371,47 @@ DecoratorSet LibraryCallKit::mo_decorator_for_access_kind(AccessKind kind) { } } +LibraryCallKit::SavedState::SavedState(LibraryCallKit* kit) : + _kit(kit), + _sp(kit->sp()), + _jvms(kit->jvms()), + _map(kit->clone_map()), + _discarded(false) +{ + for (DUIterator_Fast imax, i = kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = kit->control()->fast_out(i); + if (out->is_CFG()) { + _ctrl_succ.push(out); + } + } +} + +LibraryCallKit::SavedState::~SavedState() { + if (_discarded) { + _kit->destruct_map_clone(_map); + return; + } + _kit->jvms()->set_map(_map); + _kit->jvms()->set_sp(_sp); + _map->set_jvms(_kit->jvms()); + _kit->set_map(_map); + _kit->set_sp(_sp); + for (DUIterator_Fast imax, i = _kit->control()->fast_outs(imax); i < imax; i++) { + Node* out = _kit->control()->fast_out(i); + if (out->is_CFG() && out->in(0) == _kit->control() && out != _kit->map() && !_ctrl_succ.member(out)) { + _kit->_gvn.hash_delete(out); + out->set_req(0, _kit->C->top()); + _kit->C->record_for_igvn(out); + --i; --imax; + _kit->_gvn.hash_find_insert(out); + } + } +} + +void LibraryCallKit::SavedState::discard() { + _discarded = true; +} + bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, const AccessKind kind, const bool unaligned) { if (callee()->is_static()) return false; // caller must have the capability! DecoratorSet decorators = C2_UNSAFE_ACCESS; @@ -2434,8 +2473,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c offset = ConvL2X(offset); // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); Node* adr = make_unsafe_address(base, offset, type, kind == Relaxed); assert(!stopped(), "Inlining of unsafe access failed: address construction stopped unexpectedly"); @@ -2444,8 +2482,6 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c if (type != T_OBJECT) { decorators |= IN_NATIVE; // off-heap primitive access } else { - set_map(old_map); - set_sp(old_sp); return false; // off-heap oop accesses are not supported } } else { @@ -2463,8 +2499,6 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c const TypePtr* adr_type = _gvn.type(adr)->isa_ptr(); if (adr_type == TypePtr::NULL_PTR) { - set_map(old_map); - set_sp(old_sp); return false; // off-heap access with zero address } @@ -2474,8 +2508,6 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c if (alias_type->adr_type() == TypeInstPtr::KLASS || alias_type->adr_type() == TypeAryPtr::RANGE) { - set_map(old_map); - set_sp(old_sp); return false; // not supported } @@ -2494,8 +2526,6 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c } if ((bt == T_OBJECT) != (type == T_OBJECT)) { // Don't intrinsify mismatched object accesses - set_map(old_map); - set_sp(old_sp); return false; } mismatched = (bt != type); @@ -2503,7 +2533,7 @@ bool LibraryCallKit::inline_unsafe_access(bool is_store, const BasicType type, c mismatched = true; // conservatively mark all "wide" on-heap accesses as mismatched } - destruct_map_clone(old_map); + old_state.discard(); assert(!mismatched || alias_type->adr_type()->is_oopptr(), "off-heap access can't be mismatched"); if (mismatched) { @@ -2739,8 +2769,7 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt // 32-bit machines ignore the high half of long offsets offset = ConvL2X(offset); // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); Node* adr = make_unsafe_address(base, offset,type, false); const TypePtr *adr_type = _gvn.type(adr)->isa_ptr(); @@ -2749,12 +2778,10 @@ bool LibraryCallKit::inline_unsafe_load_store(const BasicType type, const LoadSt if (bt != T_ILLEGAL && (is_reference_type(bt) != (type == T_OBJECT))) { // Don't intrinsify mismatched object accesses. - set_map(old_map); - set_sp(old_sp); return false; } - destruct_map_clone(old_map); + old_state.discard(); // For CAS, unlike inline_unsafe_access, there seems no point in // trying to refine types. Just use the coarse types here. diff --git a/src/hotspot/share/opto/library_call.hpp b/src/hotspot/share/opto/library_call.hpp index 94b02290ccd..fbc6007d4e1 100644 --- a/src/hotspot/share/opto/library_call.hpp +++ b/src/hotspot/share/opto/library_call.hpp @@ -129,6 +129,29 @@ class LibraryCallKit : public GraphKit { virtual int reexecute_sp() { return _reexecute_sp; } + /* When an intrinsic makes changes before bailing out, it's necessary to restore the graph + * as it was. See JDK-8359344 for what can happen wrong. It's also not always possible to + * bailout before making changes because the bailing out decision might depend on new nodes + * (their types, for instance). + * + * So, if an intrinsic might cause this situation, one must start by saving the state in a + * SavedState by constructing it, and the state will be restored on destruction. If the + * intrinsic is not bailing out, one need to call discard to prevent restoring the old state. + */ + class SavedState { + LibraryCallKit* _kit; + uint _sp; + JVMState* _jvms; + SafePointNode* _map; + Unique_Node_List _ctrl_succ; + bool _discarded; + + public: + SavedState(LibraryCallKit*); + ~SavedState(); + void discard(); + }; + // Helper functions to inline natives Node* generate_guard(Node* test, RegionNode* region, float true_prob); Node* generate_slow_guard(Node* test, RegionNode* region); diff --git a/src/hotspot/share/opto/vectorIntrinsics.cpp b/src/hotspot/share/opto/vectorIntrinsics.cpp index 4aa6de2bb4c..97c5dbe03ef 100644 --- a/src/hotspot/share/opto/vectorIntrinsics.cpp +++ b/src/hotspot/share/opto/vectorIntrinsics.cpp @@ -822,8 +822,7 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { Node* offset = ConvL2X(argument(4)); // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); Node* addr = make_unsafe_address(base, offset, (is_mask ? T_BOOLEAN : elem_bt), true); @@ -860,8 +859,6 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s atype=%s ismask=no", is_store, is_store ? "store" : "load", num_elem, type2name(elem_bt), type2name(arr_type->elem()->array_element_basic_type())); - set_map(old_map); - set_sp(old_sp); return false; } @@ -873,8 +870,6 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d*8 etype=%s/8 ismask=no", is_store, "store", num_elem, type2name(elem_bt)); - set_map(old_map); - set_sp(old_sp); return false; // not supported } } else { @@ -883,8 +878,6 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d*8 etype=%s/8 ismask=no", is_store, "load", mem_num_elem, type2name(mem_elem_bt)); - set_map(old_map); - set_sp(old_sp); return false; // not supported } } @@ -892,14 +885,10 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { if (is_mask) { if (!is_store) { if (!arch_supports_vector(Op_LoadVector, num_elem, elem_bt, VecMaskUseLoad)) { - set_map(old_map); - set_sp(old_sp); return false; // not supported } } else { if (!arch_supports_vector(Op_StoreVector, num_elem, elem_bt, VecMaskUseStore)) { - set_map(old_map); - set_sp(old_sp); return false; // not supported } } @@ -914,8 +903,6 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { if (is_store) { Node* val = unbox_vector(argument(7), vbox_type, elem_bt, num_elem); if (val == nullptr) { - set_map(old_map); - set_sp(old_sp); return false; // operand unboxing failed } set_all_memory(reset_memory()); @@ -952,7 +939,7 @@ bool LibraryCallKit::inline_vector_mem_operation(bool is_store) { set_result(box); } - destruct_map_clone(old_map); + old_state.discard(); if (needs_cpu_membar) { insert_mem_bar(Op_MemBarCPUOrder); @@ -1029,8 +1016,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { Node* offset = ConvL2X(argument(5)); // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); Node* addr = make_unsafe_address(base, offset, elem_bt, true); const TypePtr *addr_type = gvn().type(addr)->isa_ptr(); @@ -1043,8 +1029,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s atype=%s", is_store, is_store ? "storeMasked" : "loadMasked", num_elem, type2name(elem_bt), type2name(arr_type->elem()->array_element_basic_type())); - set_map(old_map); - set_sp(old_sp); return false; } @@ -1066,8 +1050,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { if (!offset_in_range->is_con()) { log_if_needed(" ** missing constant: offsetInRange=%s", NodeClassNames[argument(8)->Opcode()]); - set_map(old_map); - set_sp(old_sp); return false; } needs_predicate = (offset_in_range->get_con() == 0); @@ -1077,8 +1059,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { log_if_needed(" ** not supported: op=%s vlen=%d etype=%s mismatched_ms=%d", is_store ? "storeMasked" : "loadMasked", num_elem, type2name(elem_bt), mismatched_ms ? 1 : 0); - set_map(old_map); - set_sp(old_sp); return false; } } @@ -1089,8 +1069,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { !arch_supports_vector(Op_VectorBlend, mem_num_elem, mem_elem_bt, VecMaskUseLoad))) { log_if_needed(" ** not supported: op=loadMasked vlen=%d etype=%s mismatched_ms=%d", num_elem, type2name(elem_bt), mismatched_ms ? 1 : 0); - set_map(old_map); - set_sp(old_sp); return false; } @@ -1101,8 +1079,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s mismatched_ms=1", is_store, is_store ? "storeMasked" : "loadMasked", num_elem, type2name(elem_bt)); - set_map(old_map); - set_sp(old_sp); return false; } } @@ -1113,8 +1089,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s", is_store, is_store ? "storeMasked" : "loadMasked", num_elem, type2name(elem_bt)); - set_map(old_map); - set_sp(old_sp); return false; } @@ -1135,8 +1109,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { log_if_needed(" ** unbox failed mask=%s", is_store ? NodeClassNames[argument(9)->Opcode()] : NodeClassNames[argument(8)->Opcode()]); - set_map(old_map); - set_sp(old_sp); return false; } @@ -1145,8 +1117,6 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { if (val == nullptr) { log_if_needed(" ** unbox failed vector=%s", NodeClassNames[argument(8)->Opcode()]); - set_map(old_map); - set_sp(old_sp); return false; // operand unboxing failed } set_all_memory(reset_memory()); @@ -1193,7 +1163,7 @@ bool LibraryCallKit::inline_vector_mem_masked_operation(bool is_store) { set_result(box); } - destruct_map_clone(old_map); + old_state.discard(); if (can_access_non_heap) { insert_mem_bar(Op_MemBarCPUOrder); @@ -1316,8 +1286,7 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { Node* offset = ConvL2X(argument(7)); // Save state and restore on bailout - uint old_sp = sp(); - SafePointNode* old_map = clone_map(); + SavedState old_state(this); Node* addr = nullptr; if (!is_subword_type(elem_bt)) { @@ -1339,8 +1308,6 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { log_if_needed(" ** not supported: arity=%d op=%s vlen=%d etype=%s atype=%s ismask=no", is_scatter, is_scatter ? "scatter" : "gather", num_elem, type2name(elem_bt), type2name(arr_type->elem()->array_element_basic_type())); - set_map(old_map); - set_sp(old_sp); return false; } @@ -1348,8 +1315,6 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { const TypeInstPtr* vbox_type = TypeInstPtr::make_exact(TypePtr::NotNull, vbox_klass); ciKlass* vbox_idx_klass = vector_idx_klass->const_oop()->as_instance()->java_lang_Class_klass(); if (vbox_idx_klass == nullptr) { - set_map(old_map); - set_sp(old_sp); return false; } @@ -1364,8 +1329,6 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { // Get the first index vector. indexes = unbox_vector(argument(9), vbox_idx_type, T_INT, idx_num_elem); if (indexes == nullptr) { - set_map(old_map); - set_sp(old_sp); return false; } } @@ -1378,8 +1341,6 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { mask = unbox_vector(m, mbox_type, elem_bt, num_elem); if (mask == nullptr) { log_if_needed(" ** unbox failed mask=%s", NodeClassNames[m->Opcode()]); - set_map(old_map); - set_sp(old_sp); return false; } } @@ -1388,8 +1349,6 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { if (is_scatter) { Node* val = unbox_vector(argument(10), vbox_type, elem_bt, num_elem); if (val == nullptr) { - set_map(old_map); - set_sp(old_sp); return false; // operand unboxing failed } set_all_memory(reset_memory()); @@ -1412,7 +1371,7 @@ bool LibraryCallKit::inline_vector_gather_scatter(bool is_scatter) { set_result(box); } - destruct_map_clone(old_map); + old_state.discard(); C->set_max_vector_size(MAX2(C->max_vector_size(), (uint)(num_elem * type2aelembytes(elem_bt)))); return true; } diff --git a/test/hotspot/jtreg/compiler/intrinsics/VectorIntoArrayInvalidControlFlow.java b/test/hotspot/jtreg/compiler/intrinsics/VectorIntoArrayInvalidControlFlow.java new file mode 100644 index 00000000000..e9c8edc4592 --- /dev/null +++ b/test/hotspot/jtreg/compiler/intrinsics/VectorIntoArrayInvalidControlFlow.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.intrinsics; + +/* + * @test + * @bug 8359344 + * @summary Intrinsic storeMasked can add some control flow before bailing out, leaving a malformed CFG. + * @modules jdk.incubator.vector + * @run main/othervm -XX:+UnlockDiagnosticVMOptions + * -XX:TypeProfileLevel=222 -Xbatch + * -XX:CompileCommand=compileonly,jdk.incubator.vector.Long*::intoArray0 + * -XX:+AbortVMOnCompilationFailure + * compiler.intrinsics.VectorIntoArrayInvalidControlFlow + * + * @run main compiler.intrinsics.VectorIntoArrayInvalidControlFlow + */ + +import jdk.incubator.vector.*; + +public class VectorIntoArrayInvalidControlFlow { + private static final VectorSpecies L_SPECIES = LongVector.SPECIES_128; + private static final LongVector longVector; + private static final long[] longArray = new long[L_SPECIES.length()]; + private static final boolean[] longMask = new boolean[L_SPECIES.length()]; + private static final VectorMask longVectorMask; + + static { + for (int i = 0; i < L_SPECIES.length(); i++) { + longArray[i] = i + 1; + longMask[i] = L_SPECIES.length() > 1 && i % 2 == 0; + } + longVector = LongVector.fromArray(L_SPECIES, longArray, 0); + longVectorMask = VectorMask.fromArray(L_SPECIES, longMask, 0); + } + + static long[] test() { + long[] res = new long[L_SPECIES.length()]; + for(int j = 0; j < 10_000; j++) { + longVector.intoArray(res, 0, longVectorMask); + } + return res; + } + + static public void main(String[] args) { + test(); + } +} \ No newline at end of file diff --git a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java index e7582015e86..08fd7902695 100644 --- a/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java +++ b/test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java @@ -99,9 +99,9 @@ public class IRNode { private static final String POSTFIX = "#_"; - private static final String START = "(\\d+(\\s){2}("; - private static final String MID = ".*)+(\\s){2}===.*"; - private static final String END = ")"; + public static final String START = "(\\d+(\\s){2}("; + public static final String MID = ".*)+(\\s){2}===.*"; + public static final String END = ")"; private static final String STORE_OF_CLASS_POSTFIX = "(:|\\+)\\S* \\*" + END; private static final String LOAD_OF_CLASS_POSTFIX = "(:|\\+)\\S* \\*" + END; diff --git a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java index a646728b07a..485aed4c032 100644 --- a/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java +++ b/test/hotspot/jtreg/compiler/unsafe/OpaqueAccesses.java @@ -23,32 +23,15 @@ /* * @test - * @bug 8155781 + * @bug 8155781 8359344 * @modules java.base/jdk.internal.misc - * - * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:-TieredCompilation -Xbatch - * -XX:+UseCompressedOops -XX:+UseCompressedClassPointers - * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test* - * compiler.unsafe.OpaqueAccesses - * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:-TieredCompilation -Xbatch - * -XX:+UseCompressedOops -XX:-UseCompressedClassPointers - * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test* - * compiler.unsafe.OpaqueAccesses - * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:-TieredCompilation -Xbatch - * -XX:-UseCompressedOops -XX:+UseCompressedClassPointers - * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test* - * compiler.unsafe.OpaqueAccesses - * @run main/bootclasspath/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -XX:-TieredCompilation -Xbatch - * -XX:-UseCompressedOops -XX:-UseCompressedClassPointers - * -XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test* - * compiler.unsafe.OpaqueAccesses + * @library /test/lib / + * @run driver compiler.unsafe.OpaqueAccesses */ package compiler.unsafe; +import compiler.lib.ir_framework.*; + import jdk.internal.misc.Unsafe; import java.lang.reflect.Field; @@ -77,74 +60,126 @@ public class OpaqueAccesses { private Object f = new Object(); private long l1, l2; + // To the end of a line, a new line character, repeated. + private static final String FULL_LINES = "(.*\\R)*"; + // Finish the line after the node type, skips full line, and eats until before the node types + private static final String SKIP = IRNode.MID + IRNode.END + "\\R" + FULL_LINES + "\\s*" + IRNode.START; + + private static final String CALL_STATIC_JAVA_AND_THEN_OPAQUE_NOT_NULL = IRNode.START + "CallStaticJava" + SKIP + "OpaqueNotNull" + IRNode.MID + IRNode.END; + private static final String OPAQUE_NOT_NULL_AND_THEN_CALL_STATIC_JAVA = IRNode.START + "OpaqueNotNull" + SKIP + "CallStaticJava" + IRNode.MID + IRNode.END; + /* Having both CallStaticJava and OpaqueNotNull, in any order. We use that in a failOn to make sure we have one + * or the other (or none), but not both. + * The CallStaticJava happens when the call is not intrinsified, and the OpaqueNotNull comes from the intrinsic. + * We don't want a unfinished intrinsic, with the call nevertheless. + */ + private static final String BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL = + "(" + CALL_STATIC_JAVA_AND_THEN_OPAQUE_NOT_NULL + ") | (" + OPAQUE_NOT_NULL_AND_THEN_CALL_STATIC_JAVA + ")"; + + + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testFixedOffsetField(Object o) { return UNSAFE.getReference(o, F_OFFSET); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader0(Object o) { return UNSAFE.getInt(o, 0); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader4(Object o) { return UNSAFE.getInt(o, 4); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader8(Object o) { return UNSAFE.getInt(o, 8); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader12(Object o) { return UNSAFE.getInt(o, 12); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader16(Object o) { return UNSAFE.getInt(o, 16); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeader17(Object o) { return UNSAFE.getIntUnaligned(o, 17); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testFixedBase(long off) { return UNSAFE.getReference(INSTANCE, off); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testOpaque(Object o, long off) { return UNSAFE.getReference(o, off); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray0(Object[] arr) { return UNSAFE.getInt(arr, 0); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray4(Object[] arr) { return UNSAFE.getInt(arr, 4); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray8(Object[] arr) { return UNSAFE.getInt(arr, 8); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray12(Object[] arr) { return UNSAFE.getInt(arr, 12); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray16(Object[] arr) { return UNSAFE.getInt(arr, 16); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static int testFixedOffsetHeaderArray17(Object[] arr) { return UNSAFE.getIntUnaligned(arr, 17); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testFixedOffsetArray(Object[] arr) { return UNSAFE.getReference(arr, E_OFFSET); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testFixedBaseArray(long off) { return UNSAFE.getReference(ARRAY, off); } + @Test + @IR(failOn = {BOTH_CALL_STATIC_JAVA_AND_OPAQUE_NOT_NULL}, phase = CompilePhase.BEFORE_MACRO_EXPANSION) static Object testOpaqueArray(Object[] o, long off) { return UNSAFE.getReference(o, off); } @@ -152,6 +187,7 @@ public class OpaqueAccesses { static final long ADDR = UNSAFE.allocateMemory(10); static boolean flag; + @Test static int testMixedAccess() { flag = !flag; Object o = (flag ? INSTANCE : null); @@ -159,31 +195,81 @@ public class OpaqueAccesses { return UNSAFE.getInt(o, off); } - public static void main(String[] args) { - for (int i = 0; i < 20_000; i++) { - // Instance - testFixedOffsetField(INSTANCE); - testFixedOffsetHeader0(INSTANCE); - testFixedOffsetHeader4(INSTANCE); - testFixedOffsetHeader8(INSTANCE); - testFixedOffsetHeader12(INSTANCE); - testFixedOffsetHeader16(INSTANCE); - testFixedOffsetHeader17(INSTANCE); - testFixedBase(F_OFFSET); - testOpaque(INSTANCE, F_OFFSET); - testMixedAccess(); + @Run(test = { + "testFixedOffsetField", + "testFixedOffsetHeader0", + "testFixedOffsetHeader4", + "testFixedOffsetHeader8", + "testFixedOffsetHeader12", + "testFixedOffsetHeader16", + "testFixedOffsetHeader17", + "testFixedBase", + "testOpaque", + "testMixedAccess", + "testFixedOffsetHeaderArray0", + "testFixedOffsetHeaderArray4", + "testFixedOffsetHeaderArray8", + "testFixedOffsetHeaderArray12", + "testFixedOffsetHeaderArray16", + "testFixedOffsetHeaderArray17", + "testFixedOffsetArray", + "testFixedBaseArray", + "testOpaqueArray", + }) + public static void runMethod() { + // Instance + testFixedOffsetField(INSTANCE); + testFixedOffsetHeader0(INSTANCE); + testFixedOffsetHeader4(INSTANCE); + testFixedOffsetHeader8(INSTANCE); + testFixedOffsetHeader12(INSTANCE); + testFixedOffsetHeader16(INSTANCE); + testFixedOffsetHeader17(INSTANCE); + testFixedBase(F_OFFSET); + testOpaque(INSTANCE, F_OFFSET); + testMixedAccess(); - // Array - testFixedOffsetHeaderArray0(ARRAY); - testFixedOffsetHeaderArray4(ARRAY); - testFixedOffsetHeaderArray8(ARRAY); - testFixedOffsetHeaderArray12(ARRAY); - testFixedOffsetHeaderArray16(ARRAY); - testFixedOffsetHeaderArray17(ARRAY); - testFixedOffsetArray(ARRAY); - testFixedBaseArray(E_OFFSET); - testOpaqueArray(ARRAY, E_OFFSET); - } + // Array + testFixedOffsetHeaderArray0(ARRAY); + testFixedOffsetHeaderArray4(ARRAY); + testFixedOffsetHeaderArray8(ARRAY); + testFixedOffsetHeaderArray12(ARRAY); + testFixedOffsetHeaderArray16(ARRAY); + testFixedOffsetHeaderArray17(ARRAY); + testFixedOffsetArray(ARRAY); + testFixedBaseArray(E_OFFSET); + testOpaqueArray(ARRAY, E_OFFSET); System.out.println("TEST PASSED"); } + + public static void main(String[] args) { + TestFramework.runWithFlags( + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", + "-XX:-TieredCompilation", "-Xbatch", + "-XX:+UseCompressedOops", "-XX:+UseCompressedClassPointers", + "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" + ); + TestFramework.runWithFlags( + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", + "-XX:-TieredCompilation", "-Xbatch", + "-XX:+UseCompressedOops", "-XX:-UseCompressedClassPointers", + "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" + ); + TestFramework.runWithFlags( + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", + "-XX:-TieredCompilation", "-Xbatch", + "-XX:-UseCompressedOops", "-XX:+UseCompressedClassPointers", + "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" + ); + TestFramework.runWithFlags( + "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED", + "-XX:+IgnoreUnrecognizedVMOptions", "-XX:+UnlockDiagnosticVMOptions", + "-XX:-TieredCompilation", "-Xbatch", + "-XX:-UseCompressedOops", "-XX:-UseCompressedClassPointers", + "-XX:CompileCommand=dontinline,compiler.unsafe.OpaqueAccesses::test*" + ); + } }