diff --git a/make/hotspot_version b/make/hotspot_version index cb86240983b0610044dec25532f0b87944d486f8..ebdd5c3b821bd2ffe25510cf866e99ee34f4ce2d 100644 --- a/make/hotspot_version +++ b/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2014 HS_MAJOR_VER=25 HS_MINOR_VER=40 -HS_BUILD_NUMBER=22 +HS_BUILD_NUMBER=23 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 diff --git a/src/cpu/ppc/vm/interp_masm_ppc_64.cpp b/src/cpu/ppc/vm/interp_masm_ppc_64.cpp index 234e64e6c4e855c73676df8658d00518d993e64d..0d39104acab4adbe6d57c8633bf768129b146684 100644 --- a/src/cpu/ppc/vm/interp_masm_ppc_64.cpp +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.cpp @@ -545,6 +545,9 @@ void InterpreterMacroAssembler::index_check_without_pop(Register Rarray, Registe cmplw(CCR0, Rindex, Rlength); sldi(RsxtIndex, RsxtIndex, index_shift); blt(CCR0, LnotOOR); + // Index should be in R17_tos, array should be in R4_ARG2. + mr(R17_tos, Rindex); + mr(R4_ARG2, Rarray); load_dispatch_table(Rtmp, (address*)Interpreter::_throw_ArrayIndexOutOfBoundsException_entry); mtctr(Rtmp); bctr(); @@ -1679,6 +1682,228 @@ void InterpreterMacroAssembler::record_klass_in_profile_helper( } } +// Argument and return type profilig. +// kills: tmp, tmp2, R0, CR0, CR1 +void InterpreterMacroAssembler::profile_obj_type(Register obj, Register mdo_addr_base, + RegisterOrConstant mdo_addr_offs, Register tmp, Register tmp2) { + Label do_nothing, do_update; + + // tmp2 = obj is allowed + assert_different_registers(obj, mdo_addr_base, tmp, R0); + assert_different_registers(tmp2, mdo_addr_base, tmp, R0); + const Register klass = tmp2; + + verify_oop(obj); + + ld(tmp, mdo_addr_offs, mdo_addr_base); + + // Set null_seen if obj is 0. + cmpdi(CCR0, obj, 0); + ori(R0, tmp, TypeEntries::null_seen); + beq(CCR0, do_update); + + load_klass(klass, obj); + + clrrdi(R0, tmp, exact_log2(-TypeEntries::type_klass_mask)); + // Basically same as andi(R0, tmp, TypeEntries::type_klass_mask); + cmpd(CCR1, R0, klass); + // Klass seen before, nothing to do (regardless of unknown bit). + //beq(CCR1, do_nothing); + + andi_(R0, klass, TypeEntries::type_unknown); + // Already unknown. Nothing to do anymore. + //bne(CCR0, do_nothing); + crorc(/*CCR0 eq*/2, /*CCR1 eq*/4+2, /*CCR0 eq*/2); // cr0 eq = cr1 eq or cr0 ne + beq(CCR0, do_nothing); + + clrrdi_(R0, tmp, exact_log2(-TypeEntries::type_mask)); + orr(R0, klass, tmp); // Combine klass and null_seen bit (only used if (tmp & type_mask)==0). + beq(CCR0, do_update); // First time here. Set profile type. + + // Different than before. Cannot keep accurate profile. + ori(R0, tmp, TypeEntries::type_unknown); + + bind(do_update); + // update profile + std(R0, mdo_addr_offs, mdo_addr_base); + + align(32, 12); + bind(do_nothing); +} + +void InterpreterMacroAssembler::profile_arguments_type(Register callee, Register tmp1, Register tmp2, bool is_virtual) { + if (!ProfileInterpreter) { + return; + } + + assert_different_registers(callee, tmp1, tmp2, R28_mdx); + + if (MethodData::profile_arguments() || MethodData::profile_return()) { + Label profile_continue; + + test_method_data_pointer(profile_continue); + + int off_to_start = is_virtual ? in_bytes(VirtualCallData::virtual_call_data_size()) : in_bytes(CounterData::counter_data_size()); + + lbz(tmp1, in_bytes(DataLayout::tag_offset()) - off_to_start, R28_mdx); + cmpwi(CCR0, tmp1, is_virtual ? DataLayout::virtual_call_type_data_tag : DataLayout::call_type_data_tag); + bne(CCR0, profile_continue); + + if (MethodData::profile_arguments()) { + Label done; + int off_to_args = in_bytes(TypeEntriesAtCall::args_data_offset()); + add(R28_mdx, off_to_args, R28_mdx); + + 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. + ld(tmp1, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args, R28_mdx); + cmpdi(CCR0, tmp1, (i+1)*TypeStackSlotEntries::per_arg_count()); + addi(tmp1, tmp1, -i*TypeStackSlotEntries::per_arg_count()); + blt(CCR0, done); + } + ld(tmp1, in_bytes(Method::const_offset()), callee); + lhz(tmp1, in_bytes(ConstMethod::size_of_parameters_offset()), tmp1); + // 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. But there's an extra slot at + // the top of the stack. So the offset is n - o from Lesp. + ld(tmp2, in_bytes(TypeEntriesAtCall::stack_slot_offset(i))-off_to_args, R28_mdx); + subf(tmp1, tmp2, tmp1); + + sldi(tmp1, tmp1, Interpreter::logStackElementSize); + ldx(tmp1, tmp1, R15_esp); + + profile_obj_type(tmp1, R28_mdx, in_bytes(TypeEntriesAtCall::argument_type_offset(i))-off_to_args, tmp2, tmp1); + + int to_add = in_bytes(TypeStackSlotEntries::per_arg_size()); + addi(R28_mdx, R28_mdx, to_add); + off_to_args += to_add; + } + + if (MethodData::profile_return()) { + ld(tmp1, in_bytes(TypeEntriesAtCall::cell_count_offset())-off_to_args, R28_mdx); + addi(tmp1, tmp1, -TypeProfileArgsLimit*TypeStackSlotEntries::per_arg_count()); + } + + bind(done); + + if (MethodData::profile_return()) { + // We're right after the type profile for the last + // argument. tmp1 is the number of cells 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"); + sldi(tmp1, tmp1, exact_log2(DataLayout::cell_size)); + add(R28_mdx, tmp1, R28_mdx); + } + } else { + assert(MethodData::profile_return(), "either profile call args or call ret"); + update_mdp_by_constant(in_bytes(TypeEntriesAtCall::return_only_size())); + } + + // Mdp points right after the end of the + // CallTypeData/VirtualCallTypeData, right after the cells for the + // return value type if there's one. + align(32, 12); + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_return_type(Register ret, Register tmp1, Register tmp2) { + assert_different_registers(ret, tmp1, tmp2); + if (ProfileInterpreter && MethodData::profile_return()) { + Label profile_continue; + + test_method_data_pointer(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. + lbz(tmp1, 0, R14_bcp); + lbz(tmp2, Method::intrinsic_id_offset_in_bytes(), R19_method); + cmpwi(CCR0, tmp1, Bytecodes::_invokedynamic); + cmpwi(CCR1, tmp1, Bytecodes::_invokehandle); + cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + cmpwi(CCR1, tmp2, vmIntrinsics::_compiledLambdaForm); + cror(/*CR0 eq*/2, /*CR1 eq*/4+2, /*CR0 eq*/2); + bne(CCR0, profile_continue); + } + + profile_obj_type(ret, R28_mdx, -in_bytes(ReturnTypeEntry::size()), tmp1, tmp2); + + align(32, 12); + bind(profile_continue); + } +} + +void InterpreterMacroAssembler::profile_parameters_type(Register tmp1, Register tmp2, Register tmp3, Register tmp4) { + if (ProfileInterpreter && MethodData::profile_parameters()) { + Label profile_continue, done; + + test_method_data_pointer(profile_continue); + + // Load the offset of the area within the MDO used for + // parameters. If it's negative we're not profiling any parameters. + lwz(tmp1, in_bytes(MethodData::parameters_type_data_di_offset()) - in_bytes(MethodData::data_offset()), R28_mdx); + cmpwi(CCR0, tmp1, 0); + blt(CCR0, 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 + + // Pointer to the parameter area in the MDO. + const Register mdp = tmp1; + add(mdp, tmp1, R28_mdx); + + // Pffset of the current profile entry to update. + const Register entry_offset = tmp2; + // entry_offset = array len in number of cells + ld(entry_offset, in_bytes(ArrayData::array_len_offset()), mdp); + + int off_base = in_bytes(ParametersTypeData::stack_slot_offset(0)); + assert(off_base % DataLayout::cell_size == 0, "should be a number of cells"); + + // entry_offset (number of cells) = array len - size of 1 entry + offset of the stack slot field + addi(entry_offset, entry_offset, -TypeStackSlotEntries::per_arg_count() + (off_base / DataLayout::cell_size)); + // entry_offset in bytes + sldi(entry_offset, entry_offset, exact_log2(DataLayout::cell_size)); + + Label loop; + align(32, 12); + bind(loop); + + // Load offset on the stack from the slot for this parameter. + ld(tmp3, entry_offset, mdp); + sldi(tmp3, tmp3, Interpreter::logStackElementSize); + neg(tmp3, tmp3); + // Read the parameter from the local area. + ldx(tmp3, tmp3, R18_locals); + + // Make entry_offset now point to the type field for this parameter. + int type_base = in_bytes(ParametersTypeData::type_offset(0)); + assert(type_base > off_base, "unexpected"); + addi(entry_offset, entry_offset, type_base - off_base); + + // Profile the parameter. + profile_obj_type(tmp3, mdp, entry_offset, tmp4, tmp3); + + // Go to next parameter. + int delta = TypeStackSlotEntries::per_arg_count() * DataLayout::cell_size + (type_base - off_base); + cmpdi(CCR0, entry_offset, off_base + delta); + addi(entry_offset, entry_offset, -delta); + bge(CCR0, loop); + + align(32, 12); + bind(profile_continue); + } +} + // Add a InterpMonitorElem to stack (see frame_sparc.hpp). void InterpreterMacroAssembler::add_monitor_to_stack(bool stack_is_empty, Register Rtemp1, Register Rtemp2) { @@ -2040,20 +2265,19 @@ void InterpreterMacroAssembler::verify_oop_or_return_address(Register reg, Regis bne(CCR0, test); address fd = CAST_FROM_FN_PTR(address, verify_return_address); - unsigned int nbytes_save = 10*8; // 10 volatile gprs - - save_LR_CR(Rtmp); + const int nbytes_save = 11*8; // volatile gprs except R0 + save_volatile_gprs(R1_SP, -nbytes_save); // except R0 + save_LR_CR(Rtmp); // Save in old frame. push_frame_reg_args(nbytes_save, Rtmp); - save_volatile_gprs(R1_SP, 112); // except R0 load_const_optimized(Rtmp, fd, R0); mr_if_needed(R4_ARG2, reg); mr(R3_ARG1, R19_method); call_c(Rtmp); // call C - restore_volatile_gprs(R1_SP, 112); // except R0 pop_frame(); restore_LR_CR(Rtmp); + restore_volatile_gprs(R1_SP, -nbytes_save); // except R0 b(skip); // Perform a more elaborate out-of-line call. diff --git a/src/cpu/ppc/vm/interp_masm_ppc_64.hpp b/src/cpu/ppc/vm/interp_masm_ppc_64.hpp index 37c743228e9439858c14394ea5da6b7b3ab190e9..555cc5ee9d8c4c8d8daebcd6ed2cb45c4cb9b77a 100644 --- a/src/cpu/ppc/vm/interp_masm_ppc_64.hpp +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.hpp @@ -255,6 +255,12 @@ class InterpreterMacroAssembler: public MacroAssembler { void record_klass_in_profile(Register receiver, Register scratch1, Register scratch2, bool is_virtual_call); void record_klass_in_profile_helper(Register receiver, Register scratch1, Register scratch2, int start_row, Label& done, bool is_virtual_call); + // Argument and return type profiling. + void profile_obj_type(Register obj, Register mdo_addr_base, RegisterOrConstant mdo_addr_offs, Register tmp, Register tmp2); + void profile_arguments_type(Register callee, Register tmp1, Register tmp2, bool is_virtual); + void profile_return_type(Register ret, Register tmp1, Register tmp2); + void profile_parameters_type(Register tmp1, Register tmp2, Register tmp3, Register tmp4); + #endif // !CC_INTERP // Debugging diff --git a/src/cpu/ppc/vm/macroAssembler_ppc.cpp b/src/cpu/ppc/vm/macroAssembler_ppc.cpp index ecb4deb26a8076282241df43b10022b548835463..95a70506cb9fc446579a89a7e6fbe083f5e403ee 100644 --- a/src/cpu/ppc/vm/macroAssembler_ppc.cpp +++ b/src/cpu/ppc/vm/macroAssembler_ppc.cpp @@ -806,6 +806,7 @@ void MacroAssembler::restore_nonvolatile_gprs(Register src, int offset) { // For verify_oops. void MacroAssembler::save_volatile_gprs(Register dst, int offset) { + std(R2, offset, dst); offset += 8; std(R3, offset, dst); offset += 8; std(R4, offset, dst); offset += 8; std(R5, offset, dst); offset += 8; @@ -820,6 +821,7 @@ void MacroAssembler::save_volatile_gprs(Register dst, int offset) { // For verify_oops. void MacroAssembler::restore_volatile_gprs(Register src, int offset) { + ld(R2, offset, src); offset += 8; ld(R3, offset, src); offset += 8; ld(R4, offset, src); offset += 8; ld(R5, offset, src); offset += 8; @@ -1186,6 +1188,16 @@ void MacroAssembler::call_VM(Register oop_result, address entry_point, Register call_VM(oop_result, entry_point, check_exceptions); } +void MacroAssembler::call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg_3, + bool check_exceptions) { + // R3_ARG1 is reserved for the thread + mr_if_needed(R4_ARG2, arg_1); + assert(arg_2 != R4_ARG2, "smashed argument"); + mr_if_needed(R5_ARG3, arg_2); + mr_if_needed(R6_ARG4, arg_3); + call_VM(oop_result, entry_point, check_exceptions); +} + void MacroAssembler::call_VM_leaf(address entry_point) { call_VM_leaf_base(entry_point); } @@ -3058,35 +3070,27 @@ void MacroAssembler::verify_oop(Register oop, const char* msg) { if (!VerifyOops) { return; } - // Will be preserved. - Register tmp = R11; - assert(oop != tmp, "precondition"); - unsigned int nbytes_save = 10*8; // 10 volatile gprs + address/* FunctionDescriptor** */fd = StubRoutines::verify_oop_subroutine_entry_address(); - // save tmp - mr(R0, tmp); - // kill tmp - save_LR_CR(tmp); + const Register tmp = R11; // Will be preserved. + const int nbytes_save = 11*8; // Volatile gprs except R0. + save_volatile_gprs(R1_SP, -nbytes_save); // except R0 + + if (oop == tmp) mr(R4_ARG2, oop); + save_LR_CR(tmp); // save in old frame push_frame_reg_args(nbytes_save, tmp); - // restore tmp - mr(tmp, R0); - save_volatile_gprs(R1_SP, 112); // except R0 // load FunctionDescriptor** / entry_address * - load_const(tmp, fd); + load_const_optimized(tmp, fd, R0); // load FunctionDescriptor* / entry_address ld(tmp, 0, tmp); - mr(R4_ARG2, oop); - load_const(R3_ARG1, (address)msg); - // call destination for its side effect + if (oop != tmp) mr_if_needed(R4_ARG2, oop); + load_const_optimized(R3_ARG1, (address)msg, R0); + // Call destination for its side effect. call_c(tmp); - restore_volatile_gprs(R1_SP, 112); // except R0 + pop_frame(); - // save tmp - mr(R0, tmp); - // kill tmp restore_LR_CR(tmp); - // restore tmp - mr(tmp, R0); + restore_volatile_gprs(R1_SP, -nbytes_save); // except R0 } const char* stop_types[] = { diff --git a/src/cpu/ppc/vm/macroAssembler_ppc.hpp b/src/cpu/ppc/vm/macroAssembler_ppc.hpp index 5bbea5ada2abb5b59733d579c97e5c77c98fbd2e..c7e8de457a815b78f9fea549018d274768632306 100644 --- a/src/cpu/ppc/vm/macroAssembler_ppc.hpp +++ b/src/cpu/ppc/vm/macroAssembler_ppc.hpp @@ -368,6 +368,7 @@ class MacroAssembler: public Assembler { void call_VM(Register oop_result, address entry_point, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, bool check_exceptions = true); void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, bool check_exceptions = true); + void call_VM(Register oop_result, address entry_point, Register arg_1, Register arg_2, Register arg3, bool check_exceptions = true); void call_VM_leaf(address entry_point); void call_VM_leaf(address entry_point, Register arg_1); void call_VM_leaf(address entry_point, Register arg_1, Register arg_2); diff --git a/src/cpu/ppc/vm/nativeInst_ppc.cpp b/src/cpu/ppc/vm/nativeInst_ppc.cpp index 6f40af06f400d37318c9b60ea133f7d6a6ab1464..6a8b9a479af0c845a9a0dcceaf1d16a887454008 100644 --- a/src/cpu/ppc/vm/nativeInst_ppc.cpp +++ b/src/cpu/ppc/vm/nativeInst_ppc.cpp @@ -100,10 +100,7 @@ void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { MacroAssembler* a = new MacroAssembler(&cb); // Patch the call. - if (ReoptimizeCallSequences && - a->is_within_range_of_b(dest, addr_call)) { - a->bl(dest); - } else { + if (!ReoptimizeCallSequences || !a->is_within_range_of_b(dest, addr_call)) { address trampoline_stub_addr = get_trampoline(); // We did not find a trampoline stub because the current codeblob @@ -115,9 +112,12 @@ void NativeCall::set_destination_mt_safe(address dest, bool assert_lock) { // Patch the constant in the call's trampoline stub. NativeCallTrampolineStub_at(trampoline_stub_addr)->set_destination(dest); - - a->bl(trampoline_stub_addr); + dest = trampoline_stub_addr; } + + OrderAccess::release(); + a->bl(dest); + ICache::ppc64_flush_icache_bytes(addr_call, code_size); } diff --git a/src/cpu/ppc/vm/ppc.ad b/src/cpu/ppc/vm/ppc.ad index 40bb013a87a5352faa92d9309841666546a87eb5..ce0406de1adff4ddfe58fb73061cd9521d5bc6df 100644 --- a/src/cpu/ppc/vm/ppc.ad +++ b/src/cpu/ppc/vm/ppc.ad @@ -1938,8 +1938,9 @@ ArchOpcode MachSpillCopyNode_archOpcode(MachSpillCopyNode *n, PhaseRegAlloc *ra_ // -------------------------------------------------------------------- // Check for hi bits still needing moving. Only happens for misaligned // arguments to native calls. - if (src_hi == dst_hi) + if (src_hi == dst_hi) { return ppc64Opcode_none; // Self copy; no move. + } ShouldNotReachHere(); return ppc64Opcode_undefined; @@ -1961,14 +1962,15 @@ void MachNopNode::emit(CodeBuffer &cbuf, PhaseRegAlloc *) const { } uint MachNopNode::size(PhaseRegAlloc *ra_) const { - return _count * 4; + return _count * 4; } #ifndef PRODUCT void BoxLockNode::format(PhaseRegAlloc *ra_, outputStream *st) const { int offset = ra_->reg2offset(in_RegMask(0).find_first_elem()); - int reg = ra_->get_reg_first(this); - st->print("ADDI %s, SP, %d \t// box node", Matcher::regName[reg], offset); + char reg_str[128]; + ra_->dump_register(this, reg_str); + st->print("ADDI %s, SP, %d \t// box node", reg_str, offset); } #endif diff --git a/src/cpu/ppc/vm/templateInterpreter_ppc.cpp b/src/cpu/ppc/vm/templateInterpreter_ppc.cpp index 22bbbf58b469a81cf5bd22dcfed2c8f691fa7036..a8d19d286272789c05497a7edcee7510b1926001 100644 --- a/src/cpu/ppc/vm/templateInterpreter_ppc.cpp +++ b/src/cpu/ppc/vm/templateInterpreter_ppc.cpp @@ -90,7 +90,7 @@ address TemplateInterpreterGenerator::generate_ClassCastException_verbose_handle // Thread will be loaded to R3_ARG1. // Target class oop is in register R5_ARG3 by convention! - __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException_verbose, R17_tos, R5_ARG3)); + __ call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::throw_ClassCastException_verbose), R17_tos, R5_ARG3); // Above call must not return here since exception pending. DEBUG_ONLY(__ should_not_reach_here();) return entry; @@ -171,6 +171,10 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, // Compiled code destroys templateTableBase, reload. __ load_const_optimized(R25_templateTableBase, (address)Interpreter::dispatch_table((TosState)0), R12_scratch2); + if (state == atos) { + __ profile_return_type(R3_RET, R11_scratch1, R12_scratch2); + } + const Register cache = R11_scratch1; const Register size = R12_scratch2; __ get_cache_and_index_at_bcp(cache, 1, index_size); @@ -1230,6 +1234,10 @@ address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) { __ li(R0, 1); __ stb(R0, in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()), R16_thread); } + + // Argument and return type profiling. + __ profile_parameters_type(R3_ARG1, R4_ARG2, R5_ARG3, R6_ARG4); + // Increment invocation counter and check for overflow. if (inc_counter) { generate_counter_incr(&invocation_counter_overflow, &profile_method, &profile_method_continue); @@ -1549,6 +1557,8 @@ void TemplateInterpreterGenerator::generate_throw_exception() { __ resize_frame_absolute(R12_scratch2, R11_scratch1, R0); if (ProfileInterpreter) { __ set_method_data_pointer_for_bcp(); + __ ld(R11_scratch1, 0, R1_SP); + __ std(R28_mdx, _ijava_state_neg(mdx), R11_scratch1); } #if INCLUDE_JVMTI Label L_done; @@ -1560,13 +1570,11 @@ void TemplateInterpreterGenerator::generate_throw_exception() { // The member name argument must be restored if _invokestatic is re-executed after a PopFrame call. // Detect such a case in the InterpreterRuntime function and return the member name argument, or NULL. __ ld(R4_ARG2, 0, R18_locals); - __ call_VM(R11_scratch1, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), - R4_ARG2, R19_method, R14_bcp); - - __ cmpdi(CCR0, R11_scratch1, 0); + __ MacroAssembler::call_VM(R4_ARG2, CAST_FROM_FN_PTR(address, InterpreterRuntime::member_name_arg_or_null), R4_ARG2, R19_method, R14_bcp, false); + __ restore_interpreter_state(R11_scratch1, /*bcp_and_mdx_only*/ true); + __ cmpdi(CCR0, R4_ARG2, 0); __ beq(CCR0, L_done); - - __ std(R11_scratch1, wordSize, R15_esp); + __ std(R4_ARG2, wordSize, R15_esp); __ bind(L_done); #endif // INCLUDE_JVMTI __ dispatch_next(vtos); diff --git a/src/cpu/ppc/vm/templateTable_ppc_64.cpp b/src/cpu/ppc/vm/templateTable_ppc_64.cpp index 95a60ec15f48d298b8d05eb2b91cfc1a3a3f8130..c4cc257f2316420b2bb990883ec69d2bd2e70457 100644 --- a/src/cpu/ppc/vm/templateTable_ppc_64.cpp +++ b/src/cpu/ppc/vm/templateTable_ppc_64.cpp @@ -3234,6 +3234,8 @@ void TemplateTable::generate_vtable_call(Register Rrecv_klass, Register Rindex, // Load target. __ addi(Rrecv_klass, Rrecv_klass, base + vtableEntry::method_offset_in_bytes()); __ ldx(Rtarget_method, Rindex, Rrecv_klass); + // Argument and return type profiling. + __ profile_arguments_type(Rtarget_method, Rrecv_klass /* scratch1 */, Rtemp /* scratch2 */, true); __ call_from_interpreter(Rtarget_method, Rret, Rrecv_klass /* scratch1 */, Rtemp /* scratch2 */); } @@ -3317,6 +3319,8 @@ void TemplateTable::invokevfinal_helper(Register Rmethod, Register Rflags, Regis __ null_check_throw(Rrecv, -1, Rscratch1); __ profile_final_call(Rrecv, Rscratch1); + // Argument and return type profiling. + __ profile_arguments_type(Rmethod, Rscratch1, Rscratch2, true); // Do the call. __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1, Rscratch2); @@ -3338,6 +3342,8 @@ void TemplateTable::invokespecial(int byte_no) { __ null_check_throw(Rreceiver, -1, R11_scratch1); __ profile_call(R11_scratch1, R12_scratch2); + // Argument and return type profiling. + __ profile_arguments_type(Rmethod, R11_scratch1, R12_scratch2, false); __ call_from_interpreter(Rmethod, Rret_addr, R11_scratch1, R12_scratch2); } @@ -3352,6 +3358,8 @@ void TemplateTable::invokestatic(int byte_no) { prepare_invoke(byte_no, R19_method, Rret_addr, noreg, noreg, Rflags, R11_scratch1); __ profile_call(R11_scratch1, R12_scratch2); + // Argument and return type profiling. + __ profile_arguments_type(R19_method, R11_scratch1, R12_scratch2, false); __ call_from_interpreter(R19_method, Rret_addr, R11_scratch1, R12_scratch2); } @@ -3373,6 +3381,8 @@ void TemplateTable::invokeinterface_object_method(Register Rrecv_klass, // Final call case. __ profile_final_call(Rtemp1, Rscratch); + // Argument and return type profiling. + __ profile_arguments_type(Rindex, Rscratch, Rrecv_klass /* scratch */, true); // Do the final call - the index (f2) contains the method. __ call_from_interpreter(Rindex, Rret, Rscratch, Rrecv_klass /* scratch */); @@ -3424,6 +3434,8 @@ void TemplateTable::invokeinterface(int byte_no) { __ cmpdi(CCR0, Rindex, 0); __ beq(CCR0, Lthrow_ame); // Found entry. Jump off! + // Argument and return type profiling. + __ profile_arguments_type(Rindex, Rscratch1, Rscratch2, true); __ call_from_interpreter(Rindex, Rret_addr, Rscratch1, Rscratch2); // Vtable entry was NULL => Throw abstract method error. @@ -3477,6 +3489,8 @@ void TemplateTable::invokedynamic(int byte_no) { // to be the callsite object the bootstrap method returned. This is passed to a // "link" method which does the dispatch (Most likely just grabs the MH stored // inside the callsite and does an invokehandle). + // Argument and return type profiling. + __ profile_arguments_type(Rmethod, Rscratch1, Rscratch2, false); __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */); } @@ -3503,6 +3517,8 @@ void TemplateTable::invokehandle(int byte_no) { __ profile_final_call(Rrecv, Rscratch1); // Still no call from handle => We call the method handle interpreter here. + // Argument and return type profiling. + __ profile_arguments_type(Rmethod, Rscratch1, Rscratch2, true); __ call_from_interpreter(Rmethod, Rret_addr, Rscratch1 /* scratch1 */, Rscratch2 /* scratch2 */); } diff --git a/src/cpu/ppc/vm/vm_version_ppc.cpp b/src/cpu/ppc/vm/vm_version_ppc.cpp index 28f992f41b83685807995ff9a3f2f6725df69a82..1f8375fa6b6ebd313cd5119bcd319608a30dcffb 100644 --- a/src/cpu/ppc/vm/vm_version_ppc.cpp +++ b/src/cpu/ppc/vm/vm_version_ppc.cpp @@ -139,13 +139,44 @@ void VM_Version::initialize() { } assert(AllocatePrefetchLines > 0, "invalid value"); - if (AllocatePrefetchLines < 1) // Set valid value in product VM. + if (AllocatePrefetchLines < 1) { // Set valid value in product VM. AllocatePrefetchLines = 1; // Conservative value. + } - if (AllocatePrefetchStyle == 3 && AllocatePrefetchDistance < cache_line_size) + if (AllocatePrefetchStyle == 3 && AllocatePrefetchDistance < cache_line_size) { AllocatePrefetchStyle = 1; // Fall back if inappropriate. + } assert(AllocatePrefetchStyle >= 0, "AllocatePrefetchStyle should be positive"); + + if (UseCRC32Intrinsics) { + if (!FLAG_IS_DEFAULT(UseCRC32Intrinsics)) + warning("CRC32 intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseCRC32Intrinsics, false); + } + + // The AES intrinsic stubs require AES instruction support. + if (UseAES) { + warning("AES instructions are not available on this CPU"); + FLAG_SET_DEFAULT(UseAES, false); + } + if (UseAESIntrinsics) { + if (!FLAG_IS_DEFAULT(UseAESIntrinsics)) + warning("AES intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseAESIntrinsics, false); + } + + if (UseSHA) { + warning("SHA instructions are not available on this CPU"); + FLAG_SET_DEFAULT(UseSHA, false); + } + if (UseSHA1Intrinsics || UseSHA256Intrinsics || UseSHA512Intrinsics) { + warning("SHA intrinsics are not available on this CPU"); + FLAG_SET_DEFAULT(UseSHA1Intrinsics, false); + FLAG_SET_DEFAULT(UseSHA256Intrinsics, false); + FLAG_SET_DEFAULT(UseSHA512Intrinsics, false); + } + } void VM_Version::print_features() { diff --git a/src/cpu/x86/vm/c1_Runtime1_x86.cpp b/src/cpu/x86/vm/c1_Runtime1_x86.cpp index fd6302d21be2a27c08ac605b8cd9826679780311..76303c114ce6179ac2a8383a3b3b463df4ee297b 100644 --- a/src/cpu/x86/vm/c1_Runtime1_x86.cpp +++ b/src/cpu/x86/vm/c1_Runtime1_x86.cpp @@ -675,7 +675,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) { case handle_exception_nofpu_id: case handle_exception_id: // At this point all registers MAY be live. - oop_map = save_live_registers(sasm, 1 /*thread*/, id == handle_exception_nofpu_id); + oop_map = save_live_registers(sasm, 1 /*thread*/, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: { // At this point all registers except exception oop (RAX) and @@ -748,7 +748,7 @@ OopMapSet* Runtime1::generate_handle_exception(StubID id, StubAssembler *sasm) { case handle_exception_nofpu_id: case handle_exception_id: // Restore the registers that were saved at the beginning. - restore_live_registers(sasm, id == handle_exception_nofpu_id); + restore_live_registers(sasm, id != handle_exception_nofpu_id); break; case handle_exception_from_callee_id: // WIN64_ONLY: No need to add frame::arg_reg_save_area_bytes to SP diff --git a/src/os/aix/vm/os_aix.cpp b/src/os/aix/vm/os_aix.cpp index 314b80623dd1b21081f91d22c6e6f3dbc91a05d0..9a65f741ac0a7adb9d8fd7f4e29f593f9744834f 100644 --- a/src/os/aix/vm/os_aix.cpp +++ b/src/os/aix/vm/os_aix.cpp @@ -114,12 +114,6 @@ extern "C" { } #endif -// Excerpts from systemcfg.h definitions newer than AIX 5.3 -#ifndef PV_7 -# define PV_7 0x200000 // Power PC 7 -# define PV_7_Compat 0x208000 // Power PC 7 -#endif - #define MAX_PATH (2 * K) // for timer info max values which include all bits @@ -130,17 +124,40 @@ extern "C" { #define ERROR_MP_VMGETINFO_FAILED 102 #define ERROR_MP_VMGETINFO_CLAIMS_NO_SUPPORT_FOR_64K 103 -// the semantics in this file are thus that codeptr_t is a *real code ptr* +// The semantics in this file are thus that codeptr_t is a *real code ptr*. // This means that any function taking codeptr_t as arguments will assume // a real codeptr and won't handle function descriptors (eg getFuncName), // whereas functions taking address as args will deal with function -// descriptors (eg os::dll_address_to_library_name) +// descriptors (eg os::dll_address_to_library_name). typedef unsigned int* codeptr_t; -// typedefs for stackslots, stack pointers, pointers to op codes +// Typedefs for stackslots, stack pointers, pointers to op codes. typedef unsigned long stackslot_t; typedef stackslot_t* stackptr_t; +// Excerpts from systemcfg.h definitions newer than AIX 5.3. +#ifndef PV_7 +#define PV_7 0x200000 /* Power PC 7 */ +#define PV_7_Compat 0x208000 /* Power PC 7 */ +#endif +#ifndef PV_8 +#define PV_8 0x300000 /* Power PC 8 */ +#define PV_8_Compat 0x308000 /* Power PC 8 */ +#endif + +#define trcVerbose(fmt, ...) { /* PPC port */ \ + if (Verbose) { \ + fprintf(stderr, fmt, ##__VA_ARGS__); \ + fputc('\n', stderr); fflush(stderr); \ + } \ +} +#define trc(fmt, ...) /* PPC port */ + +#define ERRBYE(s) { \ + trcVerbose(s); \ + return -1; \ +} + // query dimensions of the stack of the calling thread static void query_stack_dimensions(address* p_stack_base, size_t* p_stack_size); @@ -172,12 +189,12 @@ inline bool is_valid_codepointer(codeptr_t p) { return true; } -// macro to check a given stack pointer against given stack limits and to die if test fails +// Macro to check a given stack pointer against given stack limits and to die if test fails. #define CHECK_STACK_PTR(sp, stack_base, stack_size) { \ guarantee(is_valid_stackpointer((stackptr_t)(sp), (stackptr_t)(stack_base), stack_size), "Stack Pointer Invalid"); \ } -// macro to check the current stack pointer against given stacklimits +// Macro to check the current stack pointer against given stacklimits. #define CHECK_CURRENT_STACK_PTR(stack_base, stack_size) { \ address sp; \ sp = os::current_stack_pointer(); \ @@ -211,7 +228,7 @@ static bool check_signals = true; static pid_t _initial_pid = 0; static int SR_signum = SIGUSR2; // Signal used to suspend/resume a thread (must be > SIGSEGV, see 4355769) static sigset_t SR_sigset; -static pthread_mutex_t dl_mutex; // Used to protect dlsym() calls */ +static pthread_mutex_t dl_mutex; // Used to protect dlsym() calls. julong os::available_memory() { return Aix::available_memory(); @@ -243,7 +260,6 @@ bool os::getenv(const char* name, char* buf, int len) { return false; } - // Return true if user is running as root. bool os::have_special_privileges() { @@ -274,8 +290,7 @@ static bool my_disclaim64(char* addr, size_t size) { for (int i = 0; i < numFullDisclaimsNeeded; i ++) { if (::disclaim(p, maxDisclaimSize, DISCLAIM_ZEROMEM) != 0) { - //if (Verbose) - fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + maxDisclaimSize, errno); + trc("Cannot disclaim %p - %p (errno %d)\n", p, p + maxDisclaimSize, errno); return false; } p += maxDisclaimSize; @@ -283,8 +298,7 @@ static bool my_disclaim64(char* addr, size_t size) { if (lastDisclaimSize > 0) { if (::disclaim(p, lastDisclaimSize, DISCLAIM_ZEROMEM) != 0) { - //if (Verbose) - fprintf(stderr, "Cannot disclaim %p - %p (errno %d)\n", p, p + lastDisclaimSize, errno); + trc("Cannot disclaim %p - %p (errno %d)\n", p, p + lastDisclaimSize, errno); return false; } } @@ -324,11 +338,11 @@ pid_t os::Aix::gettid() { void os::Aix::initialize_system_info() { - // get the number of online(logical) cpus instead of configured + // Get the number of online(logical) cpus instead of configured. os::_processor_count = sysconf(_SC_NPROCESSORS_ONLN); assert(_processor_count > 0, "_processor_count must be > 0"); - // retrieve total physical storage + // Retrieve total physical storage. os::Aix::meminfo_t mi; if (!os::Aix::get_meminfo(&mi)) { fprintf(stderr, "os::Aix::get_meminfo failed.\n"); fflush(stderr); @@ -503,7 +517,6 @@ query_multipage_support_end: } // end os::Aix::query_multipage_support() -// The code for this method was initially derived from the version in os_linux.cpp. void os::init_system_properties_values() { #define DEFAULT_LIBPATH "/usr/lib:/lib" @@ -600,10 +613,11 @@ bool os::Aix::is_sig_ignored(int sig) { sigaction(sig, (struct sigaction*)NULL, &oact); void* ohlr = oact.sa_sigaction ? CAST_FROM_FN_PTR(void*, oact.sa_sigaction) : CAST_FROM_FN_PTR(void*, oact.sa_handler); - if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) + if (ohlr == CAST_FROM_FN_PTR(void*, SIG_IGN)) { return true; - else + } else { return false; + } } void os::Aix::signal_sets_init() { @@ -777,6 +791,9 @@ bool os::Aix::get_cpuinfo(cpuinfo_t* pci) { // get the processor version from _system_configuration switch (_system_configuration.version) { + case PV_8: + strcpy(pci->version, "Power PC 8"); + break; case PV_7: strcpy(pci->version, "Power PC 7"); break; @@ -804,6 +821,9 @@ bool os::Aix::get_cpuinfo(cpuinfo_t* pci) { case PV_7_Compat: strcpy(pci->version, "PV_7_Compat"); break; + case PV_8_Compat: + strcpy(pci->version, "PV_8_Compat"); + break; default: strcpy(pci->version, "unknown"); } @@ -939,7 +959,9 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { pthread_attr_destroy(&attr); - if (ret != 0) { + if (ret == 0) { + // PPC port traceOsMisc(("Created New Thread : pthread-id %u", tid)); + } else { if (PrintMiscellaneous && (Verbose || WizardMode)) { perror("pthread_create()"); } @@ -1096,8 +1118,7 @@ jlong os::javaTimeNanos() { if (os::Aix::on_pase()) { Unimplemented(); return 0; - } - else { + } else { // On AIX use the precision of processors real time clock // or time base registers. timebasestruct_t time; @@ -1150,7 +1171,6 @@ bool os::getTimesSecs(double* process_real_time, } } - char * os::local_time_string(char *buf, size_t buflen) { struct tm t; time_t long_time; @@ -1188,7 +1208,6 @@ void os::shutdown() { if (abort_hook != NULL) { abort_hook(); } - } // Note: os::abort() might be called very early during initialization, or @@ -1220,8 +1239,7 @@ void os::die() { // from src/solaris/hpi/src/system_md.c size_t os::lasterror(char *buf, size_t len) { - - if (errno == 0) return 0; + if (errno == 0) return 0; const char *s = ::strerror(errno); size_t n = ::strlen(s); @@ -1234,6 +1252,7 @@ size_t os::lasterror(char *buf, size_t len) { } intx os::current_thread_id() { return (intx)pthread_self(); } + int os::current_process_id() { // This implementation returns a unique pid, the pid of the @@ -1370,9 +1389,9 @@ bool os::dll_address_to_function_name(address addr, char *buf, if (offset) { *offset = -1; } - if (buf) { - buf[0] = '\0'; - } + // Buf is not optional, but offset is optional. + assert(buf != NULL, "sanity check"); + buf[0] = '\0'; // Resolve function ptr literals first. addr = resolve_function_descriptor_to_code_pointer(addr); @@ -1405,12 +1424,9 @@ static int getModuleName(codeptr_t pc, // [in] program counte return 0; } - if (Verbose) { - fprintf(stderr, "pc outside any module"); - } + trcVerbose("pc outside any module"); return -1; - } bool os::dll_address_to_library_name(address addr, char* buf, @@ -1418,9 +1434,9 @@ bool os::dll_address_to_library_name(address addr, char* buf, if (offset) { *offset = -1; } - if (buf) { - buf[0] = '\0'; - } + // Buf is not optional, but offset is optional. + assert(buf != NULL, "sanity check"); + buf[0] = '\0'; // Resolve function ptr literals first. addr = resolve_function_descriptor_to_code_pointer(addr); @@ -1435,7 +1451,7 @@ bool os::dll_address_to_library_name(address addr, char* buf, } // Loads .dll/.so and in case of error it checks if .dll/.so was built -// for the same architecture as Hotspot is running on +// for the same architecture as Hotspot is running on. void *os::dll_load(const char *filename, char *ebuf, int ebuflen) { if (ebuf && ebuflen > 0) { @@ -1598,7 +1614,6 @@ void os::print_siginfo(outputStream* st, void* siginfo) { st->cr(); } - static void print_signal_handler(outputStream* st, int sig, char* buf, size_t buflen); @@ -1622,7 +1637,7 @@ void os::print_signal_handlers(outputStream* st, char* buf, size_t buflen) { static char saved_jvm_path[MAXPATHLEN] = {0}; -// Find the full path to the current module, libjvm.so or libjvm_g.so +// Find the full path to the current module, libjvm.so. void os::jvm_path(char *buf, jint buflen) { // Error checking. if (buflen < MAXPATHLEN) { @@ -1692,7 +1707,7 @@ void* os::signal(int signal_number, void* handler) { // Do not block out synchronous signals in the signal handler. // Blocking synchronous signals only makes sense if you can really // be sure that those signals won't happen during signal handling, - // when the blocking applies. Normal signal handlers are lean and + // when the blocking applies. Normal signal handlers are lean and // do not cause signals. But our signal handlers tend to be "risky" // - secondary SIGSEGV, SIGILL, SIGBUS' may and do happen. // On AIX, PASE there was a case where a SIGSEGV happened, followed @@ -2967,13 +2982,9 @@ OSReturn os::set_native_priority(Thread* thread, int newpri) { param.sched_priority = newpri; int ret = pthread_setschedparam(thr, policy, ¶m); - if (Verbose) { - if (ret == 0) { - fprintf(stderr, "changed priority of thread %d to %d\n", (int)thr, newpri); - } else { - fprintf(stderr, "Could not changed priority for thread %d to %d (error %d, %s)\n", - (int)thr, newpri, ret, strerror(ret)); - } + if (ret != 0) { + trcVerbose("Could not change priority for thread %d to %d (error %d, %s)", + (int)thr, newpri, ret, strerror(ret)); } return (ret == 0) ? OS_OK : OS_ERR; } @@ -3094,7 +3105,6 @@ static void SR_handler(int sig, siginfo_t* siginfo, ucontext_t* context) { errno = old_errno; } - static int SR_initialize() { struct sigaction act; char *s; @@ -3337,7 +3347,6 @@ void javaSignalHandler(int sig, siginfo_t* info, void* uc) { JVM_handle_aix_signal(sig, info, uc, true); } - // This boolean allows users to forward their own non-matching signals // to JVM_handle_aix_signal, harmlessly. bool os::Aix::signal_handlers_are_installed = false; @@ -3531,7 +3540,7 @@ void os::Aix::install_signal_handlers() { set_signal_handler(SIGDANGER, true); if (libjsig_is_loaded) { - // Tell libjsig jvm finishes setting signal handlers + // Tell libjsig jvm finishes setting signal handlers. (*end_signal_setting)(); } @@ -3547,7 +3556,7 @@ void os::Aix::install_signal_handlers() { tty->print_cr("Info: AllowUserSignalHandlers is activated, all active signal checking is disabled"); check_signals = false; } - // need to initialize check_signal_done + // Need to initialize check_signal_done. ::sigemptyset(&check_signal_done); } } @@ -3621,7 +3630,6 @@ static void print_signal_handler(outputStream* st, int sig, st->cr(); } - #define DO_SIGNAL_CHECK(sig) \ if (!sigismember(&check_signal_done, sig)) \ os::Aix::check_signal_handler(sig) @@ -3682,7 +3690,6 @@ void os::Aix::check_signal_handler(int sig) { ? CAST_FROM_FN_PTR(address, act.sa_sigaction) : CAST_FROM_FN_PTR(address, act.sa_handler); - switch(sig) { case SIGSEGV: case SIGBUS: @@ -3830,15 +3837,13 @@ void os::init(void) { pthread_mutex_init(&dl_mutex, NULL); } -// this is called _after_ the global arguments have been parsed +// This is called _after_ the global arguments have been parsed. jint os::init_2(void) { - if (Verbose) { - fprintf(stderr, "processor count: %d\n", os::_processor_count); - fprintf(stderr, "physical memory: %lu\n", Aix::_physical_memory); - } + trcVerbose("processor count: %d", os::_processor_count); + trcVerbose("physical memory: %lu", Aix::_physical_memory); - // initially build up the loaded dll map + // Initially build up the loaded dll map. LoadedLibraries::reload(); const int page_size = Aix::page_size(); @@ -3888,7 +3893,7 @@ jint os::init_2(void) { } if (map_address != (address) MAP_FAILED) { - // map succeeded, but polling_page is not at wished address, unmap and continue. + // Map succeeded, but polling_page is not at wished address, unmap and continue. ::munmap(map_address, map_size); map_address = (address) MAP_FAILED; } @@ -3942,7 +3947,7 @@ jint os::init_2(void) { // Make the stack size a multiple of the page size so that // the yellow/red zones can be guarded. - // note that this can be 0, if no default stacksize was set + // Note that this can be 0, if no default stacksize was set. JavaThread::set_stack_size_at_create(round_to(threadStackSizeInBytes, vm_page_size())); Aix::libpthread_init(); @@ -4255,7 +4260,6 @@ int os::open(const char *path, int oflag, int mode) { return fd; } - // create binary file, rewriting existing file if required int os::create_binary_file(const char* path, bool rewrite_existing) { int oflags = O_WRONLY | O_CREAT; @@ -4324,7 +4328,6 @@ char* os::pd_map_memory(int fd, const char* file_name, size_t file_offset, return NULL; } - // Remap a block of memory. char* os::pd_remap_memory(int fd, const char* file_name, size_t file_offset, char *addr, size_t bytes, bool read_only, @@ -4372,14 +4375,14 @@ static bool thread_cpu_time_unchecked(Thread* thread, jlong* p_sys_time, jlong* jlong sys_time = 0; jlong user_time = 0; - // reimplemented using getthrds64(). + // Reimplemented using getthrds64(). // - // goes like this: + // Works like this: // For the thread in question, get the kernel thread id. Then get the // kernel thread statistics using that id. // // This only works of course when no pthread scheduling is used, - // ie there is a 1:1 relationship to kernel threads. + // i.e. there is a 1:1 relationship to kernel threads. // On AIX, see AIXTHREAD_SCOPE variable. pthread_t pthtid = thread->osthread()->pthread_id(); @@ -4526,14 +4529,12 @@ void os::Aix::initialize_os_info() { memset(&uts, 0, sizeof(uts)); strcpy(uts.sysname, "?"); if (::uname(&uts) == -1) { - fprintf(stderr, "uname failed (%d)\n", errno); + trc("uname failed (%d)", errno); guarantee(0, "Could not determine whether we run on AIX or PASE"); } else { - if (Verbose) { - fprintf(stderr,"uname says: sysname \"%s\" version \"%s\" release \"%s\" " - "node \"%s\" machine \"%s\"\n", - uts.sysname, uts.version, uts.release, uts.nodename, uts.machine); - } + trcVerbose("uname says: sysname \"%s\" version \"%s\" release \"%s\" " + "node \"%s\" machine \"%s\"\n", + uts.sysname, uts.version, uts.release, uts.nodename, uts.machine); const int major = atoi(uts.version); assert(major > 0, "invalid OS version"); const int minor = atoi(uts.release); @@ -4545,12 +4546,10 @@ void os::Aix::initialize_os_info() { // We run on AIX. We do not support versions older than AIX 5.3. _on_pase = 0; if (_os_version < 0x0503) { - fprintf(stderr, "AIX release older than AIX 5.3 not supported.\n"); + trc("AIX release older than AIX 5.3 not supported."); assert(false, "AIX release too old."); } else { - if (Verbose) { - fprintf(stderr, "We run on AIX %d.%d\n", major, minor); - } + trcVerbose("We run on AIX %d.%d\n", major, minor); } } else { assert(false, "unknown OS"); @@ -4558,7 +4557,6 @@ void os::Aix::initialize_os_info() { } guarantee(_on_pase != -1 && _os_version, "Could not determine AIX/OS400 release"); - } // end: os::Aix::initialize_os_info() // Scan environment for important settings which might effect the VM. @@ -4596,12 +4594,10 @@ void os::Aix::scan_environment() { // Note: Setting XPG_SUS_ENV in the process is too late. Must be set earlier (before // exec() ? before loading the libjvm ? ....) p = ::getenv("XPG_SUS_ENV"); - if (Verbose) { - fprintf(stderr, "XPG_SUS_ENV=%s.\n", p ? p : ""); - } + trcVerbose("XPG_SUS_ENV=%s.", p ? p : ""); if (p && strcmp(p, "ON") == 0) { _xpg_sus_mode = 1; - fprintf(stderr, "Unsupported setting: XPG_SUS_ENV=ON\n"); + trc("Unsupported setting: XPG_SUS_ENV=ON"); // This is not supported. Worst of all, it changes behaviour of mmap MAP_FIXED to // clobber address ranges. If we ever want to support that, we have to do some // testing first. @@ -4613,10 +4609,7 @@ void os::Aix::scan_environment() { // Switch off AIX internal (pthread) guard pages. This has // immediate effect for any pthread_create calls which follow. p = ::getenv("AIXTHREAD_GUARDPAGES"); - if (Verbose) { - fprintf(stderr, "AIXTHREAD_GUARDPAGES=%s.\n", p ? p : ""); - fprintf(stderr, "setting AIXTHREAD_GUARDPAGES=0.\n"); - } + trcVerbose("AIXTHREAD_GUARDPAGES=%s.", p ? p : ""); rc = ::putenv("AIXTHREAD_GUARDPAGES=0"); guarantee(rc == 0, ""); @@ -4634,7 +4627,7 @@ void os::Aix::initialize_libperfstat() { assert(os::Aix::on_aix(), "AIX only"); if (!libperfstat::init()) { - fprintf(stderr, "libperfstat initialization failed.\n"); + trc("libperfstat initialization failed."); assert(false, "libperfstat initialization failed"); } else { if (Verbose) { @@ -4806,7 +4799,6 @@ static struct timespec* compute_abstime(timespec* abstime, jlong millis) { return abstime; } - // Test-and-clear _Event, always leaves _Event set to 0, returns immediately. // Conceptually TryPark() should be equivalent to park(0). @@ -4889,7 +4881,7 @@ int os::PlatformEvent::park(jlong millis) { while (_Event < 0) { status = pthread_cond_timedwait(_cond, _mutex, &abst); assert_status(status == 0 || status == ETIMEDOUT, - status, "cond_timedwait"); + status, "cond_timedwait"); if (!FilterSpuriousWakeups) break; // previous semantics if (status == ETIMEDOUT) break; // We consume and ignore EINTR and spurious wakeups. @@ -5023,9 +5015,9 @@ void Parker::park(bool isAbsolute, jlong time) { // Optional fast-path check: // Return immediately if a permit is available. if (_counter > 0) { - _counter = 0; - OrderAccess::fence(); - return; + _counter = 0; + OrderAccess::fence(); + return; } Thread* thread = Thread::current(); @@ -5047,7 +5039,6 @@ void Parker::park(bool isAbsolute, jlong time) { unpackTime(&absTime, isAbsolute, time); } - // Enter safepoint region // Beware of deadlocks such as 6317397. // The per-thread Parker:: mutex is a classic leaf-lock. @@ -5135,7 +5126,6 @@ void Parker::unpark() { } } - extern char** environ; // Run the specified command in a separate process. Return its exit value, @@ -5154,44 +5144,43 @@ int os::fork_and_exec(char* cmd) { } else if (pid == 0) { // child process - // try to be consistent with system(), which uses "/usr/bin/sh" on AIX + // Try to be consistent with system(), which uses "/usr/bin/sh" on AIX. execve("/usr/bin/sh", argv, environ); // execve failed _exit(-1); - } else { + } else { // copied from J2SE ..._waitForProcessExit() in UNIXProcess_md.c; we don't // care about the actual exit code, for now. int status; - // Wait for the child process to exit. This returns immediately if + // Wait for the child process to exit. This returns immediately if // the child has already exited. */ while (waitpid(pid, &status, 0) < 0) { - switch (errno) { + switch (errno) { case ECHILD: return 0; case EINTR: break; default: return -1; - } + } } if (WIFEXITED(status)) { - // The child exited normally; get its exit code. - return WEXITSTATUS(status); + // The child exited normally; get its exit code. + return WEXITSTATUS(status); } else if (WIFSIGNALED(status)) { - // The child exited because of a signal - // The best value to return is 0x80 + signal number, - // because that is what all Unix shells do, and because - // it allows callers to distinguish between process exit and - // process death by signal. - return 0x80 + WTERMSIG(status); + // The child exited because of a signal. + // The best value to return is 0x80 + signal number, + // because that is what all Unix shells do, and because + // it allows callers to distinguish between process exit and + // process death by signal. + return 0x80 + WTERMSIG(status); } else { - // Unknown exit code; pass it through - return status; + // Unknown exit code; pass it through. + return status; } } - // Remove warning. return -1; } @@ -5206,7 +5195,7 @@ bool os::is_headless_jre() { struct stat statbuf; char buf[MAXPATHLEN]; char libmawtpath[MAXPATHLEN]; - const char *xawtstr = "/xawt/libmawt.so"; + const char *xawtstr = "/xawt/libmawt.so"; const char *new_xawtstr = "/libawt_xawt.so"; char *p; diff --git a/src/os/aix/vm/os_aix.hpp b/src/os/aix/vm/os_aix.hpp index a7d2717c8e0c95aa6847822a291a69b109b3d2ff..01831fb7cbc0cf4bd74e707167669d8f088653fd 100644 --- a/src/os/aix/vm/os_aix.hpp +++ b/src/os/aix/vm/os_aix.hpp @@ -209,7 +209,7 @@ class Aix { return _can_use_16M_pages == 1 ? true : false; } - static address ucontext_get_pc(ucontext_t* uc); + static address ucontext_get_pc(const ucontext_t* uc); static intptr_t* ucontext_get_sp(ucontext_t* uc); static intptr_t* ucontext_get_fp(ucontext_t* uc); // Set PC into context. Needed for continuation after signal. diff --git a/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp b/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp index 8573cf33e7342896797549aa8b16ebefe9c29c02..3dfb72d1e3d3e4940dbb2b56ae4a89154531020d 100644 --- a/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp +++ b/src/os_cpu/aix_ppc/vm/os_aix_ppc.cpp @@ -91,8 +91,9 @@ void os::initialize_thread(Thread *thread) { } // Frame information (pc, sp, fp) retrieved via ucontext // always looks like a C-frame according to the frame -// conventions in frame_ppc64.hpp. -address os::Aix::ucontext_get_pc(ucontext_t * uc) { +// conventions in frame_ppc.hpp. + +address os::Aix::ucontext_get_pc(const ucontext_t * uc) { return (address)uc->uc_mcontext.jmp_context.iar; } @@ -486,7 +487,7 @@ void os::Aix::init_thread_fpu_state(void) { //////////////////////////////////////////////////////////////////////////////// // thread stack -size_t os::Aix::min_stack_allowed = 768*K; +size_t os::Aix::min_stack_allowed = 128*K; // Aix is always in floating stack mode. The stack size for a new // thread can be set via pthread_attr_setstacksize(). @@ -499,7 +500,7 @@ size_t os::Aix::default_stack_size(os::ThreadType thr_type) { // because of the strange 'fallback logic' in os::create_thread(). // Better set CompilerThreadStackSize in globals_.hpp if you want to // specify a different stack size for compiler threads! - size_t s = (thr_type == os::compiler_thread ? 4 * M : 1024 * K); + size_t s = (thr_type == os::compiler_thread ? 4 * M : 1 * M); return s; } diff --git a/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp b/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp index 4f307279399bfd42bc8b2dcfc8eee539bafea88f..44fa7db3838806a6460b5cdb9aa2209711d00260 100644 --- a/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp +++ b/src/os_cpu/aix_ppc/vm/os_aix_ppc.hpp @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP -#define OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP +#ifndef OS_CPU_AIX_PPC_VM_OS_AIX_PPC_HPP +#define OS_CPU_AIX_PPC_VM_OS_AIX_PPC_HPP static void setup_fpu() {} @@ -32,4 +32,4 @@ // Note: Currently only used in 64 bit Windows implementations static bool register_code_area(char *low, char *high) { return true; } -#endif // OS_CPU_AIX_OJDKPPC_VM_OS_AIX_PPC_HPP +#endif // OS_CPU_AIX_PPC_VM_OS_AIX_PPC_HPP diff --git a/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp b/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp index 3d855f7d0d385e5adab6415faf57ab92ee106f37..85c62b6640ff178a6ed0cf50656b67e704debbd1 100644 --- a/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp +++ b/src/os_cpu/aix_ppc/vm/prefetch_aix_ppc.inline.hpp @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP -#define OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP +#ifndef OS_CPU_AIX_PPC_VM_PREFETCH_AIX_PPC_INLINE_HPP +#define OS_CPU_AIX_PPC_VM_PREFETCH_AIX_PPC_INLINE_HPP #include "runtime/prefetch.hpp" @@ -55,4 +55,4 @@ inline void Prefetch::write(void *loc, intx interval) { #endif } -#endif // OS_CPU_AIX_PPC_64_VM_PREFETCH_AIX_PPC_64_INLINE_HPP +#endif // OS_CPU_AIX_PPC_VM_PREFETCH_AIX_PPC_INLINE_HPP diff --git a/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp b/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp index 1aacc8f456ab293a5b5fed2ab4e1af24c84c971a..825b0b8435a3af9aaef5b68c623a9a58e6ae132b 100644 --- a/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp +++ b/src/os_cpu/aix_ppc/vm/threadLS_aix_ppc.hpp @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP -#define OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP +#ifndef OS_CPU_AIX_PPC_VM_THREADLS_AIX_PPC_HPP +#define OS_CPU_AIX_PPC_VM_THREADLS_AIX_PPC_HPP // Processor dependent parts of ThreadLocalStorage @@ -33,4 +33,4 @@ public: return (Thread *) os::thread_local_storage_at(thread_index()); } -#endif // OS_CPU_AIX_OJDKPPC_VM_THREADLS_AIX_PPC_HPP +#endif // OS_CPU_AIX_PPC_VM_THREADLS_AIX_PPC_HPP diff --git a/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp b/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp index c74b43e16b70017734219341219742a1eb7caa81..2ca7c861cc9d13debe4cdd33b15b5b9dacf69ce8 100644 --- a/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp +++ b/src/os_cpu/aix_ppc/vm/thread_aix_ppc.hpp @@ -23,8 +23,8 @@ * */ -#ifndef OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP -#define OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP +#ifndef OS_CPU_AIX_PPC_VM_THREAD_AIX_PPC_HPP +#define OS_CPU_AIX_PPC_VM_THREAD_AIX_PPC_HPP private: void pd_initialize() { @@ -76,4 +76,4 @@ intptr_t* last_interpreter_fp() { return _last_interpreter_fp; } -#endif // OS_CPU_AIX_OJDKPPC_VM_THREAD_AIX_PPC_HPP +#endif // OS_CPU_AIX_PPC_VM_THREAD_AIX_PPC_HPP diff --git a/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp b/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp index d7fd74d219a681ff964e4ebfd550264282d0e112..f2195c49b969dc84c7b666f070c52a31da2364c3 100644 --- a/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp +++ b/src/os_cpu/linux_ppc/vm/os_linux_ppc.cpp @@ -453,7 +453,7 @@ void os::Linux::set_fpu_control_word(int fpu_control) { //////////////////////////////////////////////////////////////////////////////// // thread stack -size_t os::Linux::min_stack_allowed = 768*K; +size_t os::Linux::min_stack_allowed = 128*K; bool os::Linux::supports_variable_stack_size() { return true; } diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp index 23d6cd5e756396c3097c405ba50693bc50740aad..b3b0336914a1a1e23a47dfbfeab5c604b09884cb 100644 --- a/src/share/vm/classfile/classFileParser.cpp +++ b/src/share/vm/classfile/classFileParser.cpp @@ -3058,21 +3058,39 @@ void ClassFileParser::apply_parsed_class_attributes(instanceKlassHandle k) { } } -// Transfer ownership of metadata allocated to the InstanceKlass. -void ClassFileParser::apply_parsed_class_metadata( - instanceKlassHandle this_klass, - int java_fields_count, TRAPS) { - // Assign annotations if needed - if (_annotations != NULL || _type_annotations != NULL || - _fields_annotations != NULL || _fields_type_annotations != NULL) { +// Create the Annotations object that will +// hold the annotations array for the Klass. +void ClassFileParser::create_combined_annotations(TRAPS) { + if (_annotations == NULL && + _type_annotations == NULL && + _fields_annotations == NULL && + _fields_type_annotations == NULL) { + // Don't create the Annotations object unnecessarily. + return; + } + Annotations* annotations = Annotations::allocate(_loader_data, CHECK); annotations->set_class_annotations(_annotations); annotations->set_class_type_annotations(_type_annotations); annotations->set_fields_annotations(_fields_annotations); annotations->set_fields_type_annotations(_fields_type_annotations); - this_klass->set_annotations(annotations); - } + // This is the Annotations object that will be + // assigned to InstanceKlass being constructed. + _combined_annotations = annotations; + + // The annotations arrays below has been transfered the + // _combined_annotations so these fields can now be cleared. + _annotations = NULL; + _type_annotations = NULL; + _fields_annotations = NULL; + _fields_type_annotations = NULL; +} + +// Transfer ownership of metadata allocated to the InstanceKlass. +void ClassFileParser::apply_parsed_class_metadata( + instanceKlassHandle this_klass, + int java_fields_count, TRAPS) { _cp->set_pool_holder(this_klass()); this_klass->set_constants(_cp); this_klass->set_fields(_fields, java_fields_count); @@ -3080,6 +3098,7 @@ void ClassFileParser::apply_parsed_class_metadata( this_klass->set_inner_classes(_inner_classes); this_klass->set_local_interfaces(_local_interfaces); this_klass->set_transitive_interfaces(_transitive_interfaces); + this_klass->set_annotations(_combined_annotations); // Clear out these fields so they don't get deallocated by the destructor clear_class_metadata(); @@ -3939,6 +3958,10 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, ClassAnnotationCollector parsed_annotations; parse_classfile_attributes(&parsed_annotations, CHECK_(nullHandle)); + // Finalize the Annotations metadata object, + // now that all annotation arrays have been created. + create_combined_annotations(CHECK_(nullHandle)); + // Make sure this is the end of class file stream guarantee_property(cfs->at_eos(), "Extra bytes at the end of class file %s", CHECK_(nullHandle)); @@ -4239,10 +4262,27 @@ ClassFileParser::~ClassFileParser() { InstanceKlass::deallocate_interfaces(_loader_data, _super_klass(), _local_interfaces, _transitive_interfaces); - MetadataFactory::free_array(_loader_data, _annotations); - MetadataFactory::free_array(_loader_data, _type_annotations); - Annotations::free_contents(_loader_data, _fields_annotations); - Annotations::free_contents(_loader_data, _fields_type_annotations); + if (_combined_annotations != NULL) { + // After all annotations arrays have been created, they are installed into the + // Annotations object that will be assigned to the InstanceKlass being created. + + // Deallocate the Annotations object and the installed annotations arrays. + _combined_annotations->deallocate_contents(_loader_data); + + // If the _combined_annotations pointer is non-NULL, + // then the other annotations fields should have been cleared. + assert(_annotations == NULL, "Should have been cleared"); + assert(_type_annotations == NULL, "Should have been cleared"); + assert(_fields_annotations == NULL, "Should have been cleared"); + assert(_fields_type_annotations == NULL, "Should have been cleared"); + } else { + // If the annotations arrays were not installed into the Annotations object, + // then they have to be deallocated explicitly. + MetadataFactory::free_array(_loader_data, _annotations); + MetadataFactory::free_array(_loader_data, _type_annotations); + Annotations::free_contents(_loader_data, _fields_annotations); + Annotations::free_contents(_loader_data, _fields_type_annotations); + } clear_class_metadata(); diff --git a/src/share/vm/classfile/classFileParser.hpp b/src/share/vm/classfile/classFileParser.hpp index b46cf46fa9bcc01880fe34d979d536295811eeac..0e0c741fc5986ca1f73b24cf34c34b688e92327e 100644 --- a/src/share/vm/classfile/classFileParser.hpp +++ b/src/share/vm/classfile/classFileParser.hpp @@ -75,6 +75,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { Array* _inner_classes; Array* _local_interfaces; Array* _transitive_interfaces; + Annotations* _combined_annotations; AnnotationArray* _annotations; AnnotationArray* _type_annotations; Array* _fields_annotations; @@ -86,6 +87,8 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { void set_class_generic_signature_index(u2 x) { _generic_signature_index = x; } void set_class_sde_buffer(char* x, int len) { _sde_buffer = x; _sde_length = len; } + void create_combined_annotations(TRAPS); + void init_parsed_class_attributes(ClassLoaderData* loader_data) { _loader_data = loader_data; _synthetic_flag = false; @@ -110,6 +113,7 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { _inner_classes = NULL; _local_interfaces = NULL; _transitive_interfaces = NULL; + _combined_annotations = NULL; _annotations = _type_annotations = NULL; _fields_annotations = _fields_type_annotations = NULL; } diff --git a/src/share/vm/interpreter/interpreterRuntime.cpp b/src/share/vm/interpreter/interpreterRuntime.cpp index 429ce4d4af64d682a1bf9f28ee61d699ae2b3e92..9a89c7a095b6ed6f062467819013e4c060c655fa 100644 --- a/src/share/vm/interpreter/interpreterRuntime.cpp +++ b/src/share/vm/interpreter/interpreterRuntime.cpp @@ -398,6 +398,18 @@ IRT_ENTRY(address, InterpreterRuntime::exception_handler_for_exception(JavaThrea int handler_bci; int current_bci = bci(thread); + if (thread->frames_to_pop_failed_realloc() > 0) { + // Allocation of scalar replaced object used in this frame + // failed. Unconditionally pop the frame. + thread->dec_frames_to_pop_failed_realloc(); + thread->set_vm_result(h_exception()); + // If the method is synchronized we already unlocked the monitor + // during deoptimization so the interpreter needs to skip it when + // the frame is popped. + thread->set_do_not_unlock_if_synchronized(true); + return Interpreter::remove_activation_entry(); + } + // Need to do this check first since when _do_not_unlock_if_synchronized // is set, we don't want to trigger any classloading which may make calls // into java, or surprisingly find a matching exception handler for bci 0 diff --git a/src/share/vm/memory/filemap.cpp b/src/share/vm/memory/filemap.cpp index d817e7b224c8095405e9b5d2b86080337332d385..644e3b19ead983314356b1d592cca8e6b22c2127 100644 --- a/src/share/vm/memory/filemap.cpp +++ b/src/share/vm/memory/filemap.cpp @@ -97,11 +97,11 @@ void FileMapInfo::fail_continue(const char *msg, ...) { tty->print_cr("UseSharedSpaces: %s", msg); } } + UseSharedSpaces = false; + assert(current_info() != NULL, "singleton must be registered"); + current_info()->close(); } va_end(ap); - UseSharedSpaces = false; - assert(current_info() != NULL, "singleton must be registered"); - current_info()->close(); } // Fill in the fileMapInfo structure with data about this VM instance. diff --git a/src/share/vm/memory/metaspaceShared.cpp b/src/share/vm/memory/metaspaceShared.cpp index 6302c9378b0113a5912515d4b8bcdb2a396c4d93..71fbcb9d1f8a63ac1cd4f6c68545a431161ec452 100644 --- a/src/share/vm/memory/metaspaceShared.cpp +++ b/src/share/vm/memory/metaspaceShared.cpp @@ -967,7 +967,7 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { #endif // If -Xshare:on is specified, print out the error message and exit VM, // otherwise, set UseSharedSpaces to false and continue. - if (RequireSharedSpaces) { + if (RequireSharedSpaces || PrintSharedArchiveAndExit) { vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); } else { FLAG_SET_DEFAULT(UseSharedSpaces, false); diff --git a/src/share/vm/memory/universe.cpp b/src/share/vm/memory/universe.cpp index c4814d6ba131e0f309b2b33886b90830cd3c3fe4..18021f7911a5c81a148779183df1687514144e37 100644 --- a/src/share/vm/memory/universe.cpp +++ b/src/share/vm/memory/universe.cpp @@ -119,6 +119,7 @@ oop Universe::_out_of_memory_error_metaspace = NULL; oop Universe::_out_of_memory_error_class_metaspace = NULL; oop Universe::_out_of_memory_error_array_size = NULL; oop Universe::_out_of_memory_error_gc_overhead_limit = NULL; +oop Universe::_out_of_memory_error_realloc_objects = NULL; objArrayOop Universe::_preallocated_out_of_memory_error_array = NULL; volatile jint Universe::_preallocated_out_of_memory_error_avail_count = 0; bool Universe::_verify_in_progress = false; @@ -190,6 +191,7 @@ void Universe::oops_do(OopClosure* f, bool do_all) { f->do_oop((oop*)&_out_of_memory_error_class_metaspace); f->do_oop((oop*)&_out_of_memory_error_array_size); f->do_oop((oop*)&_out_of_memory_error_gc_overhead_limit); + f->do_oop((oop*)&_out_of_memory_error_realloc_objects); f->do_oop((oop*)&_preallocated_out_of_memory_error_array); f->do_oop((oop*)&_null_ptr_exception_instance); f->do_oop((oop*)&_arithmetic_exception_instance); @@ -574,7 +576,8 @@ bool Universe::should_fill_in_stack_trace(Handle throwable) { (throwable() != Universe::_out_of_memory_error_metaspace) && (throwable() != Universe::_out_of_memory_error_class_metaspace) && (throwable() != Universe::_out_of_memory_error_array_size) && - (throwable() != Universe::_out_of_memory_error_gc_overhead_limit)); + (throwable() != Universe::_out_of_memory_error_gc_overhead_limit) && + (throwable() != Universe::_out_of_memory_error_realloc_objects)); } @@ -1044,6 +1047,7 @@ bool universe_post_init() { Universe::_out_of_memory_error_array_size = k_h->allocate_instance(CHECK_false); Universe::_out_of_memory_error_gc_overhead_limit = k_h->allocate_instance(CHECK_false); + Universe::_out_of_memory_error_realloc_objects = k_h->allocate_instance(CHECK_false); // Setup preallocated NullPointerException // (this is currently used for a cheap & dirty solution in compiler exception handling) @@ -1083,6 +1087,9 @@ bool universe_post_init() { msg = java_lang_String::create_from_str("GC overhead limit exceeded", CHECK_false); java_lang_Throwable::set_message(Universe::_out_of_memory_error_gc_overhead_limit, msg()); + msg = java_lang_String::create_from_str("Java heap space: failed reallocation of scalar replaced objects", CHECK_false); + java_lang_Throwable::set_message(Universe::_out_of_memory_error_realloc_objects, msg()); + msg = java_lang_String::create_from_str("/ by zero", CHECK_false); java_lang_Throwable::set_message(Universe::_arithmetic_exception_instance, msg()); diff --git a/src/share/vm/memory/universe.hpp b/src/share/vm/memory/universe.hpp index 7ac5d2cfc88275b1c035419cab45ff6cafd30132..2c9e90b14f076ab9c7cceb177b57568f0ccd2ef1 100644 --- a/src/share/vm/memory/universe.hpp +++ b/src/share/vm/memory/universe.hpp @@ -157,6 +157,7 @@ class Universe: AllStatic { static oop _out_of_memory_error_class_metaspace; static oop _out_of_memory_error_array_size; static oop _out_of_memory_error_gc_overhead_limit; + static oop _out_of_memory_error_realloc_objects; static Array* _the_empty_int_array; // Canonicalized int array static Array* _the_empty_short_array; // Canonicalized short array @@ -328,6 +329,7 @@ class Universe: AllStatic { static oop out_of_memory_error_class_metaspace() { return gen_out_of_memory_error(_out_of_memory_error_class_metaspace); } static oop out_of_memory_error_array_size() { return gen_out_of_memory_error(_out_of_memory_error_array_size); } static oop out_of_memory_error_gc_overhead_limit() { return gen_out_of_memory_error(_out_of_memory_error_gc_overhead_limit); } + static oop out_of_memory_error_realloc_objects() { return gen_out_of_memory_error(_out_of_memory_error_realloc_objects); } // Accessors needed for fast allocation static Klass** boolArrayKlassObj_addr() { return &_boolArrayKlassObj; } diff --git a/src/share/vm/opto/connode.cpp b/src/share/vm/opto/connode.cpp index 25485296339c2a758b1069fe55f15609f27d7352..fe12b4f49e701696d503d60150603b7d795128ca 100644 --- a/src/share/vm/opto/connode.cpp +++ b/src/share/vm/opto/connode.cpp @@ -462,7 +462,8 @@ const Type *CastIINode::Value(PhaseTransform *phase) const { // Try to improve the type of the CastII if we recognize a CmpI/If // pattern. if (_carry_dependency) { - if (in(0) != NULL && (in(0)->is_IfFalse() || in(0)->is_IfTrue())) { + if (in(0) != NULL && in(0)->in(0) != NULL && in(0)->in(0)->is_If()) { + assert(in(0)->is_IfFalse() || in(0)->is_IfTrue(), "should be If proj"); Node* proj = in(0); if (proj->in(0)->in(1)->is_Bool()) { Node* b = proj->in(0)->in(1); diff --git a/src/share/vm/opto/ifnode.cpp b/src/share/vm/opto/ifnode.cpp index f792c37956fe469f2ee22f1c31454e2064ede462..957dc88dbaf3c6a3a5fbc54be45e6f542f2da29f 100644 --- a/src/share/vm/opto/ifnode.cpp +++ b/src/share/vm/opto/ifnode.cpp @@ -820,6 +820,11 @@ static Node *remove_useless_bool(IfNode *iff, PhaseGVN *phase) { static IfNode* idealize_test(PhaseGVN* phase, IfNode* iff); +struct RangeCheck { + Node* ctl; + jint off; +}; + //------------------------------Ideal------------------------------------------ // Return a node which is more "ideal" than the current node. Strip out // control copies @@ -861,83 +866,141 @@ Node *IfNode::Ideal(PhaseGVN *phase, bool can_reshape) { jint offset1; int flip1 = is_range_check(range1, index1, offset1); if( flip1 ) { - Node *first_prev_dom = NULL; - // Try to remove extra range checks. All 'up_one_dom' gives up at merges // so all checks we inspect post-dominate the top-most check we find. // If we are going to fail the current check and we reach the top check // then we are guaranteed to fail, so just start interpreting there. - // We 'expand' the top 2 range checks to include all post-dominating + // We 'expand' the top 3 range checks to include all post-dominating // checks. - // The top 2 range checks seen - Node *prev_chk1 = NULL; - Node *prev_chk2 = NULL; + // The top 3 range checks seen + const int NRC =3; + RangeCheck prev_checks[NRC]; + int nb_checks = 0; + // Low and high offsets seen so far jint off_lo = offset1; jint off_hi = offset1; - // Scan for the top 2 checks and collect range of offsets - for( int dist = 0; dist < 999; dist++ ) { // Range-Check scan limit - if( dom->Opcode() == Op_If && // Not same opcode? - prev_dom->in(0) == dom ) { // One path of test does dominate? - if( dom == this ) return NULL; // dead loop + bool found_immediate_dominator = false; + + // Scan for the top checks and collect range of offsets + for (int dist = 0; dist < 999; dist++) { // Range-Check scan limit + if (dom->Opcode() == Op_If && // Not same opcode? + prev_dom->in(0) == dom) { // One path of test does dominate? + if (dom == this) return NULL; // dead loop // See if this is a range check Node *index2, *range2; jint offset2; int flip2 = dom->as_If()->is_range_check(range2, index2, offset2); // See if this is a _matching_ range check, checking against // the same array bounds. - if( flip2 == flip1 && range2 == range1 && index2 == index1 && - dom->outcnt() == 2 ) { + if (flip2 == flip1 && range2 == range1 && index2 == index1 && + dom->outcnt() == 2) { + if (nb_checks == 0 && dom->in(1) == in(1)) { + // Found an immediately dominating test at the same offset. + // This kind of back-to-back test can be eliminated locally, + // and there is no need to search further for dominating tests. + assert(offset2 == offset1, "Same test but different offsets"); + found_immediate_dominator = true; + break; + } // Gather expanded bounds off_lo = MIN2(off_lo,offset2); off_hi = MAX2(off_hi,offset2); - // Record top 2 range checks - prev_chk2 = prev_chk1; - prev_chk1 = prev_dom; - // If we match the test exactly, then the top test covers - // both our lower and upper bounds. - if( dom->in(1) == in(1) ) - prev_chk2 = prev_chk1; + // Record top NRC range checks + prev_checks[nb_checks%NRC].ctl = prev_dom; + prev_checks[nb_checks%NRC].off = offset2; + nb_checks++; } } prev_dom = dom; - dom = up_one_dom( dom ); - if( !dom ) break; + dom = up_one_dom(dom); + if (!dom) break; } + if (!found_immediate_dominator) { + // Attempt to widen the dominating range check to cover some later + // ones. Since range checks "fail" by uncommon-trapping to the + // interpreter, widening a check can make us speculatively enter + // the interpreter. If we see range-check deopt's, do not widen! + if (!phase->C->allow_range_check_smearing()) return NULL; - // Attempt to widen the dominating range check to cover some later - // ones. Since range checks "fail" by uncommon-trapping to the - // interpreter, widening a check can make us speculative enter the - // interpreter. If we see range-check deopt's, do not widen! - if (!phase->C->allow_range_check_smearing()) return NULL; - - // Constant indices only need to check the upper bound. - // Non-constance indices must check both low and high. - if( index1 ) { - // Didn't find 2 prior covering checks, so cannot remove anything. - if( !prev_chk2 ) return NULL; - // 'Widen' the offsets of the 1st and 2nd covering check - adjust_check( prev_chk1, range1, index1, flip1, off_lo, igvn ); - // Do not call adjust_check twice on the same projection - // as the first call may have transformed the BoolNode to a ConI - if( prev_chk1 != prev_chk2 ) { - adjust_check( prev_chk2, range1, index1, flip1, off_hi, igvn ); - } - // Test is now covered by prior checks, dominate it out - prev_dom = prev_chk2; - } else { // Didn't find prior covering check, so cannot remove anything. - if( !prev_chk1 ) return NULL; - // 'Widen' the offset of the 1st and only covering check - adjust_check( prev_chk1, range1, index1, flip1, off_hi, igvn ); - // Test is now covered by prior checks, dominate it out - prev_dom = prev_chk1; + if (nb_checks == 0) { + return NULL; + } + // Constant indices only need to check the upper bound. + // Non-constant indices must check both low and high. + int chk0 = (nb_checks - 1) % NRC; + if (index1) { + if (nb_checks == 1) { + return NULL; + } else { + // If the top range check's constant is the min or max of + // all constants we widen the next one to cover the whole + // range of constants. + RangeCheck rc0 = prev_checks[chk0]; + int chk1 = (nb_checks - 2) % NRC; + RangeCheck rc1 = prev_checks[chk1]; + if (rc0.off == off_lo) { + adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); + prev_dom = rc1.ctl; + } else if (rc0.off == off_hi) { + adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn); + prev_dom = rc1.ctl; + } else { + // If the top test's constant is not the min or max of all + // constants, we need 3 range checks. We must leave the + // top test unchanged because widening it would allow the + // accesses it protects to successfully read/write out of + // bounds. + if (nb_checks == 2) { + return NULL; + } + int chk2 = (nb_checks - 3) % NRC; + RangeCheck rc2 = prev_checks[chk2]; + // The top range check a+i covers interval: -a <= i < length-a + // The second range check b+i covers interval: -b <= i < length-b + if (rc1.off <= rc0.off) { + // if b <= a, we change the second range check to: + // -min_of_all_constants <= i < length-min_of_all_constants + // Together top and second range checks now cover: + // -min_of_all_constants <= i < length-a + // which is more restrictive than -b <= i < length-b: + // -b <= -min_of_all_constants <= i < length-a <= length-b + // The third check is then changed to: + // -max_of_all_constants <= i < length-max_of_all_constants + // so 2nd and 3rd checks restrict allowed values of i to: + // -min_of_all_constants <= i < length-max_of_all_constants + adjust_check(rc1.ctl, range1, index1, flip1, off_lo, igvn); + adjust_check(rc2.ctl, range1, index1, flip1, off_hi, igvn); + } else { + // if b > a, we change the second range check to: + // -max_of_all_constants <= i < length-max_of_all_constants + // Together top and second range checks now cover: + // -a <= i < length-max_of_all_constants + // which is more restrictive than -b <= i < length-b: + // -b < -a <= i < length-max_of_all_constants <= length-b + // The third check is then changed to: + // -max_of_all_constants <= i < length-max_of_all_constants + // so 2nd and 3rd checks restrict allowed values of i to: + // -min_of_all_constants <= i < length-max_of_all_constants + adjust_check(rc1.ctl, range1, index1, flip1, off_hi, igvn); + adjust_check(rc2.ctl, range1, index1, flip1, off_lo, igvn); + } + prev_dom = rc2.ctl; + } + } + } else { + RangeCheck rc0 = prev_checks[chk0]; + // 'Widen' the offset of the 1st and only covering check + adjust_check(rc0.ctl, range1, index1, flip1, off_hi, igvn); + // Test is now covered by prior checks, dominate it out + prev_dom = rc0.ctl; + } } - } else { // Scan for an equivalent test Node *cmp; @@ -1019,7 +1082,7 @@ void IfNode::dominated_by( Node *prev_dom, PhaseIterGVN *igvn ) { // for lower and upper bounds. ProjNode* unc_proj = proj_out(1 - prev_dom->as_Proj()->_con)->as_Proj(); if (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate)) - prev_dom = idom; + prev_dom = idom; // Now walk the current IfNode's projections. // Loop ends when 'this' has no more uses. diff --git a/src/share/vm/opto/loopopts.cpp b/src/share/vm/opto/loopopts.cpp index 8b00a9af967c6f0880654f7e371b2c5991d6c345..217effd3ac395ce87aaf83d029499d7bc7e8b57e 100644 --- a/src/share/vm/opto/loopopts.cpp +++ b/src/share/vm/opto/loopopts.cpp @@ -239,8 +239,13 @@ void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exc ProjNode* dp_proj = dp->as_Proj(); ProjNode* unc_proj = iff->as_If()->proj_out(1 - dp_proj->_con)->as_Proj(); if (exclude_loop_predicate && - unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate)) + (unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) || + unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_range_check))) { + // If this is a range check (IfNode::is_range_check), do not + // reorder because Compile::allow_range_check_smearing might have + // changed the check. return; // Let IGVN transformation change control dependence. + } IdealLoopTree *old_loop = get_loop(dp); @@ -896,23 +901,23 @@ void PhaseIdealLoop::split_if_with_blocks_post( Node *n ) { int n_op = n->Opcode(); // Check for an IF being dominated by another IF same test - if( n_op == Op_If ) { + if (n_op == Op_If) { Node *bol = n->in(1); uint max = bol->outcnt(); // Check for same test used more than once? - if( n_op == Op_If && max > 1 && bol->is_Bool() ) { + if (max > 1 && bol->is_Bool()) { // Search up IDOMs to see if this IF is dominated. Node *cutoff = get_ctrl(bol); // Now search up IDOMs till cutoff, looking for a dominating test Node *prevdom = n; Node *dom = idom(prevdom); - while( dom != cutoff ) { - if( dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom ) { + while (dom != cutoff) { + if (dom->req() > 1 && dom->in(1) == bol && prevdom->in(0) == dom) { // Replace the dominated test with an obvious true or false. // Place it on the IGVN worklist for later cleanup. C->set_major_progress(); - dominated_by( prevdom, n, false, true ); + dominated_by(prevdom, n, false, true); #ifndef PRODUCT if( VerifyLoopOptimizations ) verify(); #endif diff --git a/src/share/vm/opto/macro.cpp b/src/share/vm/opto/macro.cpp index 41e6f1e1cb8d9d07db721391d0b0331a3427bfbf..94fdbf86aacc6e00cd6258388da311be64df2974 100644 --- a/src/share/vm/opto/macro.cpp +++ b/src/share/vm/opto/macro.cpp @@ -964,7 +964,11 @@ void PhaseMacroExpand::process_users_of_allocation(CallNode *alloc) { } bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { - if (!EliminateAllocations || !alloc->_is_non_escaping) { + // Don't do scalar replacement if the frame can be popped by JVMTI: + // if reallocation fails during deoptimization we'll pop all + // interpreter frames for this compiled frame and that won't play + // nice with JVMTI popframe. + if (!EliminateAllocations || JvmtiExport::can_pop_frame() || !alloc->_is_non_escaping) { return false; } Node* klass = alloc->in(AllocateNode::KlassNode); diff --git a/src/share/vm/runtime/deoptimization.cpp b/src/share/vm/runtime/deoptimization.cpp index 13a992c0536bbe4019e72bbdafd600aa1feecf82..41fc29905eff6e9d7db3cb10f6d152706ab36f26 100644 --- a/src/share/vm/runtime/deoptimization.cpp +++ b/src/share/vm/runtime/deoptimization.cpp @@ -213,6 +213,8 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert(vf->is_compiled_frame(), "Wrong frame type"); chunk->push(compiledVFrame::cast(vf)); + bool realloc_failures = false; + #ifdef COMPILER2 // Reallocate the non-escaping objects and restore their fields. Then // relock objects if synchronization on them was eliminated. @@ -243,19 +245,16 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread tty->print_cr("SAVED OOP RESULT " INTPTR_FORMAT " in thread " INTPTR_FORMAT, (void *)result, thread); } } - bool reallocated = false; if (objects != NULL) { JRT_BLOCK - reallocated = realloc_objects(thread, &deoptee, objects, THREAD); + realloc_failures = realloc_objects(thread, &deoptee, objects, THREAD); JRT_END - } - if (reallocated) { - reassign_fields(&deoptee, &map, objects); + reassign_fields(&deoptee, &map, objects, realloc_failures); #ifndef PRODUCT if (TraceDeoptimization) { ttyLocker ttyl; tty->print_cr("REALLOC OBJECTS in thread " INTPTR_FORMAT, thread); - print_objects(objects); + print_objects(objects, realloc_failures); } #endif } @@ -273,7 +272,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread assert (cvf->scope() != NULL,"expect only compiled java frames"); GrowableArray* monitors = cvf->monitors(); if (monitors->is_nonempty()) { - relock_objects(monitors, thread); + relock_objects(monitors, thread, realloc_failures); #ifndef PRODUCT if (TraceDeoptimization) { ttyLocker ttyl; @@ -284,7 +283,12 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread first = false; tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread); } - tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); + if (mi->owner_is_scalar_replaced()) { + Klass* k = java_lang_Class::as_Klass(mi->owner_klass()); + tty->print_cr(" failed reallocation for klass %s", k->external_name()); + } else { + tty->print_cr(" object <" INTPTR_FORMAT "> locked", (void *)mi->owner()); + } } } } @@ -299,9 +303,14 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread // out the java state residing in the vframeArray will be missed. No_Safepoint_Verifier no_safepoint; - vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk); + vframeArray* array = create_vframeArray(thread, deoptee, &map, chunk, realloc_failures); +#ifdef COMPILER2 + if (realloc_failures) { + pop_frames_failed_reallocs(thread, array); + } +#endif - assert(thread->vframe_array_head() == NULL, "Pending deopt!");; + assert(thread->vframe_array_head() == NULL, "Pending deopt!"); thread->set_vframe_array_head(array); // Now that the vframeArray has been created if we have any deferred local writes @@ -753,6 +762,8 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra int exception_line = thread->exception_line(); thread->clear_pending_exception(); + bool failures = false; + for (int i = 0; i < objects->length(); i++) { assert(objects->at(i)->is_object(), "invalid debug information"); ObjectValue* sv = (ObjectValue*) objects->at(i); @@ -762,27 +773,34 @@ bool Deoptimization::realloc_objects(JavaThread* thread, frame* fr, GrowableArra if (k->oop_is_instance()) { InstanceKlass* ik = InstanceKlass::cast(k()); - obj = ik->allocate_instance(CHECK_(false)); + obj = ik->allocate_instance(THREAD); } else if (k->oop_is_typeArray()) { TypeArrayKlass* ak = TypeArrayKlass::cast(k()); assert(sv->field_size() % type2size[ak->element_type()] == 0, "non-integral array length"); int len = sv->field_size() / type2size[ak->element_type()]; - obj = ak->allocate(len, CHECK_(false)); + obj = ak->allocate(len, THREAD); } else if (k->oop_is_objArray()) { ObjArrayKlass* ak = ObjArrayKlass::cast(k()); - obj = ak->allocate(sv->field_size(), CHECK_(false)); + obj = ak->allocate(sv->field_size(), THREAD); + } + + if (obj == NULL) { + failures = true; } - assert(obj != NULL, "allocation failed"); assert(sv->value().is_null(), "redundant reallocation"); + assert(obj != NULL || HAS_PENDING_EXCEPTION, "allocation should succeed or we should get an exception"); + CLEAR_PENDING_EXCEPTION; sv->set_value(obj); } - if (pending_exception.not_null()) { + if (failures) { + THROW_OOP_(Universe::out_of_memory_error_realloc_objects(), failures); + } else if (pending_exception.not_null()) { thread->set_pending_exception(pending_exception(), exception_file, exception_line); } - return true; + return failures; } // This assumes that the fields are stored in ObjectValue in the same order @@ -920,12 +938,15 @@ void Deoptimization::reassign_object_array_elements(frame* fr, RegisterMap* reg_ // restore fields of all eliminated objects and arrays -void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects) { +void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures) { for (int i = 0; i < objects->length(); i++) { ObjectValue* sv = (ObjectValue*) objects->at(i); KlassHandle k(java_lang_Class::as_Klass(sv->klass()->as_ConstantOopReadValue()->value()())); Handle obj = sv->value(); - assert(obj.not_null(), "reallocation was missed"); + assert(obj.not_null() || realloc_failures, "reallocation was missed"); + if (obj.is_null()) { + continue; + } if (k->oop_is_instance()) { InstanceKlass* ik = InstanceKlass::cast(k()); @@ -942,34 +963,36 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr // relock objects for which synchronization was eliminated -void Deoptimization::relock_objects(GrowableArray* monitors, JavaThread* thread) { +void Deoptimization::relock_objects(GrowableArray* monitors, JavaThread* thread, bool realloc_failures) { for (int i = 0; i < monitors->length(); i++) { MonitorInfo* mon_info = monitors->at(i); if (mon_info->eliminated()) { - assert(mon_info->owner() != NULL, "reallocation was missed"); - Handle obj = Handle(mon_info->owner()); - markOop mark = obj->mark(); - if (UseBiasedLocking && mark->has_bias_pattern()) { - // New allocated objects may have the mark set to anonymously biased. - // Also the deoptimized method may called methods with synchronization - // where the thread-local object is bias locked to the current thread. - assert(mark->is_biased_anonymously() || - mark->biased_locker() == thread, "should be locked to current thread"); - // Reset mark word to unbiased prototype. - markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); - obj->set_mark(unbiased_prototype); + assert(!mon_info->owner_is_scalar_replaced() || realloc_failures, "reallocation was missed"); + if (!mon_info->owner_is_scalar_replaced()) { + Handle obj = Handle(mon_info->owner()); + markOop mark = obj->mark(); + if (UseBiasedLocking && mark->has_bias_pattern()) { + // New allocated objects may have the mark set to anonymously biased. + // Also the deoptimized method may called methods with synchronization + // where the thread-local object is bias locked to the current thread. + assert(mark->is_biased_anonymously() || + mark->biased_locker() == thread, "should be locked to current thread"); + // Reset mark word to unbiased prototype. + markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age()); + obj->set_mark(unbiased_prototype); + } + BasicLock* lock = mon_info->lock(); + ObjectSynchronizer::slow_enter(obj, lock, thread); + assert(mon_info->owner()->is_locked(), "object must be locked now"); } - BasicLock* lock = mon_info->lock(); - ObjectSynchronizer::slow_enter(obj, lock, thread); } - assert(mon_info->owner()->is_locked(), "object must be locked now"); } } #ifndef PRODUCT // print information about reallocated objects -void Deoptimization::print_objects(GrowableArray* objects) { +void Deoptimization::print_objects(GrowableArray* objects, bool realloc_failures) { fieldDescriptor fd; for (int i = 0; i < objects->length(); i++) { @@ -979,10 +1002,15 @@ void Deoptimization::print_objects(GrowableArray* objects) { tty->print(" object <" INTPTR_FORMAT "> of type ", (void *)sv->value()()); k->print_value(); - tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); + assert(obj.not_null() || realloc_failures, "reallocation was missed"); + if (obj.is_null()) { + tty->print(" allocation failed"); + } else { + tty->print(" allocated (%d bytes)", obj->size() * HeapWordSize); + } tty->cr(); - if (Verbose) { + if (Verbose && !obj.is_null()) { k->oop_print_on(obj(), tty); } } @@ -990,7 +1018,7 @@ void Deoptimization::print_objects(GrowableArray* objects) { #endif #endif // COMPILER2 -vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk) { +vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk, bool realloc_failures) { Events::log(thread, "DEOPT PACKING pc=" INTPTR_FORMAT " sp=" INTPTR_FORMAT, fr.pc(), fr.sp()); #ifndef PRODUCT @@ -1033,7 +1061,7 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re // Since the Java thread being deoptimized will eventually adjust it's own stack, // the vframeArray containing the unpacking information is allocated in the C heap. // For Compiler1, the caller of the deoptimized frame is saved for use by unpack_frames(). - vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr); + vframeArray* array = vframeArray::allocate(thread, frame_size, chunk, reg_map, sender, caller, fr, realloc_failures); // Compare the vframeArray to the collected vframes assert(array->structural_compare(thread, chunk), "just checking"); @@ -1048,6 +1076,33 @@ vframeArray* Deoptimization::create_vframeArray(JavaThread* thread, frame fr, Re return array; } +#ifdef COMPILER2 +void Deoptimization::pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array) { + // Reallocation of some scalar replaced objects failed. Record + // that we need to pop all the interpreter frames for the + // deoptimized compiled frame. + assert(thread->frames_to_pop_failed_realloc() == 0, "missed frames to pop?"); + thread->set_frames_to_pop_failed_realloc(array->frames()); + // Unlock all monitors here otherwise the interpreter will see a + // mix of locked and unlocked monitors (because of failed + // reallocations of synchronized objects) and be confused. + for (int i = 0; i < array->frames(); i++) { + MonitorChunk* monitors = array->element(i)->monitors(); + if (monitors != NULL) { + for (int j = 0; j < monitors->number_of_monitors(); j++) { + BasicObjectLock* src = monitors->at(j); + if (src->obj() != NULL) { + ObjectSynchronizer::fast_exit(src->obj(), src->lock(), thread); + } + } + array->element(i)->free_monitors(thread); +#ifdef ASSERT + array->element(i)->set_removed_monitors(); +#endif + } + } +} +#endif static void collect_monitors(compiledVFrame* cvf, GrowableArray* objects_to_revoke) { GrowableArray* monitors = cvf->monitors(); diff --git a/src/share/vm/runtime/deoptimization.hpp b/src/share/vm/runtime/deoptimization.hpp index b33f2fa83560f53c5803427a82855c2894e954ad..ec74ea9a78fda08d1dbfa2e25f48e056131b72c6 100644 --- a/src/share/vm/runtime/deoptimization.hpp +++ b/src/share/vm/runtime/deoptimization.hpp @@ -120,13 +120,14 @@ class Deoptimization : AllStatic { static bool realloc_objects(JavaThread* thread, frame* fr, GrowableArray* objects, TRAPS); static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type); static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj); - static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects); - static void relock_objects(GrowableArray* monitors, JavaThread* thread); - NOT_PRODUCT(static void print_objects(GrowableArray* objects);) + static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray* objects, bool realloc_failures); + static void relock_objects(GrowableArray* monitors, JavaThread* thread, bool realloc_failures); + static void pop_frames_failed_reallocs(JavaThread* thread, vframeArray* array); + NOT_PRODUCT(static void print_objects(GrowableArray* objects, bool realloc_failures);) #endif // COMPILER2 public: - static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk); + static vframeArray* create_vframeArray(JavaThread* thread, frame fr, RegisterMap *reg_map, GrowableArray* chunk, bool realloc_failures); // Interface used for unpacking deoptimized frames diff --git a/src/share/vm/runtime/sharedRuntime.cpp b/src/share/vm/runtime/sharedRuntime.cpp index 2de8490a18938a25b6deff4852e96c51a406cb59..3d409ed25006cee9ac732d64a47881a848f65c92 100644 --- a/src/share/vm/runtime/sharedRuntime.cpp +++ b/src/share/vm/runtime/sharedRuntime.cpp @@ -482,6 +482,7 @@ JRT_END address SharedRuntime::raw_exception_handler_for_return_address(JavaThread* thread, address return_address) { assert(frame::verify_return_pc(return_address), err_msg("must be a return address: " INTPTR_FORMAT, return_address)); + assert(thread->frames_to_pop_failed_realloc() == 0 || Interpreter::contains(return_address), "missed frames to pop?"); // Reset method handle flag. thread->set_is_method_handle_return(false); diff --git a/src/share/vm/runtime/thread.cpp b/src/share/vm/runtime/thread.cpp index 68fb1fbefb60b626cfb89d69a25aac3e671df10d..83dc76d11381d5e3c9d68b85efd2a787723a005d 100644 --- a/src/share/vm/runtime/thread.cpp +++ b/src/share/vm/runtime/thread.cpp @@ -1495,6 +1495,7 @@ void JavaThread::initialize() { _popframe_condition = popframe_inactive; _popframe_preserved_args = NULL; _popframe_preserved_args_size = 0; + _frames_to_pop_failed_realloc = 0; pd_initialize(); } diff --git a/src/share/vm/runtime/thread.hpp b/src/share/vm/runtime/thread.hpp index a551f6fec7a25d517d5014bbedeca99f7e5d4f7a..2bffb5d7432eee925740fe6be9b5248f081612aa 100644 --- a/src/share/vm/runtime/thread.hpp +++ b/src/share/vm/runtime/thread.hpp @@ -933,6 +933,12 @@ class JavaThread: public Thread { // This is set to popframe_pending to signal that top Java frame should be popped immediately int _popframe_condition; + // If reallocation of scalar replaced objects fails, we throw OOM + // and during exception propagation, pop the top + // _frames_to_pop_failed_realloc frames, the ones that reference + // failed reallocations. + int _frames_to_pop_failed_realloc; + #ifndef PRODUCT int _jmp_ring_index; struct { @@ -1585,6 +1591,10 @@ public: void clr_pop_frame_in_process(void) { _popframe_condition &= ~popframe_processing_bit; } #endif + int frames_to_pop_failed_realloc() const { return _frames_to_pop_failed_realloc; } + void set_frames_to_pop_failed_realloc(int nb) { _frames_to_pop_failed_realloc = nb; } + void dec_frames_to_pop_failed_realloc() { _frames_to_pop_failed_realloc--; } + private: // Saved incoming arguments to popped frame. // Used only when popped interpreted frame returns to deoptimized frame. diff --git a/src/share/vm/runtime/vframeArray.cpp b/src/share/vm/runtime/vframeArray.cpp index 72e2e8717cbdc0e8b14223d01ed38cdae0ed64b8..a99b573864579c89f9999a328c99887f787ac5dd 100644 --- a/src/share/vm/runtime/vframeArray.cpp +++ b/src/share/vm/runtime/vframeArray.cpp @@ -56,7 +56,7 @@ void vframeArrayElement::free_monitors(JavaThread* jt) { } } -void vframeArrayElement::fill_in(compiledVFrame* vf) { +void vframeArrayElement::fill_in(compiledVFrame* vf, bool realloc_failures) { // Copy the information from the compiled vframe to the // interpreter frame we will be creating to replace vf @@ -64,6 +64,9 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { _method = vf->method(); _bci = vf->raw_bci(); _reexecute = vf->should_reexecute(); +#ifdef ASSERT + _removed_monitors = false; +#endif int index; @@ -81,11 +84,15 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { // Migrate the BasicLocks from the stack to the monitor chunk for (index = 0; index < list->length(); index++) { MonitorInfo* monitor = list->at(index); - assert(!monitor->owner_is_scalar_replaced(), "object should be reallocated already"); - assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + assert(!monitor->owner_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); BasicObjectLock* dest = _monitors->at(index); - dest->set_obj(monitor->owner()); - monitor->lock()->move_to(monitor->owner(), dest->lock()); + if (monitor->owner_is_scalar_replaced()) { + dest->set_obj(NULL); + } else { + assert(monitor->owner() == NULL || (!monitor->owner()->is_unlocked() && !monitor->owner()->has_bias_pattern()), "object must be null or locked, and unbiased"); + dest->set_obj(monitor->owner()); + monitor->lock()->move_to(monitor->owner(), dest->lock()); + } } } @@ -110,7 +117,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { StackValue* value = locs->at(index); switch(value->type()) { case T_OBJECT: - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); // preserve object type _locals->add( new StackValue(cast_from_oop((value->get_obj()())), T_OBJECT )); break; @@ -135,7 +142,7 @@ void vframeArrayElement::fill_in(compiledVFrame* vf) { StackValue* value = exprs->at(index); switch(value->type()) { case T_OBJECT: - assert(!value->obj_is_scalar_replaced(), "object should be reallocated already"); + assert(!value->obj_is_scalar_replaced() || realloc_failures, "object should be reallocated already"); // preserve object type _expressions->add( new StackValue(cast_from_oop((value->get_obj()())), T_OBJECT )); break; @@ -286,7 +293,7 @@ void vframeArrayElement::unpack_on_stack(int caller_actual_parameters, _frame.patch_pc(thread, pc); - assert (!method()->is_synchronized() || locks > 0, "synchronized methods must have monitors"); + assert (!method()->is_synchronized() || locks > 0 || _removed_monitors, "synchronized methods must have monitors"); BasicObjectLock* top = iframe()->interpreter_frame_monitor_begin(); for (int index = 0; index < locks; index++) { @@ -438,7 +445,8 @@ int vframeArrayElement::on_stack_size(int callee_parameters, vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, - RegisterMap *reg_map, frame sender, frame caller, frame self) { + RegisterMap *reg_map, frame sender, frame caller, frame self, + bool realloc_failures) { // Allocate the vframeArray vframeArray * result = (vframeArray*) AllocateHeap(sizeof(vframeArray) + // fixed part @@ -450,19 +458,20 @@ vframeArray* vframeArray::allocate(JavaThread* thread, int frame_size, GrowableA result->_caller = caller; result->_original = self; result->set_unroll_block(NULL); // initialize it - result->fill_in(thread, frame_size, chunk, reg_map); + result->fill_in(thread, frame_size, chunk, reg_map, realloc_failures); return result; } void vframeArray::fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, - const RegisterMap *reg_map) { + const RegisterMap *reg_map, + bool realloc_failures) { // Set owner first, it is used when adding monitor chunks _frame_size = frame_size; for(int i = 0; i < chunk->length(); i++) { - element(i)->fill_in(chunk->at(i)); + element(i)->fill_in(chunk->at(i), realloc_failures); } // Copy registers for callee-saved registers diff --git a/src/share/vm/runtime/vframeArray.hpp b/src/share/vm/runtime/vframeArray.hpp index 97e50c5c3a2df4a3869d8492884832142db8ae34..714a4d9400a0a3b8be4a3cd2906780bc1a5eaec5 100644 --- a/src/share/vm/runtime/vframeArray.hpp +++ b/src/share/vm/runtime/vframeArray.hpp @@ -58,6 +58,9 @@ class vframeArrayElement : public _ValueObj { MonitorChunk* _monitors; // active monitors for this vframe StackValueCollection* _locals; StackValueCollection* _expressions; +#ifdef ASSERT + bool _removed_monitors; +#endif public: @@ -78,7 +81,7 @@ class vframeArrayElement : public _ValueObj { StackValueCollection* expressions(void) const { return _expressions; } - void fill_in(compiledVFrame* vf); + void fill_in(compiledVFrame* vf, bool realloc_failures); // Formerly part of deoptimizedVFrame @@ -99,6 +102,12 @@ class vframeArrayElement : public _ValueObj { bool is_bottom_frame, int exec_mode); +#ifdef ASSERT + void set_removed_monitors() { + _removed_monitors = true; + } +#endif + #ifndef PRODUCT void print(outputStream* st); #endif /* PRODUCT */ @@ -160,13 +169,14 @@ class vframeArray: public CHeapObj { int frames() const { return _frames; } static vframeArray* allocate(JavaThread* thread, int frame_size, GrowableArray* chunk, - RegisterMap* reg_map, frame sender, frame caller, frame self); + RegisterMap* reg_map, frame sender, frame caller, frame self, + bool realloc_failures); vframeArrayElement* element(int index) { assert(is_within_bounds(index), "Bad index"); return &_elements[index]; } // Allocates a new vframe in the array and fills the array with vframe information in chunk - void fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, const RegisterMap *reg_map); + void fill_in(JavaThread* thread, int frame_size, GrowableArray* chunk, const RegisterMap *reg_map, bool realloc_failures); // Returns the owner of this vframeArray JavaThread* owner_thread() const { return _owner_thread; } diff --git a/test/compiler/exceptions/SumTest.java b/test/compiler/exceptions/SumTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d4555aa55aaf8b117641d690a92ffa814003900d --- /dev/null +++ b/test/compiler/exceptions/SumTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014, 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 8066900 + * @summary FP registers are not properly restored by C1 when handling exceptions + * @run main/othervm -Xbatch SumTest + * + */ +public class SumTest { + private static class Sum { + + double[] sums; + + /** + * Construct empty Sum + */ + public Sum() { + sums = new double[0]; + } + + /** + * Return the sum of all numbers added to this Sum + * + * @return the sum + */ + final public double getSum() { + double sum = 0; + for (final double s : sums) { + sum += s; + } + + return sum; + } + + /** + * Add a new number to this Sum + * + * @param a number to be added. + */ + final public void add(double a) { + try { + sums[sums.length] = -1; // Cause IndexOutOfBoundsException + } catch (final IndexOutOfBoundsException e) { + final double[] oldSums = sums; + sums = new double[oldSums.length + 1]; // Extend sums + System.arraycopy(oldSums, 0, sums, 0, oldSums.length); + sums[oldSums.length] = a; // Append a + } + } + } + + public static void main(String[] args) throws Exception { + final Sum sum = new Sum(); + for (int i = 1; i <= 10000; ++i) { + sum.add(1); + double ii = sum.getSum(); + if (i != ii) { + throw new Exception("Failure: computed = " + ii + ", expected = " + i); + } + } + } + +} + diff --git a/test/compiler/rangechecks/TestRangeCheckSmearing.java b/test/compiler/rangechecks/TestRangeCheckSmearing.java new file mode 100644 index 0000000000000000000000000000000000000000..204d276f29380be1ce43a797055f3ec7ab4cd224 --- /dev/null +++ b/test/compiler/rangechecks/TestRangeCheckSmearing.java @@ -0,0 +1,436 @@ +/* + * Copyright (c) 2014, 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 8066103 + * @summary C2's range check smearing allows out of bound array accesses + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox /testlibrary/com/oracle/java/testlibrary + * @build TestRangeCheckSmearing + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform + * @run main/othervm -ea -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestRangeCheckSmearing + * + */ + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; +import sun.hotspot.WhiteBox; +import sun.hotspot.code.NMethod; +import com.oracle.java.testlibrary.Platform; + +public class TestRangeCheckSmearing { + private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + + @Retention(RetentionPolicy.RUNTIME) + @interface Args { int[] value(); } + + // first range check is i + max of all constants + @Args({0, 8}) + static int m1(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+9]; + if (allaccesses) { + res += array[i+8]; + res += array[i+7]; + res += array[i+6]; + res += array[i+5]; + res += array[i+4]; + res += array[i+3]; + res += array[i+2]; + res += array[i+1]; + } + return res; + } + + // first range check is i + min of all constants + @Args({0, -9}) + static int m2(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+1]; + if (allaccesses) { + res += array[i+2]; + res += array[i+3]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + // first range check is not i + min/max of all constants + @Args({0, 8}) + static int m3(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i+2]; + res += array[i+1]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -9}) + static int m4(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i+4]; + res += array[i+1]; + res += array[i+2]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -3}) + static int m5(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+2]; + if (allaccesses) { + res += array[i+1]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, 6}) + static int m6(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+4]; + if (allaccesses) { + res += array[i+2]; + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, 6}) + static int m7(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+2]; + res += array[i+4]; + if (allaccesses) { + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({0, -3}) + static int m8(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+4]; + res += array[i+2]; + if (allaccesses) { + res += array[i+1]; + res += array[i+5]; + res += array[i+6]; + res += array[i+7]; + res += array[i+8]; + res += array[i+9]; + } + return res; + } + + @Args({6, 15}) + static int m9(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i-2]; + res += array[i-1]; + res += array[i-4]; + res += array[i-5]; + res += array[i-6]; + } + return res; + } + + @Args({3, 12}) + static int m10(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + if (allaccesses) { + res += array[i-2]; + res += array[i-1]; + res += array[i-3]; + res += array[i+4]; + res += array[i+5]; + res += array[i+6]; + } + return res; + } + + @Args({3, -3}) + static int m11(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i-2]; + if (allaccesses) { + res += array[i+5]; + res += array[i+6]; + } + return res; + } + + @Args({3, 6}) + static int m12(int[] array, int i, boolean allaccesses) { + int res = 0; + res += array[i+3]; + res += array[i+6]; + if (allaccesses) { + res += array[i-2]; + res += array[i-3]; + } + return res; + } + + // check that identical range check is replaced by dominating one + // only when correct + @Args({0}) + static int m13(int[] array, int i, boolean ignore) { + int res = 0; + res += array[i+3]; + res += array[i+3]; + return res; + } + + @Args({2, 0}) + static int m14(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-2]; + res += array[i]; // If range check below were to be removed first this cannot be considered identical to first range check + res += array[i-1]; // range check removed so i-1 array access depends on previous check + + return res; + } + + static int[] m15_dummy = new int[10]; + @Args({2, 0}) + static int m15(int[] array, int i, boolean ignore) { + int res = 0; + res += array[i]; + + // When the loop is optimized out we don't want the + // array[i-1] access which is dependent on array[i]'s + // range check to become dependent on the identical range + // check above. + + int[] array2 = m15_dummy; + int j = 0; + for (; j < 10; j++); + if (j == 10) { + array2 = array; + } + + res += array2[i-2]; + res += array2[i]; + res += array2[i-1]; // range check removed so i-1 array access depends on previous check + + return res; + } + + @Args({2, 0}) + static int m16(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-1]; + res += array[i-1]; + res += array[i-2]; + + return res; + } + + @Args({2, 0}) + static int m17(int[] array, int i, boolean ignore) { + int res = 0; + + res += array[i]; + res += array[i-2]; + res += array[i-2]; + res += array[i+2]; + res += array[i+2]; + res += array[i-1]; + res += array[i-1]; + + return res; + } + + static public void main(String[] args) { + if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { + throw new AssertionError("Background compilation enabled"); + } + new TestRangeCheckSmearing().doTests(); + } + boolean success = true; + boolean exception = false; + final int[] array = new int[10]; + final HashMap tests = new HashMap<>(); + { + final Class TEST_PARAM_TYPES[] = { int[].class, int.class, boolean.class }; + for (Method m : this.getClass().getDeclaredMethods()) { + if (m.getName().matches("m[0-9]+")) { + assert(Modifier.isStatic(m.getModifiers())) : m; + assert(m.getReturnType() == int.class) : m; + assert(Arrays.equals(m.getParameterTypes(), TEST_PARAM_TYPES)) : m; + tests.put(m.getName(), m); + } + } + } + + void invokeTest(Method m, int[] array, int index, boolean z) { + try { + m.invoke(null, array, index, z); + } catch (ReflectiveOperationException roe) { + Throwable ex = roe.getCause(); + if (ex instanceof ArrayIndexOutOfBoundsException) + throw (ArrayIndexOutOfBoundsException) ex; + throw new AssertionError(roe); + } + } + + void doTest(String name) { + Method m = tests.get(name); + tests.remove(name); + int[] args = m.getAnnotation(Args.class).value(); + int index0 = args[0], index1; + boolean exceptionRequired = true; + if (args.length == 2) { + index1 = args[1]; + } else { + // no negative test for this one + assert(args.length == 1); + assert(name.equals("m13")); + exceptionRequired = false; + index1 = index0; + } + // Get the method compiled. + if (!WHITE_BOX.isMethodCompiled(m)) { + // If not, try to compile it with C2 + if(!WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION)) { + // C2 compiler not available, try to compile with C1 + WHITE_BOX.enqueueMethodForCompilation(m, CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE); + } + } + if (!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " not compiled"); + } + + // valid access + invokeTest(m, array, index0, true); + + if (!WHITE_BOX.isMethodCompiled(m)) { + throw new RuntimeException(m + " deoptimized on valid array access"); + } + + exception = false; + boolean test_success = true; + try { + invokeTest(m, array, index1, false); + } catch(ArrayIndexOutOfBoundsException aioob) { + exception = true; + System.out.println("ArrayIndexOutOfBoundsException thrown in "+name); + } + if (!exception) { + System.out.println("ArrayIndexOutOfBoundsException was not thrown in "+name); + } + + if (Platform.isServer()) { + if (exceptionRequired == WHITE_BOX.isMethodCompiled(m)) { + System.out.println((exceptionRequired?"Didn't deoptimized":"deoptimized") + " in "+name); + test_success = false; + } + } + + if (exception != exceptionRequired) { + System.out.println((exceptionRequired?"exception required but not thrown":"not exception required but thrown") + " in "+name); + test_success = false; + } + + if (!test_success) { + success = false; + System.out.println("TEST FAILED: "+name); + } + + } + void doTests() { + doTest("m1"); + doTest("m2"); + doTest("m3"); + doTest("m4"); + doTest("m5"); + doTest("m6"); + doTest("m7"); + doTest("m8"); + doTest("m9"); + doTest("m10"); + doTest("m11"); + doTest("m12"); + doTest("m13"); + doTest("m14"); + doTest("m15"); + doTest("m16"); + doTest("m17"); + if (!success) { + throw new RuntimeException("Some tests failed"); + } + assert(tests.isEmpty()) : tests; + } +} diff --git a/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java b/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java new file mode 100644 index 0000000000000000000000000000000000000000..699754a8e8c428a9789b90d7e848a2d897cdcca2 --- /dev/null +++ b/test/compiler/rangechecks/TestRangeCheckSmearingLoopOpts.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2014, 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 8048170 + * @summary Following range check smearing, range check cannot be replaced by dominating identical test. + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement TestRangeCheckSmearingLoopOpts + * + */ +public class TestRangeCheckSmearingLoopOpts { + + static int dummy; + + static int m1(int[] array, int i) { + for (;;) { + for (;;) { + if (array[i] < 0) { // range check (i+0) dominates equivalent check below + break; + } + i++; + } + + // A control flow that stops IfNode::up_one_dom() + if ((i % 2)== 0) { + if ((array[i] % 2) == 0) { + dummy = i; + } + } + + // IfNode::Ideal will rewrite some range checks if Compile::allow_range_check_smearing + if (array[i-1] == 9) { // range check (i-1) unchanged + int res = array[i-3]; // range check (i-3) unchanged + res += array[i]; // range check (i+0) unchanged + res += array[i-2]; // removed redundant range check + // the previous access might be hoisted by + // PhaseIdealLoop::split_if_with_blocks_post because + // it appears to have the same guard, but it also + // depends on the previous guards + return res; + } + i++; + } + } + + static public void main(String[] args) { + int[] array = { 0, 1, 2, -3, 4, 5, -2, 7, 8, 9, -1 }; + for (int i = 0; i < 20000; i++) { + m1(array, 0); + } + array[0] = -1; + try { + m1(array, 0); + } catch(ArrayIndexOutOfBoundsException aioobe) {} + } +} diff --git a/test/compiler/uncommontrap/TestDeoptOOM.java b/test/compiler/uncommontrap/TestDeoptOOM.java new file mode 100644 index 0000000000000000000000000000000000000000..5342582fb000e3eb8f10042f0ef6236195fe1e17 --- /dev/null +++ b/test/compiler/uncommontrap/TestDeoptOOM.java @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2014, 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 6898462 + * @summary failed reallocations of scalar replaced objects during deoptimization causes crash + * @run main/othervm -XX:-BackgroundCompilation -XX:CompileCommand=exclude,TestDeoptOOM::main -XX:CompileCommand=exclude,TestDeoptOOM::m9_1 -Xmx128M TestDeoptOOM + * + */ + +public class TestDeoptOOM { + + long f1; + long f2; + long f3; + long f4; + long f5; + + static class LinkedList { + LinkedList l; + long[] array; + LinkedList(LinkedList l, int size) { + array = new long[size]; + this.l = l; + } + } + + static LinkedList ll; + + static void consume_all_memory() { + int size = 128 * 1024 * 1024; + while(size > 0) { + try { + while(true) { + ll = new LinkedList(ll, size); + } + } catch(OutOfMemoryError oom) { + } + size = size / 2; + } + } + + static void free_memory() { + ll = null; + } + + static TestDeoptOOM m1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m1"); + } + return null; + } + + static TestDeoptOOM m2_1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m2_1"); + } + return null; + } + + static TestDeoptOOM m2(boolean deopt) { + try { + return m2_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m2"); + } + return null; + } + + static TestDeoptOOM m3_3(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_3"); + } + return null; + } + + static boolean m3_2(boolean deopt) { + try { + return m3_3(deopt) != null; + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_2"); + } + return false; + } + + static TestDeoptOOM m3_1(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (m3_2(deopt)) { + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3_1"); + } + return null; + } + + static TestDeoptOOM m3(boolean deopt) { + try { + return m3_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m3"); + } + return null; + } + + static TestDeoptOOM m4(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + tdoom.f1 = 1l; + tdoom.f2 = 2l; + tdoom.f3 = 3l; + return tdoom; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m4"); + } + return null; + } + + static TestDeoptOOM m5(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + synchronized(tdoom) { + if (deopt) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m5"); + } + return null; + } + + synchronized TestDeoptOOM m6_1(boolean deopt) { + if (deopt) { + return this; + } + return null; + } + + static TestDeoptOOM m6(boolean deopt) { + try { + TestDeoptOOM tdoom = new TestDeoptOOM(); + return tdoom.m6_1(deopt); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m6"); + } + return null; + } + + static TestDeoptOOM m7_1(boolean deopt, Object lock) { + try { + synchronized(lock) { + TestDeoptOOM tdoom = new TestDeoptOOM(); + if (deopt) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m7_1"); + } + return null; + } + + static TestDeoptOOM m7(boolean deopt, Object lock) { + try { + return m7_1(deopt, lock); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m7"); + } + return null; + } + + static class A { + long f1; + long f2; + long f3; + long f4; + long f5; + } + + static class B { + long f1; + long f2; + long f3; + long f4; + long f5; + + A a; + } + + static B m8(boolean deopt) { + try { + A a = new A(); + B b = new B(); + b.a = a; + if (deopt) { + return b; + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m8"); + } + return null; + } + + static void m9_1(int i) { + if (i > 90000) { + consume_all_memory(); + } + } + + static TestDeoptOOM m9() { + try { + for (int i = 0; i < 100000; i++) { + TestDeoptOOM tdoom = new TestDeoptOOM(); + m9_1(i); + if (i > 90000) { + return tdoom; + } + } + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in m1"); + } + return null; + } + + public static void main(String[] args) { + for (int i = 0; i < 20000; i++) { + m1(false); + } + + consume_all_memory(); + + try { + m1(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main " + oom.getMessage()); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m2(false); + } + + consume_all_memory(); + + try { + m2(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m3(false); + } + + consume_all_memory(); + + try { + m3(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m4(false); + } + + consume_all_memory(); + + try { + m4(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m5(false); + } + + consume_all_memory(); + + try { + m5(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + for (int i = 0; i < 20000; i++) { + m6(false); + } + + consume_all_memory(); + + try { + m6(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + final Object lock = new Object(); + + for (int i = 0; i < 20000; i++) { + m7(false, lock); + } + + consume_all_memory(); + + try { + m7(true, lock); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + Thread thread = new Thread() { + public void run() { + System.out.println("Acquiring lock"); + synchronized(lock) { + System.out.println("Lock acquired"); + } + System.out.println("Lock released"); + } + }; + thread.start(); + try { + thread.join(); + } catch(InterruptedException ie) { + } + + for (int i = 0; i < 20000; i++) { + m8(false); + } + + consume_all_memory(); + + try { + m8(true); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + + try { + m9(); + } catch(OutOfMemoryError oom) { + free_memory(); + System.out.println("OOM caught in main"); + } + + free_memory(); + } +} diff --git a/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java b/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java new file mode 100644 index 0000000000000000000000000000000000000000..563bbbbe00eab5690db0edbffd615583656ed181 --- /dev/null +++ b/test/compiler/uncommontrap/TraceDeoptimizationNoRealloc.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014, 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 8067144 + * @summary -XX:+TraceDeoptimization tries to print realloc'ed objects even when there are none + * @run main/othervm -XX:-BackgroundCompilation -XX:-UseOnStackReplacement -XX:+IgnoreUnrecognizedVMOptions -XX:+TraceDeoptimization TraceDeoptimizationNoRealloc + * + */ + +public class TraceDeoptimizationNoRealloc { + + static void m(boolean some_condition) { + if (some_condition) { + return; + } + } + + + static public void main(String[] args) { + for (int i = 0; i < 20000; i++) { + m(false); + } + m(true); + } +} diff --git a/test/runtime/CheckEndorsedAndExtDirs/EndorsedExtDirs.java b/test/runtime/CheckEndorsedAndExtDirs/EndorsedExtDirs.java index 2184318892eb6415abe7b16228bfebc284a04b5d..155123183121162c21f038802da73fbd9da331a4 100644 --- a/test/runtime/CheckEndorsedAndExtDirs/EndorsedExtDirs.java +++ b/test/runtime/CheckEndorsedAndExtDirs/EndorsedExtDirs.java @@ -26,6 +26,7 @@ * @bug 8064667 * @summary Sanity test for -XX:+CheckEndorsedAndExtDirs * @library /testlibrary + * @build com.oracle.java.testlibrary.* * @run main/othervm EndorsedExtDirs */ diff --git a/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java b/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java new file mode 100644 index 0000000000000000000000000000000000000000..e3e81ca9d4380470832213f54a268080869b669e --- /dev/null +++ b/test/runtime/SharedArchiveFile/PrintSharedArchiveAndExit.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2014, 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 8066670 + * @summary Testing -XX:+PrintSharedArchiveAndExit option + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class PrintSharedArchiveAndExit { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", "-Xshare:dump"); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + try { + output.shouldContain("Loading classes to share"); + output.shouldHaveExitValue(0); + + // (1) With a valid archive + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + output.shouldNotContain("java version"); // Should not print JVM version + output.shouldHaveExitValue(0); // Should report success in error code. + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is valid"); + output.shouldNotContain("Usage:"); // Should not print JVM help message + output.shouldHaveExitValue(0); // Should report success in error code. + + // (2) With an invalid archive (boot class path has been prepended) + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/p:foo.jar", + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit", "-version"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is invalid"); + output.shouldNotContain("java version"); // Should not print JVM version + output.shouldHaveExitValue(1); // Should report failure in error code. + + pb = ProcessTools.createJavaProcessBuilder( + "-Xbootclasspath/p:foo.jar", + "-XX:+UnlockDiagnosticVMOptions", "-XX:SharedArchiveFile=./sample.jsa", + "-XX:+PrintSharedArchiveAndExit"); + output = new OutputAnalyzer(pb.start()); + output.shouldContain("archive is invalid"); + output.shouldNotContain("Usage:"); // Should not print JVM help message + output.shouldHaveExitValue(1); // Should report failure in error code. + } catch (RuntimeException e) { + e.printStackTrace(); + output.shouldContain("Unable to use shared archive"); + output.shouldHaveExitValue(1); + } + } +}