| 1 | /* |
| 2 | * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #include "precompiled.hpp" |
| 26 | #include "code/codeCache.hpp" |
| 27 | #include "code/compiledIC.hpp" |
| 28 | #include "code/icBuffer.hpp" |
| 29 | #include "code/nmethod.hpp" |
| 30 | #include "code/scopeDesc.hpp" |
| 31 | #include "gc/shared/collectedHeap.inline.hpp" |
| 32 | #include "interpreter/interpreter.hpp" |
| 33 | #include "interpreter/linkResolver.hpp" |
| 34 | #include "memory/resourceArea.hpp" |
| 35 | #include "oops/method.hpp" |
| 36 | #include "oops/oop.inline.hpp" |
| 37 | #include "runtime/handles.inline.hpp" |
| 38 | #include "runtime/mutexLocker.hpp" |
| 39 | #include "runtime/stubRoutines.hpp" |
| 40 | #include "runtime/thread.hpp" |
| 41 | |
| 42 | DEF_STUB_INTERFACE(ICStub); |
| 43 | |
| 44 | StubQueue* InlineCacheBuffer::_buffer = NULL; |
| 45 | |
| 46 | CompiledICHolder* InlineCacheBuffer::_pending_released = NULL; |
| 47 | int InlineCacheBuffer::_pending_count = 0; |
| 48 | |
| 49 | #ifdef ASSERT |
| 50 | ICRefillVerifier::ICRefillVerifier() |
| 51 | : _refill_requested(false), |
| 52 | _refill_remembered(false) |
| 53 | { |
| 54 | Thread* thread = Thread::current(); |
| 55 | assert(thread->missed_ic_stub_refill_verifier() == NULL, "nesting not supported" ); |
| 56 | thread->set_missed_ic_stub_refill_verifier(this); |
| 57 | } |
| 58 | |
| 59 | ICRefillVerifier::~ICRefillVerifier() { |
| 60 | assert(!_refill_requested || _refill_remembered, |
| 61 | "Forgot to refill IC stubs after failed IC transition" ); |
| 62 | Thread::current()->set_missed_ic_stub_refill_verifier(NULL); |
| 63 | } |
| 64 | |
| 65 | ICRefillVerifierMark::ICRefillVerifierMark(ICRefillVerifier* verifier) { |
| 66 | Thread* thread = Thread::current(); |
| 67 | assert(thread->missed_ic_stub_refill_verifier() == NULL, "nesting not supported" ); |
| 68 | thread->set_missed_ic_stub_refill_verifier(verifier); |
| 69 | } |
| 70 | |
| 71 | ICRefillVerifierMark::~ICRefillVerifierMark() { |
| 72 | Thread::current()->set_missed_ic_stub_refill_verifier(NULL); |
| 73 | } |
| 74 | |
| 75 | static ICRefillVerifier* current_ic_refill_verifier() { |
| 76 | Thread* current = Thread::current(); |
| 77 | ICRefillVerifier* verifier = current->missed_ic_stub_refill_verifier(); |
| 78 | assert(verifier != NULL, "need a verifier for safety" ); |
| 79 | return verifier; |
| 80 | } |
| 81 | #endif |
| 82 | |
| 83 | void ICStub::finalize() { |
| 84 | if (!is_empty()) { |
| 85 | ResourceMark rm; |
| 86 | CompiledIC *ic = CompiledIC_at(CodeCache::find_compiled(ic_site()), ic_site()); |
| 87 | assert(CodeCache::find_compiled(ic->instruction_address()) != NULL, "inline cache in non-compiled?" ); |
| 88 | |
| 89 | assert(this == ICStub_from_destination_address(ic->stub_address()), "wrong owner of ic buffer" ); |
| 90 | ic->set_ic_destination_and_value(destination(), cached_value()); |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | |
| 95 | address ICStub::destination() const { |
| 96 | return InlineCacheBuffer::ic_buffer_entry_point(code_begin()); |
| 97 | } |
| 98 | |
| 99 | void* ICStub::cached_value() const { |
| 100 | return InlineCacheBuffer::ic_buffer_cached_value(code_begin()); |
| 101 | } |
| 102 | |
| 103 | |
| 104 | void ICStub::set_stub(CompiledIC *ic, void* cached_val, address dest_addr) { |
| 105 | // We cannot store a pointer to the 'ic' object, since it is resource allocated. Instead we |
| 106 | // store the location of the inline cache. Then we have enough information recreate the CompiledIC |
| 107 | // object when we need to remove the stub. |
| 108 | _ic_site = ic->instruction_address(); |
| 109 | |
| 110 | // Assemble new stub |
| 111 | InlineCacheBuffer::assemble_ic_buffer_code(code_begin(), cached_val, dest_addr); |
| 112 | assert(destination() == dest_addr, "can recover destination" ); |
| 113 | assert(cached_value() == cached_val, "can recover destination" ); |
| 114 | } |
| 115 | |
| 116 | |
| 117 | void ICStub::clear() { |
| 118 | if (CompiledIC::is_icholder_entry(destination())) { |
| 119 | InlineCacheBuffer::queue_for_release((CompiledICHolder*)cached_value()); |
| 120 | } |
| 121 | _ic_site = NULL; |
| 122 | } |
| 123 | |
| 124 | |
| 125 | #ifndef PRODUCT |
| 126 | // anybody calling to this stub will trap |
| 127 | |
| 128 | void ICStub::verify() { |
| 129 | } |
| 130 | |
| 131 | void ICStub::print() { |
| 132 | tty->print_cr("ICStub: site: " INTPTR_FORMAT, p2i(_ic_site)); |
| 133 | } |
| 134 | #endif |
| 135 | |
| 136 | //----------------------------------------------------------------------------------------------- |
| 137 | // Implementation of InlineCacheBuffer |
| 138 | |
| 139 | |
| 140 | void InlineCacheBuffer::initialize() { |
| 141 | if (_buffer != NULL) return; // already initialized |
| 142 | _buffer = new StubQueue(new ICStubInterface, 10*K, InlineCacheBuffer_lock, "InlineCacheBuffer" ); |
| 143 | assert (_buffer != NULL, "cannot allocate InlineCacheBuffer" ); |
| 144 | } |
| 145 | |
| 146 | |
| 147 | ICStub* InlineCacheBuffer::new_ic_stub() { |
| 148 | return (ICStub*)buffer()->request_committed(ic_stub_code_size()); |
| 149 | } |
| 150 | |
| 151 | |
| 152 | void InlineCacheBuffer::refill_ic_stubs() { |
| 153 | #ifdef ASSERT |
| 154 | ICRefillVerifier* verifier = current_ic_refill_verifier(); |
| 155 | verifier->request_remembered(); |
| 156 | #endif |
| 157 | // we ran out of inline cache buffer space; must enter safepoint. |
| 158 | // We do this by forcing a safepoint |
| 159 | EXCEPTION_MARK; |
| 160 | |
| 161 | VM_ICBufferFull ibf; |
| 162 | VMThread::execute(&ibf); |
| 163 | // We could potential get an async. exception at this point. |
| 164 | // In that case we will rethrow it to ourselvs. |
| 165 | if (HAS_PENDING_EXCEPTION) { |
| 166 | oop exception = PENDING_EXCEPTION; |
| 167 | CLEAR_PENDING_EXCEPTION; |
| 168 | Thread::send_async_exception(JavaThread::current()->threadObj(), exception); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | |
| 173 | void InlineCacheBuffer::update_inline_caches() { |
| 174 | if (buffer()->number_of_stubs() > 0) { |
| 175 | if (TraceICBuffer) { |
| 176 | tty->print_cr("[updating inline caches with %d stubs]" , buffer()->number_of_stubs()); |
| 177 | } |
| 178 | buffer()->remove_all(); |
| 179 | } |
| 180 | release_pending_icholders(); |
| 181 | } |
| 182 | |
| 183 | |
| 184 | bool InlineCacheBuffer::contains(address instruction_address) { |
| 185 | return buffer()->contains(instruction_address); |
| 186 | } |
| 187 | |
| 188 | |
| 189 | bool InlineCacheBuffer::is_empty() { |
| 190 | return buffer()->number_of_stubs() == 0; |
| 191 | } |
| 192 | |
| 193 | |
| 194 | void InlineCacheBuffer_init() { |
| 195 | InlineCacheBuffer::initialize(); |
| 196 | } |
| 197 | |
| 198 | bool InlineCacheBuffer::create_transition_stub(CompiledIC *ic, void* cached_value, address entry) { |
| 199 | assert(!SafepointSynchronize::is_at_safepoint(), "should not be called during a safepoint" ); |
| 200 | assert(CompiledICLocker::is_safe(ic->instruction_address()), "mt unsafe call" ); |
| 201 | if (TraceICBuffer) { |
| 202 | tty->print_cr(" create transition stub for " INTPTR_FORMAT " destination " INTPTR_FORMAT " cached value " INTPTR_FORMAT, |
| 203 | p2i(ic->instruction_address()), p2i(entry), p2i(cached_value)); |
| 204 | } |
| 205 | |
| 206 | // allocate and initialize new "out-of-line" inline-cache |
| 207 | ICStub* ic_stub = new_ic_stub(); |
| 208 | if (ic_stub == NULL) { |
| 209 | #ifdef ASSERT |
| 210 | ICRefillVerifier* verifier = current_ic_refill_verifier(); |
| 211 | verifier->request_refill(); |
| 212 | #endif |
| 213 | return false; |
| 214 | } |
| 215 | |
| 216 | // If an transition stub is already associate with the inline cache, then we remove the association. |
| 217 | if (ic->is_in_transition_state()) { |
| 218 | ICStub* old_stub = ICStub_from_destination_address(ic->stub_address()); |
| 219 | old_stub->clear(); |
| 220 | } |
| 221 | |
| 222 | ic_stub->set_stub(ic, cached_value, entry); |
| 223 | |
| 224 | // Update inline cache in nmethod to point to new "out-of-line" allocated inline cache |
| 225 | ic->set_ic_destination(ic_stub); |
| 226 | return true; |
| 227 | } |
| 228 | |
| 229 | |
| 230 | address InlineCacheBuffer::ic_destination_for(CompiledIC *ic) { |
| 231 | ICStub* stub = ICStub_from_destination_address(ic->stub_address()); |
| 232 | return stub->destination(); |
| 233 | } |
| 234 | |
| 235 | |
| 236 | void* InlineCacheBuffer::cached_value_for(CompiledIC *ic) { |
| 237 | ICStub* stub = ICStub_from_destination_address(ic->stub_address()); |
| 238 | return stub->cached_value(); |
| 239 | } |
| 240 | |
| 241 | |
| 242 | // Free CompiledICHolder*s that are no longer in use |
| 243 | void InlineCacheBuffer::release_pending_icholders() { |
| 244 | assert(SafepointSynchronize::is_at_safepoint(), "should only be called during a safepoint" ); |
| 245 | CompiledICHolder* holder = _pending_released; |
| 246 | _pending_released = NULL; |
| 247 | while (holder != NULL) { |
| 248 | CompiledICHolder* next = holder->next(); |
| 249 | delete holder; |
| 250 | holder = next; |
| 251 | _pending_count--; |
| 252 | } |
| 253 | assert(_pending_count == 0, "wrong count" ); |
| 254 | } |
| 255 | |
| 256 | // Enqueue this icholder for release during the next safepoint. It's |
| 257 | // not safe to free them until them since they might be visible to |
| 258 | // another thread. |
| 259 | void InlineCacheBuffer::queue_for_release(CompiledICHolder* icholder) { |
| 260 | MutexLocker mex(InlineCacheBuffer_lock, Mutex::_no_safepoint_check_flag); |
| 261 | icholder->set_next(_pending_released); |
| 262 | _pending_released = icholder; |
| 263 | _pending_count++; |
| 264 | if (TraceICBuffer) { |
| 265 | tty->print_cr("enqueueing icholder " INTPTR_FORMAT " to be freed" , p2i(icholder)); |
| 266 | } |
| 267 | } |
| 268 | |