/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*
*/
#include "precompiled.hpp"
#include "ci/bcEscapeAnalyzer.hpp"
#include "ci/ciConstant.hpp"
#include "ci/ciField.hpp"
#include "ci/ciMethodBlocks.hpp"
#include "ci/ciStreams.hpp"
#include "interpreter/bytecode.hpp"
#include "utilities/bitMap.inline.hpp"
#ifndef PRODUCT
code; \
}
#else
#endif
// Maintain a map of which aguments a local variable or
// stack slot may contain. In addition to tracking
// arguments, it tracks two special values, "allocated"
// which represents any object allocated in the current
// method, and "unknown" which is any other object.
// Up to 30 arguments are handled, with the last one
// representing summary information for any extra arguments
if (e > MAXBIT)
e = MAXBIT;
return (1 << (e + 2));
}
public:
};
public:
short _stack_height;
short _max_stack;
bool _initialized;
StateInfo() {
}
ArgumentMap raw_pop() { guarantee(_stack_height > 0, "stack underflow"); return _stack[--_stack_height]; }
void raw_push(ArgumentMap i) { guarantee(_stack_height < _max_stack, "stack overflow"); _stack[_stack_height++] = i; }
};
for (int i = 0; i < _arg_size; i++) {
_arg_returned.set(i);
}
_return_allocated = _return_allocated && vars.contains_allocated() && !(vars.contains_unknown() || vars.contains_vars());
}
// return true if any element of vars is an argument
for (int i = 0; i < _arg_size; i++) {
return true;
}
return false;
}
// return true if any element of vars is an arg_stack argument
if (_conservative)
return true;
for (int i = 0; i < _arg_size; i++) {
return true;
}
return false;
}
for (int i = 0; i < _arg_size; i++) {
bm >>= i;
}
}
}
}
if (vars.contains_allocated())
_allocated_escapes = true;
// Merge new state into already processed block.
// New state is not taken into account and
// it may invalidate set_returned() result.
_return_local = false;
}
_return_allocated = false;
}
}
}
}
for (int i = 0; i < _arg_size; i++) {
}
}
if (vars.contains_unknown())
_unknown_modified = true;
}
return true;
}
}
return false;
}
if (offset == OFFSET_ANY)
return _arg_modified[arg] != 0;
bool modified = false;
int l = offset / HeapWordSize;
if (l > ARG_OFFSET_MAX)
l = ARG_OFFSET_MAX;
if (h > ARG_OFFSET_MAX+1)
h = ARG_OFFSET_MAX + 1;
for (int i = l; i < h; i++) {
}
return modified;
}
if (offset == OFFSET_ANY) {
return;
}
int l = offset / HeapWordSize;
if (l > ARG_OFFSET_MAX)
l = ARG_OFFSET_MAX;
if (h > ARG_OFFSET_MAX+1)
h = ARG_OFFSET_MAX + 1;
for (int i = l; i < h; i++) {
}
}
void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod* target, ciKlass* holder) {
int i;
// retrieve information about the callee
// Some methods are obviously bindable without any type checks so
// convert them directly to an invokespecial or invokestatic.
switch (code) {
case Bytecodes::_invokevirtual:
break;
case Bytecodes::_invokehandle:
break;
}
}
// compute size of arguments
// direct recursive calls are skipped if they can be bound statically without introducing
// dependencies and if parameters are passed at the same position as in the current method
// other calls are skipped if there are no unescaped arguments passed to them
(code != Bytecodes::_invokevirtual || target->is_final_method() || state._stack[arg_base] .is_empty());
// check if analysis of callee can safely be skipped
bool skip_callee = true;
skip_callee = !is_argument(arg) || !is_arg_stack(arg) || (directly_recursive && arg.is_singleton(i - arg_base));
}
// For now we conservatively skip invokedynamic.
skip_callee = true;
}
if (skip_callee) {
TRACE_BCEA(3, tty->print_cr("[EA] skipping method %s::%s", holder->name()->as_utf8(), target->name()->as_utf8()));
for (i = 0; i < arg_size; i++) {
}
_unknown_modified = true; // assume the worst since we don't analyze the called method
return;
}
// determine actual method (use CHA if necessary)
} else {
}
}
// analyze callee
// adjust escape state of actual parameters
bool must_record_dependencies = false;
for (i = arg_size - 1; i >= 0; i--) {
if (!is_argument(arg))
continue;
for (int j = 0; j < _arg_size; j++) {
}
}
if (!is_arg_stack(arg)) {
// arguments have already been recognized as escaping
must_record_dependencies = true;
} else {
}
}
// record dependencies if at least one parameter retained stack-allocatable
if (must_record_dependencies) {
if (code == Bytecodes::_invokeinterface || code == Bytecodes::_invokevirtual && !target->is_final_method()) {
}
}
} else {
// conservatively mark all actual parameters as escaping globally
for (i = 0; i < arg_size; i++) {
if (!is_argument(arg))
continue;
}
_unknown_modified = true; // assume the worst since we don't know the called method
}
}
}
void BCEscapeAnalyzer::iterate_one_block(ciBlock *blk, StateInfo &state, GrowableArray<ciBlock *> &successors) {
blk->set_processed();
ciBytecodeStream s(method());
bool fall_through = false;
fall_through = true;
switch (s.cur_bc()) {
break;
case Bytecodes::_aconst_null:
break;
case Bytecodes::_iconst_m1:
break;
break;
{
// Avoid calling get_constant() which will try to allocate
// unloaded constant. We need only constant's type.
// Only longs and doubles use 2 stack slots.
} else {
}
break;
}
break;
break;
break;
break;
break;
break;
break;
break;
break;
}
break;
break;
break;
break;
break;
break;
break;
break;
{
break;
}
{
break;
}
{
break;
}
break;
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
break;
}
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
{
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:
{
break;
}
case Bytecodes::_if_acmpeq:
case Bytecodes::_if_acmpne:
{
break;
}
{
fall_through = false;
break;
}
{
fall_through = false;
break;
}
// we don't track the destination of a "ret" instruction
fall_through = false;
break;
fall_through = false;
break;
case Bytecodes::_tableswitch:
{
Bytecode_tableswitch sw(&s);
int dest_bci;
for (int i = 0; i < len; i++) {
}
fall_through = false;
break;
}
case Bytecodes::_lookupswitch:
{
Bytecode_lookupswitch sw(&s);
int dest_bci;
for (int i = 0; i < len; i++) {
}
fall_through = false;
break;
}
fall_through = false;
break;
fall_through = false;
break;
fall_through = false;
break;
case Bytecodes::_getstatic:
{ bool ignored_will_link;
}
} else {
}
}
break;
case Bytecodes::_putstatic:
{ bool will_link;
} else {
}
}
}
break;
case Bytecodes::_invokevirtual:
case Bytecodes::_invokespecial:
case Bytecodes::_invokestatic:
case Bytecodes::_invokedynamic:
case Bytecodes::_invokeinterface:
{ bool ignored_will_link;
// Push appendix argument, if one.
if (s.has_appendix()) {
}
// Pass in raw bytecode because we need to see invokehandle instructions.
// 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).
if (!return_type->is_primitive_type()) {
} else if (return_type->is_one_word()) {
} else if (return_type->is_two_word()) {
}
}
break;
break;
case Bytecodes::_anewarray:
break;
case Bytecodes::_multianewarray:
{ int i = s.cur_bcp()[3];
}
break;
case Bytecodes::_arraylength:
break;
fall_through = false;
break;
case Bytecodes::_checkcast:
}
break;
case Bytecodes::_instanceof:
break;
case Bytecodes::_monitorenter:
case Bytecodes::_monitorexit:
break;
break;
case Bytecodes::_ifnonnull:
{
break;
}
{
fall_through = false;
break;
}
{
fall_through = false;
break;
}
case Bytecodes::_breakpoint:
break;
default:
break;
}
}
if (fall_through) {
}
}
}
void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, StateInfo *s_state) {
// exceptions may cause transfer of control to handlers in the middle of a
// block, so we don't merge the incoming state of exception handlers
if (dest->is_handler())
return;
if (!d_state->_initialized ) {
// destination not initialized, just copy
for (int i = 0; i < nlocals; i++) {
}
for (int i = 0; i < s_state->_stack_height; i++) {
}
d_state->_initialized = true;
// we have not yet walked the bytecodes of dest, we can merge
// the states
for (int i = 0; i < nlocals; i++) {
}
for (int i = 0; i < s_state->_stack_height; i++) {
}
} else {
// the bytecodes of dest have already been processed, mark any
// arguments in the source state which are not in the dest state
// as global escape.
// Future refinement: we only need to mark these variable to the
// maximum escape of any variables in dest state
for (int i = 0; i < nlocals; i++) {
ArgumentMap t;
extra_vars.set_union(t);
}
for (int i = 0; i < s_state->_stack_height; i++) {
ArgumentMap t;
//extra_vars |= !d_state->_vars[i] & s_state->_vars[i];
t.clear();
extra_vars.set_union(t);
}
set_global_escape(extra_vars, true);
}
}
state._initialized = false;
for (int i = 0; i < numblocks; i++) {
blockstates[i]._initialized = false;
blockstates[i]._stack_height = 0;
}
// initialize block 0 state from method signature
int j = 0;
// record information for "this"
j++;
}
if (!t->is_primitive_type()) {
}
j += t->size();
}
// for an exception handler or a target of a ret instruction, we assume the worst case,
// that any variable could contain any argument
for (int i = 0; i < numLocals; i++) {
}
if (blk->is_handler()) {
} else {
}
for (int i = 0; i < state._stack_height; i++) {
// ??? should this be unknown_map ???
}
} else {
for (int i = 0; i < numLocals; i++) {
}
for (int i = 0; i < blkState->_stack_height; i++) {
}
}
// if this block has any exception handlers, push them
// onto successor list
if (blk->has_handler()) {
DEBUG_ONLY(int handler_count = 0;)
for (int i = 0; i < numblocks; i++) {
if (b->is_handler()) {
successors.push(b);
}
}
}
}
// merge computed variable state with successors
while(successors.length() > 0) {
}
}
}
// identify basic blocks
// TEMPORARY
return true;
}
return iid;
else
return vmIntrinsics::_none;
}
switch (iid) {
case vmIntrinsics::_getClass:
_return_local = false;
break;
case vmIntrinsics::_fillInStackTrace:
break;
case vmIntrinsics::_hashCode:
// initialized state is correct
break;
default:
assert(false, "unexpected intrinsic");
}
return true;
}
int i;
// clear escape information (method may have been deoptimized)
methodData()->clear_escape_info();
// initialize escape state of object parameters
int j = 0;
_arg_local.set(0);
_arg_stack.set(0);
j++;
}
if (!t->is_primitive_type()) {
_arg_local.set(j);
_arg_stack.set(j);
}
j += t->size();
}
// start with optimistic assumption
if (rt->is_primitive_type()) {
_return_local = false;
_return_allocated = false;
} else {
_return_local = true;
_return_allocated = true;
}
_allocated_escapes = false;
_unknown_modified = false;
}
arg_count++; // allow for "this"
}
for (int i = 0; i < arg_count; i++) {
}
_arg_local.Clear();
_arg_stack.Clear();
_return_local = false;
_return_allocated = false;
_allocated_escapes = true;
_unknown_modified = true;
}
int i;
// check if method can be analyzed
if (iid == vmIntrinsics::_none && (method()->is_abstract() || method()->is_native() || !method()->holder()->is_initialized()
if (BCEATraceLevel >= 1) {
if (method()->is_abstract())
else if (_level > MaxBCEAEstimateLevel)
else
}
return;
}
if (BCEATraceLevel >= 1) {
method()->print_short_name();
}
bool success;
initialize();
// Do not scan method if it has no object parameters and
// does not returns an object (_return_allocated is set in initialize()).
// Clear all info since method's bytecode was not analysed and
// set pessimistic escape information.
return;
}
else {
success = do_analysis();
}
// don't store interprocedural escape information if it introduces
// dependencies or if method data is empty
//
for (i = 0; i < _arg_size; i++) {
if (_arg_local.test(i)) {
methodData()->set_arg_local(i);
methodData()->set_arg_stack(i);
} else if (_arg_stack.test(i)) {
methodData()->set_arg_stack(i);
}
if (_arg_returned.test(i)) {
methodData()->set_arg_returned(i);
}
}
if (_return_local) {
}
if (_return_allocated) {
}
if (_allocated_escapes) {
}
if (_unknown_modified) {
}
}
}
// read escape information from method descriptor
for (int i = 0; i < _arg_size; i++) {
if (methodData()->is_arg_local(i))
_arg_local.set(i);
if (methodData()->is_arg_stack(i))
_arg_stack.set(i);
if (methodData()->is_arg_returned(i))
_arg_returned.set(i);
}
}
#ifndef PRODUCT
method()->print_short_name();
if (_return_local) {
} else if (is_return_allocated()) {
} else {
}
for (int i = 0; i < _arg_size; i++) {
if (_arg_modified[i] == 0)
else
}
if (_return_allocated)
if (_allocated_escapes)
if (_unknown_modified)
}
#endif
, _arg_local(_arena)
, _arg_stack(_arena)
, _return_local(false)
, _return_allocated(false)
, _allocated_escapes(false)
, _unknown_modified(false)
if (!_conservative) {
_arg_local.Clear();
_arg_stack.Clear();
if (methodData() == NULL)
return;
if (methodData()->has_escape_info()) {
} else {
methodData()->update_escape_info();
}
#ifndef PRODUCT
if (BCEATraceLevel >= 3) {
// dump escape information
dump();
}
#endif
}
}
// Also record evol dependencies so redefinition of the
// callee will trigger recompilation.
}
deps->assert_unique_concrete_method(k, m);
}
}