blob: f63a31c3dc855f146af5af528028f6e3f5cf60ee [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Code_Emitter.h"
#include "environment.h"
#include "m2n.h"
#include "m2n_ipf_internal.h"
#include "vm_ipf.h"
#include "vm_threads.h"
#include "open/types.h"
#include "open/vm_util.h"
#include "stub_code_utils.h"
#include "interpreter.h"
#include "exceptions.h"
//////////////////////////////////////////////////////////////////////////
// Utilities
extern "C" void *do_flushrs_asm();
extern "C" void *do_flushrs()
{
return do_flushrs_asm();
} //do_flushrs
// Given a bsp value for register 32 and a stacked register number
// return a pointer to where the stacked register is spilled
uint64* get_stacked_register_address(uint64* bsp, unsigned reg)
{
if (interpreter_enabled()) {
return interpreter.interpreter_get_stacked_register_address(bsp, reg);
}
assert(bsp && 32<=reg && reg<128);
unsigned r = (reg-32)<<3;
uint64 b = (uint64)bsp;
uint64 d4 = b+r;
uint64 d5 = (b&0x1f8)+r;
if (d5>=63*8)
if (d5>=126*8)
d4 += 16;
else
d4 += 8;
return (uint64*)d4;
}
// Get the bsp value for register 32 of the M2nFrame
uint64* m2n_get_bsp(M2nFrame* m2nf)
{
return (uint64*)m2nf;
}
uint64* m2n_get_extra_saved(M2nFrame* m2nf)
{
do_flushrs();
return (uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_EXTRA_SAVED_PTR);
}
//////////////////////////////////////////////////////////////////////////
// M2nFrame Interface
//***** Generic Interface
// fill m2n frame as empty
void m2n_null_init(M2nFrame* m2n){
memset(m2n, 0, sizeof(M2nFrame));
}
VMEXPORT // temporary solution for interpreter unplug
M2nFrame* m2n_get_last_frame()
{
return (M2nFrame*)p_TLS_vmthread->last_m2n_frame;
}
VMEXPORT // temporary solution for interpreter unplug
M2nFrame* m2n_get_last_frame(VM_thread* thread)
{
return (M2nFrame*)thread->last_m2n_frame;
}
VMEXPORT // temporary solution for interpreter unplug
void m2n_set_last_frame(M2nFrame* lm2nf)
{
vm_thread_t vm_thread = jthread_self_vm_thread_unsafe();
vm_thread->last_m2n_frame = lm2nf;
}
VMEXPORT
void m2n_set_last_frame(VM_thread* thread, M2nFrame* lm2nf)
{
thread->last_m2n_frame = lm2nf;
}
VMEXPORT // temporary solution for interpreter unplug
M2nFrame* m2n_get_previous_frame(M2nFrame* m2nfl)
{
assert(m2nfl);
do_flushrs();
return (M2nFrame*)*get_stacked_register_address(m2n_get_bsp(m2nfl), M2N_SAVED_M2NFL);
}
ObjectHandles* m2n_get_local_handles(M2nFrame* m2nf)
{
assert(m2nf);
do_flushrs();
return (ObjectHandles*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES);
}
void m2n_set_local_handles(M2nFrame* m2nf, ObjectHandles* handles)
{
assert(m2nf);
do_flushrs();
uint64* p_head = get_stacked_register_address(m2n_get_bsp(m2nf), M2N_OBJECT_HANDLES);
*p_head = (uint64)handles;
}
NativeCodePtr m2n_get_ip(M2nFrame* m2nf)
{
assert(m2nf);
do_flushrs();
uint64 * UNUSED bsp = (uint64 *)m2nf;
assert(bsp);
return (NativeCodePtr)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_RETURN_ADDRESS);
}
// 20040708 New function - needs proper implementation.
void m2n_set_ip(M2nFrame* lm2nf, NativeCodePtr ip)
{
assert(lm2nf);
LDIE(51, "Not implemented");
}
// sets pointer to the registers used for jvmti PopFrame
void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) {
// FIXME: not sure we want to support this function on IPF
LDIE(51, "Not implemented");
}
// returns pointer to the registers used for jvmti PopFrame
Registers* get_pop_frame_registers(M2nFrame* m2nf) {
// FIXME: not sure we want to support this function on IPF
LDIE(51, "Not implemented");
return 0;
}
Method_Handle m2n_get_method(M2nFrame* m2nf)
{
assert(m2nf);
do_flushrs();
uint64 * UNUSED bsp = (uint64 *)m2nf;
assert(bsp);
return (Method_Handle)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_METHOD);
}
// Returns type of noted m2n frame
frame_type m2n_get_frame_type(M2nFrame* m2nf) {
assert(m2nf);
do_flushrs();
uint64 * UNUSED bsp = (uint64 *)m2nf;
assert(bsp);
return (frame_type)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE);
}
// Sets type of noted m2n frame
void m2n_set_frame_type(M2nFrame* m2nf, frame_type m2nf_type) {
assert(m2nf);
do_flushrs();
uint64 * UNUSED bsp = (uint64 *)m2nf;
assert(bsp);
*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_FRAME_TYPE) = m2nf_type;
}
size_t m2n_get_size() {
return sizeof(M2nFrame);
}
//***** Stub Interface
// Flushes register stack of the current thread into backing store and calls target procedure.
NativeCodePtr m2n_gen_flush_and_call() {
static NativeCodePtr addr = NULL;
if (addr != NULL) {
return addr;
}
tl::MemoryPool mem_pool;
Merced_Code_Emitter emitter(mem_pool, 2, 0);
emitter.disallow_instruction_exchange();
emitter.memory_type_is_unknown();
// We need to remember pfs & b0 here but there is no space to save them in.
// Register stack contains valid outputs and we don't know how many registers are used.
// Memory stack holds output values beyound those 8 which are on register stack.
// The only place is general caller-saves registers. It is save to use them with out preserving
// because they are alredy preserved by the corresponding M2N frame.
// r4 is used to keep a thread pointer...so let's use r5 & r6.
emitter.ipf_mfap(PRESERV_GENERAL_REG1, AR_pfs);
emitter.ipf_mfbr(PRESERV_GENERAL_REG2, BRANCH_RETURN_LINK_REG);
emitter.flush_buffer();
emitter.ipf_flushrs();
emitter.ipf_bricall(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG, BRANCH_CALL_REG);
emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, PRESERV_GENERAL_REG2);
emitter.ipf_mtap(AR_pfs, PRESERV_GENERAL_REG1);
emitter.ipf_brret(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG);
addr = finalize_stub(emitter, "");
return addr;
}
unsigned m2n_gen_push_m2n(Merced_Code_Emitter* emitter, Method_Handle method, frame_type current_frame_type, bool handles, unsigned num_on_stack, unsigned num_local, unsigned num_out, bool do_alloc)
{
// Allocate new frame
if (do_alloc) {
emitter->ipf_alloc(M2N_SAVED_PFS, 8, M2N_NUMBER_LOCALS+num_local, num_out, 0);
// The alloc instruction saves pfs, now save return address and GP
emitter->ipf_mfbr(M2N_SAVED_RETURN_ADDRESS, BRANCH_RETURN_LINK_REG);
emitter->ipf_mov (M2N_SAVED_GP, GP_REG);
}
// 20031205: The alloc writes the CFM and the mfpr reads it, so they must be separated by a stop bit, this is a brutal way of achieving this.
emitter->flush_buffer();
// Save predicates, SP, and callee saves general registers
emitter->ipf_adds(M2N_SAVED_SP, num_on_stack, SP_REG);
emitter->ipf_mfpr(M2N_SAVED_PR );
emitter->ipf_mfap(M2N_SAVED_UNAT, AR_unat);
emitter->ipf_mov (M2N_SAVED_R4, 4);
emitter->ipf_mov (M2N_SAVED_R5, 5);
emitter->ipf_mov (M2N_SAVED_R6, 6);
emitter->ipf_mov (M2N_SAVED_R7, 7);
// Set object handles to NULL and set method information
emitter->ipf_mov(M2N_OBJECT_HANDLES, 0);
emit_mov_imm_compactor(*emitter, M2N_METHOD, (uint64)method);
emit_mov_imm_compactor(*emitter, M2N_FRAME_TYPE, (uint64)current_frame_type);
const int P1 = SCRATCH_PRED_REG;
const int P2 = SCRATCH_PRED_REG2;
const int OLD_RSE_MODE = SCRATCH_GENERAL_REG2;
const int NEW_RSE_MODE = SCRATCH_GENERAL_REG3;
// SCRATCH_GENERAL_REG4 & SCRATCH_GENERAL_REG5 are reserved for std places.
const int BSP = SCRATCH_GENERAL_REG6;
const int IMM_8 = SCRATCH_GENERAL_REG7;
const int IMM_1F8 = SCRATCH_GENERAL_REG8;
const int TMP_REG = SCRATCH_GENERAL_REG9;
// Scratch branch register.
const int TMP_BRANCH_REG = 6;
// Switch RSE to "forced lazy" mode. This is required to access RNAT.
emitter->ipf_mfap(OLD_RSE_MODE, AR_rsc);
emitter->ipf_dep(NEW_RSE_MODE, 0, OLD_RSE_MODE, 0, 2);
emitter->ipf_mtap(AR_rsc, NEW_RSE_MODE);
// Flush must be the first instruction in the group.
emitter->flush_buffer();
// Spill parent frames so that corresponding RNAT bits become valid.
emitter->ipf_flushrs();
// Extract backing store pointer
emitter->ipf_mfap(BSP, AR_bsp);
// Remember parent RNAT collection.
emitter->ipf_mfap(M2N_EXTRA_RNAT, AR_rnat);
// TODO: This is not fully legal reset nat bits for the whole m2n frame because it
// contains r4-r7 general registers which may have corresponding unat bits up.
emitter->ipf_mov(M2N_EXTRA_UNAT, 0);
/* The following code spills M2N into backing store.
emitter->ipf_movl(IMM_1F8, 0, (uint64)0x1f8);
emitter->ipf_movl(IMM_8, 0, (uint64)0x8);
// Forcebly spill M2N frame into backing store.
for(int i = M2N_NUMBER_INPUTS; i < M2N_NUMBER_LOCALS; i++) {
emitter->ipf_and(TMP_REG, IMM_1F8, BSP);
emitter->ipf_cmp(icmp_eq, cmp_none, P1, P2, IMM_1F8, TMP_REG);
emitter->ipf_add(BSP, BSP, IMM_8, P1);
emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, BSP, 32 + i, 8);
}
// Remember UNAT collection for the current frame.
emitter->ipf_sub(BSP, BSP, IMM_8);
emitter->ipf_mfap(M2N_EXTRA_UNAT, AR_unat);
emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, BSP, M2N_EXTRA_UNAT);
// Restore original UNAT.
emitter->ipf_mtap(AR_unat, M2N_SAVED_UNAT);
emitter->flush_buffer();
*/
// Switch RSE to the original mode.
emitter->ipf_mtap(AR_rsc, OLD_RSE_MODE);
// Link M2nFrame into list of current thread
size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame;
emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG);
emitter->ipf_ld(int_mem_size_8, mem_ld_none, mem_none, M2N_SAVED_M2NFL, SCRATCH_GENERAL_REG2);
emitter->ipf_mfap(SCRATCH_GENERAL_REG7, AR_bsp);
emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, SCRATCH_GENERAL_REG7);
return 32+8+M2N_NUMBER_LOCALS;
}
void m2n_gen_set_local_handles(Merced_Code_Emitter* emitter, unsigned src_reg)
{
emitter->ipf_mov(M2N_OBJECT_HANDLES, src_reg);
}
void m2n_gen_set_local_handles_imm(Merced_Code_Emitter* emitter, uint64 imm_val)
{
int64 UNUSED imm = (int64)imm_val;
assert(imm>=-0x200000 && imm<-0x200000);
emitter->ipf_movi(M2N_OBJECT_HANDLES, (int)imm_val);
}
static void m2n_pop_local_handles() {
assert(!hythread_is_suspend_enabled());
exn_rethrow_if_pending();
M2nFrame *m2n = m2n_get_last_frame();
free_local_object_handles2(m2n_get_local_handles(m2n));
}
static void m2n_free_local_handles() {
assert(!hythread_is_suspend_enabled());
if (exn_raised()) {
exn_rethrow();
}
M2nFrame * m2n = m2n_get_last_frame();
// iche free_local_object_handles3(m2n->local_object_handles);
free_local_object_handles3(m2n_get_local_handles(m2n)); // iche
}
void m2n_gen_pop_m2n(Merced_Code_Emitter* emitter, bool handles, M2nPreserveRet preserve_ret, bool do_alloc, unsigned out_reg, int target)
{
unsigned free_target;
if (handles) {
assert(target != -1); // make sure a target has been provided
// Do we need to call free?
free_target = (unsigned) target;
emitter->ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, M2N_OBJECT_HANDLES, 0);
emitter->ipf_br(br_cond, br_many, br_spnt, br_none, free_target, SCRATCH_PRED_REG);
}
// Yes, save return register
if (preserve_ret == MPR_Gr) {
emitter->ipf_add(6, RETURN_VALUE_REG, 0);
} else if (preserve_ret == MPR_Fr) {
emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, RETURN_VALUE_REG, unsigned(-16));
}
if (handles) {
emit_call_with_gp(*emitter, (void**)m2n_pop_local_handles, false);
} else {
emit_call_with_gp(*emitter, (void**)m2n_free_local_handles, false);
}
// Restore return register
if (preserve_ret == MPR_Gr) {
emitter->ipf_add(RETURN_VALUE_REG, 6, 0);
} else if (preserve_ret == MPR_Fr) {
emitter->ipf_adds(SP_REG, 16, SP_REG);
emitter->ipf_ldf(float_mem_size_e, mem_ld_fill, mem_none, RETURN_VALUE_REG, SP_REG);
}
if (handles) {
emitter->set_target(free_target);
}
// Unlink the M2nFrame from the list of the current thread
size_t offset_lm2nf = (size_t)&((VM_thread*)0)->last_m2n_frame;
emitter->ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_lm2nf, THREAD_PTR_REG);
emitter->ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG2, M2N_SAVED_M2NFL);
// Restore callee saved general registers, predicates, return address, and pfs
emitter->ipf_mov (7, M2N_SAVED_R7);
emitter->ipf_mov (6, M2N_SAVED_R6);
emitter->ipf_mov (5, M2N_SAVED_R5);
emitter->ipf_mov (4, M2N_SAVED_R4);
emitter->ipf_mtpr( M2N_SAVED_PR);
if (do_alloc) {
emitter->ipf_mov (GP_REG, M2N_SAVED_GP);
emitter->ipf_mtbr(BRANCH_RETURN_LINK_REG, M2N_SAVED_RETURN_ADDRESS);
emitter->ipf_mtap(AR_pfs, M2N_SAVED_PFS);
}
}
void m2n_gen_save_extra_preserved_registers(Merced_Code_Emitter* emitter)
{
unsigned reg;
// Save pointer to saves area
emitter->ipf_mov(M2N_EXTRA_SAVED_PTR, SP_REG);
// Save callee saves floating point registers
for (reg = 2; reg < 6; reg++)
emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16));
for (reg = 16; reg < 32; reg++)
emitter->ipf_stf_inc_imm(float_mem_size_e, mem_st_spill, mem_none, SP_REG, reg, unsigned(-16));
// Save callee saves branch registers
for (reg = 1; reg < 6; reg++) {
emitter->ipf_mfbr(SCRATCH_GENERAL_REG, reg);
emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8));
}
// Save ar.fpsr, ar.unat, and ar.lc
emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8));
emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_unat);
emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-8));
emitter->ipf_mfap(SCRATCH_GENERAL_REG, AR_lc);
emitter->ipf_st_inc_imm(int_mem_size_8, mem_st_none, mem_none, SP_REG, SCRATCH_GENERAL_REG, unsigned(-24));
// Note that the last postdec (postinc of -24) has created the required scratch area on the memory stack
}
unsigned m2n_get_last_m2n_reg() {
return M2N_SAVED_PFS + M2N_NUMBER_LOCALS - 1;
}
unsigned m2n_get_pfs_save_reg() {
return M2N_SAVED_PFS;
}
unsigned m2n_get_return_save_reg() {
return M2N_SAVED_RETURN_ADDRESS;
}
unsigned m2n_get_gp_save_reg() {
return M2N_SAVED_GP;
}
uint64* m2n_get_arg_word(M2nFrame* m2nf, unsigned n)
{
do_flushrs();
if (n<8)
return get_stacked_register_address(m2n_get_bsp(m2nf), n+32);
else
return ((uint64*)*get_stacked_register_address(m2n_get_bsp(m2nf), M2N_SAVED_SP))+(n-8+2); // +2 is for 16-bytes scratch on mem stack
}
void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs)
{
LDIE(86, "check that it works"); // FIXME: check that it works
m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs);
}
void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs)
{
LDIE(51, "Not implemented");
}
M2nFrame* m2n_push_suspended_frame(Registers* regs)
{
LDIE(86, "check that it works"); // FIXME: check that it works
return m2n_push_suspended_frame(p_TLS_vmthread, regs);
}
M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs)
{
LDIE(86, "check that it works"); // FIXME: check that it works
M2nFrame* m2nf = (M2nFrame*)STD_MALLOC(sizeof(M2nFrame));
assert(m2nf);
m2n_push_suspended_frame(thread, m2nf, regs);
return m2nf;
}
bool m2n_is_suspended_frame(M2nFrame * m2nf) {
LDIE(51, "Not implemented");
return false;
}