From 75ce836b542be1337553658aebeb7f6db235c28b Mon Sep 17 00:00:00 2001 From: johnc Date: Wed, 6 Feb 2013 14:50:37 -0800 Subject: 7052429: G1: Avoid unnecessary scanning of humongous regions during concurrent marking Summary: Skip unnecessary scanning of bitmap for unmarked humongous objects/regions. Reviewed-by: jwilhelm, johnc Contributed-by: Tao Mao --- .../vm/gc_implementation/g1/concurrentMark.cpp | 22 +++++++++++++++++----- src/share/vm/runtime/globals.hpp | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/src/share/vm/gc_implementation/g1/concurrentMark.cpp index 28c733c15..810e3c27a 100644 --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp @@ -4066,11 +4066,23 @@ void CMTask::do_marking_step(double time_target_ms, _worker_id, _finger, _region_limit, _curr_region); } - // Let's iterate over the bitmap of the part of the - // region that is left. - if (mr.is_empty() || _nextMarkBitMap->iterate(&bitmap_closure, mr)) { - // We successfully completed iterating over the region. Now, - // let's give up the region. + HeapRegion* hr = _g1h->heap_region_containing(mr.start()); + assert(!hr->isHumongous() || mr.start() == hr->bottom(), + "the start of HeapRegion and MemRegion should be consistent for humongous regions"); + + // The special case of the bitmap of a humongous region with its first + // bit NOT marked should be avoided from (wasteful) iterating. + // Note that the alternative case of the bitmap of a humongous region + // with its first bit marked is handled properly in the iterate() routine. + // Then, let's iterate over the bitmap of the part of the region that is + // left. + // If the iteration is successful, give up the region. + // Also note that the case of the bitmap of a humongous region with its + // first bit NOT marked is considered "successful", leveraging the fact + // that the entire bitmap consists of all 0's in such case. + if (mr.is_empty() || + (hr != NULL && hr->isHumongous() && !_nextMarkBitMap->isMarked(mr.start())) || + _nextMarkBitMap->iterate(&bitmap_closure, mr)) { giveup_current_region(); regular_clock_call(); } else { diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index 7db81d805..f6c108986 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1816,7 +1816,7 @@ class CommandLineFlags { \ product(uintx, InitiatingHeapOccupancyPercent, 45, \ "Percentage of the (entire) heap occupancy to start a " \ - "concurrent GC cycle. It us used by GCs that trigger a " \ + "concurrent GC cycle. It is used by GCs that trigger a " \ "concurrent GC cycle based on the occupancy of the entire heap, " \ "not just one of the generations (e.g., G1). A value of 0 " \ "denotes 'do constant GC cycles'.") \ -- cgit v1.2.3 From df8af747e4eab84008b4820a748baeab456187c5 Mon Sep 17 00:00:00 2001 From: jwilhelm Date: Thu, 7 Feb 2013 15:51:25 +0100 Subject: 8006432: Ratio flags should be unsigned Summary: Flags changed to be of uintx type Reviewed-by: johnc, tamao --- src/cpu/zero/vm/shark_globals_zero.hpp | 4 ++-- src/os_cpu/bsd_x86/vm/globals_bsd_x86.hpp | 4 ++-- .../concurrentMarkSweep/concurrentMarkSweepGeneration.cpp | 4 ++-- .../concurrentMarkSweep/concurrentMarkSweepGeneration.hpp | 4 ++-- src/share/vm/gc_implementation/g1/g1_globals.hpp | 3 --- src/share/vm/runtime/arguments.cpp | 4 ++-- src/share/vm/runtime/globals.hpp | 8 ++++---- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/cpu/zero/vm/shark_globals_zero.hpp b/src/cpu/zero/vm/shark_globals_zero.hpp index dae724900..1d1714376 100644 --- a/src/cpu/zero/vm/shark_globals_zero.hpp +++ b/src/cpu/zero/vm/shark_globals_zero.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * Copyright 2008, 2009, 2010 Red Hat, Inc. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -51,7 +51,7 @@ define_pd_global(intx, Tier4BackEdgeThreshold, 100000); define_pd_global(intx, OnStackReplacePercentage, 933 ); define_pd_global(intx, FreqInlineSize, 325 ); define_pd_global(intx, InlineSmallCode, 1000 ); -define_pd_global(intx, NewRatio, 12 ); +define_pd_global(uintx, NewRatio, 12 ); define_pd_global(intx, NewSizeThreadIncrease, 4*K ); define_pd_global(intx, InitialCodeCacheSize, 160*K); define_pd_global(intx, ReservedCodeCacheSize, 32*M ); diff --git a/src/os_cpu/bsd_x86/vm/globals_bsd_x86.hpp b/src/os_cpu/bsd_x86/vm/globals_bsd_x86.hpp index 3a8d42ab6..1144115bc 100644 --- a/src/os_cpu/bsd_x86/vm/globals_bsd_x86.hpp +++ b/src/os_cpu/bsd_x86/vm/globals_bsd_x86.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ define_pd_global(intx, VMThreadStackSize, 512); #endif // AMD64 define_pd_global(intx, CompilerThreadStackSize, 0); -define_pd_global(intx, SurvivorRatio, 8); +define_pd_global(uintx, SurvivorRatio, 8); define_pd_global(uintx, JVMInvokeMethodSlack, 8192); diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp index 6c1de182b..a9b2714c2 100644 --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp @@ -274,8 +274,8 @@ ConcurrentMarkSweepGeneration::ConcurrentMarkSweepGeneration( // end of a collection, we let CMSTriggerRatio of the (purported) free // space be allocated before initiating a new collection cycle. // -void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, intx tr) { - assert(io <= 100 && tr >= 0 && tr <= 100, "Check the arguments"); +void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, uintx tr) { + assert(io <= 100 && tr <= 100, "Check the arguments"); if (io >= 0) { _initiating_occupancy = (double)io / 100.0; } else { diff --git a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp index abc9be62f..b688be735 100644 --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1093,7 +1093,7 @@ class ConcurrentMarkSweepGeneration: public CardGeneration { // getter and initializer for _initiating_occupancy field. double initiating_occupancy() const { return _initiating_occupancy; } - void init_initiating_occupancy(intx io, intx tr); + void init_initiating_occupancy(intx io, uintx tr); public: ConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size, diff --git a/src/share/vm/gc_implementation/g1/g1_globals.hpp b/src/share/vm/gc_implementation/g1/g1_globals.hpp index d362956ea..1111cecfa 100644 --- a/src/share/vm/gc_implementation/g1/g1_globals.hpp +++ b/src/share/vm/gc_implementation/g1/g1_globals.hpp @@ -101,9 +101,6 @@ "to-space, we will allow regions whose survival rate is up to " \ "S + (1 - S)*X, where X is this parameter (as a fraction.)") \ \ - develop(intx, G1InitYoungSurvRatio, 50, \ - "Expected Survival Rate for newly allocated bytes") \ - \ develop(bool, G1SATBPrintStubs, false, \ "If true, print generated stubs for the SATB barrier") \ \ diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp index 9b5d7f912..8250ddb37 100644 --- a/src/share/vm/runtime/arguments.cpp +++ b/src/share/vm/runtime/arguments.cpp @@ -1239,7 +1239,7 @@ void Arguments::set_cms_and_parnew_gc_flags() { // prefer minuscule survivor spaces so as not to waste // space for (non-existent) survivors if (FLAG_IS_DEFAULT(SurvivorRatio) && MaxTenuringThreshold == 0) { - FLAG_SET_ERGO(intx, SurvivorRatio, MAX2((intx)1024, SurvivorRatio)); + FLAG_SET_ERGO(uintx, SurvivorRatio, MAX2((uintx)1024, SurvivorRatio)); } // If OldPLABSize is set and CMSParPromoteBlocksToClaim is not, // set CMSParPromoteBlocksToClaim equal to OldPLABSize. @@ -1881,7 +1881,7 @@ bool Arguments::check_vm_args_consistency() { if (UseParallelOldGC && ParallelOldGCSplitALot) { // Settings to encourage splitting. if (!FLAG_IS_CMDLINE(NewRatio)) { - FLAG_SET_CMDLINE(intx, NewRatio, 2); + FLAG_SET_CMDLINE(uintx, NewRatio, 2); } if (!FLAG_IS_CMDLINE(ScavengeBeforeFullGC)) { FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index f6c108986..f3b9523c7 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -1802,7 +1802,7 @@ class CommandLineFlags { product(bool, ParallelRefProcBalancingEnabled, true, \ "Enable balancing of reference processing queues") \ \ - product(intx, CMSTriggerRatio, 80, \ + product(uintx, CMSTriggerRatio, 80, \ "Percentage of MinHeapFreeRatio in CMS generation that is " \ "allocated before a CMS collection cycle commences") \ \ @@ -2977,10 +2977,10 @@ class CommandLineFlags { product(uintx, TLABWasteIncrement, 4, \ "Increment allowed waste at slow allocation") \ \ - product(intx, SurvivorRatio, 8, \ + product(uintx, SurvivorRatio, 8, \ "Ratio of eden/survivor space size") \ \ - product(intx, NewRatio, 2, \ + product(uintx, NewRatio, 2, \ "Ratio of new/old generation sizes") \ \ product_pd(uintx, NewSizeThreadIncrease, \ @@ -3031,7 +3031,7 @@ class CommandLineFlags { product(uintx, InitialTenuringThreshold, 7, \ "Initial value for tenuring threshold") \ \ - product(intx, TargetSurvivorRatio, 50, \ + product(uintx, TargetSurvivorRatio, 50, \ "Desired percentage of survivor space used after scavenge") \ \ product(uintx, MarkSweepDeadRatio, 5, \ -- cgit v1.2.3 From ddfb804e517e180cc09d5a6a74859ef0eafd9b57 Mon Sep 17 00:00:00 2001 From: brutisso Date: Sun, 10 Feb 2013 21:15:16 +0100 Subject: 8002144: G1: large number of evacuation failures may lead to large c heap memory usage Summary: Use Stack<> instead of GrowableArray to keep track of preserved marks. Also reviewed by vitalyd@gmail.com. Reviewed-by: johnc, jcoomes --HG-- extra : rebase_source : a00d28334ea9e9b55cdf9c64882e4f5e73d40082 --- .../vm/gc_implementation/g1/g1CollectedHeap.cpp | 35 +++++++--------------- .../vm/gc_implementation/g1/g1CollectedHeap.hpp | 8 ++--- 2 files changed, 14 insertions(+), 29 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp index cc11754c9..646479790 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp @@ -1893,7 +1893,6 @@ G1CollectedHeap::G1CollectedHeap(G1CollectorPolicy* policy_) : _ref_processor_stw(NULL), _process_strong_tasks(new SubTasksDone(G1H_PS_NumElements)), _bot_shared(NULL), - _objs_with_preserved_marks(NULL), _preserved_marks_of_objs(NULL), _evac_failure_scan_stack(NULL) , _mark_in_progress(false), _cg1r(NULL), _summary_bytes_used(0), @@ -4215,22 +4214,15 @@ void G1CollectedHeap::remove_self_forwarding_pointers() { assert(check_cset_heap_region_claim_values(HeapRegion::InitialClaimValue), "sanity"); // Now restore saved marks, if any. - if (_objs_with_preserved_marks != NULL) { - assert(_preserved_marks_of_objs != NULL, "Both or none."); - guarantee(_objs_with_preserved_marks->length() == - _preserved_marks_of_objs->length(), "Both or none."); - for (int i = 0; i < _objs_with_preserved_marks->length(); i++) { - oop obj = _objs_with_preserved_marks->at(i); - markOop m = _preserved_marks_of_objs->at(i); - obj->set_mark(m); - } - - // Delete the preserved marks growable arrays (allocated on the C heap). - delete _objs_with_preserved_marks; - delete _preserved_marks_of_objs; - _objs_with_preserved_marks = NULL; - _preserved_marks_of_objs = NULL; + assert(_objs_with_preserved_marks.size() == + _preserved_marks_of_objs.size(), "Both or none."); + while (!_objs_with_preserved_marks.is_empty()) { + oop obj = _objs_with_preserved_marks.pop(); + markOop m = _preserved_marks_of_objs.pop(); + obj->set_mark(m); } + _objs_with_preserved_marks.clear(true); + _preserved_marks_of_objs.clear(true); } void G1CollectedHeap::push_on_evac_failure_scan_stack(oop obj) { @@ -4313,15 +4305,8 @@ void G1CollectedHeap::preserve_mark_if_necessary(oop obj, markOop m) { // We want to call the "for_promotion_failure" version only in the // case of a promotion failure. if (m->must_be_preserved_for_promotion_failure(obj)) { - if (_objs_with_preserved_marks == NULL) { - assert(_preserved_marks_of_objs == NULL, "Both or none."); - _objs_with_preserved_marks = - new (ResourceObj::C_HEAP, mtGC) GrowableArray(40, true); - _preserved_marks_of_objs = - new (ResourceObj::C_HEAP, mtGC) GrowableArray(40, true); - } - _objs_with_preserved_marks->push(obj); - _preserved_marks_of_objs->push(m); + _objs_with_preserved_marks.push(obj); + _preserved_marks_of_objs.push(m); } } diff --git a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp index 816d4f253..7dc5bd830 100644 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.hpp @@ -37,6 +37,7 @@ #include "memory/barrierSet.hpp" #include "memory/memRegion.hpp" #include "memory/sharedHeap.hpp" +#include "utilities/stack.hpp" // A "G1CollectedHeap" is an implementation of a java heap for HotSpot. // It uses the "Garbage First" heap organization and algorithm, which @@ -877,10 +878,9 @@ protected: // forwarding pointers to themselves. Reset them. void remove_self_forwarding_pointers(); - // When one is non-null, so is the other. Together, they each pair is - // an object with a preserved mark, and its mark value. - GrowableArray* _objs_with_preserved_marks; - GrowableArray* _preserved_marks_of_objs; + // Together, these store an object with a preserved mark, and its mark value. + Stack _objs_with_preserved_marks; + Stack _preserved_marks_of_objs; // Preserve the mark of "obj", if necessary, in preparation for its mark // word being overwritten with a self-forwarding-pointer. -- cgit v1.2.3 From 72b4e6107b4b642a47050d982cc5de476b95104c Mon Sep 17 00:00:00 2001 From: johnc Date: Mon, 11 Feb 2013 15:24:48 -0800 Subject: 8007772: G1: assert(!hr->isHumongous() || mr.start() == hr->bottom()) failed: the start of HeapRegion and MemRegion should be consistent for humongous regions Summary: In do_marking_step(), we should always give up current region after scanning the object, if the region is humongous. Reviewed-by: brutisso, jwilhelm, tamao --HG-- extra : rebase_source : a3e6db9cfbfea67f60d19915aa316db407a6f201 --- .../vm/gc_implementation/g1/concurrentMark.cpp | 43 +++++++++++++--------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/share/vm/gc_implementation/g1/concurrentMark.cpp b/src/share/vm/gc_implementation/g1/concurrentMark.cpp index 810e3c27a..cc2a611f0 100644 --- a/src/share/vm/gc_implementation/g1/concurrentMark.cpp +++ b/src/share/vm/gc_implementation/g1/concurrentMark.cpp @@ -4062,27 +4062,36 @@ void CMTask::do_marking_step(double time_target_ms, if (_cm->verbose_low()) { gclog_or_tty->print_cr("[%u] we're scanning part " "["PTR_FORMAT", "PTR_FORMAT") " - "of region "PTR_FORMAT, - _worker_id, _finger, _region_limit, _curr_region); + "of region "HR_FORMAT, + _worker_id, _finger, _region_limit, + HR_FORMAT_PARAMS(_curr_region)); } - HeapRegion* hr = _g1h->heap_region_containing(mr.start()); - assert(!hr->isHumongous() || mr.start() == hr->bottom(), - "the start of HeapRegion and MemRegion should be consistent for humongous regions"); + assert(!_curr_region->isHumongous() || mr.start() == _curr_region->bottom(), + "humongous regions should go around loop once only"); - // The special case of the bitmap of a humongous region with its first - // bit NOT marked should be avoided from (wasteful) iterating. - // Note that the alternative case of the bitmap of a humongous region - // with its first bit marked is handled properly in the iterate() routine. - // Then, let's iterate over the bitmap of the part of the region that is - // left. + // Some special cases: + // If the memory region is empty, we can just give up the region. + // If the current region is humongous then we only need to check + // the bitmap for the bit associated with the start of the object, + // scan the object if it's live, and give up the region. + // Otherwise, let's iterate over the bitmap of the part of the region + // that is left. // If the iteration is successful, give up the region. - // Also note that the case of the bitmap of a humongous region with its - // first bit NOT marked is considered "successful", leveraging the fact - // that the entire bitmap consists of all 0's in such case. - if (mr.is_empty() || - (hr != NULL && hr->isHumongous() && !_nextMarkBitMap->isMarked(mr.start())) || - _nextMarkBitMap->iterate(&bitmap_closure, mr)) { + if (mr.is_empty()) { + giveup_current_region(); + regular_clock_call(); + } else if (_curr_region->isHumongous() && mr.start() == _curr_region->bottom()) { + if (_nextMarkBitMap->isMarked(mr.start())) { + // The object is marked - apply the closure + BitMap::idx_t offset = _nextMarkBitMap->heapWordToOffset(mr.start()); + bitmap_closure.do_bit(offset); + } + // Even if this task aborted while scanning the humongous object + // we can (and should) give up the current region. + giveup_current_region(); + regular_clock_call(); + } else if (_nextMarkBitMap->iterate(&bitmap_closure, mr)) { giveup_current_region(); regular_clock_call(); } else { -- cgit v1.2.3 From 477589038f09d134d6c02e19166dbdee073da93e Mon Sep 17 00:00:00 2001 From: jmasa Date: Wed, 23 Jan 2013 19:08:04 -0800 Subject: 8005452: NPG: Create new flags for Metaspace resizing policy Reviewed-by: johnc, jwilhelm, coleenp, stefank --HG-- extra : rebase_source : 73019f133f2e71b847417fab62267d77b57fe23f --- src/share/vm/memory/metaspace.cpp | 10 +++++----- src/share/vm/runtime/arguments.cpp | 18 ++++++++++++++++++ src/share/vm/runtime/globals.hpp | 10 ++++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/share/vm/memory/metaspace.cpp b/src/share/vm/memory/metaspace.cpp index c629e175f..b0891150d 100644 --- a/src/share/vm/memory/metaspace.cpp +++ b/src/share/vm/memory/metaspace.cpp @@ -1064,11 +1064,11 @@ bool VirtualSpaceList::contains(const void *ptr) { // // After the GC the compute_new_size() for MetaspaceGC is called to // resize the capacity of the metaspaces. The current implementation -// is based on the flags MinHeapFreeRatio and MaxHeapFreeRatio used +// is based on the flags MinMetaspaceFreeRatio and MaxHeapFreeRatio used // to resize the Java heap by some GC's. New flags can be implemented // if really needed. MinHeapFreeRatio is used to calculate how much // free space is desirable in the metaspace capacity to decide how much -// to increase the HWM. MaxHeapFreeRatio is used to decide how much +// to increase the HWM. MaxMetaspaceFreeRatio is used to decide how much // free space is desirable in the metaspace capacity before decreasing // the HWM. @@ -1166,7 +1166,7 @@ void MetaspaceGC::compute_new_size() { size_t capacity_until_GC = vsl->capacity_bytes_sum(); size_t free_after_gc = capacity_until_GC - used_after_gc; - const double minimum_free_percentage = MinHeapFreeRatio / 100.0; + const double minimum_free_percentage = MinMetaspaceFreeRatio / 100.0; const double maximum_used_percentage = 1.0 - minimum_free_percentage; const double min_tmp = used_after_gc / maximum_used_percentage; @@ -1232,8 +1232,8 @@ void MetaspaceGC::compute_new_size() { max_shrink_words)); // Should shrinking be considered? - if (MaxHeapFreeRatio < 100) { - const double maximum_free_percentage = MaxHeapFreeRatio / 100.0; + if (MaxMetaspaceFreeRatio < 100) { + const double maximum_free_percentage = MaxMetaspaceFreeRatio / 100.0; const double minimum_used_percentage = 1.0 - maximum_free_percentage; const double max_tmp = used_after_gc / minimum_used_percentage; size_t maximum_desired_capacity = (size_t)MIN2(max_tmp, double(max_uintx)); diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp index 87c31383b..0fc125227 100644 --- a/src/share/vm/runtime/arguments.cpp +++ b/src/share/vm/runtime/arguments.cpp @@ -1896,6 +1896,24 @@ bool Arguments::check_vm_args_consistency() { // Keeping the heap 100% free is hard ;-) so limit it to 99%. MinHeapFreeRatio = MIN2(MinHeapFreeRatio, (uintx) 99); + // Min/MaxMetaspaceFreeRatio + status = status && verify_percentage(MinMetaspaceFreeRatio, "MinMetaspaceFreeRatio"); + status = status && verify_percentage(MaxMetaspaceFreeRatio, "MaxMetaspaceFreeRatio"); + + if (MinMetaspaceFreeRatio > MaxMetaspaceFreeRatio) { + jio_fprintf(defaultStream::error_stream(), + "MinMetaspaceFreeRatio (%s" UINTX_FORMAT ") must be less than or " + "equal to MaxMetaspaceFreeRatio (%s" UINTX_FORMAT ")\n", + FLAG_IS_DEFAULT(MinMetaspaceFreeRatio) ? "Default: " : "", + MinMetaspaceFreeRatio, + FLAG_IS_DEFAULT(MaxMetaspaceFreeRatio) ? "Default: " : "", + MaxMetaspaceFreeRatio); + status = false; + } + + // Trying to keep 100% free is not practical + MinMetaspaceFreeRatio = MIN2(MinMetaspaceFreeRatio, (uintx) 99); + if (FullGCALot && FLAG_IS_DEFAULT(MarkSweepAlwaysCompactCount)) { MarkSweepAlwaysCompactCount = 1; // Move objects every gc. } diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index f3b9523c7..8a0be8f8a 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -3010,10 +3010,16 @@ class CommandLineFlags { "Min change in heap space due to GC (in bytes)") \ \ product(uintx, MinMetaspaceExpansion, ScaleForWordSize(256*K), \ - "Min expansion of permanent heap (in bytes)") \ + "Min expansion of Metaspace (in bytes)") \ + \ + product(uintx, MinMetaspaceFreeRatio, 40, \ + "Min percentage of Metaspace free after GC to avoid expansion") \ + \ + product(uintx, MaxMetaspaceFreeRatio, 70, \ + "Max percentage of Metaspace free after GC to avoid shrinking") \ \ product(uintx, MaxMetaspaceExpansion, ScaleForWordSize(4*M), \ - "Max expansion of permanent heap without full GC (in bytes)") \ + "Max expansion of Metaspace without full GC (in bytes)") \ \ product(intx, QueuedAllocationWarningCount, 0, \ "Number of times an allocation that queues behind a GC " \ -- cgit v1.2.3 From 9353d9d4c6c026b96fe23b51ed0918dedbd3cbe2 Mon Sep 17 00:00:00 2001 From: dcubed Date: Wed, 6 Feb 2013 14:31:37 -0800 Subject: 7182152: Instrumentation hot swap test incorrect monitor count Summary: Add/refine new tracing support using -XX:TraceRedefineClasses=16384. Reviewed-by: coleenp, acorn, sspitsyn --- src/share/vm/oops/cpCache.cpp | 50 +++++-- src/share/vm/oops/cpCache.hpp | 15 +- src/share/vm/oops/klassVtable.cpp | 102 ++++++++----- src/share/vm/oops/klassVtable.hpp | 20 ++- src/share/vm/oops/method.cpp | 9 +- src/share/vm/oops/method.hpp | 6 +- src/share/vm/prims/jvmtiRedefineClasses.cpp | 176 ++++++++++++++--------- src/share/vm/prims/jvmtiRedefineClasses.hpp | 6 +- src/share/vm/prims/jvmtiRedefineClassesTrace.hpp | 11 +- src/share/vm/utilities/accessFlags.cpp | 6 +- src/share/vm/utilities/accessFlags.hpp | 6 +- 11 files changed, 254 insertions(+), 153 deletions(-) diff --git a/src/share/vm/oops/cpCache.cpp b/src/share/vm/oops/cpCache.cpp index 06c70cd02..f695c84e3 100644 --- a/src/share/vm/oops/cpCache.cpp +++ b/src/share/vm/oops/cpCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -401,8 +401,9 @@ oop ConstantPoolCacheEntry::method_type_if_resolved(constantPoolHandle cpool) { } +#if INCLUDE_JVMTI // RedefineClasses() API support: -// If this constantPoolCacheEntry refers to old_method then update it +// If this ConstantPoolCacheEntry refers to old_method then update it // to refer to new_method. bool ConstantPoolCacheEntry::adjust_method_entry(Method* old_method, Method* new_method, bool * trace_name_printed) { @@ -460,16 +461,24 @@ bool ConstantPoolCacheEntry::adjust_method_entry(Method* old_method, return false; } -#ifndef PRODUCT -bool ConstantPoolCacheEntry::check_no_old_entries() { +// a constant pool cache entry should never contain old or obsolete methods +bool ConstantPoolCacheEntry::check_no_old_or_obsolete_entries() { if (is_vfinal()) { + // virtual and final so _f2 contains method ptr instead of vtable index Metadata* f2 = (Metadata*)_f2; - return (f2->is_valid() && f2->is_method() && !((Method*)f2)->is_old()); - } else { - return (_f1 == NULL || (_f1->is_valid() && _f1->is_method() && !((Method*)_f1)->is_old())); + // Return false if _f2 refers to an old or an obsolete method. + // _f2 == NULL || !_f2->is_method() are just as unexpected here. + return (f2 != NULL NOT_PRODUCT(&& f2->is_valid()) && f2->is_method() && + !((Method*)f2)->is_old() && !((Method*)f2)->is_obsolete()); + } else if (_f1 == NULL || + (NOT_PRODUCT(_f1->is_valid() &&) !_f1->is_method())) { + // _f1 == NULL || !_f1->is_method() are OK here + return true; } + // return false if _f1 refers to an old or an obsolete method + return (NOT_PRODUCT(_f1->is_valid() &&) _f1->is_method() && + !((Method*)_f1)->is_old() && !((Method*)_f1)->is_obsolete()); } -#endif bool ConstantPoolCacheEntry::is_interesting_method_entry(Klass* k) { if (!is_method_entry()) { @@ -502,13 +511,15 @@ bool ConstantPoolCacheEntry::is_interesting_method_entry(Klass* k) { // the method is in the interesting class so the entry is interesting return true; } +#endif // INCLUDE_JVMTI void ConstantPoolCacheEntry::print(outputStream* st, int index) const { // print separator if (index == 0) st->print_cr(" -------------"); // print entry st->print("%3d ("PTR_FORMAT") ", index, (intptr_t)this); - st->print_cr("[%02x|%02x|%5d]", bytecode_2(), bytecode_1(), constant_pool_index()); + st->print_cr("[%02x|%02x|%5d]", bytecode_2(), bytecode_1(), + constant_pool_index()); st->print_cr(" [ "PTR_FORMAT"]", (intptr_t)_f1); st->print_cr(" [ "PTR_FORMAT"]", (intptr_t)_f2); st->print_cr(" [ "PTR_FORMAT"]", (intptr_t)_flags); @@ -552,8 +563,9 @@ void ConstantPoolCache::initialize(intArray& inverse_index_map, intArray& invoke } } +#if INCLUDE_JVMTI // RedefineClasses() API support: -// If any entry of this constantPoolCache points to any of +// If any entry of this ConstantPoolCache points to any of // old_methods, replace it with the corresponding new_method. void ConstantPoolCache::adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed) { @@ -572,7 +584,7 @@ void ConstantPoolCache::adjust_method_entries(Method** old_methods, Method** new continue; } - // The constantPoolCache contains entries for several different + // The ConstantPoolCache contains entries for several different // things, but we only care about methods. In fact, we only care // about methods in the same class as the one that contains the // old_methods. At this point, we have an interesting entry. @@ -591,17 +603,25 @@ void ConstantPoolCache::adjust_method_entries(Method** old_methods, Method** new } } -#ifndef PRODUCT -bool ConstantPoolCache::check_no_old_entries() { +// the constant pool cache should never contain old or obsolete methods +bool ConstantPoolCache::check_no_old_or_obsolete_entries() { for (int i = 1; i < length(); i++) { if (entry_at(i)->is_interesting_method_entry(NULL) && - !entry_at(i)->check_no_old_entries()) { + !entry_at(i)->check_no_old_or_obsolete_entries()) { return false; } } return true; } -#endif // PRODUCT + +void ConstantPoolCache::dump_cache() { + for (int i = 1; i < length(); i++) { + if (entry_at(i)->is_interesting_method_entry(NULL)) { + entry_at(i)->print(tty, i); + } + } +} +#endif // INCLUDE_JVMTI // Printing diff --git a/src/share/vm/oops/cpCache.hpp b/src/share/vm/oops/cpCache.hpp index 90308e44a..c1e058d81 100644 --- a/src/share/vm/oops/cpCache.hpp +++ b/src/share/vm/oops/cpCache.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -337,16 +337,18 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC { static ByteSize f2_offset() { return byte_offset_of(ConstantPoolCacheEntry, _f2); } static ByteSize flags_offset() { return byte_offset_of(ConstantPoolCacheEntry, _flags); } +#if INCLUDE_JVMTI // RedefineClasses() API support: - // If this constantPoolCacheEntry refers to old_method then update it + // If this ConstantPoolCacheEntry refers to old_method then update it // to refer to new_method. // trace_name_printed is set to true if the current call has // printed the klass name so that other routines in the adjust_* // group don't print the klass name. bool adjust_method_entry(Method* old_method, Method* new_method, bool * trace_name_printed); - NOT_PRODUCT(bool check_no_old_entries();) + bool check_no_old_or_obsolete_entries(); bool is_interesting_method_entry(Klass* k); +#endif // INCLUDE_JVMTI // Debugging & Printing void print (outputStream* st, int index) const; @@ -423,15 +425,18 @@ class ConstantPoolCache: public MetaspaceObj { return (base_offset() + ConstantPoolCacheEntry::size_in_bytes() * index); } +#if INCLUDE_JVMTI // RedefineClasses() API support: - // If any entry of this constantPoolCache points to any of + // If any entry of this ConstantPoolCache points to any of // old_methods, replace it with the corresponding new_method. // trace_name_printed is set to true if the current call has // printed the klass name so that other routines in the adjust_* // group don't print the klass name. void adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed); - NOT_PRODUCT(bool check_no_old_entries();) + bool check_no_old_or_obsolete_entries(); + void dump_cache(); +#endif // INCLUDE_JVMTI // Deallocate - no fields to deallocate DEBUG_ONLY(bool on_stack() { return false; }) diff --git a/src/share/vm/oops/klassVtable.cpp b/src/share/vm/oops/klassVtable.cpp index f8e4d0223..43036e754 100644 --- a/src/share/vm/oops/klassVtable.cpp +++ b/src/share/vm/oops/klassVtable.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -610,6 +610,7 @@ void klassVtable::copy_vtable_to(vtableEntry* start) { Copy::disjoint_words((HeapWord*)table(), (HeapWord*)start, _length * vtableEntry::size()); } +#if INCLUDE_JVMTI void klassVtable::adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed) { // search the vtable for uses of either obsolete or EMCP methods @@ -638,11 +639,39 @@ void klassVtable::adjust_method_entries(Method** old_methods, Method** new_metho new_method->name()->as_C_string(), new_method->signature()->as_C_string())); } + // cannot 'break' here; see for-loop comment above. } } } } +// a vtable should never contain old or obsolete methods +bool klassVtable::check_no_old_or_obsolete_entries() { + for (int i = 0; i < length(); i++) { + Method* m = unchecked_method_at(i); + if (m != NULL && + (NOT_PRODUCT(!m->is_valid() ||) m->is_old() || m->is_obsolete())) { + return false; + } + } + return true; +} + +void klassVtable::dump_vtable() { + tty->print_cr("vtable dump --"); + for (int i = 0; i < length(); i++) { + Method* m = unchecked_method_at(i); + if (m != NULL) { + tty->print(" (%5d) ", i); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + } +} +#endif // INCLUDE_JVMTI + // CDS/RedefineClasses support - clear vtables so they can be reinitialized void klassVtable::clear_vtable() { for (int i = 0; i < _length; i++) table()[i].clear(); @@ -805,6 +834,7 @@ void klassItable::initialize_with_method(Method* m) { } } +#if INCLUDE_JVMTI void klassItable::adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed) { // search the itable for uses of either obsolete or EMCP methods @@ -833,13 +863,44 @@ void klassItable::adjust_method_entries(Method** old_methods, Method** new_metho new_method->name()->as_C_string(), new_method->signature()->as_C_string())); } - // Cannot break because there might be another entry for this method + // cannot 'break' here; see for-loop comment above. } ime++; } } } +// an itable should never contain old or obsolete methods +bool klassItable::check_no_old_or_obsolete_entries() { + itableMethodEntry* ime = method_entry(0); + for (int i = 0; i < _size_method_table; i++) { + Method* m = ime->method(); + if (m != NULL && + (NOT_PRODUCT(!m->is_valid() ||) m->is_old() || m->is_obsolete())) { + return false; + } + ime++; + } + return true; +} + +void klassItable::dump_itable() { + itableMethodEntry* ime = method_entry(0); + tty->print_cr("itable dump --"); + for (int i = 0; i < _size_method_table; i++) { + Method* m = ime->method(); + if (m != NULL) { + tty->print(" (%5d) ", i); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + ime++; + } +} +#endif // INCLUDE_JVMTI + // Setup class InterfaceVisiterClosure : public StackObj { @@ -1126,43 +1187,6 @@ void klassVtable::print_statistics() { tty->print_cr("%6d bytes total", total); } -bool klassVtable::check_no_old_entries() { - // Check that there really is no entry - for (int i = 0; i < length(); i++) { - Method* m = unchecked_method_at(i); - if (m != NULL) { - if (!m->is_valid() || m->is_old()) { - return false; - } - } - } - return true; -} - -void klassVtable::dump_vtable() { - tty->print_cr("vtable dump --"); - for (int i = 0; i < length(); i++) { - Method* m = unchecked_method_at(i); - if (m != NULL) { - tty->print(" (%5d) ", i); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - } -} - -bool klassItable::check_no_old_entries() { - itableMethodEntry* ime = method_entry(0); - for(int i = 0; i < _size_method_table; i++) { - Method* m = ime->method(); - if (m != NULL && (!m->is_valid() || m->is_old())) return false; - ime++; - } - return true; -} - int klassItable::_total_classes; // Total no. of classes with itables long klassItable::_total_size; // Total no. of bytes used for itables diff --git a/src/share/vm/oops/klassVtable.hpp b/src/share/vm/oops/klassVtable.hpp index c2c0c5cf1..01495e35d 100644 --- a/src/share/vm/oops/klassVtable.hpp +++ b/src/share/vm/oops/klassVtable.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,6 +90,7 @@ class klassVtable : public ResourceObj { Array* methods, AccessFlags class_flags, Handle classloader, Symbol* classname, Array* local_interfaces, TRAPS); +#if INCLUDE_JVMTI // RedefineClasses() API support: // If any entry of this vtable points to any of old_methods, // replace it with the corresponding new_method. @@ -98,17 +99,15 @@ class klassVtable : public ResourceObj { // group don't print the klass name. void adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed); + bool check_no_old_or_obsolete_entries(); + void dump_vtable(); +#endif // INCLUDE_JVMTI // Debugging code void print() PRODUCT_RETURN; void verify(outputStream* st, bool force = false); static void print_statistics() PRODUCT_RETURN; -#ifndef PRODUCT - bool check_no_old_entries(); - void dump_vtable(); -#endif - protected: friend class vtableEntry; private: @@ -275,6 +274,7 @@ class klassItable : public ResourceObj { // Updates void initialize_with_method(Method* m); +#if INCLUDE_JVMTI // RedefineClasses() API support: // if any entry of this itable points to any of old_methods, // replace it with the corresponding new_method. @@ -283,6 +283,9 @@ class klassItable : public ResourceObj { // group don't print the klass name. void adjust_method_entries(Method** old_methods, Method** new_methods, int methods_length, bool * trace_name_printed); + bool check_no_old_or_obsolete_entries(); + void dump_itable(); +#endif // INCLUDE_JVMTI // Setup of itable static int compute_itable_size(Array* transitive_interfaces); @@ -307,11 +310,6 @@ class klassItable : public ResourceObj { NOT_PRODUCT(static long _total_size;) // Total no. of bytes used for itables static void update_stats(int size) PRODUCT_RETURN NOT_PRODUCT({ _total_classes++; _total_size += size; }) - - public: -#ifndef PRODUCT - bool check_no_old_entries(); -#endif }; #endif // SHARE_VM_OOPS_KLASSVTABLE_HPP diff --git a/src/share/vm/oops/method.cpp b/src/share/vm/oops/method.cpp index c2e7993c0..5695ca53d 100644 --- a/src/share/vm/oops/method.cpp +++ b/src/share/vm/oops/method.cpp @@ -1386,9 +1386,9 @@ void Method::sort_methods(Array* methods, //----------------------------------------------------------------------------------- -// Non-product code +// Non-product code unless JVM/TI needs it -#ifndef PRODUCT +#if !defined(PRODUCT) || INCLUDE_JVMTI class SignatureTypePrinter : public SignatureTypeNames { private: outputStream* _st; @@ -1423,8 +1423,13 @@ void Method::print_name(outputStream* st) { sig.print_parameters(); st->print(")"); } +#endif // !PRODUCT || INCLUDE_JVMTI + +//----------------------------------------------------------------------------------- +// Non-product code +#ifndef PRODUCT void Method::print_codes_on(outputStream* st) const { print_codes_on(0, code_size(), st); } diff --git a/src/share/vm/oops/method.hpp b/src/share/vm/oops/method.hpp index 558b834e7..8ea62ac01 100644 --- a/src/share/vm/oops/method.hpp +++ b/src/share/vm/oops/method.hpp @@ -800,8 +800,12 @@ class Method : public Metadata { static bool has_unloaded_classes_in_signature(methodHandle m, TRAPS); // Printing - void print_short_name(outputStream* st = tty) /*PRODUCT_RETURN*/; // prints as klassname::methodname; Exposed so field engineers can debug VM + void print_short_name(outputStream* st = tty); // prints as klassname::methodname; Exposed so field engineers can debug VM +#if INCLUDE_JVMTI + void print_name(outputStream* st = tty); // prints as "virtual void foo(int)"; exposed for TraceRedefineClasses +#else void print_name(outputStream* st = tty) PRODUCT_RETURN; // prints as "virtual void foo(int)" +#endif // Helper routine used for method sorting static void sort_methods(Array* methods, diff --git a/src/share/vm/prims/jvmtiRedefineClasses.cpp b/src/share/vm/prims/jvmtiRedefineClasses.cpp index ce1ec56ef..8e67f08b0 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -154,8 +154,15 @@ void VM_RedefineClasses::doit() { // See jvmtiExport.hpp for detailed explanation. JvmtiExport::set_has_redefined_a_class(); -#ifdef ASSERT - SystemDictionary::classes_do(check_class, thread); +// check_class() is optionally called for product bits, but is +// always called for non-product bits. +#ifdef PRODUCT + if (RC_TRACE_ENABLED(0x00004000)) { +#endif + RC_TRACE_WITH_THREAD(0x00004000, thread, ("calling check_class")); + SystemDictionary::classes_do(check_class, thread); +#ifdef PRODUCT + } #endif } @@ -1564,9 +1571,9 @@ void VM_RedefineClasses::rewrite_cp_refs_in_method(methodHandle method, bcp, cp_index, new_index)); // Rewriter::rewrite_method() uses put_native_u2() in this // situation because it is reusing the constant pool index - // location for a native index into the constantPoolCache. + // location for a native index into the ConstantPoolCache. // Since we are updating the constant pool index prior to - // verification and constantPoolCache initialization, we + // verification and ConstantPoolCache initialization, we // need to keep the new index in Java byte order. Bytes::put_Java_u2(p, new_index); } @@ -3371,7 +3378,6 @@ void VM_RedefineClasses::increment_class_counter(InstanceKlass *ik, TRAPS) { } } -#ifndef PRODUCT void VM_RedefineClasses::check_class(Klass* k_oop, ClassLoaderData* initiating_loader, TRAPS) { @@ -3379,82 +3385,110 @@ void VM_RedefineClasses::check_class(Klass* k_oop, if (k->oop_is_instance()) { HandleMark hm(THREAD); InstanceKlass *ik = (InstanceKlass *) k; + bool no_old_methods = true; // be optimistic + ResourceMark rm(THREAD); - if (ik->vtable_length() > 0) { - ResourceMark rm(THREAD); - if (!ik->vtable()->check_no_old_entries()) { - tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name()); + // a vtable should never contain old or obsolete methods + if (ik->vtable_length() > 0 && + !ik->vtable()->check_no_old_or_obsolete_entries()) { + if (RC_TRACE_ENABLED(0x00004000)) { + RC_TRACE_WITH_THREAD(0x00004000, THREAD, + ("klassVtable::check_no_old_or_obsolete_entries failure" + " -- OLD or OBSOLETE method found -- class: %s", + ik->signature_name())); ik->vtable()->dump_vtable(); - assert(false, "OLD method found"); } - } - if (ik->itable_length() > 0) { - ResourceMark rm(THREAD); - if (!ik->itable()->check_no_old_entries()) { - tty->print_cr("klassItable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name()); - assert(false, "OLD method found"); + no_old_methods = false; + } + + // an itable should never contain old or obsolete methods + if (ik->itable_length() > 0 && + !ik->itable()->check_no_old_or_obsolete_entries()) { + if (RC_TRACE_ENABLED(0x00004000)) { + RC_TRACE_WITH_THREAD(0x00004000, THREAD, + ("klassItable::check_no_old_or_obsolete_entries failure" + " -- OLD or OBSOLETE method found -- class: %s", + ik->signature_name())); + ik->itable()->dump_itable(); } + no_old_methods = false; } - // Check that the constant pool cache has no deleted entries. + + // the constant pool cache should never contain old or obsolete methods if (ik->constants() != NULL && ik->constants()->cache() != NULL && - !ik->constants()->cache()->check_no_old_entries()) { - tty->print_cr("klassVtable::check_no_old_entries failure -- OLD method found -- class: %s", ik->signature_name()); - assert(false, "OLD method found"); + !ik->constants()->cache()->check_no_old_or_obsolete_entries()) { + if (RC_TRACE_ENABLED(0x00004000)) { + RC_TRACE_WITH_THREAD(0x00004000, THREAD, + ("cp-cache::check_no_old_or_obsolete_entries failure" + " -- OLD or OBSOLETE method found -- class: %s", + ik->signature_name())); + ik->constants()->cache()->dump_cache(); + } + no_old_methods = false; + } + + if (!no_old_methods) { + if (RC_TRACE_ENABLED(0x00004000)) { + dump_methods(); + } else { + tty->print_cr("INFO: use the '-XX:TraceRedefineClasses=16384' option " + "to see more info about the following guarantee() failure."); + } + guarantee(false, "OLD and/or OBSOLETE method(s) found"); } } } void VM_RedefineClasses::dump_methods() { - int j; - tty->print_cr("_old_methods --"); - for (j = 0; j < _old_methods->length(); ++j) { - Method* m = _old_methods->at(j); - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_new_methods --"); - for (j = 0; j < _new_methods->length(); ++j) { - Method* m = _new_methods->at(j); - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_matching_(old/new)_methods --"); - for (j = 0; j < _matching_methods_length; ++j) { - Method* m = _matching_old_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - m = _matching_new_methods[j]; - tty->print(" (%5d) ", m->vtable_index()); - m->access_flags().print_on(tty); - tty->cr(); - } - tty->print_cr("_deleted_methods --"); - for (j = 0; j < _deleted_methods_length; ++j) { - Method* m = _deleted_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } - tty->print_cr("_added_methods --"); - for (j = 0; j < _added_methods_length; ++j) { - Method* m = _added_methods[j]; - tty->print("%4d (%5d) ", j, m->vtable_index()); - m->access_flags().print_on(tty); - tty->print(" -- "); - m->print_name(tty); - tty->cr(); - } + int j; + RC_TRACE(0x00004000, ("_old_methods --")); + for (j = 0; j < _old_methods->length(); ++j) { + Method* m = _old_methods->at(j); + RC_TRACE_NO_CR(0x00004000, ("%4d (%5d) ", j, m->vtable_index())); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + RC_TRACE(0x00004000, ("_new_methods --")); + for (j = 0; j < _new_methods->length(); ++j) { + Method* m = _new_methods->at(j); + RC_TRACE_NO_CR(0x00004000, ("%4d (%5d) ", j, m->vtable_index())); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + RC_TRACE(0x00004000, ("_matching_(old/new)_methods --")); + for (j = 0; j < _matching_methods_length; ++j) { + Method* m = _matching_old_methods[j]; + RC_TRACE_NO_CR(0x00004000, ("%4d (%5d) ", j, m->vtable_index())); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + m = _matching_new_methods[j]; + RC_TRACE_NO_CR(0x00004000, (" (%5d) ", m->vtable_index())); + m->access_flags().print_on(tty); + tty->cr(); + } + RC_TRACE(0x00004000, ("_deleted_methods --")); + for (j = 0; j < _deleted_methods_length; ++j) { + Method* m = _deleted_methods[j]; + RC_TRACE_NO_CR(0x00004000, ("%4d (%5d) ", j, m->vtable_index())); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } + RC_TRACE(0x00004000, ("_added_methods --")); + for (j = 0; j < _added_methods_length; ++j) { + Method* m = _added_methods[j]; + RC_TRACE_NO_CR(0x00004000, ("%4d (%5d) ", j, m->vtable_index())); + m->access_flags().print_on(tty); + tty->print(" -- "); + m->print_name(tty); + tty->cr(); + } } -#endif diff --git a/src/share/vm/prims/jvmtiRedefineClasses.hpp b/src/share/vm/prims/jvmtiRedefineClasses.hpp index d348ed2c7..22dd343f6 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.hpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -468,9 +468,9 @@ class VM_RedefineClasses: public VM_Operation { void flush_dependent_code(instanceKlassHandle k_h, TRAPS); - static void check_class(Klass* k_oop, ClassLoaderData* initiating_loader, TRAPS) PRODUCT_RETURN; - - static void dump_methods() PRODUCT_RETURN; + static void check_class(Klass* k_oop, ClassLoaderData* initiating_loader, + TRAPS); + static void dump_methods(); public: VM_RedefineClasses(jint class_count, diff --git a/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp b/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp index e79534f01..878d300f5 100644 --- a/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp +++ b/src/share/vm/prims/jvmtiRedefineClassesTrace.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -54,7 +54,7 @@ // 0x00000800 | 2048 - previous class breakpoint mgmt // 0x00001000 | 4096 - detect calls to obsolete methods // 0x00002000 | 8192 - fail a guarantee() in addition to detection -// 0x00004000 | 16384 - unused +// 0x00004000 | 16384 - detect old/obsolete methods in metadata // 0x00008000 | 32768 - old/new method matching/add/delete // 0x00010000 | 65536 - impl details: CP size info // 0x00020000 | 131072 - impl details: CP merge pass info @@ -82,6 +82,13 @@ tty->print_cr args; \ } while (0) +#define RC_TRACE_NO_CR(level, args) \ + if ((TraceRedefineClasses & level) != 0) { \ + ResourceMark rm; \ + tty->print("RedefineClasses-0x%x: ", level); \ + tty->print args; \ + } while (0) + #define RC_TRACE_WITH_THREAD(level, thread, args) \ if ((TraceRedefineClasses & level) != 0) { \ ResourceMark rm(thread); \ diff --git a/src/share/vm/utilities/accessFlags.cpp b/src/share/vm/utilities/accessFlags.cpp index 933f1cba2..6ae8e74c2 100644 --- a/src/share/vm/utilities/accessFlags.cpp +++ b/src/share/vm/utilities/accessFlags.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ void AccessFlags::atomic_clear_bits(jint bits) { } while(f != old_flags); } -#ifndef PRODUCT +#if !defined(PRODUCT) || INCLUDE_JVMTI void AccessFlags::print_on(outputStream* st) const { if (is_public ()) st->print("public " ); @@ -80,7 +80,7 @@ void AccessFlags::print_on(outputStream* st) const { if (on_stack ()) st->print("{on_stack} " ); } -#endif +#endif // !PRODUCT || INCLUDE_JVMTI void accessFlags_init() { assert(sizeof(AccessFlags) == sizeof(jint), "just checking size of flags"); diff --git a/src/share/vm/utilities/accessFlags.hpp b/src/share/vm/utilities/accessFlags.hpp index c2729eb1b..3d2d9aa92 100644 --- a/src/share/vm/utilities/accessFlags.hpp +++ b/src/share/vm/utilities/accessFlags.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -239,7 +239,11 @@ class AccessFlags VALUE_OBJ_CLASS_SPEC { inline friend AccessFlags accessFlags_from(jint flags); // Printing/debugging +#if INCLUDE_JVMTI + void print_on(outputStream* st) const; +#else void print_on(outputStream* st) const PRODUCT_RETURN; +#endif }; inline AccessFlags accessFlags_from(jint flags) { -- cgit v1.2.3 From 173b002632445173b8e3666c464f9cb95e8af52f Mon Sep 17 00:00:00 2001 From: ctornqvi Date: Fri, 8 Feb 2013 10:42:24 +0100 Subject: 8007434: Write tests for 8006298 Summary: Four tests written for 8006298 Reviewed-by: mgerdin, coleenp --- .../CommandLine/BooleanFlagWithInvalidValue.java | 49 +++++++++++++++++++++ test/runtime/CommandLine/FlagWithInvalidValue.java | 42 ++++++++++++++++++ .../NonBooleanFlagWithInvalidBooleanPrefix.java | 50 ++++++++++++++++++++++ test/runtime/CommandLine/UnrecognizedVMOption.java | 42 ++++++++++++++++++ 4 files changed, 183 insertions(+) create mode 100644 test/runtime/CommandLine/BooleanFlagWithInvalidValue.java create mode 100644 test/runtime/CommandLine/FlagWithInvalidValue.java create mode 100644 test/runtime/CommandLine/NonBooleanFlagWithInvalidBooleanPrefix.java create mode 100644 test/runtime/CommandLine/UnrecognizedVMOption.java diff --git a/test/runtime/CommandLine/BooleanFlagWithInvalidValue.java b/test/runtime/CommandLine/BooleanFlagWithInvalidValue.java new file mode 100644 index 000000000..85f533a88 --- /dev/null +++ b/test/runtime/CommandLine/BooleanFlagWithInvalidValue.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8006298 + * @summary Setting an invalid value for a bool argument should result in a useful error message + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class BooleanFlagWithInvalidValue { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:+UseLargePages=8", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Improperly specified VM option 'UseLargePages=8'"); + output.shouldHaveExitValue(1); + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:-UseLargePages=8", "-version"); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Improperly specified VM option 'UseLargePages=8'"); + output.shouldHaveExitValue(1); + } +} diff --git a/test/runtime/CommandLine/FlagWithInvalidValue.java b/test/runtime/CommandLine/FlagWithInvalidValue.java new file mode 100644 index 000000000..9d475c219 --- /dev/null +++ b/test/runtime/CommandLine/FlagWithInvalidValue.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8006298 + * @summary Setting a flag to an invalid value should print a useful error message + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class FlagWithInvalidValue { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:ObjectAlignmentInBytes=v", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Improperly specified VM option 'ObjectAlignmentInBytes=v'"); + output.shouldHaveExitValue(1); + } +} diff --git a/test/runtime/CommandLine/NonBooleanFlagWithInvalidBooleanPrefix.java b/test/runtime/CommandLine/NonBooleanFlagWithInvalidBooleanPrefix.java new file mode 100644 index 000000000..d84570eb7 --- /dev/null +++ b/test/runtime/CommandLine/NonBooleanFlagWithInvalidBooleanPrefix.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8006298 + * @summary Using a bool (+/-) prefix on non-bool flag should result in a useful error message + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class NonBooleanFlagWithInvalidBooleanPrefix { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:-ObjectAlignmentInBytes=16", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Unexpected +/- setting in VM option 'ObjectAlignmentInBytes=16'"); + output.shouldHaveExitValue(1); + + pb = ProcessTools.createJavaProcessBuilder( + "-XX:+ObjectAlignmentInBytes=16", "-version"); + + output = new OutputAnalyzer(pb.start()); + output.shouldContain("Unexpected +/- setting in VM option 'ObjectAlignmentInBytes=16'"); + output.shouldHaveExitValue(1); + + } +} diff --git a/test/runtime/CommandLine/UnrecognizedVMOption.java b/test/runtime/CommandLine/UnrecognizedVMOption.java new file mode 100644 index 000000000..040704f08 --- /dev/null +++ b/test/runtime/CommandLine/UnrecognizedVMOption.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8006298 + * @summary Using an unrecognized VM option should print the name of the option + * @library /testlibrary + */ + +import com.oracle.java.testlibrary.*; + +public class UnrecognizedVMOption { + public static void main(String[] args) throws Exception { + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder( + "-XX:bogus_option", "-version"); + + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + output.shouldContain("Unrecognized VM option 'bogus_option'"); + output.shouldHaveExitValue(1); + } +} -- cgit v1.2.3 From 0b15c46dbcdb6a4f8a016203745fe24e3de9a3e7 Mon Sep 17 00:00:00 2001 From: sla Date: Fri, 8 Feb 2013 12:48:24 +0100 Subject: 8006423: SA: NullPointerException in sun.jvm.hotspot.debugger.bsd.BsdThread.getContext(BsdThread.java:67) Summary: Do not rely on mach thread port names to identify threads from SA Reviewed-by: dholmes, minqi, rbackman --- agent/src/os/bsd/MacosxDebuggerLocal.m | 132 +++++++++++++++++---- .../sun/jvm/hotspot/debugger/bsd/BsdDebugger.java | 2 +- .../jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java | 19 +-- .../sun/jvm/hotspot/debugger/bsd/BsdThread.java | 18 +-- .../bsd_amd64/BsdAMD64JavaThreadPDAccess.java | 13 +- src/os/bsd/vm/osThread_bsd.hpp | 9 ++ src/os/bsd/vm/os_bsd.cpp | 14 +++ src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp | 7 +- 8 files changed, 167 insertions(+), 47 deletions(-) diff --git a/agent/src/os/bsd/MacosxDebuggerLocal.m b/agent/src/os/bsd/MacosxDebuggerLocal.m index 435262c45..2f064a986 100644 --- a/agent/src/os/bsd/MacosxDebuggerLocal.m +++ b/agent/src/os/bsd/MacosxDebuggerLocal.m @@ -97,7 +97,8 @@ static void throw_new_debugger_exception(JNIEnv* env, const char* errMsg) { * Method: init0 * Signature: ()V */ -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) { +JNIEXPORT void JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0(JNIEnv *env, jclass cls) { symbolicatorID = (*env)->GetFieldID(env, cls, "symbolicator", "J"); taskID = (*env)->GetFieldID(env, cls, "task", "J"); CHECK_EXCEPTION; @@ -108,7 +109,11 @@ JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_init0( * Method: lookupByName0 * Signature: (Ljava/lang/String;Ljava/lang/String;)J */ -JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0(JNIEnv *env, jobject this_obj, jstring objectName, jstring symbolName) { +JNIEXPORT jlong JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_lookupByName0( + JNIEnv *env, jobject this_obj, + jstring objectName, jstring symbolName) +{ jlong address = 0; JNF_COCOA_ENTER(env); @@ -137,7 +142,11 @@ JNF_COCOA_EXIT(env); * Method: readBytesFromProcess0 * Signature: (JJ)Lsun/jvm/hotspot/debugger/ReadResult; */ -JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0(JNIEnv *env, jobject this_obj, jlong addr, jlong numBytes) { +JNIEXPORT jbyteArray JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_readBytesFromProcess0( + JNIEnv *env, jobject this_obj, + jlong addr, jlong numBytes) +{ if (debug) printf("readBytesFromProcess called. addr = %llx numBytes = %lld\n", addr, numBytes); // must allocate storage instead of using former parameter buf @@ -209,12 +218,74 @@ JNIEXPORT jbyteArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_ return array; } + /* - * Class: sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal + * Lookup the thread_t that corresponds to the given thread_id. + * The thread_id should be the result from calling thread_info() with THREAD_IDENTIFIER_INFO + * and reading the m_ident_info.thread_id returned. + * The returned thread_t is the mach send right to the kernel port for the corresponding thread. + * + * We cannot simply use the OSThread._thread_id field in the JVM. This is set to ::mach_thread_self() + * in the VM, but that thread port is not valid for a remote debugger to access the thread. + */ +thread_t +lookupThreadFromThreadId(task_t task, jlong thread_id) { + if (debug) { + printf("lookupThreadFromThreadId thread_id=0x%llx\n", thread_id); + } + + thread_array_t thread_list = NULL; + mach_msg_type_number_t thread_list_count = 0; + thread_t result_thread = 0; + int i; + + // get the list of all the send rights + kern_return_t result = task_threads(task, &thread_list, &thread_list_count); + if (result != KERN_SUCCESS) { + if (debug) { + printf("task_threads returned 0x%x\n", result); + } + return 0; + } + + for(i = 0 ; i < thread_list_count; i++) { + thread_identifier_info_data_t m_ident_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + + // get the THREAD_IDENTIFIER_INFO for the send right + result = thread_info(thread_list[i], THREAD_IDENTIFIER_INFO, (thread_info_t) &m_ident_info, &count); + if (result != KERN_SUCCESS) { + if (debug) { + printf("thread_info returned 0x%x\n", result); + } + break; + } + + // if this is the one we're looking for, return the send right + if (thread_id == m_ident_info.thread_id) + { + result_thread = thread_list[i]; + break; + } + } + + vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t)); + vm_deallocate(mach_task_self(), (vm_address_t) thread_list, thread_list_count); + + return result_thread; +} + + +/* + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal * Method: getThreadIntegerRegisterSet0 - * Signature: (I)[J + * Signature: (J)[J */ -JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0(JNIEnv *env, jobject this_obj, jint lwp_id) { +JNIEXPORT jlongArray JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_getThreadIntegerRegisterSet0( + JNIEnv *env, jobject this_obj, + jlong thread_id) +{ if (debug) printf("getThreadRegisterSet0 called\n"); @@ -226,8 +297,9 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_ int i; jlongArray registerArray; jlong *primitiveArray; + task_t gTask = getTask(env, this_obj); - tid = lwp_id; + tid = lookupThreadFromThreadId(gTask, thread_id); result = thread_get_state(tid, HSDB_THREAD_STATE, (thread_state_t)&state, &count); @@ -328,19 +400,21 @@ JNIEXPORT jlongArray JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_ } /* - * Class: sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal + * Class: sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal * Method: translateTID0 * Signature: (I)I */ JNIEXPORT jint JNICALL -Java_sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal_translateTID0(JNIEnv *env, jobject this_obj, jint tid) { +Java_sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal_translateTID0( + JNIEnv *env, jobject this_obj, jint tid) +{ if (debug) printf("translateTID0 called on tid = 0x%x\n", (int)tid); kern_return_t result; thread_t foreign_tid, usable_tid; mach_msg_type_name_t type; - + foreign_tid = tid; task_t gTask = getTask(env, this_obj); @@ -361,7 +435,10 @@ Java_sun_jvm_hotspot_debugger_macosx_MacOSXDebuggerLocal_translateTID0(JNIEnv *e * Method: attach0 * Signature: (I)V */ -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I(JNIEnv *env, jobject this_obj, jint jpid) { +JNIEXPORT void JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_attach0__I( + JNIEnv *env, jobject this_obj, jint jpid) +{ JNF_COCOA_ENTER(env); if (getenv("JAVA_SAPROC_DEBUG") != NULL) debug = JNI_TRUE; @@ -401,7 +478,10 @@ JNF_COCOA_EXIT(env); * Method: detach0 * Signature: ()V */ -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0(JNIEnv *env, jobject this_obj) { +JNIEXPORT void JNICALL +Java_sun_jvm_hotspot_debugger_bsd_BsdDebuggerLocal_detach0( + JNIEnv *env, jobject this_obj) +{ JNF_COCOA_ENTER(env); if (debug) printf("detach0 called\n"); @@ -419,10 +499,13 @@ JNF_COCOA_EXIT(env); * Method: load_library * Signature: (Ljava/lang/String;)L */ -JNIEXPORT jlong JNICALL Java_sun_jvm_hotspot_asm_Disassembler_load_1library(JNIEnv * env, - jclass disclass, - jstring jrepath_s, - jstring libname_s) { +JNIEXPORT jlong JNICALL +Java_sun_jvm_hotspot_asm_Disassembler_load_1library( + JNIEnv * env, + jclass disclass, + jstring jrepath_s, + jstring libname_s) +{ uintptr_t func = 0; const char* error_message = NULL; const char* java_home; @@ -533,13 +616,16 @@ static int printf_to_env(void* env_pv, const char* format, ...) { * Method: decode * Signature: (Lsun/jvm/hotspot/asm/InstructionVisitor;J[BLjava/lang/String;J)V */ -JNIEXPORT void JNICALL Java_sun_jvm_hotspot_asm_Disassembler_decode(JNIEnv * env, - jobject dis, - jobject visitor, - jlong startPc, - jbyteArray code, - jstring options_s, - jlong decode_instructions_virtual) { +JNIEXPORT void JNICALL +Java_sun_jvm_hotspot_asm_Disassembler_decode( + JNIEnv * env, + jobject dis, + jobject visitor, + jlong startPc, + jbyteArray code, + jstring options_s, + jlong decode_instructions_virtual) +{ jboolean isCopy; jbyte* start = (*env)->GetByteArrayElements(env, code, &isCopy); jbyte* end = start + (*env)->GetArrayLength(env, code); diff --git a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebugger.java b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebugger.java index 3500e38f1..34e848b42 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebugger.java +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebugger.java @@ -49,7 +49,7 @@ public interface BsdDebugger extends JVMDebugger { public BsdAddress readCompKlassAddress(long address) throws DebuggerException; public BsdOopHandle readOopHandle(long address) throws DebuggerException; public BsdOopHandle readCompOopHandle(long address) throws DebuggerException; - public long[] getThreadIntegerRegisterSet(int lwp_id) throws DebuggerException; + public long[] getThreadIntegerRegisterSet(long unique_thread_id) throws DebuggerException; public long getAddressValue(Address addr) throws DebuggerException; public Address newAddress(long value) throws DebuggerException; diff --git a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java index 9596d26e2..c6fbb7dab 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdDebuggerLocal.java @@ -90,7 +90,7 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { throws DebuggerException; private native ClosestSymbol lookupByAddress0(long address) throws DebuggerException; - private native long[] getThreadIntegerRegisterSet0(int lwp_id) + private native long[] getThreadIntegerRegisterSet0(long unique_thread_id) throws DebuggerException; private native byte[] readBytesFromProcess0(long address, long numBytes) throws DebuggerException; @@ -400,10 +400,15 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { // /** From the ThreadAccess interface via Debugger and JVMDebugger */ + public ThreadProxy getThreadForIdentifierAddress(Address threadIdAddr, Address uniqueThreadIdAddr) { + return new BsdThread(this, threadIdAddr, uniqueThreadIdAddr); + } + @Override public ThreadProxy getThreadForIdentifierAddress(Address addr) { - return new BsdThread(this, addr); + throw new RuntimeException("unimplemented"); } + /** From the ThreadAccess interface via Debugger and JVMDebugger */ public ThreadProxy getThreadForThreadId(long id) { return new BsdThread(this, id); @@ -455,22 +460,22 @@ public class BsdDebuggerLocal extends DebuggerBase implements BsdDebugger { // Thread context access // - public synchronized long[] getThreadIntegerRegisterSet(int lwp_id) + public synchronized long[] getThreadIntegerRegisterSet(long unique_thread_id) throws DebuggerException { requireAttach(); if (isCore) { - return getThreadIntegerRegisterSet0(lwp_id); + return getThreadIntegerRegisterSet0(unique_thread_id); } else { class GetThreadIntegerRegisterSetTask implements WorkerThreadTask { - int lwp_id; + long unique_thread_id; long[] result; public void doit(BsdDebuggerLocal debugger) { - result = debugger.getThreadIntegerRegisterSet0(lwp_id); + result = debugger.getThreadIntegerRegisterSet0(unique_thread_id); } } GetThreadIntegerRegisterSetTask task = new GetThreadIntegerRegisterSetTask(); - task.lwp_id = lwp_id; + task.unique_thread_id = unique_thread_id; workerThread.execute(task); return task.result; } diff --git a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java index b7c507c29..f3351fb9c 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java +++ b/agent/src/share/classes/sun/jvm/hotspot/debugger/bsd/BsdThread.java @@ -28,21 +28,23 @@ import sun.jvm.hotspot.debugger.*; class BsdThread implements ThreadProxy { private BsdDebugger debugger; - private int lwp_id; + private int thread_id; + private long unique_thread_id; /** The address argument must be the address of the _thread_id in the OSThread. It's value is result ::gettid() call. */ - BsdThread(BsdDebugger debugger, Address addr) { + BsdThread(BsdDebugger debugger, Address threadIdAddr, Address uniqueThreadIdAddr) { this.debugger = debugger; // FIXME: size of data fetched here should be configurable. // However, making it so would produce a dependency on the "types" // package from the debugger package, which is not desired. - this.lwp_id = (int) addr.getCIntegerAt(0, 4, true); + this.thread_id = (int) threadIdAddr.getCIntegerAt(0, 4, true); + this.unique_thread_id = uniqueThreadIdAddr.getCIntegerAt(0, 8, true); } BsdThread(BsdDebugger debugger, long id) { this.debugger = debugger; - this.lwp_id = (int) id; + this.thread_id = (int) id; } public boolean equals(Object obj) { @@ -50,19 +52,19 @@ class BsdThread implements ThreadProxy { return false; } - return (((BsdThread) obj).lwp_id == lwp_id); + return (((BsdThread) obj).thread_id == thread_id); } public int hashCode() { - return lwp_id; + return thread_id; } public String toString() { - return Integer.toString(lwp_id); + return Integer.toString(thread_id); } public ThreadContext getContext() throws IllegalThreadStateException { - long[] data = debugger.getThreadIntegerRegisterSet(lwp_id); + long[] data = debugger.getThreadIntegerRegisterSet(unique_thread_id); ThreadContext context = BsdThreadContextFactory.createThreadContext(debugger); for (int i = 0; i < data.length; i++) { context.setRegister(i, data[i]); diff --git a/agent/src/share/classes/sun/jvm/hotspot/runtime/bsd_amd64/BsdAMD64JavaThreadPDAccess.java b/agent/src/share/classes/sun/jvm/hotspot/runtime/bsd_amd64/BsdAMD64JavaThreadPDAccess.java index c0de1381c..03e35de56 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/runtime/bsd_amd64/BsdAMD64JavaThreadPDAccess.java +++ b/agent/src/share/classes/sun/jvm/hotspot/runtime/bsd_amd64/BsdAMD64JavaThreadPDAccess.java @@ -28,6 +28,8 @@ import java.io.*; import java.util.*; import sun.jvm.hotspot.debugger.*; import sun.jvm.hotspot.debugger.amd64.*; +import sun.jvm.hotspot.debugger.bsd.BsdDebugger; +import sun.jvm.hotspot.debugger.bsd.BsdDebuggerLocal; import sun.jvm.hotspot.runtime.*; import sun.jvm.hotspot.runtime.amd64.*; import sun.jvm.hotspot.runtime.x86.*; @@ -38,8 +40,9 @@ public class BsdAMD64JavaThreadPDAccess implements JavaThreadPDAccess { private static AddressField lastJavaFPField; private static AddressField osThreadField; - // Field from OSThread + // Fields from OSThread private static CIntegerField osThreadThreadIDField; + private static CIntegerField osThreadUniqueThreadIDField; // This is currently unneeded but is being kept in case we change // the currentFrameGuess algorithm @@ -61,7 +64,8 @@ public class BsdAMD64JavaThreadPDAccess implements JavaThreadPDAccess { lastJavaFPField = anchorType.getAddressField("_last_Java_fp"); Type osThreadType = db.lookupType("OSThread"); - osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); + osThreadThreadIDField = osThreadType.getCIntegerField("_thread_id"); + osThreadUniqueThreadIDField = osThreadType.getCIntegerField("_unique_thread_id"); } public Address getLastJavaFP(Address addr) { @@ -125,8 +129,9 @@ public class BsdAMD64JavaThreadPDAccess implements JavaThreadPDAccess { Address osThreadAddr = osThreadField.getValue(addr); // Get the address of the _thread_id from the OSThread Address threadIdAddr = osThreadAddr.addOffsetTo(osThreadThreadIDField.getOffset()); + Address uniqueThreadIdAddr = osThreadAddr.addOffsetTo(osThreadUniqueThreadIDField.getOffset()); - JVMDebugger debugger = VM.getVM().getDebugger(); - return debugger.getThreadForIdentifierAddress(threadIdAddr); + BsdDebuggerLocal debugger = (BsdDebuggerLocal) VM.getVM().getDebugger(); + return debugger.getThreadForIdentifierAddress(threadIdAddr, uniqueThreadIdAddr); } } diff --git a/src/os/bsd/vm/osThread_bsd.hpp b/src/os/bsd/vm/osThread_bsd.hpp index a5d5039f4..b49c3caec 100644 --- a/src/os/bsd/vm/osThread_bsd.hpp +++ b/src/os/bsd/vm/osThread_bsd.hpp @@ -49,6 +49,11 @@ // (e.g. pthread_kill). pthread_t _pthread_id; + // This is the "thread_id" from struct thread_identifier_info. According to a + // comment in thread_info.h, this is a "system-wide unique 64-bit thread id". + // The value is used by SA to correlate threads. + uint64_t _unique_thread_id; + sigset_t _caller_sigmask; // Caller's signal mask public: @@ -77,6 +82,10 @@ _pthread_id = tid; } + void set_unique_thread_id(uint64_t id) { + _unique_thread_id = id; + } + // *************************************************************** // suspension support. // *************************************************************** diff --git a/src/os/bsd/vm/os_bsd.cpp b/src/os/bsd/vm/os_bsd.cpp index 477b4923e..a11ca3700 100644 --- a/src/os/bsd/vm/os_bsd.cpp +++ b/src/os/bsd/vm/os_bsd.cpp @@ -657,6 +657,18 @@ extern "C" objc_registerThreadWithCollector_t objc_registerThreadWithCollectorFu objc_registerThreadWithCollector_t objc_registerThreadWithCollectorFunction = NULL; #endif +#ifdef __APPLE__ +static uint64_t locate_unique_thread_id() { + // Additional thread_id used to correlate threads in SA + thread_identifier_info_data_t m_ident_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + + thread_info(::mach_thread_self(), THREAD_IDENTIFIER_INFO, + (thread_info_t) &m_ident_info, &count); + return m_ident_info.thread_id; +} +#endif + // Thread start routine for all newly created threads static void *java_start(Thread *thread) { // Try to randomize the cache line index of hot stack frames. @@ -685,6 +697,7 @@ static void *java_start(Thread *thread) { #ifdef __APPLE__ // thread_id is mach thread on macos osthread->set_thread_id(::mach_thread_self()); + osthread->set_unique_thread_id(locate_unique_thread_id()); #else // thread_id is pthread_id on BSD osthread->set_thread_id(::pthread_self()); @@ -847,6 +860,7 @@ bool os::create_attached_thread(JavaThread* thread) { // Store pthread info into the OSThread #ifdef __APPLE__ osthread->set_thread_id(::mach_thread_self()); + osthread->set_unique_thread_id(locate_unique_thread_id()); #else osthread->set_thread_id(::pthread_self()); #endif diff --git a/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp b/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp index edfbcfa9a..b1460eddc 100644 --- a/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp +++ b/src/os_cpu/bsd_x86/vm/vmStructs_bsd_x86.hpp @@ -35,17 +35,16 @@ /* Threads (NOTE: incomplete) */ \ /******************************/ \ nonstatic_field(OSThread, _thread_id, OSThread::thread_id_t) \ - nonstatic_field(OSThread, _pthread_id, pthread_t) + nonstatic_field(OSThread, _unique_thread_id, uint64_t) #define VM_TYPES_OS_CPU(declare_type, declare_toplevel_type, declare_oop_type, declare_integer_type, declare_unsigned_integer_type, declare_c1_toplevel_type, declare_c2_type, declare_c2_toplevel_type) \ \ /**********************/ \ - /* Posix Thread IDs */ \ + /* Thread IDs */ \ /**********************/ \ \ - declare_unsigned_integer_type(OSThread::thread_id_t) \ - declare_unsigned_integer_type(pthread_t) + declare_unsigned_integer_type(OSThread::thread_id_t) #define VM_INT_CONSTANTS_OS_CPU(declare_constant, declare_preprocessor_constant, declare_c1_constant, declare_c2_constant, declare_c2_preprocessor_constant) -- cgit v1.2.3 From f5608a229f8fef7787be0e18cf38daf477ffaa8f Mon Sep 17 00:00:00 2001 From: amurillo Date: Fri, 8 Feb 2013 08:16:00 -0800 Subject: 8007801: new hotspot build - hs25-b19 Reviewed-by: jcoomes --- make/hotspot_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/hotspot_version b/make/hotspot_version index 6f09485a3..beedf8272 100644 --- a/make/hotspot_version +++ b/make/hotspot_version @@ -35,7 +35,7 @@ HOTSPOT_VM_COPYRIGHT=Copyright 2013 HS_MAJOR_VER=25 HS_MINOR_VER=0 -HS_BUILD_NUMBER=18 +HS_BUILD_NUMBER=19 JDK_MAJOR_VER=1 JDK_MINOR_VER=8 -- cgit v1.2.3 From c942fccfda47416e10a2c9ad7fb2f5c38e501fd9 Mon Sep 17 00:00:00 2001 From: zgu Date: Fri, 8 Feb 2013 14:49:01 -0500 Subject: 8007791: More Restricted hs_err file permission Summary: Enforce more restricted hs_file permission Reviewed-by: acorn, dcubed, dsamersoff --- src/share/vm/utilities/vmError.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp index d8fe93b64..d2ec49159 100644 --- a/src/share/vm/utilities/vmError.cpp +++ b/src/share/vm/utilities/vmError.cpp @@ -917,7 +917,7 @@ void VMError::report_and_die() { bool copy_ok = Arguments::copy_expand_pid(ErrorFile, strlen(ErrorFile), buffer, sizeof(buffer)); if (copy_ok) { - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); } } @@ -928,7 +928,7 @@ void VMError::report_and_die() { // so use the default name in the current directory jio_snprintf(&buffer[len], sizeof(buffer)-len, "%shs_err_pid%u.log", os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); } if (fd == -1) { @@ -937,7 +937,7 @@ void VMError::report_and_die() { if (tmpdir != NULL && tmpdir[0] != '\0') { jio_snprintf(buffer, sizeof(buffer), "%s%shs_err_pid%u.log", tmpdir, os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); } } -- cgit v1.2.3 From 8085516b6eefa89ef84fd49d8fffec179b5a370f Mon Sep 17 00:00:00 2001 From: zgu Date: Fri, 8 Feb 2013 16:31:48 -0500 Subject: 8006691: Remove jvm_version_info.is_kernel_jvm field Summary: Removed is_kernel_jvm from jvm_version_info as Kernel VM has been deprecated Reviewed-by: mchung, coleenp --- src/share/vm/prims/jvm.cpp | 1 - src/share/vm/prims/jvm.h | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/share/vm/prims/jvm.cpp b/src/share/vm/prims/jvm.cpp index 84ab5f048..da5ec21f2 100644 --- a/src/share/vm/prims/jvm.cpp +++ b/src/share/vm/prims/jvm.cpp @@ -4528,6 +4528,5 @@ JVM_ENTRY(void, JVM_GetVersionInfo(JNIEnv* env, jvm_version_info* info, size_t i // consider to expose this new capability in the sun.rt.jvmCapabilities jvmstat // counter defined in runtimeService.cpp. info->is_attachable = AttachListener::is_attach_supported(); - info->is_kernel_jvm = 0; // false; } JVM_END diff --git a/src/share/vm/prims/jvm.h b/src/share/vm/prims/jvm.h index dea46d126..52cef9307 100644 --- a/src/share/vm/prims/jvm.h +++ b/src/share/vm/prims/jvm.h @@ -1559,8 +1559,7 @@ typedef struct { * the new bit is also added in the main/baseline. */ unsigned int is_attachable : 1; - unsigned int is_kernel_jvm : 1; - unsigned int : 30; + unsigned int : 31; unsigned int : 32; unsigned int : 32; } jvm_version_info; -- cgit v1.2.3 From 7fdb75ca278e7382d875d81228974d1224196741 Mon Sep 17 00:00:00 2001 From: kvn Date: Fri, 8 Feb 2013 15:07:17 -0800 Subject: 8007708: compiler/6855215 assert(VM_Version::supports_sse4_2()) Summary: Added missing UseSSE42 check. Also added missing avx2 assert for vpermq instruction. Reviewed-by: roland, twisti --- src/cpu/x86/vm/assembler_x86.cpp | 9 +++++---- src/cpu/x86/vm/macroAssembler_x86.cpp | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cpu/x86/vm/assembler_x86.cpp b/src/cpu/x86/vm/assembler_x86.cpp index b94aaa936..a18d8047f 100644 --- a/src/cpu/x86/vm/assembler_x86.cpp +++ b/src/cpu/x86/vm/assembler_x86.cpp @@ -2270,10 +2270,11 @@ void Assembler::vpackuswb(XMMRegister dst, XMMRegister nds, XMMRegister src, boo } void Assembler::vpermq(XMMRegister dst, XMMRegister src, int imm8, bool vector256) { - int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true, vector256); - emit_int8(0x00); - emit_int8(0xC0 | encode); - emit_int8(imm8); + assert(VM_Version::supports_avx2(), ""); + int encode = simd_prefix_and_encode(dst, xnoreg, src, VEX_SIMD_66, VEX_OPCODE_0F_3A, true, vector256); + emit_int8(0x00); + emit_int8(0xC0 | encode); + emit_int8(imm8); } void Assembler::pcmpestri(XMMRegister dst, Address src, int imm8) { diff --git a/src/cpu/x86/vm/macroAssembler_x86.cpp b/src/cpu/x86/vm/macroAssembler_x86.cpp index c1e4c96e6..bda1550ac 100644 --- a/src/cpu/x86/vm/macroAssembler_x86.cpp +++ b/src/cpu/x86/vm/macroAssembler_x86.cpp @@ -5691,7 +5691,7 @@ void MacroAssembler::string_compare(Register str1, Register str2, Address::ScaleFactor scale = Address::times_2; int stride = 8; - if (UseAVX >= 2) { + if (UseAVX >= 2 && UseSSE42Intrinsics) { Label COMPARE_WIDE_VECTORS, VECTOR_NOT_EQUAL, COMPARE_WIDE_TAIL, COMPARE_SMALL_STR; Label COMPARE_WIDE_VECTORS_LOOP, COMPARE_16_CHARS, COMPARE_INDEX_CHAR; Label COMPARE_TAIL_LONG; -- cgit v1.2.3 From 03d03ab08f0de29974be81c3859c2b136bd70f61 Mon Sep 17 00:00:00 2001 From: morris Date: Fri, 8 Feb 2013 15:39:43 -0800 Subject: 8006851: When TieredCompilation is set, max code cache should be bumped to 256mb Summary: Set ReservedCodeCacheSize to (default value)*5 when TieredCompilation is on. Reviewed-by: kvn, twisti --- src/share/vm/runtime/arguments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/share/vm/runtime/arguments.cpp b/src/share/vm/runtime/arguments.cpp index de596c1a8..fc1e488d5 100644 --- a/src/share/vm/runtime/arguments.cpp +++ b/src/share/vm/runtime/arguments.cpp @@ -1086,7 +1086,7 @@ void Arguments::set_tiered_flags() { } // Increase the code cache size - tiered compiles a lot more. if (FLAG_IS_DEFAULT(ReservedCodeCacheSize)) { - FLAG_SET_DEFAULT(ReservedCodeCacheSize, ReservedCodeCacheSize * 2); + FLAG_SET_DEFAULT(ReservedCodeCacheSize, ReservedCodeCacheSize * 5); } } -- cgit v1.2.3 From 2aa9b53fd28ff6e9339c3795ad005e8cb97d26f2 Mon Sep 17 00:00:00 2001 From: drchase Date: Sat, 9 Feb 2013 12:55:09 -0800 Subject: 8007402: Code cleanup to remove Parfait false positive Summary: add array access range check Reviewed-by: kvn --- src/share/vm/opto/regmask.cpp | 21 +++++++++++---------- src/share/vm/opto/regmask.hpp | 12 ++++++------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/share/vm/opto/regmask.cpp b/src/share/vm/opto/regmask.cpp index f286a8734..9e9bac6f7 100644 --- a/src/share/vm/opto/regmask.cpp +++ b/src/share/vm/opto/regmask.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -241,7 +241,8 @@ int RegMask::is_bound_pair() const { } else { // Else its a split-pair case if( bit != _A[i] ) return false; // Found many bits, so fail i++; // Skip iteration forward - if( _A[i] != 1 ) return false; // Require 1 lo bit in next word + if( i >= RM_SIZE || _A[i] != 1 ) + return false; // Require 1 lo bit in next word } } } @@ -254,7 +255,7 @@ static int low_bits[3] = { 0x55555555, 0x11111111, 0x01010101 }; // Find the lowest-numbered register set in the mask. Return the // HIGHEST register number in the set, or BAD if no sets. // Works also for size 1. -OptoReg::Name RegMask::find_first_set(int size) const { +OptoReg::Name RegMask::find_first_set(const int size) const { verify_sets(size); for (int i = 0; i < RM_SIZE; i++) { if (_A[i]) { // Found some bits @@ -268,7 +269,7 @@ OptoReg::Name RegMask::find_first_set(int size) const { //------------------------------clear_to_sets---------------------------------- // Clear out partial bits; leave only aligned adjacent bit pairs -void RegMask::clear_to_sets(int size) { +void RegMask::clear_to_sets(const int size) { if (size == 1) return; assert(2 <= size && size <= 8, "update low bits table"); assert(is_power_of_2(size), "sanity"); @@ -293,7 +294,7 @@ void RegMask::clear_to_sets(int size) { //------------------------------smear_to_sets---------------------------------- // Smear out partial bits to aligned adjacent bit sets -void RegMask::smear_to_sets(int size) { +void RegMask::smear_to_sets(const int size) { if (size == 1) return; assert(2 <= size && size <= 8, "update low bits table"); assert(is_power_of_2(size), "sanity"); @@ -318,7 +319,7 @@ void RegMask::smear_to_sets(int size) { } //------------------------------is_aligned_set-------------------------------- -bool RegMask::is_aligned_sets(int size) const { +bool RegMask::is_aligned_sets(const int size) const { if (size == 1) return true; assert(2 <= size && size <= 8, "update low bits table"); assert(is_power_of_2(size), "sanity"); @@ -344,7 +345,7 @@ bool RegMask::is_aligned_sets(int size) const { //------------------------------is_bound_set----------------------------------- // Return TRUE if the mask contains one adjacent set of bits and no other bits. // Works also for size 1. -int RegMask::is_bound_set(int size) const { +int RegMask::is_bound_set(const int size) const { if( is_AllStack() ) return false; assert(1 <= size && size <= 8, "update low bits table"); int bit = -1; // Set to hold the one bit allowed @@ -352,7 +353,7 @@ int RegMask::is_bound_set(int size) const { if (_A[i] ) { // Found some bits if (bit != -1) return false; // Already had bits, so fail - bit = _A[i] & -_A[i]; // Extract 1 bit from mask + bit = _A[i] & -_A[i]; // Extract low bit from mask int hi_bit = bit << (size-1); // high bit if (hi_bit != 0) { // Bit set stays in same word? int set = hi_bit + ((hi_bit-1) & ~(bit-1)); @@ -362,12 +363,12 @@ int RegMask::is_bound_set(int size) const { if (((-1) & ~(bit-1)) != _A[i]) return false; // Found many bits, so fail i++; // Skip iteration forward and check high part - assert(size <= 8, "update next code"); // The lower 24 bits should be 0 since it is split case and size <= 8. int set = bit>>24; set = set & -set; // Remove sign extension. set = (((set << size) - 1) >> 8); - if (_A[i] != set) return false; // Require 1 lo bit in next word + if (i >= RM_SIZE || _A[i] != set) + return false; // Require expected low bits in next word } } } diff --git a/src/share/vm/opto/regmask.hpp b/src/share/vm/opto/regmask.hpp index 7e3376b38..7171ceb8c 100644 --- a/src/share/vm/opto/regmask.hpp +++ b/src/share/vm/opto/regmask.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -225,22 +225,22 @@ public: // Find the lowest-numbered register set in the mask. Return the // HIGHEST register number in the set, or BAD if no sets. // Assert that the mask contains only bit sets. - OptoReg::Name find_first_set(int size) const; + OptoReg::Name find_first_set(const int size) const; // Clear out partial bits; leave only aligned adjacent bit sets of size. - void clear_to_sets(int size); + void clear_to_sets(const int size); // Smear out partial bits to aligned adjacent bit sets. - void smear_to_sets(int size); + void smear_to_sets(const int size); // Verify that the mask contains only aligned adjacent bit sets void verify_sets(int size) const { assert(is_aligned_sets(size), "mask is not aligned, adjacent sets"); } // Test that the mask contains only aligned adjacent bit sets - bool is_aligned_sets(int size) const; + bool is_aligned_sets(const int size) const; // mask is a set of misaligned registers bool is_misaligned_set(int size) const { return (int)Size()==size && !is_aligned_sets(size);} // Test for a single adjacent set - int is_bound_set(int size) const; + int is_bound_set(const int size) const; static bool is_vector(uint ireg); static int num_registers(uint ireg); -- cgit v1.2.3 From 0bd42cc9ed9f179dbb8842d8f5636aa9d9420bc4 Mon Sep 17 00:00:00 2001 From: kmo Date: Sun, 10 Feb 2013 22:35:38 -0800 Subject: 8006430: TraceTypeProfile is a product flag while it should be a diagnostic flag Summary: make sure all diagnostic and experimental flag kinds are checked in Flag::is_unlocked() Reviewed-by: kvn --- src/share/vm/runtime/globals.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/share/vm/runtime/globals.cpp b/src/share/vm/runtime/globals.cpp index dd32cb375..9d0b938fa 100644 --- a/src/share/vm/runtime/globals.cpp +++ b/src/share/vm/runtime/globals.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,7 +69,10 @@ bool Flag::is_unlocker() const { } bool Flag::is_unlocked() const { - if (strcmp(kind, "{diagnostic}") == 0) { + if (strcmp(kind, "{diagnostic}") == 0 || + strcmp(kind, "{C2 diagnostic}") == 0 || + strcmp(kind, "{ARCH diagnostic}") == 0 || + strcmp(kind, "{Shark diagnostic}") == 0) { if (strcmp(name, "EnableInvokeDynamic") == 0 && UnlockExperimentalVMOptions && !UnlockDiagnosticVMOptions) { // transitional logic to allow tests to run until they are changed static int warned; @@ -78,7 +81,9 @@ bool Flag::is_unlocked() const { } return UnlockDiagnosticVMOptions; } else if (strcmp(kind, "{experimental}") == 0 || - strcmp(kind, "{C2 experimental}") == 0) { + strcmp(kind, "{C2 experimental}") == 0 || + strcmp(kind, "{ARCH experimental}") == 0 || + strcmp(kind, "{Shark experimental}") == 0) { return UnlockExperimentalVMOptions; } else { return is_unlocked_ext(); -- cgit v1.2.3 From 98401d7719b2f0acd9f76ce95980a54bafc4b0ba Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 11 Feb 2013 10:38:11 -0800 Subject: 8003251: ARM: move MacroAssembler into separate file Summary: moved MacroAssembler into separate file Reviewed-by: twisti, kvn, dlong --- src/share/vm/asm/macroAssembler.hpp | 2 +- src/share/vm/asm/macroAssembler.inline.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/share/vm/asm/macroAssembler.hpp b/src/share/vm/asm/macroAssembler.hpp index 92ecd1386..3ad5e6df8 100644 --- a/src/share/vm/asm/macroAssembler.hpp +++ b/src/share/vm/asm/macroAssembler.hpp @@ -37,7 +37,7 @@ # include "assembler_zero.hpp" #endif #ifdef TARGET_ARCH_arm -# include "assembler_arm.hpp" +# include "macroAssembler_arm.hpp" #endif #ifdef TARGET_ARCH_ppc # include "assembler_ppc.hpp" diff --git a/src/share/vm/asm/macroAssembler.inline.hpp b/src/share/vm/asm/macroAssembler.inline.hpp index 70a157c91..67886f6f1 100644 --- a/src/share/vm/asm/macroAssembler.inline.hpp +++ b/src/share/vm/asm/macroAssembler.inline.hpp @@ -37,7 +37,7 @@ # include "assembler_zero.inline.hpp" #endif #ifdef TARGET_ARCH_arm -# include "assembler_arm.inline.hpp" +# include "macroAssembler_arm.inline.hpp" #endif #ifdef TARGET_ARCH_ppc # include "assembler_ppc.inline.hpp" -- cgit v1.2.3 From 0ce18799f73c562945465c6b465a01ded2d32bc3 Mon Sep 17 00:00:00 2001 From: coleenp Date: Mon, 11 Feb 2013 14:06:22 -0500 Subject: 8007320: NPG: move method annotations Summary: allocate method annotations and attach to ConstMethod if present Reviewed-by: dcubed, jiangli, sspitsyn, iklam --- .../classes/sun/jvm/hotspot/oops/ConstMethod.java | 58 ++- src/share/vm/classfile/classFileParser.cpp | 420 ++++++++++----------- src/share/vm/classfile/classFileParser.hpp | 42 ++- src/share/vm/classfile/defaultMethods.cpp | 43 +-- src/share/vm/memory/heapInspection.hpp | 8 +- src/share/vm/oops/annotations.cpp | 76 +--- src/share/vm/oops/annotations.hpp | 89 +---- src/share/vm/oops/constMethod.cpp | 228 ++++++----- src/share/vm/oops/constMethod.hpp | 171 ++++++--- src/share/vm/oops/instanceKlass.cpp | 34 +- src/share/vm/oops/instanceKlass.hpp | 32 +- src/share/vm/oops/method.cpp | 122 ++---- src/share/vm/oops/method.hpp | 50 +-- src/share/vm/prims/jvm.cpp | 4 +- src/share/vm/prims/jvmtiRedefineClasses.cpp | 135 ++----- src/share/vm/prims/jvmtiRedefineClasses.hpp | 8 +- src/share/vm/runtime/fieldDescriptor.cpp | 9 +- src/share/vm/runtime/vmStructs.cpp | 23 +- test/runtime/8007320/ConstMethodTest.java | 148 ++++++++ 19 files changed, 862 insertions(+), 838 deletions(-) create mode 100644 test/runtime/8007320/ConstMethodTest.java diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java b/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java index ad495d73b..3d0a370cd 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/ConstMethod.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,12 +49,18 @@ public class ConstMethod extends VMObject { private static int HAS_LOCALVARIABLE_TABLE; private static int HAS_EXCEPTION_TABLE; private static int HAS_GENERIC_SIGNATURE; + private static int HAS_METHOD_ANNOTATIONS; + private static int HAS_PARAMETER_ANNOTATIONS; + private static int HAS_DEFAULT_ANNOTATIONS; + private static int HAS_TYPE_ANNOTATIONS; + + private static final int sizeofShort = 2; private static synchronized void initialize(TypeDataBase db) throws WrongTypeException { Type type = db.lookupType("ConstMethod"); constants = new MetadataField(type.getAddressField("_constants"), 0); constMethodSize = new CIntField(type.getCIntegerField("_constMethod_size"), 0); - flags = new ByteField(type.getJByteField("_flags"), 0); + flags = new CIntField(type.getCIntegerField("_flags"), 0); // enum constants for flags HAS_LINENUMBER_TABLE = db.lookupIntConstant("ConstMethod::_has_linenumber_table").intValue(); @@ -62,6 +68,10 @@ public class ConstMethod extends VMObject { HAS_LOCALVARIABLE_TABLE = db.lookupIntConstant("ConstMethod::_has_localvariable_table").intValue(); HAS_EXCEPTION_TABLE = db.lookupIntConstant("ConstMethod::_has_exception_table").intValue(); HAS_GENERIC_SIGNATURE = db.lookupIntConstant("ConstMethod::_has_generic_signature").intValue(); + HAS_METHOD_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_method_annotations").intValue(); + HAS_PARAMETER_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_parameter_annotations").intValue(); + HAS_DEFAULT_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_default_annotations").intValue(); + HAS_TYPE_ANNOTATIONS = db.lookupIntConstant("ConstMethod::_has_type_annotations").intValue(); // Size of Java bytecodes allocated immediately after ConstMethod*. codeSize = new CIntField(type.getCIntegerField("_code_size"), 0); @@ -92,7 +102,7 @@ public class ConstMethod extends VMObject { // Fields private static MetadataField constants; private static CIntField constMethodSize; - private static ByteField flags; + private static CIntField flags; private static CIntField codeSize; private static CIntField nameIndex; private static CIntField signatureIndex; @@ -123,7 +133,7 @@ public class ConstMethod extends VMObject { return constMethodSize.getValue(this); } - public byte getFlags() { + public long getFlags() { return flags.getValue(this); } @@ -253,7 +263,7 @@ public class ConstMethod extends VMObject { public void iterateFields(MetadataVisitor visitor) { visitor.doMetadata(constants, true); visitor.doCInt(constMethodSize, true); - visitor.doByte(flags, true); + visitor.doCInt(flags, true); visitor.doCInt(codeSize, true); visitor.doCInt(nameIndex, true); visitor.doCInt(signatureIndex, true); @@ -381,6 +391,22 @@ public class ConstMethod extends VMObject { return (getFlags() & HAS_GENERIC_SIGNATURE) != 0; } + private boolean hasMethodAnnotations() { + return (getFlags() & HAS_METHOD_ANNOTATIONS) != 0; + } + + private boolean hasParameterAnnotations() { + return (getFlags() & HAS_PARAMETER_ANNOTATIONS) != 0; + } + + private boolean hasDefaultAnnotations() { + return (getFlags() & HAS_DEFAULT_ANNOTATIONS) != 0; + } + + private boolean hasTypeAnnotations() { + return (getFlags() & HAS_TYPE_ANNOTATIONS) != 0; + } + //--------------------------------------------------------------------------- // Internals only below this point @@ -400,9 +426,15 @@ public class ConstMethod extends VMObject { return offsetOfCodeEnd() + (isNative() ? 2 * VM.getVM().getAddressSize() : 0); } - // Offset of last short in Method* + // Offset of last short in Method* before annotations, if present private long offsetOfLastU2Element() { - return getSize() * VM.getVM().getObjectHeap().getOopSize() - 2; + int offset = 0; + if (hasMethodAnnotations()) offset++; + if (hasParameterAnnotations()) offset++; + if (hasTypeAnnotations()) offset++; + if (hasDefaultAnnotations()) offset++; + long wordSize = VM.getVM().getObjectHeap().getOopSize(); + return (getSize() * wordSize) - (offset * wordSize) - sizeofShort; } // Offset of the generic signature index @@ -411,7 +443,7 @@ public class ConstMethod extends VMObject { } private long offsetOfCheckedExceptionsLength() { - return hasGenericSignature() ? offsetOfLastU2Element() - 2 : + return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort : offsetOfLastU2Element(); } @@ -461,11 +493,11 @@ public class ConstMethod extends VMObject { } if (hasExceptionTable()) { - return offsetOfExceptionTable() - 2; + return offsetOfExceptionTable() - sizeofShort; } else if (hasCheckedExceptions()) { - return offsetOfCheckedExceptions() - 2; + return offsetOfCheckedExceptions() - sizeofShort; } else { - return hasGenericSignature() ? offsetOfLastU2Element() - 2 : + return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort : offsetOfLastU2Element(); } } @@ -493,9 +525,9 @@ public class ConstMethod extends VMObject { Assert.that(hasExceptionTable(), "should only be called if table is present"); } if (hasCheckedExceptions()) { - return offsetOfCheckedExceptions() - 2; + return offsetOfCheckedExceptions() - sizeofShort; } else { - return hasGenericSignature() ? offsetOfLastU2Element() - 2 : + return hasGenericSignature() ? offsetOfLastU2Element() - sizeofShort : offsetOfLastU2Element(); } } diff --git a/src/share/vm/classfile/classFileParser.cpp b/src/share/vm/classfile/classFileParser.cpp index d46a1eab8..3650e0540 100644 --- a/src/share/vm/classfile/classFileParser.cpp +++ b/src/share/vm/classfile/classFileParser.cpp @@ -1856,6 +1856,154 @@ void ClassFileParser::ClassAnnotationCollector::apply_to(instanceKlassHandle k) #define MAX_CODE_SIZE 65535 #define INITIAL_MAX_LVT_NUMBER 256 +/* Copy class file LVT's/LVTT's into the HotSpot internal LVT. + * + * Rules for LVT's and LVTT's are: + * - There can be any number of LVT's and LVTT's. + * - If there are n LVT's, it is the same as if there was just + * one LVT containing all the entries from the n LVT's. + * - There may be no more than one LVT entry per local variable. + * Two LVT entries are 'equal' if these fields are the same: + * start_pc, length, name, slot + * - There may be no more than one LVTT entry per each LVT entry. + * Each LVTT entry has to match some LVT entry. + * - HotSpot internal LVT keeps natural ordering of class file LVT entries. + */ +void ClassFileParser::copy_localvariable_table(ConstMethod* cm, + int lvt_cnt, + u2* localvariable_table_length, + u2** localvariable_table_start, + int lvtt_cnt, + u2* localvariable_type_table_length, + u2** localvariable_type_table_start, + TRAPS) { + + LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); + initialize_hashtable(lvt_Hash); + + // To fill LocalVariableTable in + Classfile_LVT_Element* cf_lvt; + LocalVariableTableElement* lvt = cm->localvariable_table_start(); + + for (int tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { + cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; + for (int idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { + copy_lvt_element(&cf_lvt[idx], lvt); + // If no duplicates, add LVT elem in hashtable lvt_Hash. + if (LVT_put_after_lookup(lvt, lvt_Hash) == false + && _need_verify + && _major_version >= JAVA_1_5_VERSION) { + clear_hashtable(lvt_Hash); + ConstantPool* cp = cm->constants(); + classfile_parse_error("Duplicated LocalVariableTable attribute " + "entry for '%s' in class file %s", + cp->symbol_at(lvt->name_cp_index)->as_utf8(), + CHECK); + } + } + } + + // To merge LocalVariableTable and LocalVariableTypeTable + Classfile_LVT_Element* cf_lvtt; + LocalVariableTableElement lvtt_elem; + + for (int tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { + cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; + for (int idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { + copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); + int index = hash(&lvtt_elem); + LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); + if (entry == NULL) { + if (_need_verify) { + clear_hashtable(lvt_Hash); + ConstantPool* cp = cm->constants(); + classfile_parse_error("LVTT entry for '%s' in class file %s " + "does not match any LVT entry", + cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK); + } + } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { + clear_hashtable(lvt_Hash); + ConstantPool* cp = cm->constants(); + classfile_parse_error("Duplicated LocalVariableTypeTable attribute " + "entry for '%s' in class file %s", + cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), + CHECK); + } else { + // to add generic signatures into LocalVariableTable + entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; + } + } + } + clear_hashtable(lvt_Hash); +} + + +void ClassFileParser::copy_method_annotations(ClassLoaderData* loader_data, + ConstMethod* cm, + u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, + u1* runtime_visible_parameter_annotations, + int runtime_visible_parameter_annotations_length, + u1* runtime_invisible_parameter_annotations, + int runtime_invisible_parameter_annotations_length, + u1* runtime_visible_type_annotations, + int runtime_visible_type_annotations_length, + u1* runtime_invisible_type_annotations, + int runtime_invisible_type_annotations_length, + u1* annotation_default, + int annotation_default_length, + TRAPS) { + + AnnotationArray* a; + + if (runtime_visible_annotations_length + + runtime_invisible_annotations_length > 0) { + a = assemble_annotations(loader_data, + runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + CHECK); + cm->set_method_annotations(a); + } + + if (runtime_visible_parameter_annotations_length + + runtime_invisible_parameter_annotations_length > 0) { + a = assemble_annotations(loader_data, + runtime_visible_parameter_annotations, + runtime_visible_parameter_annotations_length, + runtime_invisible_parameter_annotations, + runtime_invisible_parameter_annotations_length, + CHECK); + cm->set_parameter_annotations(a); + } + + if (annotation_default_length > 0) { + a = assemble_annotations(loader_data, + annotation_default, + annotation_default_length, + NULL, + 0, + CHECK); + cm->set_default_annotations(a); + } + + if (runtime_visible_type_annotations_length + + runtime_invisible_type_annotations_length > 0) { + a = assemble_annotations(loader_data, + runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + CHECK); + cm->set_type_annotations(a); + } +} + + // Note: the parse_method below is big and clunky because all parsing of the code and exceptions // attribute is inlined. This is cumbersome to avoid since we inline most of the parts in the // Method* to save footprint, so we only know the size of the resulting Method* when the @@ -1869,10 +2017,6 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, constantPoolHandle cp, bool is_interface, AccessFlags *promoted_flags, - AnnotationArray** method_annotations, - AnnotationArray** method_parameter_annotations, - AnnotationArray** method_default_annotations, - AnnotationArray** method_type_annotations, TRAPS) { ClassFileStream* cfs = stream(); methodHandle nullHandle; @@ -2273,10 +2417,24 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, } // All sizing information for a Method* is finally available, now create it + InlineTableSizes sizes( + total_lvt_length, + linenumber_table_length, + exception_table_length, + checked_exceptions_length, + method_parameters_length, + generic_signature_index, + runtime_visible_annotations_length + + runtime_invisible_annotations_length, + runtime_visible_parameter_annotations_length + + runtime_invisible_parameter_annotations_length, + runtime_visible_type_annotations_length + + runtime_invisible_type_annotations_length, + annotation_default_length, + 0); + Method* m = Method::allocate( - loader_data, code_length, access_flags, linenumber_table_length, - total_lvt_length, exception_table_length, checked_exceptions_length, - method_parameters_length, generic_signature_index, + loader_data, code_length, access_flags, &sizes, ConstMethod::NORMAL, CHECK_(nullHandle)); ClassLoadingService::add_class_method_size(m->size()*HeapWordSize); @@ -2347,107 +2505,37 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, copy_u2_with_conversion((u2*) m->checked_exceptions_start(), checked_exceptions_start, size); } - /* Copy class file LVT's/LVTT's into the HotSpot internal LVT. - * - * Rules for LVT's and LVTT's are: - * - There can be any number of LVT's and LVTT's. - * - If there are n LVT's, it is the same as if there was just - * one LVT containing all the entries from the n LVT's. - * - There may be no more than one LVT entry per local variable. - * Two LVT entries are 'equal' if these fields are the same: - * start_pc, length, name, slot - * - There may be no more than one LVTT entry per each LVT entry. - * Each LVTT entry has to match some LVT entry. - * - HotSpot internal LVT keeps natural ordering of class file LVT entries. - */ + // Copy class file LVT's/LVTT's into the HotSpot internal LVT. if (total_lvt_length > 0) { - int tbl_no, idx; - promoted_flags->set_has_localvariable_table(); - - LVT_Hash** lvt_Hash = NEW_RESOURCE_ARRAY(LVT_Hash*, HASH_ROW_SIZE); - initialize_hashtable(lvt_Hash); - - // To fill LocalVariableTable in - Classfile_LVT_Element* cf_lvt; - LocalVariableTableElement* lvt = m->localvariable_table_start(); - - for (tbl_no = 0; tbl_no < lvt_cnt; tbl_no++) { - cf_lvt = (Classfile_LVT_Element *) localvariable_table_start[tbl_no]; - for (idx = 0; idx < localvariable_table_length[tbl_no]; idx++, lvt++) { - copy_lvt_element(&cf_lvt[idx], lvt); - // If no duplicates, add LVT elem in hashtable lvt_Hash. - if (LVT_put_after_lookup(lvt, lvt_Hash) == false - && _need_verify - && _major_version >= JAVA_1_5_VERSION ) { - clear_hashtable(lvt_Hash); - classfile_parse_error("Duplicated LocalVariableTable attribute " - "entry for '%s' in class file %s", - cp->symbol_at(lvt->name_cp_index)->as_utf8(), - CHECK_(nullHandle)); - } - } - } - - // To merge LocalVariableTable and LocalVariableTypeTable - Classfile_LVT_Element* cf_lvtt; - LocalVariableTableElement lvtt_elem; - - for (tbl_no = 0; tbl_no < lvtt_cnt; tbl_no++) { - cf_lvtt = (Classfile_LVT_Element *) localvariable_type_table_start[tbl_no]; - for (idx = 0; idx < localvariable_type_table_length[tbl_no]; idx++) { - copy_lvt_element(&cf_lvtt[idx], &lvtt_elem); - int index = hash(&lvtt_elem); - LVT_Hash* entry = LVT_lookup(&lvtt_elem, index, lvt_Hash); - if (entry == NULL) { - if (_need_verify) { - clear_hashtable(lvt_Hash); - classfile_parse_error("LVTT entry for '%s' in class file %s " - "does not match any LVT entry", - cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), - CHECK_(nullHandle)); - } - } else if (entry->_elem->signature_cp_index != 0 && _need_verify) { - clear_hashtable(lvt_Hash); - classfile_parse_error("Duplicated LocalVariableTypeTable attribute " - "entry for '%s' in class file %s", - cp->symbol_at(lvtt_elem.name_cp_index)->as_utf8(), - CHECK_(nullHandle)); - } else { - // to add generic signatures into LocalVariableTable - entry->_elem->signature_cp_index = lvtt_elem.descriptor_cp_index; - } - } - } - clear_hashtable(lvt_Hash); + copy_localvariable_table(m->constMethod(), lvt_cnt, + localvariable_table_length, + localvariable_table_start, + lvtt_cnt, + localvariable_type_table_length, + localvariable_type_table_start, CHECK_NULL); } if (parsed_annotations.has_any_annotations()) parsed_annotations.apply_to(m); - *method_annotations = assemble_annotations(loader_data, - runtime_visible_annotations, - runtime_visible_annotations_length, - runtime_invisible_annotations, - runtime_invisible_annotations_length, - CHECK_(nullHandle)); - *method_parameter_annotations = assemble_annotations(loader_data, - runtime_visible_parameter_annotations, - runtime_visible_parameter_annotations_length, - runtime_invisible_parameter_annotations, - runtime_invisible_parameter_annotations_length, - CHECK_(nullHandle)); - *method_default_annotations = assemble_annotations(loader_data, - annotation_default, - annotation_default_length, - NULL, - 0, - CHECK_(nullHandle)); - *method_type_annotations = assemble_annotations(loader_data, - runtime_visible_type_annotations, - runtime_visible_type_annotations_length, - runtime_invisible_type_annotations, - runtime_invisible_type_annotations_length, - CHECK_(nullHandle)); + + // Copy annotations + copy_method_annotations(loader_data, m->constMethod(), + runtime_visible_annotations, + runtime_visible_annotations_length, + runtime_invisible_annotations, + runtime_invisible_annotations_length, + runtime_visible_parameter_annotations, + runtime_visible_parameter_annotations_length, + runtime_invisible_parameter_annotations, + runtime_invisible_parameter_annotations_length, + runtime_visible_type_annotations, + runtime_visible_type_annotations_length, + runtime_invisible_type_annotations, + runtime_invisible_type_annotations_length, + annotation_default, + annotation_default_length, + CHECK_NULL); if (name == vmSymbols::finalize_method_name() && signature == vmSymbols::void_method_signature()) { @@ -2463,6 +2551,7 @@ methodHandle ClassFileParser::parse_method(ClassLoaderData* loader_data, _has_vanilla_constructor = true; } + NOT_PRODUCT(m->verify()); return m; } @@ -2476,17 +2565,9 @@ Array* ClassFileParser::parse_methods(ClassLoaderData* loader_data, bool is_interface, AccessFlags* promoted_flags, bool* has_final_method, - Array** methods_annotations, - Array** methods_parameter_annotations, - Array** methods_default_annotations, - Array** methods_type_annotations, bool* has_default_methods, TRAPS) { ClassFileStream* cfs = stream(); - AnnotationArray* method_annotations = NULL; - AnnotationArray* method_parameter_annotations = NULL; - AnnotationArray* method_default_annotations = NULL; - AnnotationArray* method_type_annotations = NULL; cfs->guarantee_more(2, CHECK_NULL); // length u2 length = cfs->get_u2_fast(); if (length == 0) { @@ -2500,10 +2581,6 @@ Array* ClassFileParser::parse_methods(ClassLoaderData* loader_data, methodHandle method = parse_method(loader_data, cp, is_interface, promoted_flags, - &method_annotations, - &method_parameter_annotations, - &method_default_annotations, - &method_type_annotations, CHECK_NULL); if (method->is_final()) { @@ -2514,38 +2591,6 @@ Array* ClassFileParser::parse_methods(ClassLoaderData* loader_data, *has_default_methods = true; } methods->at_put(index, method()); - - if (method_annotations != NULL) { - if (*methods_annotations == NULL) { - *methods_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); - } - (*methods_annotations)->at_put(index, method_annotations); - } - - if (method_parameter_annotations != NULL) { - if (*methods_parameter_annotations == NULL) { - *methods_parameter_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); - } - (*methods_parameter_annotations)->at_put(index, method_parameter_annotations); - } - - if (method_default_annotations != NULL) { - if (*methods_default_annotations == NULL) { - *methods_default_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); - } - (*methods_default_annotations)->at_put(index, method_default_annotations); - } - - if (method_type_annotations != NULL) { - if (*methods_type_annotations == NULL) { - *methods_type_annotations = - MetadataFactory::new_array(loader_data, length, NULL, CHECK_NULL); - } - (*methods_type_annotations)->at_put(index, method_type_annotations); - } } if (_need_verify && length > 1) { @@ -2578,11 +2623,7 @@ Array* ClassFileParser::parse_methods(ClassLoaderData* loader_data, Array* ClassFileParser::sort_methods(ClassLoaderData* loader_data, Array* methods, - Array* methods_annotations, - Array* methods_parameter_annotations, - Array* methods_default_annotations, - Array* methods_type_annotations, - TRAPS) { + TRAPS) { int length = methods->length(); // If JVMTI original method ordering or sharing is enabled we have to // remember the original class file ordering. @@ -2598,10 +2639,7 @@ Array* ClassFileParser::sort_methods(ClassLoaderData* loader_data, } // Sort method array by ascending method name (for faster lookups & vtable construction) // Note that the ordering is not alphabetical, see Symbol::fast_compare - Method::sort_methods(methods, methods_annotations, - methods_parameter_annotations, - methods_default_annotations, - methods_type_annotations); + Method::sort_methods(methods); // If JVMTI original method ordering or sharing is enabled construct int // array remembering the original ordering @@ -3048,9 +3086,6 @@ void ClassFileParser::apply_parsed_class_attributes(instanceKlassHandle k) { k->set_source_debug_extension(_sde_buffer, _sde_length); } k->set_inner_classes(_inner_classes); - if (_annotations != NULL) { - k->annotations()->set_class_annotations(_annotations); - } } AnnotationArray* ClassFileParser::assemble_annotations(ClassLoaderData* loader_data, @@ -3361,19 +3396,10 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, bool has_final_method = false; AccessFlags promoted_flags; promoted_flags.set_flags(0); - - Array* methods_annotations = NULL; - Array* methods_parameter_annotations = NULL; - Array* methods_default_annotations = NULL; - Array* methods_type_annotations = NULL; Array* methods = parse_methods(loader_data, cp, access_flags.is_interface(), &promoted_flags, &has_final_method, - &methods_annotations, - &methods_parameter_annotations, - &methods_default_annotations, - &methods_type_annotations, &has_default_methods, CHECK_(nullHandle)); @@ -3432,10 +3458,6 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // sort methods Array* method_ordering = sort_methods(loader_data, methods, - methods_annotations, - methods_parameter_annotations, - methods_default_annotations, - methods_type_annotations, CHECK_(nullHandle)); // promote flags from parse_methods() to the klass' flags @@ -4035,7 +4057,6 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, const unsigned int total_oop_map_count = compute_oop_map_count(super_klass, nonstatic_oop_map_count, first_nonstatic_oop_offset); - // Compute reference type ReferenceType rt; if (super_klass() == NULL) { @@ -4057,7 +4078,7 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, access_flags, name, super_klass(), - host_klass, + !host_klass.is_null(), CHECK_(nullHandle)); // Add all classes to our internal class loader list here, @@ -4103,31 +4124,15 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, if (is_anonymous()) // I am well known to myself cp->klass_at_put(this_class_index, this_klass()); // eagerly resolve - // Allocate an annotation type if needed. - if (fields_annotations != NULL || - methods_annotations != NULL || - methods_parameter_annotations != NULL || - methods_default_annotations != NULL || - fields_type_annotations != NULL || - methods_type_annotations != NULL) { - Annotations* anno = Annotations::allocate(loader_data, - fields_annotations, methods_annotations, - methods_parameter_annotations, - methods_default_annotations, CHECK_(nullHandle)); - this_klass->set_annotations(anno); - } else { - this_klass->set_annotations(NULL); - } - - if (fields_type_annotations != NULL || - methods_type_annotations != NULL) { - assert(this_klass->annotations() != NULL, "annotations should have been allocated"); - Annotations* anno = Annotations::allocate(loader_data, - fields_type_annotations, - methods_type_annotations, - NULL, - NULL, CHECK_(nullHandle)); - this_klass->annotations()->set_type_annotations(anno); + // Assign allocations if needed + if (_annotations != NULL || _type_annotations != NULL || + fields_annotations != NULL || fields_type_annotations != NULL) { + Annotations* annotations = Annotations::allocate(loader_data, CHECK_NULL); + annotations->set_class_annotations(_annotations); + annotations->set_class_type_annotations(_type_annotations); + annotations->set_fields_annotations(fields_annotations); + annotations->set_fields_type_annotations(fields_type_annotations); + this_klass->set_annotations(annotations); } this_klass->set_minor_version(minor_version); @@ -4153,27 +4158,8 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name, // Fill in field values obtained by parse_classfile_attributes if (parsed_annotations.has_any_annotations()) parsed_annotations.apply_to(this_klass); - - // Create annotations - if (_annotations != NULL && this_klass->annotations() == NULL) { - Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL); - this_klass->set_annotations(anno); - } apply_parsed_class_attributes(this_klass); - // Create type annotations - if (_type_annotations != NULL) { - if (this_klass->annotations() == NULL) { - Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL); - this_klass->set_annotations(anno); - } - if (this_klass->annotations()->type_annotations() == NULL) { - Annotations* anno = Annotations::allocate(loader_data, CHECK_NULL); - this_klass->annotations()->set_type_annotations(anno); - } - this_klass->annotations()->type_annotations()->set_class_annotations(_type_annotations); - } - // Miranda methods if ((num_miranda_methods > 0) || // if this class introduced new miranda methods or diff --git a/src/share/vm/classfile/classFileParser.hpp b/src/share/vm/classfile/classFileParser.hpp index cc0193a15..7cedcefb3 100644 --- a/src/share/vm/classfile/classFileParser.hpp +++ b/src/share/vm/classfile/classFileParser.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -199,29 +199,17 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { constantPoolHandle cp, bool is_interface, AccessFlags* promoted_flags, - AnnotationArray** method_annotations, - AnnotationArray** method_parameter_annotations, - AnnotationArray** method_default_annotations, - AnnotationArray** method_type_annotations, TRAPS); Array* parse_methods(ClassLoaderData* loader_data, constantPoolHandle cp, bool is_interface, AccessFlags* promoted_flags, bool* has_final_method, - Array** methods_annotations, - Array** methods_parameter_annotations, - Array** methods_default_annotations, - Array** methods_type_annotations, bool* has_default_method, TRAPS); Array* sort_methods(ClassLoaderData* loader_data, Array* methods, - Array* methods_annotations, - Array* methods_parameter_annotations, - Array* methods_default_annotations, - Array* methods_type_annotations, - TRAPS); + TRAPS); u2* parse_exception_table(ClassLoaderData* loader_data, u4 code_length, u4 exception_table_length, constantPoolHandle cp, TRAPS); @@ -377,6 +365,32 @@ class ClassFileParser VALUE_OBJ_CLASS_SPEC { : cp->tag_at(index).is_klass_reference()); } + void copy_localvariable_table(ConstMethod* cm, int lvt_cnt, + u2* localvariable_table_length, + u2** localvariable_table_start, + int lvtt_cnt, + u2* localvariable_type_table_length, + u2** localvariable_type_table_start, + TRAPS); + + void copy_method_annotations(ClassLoaderData* loader_data, + ConstMethod* cm, + u1* runtime_visible_annotations, + int runtime_visible_annotations_length, + u1* runtime_invisible_annotations, + int runtime_invisible_annotations_length, + u1* runtime_visible_parameter_annotations, + int runtime_visible_parameter_annotations_length, + u1* runtime_invisible_parameter_annotations, + int runtime_invisible_parameter_annotations_length, + u1* runtime_visible_type_annotations, + int runtime_visible_type_annotations_length, + u1* runtime_invisible_type_annotations, + int runtime_invisible_type_annotations_length, + u1* annotation_default, + int annotation_default_length, + TRAPS); + public: // Constructor ClassFileParser(ClassFileStream* st) { set_stream(st); } diff --git a/src/share/vm/classfile/defaultMethods.cpp b/src/share/vm/classfile/defaultMethods.cpp index ce516b84b..4e43d7fd3 100644 --- a/src/share/vm/classfile/defaultMethods.cpp +++ b/src/share/vm/classfile/defaultMethods.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1146,9 +1146,10 @@ static Method* new_method( address code_start = static_cast
(bytecodes->adr_at(0)); int code_length = bytecodes->length(); + InlineTableSizes sizes; Method* m = Method::allocate(cp->pool_holder()->class_loader_data(), - code_length, flags, 0, 0, 0, 0, 0, 0, + code_length, flags, &sizes, mt, CHECK_NULL); m->set_constants(NULL); // This will get filled in later @@ -1285,33 +1286,15 @@ static void merge_in_new_methods(InstanceKlass* klass, enum { ANNOTATIONS, PARAMETERS, DEFAULTS, NUM_ARRAYS }; - Array* original_annots[NUM_ARRAYS] = { NULL }; - Array* original_methods = klass->methods(); - Annotations* annots = klass->annotations(); - if (annots != NULL) { - original_annots[ANNOTATIONS] = annots->methods_annotations(); - original_annots[PARAMETERS] = annots->methods_parameter_annotations(); - original_annots[DEFAULTS] = annots->methods_default_annotations(); - } - Array* original_ordering = klass->method_ordering(); Array* merged_ordering = Universe::the_empty_int_array(); int new_size = klass->methods()->length() + new_methods->length(); - Array* merged_annots[NUM_ARRAYS]; - Array* merged_methods = MetadataFactory::new_array( klass->class_loader_data(), new_size, NULL, CHECK); - for (int i = 0; i < NUM_ARRAYS; ++i) { - if (original_annots[i] != NULL) { - merged_annots[i] = MetadataFactory::new_array( - klass->class_loader_data(), new_size, CHECK); - } else { - merged_annots[i] = NULL; - } - } + if (original_ordering != NULL && original_ordering->length() > 0) { merged_ordering = MetadataFactory::new_array( klass->class_loader_data(), new_size, CHECK); @@ -1338,12 +1321,6 @@ static void merge_in_new_methods(InstanceKlass* klass, (new_method == NULL || orig_method->name() < new_method->name())) { merged_methods->at_put(i, orig_method); original_methods->at_put(orig_idx, NULL); - for (int j = 0; j < NUM_ARRAYS; ++j) { - if (merged_annots[j] != NULL) { - merged_annots[j]->at_put(i, original_annots[j]->at(orig_idx)); - original_annots[j]->at_put(orig_idx, NULL); - } - } if (merged_ordering->length() > 0) { merged_ordering->at_put(i, original_ordering->at(orig_idx)); } @@ -1372,21 +1349,9 @@ static void merge_in_new_methods(InstanceKlass* klass, // Replace klass methods with new merged lists klass->set_methods(merged_methods); - if (annots != NULL) { - annots->set_methods_annotations(merged_annots[ANNOTATIONS]); - annots->set_methods_parameter_annotations(merged_annots[PARAMETERS]); - annots->set_methods_default_annotations(merged_annots[DEFAULTS]); - } else { - assert(merged_annots[ANNOTATIONS] == NULL, "Must be"); - assert(merged_annots[PARAMETERS] == NULL, "Must be"); - assert(merged_annots[DEFAULTS] == NULL, "Must be"); - } ClassLoaderData* cld = klass->class_loader_data(); MetadataFactory::free_array(cld, original_methods); - for (int i = 0; i < NUM_ARRAYS; ++i) { - MetadataFactory::free_array(cld, original_annots[i]); - } if (original_ordering->length() > 0) { klass->set_method_ordering(merged_ordering); MetadataFactory::free_array(cld, original_ordering); diff --git a/src/share/vm/memory/heapInspection.hpp b/src/share/vm/memory/heapInspection.hpp index c080cb842..a5de4551e 100644 --- a/src/share/vm/memory/heapInspection.hpp +++ b/src/share/vm/memory/heapInspection.hpp @@ -85,16 +85,20 @@ "Number of bytes used by the InstanceKlass::singers() array") \ f(class_annotations_bytes, class_annotations, \ "Size of class annotations") \ + f(class_type_annotations_bytes, class_type_annotations, \ + "Size of class type annotations") \ f(fields_annotations_bytes, fields_annotations, \ "Size of field annotations") \ + f(fields_type_annotations_bytes, fields_type_annotations, \ + "Size of field type annotations") \ f(methods_annotations_bytes, methods_annotations, \ "Size of method annotations") \ f(methods_parameter_annotations_bytes, methods_parameter_annotations, \ "Size of method parameter annotations") \ + f(methods_type_annotations_bytes, methods_type_annotations, \ + "Size of methods type annotations") \ f(methods_default_annotations_bytes, methods_default_annotations, \ "Size of methods default annotations") \ - f(type_annotations_bytes, type_annotations, \ - "Size of type annotations") \ f(annotations_bytes, annotations, \ "Size of all annotations") \ f(cp_bytes, Cp, \ diff --git a/src/share/vm/oops/annotations.cpp b/src/share/vm/oops/annotations.cpp index 9e5d6ef37..546257a17 100644 --- a/src/share/vm/oops/annotations.cpp +++ b/src/share/vm/oops/annotations.cpp @@ -36,16 +36,8 @@ Annotations* Annotations::allocate(ClassLoaderData* loader_data, TRAPS) { return new (loader_data, size(), true, THREAD) Annotations(); } -Annotations* Annotations::allocate(ClassLoaderData* loader_data, - Array* fa, - Array* ma, - Array* mpa, - Array* mda, TRAPS) { - return new (loader_data, size(), true, THREAD) Annotations(fa, ma, mpa, mda); -} - // helper -static void free_contents(ClassLoaderData* loader_data, Array* p) { +void Annotations::free_contents(ClassLoaderData* loader_data, Array* p) { if (p != NULL) { for (int i = 0; i < p->length(); i++) { MetadataFactory::free_array(loader_data, p->at(i)); @@ -59,44 +51,16 @@ void Annotations::deallocate_contents(ClassLoaderData* loader_data) { MetadataFactory::free_array(loader_data, class_annotations()); } free_contents(loader_data, fields_annotations()); - free_contents(loader_data, methods_annotations()); - free_contents(loader_data, methods_parameter_annotations()); - free_contents(loader_data, methods_default_annotations()); - // Recursively deallocate optional Annotations linked through this one - MetadataFactory::free_metadata(loader_data, type_annotations()); -} - -// Set the annotation at 'idnum' to 'anno'. -// We don't want to create or extend the array if 'anno' is NULL, since that is the -// default value. However, if the array exists and is long enough, we must set NULL values. -void Annotations::set_methods_annotations_of(instanceKlassHandle ik, - int idnum, AnnotationArray* anno, - Array** md_p, - TRAPS) { - Array* md = *md_p; - if (md != NULL && md->length() > idnum) { - md->at_put(idnum, anno); - } else if (anno != NULL) { - // create the array - int length = MAX2(idnum+1, (int)ik->idnum_allocated_count()); - md = MetadataFactory::new_array(ik->class_loader_data(), length, CHECK); - if (*md_p != NULL) { - // copy the existing entries - for (int index = 0; index < (*md_p)->length(); index++) { - md->at_put(index, (*md_p)->at(index)); - } - } - set_annotations(md, md_p); - md->at_put(idnum, anno); - } // if no array and idnum isn't included there is nothing to do + if (class_type_annotations() != NULL) { + MetadataFactory::free_array(loader_data, class_type_annotations()); + } + free_contents(loader_data, fields_type_annotations()); } -// Keep created annotations in a global growable array (should be hashtable) -// need to add, search, delete when class is unloaded. -// Does it need a lock? yes. This sucks. - // Copy annotations to JVM call or reflection to the java heap. +// The alternative to creating this array and adding to Java heap pressure +// is to have a hashtable of the already created typeArrayOops typeArrayOop Annotations::make_java_array(AnnotationArray* annotations, TRAPS) { if (annotations != NULL) { int length = annotations->length(); @@ -132,28 +96,15 @@ julong Annotations::count_bytes(Array* p) { void Annotations::collect_statistics(KlassSizeStats *sz) const { sz->_annotations_bytes = sz->count(this); sz->_class_annotations_bytes = sz->count(class_annotations()); + sz->_class_type_annotations_bytes = sz->count(class_type_annotations()); sz->_fields_annotations_bytes = count_bytes(fields_annotations()); - sz->_methods_annotations_bytes = count_bytes(methods_annotations()); - sz->_methods_parameter_annotations_bytes = - count_bytes(methods_parameter_annotations()); - sz->_methods_default_annotations_bytes = - count_bytes(methods_default_annotations()); - - const Annotations* type_anno = type_annotations(); - if (type_anno != NULL) { - sz->_type_annotations_bytes = sz->count(type_anno); - sz->_type_annotations_bytes += sz->count(type_anno->class_annotations()); - sz->_type_annotations_bytes += count_bytes(type_anno->fields_annotations()); - sz->_type_annotations_bytes += count_bytes(type_anno->methods_annotations()); - } + sz->_fields_type_annotations_bytes = count_bytes(fields_type_annotations()); sz->_annotations_bytes += sz->_class_annotations_bytes + + sz->_class_type_annotations_bytes + sz->_fields_annotations_bytes + - sz->_methods_annotations_bytes + - sz->_methods_parameter_annotations_bytes + - sz->_methods_default_annotations_bytes + - sz->_type_annotations_bytes; + sz->_fields_type_annotations_bytes; sz->_ro_bytes += sz->_annotations_bytes; } @@ -165,8 +116,7 @@ void Annotations::collect_statistics(KlassSizeStats *sz) const { void Annotations::print_on(outputStream* st) const { st->print(BULLET"class_annotations "); class_annotations()->print_value_on(st); st->print(BULLET"fields_annotations "); fields_annotations()->print_value_on(st); - st->print(BULLET"methods_annotations "); methods_annotations()->print_value_on(st); - st->print(BULLET"methods_parameter_annotations"); methods_parameter_annotations()->print_value_on(st); - st->print(BULLET"methods_default_annotations "); methods_default_annotations()->print_value_on(st); + st->print(BULLET"class_type_annotations "); class_type_annotations()->print_value_on(st); + st->print(BULLET"fields_type_annotations "); fields_type_annotations()->print_value_on(st); } #endif // PRODUCT diff --git a/src/share/vm/oops/annotations.hpp b/src/share/vm/oops/annotations.hpp index 82d701ac5..ad405a8db 100644 --- a/src/share/vm/oops/annotations.hpp +++ b/src/share/vm/oops/annotations.hpp @@ -49,38 +49,15 @@ class Annotations: public MetaspaceObj { // Annotation objects (byte arrays) for fields, or null if no annotations. // Indices correspond to entries (not indices) in fields array. Array* _fields_annotations; - // Annotation objects (byte arrays) for methods, or null if no annotations. - // Index is the idnum, which is initially the same as the methods array index. - Array* _methods_annotations; - // Annotation objects (byte arrays) for methods' parameters, or null if no - // such annotations. - // Index is the idnum, which is initially the same as the methods array index. - Array* _methods_parameter_annotations; - // Annotation objects (byte arrays) for methods' default values, or null if no - // such annotations. - // Index is the idnum, which is initially the same as the methods array index. - Array* _methods_default_annotations; // Type annotations for this class, or null if none. - Annotations* _type_annotations; - - // Constructor where some some values are known to not be null - Annotations(Array* fa, Array* ma, - Array* mpa, Array* mda) : - _class_annotations(NULL), - _fields_annotations(fa), - _methods_annotations(ma), - _methods_parameter_annotations(mpa), - _methods_default_annotations(mda), - _type_annotations(NULL) {} + AnnotationArray* _class_type_annotations; + Array* _fields_type_annotations; public: // Allocate instance of this class static Annotations* allocate(ClassLoaderData* loader_data, TRAPS); - static Annotations* allocate(ClassLoaderData* loader_data, - Array* fa, - Array* ma, - Array* mpa, - Array* mda, TRAPS); + + static void free_contents(ClassLoaderData* loader_data, Array* p); void deallocate_contents(ClassLoaderData* loader_data); DEBUG_ONLY(bool on_stack() { return false; }) // for template @@ -93,61 +70,24 @@ class Annotations: public MetaspaceObj { // Constructor to initialize to null Annotations() : _class_annotations(NULL), _fields_annotations(NULL), - _methods_annotations(NULL), - _methods_parameter_annotations(NULL), - _methods_default_annotations(NULL), - _type_annotations(NULL) {} + _class_type_annotations(NULL), + _fields_type_annotations(NULL) {} AnnotationArray* class_annotations() const { return _class_annotations; } Array* fields_annotations() const { return _fields_annotations; } - Array* methods_annotations() const { return _methods_annotations; } - Array* methods_parameter_annotations() const { return _methods_parameter_annotations; } - Array* methods_default_annotations() const { return _methods_default_annotations; } - Annotations* type_annotations() const { return _type_annotations; } + AnnotationArray* class_type_annotations() const { return _class_type_annotations; } + Array* fields_type_annotations() const { return _fields_type_annotations; } void set_class_annotations(AnnotationArray* md) { _class_annotations = md; } void set_fields_annotations(Array* md) { _fields_annotations = md; } - void set_methods_annotations(Array* md) { _methods_annotations = md; } - void set_methods_parameter_annotations(Array* md) { _methods_parameter_annotations = md; } - void set_methods_default_annotations(Array* md) { _methods_default_annotations = md; } - void set_type_annotations(Annotations* annos) { _type_annotations = annos; } - - // Redefine classes support - AnnotationArray* get_method_annotations_of(int idnum) - { return get_method_annotations_from(idnum, _methods_annotations); } - - AnnotationArray* get_method_parameter_annotations_of(int idnum) - { return get_method_annotations_from(idnum, _methods_parameter_annotations); } - AnnotationArray* get_method_default_annotations_of(int idnum) - { return get_method_annotations_from(idnum, _methods_default_annotations); } - - - void set_method_annotations_of(instanceKlassHandle ik, - int idnum, AnnotationArray* anno, TRAPS) { - set_methods_annotations_of(ik, idnum, anno, &_methods_annotations, THREAD); - } - - void set_method_parameter_annotations_of(instanceKlassHandle ik, - int idnum, AnnotationArray* anno, TRAPS) { - set_methods_annotations_of(ik, idnum, anno, &_methods_parameter_annotations, THREAD); - } - - void set_method_default_annotations_of(instanceKlassHandle ik, - int idnum, AnnotationArray* anno, TRAPS) { - set_methods_annotations_of(ik, idnum, anno, &_methods_default_annotations, THREAD); - } + void set_class_type_annotations(AnnotationArray* cta) { _class_type_annotations = cta; } + void set_fields_type_annotations(Array* fta) { _fields_type_annotations = fta; } // Turn metadata annotations into a Java heap object (oop) static typeArrayOop make_java_array(AnnotationArray* annotations, TRAPS); - inline AnnotationArray* get_method_annotations_from(int idnum, Array* annos); - void set_annotations(Array* md, Array** md_p) { *md_p = md; } - bool is_klass() const { return false; } private: - void set_methods_annotations_of(instanceKlassHandle ik, - int idnum, AnnotationArray* anno, - Array** md_p, TRAPS); static julong count_bytes(Array* p); public: const char* internal_name() const { return "{constant pool}"; } @@ -156,13 +96,4 @@ class Annotations: public MetaspaceObj { #endif void print_value_on(outputStream* st) const; }; - - -// For method with idnum get the method's Annotations -inline AnnotationArray* Annotations::get_method_annotations_from(int idnum, Array* annos) { - if (annos == NULL || annos->length() <= idnum) { - return NULL; - } - return annos->at(idnum); -} #endif // SHARE_VM_OOPS_ANNOTATIONS_HPP diff --git a/src/share/vm/oops/constMethod.cpp b/src/share/vm/oops/constMethod.cpp index 3358d225e..35b7be24f 100644 --- a/src/share/vm/oops/constMethod.cpp +++ b/src/share/vm/oops/constMethod.cpp @@ -36,51 +36,26 @@ const u2 ConstMethod::UNSET_IDNUM = 0xFFFF; ConstMethod* ConstMethod::allocate(ClassLoaderData* loader_data, int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, MethodType method_type, TRAPS) { - int size = ConstMethod::size(byte_code_size, - compressed_line_number_size, - localvariable_table_length, - exception_table_length, - checked_exceptions_length, - method_parameters_length, - generic_signature_index); + int size = ConstMethod::size(byte_code_size, sizes); return new (loader_data, size, true, THREAD) ConstMethod( - byte_code_size, compressed_line_number_size, localvariable_table_length, - exception_table_length, checked_exceptions_length, - method_parameters_length, generic_signature_index, - method_type, size); + byte_code_size, sizes, method_type, size); } ConstMethod::ConstMethod(int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, MethodType method_type, int size) { No_Safepoint_Verifier no_safepoint; - set_interpreter_kind(Interpreter::invalid); init_fingerprint(); set_constants(NULL); set_stackmap_data(NULL); set_code_size(byte_code_size); set_constMethod_size(size); - set_inlined_tables_length(generic_signature_index, - checked_exceptions_length, - compressed_line_number_size, - localvariable_table_length, - exception_table_length, - method_parameters_length); + set_inlined_tables_length(sizes); set_method_type(method_type); assert(this->size() == size, "wrong size for object"); } @@ -88,47 +63,70 @@ ConstMethod::ConstMethod(int byte_code_size, // Deallocate metadata fields associated with ConstMethod* void ConstMethod::deallocate_contents(ClassLoaderData* loader_data) { - set_interpreter_kind(Interpreter::invalid); if (stackmap_data() != NULL) { MetadataFactory::free_array(loader_data, stackmap_data()); } set_stackmap_data(NULL); + + // deallocate annotation arrays + if (has_method_annotations()) + MetadataFactory::free_array(loader_data, method_annotations()); + if (has_parameter_annotations()) + MetadataFactory::free_array(loader_data, parameter_annotations()); + if (has_type_annotations()) + MetadataFactory::free_array(loader_data, type_annotations()); + if (has_default_annotations()) + MetadataFactory::free_array(loader_data, default_annotations()); } // How big must this constMethodObject be? int ConstMethod::size(int code_size, - int compressed_line_number_size, - int local_variable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index) { + InlineTableSizes* sizes) { int extra_bytes = code_size; - if (compressed_line_number_size > 0) { - extra_bytes += compressed_line_number_size; + if (sizes->compressed_linenumber_size() > 0) { + extra_bytes += sizes->compressed_linenumber_size(); } - if (checked_exceptions_length > 0) { + if (sizes->checked_exceptions_length() > 0) { extra_bytes += sizeof(u2); - extra_bytes += checked_exceptions_length * sizeof(CheckedExceptionElement); + extra_bytes += sizes->checked_exceptions_length() * sizeof(CheckedExceptionElement); } - if (local_variable_table_length > 0) { + if (sizes->localvariable_table_length() > 0) { extra_bytes += sizeof(u2); extra_bytes += - local_variable_table_length * sizeof(LocalVariableTableElement); + sizes->localvariable_table_length() * sizeof(LocalVariableTableElement); } - if (exception_table_length > 0) { + if (sizes->exception_table_length() > 0) { extra_bytes += sizeof(u2); - extra_bytes += exception_table_length * sizeof(ExceptionTableElement); + extra_bytes += sizes->exception_table_length() * sizeof(ExceptionTableElement); } - if (generic_signature_index != 0) { + if (sizes->generic_signature_index() != 0) { extra_bytes += sizeof(u2); } - if (method_parameters_length > 0) { + if (sizes->method_parameters_length() > 0) { extra_bytes += sizeof(u2); - extra_bytes += method_parameters_length * sizeof(MethodParametersElement); + extra_bytes += sizes->method_parameters_length() * sizeof(MethodParametersElement); + } + + // Align sizes up to a word. + extra_bytes = align_size_up(extra_bytes, BytesPerWord); + + // One pointer per annotation array + if (sizes->method_annotations_length() > 0) { + extra_bytes += sizeof(AnnotationArray*); + } + if (sizes->parameter_annotations_length() > 0) { + extra_bytes += sizeof(AnnotationArray*); + } + if (sizes->type_annotations_length() > 0) { + extra_bytes += sizeof(AnnotationArray*); } + if (sizes->default_annotations_length() > 0) { + extra_bytes += sizeof(AnnotationArray*); + } + int extra_words = align_size_up(extra_bytes, BytesPerWord) / BytesPerWord; + assert(extra_words == extra_bytes/BytesPerWord, "should already be aligned"); return align_object_size(header_size() + extra_words); } @@ -145,12 +143,28 @@ u_char* ConstMethod::compressed_linenumber_table() const { return code_end(); } +// Last short in ConstMethod* before annotations +u2* ConstMethod::last_u2_element() const { + int offset = 0; + if (has_method_annotations()) offset++; + if (has_parameter_annotations()) offset++; + if (has_type_annotations()) offset++; + if (has_default_annotations()) offset++; + return (u2*)((AnnotationArray**)constMethod_end() - offset) - 1; +} + u2* ConstMethod::generic_signature_index_addr() const { // Located at the end of the constMethod. assert(has_generic_signature(), "called only if generic signature exists"); return last_u2_element(); } +u2* ConstMethod::method_parameters_length_addr() const { + assert(has_method_parameters(), "called only if table is present"); + return has_generic_signature() ? (last_u2_element() - 1) : + last_u2_element(); +} + u2* ConstMethod::checked_exceptions_length_addr() const { // Located immediately before the generic signature index. assert(has_checked_exceptions(), "called only if table is present"); @@ -164,12 +178,6 @@ u2* ConstMethod::checked_exceptions_length_addr() const { } } -u2* ConstMethod::method_parameters_length_addr() const { - assert(has_method_parameters(), "called only if table is present"); - return has_generic_signature() ? (last_u2_element() - 1) : - last_u2_element(); -} - u2* ConstMethod::exception_table_length_addr() const { assert(has_exception_handler(), "called only if table is present"); if (has_checked_exceptions()) { @@ -181,9 +189,9 @@ u2* ConstMethod::exception_table_length_addr() const { return (u2*)method_parameters_start() - 1; } else { // Else, the exception table is at the end of the constMethod. - return has_generic_signature() ? (last_u2_element() - 1) : - last_u2_element(); - } + return has_generic_signature() ? (last_u2_element() - 1) : + last_u2_element(); + } } } @@ -204,32 +212,38 @@ u2* ConstMethod::localvariable_table_length_addr() const { // Else, the exception table is at the end of the constMethod. return has_generic_signature() ? (last_u2_element() - 1) : last_u2_element(); + } } } - } } // Update the flags to indicate the presence of these optional fields. -void ConstMethod::set_inlined_tables_length(u2 generic_signature_index, - int checked_exceptions_len, - int compressed_line_number_size, - int localvariable_table_len, - int exception_table_len, - int method_parameters_len) { - assert(_flags == 0, "Error"); - if (compressed_line_number_size > 0) +void ConstMethod::set_inlined_tables_length(InlineTableSizes* sizes) { + _flags = 0; + if (sizes->compressed_linenumber_size() > 0) _flags |= _has_linenumber_table; - if (generic_signature_index != 0) + if (sizes->generic_signature_index() != 0) _flags |= _has_generic_signature; - if (method_parameters_len > 0) + if (sizes->method_parameters_length() > 0) _flags |= _has_method_parameters; - if (checked_exceptions_len > 0) + if (sizes->checked_exceptions_length() > 0) _flags |= _has_checked_exceptions; - if (exception_table_len > 0) + if (sizes->exception_table_length() > 0) _flags |= _has_exception_table; - if (localvariable_table_len > 0) + if (sizes->localvariable_table_length() > 0) _flags |= _has_localvariable_table; + // annotations, they are all pointer sized embedded objects so don't have + // a length embedded also. + if (sizes->method_annotations_length() > 0) + _flags |= _has_method_annotations; + if (sizes->parameter_annotations_length() > 0) + _flags |= _has_parameter_annotations; + if (sizes->type_annotations_length() > 0) + _flags |= _has_type_annotations; + if (sizes->default_annotations_length() > 0) + _flags |= _has_default_annotations; + // This code is extremely brittle and should possibly be revised. // The *_length_addr functions walk backwards through the // constMethod data, using each of the length indexes ahead of them, @@ -242,17 +256,17 @@ void ConstMethod::set_inlined_tables_length(u2 generic_signature_index, // Also, the servicability agent needs to be informed anytime // anything is added here. It might be advisable to have some sort // of indication of this inline. - if (generic_signature_index != 0) - *(generic_signature_index_addr()) = generic_signature_index; + if (sizes->generic_signature_index() != 0) + *(generic_signature_index_addr()) = sizes->generic_signature_index(); // New data should probably go here. - if (method_parameters_len > 0) - *(method_parameters_length_addr()) = method_parameters_len; - if (checked_exceptions_len > 0) - *(checked_exceptions_length_addr()) = checked_exceptions_len; - if (exception_table_len > 0) - *(exception_table_length_addr()) = exception_table_len; - if (localvariable_table_len > 0) - *(localvariable_table_length_addr()) = localvariable_table_len; + if (sizes->method_parameters_length() > 0) + *(method_parameters_length_addr()) = sizes->method_parameters_length(); + if (sizes->checked_exceptions_length() > 0) + *(checked_exceptions_length_addr()) = sizes->checked_exceptions_length(); + if (sizes->exception_table_length() > 0) + *(exception_table_length_addr()) = sizes->exception_table_length(); + if (sizes->localvariable_table_length() > 0) + *(localvariable_table_length_addr()) = sizes->localvariable_table_length(); } int ConstMethod::method_parameters_length() const { @@ -307,6 +321,34 @@ ExceptionTableElement* ConstMethod::exception_table_start() const { return (ExceptionTableElement*)addr; } +AnnotationArray** ConstMethod::method_annotations_addr() const { + assert(has_method_annotations(), "should only be called if method annotations are present"); + return (AnnotationArray**)constMethod_end() - 1; +} + +AnnotationArray** ConstMethod::parameter_annotations_addr() const { + assert(has_parameter_annotations(), "should only be called if method parameter annotations are present"); + int offset = 1; + if (has_method_annotations()) offset++; + return (AnnotationArray**)constMethod_end() - offset; +} + +AnnotationArray** ConstMethod::type_annotations_addr() const { + assert(has_type_annotations(), "should only be called if method type annotations are present"); + int offset = 1; + if (has_method_annotations()) offset++; + if (has_parameter_annotations()) offset++; + return (AnnotationArray**)constMethod_end() - offset; +} + +AnnotationArray** ConstMethod::default_annotations_addr() const { + assert(has_default_annotations(), "should only be called if method default annotations are present"); + int offset = 1; + if (has_method_annotations()) offset++; + if (has_parameter_annotations()) offset++; + if (has_type_annotations()) offset++; + return (AnnotationArray**)constMethod_end() - offset; +} // Printing @@ -339,8 +381,25 @@ void ConstMethod::collect_statistics(KlassSizeStats *sz) const { sz->_bytecode_bytes += (n2 = code_size()); sz->_stackmap_bytes += (n3 = sz->count_array(stackmap_data())); - sz->_method_all_bytes += n1 + n3; // note: n2 is part of n3 - sz->_ro_bytes += n1 + n3; + // Count method annotations + int a1 = 0, a2 = 0, a3 = 0, a4 = 0; + if (has_method_annotations()) { + sz->_methods_annotations_bytes += (a1 = sz->count_array(method_annotations())); + } + if (has_parameter_annotations()) { + sz->_methods_parameter_annotations_bytes += (a2 = sz->count_array(parameter_annotations())); + } + if (has_type_annotations()) { + sz->_methods_type_annotations_bytes += (a3 = sz->count_array(type_annotations())); + } + if (has_default_annotations()) { + sz->_methods_default_annotations_bytes += (a4 = sz->count_array(default_annotations())); + } + + int size_annotations = a1 + a2 + a3 + a4; + + sz->_method_all_bytes += n1 + n3 + size_annotations; // note: n2 is part of n3 + sz->_ro_bytes += n1 + n3 + size_annotations; } #endif // INCLUDE_SERVICES @@ -352,10 +411,9 @@ void ConstMethod::verify_on(outputStream* st) { // Verification can occur during oop construction before the method or // other fields have been initialized. - guarantee(is_metadata(), err_msg("Should be metadata " PTR_FORMAT, this)); guarantee(method()->is_method(), "should be method"); - address m_end = (address)((oop*) this + size()); + address m_end = (address)((intptr_t) this + size()); address compressed_table_start = code_end(); guarantee(compressed_table_start <= m_end, "invalid method layout"); address compressed_table_end = compressed_table_start; diff --git a/src/share/vm/oops/constMethod.hpp b/src/share/vm/oops/constMethod.hpp index 12a36c244..21fa344bc 100644 --- a/src/share/vm/oops/constMethod.hpp +++ b/src/share/vm/oops/constMethod.hpp @@ -86,19 +86,22 @@ // | generic signature index (u2) | // | (indexed from start of constMethodOop) | // |------------------------------------------------------| +// | annotations arrays - method, parameter, type, default| +// | pointer to Array if annotation is present | +// |------------------------------------------------------| // // IMPORTANT: If anything gets added here, there need to be changes to // ensure that ServicabilityAgent doesn't get broken as a result! -// Utitily class decribing elements in checked exceptions table inlined in Method*. +// Utility class describing elements in checked exceptions table inlined in Method*. class CheckedExceptionElement VALUE_OBJ_CLASS_SPEC { public: u2 class_cp_index; }; -// Utitily class decribing elements in local variable table inlined in Method*. +// Utility class describing elements in local variable table inlined in Method*. class LocalVariableTableElement VALUE_OBJ_CLASS_SPEC { public: u2 start_bci; @@ -109,7 +112,7 @@ class LocalVariableTableElement VALUE_OBJ_CLASS_SPEC { u2 slot; }; -// Utitily class describing elements in exception table +// Utility class describing elements in exception table class ExceptionTableElement VALUE_OBJ_CLASS_SPEC { public: u2 start_pc; @@ -127,6 +130,51 @@ class MethodParametersElement VALUE_OBJ_CLASS_SPEC { class KlassSizeStats; +// Class to collect the sizes of ConstMethod inline tables +#define INLINE_TABLES_DO(do_element) \ + do_element(localvariable_table_length) \ + do_element(compressed_linenumber_size) \ + do_element(exception_table_length) \ + do_element(checked_exceptions_length) \ + do_element(method_parameters_length) \ + do_element(generic_signature_index) \ + do_element(method_annotations_length) \ + do_element(parameter_annotations_length) \ + do_element(type_annotations_length) \ + do_element(default_annotations_length) + +#define INLINE_TABLE_DECLARE(sym) int _##sym; +#define INLINE_TABLE_PARAM(sym) int sym, +#define INLINE_TABLE_INIT(sym) _##sym(sym), +#define INLINE_TABLE_NULL(sym) _##sym(0), +#define INLINE_TABLE_ACCESSOR(sym) int sym() const { return _##sym; } + +class InlineTableSizes : StackObj { + // declarations + INLINE_TABLES_DO(INLINE_TABLE_DECLARE) + int _end; + public: + InlineTableSizes( + INLINE_TABLES_DO(INLINE_TABLE_PARAM) + int end) : + INLINE_TABLES_DO(INLINE_TABLE_INIT) + _end(end) {} + + // Default constructor for no inlined tables + InlineTableSizes() : + INLINE_TABLES_DO(INLINE_TABLE_NULL) + _end(0) {} + + // Accessors + INLINE_TABLES_DO(INLINE_TABLE_ACCESSOR) +}; +#undef INLINE_TABLE_ACCESSOR +#undef INLINE_TABLE_NULL +#undef INLINE_TABLE_INIT +#undef INLINE_TABLE_PARAM +#undef INLINE_TABLE_DECLARE + + class ConstMethod : public MetaspaceObj { friend class VMStructs; @@ -135,13 +183,17 @@ public: private: enum { - _has_linenumber_table = 1, - _has_checked_exceptions = 2, - _has_localvariable_table = 4, - _has_exception_table = 8, - _has_generic_signature = 16, - _has_method_parameters = 32, - _is_overpass = 64 + _has_linenumber_table = 0x0001, + _has_checked_exceptions = 0x0002, + _has_localvariable_table = 0x0004, + _has_exception_table = 0x0008, + _has_generic_signature = 0x0010, + _has_method_parameters = 0x0020, + _is_overpass = 0x0040, + _has_method_annotations = 0x0080, + _has_parameter_annotations = 0x0100, + _has_type_annotations = 0x0200, + _has_default_annotations = 0x0400 }; // Bit vector of signature @@ -158,8 +210,7 @@ private: Array* _stackmap_data; int _constMethod_size; - jbyte _interpreter_kind; - jbyte _flags; + u2 _flags; // Size of Java bytecodes allocated immediately after Method*. u2 _code_size; @@ -174,36 +225,21 @@ private: // Constructor ConstMethod(int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, MethodType is_overpass, int size); public: static ConstMethod* allocate(ClassLoaderData* loader_data, int byte_code_size, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, MethodType mt, TRAPS); bool is_constMethod() const { return true; } // Inlined tables - void set_inlined_tables_length(u2 generic_signature_index, - int checked_exceptions_len, - int compressed_line_number_size, - int localvariable_table_len, - int exception_table_len, - int method_parameters_length); + void set_inlined_tables_length(InlineTableSizes* sizes); bool has_generic_signature() const { return (_flags & _has_generic_signature) != 0; } @@ -235,10 +271,6 @@ public: } } - - void set_interpreter_kind(int kind) { _interpreter_kind = kind; } - int interpreter_kind(void) const { return _interpreter_kind; } - // constant pool ConstantPool* constants() const { return _constants; } void set_constants(ConstantPool* c) { _constants = c; } @@ -307,12 +339,7 @@ public: } // Size needed - static int size(int code_size, int compressed_line_number_size, - int local_variable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index); + static int size(int code_size, InlineTableSizes* sizes); int size() const { return _constMethod_size;} void set_constMethod_size(int size) { _constMethod_size = size; } @@ -354,6 +381,65 @@ public: int method_parameters_length() const; MethodParametersElement* method_parameters_start() const; + // method annotations + bool has_method_annotations() const + { return (_flags & _has_method_annotations) != 0; } + + bool has_parameter_annotations() const + { return (_flags & _has_parameter_annotations) != 0; } + + bool has_type_annotations() const + { return (_flags & _has_type_annotations) != 0; } + + bool has_default_annotations() const + { return (_flags & _has_default_annotations) != 0; } + + + AnnotationArray** method_annotations_addr() const; + AnnotationArray* method_annotations() const { + return has_method_annotations() ? *(method_annotations_addr()) : NULL; + } + void set_method_annotations(AnnotationArray* anno) { + *(method_annotations_addr()) = anno; + } + + AnnotationArray** parameter_annotations_addr() const; + AnnotationArray* parameter_annotations() const { + return has_parameter_annotations() ? *(parameter_annotations_addr()) : NULL; + } + void set_parameter_annotations(AnnotationArray* anno) { + *(parameter_annotations_addr()) = anno; + } + + AnnotationArray** type_annotations_addr() const; + AnnotationArray* type_annotations() const { + return has_type_annotations() ? *(type_annotations_addr()) : NULL; + } + void set_type_annotations(AnnotationArray* anno) { + *(type_annotations_addr()) = anno; + } + + AnnotationArray** default_annotations_addr() const; + AnnotationArray* default_annotations() const { + return has_default_annotations() ? *(default_annotations_addr()) : NULL; + } + void set_default_annotations(AnnotationArray* anno) { + *(default_annotations_addr()) = anno; + } + + int method_annotations_length() const { + return has_method_annotations() ? method_annotations()->length() : 0; + } + int parameter_annotations_length() const { + return has_parameter_annotations() ? parameter_annotations()->length() : 0; + } + int type_annotations_length() const { + return has_type_annotations() ? type_annotations()->length() : 0; + } + int default_annotations_length() const { + return has_default_annotations() ? default_annotations()->length() : 0; + } + // byte codes void set_code(address code) { if (code_size() > 0) { @@ -409,11 +495,10 @@ private: // First byte after ConstMethod* address constMethod_end() const - { return (address)((oop*)this + _constMethod_size); } + { return (address)((intptr_t*)this + _constMethod_size); } // Last short in ConstMethod* - u2* last_u2_element() const - { return (u2*)constMethod_end() - 1; } + u2* last_u2_element() const; public: // Printing diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp index 4af6e3206..f492b8956 100644 --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -166,20 +166,19 @@ HS_DTRACE_PROBE_DECL5(hotspot, class__initialization__end, volatile int InstanceKlass::_total_instanceKlass_count = 0; Klass* InstanceKlass::allocate_instance_klass(ClassLoaderData* loader_data, - int vtable_len, - int itable_len, - int static_field_size, - int nonstatic_oop_map_size, - ReferenceType rt, - AccessFlags access_flags, - Symbol* name, + int vtable_len, + int itable_len, + int static_field_size, + int nonstatic_oop_map_size, + ReferenceType rt, + AccessFlags access_flags, + Symbol* name, Klass* super_klass, - KlassHandle host_klass, - TRAPS) { + bool is_anonymous, + TRAPS) { int size = InstanceKlass::size(vtable_len, itable_len, nonstatic_oop_map_size, - access_flags.is_interface(), - !host_klass.is_null()); + access_flags.is_interface(), is_anonymous); // Allocation InstanceKlass* ik; @@ -187,25 +186,25 @@ Klass* InstanceKlass::allocate_instance_klass(ClassLoaderData* loader_data, if (name == vmSymbols::java_lang_Class()) { ik = new (loader_data, size, THREAD) InstanceMirrorKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, - access_flags, !host_klass.is_null()); + access_flags, is_anonymous); } else if (name == vmSymbols::java_lang_ClassLoader() || (SystemDictionary::ClassLoader_klass_loaded() && super_klass != NULL && super_klass->is_subtype_of(SystemDictionary::ClassLoader_klass()))) { ik = new (loader_data, size, THREAD) InstanceClassLoaderKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, - access_flags, !host_klass.is_null()); + access_flags, is_anonymous); } else { // normal class ik = new (loader_data, size, THREAD) InstanceKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, - access_flags, !host_klass.is_null()); + access_flags, is_anonymous); } } else { // reference klass ik = new (loader_data, size, THREAD) InstanceRefKlass( vtable_len, itable_len, static_field_size, nonstatic_oop_map_size, rt, - access_flags, !host_klass.is_null()); + access_flags, is_anonymous); } Atomic::inc(&_total_instanceKlass_count); @@ -2793,7 +2792,10 @@ void InstanceKlass::print_on(outputStream* st) const { st->print("%s", source_debug_extension()); st->cr(); } - st->print(BULLET"annotations: "); annotations()->print_value_on(st); st->cr(); + st->print(BULLET"class annotations: "); class_annotations()->print_value_on(st); st->cr(); + st->print(BULLET"class type annotations: "); class_type_annotations()->print_value_on(st); st->cr(); + st->print(BULLET"field annotations: "); fields_annotations()->print_value_on(st); st->cr(); + st->print(BULLET"field type annotations: "); fields_type_annotations()->print_value_on(st); st->cr(); { ResourceMark rm; // PreviousVersionInfo objects returned via PreviousVersionWalker diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp index 025da1282..600cb5646 100644 --- a/src/share/vm/oops/instanceKlass.hpp +++ b/src/share/vm/oops/instanceKlass.hpp @@ -155,8 +155,8 @@ class InstanceKlass: public Klass { ReferenceType rt, AccessFlags access_flags, Symbol* name, - Klass* super_klass, - KlassHandle host_klass, + Klass* super_klass, + bool is_anonymous, TRAPS); InstanceKlass() { assert(DumpSharedSpaces || UseSharedSpaces, "only for CDS"); } @@ -679,19 +679,19 @@ class InstanceKlass: public Klass { // annotations support Annotations* annotations() const { return _annotations; } void set_annotations(Annotations* anno) { _annotations = anno; } + AnnotationArray* class_annotations() const { - if (annotations() == NULL) return NULL; - return annotations()->class_annotations(); + return (_annotations != NULL) ? _annotations->class_annotations() : NULL; } Array* fields_annotations() const { - if (annotations() == NULL) return NULL; - return annotations()->fields_annotations(); + return (_annotations != NULL) ? _annotations->fields_annotations() : NULL; } - Annotations* type_annotations() const { - if (annotations() == NULL) return NULL; - return annotations()->type_annotations(); + AnnotationArray* class_type_annotations() const { + return (_annotations != NULL) ? _annotations->class_type_annotations() : NULL; + } + Array* fields_type_annotations() const { + return (_annotations != NULL) ? _annotations->fields_type_annotations() : NULL; } - // allocation instanceOop allocate_instance(TRAPS); @@ -810,6 +810,7 @@ class InstanceKlass: public Klass { // Sizing (in words) static int header_size() { return align_object_offset(sizeof(InstanceKlass)/HeapWordSize); } + static int size(int vtable_length, int itable_length, int nonstatic_oop_map_size, bool is_interface, bool is_anonymous) { @@ -847,10 +848,14 @@ class InstanceKlass: public Klass { return (OopMapBlock*)(start_of_itable() + align_object_offset(itable_length())); } + Klass** end_of_nonstatic_oop_maps() const { + return (Klass**)(start_of_nonstatic_oop_maps() + + nonstatic_oop_map_count()); + } + Klass** adr_implementor() const { if (is_interface()) { - return (Klass**)(start_of_nonstatic_oop_maps() + - nonstatic_oop_map_count()); + return (Klass**)end_of_nonstatic_oop_maps(); } else { return NULL; } @@ -862,8 +867,7 @@ class InstanceKlass: public Klass { if (adr_impl != NULL) { return adr_impl + 1; } else { - return (Klass**)(start_of_nonstatic_oop_maps() + - nonstatic_oop_map_count()); + return end_of_nonstatic_oop_maps(); } } else { return NULL; diff --git a/src/share/vm/oops/method.cpp b/src/share/vm/oops/method.cpp index f226decae..bb9a51156 100644 --- a/src/share/vm/oops/method.cpp +++ b/src/share/vm/oops/method.cpp @@ -61,24 +61,14 @@ Method* Method::allocate(ClassLoaderData* loader_data, int byte_code_size, AccessFlags access_flags, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, ConstMethod::MethodType method_type, TRAPS) { assert(!access_flags.is_native() || byte_code_size == 0, "native methods should not contain byte codes"); ConstMethod* cm = ConstMethod::allocate(loader_data, byte_code_size, - compressed_line_number_size, - localvariable_table_length, - exception_table_length, - checked_exceptions_length, - method_parameters_length, - generic_signature_index, + sizes, method_type, CHECK_NULL); @@ -317,14 +307,6 @@ Symbol* Method::klass_name() const { } -void Method::set_interpreter_kind() { - int kind = Interpreter::method_kind(this); - assert(kind != Interpreter::invalid, - "interpreter entry must be valid"); - set_interpreter_kind(kind); -} - - // Attempt to return method oop to original state. Clear any pointers // (to objects outside the shared spaces). We won't be able to predict // where they should point in a new JVM. Further initialize some @@ -332,7 +314,6 @@ void Method::set_interpreter_kind() { void Method::remove_unshareable_info() { unlink_method(); - set_interpreter_kind(); } @@ -1045,9 +1026,9 @@ methodHandle Method::make_method_handle_intrinsic(vmIntrinsics::ID iid, methodHandle m; { + InlineTableSizes sizes; Method* m_oop = Method::allocate(loader_data, 0, - accessFlags_from(flags_bits), - 0, 0, 0, 0, 0, 0, + accessFlags_from(flags_bits), &sizes, ConstMethod::NORMAL, CHECK_(empty)); m = methodHandle(THREAD, m_oop); } @@ -1096,22 +1077,35 @@ methodHandle Method::clone_with_new_data(methodHandle m, u_char* new_code, int n assert(!m->is_native(), "cannot rewrite native methods"); // Allocate new Method* AccessFlags flags = m->access_flags(); - u2 generic_signature_index = m->generic_signature_index(); - int checked_exceptions_len = m->checked_exceptions_length(); - int localvariable_len = m->localvariable_table_length(); - int exception_table_len = m->exception_table_length(); - int method_parameters_len = m->method_parameters_length(); + + ConstMethod* cm = m->constMethod(); + int checked_exceptions_len = cm->checked_exceptions_length(); + int localvariable_len = cm->localvariable_table_length(); + int exception_table_len = cm->exception_table_length(); + int method_parameters_len = cm->method_parameters_length(); + int method_annotations_len = cm->method_annotations_length(); + int parameter_annotations_len = cm->parameter_annotations_length(); + int type_annotations_len = cm->type_annotations_length(); + int default_annotations_len = cm->default_annotations_length(); + + InlineTableSizes sizes( + localvariable_len, + new_compressed_linenumber_size, + exception_table_len, + checked_exceptions_len, + method_parameters_len, + cm->generic_signature_index(), + method_annotations_len, + parameter_annotations_len, + type_annotations_len, + default_annotations_len, + 0); ClassLoaderData* loader_data = m->method_holder()->class_loader_data(); Method* newm_oop = Method::allocate(loader_data, new_code_length, flags, - new_compressed_linenumber_size, - localvariable_len, - exception_table_len, - checked_exceptions_len, - method_parameters_len, - generic_signature_index, + &sizes, m->method_type(), CHECK_(methodHandle())); methodHandle newm (THREAD, newm_oop); @@ -1311,29 +1305,6 @@ void Method::print_short_name(outputStream* st) { MethodHandles::print_as_basic_type_signature_on(st, signature(), true); } -// This is only done during class loading, so it is OK to assume method_idnum matches the methods() array -static void reorder_based_on_method_index(Array* methods, - Array* annotations, - GrowableArray* temp_array) { - if (annotations == NULL) { - return; - } - - int length = methods->length(); - int i; - // Copy to temp array - temp_array->clear(); - for (i = 0; i < length; i++) { - temp_array->append(annotations->at(i)); - } - - // Copy back using old method indices - for (i = 0; i < length; i++) { - Method* m = methods->at(i); - annotations->at_put(i, temp_array->at(m->method_idnum())); - } -} - // Comparer for sorting an object array containing // Method*s. static int method_comparator(Method* a, Method* b) { @@ -1341,48 +1312,13 @@ static int method_comparator(Method* a, Method* b) { } // This is only done during class loading, so it is OK to assume method_idnum matches the methods() array -void Method::sort_methods(Array* methods, - Array* methods_annotations, - Array* methods_parameter_annotations, - Array* methods_default_annotations, - Array* methods_type_annotations, - bool idempotent) { +void Method::sort_methods(Array* methods, bool idempotent) { int length = methods->length(); if (length > 1) { - bool do_annotations = false; - if (methods_annotations != NULL || - methods_parameter_annotations != NULL || - methods_default_annotations != NULL || - methods_type_annotations != NULL) { - do_annotations = true; - } - if (do_annotations) { - // Remember current method ordering so we can reorder annotations - for (int i = 0; i < length; i++) { - Method* m = methods->at(i); - m->set_method_idnum(i); - } - } { No_Safepoint_Verifier nsv; QuickSort::sort(methods->data(), length, method_comparator, idempotent); } - - // Sort annotations if necessary - assert(methods_annotations == NULL || methods_annotations->length() == methods->length(), ""); - assert(methods_parameter_annotations == NULL || methods_parameter_annotations->length() == methods->length(), ""); - assert(methods_default_annotations == NULL || methods_default_annotations->length() == methods->length(), ""); - assert(methods_type_annotations == NULL || methods_type_annotations->length() == methods->length(), ""); - if (do_annotations) { - ResourceMark rm; - // Allocate temporary storage - GrowableArray* temp_array = new GrowableArray(length); - reorder_based_on_method_index(methods, methods_annotations, temp_array); - reorder_based_on_method_index(methods, methods_parameter_annotations, temp_array); - reorder_based_on_method_index(methods, methods_default_annotations, temp_array); - reorder_based_on_method_index(methods, methods_type_annotations, temp_array); - } - // Reset method ordering for (int i = 0; i < length; i++) { Method* m = methods->at(i); diff --git a/src/share/vm/oops/method.hpp b/src/share/vm/oops/method.hpp index 39b85569f..a2d24ebca 100644 --- a/src/share/vm/oops/method.hpp +++ b/src/share/vm/oops/method.hpp @@ -101,6 +101,7 @@ class LocalVariableTableElement; class AdapterHandlerEntry; class MethodData; class ConstMethod; +class InlineTableSizes; class KlassSizeStats; class Method : public Metadata { @@ -157,12 +158,7 @@ class Method : public Metadata { static Method* allocate(ClassLoaderData* loader_data, int byte_code_size, AccessFlags access_flags, - int compressed_line_number_size, - int localvariable_table_length, - int exception_table_length, - int checked_exceptions_length, - int method_parameters_length, - u2 generic_signature_index, + InlineTableSizes* sizes, ConstMethod::MethodType method_type, TRAPS); @@ -207,33 +203,17 @@ class Method : public Metadata { // annotations support AnnotationArray* annotations() const { - InstanceKlass* ik = method_holder(); - if (ik->annotations() == NULL) { - return NULL; - } - return ik->annotations()->get_method_annotations_of(method_idnum()); + return constMethod()->method_annotations(); } AnnotationArray* parameter_annotations() const { - InstanceKlass* ik = method_holder(); - if (ik->annotations() == NULL) { - return NULL; - } - return ik->annotations()->get_method_parameter_annotations_of(method_idnum()); + return constMethod()->parameter_annotations(); } AnnotationArray* annotation_default() const { - InstanceKlass* ik = method_holder(); - if (ik->annotations() == NULL) { - return NULL; - } - return ik->annotations()->get_method_default_annotations_of(method_idnum()); + return constMethod()->default_annotations(); + } + AnnotationArray* type_annotations() const { + return constMethod()->type_annotations(); } - AnnotationArray* type_annotations() const { - InstanceKlass* ik = method_holder(); - Annotations* type_annos = ik->type_annotations(); - if (type_annos == NULL) - return NULL; - return type_annos->get_method_annotations_of(method_idnum()); -} #ifdef CC_INTERP void set_result_index(BasicType type); @@ -439,13 +419,6 @@ class Method : public Metadata { address interpreter_entry() const { return _i2i_entry; } // Only used when first initialize so we can set _i2i_entry and _from_interpreted_entry void set_interpreter_entry(address entry) { _i2i_entry = entry; _from_interpreted_entry = entry; } - int interpreter_kind(void) { - return constMethod()->interpreter_kind(); - } - void set_interpreter_kind(); - void set_interpreter_kind(int kind) { - constMethod()->set_interpreter_kind(kind); - } // native function (used for native methods only) enum { @@ -808,12 +781,7 @@ class Method : public Metadata { #endif // Helper routine used for method sorting - static void sort_methods(Array* methods, - Array* methods_annotations, - Array* methods_parameter_annotations, - Array* methods_default_annotations, - Array* methods_type_annotations, - bool idempotent = false); + static void sort_methods(Array* methods, bool idempotent = false); // Deallocation function for redefine classes or if an error occurs void deallocate_contents(ClassLoaderData* loader_data); diff --git a/src/share/vm/prims/jvm.cpp b/src/share/vm/prims/jvm.cpp index da5ec21f2..1c277b76c 100644 --- a/src/share/vm/prims/jvm.cpp +++ b/src/share/vm/prims/jvm.cpp @@ -1573,9 +1573,9 @@ JVM_ENTRY(jbyteArray, JVM_GetClassTypeAnnotations(JNIEnv *env, jclass cls)) if (!java_lang_Class::is_primitive(JNIHandles::resolve(cls))) { Klass* k = java_lang_Class::as_Klass(JNIHandles::resolve(cls)); if (k->oop_is_instance()) { - Annotations* type_annotations = InstanceKlass::cast(k)->type_annotations(); + AnnotationArray* type_annotations = InstanceKlass::cast(k)->class_type_annotations(); if (type_annotations != NULL) { - typeArrayOop a = Annotations::make_java_array(type_annotations->class_annotations(), CHECK_NULL); + typeArrayOop a = Annotations::make_java_array(type_annotations, CHECK_NULL); return (jbyteArray) JNIHandles::make_local(env, a); } } diff --git a/src/share/vm/prims/jvmtiRedefineClasses.cpp b/src/share/vm/prims/jvmtiRedefineClasses.cpp index 8e67f08b0..14cb63c60 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.cpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.cpp @@ -492,26 +492,6 @@ int VM_RedefineClasses::find_or_append_indirect_entry(constantPoolHandle scratch } // end find_or_append_indirect_entry() -void VM_RedefineClasses::swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class, TRAPS) { - AnnotationArray* save; - - Annotations* sca = scratch_class->annotations(); - if (sca == NULL) return; - - save = sca->get_method_annotations_of(i); - sca->set_method_annotations_of(scratch_class, i, sca->get_method_annotations_of(j), CHECK); - sca->set_method_annotations_of(scratch_class, j, save, CHECK); - - save = sca->get_method_parameter_annotations_of(i); - sca->set_method_parameter_annotations_of(scratch_class, i, sca->get_method_parameter_annotations_of(j), CHECK); - sca->set_method_parameter_annotations_of(scratch_class, j, save, CHECK); - - save = sca->get_method_default_annotations_of(i); - sca->set_method_default_annotations_of(scratch_class, i, sca->get_method_default_annotations_of(j), CHECK); - sca->set_method_default_annotations_of(scratch_class, j, save, CHECK); -} - - jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( instanceKlassHandle the_class, instanceKlassHandle scratch_class) { @@ -693,10 +673,9 @@ jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( idnum_owner->set_method_idnum(new_num); } k_new_method->set_method_idnum(old_num); - swap_all_method_annotations(old_num, new_num, scratch_class, thread); - if (thread->has_pending_exception()) { - return JVMTI_ERROR_OUT_OF_MEMORY; - } + if (thread->has_pending_exception()) { + return JVMTI_ERROR_OUT_OF_MEMORY; + } } } RC_TRACE(0x00008000, ("Method matched: new: %s [%d] == old: %s [%d]", @@ -729,7 +708,6 @@ jvmtiError VM_RedefineClasses::compare_and_normalize_class_versions( idnum_owner->set_method_idnum(new_num); } k_new_method->set_method_idnum(num); - swap_all_method_annotations(new_num, num, scratch_class, thread); if (thread->has_pending_exception()) { return JVMTI_ERROR_OUT_OF_MEMORY; } @@ -1895,10 +1873,7 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_element_value( bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations( instanceKlassHandle scratch_class, TRAPS) { - Annotations* sca = scratch_class->annotations(); - if (sca == NULL) return true; - - Array* fields_annotations = sca->fields_annotations(); + Array* fields_annotations = scratch_class->fields_annotations(); if (fields_annotations == NULL || fields_annotations->length() == 0) { // no fields_annotations so nothing to do @@ -1933,21 +1908,10 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_fields_annotations( bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations( instanceKlassHandle scratch_class, TRAPS) { - Annotations* sca = scratch_class->annotations(); - if (sca == NULL) return true; + for (int i = 0; i < scratch_class->methods()->length(); i++) { + Method* m = scratch_class->methods()->at(i); + AnnotationArray* method_annotations = m->constMethod()->method_annotations(); - Array* methods_annotations = sca->methods_annotations(); - - if (methods_annotations == NULL || methods_annotations->length() == 0) { - // no methods_annotations so nothing to do - return true; - } - - RC_TRACE_WITH_THREAD(0x02000000, THREAD, - ("methods_annotations length=%d", methods_annotations->length())); - - for (int i = 0; i < methods_annotations->length(); i++) { - AnnotationArray* method_annotations = methods_annotations->at(i); if (method_annotations == NULL || method_annotations->length() == 0) { // this method does not have any annotations so skip it continue; @@ -1983,24 +1947,9 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_methods_annotations( bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations( instanceKlassHandle scratch_class, TRAPS) { - Annotations* sca = scratch_class->annotations(); - if (sca == NULL) return true; - - Array* methods_parameter_annotations = - sca->methods_parameter_annotations(); - - if (methods_parameter_annotations == NULL - || methods_parameter_annotations->length() == 0) { - // no methods_parameter_annotations so nothing to do - return true; - } - - RC_TRACE_WITH_THREAD(0x02000000, THREAD, - ("methods_parameter_annotations length=%d", - methods_parameter_annotations->length())); - - for (int i = 0; i < methods_parameter_annotations->length(); i++) { - AnnotationArray* method_parameter_annotations = methods_parameter_annotations->at(i); + for (int i = 0; i < scratch_class->methods()->length(); i++) { + Method* m = scratch_class->methods()->at(i); + AnnotationArray* method_parameter_annotations = m->constMethod()->parameter_annotations(); if (method_parameter_annotations == NULL || method_parameter_annotations->length() == 0) { // this method does not have any parameter annotations so skip it @@ -2050,24 +1999,9 @@ bool VM_RedefineClasses::rewrite_cp_refs_in_methods_parameter_annotations( bool VM_RedefineClasses::rewrite_cp_refs_in_methods_default_annotations( instanceKlassHandle scratch_class, TRAPS) { - Annotations* sca = scratch_class->annotations(); - if (sca == NULL) return true; - - Array* methods_default_annotations = - sca->methods_default_annotations(); - - if (methods_default_annotations == NULL - || methods_default_annotations->length() == 0) { - // no methods_default_annotations so nothing to do - return true; - } - - RC_TRACE_WITH_THREAD(0x02000000, THREAD, - ("methods_default_annotations length=%d", - methods_default_annotations->length())); - - for (int i = 0; i < methods_default_annotations->length(); i++) { - AnnotationArray* method_default_annotations = methods_default_annotations->at(i); + for (int i = 0; i < scratch_class->methods()->length(); i++) { + Method* m = scratch_class->methods()->at(i); + AnnotationArray* method_default_annotations = m->constMethod()->default_annotations(); if (method_default_annotations == NULL || method_default_annotations->length() == 0) { // this method does not have any default annotations so skip it @@ -3072,6 +3006,31 @@ void VM_RedefineClasses::compute_added_deleted_matching_methods() { } +void VM_RedefineClasses::swap_annotations(instanceKlassHandle the_class, + instanceKlassHandle scratch_class) { + // Since there is currently no rewriting of type annotations indexes + // into the CP, we null out type annotations on scratch_class before + // we swap annotations with the_class rather than facing the + // possibility of shipping annotations with broken indexes to + // Java-land. + ClassLoaderData* loader_data = scratch_class->class_loader_data(); + AnnotationArray* new_class_type_annotations = scratch_class->class_type_annotations(); + if (new_class_type_annotations != NULL) { + MetadataFactory::free_array(loader_data, new_class_type_annotations); + scratch_class->annotations()->set_class_type_annotations(NULL); + } + Array* new_field_type_annotations = scratch_class->fields_type_annotations(); + if (new_field_type_annotations != NULL) { + Annotations::free_contents(loader_data, new_field_type_annotations); + scratch_class->annotations()->set_fields_type_annotations(NULL); + } + + // Swap annotation fields values + Annotations* old_annotations = the_class->annotations(); + the_class->set_annotations(scratch_class->annotations()); + scratch_class->set_annotations(old_annotations); +} + // Install the redefinition of a class: // - house keeping (flushing breakpoints and caches, deoptimizing @@ -3282,23 +3241,7 @@ void VM_RedefineClasses::redefine_single_class(jclass the_jclass, the_class->set_access_flags(flags); } - // Since there is currently no rewriting of type annotations indexes - // into the CP, we null out type annotations on scratch_class before - // we swap annotations with the_class rather than facing the - // possibility of shipping annotations with broken indexes to - // Java-land. - Annotations* new_annotations = scratch_class->annotations(); - if (new_annotations != NULL) { - Annotations* new_type_annotations = new_annotations->type_annotations(); - if (new_type_annotations != NULL) { - MetadataFactory::free_metadata(scratch_class->class_loader_data(), new_type_annotations); - new_annotations->set_type_annotations(NULL); - } - } - // Swap annotation fields values - Annotations* old_annotations = the_class->annotations(); - the_class->set_annotations(scratch_class->annotations()); - scratch_class->set_annotations(old_annotations); + swap_annotations(the_class, scratch_class); // Replace minor version number of class file u2 old_minor_version = the_class->minor_version(); diff --git a/src/share/vm/prims/jvmtiRedefineClasses.hpp b/src/share/vm/prims/jvmtiRedefineClasses.hpp index 22dd343f6..d29674825 100644 --- a/src/share/vm/prims/jvmtiRedefineClasses.hpp +++ b/src/share/vm/prims/jvmtiRedefineClasses.hpp @@ -384,11 +384,6 @@ class VM_RedefineClasses: public VM_Operation { jvmtiError compare_and_normalize_class_versions( instanceKlassHandle the_class, instanceKlassHandle scratch_class); - // Swap annotations[i] with annotations[j] - // Used by compare_and_normalize_class_versions() when normalizing - // overloaded methods or changing idnum as when adding or deleting methods. - void swap_all_method_annotations(int i, int j, instanceKlassHandle scratch_class, TRAPS); - // Figure out which new methods match old methods in name and signature, // which methods have been added, and which are no longer present void compute_added_deleted_matching_methods(); @@ -417,6 +412,9 @@ class VM_RedefineClasses: public VM_Operation { void redefine_single_class(jclass the_jclass, Klass* scratch_class_oop, TRAPS); + void swap_annotations(instanceKlassHandle new_class, + instanceKlassHandle scratch_class); + // Increment the classRedefinedCount field in the specific InstanceKlass // and in all direct and indirect subclasses. void increment_class_counter(InstanceKlass *ik, TRAPS); diff --git a/src/share/vm/runtime/fieldDescriptor.cpp b/src/share/vm/runtime/fieldDescriptor.cpp index 54a225a5f..23d679494 100644 --- a/src/share/vm/runtime/fieldDescriptor.cpp +++ b/src/share/vm/runtime/fieldDescriptor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,13 +67,10 @@ AnnotationArray* fieldDescriptor::annotations() const { AnnotationArray* fieldDescriptor::type_annotations() const { InstanceKlass* ik = field_holder(); - Annotations* type_annos = ik->type_annotations(); + Array* type_annos = ik->fields_type_annotations(); if (type_annos == NULL) return NULL; - Array* md = type_annos->fields_annotations(); - if (md == NULL) - return NULL; - return md->at(index()); + return type_annos->at(index()); } constantTag fieldDescriptor::initial_value_tag() const { diff --git a/src/share/vm/runtime/vmStructs.cpp b/src/share/vm/runtime/vmStructs.cpp index 86169fcfc..d21c2c1bb 100644 --- a/src/share/vm/runtime/vmStructs.cpp +++ b/src/share/vm/runtime/vmStructs.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -366,11 +366,10 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; volatile_nonstatic_field(Method, _from_compiled_entry, address) \ volatile_nonstatic_field(Method, _from_interpreted_entry, address) \ volatile_nonstatic_field(ConstMethod, _fingerprint, uint64_t) \ - nonstatic_field(ConstMethod, _constants, ConstantPool*) \ + nonstatic_field(ConstMethod, _constants, ConstantPool*) \ nonstatic_field(ConstMethod, _stackmap_data, Array*) \ nonstatic_field(ConstMethod, _constMethod_size, int) \ - nonstatic_field(ConstMethod, _interpreter_kind, jbyte) \ - nonstatic_field(ConstMethod, _flags, jbyte) \ + nonstatic_field(ConstMethod, _flags, u2) \ nonstatic_field(ConstMethod, _code_size, u2) \ nonstatic_field(ConstMethod, _name_index, u2) \ nonstatic_field(ConstMethod, _signature_index, u2) \ @@ -2261,14 +2260,18 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; declare_constant(Klass::_lh_array_tag_obj_value) \ \ /********************************/ \ - /* ConstMethod anon-enum */ \ + /* ConstMethod anon-enum */ \ /********************************/ \ \ - declare_constant(ConstMethod::_has_linenumber_table) \ - declare_constant(ConstMethod::_has_checked_exceptions) \ - declare_constant(ConstMethod::_has_localvariable_table) \ - declare_constant(ConstMethod::_has_exception_table) \ - declare_constant(ConstMethod::_has_generic_signature) \ + declare_constant(ConstMethod::_has_linenumber_table) \ + declare_constant(ConstMethod::_has_checked_exceptions) \ + declare_constant(ConstMethod::_has_localvariable_table) \ + declare_constant(ConstMethod::_has_exception_table) \ + declare_constant(ConstMethod::_has_generic_signature) \ + declare_constant(ConstMethod::_has_method_annotations) \ + declare_constant(ConstMethod::_has_parameter_annotations) \ + declare_constant(ConstMethod::_has_default_annotations) \ + declare_constant(ConstMethod::_has_type_annotations) \ \ /*************************************/ \ /* InstanceKlass enum */ \ diff --git a/test/runtime/8007320/ConstMethodTest.java b/test/runtime/8007320/ConstMethodTest.java new file mode 100644 index 000000000..68ca8dcb2 --- /dev/null +++ b/test/runtime/8007320/ConstMethodTest.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 8007320 + * @summary Test all optional fields in ConstMethod + * @compile -g -parameters ConstMethodTest.java + * @run main ConstMethodTest + */ + +import java.util.*; +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.io.Serializable; + +@Retention(RetentionPolicy.RUNTIME) +@interface MyAnnotation { + public String name(); + public String value(); + public String date() default "today"; +} + +@Target(ElementType.TYPE_USE) +@Retention(RetentionPolicy.RUNTIME) +@interface TypeAnno { + String value(); +} + +@Target(ElementType.TYPE_USE) +@Retention(RetentionPolicy.RUNTIME) +@interface TypeAnno2 { + String value(); +} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.PARAMETER) +@interface Named { + String value(); +} + +@Retention(RetentionPolicy.RUNTIME) +@interface ScalarTypesWithDefault { + byte b() default 11; + short s() default 12; + int i() default 13; + long l() default 14; + char c() default 'V'; +} + +// Some exception class +class OkException extends RuntimeException {}; + + +@MyAnnotation(name="someName", value = "Hello World") +public class ConstMethodTest { + + private static void check(boolean b) { + if (!b) + throw new RuntimeException(); + } + private static void fail(String msg) { + System.err.println(msg); + throw new RuntimeException(); + } + private static void equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) { + } else { + fail(x + " not equal to " + y); + } + } + private static final String[] parameter_names = { + "parameter", "parameter2", "x" + }; + + // Declare a function with everything in it. + @MyAnnotation(name="someName", value="Hello World") + static void kitchenSinkFunc(@Named(value="aName") String parameter, + @Named("bName") String parameter2, + @ScalarTypesWithDefault T x) + throws @TypeAnno("RE") @TypeAnno2("RE2") RuntimeException, + NullPointerException, + @TypeAnno("AIOOBE") ArrayIndexOutOfBoundsException { + int i, j, k; + try { + System.out.println("calling kitchenSinkFunc " + parameter); + throw new OkException(); // to see stack trace with line numbers + } catch (Exception e) { + e.printStackTrace(); + } + } + + private static void test1() throws Throwable { + for (Method m : ConstMethodTest.class.getDeclaredMethods()) { + if (m.getName().equals("kitchenSinkFunc")) { + Annotation[][] ann = m.getParameterAnnotations(); + equal(ann.length, 3); + Annotation foo = ann[0][0]; + Annotation bar = ann[1][0]; + equal(foo.toString(), "@Named(value=aName)"); + equal(bar.toString(), "@Named(value=bName)"); + check(foo.equals(foo)); + check(bar.equals(bar)); + check(! foo.equals(bar)); + // method annotations + Annotation[] ann2 = m.getAnnotations(); + equal(ann2.length, 1); + Annotation mann = ann2[0]; + equal(mann.toString(), "@MyAnnotation(date=today, name=someName, value=Hello World)"); + // Test Method parameter names + Parameter[] parameters = m.getParameters(); + if(parameters == null) + throw new Exception("getParameters should never be null"); + for(int i = 0; i < parameters.length; i++) { + Parameter p = parameters[i]; + equal(parameters[i].getName(), parameter_names[i]); + } + } + } + } + + public static void main(java.lang.String[] unused) throws Throwable { + // pass 5 so kitchenSinkFunc is instantiated with an int + kitchenSinkFunc("parameter", "param2", 5); + test1(); + } +}; + -- cgit v1.2.3 From 0f1383d455e470294e63ad3f52df70483c164c60 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 11 Feb 2013 14:47:04 -0800 Subject: 8003252: PPC: move MacroAssembler into separate file Summary: moved MacroAssembler into separate file Reviewed-by: twisti, kvn, dlong --- src/share/vm/asm/macroAssembler.hpp | 2 +- src/share/vm/asm/macroAssembler.inline.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/share/vm/asm/macroAssembler.hpp b/src/share/vm/asm/macroAssembler.hpp index 3ad5e6df8..96c6b01dc 100644 --- a/src/share/vm/asm/macroAssembler.hpp +++ b/src/share/vm/asm/macroAssembler.hpp @@ -40,7 +40,7 @@ # include "macroAssembler_arm.hpp" #endif #ifdef TARGET_ARCH_ppc -# include "assembler_ppc.hpp" +# include "macroAssembler_ppc.hpp" #endif #endif // SHARE_VM_ASM_MACROASSEMBLER_HPP diff --git a/src/share/vm/asm/macroAssembler.inline.hpp b/src/share/vm/asm/macroAssembler.inline.hpp index 67886f6f1..e61fc37d9 100644 --- a/src/share/vm/asm/macroAssembler.inline.hpp +++ b/src/share/vm/asm/macroAssembler.inline.hpp @@ -40,7 +40,7 @@ # include "macroAssembler_arm.inline.hpp" #endif #ifdef TARGET_ARCH_ppc -# include "assembler_ppc.inline.hpp" +# include "macroAssembler_ppc.inline.hpp" #endif #endif // SHARE_VM_ASM_MACROASSEMBLER_INLINE_HPP -- cgit v1.2.3 From 9b4b3bc895784f209f493034253f7562ba9ad42f Mon Sep 17 00:00:00 2001 From: roland Date: Tue, 12 Feb 2013 12:56:11 +0100 Subject: 7197327: 40% regression on 8 b41 comp 8 b40 on specjvm2008.mpegaudio on oob Summary: Add support for expensive nodes. Reviewed-by: kvn --- src/share/vm/opto/c2_globals.hpp | 3 + src/share/vm/opto/compile.cpp | 144 ++++++++++++++++++++++ src/share/vm/opto/compile.hpp | 23 ++++ src/share/vm/opto/library_call.cpp | 8 +- src/share/vm/opto/loopnode.cpp | 242 ++++++++++++++++++++++++++++++++++--- src/share/vm/opto/loopnode.hpp | 26 +++- src/share/vm/opto/node.cpp | 15 +++ src/share/vm/opto/node.hpp | 7 +- src/share/vm/opto/phaseX.cpp | 3 + src/share/vm/opto/subnode.hpp | 10 +- 10 files changed, 456 insertions(+), 25 deletions(-) diff --git a/src/share/vm/opto/c2_globals.hpp b/src/share/vm/opto/c2_globals.hpp index e18912261..43895fc6e 100644 --- a/src/share/vm/opto/c2_globals.hpp +++ b/src/share/vm/opto/c2_globals.hpp @@ -618,6 +618,9 @@ \ product(intx, LiveNodeCountInliningCutoff, 20000, \ "max number of live nodes in a method") \ + \ + diagnostic(bool, OptimizeExpensiveOps, true, \ + "Find best control for expensive operations") \ C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG) diff --git a/src/share/vm/opto/compile.cpp b/src/share/vm/opto/compile.cpp index ae9d6996a..4b8048386 100644 --- a/src/share/vm/opto/compile.cpp +++ b/src/share/vm/opto/compile.cpp @@ -409,6 +409,13 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) { remove_macro_node(n); } } + // Remove useless expensive node + for (int i = C->expensive_count()-1; i >= 0; i--) { + Node* n = C->expensive_node(i); + if (!useful.member(n)) { + remove_expensive_node(n); + } + } // clean up the late inline lists remove_useless_late_inlines(&_string_late_inlines, useful); remove_useless_late_inlines(&_late_inlines, useful); @@ -1061,6 +1068,7 @@ void Compile::Init(int aliaslevel) { _intrinsics = NULL; _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); register_library_intrinsics(); } @@ -1927,6 +1935,10 @@ void Compile::Optimize() { if (failing()) return; + // No more new expensive nodes will be added to the list from here + // so keep only the actual candidates for optimizations. + cleanup_expensive_nodes(igvn); + // Perform escape analysis if (_do_escape_analysis && ConnectionGraph::has_candidates(this)) { if (has_loops()) { @@ -3010,6 +3022,15 @@ bool Compile::final_graph_reshaping() { return true; } + // Expensive nodes have their control input set to prevent the GVN + // from freely commoning them. There's no GVN beyond this point so + // no need to keep the control input. We want the expensive nodes to + // be freely moved to the least frequent code path by gcm. + assert(OptimizeExpensiveOps || expensive_count() == 0, "optimization off but list non empty?"); + for (int i = 0; i < expensive_count(); i++) { + _expensive_nodes->at(i)->set_req(0, NULL); + } + Final_Reshape_Counts frc; // Visit everybody reachable! @@ -3525,3 +3546,126 @@ void Compile::dump_inlining() { } } } + +int Compile::cmp_expensive_nodes(Node* n1, Node* n2) { + if (n1->Opcode() < n2->Opcode()) return -1; + else if (n1->Opcode() > n2->Opcode()) return 1; + + assert(n1->req() == n2->req(), err_msg_res("can't compare %s nodes: n1->req() = %d, n2->req() = %d", NodeClassNames[n1->Opcode()], n1->req(), n2->req())); + for (uint i = 1; i < n1->req(); i++) { + if (n1->in(i) < n2->in(i)) return -1; + else if (n1->in(i) > n2->in(i)) return 1; + } + + return 0; +} + +int Compile::cmp_expensive_nodes(Node** n1p, Node** n2p) { + Node* n1 = *n1p; + Node* n2 = *n2p; + + return cmp_expensive_nodes(n1, n2); +} + +void Compile::sort_expensive_nodes() { + if (!expensive_nodes_sorted()) { + _expensive_nodes->sort(cmp_expensive_nodes); + } +} + +bool Compile::expensive_nodes_sorted() const { + for (int i = 1; i < _expensive_nodes->length(); i++) { + if (cmp_expensive_nodes(_expensive_nodes->adr_at(i), _expensive_nodes->adr_at(i-1)) < 0) { + return false; + } + } + return true; +} + +bool Compile::should_optimize_expensive_nodes(PhaseIterGVN &igvn) { + if (_expensive_nodes->length() == 0) { + return false; + } + + assert(OptimizeExpensiveOps, "optimization off?"); + + // Take this opportunity to remove dead nodes from the list + int j = 0; + for (int i = 0; i < _expensive_nodes->length(); i++) { + Node* n = _expensive_nodes->at(i); + if (!n->is_unreachable(igvn)) { + assert(n->is_expensive(), "should be expensive"); + _expensive_nodes->at_put(j, n); + j++; + } + } + _expensive_nodes->trunc_to(j); + + // Then sort the list so that similar nodes are next to each other + // and check for at least two nodes of identical kind with same data + // inputs. + sort_expensive_nodes(); + + for (int i = 0; i < _expensive_nodes->length()-1; i++) { + if (cmp_expensive_nodes(_expensive_nodes->adr_at(i), _expensive_nodes->adr_at(i+1)) == 0) { + return true; + } + } + + return false; +} + +void Compile::cleanup_expensive_nodes(PhaseIterGVN &igvn) { + if (_expensive_nodes->length() == 0) { + return; + } + + assert(OptimizeExpensiveOps, "optimization off?"); + + // Sort to bring similar nodes next to each other and clear the + // control input of nodes for which there's only a single copy. + sort_expensive_nodes(); + + int j = 0; + int identical = 0; + int i = 0; + for (; i < _expensive_nodes->length()-1; i++) { + assert(j <= i, "can't write beyond current index"); + if (_expensive_nodes->at(i)->Opcode() == _expensive_nodes->at(i+1)->Opcode()) { + identical++; + _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); + continue; + } + if (identical > 0) { + _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); + identical = 0; + } else { + Node* n = _expensive_nodes->at(i); + igvn.hash_delete(n); + n->set_req(0, NULL); + igvn.hash_insert(n); + } + } + if (identical > 0) { + _expensive_nodes->at_put(j++, _expensive_nodes->at(i)); + } else if (_expensive_nodes->length() >= 1) { + Node* n = _expensive_nodes->at(i); + igvn.hash_delete(n); + n->set_req(0, NULL); + igvn.hash_insert(n); + } + _expensive_nodes->trunc_to(j); +} + +void Compile::add_expensive_node(Node * n) { + assert(!_expensive_nodes->contains(n), "duplicate entry in expensive list"); + assert(n->is_expensive(), "expensive nodes with non-null control here only"); + assert(!n->is_CFG() && !n->is_Mem(), "no cfg or memory nodes here"); + if (OptimizeExpensiveOps) { + _expensive_nodes->append(n); + } else { + // Clear control input and let IGVN optimize expensive nodes if + // OptimizeExpensiveOps is off. + n->set_req(0, NULL); + } +} diff --git a/src/share/vm/opto/compile.hpp b/src/share/vm/opto/compile.hpp index 58482956a..2cb12fa2e 100644 --- a/src/share/vm/opto/compile.hpp +++ b/src/share/vm/opto/compile.hpp @@ -314,6 +314,7 @@ class Compile : public Phase { GrowableArray* _intrinsics; // List of intrinsics. 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 ConnectionGraph* _congraph; #ifndef PRODUCT IdealGraphPrinter* _printer; @@ -398,6 +399,13 @@ class Compile : public Phase { GrowableArray* _print_inlining_list; int _print_inlining; + // Only keep nodes in the expensive node list that need to be optimized + void cleanup_expensive_nodes(PhaseIterGVN &igvn); + // Use for sorting expensive nodes to bring similar nodes together + static int cmp_expensive_nodes(Node** n1, Node** n2); + // Expensive nodes list already sorted? + bool expensive_nodes_sorted() const; + public: outputStream* print_inlining_stream() const { @@ -573,8 +581,10 @@ class Compile : public Phase { int macro_count() { return _macro_nodes->length(); } int predicate_count() { return _predicate_opaqs->length();} + int expensive_count() { return _expensive_nodes->length(); } Node* macro_node(int idx) { return _macro_nodes->at(idx); } Node* predicate_opaque1_node(int idx) { return _predicate_opaqs->at(idx);} + Node* expensive_node(int idx) { return _expensive_nodes->at(idx); } ConnectionGraph* congraph() { return _congraph;} void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;} void add_macro_node(Node * n) { @@ -592,6 +602,12 @@ class Compile : public Phase { _predicate_opaqs->remove(n); } } + void add_expensive_node(Node * n); + void remove_expensive_node(Node * n) { + if (_expensive_nodes->contains(n)) { + _expensive_nodes->remove(n); + } + } void add_predicate_opaq(Node * n) { assert(!_predicate_opaqs->contains(n), " duplicate entry in predicate opaque1"); assert(_macro_nodes->contains(n), "should have already been in macro list"); @@ -604,6 +620,13 @@ class Compile : public Phase { return _predicate_opaqs->contains(n); } + // Are there candidate expensive nodes for optimization? + bool should_optimize_expensive_nodes(PhaseIterGVN &igvn); + // Check whether n1 and n2 are similar + static int cmp_expensive_nodes(Node* n1, Node* n2); + // Sort expensive nodes to locate similar expensive nodes + void sort_expensive_nodes(); + // Compilation environment. Arena* comp_arena() { return &_comp_arena; } ciEnv* env() const { return _env; } diff --git a/src/share/vm/opto/library_call.cpp b/src/share/vm/opto/library_call.cpp index e3aee707e..3969f383f 100644 --- a/src/share/vm/opto/library_call.cpp +++ b/src/share/vm/opto/library_call.cpp @@ -1653,7 +1653,7 @@ void LibraryCallKit::finish_pow_exp(Node* result, Node* x, Node* y, const TypeFu // really odd corner cases (+/- Infinity). Just uncommon-trap them. bool LibraryCallKit::inline_exp() { Node* arg = round_double_node(argument(0)); - Node* n = _gvn.transform(new (C) ExpDNode(0, arg)); + Node* n = _gvn.transform(new (C) ExpDNode(C, control(), arg)); finish_pow_exp(n, arg, NULL, OptoRuntime::Math_D_D_Type(), CAST_FROM_FN_PTR(address, SharedRuntime::dexp), "EXP"); @@ -1688,7 +1688,7 @@ bool LibraryCallKit::inline_pow() { if (!too_many_traps(Deoptimization::Reason_intrinsic)) { // Short form: skip the fancy tests and just check for NaN result. - result = _gvn.transform(new (C) PowDNode(0, x, y)); + result = _gvn.transform(new (C) PowDNode(C, control(), x, y)); } else { // If this inlining ever returned NaN in the past, include all // checks + call to the runtime. @@ -1715,7 +1715,7 @@ bool LibraryCallKit::inline_pow() { Node *complex_path = _gvn.transform( new (C) IfTrueNode(if1) ); // Set fast path result - Node *fast_result = _gvn.transform( new (C) PowDNode(0, x, y) ); + Node *fast_result = _gvn.transform( new (C) PowDNode(C, control(), x, y) ); phi->init_req(3, fast_result); // Complex path @@ -1775,7 +1775,7 @@ bool LibraryCallKit::inline_pow() { // abs(x) Node *absx=_gvn.transform( new (C) AbsDNode(x)); // abs(x)^y - Node *absxpowy = _gvn.transform( new (C) PowDNode(0, absx, y) ); + Node *absxpowy = _gvn.transform( new (C) PowDNode(C, control(), absx, y) ); // -abs(x)^y Node *negabsxpowy = _gvn.transform(new (C) NegDNode (absxpowy)); // (1&(long)y)==1?-DPow(abs(x), y):DPow(abs(x), y) diff --git a/src/share/vm/opto/loopnode.cpp b/src/share/vm/opto/loopnode.cpp index a483f0180..6812c62fa 100644 --- a/src/share/vm/opto/loopnode.cpp +++ b/src/share/vm/opto/loopnode.cpp @@ -88,9 +88,9 @@ Node *PhaseIdealLoop::get_early_ctrl( Node *n ) { assert( !n->is_Phi() && !n->is_CFG(), "this code only handles data nodes" ); uint i; Node *early; - if( n->in(0) ) { + if (n->in(0) && !n->is_expensive()) { early = n->in(0); - if( !early->is_CFG() ) // Might be a non-CFG multi-def + if (!early->is_CFG()) // Might be a non-CFG multi-def early = get_ctrl(early); // So treat input as a straight data input i = 1; } else { @@ -99,28 +99,28 @@ Node *PhaseIdealLoop::get_early_ctrl( Node *n ) { } uint e_d = dom_depth(early); assert( early, "" ); - for( ; i < n->req(); i++ ) { + for (; i < n->req(); i++) { Node *cin = get_ctrl(n->in(i)); assert( cin, "" ); // Keep deepest dominator depth uint c_d = dom_depth(cin); - if( c_d > e_d ) { // Deeper guy? + if (c_d > e_d) { // Deeper guy? early = cin; // Keep deepest found so far e_d = c_d; - } else if( c_d == e_d && // Same depth? - early != cin ) { // If not equal, must use slower algorithm + } else if (c_d == e_d && // Same depth? + early != cin) { // If not equal, must use slower algorithm // If same depth but not equal, one _must_ dominate the other // and we want the deeper (i.e., dominated) guy. Node *n1 = early; Node *n2 = cin; - while( 1 ) { + while (1) { n1 = idom(n1); // Walk up until break cycle n2 = idom(n2); - if( n1 == cin || // Walked early up to cin - dom_depth(n2) < c_d ) + if (n1 == cin || // Walked early up to cin + dom_depth(n2) < c_d) break; // early is deeper; keep him - if( n2 == early || // Walked cin up to early - dom_depth(n1) < c_d ) { + if (n2 == early || // Walked cin up to early + dom_depth(n1) < c_d) { early = cin; // cin is deeper; keep him break; } @@ -132,9 +132,108 @@ Node *PhaseIdealLoop::get_early_ctrl( Node *n ) { // Return earliest legal location assert(early == find_non_split_ctrl(early), "unexpected early control"); + if (n->is_expensive()) { + assert(n->in(0), "should have control input"); + early = get_early_ctrl_for_expensive(n, early); + } + return early; } +//------------------------------get_early_ctrl_for_expensive--------------------------------- +// Move node up the dominator tree as high as legal while still beneficial +Node *PhaseIdealLoop::get_early_ctrl_for_expensive(Node *n, Node* earliest) { + assert(n->in(0) && n->is_expensive(), "expensive node with control input here"); + assert(OptimizeExpensiveOps, "optimization off?"); + + Node* ctl = n->in(0); + assert(ctl->is_CFG(), "expensive input 0 must be cfg"); + uint min_dom_depth = dom_depth(earliest); +#ifdef ASSERT + if (!is_dominator(ctl, earliest) && !is_dominator(earliest, ctl)) { + dump_bad_graph("Bad graph detected in get_early_ctrl_for_expensive", n, earliest, ctl); + assert(false, "Bad graph detected in get_early_ctrl_for_expensive"); + } +#endif + if (dom_depth(ctl) < min_dom_depth) { + return earliest; + } + + while (1) { + Node *next = ctl; + // Moving the node out of a loop on the projection of a If + // confuses loop predication. So once we hit a Loop in a If branch + // that doesn't branch to an UNC, we stop. The code that process + // expensive nodes will notice the loop and skip over it to try to + // move the node further up. + if (ctl->is_CountedLoop() && ctl->in(1) != NULL && ctl->in(1)->in(0) != NULL && ctl->in(1)->in(0)->is_If()) { + if (!is_uncommon_trap_if_pattern(ctl->in(1)->as_Proj(), Deoptimization::Reason_none)) { + break; + } + next = idom(ctl->in(1)->in(0)); + } else if (ctl->is_Proj()) { + // We only move it up along a projection if the projection is + // the single control projection for its parent: same code path, + // if it's a If with UNC or fallthrough of a call. + Node* parent_ctl = ctl->in(0); + if (parent_ctl == NULL) { + break; + } else if (parent_ctl->is_CountedLoopEnd() && parent_ctl->as_CountedLoopEnd()->loopnode() != NULL) { + next = parent_ctl->as_CountedLoopEnd()->loopnode()->init_control(); + } else if (parent_ctl->is_If()) { + if (!is_uncommon_trap_if_pattern(ctl->as_Proj(), Deoptimization::Reason_none)) { + break; + } + assert(idom(ctl) == parent_ctl, "strange"); + next = idom(parent_ctl); + } else if (ctl->is_CatchProj()) { + if (ctl->as_Proj()->_con != CatchProjNode::fall_through_index) { + break; + } + assert(parent_ctl->in(0)->in(0)->is_Call(), "strange graph"); + next = parent_ctl->in(0)->in(0)->in(0); + } else { + // Check if parent control has a single projection (this + // control is the only possible successor of the parent + // control). If so, we can try to move the node above the + // parent control. + int nb_ctl_proj = 0; + for (DUIterator_Fast imax, i = parent_ctl->fast_outs(imax); i < imax; i++) { + Node *p = parent_ctl->fast_out(i); + if (p->is_Proj() && p->is_CFG()) { + nb_ctl_proj++; + if (nb_ctl_proj > 1) { + break; + } + } + } + + if (nb_ctl_proj > 1) { + break; + } + assert(parent_ctl->is_Start() || parent_ctl->is_MemBar() || parent_ctl->is_Call(), "unexpected node"); + assert(idom(ctl) == parent_ctl, "strange"); + next = idom(parent_ctl); + } + } else { + next = idom(ctl); + } + if (next->is_Root() || next->is_Start() || dom_depth(next) < min_dom_depth) { + break; + } + ctl = next; + } + + if (ctl != n->in(0)) { + _igvn.hash_delete(n); + n->set_req(0, ctl); + _igvn.hash_insert(n); + } + + return ctl; +} + + //------------------------------set_early_ctrl--------------------------------- // Set earliest legal control void PhaseIdealLoop::set_early_ctrl( Node *n ) { @@ -1892,6 +1991,98 @@ void PhaseIdealLoop::eliminate_useless_predicates() { } } +//------------------------process_expensive_nodes----------------------------- +// Expensive nodes have their control input set to prevent the GVN +// from commoning them and as a result forcing the resulting node to +// be in a more frequent path. Use CFG information here, to change the +// control inputs so that some expensive nodes can be commoned while +// not executed more frequently. +bool PhaseIdealLoop::process_expensive_nodes() { + assert(OptimizeExpensiveOps, "optimization off?"); + + // Sort nodes to bring similar nodes together + C->sort_expensive_nodes(); + + bool progress = false; + + for (int i = 0; i < C->expensive_count(); ) { + Node* n = C->expensive_node(i); + int start = i; + // Find nodes similar to n + i++; + for (; i < C->expensive_count() && Compile::cmp_expensive_nodes(n, C->expensive_node(i)) == 0; i++); + int end = i; + // And compare them two by two + for (int j = start; j < end; j++) { + Node* n1 = C->expensive_node(j); + if (is_node_unreachable(n1)) { + continue; + } + for (int k = j+1; k < end; k++) { + Node* n2 = C->expensive_node(k); + if (is_node_unreachable(n2)) { + continue; + } + + assert(n1 != n2, "should be pair of nodes"); + + Node* c1 = n1->in(0); + Node* c2 = n2->in(0); + + Node* parent_c1 = c1; + Node* parent_c2 = c2; + + // The call to get_early_ctrl_for_expensive() moves the + // expensive nodes up but stops at loops that are in a if + // branch. See whether we can exit the loop and move above the + // If. + if (c1->is_Loop()) { + parent_c1 = c1->in(1); + } + if (c2->is_Loop()) { + parent_c2 = c2->in(1); + } + + if (parent_c1 == parent_c2) { + _igvn._worklist.push(n1); + _igvn._worklist.push(n2); + continue; + } + + // Look for identical expensive node up the dominator chain. + if (is_dominator(c1, c2)) { + c2 = c1; + } else if (is_dominator(c2, c1)) { + c1 = c2; + } else if (parent_c1->is_Proj() && parent_c1->in(0)->is_If() && + parent_c2->is_Proj() && parent_c1->in(0) == parent_c2->in(0)) { + // Both branches have the same expensive node so move it up + // before the if. + c1 = c2 = idom(parent_c1->in(0)); + } + // Do the actual moves + if (n1->in(0) != c1) { + _igvn.hash_delete(n1); + n1->set_req(0, c1); + _igvn.hash_insert(n1); + _igvn._worklist.push(n1); + progress = true; + } + if (n2->in(0) != c2) { + _igvn.hash_delete(n2); + n2->set_req(0, c2); + _igvn.hash_insert(n2); + _igvn._worklist.push(n2); + progress = true; + } + } + } + } + + return progress; +} + + //============================================================================= //----------------------------build_and_optimize------------------------------- // Create a PhaseLoop. Build the ideal Loop tree. Map each Ideal Node to @@ -1960,7 +2151,9 @@ void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool skip_loop_opts) } // Nothing to do, so get out - if( !C->has_loops() && !skip_loop_opts && !do_split_ifs && !_verify_me && !_verify_only ) { + bool stop_early = !C->has_loops() && !skip_loop_opts && !do_split_ifs && !_verify_me && !_verify_only; + bool do_expensive_nodes = C->should_optimize_expensive_nodes(_igvn); + if (stop_early && !do_expensive_nodes) { _igvn.optimize(); // Cleanup NeverBranches return; } @@ -2058,6 +2251,21 @@ void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool skip_loop_opts) return; } + if (stop_early) { + assert(do_expensive_nodes, "why are we here?"); + if (process_expensive_nodes()) { + // If we made some progress when processing expensive nodes then + // the IGVN may modify the graph in a way that will allow us to + // make some more progress: we need to try processing expensive + // nodes again. + C->set_major_progress(); + } + + _igvn.optimize(); + + return; + } + // Some parser-inserted loop predicates could never be used by loop // predication or they were moved away from loop during some optimizations. // For example, peeling. Eliminate them before next loop optimizations. @@ -2120,6 +2328,10 @@ void PhaseIdealLoop::build_and_optimize(bool do_split_ifs, bool skip_loop_opts) NOT_PRODUCT( if( VerifyLoopOptimizations ) verify(); ); } + if (!C->major_progress() && do_expensive_nodes && process_expensive_nodes()) { + C->set_major_progress(); + } + // Perform loop predication before iteration splitting if (C->has_loops() && !C->major_progress() && (C->predicate_count() > 0)) { _ltree_root->_child->loop_predication(this); @@ -3299,7 +3511,7 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) { #ifdef ASSERT if (legal->is_Start() && !early->is_Root()) { // Bad graph. Print idom path and fail. - dump_bad_graph(n, early, LCA); + dump_bad_graph("Bad graph detected in build_loop_late", n, early, LCA); assert(false, "Bad graph detected in build_loop_late"); } #endif @@ -3350,8 +3562,8 @@ void PhaseIdealLoop::build_loop_late_post( Node *n ) { } #ifdef ASSERT -void PhaseIdealLoop::dump_bad_graph(Node* n, Node* early, Node* LCA) { - tty->print_cr( "Bad graph detected in build_loop_late"); +void PhaseIdealLoop::dump_bad_graph(const char* msg, Node* n, Node* early, Node* LCA) { + tty->print_cr(msg); tty->print("n: "); n->dump(); tty->print("early(n): "); early->dump(); if (n->in(0) != NULL && !n->in(0)->is_top() && diff --git a/src/share/vm/opto/loopnode.hpp b/src/share/vm/opto/loopnode.hpp index 42096c0da..104d7b787 100644 --- a/src/share/vm/opto/loopnode.hpp +++ b/src/share/vm/opto/loopnode.hpp @@ -263,9 +263,18 @@ public: bool stride_is_con() const { Node *tmp = stride (); return (tmp != NULL && tmp->is_Con()); } BoolTest::mask test_trip() const { return in(TestValue)->as_Bool()->_test._test; } CountedLoopNode *loopnode() const { + // The CountedLoopNode that goes with this CountedLoopEndNode may + // have been optimized out by the IGVN so be cautious with the + // pattern matching on the graph + if (phi() == NULL) { + return NULL; + } Node *ln = phi()->in(0); - assert( ln->Opcode() == Op_CountedLoop, "malformed loop" ); - return (CountedLoopNode*)ln; } + if (ln->is_CountedLoop() && ln->as_CountedLoop()->loopexit() == this) { + return (CountedLoopNode*)ln; + } + return NULL; + } #ifndef PRODUCT virtual void dump_spec(outputStream *st) const; @@ -598,6 +607,7 @@ public: // check if transform created new nodes that need _ctrl recorded Node *get_late_ctrl( Node *n, Node *early ); Node *get_early_ctrl( Node *n ); + Node *get_early_ctrl_for_expensive(Node *n, Node* earliest); void set_early_ctrl( Node *n ); void set_subtree_ctrl( Node *root ); void set_ctrl( Node *n, Node *ctrl ) { @@ -905,6 +915,16 @@ public: void collect_potentially_useful_predicates(IdealLoopTree *loop, Unique_Node_List &predicate_opaque1); void eliminate_useless_predicates(); + // Change the control input of expensive nodes to allow commoning by + // IGVN when it is guaranteed to not result in a more frequent + // execution of the expensive node. Return true if progress. + bool process_expensive_nodes(); + + // Check whether node has become unreachable + bool is_node_unreachable(Node *n) const { + return !has_node(n) || n->is_unreachable(_igvn); + } + // Eliminate range-checks and other trip-counter vs loop-invariant tests. void do_range_check( IdealLoopTree *loop, Node_List &old_new ); @@ -1043,7 +1063,7 @@ public: void register_new_node( Node *n, Node *blk ); #ifdef ASSERT -void dump_bad_graph(Node* n, Node* early, Node* LCA); + void dump_bad_graph(const char* msg, Node* n, Node* early, Node* LCA); #endif #ifndef PRODUCT diff --git a/src/share/vm/opto/node.cpp b/src/share/vm/opto/node.cpp index 16157f68b..5ec7ddd5d 100644 --- a/src/share/vm/opto/node.cpp +++ b/src/share/vm/opto/node.cpp @@ -493,6 +493,8 @@ Node *Node::clone() const { } if (is_macro()) compile->add_macro_node(n); + if (is_expensive()) + compile->add_expensive_node(n); n->set_idx(compile->next_unique()); // Get new unique index as well debug_only( n->verify_construction() ); @@ -616,6 +618,9 @@ void Node::destruct() { if (is_macro()) { compile->remove_macro_node(this); } + if (is_expensive()) { + compile->remove_expensive_node(this); + } #ifdef ASSERT // We will not actually delete the storage, but we'll make the node unusable. *(address*)this = badAddress; // smash the C++ vtbl, probably @@ -689,6 +694,13 @@ bool Node::is_dead() const { } #endif + +//------------------------------is_unreachable--------------------------------- +bool Node::is_unreachable(PhaseIterGVN &igvn) const { + assert(!is_Mach(), "doesn't work with MachNodes"); + return outcnt() == 0 || igvn.type(this) == Type::TOP || in(0)->is_top(); +} + //------------------------------add_req---------------------------------------- // Add a new required input at the end void Node::add_req( Node *n ) { @@ -1246,6 +1258,9 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) { if (dead->is_macro()) { igvn->C->remove_macro_node(dead); } + if (dead->is_expensive()) { + igvn->C->remove_expensive_node(dead); + } // Kill all inputs to the dead guy for (uint i=0; i < dead->req(); i++) { Node *n = dead->in(i); // Get input to dead guy diff --git a/src/share/vm/opto/node.hpp b/src/share/vm/opto/node.hpp index 0dafb0018..f8f2c24e8 100644 --- a/src/share/vm/opto/node.hpp +++ b/src/share/vm/opto/node.hpp @@ -378,6 +378,8 @@ protected: bool is_dead() const; #define is_not_dead(n) ((n) == NULL || !VerifyIterativeGVN || !((n)->is_dead())) #endif + // Check whether node has become unreachable + bool is_unreachable(PhaseIterGVN &igvn) const; // Set a required input edge, also updates corresponding output edge void add_req( Node *n ); // Append a NEW required input @@ -646,7 +648,8 @@ public: Flag_may_be_short_branch = Flag_is_dead_loop_safe << 1, Flag_avoid_back_to_back = Flag_may_be_short_branch << 1, Flag_has_call = Flag_avoid_back_to_back << 1, - _max_flags = (Flag_has_call << 1) - 1 // allow flags combination + Flag_is_expensive = Flag_has_call << 1, + _max_flags = (Flag_is_expensive << 1) - 1 // allow flags combination }; private: @@ -819,6 +822,8 @@ public: // The node is a "macro" node which needs to be expanded before matching bool is_macro() const { return (_flags & Flag_is_macro) != 0; } + // The node is expensive: the best control is set during loop opts + bool is_expensive() const { return (_flags & Flag_is_expensive) != 0 && in(0) != NULL; } //----------------- Optimization diff --git a/src/share/vm/opto/phaseX.cpp b/src/share/vm/opto/phaseX.cpp index 7feb0faee..130efbec8 100644 --- a/src/share/vm/opto/phaseX.cpp +++ b/src/share/vm/opto/phaseX.cpp @@ -1203,6 +1203,9 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) { if (dead->is_macro()) { C->remove_macro_node(dead); } + if (dead->is_expensive()) { + C->remove_expensive_node(dead); + } if (recurse) { continue; diff --git a/src/share/vm/opto/subnode.hpp b/src/share/vm/opto/subnode.hpp index 862519d05..c3a31ffd8 100644 --- a/src/share/vm/opto/subnode.hpp +++ b/src/share/vm/opto/subnode.hpp @@ -456,7 +456,10 @@ public: // Exponentiate a double class ExpDNode : public Node { public: - ExpDNode( Node *c, Node *in1 ) : Node(c, in1) {} + ExpDNode(Compile* C, Node *c, Node *in1) : Node(c, in1) { + init_flags(Flag_is_expensive); + C->add_expensive_node(this); + } virtual int Opcode() const; const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } @@ -489,7 +492,10 @@ public: // Raise a double to a double power class PowDNode : public Node { public: - PowDNode(Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) {} + PowDNode(Compile* C, Node *c, Node *in1, Node *in2 ) : Node(c, in1, in2) { + init_flags(Flag_is_expensive); + C->add_expensive_node(this); + } virtual int Opcode() const; const Type *bottom_type() const { return Type::DOUBLE; } virtual uint ideal_reg() const { return Op_RegD; } -- cgit v1.2.3 From 79ebd82e7ac8087645efdc418236b4c616e1e203 Mon Sep 17 00:00:00 2001 From: kmo Date: Tue, 12 Feb 2013 07:39:42 -0800 Subject: 8002169: TEST_BUG: compiler/7009359/Test7009359.java sometimes times out Summary: make the test less prone to timeout by reducing the amount of iteration and allowing main to be compiled Reviewed-by: jrose --- test/compiler/7009359/Test7009359.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/compiler/7009359/Test7009359.java b/test/compiler/7009359/Test7009359.java index 408fd713e..86724931d 100644 --- a/test/compiler/7009359/Test7009359.java +++ b/test/compiler/7009359/Test7009359.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,13 +27,13 @@ * @bug 7009359 * @summary HS with -XX:+AggressiveOpts optimize new StringBuffer(null) so it does not throw NPE as expected * - * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+OptimizeStringConcat -XX:CompileCommand=exclude,Test7009359,main Test7009359 + * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:+OptimizeStringConcat -XX:CompileCommand=dontinline,Test7009359,stringmakerBUG Test7009359 * */ public class Test7009359 { public static void main (String[] args) { - for(int i = 0; i < 1000000; i++) { + for(int i = 0; i < 100000; i++) { if(!stringmakerBUG(null).equals("NPE")) { System.out.println("StringBuffer(null) does not throw NPE"); System.exit(97); -- cgit v1.2.3 From 531063fe148131c5cd6100a2b5979423b1f4725c Mon Sep 17 00:00:00 2001 From: zgu Date: Tue, 12 Feb 2013 12:19:28 -0500 Subject: 8007950: Undo hs_file permission change Summary: Reverse hs_err file permission back to 0666, as early push was premature Reviewed-by: dsamersoff, dcubed, acorn --- src/share/vm/utilities/vmError.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp index d2ec49159..d8fe93b64 100644 --- a/src/share/vm/utilities/vmError.cpp +++ b/src/share/vm/utilities/vmError.cpp @@ -917,7 +917,7 @@ void VMError::report_and_die() { bool copy_ok = Arguments::copy_expand_pid(ErrorFile, strlen(ErrorFile), buffer, sizeof(buffer)); if (copy_ok) { - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); } } @@ -928,7 +928,7 @@ void VMError::report_and_die() { // so use the default name in the current directory jio_snprintf(&buffer[len], sizeof(buffer)-len, "%shs_err_pid%u.log", os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); } if (fd == -1) { @@ -937,7 +937,7 @@ void VMError::report_and_die() { if (tmpdir != NULL && tmpdir[0] != '\0') { jio_snprintf(buffer, sizeof(buffer), "%s%shs_err_pid%u.log", tmpdir, os::file_separator(), os::current_process_id()); - fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0600); + fd = open(buffer, O_RDWR | O_CREAT | O_TRUNC, 0666); } } -- cgit v1.2.3 From 6f66876600a1eab534840059cfddeec1d04fefec Mon Sep 17 00:00:00 2001 From: iignatyev Date: Wed, 13 Feb 2013 08:29:04 -0800 Subject: 8006683: Add WhiteBox API to testing of compiler Reviewed-by: kvn, vlivanov --- src/share/tools/whitebox/sun/hotspot/WhiteBox.java | 13 ++ src/share/vm/prims/wbtestmethods/parserTests.hpp | 2 +- src/share/vm/prims/whitebox.cpp | 108 +++++++++++++++- src/share/vm/prims/whitebox.hpp | 2 +- test/compiler/whitebox/CompilerWhiteBoxTest.java | 142 +++++++++++++++++++++ test/compiler/whitebox/DeoptimizeAllTest.java | 45 +++++++ test/compiler/whitebox/DeoptimizeMethodTest.java | 45 +++++++ test/compiler/whitebox/IsMethodCompilableTest.java | 84 ++++++++++++ .../whitebox/MakeMethodNotCompilableTest.java | 54 ++++++++ .../compiler/whitebox/SetDontInlineMethodTest.java | 59 +++++++++ 10 files changed, 549 insertions(+), 5 deletions(-) create mode 100644 test/compiler/whitebox/CompilerWhiteBoxTest.java create mode 100644 test/compiler/whitebox/DeoptimizeAllTest.java create mode 100644 test/compiler/whitebox/DeoptimizeMethodTest.java create mode 100644 test/compiler/whitebox/IsMethodCompilableTest.java create mode 100644 test/compiler/whitebox/MakeMethodNotCompilableTest.java create mode 100644 test/compiler/whitebox/SetDontInlineMethodTest.java diff --git a/src/share/tools/whitebox/sun/hotspot/WhiteBox.java b/src/share/tools/whitebox/sun/hotspot/WhiteBox.java index ecc508589..70ee48b66 100644 --- a/src/share/tools/whitebox/sun/hotspot/WhiteBox.java +++ b/src/share/tools/whitebox/sun/hotspot/WhiteBox.java @@ -23,6 +23,8 @@ */ package sun.hotspot; + +import java.lang.reflect.Method; import java.security.BasicPermission; import sun.hotspot.parser.DiagnosticCommand; @@ -81,4 +83,15 @@ public class WhiteBox { public native boolean NMTAllocTest(); public native boolean NMTFreeTestMemory(); public native boolean NMTWaitForDataMerge(); + + // Compiler + public native void deoptimizeAll(); + public native boolean isMethodCompiled(Method method); + public native boolean isMethodCompilable(Method method); + public native boolean isMethodQueuedForCompilation(Method method); + public native int deoptimizeMethod(Method method); + public native void makeMethodNotCompilable(Method method); + public native int getMethodCompilationLevel(Method method); + public native boolean setDontInlineMethod(Method method, boolean value); + public native int getCompileQueuesSize(); } diff --git a/src/share/vm/prims/wbtestmethods/parserTests.hpp b/src/share/vm/prims/wbtestmethods/parserTests.hpp index 8efdd9db1..ae8086407 100644 --- a/src/share/vm/prims/wbtestmethods/parserTests.hpp +++ b/src/share/vm/prims/wbtestmethods/parserTests.hpp @@ -27,6 +27,6 @@ #include "prims/jni.h" #include "prims/whitebox.hpp" -WB_METHOD_DECLARE WB_ParseCommandLine(JNIEnv* env, jobject o, jstring args, jobjectArray arguments); +WB_METHOD_DECLARE(jobjectArray) WB_ParseCommandLine(JNIEnv* env, jobject o, jstring args, jobjectArray arguments); #endif //SHARE_VM_PRIMS_WBTESTMETHODS_PARSERTESTS_H diff --git a/src/share/vm/prims/whitebox.cpp b/src/share/vm/prims/whitebox.cpp index 7bbec834e..4758210ee 100644 --- a/src/share/vm/prims/whitebox.cpp +++ b/src/share/vm/prims/whitebox.cpp @@ -48,6 +48,8 @@ #include "services/memTracker.hpp" #endif // INCLUDE_NMT +#include "compiler/compileBroker.hpp" + bool WhiteBox::_used = false; WB_ENTRY(jlong, WB_GetObjectAddress(JNIEnv* env, jobject o, jobject obj)) @@ -169,6 +171,89 @@ WB_END #endif // INCLUDE_NMT +static jmethodID reflected_method_to_jmid(JavaThread* thread, JNIEnv* env, jobject method) { + assert(method != NULL, "method should not be null"); + ThreadToNativeFromVM ttn(thread); + return env->FromReflectedMethod(method); +} + +WB_ENTRY(void, WB_DeoptimizeAll(JNIEnv* env, jobject o)) + MutexLockerEx mu(Compile_lock); + CodeCache::mark_all_nmethods_for_deoptimization(); + VM_Deoptimize op; + VMThread::execute(&op); +WB_END + +WB_ENTRY(jint, WB_DeoptimizeMethod(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + MutexLockerEx mu(Compile_lock); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + int result = 0; + nmethod* code = mh->code(); + if (code != NULL) { + code->mark_for_deoptimization(); + ++result; + } + result += CodeCache::mark_for_deoptimization(mh()); + if (result > 0) { + VM_Deoptimize op; + VMThread::execute(&op); + } + return result; +WB_END + +WB_ENTRY(jboolean, WB_IsMethodCompiled(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + MutexLockerEx mu(Compile_lock); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + nmethod* code = mh->code(); + if (code == NULL) { + return JNI_FALSE; + } + return (code->is_alive() && !code->is_marked_for_deoptimization()); +WB_END + +WB_ENTRY(jboolean, WB_IsMethodCompilable(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + MutexLockerEx mu(Compile_lock); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + return !mh->is_not_compilable(); +WB_END + +WB_ENTRY(jboolean, WB_IsMethodQueuedForCompilation(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + MutexLockerEx mu(Compile_lock); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + return mh->queued_for_compilation(); +WB_END + +WB_ENTRY(jint, WB_GetMethodCompilationLevel(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + nmethod* code = mh->code(); + return (code != NULL ? code->comp_level() : CompLevel_none); +WB_END + + +WB_ENTRY(void, WB_MakeMethodNotCompilable(JNIEnv* env, jobject o, jobject method)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + mh->set_not_compilable(); +WB_END + +WB_ENTRY(jboolean, WB_SetDontInlineMethod(JNIEnv* env, jobject o, jobject method, jboolean value)) + jmethodID jmid = reflected_method_to_jmid(thread, env, method); + methodHandle mh(THREAD, Method::checked_resolve_jmethod_id(jmid)); + bool result = mh->dont_inline(); + mh->set_dont_inline(value == JNI_TRUE); + return result; +WB_END + +WB_ENTRY(jint, WB_GetCompileQueuesSize(JNIEnv* env, jobject o)) + return CompileBroker::queue_size(CompLevel_full_optimization) /* C2 */ + + CompileBroker::queue_size(CompLevel_full_profile) /* C1 */; +WB_END + //Some convenience methods to deal with objects from java int WhiteBox::offset_for_field(const char* field_name, oop object, Symbol* signature_symbol) { @@ -225,9 +310,9 @@ bool WhiteBox::lookup_bool(const char* field_name, oop object) { static JNINativeMethod methods[] = { {CC"getObjectAddress", CC"(Ljava/lang/Object;)J", (void*)&WB_GetObjectAddress }, {CC"getHeapOopSize", CC"()I", (void*)&WB_GetHeapOopSize }, - {CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive }, - {CC "parseCommandLine", - CC "(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", + {CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive }, + {CC"parseCommandLine", + CC"(Ljava/lang/String;[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", (void*) &WB_ParseCommandLine }, #if INCLUDE_ALL_GCS @@ -241,6 +326,23 @@ static JNINativeMethod methods[] = { {CC"NMTFreeTestMemory", CC"()Z", (void*)&WB_NMTFreeTestMemory }, {CC"NMTWaitForDataMerge",CC"()Z", (void*)&WB_NMTWaitForDataMerge}, #endif // INCLUDE_NMT + {CC"deoptimizeAll", CC"()V", (void*)&WB_DeoptimizeAll }, + {CC"deoptimizeMethod", CC"(Ljava/lang/reflect/Method;)I", + (void*)&WB_DeoptimizeMethod }, + {CC"isMethodCompiled", CC"(Ljava/lang/reflect/Method;)Z", + (void*)&WB_IsMethodCompiled }, + {CC"isMethodCompilable", CC"(Ljava/lang/reflect/Method;)Z", + (void*)&WB_IsMethodCompilable}, + {CC"isMethodQueuedForCompilation", + CC"(Ljava/lang/reflect/Method;)Z", (void*)&WB_IsMethodQueuedForCompilation}, + {CC"makeMethodNotCompilable", + CC"(Ljava/lang/reflect/Method;)V", (void*)&WB_MakeMethodNotCompilable}, + {CC"setDontInlineMethod", + CC"(Ljava/lang/reflect/Method;Z)Z", (void*)&WB_SetDontInlineMethod}, + {CC"getMethodCompilationLevel", + CC"(Ljava/lang/reflect/Method;)I", (void*)&WB_GetMethodCompilationLevel}, + {CC"getCompileQueuesSize", + CC"()I", (void*)&WB_GetCompileQueuesSize}, }; #undef CC diff --git a/src/share/vm/prims/whitebox.hpp b/src/share/vm/prims/whitebox.hpp index a5dda9b46..a404b8411 100644 --- a/src/share/vm/prims/whitebox.hpp +++ b/src/share/vm/prims/whitebox.hpp @@ -34,7 +34,7 @@ #define WB_ENTRY(result_type, header) JNI_ENTRY(result_type, header) #define WB_END JNI_END -#define WB_METHOD_DECLARE extern "C" jobjectArray JNICALL +#define WB_METHOD_DECLARE(result_type) extern "C" result_type JNICALL class WhiteBox : public AllStatic { private: diff --git a/test/compiler/whitebox/CompilerWhiteBoxTest.java b/test/compiler/whitebox/CompilerWhiteBoxTest.java new file mode 100644 index 000000000..ebd5a9af4 --- /dev/null +++ b/test/compiler/whitebox/CompilerWhiteBoxTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import sun.hotspot.WhiteBox; +import sun.management.ManagementFactoryHelper; +import com.sun.management.HotSpotDiagnosticMXBean; + +import java.lang.reflect.Method; + +/* + * @author igor.ignatyev@oracle.com + */ +public abstract class CompilerWhiteBoxTest { + protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); + protected static final Method METHOD = getMethod("method"); + protected static final int COMPILE_THRESHOLD + = Integer.parseInt(getVMOption("CompileThreshold", "10000")); + + protected static Method getMethod(String name) { + try { + return CompilerWhiteBoxTest.class.getDeclaredMethod(name); + } catch (NoSuchMethodException | SecurityException e) { + throw new RuntimeException( + "exception on getting method " + name, e); + } + } + + protected static String getVMOption(String name, String defaultValue) { + String result; + HotSpotDiagnosticMXBean diagnostic + = ManagementFactoryHelper.getDiagnosticMXBean(); + result = diagnostic.getVMOption(name).getValue(); + return result == null ? defaultValue : result; + } + + protected final void runTest() throws RuntimeException { + if (ManagementFactoryHelper.getCompilationMXBean() == null) { + System.err.println( + "Warning: test is not applicable in interpreted mode"); + return; + } + System.out.println("at test's start:"); + printInfo(METHOD); + try { + test(); + } catch (Exception e) { + System.out.printf("on exception '%s':", e.getMessage()); + printInfo(METHOD); + throw new RuntimeException(e); + } + System.out.println("at test's end:"); + printInfo(METHOD); + } + + protected static void checkNotCompiled(Method method) { + if (WHITE_BOX.isMethodCompiled(method)) { + throw new RuntimeException(method + " must be not compiled"); + } + if (WHITE_BOX.getMethodCompilationLevel(method) != 0) { + throw new RuntimeException(method + " comp_level must be == 0"); + } + } + + protected static void checkCompiled(Method method) + throws InterruptedException { + final long start = System.currentTimeMillis(); + waitBackgroundCompilation(method); + if (WHITE_BOX.isMethodQueuedForCompilation(method)) { + System.err.printf("Warning: %s is still in queue after %dms%n", + method, System.currentTimeMillis() - start); + return; + } + if (!WHITE_BOX.isMethodCompiled(method)) { + throw new RuntimeException(method + " must be compiled"); + } + if (WHITE_BOX.getMethodCompilationLevel(method) == 0) { + throw new RuntimeException(method + " comp_level must be != 0"); + } + } + + protected static void waitBackgroundCompilation(Method method) + throws InterruptedException { + final Object obj = new Object(); + synchronized (obj) { + for (int i = 0; i < 10; ++i) { + if (!WHITE_BOX.isMethodQueuedForCompilation(method)) { + break; + } + obj.wait(1000); + } + } + } + + protected static void printInfo(Method method) { + System.out.printf("%n%s:%n", method); + System.out.printf("\tcompilable:\t%b%n", + WHITE_BOX.isMethodCompilable(method)); + System.out.printf("\tcompiled:\t%b%n", + WHITE_BOX.isMethodCompiled(method)); + System.out.printf("\tcomp_level:\t%d%n", + WHITE_BOX.getMethodCompilationLevel(method)); + System.out.printf("\tin_queue:\t%b%n", + WHITE_BOX.isMethodQueuedForCompilation(method)); + System.out.printf("compile_queues_size:\t%d%n%n", + WHITE_BOX.getCompileQueuesSize()); + } + + protected abstract void test() throws Exception; + + protected final int compile() { + int result = 0; + for (int i = 0; i < COMPILE_THRESHOLD; ++i) { + result += method(); + } + return result; + } + + + protected int method() { + return 42; + } +} diff --git a/test/compiler/whitebox/DeoptimizeAllTest.java b/test/compiler/whitebox/DeoptimizeAllTest.java new file mode 100644 index 000000000..a0b41d368 --- /dev/null +++ b/test/compiler/whitebox/DeoptimizeAllTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test DeoptimizeAllTest + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI CompilerWhiteBoxTest.java + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI DeoptimizeAllTest.java + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI DeoptimizeAllTest + * @author igor.ignatyev@oracle.com + */ +public class DeoptimizeAllTest extends CompilerWhiteBoxTest { + + public static void main(String[] args) throws Exception { + new DeoptimizeAllTest().runTest(); + } + + protected void test() throws Exception { + // to prevent inlining #method into #compile() + WHITE_BOX.setDontInlineMethod(METHOD, true); + compile(); + checkCompiled(METHOD); + WHITE_BOX.deoptimizeAll(); + checkNotCompiled(METHOD); + } +} diff --git a/test/compiler/whitebox/DeoptimizeMethodTest.java b/test/compiler/whitebox/DeoptimizeMethodTest.java new file mode 100644 index 000000000..66b45cc28 --- /dev/null +++ b/test/compiler/whitebox/DeoptimizeMethodTest.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test DeoptimizeMethodTest + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI CompilerWhiteBoxTest.java + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI DeoptimizeMethodTest.java + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI DeoptimizeMethodTest + * @author igor.ignatyev@oracle.com + */ +public class DeoptimizeMethodTest extends CompilerWhiteBoxTest { + + public static void main(String[] args) throws Exception { + new DeoptimizeMethodTest().runTest(); + } + + protected void test() throws Exception { + // to prevent inlining #method into #compile() + WHITE_BOX.setDontInlineMethod(METHOD, true); + compile(); + checkCompiled(METHOD); + WHITE_BOX.deoptimizeMethod(METHOD); + checkNotCompiled(METHOD); + } +} diff --git a/test/compiler/whitebox/IsMethodCompilableTest.java b/test/compiler/whitebox/IsMethodCompilableTest.java new file mode 100644 index 000000000..21878b533 --- /dev/null +++ b/test/compiler/whitebox/IsMethodCompilableTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test IsMethodCompilableTest + * @bug 8007270 + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI CompilerWhiteBoxTest.java + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI IsMethodCompilableTest.java + * @run main/othervm/timeout=600 -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI IsMethodCompilableTest + * @author igor.ignatyev@oracle.com + */ +public class IsMethodCompilableTest extends CompilerWhiteBoxTest { + protected static final long PER_METHOD_RECOMPILATION_CUTOFF; + + static { + long tmp = Long.parseLong( + getVMOption("PerMethodRecompilationCutoff", "400")); + if (tmp == -1) { + PER_METHOD_RECOMPILATION_CUTOFF = -1 /* Inf */; + } else { + PER_METHOD_RECOMPILATION_CUTOFF = 1 + (0xFFFFFFFFL & tmp); + } + } + + public static void main(String[] args) throws Exception { + new IsMethodCompilableTest().runTest(); + } + + protected void test() throws Exception { + if (!WHITE_BOX.isMethodCompilable(METHOD)) { + throw new RuntimeException(METHOD + " must be compilable"); + } + System.out.println("PerMethodRecompilationCutoff = " + + PER_METHOD_RECOMPILATION_CUTOFF); + if (PER_METHOD_RECOMPILATION_CUTOFF == -1) { + System.err.println( + "Warning: test is not applicable if PerMethodRecompilationCutoff == Inf"); + return; + } + // to prevent inlining #method into #compile() + WHITE_BOX.setDontInlineMethod(METHOD, true); + boolean madeNotCompilable = false; + + for (long i = 0; i < PER_METHOD_RECOMPILATION_CUTOFF; ++i) { + compile(); + waitBackgroundCompilation(METHOD); + WHITE_BOX.deoptimizeMethod(METHOD); + if (!WHITE_BOX.isMethodCompilable(METHOD)) { + madeNotCompilable = true; + break; + } + } + if (!madeNotCompilable) { + throw new RuntimeException(METHOD + " is still compilable after " + + PER_METHOD_RECOMPILATION_CUTOFF + " iterations"); + } + compile(); + if (WHITE_BOX.isMethodCompiled(METHOD)) { + printInfo(METHOD); + throw new RuntimeException( + METHOD + " is not compilable but compiled"); + } + } +} diff --git a/test/compiler/whitebox/MakeMethodNotCompilableTest.java b/test/compiler/whitebox/MakeMethodNotCompilableTest.java new file mode 100644 index 000000000..6916b16b0 --- /dev/null +++ b/test/compiler/whitebox/MakeMethodNotCompilableTest.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test MakeMethodNotCompilableTest + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI CompilerWhiteBoxTest.java + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI MakeMethodNotCompilableTest.java + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI MakeMethodNotCompilableTest + * @author igor.ignatyev@oracle.com + */ +public class MakeMethodNotCompilableTest extends CompilerWhiteBoxTest { + + public static void main(String[] args) throws Exception { + new MakeMethodNotCompilableTest().runTest(); + } + + protected void test() throws Exception { + if (!WHITE_BOX.isMethodCompilable(METHOD)) { + throw new RuntimeException(METHOD + " must be compilable"); + } + WHITE_BOX.makeMethodNotCompilable(METHOD); + if (WHITE_BOX.isMethodCompilable(METHOD)) { + throw new RuntimeException(METHOD + " must be not compilable"); + } + compile(); + if (WHITE_BOX.isMethodQueuedForCompilation(METHOD)) { + throw new RuntimeException(METHOD + " must not be in queue"); + } + checkNotCompiled(METHOD); + if (WHITE_BOX.isMethodCompilable(METHOD)) { + throw new RuntimeException(METHOD + " must be not compilable"); + } + } +} diff --git a/test/compiler/whitebox/SetDontInlineMethodTest.java b/test/compiler/whitebox/SetDontInlineMethodTest.java new file mode 100644 index 000000000..01daa0e31 --- /dev/null +++ b/test/compiler/whitebox/SetDontInlineMethodTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test SetDontInlineMethodTest + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI CompilerWhiteBoxTest.java + * @compile -J-XX:+UnlockDiagnosticVMOptions -J-XX:+WhiteBoxAPI SetDontInlineMethodTest.java + * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI SetDontInlineMethodTest + * @author igor.ignatyev@oracle.com + */ +public class SetDontInlineMethodTest extends CompilerWhiteBoxTest { + + public static void main(String[] args) throws Exception { + new SetDontInlineMethodTest().runTest(); + } + + protected void test() throws Exception { + if (WHITE_BOX.setDontInlineMethod(METHOD, true)) { + throw new RuntimeException("on start " + METHOD + + " must be inlineable"); + } + if (!WHITE_BOX.setDontInlineMethod(METHOD, true)) { + throw new RuntimeException("after first change to true " + METHOD + + " must be not inlineable"); + } + if (!WHITE_BOX.setDontInlineMethod(METHOD, false)) { + throw new RuntimeException("after second change to true " + METHOD + + " must be still not inlineable"); + } + if (WHITE_BOX.setDontInlineMethod(METHOD, false)) { + throw new RuntimeException("after first change to false" + METHOD + + " must be inlineable"); + } + if (WHITE_BOX.setDontInlineMethod(METHOD, false)) { + throw new RuntimeException("after second change to false " + METHOD + + " must be inlineable"); + } + } +} -- cgit v1.2.3