# HG changeset patch # User robm # Date 1454680396 0 # Node ID d992b4af24c77e48d690d1eef9fbdbfbadc41a3f # Parent 2681e95121ac1212e307724c8c545c1a5d8ed821# Parent 523d1731c39131e90c817ba7b7272af365aeb715 Merge diff -r 2681e95121ac -r d992b4af24c7 agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java --- a/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java Mon Jan 25 15:46:56 2016 -0800 +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/OopUtilities.java Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 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 @@ -219,7 +219,7 @@ if (threadNameField == null) { SystemDictionary sysDict = VM.getVM().getSystemDictionary(); InstanceKlass k = sysDict.getThreadKlass(); - threadNameField = (OopField) k.findField("name", "[C"); + threadNameField = (OopField) k.findField("name", "Ljava/lang/String;"); threadGroupField = (OopField) k.findField("group", "Ljava/lang/ThreadGroup;"); threadEETopField = (LongField) k.findField("eetop", "J"); threadTIDField = (LongField) k.findField("tid", "J"); @@ -258,7 +258,7 @@ public static String threadOopGetName(Oop threadOop) { initThreadFields(); - return charArrayToString((TypeArray) threadNameField.getValue(threadOop)); + return stringOopToString(threadNameField.getValue(threadOop)); } /** May return null if, e.g., thread was not started */ diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/classfile/javaClasses.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -936,7 +936,7 @@ assert(_group_offset == 0, "offsets should be initialized only once"); Klass* k = SystemDictionary::Thread_klass(); - compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::char_array_signature()); + compute_offset(_name_offset, k, vmSymbols::name_name(), vmSymbols::string_signature()); compute_offset(_group_offset, k, vmSymbols::group_name(), vmSymbols::threadgroup_signature()); compute_offset(_contextClassLoader_offset, k, vmSymbols::contextClassLoader_name(), vmSymbols::classloader_signature()); compute_offset(_inheritedAccessControlContext_offset, k, vmSymbols::inheritedAccessControlContext_name(), vmSymbols::accesscontrolcontext_signature()); @@ -966,15 +966,12 @@ } -typeArrayOop java_lang_Thread::name(oop java_thread) { - oop name = java_thread->obj_field(_name_offset); - assert(name == NULL || (name->is_typeArray() && TypeArrayKlass::cast(name->klass())->element_type() == T_CHAR), "just checking"); - return typeArrayOop(name); -} - - -void java_lang_Thread::set_name(oop java_thread, typeArrayOop name) { - assert(java_thread->obj_field(_name_offset) == NULL, "name should be NULL"); +oop java_lang_Thread::name(oop java_thread) { + return java_thread->obj_field(_name_offset); +} + + +void java_lang_Thread::set_name(oop java_thread, oop name) { java_thread->obj_field_put(_name_offset, name); } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/classfile/javaClasses.hpp --- a/src/share/vm/classfile/javaClasses.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/classfile/javaClasses.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -343,8 +343,8 @@ // Set JavaThread for instance static void set_thread(oop java_thread, JavaThread* thread); // Name - static typeArrayOop name(oop java_thread); - static void set_name(oop java_thread, typeArrayOop name); + static oop name(oop java_thread); + static void set_name(oop java_thread, oop name); // Priority static ThreadPriority priority(oop java_thread); static void set_priority(oop java_thread, ThreadPriority priority); diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -3839,6 +3839,16 @@ _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 @@ 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 @@ // 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 -r 2681e95121ac -r d992b4af24c7 src/share/vm/gc_implementation/g1/heapRegion.cpp --- a/src/share/vm/gc_implementation/g1/heapRegion.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegion.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -639,8 +639,8 @@ 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 @@ // _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 @@ 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 @@ 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 @@ 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(); +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); + } - 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); + 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(); - 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); + 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 @@ 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 @@ 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 @@ } } } 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 @@ 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 -r 2681e95121ac -r d992b4af24c7 src/share/vm/gc_implementation/g1/heapRegion.hpp --- a/src/share/vm/gc_implementation/g1/heapRegion.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegion.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -779,6 +779,9 @@ // 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 -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/compile.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -412,6 +412,13 @@ remove_macro_node(n); } } + // Remove useless CastII nodes with range check dependency + for (int i = range_check_cast_count() - 1; i >= 0; i--) { + Node* cast = range_check_cast_node(i); + if (!useful.member(cast)) { + remove_range_check_cast(cast); + } + } // Remove useless expensive node for (int i = C->expensive_count()-1; i >= 0; i--) { Node* n = C->expensive_node(i); @@ -1148,6 +1155,7 @@ _macro_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); _predicate_opaqs = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); _expensive_nodes = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); + _range_check_casts = new(comp_arena()) GrowableArray(comp_arena(), 8, 0, NULL); register_library_intrinsics(); } @@ -1876,6 +1884,22 @@ assert(predicate_count()==0, "should be clean!"); } +void Compile::add_range_check_cast(Node* n) { + assert(n->isa_CastII()->has_range_check(), "CastII should have range check dependency"); + assert(!_range_check_casts->contains(n), "duplicate entry in range check casts"); + _range_check_casts->append(n); +} + +// Remove all range check dependent CastIINodes. +void Compile::remove_range_check_casts(PhaseIterGVN &igvn) { + for (int i = range_check_cast_count(); i > 0; i--) { + Node* cast = range_check_cast_node(i-1); + assert(cast->isa_CastII()->has_range_check(), "CastII should have range check dependency"); + igvn.replace_node(cast, cast->in(1)); + } + assert(range_check_cast_count() == 0, "should be empty"); +} + // StringOpts and late inlining of string methods void Compile::inline_string_calls(bool parse_time) { { @@ -2218,6 +2242,12 @@ PhaseIdealLoop::verify(igvn); } + if (range_check_cast_count() > 0) { + // No more loop optimizations. Remove all range check dependent CastIINodes. + C->remove_range_check_casts(igvn); + igvn.optimize(); + } + { NOT_PRODUCT( TracePhase t2("macroExpand", &_t_macroExpand, TimeCompiler); ) PhaseMacroExpand mex(igvn); @@ -2987,6 +3017,16 @@ #endif +#ifdef ASSERT + case Op_CastII: + // Verify that all range check dependent CastII nodes were removed. + if (n->isa_CastII()->has_range_check()) { + n->dump(3); + assert(false, "Range check dependent CastII node was not removed"); + } + break; +#endif + case Op_ModI: if (UseDivMod) { // Check if a%b and a/b both exist @@ -4024,6 +4064,24 @@ } } +// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check) +Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl) { + if (ctrl != NULL) { + // Express control dependency by a CastII node with a narrow type. + value = new (phase->C) CastIINode(value, itype, false, true /* range check dependency */); + // Make the CastII node dependent on the control input to prevent the narrowed ConvI2L + // node from floating above the range check during loop optimizations. Otherwise, the + // ConvI2L node may be eliminated independently of the range check, causing the data path + // to become TOP while the control path is still there (although it's unreachable). + value->set_req(0, ctrl); + // Save CastII node to remove it after loop optimizations. + phase->C->add_range_check_cast(value); + value = phase->transform(value); + } + const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen); + return phase->transform(new (phase->C) ConvI2LNode(value, ltype)); +} + // Auxiliary method to support randomized stressing/fuzzing. // // This method can be called the arbitrary number of times, with current count diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/compile.hpp --- a/src/share/vm/opto/compile.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/compile.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -75,6 +75,7 @@ class JVMState; class Type; class TypeData; +class TypeInt; class TypePtr; class TypeOopPtr; class TypeFunc; @@ -334,6 +335,7 @@ GrowableArray* _macro_nodes; // List of nodes which need to be expanded before matching. GrowableArray* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates. GrowableArray* _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common + GrowableArray* _range_check_casts; // List of CastII nodes with a range check dependency ConnectionGraph* _congraph; #ifndef PRODUCT IdealGraphPrinter* _printer; @@ -669,7 +671,7 @@ void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { //assert(n->is_macro(), "must be a macro node"); - assert(!_macro_nodes->contains(n), " duplicate entry in expand list"); + assert(!_macro_nodes->contains(n), "duplicate entry in expand list"); _macro_nodes->append(n); } void remove_macro_node(Node * n) { @@ -689,10 +691,23 @@ } } void add_predicate_opaq(Node * n) { - assert(!_predicate_opaqs->contains(n), " duplicate entry in predicate opaque1"); + assert(!_predicate_opaqs->contains(n), "duplicate entry in predicate opaque1"); assert(_macro_nodes->contains(n), "should have already been in macro list"); _predicate_opaqs->append(n); } + + // Range check dependent CastII nodes that can be removed after loop optimizations + void add_range_check_cast(Node* n); + void remove_range_check_cast(Node* n) { + if (_range_check_casts->contains(n)) { + _range_check_casts->remove(n); + } + } + Node* range_check_cast_node(int idx) const { return _range_check_casts->at(idx); } + int range_check_cast_count() const { return _range_check_casts->length(); } + // Remove all range check dependent CastIINodes. + void remove_range_check_casts(PhaseIterGVN &igvn); + // remove the opaque nodes that protect the predicates so that the unused checks and // uncommon traps will be eliminated from the graph. void cleanup_loop_predicates(PhaseIterGVN &igvn); @@ -1201,6 +1216,9 @@ // Definitions of pd methods static void pd_compiler2_init(); + // Convert integer value to a narrowed long type dependent on ctrl (for example, a range check) + static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl); + // Auxiliary method for randomized fuzzing/stressing static bool randomized_select(int count); }; diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/connode.cpp --- a/src/share/vm/opto/connode.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/connode.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -535,6 +535,9 @@ if (_carry_dependency) { st->print(" carry dependency"); } + if (_range_check_dependency) { + st->print(" range check dependency"); + } } #endif @@ -994,7 +997,8 @@ } #ifdef _LP64 - // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) , + // Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) or + // ConvI2L(CastII(AddI(x, y))) to AddL(ConvI2L(CastII(x)), ConvI2L(CastII(y))), // but only if x and y have subranges that cannot cause 32-bit overflow, // under the assumption that x+y is in my own subrange this->type(). @@ -1018,6 +1022,13 @@ Node* z = in(1); int op = z->Opcode(); + Node* ctrl = NULL; + if (op == Op_CastII && z->as_CastII()->has_range_check()) { + // Skip CastII node but save control dependency + ctrl = z->in(0); + z = z->in(1); + op = z->Opcode(); + } if (op == Op_AddI || op == Op_SubI) { Node* x = z->in(1); Node* y = z->in(2); @@ -1075,9 +1086,10 @@ rylo = -ryhi; ryhi = -rylo0; } - - Node* cx = phase->transform( new (phase->C) ConvI2LNode(x, TypeLong::make(rxlo, rxhi, widen)) ); - Node* cy = phase->transform( new (phase->C) ConvI2LNode(y, TypeLong::make(rylo, ryhi, widen)) ); + assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow"); + assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow"); + Node* cx = phase->C->constrained_convI2L(phase, x, TypeInt::make(rxlo, rxhi, widen), ctrl); + Node* cy = phase->C->constrained_convI2L(phase, y, TypeInt::make(rylo, ryhi, widen), ctrl); switch (op) { case Op_AddI: return new (phase->C) AddLNode(cx, cy); case Op_SubI: return new (phase->C) SubLNode(cx, cy); diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/connode.hpp --- a/src/share/vm/opto/connode.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/connode.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -244,19 +244,31 @@ private: // Can this node be removed post CCP or does it carry a required dependency? const bool _carry_dependency; + // Is this node dependent on a range check? + const bool _range_check_dependency; protected: virtual uint cmp( const Node &n ) const; virtual uint size_of() const; public: - CastIINode(Node *n, const Type *t, bool carry_dependency = false) - : ConstraintCastNode(n,t), _carry_dependency(carry_dependency) {} + CastIINode(Node *n, const Type *t, bool carry_dependency = false, bool range_check_dependency = false) + : ConstraintCastNode(n,t), _carry_dependency(carry_dependency), _range_check_dependency(range_check_dependency) { + init_class_id(Class_CastII); + } virtual int Opcode() const; virtual uint ideal_reg() const { return Op_RegI; } virtual Node *Identity( PhaseTransform *phase ); virtual const Type *Value( PhaseTransform *phase ) const; virtual Node *Ideal_DU_postCCP( PhaseCCP * ); + const bool has_range_check() { + #ifdef _LP64 + return _range_check_dependency; + #else + assert(!_range_check_dependency, "Should not have range check dependency"); + return false; + #endif + } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; #endif diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/graphKit.cpp --- a/src/share/vm/opto/graphKit.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/graphKit.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1645,7 +1645,7 @@ //-------------------------array_element_address------------------------- Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt, - const TypeInt* sizetype) { + const TypeInt* sizetype, Node* ctrl) { uint shift = exact_log2(type2aelembytes(elembt)); uint header = arrayOopDesc::base_offset_in_bytes(elembt); @@ -1670,9 +1670,9 @@ // number. (The prior range check has ensured this.) // This assertion is used by ConvI2LNode::Ideal. int index_max = max_jint - 1; // array size is max_jint, index is one less - if (sizetype != NULL) index_max = sizetype->_hi - 1; - const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax); - idx = _gvn.transform( new (C) ConvI2LNode(idx, lidxtype) ); + if (sizetype != NULL) index_max = sizetype->_hi - 1; + const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax); + idx = C->constrained_convI2L(&_gvn, idx, iidxtype, ctrl); #endif Node* scale = _gvn.transform( new (C) LShiftXNode(idx, intcon(shift)) ); return basic_plus_adr(ary, base, scale); @@ -3491,10 +3491,6 @@ Node* initial_slow_cmp = _gvn.transform( new (C) CmpUNode( length, intcon( fast_size_limit ) ) ); Node* initial_slow_test = _gvn.transform( new (C) BoolNode( initial_slow_cmp, BoolTest::gt ) ); - if (initial_slow_test->is_Bool()) { - // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick. - initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); - } // --- Size Computation --- // array_size = round_to_heap(array_header + (length << elem_shift)); @@ -3540,13 +3536,35 @@ Node* lengthx = ConvI2X(length); Node* headerx = ConvI2X(header_size); #ifdef _LP64 - { const TypeLong* tllen = _gvn.find_long_type(lengthx); - if (tllen != NULL && tllen->_lo < 0) { + { const TypeInt* tilen = _gvn.find_int_type(length); + if (tilen != NULL && tilen->_lo < 0) { // Add a manual constraint to a positive range. Cf. array_element_address. - jlong size_max = arrayOopDesc::max_array_length(T_BYTE); - if (size_max > tllen->_hi) size_max = tllen->_hi; - const TypeLong* tlcon = TypeLong::make(CONST64(0), size_max, Type::WidenMin); - lengthx = _gvn.transform( new (C) ConvI2LNode(length, tlcon)); + jlong size_max = fast_size_limit; + if (size_max > tilen->_hi) size_max = tilen->_hi; + const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin); + + // Only do a narrow I2L conversion if the range check passed. + IfNode* iff = new (C) IfNode(control(), initial_slow_test, PROB_MIN, COUNT_UNKNOWN); + _gvn.transform(iff); + RegionNode* region = new (C) RegionNode(3); + _gvn.set_type(region, Type::CONTROL); + lengthx = new (C) PhiNode(region, TypeLong::LONG); + _gvn.set_type(lengthx, TypeLong::LONG); + + // Range check passed. Use ConvI2L node with narrow type. + Node* passed = IfFalse(iff); + region->init_req(1, passed); + // Make I2L conversion control dependent to prevent it from + // floating above the range check during loop optimizations. + lengthx->init_req(1, C->constrained_convI2L(&_gvn, length, tlcon, passed)); + + // Range check failed. Use ConvI2L with wide type because length may be invalid. + region->init_req(2, IfTrue(iff)); + lengthx->init_req(2, ConvI2X(length)); + + set_control(region); + record_for_igvn(region); + record_for_igvn(lengthx); } } #endif @@ -3577,6 +3595,11 @@ Node *mem = reset_memory(); set_all_memory(mem); // Create new memory state + if (initial_slow_test->is_Bool()) { + // Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick. + initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn); + } + // Create the AllocateArrayNode and its result projections AllocateArrayNode* alloc = new (C) AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT), diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/graphKit.hpp --- a/src/share/vm/opto/graphKit.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/graphKit.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -626,7 +626,9 @@ // Return addressing for an array element. Node* array_element_address(Node* ary, Node* idx, BasicType elembt, // Optional constraint on the array size: - const TypeInt* sizetype = NULL); + const TypeInt* sizetype = NULL, + // Optional control dependency (for example, on range check) + Node* ctrl = NULL); // Return a load of array element at idx. Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype); diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/loopTransform.cpp --- a/src/share/vm/opto/loopTransform.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/loopTransform.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -2438,7 +2438,7 @@ //============================================================================= // Process all the loops in the loop tree and replace any fill -// patterns with an intrisc version. +// patterns with an intrinsic version. bool PhaseIdealLoop::do_intrinsify_fill() { bool changed = false; for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { @@ -2536,8 +2536,9 @@ } // Make sure the address expression can be handled. It should be - // head->phi * elsize + con. head->phi might have a ConvI2L. + // head->phi * elsize + con. head->phi might have a ConvI2L(CastII()). Node* elements[4]; + Node* cast = NULL; Node* conv = NULL; bool found_index = false; int count = store->in(MemNode::Address)->as_AddP()->unpack_offsets(elements, ARRAY_SIZE(elements)); @@ -2552,6 +2553,12 @@ conv = value; value = value->in(1); } + if (value->Opcode() == Op_CastII && + value->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + cast = value; + value = value->in(1); + } #endif if (value != head->phi()) { msg = "unhandled shift in address"; @@ -2564,9 +2571,16 @@ } } } else if (n->Opcode() == Op_ConvI2L && conv == NULL) { - if (n->in(1) == head->phi()) { + conv = n; + n = n->in(1); + if (n->Opcode() == Op_CastII && + n->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + cast = n; + n = n->in(1); + } + if (n == head->phi()) { found_index = true; - conv = n; } else { msg = "unhandled input to ConvI2L"; } @@ -2625,6 +2639,7 @@ // Address elements are ok if (con) ok.set(con->_idx); if (shift) ok.set(shift->_idx); + if (cast) ok.set(cast->_idx); if (conv) ok.set(conv->_idx); for (uint i = 0; msg == NULL && i < lpt->_body.size(); i++) { diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/loopopts.cpp --- a/src/share/vm/opto/loopopts.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/loopopts.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -772,6 +772,9 @@ #ifdef _LP64 if (m->Opcode() == Op_ConvI2L) return false; + if (m->is_CastII() && m->isa_CastII()->has_range_check()) { + return false; + } #endif } } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/node.cpp --- a/src/share/vm/opto/node.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/node.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -521,6 +521,11 @@ C->add_macro_node(n); if (is_expensive()) C->add_expensive_node(n); + // If the cloned node is a range check dependent CastII, add it to the list. + CastIINode* cast = n->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + C->add_range_check_cast(cast); + } n->set_idx(C->next_unique()); // Get new unique index as well debug_only( n->verify_construction() ); @@ -649,6 +654,11 @@ if (is_expensive()) { compile->remove_expensive_node(this); } + CastIINode* cast = isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + compile->remove_range_check_cast(cast); + } + if (is_SafePoint()) { as_SafePoint()->delete_replaced_nodes(); } @@ -1344,6 +1354,10 @@ if (dead->is_expensive()) { igvn->C->remove_expensive_node(dead); } + CastIINode* cast = dead->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + igvn->C->remove_range_check_cast(cast); + } igvn->C->record_dead_node(dead->_idx); // Kill all inputs to the dead guy for (uint i=0; i < dead->req(); i++) { diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/node.hpp --- a/src/share/vm/opto/node.hpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/node.hpp Fri Feb 05 13:53:16 2016 +0000 @@ -54,6 +54,7 @@ class CatchNode; class CatchProjNode; class CheckCastPPNode; +class CastIINode; class ClearArrayNode; class CmpNode; class CodeBuffer; @@ -603,6 +604,7 @@ DEFINE_CLASS_ID(Type, Node, 2) DEFINE_CLASS_ID(Phi, Type, 0) DEFINE_CLASS_ID(ConstraintCast, Type, 1) + DEFINE_CLASS_ID(CastII, ConstraintCast, 0) DEFINE_CLASS_ID(CheckCastPP, Type, 2) DEFINE_CLASS_ID(CMove, Type, 3) DEFINE_CLASS_ID(SafePointScalarObject, Type, 4) @@ -727,6 +729,7 @@ DEFINE_CLASS_QUERY(Catch) DEFINE_CLASS_QUERY(CatchProj) DEFINE_CLASS_QUERY(CheckCastPP) + DEFINE_CLASS_QUERY(CastII) DEFINE_CLASS_QUERY(ConstraintCast) DEFINE_CLASS_QUERY(ClearArray) DEFINE_CLASS_QUERY(CMove) diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/parse2.cpp --- a/src/share/vm/opto/parse2.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/parse2.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -154,7 +154,9 @@ // Check for always knowing you are throwing a range-check exception if (stopped()) return top(); - Node* ptr = array_element_address(ary, idx, type, sizetype); + // Make array address computation control dependent to prevent it + // from floating above the range check during loop optimizations. + Node* ptr = array_element_address(ary, idx, type, sizetype, control()); if (result2 != NULL) *result2 = elemtype; @@ -457,9 +459,12 @@ #ifdef _LP64 // Clean the 32-bit int into a real 64-bit offset. // Otherwise, the jint value 0 might turn into an offset of 0x0800000000. - const TypeLong* lkeytype = TypeLong::make(CONST64(0), num_cases-1, Type::WidenMin); - key_val = _gvn.transform( new (C) ConvI2LNode(key_val, lkeytype) ); + const TypeInt* ikeytype = TypeInt::make(0, num_cases-1, Type::WidenMin); + // Make I2L conversion control dependent to prevent it from + // floating above the range check during loop optimizations. + key_val = C->constrained_convI2L(&_gvn, key_val, ikeytype, control()); #endif + // Shift the value by wordsize so we have an index into the table, rather // than a switch value Node *shiftWord = _gvn.MakeConX(wordSize); diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/phaseX.cpp --- a/src/share/vm/opto/phaseX.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/phaseX.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1339,6 +1339,10 @@ if (dead->is_expensive()) { C->remove_expensive_node(dead); } + CastIINode* cast = dead->isa_CastII(); + if (cast != NULL && cast->has_range_check()) { + C->remove_range_check_cast(cast); + } } } // while (_stack.is_nonempty()) } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/opto/superword.cpp --- a/src/share/vm/opto/superword.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/opto/superword.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -2388,6 +2388,11 @@ return true; } } else if (opc == Op_ConvI2L) { + if (n->in(1)->Opcode() == Op_CastII && + n->in(1)->as_CastII()->has_range_check()) { + // Skip range check dependent CastII nodes + n = n->in(1); + } if (scaled_iv_plus_offset(n->in(1))) { return true; } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/prims/jvmtiEnv.cpp --- a/src/share/vm/prims/jvmtiEnv.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/prims/jvmtiEnv.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -946,7 +946,7 @@ return JVMTI_ERROR_INVALID_THREAD; Handle thread_obj(current_thread, thread_oop); - typeArrayHandle name; + Handle name; ThreadPriority priority; Handle thread_group; Handle context_class_loader; @@ -954,7 +954,7 @@ { MutexLocker mu(Threads_lock); - name = typeArrayHandle(current_thread, java_lang_Thread::name(thread_obj())); + name = Handle(current_thread, java_lang_Thread::name(thread_obj())); priority = java_lang_Thread::priority(thread_obj()); thread_group = Handle(current_thread, java_lang_Thread::threadGroup(thread_obj())); is_daemon = java_lang_Thread::is_daemon(thread_obj()); @@ -965,7 +965,7 @@ { const char *n; if (name() != NULL) { - n = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + n = java_lang_String::as_utf8_string(name()); } else { n = UNICODE::as_utf8(NULL, 0); } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/prims/jvmtiTrace.cpp --- a/src/share/vm/prims/jvmtiTrace.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/prims/jvmtiTrace.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 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 @@ -266,11 +266,11 @@ if (threadObj == NULL) { return "NULL"; } - typeArrayOop name = java_lang_Thread::name(threadObj); + oop name = java_lang_Thread::name(threadObj); if (name == NULL) { return ""; } - return UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + return java_lang_String::as_utf8_string(name); } diff -r 2681e95121ac -r d992b4af24c7 src/share/vm/runtime/thread.cpp --- a/src/share/vm/runtime/thread.cpp Mon Jan 25 15:46:56 2016 -0800 +++ b/src/share/vm/runtime/thread.cpp Fri Feb 05 13:53:16 2016 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -2926,13 +2926,13 @@ const char* name_str; oop thread_obj = threadObj(); if (thread_obj != NULL) { - typeArrayOop name = java_lang_Thread::name(thread_obj); + oop name = java_lang_Thread::name(thread_obj); if (name != NULL) { if (buf == NULL) { - name_str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length()); + name_str = java_lang_String::as_utf8_string(name); } else { - name_str = UNICODE::as_utf8((jchar*) name->base(T_CHAR), name->length(), buf, buflen); + name_str = java_lang_String::as_utf8_string(name, buf, buflen); } } else if (is_attaching_via_jni()) { // workaround for 6412693 - see 6404306 diff -r 2681e95121ac -r d992b4af24c7 test/compiler/loopopts/CountedLoopProblem.java --- a/test/compiler/loopopts/CountedLoopProblem.java Mon Jan 25 15:46:56 2016 -0800 +++ b/test/compiler/loopopts/CountedLoopProblem.java Fri Feb 05 13:53:16 2016 +0000 @@ -36,18 +36,22 @@ 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 -r 2681e95121ac -r d992b4af24c7 test/compiler/loopopts/TestLoopPeeling.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/loopopts/TestLoopPeeling.java Fri Feb 05 13:53:16 2016 +0000 @@ -0,0 +1,100 @@ +/* + * 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. + */ + +/* + * @test + * @bug 8078262 + * @summary Tests correct dominator information after loop peeling. + * @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestLoopPeeling::test* TestLoopPeeling + */ +public class TestLoopPeeling { + + public int[] array = new int[100]; + + public static void main(String args[]) { + TestLoopPeeling test = new TestLoopPeeling(); + try { + test.testArrayAccess(0, 1); + test.testArrayAllocation(0, 1); + } catch (Exception e) { + // Ignore exceptions + } + } + + public void testArrayAccess(int index, int inc) { + int storeIndex = -1; + + for (; index < 10; index += inc) { + // This loop invariant check triggers loop peeling because it can + // be moved out of the loop (see 'IdealLoopTree::policy_peeling'). + if (inc == 42) return; + + // This loop variant usage of LShiftL( ConvI2L( Phi(storeIndex) ) ) + // prevents the split if optimization that would otherwise clone the + // LShiftL and ConvI2L nodes and assign them to their corresponding array + // address computation (see 'PhaseIdealLoop::split_if_with_blocks_post'). + if (storeIndex > 0 && array[storeIndex] == 42) return; + + if (index == 42) { + // This store and the corresponding range check are moved out of the + // loop and both used after old loop and the peeled iteration exit. + // For the peeled iteration, storeIndex is always -1 and the ConvI2L + // is replaced by TOP. However, the range check is not folded because + // we don't do the split if optimization in PhaseIdealLoop2. + // As a result, we have a (dead) control path from the peeled iteration + // to the StoreI but the data path is removed. + array[storeIndex] = 1; + return; + } + + storeIndex++; + } + } + + public byte[] testArrayAllocation(int index, int inc) { + int allocationCount = -1; + byte[] result; + + for (; index < 10; index += inc) { + // This loop invariant check triggers loop peeling because it can + // be moved out of the loop (see 'IdealLoopTree::policy_peeling'). + if (inc == 42) return null; + + if (index == 42) { + // This allocation and the corresponding size check are moved out of the + // loop and both used after old loop and the peeled iteration exit. + // For the peeled iteration, allocationCount is always -1 and the ConvI2L + // is replaced by TOP. However, the size check is not folded because + // we don't do the split if optimization in PhaseIdealLoop2. + // As a result, we have a (dead) control path from the peeled iteration + // to the allocation but the data path is removed. + result = new byte[allocationCount]; + return result; + } + + allocationCount++; + } + return null; + } +} + diff -r 2681e95121ac -r d992b4af24c7 test/stress/gc/TestStressRSetCoarsening.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/stress/gc/TestStressRSetCoarsening.java Fri Feb 05 13:53:16 2016 +0000 @@ -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); + } +} +