aoqi@0: /* aoqi@0: * Copyright (c) 1998, 2012, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: * aoqi@0: */ aoqi@0: aoqi@0: #ifndef SHARE_VM_CODE_OOPRECORDER_HPP aoqi@0: #define SHARE_VM_CODE_OOPRECORDER_HPP aoqi@0: aoqi@0: #include "memory/universe.hpp" aoqi@0: #include "runtime/handles.hpp" aoqi@0: #include "utilities/growableArray.hpp" aoqi@0: aoqi@0: // Recording and retrieval of either oop relocations or metadata in compiled code. aoqi@0: aoqi@0: class CodeBlob; aoqi@0: aoqi@0: template class ValueRecorder : public StackObj { aoqi@0: public: aoqi@0: // A two-way mapping from positive indexes to oop handles. aoqi@0: // The zero index is reserved for a constant (sharable) null. aoqi@0: // Indexes may not be negative. aoqi@0: aoqi@0: // Use the given arena to manage storage, if not NULL. aoqi@0: // By default, uses the current ResourceArea. aoqi@0: ValueRecorder(Arena* arena = NULL); aoqi@0: aoqi@0: // Generate a new index on which nmethod::oop_addr_at will work. aoqi@0: // allocate_index and find_index never return the same index, aoqi@0: // and allocate_index never returns the same index twice. aoqi@0: // In fact, two successive calls to allocate_index return successive ints. aoqi@0: int allocate_index(T h) { aoqi@0: return add_handle(h, false); aoqi@0: } aoqi@0: aoqi@0: // For a given jobject or Metadata*, this will return the same index aoqi@0: // repeatedly. The index can later be given to nmethod::oop_at or aoqi@0: // metadata_at to retrieve the oop. aoqi@0: // However, the oop must not be changed via nmethod::oop_addr_at. aoqi@0: int find_index(T h) { aoqi@0: int index = maybe_find_index(h); aoqi@0: if (index < 0) { // previously unallocated aoqi@0: index = add_handle(h, true); aoqi@0: } aoqi@0: return index; aoqi@0: } aoqi@0: aoqi@0: // returns the size of the generated oop/metadata table, for sizing the aoqi@0: // CodeBlob. Must be called after all oops are allocated! aoqi@0: int size(); aoqi@0: aoqi@0: // Retrieve the value at a given index. aoqi@0: T at(int index); aoqi@0: aoqi@0: int count() { aoqi@0: if (_handles == NULL) return 0; aoqi@0: // there is always a NULL virtually present as first object aoqi@0: return _handles->length() + first_index; aoqi@0: } aoqi@0: aoqi@0: // Helper function; returns false for NULL or Universe::non_oop_word(). aoqi@0: bool is_real(T h) { aoqi@0: return h != NULL && h != (T)Universe::non_oop_word(); aoqi@0: } aoqi@0: aoqi@0: // copy the generated table to nmethod aoqi@0: void copy_values_to(nmethod* nm); aoqi@0: aoqi@0: bool is_unused() { return _handles == NULL && !_complete; } aoqi@0: #ifdef ASSERT aoqi@0: bool is_complete() { return _complete; } aoqi@0: #endif aoqi@0: aoqi@0: private: aoqi@0: // variant of find_index which does not allocate if not found (yields -1) aoqi@0: int maybe_find_index(T h); aoqi@0: aoqi@0: // leaky hash table of handle => index, to help detect duplicate insertion aoqi@0: template class IndexCache : public ResourceObj { aoqi@0: // This class is only used by the ValueRecorder class. aoqi@0: friend class ValueRecorder; aoqi@0: enum { aoqi@0: _log_cache_size = 9, aoqi@0: _cache_size = (1<<_log_cache_size), aoqi@0: // Index entries are ints. The LSBit is a collision indicator. aoqi@0: _collision_bit_shift = 0, aoqi@0: _collision_bit = 1, aoqi@0: _index_shift = _collision_bit_shift+1 aoqi@0: }; aoqi@0: int _cache[_cache_size]; aoqi@0: static juint cache_index(X handle) { aoqi@0: juint ci = (int) (intptr_t) handle; aoqi@0: ci ^= ci >> (BitsPerByte*2); aoqi@0: ci += ci >> (BitsPerByte*1); aoqi@0: return ci & (_cache_size-1); aoqi@0: } aoqi@0: int* cache_location(X handle) { aoqi@0: return &_cache[ cache_index(handle) ]; aoqi@0: } aoqi@0: static bool cache_location_collision(int* cloc) { aoqi@0: return ((*cloc) & _collision_bit) != 0; aoqi@0: } aoqi@0: static int cache_location_index(int* cloc) { aoqi@0: return (*cloc) >> _index_shift; aoqi@0: } aoqi@0: static void set_cache_location_index(int* cloc, int index) { aoqi@0: int cval0 = (*cloc); aoqi@0: int cval1 = (index << _index_shift); aoqi@0: if (cval0 != 0 && cval1 != cval0) cval1 += _collision_bit; aoqi@0: (*cloc) = cval1; aoqi@0: } aoqi@0: IndexCache(); aoqi@0: }; aoqi@0: aoqi@0: void maybe_initialize(); aoqi@0: int add_handle(T h, bool make_findable); aoqi@0: aoqi@0: enum { null_index = 0, first_index = 1, index_cache_threshold = 20 }; aoqi@0: aoqi@0: GrowableArray* _handles; // ordered list (first is always NULL) aoqi@0: GrowableArray* _no_finds; // all unfindable indexes; usually empty aoqi@0: IndexCache* _indexes; // map: handle -> its probable index aoqi@0: Arena* _arena; aoqi@0: bool _complete; aoqi@0: aoqi@0: #ifdef ASSERT aoqi@0: static int _find_index_calls, _hit_indexes, _missed_indexes; aoqi@0: #endif aoqi@0: }; aoqi@0: aoqi@0: class OopRecorder : public ResourceObj { aoqi@0: private: aoqi@0: ValueRecorder _oops; aoqi@0: ValueRecorder _metadata; aoqi@0: public: aoqi@0: OopRecorder(Arena* arena = NULL): _oops(arena), _metadata(arena) {} aoqi@0: aoqi@0: int allocate_oop_index(jobject h) { aoqi@0: return _oops.allocate_index(h); aoqi@0: } aoqi@0: int find_index(jobject h) { aoqi@0: return _oops.find_index(h); aoqi@0: } aoqi@0: jobject oop_at(int index) { aoqi@0: return _oops.at(index); aoqi@0: } aoqi@0: int oop_size() { aoqi@0: return _oops.size(); aoqi@0: } aoqi@0: int oop_count() { aoqi@0: return _oops.count(); aoqi@0: } aoqi@0: bool is_real(jobject h) { aoqi@0: return _oops.is_real(h); aoqi@0: } aoqi@0: aoqi@0: int allocate_metadata_index(Metadata* oop) { aoqi@0: return _metadata.allocate_index(oop); aoqi@0: } aoqi@0: int find_index(Metadata* h) { aoqi@0: return _metadata.find_index(h); aoqi@0: } aoqi@0: Metadata* metadata_at(int index) { aoqi@0: return _metadata.at(index); aoqi@0: } aoqi@0: int metadata_size() { aoqi@0: return _metadata.size(); aoqi@0: } aoqi@0: int metadata_count() { aoqi@0: return _metadata.count(); aoqi@0: } aoqi@0: bool is_real(Metadata* h) { aoqi@0: return _metadata.is_real(h); aoqi@0: } aoqi@0: aoqi@0: bool is_unused() { aoqi@0: return _oops.is_unused() && _metadata.is_unused(); aoqi@0: } aoqi@0: aoqi@0: void freeze() { aoqi@0: _oops.size(); aoqi@0: _metadata.size(); aoqi@0: } aoqi@0: aoqi@0: void copy_values_to(nmethod* nm) { aoqi@0: if (!_oops.is_unused()) { aoqi@0: _oops.copy_values_to(nm); aoqi@0: } aoqi@0: if (!_metadata.is_unused()) { aoqi@0: _metadata.copy_values_to(nm); aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: #ifdef ASSERT aoqi@0: bool is_complete() { aoqi@0: assert(_oops.is_complete() == _metadata.is_complete(), "must agree"); aoqi@0: return _oops.is_complete(); aoqi@0: } aoqi@0: #endif aoqi@0: }; aoqi@0: aoqi@0: aoqi@0: #endif // SHARE_VM_CODE_OOPRECORDER_HPP