< prev index next >
src/hotspot/cpu/aarch64/macroAssembler_aarch64.cpp
Print this page
*** 44,53 ****
--- 44,54 ----
#include "runtime/biasedLocking.hpp"
#include "runtime/icache.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/sharedRuntime.hpp"
+ #include "runtime/signature_cc.hpp"
#include "runtime/thread.hpp"
#ifdef COMPILER1
#include "c1/c1_LIRAssembler.hpp"
#endif
#ifdef COMPILER2
*** 4086,4095 ****
--- 4087,4097 ----
}
void MacroAssembler::access_store_at(BasicType type, DecoratorSet decorators,
Address dst, Register src,
Register tmp1, Register thread_tmp, Register tmp3) {
+
BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
decorators = AccessInternal::decorator_fixup(decorators);
bool as_raw = (decorators & AS_RAW) != 0;
if (as_raw) {
bs->BarrierSetAssembler::store_at(this, decorators, type, dst, src, tmp1, thread_tmp, tmp3);
*** 5954,5966 ****
if (VerifyStackAtCalls) {
Unimplemented();
}
}
void MacroAssembler::unpack_value_args(Compile* C, bool receiver_only) {
! // Called from MachVEP node
! unimplemented("Support for ValueTypePassFieldsAsArgs and ValueTypeReturnedAsFields is not implemented");
}
! void MacroAssembler::store_value_type_fields_to_buf(ciValueKlass* vk) {
! super_call_VM_leaf(StubRoutines::store_value_type_fields_to_buf());
}
--- 5956,6326 ----
if (VerifyStackAtCalls) {
Unimplemented();
}
}
+ int MacroAssembler::store_value_type_fields_to_buf(ciValueKlass* vk, bool from_interpreter) {
+ // A value type might be returned. If fields are in registers we
+ // need to allocate a value type instance and initialize it with
+ // the value of the fields.
+ Label skip;
+ // We only need a new buffered value if a new one is not returned
+ cmp(r0, (u1) 1);
+ br(Assembler::EQ, skip);
+ int call_offset = -1;
+
+ Label slow_case;
+
+ // Try to allocate a new buffered value (from the heap)
+ if (UseTLAB) {
+
+ if (vk != NULL) {
+ // Called from C1, where the return type is statically known.
+ mov(r1, (intptr_t)vk->get_ValueKlass());
+ jint lh = vk->layout_helper();
+ assert(lh != Klass::_lh_neutral_value, "inline class in return type must have been resolved");
+ mov(r14, lh);
+ } else {
+ // Call from interpreter. R0 contains ((the ValueKlass* of the return type) | 0x01)
+ andr(r1, r0, -2);
+ // get obj size
+ ldrw(r14, Address(rscratch1 /*klass*/, Klass::layout_helper_offset()));
+ }
+
+ ldr(r13, Address(rthread, in_bytes(JavaThread::tlab_top_offset())));
+
+ // check whether we have space in TLAB,
+ // rscratch1 contains pointer to just allocated obj
+ lea(r14, Address(r13, r14));
+ ldr(rscratch1, Address(rthread, in_bytes(JavaThread::tlab_end_offset())));
+
+ cmp(r14, rscratch1);
+ br(Assembler::GT, slow_case);
+
+ // OK we have room in TLAB,
+ // Set new TLAB top
+ str(r14, Address(rthread, in_bytes(JavaThread::tlab_top_offset())));
+
+ // Set new class always locked
+ mov(rscratch1, (uint64_t) markOopDesc::always_locked_prototype());
+ str(rscratch1, Address(r13, oopDesc::mark_offset_in_bytes()));
+
+ store_klass_gap(r13, zr); // zero klass gap for compressed oops
+ if (vk == NULL) {
+ // store_klass corrupts rbx, so save it in rax for later use (interpreter case only).
+ mov(r0, r1);
+ }
+
+ store_klass(r13, r1); // klass
+
+ if (vk != NULL) {
+ // FIXME -- do the packing in-line to avoid the runtime call
+ mov(r0, r13);
+ far_call(RuntimeAddress(vk->pack_handler())); // no need for call info as this will not safepoint.
+ } else {
+
+ // We have our new buffered value, initialize its fields with a
+ // value class specific handler
+ ldr(r1, Address(r0, InstanceKlass::adr_valueklass_fixed_block_offset()));
+ ldr(r1, Address(r1, ValueKlass::pack_handler_offset()));
+
+ // Mov new class to r0 and call pack_handler
+ mov(r0, r13);
+ blr(r1);
+ }
+ b(skip);
+ }
+
+ bind(slow_case);
+ // We failed to allocate a new value, fall back to a runtime
+ // call. Some oop field may be live in some registers but we can't
+ // tell. That runtime call will take care of preserving them
+ // across a GC if there's one.
+
+
+ if (from_interpreter) {
+ super_call_VM_leaf(StubRoutines::store_value_type_fields_to_buf());
+ } else {
+ ldr(rscratch1, RuntimeAddress(StubRoutines::store_value_type_fields_to_buf()));
+ blr(rscratch1);
+ call_offset = offset();
+ }
+
+ bind(skip);
+ return call_offset;
+ }
+
+ // Move a value between registers/stack slots and update the reg_state
+ bool MacroAssembler::move_helper(VMReg from, VMReg to, BasicType bt, RegState reg_state[], int ret_off, int extra_stack_offset) {
+ if (reg_state[to->value()] == reg_written) {
+ return true; // Already written
+ }
+
+ if (from != to && bt != T_VOID) {
+ if (reg_state[to->value()] == reg_readonly) {
+ return false; // Not yet writable
+ }
+ if (from->is_reg()) {
+ if (to->is_reg()) {
+ mov(to->as_Register(), from->as_Register());
+ } else {
+ int st_off = to->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset;
+ Address to_addr = Address(sp, st_off);
+ if (from->is_FloatRegister()) {
+ if (bt == T_DOUBLE) {
+ strd(from->as_FloatRegister(), to_addr);
+ } else {
+ assert(bt == T_FLOAT, "must be float");
+ strs(from->as_FloatRegister(), to_addr);
+ }
+ } else {
+ str(from->as_Register(), to_addr);
+ }
+ }
+ } else {
+ Address from_addr = Address(sp, from->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset);
+ if (to->is_reg()) {
+ if (to->is_FloatRegister()) {
+ if (bt == T_DOUBLE) {
+ ldrd(to->as_FloatRegister(), from_addr);
+ } else {
+ assert(bt == T_FLOAT, "must be float");
+ ldrs(to->as_FloatRegister(), from_addr);
+ }
+ } else {
+ ldr(to->as_Register(), from_addr);
+ }
+ } else {
+ int st_off = to->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset;
+ ldr(rscratch1, from_addr);
+ str(rscratch1, Address(sp, st_off));
+ }
+ }
+ }
+
+ // Update register states
+ reg_state[from->value()] = reg_writable;
+ reg_state[to->value()] = reg_written;
+ return true;
+ }
+
+ // Read all fields from a value type oop and store the values in registers/stack slots
+ bool MacroAssembler::unpack_value_helper(const GrowableArray<SigEntry>* sig, int& sig_index, VMReg from, VMRegPair* regs_to,
+ int& to_index, RegState reg_state[], int ret_off, int extra_stack_offset) {
+ Register fromReg = from->is_reg() ? from->as_Register() : noreg;
+ assert(sig->at(sig_index)._bt == T_VOID, "should be at end delimiter");
+
+
+ int vt = 1;
+ bool done = true;
+ bool mark_done = true;
+ do {
+ sig_index--;
+ BasicType bt = sig->at(sig_index)._bt;
+ if (bt == T_VALUETYPE) {
+ vt--;
+ } else if (bt == T_VOID &&
+ sig->at(sig_index-1)._bt != T_LONG &&
+ sig->at(sig_index-1)._bt != T_DOUBLE) {
+ vt++;
+ } else if (SigEntry::is_reserved_entry(sig, sig_index)) {
+ to_index--; // Ignore this
+ } else {
+ assert(to_index >= 0, "invalid to_index");
+ VMRegPair pair_to = regs_to[to_index--];
+ VMReg to = pair_to.first();
+
+ if (bt == T_VOID) continue;
+
+ int idx = (int) to->value();
+ if (reg_state[idx] == reg_readonly) {
+ if (idx != from->value()) {
+ mark_done = false;
+ }
+ done = false;
+ continue;
+ } else if (reg_state[idx] == reg_written) {
+ continue;
+ } else {
+ assert(reg_state[idx] == reg_writable, "must be writable");
+ reg_state[idx] = reg_written;
+ }
+
+ if (fromReg == noreg) {
+ int st_off = from->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset;
+ ldr(rscratch2, Address(sp, st_off));
+ fromReg = rscratch2;
+ }
+
+ int off = sig->at(sig_index)._offset;
+ assert(off > 0, "offset in object should be positive");
+ bool is_oop = (bt == T_OBJECT || bt == T_ARRAY);
+
+ Address fromAddr = Address(fromReg, off);
+ bool is_signed = (bt != T_CHAR) && (bt != T_BOOLEAN);
+
+ if (!to->is_FloatRegister()) {
+
+ Register dst = to->is_stack() ? rscratch1 : to->as_Register();
+
+ if (is_oop) {
+ load_heap_oop(dst, fromAddr);
+ } else {
+ load_sized_value(dst, fromAddr, type2aelembytes(bt), is_signed);
+ }
+ if (to->is_stack()) {
+ int st_off = to->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset;
+ str(dst, Address(sp, st_off));
+ }
+ } else {
+ if (bt == T_DOUBLE) {
+ ldrd(to->as_FloatRegister(), fromAddr);
+ } else {
+ assert(bt == T_FLOAT, "must be float");
+ ldrs(to->as_FloatRegister(), fromAddr);
+ }
+ }
+
+ }
+
+ } while (vt != 0);
+
+ if (mark_done && reg_state[from->value()] != reg_written) {
+ // This is okay because no one else will write to that slot
+ reg_state[from->value()] = reg_writable;
+ }
+ return done;
+ }
+
+ // Pack fields back into a value type oop
+ bool MacroAssembler::pack_value_helper(const GrowableArray<SigEntry>* sig, int& sig_index, int vtarg_index,
+ VMReg to, VMRegPair* regs_from, int regs_from_count, int& from_index, RegState reg_state[],
+ int ret_off, int extra_stack_offset) {
+ assert(sig->at(sig_index)._bt == T_VALUETYPE, "should be at end delimiter");
+ assert(to->is_valid(), "must be");
+
+ if (reg_state[to->value()] == reg_written) {
+ skip_unpacked_fields(sig, sig_index, regs_from, regs_from_count, from_index);
+ return true; // Already written
+ }
+
+ Register val_array = r0;
+ Register val_obj_tmp = r11;
+ Register from_reg_tmp = r10;
+ Register tmp1 = r14;
+ Register tmp2 = r13;
+ Register tmp3 = r1;
+ Register val_obj = to->is_stack() ? val_obj_tmp : to->as_Register();
+
+ if (reg_state[to->value()] == reg_readonly) {
+ if (!is_reg_in_unpacked_fields(sig, sig_index, to, regs_from, regs_from_count, from_index)) {
+ skip_unpacked_fields(sig, sig_index, regs_from, regs_from_count, from_index);
+ return false; // Not yet writable
+ }
+ val_obj = val_obj_tmp;
+ }
+
+ int index = arrayOopDesc::base_offset_in_bytes(T_OBJECT) + vtarg_index * type2aelembytes(T_VALUETYPE);
+ load_heap_oop(val_obj, Address(val_array, index));
+
+ ScalarizedValueArgsStream stream(sig, sig_index, regs_from, regs_from_count, from_index);
+ VMRegPair from_pair;
+ BasicType bt;
+
+ while (stream.next(from_pair, bt)) {
+ int off = sig->at(stream.sig_cc_index())._offset;
+ assert(off > 0, "offset in object should be positive");
+ bool is_oop = (bt == T_OBJECT || bt == T_ARRAY);
+ size_t size_in_bytes = is_java_primitive(bt) ? type2aelembytes(bt) : wordSize;
+
+ VMReg from_r1 = from_pair.first();
+ VMReg from_r2 = from_pair.second();
+
+ // Pack the scalarized field into the value object.
+ Address dst(val_obj, off);
+
+ if (!from_r1->is_FloatRegister()) {
+ Register from_reg;
+ if (from_r1->is_stack()) {
+ from_reg = from_reg_tmp;
+ int ld_off = from_r1->reg2stack() * VMRegImpl::stack_slot_size + extra_stack_offset;
+ load_sized_value(from_reg, Address(sp, ld_off), size_in_bytes, /* is_signed */ false);
+ } else {
+ from_reg = from_r1->as_Register();
+ }
+
+ if (is_oop) {
+ DecoratorSet decorators = IN_HEAP | ACCESS_WRITE;
+ store_heap_oop(dst, from_reg, tmp1, tmp2, tmp3, decorators);
+ } else {
+ store_sized_value(dst, from_reg, size_in_bytes);
+ }
+ } else {
+ if (from_r2->is_valid()) {
+ strd(from_r1->as_FloatRegister(), dst);
+ } else {
+ strs(from_r1->as_FloatRegister(), dst);
+ }
+ }
+
+ reg_state[from_r1->value()] = reg_writable;
+ }
+ sig_index = stream.sig_cc_index();
+ from_index = stream.regs_cc_index();
+
+ assert(reg_state[to->value()] == reg_writable, "must have already been read");
+ bool success = move_helper(val_obj->as_VMReg(), to, T_OBJECT, reg_state, ret_off, extra_stack_offset);
+ assert(success, "to register must be writeable");
+
+ return true;
+ }
+
+ // Unpack all value type arguments passed as oops
void MacroAssembler::unpack_value_args(Compile* C, bool receiver_only) {
! int sp_inc = unpack_value_args_common(C, receiver_only);
! // Emit code for verified entry and save increment for stack repair on return
! verified_entry(C, sp_inc);
! }
!
! int MacroAssembler::shuffle_value_args(bool is_packing, bool receiver_only, int extra_stack_offset,
! BasicType* sig_bt, const GrowableArray<SigEntry>* sig_cc,
! int args_passed, int args_on_stack, VMRegPair* regs, // from
! int args_passed_to, int args_on_stack_to, VMRegPair* regs_to) { // to
! // Check if we need to extend the stack for packing/unpacking
! int sp_inc = (args_on_stack_to - args_on_stack) * VMRegImpl::stack_slot_size;
! if (sp_inc > 0) {
! sp_inc = align_up(sp_inc, StackAlignmentInBytes);
! if (!is_packing) {
! // Save the return address, adjust the stack (make sure it is properly
! // 16-byte aligned) and copy the return address to the new top of the stack.
! // (Note: C1 does this in C1_MacroAssembler::scalarized_entry).
! // FIXME: We need not to preserve return address on aarch64
! pop(rscratch1);
! sub(sp, sp, sp_inc);
! push(rscratch1);
! }
! } else {
! // The scalarized calling convention needs less stack space than the unscalarized one.
! // No need to extend the stack, the caller will take care of these adjustments.
! sp_inc = 0;
! }
!
! int ret_off; // make sure we don't overwrite the return address
! if (is_packing) {
! // For C1 code, the VVEP doesn't have reserved slots, so we store the returned address at
! // rsp[0] during shuffling.
! ret_off = 0;
! } else {
! // C2 code ensures that sp_inc is a reserved slot.
! ret_off = sp_inc;
! }
!
! return shuffle_value_args_common(is_packing, receiver_only, extra_stack_offset,
! sig_bt, sig_cc,
! args_passed, args_on_stack, regs,
! args_passed_to, args_on_stack_to, regs_to,
! sp_inc, ret_off);
}
! VMReg MacroAssembler::spill_reg_for(VMReg reg) {
! return (reg->is_FloatRegister()) ? v0->as_VMReg() : r14->as_VMReg();
}
< prev index next >