in src/hotspot/share/ci/bcEscapeAnalyzer.cpp [370:1023]
void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, GrowableArray<ciBlock *> &successors) {
blk->set_processed();
ciBytecodeStream s(method());
int limit_bci = blk->limit_bci();
bool fall_through = false;
ArgumentMap allocated_obj;
allocated_obj.add_allocated();
ArgumentMap unknown_obj;
unknown_obj.add_unknown();
ArgumentMap empty_map;
s.reset_to_bci(blk->start_bci());
while (s.next() != ciBytecodeStream::EOBC() && s.cur_bci() < limit_bci) {
fall_through = true;
switch (s.cur_bc()) {
case Bytecodes::_nop:
break;
case Bytecodes::_aconst_null:
state.apush(unknown_obj);
break;
case Bytecodes::_iconst_m1:
case Bytecodes::_iconst_0:
case Bytecodes::_iconst_1:
case Bytecodes::_iconst_2:
case Bytecodes::_iconst_3:
case Bytecodes::_iconst_4:
case Bytecodes::_iconst_5:
case Bytecodes::_fconst_0:
case Bytecodes::_fconst_1:
case Bytecodes::_fconst_2:
case Bytecodes::_bipush:
case Bytecodes::_sipush:
state.spush();
break;
case Bytecodes::_lconst_0:
case Bytecodes::_lconst_1:
case Bytecodes::_dconst_0:
case Bytecodes::_dconst_1:
state.lpush();
break;
case Bytecodes::_ldc:
case Bytecodes::_ldc_w:
case Bytecodes::_ldc2_w:
{
// Avoid calling get_constant() which will try to allocate
// unloaded constant. We need only constant's type.
int index = s.get_constant_pool_index();
BasicType con_bt = s.get_basic_type_for_constant_at(index);
if (con_bt == T_LONG || con_bt == T_DOUBLE) {
// Only longs and doubles use 2 stack slots.
state.lpush();
} else if (con_bt == T_OBJECT) {
state.apush(unknown_obj);
} else {
state.spush();
}
break;
}
case Bytecodes::_aload:
state.apush(state._vars[s.get_index()]);
break;
case Bytecodes::_iload:
case Bytecodes::_fload:
case Bytecodes::_iload_0:
case Bytecodes::_iload_1:
case Bytecodes::_iload_2:
case Bytecodes::_iload_3:
case Bytecodes::_fload_0:
case Bytecodes::_fload_1:
case Bytecodes::_fload_2:
case Bytecodes::_fload_3:
state.spush();
break;
case Bytecodes::_lload:
case Bytecodes::_dload:
case Bytecodes::_lload_0:
case Bytecodes::_lload_1:
case Bytecodes::_lload_2:
case Bytecodes::_lload_3:
case Bytecodes::_dload_0:
case Bytecodes::_dload_1:
case Bytecodes::_dload_2:
case Bytecodes::_dload_3:
state.lpush();
break;
case Bytecodes::_aload_0:
state.apush(state._vars[0]);
break;
case Bytecodes::_aload_1:
state.apush(state._vars[1]);
break;
case Bytecodes::_aload_2:
state.apush(state._vars[2]);
break;
case Bytecodes::_aload_3:
state.apush(state._vars[3]);
break;
case Bytecodes::_iaload:
case Bytecodes::_faload:
case Bytecodes::_baload:
case Bytecodes::_caload:
case Bytecodes::_saload:
state.spop();
set_method_escape(state.apop());
state.spush();
break;
case Bytecodes::_laload:
case Bytecodes::_daload:
state.spop();
set_method_escape(state.apop());
state.lpush();
break;
case Bytecodes::_aaload:
{ state.spop();
ArgumentMap array = state.apop();
set_method_escape(array);
state.apush(unknown_obj);
}
break;
case Bytecodes::_istore:
case Bytecodes::_fstore:
case Bytecodes::_istore_0:
case Bytecodes::_istore_1:
case Bytecodes::_istore_2:
case Bytecodes::_istore_3:
case Bytecodes::_fstore_0:
case Bytecodes::_fstore_1:
case Bytecodes::_fstore_2:
case Bytecodes::_fstore_3:
state.spop();
break;
case Bytecodes::_lstore:
case Bytecodes::_dstore:
case Bytecodes::_lstore_0:
case Bytecodes::_lstore_1:
case Bytecodes::_lstore_2:
case Bytecodes::_lstore_3:
case Bytecodes::_dstore_0:
case Bytecodes::_dstore_1:
case Bytecodes::_dstore_2:
case Bytecodes::_dstore_3:
state.lpop();
break;
case Bytecodes::_astore:
state._vars[s.get_index()] = state.apop();
break;
case Bytecodes::_astore_0:
state._vars[0] = state.apop();
break;
case Bytecodes::_astore_1:
state._vars[1] = state.apop();
break;
case Bytecodes::_astore_2:
state._vars[2] = state.apop();
break;
case Bytecodes::_astore_3:
state._vars[3] = state.apop();
break;
case Bytecodes::_iastore:
case Bytecodes::_fastore:
case Bytecodes::_bastore:
case Bytecodes::_castore:
case Bytecodes::_sastore:
{
state.spop();
state.spop();
ArgumentMap arr = state.apop();
set_method_escape(arr);
set_modified(arr, OFFSET_ANY, type2size[T_INT]*HeapWordSize);
break;
}
case Bytecodes::_lastore:
case Bytecodes::_dastore:
{
state.lpop();
state.spop();
ArgumentMap arr = state.apop();
set_method_escape(arr);
set_modified(arr, OFFSET_ANY, type2size[T_LONG]*HeapWordSize);
break;
}
case Bytecodes::_aastore:
{
set_global_escape(state.apop());
state.spop();
ArgumentMap arr = state.apop();
set_modified(arr, OFFSET_ANY, type2size[T_OBJECT]*HeapWordSize);
break;
}
case Bytecodes::_pop:
state.raw_pop();
break;
case Bytecodes::_pop2:
state.raw_pop();
state.raw_pop();
break;
case Bytecodes::_dup:
{ ArgumentMap w1 = state.raw_pop();
state.raw_push(w1);
state.raw_push(w1);
}
break;
case Bytecodes::_dup_x1:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
state.raw_push(w1);
state.raw_push(w2);
state.raw_push(w1);
}
break;
case Bytecodes::_dup_x2:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
ArgumentMap w3 = state.raw_pop();
state.raw_push(w1);
state.raw_push(w3);
state.raw_push(w2);
state.raw_push(w1);
}
break;
case Bytecodes::_dup2:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
state.raw_push(w2);
state.raw_push(w1);
state.raw_push(w2);
state.raw_push(w1);
}
break;
case Bytecodes::_dup2_x1:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
ArgumentMap w3 = state.raw_pop();
state.raw_push(w2);
state.raw_push(w1);
state.raw_push(w3);
state.raw_push(w2);
state.raw_push(w1);
}
break;
case Bytecodes::_dup2_x2:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
ArgumentMap w3 = state.raw_pop();
ArgumentMap w4 = state.raw_pop();
state.raw_push(w2);
state.raw_push(w1);
state.raw_push(w4);
state.raw_push(w3);
state.raw_push(w2);
state.raw_push(w1);
}
break;
case Bytecodes::_swap:
{ ArgumentMap w1 = state.raw_pop();
ArgumentMap w2 = state.raw_pop();
state.raw_push(w1);
state.raw_push(w2);
}
break;
case Bytecodes::_iadd:
case Bytecodes::_fadd:
case Bytecodes::_isub:
case Bytecodes::_fsub:
case Bytecodes::_imul:
case Bytecodes::_fmul:
case Bytecodes::_idiv:
case Bytecodes::_fdiv:
case Bytecodes::_irem:
case Bytecodes::_frem:
case Bytecodes::_iand:
case Bytecodes::_ior:
case Bytecodes::_ixor:
state.spop();
state.spop();
state.spush();
break;
case Bytecodes::_ladd:
case Bytecodes::_dadd:
case Bytecodes::_lsub:
case Bytecodes::_dsub:
case Bytecodes::_lmul:
case Bytecodes::_dmul:
case Bytecodes::_ldiv:
case Bytecodes::_ddiv:
case Bytecodes::_lrem:
case Bytecodes::_drem:
case Bytecodes::_land:
case Bytecodes::_lor:
case Bytecodes::_lxor:
state.lpop();
state.lpop();
state.lpush();
break;
case Bytecodes::_ishl:
case Bytecodes::_ishr:
case Bytecodes::_iushr:
state.spop();
state.spop();
state.spush();
break;
case Bytecodes::_lshl:
case Bytecodes::_lshr:
case Bytecodes::_lushr:
state.spop();
state.lpop();
state.lpush();
break;
case Bytecodes::_ineg:
case Bytecodes::_fneg:
state.spop();
state.spush();
break;
case Bytecodes::_lneg:
case Bytecodes::_dneg:
state.lpop();
state.lpush();
break;
case Bytecodes::_iinc:
break;
case Bytecodes::_i2l:
case Bytecodes::_i2d:
case Bytecodes::_f2l:
case Bytecodes::_f2d:
state.spop();
state.lpush();
break;
case Bytecodes::_i2f:
case Bytecodes::_f2i:
state.spop();
state.spush();
break;
case Bytecodes::_l2i:
case Bytecodes::_l2f:
case Bytecodes::_d2i:
case Bytecodes::_d2f:
state.lpop();
state.spush();
break;
case Bytecodes::_l2d:
case Bytecodes::_d2l:
state.lpop();
state.lpush();
break;
case Bytecodes::_i2b:
case Bytecodes::_i2c:
case Bytecodes::_i2s:
state.spop();
state.spush();
break;
case Bytecodes::_lcmp:
case Bytecodes::_dcmpl:
case Bytecodes::_dcmpg:
state.lpop();
state.lpop();
state.spush();
break;
case Bytecodes::_fcmpl:
case Bytecodes::_fcmpg:
state.spop();
state.spop();
state.spush();
break;
case Bytecodes::_ifeq:
case Bytecodes::_ifne:
case Bytecodes::_iflt:
case Bytecodes::_ifge:
case Bytecodes::_ifgt:
case Bytecodes::_ifle:
{
state.spop();
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
break;
}
case Bytecodes::_if_icmpeq:
case Bytecodes::_if_icmpne:
case Bytecodes::_if_icmplt:
case Bytecodes::_if_icmpge:
case Bytecodes::_if_icmpgt:
case Bytecodes::_if_icmple:
{
state.spop();
state.spop();
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
break;
}
case Bytecodes::_if_acmpeq:
case Bytecodes::_if_acmpne:
{
set_method_escape(state.apop());
set_method_escape(state.apop());
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
break;
}
case Bytecodes::_goto:
{
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
fall_through = false;
break;
}
case Bytecodes::_jsr:
{
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
state.apush(empty_map);
successors.push(_methodBlocks->block_containing(dest_bci));
fall_through = false;
break;
}
case Bytecodes::_ret:
// we don't track the destination of a "ret" instruction
assert(s.next_bci() == limit_bci, "branch must end block");
fall_through = false;
break;
case Bytecodes::_return:
assert(s.next_bci() == limit_bci, "return must end block");
fall_through = false;
break;
case Bytecodes::_tableswitch:
{
state.spop();
Bytecode_tableswitch sw(&s);
int len = sw.length();
int dest_bci;
for (int i = 0; i < len; i++) {
dest_bci = s.cur_bci() + sw.dest_offset_at(i);
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
successors.push(_methodBlocks->block_containing(dest_bci));
}
dest_bci = s.cur_bci() + sw.default_offset();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
successors.push(_methodBlocks->block_containing(dest_bci));
assert(s.next_bci() == limit_bci, "branch must end block");
fall_through = false;
break;
}
case Bytecodes::_lookupswitch:
{
state.spop();
Bytecode_lookupswitch sw(&s);
int len = sw.number_of_pairs();
int dest_bci;
for (int i = 0; i < len; i++) {
dest_bci = s.cur_bci() + sw.pair_at(i).offset();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
successors.push(_methodBlocks->block_containing(dest_bci));
}
dest_bci = s.cur_bci() + sw.default_offset();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
successors.push(_methodBlocks->block_containing(dest_bci));
fall_through = false;
break;
}
case Bytecodes::_ireturn:
case Bytecodes::_freturn:
state.spop();
fall_through = false;
break;
case Bytecodes::_lreturn:
case Bytecodes::_dreturn:
state.lpop();
fall_through = false;
break;
case Bytecodes::_areturn:
set_returned(state.apop());
fall_through = false;
break;
case Bytecodes::_getstatic:
case Bytecodes::_getfield:
{ bool ignored_will_link;
ciField* field = s.get_field(ignored_will_link);
BasicType field_type = field->type()->basic_type();
if (s.cur_bc() != Bytecodes::_getstatic) {
set_method_escape(state.apop());
}
if (is_reference_type(field_type)) {
state.apush(unknown_obj);
} else if (type2size[field_type] == 1) {
state.spush();
} else {
state.lpush();
}
}
break;
case Bytecodes::_putstatic:
case Bytecodes::_putfield:
{ bool will_link;
ciField* field = s.get_field(will_link);
BasicType field_type = field->type()->basic_type();
if (is_reference_type(field_type)) {
set_global_escape(state.apop());
} else if (type2size[field_type] == 1) {
state.spop();
} else {
state.lpop();
}
if (s.cur_bc() != Bytecodes::_putstatic) {
ArgumentMap p = state.apop();
set_method_escape(p);
set_modified(p, will_link ? field->offset_in_bytes() : OFFSET_ANY, type2size[field_type]*HeapWordSize);
}
}
break;
case Bytecodes::_invokevirtual:
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokeinterface:
{ bool ignored_will_link;
ciSignature* declared_signature = nullptr;
ciMethod* target = s.get_method(ignored_will_link, &declared_signature);
ciKlass* holder = s.get_declared_method_holder();
assert(declared_signature != nullptr, "cannot be null");
// If the current bytecode has an attached appendix argument,
// push an unknown object to represent that argument. (Analysis
// of dynamic call sites, especially invokehandle calls, needs
// the appendix argument on the stack, in addition to "regular" arguments
// pushed onto the stack by bytecode instructions preceding the call.)
//
// The escape analyzer does _not_ use the ciBytecodeStream::has_appendix(s)
// method to determine whether the current bytecode has an appendix argument.
// The has_appendix() method obtains the appendix from the
// ConstantPoolCacheEntry::_f1 field, which can happen concurrently with
// resolution of dynamic call sites. Callees in the
// ciBytecodeStream::get_method() call above also access the _f1 field;
// interleaving the get_method() and has_appendix() calls in the current
// method with call site resolution can lead to an inconsistent view of
// the current method's argument count. In particular, some interleaving(s)
// can cause the method's argument count to not include the appendix, which
// then leads to stack over-/underflow in the escape analyzer.
//
// Instead of pushing the argument if has_appendix() is true, the escape analyzer
// pushes an appendix for all call sites targeted by invokedynamic and invokehandle
// instructions, except if the call site is the _invokeBasic intrinsic
// (that intrinsic is always targeted by an invokehandle instruction but does
// not have an appendix argument).
if (target->is_loaded() &&
Bytecodes::has_optional_appendix(s.cur_bc_raw()) &&
target->intrinsic_id() != vmIntrinsics::_invokeBasic) {
state.apush(unknown_obj);
}
// Pass in raw bytecode because we need to see invokehandle instructions.
invoke(state, s.cur_bc_raw(), target, holder);
// We are using the return type of the declared signature here because
// it might be a more concrete type than the one from the target (for
// e.g. invokedynamic and invokehandle).
ciType* return_type = declared_signature->return_type();
if (!return_type->is_primitive_type()) {
state.apush(unknown_obj);
} else if (return_type->is_one_word()) {
state.spush();
} else if (return_type->is_two_word()) {
state.lpush();
}
}
break;
case Bytecodes::_new:
state.apush(allocated_obj);
break;
case Bytecodes::_newarray:
case Bytecodes::_anewarray:
state.spop();
state.apush(allocated_obj);
break;
case Bytecodes::_multianewarray:
{ int i = s.cur_bcp()[3];
while (i-- > 0) state.spop();
state.apush(allocated_obj);
}
break;
case Bytecodes::_arraylength:
set_method_escape(state.apop());
state.spush();
break;
case Bytecodes::_athrow:
set_global_escape(state.apop());
fall_through = false;
break;
case Bytecodes::_checkcast:
{ ArgumentMap obj = state.apop();
set_method_escape(obj);
state.apush(obj);
}
break;
case Bytecodes::_instanceof:
set_method_escape(state.apop());
state.spush();
break;
case Bytecodes::_monitorenter:
case Bytecodes::_monitorexit:
state.apop();
break;
case Bytecodes::_wide:
ShouldNotReachHere();
break;
case Bytecodes::_ifnull:
case Bytecodes::_ifnonnull:
{
set_method_escape(state.apop());
int dest_bci = s.get_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
break;
}
case Bytecodes::_goto_w:
{
int dest_bci = s.get_far_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
successors.push(_methodBlocks->block_containing(dest_bci));
fall_through = false;
break;
}
case Bytecodes::_jsr_w:
{
int dest_bci = s.get_far_dest();
assert(_methodBlocks->is_block_start(dest_bci), "branch destination must start a block");
assert(s.next_bci() == limit_bci, "branch must end block");
state.apush(empty_map);
successors.push(_methodBlocks->block_containing(dest_bci));
fall_through = false;
break;
}
case Bytecodes::_breakpoint:
break;
default:
ShouldNotReachHere();
break;
}
}
if (fall_through) {
int fall_through_bci = s.cur_bci();
if (fall_through_bci < _method->code_size()) {
assert(_methodBlocks->is_block_start(fall_through_bci), "must fall through to block start.");
successors.push(_methodBlocks->block_containing(fall_through_bci));
}
}
}