blob: c53caf98c42fc85a2a29a3da4df738967187ace0 [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.
*/
/**
* @author Intel, Evgueni Brevnov
*/
// Routines used by the VM to emit stub code for the ipf. These routines use the ipf code compactor.
//
#include <assert.h>
#include "environment.h"
#include "merced.h"
#include "open/vm_util.h"
#include "vm_ipf.h"
#include "nogc.h"
#include "stub_code_utils.h"
void *get_vm_gp_value()
{
void **function_pointer = (void **)get_vm_gp_value;
return function_pointer[1];
}
void emit_alloc_for_single_call(Merced_Code_Emitter& emitter,
int num_in_args,
int num_out_args,
void **function,
int& out0_reg,
int& save_pfs_reg,
int& save_b0_reg,
int& save_gp_reg)
{
bool should_save_gp = (function[1] != get_vm_gp_value());
int num_local = (should_save_gp ? 3 : 2);
save_pfs_reg = IN_REG0 + num_in_args;
save_b0_reg = save_pfs_reg + 1;
save_gp_reg = (should_save_gp ? save_b0_reg + 1 : 0);
out0_reg = IN_REG0 + num_in_args + num_local;
emitter.ipf_alloc(save_pfs_reg, num_in_args, num_local, num_out_args, 0);
emitter.ipf_mfbr(save_b0_reg, BRANCH_RETURN_LINK_REG);
if (should_save_gp)
emitter.ipf_mov(save_gp_reg, GP_REG);
}
void emit_dealloc_for_single_call(Merced_Code_Emitter& emitter,
int save_pfs_reg,
int save_b0_reg,
int save_gp_reg,
int pred)
{
if (save_gp_reg != 0)
emitter.ipf_mov(GP_REG, save_gp_reg, pred);
emitter.ipf_mtap(AR_pfs, save_pfs_reg, pred);
emitter.ipf_mtbr(BRANCH_RETURN_LINK_REG, save_b0_reg, pred);
}
NativeCodePtr m2n_gen_flush_and_call();
void emit_call_with_gp(Merced_Code_Emitter& emitter,
void **proc_ptr,
bool flushrs,
int saved_gp_reg)
{
void *new_gp = proc_ptr[1];
void *vm_gp = get_vm_gp_value();
// Set the gp according to the IPF software conventions.
if (new_gp != vm_gp)
{
if (saved_gp_reg != 0)
emitter.ipf_mov(saved_gp_reg, GP_REG);
emit_mov_imm_compactor(emitter, GP_REG, (uint64)new_gp);
}
uint64 branch_target = (uint64)(*proc_ptr);
emit_mov_imm_compactor(emitter, SCRATCH_GENERAL_REG, branch_target, 0);
if (flushrs) {
emitter.ipf_mtbr(BRANCH_CALL_REG, SCRATCH_GENERAL_REG);
emit_mov_imm_compactor(emitter, SCRATCH_GENERAL_REG, (uint64)m2n_gen_flush_and_call(), 0);
}
emitter.ipf_mtbr(SCRATCH_BRANCH_REG, SCRATCH_GENERAL_REG);
emitter.ipf_bricall(br_few, br_sptk, br_none, BRANCH_RETURN_LINK_REG, SCRATCH_BRANCH_REG);
// Restore the saved GP.
if (new_gp != vm_gp && saved_gp_reg != 0)
emitter.ipf_mov(GP_REG, saved_gp_reg);
} //emit_call_with_gp
void emit_branch_with_gp(Merced_Code_Emitter& emitter,
void **proc_ptr)
{
void *new_gp = proc_ptr[1];
void *vm_gp = get_vm_gp_value();
// Set the gp according to the IPF software conventions.
if (new_gp != vm_gp)
{
emit_mov_imm_compactor(emitter, GP_REG, (uint64)new_gp);
}
uint64 branch_target = (uint64)(*proc_ptr);
emit_mov_imm_compactor(emitter, SCRATCH_GENERAL_REG, branch_target, 0);
emitter.ipf_mtbr(SCRATCH_BRANCH_REG, SCRATCH_GENERAL_REG);
emitter.ipf_bri(br_cond, br_few, br_sptk, br_none, SCRATCH_BRANCH_REG);
// someone else will restore the gp, according to the software conventions
} //emit_branch_with_gp
// Convenience procedure to emit a movl instruction using a 64 bit constant rather than two 32 bit constants.
void emit_movl_compactor(Merced_Code_Emitter& emitter, unsigned dst_reg, uint64 u64_value, unsigned pred)
{
uint64 hi32 = (u64_value >> 32);
uint64 lo32 = (u64_value & 0xFFffFFff);
emitter.ipf_movl(dst_reg, (unsigned)hi32, (unsigned)lo32, pred);
} //emit_movl_compactor
// Convenience procedure to select an appropriate mov instruction for a 64-bit immediate
void emit_mov_imm_compactor(Merced_Code_Emitter& emitter, unsigned dst_reg, uint64 imm, unsigned pred)
{
if (imm==0)
emitter.ipf_mov(dst_reg, 0, pred);
else if (0xffffffffffe00000<=imm || imm<0x1fffff) // imm has a valid 22-bit signed rep
emitter.ipf_movi(dst_reg, (int)imm, pred);
else
emit_movl_compactor(emitter, dst_reg, imm, pred);
} //emit_mov_imm_compactor
void *finalize_stub(Merced_Code_Emitter &emitter, const char *name)
{
emitter.flush_buffer();
size_t stub_size = emitter.get_size();
void *stub = (void *)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate);
emitter.copy((char *)stub);
flush_hw_cache((U_8*)stub, stub_size);
sync_i_cache();
return stub;
} //finalize_stub
void increment_stats_counter(Merced_Code_Emitter &emitter, void *counter_addr, unsigned pred)
{
#ifdef VM_STATS
const int counter_addr_reg = SCRATCH_GENERAL_REG;
const int counter_reg = SCRATCH_GENERAL_REG2;
uint64 addr = (uint64)(counter_addr);
emit_movl_compactor(emitter, counter_addr_reg, addr, pred);
emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, counter_reg, counter_addr_reg, pred);
emitter.ipf_adds(counter_reg, 1, counter_reg, pred);
emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, counter_addr_reg, counter_reg, pred);
#endif // VM_STATS
} //increment_stats_counter
void gen_compare_to_managed_null(Merced_Code_Emitter &emitter,
int predicate1, int predicate2, // predicate regs to set
int src, // register to compare against
int scratch // scratch register to use if needed
)
{
assert(VM_Global_State::loader_env->compress_references);
const bool cmp4 = true;
U_32 null_low = (U_32) (uint64) VM_Global_State::loader_env->managed_null;
if (null_low == 0)
{
emitter.ipf_cmp(icmp_eq, cmp_none, predicate1, predicate2, src, 0, cmp4);
}
else
{
emit_mov_imm_compactor(emitter, scratch, null_low);
emitter.ipf_cmp(icmp_eq, cmp_none, predicate1, predicate2, src, scratch, cmp4);
}
}
Boolean jit_clears_ccv_in_monitor_enter()
{
return FALSE;
}
void enforce_calling_conventions(Merced_Code_Emitter *emitter, int pred)
{
emitter->ipf_mtap(AR_ccv, 0, pred);
}
// ST a helper function used by emit_print_reg
static void print_helper(char *str, POINTER_SIZE_INT arg) {
fprintf(stderr, str, arg);
}
// ST a function that prints the value of a register, along with a message
// it presupposes the existence of two output regs; the values of these regs
// are preserved.
void emit_print_reg(Merced_Code_Emitter &emitter, char *msg, unsigned print_reg, unsigned num_inputs, unsigned first_output, bool do_alloc) {
int out0, save_pfs, save_b0, save_gp;
if (do_alloc) {
// call alloc and save various things
emit_alloc_for_single_call(emitter, num_inputs, 2,
(void **) print_helper,
out0, save_pfs, save_b0, save_gp);
}
else {
out0 = first_output;
}
// push the two output regs on the stack
emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, SP_REG, out0, unsigned(-16));
emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, SP_REG, out0+1, unsigned(-16));
// push all scratch regs on the stack
for (unsigned i=14; i <= 31; i++) {
emitter.ipf_st_inc_imm(int_mem_size_8, mem_st_spill, mem_none, SP_REG, i, unsigned(-16));
}
// fill in the two output regs
emit_movl_compactor(emitter, out0, (uint64) msg);
emitter.ipf_mov(out0+1, print_reg);
// call a helper function
emit_call_with_gp(emitter, (void **) print_helper, false);
emitter.ipf_adds(SP_REG, 16, SP_REG);
// restore the scratch regs
for (unsigned i=31; i >= 14; i--)
emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_fill, mem_none, i, SP_REG, 16);
// restore the two output regs
emitter.ipf_ld_inc_imm(int_mem_size_8, mem_ld_fill, mem_none, out0+1, SP_REG, 16);
emitter.ipf_ld(int_mem_size_8, mem_ld_fill, mem_none, out0, SP_REG);
if (do_alloc) {
// dealloc and restore various things
emit_dealloc_for_single_call(emitter, save_pfs, save_b0, save_gp);
}
}