blob: f6cab64f6d8634d833ef89e9537e87f90594edb4 [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 Artem Aliev
*/
/**
* @file thread_helpers.cpp
* @brief Set of VM helpers
*
* This file contains the set of "VM helpers" which help to optimize monitors performance
* in the code generated by JIT compiler. Typically, these functions will be called by JIT,
* but VM also could also use them with care.
*/
#include <open/hythread_ext.h>
#include <thread_helpers.h>
#include "jthread.h"
#include "object_handles.h"
#include "port_malloc.h"
#include "m2n.h"
#include <assert.h>
/**
* Generates tmn_self() call.
* The code should not contains safepoint.
* The code uses and doesn't restore eax register.
*
* @return tm_self() in eax register
*/
char* gen_hythread_self_helper(char *ss) {
#ifdef HYTHREAD_FAST_TLS
// offset isn't too large so we can use 32-bit value
unsigned offset = hythread_get_hythread_offset_in_tls();
// fs register uses for tls acces on linux x86-32
//ss = mov(ss, rdx_opnd, M_Base_Opnd(fs_reg, 0x00));
*ss++ = (char)0x64;
*ss++ = (char)0x48;
*ss++ = (char)0x8b;
*ss++ = (char)0x14;
*ss++ = (char)0x25;
*ss++ = (char)0x00;
*ss++ = (char)0x00;
*ss++ = (char)0x00;
*ss++ = (char)0x00;
ss = mov(ss, rax_opnd, M_Base_Opnd(rdx_reg, offset));
#else
ss = call(ss, (char *)hythread_self);
#endif
return ss;
}
/**
* Generates fast path of monitor enter
* the code should not contains safepoint.
*
* @param[in] ss buffer to put the assembly code to
* @param[in] input_param1 register which should point to the object lockword.
* If input_param1 == ecx it reduces one register mov.
* the code use and do not restore ecx, edx, eax registers
*
* @return 0 if success in eax register
*/
char* gen_monitorenter_fast_path_helper(char *ss, const R_Opnd & input_param1) {
if (&input_param1 != &rdi_opnd) {
ss = mov(ss, rdi_opnd, input_param1);
}
#ifdef ASM_MONITOR_HELPER
//get self_id
ss = push(ss, rdi_opnd);
ss = gen_hythread_self_helper(ss);
ss = pop(ss, rdi_opnd);
ss = mov(ss, rdx_opnd, M_Base_Opnd(rax_reg,
hythread_get_thread_id_offset())); //mov rdx,dword [rax+off]
ss = mov(ss, rax_opnd, M_Base_Opnd(rdi_reg, 2), size_16); // mov ax,word[ecx+2]
ss = alu(ss, cmp_opc, rdx_opnd, rax_opnd, size_16); // cmp dx,ax
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz check_zero
char *check_zero = ((char *)ss) - 1;
//; ax==dx it's safe to do inc
ss = mov(ss, rax_opnd, M_Base_Opnd(rdi_reg, 1), size_8); // mov al, byte[rdi+1]
//rec_inc:
ss = alu(ss, add_opc, rax_opnd,
Imm_Opnd(size_8, 0x8), size_8); // add al,0x8
ss = branch8(ss, Condition_C, Imm_Opnd(size_8, 0)); // jc failed
char *failed1 = ((char *)ss) - 1;
ss = mov(ss, M_Base_Opnd(rdi_reg, 1), rax_opnd, size_8); // mov byte[ecx+1],al
ss = alu(ss, add_opc, rsp_opnd, Imm_Opnd(size_8, 0x8)); // add rsp,0x8
ss = ret(ss); // ret
//check_zero:
POINTER_SIZE_SINT offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)check_zero - 1;
*check_zero = (char)offset;
ss = test(ss, rax_opnd, rax_opnd, size_16); // test ax,ax
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz failed
char *failed2 = ((char *)ss) - 1;
ss = prefix(ss, lock_prefix); //; here ax==0.
ss = cmpxchg(ss, M_Base_Opnd(rdi_reg, 2), rdx_opnd, size_16);// lock cmpxchg16 [ecx+2],dx
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz failed
char *failed3 = ((char *)ss) - 1;
#ifdef LOCK_RESERVATION
ss = mov(ss, rax_opnd, M_Base_Opnd(rdi_reg, 1), size_8); // mov al, byte[ecx+1]
ss = test(ss, rax_opnd, Imm_Opnd(size_8, 0x4), size_8); // test al,0x4
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz finish
char *finish = ((char *)ss) - 1;
ss = alu(ss, add_opc, rax_opnd, Imm_Opnd(size_8, 8), size_8);// add al,0x8
ss = mov(ss, M_Base_Opnd(rdi_reg, 1), rax_opnd, size_8); // mov byte[ecx+1],al
//finish:
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)finish - 1;
*finish = (char)offset;
#endif
ss = alu(ss, add_opc, rsp_opnd, Imm_Opnd(size_8, 0x8)); // add rsp,0x8
ss = ret(ss); // ret
//failed:
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)failed1 - 1;
*failed1 = (char)offset;
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)failed2 - 1;
*failed2 = (char)offset;
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)failed3 - 1;
*failed3 = (char)offset;
#endif //ASM_MONITOR_HELPER
// the second attempt to lock monitor
ss = call(ss, (char *)hythread_thin_monitor_try_enter);
return ss;
}
static IDATA rt_jthread_monitor_enter(ManagedObject* monitor) {
const unsigned handles_size = (unsigned)(sizeof(ObjectHandlesNew)+sizeof(ManagedObject*)*4);
ObjectHandlesNew* handels = (ObjectHandlesNew *)STD_ALLOCA(handles_size);
handels->capacity = 4;
handels->size = 0;
handels->next = NULL;
m2n_set_local_handles(m2n_get_last_frame(), (ObjectHandles *) handels);
ObjectHandle monitorJavaObj = oh_allocate_local_handle();
monitorJavaObj->object = monitor;
IDATA result = jthread_monitor_enter(monitorJavaObj);
free_local_object_handles2(m2n_get_local_handles(m2n_get_last_frame()));
m2n_set_local_handles(m2n_get_last_frame(), NULL);
return result;
}
/**
* Generates slow path of monitor enter.
* This code could block on monitor and contains safepoint.
* The appropriate m2n frame should be generated and
*
* @param[in] ss buffer to put the assembly code to
* @param[in] input_param1 register should point to the jobject(handle)
* If input_param1 == eax it reduces one register mov.
* the code use and do not restore ecx, edx, eax registers
* @return 0 if success in eax register
*/
char* gen_monitorenter_slow_path_helper(char *ss, const R_Opnd & input_param1) {
if (&input_param1 != &rdi_opnd) {
ss = mov(ss, rdi_opnd, input_param1);
}
ss = call(ss, (char *)rt_jthread_monitor_enter);
return ss;
}
/**
* Generates monitor exit.
* The code should not contain safepoints.
*
* @param[in] ss buffer to put the assembly code to
* @param[in] input_param1 register should point to the lockword in object header.
* If input_param1 == ecx it reduce one register mov.
* The code use and do not restore eax registers.
* @return 0 if success in eax register
*/
char* gen_monitor_exit_helper(char *ss, const R_Opnd & input_param1) {
if (&input_param1 != &rdi_opnd) {
ss = mov(ss, rdi_opnd, input_param1);
}
#ifdef ASM_MONITOR_HELPER
ss = mov(ss, rax_opnd, M_Base_Opnd(rdi_reg, 0)); // mov rax,dword[rdi]
ss = test(ss, rax_opnd, Imm_Opnd(0x80000000), size_32); // test rax,0x80000000
ss = branch8(ss, Condition_NZ, Imm_Opnd(size_8, 0)); // jnz fat
char *fat = ((char *)ss) - 1;
ss = mov(ss, rax_opnd, M_Base_Opnd(rdi_reg, 1), size_8); // mov al, byte[rdi+1]
ss = alu(ss, sub_opc, rax_opnd, Imm_Opnd(size_8,0x8),size_8);// sub al, 0x8
ss = branch8(ss, Condition_C, Imm_Opnd(size_8, 0)); // jc zero_rec
char *zero_rec = ((char *)ss) - 1;
ss = mov(ss, M_Base_Opnd(rdi_reg, 1), rax_opnd, size_8); // mov byte[rdi+1],al
ss = ret(ss); // ret
//zero_rec:
POINTER_SIZE_SINT offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)zero_rec - 1;
*zero_rec = (char)offset;
ss = mov(ss, M_Base_Opnd(rdi_reg, 2),
Imm_Opnd(size_16, 0), size_16); // mov word[rdi+2],0
ss = ret(ss); // ret
//fat:
offset = (POINTER_SIZE_SINT)ss - (POINTER_SIZE_SINT)fat - 1;
*fat = (char)offset;
#endif
ss = call(ss, (char *)hythread_thin_monitor_exit);
return ss;
}
/**
* Generates slow path of monitor exit.
* This code could block on monitor and contains safepoint.
* The appropriate m2n frame should be generated and
*
* @param[in] ss buffer to put the assembly code to
* @param[in] input_param1 register should point to the jobject(handle)
* If input_param1 == eax it reduces one register mov.
* the code use and do not restore ecx, edx, eax registers
* @return 0 if success in eax register
*/
char* gen_monitorexit_slow_path_helper(char *ss, const R_Opnd & input_param1) {
if (&input_param1 != &rdi_opnd) {
ss = mov(ss, rdi_opnd, input_param1);
}
ss = call(ss, (char *)jthread_monitor_exit);
return ss;
}
/**
* Generates fast accessor to the TLS for the given key.<br>
* Example:
* <pre><code>
* get_thread_ptr = get_tls_helper(vm_thread_block_key);
* ...
* self = get_thread_ptr();
* </code></pre>
*
* @param[in] key TLS key
* @return fast accessor to key, if one exist
*/
fast_tls_func* get_tls_helper(hythread_tls_key_t key) {
// return tm_self_tls->thread_local_storage[key];
unsigned key_offset =
(unsigned)(POINTER_SIZE_INT)&(((HyThread_public *) (0))->thread_local_storage[key]);
const int stub_size = 126;
char *stub = (char *)malloc(stub_size);
memset(stub, 0xcc /*int 3*/, stub_size);
char *ss = stub;
ss = gen_hythread_self_helper(ss);
ss = mov(ss, rax_opnd, M_Base_Opnd(rax_reg, key_offset));
ss = ret(ss, Imm_Opnd(0));
assert((ss - stub) < stub_size);
return (fast_tls_func*) stub;
}