This commit is contained in:
Tobias Hartmann 2026-07-31 09:37:41 +02:00
parent cc278dbb8a
commit b137ad640d
4 changed files with 114 additions and 6 deletions

View File

@ -1403,7 +1403,7 @@ bool CallStaticJavaNode::remove_unknown_flat_array_load(PhaseIterGVN* igvn, Node
Node* CallStaticJavaNode::replace_is_substitutable(PhaseIterGVN* igvn) {
Node* left = in(TypeFunc::Parms);
Node* right = in(TypeFunc::Parms + 1);
if (!InlineTypeNode::can_emit_substitutability_check(left, right)) {
if (!InlineTypeNode::can_emit_substitutability_check(igvn, left, right)) {
return nullptr;
}

View File

@ -598,14 +598,32 @@ static bool check_cycle(ciInlineKlass* vk) {
return false;
}
// Check if 'lhs' and 'rhs' are the same oop, possibly wrapped in an InlineTypeNode.
static bool same_oop(PhaseGVN* phase, Node* lhs, Node* rhs) {
InlineTypeNode* lhs_inline = lhs->isa_InlineType();
if (lhs_inline != nullptr && lhs_inline->is_allocated(phase)) {
lhs = lhs_inline->get_oop();
}
InlineTypeNode* rhs_inline = rhs->isa_InlineType();
if (rhs_inline != nullptr && rhs_inline->is_allocated(phase)) {
rhs = rhs_inline->get_oop();
}
return lhs->eqv_uncast(rhs);
}
// Check if a substitutability check between 'lhs' and 'rhs' can be implemented in IR
bool InlineTypeNode::can_emit_substitutability_check(Node* lhs, Node* rhs) {
bool InlineTypeNode::can_emit_substitutability_check(PhaseGVN* phase, Node* lhs, Node* rhs) {
// We can't create new InlineTypeNodes after macro expansion
if (!phase->C->allow_macro_nodes()) {
return false;
}
if (!lhs->bottom_type()->isa_ptr() ||
(rhs != nullptr && !rhs->bottom_type()->isa_ptr())) {
return false;
}
if (rhs != nullptr && lhs->eqv_uncast(rhs)) {
if (rhs != nullptr && same_oop(phase, lhs, rhs)) {
return true;
}
@ -641,7 +659,7 @@ bool InlineTypeNode::can_emit_substitutability_check(Node* lhs, Node* rhs) {
Node* lhs_fv = lhs_inline->field_value(i);
Node* rhs_fv = rhs_inline != nullptr ? rhs_inline->field_value(i) : nullptr;
if (!can_emit_substitutability_check(lhs_fv, rhs_fv)) {
if (!can_emit_substitutability_check(phase, lhs_fv, rhs_fv)) {
return false;
}
}
@ -696,7 +714,7 @@ static Node* emit_substitutability_check_pointer(GraphKit* kit, PhiNode* result,
}
Node* cmp = nullptr;
if (lhs->eqv_uncast(rhs)) {
if (same_oop(&gvn, lhs, rhs)) {
cmp = kit->intcon(0);
} else if (!lhs_type->is_ptr()->can_be_inline_type() || !rhs_type->is_ptr()->can_be_inline_type()) {
// If one of the sides is not a value object, can only be substitutable if they are the same

View File

@ -132,7 +132,7 @@ public:
void store_flat_array(GraphKit* kit, Node* base, Node* idx);
// Implementation of the substitutability check for acmp
static bool can_emit_substitutability_check(Node* lhs, Node* rhs);
static bool can_emit_substitutability_check(PhaseGVN* phase, Node* lhs, Node* rhs);
static Node* emit_substitutability_check(GraphKit* kit, Node* lhs, Node* rhs);
// Allocates the inline type (if not yet allocated)

View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2026, 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.
*/
/*
* @test
* @bug 8388441
* @summary Test acmp optimization when operands become known after macro expansion
* @requires vm.compiler2.enabled
* @library /test/lib
* @enablePreview
* @run main/othervm -XX:-TieredCompilation -Xbatch ${test.main.class}
*/
import jdk.test.lib.Asserts;
public class TestSubstitutabilityExpansionAfterMacro {
record Box(Object value) { }
value record MyValue(Object value) { }
static boolean equals(Object a, Object b) {
return a == b;
}
// Below tests trigger InlineTypeNode::can_emit_substitutability_check only
// after macro expansion.
// EA leaves the right operand as Phi(InlineType<MyValue>, LoadN<Object>).
// The cast creates a buffered InlineTypeNode whose oop is a cast of that phi.
// After InlineTypeNode removal after macro expansion, both operands are equivalent.
static boolean test1(boolean b) {
Box box = b ? new Box(new MyValue(null)) : new Box(null);
if (box.value == null) {
box = new Box(new MyValue(null));
}
return equals((MyValue) box.value, box.value);
}
// InlineTypeNode removal changes Phi(InlineType<Integer>(oop=null), exact Object)
// to Phi(null, exact Object) as the right operand. The phi then becomes an exact
// nullable Object and can_be_inline_type() changes to false after macro expansion.
static boolean test2(Object obj, boolean b) {
return equals(obj, b ? (Integer) null : new Object());
}
// Both operands are phis of an InlineTypeNode and its oop. After InlineTypeNode
// removal, both phis collapse to the same oop and the operands become equivalent
// after macro expansion.
static boolean test3(Object obj, boolean b) {
Object left = b ? (MyValue) obj : obj;
Object right = b ? obj : (MyValue) obj;
// Use local acmp for fresh profiling
return left == right;
}
public static void main(String[] args) {
// Warmup and profile acmp with identity operands
for (int i = 0; i < 10_000; i++) {
equals(args, args);
}
Object obj = new Object();
MyValue val = new MyValue(obj);
for (int i = 0; i < 50_000; i++) {
boolean b = (i & 1) == 0;
Asserts.assertEQ(test1(b), true);
Asserts.assertEQ(test2(b ? null : obj, b), b);
Asserts.assertEQ(test3(val, b), true);
}
}
}