blob: 5e525fd4dd7441be0a5034b0416bd94346d32e2c [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 Evgueni Brevnov
*/
#define LOG_DOMAIN "vm.helpers"
#include "cxxlog.h"
#include <string.h>
#include "open/types.h"
#include "port_malloc.h"
#include "vm_threads.h"
#include "exceptions.h"
#include "m2n.h"
#include "encoder.h"
#include "m2n_em64t_internal.h"
#include "lil_code_generator_em64t.h"
/* Generic Interface */
void m2n_null_init(M2nFrame * m2n){
memset(m2n, 0, sizeof(M2nFrame));
}
M2nFrame* m2n_get_last_frame() {
return (M2nFrame*)p_TLS_vmthread->last_m2n_frame;
}
M2nFrame* m2n_get_last_frame(VM_thread * thread) {
return (M2nFrame*)thread->last_m2n_frame;
}
void m2n_set_last_frame(VM_thread* thread, M2nFrame * lm2nf) {
thread->last_m2n_frame = lm2nf;
}
void m2n_set_last_frame(M2nFrame * lm2nf) {
vm_thread_t vm_thread = jthread_self_vm_thread_unsafe();
vm_thread->last_m2n_frame = lm2nf;
}
M2nFrame * m2n_get_previous_frame(M2nFrame * m2nf) {
return m2nf->prev_m2nf;
}
ObjectHandles* m2n_get_local_handles(M2nFrame * m2nf) {
return m2nf->local_object_handles;
}
void m2n_set_local_handles(M2nFrame * m2nf, ObjectHandles * handles) {
m2nf->local_object_handles = handles;
}
NativeCodePtr m2n_get_ip(M2nFrame * m2nf) {
return (NativeCodePtr *) m2nf->rip;
}
void m2n_set_ip(M2nFrame * lm2nf, NativeCodePtr ip) {
lm2nf->rip = (uint64)ip;
}
Method_Handle m2n_get_method(M2nFrame * m2nf) {
return m2nf->method;
}
frame_type m2n_get_frame_type(M2nFrame * m2nf) {
return m2nf->current_frame_type;
}
void m2n_set_frame_type(M2nFrame * m2nf, frame_type m2nf_type)
{
m2nf->current_frame_type = m2nf_type;
}
void m2n_push_suspended_frame(M2nFrame* m2nf, Registers* regs)
{
m2n_push_suspended_frame(p_TLS_vmthread, m2nf, regs);
}
void m2n_push_suspended_frame(VM_thread* thread, M2nFrame* m2nf, Registers* regs)
{
assert(m2nf);
m2nf->p_lm2nf = (M2nFrame**)1;
m2nf->method = NULL;
m2nf->local_object_handles = NULL;
m2nf->current_frame_type = FRAME_UNKNOWN;
m2nf->rip = (POINTER_SIZE_INT)regs->get_ip();
m2nf->regs = regs;
m2nf->prev_m2nf = m2n_get_last_frame(thread);
m2n_set_last_frame(thread, m2nf);
}
M2nFrame* m2n_push_suspended_frame(Registers* regs)
{
return m2n_push_suspended_frame(p_TLS_vmthread, regs);
}
M2nFrame* m2n_push_suspended_frame(VM_thread* thread, Registers* regs)
{
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) {
return (uint64)m2nf->p_lm2nf == 1;
}
void * m2n_get_frame_base(M2nFrame * m2nf) {
return &m2nf->rip;
}
/* Internal Interface */
unsigned m2n_ts_to_register_size(unsigned num_std_need_to_save,
unsigned num_ret_need_to_save) {
return 13 + (6 * num_std_need_to_save) +
3 + (6 * num_ret_need_to_save);
}
// rsp should point to the bottom of the activation frame since push may occur
// inputs should be preserved outside if required since we do a call
// num_std_need_to_save registers will be preserved
char * m2n_gen_ts_to_register(char * buf, const R_Opnd * reg,
unsigned num_callee_saves_used,
unsigned num_callee_saves_max,
unsigned num_std_need_to_save,
unsigned num_ret_need_to_save) {
// we can't preserve rax and return value on it at the same time
assert (num_ret_need_to_save == 0 || reg != &rax_opnd);
//#ifdef PLATFORM_POSIX
// preserve std places
unsigned i;
unsigned num_std_saved = 0;
// use calle-saves registers first
while (num_std_saved < num_std_need_to_save &&
(i = num_callee_saves_used + num_std_saved) < num_callee_saves_max) {
buf = mov(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_LOCALS_OFFSET + i),
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved),
size_64);
++num_std_saved;
}
// if we still have not preserved std places save them on the stack
while (num_std_saved < num_std_need_to_save) {
buf = push(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved),
size_64);
++num_std_saved;
}
assert(num_std_saved == num_std_need_to_save);
// preserve returns
unsigned num_ret_saved = 0;
while (num_ret_saved < num_ret_need_to_save &&
(i = num_callee_saves_used + num_std_saved + num_ret_saved) < num_callee_saves_max) {
buf = mov(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_LOCALS_OFFSET + i),
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved),
size_64);
++num_ret_saved;
}
// if we still have not preserved returns save them on the stack
while (num_ret_saved < num_ret_need_to_save) {
buf = push(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_RETURNS_OFFSET + num_std_saved),
size_64);
++num_ret_saved;
}
assert(num_ret_saved == num_ret_need_to_save);
// TODO: FIXME: only absolute addressing mode is supported now
buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)get_thread_ptr), size_64);
#ifdef _WIN64
buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW));
#endif
buf = call(buf, rax_opnd, size_64);
#ifdef _WIN64
buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW));
#endif
if (reg != &rax_opnd) {
buf = mov(buf, *reg, rax_opnd, size_64);
}
// restore returns from the stack
i = num_callee_saves_used + num_std_saved;
while (num_ret_saved > 0 && i + num_ret_saved > num_callee_saves_max) {
--num_ret_saved;
buf = pop(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved),
size_64);
}
// restore std places from the stack
while (num_std_saved > 0 && num_callee_saves_used + num_std_saved > num_callee_saves_max) {
--num_std_saved;
buf = pop(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved),
size_64);
}
// restore returns from callee-saves registers
i = num_callee_saves_used + num_std_saved;
while (num_ret_saved > 0) {
--num_ret_saved;
buf = mov(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_RETURNS_OFFSET + num_ret_saved),
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_LOCALS_OFFSET + i + num_ret_saved),
size_64);
}
// restore std places from callee-saves registers
while (num_std_saved > 0) {
--num_std_saved;
buf = mov(buf,
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::STD_PLACES_OFFSET + num_std_saved),
LcgEM64TContext::get_reg_from_map(
LcgEM64TContext::GR_LOCALS_OFFSET + num_callee_saves_used + num_std_saved),
size_64);
}
//#else //!PLATFORM_POSIX
// buf = prefix(buf, prefix_fs);
// buf = mov(buf, *reg, M_Opnd(0x14), size_64);
//#endif //!PLATFORM_POSIX
return buf;
}
char * m2n_gen_set_local_handles_r(char * buf, unsigned bytes_to_m2n, const R_Opnd * src_reg) {
unsigned offset_local_handles = (unsigned)(uint64) &((M2nFrame*)0)->local_object_handles;
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *src_reg, size_64);
return buf;
}
char * m2n_gen_set_local_handles_imm(char * buf, unsigned bytes_to_m2n, const Imm_Opnd * imm) {
unsigned offset_local_handles = (unsigned)(uint64)&((M2nFrame*)0)->local_object_handles;
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n + offset_local_handles), *imm, size_64);
return buf;
}
unsigned m2n_push_m2n_size(unsigned num_callee_saves,
unsigned num_std_need_to_save) {
return 91 - (5 * num_callee_saves) +
m2n_ts_to_register_size(num_std_need_to_save, 0);
}
// inputs should be preserved outside if required since we do a call
// num_std_need_to_save registers will be preserved
char * m2n_gen_push_m2n(char * buf, Method_Handle method,
frame_type current_frame_type,
bool handles,
unsigned num_callee_saves,
unsigned num_std_need_to_save,
I_32 bytes_to_m2n_top) {
// skip callee-saves registers
bytes_to_m2n_top -= num_callee_saves * LcgEM64TContext::GR_SIZE;
// TODO: check if it makes sense to save all callee-saves registers here
//store rest of callee-saves registers
for (unsigned i = num_callee_saves; i < LcgEM64TContext::MAX_GR_LOCALS; i++) {
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
buf = mov(buf,
M_Base_Opnd(rsp_reg, bytes_to_m2n_top),
LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i),
size_64);
}
// init pop_regs to null
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top),
Imm_Opnd(size_32, 0), size_64);
// store current_frame_type
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
assert(fit32(current_frame_type));
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top),
Imm_Opnd(size_32, current_frame_type), size_64);
// store a method associated with the current m2n frame
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
if (fit32((int64)method)) {
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top),
Imm_Opnd(size_32, (int64)method), size_64);
} else {
buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (int64)method), size_64);
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd);
}
// store local object handles
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top),
Imm_Opnd(size_64, (int64)0), size_64);
// move pointer to the current VM_Thread structure to rax
buf = m2n_gen_ts_to_register(buf, &rax_opnd,
num_callee_saves, LcgEM64TContext::MAX_GR_LOCALS,
num_std_need_to_save, 0);
// shift to the last_m2n_frame field
I_32 last_m2n_frame_offset = (I_32)(int64)&((VM_thread*)0)->last_m2n_frame;
buf = alu(buf, add_opc, rax_opnd, Imm_Opnd(size_32, last_m2n_frame_offset), size_64);
// store pointer to pointer to last m2n frame
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), rax_opnd, size_64);
// save pointer to the previous m2n frame
bytes_to_m2n_top -= LcgEM64TContext::GR_SIZE;
buf = mov(buf, r9_opnd, M_Base_Opnd(rax_reg, 0));
buf = mov(buf, M_Base_Opnd(rsp_reg, bytes_to_m2n_top), r9_opnd, size_64);
// update last m2n frame of the current thread
buf = lea(buf, r9_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_top));
buf = mov(buf, M_Base_Opnd(rax_reg, 0), r9_opnd, size_64);
return buf;
}
unsigned m2n_pop_m2n_size(bool handles, unsigned num_callee_saves, unsigned preserve_ret)
{
return 56 - 5*num_callee_saves + (preserve_ret ? 4: 0);
}
static void m2n_pop_local_handles() {
assert(!hythread_is_suspend_enabled());
if (exn_raised()) {
exn_rethrow();
}
M2nFrame * m2n = m2n_get_last_frame();
free_local_object_handles2(m2n->local_object_handles);
}
static void m2n_free_local_handles() {
assert(!hythread_is_suspend_enabled());
if (exn_raised()) {
exn_rethrow();
}
M2nFrame * m2n = m2n_get_last_frame();
free_local_object_handles3(m2n->local_object_handles);
}
char * m2n_gen_pop_m2n(char * buf, bool handles, unsigned num_callee_saves,
I_32 bytes_to_m2n_bottom, unsigned num_preserve_ret) {
assert (num_preserve_ret <= 2);
assert(LcgEM64TContext::GR_SIZE == 8);
if (num_preserve_ret > 0) {
// Save return value
// NOTE: don't break stack allignment by pushing only one register.
buf = push(buf, rax_opnd, size_64);
buf = push(buf, rdx_opnd, size_64);
}
if (handles) {
// There are handles located on the stack
buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_pop_local_handles), size_64);
} else {
buf = mov(buf, rax_opnd, Imm_Opnd(size_64, (uint64)m2n_free_local_handles), size_64);
}
// NOTE: the following should be true before the call ($rsp % 8 == 0 && $rsp % 16 != 0)!
// Call m2n_pop_local_handles or m2n_free_local_handles
#ifdef _WIN64
buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(-SHADOW));
#endif
buf = call(buf, rax_opnd, size_64);
#ifdef _WIN64
buf = alu(buf, add_opc, rsp_opnd, Imm_Opnd(SHADOW));
#endif
if (num_preserve_ret > 0) {
// Restore return value
buf = pop(buf, rdx_opnd, size_64);
buf = pop(buf, rax_opnd, size_64);
}
// pop prev_m2nf
buf = mov(buf, r10_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64);
bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE;
// pop p_lm2nf
buf = mov(buf, r11_opnd, M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom), size_64);
bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE;
buf = mov(buf, M_Base_Opnd(r11_reg, 0), r10_opnd, size_64);
// skip local_object_handles, method, current_frame_type, pop_regs
bytes_to_m2n_bottom += 4 * LcgEM64TContext::GR_SIZE;
// restore part of callee-saves registers
for (int i = LcgEM64TContext::MAX_GR_LOCALS - 1; i >= (int)num_callee_saves; i--) {
buf = mov(buf,
LcgEM64TContext::get_reg_from_map(LcgEM64TContext::GR_LOCALS_OFFSET + i),
M_Base_Opnd(rsp_reg, bytes_to_m2n_bottom),
size_64);
bytes_to_m2n_bottom += LcgEM64TContext::GR_SIZE;
}
return buf;
}//m2n_gen_pop_m2n
// returns pointer to the registers used for jvmti PopFrame
Registers* get_pop_frame_registers(M2nFrame* m2nf) {
return m2nf->pop_regs;
}
// sets pointer to the registers used for jvmti PopFrame
void set_pop_frame_registers(M2nFrame* m2nf, Registers* regs) {
m2nf->pop_regs = regs;
}