Skip to content

Commit 6a6cf4f

Browse files
Quan Anh MaiTobiHartmann
authored andcommitted
8335256: [lworld] C2: Remove larval InlineTypeNode
8325627: [lworld] C2 compilation bailout for TestNullableInlineTypes::test85 8354283: [lworld] TestAllocationMergeAndFolding fails since jdk-25+16, since an allocation is detected Reviewed-by: thartmann
1 parent cc76937 commit 6a6cf4f

19 files changed

+464
-569
lines changed

src/hotspot/share/opto/callGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,7 +722,7 @@ void CallGenerator::do_late_inline_helper() {
722722
Node* buffer_oop = nullptr;
723723
ciMethod* inline_method = inline_cg()->method();
724724
ciType* return_type = inline_method->return_type();
725-
if (!call->tf()->returns_inline_type_as_fields() && is_mh_late_inline() &&
725+
if (!call->tf()->returns_inline_type_as_fields() &&
726726
return_type->is_inlinetype() && return_type->as_inline_klass()->can_be_returned_as_fields()) {
727727
// Allocate a buffer for the inline type returned as fields because the caller expects an oop return.
728728
// Do this before the method handle call in case the buffer allocation triggers deoptimization and

src/hotspot/share/opto/compile.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,16 +2843,39 @@ void Compile::Optimize() {
28432843

28442844
if (failing()) return;
28452845

2846+
{
2847+
// Eliminate some macro nodes before EA to reduce analysis pressure
2848+
PhaseMacroExpand mexp(igvn);
2849+
mexp.eliminate_macro_nodes();
2850+
if (failing()) {
2851+
return;
2852+
}
2853+
igvn.set_delay_transform(false);
2854+
print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2);
2855+
}
2856+
28462857
// Perform escape analysis
28472858
if (do_escape_analysis() && ConnectionGraph::has_candidates(this)) {
28482859
if (has_loops()) {
28492860
// Cleanup graph (remove dead nodes).
28502861
TracePhase tp(_t_idealLoop);
28512862
PhaseIdealLoop::optimize(igvn, LoopOptsMaxUnroll);
2852-
if (failing()) return;
2863+
if (failing()) {
2864+
return;
2865+
}
2866+
print_method(PHASE_PHASEIDEAL_BEFORE_EA, 2);
2867+
2868+
// Eliminate some macro nodes before EA to reduce analysis pressure
2869+
PhaseMacroExpand mexp(igvn);
2870+
mexp.eliminate_macro_nodes();
2871+
if (failing()) {
2872+
return;
2873+
}
2874+
igvn.set_delay_transform(false);
2875+
print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2);
28532876
}
2877+
28542878
bool progress;
2855-
print_method(PHASE_PHASEIDEAL_BEFORE_EA, 2);
28562879
do {
28572880
ConnectionGraph::do_analysis(this, &igvn);
28582881

@@ -2870,12 +2893,10 @@ void Compile::Optimize() {
28702893
TracePhase tp(_t_macroEliminate);
28712894
PhaseMacroExpand mexp(igvn);
28722895
mexp.eliminate_macro_nodes();
2873-
if (failing()) return;
2874-
2896+
if (failing()) {
2897+
return;
2898+
}
28752899
igvn.set_delay_transform(false);
2876-
igvn.optimize();
2877-
if (failing()) return;
2878-
28792900
print_method(PHASE_ITER_GVN_AFTER_ELIMINATION, 2);
28802901
}
28812902

@@ -2976,8 +2997,14 @@ void Compile::Optimize() {
29762997

29772998
{
29782999
TracePhase tp(_t_macroExpand);
3000+
PhaseMacroExpand mex(igvn);
3001+
// Last attempt to eliminate macro nodes.
3002+
mex.eliminate_macro_nodes();
3003+
if (failing()) {
3004+
return;
3005+
}
3006+
29793007
print_method(PHASE_BEFORE_MACRO_EXPANSION, 3);
2980-
PhaseMacroExpand mex(igvn);
29813008
if (mex.expand_macro_nodes()) {
29823009
assert(failing(), "must bail out w/ explicit message");
29833010
return;

src/hotspot/share/opto/doCall.cpp

Lines changed: 86 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "ci/ciCallSite.hpp"
2626
#include "ci/ciMethodHandle.hpp"
2727
#include "ci/ciSymbols.hpp"
28+
#include "classfile/vmIntrinsics.hpp"
2829
#include "classfile/vmSymbols.hpp"
2930
#include "compiler/compileBroker.hpp"
3031
#include "compiler/compileLog.hpp"
@@ -88,6 +89,64 @@ static void trace_type_profile(Compile* C, ciMethod* method, JVMState* jvms,
8889
}
8990
}
9091

92+
static bool arg_can_be_larval(ciMethod* callee, int arg_idx) {
93+
if (callee->is_object_constructor() && arg_idx == 0) {
94+
return true;
95+
}
96+
97+
if (arg_idx != 1 || callee->intrinsic_id() == vmIntrinsicID::_none) {
98+
return false;
99+
}
100+
101+
switch (callee->intrinsic_id()) {
102+
case vmIntrinsicID::_finishPrivateBuffer:
103+
case vmIntrinsicID::_putBoolean:
104+
case vmIntrinsicID::_putBooleanOpaque:
105+
case vmIntrinsicID::_putBooleanRelease:
106+
case vmIntrinsicID::_putBooleanVolatile:
107+
case vmIntrinsicID::_putByte:
108+
case vmIntrinsicID::_putByteOpaque:
109+
case vmIntrinsicID::_putByteRelease:
110+
case vmIntrinsicID::_putByteVolatile:
111+
case vmIntrinsicID::_putChar:
112+
case vmIntrinsicID::_putCharOpaque:
113+
case vmIntrinsicID::_putCharRelease:
114+
case vmIntrinsicID::_putCharUnaligned:
115+
case vmIntrinsicID::_putCharVolatile:
116+
case vmIntrinsicID::_putShort:
117+
case vmIntrinsicID::_putShortOpaque:
118+
case vmIntrinsicID::_putShortRelease:
119+
case vmIntrinsicID::_putShortUnaligned:
120+
case vmIntrinsicID::_putShortVolatile:
121+
case vmIntrinsicID::_putInt:
122+
case vmIntrinsicID::_putIntOpaque:
123+
case vmIntrinsicID::_putIntRelease:
124+
case vmIntrinsicID::_putIntUnaligned:
125+
case vmIntrinsicID::_putIntVolatile:
126+
case vmIntrinsicID::_putLong:
127+
case vmIntrinsicID::_putLongOpaque:
128+
case vmIntrinsicID::_putLongRelease:
129+
case vmIntrinsicID::_putLongUnaligned:
130+
case vmIntrinsicID::_putLongVolatile:
131+
case vmIntrinsicID::_putFloat:
132+
case vmIntrinsicID::_putFloatOpaque:
133+
case vmIntrinsicID::_putFloatRelease:
134+
case vmIntrinsicID::_putFloatVolatile:
135+
case vmIntrinsicID::_putDouble:
136+
case vmIntrinsicID::_putDoubleOpaque:
137+
case vmIntrinsicID::_putDoubleRelease:
138+
case vmIntrinsicID::_putDoubleVolatile:
139+
case vmIntrinsicID::_putReference:
140+
case vmIntrinsicID::_putReferenceOpaque:
141+
case vmIntrinsicID::_putReferenceRelease:
142+
case vmIntrinsicID::_putReferenceVolatile:
143+
case vmIntrinsicID::_putValue:
144+
return true;
145+
default:
146+
return false;
147+
}
148+
}
149+
91150
CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool call_does_dispatch,
92151
JVMState* jvms, bool allow_inline,
93152
float prof_factor, ciKlass* speculative_receiver_type,
@@ -645,6 +704,15 @@ void Parse::do_call() {
645704
set_stack(sp() - nargs, casted_receiver);
646705
}
647706

707+
// Scalarize value objects passed into this invocation if we know that they are not larval
708+
for (int arg_idx = 0; arg_idx < nargs; arg_idx++) {
709+
if (arg_can_be_larval(callee, arg_idx)) {
710+
continue;
711+
}
712+
713+
cast_to_non_larval(peek(nargs - 1 - arg_idx));
714+
}
715+
648716
// Note: It's OK to try to inline a virtual call.
649717
// The call generator will not attempt to inline a polymorphic call
650718
// unless it knows how to optimize the receiver dispatch.
@@ -807,59 +875,27 @@ void Parse::do_call() {
807875
if (is_reference_type(ct)) {
808876
record_profiled_return_for_speculation();
809877
}
810-
if (rtype->is_inlinetype() && !peek()->is_InlineType()) {
811-
Node* retnode = pop();
812-
retnode = InlineTypeNode::make_from_oop(this, retnode, rtype->as_inline_klass());
813-
push_node(T_OBJECT, retnode);
814-
}
815878

816-
// Note that:
817-
// - The caller map is the state just before the call of the currently parsed method with all arguments
818-
// on the stack. Therefore, we have caller_map->arg(0) == this.
819-
// - local(0) contains the updated receiver after calling an inline type constructor.
820-
// - Abstract value classes are not ciInlineKlass instances and thus abstract_value_klass->is_inlinetype() is false.
821-
// We use the bottom type of the receiver node to determine if we have a value class or not.
822-
const bool is_current_method_inline_type_constructor =
823-
// Is current method a constructor (i.e <init>)?
824-
_method->is_object_constructor() &&
825-
// Is the holder of the current constructor method an inline type?
826-
_caller->map()->argument(_caller, 0)->bottom_type()->is_inlinetypeptr();
827-
assert(!is_current_method_inline_type_constructor || !cg->method()->is_object_constructor() || receiver != nullptr,
828-
"must have valid receiver after calling another constructor");
829-
if (is_current_method_inline_type_constructor &&
830-
// Is the just called method an inline type constructor?
831-
cg->method()->is_object_constructor() && receiver->bottom_type()->is_inlinetypeptr() &&
832-
// AND:
833-
// 1) ... invoked on the same receiver? Then it's another constructor on the same object doing the initialization.
834-
(receiver == _caller->map()->argument(_caller, 0) ||
835-
// 2) ... abstract? Then it's the call to the super constructor which eventually calls Object.<init> to
836-
// finish the initialization of this larval.
837-
cg->method()->holder()->is_abstract() ||
838-
// 3) ... Object.<init>? Then we know it's the final call to finish the larval initialization. Other
839-
// Object.<init> calls would have a non-inline-type receiver which we already excluded in the check above.
840-
cg->method()->holder()->is_java_lang_Object())
841-
) {
842-
assert(local(0)->is_InlineType() && receiver->bottom_type()->is_inlinetypeptr() && receiver->is_InlineType() &&
843-
_caller->map()->argument(_caller, 0)->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(),
844-
"Unexpected receiver");
845-
InlineTypeNode* updated_receiver = local(0)->as_InlineType();
846-
InlineTypeNode* cloned_updated_receiver = updated_receiver->clone_if_required(&_gvn, _map);
847-
cloned_updated_receiver->set_is_larval(false);
848-
cloned_updated_receiver = _gvn.transform(cloned_updated_receiver)->as_InlineType();
849-
// Receiver updated by the just called constructor. We need to update the map to make the effect visible. After
850-
// the super() call, only the updated receiver in local(0) will be used from now on. Therefore, we do not need
851-
// to update the original receiver 'receiver' but only the 'updated_receiver'.
852-
replace_in_map(updated_receiver, cloned_updated_receiver);
853-
854-
if (_caller->has_method()) {
855-
// If the current method is inlined, we also need to update the exit map to propagate the updated receiver
856-
// to the caller map.
857-
Node* receiver_in_caller = _caller->map()->argument(_caller, 0);
858-
assert(receiver_in_caller->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(),
859-
"Receiver type mismatch");
860-
_exits.map()->replace_edge(receiver_in_caller, cloned_updated_receiver, &_gvn);
879+
if (!rtype->is_void() && cg->method()->intrinsic_id() != vmIntrinsicID::_makePrivateBuffer) {
880+
Node* retnode = peek();
881+
const Type* rettype = gvn().type(retnode);
882+
if (rettype->is_inlinetypeptr() && !retnode->is_InlineType()) {
883+
retnode = InlineTypeNode::make_from_oop(this, retnode, rettype->inline_klass());
884+
dec_sp(1);
885+
push(retnode);
861886
}
862887
}
888+
889+
if (cg->method()->is_object_constructor() && receiver != nullptr && gvn().type(receiver)->is_inlinetypeptr()) {
890+
InlineTypeNode* non_larval = InlineTypeNode::make_from_oop(this, receiver, gvn().type(receiver)->inline_klass());
891+
// Relinquish the oop input, we will delay the allocation to the point it is needed, see the
892+
// comments in InlineTypeNode::Ideal for more details
893+
non_larval = non_larval->clone_if_required(&gvn(), nullptr);
894+
non_larval->set_oop(gvn(), null());
895+
non_larval->set_is_buffered(gvn(), false);
896+
non_larval = gvn().transform(non_larval)->as_InlineType();
897+
map()->replace_edge(receiver, non_larval);
898+
}
863899
}
864900

865901
// Restart record of parsing work after possible inlining of call

src/hotspot/share/opto/graphKit.cpp

Lines changed: 19 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -990,14 +990,7 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
990990
out_jvms->set_locoff(p);
991991
if (!can_prune_locals) {
992992
for (j = 0; j < l; j++) {
993-
Node* val = in_map->in(k + j);
994-
// Check if there's a larval that has been written in the callee state (constructor) and update it in the caller state
995-
if (callee_jvms != nullptr && val->is_InlineType() && val->as_InlineType()->is_larval() &&
996-
callee_jvms->method()->is_object_constructor() && val == in_map->argument(in_jvms, 0) &&
997-
val->bottom_type()->is_inlinetypeptr()) {
998-
val = callee_jvms->map()->local(callee_jvms, 0); // Receiver
999-
}
1000-
call->set_req(p++, val);
993+
call->set_req(p++, in_map->in(k + j));
1001994
}
1002995
} else {
1003996
p += l; // already set to top above by add_req_batch
@@ -1009,14 +1002,7 @@ void GraphKit::add_safepoint_edges(SafePointNode* call, bool must_throw) {
10091002
out_jvms->set_stkoff(p);
10101003
if (!can_prune_locals) {
10111004
for (j = 0; j < l; j++) {
1012-
Node* val = in_map->in(k + j);
1013-
// Check if there's a larval that has been written in the callee state (constructor) and update it in the caller state
1014-
if (callee_jvms != nullptr && val->is_InlineType() && val->as_InlineType()->is_larval() &&
1015-
callee_jvms->method()->is_object_constructor() && val == in_map->argument(in_jvms, 0) &&
1016-
val->bottom_type()->is_inlinetypeptr()) {
1017-
val = callee_jvms->map()->local(callee_jvms, 0); // Receiver
1018-
}
1019-
call->set_req(p++, val);
1005+
call->set_req(p++, in_map->in(k + j));
10201006
}
10211007
} else if (can_prune_locals && stack_slots_not_pruned != 0) {
10221008
// Divide stack into {S0,...,S1}, where S0 is set to top.
@@ -1515,6 +1501,17 @@ Node* GraphKit::cast_not_null(Node* obj, bool do_replace_in_map) {
15151501
return cast; // Return casted value
15161502
}
15171503

1504+
Node* GraphKit::cast_to_non_larval(Node* obj) {
1505+
const Type* obj_type = gvn().type(obj);
1506+
if (obj->is_InlineType() || !obj_type->is_inlinetypeptr()) {
1507+
return obj;
1508+
}
1509+
1510+
Node* new_obj = InlineTypeNode::make_from_oop(this, obj, obj_type->inline_klass());
1511+
replace_in_map(obj, new_obj);
1512+
return new_obj;
1513+
}
1514+
15181515
// Sometimes in intrinsics, we implicitly know an object is not null
15191516
// (there's no actual null check) so we can cast it to not null. In
15201517
// the course of optimizations, the input to the cast can become null.
@@ -1920,28 +1917,7 @@ void GraphKit::set_arguments_for_java_call(CallJavaNode* call, bool is_late_inli
19201917
continue;
19211918
} else if (arg->is_InlineType()) {
19221919
// Pass inline type argument via oop to callee
1923-
InlineTypeNode* inline_type = arg->as_InlineType();
1924-
const ciMethod* method = call->method();
1925-
ciInstanceKlass* holder = method->holder();
1926-
const bool is_receiver = (i == TypeFunc::Parms);
1927-
const bool is_abstract_or_object_klass_constructor = method->is_object_constructor() &&
1928-
(holder->is_abstract() || holder->is_java_lang_Object());
1929-
const bool is_larval_receiver_on_super_constructor = is_receiver && is_abstract_or_object_klass_constructor;
1930-
bool must_init_buffer = true;
1931-
// We always need to buffer inline types when they are escaping. However, we can skip the actual initialization
1932-
// of the buffer if the inline type is a larval because we are going to update the buffer anyway which requires
1933-
// us to create a new one. But there is one special case where we are still required to initialize the buffer:
1934-
// When we have a larval receiver invoked on an abstract (value class) constructor or the Object constructor (that
1935-
// is not going to be inlined). After this call, the larval is completely initialized and thus not a larval anymore.
1936-
// We therefore need to force an initialization of the buffer to not lose all the field writes so far in case the
1937-
// buffer needs to be used (e.g. to read from when deoptimizing at runtime) or further updated in abstract super
1938-
// value class constructors which could have more fields to be initialized. Note that we do not need to
1939-
// initialize the buffer when invoking another constructor in the same class on a larval receiver because we
1940-
// have not initialized any fields, yet (this is done completely by the other constructor call).
1941-
if (inline_type->is_larval() && !is_larval_receiver_on_super_constructor) {
1942-
must_init_buffer = false;
1943-
}
1944-
arg = inline_type->buffer(this, true, must_init_buffer);
1920+
arg = arg->as_InlineType()->buffer(this, true);
19451921
}
19461922
if (t != Type::HALF) {
19471923
arg_num++;
@@ -2018,20 +1994,6 @@ Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_p
20181994
}
20191995
}
20201996

2021-
// We just called the constructor on a value type receiver. Reload it from the buffer
2022-
ciMethod* method = call->method();
2023-
if (method->is_object_constructor() && !method->holder()->is_java_lang_Object()) {
2024-
InlineTypeNode* inline_type_receiver = call->in(TypeFunc::Parms)->isa_InlineType();
2025-
if (inline_type_receiver != nullptr) {
2026-
assert(inline_type_receiver->is_larval(), "must be larval");
2027-
assert(inline_type_receiver->is_allocated(&gvn()), "larval must be buffered");
2028-
InlineTypeNode* reloaded = InlineTypeNode::make_from_oop(this, inline_type_receiver->get_oop(),
2029-
inline_type_receiver->bottom_type()->inline_klass());
2030-
assert(!reloaded->is_larval(), "should not be larval anymore");
2031-
replace_in_map(inline_type_receiver, reloaded);
2032-
}
2033-
}
2034-
20351997
return ret;
20361998
}
20371999

@@ -3458,7 +3420,7 @@ Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replac
34583420
// If failure_control is supplied and not null, it is filled in with
34593421
// the control edge for the cast failure. Otherwise, an appropriate
34603422
// uncommon trap or exception is thrown.
3461-
Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node* *failure_control, bool null_free) {
3423+
Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node* *failure_control, bool null_free, bool maybe_larval) {
34623424
kill_dead_locals(); // Benefit all the uncommon traps
34633425
const TypeKlassPtr* klass_ptr_type = _gvn.type(superklass)->is_klassptr();
34643426
const Type* obj_type = _gvn.type(obj);
@@ -3474,6 +3436,9 @@ Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node* *failure_contro
34743436
return obj;
34753437
}
34763438

3439+
// Else it must be a non-larval object
3440+
obj = cast_to_non_larval(obj);
3441+
34773442
const TypeKlassPtr* improved_klass_ptr_type = klass_ptr_type->try_improve();
34783443
const TypeOopPtr* toop = improved_klass_ptr_type->cast_to_exactness(false)->as_instance_type();
34793444
bool safe_for_replace = (failure_control == nullptr);
@@ -3704,7 +3669,7 @@ Node* GraphKit::gen_checkcast(Node* obj, Node* superklass, Node* *failure_contro
37043669

37053670
if (!stopped() && !res->is_InlineType()) {
37063671
res = record_profiled_receiver_for_speculation(res);
3707-
if (toop->is_inlinetypeptr()) {
3672+
if (toop->is_inlinetypeptr() && !maybe_larval) {
37083673
Node* vt = InlineTypeNode::make_from_oop(this, res, toop->inline_klass());
37093674
res = vt;
37103675
if (safe_for_replace) {

0 commit comments

Comments
 (0)