mirror of
https://github.com/openjdk/jdk.git
synced 2026-08-03 22:55:40 +00:00
137 lines
5.3 KiB
C++
137 lines
5.3 KiB
C++
/*
|
|
* Copyright (c) 2018, 2025, 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.
|
|
*
|
|
*/
|
|
|
|
#include "code/codeBlob.hpp"
|
|
#include "code/nativeInst.hpp"
|
|
#include "code/nmethod.hpp"
|
|
#include "gc/shared/barrierSet.hpp"
|
|
#include "gc/shared/barrierSetAssembler.hpp"
|
|
#include "gc/shared/barrierSetNMethod.hpp"
|
|
#include "utilities/debug.hpp"
|
|
|
|
class NativeNMethodBarrier: public NativeInstruction {
|
|
|
|
address get_barrier_start_address() const {
|
|
return NativeInstruction::addr_at(0);
|
|
}
|
|
|
|
NativeMovRegMem* get_patchable_instruction_handle() const {
|
|
// Endianness is handled by NativeMovRegMem
|
|
return reinterpret_cast<NativeMovRegMem*>(get_barrier_start_address() + 3 * 4);
|
|
}
|
|
|
|
public:
|
|
int get_guard_value() const {
|
|
// Retrieve the guard value (naming of 'offset' function is misleading).
|
|
return get_patchable_instruction_handle()->offset();
|
|
}
|
|
|
|
void release_set_guard_value(int value) {
|
|
// Patching is not atomic.
|
|
// Stale observations of the "armed" state is okay as invoking the barrier stub in that case has no
|
|
// unwanted side effects. Disarming is thus a non-critical operation.
|
|
// The visibility of the "armed" state must be ensured by safepoint/handshake.
|
|
|
|
OrderAccess::release(); // Release modified oops
|
|
|
|
// Set the guard value (naming of 'offset' function is misleading).
|
|
get_patchable_instruction_handle()->set_offset(value);
|
|
}
|
|
|
|
void verify() const {
|
|
// Although it's possible to just validate the to-be-patched instruction,
|
|
// all instructions are validated to ensure that the barrier is hit properly - especially since
|
|
// the pattern used in load_const32 is a quite common one.
|
|
|
|
uint* current_instruction = reinterpret_cast<uint*>(get_barrier_start_address());
|
|
|
|
// calculate_address_from_global_toc (compound instruction)
|
|
verify_op_code_manually(current_instruction, MacroAssembler::is_addis(*current_instruction));
|
|
verify_op_code_manually(current_instruction, MacroAssembler::is_addi(*current_instruction));
|
|
|
|
verify_op_code_manually(current_instruction, MacroAssembler::is_mtctr(*current_instruction));
|
|
|
|
get_patchable_instruction_handle()->verify();
|
|
current_instruction += 2;
|
|
|
|
verify_op_code(current_instruction, Assembler::LD_OPCODE);
|
|
|
|
// cmpw (mnemonic)
|
|
verify_op_code(current_instruction, Assembler::CMP_OPCODE);
|
|
|
|
// bnectrl (mnemonic) (weak check; not checking the exact type)
|
|
verify_op_code(current_instruction, Assembler::BCCTR_OPCODE);
|
|
|
|
// isync is optional
|
|
}
|
|
|
|
private:
|
|
static void verify_op_code_manually(uint*& current_instruction, bool result) {
|
|
assert(result, "illegal instruction sequence for nmethod entry barrier");
|
|
current_instruction++;
|
|
}
|
|
static void verify_op_code(uint*& current_instruction, uint expected,
|
|
unsigned int mask = 63u << Assembler::OPCODE_SHIFT) {
|
|
// Masking both, current instruction and opcode, as some opcodes in Assembler contain additional information
|
|
// to uniquely identify simplified mnemonics.
|
|
// As long as the caller doesn't provide a custom mask, that additional information is discarded.
|
|
verify_op_code_manually(current_instruction, (*current_instruction & mask) == (expected & mask));
|
|
}
|
|
};
|
|
|
|
static NativeNMethodBarrier* get_nmethod_barrier(nmethod* nm) {
|
|
BarrierSetAssembler* bs_asm = BarrierSet::barrier_set()->barrier_set_assembler();
|
|
address barrier_address = nm->code_begin() + nm->frame_complete_offset() + (-8 * 4);
|
|
if (bs_asm->nmethod_patching_type() != NMethodPatchingType::stw_instruction_and_data_patch) {
|
|
barrier_address -= 4; // isync (see nmethod_entry_barrier)
|
|
}
|
|
|
|
auto barrier = reinterpret_cast<NativeNMethodBarrier*>(barrier_address);
|
|
DEBUG_ONLY(barrier->verify());
|
|
return barrier;
|
|
}
|
|
|
|
void BarrierSetNMethod::deoptimize(nmethod* nm, address* return_address_ptr) {
|
|
// As PPC64 always has a valid back chain (unlike other platforms), the stub can simply pop the frame.
|
|
// Thus, there's nothing to do here.
|
|
}
|
|
|
|
void BarrierSetNMethod::set_guard_value(nmethod* nm, int value) {
|
|
if (!supports_entry_barrier(nm)) {
|
|
return;
|
|
}
|
|
|
|
NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
|
|
barrier->release_set_guard_value(value);
|
|
}
|
|
|
|
int BarrierSetNMethod::guard_value(nmethod* nm) {
|
|
if (!supports_entry_barrier(nm)) {
|
|
return disarmed_guard_value();
|
|
}
|
|
|
|
NativeNMethodBarrier* barrier = get_nmethod_barrier(nm);
|
|
return barrier->get_guard_value();
|
|
}
|