blob: b56769698c0134c407964d5b376be5b3b2e6d15b [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.
*/
/* MONITOR ENTER RUNTIME SUPPORT */
#define LOG_DOMAIN "vm.helpers"
#include "cxxlog.h"
#include "environment.h"
#include "open/hythread_ext.h"
#include "jthread.h"
#include "thread_helpers.h"
//#include "open/vm_util.h"
#include "encoder.h"
#include "nogc.h"
#include "compile.h"
#include "exceptions_jit.h"
#include "lil.h"
#include "lil_code_generator.h"
#include "m2n_internal.h"
#include "object_handles.h"
#include "Class.h"
#include "jit_runtime_support.h"
#include "internal_jit_intf.h"
#include "mon_enter_exit.h"
#include "exceptions.h"
#include "exceptions_jit.h"
#include "dump.h"
#include "vm_stats.h"
// Linix x86-64 fast helpers
char * gen_convert_managed_to_unmanaged_null_em64t(char * ss,
const R_Opnd & input_param1);
#define INPUT_ARG_OFFSET 8
// Helper for monenter intstruction
static char * gen_restore_monitor_enter(char *ss, char *patch_addr_null_arg)
{
// Obtain lockword offset for the given object
const unsigned header_offset = ManagedObject::header_offset();
signed offset;
assert(header_offset);
#ifdef VM_STATS
// uint64* incr = &(VM_Statistics::get_vm_stats().num_monitor_enter);
// ss = inc(ss, M_Opnd((int64)incr));
#endif
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// check on the null
ss = test(ss, rdi_opnd, rdi_opnd);
ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0));
char *backpatch_address__null_pointer = ((char *)ss) - 1;
#endif
// skip fast path if can_generate_monitor_events capability
// was requested, so all TI events will be generated
if (!VM_Global_State::loader_env->TI->get_global_capability(
DebugUtilsTI::TI_GC_ENABLE_MONITOR_EVENTS)) {
// Fast path
ss = push(ss, rdi_opnd);
ss = alu(ss, add_opc, rdi_opnd, Imm_Opnd(header_offset)); // pop parameters
ss = gen_monitorenter_fast_path_helper(ss, rdi_opnd);
ss = pop(ss, rdi_opnd);
ss = test(ss, rax_opnd, rax_opnd);
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0));
char *backpatch_address__fast_monitor_failed = ((char *)ss) - 1;
ss = ret(ss);
offset = (int64)ss - (int64)backpatch_address__fast_monitor_failed - 1;
*backpatch_address__fast_monitor_failed = (char)offset;
}
// Slow path: happens when the monitor is busy (contention case)
ss = gen_setup_j2n_frame(ss);
ss = gen_monitorenter_slow_path_helper(ss, rdi_opnd);
ss = gen_pop_j2n_frame(ss);
ss = ret(ss);
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// Handle NPE here
int64 npe_offset = (int64)ss - (int64)backpatch_address__null_pointer - 1;
*backpatch_address__null_pointer = (char)npe_offset;
if (patch_addr_null_arg != NULL) {
npe_offset = (int64)ss - (int64)patch_addr_null_arg - 1;
*patch_addr_null_arg = (char)npe_offset;
}
// Object is null so throw a null pointer exception
ss = jump(ss, (char*)exn_get_rth_throw_null_pointer());
#endif
return ss;
} //gen_restore_monitor_enter
void * getaddress__vm_monitor_enter_naked()
{
static void *addr = NULL;
if (addr != NULL) {
return addr;
}
const int stub_size = 192;
char *stub = (char *)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_MAX/2, CAA_Allocate);
#ifdef _DEBUG
memset(stub, 0xcc /*int 3*/, stub_size);
#endif
char *ss = stub;
#ifdef VM_STATS
int * value = VM_Statistics::get_vm_stats().rt_function_calls.lookup_or_add((void*)VM_RT_MONITOR_ENTER, 0, NULL);
ss = mov(ss, rax_opnd, Imm_Opnd(size_64, (int64)value));
ss = inc(ss, M_Base_Opnd(rax_reg, 0));
#endif
ss = gen_restore_monitor_enter(ss, /*patch_addr_null_arg*/ NULL);
addr = stub;
assert((ss - stub) < stub_size);
compile_add_dynamic_generated_code_chunk("vm_monitor_enter_naked", false, stub, stub_size);
// Put TI support here
DUMP_STUB(stub, "getaddress__vm_monitor_enter_naked", ss - stub);
return addr;
}
static char * gen_restore_monitor_exit(char *ss, char *patch_addr_null_arg)
{
const unsigned header_offset = ManagedObject::header_offset();
#ifdef VM_STATS
// uint64* incr = &(VM_Statistics::get_vm_stats().num_monitor_exit);
// ss = inc(ss, M_Opnd((int64)incr));
#endif
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// check on teh null
ss = test(ss, rdi_opnd, rdi_opnd);
ss = branch8(ss, Condition_Z, Imm_Opnd(size_8, 0));
char *backpatch_address__null_pointer = ((char *)ss) - 1;
#endif
// skip fast path if can_generate_monitor_events capability
// was requested, so all TI events will be generated
if (!VM_Global_State::loader_env->TI->get_global_capability(
DebugUtilsTI::TI_GC_ENABLE_MONITOR_EVENTS)) {
// Fast path
ss = alu(ss, add_opc, rdi_opnd, Imm_Opnd(header_offset));
ss = gen_monitor_exit_helper(ss, rdi_opnd);
} else {
// Slow path
ss = gen_setup_j2n_frame(ss);
ss = call(ss, (char *)oh_convert_to_local_handle);
ss = gen_monitorexit_slow_path_helper(ss, rax_opnd);
ss = gen_pop_j2n_frame(ss);
}
ss = test(ss, rax_opnd, rax_opnd);
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0));
char *backpatch_address__fast_monitor_failed = ((char *)ss) - 1;
ss = ret(ss);
POINTER_SIZE_SINT offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)backpatch_address__fast_monitor_failed - 1;
*backpatch_address__fast_monitor_failed = (char)offset;
// Monitor illegal state happend
ss = jump(ss, (char*)exn_get_rth_throw_illegal_state_exception());
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
//NPE
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)backpatch_address__null_pointer - 1;
*backpatch_address__null_pointer = (char)offset;
if (patch_addr_null_arg != NULL) {
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)patch_addr_null_arg - 1;
*patch_addr_null_arg = (char)offset;
}
// Object is null so throw a null pointer exception
ss = jump(ss, (char*)exn_get_rth_throw_null_pointer());
#endif
return ss;
} //gen_restore_monitor_exit
void * getaddress__vm_monitor_exit_naked()
{
static void *addr = NULL;
if (addr != NULL) {
return addr;
}
const int stub_size = 144;
char *stub = (char *)malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_MAX/2, CAA_Allocate);
char *ss = stub;
#ifdef VM_STATS
int * value = VM_Statistics::get_vm_stats().rt_function_calls.lookup_or_add((void*)VM_RT_MONITOR_EXIT, 0, NULL);
ss = mov(ss, rax_opnd, Imm_Opnd(size_64, (int64)value));
ss = inc(ss, M_Base_Opnd(rax_reg, 0));
#endif
ss = gen_convert_managed_to_unmanaged_null_em64t((Emitter_Handle)ss, rdi_opnd);
ss = gen_restore_monitor_exit(ss, /*patch_addr_null_arg*/ NULL);
addr = stub;
assert((ss - stub) < stub_size);
compile_add_dynamic_generated_code_chunk("vm_monitor_exit_naked", false, stub, stub_size);
if (jvmti_should_report_event(JVMTI_EVENT_DYNAMIC_CODE_GENERATED)) {
jvmti_send_dynamic_code_generated_event("vm_monitor_exit_naked", stub, stub_size);
}
DUMP_STUB(stub, "getaddress__vm_monitor_exit_naked", ss - stub);
return addr;
} //getaddress__vm_monitor_exit_naked
// Windows x86-64 helpers
static LilCodeStub * rth_get_lil_monitor_enter_generic(LilCodeStub * cs) {
if(VM_Global_State::loader_env->TI->isEnabled() &&
VM_Global_State::loader_env->TI->get_global_capability(
DebugUtilsTI::TI_GC_ENABLE_MONITOR_EVENTS) ) {
return lil_parse_onto_end(cs,
"push_m2n 0, 0;"
"out platform:ref:void;"
"o0 = l0;"
"call %0i;"
"pop_m2n;"
"ret;",
vm_monitor_enter);
} else {
return lil_parse_onto_end(cs,
"out platform:ref:g4;"
"o0 = l0;"
"call %0i;"
"jc r!=%1i,slow_path;"
"ret;"
":slow_path;"
"push_m2n 0, 0;"
"out platform:ref:void;"
"o0 = l0;"
"call %2i;"
"pop_m2n;"
"ret;",
vm_monitor_try_enter,
(POINTER_SIZE_INT)TM_ERROR_NONE,
vm_monitor_enter);
}
}
NativeCodePtr rth_get_lil_monitor_enter() {
static NativeCodePtr addr = NULL;
if (addr != NULL) {
return addr;
}
LilCodeStub * cs = lil_parse_code_stub("entry 0:stdcall:ref:void;");
#ifdef VM_STATS
// int * value = VM_Statistics::get_vm_stats().rt_function_calls.lookup_or_add((void*)VM_RT_MONITOR_ENTER, 0, NULL);
// cs = lil_parse_onto_end(cs, "inc [%0i:pint];", value);
// assert(cs);
#endif
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// check if object is null
cs = lil_parse_onto_end(cs,
"jc i0 = %0i:ref, throw_null_pointer;",
(ManagedObject *) VM_Global_State::loader_env->managed_null
);
assert(cs);
#endif
cs = lil_parse_onto_end(cs,
"locals 1;"
"l0 = i0;"
);
assert(cs);
// append generic monitor enter code
cs = rth_get_lil_monitor_enter_generic(cs);
assert(cs);
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// throw NullPointerException
cs = lil_parse_onto_end(cs,
":throw_null_pointer;"
"out stdcall::void;"
"call.noret %0i;",
lil_npc_to_fp(exn_get_rth_throw_null_pointer())
);
assert(cs && lil_is_valid(cs));
#endif
addr = LilCodeGenerator::get_platform()->compile(cs);
DUMP_STUB((char *)addr, "monitor_enter", lil_cs_get_code_size(cs));
lil_free_code_stub(cs);
return addr;
}
/* MONITOR EXIT RUNTIME SUPPORT */
static LilCodeStub * rth_get_lil_monitor_exit_generic(LilCodeStub * cs) {
if(VM_Global_State::loader_env->TI->isEnabled() &&
VM_Global_State::loader_env->TI->get_global_capability(
DebugUtilsTI::TI_GC_ENABLE_MONITOR_EVENTS) ) {
return lil_parse_onto_end(cs,
"locals 1;"
"l0 = o0;"
"push_m2n 0, %0i;"
"out platform:ref:void;"
"o0 = l0;"
"call %1i;"
"pop_m2n;"
"ret;",
(POINTER_SIZE_INT)FRAME_NON_UNWINDABLE,
vm_monitor_exit);
} else {
return lil_parse_onto_end(cs,
"call %0i;"
"jc r!=%1i, illegal_monitor;"
"ret;"
":illegal_monitor;"
"out stdcall::void;"
"call.noret %2i;",
vm_monitor_try_exit,
(POINTER_SIZE_INT)TM_ERROR_NONE,
lil_npc_to_fp(exn_get_rth_throw_illegal_monitor_state()));
}
}
NativeCodePtr rth_get_lil_monitor_exit() {
static NativeCodePtr addr = NULL;
if (addr != NULL) {
return addr;
}
LilCodeStub * cs = lil_parse_code_stub("entry 0:stdcall:ref:void;");
#ifdef VM_STATS
// int * value = VM_Statistics::get_vm_stats().rt_function_calls.lookup_or_add((void*)VM_RT_MONITOR_EXIT, 0, NULL);
// cs = lil_parse_onto_end(cs, "inc [%0i:pint];", value);
// assert(cs);
#endif
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// check if object is null
cs = lil_parse_onto_end(cs,
"jc i0 = %0i:ref, throw_null_pointer;",
(ManagedObject *) VM_Global_State::loader_env->managed_null
);
assert(cs);
#endif
cs = lil_parse_onto_end(cs,
"in2out platform:g4;"
);
assert(cs);
// append generic monitor enter code
cs = rth_get_lil_monitor_exit_generic(cs);
assert(cs);
#ifdef _DEBUG_CHECK_NULL_//_DEBUG
// throw NullPointerException
cs = lil_parse_onto_end(cs,
":throw_null_pointer;"
"out stdcall::void;"
"call.noret %0i;",
lil_npc_to_fp(exn_get_rth_throw_null_pointer())
);
assert(cs && lil_is_valid(cs));
#endif
addr = LilCodeGenerator::get_platform()->compile(cs);
DUMP_STUB(addr, "monitor_exit", lil_cs_get_code_size(cs));
lil_free_code_stub(cs);
return addr;
}