/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2020, Red Hat Inc. All rights reserved.
* Copyright (c) 2020, 2022, Huawei Technologies Co., Ltd. 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 "precompiled.hpp"
#include "asm/assembler.hpp"
#include "asm/assembler.inline.hpp"
#include "compiler/disassembler.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "gc/shared/cardTable.hpp"
#include "gc/shared/cardTableBarrierSet.hpp"
#include "gc/shared/collectedHeap.hpp"
#include "interpreter/bytecodeHistogram.hpp"
#include "interpreter/interpreter.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
#include "nativeInst_riscv.hpp"
#include "oops/accessDecorators.hpp"
#include "oops/compressedOops.inline.hpp"
#include "oops/klass.inline.hpp"
#include "oops/oop.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "utilities/powerOfTwo.hpp"
#ifdef COMPILER2
#include "opto/compile.hpp"
#include "opto/node.hpp"
#include "opto/output.hpp"
#endif
#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
#else
#define BLOCK_COMMENT(str) block_comment(str)
#endif
#define BIND(label) bind(label); __ BLOCK_COMMENT(#label ":" )
static void pass_arg0(MacroAssembler* masm, Register arg) {
if (c_rarg0 != arg) {
masm->mv(c_rarg0, arg);
}
}
static void pass_arg1(MacroAssembler* masm, Register arg) {
if (c_rarg1 != arg) {
masm->mv(c_rarg1, arg);
}
}
static void pass_arg2(MacroAssembler* masm, Register arg) {
if (c_rarg2 != arg) {
masm->mv(c_rarg2, arg);
}
}
static void pass_arg3(MacroAssembler* masm, Register arg) {
if (c_rarg3 != arg) {
masm->mv(c_rarg3, arg);
}
}
void MacroAssembler::push_cont_fastpath(Register java_thread) {
if (!Continuations::enabled()) return ;
Label done;
ld(t0, Address(java_thread, JavaThread::cont_fastpath_offset()));
bleu(sp, t0, done);
sd(sp, Address(java_thread, JavaThread::cont_fastpath_offset()));
bind(done);
}
void MacroAssembler::pop_cont_fastpath(Register java_thread) {
if (!Continuations::enabled()) return ;
Label done;
ld(t0, Address(java_thread, JavaThread::cont_fastpath_offset()));
bltu(sp, t0, done);
sd(zr, Address(java_thread, JavaThread::cont_fastpath_offset()));
bind(done);
}
int MacroAssembler::align(int modulus, int extra_offset) {
CompressibleRegion cr(this );
intptr_t before = offset();
while ((offset() + extra_offset) % modulus != 0) { nop(); }
return (int )(offset() - before);
}
void MacroAssembler::call_VM_helper(Register oop_result, address entry_point, int number_of_arguments, bool check_exceptions) {
call_VM_base(oop_result, noreg, noreg, entry_point, number_of_arguments, check_exceptions);
}
// Implementation of call_VM versions
void MacroAssembler::call_VM(Register oop_result,
address entry_point,
bool check_exceptions) {
call_VM_helper(oop_result, entry_point, 0, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
address entry_point,
Register arg_1,
bool check_exceptions) {
pass_arg1(this , arg_1);
call_VM_helper(oop_result, entry_point, 1, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
address entry_point,
Register arg_1,
Register arg_2,
bool check_exceptions) {
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
pass_arg1(this , arg_1);
call_VM_helper(oop_result, entry_point, 2, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
address entry_point,
Register arg_1,
Register arg_2,
Register arg_3,
bool check_exceptions) {
assert(arg_1 != c_rarg3, "smashed arg" );
assert(arg_2 != c_rarg3, "smashed arg" );
pass_arg3(this , arg_3);
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
pass_arg1(this , arg_1);
call_VM_helper(oop_result, entry_point, 3, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
Register last_java_sp,
address entry_point,
int number_of_arguments,
bool check_exceptions) {
call_VM_base(oop_result, xthread, last_java_sp, entry_point, number_of_arguments, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
Register last_java_sp,
address entry_point,
Register arg_1,
bool check_exceptions) {
pass_arg1(this , arg_1);
call_VM(oop_result, last_java_sp, entry_point, 1, check_exceptions);
}
void MacroAssembler::call_VM(Register oop_result,
Register last_java_sp,
address entry_point,
Register arg_1,
Register arg_2,
bool check_exceptions) {
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
pass_arg1(this , arg_1);
call_VM(oop_result, last_java_sp, entry_point, 2, check_exceptions);
}
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) {
assert(arg_1 != c_rarg3, "smashed arg" );
assert(arg_2 != c_rarg3, "smashed arg" );
pass_arg3(this , arg_3);
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
pass_arg1(this , arg_1);
call_VM(oop_result, last_java_sp, entry_point, 3, check_exceptions);
}
void MacroAssembler::post_call_nop() {
if (!Continuations::enabled()) {
return ;
}
relocate(post_call_nop_Relocation::spec(), [&] {
nop();
li32(zr, 0);
});
}
// these are no-ops overridden by InterpreterMacroAssembler
void MacroAssembler::check_and_handle_earlyret(Register java_thread) {}
void MacroAssembler::check_and_handle_popframe(Register java_thread) {}
// Calls to C land
//
// When entering C land, the fp, & esp of the last Java frame have to be recorded
// in the (thread-local) JavaThread object. When leaving C land, the last Java fp
// has to be reset to 0. This is required to allow proper stack traversal.
void MacroAssembler::set_last_Java_frame(Register last_java_sp,
Register last_java_fp,
Register last_java_pc,
Register tmp) {
if (last_java_pc->is_valid()) {
sd(last_java_pc, Address(xthread,
JavaThread::frame_anchor_offset() +
JavaFrameAnchor::last_Java_pc_offset()));
}
// determine last_java_sp register
if (last_java_sp == sp) {
mv(tmp, sp);
last_java_sp = tmp;
} else if (!last_java_sp->is_valid()) {
last_java_sp = esp;
}
sd(last_java_sp, Address(xthread, JavaThread::last_Java_sp_offset()));
// last_java_fp is optional
if (last_java_fp->is_valid()) {
sd(last_java_fp, Address(xthread, JavaThread::last_Java_fp_offset()));
}
}
void MacroAssembler::set_last_Java_frame(Register last_java_sp,
Register last_java_fp,
address last_java_pc,
Register tmp) {
assert(last_java_pc != NULL, "must provide a valid PC" );
la(tmp, last_java_pc);
sd(tmp, Address(xthread, JavaThread::frame_anchor_offset() + JavaFrameAnchor::last_Java_pc_offset()));
set_last_Java_frame(last_java_sp, last_java_fp, noreg, tmp);
}
void MacroAssembler::set_last_Java_frame(Register last_java_sp,
Register last_java_fp,
Label &L,
Register tmp) {
if (L.is_bound()) {
set_last_Java_frame(last_java_sp, last_java_fp, target(L), tmp);
} else {
L.add_patch_at(code(), locator());
IncompressibleRegion ir(this ); // the label address will be patched back.
set_last_Java_frame(last_java_sp, last_java_fp, pc() /* Patched later */, tmp);
}
}
void MacroAssembler::reset_last_Java_frame(bool clear_fp) {
// we must set sp to zero to clear frame
sd(zr, Address(xthread, JavaThread::last_Java_sp_offset()));
// must clear fp, so that compiled frames are not confused; it is
// possible that we need it only for debugging
if (clear_fp) {
sd(zr, Address(xthread, JavaThread::last_Java_fp_offset()));
}
// Always clear the pc because it could have been set by make_walkable()
sd(zr, Address(xthread, JavaThread::last_Java_pc_offset()));
}
void MacroAssembler::call_VM_base(Register oop_result,
Register java_thread,
Register last_java_sp,
address entry_point,
int number_of_arguments,
bool check_exceptions) {
// determine java_thread register
if (!java_thread->is_valid()) {
java_thread = xthread;
}
// determine last_java_sp register
if (!last_java_sp->is_valid()) {
last_java_sp = esp;
}
// debugging support
assert(number_of_arguments >= 0 , "cannot have negative number of arguments" );
assert(java_thread == xthread, "unexpected register" );
assert(java_thread != oop_result , "cannot use the same register for java_thread & oop_result"an>);
assert(java_thread != last_java_sp, "cannot use the same register for java_thread & last_java_sp"span>);
// push java thread (becomes first argument of C function)
mv(c_rarg0, java_thread);
// set last Java frame before call
assert(last_java_sp != fp, "can't use fp" );
Label l;
set_last_Java_frame(last_java_sp, fp, l, t0);
// do the call, remove parameters
MacroAssembler::call_VM_leaf_base(entry_point, number_of_arguments, &l);
// reset last Java frame
// Only interpreter should have to clear fp
reset_last_Java_frame(true );
// C++ interp handles this in the interpreter
check_and_handle_popframe(java_thread);
check_and_handle_earlyret(java_thread);
if (check_exceptions) {
// check for pending exceptions (java_thread is set upon return)
ld(t0, Address(java_thread, in_bytes(Thread::pending_exception_offset())));
Label ok;
beqz(t0, ok);
RuntimeAddress target(StubRoutines::forward_exception_entry());
relocate(target.rspec(), [&] {
int32_t offset;
la_patchable(t0, target, offset);
jalr(x0, t0, offset);
});
bind(ok);
}
// get oop result if there is one and reset the value in the thread
if (oop_result->is_valid()) {
get_vm_result(oop_result, java_thread);
}
}
void MacroAssembler::get_vm_result(Register oop_result, Register java_thread) {
ld(oop_result, Address(java_thread, JavaThread::vm_result_offset()));
sd(zr, Address(java_thread, JavaThread::vm_result_offset()));
verify_oop_msg(oop_result, "broken oop in call_VM_base" );
}
void MacroAssembler::get_vm_result_2(Register metadata_result, Register java_thread) {
ld(metadata_result, Address(java_thread, JavaThread::vm_result_2_offset()));
sd(zr, Address(java_thread, JavaThread::vm_result_2_offset()));
}
void MacroAssembler::clinit_barrier(Register klass, Register tmp, Label* L_fast_path, Label* L_slow_path) {
assert(L_fast_path != NULL || L_slow_path != NULL, "at least one is required" );
assert_different_registers(klass, xthread, tmp);
Label L_fallthrough, L_tmp;
if (L_fast_path == NULL) {
L_fast_path = &L_fallthrough;
} else if (L_slow_path == NULL) {
L_slow_path = &L_fallthrough;
}
// Fast path check: class is fully initialized
lbu(tmp, Address(klass, InstanceKlass::init_state_offset()));
sub(tmp, tmp, InstanceKlass::fully_initialized);
beqz(tmp, *L_fast_path);
// Fast path check: current thread is initializer thread
ld(tmp, Address(klass, InstanceKlass::init_thread_offset()));
if (L_slow_path == &L_fallthrough) {
beq(xthread, tmp, *L_fast_path);
bind(*L_slow_path);
} else if (L_fast_path == &L_fallthrough) {
bne(xthread, tmp, *L_slow_path);
bind(*L_fast_path);
} else {
Unimplemented();
}
}
void MacroAssembler::_verify_oop(Register reg, const char * s, const char * file, int line) {
if (!VerifyOops) { return ; }
// Pass register number to verify_oop_subroutine
const char * b = NULL;
{
ResourceMark rm;
stringStream ss;
ss.print("verify_oop: %s: %s (%s:%d)" , reg->name(), s, file, line);
b = code_string(ss.as_string());
}
BLOCK_COMMENT("verify_oop {" );
push_reg(RegSet::of(ra, t0, t1, c_rarg0), sp);
mv(c_rarg0, reg); // c_rarg0 : x10
// The length of the instruction sequence emitted should be independent
// of the value of the local char buffer address so that the size of mach
// nodes for scratch emit and normal emit matches.
movptr(t0, (address)b);
// call indirectly to solve generation ordering problem
ExternalAddress target(StubRoutines::verify_oop_subroutine_entry_address());
relocate(target.rspec(), [&] {
int32_t offset;
la_patchable(t1, target, offset);
ld(t1, Address(t1, offset));
});
jalr(t1);
pop_reg(RegSet::of(ra, t0, t1, c_rarg0), sp);
BLOCK_COMMENT("} verify_oop" );
}
void MacroAssembler::_verify_oop_addr(Address addr, const char * s, const char * file, int line) {
if (!VerifyOops) {
return ;
}
const char * b = NULL;
{
ResourceMark rm;
stringStream ss;
ss.print("verify_oop_addr: %s (%s:%d)" , s, file, line);
b = code_string(ss.as_string());
}
BLOCK_COMMENT("verify_oop_addr {" );
push_reg(RegSet::of(ra, t0, t1, c_rarg0), sp);
if (addr.uses(sp)) {
la(x10, addr);
ld(x10, Address(x10, 4 * wordSize));
} else {
ld(x10, addr);
}
// The length of the instruction sequence emitted should be independent
// of the value of the local char buffer address so that the size of mach
// nodes for scratch emit and normal emit matches.
movptr(t0, (address)b);
// call indirectly to solve generation ordering problem
ExternalAddress target(StubRoutines::verify_oop_subroutine_entry_address());
relocate(target.rspec(), [&] {
int32_t offset;
la_patchable(t1, target, offset);
ld(t1, Address(t1, offset));
});
jalr(t1);
pop_reg(RegSet::of(ra, t0, t1, c_rarg0), sp);
BLOCK_COMMENT("} verify_oop_addr" );
}
Address MacroAssembler::argument_address(RegisterOrConstant arg_slot,
int extra_slot_offset) {
// cf. TemplateTable::prepare_invoke(), if (load_receiver).
int stackElementSize = Interpreter::stackElementSize;
int offset = Interpreter::expr_offset_in_bytes(extra_slot_offset+0);
#ifdef ASSERT
int offset1 = Interpreter::expr_offset_in_bytes(extra_slot_offset+1);
assert(offset1 - offset == stackElementSize, "correct arithmetic" );
#endif
if (arg_slot.is_constant()) {
return Address(esp, arg_slot.as_constant() * stackElementSize + offset);
} else {
assert_different_registers(t0, arg_slot.as_register());
shadd(t0, arg_slot.as_register(), esp, t0, exact_log2(stackElementSize));
return Address(t0, offset);
}
}
#ifndef PRODUCT
extern "C" void findpc(intptr_t x);
#endif
void MacroAssembler::debug64(char * msg, int64_t pc, int64_t regs[])
{
// In order to get locks to work, we need to fake a in_VM state
if (ShowMessageBoxOnError) {
JavaThread* thread = JavaThread::current();
JavaThreadState saved_state = thread->thread_state();
thread->set_thread_state(_thread_in_vm);
#ifndef PRODUCT
if (CountBytecodes || TraceBytecodes || StopInterpreterAt) {
ttyLocker ttyl;
BytecodeCounter::print();
}
#endif
if (os::message_box(msg, "Execution stopped, print registers?" )) {
ttyLocker ttyl;
tty->print_cr(" pc = 0x%016lx" , pc);
#ifndef PRODUCT
tty->cr();
findpc(pc);
tty->cr();
#endif
tty->print_cr(" x0 = 0x%016lx" , regs[0]);
tty->print_cr(" x1 = 0x%016lx" , regs[1]);
tty->print_cr(" x2 = 0x%016lx" , regs[2]);
tty->print_cr(" x3 = 0x%016lx" , regs[3]);
tty->print_cr(" x4 = 0x%016lx" , regs[4]);
tty->print_cr(" x5 = 0x%016lx" , regs[5]);
tty->print_cr(" x6 = 0x%016lx" , regs[6]);
tty->print_cr(" x7 = 0x%016lx" , regs[7]);
tty->print_cr(" x8 = 0x%016lx" , regs[8]);
tty->print_cr(" x9 = 0x%016lx" , regs[9]);
tty->print_cr("x10 = 0x%016lx" , regs[10]);
tty->print_cr("x11 = 0x%016lx" , regs[11]);
tty->print_cr("x12 = 0x%016lx" , regs[12]);
tty->print_cr("x13 = 0x%016lx" , regs[13]);
tty->print_cr("x14 = 0x%016lx" , regs[14]);
tty->print_cr("x15 = 0x%016lx" , regs[15]);
tty->print_cr("x16 = 0x%016lx" , regs[16]);
tty->print_cr("x17 = 0x%016lx" , regs[17]);
tty->print_cr("x18 = 0x%016lx" , regs[18]);
tty->print_cr("x19 = 0x%016lx" , regs[19]);
tty->print_cr("x20 = 0x%016lx" , regs[20]);
tty->print_cr("x21 = 0x%016lx" , regs[21]);
tty->print_cr("x22 = 0x%016lx" , regs[22]);
tty->print_cr("x23 = 0x%016lx" , regs[23]);
tty->print_cr("x24 = 0x%016lx" , regs[24]);
tty->print_cr("x25 = 0x%016lx" , regs[25]);
tty->print_cr("x26 = 0x%016lx" , regs[26]);
tty->print_cr("x27 = 0x%016lx" , regs[27]);
tty->print_cr("x28 = 0x%016lx" , regs[28]);
tty->print_cr("x30 = 0x%016lx" , regs[30]);
tty->print_cr("x31 = 0x%016lx" , regs[31]);
BREAKPOINT;
}
}
fatal("DEBUG MESSAGE: %s" , msg);
}
void MacroAssembler::resolve_jobject(Register value, Register tmp1, Register tmp2) {
Label done, not_weak;
beqz(value, done); // Use NULL as-is.
// Test for jweak tag.
andi(t0, value, JNIHandles::weak_tag_mask);
beqz(t0, not_weak);
// Resolve jweak.
access_load_at(T_OBJECT, IN_NATIVE | ON_PHANTOM_OOP_REF, value,
Address(value, -JNIHandles::weak_tag_value), tmp1, tmp2);
verify_oop(value);
j(done);
bind(not_weak);
// Resolve (untagged) jobject.
access_load_at(T_OBJECT, IN_NATIVE, value, Address(value, 0), tmp1, tmp2);
verify_oop(value);
bind(done);
}
void MacroAssembler::stop(const char * msg) {
BLOCK_COMMENT(msg);
illegal_instruction(Assembler::csr::time);
emit_int64((uintptr_t)msg);
}
void MacroAssembler::unimplemented(const char * what) {
const char * buf = NULL;
{
ResourceMark rm;
stringStream ss;
ss.print("unimplemented: %s" , what);
buf = code_string(ss.as_string());
}
stop(buf);
}
void MacroAssembler::emit_static_call_stub() {
IncompressibleRegion ir(this ); // Fixed length: see CompiledStaticCall::to_interp_stub_size().
// CompiledDirectStaticCall::set_to_interpreted knows the
// exact layout of this stub.
mov_metadata(xmethod, (Metadata*)NULL);
// Jump to the entry point of the c2i stub.
int32_t offset = 0;
movptr(t0, 0, offset);
jalr(x0, t0, offset);
}
void MacroAssembler::call_VM_leaf_base(address entry_point,
int number_of_arguments,
Label *retaddr) {
push_reg(RegSet::of(t0, xmethod), sp); // push << t0 & xmethod >> to sp
call(entry_point);
if (retaddr != NULL) {
bind(*retaddr);
}
pop_reg(RegSet::of(t0, xmethod), sp); // pop << t0 & xmethod >> from sp
}
void MacroAssembler::call_VM_leaf(address entry_point, int number_of_arguments) {
call_VM_leaf_base(entry_point, number_of_arguments);
}
void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0) {
pass_arg0(this , arg_0);
call_VM_leaf_base(entry_point, 1);
}
void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0, Register arg_1) {
pass_arg0(this , arg_0);
pass_arg1(this , arg_1);
call_VM_leaf_base(entry_point, 2);
}
void MacroAssembler::call_VM_leaf(address entry_point, Register arg_0,
Register arg_1, Register arg_2) {
pass_arg0(this , arg_0);
pass_arg1(this , arg_1);
pass_arg2(this , arg_2);
call_VM_leaf_base(entry_point, 3);
}
void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0) {
pass_arg0(this , arg_0);
MacroAssembler::call_VM_leaf_base(entry_point, 1);
}
void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0, Register arg_1) {
assert(arg_0 != c_rarg1, "smashed arg" );
pass_arg1(this , arg_1);
pass_arg0(this , arg_0);
MacroAssembler::call_VM_leaf_base(entry_point, 2);
}
void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0, Register arg_1, Register arg_2) {
assert(arg_0 != c_rarg2, "smashed arg" );
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
assert(arg_0 != c_rarg1, "smashed arg" );
pass_arg1(this , arg_1);
pass_arg0(this , arg_0);
MacroAssembler::call_VM_leaf_base(entry_point, 3);
}
void MacroAssembler::super_call_VM_leaf(address entry_point, Register arg_0, Register arg_1, Register arg_2, Register arg_3) {
assert(arg_0 != c_rarg3, "smashed arg" );
assert(arg_1 != c_rarg3, "smashed arg" );
assert(arg_2 != c_rarg3, "smashed arg" );
pass_arg3(this , arg_3);
assert(arg_0 != c_rarg2, "smashed arg" );
assert(arg_1 != c_rarg2, "smashed arg" );
pass_arg2(this , arg_2);
assert(arg_0 != c_rarg1, "smashed arg" );
pass_arg1(this , arg_1);
pass_arg0(this , arg_0);
MacroAssembler::call_VM_leaf_base(entry_point, 4);
}
void MacroAssembler::baseOffset32(Register Rd, const Address &adr, int32_t &offset) {
assert(Rd != noreg, "Rd must not be empty register!" );
guarantee(Rd != adr.base(), "should use different registers!" );
if (is_offset_in_range(adr.offset(), 32)) {
int32_t imm = adr.offset();
int32_t upper = imm, lower = imm;
lower = (imm << 20) >> 20;
upper -= lower;
lui(Rd, upper);
offset = lower;
} else {
offset = ((int32_t)adr.offset() << 20) >> 20;
li(Rd, adr.offset() - offset);
}
add(Rd, Rd, adr.base());
}
void MacroAssembler::baseOffset(Register Rd, const Address &adr, int32_t &offset) {
if (is_offset_in_range(adr.offset(), 12)) {
assert(Rd != noreg, "Rd must not be empty register!" );
addi(Rd, adr.base(), adr.offset());
offset = 0;
} else {
baseOffset32(Rd, adr, offset);
}
}
void MacroAssembler::la(Register Rd, const address dest) {
int64_t offset = dest - pc();
if (is_offset_in_range(offset, 32)) {
auipc(Rd, (int32_t)offset + 0x800); //0x800, Note:the 11th sign bit
addi(Rd, Rd, ((int64_t)offset << 52) >> 52);
} else {
movptr(Rd, dest);
}
}
void MacroAssembler::la(Register Rd, const Address &adr) {
switch (adr.getMode()) {
case Address::literal: {
relocInfo::relocType rtype = adr.rspec().reloc()->type();
if (rtype == relocInfo::none) {
mv(Rd, (intptr_t)(adr.target()));
} else {
relocate(adr.rspec(), [&] {
movptr(Rd, adr.target());
});
}
break ;
}
case Address::base_plus_offset: {
int32_t offset = 0;
baseOffset(Rd, adr, offset);
addi(Rd, Rd, offset);
break ;
}
default :
ShouldNotReachHere();
}
}
void MacroAssembler::la(Register Rd, Label &label) {
IncompressibleRegion ir(this ); // the label address may be patched back.
wrap_label(Rd, label, &MacroAssembler::la);
}
void MacroAssembler::li32(Register Rd, int32_t imm) {
// int32_t is in range 0x8000 0000 ~ 0x7fff ffff, and imm[31] is the sign bit
int64_t upper = imm, lower = imm;
lower = (imm << 20) >> 20;
upper -= lower;
upper = (int32_t)upper;
// lui Rd, imm[31:12] + imm[11]
lui(Rd, upper);
// use addiw to distinguish li32 to li64
addiw(Rd, Rd, lower);
}
void MacroAssembler::li64(Register Rd, int64_t imm) {
// Load upper 32 bits. upper = imm[63:32], but if imm[31] == 1 or
// (imm[31:20] == 0x7ff && imm[19] == 1), upper = imm[63:32] + 1.
int64_t lower = imm & 0xffffffff;
lower -= ((lower << 44) >> 44);
int64_t tmp_imm = ((uint64_t)(imm & 0xffffffff00000000)) + (uint64_t)lower;
int32_t upper = (tmp_imm - (int32_t)lower) >> 32;
// Load upper 32 bits
int64_t up = upper, lo = upper;
lo = (lo << 52) >> 52;
up -= lo;
up = (int32_t)up;
lui(Rd, up);
addi(Rd, Rd, lo);
// Load the rest 32 bits.
slli(Rd, Rd, 12);
addi(Rd, Rd, (int32_t)lower >> 20);
slli(Rd, Rd, 12);
lower = ((int32_t)imm << 12) >> 20;
addi(Rd, Rd, lower);
slli(Rd, Rd, 8);
lower = imm & 0xff;
addi(Rd, Rd, lower);
}
void MacroAssembler::li(Register Rd, int64_t imm) {
// int64_t is in range 0x8000 0000 0000 0000 ~ 0x7fff ffff ffff ffff
// li -> c.li
if (do_compress() && (is_imm_in_range(imm, 6, 0) && Rd != x0)) {
c_li(Rd, imm);
return ;
}
int shift = 12;
int64_t upper = imm, lower = imm;
// Split imm to a lower 12-bit sign-extended part and the remainder,
// because addi will sign-extend the lower imm.
lower = ((int32_t)imm << 20) >> 20;
upper -= lower;
// Test whether imm is a 32-bit integer.
if (!(((imm) & ~(int64_t)0x7fffffff) == 0 ||
(((imm) & ~(int64_t)0x7fffffff) == ~(int64_t)0x7fffffff))) {
while (((upper >> shift) & 1) == 0) { shift++; }
upper >>= shift;
li(Rd, upper);
slli(Rd, Rd, shift);
if (lower != 0) {
addi(Rd, Rd, lower);
}
} else {
// 32-bit integer
Register hi_Rd = zr;
if (upper != 0) {
lui(Rd, (int32_t)upper);
hi_Rd = Rd;
}
if (lower != 0 || hi_Rd == zr) {
addiw(Rd, hi_Rd, lower);
}
}
}
#define INSN(NAME, REGISTER ) \
void MacroAssembler::NAME(const address dest, Register temp) { \
assert_cond(dest != NULL); \
int64_t distance = dest - pc(); \
if (is_imm_in_range(distance, 20, 1)) { \
Assembler::jal(REGISTER , distance); \
} else { \
assert(temp != noreg, "temp must not be empty register!" ); \
int32_t offset = 0; \
movptr(temp, dest, offset); \
Assembler::jalr(REGISTER , temp, offset); \
} \
} \
INSN(j, x0);
INSN(jal, x1);
#undef INSN
#define INSN(NAME, REGISTER ) \
void MacroAssembler::NAME(const Address &adr, Register temp) { \
switch (adr.getMode()) { \
case Address::literal: { \
relocate(adr.rspec(), [&] { \
NAME(adr.target(), temp); \
}); \
break ; \
} \
case Address::base_plus_offset: { \
int32_t offset = 0; \
baseOffset(temp, adr, offset); \
Assembler::jalr(REGISTER , temp, offset); \
break ; \
} \
default : \
ShouldNotReachHere(); \
} \
}
INSN(j, x0);
INSN(jal, x1);
#undef INSN
#define INSN(NAME) \
void MacroAssembler::NAME(Register Rd, const address dest, Register temp) { \
assert_cond(dest != NULL); \
int64_t distance = dest - pc(); \
if (is_imm_in_range(distance, 20, 1)) { \
Assembler::NAME(Rd, distance); \
} else { \
assert_different_registers(Rd, temp); \
int32_t offset = 0; \
movptr(temp, dest, offset); \
jalr(Rd, temp, offset); \
} \
} \
void MacroAssembler::NAME(Register Rd, Label &L, Register temp) { \
assert_different_registers(Rd, temp); \
wrap_label(Rd, L, temp, &MacroAssembler::NAME); \
}
INSN(jal);
#undef INSN
#define INSN(NAME, REGISTER ) \
void MacroAssembler::NAME(Label &l, Register temp) { \
jal(REGISTER , l, temp); \
} \
INSN(j, x0);
INSN(jal, x1);
#undef INSN
void MacroAssembler::wrap_label(Register Rt, Label &L, Register tmp, load_insn_by_temp insn) {
if (L.is_bound()) {
(this->*insn)(Rt, target(L), tmp);
} else {
L.add_patch_at(code(), locator());
(this->*insn)(Rt, pc(), tmp);
}
}
void MacroAssembler::wrap_label(Register Rt, Label &L, jal_jalr_insn insn) {
if (L.is_bound()) {
(this->*insn)(Rt, target(L));
} else {
L.add_patch_at(code(), locator());
(this->*insn)(Rt, pc());
}
}
void MacroAssembler::wrap_label(Register r1, Register r2, Label &L,
compare_and_branch_insn insn,
compare_and_branch_label_insn neg_insn, bool is_far) {
if (is_far) {
Label done;
(this->*neg_insn)(r1, r2, done, /* is_far */ false);
j(L);
bind(done);
} else {
if (L.is_bound()) {
(this->*insn)(r1, r2, target(L));
} else {
L.add_patch_at(code(), locator());
(this->*insn)(r1, r2, pc());
}
}
}
#define INSN(NAME, NEG_INSN) \
void MacroAssembler::NAME(Register Rs1, Register Rs2, Label &L, bool is_far) { \
wrap_label(Rs1, Rs2, L, &MacroAssembler::NAME, &MacroAssembler::NEG_INSN, is_far); \
}
INSN(beq, bne);
INSN(bne, beq);
INSN(blt, bge);
INSN(bge, blt);
INSN(bltu, bgeu);
INSN(bgeu, bltu);
#undef INSN
#define INSN(NAME) \
void MacroAssembler::NAME## z(Register Rs, const address dest) { \
NAME(Rs, zr, dest); \
} \
void MacroAssembler::NAME## z(Register Rs, Label &l, bool is_far) { \
NAME(Rs, zr, l, is_far); \
} \
INSN(beq);
INSN(bne);
INSN(blt);
INSN(ble);
INSN(bge);
INSN(bgt);
#undef INSN
#define INSN(NAME, NEG_INSN) \
void MacroAssembler::NAME(Register Rs, Register Rt, const address dest) { \
NEG_INSN(Rt, Rs, dest); \
} \
void MacroAssembler::NAME(Register Rs, Register Rt, Label &l, bool is_far) { \
NEG_INSN(Rt, Rs, l, is_far); \
}
INSN(bgt, blt);
INSN(ble, bge);
INSN(bgtu, bltu);
INSN(bleu, bgeu);
#undef INSN
// Float compare branch instructions
#define INSN(NAME, FLOATCMP, BRANCH) \
void MacroAssembler::float_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far, bool is_unordered) { \
FLOATCMP## _s(t0, Rs1, Rs2); \
BRANCH(t0, l, is_far); \
} \
void MacroAssembler::double_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, bool is_far, bool is_unordered) { \
FLOATCMP## _d(t0, Rs1, Rs2); \
BRANCH(t0, l, is_far); \
}
INSN(beq, feq, bnez);
INSN(bne, feq, beqz);
#undef INSN
#define INSN(NAME, FLOATCMP1, FLOATCMP2) \
void MacroAssembler::float_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, \
bool is_far, bool is_unordered) { \
if (is_unordered) { \
/* jump if either source is NaN or condition is expected */ \
FLOATCMP2## _s(t0, Rs2, Rs1); \
beqz(t0, l, is_far); \
} else { \
/* jump if no NaN in source and condition is expected */ \
FLOATCMP1## _s(t0, Rs1, Rs2); \
bnez(t0, l, is_far); \
} \
} \
void MacroAssembler::double_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, \
bool is_far, bool is_unordered) { \
if (is_unordered) { \
/* jump if either source is NaN or condition is expected */ \
FLOATCMP2## _d(t0, Rs2, Rs1); \
beqz(t0, l, is_far); \
} else { \
/* jump if no NaN in source and condition is expected */ \
FLOATCMP1## _d(t0, Rs1, Rs2); \
bnez(t0, l, is_far); \
} \
}
INSN(ble, fle, flt);
INSN(blt, flt, fle);
#undef INSN
#define INSN(NAME, CMP) \
void MacroAssembler::float_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, \
bool is_far, bool is_unordered) { \
float_## CMP(Rs2, Rs1, l, is_far, is_unordered); \
} \
void MacroAssembler::double_## NAME(FloatRegister Rs1, FloatRegister Rs2, Label &l, \
bool is_far, bool is_unordered) { \
double_## CMP(Rs2, Rs1, l, is_far, is_unordered); \
}
INSN(bgt, blt);
INSN(bge, ble);
#undef INSN
#define INSN(NAME, CSR) \
void MacroAssembler::NAME(Register Rd) { \
csrr(Rd, CSR); \
}
INSN(rdinstret, CSR_INSTERT);
INSN(rdcycle, CSR_CYCLE);
INSN(rdtime, CSR_TIME);
INSN(frcsr, CSR_FCSR);
INSN(frrm, CSR_FRM);
INSN(frflags, CSR_FFLAGS);
#undef INSN
void MacroAssembler::csrr(Register Rd, unsigned csr) {
csrrs(Rd, csr, x0);
}
#define INSN(NAME, OPFUN) \
void MacroAssembler::NAME(unsigned csr, Register Rs) { \
OPFUN(x0, csr, Rs); \
}
INSN(csrw, csrrw);
INSN(csrs, csrrs);
INSN(csrc, csrrc);
#undef INSN
#define INSN(NAME, OPFUN) \
void MacroAssembler::NAME(unsigned csr, unsigned imm) { \
OPFUN(x0, csr, imm); \
}
INSN(csrwi, csrrwi);
INSN(csrsi, csrrsi);
INSN(csrci, csrrci);
#undef INSN
#define INSN(NAME, CSR) \
void MacroAssembler::NAME(Register Rd, Register Rs) { \
csrrw(Rd, CSR, Rs); \
}
INSN(fscsr, CSR_FCSR);
INSN(fsrm, CSR_FRM);
INSN(fsflags, CSR_FFLAGS);
#undef INSN
#define INSN(NAME) \
void MacroAssembler::NAME(Register Rs) { \
NAME(x0, Rs); \
}
INSN(fscsr);
INSN(fsrm);
INSN(fsflags);
#undef INSN
void MacroAssembler::fsrmi(Register Rd, unsigned imm) {
guarantee(imm < 5, "Rounding Mode is invalid in Rounding Mode register" );
csrrwi(Rd, CSR_FRM, imm);
}
void MacroAssembler::fsflagsi(Register Rd, unsigned imm) {
csrrwi(Rd, CSR_FFLAGS, imm);
}
#define INSN(NAME) \
void MacroAssembler::NAME(unsigned imm) { \
NAME(x0, imm); \
}
INSN(fsrmi);
INSN(fsflagsi);
#undef INSN
void MacroAssembler::push_reg(Register Rs)
{
addi(esp, esp, 0 - wordSize);
sd(Rs, Address(esp, 0));
}
void MacroAssembler::pop_reg(Register Rd)
{
ld(Rd, Address(esp, 0));
addi(esp, esp, wordSize);
}
int MacroAssembler::bitset_to_regs(unsigned int bitset, unsigned char * regs) {
int count = 0;
// Scan bitset to accumulate register pairs
for (int reg = 31; reg >= 0; reg--) {
if ((1U << 31) & bitset) {
regs[count++] = reg;
}
bitset <<= 1;
}
return count;
}
// Push integer registers in the bitset supplied. Don't push sp.
// Return the number of words pushed
int MacroAssembler::push_reg(unsigned int bitset, Register stack) {
DEBUG_ONLY(int words_pushed = 0;)
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
// reserve one slot to align for odd count
int offset = is_even(count) ? 0 : wordSize;
if (count) {
addi(stack, stack, -count * wordSize - offset);
}
for (int i = count - 1; i >= 0; i--) {
sd(as_Register(regs[i]), Address(stack, (count - 1 - i) * wordSize + offset));
DEBUG_ONLY(words_pushed++;)
}
assert(words_pushed == count, "oops, pushed != count" );
return count;
}
int MacroAssembler::pop_reg(unsigned int bitset, Register stack) {
DEBUG_ONLY(int words_popped = 0;)
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
// reserve one slot to align for odd count
int offset = is_even(count) ? 0 : wordSize;
for (int i = count - 1; i >= 0; i--) {
ld(as_Register(regs[i]), Address(stack, (count - 1 - i) * wordSize + offset));
DEBUG_ONLY(words_popped++;)
}
if (count) {
addi(stack, stack, count * wordSize + offset);
}
assert(words_popped == count, "oops, popped != count" );
return count;
}
// Push floating-point registers in the bitset supplied.
// Return the number of words pushed
int MacroAssembler::push_fp(unsigned int bitset, Register stack) {
DEBUG_ONLY(int words_pushed = 0;)
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
int push_slots = count + (count & 1);
if (count) {
addi(stack, stack, -push_slots * wordSize);
}
for (int i = count - 1; i >= 0; i--) {
fsd(as_FloatRegister(regs[i]), Address(stack, (push_slots - 1 - i) * wordSize));
DEBUG_ONLY(words_pushed++;)
}
assert(words_pushed == count, "oops, pushed(%d) != count(%d)" , words_pushed, count);
return count;
}
int MacroAssembler::pop_fp(unsigned int bitset, Register stack) {
DEBUG_ONLY(int words_popped = 0;)
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
int pop_slots = count + (count & 1);
for (int i = count - 1; i >= 0; i--) {
fld(as_FloatRegister(regs[i]), Address(stack, (pop_slots - 1 - i) * wordSize));
DEBUG_ONLY(words_popped++;)
}
if (count) {
addi(stack, stack, pop_slots * wordSize);
}
assert(words_popped == count, "oops, popped(%d) != count(%d)" , words_popped, count);
return count;
}
#ifdef COMPILER2
// Push vector registers in the bitset supplied.
// Return the number of words pushed
int MacroAssembler::push_v(unsigned int bitset, Register stack) {
int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
// Scan bitset to accumulate register pairs
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
for (int i = 0; i < count; i++) {
sub(stack, stack, vector_size_in_bytes);
vs1r_v(as_VectorRegister(regs[i]), stack);
}
return count * vector_size_in_bytes / wordSize;
}
int MacroAssembler::pop_v(unsigned int bitset, Register stack) {
int vector_size_in_bytes = Matcher::scalable_vector_reg_size(T_BYTE);
// Scan bitset to accumulate register pairs
unsigned char regs[32];
int count = bitset_to_regs(bitset, regs);
for (int i = count - 1; i >= 0; i--) {
vl1re8_v(as_VectorRegister(regs[i]), stack);
add(stack, stack, vector_size_in_bytes);
}
return count * vector_size_in_bytes / wordSize;
}
#endif // COMPILER2
void MacroAssembler::push_call_clobbered_registers_except(RegSet exclude) {
// Push integer registers x7, x10-x17, x28-x31.
push_reg(RegSet::of(x7) + RegSet::range(x10, x17) + RegSet::range(x28, x31) - exclude, sp);
// Push float registers f0-f7, f10-f17, f28-f31.
addi(sp, sp, - wordSize * 20);
int offset = 0;
for (int i = 0; i < 32; i++) {
if (i <= f7->encoding() || i >= f28->encoding() || (i >= f10->encoding() && i <= f17->encoding())) {
fsd(as_FloatRegister(i), Address(sp, wordSize * (offset++)));
}
}
}
void MacroAssembler::pop_call_clobbered_registers_except(RegSet exclude) {
int offset = 0;
for (int i = 0; i < 32; i++) {
if (i <= f7->encoding() || i >= f28->encoding() || (i >= f10->encoding() && i <= f17->encoding())) {
fld(as_FloatRegister(i), Address(sp, wordSize * (offset++)));
}
}
addi(sp, sp, wordSize * 20);
pop_reg(RegSet::of(x7) + RegSet::range(x10, x17) + RegSet::range(x28, x31) - exclude, sp);
}
void MacroAssembler::push_CPU_state(bool save_vectors, int vector_size_in_bytes) {
// integer registers, except zr(x0) & ra(x1) & sp(x2) & gp(x3) & tp(x4)
push_reg(RegSet::range(x5, x31), sp);
// float registers
addi(sp, sp, - 32 * wordSize);
for (int i = 0; i < 32; i++) {
fsd(as_FloatRegister(i), Address(sp, i * wordSize));
}
// vector registers
if (save_vectors) {
sub(sp, sp, vector_size_in_bytes * VectorRegister::number_of_registers);
vsetvli(t0, x0, Assembler::e64, Assembler::m8);
for (int i = 0; i < VectorRegister::number_of_registers; i += 8) {
add(t0, sp, vector_size_in_bytes * i);
vse64_v(as_VectorRegister(i), t0);
}
}
}
void MacroAssembler::pop_CPU_state(bool restore_vectors, int vector_size_in_bytes) {
// vector registers
if (restore_vectors) {
vsetvli(t0, x0, Assembler::e64, Assembler::m8);
for (int i = 0; i < VectorRegister::number_of_registers; i += 8) {
vle64_v(as_VectorRegister(i), sp);
add(sp, sp, vector_size_in_bytes * 8);
}
}
// float registers
for (int i = 0; i < 32; i++) {
fld(as_FloatRegister(i), Address(sp, i * wordSize));
}
addi(sp, sp, 32 * wordSize);
// integer registers, except zr(x0) & ra(x1) & sp(x2) & gp(x3) & tp(x4)
pop_reg(RegSet::range(x5, x31), sp);
}
static int patch_offset_in_jal(address branch, int64_t offset) {
assert(is_imm_in_range(offset, 20, 1), "offset is too large to be patched in one jal insrusction!\n" );
Assembler::patch(branch, 31, 31, (offset >> 20) & 0x1); // offset[20] ==> branch[31]
Assembler::patch(branch, 30, 21, (offset >> 1) & 0x3ff); // offset[10:1] ==> branch[30:21]
Assembler::patch(branch, 20, 20, (offset >> 11) & 0x1); // offset[11] ==> branch[20]
Assembler::patch(branch, 19, 12, (offset >> 12) & 0xff); // offset[19:12] ==> branch[19:12]
return NativeInstruction::instruction_size; // only one instruction
}
static int patch_offset_in_conditional_branch(address branch, int64_t offset) {
assert(is_imm_in_range(offset, 12, 1), "offset is too large to be patched in one beq/bge/bgeu/blt/bltu/bne insrusction!\n" );
Assembler::patch(branch, 31, 31, (offset >> 12) & 0x1); // offset[12] ==> branch[31]
Assembler::patch(branch, 30, 25, (offset >> 5) & 0x3f); // offset[10:5] ==> branch[30:25]
Assembler::patch(branch, 7, 7, (offset >> 11) & 0x1); // offset[11] ==> branch[7]
Assembler::patch(branch, 11, 8, (offset >> 1) & 0xf); // offset[4:1] ==> branch[11:8]
return NativeInstruction::instruction_size; // only one instruction
}
static int patch_offset_in_pc_relative(address branch, int64_t offset) {
const int PC_RELATIVE_INSTRUCTION_NUM = 2; // auipc, addi/jalr/load
Assembler::patch(branch, 31, 12, ((offset + 0x800) >> 12) & 0xfffff); // Auipc. offset[31:12] ==> branch[31:12]
Assembler::patch(branch + 4, 31, 20, offset & 0xfff); // Addi/Jalr/Load. offset[11:0] ==> branch[31:20]
return PC_RELATIVE_INSTRUCTION_NUM * NativeInstruction::instruction_size;
}
static int patch_addr_in_movptr(address branch, address target) {
const int MOVPTR_INSTRUCTIONS_NUM = 6; // lui + addi + slli + addi + slli + addi/jalr/load
int32_t lower = ((intptr_t)target << 35) >> 35;
int64_t upper = ((intptr_t)target - lower) >> 29;
Assembler::patch(branch + 0, 31, 12, upper & 0xfffff); // Lui. target[48:29] + target[28] ==> branch[31:12]
Assembler::patch(branch + 4, 31, 20, (lower >> 17) & 0xfff); // Addi. target[28:17] ==> branch[31:20]
Assembler::patch(branch + 12, 31, 20, (lower >> 6) & 0x7ff); // Addi. target[16: 6] ==> branch[31:20]
Assembler::patch(branch + 20, 31, 20, lower & 0x3f); // Addi/Jalr/Load. target[ 5: 0] ==> branch[31:20]
return MOVPTR_INSTRUCTIONS_NUM * NativeInstruction::instruction_size;
}
static int patch_imm_in_li64(address branch, address target) {
const int LI64_INSTRUCTIONS_NUM = 8; // lui + addi + slli + addi + slli + addi + slli + addi
int64_t lower = (intptr_t)target & 0xffffffff;
lower = lower - ((lower << 44) >> 44);
int64_t tmp_imm = ((uint64_t)((intptr_t)target & 0xffffffff00000000)) + (uint64_t)lower;
int32_t upper = (tmp_imm - (int32_t)lower) >> 32;
int64_t tmp_upper = upper, tmp_lower = upper;
tmp_lower = (tmp_lower << 52) >> 52;
tmp_upper -= tmp_lower;
tmp_upper >>= 12;
// Load upper 32 bits. Upper = target[63:32], but if target[31] = 1 or (target[31:20] == 0x7ff && target[19] == 1),
// upper = target[63:32] + 1.
Assembler::patch(branch + 0, 31, 12, tmp_upper & 0xfffff); // Lui.
Assembler::patch(branch + 4, 31, 20, tmp_lower & 0xfff); // Addi.
// Load the rest 32 bits.
Assembler::patch(branch + 12, 31, 20, ((int32_t)lower >> 20) & 0xfff); // Addi.
Assembler::patch(branch + 20, 31, 20, (((intptr_t)target << 44) >> 52) & 0xfff); // Addi.
Assembler::patch(branch + 28, 31, 20, (intptr_t)target & 0xff); // Addi.
return LI64_INSTRUCTIONS_NUM * NativeInstruction::instruction_size;
}
int MacroAssembler::patch_imm_in_li32(address branch, int32_t target) {
const int LI32_INSTRUCTIONS_NUM = 2; // lui + addiw
int64_t upper = (intptr_t)target;
int32_t lower = (((int32_t)target) << 20) >> 20;
upper -= lower;
upper = (int32_t)upper;
Assembler::patch(branch + 0, 31, 12, (upper >> 12) & 0xfffff); // Lui.
Assembler::patch(branch + 4, 31, 20, lower & 0xfff); // Addiw.
return LI32_INSTRUCTIONS_NUM * NativeInstruction::instruction_size;
}
static long get_offset_of_jal(address insn_addr) {
assert_cond(insn_addr != NULL);
long offset = 0;
unsigned insn = *(unsigned *)insn_addr;
long val = (long )Assembler::sextract(insn, 31, 12);
offset |= ((val >> 19) & 0x1) << 20;
offset |= (val & 0xff) << 12;
offset |= ((val >> 8) & 0x1) << 11;
offset |= ((val >> 9) & 0x3ff) << 1;
offset = (offset << 43) >> 43;
return offset;
}
static long get_offset_of_conditional_branch(address insn_addr) {
long offset = 0;
assert_cond(insn_addr != NULL);
unsigned insn = *(unsigned *)insn_addr;
offset = (long )Assembler::sextract(insn, 31, 31);
offset = (offset << 12) | (((long )(Assembler::sextract(insn, 7, 7) & 0x1)) << 11);
offset = offset | (((long )(Assembler::sextract(insn, 30, 25) & 0x3f)) << 5);
offset = offset | (((long )(Assembler::sextract(insn, 11, 8) & 0xf)) << 1);
offset = (offset << 41) >> 41;
return offset;
}
static long get_offset_of_pc_relative(address insn_addr) {
long offset = 0;
assert_cond(insn_addr != NULL);
offset = ((long )(Assembler::sextract(((unsigned *)insn_addr)[0], 31, 12))) << 12; // Auipc.
offset += ((long )Assembler::sextract(((unsigned *)insn_addr)[1], 31, 20)); // Addi/Jalr/Load.
offset = (offset << 32) >> 32;
return offset;
}
static address get_target_of_movptr(address insn_addr) {
assert_cond(insn_addr != NULL);
intptr_t target_address = (((int64_t)Assembler::sextract(((unsigned *)insn_addr)[0], 31, 12)) & 0xfffff) << 29; // Lui.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[1], 31, 20)) << 17; // Addi.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[3], 31, 20)) << 6; // Addi.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[5], 31, 20)); // Addi/Jalr/Load.
return (address) target_address;
}
static address get_target_of_li64(address insn_addr) {
assert_cond(insn_addr != NULL);
intptr_t target_address = (((int64_t)Assembler::sextract(((unsigned *)insn_addr)[0], 31, 12)) & 0xfffff) << 44; // Lui.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[1], 31, 20)) << 32; // Addi.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[3], 31, 20)) << 20; // Addi.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[5], 31, 20)) << 8; // Addi.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[7], 31, 20)); // Addi.
return (address)target_address;
}
address MacroAssembler::get_target_of_li32(address insn_addr) {
assert_cond(insn_addr != NULL);
intptr_t target_address = (((int64_t)Assembler::sextract(((unsigned *)insn_addr)[0], 31, 12)) & 0xfffff) << 12; // Lui.
target_address += ((int64_t)Assembler::sextract(((unsigned *)insn_addr)[1], 31, 20)); // Addiw.
return (address)target_address;
}
// Patch any kind of instruction; there may be several instructions.
// Return the total length (in bytes) of the instructions.
int MacroAssembler::pd_patch_instruction_size(address branch, address target) {
assert_cond(branch != NULL);
int64_t offset = target - branch;
if (NativeInstruction::is_jal_at(branch)) { // jal
return patch_offset_in_jal(branch, offset);
} else if (NativeInstruction::is_branch_at(branch)) { // beq/bge/bgeu/blt/bltu/bne
return patch_offset_in_conditional_branch(branch, offset);
} else if (NativeInstruction::is_pc_relative_at(branch)) { // auipc, addi/jalr/load
return patch_offset_in_pc_relative(branch, offset);
} else if (NativeInstruction::is_movptr_at(branch)) { // movptr
return patch_addr_in_movptr(branch, target);
} else if (NativeInstruction::is_li64_at(branch)) { // li64
return patch_imm_in_li64(branch, target);
} else if (NativeInstruction::is_li32_at(branch)) { // li32
int64_t imm = (intptr_t)target;
return patch_imm_in_li32(branch, (int32_t)imm);
} else {
#ifdef ASSERT
tty->print_cr("pd_patch_instruction_size: instruction 0x%x at " INTPTR_FORMAT " could not be patched!\n" ,
*(unsigned *)branch, p2i(branch));
Disassembler::decode(branch - 16, branch + 16);
#endif
ShouldNotReachHere();
return -1;
}
}
address MacroAssembler::target_addr_for_insn(address insn_addr) {
long offset = 0;
assert_cond(insn_addr != NULL);
if (NativeInstruction::is_jal_at(insn_addr)) { // jal
offset = get_offset_of_jal(insn_addr);
} else if (NativeInstruction::is_branch_at(insn_addr)) { // beq/bge/bgeu/blt/bltu/bne
offset = get_offset_of_conditional_branch(insn_addr);
} else if (NativeInstruction::is_pc_relative_at(insn_addr)) { // auipc, addi/jalr/load
offset = get_offset_of_pc_relative(insn_addr);
} else if (NativeInstruction::is_movptr_at(insn_addr)) { // movptr
return get_target_of_movptr(insn_addr);
} else if (NativeInstruction::is_li64_at(insn_addr)) { // li64
return get_target_of_li64(insn_addr);
} else if (NativeInstruction::is_li32_at(insn_addr)) { // li32
return get_target_of_li32(insn_addr);
} else {
ShouldNotReachHere();
}
return address(((uintptr_t)insn_addr + offset));
}
int MacroAssembler::patch_oop(address insn_addr, address o) {
// OOPs are either narrow (32 bits) or wide (48 bits). We encode
// narrow OOPs by setting the upper 16 bits in the first
// instruction.
if (NativeInstruction::is_li32_at(insn_addr)) {
// Move narrow OOP
uint32_t n = CompressedOops::narrow_oop_value(cast_to_oop(o));
return patch_imm_in_li32(insn_addr, (int32_t)n);
} else if (NativeInstruction::is_movptr_at(insn_addr)) {
// Move wide OOP
return patch_addr_in_movptr(insn_addr, o);
}
ShouldNotReachHere();
return -1;
}
void MacroAssembler::reinit_heapbase() {
if (UseCompressedOops) {
if (Universe::is_fully_initialized()) {
mv(xheapbase, CompressedOops::ptrs_base());
} else {
ExternalAddress target(CompressedOops::ptrs_base_addr());
relocate(target.rspec(), [&] {
int32_t offset;
la_patchable(xheapbase, target, offset);
ld(xheapbase, Address(xheapbase, offset));
});
}
}
}
void MacroAssembler::movptr(Register Rd, address addr, int32_t &offset) {
int64_t imm64 = (int64_t)addr;
#ifndef PRODUCT
{
char buffer[64];
snprintf(buffer, sizeof (buffer), "0x%" PRIx64, imm64);
block_comment(buffer);
}
#endif
assert(is_unsigned_imm_in_range(imm64, 47, 0) || (imm64 == (int64_t)-1),
"bit 47 overflows in address constant" );
// Load upper 31 bits
int64_t imm = imm64 >> 17;
int64_t upper = imm, lower = imm;
lower = (lower << 52) >> 52;
upper -= lower;
upper = (int32_t)upper;
lui(Rd, upper);
addi(Rd, Rd, lower);
// Load the rest 17 bits.
slli(Rd, Rd, 11);
addi(Rd, Rd, (imm64 >> 6) & 0x7ff);
slli(Rd, Rd, 6);
// This offset will be used by following jalr/ld.
offset = imm64 & 0x3f;
}
void MacroAssembler::add(Register Rd, Register Rn, int64_t increment, Register temp) {
if (is_imm_in_range(increment, 12, 0)) {
addi(Rd, Rn, increment);
} else {
assert_different_registers(Rn, temp);
li(temp, increment);
add(Rd, Rn, temp);
}
}
void MacroAssembler::addw(Register Rd, Register Rn, int32_t increment, Register temp) {
if (is_imm_in_range(increment, 12, 0)) {
addiw(Rd, Rn, increment);
} else {
assert_different_registers(Rn, temp);
li(temp, increment);
addw(Rd, Rn, temp);
}
}
void MacroAssembler::sub(Register Rd, Register Rn, int64_t decrement, Register temp) {
if (is_imm_in_range(-decrement, 12, 0)) {
addi(Rd, Rn, -decrement);
} else {
assert_different_registers(Rn, temp);
li(temp, decrement);
sub(Rd, Rn, temp);
}
}
void MacroAssembler::subw(Register Rd, Register Rn, int32_t decrement, Register temp) {
if (is_imm_in_range(-decrement, 12, 0)) {
addiw(Rd, Rn, -decrement);
} else {
assert_different_registers(Rn, temp);
li(temp, decrement);
subw(Rd, Rn, temp);
}
}
void MacroAssembler::andrw(Register Rd, Register Rs1, Register Rs2) {
andr(Rd, Rs1, Rs2);
// addw: The result is clipped to 32 bits, then the sign bit is extended,
// and the result is stored in Rd
addw(Rd, Rd, zr);
}
void MacroAssembler::orrw(Register Rd, Register Rs1, Register Rs2) {
orr(Rd, Rs1, Rs2);
// addw: The result is clipped to 32 bits, then the sign bit is extended,
// and the result is stored in Rd
addw(Rd, Rd, zr);
}
void MacroAssembler::xorrw(Register Rd, Register Rs1, Register Rs2) {
xorr(Rd, Rs1, Rs2);
// addw: The result is clipped to 32 bits, then the sign bit is extended,
// and the result is stored in Rd
addw(Rd, Rd, zr);
}
// Note: load_unsigned_short used to be called load_unsigned_word.
int MacroAssembler::load_unsigned_short(Register dst, Address src) {
int off = offset();
lhu(dst, src);
return off;
}
int MacroAssembler::load_unsigned_byte(Register dst, Address src) {
int off = offset();
lbu(dst, src);
return off;
}
int MacroAssembler::load_signed_short(Register dst, Address src) {
int off = offset();
lh(dst, src);
return off;
}
int MacroAssembler::load_signed_byte(Register dst, Address src) {
int off = offset();
lb(dst, src);
return off;
}
void MacroAssembler::load_sized_value(Register dst, Address src, size_t size_in_bytes, bool is_signed) {
switch (size_in_bytes) {
case 8: ld(dst, src); break ;
case 4: is_signed ? lw(dst, src) : lwu(dst, src); break ;
case 2: is_signed ? load_signed_short(dst, src) : load_unsigned_short(dst, src); break ;
case 1: is_signed ? load_signed_byte( dst, src) : load_unsigned_byte( dst, src); break ;
default : ShouldNotReachHere();
}
}
void MacroAssembler::store_sized_value(Address dst, Register src, size_t size_in_bytes) {
switch (size_in_bytes) {
case 8: sd(src, dst); break ;
case 4: sw(src, dst); break ;
case 2: sh(src, dst); break ;
case 1: sb(src, dst); break ;
default : ShouldNotReachHere();
}
}
// reverse bytes in halfword in lower 16 bits and sign-extend
// Rd[15:0] = Rs[7:0] Rs[15:8] (sign-extend to 64 bits)
void MacroAssembler::revb_h_h(Register Rd, Register Rs, Register tmp) {
if (UseZbb) {
rev8(Rd, Rs);
srai(Rd, Rd, 48);
return ;
}
assert_different_registers(Rs, tmp);
assert_different_registers(Rd, tmp);
srli(tmp, Rs, 8);
andi(tmp, tmp, 0xFF);
slli(Rd, Rs, 56);
srai(Rd, Rd, 48); // sign-extend
orr(Rd, Rd, tmp);
}
// reverse bytes in lower word and sign-extend
// Rd[31:0] = Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] (sign-extend to 64 bits)
void MacroAssembler::revb_w_w(Register Rd, Register Rs, Register tmp1, Register tmp2) {
if (UseZbb) {
rev8(Rd, Rs);
srai(Rd, Rd, 32);
return ;
}
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1, tmp2);
revb_h_w_u(Rd, Rs, tmp1, tmp2);
slli(tmp2, Rd, 48);
srai(tmp2, tmp2, 32); // sign-extend
srli(Rd, Rd, 16);
orr(Rd, Rd, tmp2);
}
// reverse bytes in halfword in lower 16 bits and zero-extend
// Rd[15:0] = Rs[7:0] Rs[15:8] (zero-extend to 64 bits)
void MacroAssembler::revb_h_h_u(Register Rd, Register Rs, Register tmp) {
if (UseZbb) {
rev8(Rd, Rs);
srli(Rd, Rd, 48);
return ;
}
assert_different_registers(Rs, tmp);
assert_different_registers(Rd, tmp);
srli(tmp, Rs, 8);
andi(tmp, tmp, 0xFF);
andi(Rd, Rs, 0xFF);
slli(Rd, Rd, 8);
orr(Rd, Rd, tmp);
}
// reverse bytes in halfwords in lower 32 bits and zero-extend
// Rd[31:0] = Rs[23:16] Rs[31:24] Rs[7:0] Rs[15:8] (zero-extend to 64 bits)
void MacroAssembler::revb_h_w_u(Register Rd, Register Rs, Register tmp1, Register tmp2) {
if (UseZbb) {
rev8(Rd, Rs);
rori(Rd, Rd, 32);
roriw(Rd, Rd, 16);
zero_extend(Rd, Rd, 32);
return ;
}
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1, tmp2);
srli(tmp2, Rs, 16);
revb_h_h_u(tmp2, tmp2, tmp1);
revb_h_h_u(Rd, Rs, tmp1);
slli(tmp2, tmp2, 16);
orr(Rd, Rd, tmp2);
}
// This method is only used for revb_h
// Rd = Rs[47:0] Rs[55:48] Rs[63:56]
void MacroAssembler::revb_h_helper(Register Rd, Register Rs, Register tmp1, Register tmp2) {
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1);
srli(tmp1, Rs, 48);
andi(tmp2, tmp1, 0xFF);
slli(tmp2, tmp2, 8);
srli(tmp1, tmp1, 8);
orr(tmp1, tmp1, tmp2);
slli(Rd, Rs, 16);
orr(Rd, Rd, tmp1);
}
// reverse bytes in each halfword
// Rd[63:0] = Rs[55:48] Rs[63:56] Rs[39:32] Rs[47:40] Rs[23:16] Rs[31:24] Rs[7:0] Rs[15:8]
void MacroAssembler::revb_h(Register Rd, Register Rs, Register tmp1, Register tmp2) {
if (UseZbb) {
assert_different_registers(Rs, tmp1);
assert_different_registers(Rd, tmp1);
rev8(Rd, Rs);
zero_extend(tmp1, Rd, 32);
roriw(tmp1, tmp1, 16);
slli(tmp1, tmp1, 32);
srli(Rd, Rd, 32);
roriw(Rd, Rd, 16);
zero_extend(Rd, Rd, 32);
orr(Rd, Rd, tmp1);
return ;
}
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1, tmp2);
revb_h_helper(Rd, Rs, tmp1, tmp2);
for (int i = 0; i < 3; ++i) {
revb_h_helper(Rd, Rd, tmp1, tmp2);
}
}
// reverse bytes in each word
// Rd[63:0] = Rs[39:32] Rs[47:40] Rs[55:48] Rs[63:56] Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24]
void MacroAssembler::revb_w(Register Rd, Register Rs, Register tmp1, Register tmp2) {
if (UseZbb) {
rev8(Rd, Rs);
rori(Rd, Rd, 32);
return ;
}
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1, tmp2);
revb(Rd, Rs, tmp1, tmp2);
ror_imm(Rd, Rd, 32);
}
// reverse bytes in doubleword
// Rd[63:0] = Rs[7:0] Rs[15:8] Rs[23:16] Rs[31:24] Rs[39:32] Rs[47,40] Rs[55,48] Rs[63:56]
void MacroAssembler::revb(Register Rd, Register Rs, Register tmp1, Register tmp2) {
if (UseZbb) {
rev8(Rd, Rs);
return ;
}
assert_different_registers(Rs, tmp1, tmp2);
assert_different_registers(Rd, tmp1, tmp2);
andi(tmp1, Rs, 0xFF);
slli(tmp1, tmp1, 8);
for (int step = 8; step < 56; step += 8) {
srli(tmp2, Rs, step);
andi(tmp2, tmp2, 0xFF);
orr(tmp1, tmp1, tmp2);
slli(tmp1, tmp1, 8);
}
srli(Rd, Rs, 56);
andi(Rd, Rd, 0xFF);
orr(Rd, tmp1, Rd);
}
// rotate right with shift bits
void MacroAssembler::ror_imm(Register dst, Register src, uint32_t shift, Register tmp)
{
if (UseZbb) {
rori(dst, src, shift);
return ;
}
assert_different_registers(dst, tmp);
assert_different_registers(src, tmp);
assert(shift < 64, "shift amount must be < 64" );
--> --------------------
--> maximum size reached
--> --------------------
quality 90%
¤ Dauer der Verarbeitung: 0.25 Sekunden
(vorverarbeitet)
¤
*© Formatika GbR, Deutschland