/* * Copyright (c) 2000, 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. *
*/
// These masks are used to provide 128-bit aligned bitmasks to the XMM // instructions, to allow sign-masking or sign-bit flipping. They allow // fast versions of NegF/NegD and AbsF/AbsD.
// Note: 'double' and 'long long' have 32-bits alignment on x86. static jlong* double_quadword(jlong *adr, jlong lo, jlong hi) { // Use the expression (adr)&(~0xF) to provide 128-bits aligned address // of 128-bits operands for SSE instructions.
jlong *operand = (jlong*)(((intptr_t)adr) & ((intptr_t)(~0xF))); // Store the value to a 128-bits operand.
operand[0] = lo;
operand[1] = hi; return operand;
}
// Buffer for 128-bits masks used by SSE instructions. static jlong fp_signmask_pool[(4+1)*2]; // 4*128bits(data) + 128bits(alignment)
NEEDS_CLEANUP // remove this definitions ? constRegister IC_Klass = rax; // where the IC klass is cached constRegister SYNC_header = rax; // synchronization header constRegister SHIFT_count = rcx; // where count for shift operations must be
// we jump here if osr happens with the interpreter // state set up to continue at the beginning of the // loop that triggered osr - in particular, we have // the following registers setup: // // rcx: osr buffer //
// build frame
ciMethod* m = compilation()->method();
__ build_frame(initial_frame_size_in_bytes(), bang_size_in_bytes());
// OSR buffer is // // locals[nlocals-1..0] // monitors[0..number_of_locks] // // locals is a direct copy of the interpreter frame so in the osr buffer // so first slot in the local array is the last local from the interpreter // and last slot is local[0] (receiver) from the interpreter // // Similarly with locks. The first lock slot in the osr buffer is the nth lock // from the interpreter frame, the nth lock slot in the osr buffer is 0th lock // in the interpreter frame (the method lock if a sync method)
// Initialize monitors in the compiled activation. // rcx: pointer to osr buffer // // All other registers are dead at this point and the locals will be // copied into place by code emitted in the IR.
Register OSR_buf = osrBufferPointer()->as_pointer_register();
{ assert(frame::interpreter_frame_monitor_size() == BasicObjectLock::size(), "adjust code below"); int monitor_offset = BytesPerWord * method()->max_locals() +
(BasicObjectLock::size() * BytesPerWord) * (number_of_locks - 1); // SharedRuntime::OSR_migration_begin() packs BasicObjectLocks in // the OSR buffer using 2 word entries: first the lock and then // the oop. for (int i = 0; i < number_of_locks; i++) { int slot_offset = monitor_offset - ((i * 2) * BytesPerWord); #ifdef ASSERT // verify the interpreter's monitor has a non-null object
{
Label L;
__ cmpptr(Address(OSR_buf, slot_offset + 1*BytesPerWord), NULL_WORD);
__ jcc(Assembler::notZero, L);
__ stop("locked object is NULL");
__ bind(L);
} #endif
__ movptr(rbx, Address(OSR_buf, slot_offset + 0));
__ movptr(frame_map()->address_for_monitor_lock(i), rbx);
__ movptr(rbx, Address(OSR_buf, slot_offset + 1*BytesPerWord));
__ movptr(frame_map()->address_for_monitor_object(i), rbx);
}
}
}
// inline cache check; done before the frame is built. int LIR_Assembler::check_icache() { Register receiver = FrameMap::receiver_opr->as_register(); Register ic_klass = IC_Klass; constint ic_cmp_size = LP64_ONLY(10) NOT_LP64(9); constbool do_post_padding = VerifyOops || UseCompressedClassPointers; if (!do_post_padding) { // insert some nops so that the verified entry point is aligned on CodeEntryAlignment
__ align(CodeEntryAlignment, __ offset() + ic_cmp_size);
} int offset = __ offset();
__ inline_cache_check(receiver, IC_Klass);
assert(__ offset() % CodeEntryAlignment == 0 || do_post_padding, "alignment must be correct"); if (do_post_padding) { // force alignment after the cache check. // It's been verified to be aligned if !VerifyOops
__ align(CodeEntryAlignment);
} return offset;
}
void LIR_Assembler::clinit_barrier(ciMethod* method) {
assert(VM_Version::supports_fast_class_init_checks(), "sanity");
assert(!method->holder()->is_not_initialized(), "initialization should have been started");
Label L_skip_barrier; Register klass = rscratch1; Register thread = LP64_ONLY( r15_thread ) NOT_LP64( noreg );
assert(thread != noreg, "x86_32 not implemented");
// This specifies the rsp decrement needed to build the frame int LIR_Assembler::initial_frame_size_in_bytes() const { // if rounding, must let FrameMap know!
// The frame_map records size in slots (32bit word)
// subtract two words to account for return address and link return (frame_map()->framesize() - (2*VMRegImpl::slots_per_word)) * VMRegImpl::stack_slot_size;
}
int LIR_Assembler::emit_exception_handler() { // generate code for exception handler
address handler_base = __ start_a_stub(exception_handler_size()); if (handler_base == NULL) { // not enough space left for the handler
bailout("exception handler overflow"); return -1;
}
int offset = code_offset();
// the exception oop and pc are in rax, and rdx // no other registers need to be preserved, so invalidate them
__ invalidate_registers(false, true, true, false, true, true);
// check that there is really an exception
__ verify_not_null_oop(rax);
// Emit the code to remove the frame from the stack in the exception // unwind path. int LIR_Assembler::emit_unwind_handler() { #ifndef PRODUCT if (CommentedAssembly) {
_masm->block_comment("Unwind handler");
} #endif
int offset = code_offset();
// Fetch the exception from TLS and clear out exception related thread state Register thread = NOT_LP64(rsi) LP64_ONLY(r15_thread);
NOT_LP64(__ get_thread(thread));
__ movptr(rax, Address(thread, JavaThread::exception_oop_offset()));
__ movptr(Address(thread, JavaThread::exception_oop_offset()), NULL_WORD);
__ movptr(Address(thread, JavaThread::exception_pc_offset()), NULL_WORD);
__ bind(_unwind_handler_entry);
__ verify_not_null_oop(rax); if (method()->is_synchronized() || compilation()->env()->dtrace_method_probes()) {
__ mov(rbx, rax); // Preserve the exception (rbx is always callee-saved)
}
if (method()->is_synchronized() || compilation()->env()->dtrace_method_probes()) {
__ mov(rax, rbx); // Restore the exception
}
// remove the activation and dispatch to the unwind handler
__ remove_frame(initial_frame_size_in_bytes());
__ jump(RuntimeAddress(Runtime1::entry_for(Runtime1::unwind_exception_id)));
// Emit the slow path assembly if (stub != NULL) {
stub->emit_code(this);
}
return offset;
}
int LIR_Assembler::emit_deopt_handler() { // generate code for exception handler
address handler_base = __ start_a_stub(deopt_handler_size()); if (handler_base == NULL) { // not enough space left for the handler
bailout("deopt handler overflow"); return -1;
}
int offset = code_offset();
InternalAddress here(__ pc());
void LIR_Assembler::const2stack(LIR_Opr src, LIR_Opr dest) {
assert(src->is_constant(), "should not call otherwise");
assert(dest->is_stack(), "should not call otherwise");
LIR_Const* c = src->as_constant_ptr();
switch (c->type()) { case T_INT: // fall through case T_FLOAT:
__ movl(frame_map()->address_for_slot(dest->single_stack_ix()), c->as_jint_bits()); break;
case T_ADDRESS:
__ movptr(frame_map()->address_for_slot(dest->single_stack_ix()), c->as_jint_bits()); break;
case T_OBJECT:
__ movoop(frame_map()->address_for_slot(dest->single_stack_ix()), c->as_jobject(), rscratch1); break;
case T_LONG: // fall through case T_DOUBLE: #ifdef _LP64
__ movptr(frame_map()->address_for_slot(dest->double_stack_ix(),
lo_word_offset_in_bytes),
(intptr_t)c->as_jlong_bits(),
rscratch1); #else
__ movptr(frame_map()->address_for_slot(dest->double_stack_ix(),
lo_word_offset_in_bytes), c->as_jint_lo_bits());
__ movptr(frame_map()->address_for_slot(dest->double_stack_ix(),
hi_word_offset_in_bytes), c->as_jint_hi_bits()); #endif// _LP64 break;
default:
ShouldNotReachHere();
}
}
void LIR_Assembler::const2mem(LIR_Opr src, LIR_Opr dest, BasicType type, CodeEmitInfo* info, bool wide) {
assert(src->is_constant(), "should not call otherwise");
assert(dest->is_address(), "should not call otherwise");
LIR_Const* c = src->as_constant_ptr();
LIR_Address* addr = dest->as_address_ptr();
int null_check_here = code_offset(); switch (type) { case T_INT: // fall through case T_FLOAT:
__ movl(as_Address(addr), c->as_jint_bits()); break;
case T_ADDRESS:
__ movptr(as_Address(addr), c->as_jint_bits()); break;
#ifndef _LP64 // move between fpu-registers (no instruction necessary because of fpu-stack)
} elseif (dest->is_single_fpu() || dest->is_double_fpu()) {
assert(src->is_single_fpu() || src->is_double_fpu(), "must match");
assert(src->fpu() == dest->fpu(), "currently should be nothing to do"); #endif// !_LP64
} else {
ShouldNotReachHere();
}
}
void LIR_Assembler::reg2stack(LIR_Opr src, LIR_Opr dest, BasicType type, bool pop_fpu_stack) {
assert(src->is_register(), "should not call otherwise");
assert(dest->is_stack(), "should not call otherwise");
if (is_reference_type(type)) {
__ verify_oop(src->as_register()); #ifdef _LP64 if (UseCompressedOops && !wide) {
__ movptr(compressed_src, src->as_register());
__ encode_heap_oop(compressed_src); if (patch_code != lir_patch_none) {
info->oop_map()->set_narrowoop(compressed_src->as_VMReg());
}
} #endif
}
if (patch_code != lir_patch_none) {
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
Address toa = as_Address(to_addr);
assert(toa.disp() != 0, "must have");
}
int null_check_here = code_offset(); switch (type) { case T_FLOAT: { #ifdef _LP64
assert(src->is_single_xmm(), "not a float");
__ movflt(as_Address(to_addr), src->as_xmm_float_reg()); #else if (src->is_single_xmm()) {
__ movflt(as_Address(to_addr), src->as_xmm_float_reg());
} else {
assert(src->is_single_fpu(), "must be");
assert(src->fpu_regnr() == 0, "argument must be on TOS"); if (pop_fpu_stack) __ fstp_s(as_Address(to_addr)); else __ fst_s (as_Address(to_addr));
} #endif// _LP64 break;
}
case T_DOUBLE: { #ifdef _LP64
assert(src->is_double_xmm(), "not a double");
__ movdbl(as_Address(to_addr), src->as_xmm_double_reg()); #else if (src->is_double_xmm()) {
__ movdbl(as_Address(to_addr), src->as_xmm_double_reg());
} else {
assert(src->is_double_fpu(), "must be");
assert(src->fpu_regnrLo() == 0, "argument must be on TOS"); if (pop_fpu_stack) __ fstp_d(as_Address(to_addr)); else __ fst_d (as_Address(to_addr));
} #endif// _LP64 break;
}
case T_ARRAY: // fall through case T_OBJECT: // fall through if (UseCompressedOops && !wide) {
__ movl(as_Address(to_addr), compressed_src);
} else {
__ movptr(as_Address(to_addr), src->as_register());
} break; case T_METADATA: // We get here to store a method pointer to the stack to pass to // a dtrace runtime call. This can't work on 64 bit with // compressed klass ptrs: T_METADATA can be a compressed klass // ptr or a 64 bit method pointer.
LP64_ONLY(ShouldNotReachHere());
__ movptr(as_Address(to_addr), src->as_register()); break; case T_ADDRESS:
__ movptr(as_Address(to_addr), src->as_register()); break; case T_INT:
__ movl(as_Address(to_addr), src->as_register()); break;
case T_LONG: { Register from_lo = src->as_register_lo(); Register from_hi = src->as_register_hi(); #ifdef _LP64
__ movptr(as_Address_lo(to_addr), from_lo); #else Register base = to_addr->base()->as_register(); Register index = noreg; if (to_addr->index()->is_register()) {
index = to_addr->index()->as_register();
} if (base == from_lo || index == from_lo) {
assert(base != from_hi, "can't be");
assert(index == noreg || (index != base && index != from_hi), "can't handle this");
__ movl(as_Address_hi(to_addr), from_hi); if (patch != NULL) {
patching_epilog(patch, lir_patch_high, base, info);
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
patch_code = lir_patch_low;
}
__ movl(as_Address_lo(to_addr), from_lo);
} else {
assert(index == noreg || (index != base && index != from_lo), "can't handle this");
__ movl(as_Address_lo(to_addr), from_lo); if (patch != NULL) {
patching_epilog(patch, lir_patch_low, base, info);
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
patch_code = lir_patch_high;
}
__ movl(as_Address_hi(to_addr), from_hi);
} #endif// _LP64 break;
}
case T_BYTE: // fall through case T_BOOLEAN: { Register src_reg = src->as_register();
Address dst_addr = as_Address(to_addr);
assert(VM_Version::is_P6() || src_reg->has_byte_register(), "must use byte registers if not P6");
__ movb(dst_addr, src_reg); break;
}
case T_CHAR: // fall through case T_SHORT:
__ movw(as_Address(to_addr), src->as_register()); break;
if (addr->base()->type() == T_OBJECT) {
__ verify_oop(addr->base()->as_pointer_register());
}
switch (type) { case T_BOOLEAN: // fall through case T_BYTE: // fall through case T_CHAR: // fall through case T_SHORT: if (!VM_Version::is_P6() && !from_addr.uses(dest->as_register())) { // on pre P6 processors we may get partial register stalls // so blow away the value of to_rinfo before loading a // partial word into it. Do it here so that it precedes // the potential patch point below.
__ xorptr(dest->as_register(), dest->as_register());
} break; default: break;
}
PatchingStub* patch = NULL; if (patch_code != lir_patch_none) {
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
assert(from_addr.disp() != 0, "must have");
} if (info != NULL) {
add_debug_info_for_null_check_here(info);
}
switch (type) { case T_FLOAT: { if (dest->is_single_xmm()) {
__ movflt(dest->as_xmm_float_reg(), from_addr);
} else { #ifndef _LP64
assert(dest->is_single_fpu(), "must be");
assert(dest->fpu_regnr() == 0, "dest must be TOS");
__ fld_s(from_addr); #else
ShouldNotReachHere(); #endif// !LP64
} break;
}
case T_DOUBLE: { if (dest->is_double_xmm()) {
__ movdbl(dest->as_xmm_double_reg(), from_addr);
} else { #ifndef _LP64
assert(dest->is_double_fpu(), "must be");
assert(dest->fpu_regnrLo() == 0, "dest must be TOS");
__ fld_d(from_addr); #else
ShouldNotReachHere(); #endif// !LP64
} break;
}
case T_OBJECT: // fall through case T_ARRAY: // fall through if (UseCompressedOops && !wide) {
__ movl(dest->as_register(), from_addr);
} else {
__ movptr(dest->as_register(), from_addr);
} break;
case T_ADDRESS:
__ movptr(dest->as_register(), from_addr); break; case T_INT:
__ movl(dest->as_register(), from_addr); break;
case T_LONG: { Register to_lo = dest->as_register_lo(); Register to_hi = dest->as_register_hi(); #ifdef _LP64
__ movptr(to_lo, as_Address_lo(addr)); #else Register base = addr->base()->as_register(); Register index = noreg; if (addr->index()->is_register()) {
index = addr->index()->as_register();
} if ((base == to_lo && index == to_hi) ||
(base == to_hi && index == to_lo)) { // addresses with 2 registers are only formed as a result of // array access so this code will never have to deal with // patches or null checks.
assert(info == NULL && patch == NULL, "must be");
__ lea(to_hi, as_Address(addr));
__ movl(to_lo, Address(to_hi, 0));
__ movl(to_hi, Address(to_hi, BytesPerWord));
} elseif (base == to_lo || index == to_lo) {
assert(base != to_hi, "can't be");
assert(index == noreg || (index != base && index != to_hi), "can't handle this");
__ movl(to_hi, as_Address_hi(addr)); if (patch != NULL) {
patching_epilog(patch, lir_patch_high, base, info);
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
patch_code = lir_patch_low;
}
__ movl(to_lo, as_Address_lo(addr));
} else {
assert(index == noreg || (index != base && index != to_lo), "can't handle this");
__ movl(to_lo, as_Address_lo(addr)); if (patch != NULL) {
patching_epilog(patch, lir_patch_low, base, info);
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
patch_code = lir_patch_high;
}
__ movl(to_hi, as_Address_hi(addr));
} #endif// _LP64 break;
}
case T_BOOLEAN: // fall through case T_BYTE: { Register dest_reg = dest->as_register();
assert(VM_Version::is_P6() || dest_reg->has_byte_register(), "must use byte registers if not P6"); if (VM_Version::is_P6() || from_addr.uses(dest_reg)) {
__ movsbl(dest_reg, from_addr);
} else {
__ movb(dest_reg, from_addr);
__ shll(dest_reg, 24);
__ sarl(dest_reg, 24);
} break;
}
case T_CHAR: { Register dest_reg = dest->as_register();
assert(VM_Version::is_P6() || dest_reg->has_byte_register(), "must use byte registers if not P6"); if (VM_Version::is_P6() || from_addr.uses(dest_reg)) {
__ movzwl(dest_reg, from_addr);
} else {
__ movw(dest_reg, from_addr);
} break;
}
if (patch != NULL) {
patching_epilog(patch, patch_code, addr->base()->as_register(), info);
}
if (is_reference_type(type)) { #ifdef _LP64 if (UseCompressedOops && !wide) {
__ decode_heap_oop(dest->as_register());
} #endif
// Load barrier has not yet been applied, so ZGC can't verify the oop here if (!UseZGC) {
__ verify_oop(dest->as_register());
}
}
}
NEEDS_CLEANUP; // This could be static?
Address::ScaleFactor LIR_Assembler::array_element_size(BasicType type) const { int elem_size = type2aelembytes(type); switch (elem_size) { case 1: return Address::times_1; case 2: return Address::times_2; case 4: return Address::times_4; case 8: return Address::times_8;
}
ShouldNotReachHere(); return Address::no_scale;
}
void LIR_Assembler::emit_op3(LIR_Op3* op) { switch (op->code()) { case lir_idiv: case lir_irem:
arithmetic_idiv(op->code(),
op->in_opr1(),
op->in_opr2(),
op->in_opr3(),
op->result_opr(),
op->info()); break; case lir_fmad:
__ fmad(op->result_opr()->as_xmm_double_reg(),
op->in_opr1()->as_xmm_double_reg(),
op->in_opr2()->as_xmm_double_reg(),
op->in_opr3()->as_xmm_double_reg()); break; case lir_fmaf:
__ fmaf(op->result_opr()->as_xmm_float_reg(),
op->in_opr1()->as_xmm_float_reg(),
op->in_opr2()->as_xmm_float_reg(),
op->in_opr3()->as_xmm_float_reg()); break; default: ShouldNotReachHere(); break;
}
}
case Bytecodes::_i2b:
move_regs(src->as_register(), dest->as_register());
__ sign_extend_byte(dest->as_register()); break;
case Bytecodes::_i2c:
move_regs(src->as_register(), dest->as_register());
__ andl(dest->as_register(), 0xFFFF); break;
case Bytecodes::_i2s:
move_regs(src->as_register(), dest->as_register());
__ sign_extend_short(dest->as_register()); break;
#ifdef _LP64 case Bytecodes::_f2d:
__ cvtss2sd(dest->as_xmm_double_reg(), src->as_xmm_float_reg()); break;
case Bytecodes::_d2f:
__ cvtsd2ss(dest->as_xmm_float_reg(), src->as_xmm_double_reg()); break;
case Bytecodes::_i2f:
__ cvtsi2ssl(dest->as_xmm_float_reg(), src->as_register()); break;
case Bytecodes::_i2d:
__ cvtsi2sdl(dest->as_xmm_double_reg(), src->as_register()); break;
case Bytecodes::_l2f:
__ cvtsi2ssq(dest->as_xmm_float_reg(), src->as_register_lo()); break;
case Bytecodes::_l2d:
__ cvtsi2sdq(dest->as_xmm_double_reg(), src->as_register_lo()); break;
case Bytecodes::_f2i:
__ convert_f2i(dest->as_register(), src->as_xmm_float_reg()); break;
case Bytecodes::_d2i:
__ convert_d2i(dest->as_register(), src->as_xmm_double_reg()); break;
case Bytecodes::_f2l:
__ convert_f2l(dest->as_register_lo(), src->as_xmm_float_reg()); break;
case Bytecodes::_d2l:
__ convert_d2l(dest->as_register_lo(), src->as_xmm_double_reg()); break; #else case Bytecodes::_f2d: case Bytecodes::_d2f: if (dest->is_single_xmm()) {
__ cvtsd2ss(dest->as_xmm_float_reg(), src->as_xmm_double_reg());
} elseif (dest->is_double_xmm()) {
__ cvtss2sd(dest->as_xmm_double_reg(), src->as_xmm_float_reg());
} else {
assert(src->fpu() == dest->fpu(), "register must be equal"); // do nothing (float result is rounded later through spilling)
} break;
case Bytecodes::_i2f: case Bytecodes::_i2d: if (dest->is_single_xmm()) {
__ cvtsi2ssl(dest->as_xmm_float_reg(), src->as_register());
} elseif (dest->is_double_xmm()) {
__ cvtsi2sdl(dest->as_xmm_double_reg(), src->as_register());
} else {
assert(dest->fpu() == 0, "result must be on TOS");
__ movl(Address(rsp, 0), src->as_register());
__ fild_s(Address(rsp, 0));
} break;
case Bytecodes::_l2f: case Bytecodes::_l2d:
assert(!dest->is_xmm_register(), "result in xmm register not supported (no SSE instruction present)");
assert(dest->fpu() == 0, "result must be on TOS");
__ movptr(Address(rsp, 0), src->as_register_lo());
__ movl(Address(rsp, BytesPerWord), src->as_register_hi());
__ fild_d(Address(rsp, 0)); // float result is rounded later through spilling break;
case Bytecodes::_f2i: case Bytecodes::_d2i: if (src->is_single_xmm()) {
__ cvttss2sil(dest->as_register(), src->as_xmm_float_reg());
} elseif (src->is_double_xmm()) {
__ cvttsd2sil(dest->as_register(), src->as_xmm_double_reg());
} else {
assert(src->fpu() == 0, "input must be on TOS");
__ fldcw(ExternalAddress(StubRoutines::x86::addr_fpu_cntrl_wrd_trunc()));
__ fist_s(Address(rsp, 0));
__ movl(dest->as_register(), Address(rsp, 0));
__ fldcw(ExternalAddress(StubRoutines::x86::addr_fpu_cntrl_wrd_std()));
} // IA32 conversion instructions do not match JLS for overflow, underflow and NaN -> fixup in stub
assert(op->stub() != NULL, "stub required");
__ cmpl(dest->as_register(), 0x80000000);
__ jcc(Assembler::equal, *op->stub()->entry());
__ bind(*op->stub()->continuation()); break;
case Bytecodes::_f2l: case Bytecodes::_d2l:
assert(!src->is_xmm_register(), "input in xmm register not supported (no SSE instruction present)");
assert(src->fpu() == 0, "input must be on TOS");
assert(dest == FrameMap::long0_opr, "runtime stub places result in these registers");
// instruction sequence too long to inline it here
{
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::fpu2long_stub_id)));
} break; #endif// _LP64
if (op->fast_check()) { // get object class // not a safepoint as obj null check happens earlier #ifdef _LP64 if (UseCompressedClassPointers) {
__ load_klass(Rtmp1, obj, tmp_load_klass);
__ cmpptr(k_RInfo, Rtmp1);
} else {
__ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes()));
} #else if (k->is_loaded()) {
__ cmpklass(Address(obj, oopDesc::klass_offset_in_bytes()), k->constant_encoding());
} else {
__ cmpptr(k_RInfo, Address(obj, oopDesc::klass_offset_in_bytes()));
} #endif
__ jcc(Assembler::notEqual, *failure_target); // successful cast, fall through to profile or jump
} else { // get object class // not a safepoint as obj null check happens earlier
__ load_klass(klass_RInfo, obj, tmp_load_klass); if (k->is_loaded()) { // See if we get an immediate positive hit #ifdef _LP64
__ cmpptr(k_RInfo, Address(klass_RInfo, k->super_check_offset())); #else
__ cmpklass(Address(klass_RInfo, k->super_check_offset()), k->constant_encoding()); #endif// _LP64 if ((juint)in_bytes(Klass::secondary_super_cache_offset()) != k->super_check_offset()) {
__ jcc(Assembler::notEqual, *failure_target); // successful cast, fall through to profile or jump
} else { // See if we get an immediate positive hit
__ jcc(Assembler::equal, *success_target); // check for self #ifdef _LP64
__ cmpptr(klass_RInfo, k_RInfo); #else
__ cmpklass(klass_RInfo, k->constant_encoding()); #endif// _LP64
__ jcc(Assembler::equal, *success_target);
__ push(klass_RInfo); #ifdef _LP64
__ push(k_RInfo); #else
__ pushklass(k->constant_encoding(), noreg); #endif// _LP64
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id)));
__ pop(klass_RInfo);
__ pop(klass_RInfo); // result is a boolean
__ cmpl(klass_RInfo, 0);
__ jcc(Assembler::equal, *failure_target); // successful cast, fall through to profile or jump
}
} else { // perform the fast part of the checking logic
__ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, NULL); // call out-of-line instance of __ check_klass_subtype_slow_path(...):
__ push(klass_RInfo);
__ push(k_RInfo);
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id)));
__ pop(klass_RInfo);
__ pop(k_RInfo); // result is a boolean
__ cmpl(k_RInfo, 0);
__ jcc(Assembler::equal, *failure_target); // successful cast, fall through to profile or jump
}
} if (op->should_profile()) { Register mdo = klass_RInfo, recv = k_RInfo;
__ bind(profile_cast_success);
__ mov_metadata(mdo, md->constant_encoding());
__ load_klass(recv, obj, tmp_load_klass);
type_profile_helper(mdo, md, data, recv, success);
__ jmp(*success);
// get instance klass (it's already uncompressed)
__ movptr(k_RInfo, Address(k_RInfo, ObjArrayKlass::element_klass_offset())); // perform the fast part of the checking logic
__ check_klass_subtype_fast_path(klass_RInfo, k_RInfo, Rtmp1, success_target, failure_target, NULL); // call out-of-line instance of __ check_klass_subtype_slow_path(...):
__ push(klass_RInfo);
__ push(k_RInfo);
__ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id)));
__ pop(klass_RInfo);
__ pop(k_RInfo); // result is a boolean
__ cmpl(k_RInfo, 0);
__ jcc(Assembler::equal, *failure_target); // fall through to the success case
} elseif (op->code() == lir_cas_int || op->code() == lir_cas_obj ) {
NOT_LP64(assert(op->addr()->is_single_cpu(), "must be single");) Register addr = (op->addr()->is_single_cpu() ? op->addr()->as_register() : op->addr()->as_register_lo()); Register newval = op->new_value()->as_register(); Register cmpval = op->cmp_value()->as_register();
assert(cmpval == rax, "wrong register");
assert(newval != noreg, "new val must be register");
assert(cmpval != newval, "cmp and new values must be in different registers");
assert(cmpval != addr, "cmp and addr must be in different registers");
assert(newval != addr, "new value and addr must be in different registers");
if ( op->code() == lir_cas_obj) { #ifdef _LP64 if (UseCompressedOops) {
__ encode_heap_oop(cmpval);
__ mov(rscratch1, newval);
__ encode_heap_oop(rscratch1);
__ lock(); // cmpval (rax) is implicitly used by this instruction
__ cmpxchgl(rscratch1, Address(addr, 0));
} else #endif
{
__ lock();
__ cmpxchgptr(newval, Address(addr, 0));
}
} else {
assert(op->code() == lir_cas_int, "lir_cas_int expected");
__ lock();
__ cmpxchgl(newval, Address(addr, 0));
} #ifdef _LP64
} elseif (op->code() == lir_cas_long) { Register addr = (op->addr()->is_single_cpu() ? op->addr()->as_register() : op->addr()->as_register_lo()); Register newval = op->new_value()->as_register_lo(); Register cmpval = op->cmp_value()->as_register_lo();
assert(cmpval == rax, "wrong register");
assert(newval != noreg, "new val must be register");
assert(cmpval != newval, "cmp and new values must be in different registers");
assert(cmpval != addr, "cmp and addr must be in different registers");
assert(newval != addr, "new value and addr must be in different registers");
__ lock();
__ cmpxchgq(newval, Address(addr, 0)); #endif// _LP64
} else {
Unimplemented();
}
}
void LIR_Assembler::arith_op(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr dest, CodeEmitInfo* info, bool pop_fpu_stack) {
assert(info == NULL, "should never be used, idiv/irem and ldiv/lrem not handled by this method");
if (left->is_single_cpu()) {
assert(left == dest, "left and dest must be equal"); Register lreg = left->as_register();
if (right->is_single_cpu()) { // cpu register - cpu register Register rreg = right->as_register(); switch (code) { case lir_add: __ addl (lreg, rreg); break; case lir_sub: __ subl (lreg, rreg); break; case lir_mul: __ imull(lreg, rreg); break; default: ShouldNotReachHere();
}
case lir_sqrt: __ sqrtsd(dest->as_xmm_double_reg(), value->as_xmm_double_reg()); break; // all other intrinsics are not available in the SSE instruction set, so FPU is used default : ShouldNotReachHere();
}
#ifndef _LP64
} elseif (value->is_double_fpu()) {
assert(value->fpu_regnrLo() == 0 && dest->fpu_regnrLo() == 0, "both must be on TOS"); switch(code) { case lir_abs : __ fabs() ; break; case lir_sqrt : __ fsqrt(); break; default : ShouldNotReachHere();
} #endif// !_LP64
} else {
Unimplemented();
}
}
// we assume that rax, and rdx can be overwritten void LIR_Assembler::arithmetic_idiv(LIR_Code code, LIR_Opr left, LIR_Opr right, LIR_Opr temp, LIR_Opr result, CodeEmitInfo* info) {
assert(left->is_single_cpu(), "left must be register");
assert(right->is_single_cpu() || right->is_constant(), "right must be register or constant");
assert(result->is_single_cpu(), "result must be register");
#ifndef _LP64
} elseif(opr1->is_single_fpu() || opr1->is_double_fpu()) {
assert(opr1->is_fpu_register() && opr1->fpu() == 0, "currently left-hand side must be on TOS (relax this restriction)");
assert(opr2->is_fpu_register(), "both must be registers");
__ fcmp(noreg, opr2->fpu(), op->fpu_pop_count() > 0, op->fpu_pop_count() > 1); #endif// LP64
} elseif (opr1->is_address() && opr2->is_constant()) {
LIR_Const* c = opr2->as_constant_ptr(); #ifdef _LP64 if (is_reference_type(c->type())) {
assert(condition == lir_cond_equal || condition == lir_cond_notEqual, "need to reverse");
__ movoop(rscratch1, c->as_jobject());
} #endif// LP64 if (op->info() != NULL) {
add_debug_info_for_null_check_here(op->info());
} // special case: address - constant
LIR_Address* addr = opr1->as_address_ptr(); if (c->type() == T_INT) {
__ cmpl(as_Address(addr), c->as_jint());
} elseif (is_reference_type(c->type())) { #ifdef _LP64 // %%% Make this explode if addr isn't reachable until we figure out a // better strategy by giving noreg as the temp for as_Address
__ cmpoop(rscratch1, as_Address(addr, noreg)); #else
__ cmpoop(as_Address(addr), c->as_jobject()); #endif// _LP64
} else {
ShouldNotReachHere();
}
void LIR_Assembler::align_call(LIR_Code code) { // make sure that the displacement word of the call ends up word aligned int offset = __ offset(); switch (code) { case lir_static_call: case lir_optvirtual_call: case lir_dynamic_call:
offset += NativeCall::displacement_offset; break; case lir_icvirtual_call:
offset += NativeCall::displacement_offset + NativeMovConstReg::instruction_size; break; default: ShouldNotReachHere();
}
__ align(BytesPerWord, offset);
}
// make sure that the displacement word of the call ends up word aligned
__ align(BytesPerWord, __ offset() + NativeMovConstReg::instruction_size + NativeCall::displacement_offset);
__ relocate(static_stub_Relocation::spec(call_pc));
__ mov_metadata(rbx, (Metadata*)NULL); // must be set to -1 at code generation time
assert(((__ offset() + 1) % BytesPerWord) == 0, "must be aligned"); // On 64bit this will die since it will take a movq & jmp, must be only a jmp
__ jump(RuntimeAddress(__ pc()));
// exception object is not added to oop map by LinearScan // (LinearScan assumes that no oops are in fixed registers)
info->add_register_oop(exceptionOop);
Runtime1::StubID unwind_id;
// get current pc information // pc is only needed if the method has an exception handler, the unwind code does not need it. int pc_for_athrow_offset = __ offset();
InternalAddress pc_for_athrow(__ pc());
__ lea(exceptionPC->as_register(), pc_for_athrow);
add_call_info(pc_for_athrow_offset, info); // for exception handler
// optimized version for linear scan: // * count must be already in ECX (guaranteed by LinearScan) // * left and dest must be equal // * tmp must be unused
assert(count->as_register() == SHIFT_count, "count must be in ECX");
assert(left == dest, "left and dest must be equal");
assert(tmp->is_illegal(), "wasting a register if tmp is allocated");
if (left->is_single_cpu()) { Register value = left->as_register();
assert(value != SHIFT_count, "left cannot be ECX");
switch (code) { case lir_shl: __ shll(value); break; case lir_shr: __ sarl(value); break; case lir_ushr: __ shrl(value); break; default: ShouldNotReachHere();
}
} elseif (left->is_double_cpu()) { Register lo = left->as_register_lo(); Register hi = left->as_register_hi();
assert(lo != SHIFT_count && hi != SHIFT_count, "left cannot be ECX"); #ifdef _LP64 switch (code) { case lir_shl: __ shlptr(lo); break; case lir_shr: __ sarptr(lo); break; case lir_ushr: __ shrptr(lo); break; default: ShouldNotReachHere();
} #else
void LIR_Assembler::shift_op(LIR_Code code, LIR_Opr left, jint count, LIR_Opr dest) { if (dest->is_single_cpu()) { // first move left into dest so that left is not destroyed by the shift Register value = dest->as_register();
count = count & 0x1F; // Java spec
move_regs(left->as_register(), value); switch (code) { case lir_shl: __ shll(value, count); break; case lir_shr: __ sarl(value, count); break; case lir_ushr: __ shrl(value, count); break; default: ShouldNotReachHere();
}
} elseif (dest->is_double_cpu()) { #ifndef _LP64
Unimplemented(); #else // first move left into dest so that left is not destroyed by the shift Register value = dest->as_register_lo();
count = count & 0x1F; // Java spec
void LIR_Assembler::store_parameter(Register r, int offset_from_rsp_in_words) {
assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord;
assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
__ movptr (Address(rsp, offset_from_rsp_in_bytes), r);
}
void LIR_Assembler::store_parameter(jint c, int offset_from_rsp_in_words) {
assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord;
assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
__ movptr (Address(rsp, offset_from_rsp_in_bytes), c);
}
void LIR_Assembler::store_parameter(jobject o, int offset_from_rsp_in_words) {
assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord;
assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
__ movoop(Address(rsp, offset_from_rsp_in_bytes), o, rscratch1);
}
void LIR_Assembler::store_parameter(Metadata* m, int offset_from_rsp_in_words) {
assert(offset_from_rsp_in_words >= 0, "invalid offset from rsp"); int offset_from_rsp_in_bytes = offset_from_rsp_in_words * BytesPerWord;
assert(offset_from_rsp_in_bytes < frame_map()->reserved_argument_area_size(), "invalid offset");
__ mov_metadata(Address(rsp, offset_from_rsp_in_bytes), m, rscratch1);
}
// This code replaces a call to arraycopy; no exception may // be thrown in this code, they must be thrown in the System.arraycopy // activation frame; we could save some checks if this would not be the case void LIR_Assembler::emit_arraycopy(LIR_OpArrayCopy* op) {
ciArrayKlass* default_type = op->expected_type(); Register src = op->src()->as_register(); Register dst = op->dst()->as_register(); Register src_pos = op->src_pos()->as_register(); Register dst_pos = op->dst_pos()->as_register(); Register length = op->length()->as_register(); Register tmp = op->tmp()->as_register(); Register tmp_load_klass = LP64_ONLY(rscratch1) NOT_LP64(noreg);
// if we don't know anything, just go through the generic arraycopy if (default_type == NULL) { // save outgoing arguments on stack in case call to System.arraycopy is needed // HACK ALERT. This code used to push the parameters in a hardwired fashion // for interpreter calling conventions. Now we have to do it in new style conventions. // For the moment until C1 gets the new register allocator I just force all the // args to the right place (except the register args) and then on the back side // reload the register args properly if we go slow path. Yuck
// These are proper for the calling convention
store_parameter(length, 2);
store_parameter(dst_pos, 1);
store_parameter(dst, 0);
// these are just temporary placements until we need to reload
store_parameter(src_pos, 3);
store_parameter(src, 4);
NOT_LP64(assert(src == rcx && src_pos == rdx, "mismatch in calling convention");)
// pass arguments: may push as this is not a safepoint; SP must be fix at each safepoint #ifdef _LP64 // The arguments are in java calling convention so we can trivially shift them to C // convention
assert_different_registers(c_rarg0, j_rarg1, j_rarg2, j_rarg3, j_rarg4);
__ mov(c_rarg0, j_rarg0);
assert_different_registers(c_rarg1, j_rarg2, j_rarg3, j_rarg4);
__ mov(c_rarg1, j_rarg1);
assert_different_registers(c_rarg2, j_rarg3, j_rarg4);
__ mov(c_rarg2, j_rarg2);
assert_different_registers(c_rarg3, j_rarg4);
__ mov(c_rarg3, j_rarg3); #ifdef _WIN64 // Allocate abi space for args but be sure to keep stack aligned
__ subptr(rsp, 6*wordSize);
store_parameter(j_rarg4, 4); #ifndef PRODUCT if (PrintC1Statistics) {
__ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt), rscratch1);
} #endif
__ call(RuntimeAddress(copyfunc_addr));
__ addptr(rsp, 6*wordSize); #else
__ mov(c_rarg4, j_rarg4); #ifndef PRODUCT if (PrintC1Statistics) {
__ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt), rscratch1);
} #endif
__ call(RuntimeAddress(copyfunc_addr)); #endif// _WIN64 #else
__ push(length);
__ push(dst_pos);
__ push(dst);
__ push(src_pos);
__ push(src);
#ifndef PRODUCT if (PrintC1Statistics) {
__ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt), rscratch1);
} #endif
__ call_VM_leaf(copyfunc_addr, 5); // removes pushed parameter from the stack
// length and pos's are all sign extended at this point on 64bit
// test for NULL if (flags & LIR_OpArrayCopy::src_null_check) {
__ testptr(src, src);
__ jcc(Assembler::zero, *stub->entry());
} if (flags & LIR_OpArrayCopy::dst_null_check) {
__ testptr(dst, dst);
__ jcc(Assembler::zero, *stub->entry());
}
// If the compiler was not able to prove that exact type of the source or the destination // of the arraycopy is an array type, check at runtime if the source or the destination is // an instance type. if (flags & LIR_OpArrayCopy::type_check) { if (!(flags & LIR_OpArrayCopy::dst_objarray)) {
__ load_klass(tmp, dst, tmp_load_klass);
__ cmpl(Address(tmp, in_bytes(Klass::layout_helper_offset())), Klass::_lh_neutral_value);
__ jcc(Assembler::greaterEqual, *stub->entry());
}
#ifdef _LP64
__ movl2ptr(src_pos, src_pos); //higher 32bits must be null
__ movl2ptr(dst_pos, dst_pos); //higher 32bits must be null #endif
if (flags & LIR_OpArrayCopy::type_check) { // We don't know the array types are compatible if (basic_type != T_OBJECT) { // Simple test for basic type arrays if (UseCompressedClassPointers) {
__ movl(tmp, src_klass_addr);
__ cmpl(tmp, dst_klass_addr);
} else {
__ movptr(tmp, src_klass_addr);
__ cmpptr(tmp, dst_klass_addr);
}
__ jcc(Assembler::notEqual, *stub->entry());
} else { // For object arrays, if src is a sub class of dst then we can // safely do the copy.
Label cont, slow;
address copyfunc_addr = StubRoutines::checkcast_arraycopy(); if (copyfunc_addr != NULL) { // use stub if available // src is not a sub class of dst so we have to do a // per-element check.
int mask = LIR_OpArrayCopy::src_objarray|LIR_OpArrayCopy::dst_objarray; if ((flags & mask) != mask) { // Check that at least both of them object arrays.
assert(flags & mask, "one of the two should be known to be an object array");
// Spill because stubs can use any register they like and it's // easier to restore just those that we care about.
store_parameter(dst, 0);
store_parameter(dst_pos, 1);
store_parameter(length, 2);
store_parameter(src_pos, 3);
store_parameter(src, 4);
#ifdef ASSERT if (basic_type != T_OBJECT || !(flags & LIR_OpArrayCopy::type_check)) { // Sanity check the known type with the incoming class. For the // primitive case the types must match exactly with src.klass and // dst.klass each exactly matching the default type. For the // object array case, if no type check is needed then either the // dst type is exactly the expected type and the src type is a // subtype which we can't check or src is the same array as dst // but not necessarily exactly of type default_type.
Label known_ok, halt;
__ mov_metadata(tmp, default_type->constant_encoding()); #ifdef _LP64 if (UseCompressedClassPointers) {
__ encode_klass_not_null(tmp, rscratch1);
} #endif
if (basic_type != T_OBJECT) {
if (UseCompressedClassPointers) __ cmpl(tmp, dst_klass_addr); else __ cmpptr(tmp, dst_klass_addr);
__ jcc(Assembler::notEqual, halt); if (UseCompressedClassPointers) __ cmpl(tmp, src_klass_addr); else __ cmpptr(tmp, src_klass_addr);
__ jcc(Assembler::equal, known_ok);
} else { if (UseCompressedClassPointers) __ cmpl(tmp, dst_klass_addr); else __ cmpptr(tmp, dst_klass_addr);
__ jcc(Assembler::equal, known_ok);
__ cmpptr(src, dst);
__ jcc(Assembler::equal, known_ok);
}
__ bind(halt);
__ stop("incorrect type information in arraycopy");
__ bind(known_ok);
} #endif
#ifndef PRODUCT if (PrintC1Statistics) {
__ incrementl(ExternalAddress(Runtime1::arraycopy_count_address(basic_type)), rscratch1);
} #endif
void LIR_Assembler::emit_updatecrc32(LIR_OpUpdateCRC32* op) {
assert(op->crc()->is_single_cpu(), "crc must be register");
assert(op->val()->is_single_cpu(), "byte value must be register");
assert(op->result_opr()->is_single_cpu(), "result must be register"); Register crc = op->crc()->as_register(); Register val = op->val()->as_register(); Register res = op->result_opr()->as_register();
void LIR_Assembler::emit_lock(LIR_OpLock* op) { Register obj = op->obj_opr()->as_register(); // may not be an oop Register hdr = op->hdr_opr()->as_register(); Register lock = op->lock_opr()->as_register(); if (UseHeavyMonitors) { if (op->info() != NULL) {
add_debug_info_for_null_check_here(op->info());
__ null_check(obj);
}
__ jmp(*op->stub()->entry());
} elseif (op->code() == lir_lock) {
assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header"); // add debug info for NullPointerException only if one is possible int null_check_offset = __ lock_object(hdr, obj, lock, *op->stub()->entry()); if (op->info() != NULL) {
add_debug_info_for_null_check(null_check_offset, op->info());
} // done
} elseif (op->code() == lir_unlock) {
assert(BasicLock::displaced_header_offset_in_bytes() == 0, "lock_reg must point to the displaced header");
__ unlock_object(hdr, obj, lock, *op->stub()->entry());
} else {
Unimplemented();
}
__ bind(*op->stub()->continuation());
}
// Update counter for all call types
ciMethodData* md = method->method_data_or_null();
assert(md != NULL, "Sanity");
ciProfileData* data = md->bci_to_data(bci);
assert(data != NULL && data->is_CounterData(), "need CounterData for calls");
assert(op->mdo()->is_single_cpu(), "mdo must be allocated"); Register mdo = op->mdo()->as_register();
__ mov_metadata(mdo, md->constant_encoding());
Address counter_addr(mdo, md->byte_offset_of_slot(data, CounterData::count_offset())); // Perform additional virtual call profiling for invokevirtual and // invokeinterface bytecodes if (op->should_profile_receiver_type()) {
assert(op->recv()->is_single_cpu(), "recv must be allocated"); Register recv = op->recv()->as_register();
assert_different_registers(mdo, recv);
assert(data->is_VirtualCallData(), "need VirtualCallData for virtual calls");
ciKlass* known_klass = op->known_holder(); if (C1OptimizeVirtualCallProfiling && known_klass != NULL) { // We know the type that will be seen at this call site; we can // statically update the MethodData* rather than needing to do // dynamic tests on the receiver type
// NOTE: we should probably put a lock around this search to // avoid collisions by concurrent compilations
ciVirtualCallData* vc_data = (ciVirtualCallData*) data;
uint i; for (i = 0; i < VirtualCallData::row_limit(); i++) {
ciKlass* receiver = vc_data->receiver(i); if (known_klass->equals(receiver)) {
Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)));
__ addptr(data_addr, DataLayout::counter_increment); return;
}
}
// Receiver type not found in profile data; select an empty slot
// Note that this is less efficient than it should be because it // always does a write to the receiver part of the // VirtualCallData rather than just the first time for (i = 0; i < VirtualCallData::row_limit(); i++) {
ciKlass* receiver = vc_data->receiver(i); if (receiver == NULL) {
Address recv_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_offset(i)));
__ mov_metadata(recv_addr, known_klass->constant_encoding(), rscratch1);
Address data_addr(mdo, md->byte_offset_of_slot(data, VirtualCallData::receiver_count_offset(i)));
__ addptr(data_addr, DataLayout::counter_increment); return;
}
}
} else {
__ load_klass(recv, recv, tmp_load_klass);
Label update_done;
type_profile_helper(mdo, md, data, recv, &update_done); // Receiver did not match any saved receiver and there is no empty row for it. // Increment total counter to indicate polymorphic case.
__ addptr(counter_addr, DataLayout::counter_increment);
if (do_update) { #ifdef ASSERT if (exact_klass != NULL) {
Label ok;
__ load_klass(tmp, tmp, tmp_load_klass);
__ push(tmp);
__ mov_metadata(tmp, exact_klass->constant_encoding());
__ cmpptr(tmp, Address(rsp, 0));
__ jcc(Assembler::equal, ok);
__ stop("exact klass and actual klass differ");
__ bind(ok);
__ pop(tmp);
} #endif if (!no_conflict) { if (exact_klass == NULL || TypeEntries::is_type_none(current_klass)) { if (exact_klass != NULL) {
__ mov_metadata(tmp, exact_klass->constant_encoding());
} else {
__ load_klass(tmp, tmp, tmp_load_klass);
}
__ xorptr(tmp, mdo_addr);
__ testptr(tmp, TypeEntries::type_klass_mask); // klass seen before, nothing to do. The unknown bit may have been // set already but no need to check.
__ jccb(Assembler::zero, next);
__ testptr(tmp, TypeEntries::type_unknown);
__ jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore.
if (TypeEntries::is_type_none(current_klass)) {
__ cmpptr(mdo_addr, 0);
__ jccb(Assembler::equal, none);
__ cmpptr(mdo_addr, TypeEntries::null_seen);
__ jccb(Assembler::equal, none); // There is a chance that the checks above (re-reading profiling // data from memory) fail if another thread has just set the // profiling to this obj's klass
__ xorptr(tmp, mdo_addr);
__ testptr(tmp, TypeEntries::type_klass_mask);
__ jccb(Assembler::zero, next);
}
} else {
assert(ciTypeEntries::valid_ciklass(current_klass) != NULL &&
ciTypeEntries::valid_ciklass(current_klass) != exact_klass, "conflict only");
__ movptr(tmp, mdo_addr);
__ testptr(tmp, TypeEntries::type_unknown);
__ jccb(Assembler::notZero, next); // already unknown. Nothing to do anymore.
}
// different than before. Cannot keep accurate profile.
__ orptr(mdo_addr, TypeEntries::type_unknown);
if (TypeEntries::is_type_none(current_klass)) {
__ jmpb(next);
__ bind(none); // first time here. Set profile type.
__ movptr(mdo_addr, tmp);
}
} else { // There's a single possible klass at this profile point
assert(exact_klass != NULL, "should be"); if (TypeEntries::is_type_none(current_klass)) {
__ mov_metadata(tmp, exact_klass->constant_encoding());
__ xorptr(tmp, mdo_addr);
__ testptr(tmp, TypeEntries::type_klass_mask); #ifdef ASSERT
__ jcc(Assembler::zero, next);
{
Label ok;
__ push(tmp);
__ cmpptr(mdo_addr, 0);
__ jcc(Assembler::equal, ok);
__ cmpptr(mdo_addr, TypeEntries::null_seen);
__ jcc(Assembler::equal, ok); // may have been set by another thread
__ mov_metadata(tmp, exact_klass->constant_encoding());
__ xorptr(tmp, mdo_addr);
__ testptr(tmp, TypeEntries::type_mask);
__ jcc(Assembler::zero, ok);
} elseif (dest->is_single_xmm()) { #ifdef _LP64 if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
assert(tmp->is_valid(), "need temporary");
assert_different_registers(left->as_xmm_float_reg(), tmp->as_xmm_float_reg());
__ vpxor(dest->as_xmm_float_reg(), tmp->as_xmm_float_reg(), left->as_xmm_float_reg(), 2);
} else #endif
{
assert(!tmp->is_valid(), "do not need temporary"); if (left->as_xmm_float_reg() != dest->as_xmm_float_reg()) {
__ movflt(dest->as_xmm_float_reg(), left->as_xmm_float_reg());
}
__ xorps(dest->as_xmm_float_reg(),
ExternalAddress((address)float_signflip_pool),
rscratch1);
}
} elseif (dest->is_double_xmm()) { #ifdef _LP64 if (UseAVX > 2 && !VM_Version::supports_avx512vl()) {
assert(tmp->is_valid(), "need temporary");
assert_different_registers(left->as_xmm_double_reg(), tmp->as_xmm_double_reg());
__ vpxor(dest->as_xmm_double_reg(), tmp->as_xmm_double_reg(), left->as_xmm_double_reg(), 2);
} else #endif
{
assert(!tmp->is_valid(), "do not need temporary"); if (left->as_xmm_double_reg() != dest->as_xmm_double_reg()) {
__ movdbl(dest->as_xmm_double_reg(), left->as_xmm_double_reg());
}
__ xorpd(dest->as_xmm_double_reg(),
ExternalAddress((address)double_signflip_pool),
rscratch1);
} #ifndef _LP64
} elseif (left->is_single_fpu() || left->is_double_fpu()) {
assert(left->fpu() == 0, "arg must be on TOS");
assert(dest->fpu() == 0, "dest must be TOS");
__ fchs(); #endif// !_LP64
} else {
ShouldNotReachHere();
}
}
void LIR_Assembler::leal(LIR_Opr src, LIR_Opr dest, LIR_PatchCode patch_code, CodeEmitInfo* info) {
assert(src->is_address(), "must be an address");
assert(dest->is_register(), "must be a register");
PatchingStub* patch = NULL; if (patch_code != lir_patch_none) {
patch = new PatchingStub(_masm, PatchingStub::access_field_id);
}
if (op->in_opr1()->is_valid()) {
assert(op->in_opr2()->is_valid(), "both operands must be valid");
comp_op(op->condition(), op->in_opr1(), op->in_opr2(), op);
} else {
assert(op->in_opr2()->is_illegal(), "both operands must be illegal");
assert(op->condition() == lir_cond_always, "no other conditions allowed");
}
Label ok; if (op->condition() != lir_cond_always) {
Assembler::Condition acond = Assembler::zero; switch (op->condition()) { case lir_cond_equal: acond = Assembler::equal; break; case lir_cond_notEqual: acond = Assembler::notEqual; break; case lir_cond_less: acond = Assembler::less; break; case lir_cond_lessEqual: acond = Assembler::lessEqual; break; case lir_cond_greaterEqual: acond = Assembler::greaterEqual;break; case lir_cond_greater: acond = Assembler::greater; break; case lir_cond_belowEqual: acond = Assembler::belowEqual; break; case lir_cond_aboveEqual: acond = Assembler::aboveEqual; break; default: ShouldNotReachHere();
}
__ jcc(acond, ok);
} if (op->halt()) { constchar* str = __ code_string(op->msg());
__ stop(str);
} else {
breakpoint();
}
__ bind(ok);
} #endif
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 und die Messung sind noch experimentell.