diff --git a/.hgtags b/.hgtags index 0f972b265ad8638fdd391acc04c092001ed0d13b..a0a0e11ea519504b5dd62f50189b9fdaf175fd55 100644 --- a/.hgtags +++ b/.hgtags @@ -817,6 +817,8 @@ ca3b8c8e390ab0540b0cc2e5def869b38e460d86 jdk8u75-b01 32b682649973231b54740c09b10889660f6ebde5 jdk8u75-b04 1f43bd4fab06d2ca5d1964611df14d8506d6b36e jdk8u75-b05 916712f178c39d0acbc590f38802133fc86a7346 jdk8u75-b06 +8c791dd1c24d85ebd18b03d49185c2a25263c129 jdk8u75-b07 +e4a935cb6f7178912fd653e2a9514eadec7935ab jdk8u75-b08 d7b01fb81aa8a5437cb03bc36afe15cf0e55fb89 jdk8u76-b00 c1679cc87ba045219169cabb6b9b378c2b5cc578 jdk8u76-b01 218483967e52b419d885d34af4488a81c5133804 jdk8u76-b02 @@ -824,3 +826,5 @@ c1679cc87ba045219169cabb6b9b378c2b5cc578 jdk8u76-b01 16f7b676725aadafb79ea105b22df112e2593a78 jdk8u76-b04 35bfaf7f9021b5c1e86effbeac075753a82e9a0c jdk8u76-b05 6449ee3bf707225372709ac830524c00984c601f jdk8u76-b06 +7d1074c74d6000ec8257917ebfcee3fed4249f7d jdk8u76-b07 +392f8722fc513e28f78c5c563d51af7dc8466b29 jdk8u76-b08 diff --git a/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java b/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java index 3f09ee30c198593b2f321ce45ba92edd1b7273ff..8f51fb8cb8b90a15f864b43e176f4ba5660eb9e9 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java +++ b/agent/src/share/classes/sun/jvm/hotspot/interpreter/Bytecodes.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2016, 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 @@ -253,29 +253,30 @@ public class Bytecodes { public static final int _fast_sgetfield = 210; public static final int _fast_aputfield = 211; public static final int _fast_bputfield = 212; - public static final int _fast_cputfield = 213; - public static final int _fast_dputfield = 214; - public static final int _fast_fputfield = 215; - public static final int _fast_iputfield = 216; - public static final int _fast_lputfield = 217; - public static final int _fast_sputfield = 218; - public static final int _fast_aload_0 = 219; - public static final int _fast_iaccess_0 = 220; - public static final int _fast_aaccess_0 = 221; - public static final int _fast_faccess_0 = 222; - public static final int _fast_iload = 223; - public static final int _fast_iload2 = 224; - public static final int _fast_icaload = 225; - public static final int _fast_invokevfinal = 226; - public static final int _fast_linearswitch = 227; - public static final int _fast_binaryswitch = 228; - public static final int _fast_aldc = 229; - public static final int _fast_aldc_w = 230; - public static final int _return_register_finalizer = 231; - public static final int _invokehandle = 232; - public static final int _shouldnotreachhere = 233; // For debugging - - public static final int number_of_codes = 234; + public static final int _fast_zputfield = 213; + public static final int _fast_cputfield = 214; + public static final int _fast_dputfield = 215; + public static final int _fast_fputfield = 216; + public static final int _fast_iputfield = 217; + public static final int _fast_lputfield = 218; + public static final int _fast_sputfield = 219; + public static final int _fast_aload_0 = 220; + public static final int _fast_iaccess_0 = 221; + public static final int _fast_aaccess_0 = 222; + public static final int _fast_faccess_0 = 223; + public static final int _fast_iload = 224; + public static final int _fast_iload2 = 225; + public static final int _fast_icaload = 226; + public static final int _fast_invokevfinal = 227; + public static final int _fast_linearswitch = 228; + public static final int _fast_binaryswitch = 229; + public static final int _fast_aldc = 230; + public static final int _fast_aldc_w = 231; + public static final int _return_register_finalizer = 232; + public static final int _invokehandle = 233; + public static final int _shouldnotreachhere = 234; // For debugging + + public static final int number_of_codes = 235; // Flag bits derived from format strings, can_trap, can_rewrite, etc.: // semantic flags: @@ -776,6 +777,7 @@ public class Bytecodes { def(_fast_aputfield , "fast_aputfield" , "bJJ" , null , BasicType.getTObject() , 0, true , _putfield ); def(_fast_bputfield , "fast_bputfield" , "bJJ" , null , BasicType.getTInt() , 0, true , _putfield ); + def(_fast_zputfield , "fast_zputfield" , "bJJ" , null , BasicType.getTInt() , 0, true , _putfield ); def(_fast_cputfield , "fast_cputfield" , "bJJ" , null , BasicType.getTChar() , 0, true , _putfield ); def(_fast_dputfield , "fast_dputfield" , "bJJ" , null , BasicType.getTDouble() , 0, true , _putfield ); def(_fast_fputfield , "fast_fputfield" , "bJJ" , null , BasicType.getTFloat() , 0, true , _putfield ); diff --git a/src/cpu/ppc/vm/interp_masm_ppc_64.cpp b/src/cpu/ppc/vm/interp_masm_ppc_64.cpp index 0d39104acab4adbe6d57c8633bf768129b146684..44763647c5bc5be7977f64dfffd87f5ccf444f80 100644 --- a/src/cpu/ppc/vm/interp_masm_ppc_64.cpp +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.cpp @@ -174,6 +174,7 @@ void InterpreterMacroAssembler::load_earlyret_value(TosState state, Register Rsc case ltos: ld(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); break; case btos: // fall through + case ztos: // fall through case ctos: // fall through case stos: // fall through case itos: lwz(R17_tos, in_bytes(JvmtiThreadState::earlyret_value_offset()), RjvmtiState); @@ -302,6 +303,7 @@ void InterpreterMacroAssembler::push(TosState state) { switch (state) { case atos: push_ptr(); break; case btos: + case ztos: case ctos: case stos: case itos: push_i(); break; @@ -317,6 +319,7 @@ void InterpreterMacroAssembler::pop(TosState state) { switch (state) { case atos: pop_ptr(); break; case btos: + case ztos: case ctos: case stos: case itos: pop_i(); break; @@ -751,6 +754,43 @@ void InterpreterMacroAssembler::merge_frames(Register Rsender_sp, Register retur stdux(Rscratch2, R1_SP, Rscratch1); // atomically set *(SP = top_frame_sp) = **SP } +void InterpreterMacroAssembler::narrow(Register result) { + Register ret_type = R11_scratch1; + ld(R11_scratch1, in_bytes(Method::const_offset()), R19_method); + lbz(ret_type, in_bytes(ConstMethod::result_type_offset()), R11_scratch1); + + Label notBool, notByte, notChar, done; + + // common case first + cmpwi(CCR0, ret_type, T_INT); + beq(CCR0, done); + + cmpwi(CCR0, ret_type, T_BOOLEAN); + bne(CCR0, notBool); + andi(result, result, 0x1); + b(done); + + bind(notBool); + cmpwi(CCR0, ret_type, T_BYTE); + bne(CCR0, notByte); + extsb(result, result); + b(done); + + bind(notByte); + cmpwi(CCR0, ret_type, T_CHAR); + bne(CCR0, notChar); + andi(result, result, 0xffff); + b(done); + + bind(notChar); + // cmpwi(CCR0, ret_type, T_SHORT); // all that's left + // bne(CCR0, done); + extsh(result, result); + + // Nothing to do for T_INT + bind(done); +} + // Remove activation. // // Unlock the receiver if this is a synchronized method. diff --git a/src/cpu/ppc/vm/interp_masm_ppc_64.hpp b/src/cpu/ppc/vm/interp_masm_ppc_64.hpp index 555cc5ee9d8c4c8d8daebcd6ed2cb45c4cb9b77a..4b9916ca58b8abc088046ffc58e2c63dedc28c98 100644 --- a/src/cpu/ppc/vm/interp_masm_ppc_64.hpp +++ b/src/cpu/ppc/vm/interp_masm_ppc_64.hpp @@ -149,6 +149,8 @@ class InterpreterMacroAssembler: public MacroAssembler { void get_cpool_and_tags(Register Rcpool, Register Rtags); void is_a(Label& L); + void narrow(Register result); + // Java Call Helpers void call_from_interpreter(Register Rtarget_method, Register Rret_addr, Register Rscratch1, Register Rscratch2); diff --git a/src/cpu/ppc/vm/interpreter_ppc.cpp b/src/cpu/ppc/vm/interpreter_ppc.cpp index 00c80b65b81962d1bd0b7d38049723b2203cb1c2..b6b9907df21f58b7a2f054cd5e3d6171ea5f05c4 100644 --- a/src/cpu/ppc/vm/interpreter_ppc.cpp +++ b/src/cpu/ppc/vm/interpreter_ppc.cpp @@ -640,6 +640,16 @@ address InterpreterGenerator::generate_accessor_entry(void) { __ blr(); } + if (branch_table[ztos] == 0) { // generate only once + __ align(32, 28, 28); // align load + __ fence(); // volatile entry point (one instruction before non-volatile_entry point) + branch_table[ztos] = __ pc(); // non-volatile_entry point + __ lbzx(R3_RET, Rclass_or_obj, Roffset); + __ extsb(R3_RET, R3_RET); + __ beq(CCR6, Lacquire); + __ blr(); + } + if (branch_table[ctos] == 0) { // generate only once __ align(32, 28, 28); // align load __ fence(); // volatile entry point (one instruction before non-volatile_entry point) diff --git a/src/cpu/ppc/vm/templateInterpreter_ppc.cpp b/src/cpu/ppc/vm/templateInterpreter_ppc.cpp index a8d19d286272789c05497a7edcee7510b1926001..de148ae3960e821c12ad4cef2fbbec31312f8086 100644 --- a/src/cpu/ppc/vm/templateInterpreter_ppc.cpp +++ b/src/cpu/ppc/vm/templateInterpreter_ppc.cpp @@ -154,6 +154,7 @@ address TemplateInterpreterGenerator::generate_return_entry_for(TosState state, switch (state) { case ltos: case btos: + case ztos: case ctos: case stos: case atos: @@ -200,6 +201,7 @@ address TemplateInterpreterGenerator::generate_deopt_entry_for(TosState state, i switch (state) { case ltos: case btos: + case ztos: case ctos: case stos: case atos: @@ -1642,12 +1644,14 @@ address TemplateInterpreterGenerator::generate_earlyret_entry_for(TosState state // Copied from TemplateTable::_return. // Restoration of lr done by remove_activation. switch (state) { + // Narrow result if state is itos but result type is smaller. + case itos: __ narrow(R17_tos); /* fall through */ case ltos: case btos: + case ztos: case ctos: case stos: - case atos: - case itos: __ mr(R3_RET, R17_tos); break; + case atos: __ mr(R3_RET, R17_tos); break; case ftos: case dtos: __ fmr(F1_RET, F15_ftos); break; case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need @@ -1717,6 +1721,10 @@ address TemplateInterpreterGenerator::generate_trace_code(TosState state) { bname = "trace_code_btos {"; tsize = 2; break; + case ztos: + bname = "trace_code_ztos {"; + tsize = 2; + break; case ctos: bname = "trace_code_ctos {"; tsize = 2; diff --git a/src/cpu/ppc/vm/templateTable_ppc_64.cpp b/src/cpu/ppc/vm/templateTable_ppc_64.cpp index c4cc257f2316420b2bb990883ec69d2bd2e70457..ee7b925bc0afcf02f08d14f3f4f3347107a8cd9f 100644 --- a/src/cpu/ppc/vm/templateTable_ppc_64.cpp +++ b/src/cpu/ppc/vm/templateTable_ppc_64.cpp @@ -173,6 +173,7 @@ void TemplateTable::patch_bytecode(Bytecodes::Code new_bc, Register Rnew_bc, Reg switch (new_bc) { case Bytecodes::_fast_aputfield: case Bytecodes::_fast_bputfield: + case Bytecodes::_fast_zputfield: case Bytecodes::_fast_cputfield: case Bytecodes::_fast_dputfield: case Bytecodes::_fast_fputfield: @@ -969,9 +970,21 @@ void TemplateTable::bastore() { Rarray = R12_scratch2, Rscratch = R3_ARG1; __ pop_i(Rindex); + __ pop_ptr(Rarray); // tos: val - // Rarray: array ptr (popped by index_check) - __ index_check(Rarray, Rindex, 0, Rscratch, Rarray); + + // Need to check whether array is boolean or byte + // since both types share the bastore bytecode. + __ load_klass(Rscratch, Rarray); + __ lwz(Rscratch, in_bytes(Klass::layout_helper_offset()), Rscratch); + int diffbit = exact_log2(Klass::layout_helper_boolean_diffbit()); + __ testbitdi(CCR0, R0, Rscratch, diffbit); + Label L_skip; + __ bfalse(CCR0, L_skip); + __ andi(R17_tos, R17_tos, 1); // if it is a T_BOOLEAN array, mask the stored value to 0/1 + __ bind(L_skip); + + __ index_check_without_pop(Rarray, Rindex, 0, Rscratch, Rarray); __ stb(R17_tos, arrayOopDesc::base_offset_in_bytes(T_BYTE), Rarray); } @@ -2100,12 +2113,16 @@ void TemplateTable::_return(TosState state) { __ remove_activation(state, /* throw_monitor_exception */ true); // Restoration of lr done by remove_activation. switch (state) { + // Narrow result if state is itos but result type is smaller. + // Need to narrow in the return bytecode rather than in generate_return_entry + // since compiled code callers expect the result to already be narrowed. + case itos: __ narrow(R17_tos); /* fall through */ case ltos: case btos: + case ztos: case ctos: case stos: - case atos: - case itos: __ mr(R3_RET, R17_tos); break; + case atos: __ mr(R3_RET, R17_tos); break; case ftos: case dtos: __ fmr(F1_RET, F15_ftos); break; case vtos: // This might be a constructor. Final fields (and volatile fields on PPC64) need @@ -2500,6 +2517,21 @@ void TemplateTable::getfield_or_static(int byte_no, bool is_static) { __ beq(CCR6, Lacquire); // Volatile? __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + __ align(32, 28, 28); // Align load. + // __ bind(Lztos); (same code as btos) + __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ztos] == 0, "can't compute twice"); + branch_table[ztos] = __ pc(); // non-volatile_entry point + __ lbzx(R17_tos, Rclass_or_obj, Roffset); + __ extsb(R17_tos, R17_tos); + __ push(ztos); + if (!is_static) { + // use btos rewriting, no truncating to t/f bit is needed for getfield. + patch_bytecode(Bytecodes::_fast_bgetfield, Rbc, Rscratch); + } + __ beq(CCR6, Lacquire); // Volatile? + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + __ align(32, 28, 28); // Align load. // __ bind(Lctos); __ fence(); // Volatile entry point (one instruction before non-volatile_entry point). @@ -2590,6 +2622,7 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rscratch, boo case Bytecodes::_fast_aputfield: __ push_ptr(); offs+= Interpreter::stackElementSize; break; case Bytecodes::_fast_iputfield: // Fall through case Bytecodes::_fast_bputfield: // Fall through + case Bytecodes::_fast_zputfield: // Fall through case Bytecodes::_fast_cputfield: // Fall through case Bytecodes::_fast_sputfield: __ push_i(); offs+= Interpreter::stackElementSize; break; case Bytecodes::_fast_lputfield: __ push_l(); offs+=2*Interpreter::stackElementSize; break; @@ -2630,6 +2663,7 @@ void TemplateTable::jvmti_post_field_mod(Register Rcache, Register Rscratch, boo case Bytecodes::_fast_aputfield: __ pop_ptr(); break; case Bytecodes::_fast_iputfield: // Fall through case Bytecodes::_fast_bputfield: // Fall through + case Bytecodes::_fast_zputfield: // Fall through case Bytecodes::_fast_cputfield: // Fall through case Bytecodes::_fast_sputfield: __ pop_i(); break; case Bytecodes::_fast_lputfield: __ pop_l(); break; @@ -2780,6 +2814,21 @@ void TemplateTable::putfield_or_static(int byte_no, bool is_static) { } __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + __ align(32, 28, 28); // Align pop. + // __ bind(Lztos); + __ release(); // Volatile entry point (one instruction before non-volatile_entry point). + assert(branch_table[ztos] == 0, "can't compute twice"); + branch_table[ztos] = __ pc(); // non-volatile_entry point + __ pop(ztos); + if (!is_static) { pop_and_check_object(Rclass_or_obj); } // Kills R11_scratch1. + __ andi(R17_tos, R17_tos, 0x1); + __ stbx(R17_tos, Rclass_or_obj, Roffset); + if (!is_static) { patch_bytecode(Bytecodes::_fast_zputfield, Rbc, Rscratch, true, byte_no); } + if (!support_IRIW_for_not_multiple_copy_atomic_cpu) { + __ beq(CR_is_vol, Lvolatile); // Volatile? + } + __ dispatch_epilog(vtos, Bytecodes::length_for(bytecode())); + __ align(32, 28, 28); // Align pop. // __ bind(Lctos); __ release(); // Volatile entry point (one instruction before non-volatile_entry point). @@ -2895,6 +2944,9 @@ void TemplateTable::fast_storefield(TosState state) { __ stdx(R17_tos, Rclass_or_obj, Roffset); break; + case Bytecodes::_fast_zputfield: + __ andi(R17_tos, R17_tos, 0x1); // boolean is true if LSB is 1 + // fall through to bputfield case Bytecodes::_fast_bputfield: __ stbx(R17_tos, Rclass_or_obj, Roffset); break; diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index 7d118edb57fa76a844c9f8dcb413f44a1f0158ca..6b654a5bfd99a22356e0182ec417c79ec8a13819 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -3839,6 +3839,16 @@ G1CollectedHeap::cleanup_surviving_young_words() { _surviving_young_words = NULL; } +class VerifyRegionRemSetClosure : public HeapRegionClosure { + public: + bool doHeapRegion(HeapRegion* hr) { + if (!hr->continuesHumongous()) { + hr->verify_rem_set(); + } + return false; + } +}; + #ifdef ASSERT class VerifyCSetClosure: public HeapRegionClosure { public: @@ -4015,6 +4025,14 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { increment_total_collections(false /* full gc */); increment_gc_time_stamp(); + if (VerifyRememberedSets) { + if (!VerifySilently) { + gclog_or_tty->print_cr("[Verifying RemSets before GC]"); + } + VerifyRegionRemSetClosure v_cl; + heap_region_iterate(&v_cl); + } + verify_before_gc(); check_bitmaps("GC Start"); @@ -4246,6 +4264,14 @@ G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) { // scanning cards (see CR 7039627). increment_gc_time_stamp(); + if (VerifyRememberedSets) { + if (!VerifySilently) { + gclog_or_tty->print_cr("[Verifying RemSets after GC]"); + } + VerifyRegionRemSetClosure v_cl; + heap_region_iterate(&v_cl); + } + verify_after_gc(); check_bitmaps("GC End"); diff --git a/src/share/vm/gc_implementation/g1/heapRegion.cpp b/src/share/vm/gc_implementation/g1/heapRegion.cpp index d5a4b2617b5abb1220dd6464e50a14ac9667acc1..d95d37949a4b4e5b78a530f31af86e66143c12ca 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp @@ -639,8 +639,8 @@ void HeapRegion::print_on(outputStream* st) const { G1OffsetTableContigSpace::print_on(st); } -class VerifyLiveClosure: public OopClosure { -private: +class G1VerificationClosure : public OopClosure { +protected: G1CollectedHeap* _g1h; CardTableModRefBS* _bs; oop _containing_obj; @@ -651,7 +651,7 @@ public: // _vo == UsePrevMarking -> use "prev" marking information, // _vo == UseNextMarking -> use "next" marking information, // _vo == UseMarkWord -> use mark word from object header. - VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) : + G1VerificationClosure(G1CollectedHeap* g1h, VerifyOption vo) : _g1h(g1h), _bs(NULL), _containing_obj(NULL), _failures(false), _n_failures(0), _vo(vo) { @@ -667,9 +667,6 @@ public: bool failures() { return _failures; } int n_failures() { return _n_failures; } - virtual void do_oop(narrowOop* p) { do_oop_work(p); } - virtual void do_oop( oop* p) { do_oop_work(p); } - void print_object(outputStream* out, oop obj) { #ifdef PRODUCT Klass* k = obj->klass(); @@ -679,19 +676,31 @@ public: obj->print_on(out); #endif // PRODUCT } +}; + +class VerifyLiveClosure : public G1VerificationClosure { +public: + VerifyLiveClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo) {} + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } template void do_oop_work(T* p) { assert(_containing_obj != NULL, "Precondition"); assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), - "Precondition"); + "Precondition"); + verify_liveness(p); + } + + template + void verify_liveness(T* p) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); bool failed = false; if (!_g1h->is_in_closed_subset(obj) || _g1h->is_obj_dead_cond(obj, _vo)) { MutexLockerEx x(ParGCRareEvent_lock, - Mutex::_no_safepoint_check_flag); + Mutex::_no_safepoint_check_flag); if (!_failures) { gclog_or_tty->cr(); @@ -727,50 +736,71 @@ public: failed = true; _n_failures++; } + } + } +}; - if (!_g1h->full_collection() || G1VerifyRSetsDuringFullGC) { - HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p); - HeapRegion* to = _g1h->heap_region_containing(obj); - if (from != NULL && to != NULL && - from != to && - !to->isHumongous()) { - jbyte cv_obj = *_bs->byte_for_const(_containing_obj); - jbyte cv_field = *_bs->byte_for_const(p); - const jbyte dirty = CardTableModRefBS::dirty_card_val(); - - bool is_bad = !(from->is_young() - || to->rem_set()->contains_reference(p) - || !G1HRRSFlushLogBuffersOnVerify && // buffers were not flushed - (_containing_obj->is_objArray() ? - cv_field == dirty - : cv_obj == dirty || cv_field == dirty)); - if (is_bad) { - MutexLockerEx x(ParGCRareEvent_lock, - Mutex::_no_safepoint_check_flag); - - if (!_failures) { - gclog_or_tty->cr(); - gclog_or_tty->print_cr("----------"); - } - gclog_or_tty->print_cr("Missing rem set entry:"); - gclog_or_tty->print_cr("Field "PTR_FORMAT" " - "of obj "PTR_FORMAT", " - "in region "HR_FORMAT, - p, (void*) _containing_obj, - HR_FORMAT_PARAMS(from)); - _containing_obj->print_on(gclog_or_tty); - gclog_or_tty->print_cr("points to obj "PTR_FORMAT" " - "in region "HR_FORMAT, - (void*) obj, - HR_FORMAT_PARAMS(to)); - obj->print_on(gclog_or_tty); - gclog_or_tty->print_cr("Obj head CTE = %d, field CTE = %d.", - cv_obj, cv_field); +class VerifyRemSetClosure : public G1VerificationClosure { +public: + VerifyRemSetClosure(G1CollectedHeap* g1h, VerifyOption vo) : G1VerificationClosure(g1h, vo) {} + virtual void do_oop(narrowOop* p) { do_oop_work(p); } + virtual void do_oop(oop* p) { do_oop_work(p); } + + template + void do_oop_work(T* p) { + assert(_containing_obj != NULL, "Precondition"); + assert(!_g1h->is_obj_dead_cond(_containing_obj, _vo), + "Precondition"); + verify_remembered_set(p); + } + + template + void verify_remembered_set(T* p) { + T heap_oop = oopDesc::load_heap_oop(p); + if (!oopDesc::is_null(heap_oop)) { + oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); + bool failed = false; + HeapRegion* from = _g1h->heap_region_containing((HeapWord*)p); + HeapRegion* to = _g1h->heap_region_containing(obj); + if (from != NULL && to != NULL && + from != to && + !to->isHumongous()) { + jbyte cv_obj = *_bs->byte_for_const(_containing_obj); + jbyte cv_field = *_bs->byte_for_const(p); + const jbyte dirty = CardTableModRefBS::dirty_card_val(); + + bool is_bad = !(from->is_young() + || to->rem_set()->contains_reference(p) + || !G1HRRSFlushLogBuffersOnVerify && // buffers were not flushed + (_containing_obj->is_objArray() ? + cv_field == dirty + : cv_obj == dirty || cv_field == dirty)); + if (is_bad) { + MutexLockerEx x(ParGCRareEvent_lock, + Mutex::_no_safepoint_check_flag); + + if (!_failures) { + gclog_or_tty->cr(); gclog_or_tty->print_cr("----------"); - gclog_or_tty->flush(); - _failures = true; - if (!failed) _n_failures++; } + gclog_or_tty->print_cr("Missing rem set entry:"); + gclog_or_tty->print_cr("Field "PTR_FORMAT" " + "of obj "PTR_FORMAT", " + "in region "HR_FORMAT, + p, (void*) _containing_obj, + HR_FORMAT_PARAMS(from)); + _containing_obj->print_on(gclog_or_tty); + gclog_or_tty->print_cr("points to obj "PTR_FORMAT" " + "in region "HR_FORMAT, + (void*) obj, + HR_FORMAT_PARAMS(to)); + obj->print_on(gclog_or_tty); + gclog_or_tty->print_cr("Obj head CTE = %d, field CTE = %d.", + cv_obj, cv_field); + gclog_or_tty->print_cr("----------"); + gclog_or_tty->flush(); + _failures = true; + if (!failed) _n_failures++; } } } @@ -787,6 +817,7 @@ void HeapRegion::verify(VerifyOption vo, HeapWord* p = bottom(); HeapWord* prev_p = NULL; VerifyLiveClosure vl_cl(g1, vo); + VerifyRemSetClosure vr_cl(g1, vo); bool is_humongous = isHumongous(); bool do_bot_verify = !is_young(); size_t object_num = 0; @@ -832,7 +863,23 @@ void HeapRegion::verify(VerifyOption vo, return; } else { vl_cl.set_containing_obj(obj); - obj->oop_iterate_no_header(&vl_cl); + if (!g1->full_collection() || G1VerifyRSetsDuringFullGC) { + // verify liveness and rem_set + vr_cl.set_containing_obj(obj); + G1Mux2Closure mux(&vl_cl, &vr_cl); + obj->oop_iterate_no_header(&mux); + + if (vr_cl.failures()) { + *failures = true; + } + if (G1MaxVerifyFailures >= 0 && + vr_cl.n_failures() >= G1MaxVerifyFailures) { + return; + } + } else { + // verify only liveness + obj->oop_iterate_no_header(&vl_cl); + } if (vl_cl.failures()) { *failures = true; } @@ -842,7 +889,7 @@ void HeapRegion::verify(VerifyOption vo, } } } else { - gclog_or_tty->print_cr(PTR_FORMAT" no an oop", (void *)obj); + gclog_or_tty->print_cr(PTR_FORMAT" not an oop", (void *)obj); *failures = true; return; } @@ -930,6 +977,46 @@ void HeapRegion::verify() const { verify(VerifyOption_G1UsePrevMarking, /* failures */ &dummy); } +void HeapRegion::verify_rem_set(VerifyOption vo, bool* failures) const { + G1CollectedHeap* g1 = G1CollectedHeap::heap(); + *failures = false; + HeapWord* p = bottom(); + HeapWord* prev_p = NULL; + VerifyRemSetClosure vr_cl(g1, vo); + while (p < top()) { + oop obj = oop(p); + size_t obj_size = block_size(p); + + if (!g1->is_obj_dead_cond(obj, this, vo)) { + if (obj->is_oop()) { + vr_cl.set_containing_obj(obj); + obj->oop_iterate_no_header(&vr_cl); + + if (vr_cl.failures()) { + *failures = true; + } + if (G1MaxVerifyFailures >= 0 && + vr_cl.n_failures() >= G1MaxVerifyFailures) { + return; + } + } else { + gclog_or_tty->print_cr(PTR_FORMAT " not an oop", p2i(obj)); + *failures = true; + return; + } + } + + prev_p = p; + p += obj_size; + } +} + +void HeapRegion::verify_rem_set() const { + bool failures = false; + verify_rem_set(VerifyOption_G1UsePrevMarking, &failures); + guarantee(!failures, "HeapRegion RemSet verification failed"); +} + // G1OffsetTableContigSpace code; copied from space.cpp. Hope this can go // away eventually. diff --git a/src/share/vm/gc_implementation/g1/heapRegion.hpp b/src/share/vm/gc_implementation/g1/heapRegion.hpp index 80ba2231395b6856a4d86bceea18bc56b84365ba..e62a2abbc660e0e7aeaa3ef8634d61e142ec245b 100644 --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp @@ -779,6 +779,9 @@ class HeapRegion: public G1OffsetTableContigSpace { // Override; it uses the "prev" marking information virtual void verify() const; + + void verify_rem_set(VerifyOption vo, bool *failures) const; + void verify_rem_set() const; }; // HeapRegionClosure is used for iterating over regions. diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp index 4780cc6273c6ee92f9e81e2092f0dc237d3bc608..8a41ca4391c5ff7a14ea031f627851f8491f6aca 100644 --- a/src/share/vm/prims/jni.cpp +++ b/src/share/vm/prims/jni.cpp @@ -1121,7 +1121,14 @@ class JNI_ArgumentPusherVaArg : public JNI_ArgumentPusher { protected: va_list _ap; - inline void get_bool() { _arguments->push_int(va_arg(_ap, jint)); } // bool is coerced to int when using va_arg + inline void get_bool() { + // Normalize boolean arguments from native code by converting 1-255 to JNI_TRUE and + // 0 to JNI_FALSE. Boolean return values from native are normalized the same in + // TemplateInterpreterGenerator::generate_result_handler_for and + // SharedRuntime::generate_native_wrapper. + jboolean b = va_arg(_ap, jint); + _arguments->push_int((jint)(b == 0 ? JNI_FALSE : JNI_TRUE)); + } inline void get_char() { _arguments->push_int(va_arg(_ap, jint)); } // char is coerced to int when using va_arg inline void get_short() { _arguments->push_int(va_arg(_ap, jint)); } // short is coerced to int when using va_arg inline void get_byte() { _arguments->push_int(va_arg(_ap, jint)); } // byte is coerced to int when using va_arg @@ -1167,9 +1174,17 @@ class JNI_ArgumentPusherVaArg : public JNI_ArgumentPusher { while ( 1 ) { switch ( fingerprint & parameter_feature_mask ) { case bool_parm: + get_bool(); + break; case char_parm: + get_char(); + break; case short_parm: + get_short(); + break; case byte_parm: + get_byte(); + break; case int_parm: get_int(); break; @@ -1203,7 +1218,14 @@ class JNI_ArgumentPusherArray : public JNI_ArgumentPusher { protected: const jvalue *_ap; - inline void get_bool() { _arguments->push_int((jint)(_ap++)->z); } + inline void get_bool() { + // Normalize boolean arguments from native code by converting 1-255 to JNI_TRUE and + // 0 to JNI_FALSE. Boolean return values from native are normalized the same in + // TemplateInterpreterGenerator::generate_result_handler_for and + // SharedRuntime::generate_native_wrapper. + jboolean b = (_ap++)->z; + _arguments->push_int((jint)(b == 0 ? JNI_FALSE : JNI_TRUE)); + } inline void get_char() { _arguments->push_int((jint)(_ap++)->c); } inline void get_short() { _arguments->push_int((jint)(_ap++)->s); } inline void get_byte() { _arguments->push_int((jint)(_ap++)->b); } diff --git a/test/compiler/loopopts/CountedLoopProblem.java b/test/compiler/loopopts/CountedLoopProblem.java index da206cb86d3144e1f5dc47b39dae42ed4fa3bfab..23cc0bb39e6ecc0840d6750d58666318b2b1a18b 100644 --- a/test/compiler/loopopts/CountedLoopProblem.java +++ b/test/compiler/loopopts/CountedLoopProblem.java @@ -36,18 +36,22 @@ public class CountedLoopProblem { public static void main(String[] args) throws Exception { Random r = new Random(42); int x = 0; - StringBuilder sb = new StringBuilder(); - for(int i = 0; i < 1000000; ++i) { - int v = Math.abs(r.nextInt()); - sb.append('+').append(v).append('\n'); - x += v; - // To trigger the problem we must OSR in the following loop - // To make the problem 100% reproducible run with -XX:-TieredCompilation -XX:OSROnlyBCI=62 - while(x < 0) x += 1000000000; - sb.append('=').append(x).append('\n'); - } - if (sb.toString().hashCode() != 0xaba94591) { - throw new Exception("Unexpected result"); + try { + StringBuilder sb = new StringBuilder(); + for(int i = 0; i < 1000000; ++i) { + int v = Math.abs(r.nextInt()); + sb.append('+').append(v).append('\n'); + x += v; + // To trigger the problem we must OSR in the following loop + // To make the problem 100% reproducible run with -XX:-TieredCompilation -XX:OSROnlyBCI=62 + while(x < 0) x += 1000000000; + sb.append('=').append(x).append('\n'); + } + if (sb.toString().hashCode() != 0xaba94591) { + throw new Exception("Unexpected result"); + } + } catch(OutOfMemoryError e) { + // small heap, ignore } } } diff --git a/test/stress/gc/TestStressRSetCoarsening.java b/test/stress/gc/TestStressRSetCoarsening.java new file mode 100644 index 0000000000000000000000000000000000000000..9e94a320d45d0b559dc5d4c9d642affa71a49020 --- /dev/null +++ b/test/stress/gc/TestStressRSetCoarsening.java @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2016, 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. + */ + +import java.util.concurrent.TimeoutException; +import sun.hotspot.WhiteBox; + +/* + * @test TestStressRSetCoarsening.java + * @key stress + * @bug 8146984 8147087 + * @requires vm.gc=="G1" | vm.gc=="null" + * @requires os.maxMemory > 3G + * + * @summary Stress G1 Remembered Set by creating a lot of cross region links + * @library /testlibrary /testlibrary/whitebox + * @build sun.hotspot.WhiteBox + * @run main ClassFileInstaller sun.hotspot.WhiteBox + * sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=300 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 0 300 + * @run main/othervm/timeout=300 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx500m -XX:G1HeapRegionSize=8m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 1 10 300 + * @run main/othervm/timeout=300 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx500m -XX:G1HeapRegionSize=32m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 42 10 300 + * @run main/othervm/timeout=300 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx500m -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 2 0 300 + * @run main/othervm/timeout=1800 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 500 0 1800 + * @run main/othervm/timeout=1800 + * -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:+UseG1GC + * -XX:+IgnoreUnrecognizedVMOptions -XX:+PrintGC -XX:+PrintGCTimeStamps -Xlog:gc + * -Xmx1G -XX:G1HeapRegionSize=1m -XX:MaxGCPauseMillis=1000 TestStressRSetCoarsening 10 10 1800 + */ + +/** + * What the test does. + * Preparation stage: + * Fill out ~90% of the heap with objects, each object is an object array. + * If we want to allocate K objects per region, we calculate N to meet: + * sizeOf(Object[N]) ~= regionSize / K + * Stress stage: + * No more allocation, so no more GC. + * We will perform a number of iterations. On each iteration i, + * for each pair of regions Rx and Ry we will set c[i] references + * from Rx to Ry. If c[i] less than c[i-1] at the end of iteration + * concurrent mark cycle will be initiated (to recalculate remembered sets). + * As the result RSet will be growing up and down, up and down many times. + * + * The test expects: no crash and no timeouts. + * + * Test Parameters: + * args[0] - number of objects per Heap Region (1 - means humongous) + * args[1] - number of regions to refresh to provoke GC at the end of cycle. + * (0 - means no GC, i.e. no reading from RSet) + * args[2] - timeout in seconds (to stop execution to avoid jtreg timeout) + */ +public class TestStressRSetCoarsening { + + public static void main(String... args) throws InterruptedException { + if (args.length != 3) { + throw new IllegalArgumentException("Wrong number of arguments " + args.length); + } + int objectsPerRegion = Integer.parseInt(args[0]); // 1 means humongous + int regsToRefresh = Integer.parseInt(args[1]); // 0 means no regions to refresh at the end of cycle + int timeout = Integer.parseInt(args[2]); // in seconds, test should stop working eariler + new TestStressRSetCoarsening(objectsPerRegion, regsToRefresh, timeout).go(); + } + + private static final long KB = 1024; + private static final long MB = 1024 * KB; + + private static final WhiteBox WB = WhiteBox.getWhiteBox(); + + public final Object[][] storage; + + /** + * Number of objects per region. This is a test parameter. + */ + public final int K; + + /** + * Length of object array: sizeOf(Object[N]) ~= regionSize / K + * N will be calculated as function of K. + */ + public final int N; + + /** + * How many regions involved into testing. + * Will be calculated as heapFractionToAllocate * freeRegionCount. + */ + public final int regionCount; + + /** + * How much heap to use. + */ + public final float heapFractionToAllocate = 0.9f; + + /** + * How many regions to be refreshed at the end of cycle. + * This is a test parameter. + */ + public final int regsToRefresh; + + /** + * Initial time. + */ + public final long start; + + /** + * Time when the test should stop working. + */ + public final long finishAt; + + /** + * Does pre-calculation and allocate necessary objects. + * + * @param objPerRegions how many objects per G1 heap region + */ + TestStressRSetCoarsening(int objPerRegions, int regsToRefresh, int timeout) { + this.K = objPerRegions; + this.regsToRefresh = regsToRefresh; + this.start = System.currentTimeMillis(); + this.finishAt = start + timeout * 900; // 10% ahead of jtreg timeout + + long regionSize = WB.g1RegionSize(); + + // How many free regions + Runtime rt = Runtime.getRuntime(); + long used = rt.totalMemory() - rt.freeMemory(); + long totalFree = rt.maxMemory() - used; + regionCount = (int) ((totalFree / regionSize) * heapFractionToAllocate); + long toAllocate = regionCount * regionSize; + System.out.println("%% Test parameters"); + System.out.println("%% Objects per region : " + K); + System.out.println("%% Heap fraction to allocate : " + (int) (heapFractionToAllocate * 100) + "%"); + System.out.println("%% Regions to refresh to provoke GC: " + regsToRefresh); + + System.out.println("%% Memory"); + System.out.println("%% used : " + used / MB + "M"); + System.out.println("%% available : " + totalFree / MB + "M"); + System.out.println("%% to allocate : " + toAllocate / MB + "M"); + System.out.println("%% (in regs) : " + regionCount); + System.out.println("%% G1 Region Size: " + regionSize / MB + "M"); + + int refSize = WB.getHeapOopSize(); + + // Calculate N: K*sizeOf(Object[N]) ~= regionSize + // sizeOf(Object[N]) ~= (N+4)*refSize + // ==> + // N = regionSize / K / refSize - 4; + N = (int) ((regionSize / K) / refSize) - 5; + + /* + * -------------- + * region0 storage[0] = new Object[N] + * ... + * storage[K-1] = new Object[N] + * --------------- + * region1 storage[K] = new Object[N] + * ... + * storage[2*K - 1] = new Object[N] + * -------------- + * ... + * -------------- + * regionX storage[X*K] = new Object[N] + * ... + * storage[(X+1)*K -1] = new Object[N] + * where X = HeapFraction * TotalRegions + * ------------- + */ + System.out.println("%% Objects"); + System.out.println("%% N (array length) : " + N); + System.out.println("%% K (objects in regions): " + K); + System.out.println("%% Reference size : " + refSize); + System.out.println("%% Approximate obj size : " + (N + 2) * refSize / KB + "K)"); + + storage = new Object[regionCount * K][]; + for (int i = 0; i < storage.length; i++) { + storage[i] = new Object[N]; + } + } + + public void go() throws InterruptedException { + // threshold for sparce -> fine + final int FINE = WB.getIntxVMFlag("G1RSetSparseRegionEntries").intValue(); + + // threshold for fine -> coarse + final int COARSE = WB.getIntxVMFlag("G1RSetRegionEntries").intValue(); + + // regToRegRefCounts - array of reference counts from region to region + // at the the end of iteration. + // The number of test iterations is array length - 1. + // If c[i] > c[i-1] then during the iteration i more references will + // be created. + // If c[i] < c[i-1] then some referenes will be cleaned. + int[] regToRegRefCounts = {0, FINE / 2, 0, FINE, (FINE + COARSE) / 2, 0, + COARSE, COARSE + 10, FINE + 1, FINE / 2, 0}; + + // For progress tracking + int[] progress = new int[regToRegRefCounts.length]; + progress[0] = 0; + for (int i = 1; i < regToRegRefCounts.length; i++) { + progress[i] = progress[i - 1] + Math.abs(regToRegRefCounts[i] - regToRegRefCounts[i - 1]); + } + try { + for (int i = 1; i < regToRegRefCounts.length; i++) { + int pre = regToRegRefCounts[i - 1]; + int cur = regToRegRefCounts[i]; + float prog = ((float) progress[i - 1] / progress[progress.length - 1]); + + System.out.println("%% step " + i + + " out of " + (regToRegRefCounts.length - 1) + + " (~" + (int) (100 * prog) + "% done)"); + System.out.println("%% " + pre + " --> " + cur); + for (int to = 0; to < regionCount; to++) { + // Select a celebrity object that we will install references to. + // The celebrity will be referred from all other regions. + // If the number of references after should be less than they + // were before, select NULL. + Object celebrity = cur > pre ? storage[to * K] : null; + for (int from = 0; from < regionCount; from++) { + if (to == from) { + continue; // no need to refer to itself + } + + int step = cur > pre ? +1 : -1; + for (int rn = pre; rn != cur; rn += step) { + storage[getY(to, from, rn)][getX(to, from, rn)] = celebrity; + if (System.currentTimeMillis() > finishAt) { + throw new TimeoutException(); + } + } + } + } + if (pre > cur) { + // Number of references went down. + // Need to provoke recalculation of RSet. + WB.g1StartConcMarkCycle(); + while (WB.g1InConcurrentMark()) { + Thread.sleep(1); + } + } + + // To force the use of rememebered set entries we need to provoke a GC. + // To induce some fragmentation, and some mixed GCs, we need + // to make a few objects unreachable. + for (int toClean = i * regsToRefresh; toClean < (i + 1) * regsToRefresh; toClean++) { + int to = toClean % regionCount; + // Need to remove all references from all regions to the region 'to' + for (int from = 0; from < regionCount; from++) { + if (to == from) { + continue; // no need to refer to itself + } + for (int rn = 0; rn <= cur; rn++) { + storage[getY(to, from, rn)][getX(to, from, rn)] = null; + } + } + // 'Refresh' storage elements for the region 'to' + // After that loop all 'old' objects in the region 'to' + // should become unreachable. + for (int k = 0; k < K; k++) { + storage[(to * K + k) % storage.length] = new Object[N]; + } + } + } + } catch (TimeoutException e) { + System.out.println("%% TIMEOUT!!!"); + } + long now = System.currentTimeMillis(); + System.out.println("%% Summary"); + System.out.println("%% Time spent : " + ((now - start) / 1000) + " seconds"); + System.out.println("%% Free memory left : " + Runtime.getRuntime().freeMemory() / KB + "K"); + System.out.println("%% Test passed"); + } + + /** + * Returns X index in the Storage of the reference #rn from the region + * 'from' to the region 'to'. + * + * @param to region # to refer to + * @param from region # to refer from + * @param rn number of reference + * + * @return X index in the range: [0 ... N-1] + */ + private int getX(int to, int from, int rn) { + return (rn * regionCount + to) % N; + } + + /** + * Returns Y index in the Storage of the reference #rn from the region + * 'from' to the region 'to'. + * + * @param to region # to refer to + * @param from region # to refer from + * @param rn number of reference + * + * @return Y index in the range: [0 ... K*regionCount -1] + */ + private int getY(int to, int from, int rn) { + return ((rn * regionCount + to) / N + from * K) % (regionCount * K); + } +} +