# HG changeset patch # User adlertz # Date 1382556148 0 # Node ID 97d4006624264ef93b3c79725a3ea3d763284ef5 # Parent 4c9115774c8e7abf57502c8bd6a4a3bab9b43322# Parent b2ee5dc63353db84ba5c0f2fef6dd1e6b2275271 Merge diff -r 4c9115774c8e -r 97d400662426 make/jprt.properties --- a/make/jprt.properties Wed Oct 23 10:44:12 2013 +0200 +++ b/make/jprt.properties Wed Oct 23 19:22:28 2013 +0000 @@ -24,12 +24,7 @@ # Properties for jprt -# All build result bundles are full jdks, so the 64bit testing does not -# need the 32bit sibling bundle installed. -# Note: If the hotspot/make/Makefile changed to only bundle the 64bit files -# when bundling 64bit, and stripped out the 64bit files from any 32bit -# bundles, then this setting would be need to be "true". - +# All build result bundles are full jdks. jprt.need.sibling.build=false # At submit time, the release supplied will be in jprt.submit.release @@ -52,21 +47,11 @@ # sparc etc. # Define the Solaris platforms we want for the various releases -jprt.my.solaris.sparc.jdk8=solaris_sparc_5.10 -jprt.my.solaris.sparc.jdk7=solaris_sparc_5.10 -jprt.my.solaris.sparc.jdk7u8=${jprt.my.solaris.sparc.jdk7} -jprt.my.solaris.sparc=${jprt.my.solaris.sparc.${jprt.tools.default.release}} - jprt.my.solaris.sparcv9.jdk8=solaris_sparcv9_5.10 jprt.my.solaris.sparcv9.jdk7=solaris_sparcv9_5.10 jprt.my.solaris.sparcv9.jdk7u8=${jprt.my.solaris.sparcv9.jdk7} jprt.my.solaris.sparcv9=${jprt.my.solaris.sparcv9.${jprt.tools.default.release}} -jprt.my.solaris.i586.jdk8=solaris_i586_5.10 -jprt.my.solaris.i586.jdk7=solaris_i586_5.10 -jprt.my.solaris.i586.jdk7u8=${jprt.my.solaris.i586.jdk7} -jprt.my.solaris.i586=${jprt.my.solaris.i586.${jprt.tools.default.release}} - jprt.my.solaris.x64.jdk8=solaris_x64_5.10 jprt.my.solaris.x64.jdk7=solaris_x64_5.10 jprt.my.solaris.x64.jdk7u8=${jprt.my.solaris.x64.jdk7} @@ -133,9 +118,7 @@ # Standard list of jprt build targets for this source tree jprt.build.targets.standard= \ - ${jprt.my.solaris.sparc}-{product|fastdebug}, \ ${jprt.my.solaris.sparcv9}-{product|fastdebug|optimized}, \ - ${jprt.my.solaris.i586}-{product|fastdebug}, \ ${jprt.my.solaris.x64}-{product|fastdebug}, \ ${jprt.my.linux.i586}-{product|fastdebug}, \ ${jprt.my.linux.x64}-{product|fastdebug|optimized}, \ @@ -145,7 +128,6 @@ ${jprt.my.linux.armvh}-{product|fastdebug} jprt.build.targets.open= \ - ${jprt.my.solaris.i586}-{productOpen}, \ ${jprt.my.solaris.x64}-{debugOpen}, \ ${jprt.my.linux.x64}-{productOpen} @@ -168,31 +150,6 @@ # Subset lists of test targets for this source tree -jprt.my.solaris.sparc.test.targets= \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jvm98, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jvm98_nontiered, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-scimark, \ - ${jprt.my.solaris.sparc}-product-{c1|c2}-runThese, \ - ${jprt.my.solaris.sparc}-fastdebug-c1-runThese_Xshare, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_SerialGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_ParallelGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_ParNewGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_CMS, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_G1, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCBasher_ParOldGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_SerialGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_ParallelGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_ParNewGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_CMS, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_G1, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-GCOld_ParOldGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-c2-jbb_default_nontiered, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_SerialGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_ParallelGC, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_CMS, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_G1, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-{c1|c2}-jbb_ParOldGC - jprt.my.solaris.sparcv9.test.targets= \ ${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jvm98, \ ${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-jvm98_nontiered, \ @@ -242,37 +199,6 @@ ${jprt.my.solaris.x64}-{product|fastdebug}-c2-GCOld_G1, \ ${jprt.my.solaris.x64}-{product|fastdebug}-c2-GCOld_ParOldGC -jprt.my.solaris.i586.test.targets= \ - ${jprt.my.solaris.i586}-{product|fastdebug}-{c1|c2}-jvm98, \ - ${jprt.my.solaris.i586}-{product|fastdebug}-c2-jvm98_nontiered, \ - ${jprt.my.solaris.i586}-{product|fastdebug}-{c1|c2}-scimark, \ - ${jprt.my.solaris.i586}-product-{c1|c2}-runThese_Xcomp, \ - ${jprt.my.solaris.i586}-fastdebug-c1-runThese_Xcomp, \ - ${jprt.my.solaris.i586}-fastdebug-c1-runThese_Xshare, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_SerialGC, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_ParallelGC, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_ParNewGC, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_CMS, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_G1, \ - ${jprt.my.solaris.i586}-product-c1-GCBasher_ParOldGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_SerialGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_ParallelGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_ParNewGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_CMS, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_G1, \ - ${jprt.my.solaris.i586}-fastdebug-c2-GCBasher_ParOldGC, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_SerialGC, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_ParallelGC, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_ParNewGC, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_CMS, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_G1, \ - ${jprt.my.solaris.i586}-product-c1-GCOld_ParOldGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-jbb_default_nontiered, \ - ${jprt.my.solaris.i586}-fastdebug-c2-jbb_ParallelGC, \ - ${jprt.my.solaris.i586}-fastdebug-c2-jbb_CMS, \ - ${jprt.my.solaris.i586}-fastdebug-c2-jbb_G1, \ - ${jprt.my.solaris.i586}-fastdebug-c2-jbb_ParOldGC - jprt.my.linux.i586.test.targets = \ ${jprt.my.linux.i586}-{product|fastdebug}-{c1|c2}-jvm98, \ ${jprt.my.linux.i586}-{product|fastdebug}-c2-jvm98_nontiered, \ @@ -395,7 +321,6 @@ # Some basic "smoke" tests for OpenJDK builds jprt.test.targets.open = \ ${jprt.my.solaris.x64}-{productOpen|fastdebugOpen}-c2-jvm98, \ - ${jprt.my.solaris.i586}-{productOpen|fastdebugOpen}-c2-jvm98, \ ${jprt.my.linux.x64}-{productOpen|fastdebugOpen}-c2-jvm98 # Testing for actual embedded builds is different to standard @@ -407,9 +332,7 @@ jprt.test.targets.standard = \ ${jprt.my.linux.i586.test.targets.embedded}, \ - ${jprt.my.solaris.sparc.test.targets}, \ ${jprt.my.solaris.sparcv9.test.targets}, \ - ${jprt.my.solaris.i586.test.targets}, \ ${jprt.my.solaris.x64.test.targets}, \ ${jprt.my.linux.i586.test.targets}, \ ${jprt.my.linux.x64.test.targets}, \ @@ -420,15 +343,12 @@ jprt.test.targets.embedded= \ ${jprt.my.linux.i586.test.targets.embedded}, \ - ${jprt.my.solaris.sparc.test.targets}, \ ${jprt.my.solaris.sparcv9.test.targets}, \ - ${jprt.my.solaris.i586.test.targets}, \ ${jprt.my.solaris.x64.test.targets}, \ ${jprt.my.linux.x64.test.targets}, \ ${jprt.my.windows.i586.test.targets}, \ ${jprt.my.windows.x64.test.targets} - jprt.test.targets.jdk8=${jprt.test.targets.standard} jprt.test.targets.jdk7=${jprt.test.targets.standard} jprt.test.targets.jdk7u8=${jprt.test.targets.jdk7} @@ -439,15 +359,11 @@ #jprt.make.rule.test.targets=*-product-*-packtest jprt.make.rule.test.targets.standard.client = \ - ${jprt.my.solaris.sparc}-*-c1-clienttest, \ - ${jprt.my.solaris.i586}-*-c1-clienttest, \ ${jprt.my.linux.i586}-*-c1-clienttest, \ ${jprt.my.windows.i586}-*-c1-clienttest jprt.make.rule.test.targets.standard.server = \ - ${jprt.my.solaris.sparc}-*-c2-servertest, \ ${jprt.my.solaris.sparcv9}-*-c2-servertest, \ - ${jprt.my.solaris.i586}-*-c2-servertest, \ ${jprt.my.solaris.x64}-*-c2-servertest, \ ${jprt.my.linux.i586}-*-c2-servertest, \ ${jprt.my.linux.x64}-*-c2-servertest, \ @@ -456,9 +372,7 @@ ${jprt.my.windows.x64}-*-c2-servertest jprt.make.rule.test.targets.standard.internalvmtests = \ - ${jprt.my.solaris.sparc}-fastdebug-c2-internalvmtests, \ ${jprt.my.solaris.sparcv9}-fastdebug-c2-internalvmtests, \ - ${jprt.my.solaris.i586}-fastdebug-c2-internalvmtests, \ ${jprt.my.solaris.x64}-fastdebug-c2-internalvmtests, \ ${jprt.my.linux.i586}-fastdebug-c2-internalvmtests, \ ${jprt.my.linux.x64}-fastdebug-c2-internalvmtests, \ @@ -467,16 +381,12 @@ ${jprt.my.windows.x64}-fastdebug-c2-internalvmtests jprt.make.rule.test.targets.standard.wbapi = \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.solaris.sparcv9}-{product|fastdebug}-c2-wbapitest, \ - ${jprt.my.solaris.i586}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.solaris.x64}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.linux.i586}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.linux.x64}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.windows.i586}-{product|fastdebug}-c2-wbapitest, \ ${jprt.my.windows.x64}-{product|fastdebug}-c2-wbapitest, \ - ${jprt.my.solaris.sparc}-{product|fastdebug}-c1-wbapitest, \ - ${jprt.my.solaris.i586}-{product|fastdebug}-c1-wbapitest, \ ${jprt.my.linux.i586}-{product|fastdebug}-c1-wbapitest, \ ${jprt.my.windows.i586}-{product|fastdebug}-c1-wbapitest diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/bytecodeInterpreter_x86.cpp --- a/src/cpu/x86/vm/bytecodeInterpreter_x86.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/bytecodeInterpreter_x86.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -40,11 +40,8 @@ #include "runtime/synchronizer.hpp" #include "runtime/vframeArray.hpp" #include "utilities/debug.hpp" -#ifdef TARGET_ARCH_MODEL_x86_32 -# include "interp_masm_x86_32.hpp" -#endif -#ifdef TARGET_ARCH_MODEL_x86_64 -# include "interp_masm_x86_64.hpp" +#ifdef TARGET_ARCH_x86 +# include "interp_masm_x86.hpp" #endif #ifdef CC_INTERP diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/globals_x86.hpp --- a/src/cpu/x86/vm/globals_x86.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/globals_x86.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -79,7 +79,7 @@ // GC Ergo Flags define_pd_global(uintx, CMSYoungGenPerWorker, 64*M); // default max size of CMS young gen, per GC worker thread -define_pd_global(uintx, TypeProfileLevel, 11); +define_pd_global(uintx, TypeProfileLevel, 111); #define ARCH_FLAGS(develop, product, diagnostic, experimental, notproduct) \ \ diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/interp_masm_x86.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -0,0 +1,229 @@ +/* + * Copyright (c) 1997, 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 "interp_masm_x86.hpp" +#include "interpreter/interpreter.hpp" +#include "oops/methodData.hpp" + +#ifndef CC_INTERP +void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { + Label update, next, none; + + verify_oop(obj); + + testptr(obj, obj); + jccb(Assembler::notZero, update); + orptr(mdo_addr, TypeEntries::null_seen); + jmpb(next); + + bind(update); + load_klass(obj, obj); + + xorptr(obj, mdo_addr); + testptr(obj, TypeEntries::type_klass_mask); + jccb(Assembler::zero, next); // klass seen before, nothing to + // do. The unknown bit may have been + // set already but no need to check. + + testptr(obj, TypeEntries::type_unknown); + jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. + + cmpptr(mdo_addr, 0); + jccb(Assembler::equal, none); + cmpptr(mdo_addr, TypeEntries::null_seen); + jccb(Assembler::equal, none); + // There is a chance that the checks above (re-reading profiling + // data from memory) fail if another thread has just set the + // profiling to this obj's klass + xorptr(obj, mdo_addr); + testptr(obj, TypeEntries::type_klass_mask); + jccb(Assembler::zero, next); + + // different than before. Cannot keep accurate profile. + orptr(mdo_addr, TypeEntries::type_unknown); + jmpb(next); + + bind(none); + // first time here. Set profile type. + movptr(mdo_addr, obj); + + bind(next); +} + +void InterpreterMacroAssembler::profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual) { + if (!ProfileInterpreter) { + return; + } + + if (MethodData::profile_arguments() || MethodData::profile_return()) { + Label profile_continue; + + test_method_data_pointer(mdp, profile_continue); + + int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); + + cmpb(Address(mdp, in_bytes(DataLayout::tag_offset()) - off_to_start), is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); + jcc(Assembler::notEqual, profile_continue); + + if (MethodData::profile_arguments()) { + Label done; + int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); + addptr(mdp, off_to_args); + + for (int i = 0; i < TypeProfileArgsLimit; i++) { + if (i > 0 || MethodData::profile_return()) { + // If return value type is profiled we may have no argument to profile + movptr(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); + subl(tmp, i*TypeStackSlotEntries::per_arg_count()); + cmpl(tmp, TypeStackSlotEntries::per_arg_count()); + jcc(Assembler::less, done); + } + movptr(tmp, Address(callee, Method::const_offset())); + load_unsigned_short(tmp, Address(tmp, ConstMethod::size_of_parameters_offset())); + // stack offset o (zero based) from the start of the argument + // list, for n arguments translates into offset n - o - 1 from + // the end of the argument list + subptr(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args)); + subl(tmp, 1); + Address arg_addr = argument_address(tmp); + movptr(tmp, arg_addr); + + Address mdo_arg_addr(mdp, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); + profile_obj_type(tmp, mdo_arg_addr); + + int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); + addptr(mdp, to_add); + off_to_args += to_add; + } + + if (MethodData::profile_return()) { + movptr(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); + subl(tmp, TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count()); + } + + bind(done); + + if (MethodData::profile_return()) { + // We're right after the type profile for the last + // argument. tmp is the number of cell left in the + // CallTypeData/VirtualCallTypeData to reach its end. Non null + // if there's a return to profile. + assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); + shll(tmp, exact_log2(DataLayout::cell_size)); + addptr(mdp, tmp); + } + movptr(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp); + } else { + assert(MethodData::profile_return(), "either profile call args or call ret"); + update_mdp_by_constant(mdp, in_bytes(ReturnTypeEntry::size())); + } + + // mdp points right after the end of the + // CallTypeData/VirtualCallTypeData, right after the cells for the + // return value type if there's one + + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, Register tmp) { + assert_different_registers(mdp, ret, tmp, _bcp_register); + if (ProfileInterpreter && MethodData::profile_return()) { + Label profile_continue, done; + + test_method_data_pointer(mdp, profile_continue); + + if (MethodData::profile_return_jsr292_only()) { + // If we don't profile all invoke bytecodes we must make sure + // it's a bytecode we indeed profile. We can't go back to the + // begining of the ProfileData we intend to update to check its + // type because we're right after it and we don't known its + // length + Label do_profile; + cmpb(Address(_bcp_register, 0), Bytecodes::_invokedynamic); + jcc(Assembler::equal, do_profile); + cmpb(Address(_bcp_register, 0), Bytecodes::_invokehandle); + jcc(Assembler::equal, do_profile); + get_method(tmp); + cmpb(Address(tmp, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_compiledLambdaForm); + jcc(Assembler::notEqual, profile_continue); + + bind(do_profile); + } + + Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size())); + mov(tmp, ret); + profile_obj_type(tmp, mdo_ret_addr); + + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_parameters_type(Register mdp, Register tmp1, Register tmp2) { + if (ProfileInterpreter && MethodData::profile_parameters()) { + Label profile_continue, done; + + test_method_data_pointer(mdp, profile_continue); + + // Load the offset of the area within the MDO used for + // parameters. If it's negative we're not profiling any parameters + movl(tmp1, Address(mdp, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset()))); + testl(tmp1, tmp1); + jcc(Assembler::negative, profile_continue); + + // Compute a pointer to the area for parameters from the offset + // and move the pointer to the slot for the last + // parameters. Collect profiling from last parameter down. + // mdo start + parameters offset + array length - 1 + addptr(mdp, tmp1); + movptr(tmp1, Address(mdp, in_bytes(ArrayData::array_len_offset()))); + decrement(tmp1, TypeStackSlotEntries::per_arg_count()); + + Label loop; + bind(loop); + + int off_base = in_bytes(ParametersTypeData::stack_slot_offset(0)); + int type_base = in_bytes(ParametersTypeData::type_offset(0)); + Address::ScaleFactor per_arg_scale = Address::times(DataLayout::cell_size); + Address arg_off(mdp, tmp1, per_arg_scale, off_base); + Address arg_type(mdp, tmp1, per_arg_scale, type_base); + + // load offset on the stack from the slot for this parameter + movptr(tmp2, arg_off); + negptr(tmp2); + // read the parameter from the local area + movptr(tmp2, Address(_locals_register, tmp2, Interpreter::stackElementScale())); + + // profile the parameter + profile_obj_type(tmp2, arg_type); + + // go to next parameter + decrement(tmp1, TypeStackSlotEntries::per_arg_count()); + jcc(Assembler::positive, loop); + + bind(profile_continue); + } +} +#endif diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/cpu/x86/vm/interp_masm_x86.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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. + * + */ + +#ifndef CPU_X86_VM_INTERP_MASM_X86_HPP +#define CPU_X86_VM_INTERP_MASM_X86_HPP + +#include "asm/macroAssembler.hpp" +#include "asm/macroAssembler.inline.hpp" +#include "interpreter/invocationCounter.hpp" +#include "runtime/frame.hpp" + +// This file specializes the assember with interpreter-specific macros + + +class InterpreterMacroAssembler: public MacroAssembler { + +#ifdef TARGET_ARCH_MODEL_x86_32 +# include "interp_masm_x86_32.hpp" +#endif +#ifdef TARGET_ARCH_MODEL_x86_64 +# include "interp_masm_x86_64.hpp" +#endif + + private: + + Register _locals_register; // register that contains the pointer to the locals + Register _bcp_register; // register that contains the bcp + + public: +#ifndef CC_INTERP + void profile_obj_type(Register obj, const Address& mdo_addr); + void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual); + void profile_return_type(Register mdp, Register ret, Register tmp); + void profile_parameters_type(Register mdp, Register tmp1, Register tmp2); +#endif /* !CC_INTERP */ + +}; + +#endif // CPU_X86_VM_INTERP_MASM_X86_HPP diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86_32.cpp --- a/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/interp_masm_x86_32.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -23,7 +23,7 @@ */ #include "precompiled.hpp" -#include "interp_masm_x86_32.hpp" +#include "interp_masm_x86.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "oops/arrayOop.hpp" @@ -1046,159 +1046,6 @@ } } -void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { - Label update, next, none; - - verify_oop(obj); - - testptr(obj, obj); - jccb(Assembler::notZero, update); - orptr(mdo_addr, TypeEntries::null_seen); - jmpb(next); - - bind(update); - load_klass(obj, obj); - - xorptr(obj, mdo_addr); - testptr(obj, TypeEntries::type_klass_mask); - jccb(Assembler::zero, next); // klass seen before, nothing to - // do. The unknown bit may have been - // set already but no need to check. - - testptr(obj, TypeEntries::type_unknown); - jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. - - cmpptr(mdo_addr, 0); - jccb(Assembler::equal, none); - cmpptr(mdo_addr, TypeEntries::null_seen); - jccb(Assembler::equal, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the - // profiling to this obj's klass - xorptr(obj, mdo_addr); - testptr(obj, TypeEntries::type_klass_mask); - jccb(Assembler::zero, next); - - // different than before. Cannot keep accurate profile. - orptr(mdo_addr, TypeEntries::type_unknown); - jmpb(next); - - bind(none); - // first time here. Set profile type. - movptr(mdo_addr, obj); - - bind(next); -} - -void InterpreterMacroAssembler::profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual) { - if (!ProfileInterpreter) { - return; - } - - if (MethodData::profile_arguments() || MethodData::profile_return()) { - Label profile_continue; - - test_method_data_pointer(mdp, profile_continue); - - int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); - - cmpb(Address(mdp, in_bytes(DataLayout::tag_offset()) - off_to_start), is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); - jcc(Assembler::notEqual, profile_continue); - - if (MethodData::profile_arguments()) { - Label done; - int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); - addptr(mdp, off_to_args); - - for (int i = 0; i < TypeProfileArgsLimit; i++) { - if (i > 0 || MethodData::profile_return()) { - // If return value type is profiled we may have no argument to profile - movl(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); - subl(tmp, i*TypeStackSlotEntries::per_arg_count()); - cmpl(tmp, TypeStackSlotEntries::per_arg_count()); - jcc(Assembler::less, done); - } - movptr(tmp, Address(callee, Method::const_offset())); - load_unsigned_short(tmp, Address(tmp, ConstMethod::size_of_parameters_offset())); - // stack offset o (zero based) from the start of the argument - // list, for n arguments translates into offset n - o - 1 from - // the end of the argument list - subl(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args)); - subl(tmp, 1); - Address arg_addr = argument_address(tmp); - movptr(tmp, arg_addr); - - Address mdo_arg_addr(mdp, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); - profile_obj_type(tmp, mdo_arg_addr); - - int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); - addptr(mdp, to_add); - off_to_args += to_add; - } - - if (MethodData::profile_return()) { - movl(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); - subl(tmp, TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count()); - } - - bind(done); - - if (MethodData::profile_return()) { - // We're right after the type profile for the last - // argument. tmp is the number of cell left in the - // CallTypeData/VirtualCallTypeData to reach its end. Non null - // if there's a return to profile. - assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); - shll(tmp, exact_log2(DataLayout::cell_size)); - addptr(mdp, tmp); - } - movptr(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp); - } else { - assert(MethodData::profile_return(), "either profile call args or call ret"); - update_mdp_by_constant(mdp, in_bytes(ReturnTypeEntry::size())); - } - - // mdp points right after the end of the - // CallTypeData/VirtualCallTypeData, right after the cells for the - // return value type if there's one - - bind(profile_continue); - } -} - -void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, Register tmp) { - assert_different_registers(mdp, ret, tmp, rsi); - if (ProfileInterpreter && MethodData::profile_return()) { - Label profile_continue, done; - - test_method_data_pointer(mdp, profile_continue); - - if (MethodData::profile_return_jsr292_only()) { - // If we don't profile all invoke bytecodes we must make sure - // it's a bytecode we indeed profile. We can't go back to the - // begining of the ProfileData we intend to update to check its - // type because we're right after it and we don't known its - // length - Label do_profile; - cmpb(Address(rsi, 0), Bytecodes::_invokedynamic); - jcc(Assembler::equal, do_profile); - cmpb(Address(rsi, 0), Bytecodes::_invokehandle); - jcc(Assembler::equal, do_profile); - get_method(tmp); - cmpb(Address(tmp, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_compiledLambdaForm); - jcc(Assembler::notEqual, profile_continue); - - bind(do_profile); - } - - Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size())); - mov(tmp, ret); - profile_obj_type(tmp, mdo_ret_addr); - - bind(profile_continue); - } -} - void InterpreterMacroAssembler::profile_call(Register mdp) { if (ProfileInterpreter) { Label profile_continue; diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86_32.hpp --- a/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/interp_masm_x86_32.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -22,18 +22,6 @@ * */ -#ifndef CPU_X86_VM_INTERP_MASM_X86_32_HPP -#define CPU_X86_VM_INTERP_MASM_X86_32_HPP - -#include "asm/macroAssembler.hpp" -#include "asm/macroAssembler.inline.hpp" -#include "interpreter/invocationCounter.hpp" -#include "runtime/frame.hpp" - -// This file specializes the assember with interpreter-specific macros - - -class InterpreterMacroAssembler: public MacroAssembler { #ifndef CC_INTERP protected: // Interpreter specific version of call_VM_base @@ -59,7 +47,7 @@ #endif /* CC_INTERP */ public: - InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} + InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code), _locals_register(rdi), _bcp_register(rsi) {} void load_earlyret_value(TosState state); @@ -215,9 +203,6 @@ void profile_taken_branch(Register mdp, Register bumped_count); void profile_not_taken_branch(Register mdp); - void profile_obj_type(Register obj, const Address& mdo_addr); - void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual); - void profile_return_type(Register mdp, Register ret, Register tmp); void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, Register scratch2, @@ -236,7 +221,3 @@ // support for jvmti void notify_method_entry(); void notify_method_exit(TosState state, NotifyMethodExitMode mode); - -}; - -#endif // CPU_X86_VM_INTERP_MASM_X86_32_HPP diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86_64.cpp --- a/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/interp_masm_x86_64.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -23,7 +23,7 @@ */ #include "precompiled.hpp" -#include "interp_masm_x86_64.hpp" +#include "interp_masm_x86.hpp" #include "interpreter/interpreter.hpp" #include "interpreter/interpreterRuntime.hpp" #include "oops/arrayOop.hpp" @@ -1067,160 +1067,6 @@ } } -void InterpreterMacroAssembler::profile_obj_type(Register obj, const Address& mdo_addr) { - Label update, next, none; - - verify_oop(obj); - - testptr(obj, obj); - jccb(Assembler::notZero, update); - orptr(mdo_addr, TypeEntries::null_seen); - jmpb(next); - - bind(update); - load_klass(obj, obj); - - xorptr(obj, mdo_addr); - testptr(obj, TypeEntries::type_klass_mask); - jccb(Assembler::zero, next); // klass seen before, nothing to - // do. The unknown bit may have been - // set already but no need to check. - - testptr(obj, TypeEntries::type_unknown); - jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore. - - // There is a chance that by the time we do these checks (re-reading - // profiling data from memory) another thread has set the profling - // to this obj's klass and we set the profiling as unknow - // erroneously - cmpptr(mdo_addr, 0); - jccb(Assembler::equal, none); - cmpptr(mdo_addr, TypeEntries::null_seen); - jccb(Assembler::equal, none); - // There is a chance that the checks above (re-reading profiling - // data from memory) fail if another thread has just set the - // profiling to this obj's klass - xorptr(obj, mdo_addr); - testptr(obj, TypeEntries::type_klass_mask); - jccb(Assembler::zero, next); - - // different than before. Cannot keep accurate profile. - orptr(mdo_addr, TypeEntries::type_unknown); - jmpb(next); - - bind(none); - // first time here. Set profile type. - movptr(mdo_addr, obj); - - bind(next); -} - -void InterpreterMacroAssembler::profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual) { - if (!ProfileInterpreter) { - return; - } - - if (MethodData::profile_arguments() || MethodData::profile_return()) { - Label profile_continue; - - test_method_data_pointer(mdp, profile_continue); - - int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); - - cmpb(Address(mdp, in_bytes(DataLayout::tag_offset()) - off_to_start), is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); - jcc(Assembler::notEqual, profile_continue); - - if (MethodData::profile_arguments()) { - Label done; - int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); - addptr(mdp, off_to_args); - - for (int i = 0; i < TypeProfileArgsLimit; i++) { - if (i > 0 || MethodData::profile_return()) { - // If return value type is profiled we may have no argument to profile - movq(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); - subl(tmp, i*TypeStackSlotEntries::per_arg_count()); - cmpl(tmp, TypeStackSlotEntries::per_arg_count()); - jcc(Assembler::less, done); - } - movptr(tmp, Address(callee, Method::const_offset())); - load_unsigned_short(tmp, Address(tmp, ConstMethod::size_of_parameters_offset())); - subq(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args)); - subl(tmp, 1); - Address arg_addr = argument_address(tmp); - movptr(tmp, arg_addr); - - Address mdo_arg_addr(mdp, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args); - profile_obj_type(tmp, mdo_arg_addr); - - int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); - addptr(mdp, to_add); - off_to_args += to_add; - } - - if (MethodData::profile_return()) { - movq(tmp, Address(mdp, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args)); - subl(tmp, TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count()); - } - - bind(done); - - if (MethodData::profile_return()) { - // We're right after the type profile for the last - // argument. tmp is the number of cell left in the - // CallTypeData/VirtualCallTypeData to reach its end. Non null - // if there's a return to profile. - assert(ReturnTypeEntry::static_cell_count() < TypeStackSlotEntries::per_arg_count(), "can't move past ret type"); - shll(tmp, exact_log2(DataLayout::cell_size)); - addptr(mdp, tmp); - } - movptr(Address(rbp, frame::interpreter_frame_mdx_offset * wordSize), mdp); - } else { - assert(MethodData::profile_return(), "either profile call args or call ret"); - update_mdp_by_constant(mdp, in_bytes(ReturnTypeEntry::size())); - } - - // mdp points right after the end of the - // CallTypeData/VirtualCallTypeData, right after the cells for the - // return value type if there's one - - bind(profile_continue); - } -} - -void InterpreterMacroAssembler::profile_return_type(Register mdp, Register ret, Register tmp) { - assert_different_registers(mdp, ret, tmp, r13); - if (ProfileInterpreter && MethodData::profile_return()) { - Label profile_continue, done; - - test_method_data_pointer(mdp, profile_continue); - - if (MethodData::profile_return_jsr292_only()) { - // If we don't profile all invoke bytecodes we must make sure - // it's a bytecode we indeed profile. We can't go back to the - // begining of the ProfileData we intend to update to check its - // type because we're right after it and we don't known its - // length - Label do_profile; - cmpb(Address(r13, 0), Bytecodes::_invokedynamic); - jcc(Assembler::equal, do_profile); - cmpb(Address(r13, 0), Bytecodes::_invokehandle); - jcc(Assembler::equal, do_profile); - get_method(tmp); - cmpb(Address(tmp, Method::intrinsic_id_offset_in_bytes()), vmIntrinsics::_compiledLambdaForm); - jcc(Assembler::notEqual, profile_continue); - - bind(do_profile); - } - - Address mdo_ret_addr(mdp, -in_bytes(ReturnTypeEntry::size())); - mov(tmp, ret); - profile_obj_type(tmp, mdo_ret_addr); - - bind(profile_continue); - } -} - void InterpreterMacroAssembler::profile_call(Register mdp) { if (ProfileInterpreter) { Label profile_continue; diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/interp_masm_x86_64.hpp --- a/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/interp_masm_x86_64.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -22,18 +22,6 @@ * */ -#ifndef CPU_X86_VM_INTERP_MASM_X86_64_HPP -#define CPU_X86_VM_INTERP_MASM_X86_64_HPP - -#include "asm/macroAssembler.hpp" -#include "asm/macroAssembler.inline.hpp" -#include "interpreter/invocationCounter.hpp" -#include "runtime/frame.hpp" - -// This file specializes the assember with interpreter-specific macros - - -class InterpreterMacroAssembler: public MacroAssembler { #ifndef CC_INTERP protected: // Interpreter specific version of call_VM_base @@ -55,7 +43,7 @@ #endif // CC_INTERP public: - InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code) {} + InterpreterMacroAssembler(CodeBuffer* code) : MacroAssembler(code), _locals_register(r14), _bcp_register(r13) {} void load_earlyret_value(TosState state); @@ -224,9 +212,6 @@ void profile_taken_branch(Register mdp, Register bumped_count); void profile_not_taken_branch(Register mdp); - void profile_obj_type(Register obj, const Address& mdo_addr); - void profile_arguments_type(Register mdp, Register callee, Register tmp, bool is_virtual); - void profile_return_type(Register mdp, Register ret, Register tmp); void profile_call(Register mdp); void profile_final_call(Register mdp); void profile_virtual_call(Register receiver, Register mdp, @@ -253,6 +238,3 @@ // support for jvmti/dtrace void notify_method_entry(); void notify_method_exit(TosState state, NotifyMethodExitMode mode); -}; - -#endif // CPU_X86_VM_INTERP_MASM_X86_64_HPP diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/register_definitions_x86.cpp --- a/src/cpu/x86/vm/register_definitions_x86.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/register_definitions_x86.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -26,11 +26,8 @@ #include "asm/assembler.hpp" #include "asm/register.hpp" #include "register_x86.hpp" -#ifdef TARGET_ARCH_MODEL_x86_32 -# include "interp_masm_x86_32.hpp" -#endif -#ifdef TARGET_ARCH_MODEL_x86_64 -# include "interp_masm_x86_64.hpp" +#ifdef TARGET_ARCH_x86 +# include "interp_masm_x86.hpp" #endif REGISTER_DEFINITION(Register, noreg); diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/templateInterpreter_x86_32.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/templateInterpreter_x86_32.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1490,6 +1490,7 @@ in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); __ movbool(do_not_unlock_if_synchronized, true); + __ profile_parameters_type(rax, rcx, rdx); // increment invocation count & check for overflow Label invocation_counter_overflow; Label profile_method; diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/templateInterpreter_x86_64.cpp --- a/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/templateInterpreter_x86_64.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1497,6 +1497,7 @@ in_bytes(JavaThread::do_not_unlock_if_synchronized_offset())); __ movbool(do_not_unlock_if_synchronized, true); + __ profile_parameters_type(rax, rcx, rdx); // increment invocation count & check for overflow Label invocation_counter_overflow; Label profile_method; diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/vtableStubs_x86_32.cpp --- a/src/cpu/x86/vm/vtableStubs_x86_32.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/vtableStubs_x86_32.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" #include "code/vtableStubs.hpp" -#include "interp_masm_x86_32.hpp" +#include "interp_masm_x86.hpp" #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" diff -r 4c9115774c8e -r 97d400662426 src/cpu/x86/vm/vtableStubs_x86_64.cpp --- a/src/cpu/x86/vm/vtableStubs_x86_64.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/cpu/x86/vm/vtableStubs_x86_64.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -25,7 +25,7 @@ #include "precompiled.hpp" #include "asm/macroAssembler.hpp" #include "code/vtableStubs.hpp" -#include "interp_masm_x86_64.hpp" +#include "interp_masm_x86.hpp" #include "memory/resourceArea.hpp" #include "oops/instanceKlass.hpp" #include "oops/klassVtable.hpp" diff -r 4c9115774c8e -r 97d400662426 src/share/vm/c1/c1_Compilation.hpp --- a/src/share/vm/c1/c1_Compilation.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/c1/c1_Compilation.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -238,7 +238,18 @@ return env()->comp_level() == CompLevel_full_profile && C1UpdateMethodData && C1ProfileCheckcasts; } - + bool profile_parameters() { + return env()->comp_level() == CompLevel_full_profile && + C1UpdateMethodData && MethodData::profile_parameters(); + } + bool profile_arguments() { + return env()->comp_level() == CompLevel_full_profile && + C1UpdateMethodData && MethodData::profile_arguments(); + } + bool profile_return() { + return env()->comp_level() == CompLevel_full_profile && + C1UpdateMethodData && MethodData::profile_return(); + } // will compilation make optimistic assumptions that might lead to // deoptimization and that the runtime will account for? bool is_optimistic() const { diff -r 4c9115774c8e -r 97d400662426 src/share/vm/c1/c1_GraphBuilder.cpp --- a/src/share/vm/c1/c1_GraphBuilder.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/c1/c1_GraphBuilder.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1470,7 +1470,7 @@ set_state(state()->caller_state()->copy_for_parsing()); if (x != NULL) { state()->push(x->type(), x); - if (profile_calls() && MethodData::profile_return() && x->type()->is_object_kind()) { + if (profile_return() && x->type()->is_object_kind()) { ciMethod* caller = state()->scope()->method(); ciMethodData* md = caller->method_data_or_null(); ciProfileData* data = md->bci_to_data(invoke_bci); @@ -1672,15 +1672,23 @@ } // How many arguments do we want to profile? -Values* GraphBuilder::args_list_for_profiling(int& start, bool may_have_receiver) { +Values* GraphBuilder::args_list_for_profiling(ciMethod* target, int& start, bool may_have_receiver) { int n = 0; - assert(start == 0, "should be initialized"); - if (MethodData::profile_arguments()) { + bool has_receiver = may_have_receiver && Bytecodes::has_receiver(method()->java_code_at_bci(bci())); + start = has_receiver ? 1 : 0; + if (profile_arguments()) { ciProfileData* data = method()->method_data()->bci_to_data(bci()); if (data->is_CallTypeData() || data->is_VirtualCallTypeData()) { n = data->is_CallTypeData() ? data->as_CallTypeData()->number_of_arguments() : data->as_VirtualCallTypeData()->number_of_arguments(); - bool has_receiver = may_have_receiver && Bytecodes::has_receiver(method()->java_code_at_bci(bci())); - start = has_receiver ? 1 : 0; + } + } + // If we are inlining then we need to collect arguments to profile parameters for the target + if (profile_parameters() && target != NULL) { + if (target->method_data() != NULL && target->method_data()->parameters_type_data() != NULL) { + // The receiver is profiled on method entry so it's included in + // the number of parameters but here we're only interested in + // actual arguments. + n = MAX2(n, target->method_data()->parameters_type_data()->number_of_parameters() - start); } } if (n > 0) { @@ -1690,9 +1698,9 @@ } // Collect arguments that we want to profile in a list -Values* GraphBuilder::collect_args_for_profiling(Values* args, bool may_have_receiver) { +Values* GraphBuilder::collect_args_for_profiling(Values* args, ciMethod* target, bool may_have_receiver) { int start = 0; - Values* obj_args = args_list_for_profiling(start, may_have_receiver); + Values* obj_args = args_list_for_profiling(target, start, may_have_receiver); if (obj_args == NULL) { return NULL; } @@ -2006,7 +2014,7 @@ } else if (exact_target != NULL) { target_klass = exact_target->holder(); } - profile_call(target, recv, target_klass, collect_args_for_profiling(args, false), false); + profile_call(target, recv, target_klass, collect_args_for_profiling(args, NULL, false), false); } } @@ -2021,7 +2029,7 @@ push(result_type, result); } } - if (profile_calls() && MethodData::profile_return() && result_type->is_object_kind()) { + if (profile_return() && result_type->is_object_kind()) { profile_return_type(result, target); } } @@ -3561,7 +3569,7 @@ recv = args->at(0); null_check(recv); } - profile_call(callee, recv, NULL, collect_args_for_profiling(args, true), true); + profile_call(callee, recv, NULL, collect_args_for_profiling(args, callee, true), true); } } } @@ -3572,7 +3580,7 @@ Value value = append_split(result); if (result_type != voidType) push(result_type, value); - if (callee != method() && profile_calls() && MethodData::profile_return() && result_type->is_object_kind()) { + if (callee != method() && profile_return() && result_type->is_object_kind()) { profile_return_type(result, callee); } @@ -3820,7 +3828,7 @@ if (profile_calls()) { int start = 0; - Values* obj_args = args_list_for_profiling(start, has_receiver); + Values* obj_args = args_list_for_profiling(callee, start, has_receiver); if (obj_args != NULL) { int s = obj_args->size(); // if called through method handle invoke, some arguments may have been popped diff -r 4c9115774c8e -r 97d400662426 src/share/vm/c1/c1_GraphBuilder.hpp --- a/src/share/vm/c1/c1_GraphBuilder.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/c1/c1_GraphBuilder.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -386,9 +386,12 @@ bool profile_calls() { return _compilation->profile_calls(); } bool profile_inlined_calls() { return _compilation->profile_inlined_calls(); } bool profile_checkcasts() { return _compilation->profile_checkcasts(); } + bool profile_parameters() { return _compilation->profile_parameters(); } + bool profile_arguments() { return _compilation->profile_arguments(); } + bool profile_return() { return _compilation->profile_return(); } - Values* args_list_for_profiling(int& start, bool may_have_receiver); - Values* collect_args_for_profiling(Values* args, bool may_have_receiver); + Values* args_list_for_profiling(ciMethod* target, int& start, bool may_have_receiver); + Values* collect_args_for_profiling(Values* args, ciMethod* target, bool may_have_receiver); public: NOT_PRODUCT(void print_stats();) diff -r 4c9115774c8e -r 97d400662426 src/share/vm/c1/c1_LIRGenerator.cpp --- a/src/share/vm/c1/c1_LIRGenerator.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/c1/c1_LIRGenerator.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -2647,6 +2647,39 @@ return result; } +// profile parameters on entry to the root of the compilation +void LIRGenerator::profile_parameters(Base* x) { + if (compilation()->profile_parameters()) { + CallingConvention* args = compilation()->frame_map()->incoming_arguments(); + ciMethodData* md = scope()->method()->method_data_or_null(); + assert(md != NULL, "Sanity"); + + if (md->parameters_type_data() != NULL) { + ciParametersTypeData* parameters_type_data = md->parameters_type_data(); + ciTypeStackSlotEntries* parameters = parameters_type_data->parameters(); + LIR_Opr mdp = LIR_OprFact::illegalOpr; + for (int java_index = 0, i = 0, j = 0; j < parameters_type_data->number_of_parameters(); i++) { + LIR_Opr src = args->at(i); + assert(!src->is_illegal(), "check"); + BasicType t = src->type(); + if (t == T_OBJECT || t == T_ARRAY) { + intptr_t profiled_k = parameters->type(j); + Local* local = x->state()->local_at(java_index)->as_Local(); + ciKlass* exact = profile_arg_type(md, md->byte_offset_of_slot(parameters_type_data, ParametersTypeData::type_offset(0)), + in_bytes(ParametersTypeData::type_offset(j)) - in_bytes(ParametersTypeData::type_offset(0)), + profiled_k, local, mdp, false, local->declared_type()->as_klass()); + // If the profile is known statically set it once for all and do not emit any code + if (exact != NULL) { + md->set_parameter_type(j, exact); + } + j++; + } + java_index += type2size[t]; + } + } + } +} + void LIRGenerator::do_Base(Base* x) { __ std_entry(LIR_OprFact::illegalOpr); // Emit moves from physical registers / stack slots to virtual registers @@ -2722,6 +2755,7 @@ // increment invocation counters if needed if (!method()->is_accessor()) { // Accessors do not have MDOs, so no counting. + profile_parameters(x); CodeEmitInfo* info = new CodeEmitInfo(scope()->start()->state()->copy(ValueStack::StateBefore, SynchronizationEntryBCI), NULL, false); increment_invocation_counter(info); } @@ -3081,11 +3115,12 @@ } void LIRGenerator::profile_arguments(ProfileCall* x) { - if (MethodData::profile_arguments()) { + if (compilation()->profile_arguments()) { int bci = x->bci_of_invoke(); ciMethodData* md = x->method()->method_data_or_null(); ciProfileData* data = md->bci_to_data(bci); - if (data->is_CallTypeData() || data->is_VirtualCallTypeData()) { + if ((data->is_CallTypeData() && data->as_CallTypeData()->has_arguments()) || + (data->is_VirtualCallTypeData() && data->as_VirtualCallTypeData()->has_arguments())) { ByteSize extra = data->is_CallTypeData() ? CallTypeData::args_data_offset() : VirtualCallTypeData::args_data_offset(); int base_offset = md->byte_offset_of_slot(data, extra); LIR_Opr mdp = LIR_OprFact::illegalOpr; @@ -3111,6 +3146,71 @@ md->set_argument_type(bci, i, exact); } } + } else { +#ifdef ASSERT + Bytecodes::Code code = x->method()->raw_code_at_bci(x->bci_of_invoke()); + int n = x->nb_profiled_args(); + assert(MethodData::profile_parameters() && x->inlined() && + ((code == Bytecodes::_invokedynamic && n <= 1) || (code == Bytecodes::_invokehandle && n <= 2)), + "only at JSR292 bytecodes"); +#endif + } + } +} + +// profile parameters on entry to an inlined method +void LIRGenerator::profile_parameters_at_call(ProfileCall* x) { + if (compilation()->profile_parameters() && x->inlined()) { + ciMethodData* md = x->callee()->method_data_or_null(); + if (md != NULL) { + ciParametersTypeData* parameters_type_data = md->parameters_type_data(); + if (parameters_type_data != NULL) { + ciTypeStackSlotEntries* parameters = parameters_type_data->parameters(); + LIR_Opr mdp = LIR_OprFact::illegalOpr; + bool has_receiver = !x->callee()->is_static(); + ciSignature* sig = x->callee()->signature(); + ciSignatureStream sig_stream(sig, has_receiver ? x->callee()->holder() : NULL); + int i = 0; // to iterate on the Instructions + Value arg = x->recv(); + bool not_null = false; + int bci = x->bci_of_invoke(); + Bytecodes::Code bc = x->method()->java_code_at_bci(bci); + // The first parameter is the receiver so that's what we start + // with if it exists. On exception if method handle call to + // virtual method has receiver in the args list + if (arg == NULL || !Bytecodes::has_receiver(bc)) { + i = 1; + arg = x->profiled_arg_at(0); + not_null = !x->arg_needs_null_check(0); + } + int k = 0; // to iterate on the profile data + for (;;) { + intptr_t profiled_k = parameters->type(k); + ciKlass* exact = profile_arg_type(md, md->byte_offset_of_slot(parameters_type_data, ParametersTypeData::type_offset(0)), + in_bytes(ParametersTypeData::type_offset(k)) - in_bytes(ParametersTypeData::type_offset(0)), + profiled_k, arg, mdp, not_null, sig_stream.next_klass()); + // If the profile is known statically set it once for all and do not emit any code + if (exact != NULL) { + md->set_parameter_type(k, exact); + } + k++; + if (k >= parameters_type_data->number_of_parameters()) { +#ifdef ASSERT + int extra = 0; + if (MethodData::profile_arguments() && TypeProfileParmsLimit != -1 && + x->nb_profiled_args() >= TypeProfileParmsLimit && + x->recv() != NULL && Bytecodes::has_receiver(bc)) { + extra += 1; + } + assert(i == x->nb_profiled_args() - extra || (TypeProfileParmsLimit != -1 && TypeProfileArgsLimit > TypeProfileParmsLimit), "unused parameters?"); +#endif + break; + } + arg = x->profiled_arg_at(i); + not_null = !x->arg_needs_null_check(i); + i++; + } + } } } } @@ -3126,6 +3226,11 @@ profile_arguments(x); } + // profile parameters on inlined method entry including receiver + if (x->recv() != NULL || x->nb_profiled_args() > 0) { + profile_parameters_at_call(x); + } + if (x->recv() != NULL) { LIRItem value(x->recv(), this); value.load_item(); diff -r 4c9115774c8e -r 97d400662426 src/share/vm/c1/c1_LIRGenerator.hpp --- a/src/share/vm/c1/c1_LIRGenerator.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/c1/c1_LIRGenerator.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -436,6 +436,8 @@ #endif ciKlass* profile_arg_type(ciMethodData* md, int md_first_offset, int md_offset, intptr_t profiled_k, Value arg, LIR_Opr& mdp, bool not_null, ciKlass* signature_k); void profile_arguments(ProfileCall* x); + void profile_parameters(Base* x); + void profile_parameters_at_call(ProfileCall* x); public: Compilation* compilation() const { return _compilation; } diff -r 4c9115774c8e -r 97d400662426 src/share/vm/ci/ciMethod.cpp --- a/src/share/vm/ci/ciMethod.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/ci/ciMethod.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -565,6 +565,116 @@ if (_limit < MorphismLimit) _limit++; } + +void ciMethod::assert_virtual_call_type_ok(int bci) { + assert(java_code_at_bci(bci) == Bytecodes::_invokevirtual || + java_code_at_bci(bci) == Bytecodes::_invokeinterface, err_msg("unexpected bytecode %s", Bytecodes::name(java_code_at_bci(bci)))); +} + +void ciMethod::assert_call_type_ok(int bci) { + assert(java_code_at_bci(bci) == Bytecodes::_invokestatic || + java_code_at_bci(bci) == Bytecodes::_invokespecial || + java_code_at_bci(bci) == Bytecodes::_invokedynamic, err_msg("unexpected bytecode %s", Bytecodes::name(java_code_at_bci(bci)))); +} + +/** + * Check whether profiling provides a type for the argument i to the + * call at bci bci + * + * @param bci bci of the call + * @param i argument number + * @return profiled type + * + * If the profile reports that the argument may be null, return false + * at least for now. + */ +ciKlass* ciMethod::argument_profiled_type(int bci, int i) { + if (MethodData::profile_parameters() && method_data() != NULL && method_data()->is_mature()) { + ciProfileData* data = method_data()->bci_to_data(bci); + if (data != NULL) { + if (data->is_VirtualCallTypeData()) { + assert_virtual_call_type_ok(bci); + ciVirtualCallTypeData* call = (ciVirtualCallTypeData*)data->as_VirtualCallTypeData(); + if (i >= call->number_of_arguments()) { + return NULL; + } + ciKlass* type = call->valid_argument_type(i); + if (type != NULL && !call->argument_maybe_null(i)) { + return type; + } + } else if (data->is_CallTypeData()) { + assert_call_type_ok(bci); + ciCallTypeData* call = (ciCallTypeData*)data->as_CallTypeData(); + if (i >= call->number_of_arguments()) { + return NULL; + } + ciKlass* type = call->valid_argument_type(i); + if (type != NULL && !call->argument_maybe_null(i)) { + return type; + } + } + } + } + return NULL; +} + +/** + * Check whether profiling provides a type for the return value from + * the call at bci bci + * + * @param bci bci of the call + * @return profiled type + * + * If the profile reports that the argument may be null, return false + * at least for now. + */ +ciKlass* ciMethod::return_profiled_type(int bci) { + if (MethodData::profile_return() && method_data() != NULL && method_data()->is_mature()) { + ciProfileData* data = method_data()->bci_to_data(bci); + if (data != NULL) { + if (data->is_VirtualCallTypeData()) { + assert_virtual_call_type_ok(bci); + ciVirtualCallTypeData* call = (ciVirtualCallTypeData*)data->as_VirtualCallTypeData(); + ciKlass* type = call->valid_return_type(); + if (type != NULL && !call->return_maybe_null()) { + return type; + } + } else if (data->is_CallTypeData()) { + assert_call_type_ok(bci); + ciCallTypeData* call = (ciCallTypeData*)data->as_CallTypeData(); + ciKlass* type = call->valid_return_type(); + if (type != NULL && !call->return_maybe_null()) { + return type; + } + } + } + } + return NULL; +} + +/** + * Check whether profiling provides a type for the parameter i + * + * @param i parameter number + * @return profiled type + * + * If the profile reports that the argument may be null, return false + * at least for now. + */ +ciKlass* ciMethod::parameter_profiled_type(int i) { + if (MethodData::profile_parameters() && method_data() != NULL && method_data()->is_mature()) { + ciParametersTypeData* parameters = method_data()->parameters_type_data(); + if (parameters != NULL && i < parameters->number_of_parameters()) { + ciKlass* type = parameters->valid_parameter_type(i); + if (type != NULL && !parameters->parameter_maybe_null(i)) { + return type; + } + } + } + return NULL; +} + + // ------------------------------------------------------------------ // ciMethod::find_monomorphic_target // diff -r 4c9115774c8e -r 97d400662426 src/share/vm/ci/ciMethod.hpp --- a/src/share/vm/ci/ciMethod.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/ci/ciMethod.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -117,6 +117,10 @@ *bcp = code; } + // Check bytecode and profile data collected are compatible + void assert_virtual_call_type_ok(int bci); + void assert_call_type_ok(int bci); + public: // Basic method information. ciFlags flags() const { check_is_loaded(); return _flags; } @@ -230,6 +234,11 @@ ciCallProfile call_profile_at_bci(int bci); int interpreter_call_site_count(int bci); + // Does type profiling provide a useful type at this point? + ciKlass* argument_profiled_type(int bci, int i); + ciKlass* parameter_profiled_type(int i); + ciKlass* return_profiled_type(int bci); + ciField* get_field_at_bci( int bci, bool &will_link); ciMethod* get_method_at_bci(int bci, bool &will_link, ciSignature* *declared_signature); diff -r 4c9115774c8e -r 97d400662426 src/share/vm/ci/ciMethodData.cpp --- a/src/share/vm/ci/ciMethodData.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/ci/ciMethodData.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -53,6 +53,7 @@ _hint_di = first_di(); // Initialize the escape information (to "don't know."); _eflags = _arg_local = _arg_stack = _arg_returned = 0; + _parameters = NULL; } // ------------------------------------------------------------------ @@ -74,6 +75,7 @@ _hint_di = first_di(); // Initialize the escape information (to "don't know."); _eflags = _arg_local = _arg_stack = _arg_returned = 0; + _parameters = NULL; } void ciMethodData::load_data() { @@ -108,6 +110,12 @@ ci_data = next_data(ci_data); data = mdo->next_data(data); } + if (mdo->parameters_type_data() != NULL) { + _parameters = data_layout_at(mdo->parameters_type_data_di()); + ciParametersTypeData* parameters = new ciParametersTypeData(_parameters); + parameters->translate_from(mdo->parameters_type_data()); + } + // Note: Extra data are all BitData, and do not need translation. _current_mileage = MethodData::mileage_of(mdo->method()); _invocation_counter = mdo->invocation_count(); @@ -182,6 +190,8 @@ return new ciCallTypeData(data_layout); case DataLayout::virtual_call_type_data_tag: return new ciVirtualCallTypeData(data_layout); + case DataLayout::parameters_type_data_tag: + return new ciParametersTypeData(data_layout); }; } @@ -318,6 +328,14 @@ } } +void ciMethodData::set_parameter_type(int i, ciKlass* k) { + VM_ENTRY_MARK; + MethodData* mdo = get_MethodData(); + if (mdo != NULL) { + mdo->parameters_type_data()->set_type(i, k->get_Klass()); + } +} + void ciMethodData::set_return_type(int bci, ciKlass* k) { VM_ENTRY_MARK; MethodData* mdo = get_MethodData(); @@ -605,4 +623,9 @@ ret()->print_data_on(st); } } + +void ciParametersTypeData::print_data_on(outputStream* st) const { + st->print_cr("Parametertypes"); + parameters()->print_data_on(st); +} #endif diff -r 4c9115774c8e -r 97d400662426 src/share/vm/ci/ciMethodData.hpp --- a/src/share/vm/ci/ciMethodData.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/ci/ciMethodData.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -43,6 +43,7 @@ class ciArgInfoData; class ciCallTypeData; class ciVirtualCallTypeData; +class ciParametersTypeData; typedef ProfileData ciProfileData; @@ -99,6 +100,10 @@ return valid_ciklass(type(i)); } + bool maybe_null(int i) const { + return was_null_seen(type(i)); + } + #ifndef PRODUCT void print_data_on(outputStream* st) const; #endif @@ -112,6 +117,10 @@ return valid_ciklass(type()); } + bool maybe_null() const { + return was_null_seen(type()); + } + #ifndef PRODUCT void print_data_on(outputStream* st) const; #endif @@ -124,7 +133,7 @@ ciTypeStackSlotEntries* args() const { return (ciTypeStackSlotEntries*)CallTypeData::args(); } ciReturnTypeEntry* ret() const { return (ciReturnTypeEntry*)CallTypeData::ret(); } - void translate_type_data_from(const ProfileData* data) { + void translate_from(const ProfileData* data) { if (has_arguments()) { args()->translate_type_data_from(data->as_CallTypeData()->args()); } @@ -153,6 +162,14 @@ return ret()->valid_type(); } + bool argument_maybe_null(int i) const { + return args()->maybe_null(i); + } + + bool return_maybe_null() const { + return ret()->maybe_null(); + } + #ifndef PRODUCT void print_data_on(outputStream* st) const; #endif @@ -259,6 +276,14 @@ return ret()->valid_type(); } + bool argument_maybe_null(int i) const { + return args()->maybe_null(i); + } + + bool return_maybe_null() const { + return ret()->maybe_null(); + } + #ifndef PRODUCT void print_data_on(outputStream* st) const; #endif @@ -290,6 +315,29 @@ ciArgInfoData(DataLayout* layout) : ArgInfoData(layout) {}; }; +class ciParametersTypeData : public ParametersTypeData { +public: + ciParametersTypeData(DataLayout* layout) : ParametersTypeData(layout) {} + + virtual void translate_from(const ProfileData* data) { + parameters()->translate_type_data_from(data->as_ParametersTypeData()->parameters()); + } + + ciTypeStackSlotEntries* parameters() const { return (ciTypeStackSlotEntries*)ParametersTypeData::parameters(); } + + ciKlass* valid_parameter_type(int i) const { + return parameters()->valid_type(i); + } + + bool parameter_maybe_null(int i) const { + return parameters()->maybe_null(i); + } + +#ifndef PRODUCT + void print_data_on(outputStream* st) const; +#endif +}; + // ciMethodData // // This class represents a MethodData* in the HotSpot virtual @@ -335,6 +383,10 @@ // Coherent snapshot of original header. MethodData _orig; + // Dedicated area dedicated to parameters. Null if no parameter + // profiling for this method. + DataLayout* _parameters; + ciMethodData(MethodData* md); ciMethodData(); @@ -403,6 +455,7 @@ // If the compiler finds a profiled type that is known statically // for sure, set it in the MethodData void set_argument_type(int bci, int i, ciKlass* k); + void set_parameter_type(int i, ciKlass* k); void set_return_type(int bci, ciKlass* k); void load_data(); @@ -467,6 +520,10 @@ bool is_arg_returned(int i) const; uint arg_modified(int arg) const; + ciParametersTypeData* parameters_type_data() const { + return _parameters != NULL ? new ciParametersTypeData(_parameters) : NULL; + } + // Code generation helper ByteSize offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data); int byte_offset_of_slot(ciProfileData* data, ByteSize slot_offset_in_data) { return in_bytes(offset_of_slot(data, slot_offset_in_data)); } diff -r 4c9115774c8e -r 97d400662426 src/share/vm/compiler/compileBroker.cpp --- a/src/share/vm/compiler/compileBroker.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/compiler/compileBroker.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1297,13 +1297,6 @@ method->jmethod_id(); } - // If the compiler is shut off due to code cache getting full - // fail out now so blocking compiles dont hang the java thread - if (!should_compile_new_jobs()) { - CompilationPolicy::policy()->delay_compilation(method()); - return NULL; - } - // do the compilation if (method->is_native()) { if (!PreferInterpreterNativeStubs || method->is_method_handle_intrinsic()) { @@ -1313,11 +1306,22 @@ MutexLocker locker(MethodCompileQueue_lock, THREAD); compile_id = assign_compile_id(method, standard_entry_bci); } + // To properly handle the appendix argument for out-of-line calls we are using a small trampoline that + // pops off the appendix argument and jumps to the target (see gen_special_dispatch in SharedRuntime). + // + // Since normal compiled-to-compiled calls are not able to handle such a thing we MUST generate an adapter + // in this case. If we can't generate one and use it we can not execute the out-of-line method handle calls. (void) AdapterHandlerLibrary::create_native_wrapper(method, compile_id); } else { return NULL; } } else { + // If the compiler is shut off due to code cache getting full + // fail out now so blocking compiles dont hang the java thread + if (!should_compile_new_jobs()) { + CompilationPolicy::policy()->delay_compilation(method()); + return NULL; + } compile_method_base(method, osr_bci, comp_level, hot_method, hot_count, comment, THREAD); } diff -r 4c9115774c8e -r 97d400662426 src/share/vm/interpreter/abstractInterpreter.hpp --- a/src/share/vm/interpreter/abstractInterpreter.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/interpreter/abstractInterpreter.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -30,11 +30,8 @@ #include "runtime/thread.inline.hpp" #include "runtime/vmThread.hpp" #include "utilities/top.hpp" -#ifdef TARGET_ARCH_MODEL_x86_32 -# include "interp_masm_x86_32.hpp" -#endif -#ifdef TARGET_ARCH_MODEL_x86_64 -# include "interp_masm_x86_64.hpp" +#ifdef TARGET_ARCH_x86 +# include "interp_masm_x86.hpp" #endif #ifdef TARGET_ARCH_MODEL_sparc # include "interp_masm_sparc.hpp" diff -r 4c9115774c8e -r 97d400662426 src/share/vm/interpreter/templateTable.hpp --- a/src/share/vm/interpreter/templateTable.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/interpreter/templateTable.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -28,11 +28,8 @@ #include "interpreter/bytecodes.hpp" #include "memory/allocation.hpp" #include "runtime/frame.hpp" -#ifdef TARGET_ARCH_MODEL_x86_32 -# include "interp_masm_x86_32.hpp" -#endif -#ifdef TARGET_ARCH_MODEL_x86_64 -# include "interp_masm_x86_64.hpp" +#ifdef TARGET_ARCH_x86 +# include "interp_masm_x86.hpp" #endif #ifdef TARGET_ARCH_MODEL_sparc # include "interp_masm_sparc.hpp" diff -r 4c9115774c8e -r 97d400662426 src/share/vm/oops/methodData.cpp --- a/src/share/vm/oops/methodData.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/oops/methodData.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -41,7 +41,7 @@ // Some types of data layouts need a length field. bool DataLayout::needs_array_len(u1 tag) { - return (tag == multi_branch_data_tag) || (tag == arg_info_data_tag); + return (tag == multi_branch_data_tag) || (tag == arg_info_data_tag) || (tag == parameters_type_data_tag); } // Perform generic initialization of the data. More specific @@ -156,10 +156,13 @@ } #endif // !PRODUCT -int TypeStackSlotEntries::compute_cell_count(Symbol* signature, int max) { +int TypeStackSlotEntries::compute_cell_count(Symbol* signature, bool include_receiver, int max) { + // Parameter profiling include the receiver + int args_count = include_receiver ? 1 : 0; ResourceMark rm; SignatureStream ss(signature); - int args_count = MIN2(ss.reference_parameter_count(), max); + args_count += ss.reference_parameter_count(); + args_count = MIN2(args_count, max); return args_count * per_arg_cell_count; } @@ -169,7 +172,7 @@ Bytecode_invoke inv(stream->method(), stream->bci()); int args_cell = 0; if (arguments_profiling_enabled()) { - args_cell = TypeStackSlotEntries::compute_cell_count(inv.signature(), TypeProfileArgsLimit); + args_cell = TypeStackSlotEntries::compute_cell_count(inv.signature(), false, TypeProfileArgsLimit); } int ret_cell = 0; if (return_profiling_enabled() && (inv.result_type() == T_OBJECT || inv.result_type() == T_ARRAY)) { @@ -212,12 +215,19 @@ int off_at(int i) const { return _offsets.at(i); } }; -void TypeStackSlotEntries::post_initialize(Symbol* signature, bool has_receiver) { +void TypeStackSlotEntries::post_initialize(Symbol* signature, bool has_receiver, bool include_receiver) { ResourceMark rm; - ArgumentOffsetComputer aos(signature, _number_of_entries); + int start = 0; + // Parameter profiling include the receiver + if (include_receiver && has_receiver) { + set_stack_slot(0, 0); + set_type(0, type_none()); + start += 1; + } + ArgumentOffsetComputer aos(signature, _number_of_entries-start); aos.total(); - for (int i = 0; i < _number_of_entries; i++) { - set_stack_slot(i, aos.off_at(i) + (has_receiver ? 1 : 0)); + for (int i = start; i < _number_of_entries; i++) { + set_stack_slot(i, aos.off_at(i-start) + (has_receiver ? 1 : 0)); set_type(i, type_none()); } } @@ -234,7 +244,7 @@ assert(count > 0, "room for args type but none found?"); check_number_of_arguments(count); #endif - _args.post_initialize(inv.signature(), inv.has_receiver()); + _args.post_initialize(inv.signature(), inv.has_receiver(), false); } if (has_return()) { @@ -255,7 +265,7 @@ assert(count > 0, "room for args type but none found?"); check_number_of_arguments(count); #endif - _args.post_initialize(inv.signature(), inv.has_receiver()); + _args.post_initialize(inv.signature(), inv.has_receiver(), false); } if (has_return()) { @@ -579,6 +589,34 @@ } #endif + +int ParametersTypeData::compute_cell_count(Method* m) { + if (!MethodData::profile_parameters_for_method(m)) { + return 0; + } + int max = TypeProfileParmsLimit == -1 ? INT_MAX : TypeProfileParmsLimit; + int obj_args = TypeStackSlotEntries::compute_cell_count(m->signature(), !m->is_static(), max); + if (obj_args > 0) { + return obj_args + 1; // 1 cell for array len + } + return 0; +} + +void ParametersTypeData::post_initialize(BytecodeStream* stream, MethodData* mdo) { + _parameters.post_initialize(mdo->method()->signature(), !mdo->method()->is_static(), true); +} + +bool ParametersTypeData::profiling_enabled() { + return MethodData::profile_parameters(); +} + +#ifndef PRODUCT +void ParametersTypeData::print_data_on(outputStream* st) const { + st->print("parameter types"); + _parameters.print_data_on(st); +} +#endif + // ================================================================== // MethodData* // @@ -741,6 +779,12 @@ int arg_size = method->size_of_parameters(); object_size += DataLayout::compute_size_in_bytes(arg_size+1); + // Reserve room for an area of the MDO dedicated to profiling of + // parameters + int args_cell = ParametersTypeData::compute_cell_count(method()); + if (args_cell > 0) { + object_size += DataLayout::compute_size_in_bytes(args_cell); + } return object_size; } @@ -915,6 +959,8 @@ return new CallTypeData(this); case DataLayout::virtual_call_type_data_tag: return new VirtualCallTypeData(this); + case DataLayout::parameters_type_data_tag: + return new ParametersTypeData(this); }; } @@ -936,6 +982,9 @@ stream->next(); data->post_initialize(stream, this); } + if (_parameters_type_data_di != -1) { + parameters_type_data()->post_initialize(NULL, this); + } } // Initialize the MethodData* corresponding to a given method. @@ -975,7 +1024,23 @@ int arg_size = method->size_of_parameters(); dp->initialize(DataLayout::arg_info_data_tag, 0, arg_size+1); - object_size += extra_size + DataLayout::compute_size_in_bytes(arg_size+1); + int arg_data_size = DataLayout::compute_size_in_bytes(arg_size+1); + object_size += extra_size + arg_data_size; + + int args_cell = ParametersTypeData::compute_cell_count(method()); + // If we are profiling parameters, we reserver an area near the end + // of the MDO after the slots for bytecodes (because there's no bci + // for method entry so they don't fit with the framework for the + // profiling of bytecodes). We store the offset within the MDO of + // this area (or -1 if no parameter is profiled) + if (args_cell > 0) { + object_size += DataLayout::compute_size_in_bytes(args_cell); + _parameters_type_data_di = data_size + extra_size + arg_data_size; + DataLayout *dp = data_layout_at(data_size + extra_size + arg_data_size); + dp->initialize(DataLayout::parameters_type_data_tag, 0, args_cell); + } else { + _parameters_type_data_di = -1; + } // Set an initial hint. Don't use set_hint_di() because // first_di() may be out of bounds if data_size is 0. @@ -1134,6 +1199,9 @@ void MethodData::print_data_on(outputStream* st) const { ResourceMark rm; ProfileData* data = first_data(); + if (_parameters_type_data_di != -1) { + parameters_type_data()->print_data_on(st); + } for ( ; is_valid(data); data = next_data(data)) { st->print("%d", dp_to_di(data->dp())); st->fill_to(6); @@ -1222,7 +1290,7 @@ } int MethodData::profile_return_flag() { - return TypeProfileLevel / 10; + return (TypeProfileLevel % 100) / 10; } bool MethodData::profile_return() { @@ -1249,3 +1317,32 @@ assert(profile_return_jsr292_only(), "inconsistent"); return profile_jsr292(m, bci); } + +int MethodData::profile_parameters_flag() { + return TypeProfileLevel / 100; +} + +bool MethodData::profile_parameters() { + return profile_parameters_flag() > no_type_profile && profile_parameters_flag() <= type_profile_all; +} + +bool MethodData::profile_parameters_jsr292_only() { + return profile_parameters_flag() == type_profile_jsr292; +} + +bool MethodData::profile_all_parameters() { + return profile_parameters_flag() == type_profile_all; +} + +bool MethodData::profile_parameters_for_method(methodHandle m) { + if (!profile_parameters()) { + return false; + } + + if (profile_all_parameters()) { + return true; + } + + assert(profile_parameters_jsr292_only(), "inconsistent"); + return m->is_compiled_lambda_form(); +} diff -r 4c9115774c8e -r 97d400662426 src/share/vm/oops/methodData.hpp --- a/src/share/vm/oops/methodData.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/oops/methodData.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -119,7 +119,8 @@ multi_branch_data_tag, arg_info_data_tag, call_type_data_tag, - virtual_call_type_data_tag + virtual_call_type_data_tag, + parameters_type_data_tag }; enum { @@ -264,6 +265,7 @@ class ArrayData; class MultiBranchData; class ArgInfoData; +class ParametersTypeData; // ProfileData // @@ -397,6 +399,7 @@ virtual bool is_ArgInfoData() const { return false; } virtual bool is_CallTypeData() const { return false; } virtual bool is_VirtualCallTypeData()const { return false; } + virtual bool is_ParametersTypeData() const { return false; } BitData* as_BitData() const { @@ -447,6 +450,10 @@ assert(is_VirtualCallTypeData(), "wrong type"); return is_VirtualCallTypeData() ? (VirtualCallTypeData*)this : NULL; } + ParametersTypeData* as_ParametersTypeData() const { + assert(is_ParametersTypeData(), "wrong type"); + return is_ParametersTypeData() ? (ParametersTypeData*)this : NULL; + } // Subclass specific initialization @@ -767,9 +774,9 @@ TypeStackSlotEntries(int base_off, int nb_entries) : TypeEntries(base_off), _number_of_entries(nb_entries) {} - static int compute_cell_count(Symbol* signature, int max); + static int compute_cell_count(Symbol* signature, bool include_receiver, int max); - void post_initialize(Symbol* signature, bool has_receiver); + void post_initialize(Symbol* signature, bool has_receiver, bool include_receiver); // offset of cell for stack slot for entry i within this block of cells for a TypeStackSlotEntries static int stack_slot_local_offset(int i) { @@ -946,17 +953,6 @@ assert(number_of_arguments() == total, "should be set in DataLayout::initialize"); } -protected: - // An entry for a return value takes less space than an entry for an - // argument so if the number of cells exceeds the number of cells - // needed for an argument, this object contains type information for - // at least one argument. - bool has_arguments() const { - bool res = cell_count_no_header() >= TypeStackSlotEntries::per_arg_count(); - assert (!res || TypeEntriesAtCall::arguments_profiling_enabled(), "no profiling of arguments"); - return res; - } - public: CallTypeData(DataLayout* layout) : CounterData(layout), @@ -1018,6 +1014,16 @@ } // An entry for a return value takes less space than an entry for an + // argument so if the number of cells exceeds the number of cells + // needed for an argument, this object contains type information for + // at least one argument. + bool has_arguments() const { + bool res = cell_count_no_header() >= TypeStackSlotEntries::per_arg_count(); + assert (!res || TypeEntriesAtCall::arguments_profiling_enabled(), "no profiling of arguments"); + return res; + } + + // An entry for a return value takes less space than an entry for an // argument, so if the remainder of the number of cells divided by // the number of cells for an argument is not null, a return value // is profiled in this object. @@ -1213,17 +1219,6 @@ assert(number_of_arguments() == total, "should be set in DataLayout::initialize"); } -protected: - // An entry for a return value takes less space than an entry for an - // argument so if the number of cells exceeds the number of cells - // needed for an argument, this object contains type information for - // at least one argument. - bool has_arguments() const { - bool res = cell_count_no_header() >= TypeStackSlotEntries::per_arg_count(); - assert (!res || TypeEntriesAtCall::arguments_profiling_enabled(), "no profiling of arguments"); - return res; - } - public: VirtualCallTypeData(DataLayout* layout) : VirtualCallData(layout), @@ -1294,6 +1289,16 @@ return res; } + // An entry for a return value takes less space than an entry for an + // argument so if the number of cells exceeds the number of cells + // needed for an argument, this object contains type information for + // at least one argument. + bool has_arguments() const { + bool res = cell_count_no_header() >= TypeStackSlotEntries::per_arg_count(); + assert (!res || TypeEntriesAtCall::arguments_profiling_enabled(), "no profiling of arguments"); + return res; + } + // Code generation support static ByteSize args_data_offset() { return cell_offset(VirtualCallData::static_cell_count()) + TypeEntriesAtCall::args_data_offset(); @@ -1662,6 +1667,75 @@ #endif }; +// ParametersTypeData +// +// A ParametersTypeData is used to access profiling information about +// types of parameters to a method +class ParametersTypeData : public ArrayData { + +private: + TypeStackSlotEntries _parameters; + + static int stack_slot_local_offset(int i) { + assert_profiling_enabled(); + return array_start_off_set + TypeStackSlotEntries::stack_slot_local_offset(i); + } + + static int type_local_offset(int i) { + assert_profiling_enabled(); + return array_start_off_set + TypeStackSlotEntries::type_local_offset(i); + } + + static bool profiling_enabled(); + static void assert_profiling_enabled() { + assert(profiling_enabled(), "method parameters profiling should be on"); + } + +public: + ParametersTypeData(DataLayout* layout) : ArrayData(layout), _parameters(1, number_of_parameters()) { + assert(layout->tag() == DataLayout::parameters_type_data_tag, "wrong type"); + // Some compilers (VC++) don't want this passed in member initialization list + _parameters.set_profile_data(this); + } + + static int compute_cell_count(Method* m); + + virtual bool is_ParametersTypeData() const { return true; } + + virtual void post_initialize(BytecodeStream* stream, MethodData* mdo); + + int number_of_parameters() const { + return array_len() / TypeStackSlotEntries::per_arg_count(); + } + + const TypeStackSlotEntries* parameters() const { return &_parameters; } + + uint stack_slot(int i) const { + return _parameters.stack_slot(i); + } + + void set_type(int i, Klass* k) { + intptr_t current = _parameters.type(i); + _parameters.set_type(i, TypeEntries::with_status((intptr_t)k, current)); + } + + virtual void clean_weak_klass_links(BoolObjectClosure* is_alive_closure) { + _parameters.clean_weak_klass_links(is_alive_closure); + } + +#ifndef PRODUCT + virtual void print_data_on(outputStream* st) const; +#endif + + static ByteSize stack_slot_offset(int i) { + return cell_offset(stack_slot_local_offset(i)); + } + + static ByteSize type_offset(int i) { + return cell_offset(type_local_offset(i)); + } +}; + // MethodData* // // A MethodData* holds information which has been collected about @@ -1773,6 +1847,10 @@ // Size of _data array in bytes. (Excludes header and extra_data fields.) int _data_size; + // data index for the area dedicated to parameters. -1 if no + // parameter profiling. + int _parameters_type_data_di; + // Beginning of the data entries intptr_t _data[1]; @@ -1842,6 +1920,9 @@ static int profile_return_flag(); static bool profile_all_return(); static bool profile_return_for_invoke(methodHandle m, int bci); + static int profile_parameters_flag(); + static bool profile_parameters_jsr292_only(); + static bool profile_all_parameters(); public: static int header_size() { @@ -2048,6 +2129,16 @@ } } + // Return pointer to area dedicated to parameters in MDO + ParametersTypeData* parameters_type_data() const { + return _parameters_type_data_di != -1 ? data_layout_at(_parameters_type_data_di)->data_in()->as_ParametersTypeData() : NULL; + } + + int parameters_type_data_di() const { + assert(_parameters_type_data_di != -1, "no args type data"); + return _parameters_type_data_di; + } + // Support for code generation static ByteSize data_offset() { return byte_offset_of(MethodData, _data[0]); @@ -2060,6 +2151,10 @@ return byte_offset_of(MethodData, _backedge_counter); } + static ByteSize parameters_type_data_di_offset() { + return byte_offset_of(MethodData, _parameters_type_data_di); + } + // Deallocation support - no pointer fields to deallocate void deallocate_contents(ClassLoaderData* loader_data) {} @@ -2083,8 +2178,10 @@ void verify_on(outputStream* st); void verify_data_on(outputStream* st); + static bool profile_parameters_for_method(methodHandle m); static bool profile_arguments(); static bool profile_return(); + static bool profile_parameters(); static bool profile_return_jsr292_only(); }; diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/c2_globals.hpp --- a/src/share/vm/opto/c2_globals.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/c2_globals.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -641,7 +641,10 @@ "Enables intrinsification of various java.lang.Math functions") \ \ experimental(bool, ReplaceInParentMaps, false, \ - "Propagate type improvements in callers of inlinee if possible") + "Propagate type improvements in callers of inlinee if possible") \ + \ + experimental(bool, UseTypeSpeculation, false, \ + "Speculatively propagate types from profiles") C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG) diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/callGenerator.cpp --- a/src/share/vm/opto/callGenerator.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/callGenerator.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -486,6 +486,8 @@ JVMState* new_jvms = DirectCallGenerator::generate(jvms, parent_parser); return new_jvms; } + + virtual bool is_string_late_inline() const { return true; } }; CallGenerator* CallGenerator::for_string_late_inline(ciMethod* method, CallGenerator* inline_cg) { @@ -773,7 +775,7 @@ ciMethod* target = oop_ptr->const_oop()->as_method_handle()->get_vmtarget(); guarantee(!target->is_method_handle_intrinsic(), "should not happen"); // XXX remove const int vtable_index = Method::invalid_vtable_index; - CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS, true, true); + CallGenerator* cg = C->call_generator(target, vtable_index, false, jvms, true, PROB_ALWAYS, NULL, true, true); assert(!cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here"); if (cg != NULL && cg->is_inline()) return cg; @@ -829,6 +831,7 @@ int vtable_index = Method::invalid_vtable_index; bool call_does_dispatch = false; + ciKlass* speculative_receiver_type = NULL; if (is_virtual_or_interface) { ciInstanceKlass* klass = target->holder(); Node* receiver_node = kit.argument(0); @@ -837,9 +840,12 @@ target = C->optimize_virtual_call(caller, jvms->bci(), klass, target, receiver_type, is_virtual, call_does_dispatch, vtable_index); // out-parameters + // We lack profiling at this call but type speculation may + // provide us with a type + speculative_receiver_type = receiver_type->speculative_type(); } - CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, true, PROB_ALWAYS, true, true); + CallGenerator* cg = C->call_generator(target, vtable_index, call_does_dispatch, jvms, true, PROB_ALWAYS, speculative_receiver_type, true, true); assert(!cg->is_late_inline() || cg->is_mh_late_inline(), "no late inline here"); if (cg != NULL && cg->is_inline()) return cg; diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/callGenerator.hpp --- a/src/share/vm/opto/callGenerator.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/callGenerator.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -74,6 +74,7 @@ virtual bool is_late_inline() const { return false; } // same but for method handle calls virtual bool is_mh_late_inline() const { return false; } + virtual bool is_string_late_inline() const{ return false; } // for method handle calls: have we tried inlinining the call already? virtual bool already_attempted() const { ShouldNotReachHere(); return false; } diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/compile.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1360,7 +1360,7 @@ // During the 2nd round of IterGVN, NotNull castings are removed. // Make sure the Bottom and NotNull variants alias the same. // Also, make sure exact and non-exact variants alias the same. - if( ptr == TypePtr::NotNull || ta->klass_is_exact() ) { + if (ptr == TypePtr::NotNull || ta->klass_is_exact() || ta->speculative() != NULL) { tj = ta = TypeAryPtr::make(TypePtr::BotPTR,ta->ary(),ta->klass(),false,offset); } } @@ -1385,6 +1385,9 @@ // Also, make sure exact and non-exact variants alias the same. tj = to = TypeInstPtr::make(TypePtr::BotPTR,to->klass(),false,0,offset); } + if (to->speculative() != NULL) { + tj = to = TypeInstPtr::make(to->ptr(),to->klass(),to->klass_is_exact(),to->const_oop(),to->offset(), to->instance_id()); + } // Canonicalize the holder of this field if (offset >= 0 && offset < instanceOopDesc::base_offset_in_bytes()) { // First handle header references such as a LoadKlassNode, even if the @@ -2013,6 +2016,12 @@ if (failing()) return; } + // Remove the speculative part of types and clean up the graph from + // the extra CastPP nodes whose only purpose is to carry them. Do + // that early so that optimizations are not disrupted by the extra + // CastPP nodes. + remove_speculative_types(igvn); + // No more new expensive nodes will be added to the list from here // so keep only the actual candidates for optimizations. cleanup_expensive_nodes(igvn); @@ -3006,10 +3015,15 @@ if (result != NULL) { for (DUIterator_Fast jmax, j = result->fast_outs(jmax); j < jmax; j++) { Node* out = result->fast_out(j); - if (out->in(0) == NULL) { - out->set_req(0, non_throwing); - } else if (out->in(0) == ctrl) { - out->set_req(0, non_throwing); + // Phi nodes shouldn't be moved. They would only match below if they + // had the same control as the MathExactNode. The only time that + // would happen is if the Phi is also an input to the MathExact + if (!out->is_Phi()) { + if (out->in(0) == NULL) { + out->set_req(0, non_throwing); + } else if (out->in(0) == ctrl) { + out->set_req(0, non_throwing); + } } } } @@ -3794,6 +3808,45 @@ } } +/** + * Remove the speculative part of types and clean up the graph + */ +void Compile::remove_speculative_types(PhaseIterGVN &igvn) { + if (UseTypeSpeculation) { + Unique_Node_List worklist; + worklist.push(root()); + int modified = 0; + // Go over all type nodes that carry a speculative type, drop the + // speculative part of the type and enqueue the node for an igvn + // which may optimize it out. + for (uint next = 0; next < worklist.size(); ++next) { + Node *n = worklist.at(next); + if (n->is_Type() && n->as_Type()->type()->isa_oopptr() != NULL && + n->as_Type()->type()->is_oopptr()->speculative() != NULL) { + TypeNode* tn = n->as_Type(); + const TypeOopPtr* t = tn->type()->is_oopptr(); + bool in_hash = igvn.hash_delete(n); + assert(in_hash, "node should be in igvn hash table"); + tn->set_type(t->remove_speculative()); + igvn.hash_insert(n); + igvn._worklist.push(n); // give it a chance to go away + modified++; + } + uint max = n->len(); + for( uint i = 0; i < max; ++i ) { + Node *m = n->in(i); + if (not_a_node(m)) continue; + worklist.push(m); + } + } + // Drop the speculative part of all types in the igvn's type table + igvn.remove_speculative_types(); + if (modified > 0) { + igvn.optimize(); + } + } +} + // Auxiliary method to support randomized stressing/fuzzing. // // This method can be called the arbitrary number of times, with current count diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/compile.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -424,6 +424,8 @@ static int cmp_expensive_nodes(Node** n1, Node** n2); // Expensive nodes list already sorted? bool expensive_nodes_sorted() const; + // Remove the speculative part of types and clean up the graph + void remove_speculative_types(PhaseIterGVN &igvn); // Are we within a PreserveJVMState block? int _preserve_jvm_state; @@ -824,8 +826,8 @@ // Decide how to build a call. // The profile factor is a discount to apply to this site's interp. profile. CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_does_dispatch, - JVMState* jvms, bool allow_inline, float profile_factor, bool allow_intrinsics = true, - bool delayed_forbidden = false); + JVMState* jvms, bool allow_inline, float profile_factor, ciKlass* speculative_receiver_type = NULL, + bool allow_intrinsics = true, bool delayed_forbidden = false); bool should_delay_inlining(ciMethod* call_method, JVMState* jvms) { return should_delay_string_inlining(call_method, jvms) || should_delay_boxing_inlining(call_method, jvms); diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/doCall.cpp --- a/src/share/vm/opto/doCall.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/doCall.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -63,7 +63,8 @@ CallGenerator* Compile::call_generator(ciMethod* callee, int vtable_index, bool call_does_dispatch, JVMState* jvms, bool allow_inline, - float prof_factor, bool allow_intrinsics, bool delayed_forbidden) { + float prof_factor, ciKlass* speculative_receiver_type, + bool allow_intrinsics, bool delayed_forbidden) { ciMethod* caller = jvms->method(); int bci = jvms->bci(); Bytecodes::Code bytecode = caller->java_code_at_bci(bci); @@ -117,7 +118,7 @@ if (cg->is_predicted()) { // Code without intrinsic but, hopefully, inlined. CallGenerator* inline_cg = this->call_generator(callee, - vtable_index, call_does_dispatch, jvms, allow_inline, prof_factor, false); + vtable_index, call_does_dispatch, jvms, allow_inline, prof_factor, speculative_receiver_type, false); if (inline_cg != NULL) { cg = CallGenerator::for_predicted_intrinsic(cg, inline_cg); } @@ -212,8 +213,24 @@ // The major receiver's count >= TypeProfileMajorReceiverPercent of site_count. bool have_major_receiver = (100.*profile.receiver_prob(0) >= (float)TypeProfileMajorReceiverPercent); ciMethod* receiver_method = NULL; - if (have_major_receiver || profile.morphism() == 1 || - (profile.morphism() == 2 && UseBimorphicInlining)) { + + int morphism = profile.morphism(); + if (speculative_receiver_type != NULL) { + // We have a speculative type, we should be able to resolve + // the call. We do that before looking at the profiling at + // this invoke because it may lead to bimorphic inlining which + // a speculative type should help us avoid. + receiver_method = callee->resolve_invoke(jvms->method()->holder(), + speculative_receiver_type); + if (receiver_method == NULL) { + speculative_receiver_type = NULL; + } else { + morphism = 1; + } + } + if (receiver_method == NULL && + (have_major_receiver || morphism == 1 || + (morphism == 2 && UseBimorphicInlining))) { // receiver_method = profile.method(); // Profiles do not suggest methods now. Look it up in the major receiver. receiver_method = callee->resolve_invoke(jvms->method()->holder(), @@ -227,7 +244,7 @@ // Look up second receiver. CallGenerator* next_hit_cg = NULL; ciMethod* next_receiver_method = NULL; - if (profile.morphism() == 2 && UseBimorphicInlining) { + if (morphism == 2 && UseBimorphicInlining) { next_receiver_method = callee->resolve_invoke(jvms->method()->holder(), profile.receiver(1)); if (next_receiver_method != NULL) { @@ -242,11 +259,10 @@ } } CallGenerator* miss_cg; - Deoptimization::DeoptReason reason = (profile.morphism() == 2) ? + Deoptimization::DeoptReason reason = morphism == 2 ? Deoptimization::Reason_bimorphic : Deoptimization::Reason_class_check; - if (( profile.morphism() == 1 || - (profile.morphism() == 2 && next_hit_cg != NULL) ) && + if ((morphism == 1 || (morphism == 2 && next_hit_cg != NULL)) && !too_many_traps(jvms->method(), jvms->bci(), reason) ) { // Generate uncommon trap for class check failure path @@ -260,6 +276,7 @@ } if (miss_cg != NULL) { if (next_hit_cg != NULL) { + assert(speculative_receiver_type == NULL, "shouldn't end up here if we used speculation"); trace_type_profile(C, jvms->method(), jvms->depth() - 1, jvms->bci(), next_receiver_method, profile.receiver(1), site_count, profile.receiver_count(1)); // We don't need to record dependency on a receiver here and below. // Whenever we inline, the dependency is added by Parse::Parse(). @@ -267,7 +284,9 @@ } if (miss_cg != NULL) { trace_type_profile(C, jvms->method(), jvms->depth() - 1, jvms->bci(), receiver_method, profile.receiver(0), site_count, receiver_count); - CallGenerator* cg = CallGenerator::for_predicted_call(profile.receiver(0), miss_cg, hit_cg, profile.receiver_prob(0)); + ciKlass* k = speculative_receiver_type != NULL ? speculative_receiver_type : profile.receiver(0); + float hit_prob = speculative_receiver_type != NULL ? 1.0 : profile.receiver_prob(0); + CallGenerator* cg = CallGenerator::for_predicted_call(k, miss_cg, hit_cg, hit_prob); if (cg != NULL) return cg; } } @@ -446,13 +465,16 @@ int vtable_index = Method::invalid_vtable_index; bool call_does_dispatch = false; + // Speculative type of the receiver if any + ciKlass* speculative_receiver_type = NULL; if (is_virtual_or_interface) { - Node* receiver_node = stack(sp() - nargs); + Node* receiver_node = stack(sp() - nargs); const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr(); // call_does_dispatch and vtable_index are out-parameters. They might be changed. callee = C->optimize_virtual_call(method(), bci(), klass, orig_callee, receiver_type, is_virtual, call_does_dispatch, vtable_index); // out-parameters + speculative_receiver_type = receiver_type != NULL ? receiver_type->speculative_type() : NULL; } // Note: It's OK to try to inline a virtual call. @@ -468,7 +490,7 @@ // Decide call tactic. // This call checks with CHA, the interpreter profile, intrinsics table, etc. // It decides whether inlining is desirable or not. - CallGenerator* cg = C->call_generator(callee, vtable_index, call_does_dispatch, jvms, try_inline, prof_factor()); + CallGenerator* cg = C->call_generator(callee, vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), speculative_receiver_type); // NOTE: Don't use orig_callee and callee after this point! Use cg->method() instead. orig_callee = callee = NULL; @@ -477,6 +499,10 @@ // Round double arguments before call round_double_arguments(cg->method()); + // Feed profiling data for arguments to the type system so it can + // propagate it as speculative types + record_profiled_arguments_for_speculation(cg->method(), bc()); + #ifndef PRODUCT // bump global counters for calls count_compiled_calls(/*at_method_entry*/ false, cg->is_inline()); @@ -491,6 +517,13 @@ // save across call, for a subsequent cast_not_null. Node* receiver = has_receiver ? argument(0) : NULL; + // The extra CheckCastPP for speculative types mess with PhaseStringOpts + if (receiver != NULL && !call_does_dispatch && !cg->is_string_late_inline()) { + // Feed profiling data for a single receiver to the type system so + // it can propagate it as a speculative type + receiver = record_profiled_receiver_for_speculation(receiver); + } + // Bump method data counters (We profile *before* the call is made // because exceptions don't return to the call site.) profile_call(receiver); @@ -508,7 +541,7 @@ // the call site, perhaps because it did not match a pattern the // intrinsic was expecting to optimize. Should always be possible to // get a normal java call that may inline in that case - cg = C->call_generator(cg->method(), vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), /* allow_intrinsics= */ false); + cg = C->call_generator(cg->method(), vtable_index, call_does_dispatch, jvms, try_inline, prof_factor(), speculative_receiver_type, /* allow_intrinsics= */ false); if ((new_jvms = cg->generate(jvms, this)) == NULL) { guarantee(failing(), "call failed to generate: calls should work"); return; @@ -607,6 +640,16 @@ null_assert(peek()); set_bci(iter().cur_bci()); // put it back } + BasicType ct = ctype->basic_type(); + if (ct == T_OBJECT || ct == T_ARRAY) { + ciKlass* better_type = method()->return_profiled_type(bci()); + if (UseTypeSpeculation && better_type != NULL) { + // If profiling reports a single type for the return value, + // feed it to the type system so it can propagate it as a + // speculative type + record_profile_for_speculation(stack(sp()-1), better_type); + } + } } // Restart record of parsing work after possible inlining of call diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/graphKit.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -2098,6 +2098,104 @@ } } +/** + * Record profiling data exact_kls for Node n with the type system so + * that it can propagate it (speculation) + * + * @param n node that the type applies to + * @param exact_kls type from profiling + * + * @return node with improved type + */ +Node* GraphKit::record_profile_for_speculation(Node* n, ciKlass* exact_kls) { + const TypeOopPtr* current_type = _gvn.type(n)->isa_oopptr(); + assert(UseTypeSpeculation, "type speculation must be on"); + if (exact_kls != NULL && + // nothing to improve if type is already exact + (current_type == NULL || + (!current_type->klass_is_exact() && + (current_type->speculative() == NULL || + !current_type->speculative()->klass_is_exact())))) { + const TypeKlassPtr* tklass = TypeKlassPtr::make(exact_kls); + const TypeOopPtr* xtype = tklass->as_instance_type(); + assert(xtype->klass_is_exact(), "Should be exact"); + + // Build a type with a speculative type (what we think we know + // about the type but will need a guard when we use it) + const TypeOopPtr* spec_type = TypeOopPtr::make(TypePtr::BotPTR, Type::OffsetBot, TypeOopPtr::InstanceBot, xtype); + // We're changing the type, we need a new cast node to carry the + // new type. The new type depends on the control: what profiling + // tells us is only valid from here as far as we can tell. + Node* cast = new(C) CastPPNode(n, spec_type); + cast->init_req(0, control()); + cast = _gvn.transform(cast); + replace_in_map(n, cast); + n = cast; + } + return n; +} + +/** + * Record profiling data from receiver profiling at an invoke with the + * type system so that it can propagate it (speculation) + * + * @param n receiver node + * + * @return node with improved type + */ +Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) { + if (!UseTypeSpeculation) { + return n; + } + ciKlass* exact_kls = profile_has_unique_klass(); + return record_profile_for_speculation(n, exact_kls); +} + +/** + * Record profiling data from argument profiling at an invoke with the + * type system so that it can propagate it (speculation) + * + * @param dest_method target method for the call + * @param bc what invoke bytecode is this? + */ +void GraphKit::record_profiled_arguments_for_speculation(ciMethod* dest_method, Bytecodes::Code bc) { + if (!UseTypeSpeculation) { + return; + } + const TypeFunc* tf = TypeFunc::make(dest_method); + int nargs = tf->_domain->_cnt - TypeFunc::Parms; + int skip = Bytecodes::has_receiver(bc) ? 1 : 0; + for (int j = skip, i = 0; j < nargs && i < TypeProfileArgsLimit; j++) { + const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms); + if (targ->basic_type() == T_OBJECT || targ->basic_type() == T_ARRAY) { + ciKlass* better_type = method()->argument_profiled_type(bci(), i); + if (better_type != NULL) { + record_profile_for_speculation(argument(j), better_type); + } + i++; + } + } +} + +/** + * Record profiling data from parameter profiling at an invoke with + * the type system so that it can propagate it (speculation) + */ +void GraphKit::record_profiled_parameters_for_speculation() { + if (!UseTypeSpeculation) { + return; + } + for (int i = 0, j = 0; i < method()->arg_size() ; i++) { + if (_gvn.type(local(i))->isa_oopptr()) { + ciKlass* better_type = method()->parameter_profiled_type(j); + if (better_type != NULL) { + record_profile_for_speculation(local(i), better_type); + } + j++; + } + } +} + void GraphKit::round_double_result(ciMethod* dest_method) { // A non-strict method may return a double value which has an extended // exponent, but this must not be visible in a caller which is 'strict' @@ -2635,10 +2733,10 @@ // If the profile has seen exactly one type, narrow to exactly that type. // Subsequent type checks will always fold up. Node* GraphKit::maybe_cast_profiled_receiver(Node* not_null_obj, - ciProfileData* data, - ciKlass* require_klass) { + ciKlass* require_klass, + ciKlass* spec_klass, + bool safe_for_replace) { if (!UseTypeProfile || !TypeProfileCasts) return NULL; - if (data == NULL) return NULL; // Make sure we haven't already deoptimized from this tactic. if (too_many_traps(Deoptimization::Reason_class_check)) @@ -2646,15 +2744,15 @@ // (No, this isn't a call, but it's enough like a virtual call // to use the same ciMethod accessor to get the profile info...) - ciCallProfile profile = method()->call_profile_at_bci(bci()); - if (profile.count() >= 0 && // no cast failures here - profile.has_receiver(0) && - profile.morphism() == 1) { - ciKlass* exact_kls = profile.receiver(0); + // If we have a speculative type use it instead of profiling (which + // may not help us) + ciKlass* exact_kls = spec_klass == NULL ? profile_has_unique_klass() : spec_klass; + if (exact_kls != NULL) {// no cast failures here if (require_klass == NULL || static_subtype_check(require_klass, exact_kls) == SSC_always_true) { - // If we narrow the type to match what the type profile sees, - // we can then remove the rest of the cast. + // If we narrow the type to match what the type profile sees or + // the speculative type, we can then remove the rest of the + // cast. // This is a win, even if the exact_kls is very specific, // because downstream operations, such as method calls, // will often benefit from the sharper type. @@ -2666,7 +2764,9 @@ uncommon_trap(Deoptimization::Reason_class_check, Deoptimization::Action_maybe_recompile); } - replace_in_map(not_null_obj, exact_obj); + if (safe_for_replace) { + replace_in_map(not_null_obj, exact_obj); + } return exact_obj; } // assert(ssc == SSC_always_true)... except maybe the profile lied to us. @@ -2675,11 +2775,59 @@ return NULL; } +/** + * Cast obj to type and emit guard unless we had too many traps here + * already + * + * @param obj node being casted + * @param type type to cast the node to + * @param not_null true if we know node cannot be null + */ +Node* GraphKit::maybe_cast_profiled_obj(Node* obj, + ciKlass* type, + bool not_null) { + // type == NULL if profiling tells us this object is always null + if (type != NULL) { + if (!too_many_traps(Deoptimization::Reason_null_check) && + !too_many_traps(Deoptimization::Reason_class_check)) { + Node* not_null_obj = NULL; + // not_null is true if we know the object is not null and + // there's no need for a null check + if (!not_null) { + Node* null_ctl = top(); + not_null_obj = null_check_oop(obj, &null_ctl, true, true); + assert(null_ctl->is_top(), "no null control here"); + } else { + not_null_obj = obj; + } + + Node* exact_obj = not_null_obj; + ciKlass* exact_kls = type; + Node* slow_ctl = type_check_receiver(exact_obj, exact_kls, 1.0, + &exact_obj); + { + PreserveJVMState pjvms(this); + set_control(slow_ctl); + uncommon_trap(Deoptimization::Reason_class_check, + Deoptimization::Action_maybe_recompile); + } + replace_in_map(not_null_obj, exact_obj); + obj = exact_obj; + } + } else { + if (!too_many_traps(Deoptimization::Reason_null_assert)) { + Node* exact_obj = null_assert(obj); + replace_in_map(obj, exact_obj); + obj = exact_obj; + } + } + return obj; +} //-------------------------------gen_instanceof-------------------------------- // Generate an instance-of idiom. Used by both the instance-of bytecode // and the reflective instance-of call. -Node* GraphKit::gen_instanceof(Node* obj, Node* superklass) { +Node* GraphKit::gen_instanceof(Node* obj, Node* superklass, bool safe_for_replace) { kill_dead_locals(); // Benefit all the uncommon traps assert( !stopped(), "dead parse path should be checked in callers" ); assert(!TypePtr::NULL_PTR->higher_equal(_gvn.type(superklass)->is_klassptr()), @@ -2692,10 +2840,8 @@ C->set_has_split_ifs(true); // Has chance for split-if optimization ciProfileData* data = NULL; - bool safe_for_replace = false; if (java_bc() == Bytecodes::_instanceof) { // Only for the bytecode data = method()->method_data()->bci_to_data(bci()); - safe_for_replace = true; } bool never_see_null = (ProfileDynamicTypes // aggressive use of profile && seems_never_null(obj, data)); @@ -2719,14 +2865,37 @@ phi ->del_req(_null_path); } - if (ProfileDynamicTypes && data != NULL) { - Node* cast_obj = maybe_cast_profiled_receiver(not_null_obj, data, NULL); - if (stopped()) { // Profile disagrees with this path. - set_control(null_ctl); // Null is the only remaining possibility. - return intcon(0); + // Do we know the type check always succeed? + bool known_statically = false; + if (_gvn.type(superklass)->singleton()) { + ciKlass* superk = _gvn.type(superklass)->is_klassptr()->klass(); + ciKlass* subk = _gvn.type(obj)->is_oopptr()->klass(); + if (subk != NULL && subk->is_loaded()) { + int static_res = static_subtype_check(superk, subk); + known_statically = (static_res == SSC_always_true || static_res == SSC_always_false); } - if (cast_obj != NULL) - not_null_obj = cast_obj; + } + + if (known_statically && UseTypeSpeculation) { + // If we know the type check always succeed then we don't use the + // profiling data at this bytecode. Don't lose it, feed it to the + // type system as a speculative type. + not_null_obj = record_profiled_receiver_for_speculation(not_null_obj); + } else { + const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr(); + // We may not have profiling here or it may not help us. If we + // have a speculative type use it to perform an exact cast. + ciKlass* spec_obj_type = obj_type->speculative_type(); + if (spec_obj_type != NULL || (ProfileDynamicTypes && data != NULL)) { + Node* cast_obj = maybe_cast_profiled_receiver(not_null_obj, NULL, spec_obj_type, safe_for_replace); + if (stopped()) { // Profile disagrees with this path. + set_control(null_ctl); // Null is the only remaining possibility. + return intcon(0); + } + if (cast_obj != NULL) { + not_null_obj = cast_obj; + } + } } // Load the object's klass @@ -2773,7 +2942,10 @@ if (objtp != NULL && objtp->klass() != NULL) { switch (static_subtype_check(tk->klass(), objtp->klass())) { case SSC_always_true: - return obj; + // If we know the type check always succeed then we don't use + // the profiling data at this bytecode. Don't lose it, feed it + // to the type system as a speculative type. + return record_profiled_receiver_for_speculation(obj); case SSC_always_false: // It needs a null check because a null will *pass* the cast check. // A non-null value will always produce an exception. @@ -2822,12 +2994,17 @@ } Node* cast_obj = NULL; - if (data != NULL && - // Counter has never been decremented (due to cast failure). - // ...This is a reasonable thing to expect. It is true of - // all casts inserted by javac to implement generic types. - data->as_CounterData()->count() >= 0) { - cast_obj = maybe_cast_profiled_receiver(not_null_obj, data, tk->klass()); + const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr(); + // We may not have profiling here or it may not help us. If we have + // a speculative type use it to perform an exact cast. + ciKlass* spec_obj_type = obj_type->speculative_type(); + if (spec_obj_type != NULL || + (data != NULL && + // Counter has never been decremented (due to cast failure). + // ...This is a reasonable thing to expect. It is true of + // all casts inserted by javac to implement generic types. + data->as_CounterData()->count() >= 0)) { + cast_obj = maybe_cast_profiled_receiver(not_null_obj, tk->klass(), spec_obj_type, safe_for_replace); if (cast_obj != NULL) { if (failure_control != NULL) // failure is now impossible (*failure_control) = top(); diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/graphKit.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -386,10 +386,33 @@ // Check the null_seen bit. bool seems_never_null(Node* obj, ciProfileData* data); + // Check for unique class for receiver at call + ciKlass* profile_has_unique_klass() { + ciCallProfile profile = method()->call_profile_at_bci(bci()); + if (profile.count() >= 0 && // no cast failures here + profile.has_receiver(0) && + profile.morphism() == 1) { + return profile.receiver(0); + } + return NULL; + } + + // record type from profiling with the type system + Node* record_profile_for_speculation(Node* n, ciKlass* exact_kls); + Node* record_profiled_receiver_for_speculation(Node* n); + void record_profiled_arguments_for_speculation(ciMethod* dest_method, Bytecodes::Code bc); + void record_profiled_parameters_for_speculation(); + // Use the type profile to narrow an object type. Node* maybe_cast_profiled_receiver(Node* not_null_obj, - ciProfileData* data, - ciKlass* require_klass); + ciKlass* require_klass, + ciKlass* spec, + bool safe_for_replace); + + // Cast obj to type and emit guard unless we had too many traps here already + Node* maybe_cast_profiled_obj(Node* obj, + ciKlass* type, + bool not_null = false); // Cast obj to not-null on this path Node* cast_not_null(Node* obj, bool do_replace_in_map = true); @@ -775,7 +798,7 @@ // Generate an instance-of idiom. Used by both the instance-of bytecode // and the reflective instance-of call. - Node* gen_instanceof( Node *subobj, Node* superkls ); + Node* gen_instanceof(Node *subobj, Node* superkls, bool safe_for_replace = false); // Generate a check-cast idiom. Used by both the check-cast bytecode // and the array-store bytecode diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/library_call.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -3353,6 +3353,7 @@ // If kls is null, we have a primitive mirror. phi->init_req(_prim_path, prim_return_value); if (stopped()) { set_result(region, phi); return true; } + bool safe_for_replace = (region->in(_prim_path) == top()); Node* p; // handy temp Node* null_ctl; @@ -3363,7 +3364,7 @@ switch (id) { case vmIntrinsics::_isInstance: // nothing is an instance of a primitive type - query_value = gen_instanceof(obj, kls); + query_value = gen_instanceof(obj, kls, safe_for_replace); break; case vmIntrinsics::_getModifiers: @@ -4553,8 +4554,62 @@ const Type* dest_type = dest->Value(&_gvn); const TypeAryPtr* top_src = src_type->isa_aryptr(); const TypeAryPtr* top_dest = dest_type->isa_aryptr(); - if (top_src == NULL || top_src->klass() == NULL || - top_dest == NULL || top_dest->klass() == NULL) { + + // Do we have the type of src? + bool has_src = (top_src != NULL && top_src->klass() != NULL); + // Do we have the type of dest? + bool has_dest = (top_dest != NULL && top_dest->klass() != NULL); + // Is the type for src from speculation? + bool src_spec = false; + // Is the type for dest from speculation? + bool dest_spec = false; + + if (!has_src || !has_dest) { + // We don't have sufficient type information, let's see if + // speculative types can help. We need to have types for both src + // and dest so that it pays off. + + // Do we already have or could we have type information for src + bool could_have_src = has_src; + // Do we already have or could we have type information for dest + bool could_have_dest = has_dest; + + ciKlass* src_k = NULL; + if (!has_src) { + src_k = src_type->speculative_type(); + if (src_k != NULL && src_k->is_array_klass()) { + could_have_src = true; + } + } + + ciKlass* dest_k = NULL; + if (!has_dest) { + dest_k = dest_type->speculative_type(); + if (dest_k != NULL && dest_k->is_array_klass()) { + could_have_dest = true; + } + } + + if (could_have_src && could_have_dest) { + // This is going to pay off so emit the required guards + if (!has_src) { + src = maybe_cast_profiled_obj(src, src_k); + src_type = _gvn.type(src); + top_src = src_type->isa_aryptr(); + has_src = (top_src != NULL && top_src->klass() != NULL); + src_spec = true; + } + if (!has_dest) { + dest = maybe_cast_profiled_obj(dest, dest_k); + dest_type = _gvn.type(dest); + top_dest = dest_type->isa_aryptr(); + has_dest = (top_dest != NULL && top_dest->klass() != NULL); + dest_spec = true; + } + } + } + + if (!has_src || !has_dest) { // Conservatively insert a memory barrier on all memory slices. // Do not let writes into the source float below the arraycopy. insert_mem_bar(Op_MemBarCPUOrder); @@ -4589,6 +4644,40 @@ return true; } + if (src_elem == T_OBJECT) { + // If both arrays are object arrays then having the exact types + // for both will remove the need for a subtype check at runtime + // before the call and may make it possible to pick a faster copy + // routine (without a subtype check on every element) + // Do we have the exact type of src? + bool could_have_src = src_spec; + // Do we have the exact type of dest? + bool could_have_dest = dest_spec; + ciKlass* src_k = top_src->klass(); + ciKlass* dest_k = top_dest->klass(); + if (!src_spec) { + src_k = src_type->speculative_type(); + if (src_k != NULL && src_k->is_array_klass()) { + could_have_src = true; + } + } + if (!dest_spec) { + dest_k = dest_type->speculative_type(); + if (dest_k != NULL && dest_k->is_array_klass()) { + could_have_dest = true; + } + } + if (could_have_src && could_have_dest) { + // If we can have both exact types, emit the missing guards + if (could_have_src && !src_spec) { + src = maybe_cast_profiled_obj(src, src_k); + } + if (could_have_dest && !dest_spec) { + dest = maybe_cast_profiled_obj(dest, dest_k); + } + } + } + //--------------------------------------------------------------------------- // We will make a fast path for this call to arraycopy. diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/parse.hpp --- a/src/share/vm/opto/parse.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/parse.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -607,6 +607,9 @@ // Assumes that there is no applicable local handler. void throw_to_exit(SafePointNode* ex_map); + // Use speculative type to optimize CmpP node + Node* optimize_cmp_with_klass(Node* c); + public: #ifndef PRODUCT // Handle PrintOpto, etc. diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/parse1.cpp --- a/src/share/vm/opto/parse1.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/parse1.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1102,6 +1102,10 @@ _synch_lock = shared_lock(lock_obj); } + // Feed profiling data for parameters to the type system so it can + // propagate it as speculative types + record_profiled_parameters_for_speculation(); + if (depth() == 1) { increment_and_test_invocation_counter(Tier2CompileThreshold); } diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/parse2.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1366,6 +1366,56 @@ } } +/** + * Use speculative type to optimize CmpP node: if comparison is + * against the low level class, cast the object to the speculative + * type if any. CmpP should then go away. + * + * @param c expected CmpP node + * @return result of CmpP on object casted to speculative type + * + */ +Node* Parse::optimize_cmp_with_klass(Node* c) { + // If this is transformed by the _gvn to a comparison with the low + // level klass then we may be able to use speculation + if (c->Opcode() == Op_CmpP && + (c->in(1)->Opcode() == Op_LoadKlass || c->in(1)->Opcode() == Op_DecodeNKlass) && + c->in(2)->is_Con()) { + Node* load_klass = NULL; + Node* decode = NULL; + if (c->in(1)->Opcode() == Op_DecodeNKlass) { + decode = c->in(1); + load_klass = c->in(1)->in(1); + } else { + load_klass = c->in(1); + } + if (load_klass->in(2)->is_AddP()) { + Node* addp = load_klass->in(2); + Node* obj = addp->in(AddPNode::Address); + const TypeOopPtr* obj_type = _gvn.type(obj)->is_oopptr(); + if (obj_type->speculative_type() != NULL) { + ciKlass* k = obj_type->speculative_type(); + inc_sp(2); + obj = maybe_cast_profiled_obj(obj, k); + dec_sp(2); + // Make the CmpP use the casted obj + addp = basic_plus_adr(obj, addp->in(AddPNode::Offset)); + load_klass = load_klass->clone(); + load_klass->set_req(2, addp); + load_klass = _gvn.transform(load_klass); + if (decode != NULL) { + decode = decode->clone(); + decode->set_req(1, load_klass); + load_klass = _gvn.transform(decode); + } + c = c->clone(); + c->set_req(1, load_klass); + c = _gvn.transform(c); + } + } + } + return c; +} //------------------------------do_one_bytecode-------------------------------- // Parse this bytecode, and alter the Parsers JVM->Node mapping @@ -2239,6 +2289,7 @@ a = pop(); b = pop(); c = _gvn.transform( new (C) CmpPNode(b, a) ); + c = optimize_cmp_with_klass(c); do_if(btest, c); break; diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/parseHelper.cpp --- a/src/share/vm/opto/parseHelper.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/parseHelper.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -128,7 +128,7 @@ } // Push the bool result back on stack - Node* res = gen_instanceof(peek(), makecon(TypeKlassPtr::make(klass))); + Node* res = gen_instanceof(peek(), makecon(TypeKlassPtr::make(klass)), true); // Pop from stack AFTER gen_instanceof because it can uncommon trap. pop(); diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/phaseX.cpp --- a/src/share/vm/opto/phaseX.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/phaseX.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -1385,6 +1385,20 @@ } } +/** + * Remove the speculative part of all types that we know of + */ +void PhaseIterGVN::remove_speculative_types() { + assert(UseTypeSpeculation, "speculation is off"); + for (uint i = 0; i < _types.Size(); i++) { + const Type* t = _types.fast_lookup(i); + if (t != NULL && t->isa_oopptr()) { + const TypeOopPtr* to = t->is_oopptr(); + _types.map(i, to->remove_speculative()); + } + } +} + //============================================================================= #ifndef PRODUCT uint PhaseCCP::_total_invokes = 0; diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/phaseX.hpp --- a/src/share/vm/opto/phaseX.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/phaseX.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -500,6 +500,8 @@ ProjNode* create_new_if_for_predicate(ProjNode* cont_proj, Node* new_entry, Deoptimization::DeoptReason reason); + void remove_speculative_types(); + #ifndef PRODUCT protected: // Sub-quadratic implementation of VerifyIterativeGVN. diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/type.cpp --- a/src/share/vm/opto/type.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/type.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -358,7 +358,7 @@ false, 0, oopDesc::mark_offset_in_bytes()); TypeInstPtr::KLASS = TypeInstPtr::make(TypePtr::BotPTR, current->env()->Object_klass(), false, 0, oopDesc::klass_offset_in_bytes()); - TypeOopPtr::BOTTOM = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot, TypeOopPtr::InstanceBot); + TypeOopPtr::BOTTOM = TypeOopPtr::make(TypePtr::BotPTR, OffsetBot, TypeOopPtr::InstanceBot, NULL); TypeMetadataPtr::BOTTOM = TypeMetadataPtr::make(TypePtr::BotPTR, NULL, OffsetBot); @@ -577,7 +577,7 @@ //----------------------interface_vs_oop--------------------------------------- #ifdef ASSERT -bool Type::interface_vs_oop(const Type *t) const { +bool Type::interface_vs_oop_helper(const Type *t) const { bool result = false; const TypePtr* this_ptr = this->make_ptr(); // In case it is narrow_oop @@ -595,6 +595,29 @@ return result; } + +bool Type::interface_vs_oop(const Type *t) const { + if (interface_vs_oop_helper(t)) { + return true; + } + // Now check the speculative parts as well + const TypeOopPtr* this_spec = isa_oopptr() != NULL ? isa_oopptr()->speculative() : NULL; + const TypeOopPtr* t_spec = t->isa_oopptr() != NULL ? t->isa_oopptr()->speculative() : NULL; + if (this_spec != NULL && t_spec != NULL) { + if (this_spec->interface_vs_oop_helper(t_spec)) { + return true; + } + return false; + } + if (this_spec != NULL && this_spec->interface_vs_oop_helper(t)) { + return true; + } + if (t_spec != NULL && interface_vs_oop_helper(t_spec)) { + return true; + } + return false; +} + #endif //------------------------------meet------------------------------------------- @@ -2407,14 +2430,15 @@ const TypeOopPtr *TypeOopPtr::BOTTOM; //------------------------------TypeOopPtr------------------------------------- -TypeOopPtr::TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ) +TypeOopPtr::TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative) : TypePtr(t, ptr, offset), _const_oop(o), _klass(k), _klass_is_exact(xk), _is_ptr_to_narrowoop(false), _is_ptr_to_narrowklass(false), _is_ptr_to_boxed_value(false), - _instance_id(instance_id) { + _instance_id(instance_id), + _speculative(speculative) { if (Compile::current()->eliminate_boxing() && (t == InstPtr) && (offset > 0) && xk && (k != 0) && k->is_instance_klass()) { _is_ptr_to_boxed_value = k->as_instance_klass()->is_boxed_value_offset(offset); @@ -2481,12 +2505,12 @@ //------------------------------make------------------------------------------- const TypeOopPtr *TypeOopPtr::make(PTR ptr, - int offset, int instance_id) { + int offset, int instance_id, const TypeOopPtr* speculative) { assert(ptr != Constant, "no constant generic pointers"); ciKlass* k = Compile::current()->env()->Object_klass(); bool xk = false; ciObject* o = NULL; - return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id))->hashcons(); + return (TypeOopPtr*)(new TypeOopPtr(OopPtr, ptr, k, xk, o, offset, instance_id, speculative))->hashcons(); } @@ -2494,7 +2518,7 @@ const Type *TypeOopPtr::cast_to_ptr_type(PTR ptr) const { assert(_base == OopPtr, "subclass must override cast_to_ptr_type"); if( ptr == _ptr ) return this; - return make(ptr, _offset, _instance_id); + return make(ptr, _offset, _instance_id, _speculative); } //-----------------------------cast_to_instance_id---------------------------- @@ -2524,10 +2548,31 @@ return TypeKlassPtr::make(xk? Constant: NotNull, k, 0); } +const Type *TypeOopPtr::xmeet(const Type *t) const { + const Type* res = xmeet_helper(t); + if (res->isa_oopptr() == NULL) { + return res; + } + + if (res->isa_oopptr() != NULL) { + // type->speculative() == NULL means that speculation is no better + // than type, i.e. type->speculative() == type. So there are 2 + // ways to represent the fact that we have no useful speculative + // data and we should use a single one to be able to test for + // equality between types. Check whether type->speculative() == + // type and set speculative to NULL if it is the case. + const TypeOopPtr* res_oopptr = res->is_oopptr(); + if (res_oopptr->remove_speculative() == res_oopptr->speculative()) { + return res_oopptr->remove_speculative(); + } + } + + return res; +} //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeOopPtr::xmeet( const Type *t ) const { +const Type *TypeOopPtr::xmeet_helper(const Type *t) const { // Perform a fast test for common case; meeting the same types together. if( this == t ) return this; // Meeting same type-rep? @@ -2569,7 +2614,8 @@ case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); - return make(ptr, offset, instance_id); + const TypeOopPtr* speculative = _speculative; + return make(ptr, offset, instance_id, speculative); } case BotPTR: case NotNull: @@ -2581,7 +2627,8 @@ case OopPtr: { // Meeting to other OopPtrs const TypeOopPtr *tp = t->is_oopptr(); int instance_id = meet_instance_id(tp->instance_id()); - return make( meet_ptr(tp->ptr()), meet_offset(tp->offset()), instance_id ); + const TypeOopPtr* speculative = meet_speculative(tp); + return make(meet_ptr(tp->ptr()), meet_offset(tp->offset()), instance_id, speculative); } case InstPtr: // For these, flip the call around to cut down @@ -2598,7 +2645,7 @@ const Type *TypeOopPtr::xdual() const { assert(klass() == Compile::current()->env()->Object_klass(), "no klasses here"); assert(const_oop() == NULL, "no constants here"); - return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id() ); + return new TypeOopPtr(_base, dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative()); } //--------------------------make_from_klass_common----------------------------- @@ -2689,7 +2736,7 @@ } else if (!o->should_be_constant()) { return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0); } - const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, is_autobox_cache); + const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0, InstanceBot, NULL, is_autobox_cache); return arr; } else if (klass->is_type_array_klass()) { // Element is an typeArray @@ -2789,7 +2836,8 @@ bool TypeOopPtr::eq( const Type *t ) const { const TypeOopPtr *a = (const TypeOopPtr*)t; if (_klass_is_exact != a->_klass_is_exact || - _instance_id != a->_instance_id) return false; + _instance_id != a->_instance_id || + !eq_speculative(a)) return false; ciObject* one = const_oop(); ciObject* two = a->const_oop(); if (one == NULL || two == NULL) { @@ -2806,6 +2854,7 @@ (const_oop() ? const_oop()->hash() : 0) + _klass_is_exact + _instance_id + + hash_speculative() + TypePtr::hash(); } @@ -2825,6 +2874,19 @@ st->print(",iid=top"); else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + + dump_speculative(st); +} + +/** + *dump the speculative part of the type + */ +void TypeOopPtr::dump_speculative(outputStream *st) const { + if (_speculative != NULL) { + st->print(" (speculative="); + _speculative->dump_on(st); + st->print(")"); + } } #endif @@ -2838,8 +2900,15 @@ } //------------------------------add_offset------------------------------------- -const TypePtr *TypeOopPtr::add_offset( intptr_t offset ) const { - return make( _ptr, xadd_offset(offset), _instance_id); +const TypePtr *TypeOopPtr::add_offset(intptr_t offset) const { + return make(_ptr, xadd_offset(offset), _instance_id, add_offset_speculative(offset)); +} + +/** + * Return same type without a speculative part + */ +const TypeOopPtr* TypeOopPtr::remove_speculative() const { + return make(_ptr, _offset, _instance_id, NULL); } //------------------------------meet_instance_id-------------------------------- @@ -2859,6 +2928,89 @@ return _instance_id; // Map everything else into self } +/** + * meet of the speculative parts of 2 types + * + * @param other type to meet with + */ +const TypeOopPtr* TypeOopPtr::meet_speculative(const TypeOopPtr* other) const { + bool this_has_spec = (_speculative != NULL); + bool other_has_spec = (other->speculative() != NULL); + + if (!this_has_spec && !other_has_spec) { + return NULL; + } + + // If we are at a point where control flow meets and one branch has + // a speculative type and the other has not, we meet the speculative + // type of one branch with the actual type of the other. If the + // actual type is exact and the speculative is as well, then the + // result is a speculative type which is exact and we can continue + // speculation further. + const TypeOopPtr* this_spec = _speculative; + const TypeOopPtr* other_spec = other->speculative(); + + if (!this_has_spec) { + this_spec = this; + } + + if (!other_has_spec) { + other_spec = other; + } + + return this_spec->meet(other_spec)->is_oopptr(); +} + +/** + * dual of the speculative part of the type + */ +const TypeOopPtr* TypeOopPtr::dual_speculative() const { + if (_speculative == NULL) { + return NULL; + } + return _speculative->dual()->is_oopptr(); +} + +/** + * add offset to the speculative part of the type + * + * @param offset offset to add + */ +const TypeOopPtr* TypeOopPtr::add_offset_speculative(intptr_t offset) const { + if (_speculative == NULL) { + return NULL; + } + return _speculative->add_offset(offset)->is_oopptr(); +} + +/** + * Are the speculative parts of 2 types equal? + * + * @param other type to compare this one to + */ +bool TypeOopPtr::eq_speculative(const TypeOopPtr* other) const { + if (_speculative == NULL || other->speculative() == NULL) { + return _speculative == other->speculative(); + } + + if (_speculative->base() != other->speculative()->base()) { + return false; + } + + return _speculative->eq(other->speculative()); +} + +/** + * Hash of the speculative part of the type + */ +int TypeOopPtr::hash_speculative() const { + if (_speculative == NULL) { + return 0; + } + + return _speculative->hash(); +} + //============================================================================= // Convenience common pre-built types. @@ -2869,8 +3021,8 @@ const TypeInstPtr *TypeInstPtr::KLASS; //------------------------------TypeInstPtr------------------------------------- -TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int off, int instance_id) - : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id), _name(k->name()) { +TypeInstPtr::TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int off, int instance_id, const TypeOopPtr* speculative) + : TypeOopPtr(InstPtr, ptr, k, xk, o, off, instance_id, speculative), _name(k->name()) { assert(k != NULL && (k->is_loaded() || o == NULL), "cannot have constants with non-loaded klass"); @@ -2882,7 +3034,8 @@ bool xk, ciObject* o, int offset, - int instance_id) { + int instance_id, + const TypeOopPtr* speculative) { assert( !k->is_loaded() || k->is_instance_klass(), "Must be for instance"); // Either const_oop() is NULL or else ptr is Constant assert( (!o && ptr != Constant) || (o && ptr == Constant), @@ -2903,7 +3056,7 @@ // Now hash this baby TypeInstPtr *result = - (TypeInstPtr*)(new TypeInstPtr(ptr, k, xk, o ,offset, instance_id))->hashcons(); + (TypeInstPtr*)(new TypeInstPtr(ptr, k, xk, o ,offset, instance_id, speculative))->hashcons(); return result; } @@ -2936,7 +3089,7 @@ if( ptr == _ptr ) return this; // Reconstruct _sig info here since not a problem with later lazy // construction, _sig will show up on demand. - return make(ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id); + return make(ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, _speculative); } @@ -2948,13 +3101,13 @@ ciInstanceKlass* ik = _klass->as_instance_klass(); if( (ik->is_final() || _const_oop) ) return this; // cannot clear xk if( ik->is_interface() ) return this; // cannot set xk - return make(ptr(), klass(), klass_is_exact, const_oop(), _offset, _instance_id); + return make(ptr(), klass(), klass_is_exact, const_oop(), _offset, _instance_id, _speculative); } //-----------------------------cast_to_instance_id---------------------------- const TypeOopPtr *TypeInstPtr::cast_to_instance_id(int instance_id) const { if( instance_id == _instance_id ) return this; - return make(_ptr, klass(), _klass_is_exact, const_oop(), _offset, instance_id); + return make(_ptr, klass(), _klass_is_exact, const_oop(), _offset, instance_id, _speculative); } //------------------------------xmeet_unloaded--------------------------------- @@ -2964,6 +3117,7 @@ int off = meet_offset(tinst->offset()); PTR ptr = meet_ptr(tinst->ptr()); int instance_id = meet_instance_id(tinst->instance_id()); + const TypeOopPtr* speculative = meet_speculative(tinst); const TypeInstPtr *loaded = is_loaded() ? this : tinst; const TypeInstPtr *unloaded = is_loaded() ? tinst : this; @@ -2984,7 +3138,7 @@ assert(loaded->ptr() != TypePtr::Null, "insanity check"); // if( loaded->ptr() == TypePtr::TopPTR ) { return unloaded; } - else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make( ptr, unloaded->klass(), false, NULL, off, instance_id ); } + else if (loaded->ptr() == TypePtr::AnyNull) { return TypeInstPtr::make(ptr, unloaded->klass(), false, NULL, off, instance_id, speculative); } else if (loaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } else if (loaded->ptr() == TypePtr::Constant || loaded->ptr() == TypePtr::NotNull) { if (unloaded->ptr() == TypePtr::BotPTR ) { return TypeInstPtr::BOTTOM; } @@ -3006,7 +3160,7 @@ //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeInstPtr::xmeet( const Type *t ) const { +const Type *TypeInstPtr::xmeet_helper(const Type *t) const { // Perform a fast test for common case; meeting the same types together. if( this == t ) return this; // Meeting same type-rep? @@ -3040,16 +3194,20 @@ int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); int instance_id = meet_instance_id(tp->instance_id()); + const TypeOopPtr* speculative = meet_speculative(tp); switch (ptr) { case TopPTR: case AnyNull: // Fall 'down' to dual of object klass - if (klass()->equals(ciEnv::current()->Object_klass())) { - return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id); + // For instances when a subclass meets a superclass we fall + // below the centerline when the superclass is exact. We need to + // do the same here. + if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) { + return TypeAryPtr::make(ptr, tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative); } else { // cannot subclass, so the meet has to fall badly below the centerline ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id); + return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative); } case Constant: case NotNull: @@ -3058,10 +3216,13 @@ if( above_centerline(_ptr) ) { // if( _ptr == TopPTR || _ptr == AnyNull ) // If 'this' (InstPtr) is above the centerline and it is Object class // then we can subclass in the Java class hierarchy. - if (klass()->equals(ciEnv::current()->Object_klass())) { + // For instances when a subclass meets a superclass we fall + // below the centerline when the superclass is exact. We need + // to do the same here. + if (klass()->equals(ciEnv::current()->Object_klass()) && !klass_is_exact()) { // that is, tp's array type is a subtype of my klass return TypeAryPtr::make(ptr, (ptr == Constant ? tp->const_oop() : NULL), - tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id); + tp->ary(), tp->klass(), tp->klass_is_exact(), offset, instance_id, speculative); } } // The other case cannot happen, since I cannot be a subtype of an array. @@ -3069,7 +3230,7 @@ if( ptr == Constant ) ptr = NotNull; instance_id = InstanceBot; - return make( ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id ); + return make(ptr, ciEnv::current()->Object_klass(), false, NULL, offset, instance_id, speculative); default: typerr(t); } } @@ -3083,13 +3244,15 @@ case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); + const TypeOopPtr* speculative = meet_speculative(tp); return make(ptr, klass(), klass_is_exact(), - (ptr == Constant ? const_oop() : NULL), offset, instance_id); + (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative); } case NotNull: case BotPTR: { int instance_id = meet_instance_id(tp->instance_id()); - return TypeOopPtr::make(ptr, offset, instance_id); + const TypeOopPtr* speculative = meet_speculative(tp); + return TypeOopPtr::make(ptr, offset, instance_id, speculative); } default: typerr(t); } @@ -3102,17 +3265,18 @@ PTR ptr = meet_ptr(tp->ptr()); switch (tp->ptr()) { case Null: - if( ptr == Null ) return TypePtr::make( AnyPtr, ptr, offset ); + if( ptr == Null ) return TypePtr::make(AnyPtr, ptr, offset); // else fall through to AnyNull case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); - return make( ptr, klass(), klass_is_exact(), - (ptr == Constant ? const_oop() : NULL), offset, instance_id); + const TypeOopPtr* speculative = _speculative; + return make(ptr, klass(), klass_is_exact(), + (ptr == Constant ? const_oop() : NULL), offset, instance_id, speculative); } case NotNull: case BotPTR: - return TypePtr::make( AnyPtr, ptr, offset ); + return TypePtr::make(AnyPtr, ptr, offset); default: typerr(t); } } @@ -3139,13 +3303,14 @@ int off = meet_offset( tinst->offset() ); PTR ptr = meet_ptr( tinst->ptr() ); int instance_id = meet_instance_id(tinst->instance_id()); + const TypeOopPtr* speculative = meet_speculative(tinst); // Check for easy case; klasses are equal (and perhaps not loaded!) // If we have constants, then we created oops so classes are loaded // and we can handle the constants further down. This case handles // both-not-loaded or both-loaded classes if (ptr != Constant && klass()->equals(tinst->klass()) && klass_is_exact() == tinst->klass_is_exact()) { - return make( ptr, klass(), klass_is_exact(), NULL, off, instance_id ); + return make(ptr, klass(), klass_is_exact(), NULL, off, instance_id, speculative); } // Classes require inspection in the Java klass hierarchy. Must be loaded. @@ -3167,7 +3332,8 @@ } // Handle mixing oops and interfaces first. - if( this_klass->is_interface() && !tinst_klass->is_interface() ) { + if( this_klass->is_interface() && !(tinst_klass->is_interface() || + tinst_klass == ciEnv::current()->Object_klass())) { ciKlass *tmp = tinst_klass; // Swap interface around tinst_klass = this_klass; this_klass = tmp; @@ -3208,7 +3374,7 @@ // Find out which constant. o = (this_klass == klass()) ? const_oop() : tinst->const_oop(); } - return make( ptr, k, xk, o, off, instance_id ); + return make(ptr, k, xk, o, off, instance_id, speculative); } // Either oop vs oop or interface vs interface or interface vs Object @@ -3285,7 +3451,7 @@ else ptr = NotNull; } - return make( ptr, this_klass, this_xk, o, off, instance_id ); + return make(ptr, this_klass, this_xk, o, off, instance_id, speculative); } // Else classes are not equal // Since klasses are different, we require a LCA in the Java @@ -3296,7 +3462,7 @@ // Now we find the LCA of Java classes ciKlass* k = this_klass->least_common_ancestor(tinst_klass); - return make( ptr, k, false, NULL, off, instance_id ); + return make(ptr, k, false, NULL, off, instance_id, speculative); } // End of case InstPtr } // End of switch @@ -3320,7 +3486,7 @@ // Dual: do NOT dual on klasses. This means I do NOT understand the Java // inheritance mechanism. const Type *TypeInstPtr::xdual() const { - return new TypeInstPtr( dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id() ); + return new TypeInstPtr(dual_ptr(), klass(), klass_is_exact(), const_oop(), dual_offset(), dual_instance_id(), dual_speculative()); } //------------------------------eq--------------------------------------------- @@ -3376,12 +3542,18 @@ st->print(",iid=top"); else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + + dump_speculative(st); } #endif //------------------------------add_offset------------------------------------- -const TypePtr *TypeInstPtr::add_offset( intptr_t offset ) const { - return make( _ptr, klass(), klass_is_exact(), const_oop(), xadd_offset(offset), _instance_id ); +const TypePtr *TypeInstPtr::add_offset(intptr_t offset) const { + return make(_ptr, klass(), klass_is_exact(), const_oop(), xadd_offset(offset), _instance_id, add_offset_speculative(offset)); +} + +const TypeOopPtr *TypeInstPtr::remove_speculative() const { + return make(_ptr, klass(), klass_is_exact(), const_oop(), _offset, _instance_id, NULL); } //============================================================================= @@ -3398,30 +3570,30 @@ const TypeAryPtr *TypeAryPtr::DOUBLES; //------------------------------make------------------------------------------- -const TypeAryPtr *TypeAryPtr::make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id ) { +const TypeAryPtr *TypeAryPtr::make(PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative) { assert(!(k == NULL && ary->_elem->isa_int()), "integral arrays must be pre-equipped with a class"); if (!xk) xk = ary->ary_must_be_exact(); assert(instance_id <= 0 || xk || !UseExactTypes, "instances are always exactly typed"); if (!UseExactTypes) xk = (ptr == Constant); - return (TypeAryPtr*)(new TypeAryPtr(ptr, NULL, ary, k, xk, offset, instance_id, false))->hashcons(); + return (TypeAryPtr*)(new TypeAryPtr(ptr, NULL, ary, k, xk, offset, instance_id, false, speculative))->hashcons(); } //------------------------------make------------------------------------------- -const TypeAryPtr *TypeAryPtr::make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, bool is_autobox_cache) { +const TypeAryPtr *TypeAryPtr::make(PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id, const TypeOopPtr* speculative, bool is_autobox_cache) { assert(!(k == NULL && ary->_elem->isa_int()), "integral arrays must be pre-equipped with a class"); assert( (ptr==Constant && o) || (ptr!=Constant && !o), "" ); if (!xk) xk = (o != NULL) || ary->ary_must_be_exact(); assert(instance_id <= 0 || xk || !UseExactTypes, "instances are always exactly typed"); if (!UseExactTypes) xk = (ptr == Constant); - return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id, is_autobox_cache))->hashcons(); + return (TypeAryPtr*)(new TypeAryPtr(ptr, o, ary, k, xk, offset, instance_id, is_autobox_cache, speculative))->hashcons(); } //------------------------------cast_to_ptr_type------------------------------- const Type *TypeAryPtr::cast_to_ptr_type(PTR ptr) const { if( ptr == _ptr ) return this; - return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), _offset, _instance_id); + return make(ptr, const_oop(), _ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative); } @@ -3430,13 +3602,13 @@ if( klass_is_exact == _klass_is_exact ) return this; if (!UseExactTypes) return this; if (_ary->ary_must_be_exact()) return this; // cannot clear xk - return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id); + return make(ptr(), const_oop(), _ary, klass(), klass_is_exact, _offset, _instance_id, _speculative); } //-----------------------------cast_to_instance_id---------------------------- const TypeOopPtr *TypeAryPtr::cast_to_instance_id(int instance_id) const { if( instance_id == _instance_id ) return this; - return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, instance_id); + return make(_ptr, const_oop(), _ary, klass(), _klass_is_exact, _offset, instance_id, _speculative); } //-----------------------------narrow_size_type------------------------------- @@ -3499,7 +3671,7 @@ new_size = narrow_size_type(new_size); if (new_size == size()) return this; const TypeAry* new_ary = TypeAry::make(elem(), new_size, is_stable()); - return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id); + return make(ptr(), const_oop(), new_ary, klass(), klass_is_exact(), _offset, _instance_id, _speculative); } @@ -3548,7 +3720,7 @@ //------------------------------meet------------------------------------------- // Compute the MEET of two types. It returns a new Type object. -const Type *TypeAryPtr::xmeet( const Type *t ) const { +const Type *TypeAryPtr::xmeet_helper(const Type *t) const { // Perform a fast test for common case; meeting the same types together. if( this == t ) return this; // Meeting same type-rep? // Current "this->_base" is Pointer @@ -3582,13 +3754,15 @@ case TopPTR: case AnyNull: { int instance_id = meet_instance_id(InstanceTop); + const TypeOopPtr* speculative = meet_speculative(tp); return make(ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id); + _ary, _klass, _klass_is_exact, offset, instance_id, speculative); } case BotPTR: case NotNull: { int instance_id = meet_instance_id(tp->instance_id()); - return TypeOopPtr::make(ptr, offset, instance_id); + const TypeOopPtr* speculative = meet_speculative(tp); + return TypeOopPtr::make(ptr, offset, instance_id, speculative); } default: ShouldNotReachHere(); } @@ -3610,8 +3784,9 @@ // else fall through to AnyNull case AnyNull: { int instance_id = meet_instance_id(InstanceTop); - return make( ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id); + const TypeOopPtr* speculative = _speculative; + return make(ptr, (ptr == Constant ? const_oop() : NULL), + _ary, _klass, _klass_is_exact, offset, instance_id, speculative); } default: ShouldNotReachHere(); } @@ -3627,6 +3802,7 @@ const TypeAry *tary = _ary->meet(tap->_ary)->is_ary(); PTR ptr = meet_ptr(tap->ptr()); int instance_id = meet_instance_id(tap->instance_id()); + const TypeOopPtr* speculative = meet_speculative(tap); ciKlass* lazy_klass = NULL; if (tary->_elem->isa_int()) { // Integral array element types have irrelevant lattice relations. @@ -3654,7 +3830,7 @@ // 'this' is exact and super or unrelated: (this->_klass_is_exact && !klass()->is_subtype_of(tap->klass())))) { tary = TypeAry::make(Type::BOTTOM, tary->_size, tary->_stable); - return make( NotNull, NULL, tary, lazy_klass, false, off, InstanceBot ); + return make(NotNull, NULL, tary, lazy_klass, false, off, InstanceBot); } bool xk = false; @@ -3662,8 +3838,12 @@ case AnyNull: case TopPTR: // Compute new klass on demand, do not use tap->_klass - xk = (tap->_klass_is_exact | this->_klass_is_exact); - return make( ptr, const_oop(), tary, lazy_klass, xk, off, instance_id ); + if (below_centerline(this->_ptr)) { + xk = this->_klass_is_exact; + } else { + xk = (tap->_klass_is_exact | this->_klass_is_exact); + } + return make(ptr, const_oop(), tary, lazy_klass, xk, off, instance_id, speculative); case Constant: { ciObject* o = const_oop(); if( _ptr == Constant ) { @@ -3675,25 +3855,23 @@ } else { xk = true; } - } else if( above_centerline(_ptr) ) { + } else if(above_centerline(_ptr)) { o = tap->const_oop(); xk = true; } else { // Only precise for identical arrays xk = this->_klass_is_exact && (klass() == tap->klass()); } - return TypeAryPtr::make( ptr, o, tary, lazy_klass, xk, off, instance_id ); + return TypeAryPtr::make(ptr, o, tary, lazy_klass, xk, off, instance_id, speculative); } case NotNull: case BotPTR: // Compute new klass on demand, do not use tap->_klass if (above_centerline(this->_ptr)) xk = tap->_klass_is_exact; - else if (above_centerline(tap->_ptr)) - xk = this->_klass_is_exact; else xk = (tap->_klass_is_exact & this->_klass_is_exact) && (klass() == tap->klass()); // Only precise for identical arrays - return TypeAryPtr::make( ptr, NULL, tary, lazy_klass, xk, off, instance_id ); + return TypeAryPtr::make(ptr, NULL, tary, lazy_klass, xk, off, instance_id, speculative); default: ShouldNotReachHere(); } } @@ -3704,16 +3882,20 @@ int offset = meet_offset(tp->offset()); PTR ptr = meet_ptr(tp->ptr()); int instance_id = meet_instance_id(tp->instance_id()); + const TypeOopPtr* speculative = meet_speculative(tp); switch (ptr) { case TopPTR: case AnyNull: // Fall 'down' to dual of object klass - if( tp->klass()->equals(ciEnv::current()->Object_klass()) ) { - return TypeAryPtr::make( ptr, _ary, _klass, _klass_is_exact, offset, instance_id ); + // For instances when a subclass meets a superclass we fall + // below the centerline when the superclass is exact. We need to + // do the same here. + if (tp->klass()->equals(ciEnv::current()->Object_klass()) && !tp->klass_is_exact()) { + return TypeAryPtr::make(ptr, _ary, _klass, _klass_is_exact, offset, instance_id, speculative); } else { // cannot subclass, so the meet has to fall badly below the centerline ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id); + return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative); } case Constant: case NotNull: @@ -3722,10 +3904,13 @@ if (above_centerline(tp->ptr())) { // If 'tp' is above the centerline and it is Object class // then we can subclass in the Java class hierarchy. - if( tp->klass()->equals(ciEnv::current()->Object_klass()) ) { + // For instances when a subclass meets a superclass we fall + // below the centerline when the superclass is exact. We need + // to do the same here. + if (tp->klass()->equals(ciEnv::current()->Object_klass()) && !tp->klass_is_exact()) { // that is, my array type is a subtype of 'tp' klass - return make( ptr, (ptr == Constant ? const_oop() : NULL), - _ary, _klass, _klass_is_exact, offset, instance_id ); + return make(ptr, (ptr == Constant ? const_oop() : NULL), + _ary, _klass, _klass_is_exact, offset, instance_id, speculative); } } // The other case cannot happen, since t cannot be a subtype of an array. @@ -3733,7 +3918,7 @@ if( ptr == Constant ) ptr = NotNull; instance_id = InstanceBot; - return TypeInstPtr::make( ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id); + return TypeInstPtr::make(ptr, ciEnv::current()->Object_klass(), false, NULL,offset, instance_id, speculative); default: typerr(t); } } @@ -3744,7 +3929,7 @@ //------------------------------xdual------------------------------------------ // Dual: compute field-by-field dual const Type *TypeAryPtr::xdual() const { - return new TypeAryPtr( dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance_id(), is_autobox_cache() ); + return new TypeAryPtr(dual_ptr(), _const_oop, _ary->dual()->is_ary(),_klass, _klass_is_exact, dual_offset(), dual_instance_id(), is_autobox_cache(), dual_speculative()); } //----------------------interface_vs_oop--------------------------------------- @@ -3796,6 +3981,8 @@ st->print(",iid=top"); else if (_instance_id != InstanceBot) st->print(",iid=%d",_instance_id); + + dump_speculative(st); } #endif @@ -3805,10 +3992,13 @@ } //------------------------------add_offset------------------------------------- -const TypePtr *TypeAryPtr::add_offset( intptr_t offset ) const { - return make( _ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id ); -} - +const TypePtr *TypeAryPtr::add_offset(intptr_t offset) const { + return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, xadd_offset(offset), _instance_id, add_offset_speculative(offset)); +} + +const TypeOopPtr *TypeAryPtr::remove_speculative() const { + return make(_ptr, _const_oop, _ary, _klass, _klass_is_exact, _offset, _instance_id, NULL); +} //============================================================================= diff -r 4c9115774c8e -r 97d400662426 src/share/vm/opto/type.hpp --- a/src/share/vm/opto/type.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/opto/type.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -159,6 +159,11 @@ // Table for efficient dualing of base types static const TYPES dual_type[lastype]; +#ifdef ASSERT + // One type is interface, the other is oop + virtual bool interface_vs_oop_helper(const Type *t) const; +#endif + protected: // Each class of type is also identified by its base. const TYPES _base; // Enum of Types type @@ -376,6 +381,9 @@ bool require_constant = false, bool is_autobox_cache = false); + // Speculative type. See TypeInstPtr + virtual ciKlass* speculative_type() const { return NULL; } + private: // support arrays static const BasicType _basic_type[]; @@ -784,7 +792,7 @@ // Some kind of oop (Java pointer), either klass or instance or array. class TypeOopPtr : public TypePtr { protected: - TypeOopPtr( TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ); + TypeOopPtr(TYPES t, PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative); public: virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -810,11 +818,27 @@ // This is the the node index of the allocation node creating this instance. int _instance_id; + // Extra type information profiling gave us. We propagate it the + // same way the rest of the type info is propagated. If we want to + // use it, then we have to emit a guard: this part of the type is + // not something we know but something we speculate about the type. + const TypeOopPtr* _speculative; + static const TypeOopPtr* make_from_klass_common(ciKlass* klass, bool klass_change, bool try_for_exact); int dual_instance_id() const; int meet_instance_id(int uid) const; + // utility methods to work on the speculative part of the type + const TypeOopPtr* dual_speculative() const; + const TypeOopPtr* meet_speculative(const TypeOopPtr* other) const; + bool eq_speculative(const TypeOopPtr* other) const; + int hash_speculative() const; + const TypeOopPtr* add_offset_speculative(intptr_t offset) const; +#ifndef PRODUCT + void dump_speculative(outputStream *st) const; +#endif + public: // Creates a type given a klass. Correctly handles multi-dimensional arrays // Respects UseUniqueSubclasses. @@ -841,7 +865,7 @@ bool not_null_elements = false); // Make a generic (unclassed) pointer to an oop. - static const TypeOopPtr* make(PTR ptr, int offset, int instance_id); + static const TypeOopPtr* make(PTR ptr, int offset, int instance_id, const TypeOopPtr* speculative); ciObject* const_oop() const { return _const_oop; } virtual ciKlass* klass() const { return _klass; } @@ -855,6 +879,7 @@ bool is_known_instance() const { return _instance_id > 0; } int instance_id() const { return _instance_id; } bool is_known_instance_field() const { return is_known_instance() && _offset >= 0; } + const TypeOopPtr* speculative() const { return _speculative; } virtual intptr_t get_con() const; @@ -868,9 +893,13 @@ const TypeKlassPtr* as_klass_type() const; virtual const TypePtr *add_offset( intptr_t offset ) const; + // Return same type without a speculative part + virtual const TypeOopPtr* remove_speculative() const; - virtual const Type *xmeet( const Type *t ) const; + virtual const Type *xmeet(const Type *t) const; virtual const Type *xdual() const; // Compute dual right now. + // the core of the computation of the meet for TypeOopPtr and for its subclasses + virtual const Type *xmeet_helper(const Type *t) const; // Do not allow interface-vs.-noninterface joins to collapse to top. virtual const Type *filter( const Type *kills ) const; @@ -880,13 +909,24 @@ #ifndef PRODUCT virtual void dump2( Dict &d, uint depth, outputStream *st ) const; #endif + + // Return the speculative type if any + ciKlass* speculative_type() const { + if (_speculative != NULL) { + const TypeOopPtr* speculative = _speculative->join(this)->is_oopptr(); + if (speculative->klass_is_exact()) { + return speculative->klass(); + } + } + return NULL; + } }; //------------------------------TypeInstPtr------------------------------------ // Class of Java object pointers, pointing either to non-array Java instances // or to a Klass* (including array klasses). class TypeInstPtr : public TypeOopPtr { - TypeInstPtr( PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id ); + TypeInstPtr(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id, const TypeOopPtr* speculative); virtual bool eq( const Type *t ) const; virtual int hash() const; // Type specific hashing @@ -899,30 +939,30 @@ // Make a pointer to a constant oop. static const TypeInstPtr *make(ciObject* o) { - return make(TypePtr::Constant, o->klass(), true, o, 0); + return make(TypePtr::Constant, o->klass(), true, o, 0, InstanceBot); } // Make a pointer to a constant oop with offset. static const TypeInstPtr *make(ciObject* o, int offset) { - return make(TypePtr::Constant, o->klass(), true, o, offset); + return make(TypePtr::Constant, o->klass(), true, o, offset, InstanceBot); } // Make a pointer to some value of type klass. static const TypeInstPtr *make(PTR ptr, ciKlass* klass) { - return make(ptr, klass, false, NULL, 0); + return make(ptr, klass, false, NULL, 0, InstanceBot); } // Make a pointer to some non-polymorphic value of exactly type klass. static const TypeInstPtr *make_exact(PTR ptr, ciKlass* klass) { - return make(ptr, klass, true, NULL, 0); + return make(ptr, klass, true, NULL, 0, InstanceBot); } // Make a pointer to some value of type klass with offset. static const TypeInstPtr *make(PTR ptr, ciKlass* klass, int offset) { - return make(ptr, klass, false, NULL, offset); + return make(ptr, klass, false, NULL, offset, InstanceBot); } // Make a pointer to an oop. - static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = InstanceBot ); + static const TypeInstPtr *make(PTR ptr, ciKlass* k, bool xk, ciObject* o, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL); /** Create constant type for a constant boxed value */ const Type* get_const_boxed_value() const; @@ -939,8 +979,11 @@ virtual const TypeOopPtr *cast_to_instance_id(int instance_id) const; virtual const TypePtr *add_offset( intptr_t offset ) const; + // Return same type without a speculative part + virtual const TypeOopPtr* remove_speculative() const; - virtual const Type *xmeet( const Type *t ) const; + // the core of the computation of the meet of 2 types + virtual const Type *xmeet_helper(const Type *t) const; virtual const TypeInstPtr *xmeet_unloaded( const TypeInstPtr *t ) const; virtual const Type *xdual() const; // Compute dual right now. @@ -959,8 +1002,8 @@ // Class of Java array pointers class TypeAryPtr : public TypeOopPtr { TypeAryPtr( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, - int offset, int instance_id, bool is_autobox_cache ) - : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id), + int offset, int instance_id, bool is_autobox_cache, const TypeOopPtr* speculative) + : TypeOopPtr(AryPtr,ptr,k,xk,o,offset, instance_id, speculative), _ary(ary), _is_autobox_cache(is_autobox_cache) { @@ -998,9 +1041,9 @@ bool is_autobox_cache() const { return _is_autobox_cache; } - static const TypeAryPtr *make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot); + static const TypeAryPtr *make( PTR ptr, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL); // Constant pointer to array - static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, bool is_autobox_cache = false); + static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot, const TypeOopPtr* speculative = NULL, bool is_autobox_cache = false); // Return a 'ptr' version of this type virtual const Type *cast_to_ptr_type(PTR ptr) const; @@ -1014,8 +1057,11 @@ virtual bool empty(void) const; // TRUE if type is vacuous virtual const TypePtr *add_offset( intptr_t offset ) const; + // Return same type without a speculative part + virtual const TypeOopPtr* remove_speculative() const; - virtual const Type *xmeet( const Type *t ) const; + // the core of the computation of the meet of 2 types + virtual const Type *xmeet_helper(const Type *t) const; virtual const Type *xdual() const; // Compute dual right now. const TypeAryPtr* cast_to_stable(bool stable, int stable_dimension = 1) const; diff -r 4c9115774c8e -r 97d400662426 src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/runtime/arguments.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -3721,6 +3721,14 @@ // incremental inlining: bump MaxNodeLimit FLAG_SET_DEFAULT(MaxNodeLimit, (intx)75000); } + if (!UseTypeSpeculation && FLAG_IS_DEFAULT(TypeProfileLevel)) { + // nothing to use the profiling, turn if off + FLAG_SET_DEFAULT(TypeProfileLevel, 0); + } + if (UseTypeSpeculation && FLAG_IS_DEFAULT(ReplaceInParentMaps)) { + // Doing the replace in parent maps helps speculation + FLAG_SET_DEFAULT(ReplaceInParentMaps, true); + } #endif if (PrintAssembly && FLAG_IS_DEFAULT(DebugNonSafepoints)) { diff -r 4c9115774c8e -r 97d400662426 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/runtime/globals.hpp Wed Oct 23 19:22:28 2013 +0000 @@ -2671,13 +2671,18 @@ "Enable aggressive optimizations - see arguments.cpp") \ \ product_pd(uintx, TypeProfileLevel, \ - "=XY, with Y, Type profiling of arguments at call" \ - " X, Type profiling of return value at call" \ - "X and Y in 0->off ; 1->js292 only; 2->all methods") \ + "=XYZ, with Z: Type profiling of arguments at call; " \ + "Y: Type profiling of return value at call; " \ + "X: Type profiling of parameters to methods; " \ + "X, Y and Z in 0=off ; 1=jsr292 only; 2=all methods") \ \ product(intx, TypeProfileArgsLimit, 2, \ "max number of call arguments to consider for type profiling") \ \ + product(intx, TypeProfileParmsLimit, 2, \ + "max number of incoming parameters to consider for type profiling"\ + ", -1 for all") \ + \ /* statistics */ \ develop(bool, CountCompiledCalls, false, \ "Count method invocations") \ diff -r 4c9115774c8e -r 97d400662426 src/share/vm/runtime/java.cpp --- a/src/share/vm/runtime/java.cpp Wed Oct 23 10:44:12 2013 +0200 +++ b/src/share/vm/runtime/java.cpp Wed Oct 23 19:22:28 2013 +0000 @@ -193,6 +193,11 @@ m->print_invocation_count(); tty->print_cr(" mdo size: %d bytes", m->method_data()->size_in_bytes()); tty->cr(); + // Dump data on parameters if any + if (m->method_data() != NULL && m->method_data()->parameters_type_data() != NULL) { + tty->fill_to(2); + m->method_data()->parameters_type_data()->print_data_on(tty); + } m->print_codes(); total_size += m->method_data()->size_in_bytes(); } diff -r 4c9115774c8e -r 97d400662426 test/compiler/types/TypeSpeculation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/types/TypeSpeculation.java Wed Oct 23 19:22:28 2013 +0000 @@ -0,0 +1,428 @@ +/* + * Copyright (c) 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. + */ + +/* + * @test + * @bug 8024070 + * @summary Test that type speculation doesn't cause incorrect execution + * @run main/othervm -XX:-UseOnStackReplacement -XX:-BackgroundCompilation -XX:TypeProfileLevel=222 TypeSpeculation + * + */ + +public class TypeSpeculation { + + interface I { + } + + static class A { + int m() { + return 1; + } + } + + static class B extends A implements I { + int m() { + return 2; + } + } + + static class C extends B { + int m() { + return 3; + } + } + + static int test1_invokevirtual(A a) { + return a.m(); + } + + static int test1_1(A a) { + return test1_invokevirtual(a); + } + + static boolean test1() { + A a = new A(); + B b = new B(); + C c = new C(); + + // pollute profile at test1_invokevirtual to make sure the + // compiler cannot rely on it + for (int i = 0; i < 5000; i++) { + test1_invokevirtual(a); + test1_invokevirtual(b); + test1_invokevirtual(c); + } + + // profiling + speculation should make test1_invokevirtual + // inline A.m() with a guard + for (int i = 0; i < 20000; i++) { + int res = test1_1(b); + if (res != b.m()) { + System.out.println("test1 failed with class B"); + return false; + } + } + // check that the guard works as expected by passing a + // different type + int res = test1_1(a); + if (res != a.m()) { + System.out.println("test1 failed with class A"); + return false; + } + return true; + } + + static int test2_invokevirtual(A a) { + return a.m(); + } + + static int test2_1(A a, boolean t) { + A aa; + if (t) { + aa = (B)a; + } else { + aa = a; + } + // if a of type B is passed to test2_1, the static type of aa + // here is no better than A but the profiled type is B so this + // should inline + return test2_invokevirtual(aa); + } + + static boolean test2() { + A a = new A(); + B b = new B(); + C c = new C(); + + // pollute profile at test2_invokevirtual to make sure the + // compiler cannot rely on it + for (int i = 0; i < 5000; i++) { + test2_invokevirtual(a); + test2_invokevirtual(b); + test2_invokevirtual(c); + } + + // profiling + speculation should make test2_invokevirtual + // inline A.m() with a guard + for (int i = 0; i < 20000; i++) { + int res = test2_1(b, (i % 2) == 0); + if (res != b.m()) { + System.out.println("test2 failed with class B"); + return false; + } + } + // check that the guard works as expected by passing a + // different type + int res = test2_1(a, false); + if (res != a.m()) { + System.out.println("test2 failed with class A"); + return false; + } + return true; + } + + static int test3_invokevirtual(A a) { + return a.m(); + } + + static void test3_2(A a) { + } + + static int test3_1(A a, int i) { + if (i == 0) { + return 0; + } + // If we come here and a is of type B but parameter profiling + // is polluted, both branches of the if below should have + // profiling that tell us and inlining of the virtual call + // should happen + if (i == 1) { + test3_2(a); + } else { + test3_2(a); + } + return test3_invokevirtual(a); + } + + static boolean test3() { + A a = new A(); + B b = new B(); + C c = new C(); + + // pollute profile at test3_invokevirtual and test3_1 to make + // sure the compiler cannot rely on it + for (int i = 0; i < 3000; i++) { + test3_invokevirtual(a); + test3_invokevirtual(b); + test3_invokevirtual(c); + test3_1(a, 0); + test3_1(b, 0); + } + + // profiling + speculation should make test3_invokevirtual + // inline A.m() with a guard + for (int i = 0; i < 20000; i++) { + int res = test3_1(b, (i % 2) + 1); + if (res != b.m()) { + System.out.println("test3 failed with class B"); + return false; + } + } + // check that the guard works as expected by passing a + // different type + int res = test3_1(a, 1); + if (res != a.m()) { + System.out.println("test3 failed with class A"); + return false; + } + return true; + } + + // Mix 2 incompatible profiled types + static int test4_invokevirtual(A a) { + return a.m(); + } + + static void test4_2(A a) { + } + + static int test4_1(A a, boolean b) { + if (b) { + test4_2(a); + } else { + test4_2(a); + } + // shouldn't inline + return test4_invokevirtual(a); + } + + static boolean test4() { + A a = new A(); + B b = new B(); + C c = new C(); + + // pollute profile at test3_invokevirtual and test3_1 to make + // sure the compiler cannot rely on it + for (int i = 0; i < 3000; i++) { + test4_invokevirtual(a); + test4_invokevirtual(b); + test4_invokevirtual(c); + } + + for (int i = 0; i < 20000; i++) { + if ((i % 2) == 0) { + int res = test4_1(a, true); + if (res != a.m()) { + System.out.println("test4 failed with class A"); + return false; + } + } else { + int res = test4_1(b, false); + if (res != b.m()) { + System.out.println("test4 failed with class B"); + return false; + } + } + } + return true; + } + + // Mix one profiled type with an incompatible type + static int test5_invokevirtual(A a) { + return a.m(); + } + + static void test5_2(A a) { + } + + static int test5_1(A a, boolean b) { + if (b) { + test5_2(a); + } else { + A aa = (B)a; + } + // shouldn't inline + return test5_invokevirtual(a); + } + + static boolean test5() { + A a = new A(); + B b = new B(); + C c = new C(); + + // pollute profile at test3_invokevirtual and test3_1 to make + // sure the compiler cannot rely on it + for (int i = 0; i < 3000; i++) { + test5_invokevirtual(a); + test5_invokevirtual(b); + test5_invokevirtual(c); + } + + for (int i = 0; i < 20000; i++) { + if ((i % 2) == 0) { + int res = test5_1(a, true); + if (res != a.m()) { + System.out.println("test5 failed with class A"); + return false; + } + } else { + int res = test5_1(b, false); + if (res != b.m()) { + System.out.println("test5 failed with class B"); + return false; + } + } + } + return true; + } + + // Mix incompatible profiled types + static void test6_2(Object o) { + } + + static Object test6_1(Object o, boolean b) { + if (b) { + test6_2(o); + } else { + test6_2(o); + } + return o; + } + + static boolean test6() { + A a = new A(); + A[] aa = new A[10]; + + for (int i = 0; i < 20000; i++) { + if ((i % 2) == 0) { + test6_1(a, true); + } else { + test6_1(aa, false); + } + } + return true; + } + + // Mix a profiled type with an incompatible type + static void test7_2(Object o) { + } + + static Object test7_1(Object o, boolean b) { + if (b) { + test7_2(o); + } else { + Object oo = (A[])o; + } + return o; + } + + static boolean test7() { + A a = new A(); + A[] aa = new A[10]; + + for (int i = 0; i < 20000; i++) { + if ((i % 2) == 0) { + test7_1(a, true); + } else { + test7_1(aa, false); + } + } + return true; + } + + // Mix a profiled type with an interface + static void test8_2(Object o) { + } + + static I test8_1(Object o) { + test8_2(o); + return (I)o; + } + + static boolean test8() { + A a = new A(); + B b = new B(); + C c = new C(); + + for (int i = 0; i < 20000; i++) { + test8_1(b); + } + return true; + } + + // Mix a profiled type with a constant + static void test9_2(Object o) { + } + + static Object test9_1(Object o, boolean b) { + Object oo; + if (b) { + test9_2(o); + oo = o; + } else { + oo = "some string"; + } + return oo; + } + + static boolean test9() { + A a = new A(); + + for (int i = 0; i < 20000; i++) { + if ((i % 2) == 0) { + test9_1(a, true); + } else { + test9_1(a, false); + } + } + return true; + } + + static public void main(String[] args) { + boolean success = true; + + success = test1() && success; + + success = test2() && success; + + success = test3() && success; + + success = test4() && success; + + success = test5() && success; + + success = test6() && success; + + success = test7() && success; + + success = test8() && success; + + success = test9() && success; + + if (success) { + System.out.println("TEST PASSED"); + } else { + throw new RuntimeException("TEST FAILED: erroneous bound check elimination"); + } + } +}