Skip to content

Commit c9de912

Browse files
navyxliuXin Liu
andauthored
JVM-1738: Incorrect jvms when PEA materializes an object in merge_common (openjdk#7)
* JVM-1738: Incorrect jvms when PEA materializes an object in merge_common This patch copys all debuginfo nodes from the original AllocateNode and clones its JVMState as well. We deploy a uncommon_trap rather than generating the exception for real. That shouluhd resolve JVM-1730. The reason we give up the idea to use add_safepoint_edges() is that GraphKit's bci may point to an arbitary bytecode and sp even doesn't match its current bci. The another benefit is that it can mimic the original AllocateNode with the exactly same debuginfo and JVMState. If OOME does emerge, the stacktrace is as if we doesn't move AllocateNode ever. * Update passive materialization. * Add a test for JVM-1738 --------- Co-authored-by: Xin Liu <xxinliu@amazon.com>
1 parent 2f425d2 commit c9de912

File tree

9 files changed

+124
-42
lines changed

9 files changed

+124
-42
lines changed

PEA/CrazyException.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
class CrazyException {
2+
void blackhole() {}
3+
public static Object foo(boolean cond) {
4+
var x = new CrazyException();
5+
for (int i=0; i< 100; ++i) {
6+
if (cond) {
7+
try {
8+
x.blackhole(); // not inline. escape here.
9+
} finally { // hidden catch(Throwable) is here!
10+
close();
11+
}
12+
}
13+
}
14+
return x;
15+
}
16+
public static void close() {}
17+
public static void main(String[] args) {
18+
int iterations = 0;
19+
try {
20+
while (iterations < 20_000) {
21+
CrazyException.foo(0 == (iterations & 0xf));
22+
iterations++;
23+
}
24+
} finally {
25+
System.err.println("Epsilon Test: " + iterations);
26+
}
27+
}
28+
}

PEA/auto.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ JVM_FLAGS="-XX:+PrintEscapeAnalysis -XX:+PrintEliminateAllocations -XX:+PrintOpt
1212
./run3_2.sh -Xlog:gc -XX:+DoPartialEscapeAnalysis $JVM_FLAGS
1313
./run3.sh -Xlog:gc -XX:+DoPartialEscapeAnalysis $JVM_FLAGS
1414
./run_str.sh -Xlog:gc -XX:+DoPartialEscapeAnalysis -XX:-UseTLAB $JVM_FLAGS
15+
./run_exception.sh -Xlog:gc -XX:+DoPartialEscapeAnalysis $JVM_FLAGS

PEA/run_exception.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/usr/bin/bash
2+
java -Xms16M -Xmx16M -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:-TieredCompilation -XX:+UseEpsilonGC -XX:-UseOnStackReplacement -XX:CompileCommand=dontinline,CrazyException.blackhole -XX:CompileCommand=compileonly,CrazyException.foo -XX:CompileCommand=dontinline,CrazyException.close $* CrazyException

src/hotspot/share/opto/doCall.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ void Parse::do_call() {
668668
AllocateNode* alloc = state.is_alias(arg);
669669

670670
if (alloc != nullptr && state.get_object_state(alloc)->is_virtual()) {
671-
EscapedState* es = state.materialize(this, arg, map());
671+
EscapedState* es = state.materialize(this, arg);
672672
Node* objx = es->get_materialized_value();
673673
set_argument(i, objx);
674674
AllocateNode* allocx = objx->in(1)->in(0)->as_Allocate();

src/hotspot/share/opto/graphKit.cpp

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3533,14 +3533,10 @@ static void hook_memory_on_init(GraphKit& kit, int alias_idx,
35333533
init_in_merge->set_memory_at(alias_idx, prevmem);
35343534
kit.set_memory(init_out_raw, alias_idx);
35353535
}
3536-
3537-
//---------------------------set_output_for_allocation-------------------------
3538-
Node* GraphKit::set_output_for_allocation(AllocateNode* alloc,
3536+
Node* GraphKit::set_output_for_allocation_common(AllocateNode* alloc,
35393537
const TypeOopPtr* oop_type,
35403538
bool deoptimize_on_exception) {
35413539
int rawidx = Compile::AliasIdxRaw;
3542-
alloc->set_req( TypeFunc::FramePtr, frameptr() );
3543-
add_safepoint_edges(alloc);
35443540
Node* allocx = _gvn.transform(alloc);
35453541
set_control( _gvn.transform(new ProjNode(allocx, TypeFunc::Control) ) );
35463542
// create memory projection for i_o
@@ -3619,6 +3615,43 @@ Node* GraphKit::set_output_for_allocation(AllocateNode* alloc,
36193615
return javaoop;
36203616
}
36213617

3618+
//---------------------------set_output_for_allocation-------------------------
3619+
Node* GraphKit::set_output_for_allocation(AllocateNode* alloc,
3620+
const TypeOopPtr* oop_type,
3621+
bool deoptimize_on_exception) {
3622+
alloc->set_req( TypeFunc::FramePtr, frameptr() );
3623+
add_safepoint_edges(alloc);
3624+
return set_output_for_allocation_common(alloc, oop_type, deoptimize_on_exception);
3625+
}
3626+
3627+
3628+
Node* GraphKit::materialize_object(AllocateNode* alloc, const TypeOopPtr* oop_type) {
3629+
Node *mem = reset_memory();
3630+
set_all_memory(mem); // Create new memory state
3631+
AllocateNode* allocx = new AllocateNode(C, alloc->tf(), control(), mem, i_o(),
3632+
alloc->in(AllocateNode::AllocSize),
3633+
alloc->in(AllocateNode::KlassNode),
3634+
alloc->in(AllocateNode::InitialTest));
3635+
3636+
allocx->set_req(TypeFunc::FramePtr, frameptr());
3637+
3638+
JVMState* out_jvms = alloc->jvms()->clone_deep(C);
3639+
allocx->set_jvms(out_jvms);
3640+
// same safepoint as original alloc
3641+
for (uint i=allocx->req(); i < alloc->req(); ++i) {
3642+
allocx->add_req(alloc->in(i));
3643+
}
3644+
3645+
while (out_jvms != nullptr) {
3646+
out_jvms->set_map(allocx);
3647+
out_jvms = out_jvms->caller();
3648+
}
3649+
int saved_bci = bci();
3650+
set_bci(alloc->jvms()->bci()); // reset to bytecode new because uncommon_trap checks bc() and sp
3651+
Node* objx = set_output_for_allocation_common(allocx, oop_type, true /*deoptimize_on_ex*/);
3652+
set_bci(saved_bci);
3653+
return objx;
3654+
}
36223655
//---------------------------new_instance--------------------------------------
36233656
// This routine takes a klass_node which may be constant (for a static type)
36243657
// or may be non-constant (for reflective code). It will work equally well

src/hotspot/share/opto/graphKit.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,10 +850,17 @@ class GraphKit : public Phase {
850850
Node* subtype_check_receiver(Node* receiver, ciKlass* klass,
851851
Node** casted_receiver);
852852

853+
Node* set_output_for_allocation_common(AllocateNode* alloc,
854+
const TypeOopPtr* oop_type,
855+
bool deoptimize_on_exception);
856+
853857
// implementation of object creation
854858
Node* set_output_for_allocation(AllocateNode* alloc,
855859
const TypeOopPtr* oop_type,
856860
bool deoptimize_on_exception=false);
861+
862+
Node* materialize_object(AllocateNode* alloc, const TypeOopPtr* oop_type);
863+
857864
Node* get_layout_helper(Node* klass_node, jint& constant_value);
858865
Node* new_instance(Node* klass_node,
859866
Node* slow_test = NULL,

src/hotspot/share/opto/parse1.cpp

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1781,7 +1781,8 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
17811781
// Update all the non-control inputs to map:
17821782
assert(TypeFunc::Parms == newin->jvms()->locoff(), "parser map should contain only youngest jvms");
17831783
bool check_elide_phi = target->is_SEL_backedge(save_block);
1784-
// from right to left, we update I_O and memory last because PEA materialization may change them.
1784+
// from right to left, we update ctrl, I_O and memory til the last minute
1785+
// because PEA materialization may change them.
17851786
for (uint j = newin->req()-1; j >= 1; --j) {
17861787
Node* m = map()->in(j); // Current state of target.
17871788
Node* n = newin->in(j); // Incoming change to target state.
@@ -1829,10 +1830,8 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
18291830
if (!pred_os->is_virtual()) {
18301831
// materialize 'm' because it has been materialized in save_block.
18311832
assert(pred_os->get_materialized_value() == n, "sanity: materialized value in save_block is n");
1832-
SafePointNode* map = block()->from_block()->start_map();
1833-
// object materialization takes place in from_block. It is the initial block that the current block(target) merged.
1834-
// from block isn't save_block! see Block::record_state().
1835-
Node* mv = ensure_object_materialized(m, map->jvms()->alloc_state(), map, r, block()->init_pnum());
1833+
as.add_alias(id, m);
1834+
Node* mv = ensure_object_materialized(m, as, map(), r, block()->init_pnum());
18361835
phi->replace_edge(m, mv);
18371836
as.update(id, new EscapedState(phi));
18381837
} else {
@@ -2174,16 +2173,50 @@ PhiNode *Parse::ensure_memory_phi(int idx, bool nocreate) {
21742173
return phi;
21752174
}
21762175

2176+
// Passive Materialization
2177+
// ------------------------
2178+
// C2 has to materialize a virtual object at the merging point because the object has been materialized in
2179+
// any other predecessor. For instance:
2180+
//
2181+
// obj(virtual) obj(materialized)
2182+
// \ /
2183+
// Reion(,c1,c2) | |
2184+
// \ | |
2185+
// \c | |
2186+
// o3 = Phi (o1, o2)
2187+
//
2188+
// Node* var is o1;
2189+
// r[pnum] is c1;
2190+
//
2191+
// There are 2 cases:
2192+
// 1) materialize m from map().
2193+
// 2) materialize n from newin.
2194+
// so from_map is either from current map or newin of merge_common().
2195+
// for case 1, the control of from_map is Region, so we need to adjust it to c1.
2196+
//
2197+
// Regarding compile-time state, We also need to consider ABIO(in(1)) and Memory(in(2)) of from_map.
2198+
// we reverse the iteration order in merge_common(). For the time being, we haven't merged ABIO and Memory yet.
2199+
// I.e. we use (Ctrl, ABIO, Memory) of from_map before merging.
2200+
//
21772201
Node* Parse::ensure_object_materialized(Node* var, PEAState& state, SafePointNode* from_map, RegionNode* r, int pnum) {
2178-
assert(map() != from_map, "should be different");
2179-
21802202
GraphKit kit(from_map->jvms());
2181-
kit.set_control(r->in(pnum));
2182-
EscapedState* es = state.materialize(&kit, var, from_map);
2183-
add_exception_states_from(kit.transfer_exceptions_into_jvms());
2203+
bool update_ctrl = from_map == map();
2204+
2205+
if (update_ctrl) {
2206+
assert(kit.control() == r, "sanity");
2207+
kit.set_control(r->in(pnum));
2208+
} else {
2209+
assert(kit.control() == r->in(pnum), "sanity");
2210+
}
2211+
2212+
EscapedState* es = state.materialize(&kit, var);
21842213
Node* mv = es->get_materialized_value();
2214+
assert(kit.control() == mv->in(0), "sanity");
21852215
r->set_req(pnum, mv->in(0));
2186-
do_exceptions();
2216+
2217+
if (update_ctrl) {
2218+
kit.set_control(r);
2219+
}
21872220
return mv;
21882221
}
21892222

src/hotspot/share/opto/parseHelper.cpp

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -456,39 +456,17 @@ static void replace_in_map(GraphKit* kit, Node* old, Node* neww) {
456456
}
457457
}
458458

459-
EscapedState* PEAState::materialize(GraphKit* kit, Node* var, SafePointNode* map) {
459+
EscapedState* PEAState::materialize(GraphKit* kit, Node* var) {
460460
ObjID alloc = is_alias(var);
461461
assert(alloc != nullptr && get_object_state(alloc)->is_virtual(), "sanity check");
462462
#ifndef PRODUCT
463463
if (Verbose) {
464464
tty->print_cr("PEA materializes a virtual object: %d", alloc->_idx);
465465
}
466466
#endif
467-
468-
if (map == nullptr) {
469-
JVMState* jvms = kit->sync_jvms();
470-
map = kit->map();
471-
472-
kit->kill_dead_locals();
473-
kit->clean_stack(jvms->sp());
474-
jvms->set_should_reexecute(false);
475-
}
476-
477467
Compile* C = kit->C;
478-
// The entire memory state is needed for slow path of the allocation
479-
// since GC and deoptimization can happened.
480-
Node *mem = kit->reset_memory();
481-
kit->set_all_memory(mem); // Create new memory state
482-
483-
// clean up map/jvms before we clone a new AllocateNode.
484-
AllocateNode* allocx = new AllocateNode(C, alloc->tf(), map->control(), mem, map->i_o(),
485-
alloc->in(AllocateNode::AllocSize),
486-
alloc->in(AllocateNode::KlassNode),
487-
alloc->in(AllocateNode::InitialTest));
488-
489468
const TypeOopPtr* oop_type = var->as_Type()->type()->is_oopptr();
490-
Node* objx = kit->set_output_for_allocation(allocx, oop_type);
491-
allocx->jvms()->set_bci(alloc->jvms()->bci());
469+
Node* objx = kit->materialize_object(alloc, oop_type);
492470
VirtualState* virt = static_cast<VirtualState*>(get_object_state(alloc));
493471

494472
if (oop_type->isa_instptr()) {

src/hotspot/share/opto/partialEscape.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class PEAState {
145145
void remove_alias(ObjID id, Node* var);
146146

147147
void add_new_allocation(Node* obj);
148-
EscapedState* materialize(GraphKit* kit, Node* var, SafePointNode* map = nullptr);
148+
EscapedState* materialize(GraphKit* kit, Node* var);
149149

150150
#ifndef PRODUCT
151151
void print_on(outputStream* os) const;

0 commit comments

Comments
 (0)