/* * Copyright (c) 2008, 2022, 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. *
*/
void AddressLiteral::set_rspec(relocInfo::relocType rtype) { switch (rtype) { case relocInfo::oop_type: // Oops are a special case. Normally they would be their own section // but in cases like icBuffer they are literals in the code stream that // we don't have a section for. We use none so that we get a literal address // which is always patchable. break; case relocInfo::external_word_type:
_rspec = external_word_Relocation::spec(_target); break; case relocInfo::internal_word_type:
_rspec = internal_word_Relocation::spec(_target); break; case relocInfo::opt_virtual_call_type:
_rspec = opt_virtual_call_Relocation::spec(); break; case relocInfo::static_call_type:
_rspec = static_call_Relocation::spec(); break; case relocInfo::runtime_call_type:
_rspec = runtime_call_Relocation::spec(); break; case relocInfo::poll_type: case relocInfo::poll_return_type:
_rspec = Relocation::spec_simple(rtype); break; case relocInfo::none: break; default:
ShouldNotReachHere(); break;
}
}
Label L_fallthrough; int label_nulls = 0; if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; } if (L_slow_path == NULL) { L_slow_path = &L_fallthrough; label_nulls++; }
assert(label_nulls <= 1, "at most one NULL in the batch");
int sc_offset = in_bytes(Klass::secondary_super_cache_offset()); int sco_offset = in_bytes(Klass::super_check_offset_offset());
Address super_check_offset_addr(super_klass, sco_offset);
// If the pointers are equal, we are done (e.g., String[] elements). // This self-check enables sharing of secondary supertype arrays among // non-primary types such as array-of-interface. Otherwise, each such // type would need its own customized SSA. // We move this check to the front of the fast path because many // type checks are in fact trivially successful in this manner, // so we get a nicely predicted branch right at the start of the check.
cmp(sub_klass, super_klass);
b(*L_success, eq);
// Check the supertype display:
ldr_u32(super_check_offset, super_check_offset_addr);
// This check has worked decisively for primary supers. // Secondary supers are sought in the super_cache ('super_cache_addr'). // (Secondary supers are interfaces and very deeply nested subtypes.) // This works in the same check above because of a tricky aliasing // between the super_cache and the primary super display elements. // (The 'super_check_addr' can address either, as the case requires.) // Note that the cache is updated below if it does not help us find // what we need immediately. // So if it was a primary super, we can just fail immediately. // Otherwise, it's the slow path for us (no success at this point).
void MacroAssembler::check_klass_subtype_slow_path(Register sub_klass, Register super_klass, Register temp_reg, Register temp2_reg, Register temp3_reg,
Label* L_success,
Label* L_failure, bool set_cond_codes) { // Note: if used by code that expects a register to be 0 on success, // this register must be temp_reg and set_cond_codes must be true
Register saved_reg = noreg;
// get additional tmp registers if (temp3_reg == noreg) {
saved_reg = temp3_reg = LR;
push(saved_reg);
}
assert(temp2_reg != noreg, "need all the temporary registers");
assert_different_registers(sub_klass, super_klass, temp_reg, temp2_reg, temp3_reg);
Label L_fallthrough; int label_nulls = 0; if (L_success == NULL) { L_success = &L_fallthrough; label_nulls++; } if (L_failure == NULL) { L_failure = &L_fallthrough; label_nulls++; }
assert(label_nulls <= 1, "at most one NULL in the batch");
// a couple of useful fields in sub_klass: int ss_offset = in_bytes(Klass::secondary_supers_offset()); int sc_offset = in_bytes(Klass::secondary_super_cache_offset());
Address secondary_supers_addr(sub_klass, ss_offset);
Address super_cache_addr( sub_klass, sc_offset);
// Top of search loop
bind(L_loop); // Notes: // scan_temp starts at the array elements // count_temp is 1+size
subs(count_temp, count_temp, 1); if ((L_failure != &L_fallthrough) && (! set_cond_codes) && (saved_reg == noreg)) { // direct jump to L_failure if failed and no cleanup needed
b(*L_failure, eq); // not found and
} else {
b(L_fail, eq); // not found in the array
}
// Load next super to check // In the array of super classes elements are pointer sized. int element_size = wordSize;
ldr(cmp_temp, Address(scan_temp, element_size, post_indexed));
// Look for Rsuper_klass on Rsub_klass's secondary super-class-overflow list
subs(cmp_temp, cmp_temp, search_key);
// A miss means we are NOT a subtype and need to keep looping
b(L_loop, ne);
// Falling out the bottom means we found a hit; we ARE a subtype
// Note: temp_reg/cmp_temp is already 0 and flag Z is set
// Success. Cache the super we found and proceed in triumph.
str(super_klass, Address(sub_klass, sc_offset));
if (saved_reg != noreg) { // Return success
pop(saved_reg);
}
b(*L_success);
bind(L_fail); // Note1: check "b(*L_failure, eq)" above if adding extra instructions here if (set_cond_codes) {
movs(temp_reg, sub_klass); // clears Z and sets temp_reg to non-0 if needed
} if (saved_reg != noreg) {
pop(saved_reg);
} if (L_failure != &L_fallthrough) {
b(*L_failure);
}
bind(L_fallthrough);
}
// Returns address of receiver parameter, using tmp as base register. tmp and params_count can be the same.
Address MacroAssembler::receiver_argument_address(Register params_base, Register params_count, Register tmp) {
assert_different_registers(params_base, params_count);
add(tmp, params_base, AsmOperand(params_count, lsl, Interpreter::logStackElementSize)); return Address(tmp, -Interpreter::stackElementSize);
}
int MacroAssembler::set_last_Java_frame(Register last_java_sp, Register last_java_fp, bool save_last_java_pc, Register tmp) { int pc_offset; if (last_java_fp != noreg) { // optional
str(last_java_fp, Address(Rthread, JavaThread::last_Java_fp_offset()));
_fp_saved = true;
} else {
_fp_saved = false;
} if (save_last_java_pc) {
str(PC, Address(Rthread, JavaThread::last_Java_pc_offset()));
pc_offset = offset() + VM_Version::stored_pc_adjustment();
_pc_saved = true;
} else {
_pc_saved = false;
pc_offset = -1;
} // According to comment in javaFrameAnchorm SP must be saved last, so that other // entries are valid when SP is set.
// However, this is probably not a strong constrainst since for instance PC is // sometimes read from the stack at SP... but is pushed later (by the call). Hence, // we now write the fields in the expected order but we have not added a StoreStore // barrier.
// XXX: if the ordering is really important, PC should always be saved (without forgetting // to update oop_map offsets) and a StoreStore barrier might be needed.
void MacroAssembler::call_VM_leaf_helper(address entry_point, int number_of_arguments) {
assert(number_of_arguments >= 0, "cannot have negative number of arguments");
assert(number_of_arguments <= 4, "cannot have more than 4 arguments");
// Safer to save R9 here since callers may have been written // assuming R9 survives. This is suboptimal but is not worth // optimizing for the few platforms where R9 is scratched.
push(RegisterSet(R4) | R9ifScratched);
mov(R4, SP);
bic(SP, SP, StackAlignmentInBytes - 1);
call(entry_point, relocInfo::runtime_call_type);
mov(SP, R4);
pop(RegisterSet(R4) | R9ifScratched);
}
void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) {
assert(number_of_arguments >= 0, "cannot have negative number of arguments");
assert(number_of_arguments <= 3, "cannot have more than 3 arguments");
#if R9_IS_SCRATCHED // Safer to save R9 here since callers may have been written // assuming R9 survives. This is suboptimal but is not worth // optimizing for the few platforms where R9 is scratched.
// Note: cannot save R9 above the saved SP (some calls expect for // instance the Java stack top at the saved SP) // => once saved (with set_last_Java_frame), decrease SP before rounding to // ensure the slot at SP will be free for R9).
sub(SP, SP, 4);
bic(SP, SP, StackAlignmentInBytes - 1);
str(R9, Address(SP, 0)); #else
bic(SP, SP, StackAlignmentInBytes - 1); #endif// R9_IS_SCRATCHED
void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, int number_of_arguments, bool check_exceptions) { // Not used on ARM
Unimplemented();
}
void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, bool check_exceptions) { // Not used on ARM
Unimplemented();
}
void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, bool check_exceptions) { // Not used on ARM
Unimplemented();
}
void MacroAssembler::call_VM(Register oop_result, Register last_java_sp, address entry_point, Register arg_1, Register arg_2, Register arg_3, bool check_exceptions) { // Not used on ARM
Unimplemented();
}
// Raw call, without saving/restoring registers, exception handling, etc. // Mainly used from various stubs. void MacroAssembler::call_VM(address entry_point, bool save_R9_if_scratched) { constRegister tmp = Rtemp; // Rtemp free since scratched by call
set_last_Java_frame(SP, FP, true, tmp); #if R9_IS_SCRATCHED if (save_R9_if_scratched) { // Note: Saving also R10 for alignment.
push(RegisterSet(R9, R10));
} #endif
mov(R0, Rthread);
call(entry_point, relocInfo::runtime_call_type); #if R9_IS_SCRATCHED if (save_R9_if_scratched) {
pop(RegisterSet(R9, R10));
} #endif
reset_last_Java_frame(tmp);
}
void MacroAssembler::add_slow(Register rd, Register rn, int c) { // This function is used in compiler for handling large frame offsets if ((c < 0) && (((-c) & ~0x3fc) == 0)) { return sub(rd, rn, (-c));
} int low = c & 0x3fc; if (low != 0) {
add(rd, rn, low);
rn = rd;
} if (c & ~0x3fc) {
assert(AsmOperand::is_rotated_imm(c & ~0x3fc), "unsupported add_slow offset %d", c);
add(rd, rn, c & ~0x3fc);
} elseif (rd != rn) {
assert(c == 0, "");
mov(rd, rn); // need to generate at least one move!
}
}
void MacroAssembler::sub_slow(Register rd, Register rn, int c) { // This function is used in compiler for handling large frame offsets if ((c < 0) && (((-c) & ~0x3fc) == 0)) { return add(rd, rn, (-c));
} int low = c & 0x3fc; if (low != 0) {
sub(rd, rn, low);
rn = rd;
} if (c & ~0x3fc) {
assert(AsmOperand::is_rotated_imm(c & ~0x3fc), "unsupported sub_slow offset %d", c);
sub(rd, rn, c & ~0x3fc);
} elseif (rd != rn) {
assert(c == 0, "");
mov(rd, rn); // need to generate at least one move!
}
}
void MacroAssembler::mov_slow(Register rd, address addr) { // do *not* call the non relocated mov_related_address
mov_slow(rd, (intptr_t)addr);
}
void MacroAssembler::mov_slow(Register rd, intptr_t c, AsmCondition cond) { if (AsmOperand::is_rotated_imm(c)) {
mov(rd, c, cond);
} elseif (AsmOperand::is_rotated_imm(~c)) {
mvn(rd, ~c, cond);
} elseif (VM_Version::supports_movw()) {
movw(rd, c & 0xffff, cond); if ((unsignedint)c >> 16) {
movt(rd, (unsignedint)c >> 16, cond);
}
} else { // Find first non-zero bit int shift = 0; while ((c & (3 << shift)) == 0) {
shift += 2;
} // Put the least significant part of the constant int mask = 0xff << shift;
mov(rd, c & mask, cond); // Add up to 3 other parts of the constant; // each of them can be represented as rotated_imm if (c & (mask << 8)) {
orr(rd, rd, c & (mask << 8), cond);
} if (c & (mask << 16)) {
orr(rd, rd, c & (mask << 16), cond);
} if (c & (mask << 24)) {
orr(rd, rd, c & (mask << 24), cond);
}
}
}
void MacroAssembler::mov_oop(Register rd, jobject o, int oop_index,
AsmCondition cond
) {
if (o == NULL) {
mov(rd, 0, cond); return;
}
if (oop_index == 0) {
oop_index = oop_recorder()->allocate_oop_index(o);
}
relocate(oop_Relocation::spec(oop_index));
if (VM_Version::supports_movw()) {
movw(rd, 0, cond);
movt(rd, 0, cond);
} else {
ldr(rd, Address(PC), cond); // Extra nop to handle case of large offset of oop placeholder (see NativeMovConstReg::set_data).
nop();
}
}
void MacroAssembler::mov_metadata(Register rd, Metadata* o, int metadata_index) { if (o == NULL) {
mov(rd, 0); return;
}
if (metadata_index == 0) {
metadata_index = oop_recorder()->allocate_metadata_index(o);
}
relocate(metadata_Relocation::spec(metadata_index));
if (VM_Version::supports_movw()) {
movw(rd, ((int)o) & 0xffff);
movt(rd, (unsignedint)o >> 16);
} else {
ldr(rd, Address(PC)); // Extra nop to handle case of large offset of metadata placeholder (see NativeMovConstReg::set_data).
nop();
}
}
void MacroAssembler::_verify_oop(Register reg, constchar* s, constchar* file, int line) { // This code pattern is matched in NativeIntruction::skip_verify_oop. // Update it at modifications. if (!VerifyOops) return;
// call indirectly to solve generation ordering problem
ldr_global_ptr(Rtemp, StubRoutines::verify_oop_subroutine_entry_address());
call(Rtemp);
restore_all_registers();
b(done); #ifdef COMPILER2 int off = offset(); #endif
bind_literal(Lmsg); #ifdef COMPILER2 if (offset() - off == 1 * wordSize) { // no padding, so insert nop for worst-case sizing
nop();
} #endif
bind(done);
}
void MacroAssembler::_verify_oop_addr(Address addr, constchar* s, constchar* file, int line) { if (!VerifyOops) return;
// call indirectly to solve generation ordering problem
ldr_global_ptr(Rtemp, StubRoutines::verify_oop_subroutine_entry_address());
call(Rtemp);
restore_all_registers();
b(done);
bind_literal(Lmsg);
bind(done);
}
void MacroAssembler::c2bool(Register x)
{
tst(x, 0xff); // Only look at the lowest byte
mov(x, 1, ne);
}
void MacroAssembler::null_check(Register reg, Register tmp, int offset) { if (needs_explicit_null_check(offset)) {
assert_different_registers(reg, tmp); if (tmp == noreg) {
tmp = Rtemp;
assert((! Thread::current()->is_Compiler_thread()) ||
(! (ciEnv::current()->task() == NULL)) ||
(! (ciEnv::current()->comp_level() == CompLevel_full_optimization)), "Rtemp not available in C2"); // explicit tmp register required // XXX: could we mark the code buffer as not compatible with C2 ?
}
ldr(tmp, Address(reg));
}
}
// Puts address of allocated object into register `obj` and end of allocated object into register `obj_end`. void MacroAssembler::tlab_allocate(Register obj, Register obj_end, Register tmp1,
RegisterOrConstant size_expression, Label& slow_case) {
BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
bs->tlab_allocate(this, obj, obj_end, tmp1, size_expression, slow_case);
}
// Fills memory regions [start..end] with zeroes. Clobbers `start` and `tmp` registers. void MacroAssembler::zero_memory(Register start, Register end, Register tmp) {
Label loop; constRegister ptr = start;
void MacroAssembler::arm_stack_overflow_check(int frame_size_in_bytes, Register tmp) { // Version of AbstractAssembler::generate_stack_overflow_check optimized for ARM constint page_size = os::vm_page_size();
void MacroAssembler::stop(constchar* msg) { // This code pattern is matched in NativeIntruction::is_stop. // Update it at modifications. #ifdef COMPILER1 if (CommentedAssembly) {
block_comment("stop");
} #endif
int MacroAssembler::save_all_registers() { // This code pattern is matched in NativeIntruction::is_save_all_registers. // Update it at modifications.
push(RegisterSet(R0, R12) | RegisterSet(LR) | RegisterSet(PC)); return 15*wordSize;
}
void MacroAssembler::debug(constchar* msg, const intx* registers) { // In order to get locks to work, we need to fake a in_VM state
JavaThread* thread = JavaThread::current();
thread->set_thread_state(_thread_in_vm);
for (int i = 0; i < nregs; i++) {
tty->print_cr("%s = " INTPTR_FORMAT, regs[i]->name(), registers[i]);
}
// derive original SP value from the address of register save area
tty->print_cr("%s = " INTPTR_FORMAT, SP->name(), p2i(®isters[nregs]));
}
BREAKPOINT;
} else {
::tty->print_cr("=============== DEBUG MESSAGE: %s ================\n", msg);
}
assert(false, "DEBUG MESSAGE: %s", msg);
fatal("%s", msg); // returning from MacroAssembler::debug is not supported
}
FixedSizeCodeBlock::~FixedSizeCodeBlock() { if (_enabled) {
address curr_pc = _masm->pc();
assert(_start < curr_pc, "invalid current pc");
guarantee(curr_pc <= _start + _size_in_instrs * Assembler::InstructionSize, "code block is too long");
int nops_count = (_start - curr_pc) / Assembler::InstructionSize + _size_in_instrs; for (int i = 0; i < nops_count; i++) {
_masm->nop();
}
}
}
// Serializes memory. Potentially blows flags and reg. // tmp is a scratch for v6 co-processor write op (could be noreg for other architecture versions) // preserve_flags takes a longer path in LoadStore case (dmb rather then control dependency) to preserve status flags. Optional. // load_tgt is an ordered load target in a LoadStore case only, to create dependency between the load operation and conditional branch. Optional. void MacroAssembler::membar(Membar_mask_bits order_constraint, Register tmp, bool preserve_flags, Register load_tgt) {
// By providing an ordered load target register, we avoid an extra memory load reference
Label not_taken;
bind(not_taken);
cmp(load_tgt, load_tgt);
b(not_taken, ne);
}
}
// If "allow_fallthrough_on_failure" is false, we always branch to "slow_case" // on failure, so fall-through can only mean success. // "one_shot" controls whether we loop and retry to mitigate spurious failures. // This is only needed for C2, which for some reason does not rety, // while C1/interpreter does. // TODO: measure if it makes a difference
// MemBarAcquireLock barrier // According to JSR-133 Cookbook, this should be LoadLoad | LoadStore, // but that doesn't prevent a load or store from floating up between // the load and store in the CAS sequence, so play it safe and // do a full fence.
membar(Membar_mask_bits(LoadLoad | LoadStore | StoreStore | StoreLoad), noreg); if (!fallthrough_is_success && !allow_fallthrough_on_failure) {
b(slow_case, ne);
}
}
// MemBarReleaseLock barrier // According to JSR-133 Cookbook, this should be StoreStore | LoadStore, // but that doesn't prevent a load or store from floating down between // the load and store in the CAS sequence, so play it safe and // do a full fence.
membar(Membar_mask_bits(LoadLoad | LoadStore | StoreStore | StoreLoad), tmp);
// ExitEnter // According to JSR-133 Cookbook, this should be StoreLoad, the same // barrier that follows volatile store. // TODO: Should be able to remove on armv8 if volatile loads // use the load-acquire instruction.
membar(StoreLoad, noreg);
}
#ifndef PRODUCT
// Preserves flags and all registers. // On SMP the updated value might not be visible to external observers without a synchronization barrier void MacroAssembler::cond_atomic_inc32(AsmCondition cond, int* counter_addr) { if (counter_addr != NULL) {
InlinedAddress counter_addr_literal((address)counter_addr);
Label done, retry; if (cond != al) {
b(done, inverse(cond));
}
// Look up the method for a megamorphic invokeinterface call. // The target method is determined by <Rinterf, Rindex>. // The receiver klass is in Rklass. // On success, the result will be in method_result, and execution falls through. // On failure, execution transfers to the given label. void MacroAssembler::lookup_interface_method(Register Rklass, Register Rintf,
RegisterOrConstant itable_index, Register method_result, Register Rscan, Register Rtmp,
Label& L_no_such_interface) {
// Compute start of first itableOffsetEntry (which is at the end of the vtable) constint base = in_bytes(Klass::vtable_start_offset()); constint scale = exact_log2(vtableEntry::size_in_bytes());
ldr_s32(Rtmp, Address(Rklass, Klass::vtable_length_offset())); // Get length of vtable
add(Rscan, Rklass, base);
add(Rscan, Rscan, AsmOperand(Rtmp, lsl, scale));
// Search through the itable for an interface equal to incoming Rintf // itable looks like [intface][offset][intface][offset][intface][offset]
Label loop;
bind(loop);
ldr(Rtmp, Address(Rscan, entry_size, post_indexed));
cmp(Rtmp, Rintf); // set ZF and CF if interface is found
cmn(Rtmp, 0, ne); // check if tmp == 0 and clear CF if it is
b(loop, ne);
// CF == 0 means we reached the end of itable without finding icklass
b(L_no_such_interface, cc);
if (method_result != noreg) { // Interface found at previous position of Rscan, now load the method
ldr_s32(Rtmp, Address(Rscan, itableOffsetEntry::offset_offset_in_bytes() - entry_size)); if (itable_index.is_register()) {
add(Rtmp, Rtmp, Rklass); // Add offset to Klass*
assert(itableMethodEntry::size() * HeapWordSize == wordSize, "adjust the scaling in the code below");
assert(itableMethodEntry::method_offset_in_bytes() == 0, "adjust the offset in the code below");
ldr(method_result, Address::indexed_ptr(Rtmp, itable_index.as_register()));
} else { int method_offset = itableMethodEntry::size() * HeapWordSize * itable_index.as_constant() +
itableMethodEntry::method_offset_in_bytes();
add_slow(method_result, Rklass, method_offset);
ldr(method_result, Address(method_result, Rtmp));
}
}
}
// 24-bit word range == 26-bit byte range bool check26(int offset) { // this could be simplified, but it mimics encoding and decoding // an actual branch insrtuction int off1 = offset << 6 >> 8; int encoded = off1 & ((1<<24)-1); int decoded = encoded << 8 >> 6; return offset == decoded;
}
// Perform some slight adjustments so the default 32MB code cache // is fully reachable. staticinline address first_cache_address() { return CodeCache::low_bound() + sizeof(HeapBlock::Header);
} staticinline address last_cache_address() { return CodeCache::high_bound() - Assembler::InstructionSize;
}
// Can we reach target using unconditional branch or call from anywhere // in the code cache (because code can be relocated)? bool MacroAssembler::_reachable_from_cache(address target) { #ifdef __thumb__ if ((1 & (intptr_t)target) != 0) { // Return false to avoid 'b' if we need switching to THUMB mode. returnfalse;
} #endif
if (ForceUnreachable) { // Only addresses from CodeCache can be treated as reachable. if (target < CodeCache::low_bound() || CodeCache::high_bound() < target) { returnfalse;
}
}
// Note: relocate is not needed for the code below, // encoding targets in absolute format. if (ignore_non_patchable_relocations()) {
rtype = relocInfo::none;
}
if (VM_Version::supports_movw() && (scratch != noreg) && (rtype == relocInfo::none)) { // Note: this version cannot be (atomically) patched
mov_slow(scratch, (intptr_t)target, cond);
bx(scratch, cond);
} else {
Label skip;
InlinedAddress address_literal(target); if (cond != al) {
b(skip, inverse(cond));
}
relocate(rtype);
ldr_literal(PC, address_literal);
bind_literal(address_literal);
bind(skip);
}
}
// Similar to jump except that: // - near calls are valid only if any destination in the cache is near // - no movt/movw (not atomically patchable) void MacroAssembler::patchable_jump(address target, relocInfo::relocType rtype, Register scratch, AsmCondition cond) {
assert((rtype == relocInfo::runtime_call_type) || (rtype == relocInfo::none), "not supported"); if (cache_fully_reachable()) { // Note: this assumes that all possible targets (the initial one // and the addressed patched to) are all in the code cache.
assert(CodeCache::contains(target), "target might be too far");
relocate(rtype);
b(target, cond); return;
}
// Discard the relocation information if not needed for CacheCompiledCode // since the next encodings are all in absolute format. if (ignore_non_patchable_relocations()) {
rtype = relocInfo::none;
}
// Note: relocate is not needed for the code below, // encoding targets in absolute format. if (ignore_non_patchable_relocations()) { // This assumes the information was needed only for relocating the code.
rspec = RelocationHolder::none;
}
if (VM_Version::supports_movw() && (rspec.type() == relocInfo::none)) { // Note: this version cannot be (atomically) patched
mov_slow(scratch, (intptr_t)target, cond);
blx(scratch, cond); return;
}
// Always generate the relocation information, needed for patching
relocate(rspec); // used by NativeCall::is_call_before() if (cache_fully_reachable()) { // Note: this assumes that all possible targets (the initial one // and the addresses patched to) are all in the code cache.
assert(CodeCache::contains(target), "target might be too far");
bl(target);
} else {
Label ret_addr;
InlinedAddress address_literal(target);
adr(LR, ret_addr);
ldr_literal(PC, address_literal);
bind_literal(address_literal);
bind(ret_addr);
} return offset();
}
// ((OopHandle)result).resolve(); void MacroAssembler::resolve_oop_handle(Register result) { // OopHandle::resolve is an indirection.
ldr(result, Address(result, 0));
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.