Mercurial > hg > truffle
view src/share/vm/ci/bcEscapeAnalyzer.cpp @ 17716:cdb71841f4bc
6498581: ThreadInterruptTest3 produces wrong output on Windows
Summary: There is race condition between os::interrupt and os::is_interrupted on Windows. In JVM_Sleep(Thread.sleep), check if thread gets interrupted, it may see interrupted but not really interrupted so cause spurious waking up (early return from sleep). Fix by checking if interrupt event really gets set thus prevent false return. For intrinsic of _isInterrupted, on Windows, go fastpath only on bit not set.
Reviewed-by: acorn, kvn
Contributed-by: david.holmes@oracle.com, yumin.qi@oracle.com
author | minqi |
---|---|
date | Wed, 26 Feb 2014 15:20:41 -0800 |
parents | 55fb97c4c58d |
children | 4ca6dc0799b6 78bbf4d43a14 |
line wrap: on
line source
/* * Copyright (c) 2005, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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 #define TRACE_BCEA(level, code) \ if (EstimateArgEscape && BCEATraceLevel >= level) { \ code; \ } #else #define TRACE_BCEA(level, code) #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 class BCEscapeAnalyzer::ArgumentMap { uint _bits; enum {MAXBIT = 29, ALLOCATED = 1, UNKNOWN = 2}; uint int_to_bit(uint e) const { if (e > MAXBIT) e = MAXBIT; return (1 << (e + 2)); } public: ArgumentMap() { _bits = 0;} void set_bits(uint bits) { _bits = bits;} uint get_bits() const { return _bits;} void clear() { _bits = 0;} void set_all() { _bits = ~0u; } bool is_empty() const { return _bits == 0; } bool contains(uint var) const { return (_bits & int_to_bit(var)) != 0; } bool is_singleton(uint var) const { return (_bits == int_to_bit(var)); } bool contains_unknown() const { return (_bits & UNKNOWN) != 0; } bool contains_allocated() const { return (_bits & ALLOCATED) != 0; } bool contains_vars() const { return (_bits & (((1 << MAXBIT) -1) << 2)) != 0; } void set(uint var) { _bits = int_to_bit(var); } void add(uint var) { _bits |= int_to_bit(var); } void add_unknown() { _bits = UNKNOWN; } void add_allocated() { _bits = ALLOCATED; } void set_union(const ArgumentMap &am) { _bits |= am._bits; } void set_intersect(const ArgumentMap &am) { _bits |= am._bits; } void set_difference(const ArgumentMap &am) { _bits &= ~am._bits; } void operator=(const ArgumentMap &am) { _bits = am._bits; } bool operator==(const ArgumentMap &am) { return _bits == am._bits; } bool operator!=(const ArgumentMap &am) { return _bits != am._bits; } }; class BCEscapeAnalyzer::StateInfo { public: ArgumentMap *_vars; ArgumentMap *_stack; short _stack_height; short _max_stack; bool _initialized; ArgumentMap empty_map; StateInfo() { empty_map.clear(); } ArgumentMap raw_pop() { guarantee(_stack_height > 0, "stack underflow"); return _stack[--_stack_height]; } ArgumentMap apop() { return raw_pop(); } void spop() { raw_pop(); } void lpop() { spop(); spop(); } void raw_push(ArgumentMap i) { guarantee(_stack_height < _max_stack, "stack overflow"); _stack[_stack_height++] = i; } void apush(ArgumentMap i) { raw_push(i); } void spush() { raw_push(empty_map); } void lpush() { spush(); spush(); } }; void BCEscapeAnalyzer::set_returned(ArgumentMap vars) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i)) _arg_returned.set(i); } _return_local = _return_local && !(vars.contains_unknown() || vars.contains_allocated()); _return_allocated = _return_allocated && vars.contains_allocated() && !(vars.contains_unknown() || vars.contains_vars()); } // return true if any element of vars is an argument bool BCEscapeAnalyzer::is_argument(ArgumentMap vars) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i)) return true; } return false; } // return true if any element of vars is an arg_stack argument bool BCEscapeAnalyzer::is_arg_stack(ArgumentMap vars){ if (_conservative) return true; for (int i = 0; i < _arg_size; i++) { if (vars.contains(i) && _arg_stack.test(i)) return true; } return false; } // return true if all argument elements of vars are returned bool BCEscapeAnalyzer::returns_all(ArgumentMap vars) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i) && !_arg_returned.test(i)) { return false; } } return true; } void BCEscapeAnalyzer::clear_bits(ArgumentMap vars, VectorSet &bm) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i)) { bm >>= i; } } } void BCEscapeAnalyzer::set_method_escape(ArgumentMap vars) { clear_bits(vars, _arg_local); } void BCEscapeAnalyzer::set_global_escape(ArgumentMap vars, bool merge) { clear_bits(vars, _arg_local); clear_bits(vars, _arg_stack); if (vars.contains_allocated()) _allocated_escapes = true; if (merge && !vars.is_empty()) { // Merge new state into already processed block. // New state is not taken into account and // it may invalidate set_returned() result. if (vars.contains_unknown() || vars.contains_allocated()) { _return_local = false; } if (vars.contains_unknown() || vars.contains_vars()) { _return_allocated = false; } if (_return_local && vars.contains_vars() && !returns_all(vars)) { // Return result should be invalidated if args in new // state are not recorded in return state. _return_local = false; } } } void BCEscapeAnalyzer::set_dirty(ArgumentMap vars) { clear_bits(vars, _dirty); } void BCEscapeAnalyzer::set_modified(ArgumentMap vars, int offs, int size) { for (int i = 0; i < _arg_size; i++) { if (vars.contains(i)) { set_arg_modified(i, offs, size); } } if (vars.contains_unknown()) _unknown_modified = true; } bool BCEscapeAnalyzer::is_recursive_call(ciMethod* callee) { for (BCEscapeAnalyzer* scope = this; scope != NULL; scope = scope->_parent) { if (scope->method() == callee) { return true; } } return false; } bool BCEscapeAnalyzer::is_arg_modified(int arg, int offset, int size_in_bytes) { if (offset == OFFSET_ANY) return _arg_modified[arg] != 0; assert(arg >= 0 && arg < _arg_size, "must be an argument."); bool modified = false; int l = offset / HeapWordSize; int h = round_to(offset + size_in_bytes, HeapWordSize) / 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++) { modified = modified || (_arg_modified[arg] & (1 << i)) != 0; } return modified; } void BCEscapeAnalyzer::set_arg_modified(int arg, int offset, int size_in_bytes) { if (offset == OFFSET_ANY) { _arg_modified[arg] = (uint) -1; return; } assert(arg >= 0 && arg < _arg_size, "must be an argument."); int l = offset / HeapWordSize; int h = round_to(offset + size_in_bytes, HeapWordSize) / 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++) { _arg_modified[arg] |= (1 << i); } } void BCEscapeAnalyzer::invoke(StateInfo &state, Bytecodes::Code code, ciMethod* target, ciKlass* holder) { int i; // retrieve information about the callee ciInstanceKlass* klass = target->holder(); ciInstanceKlass* calling_klass = method()->holder(); ciInstanceKlass* callee_holder = ciEnv::get_instance_klass_for_declared_method_holder(holder); ciInstanceKlass* actual_recv = callee_holder; // Some methods are obviously bindable without any type checks so // convert them directly to an invokespecial or invokestatic. if (target->is_loaded() && !target->is_abstract() && target->can_be_statically_bound()) { switch (code) { case Bytecodes::_invokevirtual: code = Bytecodes::_invokespecial; break; case Bytecodes::_invokehandle: code = target->is_static() ? Bytecodes::_invokestatic : Bytecodes::_invokespecial; break; } } // compute size of arguments int arg_size = target->invoke_arg_size(code); int arg_base = MAX2(state._stack_height - arg_size, 0); // 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 bool directly_recursive = (method() == target) && (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; for (i = state._stack_height - 1; i >= arg_base && skip_callee; i--) { ArgumentMap arg = state._stack[i]; skip_callee = !is_argument(arg) || !is_arg_stack(arg) || (directly_recursive && arg.is_singleton(i - arg_base)); } // For now we conservatively skip invokedynamic. if (code == Bytecodes::_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++) { set_method_escape(state.raw_pop()); } _unknown_modified = true; // assume the worst since we don't analyze the called method return; } // determine actual method (use CHA if necessary) ciMethod* inline_target = NULL; if (target->is_loaded() && klass->is_loaded() && (klass->is_initialized() || klass->is_interface() && target->holder()->is_initialized()) && target->is_loaded()) { if (code == Bytecodes::_invokestatic || code == Bytecodes::_invokespecial || code == Bytecodes::_invokevirtual && target->is_final_method()) { inline_target = target; } else { inline_target = target->find_monomorphic_target(calling_klass, callee_holder, actual_recv); } } if (inline_target != NULL && !is_recursive_call(inline_target)) { // analyze callee BCEscapeAnalyzer analyzer(inline_target, this); // adjust escape state of actual parameters bool must_record_dependencies = false; for (i = arg_size - 1; i >= 0; i--) { ArgumentMap arg = state.raw_pop(); if (!is_argument(arg)) continue; for (int j = 0; j < _arg_size; j++) { if (arg.contains(j)) { _arg_modified[j] |= analyzer._arg_modified[i]; } } if (!is_arg_stack(arg)) { // arguments have already been recognized as escaping } else if (analyzer.is_arg_stack(i) && !analyzer.is_arg_returned(i)) { set_method_escape(arg); must_record_dependencies = true; } else { set_global_escape(arg); } } _unknown_modified = _unknown_modified || analyzer.has_non_arg_side_affects(); // 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()) { _dependencies.append(actual_recv); _dependencies.append(inline_target); } _dependencies.appendAll(analyzer.dependencies()); } } else { TRACE_BCEA(1, tty->print_cr("[EA] virtual method %s is not monomorphic.", target->name()->as_utf8())); // conservatively mark all actual parameters as escaping globally for (i = 0; i < arg_size; i++) { ArgumentMap arg = state.raw_pop(); if (!is_argument(arg)) continue; set_modified(arg, OFFSET_ANY, type2size[T_INT]*HeapWordSize); set_global_escape(arg); } _unknown_modified = true; // assume the worst since we don't know the called method } } bool BCEscapeAnalyzer::contains(uint arg_set1, uint arg_set2) { return ((~arg_set1) | arg_set2) == 0; } 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(); constantTag tag = s.get_constant_pool_tag(index); if (tag.is_long() || tag.is_double()) { // Only longs and doubles use 2 stack slots. state.lpush(); } else if (tag.basic_type() == 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); set_dirty(array); } 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 (field_type == T_OBJECT || field_type == T_ARRAY) { 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 (field_type == T_OBJECT || field_type == T_ARRAY) { 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() : 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 = NULL; ciMethod* target = s.get_method(ignored_will_link, &declared_signature); ciKlass* holder = s.get_declared_method_holder(); assert(declared_signature != NULL, "cannot be null"); // Push appendix argument, if one. if (s.has_appendix()) { 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)); } } } void BCEscapeAnalyzer::merge_block_states(StateInfo *blockstates, ciBlock *dest, StateInfo *s_state) { StateInfo *d_state = blockstates + dest->index(); int nlocals = _method->max_locals(); // 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++) { d_state->_vars[i] = s_state->_vars[i]; } for (int i = 0; i < s_state->_stack_height; i++) { d_state->_stack[i] = s_state->_stack[i]; } d_state->_stack_height = s_state->_stack_height; d_state->_max_stack = s_state->_max_stack; d_state->_initialized = true; } else if (!dest->processed()) { // we have not yet walked the bytecodes of dest, we can merge // the states assert(d_state->_stack_height == s_state->_stack_height, "computed stack heights must match"); for (int i = 0; i < nlocals; i++) { d_state->_vars[i].set_union(s_state->_vars[i]); } for (int i = 0; i < s_state->_stack_height; i++) { d_state->_stack[i].set_union(s_state->_stack[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 assert(d_state->_stack_height == s_state->_stack_height, "computed stack heights must match"); ArgumentMap extra_vars; for (int i = 0; i < nlocals; i++) { ArgumentMap t; t = s_state->_vars[i]; t.set_difference(d_state->_vars[i]); 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(); t = s_state->_stack[i]; t.set_difference(d_state->_stack[i]); extra_vars.set_union(t); } set_global_escape(extra_vars, true); } } void BCEscapeAnalyzer::iterate_blocks(Arena *arena) { int numblocks = _methodBlocks->num_blocks(); int stkSize = _method->max_stack(); int numLocals = _method->max_locals(); StateInfo state; int datacount = (numblocks + 1) * (stkSize + numLocals); int datasize = datacount * sizeof(ArgumentMap); StateInfo *blockstates = (StateInfo *) arena->Amalloc(numblocks * sizeof(StateInfo)); ArgumentMap *statedata = (ArgumentMap *) arena->Amalloc(datasize); for (int i = 0; i < datacount; i++) ::new ((void*)&statedata[i]) ArgumentMap(); ArgumentMap *dp = statedata; state._vars = dp; dp += numLocals; state._stack = dp; dp += stkSize; state._initialized = false; state._max_stack = stkSize; for (int i = 0; i < numblocks; i++) { blockstates[i]._vars = dp; dp += numLocals; blockstates[i]._stack = dp; dp += stkSize; blockstates[i]._initialized = false; blockstates[i]._stack_height = 0; blockstates[i]._max_stack = stkSize; } GrowableArray<ciBlock *> worklist(arena, numblocks / 4, 0, NULL); GrowableArray<ciBlock *> successors(arena, 4, 0, NULL); _methodBlocks->clear_processed(); // initialize block 0 state from method signature ArgumentMap allVars; // all oop arguments to method ciSignature* sig = method()->signature(); int j = 0; ciBlock* first_blk = _methodBlocks->block_containing(0); int fb_i = first_blk->index(); if (!method()->is_static()) { // record information for "this" blockstates[fb_i]._vars[j].set(j); allVars.add(j); j++; } for (int i = 0; i < sig->count(); i++) { ciType* t = sig->type_at(i); if (!t->is_primitive_type()) { blockstates[fb_i]._vars[j].set(j); allVars.add(j); } j += t->size(); } blockstates[fb_i]._initialized = true; assert(j == _arg_size, "just checking"); ArgumentMap unknown_map; unknown_map.add_unknown(); worklist.push(first_blk); while(worklist.length() > 0) { ciBlock *blk = worklist.pop(); StateInfo *blkState = blockstates + blk->index(); if (blk->is_handler() || blk->is_ret_target()) { // 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++) { state._vars[i] = allVars; } if (blk->is_handler()) { state._stack_height = 1; } else { state._stack_height = blkState->_stack_height; } for (int i = 0; i < state._stack_height; i++) { // ??? should this be unknown_map ??? state._stack[i] = allVars; } } else { for (int i = 0; i < numLocals; i++) { state._vars[i] = blkState->_vars[i]; } for (int i = 0; i < blkState->_stack_height; i++) { state._stack[i] = blkState->_stack[i]; } state._stack_height = blkState->_stack_height; } iterate_one_block(blk, state, successors); // if this block has any exception handlers, push them // onto successor list if (blk->has_handler()) { DEBUG_ONLY(int handler_count = 0;) int blk_start = blk->start_bci(); int blk_end = blk->limit_bci(); for (int i = 0; i < numblocks; i++) { ciBlock *b = _methodBlocks->block(i); if (b->is_handler()) { int ex_start = b->ex_start_bci(); int ex_end = b->ex_limit_bci(); if ((ex_start >= blk_start && ex_start < blk_end) || (ex_end > blk_start && ex_end <= blk_end)) { successors.push(b); } DEBUG_ONLY(handler_count++;) } } assert(handler_count > 0, "must find at least one handler"); } // merge computed variable state with successors while(successors.length() > 0) { ciBlock *succ = successors.pop(); merge_block_states(blockstates, succ, &state); if (!succ->processed()) worklist.push(succ); } } } bool BCEscapeAnalyzer::do_analysis() { Arena* arena = CURRENT_ENV->arena(); // identify basic blocks _methodBlocks = _method->get_method_blocks(); iterate_blocks(arena); // TEMPORARY return true; } vmIntrinsics::ID BCEscapeAnalyzer::known_intrinsic() { vmIntrinsics::ID iid = method()->intrinsic_id(); if (iid == vmIntrinsics::_getClass || iid == vmIntrinsics::_fillInStackTrace || iid == vmIntrinsics::_hashCode) return iid; else return vmIntrinsics::_none; } bool BCEscapeAnalyzer::compute_escape_for_intrinsic(vmIntrinsics::ID iid) { ArgumentMap arg; arg.clear(); switch (iid) { case vmIntrinsics::_getClass: _return_local = false; break; case vmIntrinsics::_fillInStackTrace: arg.set(0); // 'this' set_returned(arg); break; case vmIntrinsics::_hashCode: // initialized state is correct break; default: assert(false, "unexpected intrinsic"); } return true; } void BCEscapeAnalyzer::initialize() { int i; // clear escape information (method may have been deoptimized) methodData()->clear_escape_info(); // initialize escape state of object parameters ciSignature* sig = method()->signature(); int j = 0; if (!method()->is_static()) { _arg_local.set(0); _arg_stack.set(0); j++; } for (i = 0; i < sig->count(); i++) { ciType* t = sig->type_at(i); if (!t->is_primitive_type()) { _arg_local.set(j); _arg_stack.set(j); } j += t->size(); } assert(j == _arg_size, "just checking"); // start with optimistic assumption ciType *rt = _method->return_type(); if (rt->is_primitive_type()) { _return_local = false; _return_allocated = false; } else { _return_local = true; _return_allocated = true; } _allocated_escapes = false; _unknown_modified = false; } void BCEscapeAnalyzer::clear_escape_info() { ciSignature* sig = method()->signature(); int arg_count = sig->count(); ArgumentMap var; if (!method()->is_static()) { arg_count++; // allow for "this" } for (int i = 0; i < arg_count; i++) { set_arg_modified(i, OFFSET_ANY, 4); var.clear(); var.set(i); set_modified(var, OFFSET_ANY, 4); set_global_escape(var); } _arg_local.Clear(); _arg_stack.Clear(); _arg_returned.Clear(); _return_local = false; _return_allocated = false; _allocated_escapes = true; _unknown_modified = true; } void BCEscapeAnalyzer::compute_escape_info() { int i; assert(!methodData()->has_escape_info(), "do not overwrite escape info"); vmIntrinsics::ID iid = known_intrinsic(); // check if method can be analyzed if (iid == vmIntrinsics::_none && (method()->is_abstract() || method()->is_native() || !method()->holder()->is_initialized() || _level > MaxBCEAEstimateLevel || method()->code_size() > MaxBCEAEstimateSize)) { if (BCEATraceLevel >= 1) { tty->print("Skipping method because: "); if (method()->is_abstract()) tty->print_cr("method is abstract."); else if (method()->is_native()) tty->print_cr("method is native."); else if (!method()->holder()->is_initialized()) tty->print_cr("class of method is not initialized."); else if (_level > MaxBCEAEstimateLevel) tty->print_cr("level (%d) exceeds MaxBCEAEstimateLevel (%d).", _level, MaxBCEAEstimateLevel); else if (method()->code_size() > MaxBCEAEstimateSize) tty->print_cr("code size (%d) exceeds MaxBCEAEstimateSize.", method()->code_size(), MaxBCEAEstimateSize); else ShouldNotReachHere(); } clear_escape_info(); return; } if (BCEATraceLevel >= 1) { tty->print("[EA] estimating escape information for"); if (iid != vmIntrinsics::_none) tty->print(" intrinsic"); method()->print_short_name(); tty->print_cr(" (%d bytes)", method()->code_size()); } 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()). if (_arg_local.Size() == 0 && !_return_allocated) { // Clear all info since method's bytecode was not analysed and // set pessimistic escape information. clear_escape_info(); methodData()->set_eflag(MethodData::allocated_escapes); methodData()->set_eflag(MethodData::unknown_modified); methodData()->set_eflag(MethodData::estimated); return; } if (iid != vmIntrinsics::_none) success = compute_escape_for_intrinsic(iid); else { success = do_analysis(); } // don't store interprocedural escape information if it introduces // dependencies or if method data is empty // if (!has_dependencies() && !methodData()->is_empty()) { for (i = 0; i < _arg_size; i++) { if (_arg_local.test(i)) { assert(_arg_stack.test(i), "inconsistent escape info"); 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); } methodData()->set_arg_modified(i, _arg_modified[i]); } if (_return_local) { methodData()->set_eflag(MethodData::return_local); } if (_return_allocated) { methodData()->set_eflag(MethodData::return_allocated); } if (_allocated_escapes) { methodData()->set_eflag(MethodData::allocated_escapes); } if (_unknown_modified) { methodData()->set_eflag(MethodData::unknown_modified); } methodData()->set_eflag(MethodData::estimated); } } void BCEscapeAnalyzer::read_escape_info() { assert(methodData()->has_escape_info(), "no escape info available"); // 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); _arg_modified[i] = methodData()->arg_modified(i); } _return_local = methodData()->eflag_set(MethodData::return_local); _return_allocated = methodData()->eflag_set(MethodData::return_allocated); _allocated_escapes = methodData()->eflag_set(MethodData::allocated_escapes); _unknown_modified = methodData()->eflag_set(MethodData::unknown_modified); } #ifndef PRODUCT void BCEscapeAnalyzer::dump() { tty->print("[EA] estimated escape information for"); method()->print_short_name(); tty->print_cr(has_dependencies() ? " (not stored)" : ""); tty->print(" non-escaping args: "); _arg_local.print_on(tty); tty->print(" stack-allocatable args: "); _arg_stack.print_on(tty); if (_return_local) { tty->print(" returned args: "); _arg_returned.print_on(tty); } else if (is_return_allocated()) { tty->print_cr(" return allocated value"); } else { tty->print_cr(" return non-local value"); } tty->print(" modified args: "); for (int i = 0; i < _arg_size; i++) { if (_arg_modified[i] == 0) tty->print(" 0"); else tty->print(" 0x%x", _arg_modified[i]); } tty->cr(); tty->print(" flags: "); if (_return_allocated) tty->print(" return_allocated"); if (_allocated_escapes) tty->print(" allocated_escapes"); if (_unknown_modified) tty->print(" unknown_modified"); tty->cr(); } #endif BCEscapeAnalyzer::BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent) : _conservative(method == NULL || !EstimateArgEscape) , _arena(CURRENT_ENV->arena()) , _method(method) , _methodData(method ? method->method_data() : NULL) , _arg_size(method ? method->arg_size() : 0) , _arg_local(_arena) , _arg_stack(_arena) , _arg_returned(_arena) , _dirty(_arena) , _return_local(false) , _return_allocated(false) , _allocated_escapes(false) , _unknown_modified(false) , _dependencies(_arena, 4, 0, NULL) , _parent(parent) , _level(parent == NULL ? 0 : parent->level() + 1) { if (!_conservative) { _arg_local.Clear(); _arg_stack.Clear(); _arg_returned.Clear(); _dirty.Clear(); Arena* arena = CURRENT_ENV->arena(); _arg_modified = (uint *) arena->Amalloc(_arg_size * sizeof(uint)); Copy::zero_to_bytes(_arg_modified, _arg_size * sizeof(uint)); if (methodData() == NULL) return; bool printit = _method->should_print_assembly(); if (methodData()->has_escape_info()) { TRACE_BCEA(2, tty->print_cr("[EA] Reading previous results for %s.%s", method->holder()->name()->as_utf8(), method->name()->as_utf8())); read_escape_info(); } else { TRACE_BCEA(2, tty->print_cr("[EA] computing results for %s.%s", method->holder()->name()->as_utf8(), method->name()->as_utf8())); compute_escape_info(); methodData()->update_escape_info(); } #ifndef PRODUCT if (BCEATraceLevel >= 3) { // dump escape information dump(); } #endif } } void BCEscapeAnalyzer::copy_dependencies(Dependencies *deps) { if (ciEnv::current()->jvmti_can_hotswap_or_post_breakpoint()) { // Also record evol dependencies so redefinition of the // callee will trigger recompilation. deps->assert_evol_method(method()); } for (int i = 0; i < _dependencies.length(); i+=2) { ciKlass *k = _dependencies.at(i)->as_klass(); ciMethod *m = _dependencies.at(i+1)->as_method(); deps->assert_unique_concrete_method(k, m); } }