Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8273323: [lworld] Fix post-parse call devirtualization with inline type receiver #549

Closed
wants to merge 2 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -2463,7 +2463,10 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) {
// Tracking and verification of modified nodes is disabled by setting "_modified_nodes == NULL"
// as if "inlining_incrementally() == true" were set.
assert(inlining_incrementally() == false, "not allowed");
assert(_modified_nodes == NULL, "not allowed");
#ifdef ASSERT
Unique_Node_List* modified_nodes = _modified_nodes;
_modified_nodes = NULL;
#endif
assert(_late_inlines.length() > 0, "sanity");

while (_late_inlines.length() > 0) {
@@ -2477,6 +2480,7 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) {

inline_incrementally_cleanup(igvn);
}
DEBUG_ONLY( _modified_nodes = modified_nodes; )
}

bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) {
@@ -2730,6 +2734,14 @@ void Compile::Optimize() {
// Process inline type nodes again after loop opts
process_inline_types(igvn);

assert(_late_inlines.length() == 0 || IncrementalInlineMH || IncrementalInlineVirtual, "not empty");

if (_late_inlines.length() > 0) {
// More opportunities to optimize virtual and MH calls.
// Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option.
process_late_inline_calls_no_inline(igvn);
}

{
TracePhase tp("macroExpand", &timers[_t_macroExpand]);
PhaseMacroExpand mex(igvn);
@@ -2761,14 +2773,7 @@ void Compile::Optimize() {
DEBUG_ONLY( _modified_nodes = NULL; )

assert(igvn._worklist.size() == 0, "not empty");

assert(_late_inlines.length() == 0 || IncrementalInlineMH || IncrementalInlineVirtual, "not empty");

if (_late_inlines.length() > 0) {
// More opportunities to optimize virtual and MH calls.
// Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option.
process_late_inline_calls_no_inline(igvn);
}
assert(_late_inlines.length() == 0, "missed optimization opportunity");
} // (End scope of igvn; run destructor if necessary for asserts.)

check_no_dead_use();
@@ -673,10 +673,11 @@ Node* InlineTypeNode::make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk,
PhaseGVN& gvn = kit->gvn();

if (vk->is_empty()) {
InlineTypeNode* def = make_default(gvn, vk);
Node* def = make_default(gvn, vk);
if (!null_free) {
return gvn.transform(new InlineTypePtrNode(def, false));
def = gvn.transform(new InlineTypePtrNode(def->as_InlineType(), false));
}
kit->record_for_igvn(def);
return def;
}
// Create and initialize an InlineTypeNode by loading all field
@@ -693,6 +694,7 @@ Node* InlineTypeNode::make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk,
for (uint i = Values; i < vtptr->req(); ++i) {
vt->init_req(i, vtptr->in(i));
}
kit->record_for_igvn(vt);
return gvn.transform(vt);
} else if (gvn.type(oop)->maybe_null()) {
// Add a null check because the oop may be null
@@ -702,10 +704,12 @@ Node* InlineTypeNode::make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk,
// Constant null
kit->set_control(null_ctl);
if (null_free) {
return make_default(gvn, vk);
vt = make_default(gvn, vk);
} else {
return InlineTypePtrNode::make_null(gvn, vk);
vt = InlineTypePtrNode::make_null(gvn, vk);
}
kit->record_for_igvn(vt);
return vt;
}
if (null_free) {
vt = new InlineTypeNode(vk, not_null_oop);
@@ -744,6 +748,7 @@ Node* InlineTypeNode::make_from_oop(GraphKit* kit, Node* oop, ciInlineKlass* vk,
AllocateNode::Ideal_allocation(oop, &gvn) != NULL || vt->as_InlineType()->is_loaded(&gvn) == oop, "inline type should be loaded");
}
assert(!null_free || vt->is_allocated(&gvn), "inline type should be allocated");
kit->record_for_igvn(vt);
return gvn.transform(vt);
}

@@ -2037,7 +2037,9 @@ Node *PhaseCCP::transform_once( Node *n ) {

// TEMPORARY fix to ensure that 2nd GVN pass eliminates NULL checks
switch( n->Opcode() ) {
case Op_FastLock: // Revisit FastLocks for lock coarsening
case Op_CallStaticJava: // Give post-parse call devirtualization a chance
case Op_CallDynamicJava:
case Op_FastLock: // Revisit FastLocks for lock coarsening
case Op_If:
case Op_CountedLoopEnd:
case Op_Region:
@@ -115,6 +115,8 @@ public class IRNode {
public static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;

public static final String CALL = START + "CallStaticJava" + MID + END;
public static final String DYNAMIC_CALL_OF_METHOD = COMPOSITE_PREFIX + START + "CallDynamicJava" + MID + IS_REPLACED + END;
public static final String STATIC_CALL_OF_METHOD = COMPOSITE_PREFIX + START + "CallStaticJava" + MID + IS_REPLACED + END;
public static final String TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*reason" + END;
public static final String PREDICATE_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*predicate" + END;
public static final String UNSTABLE_IF_TRAP = START + "CallStaticJava" + MID + "uncommon_trap.*unstable_if" + END;
@@ -4093,4 +4093,52 @@ public void test149_verifier() {
Asserts.assertEQ(test149(testValue1), testValue1);
Asserts.assertEQ(test149(null), null);
}

// Test post-parse call devirtualization with inline type receiver
@Test
@IR(applyIf = {"InlineTypePassFieldsAsArgs", "true"},
failOn = {ALLOC})
@IR(failOn = {compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD, "MyValue2::hash"},
counts = {compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD, "MyValue2::hash", "= 1"})
public long test150() {
MyValue2 val = MyValue2.createWithFieldsInline(rI, rD);
MyInterface receiver = MyValue1.createWithFieldsInline(rI, rL);

for (int i = 0; i < 4; i++) {
if ((i % 2) == 0) {
receiver = val;
}
}
// Trigger post parse call devirtualization (strength-reducing
// virtual calls to direct calls).
return receiver.hash();
}

@Run(test = "test150")
public void test150_verifier() {
Asserts.assertEquals(test150(), testValue2.hash());
}

// Same as test150 but with val not being allocated in the scope of the method
@Test
@IR(failOn = {compiler.lib.ir_framework.IRNode.DYNAMIC_CALL_OF_METHOD, "MyValue2::hash"},
counts = {compiler.lib.ir_framework.IRNode.STATIC_CALL_OF_METHOD, "MyValue2::hash", "= 1"})
public long test151(MyValue2 val) {
MyAbstract receiver = MyValue1.createWithFieldsInline(rI, rL);

for (int i = 0; i < 100; i++) {
if ((i % 2) == 0) {
receiver = val;
}
}
// Trigger post parse call devirtualization (strength-reducing
// virtual calls to direct calls).
return receiver.hash();
}

@Run(test = "test151")
@Warmup(0) // Make sure there is no receiver type profile
public void test151_verifier() {
Asserts.assertEquals(test151(testValue2), testValue2.hash());
}
}