blob: 4490ce5da735e9d5d579bcfc78d0fa661f0c7133 [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, Ivan Volosyuk
*/
#include "vtable.h"
#include "Class.h"
#include "environment.h"
#include "lil.h"
#include "native_overrides.h"
#include "nogc.h"
#include "object_generic.h"
#include "object_layout.h"
#include "open/types.h"
#include "open/vm_util.h"
#include "open/vm_ee.h"
#include "open/vm.h"
// *** This is for readInternal override
#include "jni_utils.h"
#include "vm_arrays.h"
#include "open/vm_util.h"
#include <fcntl.h>
#ifdef PLATFORM_NT
#include <io.h>
#include <direct.h>
#elif defined(PLATFORM_POSIX)
#include <unistd.h>
#endif
#include <errno.h>
// *** This is for currenttimemillis override
#include "platform_core_natives.h"
// *** This is for the newInstance override
#include "exceptions.h"
#include "dump.h"
int readinternal_override_lil(JNIEnv *jenv,
Java_java_io_FileInputStream * UNREF pThis,
int fd,
Vector_Handle pArrayOfByte,
int offset,
int len)
{
// Read into byte array "count" bytes starting at index "offset"
// Check if we have been passed a null pointer
if (!pArrayOfByte) {
throw_exception_from_jni(jenv, "java/lang/NullPointerException",
"Null pointer passed to read()");
return 0;
}
int array_len = jenv->GetArrayLength((jbyteArray)(&pArrayOfByte));
if (len < 0 || offset < 0 || offset + len > array_len) {
throw_exception_from_jni(jenv, "java/lang/ArrayIndexOutOfBoundsException",
"Index check failed");
return 0;
}
// Get the array reference.
void *bufp = get_vector_element_address_int8(pArrayOfByte, offset);
int ret = read (fd, bufp, len);
if (ret == 0) {
return -1;
}
// No "interrupted" check for now.
if (ret == -1) {
// Throw IOException since read failed somehow.. use strerror(errno)
throw_exception_from_jni(jenv, "java/io/IOException", 0);
return 0;
}
assert(ret >= 0);
return ret;
} // readinternal_override_lil
LilCodeStub* nso_readinternal(LilCodeStub* cs, Method_Handle)
{
cs = lil_parse_onto_end(cs, "out platform:pint,ref,g4,ref,g4,g4:g4; o0=%0i;",
p_TLS_vmthread->jni_env);
cs = lil_parse_onto_end(cs, "o1=i0; o2=i1; o3=i2; o4=i3; o5=i4;");
cs = lil_parse_onto_end(cs, "call %0i; ret;", readinternal_override_lil);
assert(cs);
return cs;
} // nso_readinternal
LilCodeStub* nso_system_currenttimemillis(LilCodeStub* cs, Method_Handle)
{
cs = lil_parse_onto_end(cs, "out platform::g8;");
cs = lil_parse_onto_end(cs, "call %0i; ret;", get_current_time);
assert(cs);
return cs;
} // nso_system_currenttimemillis
LilCodeStub* nso_newinstance(LilCodeStub* cs, Method_Handle)
{
Global_Env *env = VM_Global_State::loader_env;
assert(env->vm_class_offset != 0);
unsigned current_offset = 1;
unsigned limit_offset = 1;
if(gc_supports_frontier_allocation(&current_offset, &limit_offset)){
cs = lil_parse_onto_end(cs, "locals 4;");
// Get Class_Handle from java.lang.Class object
cs = lil_parse_onto_end(cs, "ld l0,[i0+%0i:pint];", (POINTER_SIZE_INT)env->vm_class_offset);
// Determine if this class supports fast allocation
size_t offset_is_fast_allocation_possible = env->Void_Class->get_offset_of_fast_allocation_flag();
cs = lil_parse_onto_end(cs, "ld l1,[l0+%0i:g1];", offset_is_fast_allocation_possible);
cs = lil_parse_onto_end(cs, "jc l1=0,fallback;");
// Class supports fast allocation, now use frontier allocation technique
size_t offset_gc_local = (U_8*)&(p_TLS_vmthread->_gc_private_information) - (U_8*)p_TLS_vmthread;
size_t offset_allocation_handle = env->Void_Class->get_offset_of_allocation_handle();
size_t offset_instance_data_size = env->Void_Class->get_offset_of_instance_data_size();
current_offset += (unsigned) offset_gc_local;
limit_offset += (unsigned) offset_gc_local;
// Get frontier into r, limit into l2, instance size into l3
cs = lil_parse_onto_end(cs, "l1=ts;");
cs = lil_parse_onto_end(cs, "ld r,[l1+%0i:ref];", (POINTER_SIZE_INT)current_offset);
cs = lil_parse_onto_end(cs, "ld l2,[l1+%0i:pint];", (POINTER_SIZE_INT)limit_offset);
cs = lil_parse_onto_end(cs, "ld l3,[l0+%0i:g4];", offset_instance_data_size);
// Compute new frontier
cs = lil_parse_onto_end(cs, "l3=l3:pint+r:pint;");
// Is it within limit?
cs = lil_parse_onto_end(cs, "jc l2<l3,fallback;");
// Yes, store new frontier, initialize vtable
cs = lil_parse_onto_end(cs, "st [l1+%0i:pint],l3;", (POINTER_SIZE_INT)current_offset);
cs = lil_parse_onto_end(cs, "ld l3,[l0+%0i:pint];", offset_allocation_handle);
cs = lil_parse_onto_end(cs, "l2=r; st [l2+0:pint],l3;");
// Done
cs = lil_parse_onto_end(cs, "ret; :fallback;");
}
assert(cs);
return cs;
} // nso_newinstance
static ManagedObject* get_class_ptr(ManagedObject* obj)
{
return *(obj->vt()->clss->get_class_handle());
}
LilCodeStub* nso_get_class(LilCodeStub* cs, Method_Handle)
{
cs = lil_parse_onto_end(cs, "in2out platform:ref; call %0i; jc r=0,null; ret; :null;", get_class_ptr);
assert(cs);
return cs;
}
LilCodeStub* nso_array_copy(LilCodeStub* cs, Method_Handle)
{
ArrayCopyResult (*p_array_copy)(ManagedObject* src, I_32 src_off, ManagedObject* dst, I_32 dst_off, I_32 count);
p_array_copy = array_copy;
cs = lil_parse_onto_end(cs,
"in2out platform:g4;"
"call %0i;"
"jc r!=%1i,bad;"
"ret;"
":bad;",
p_array_copy, (POINTER_SIZE_INT)ACR_Okay);
return cs;
}
// The following override is only for IA32
#ifdef _IA32_
#include "encoder.h"
#define MOV_PAIR 8
static NativeCodePtr fast_array_copy()
{
static NativeCodePtr addr = NULL;
if (addr) return addr;
unsigned stub_size = 0x87;
char* stub = (char *) malloc_fixed_code_for_jit(stub_size, DEFAULT_CODE_ALIGNMENT, CODE_BLOCK_HEAT_DEFAULT, CAA_Allocate);
char* s = stub;
s = push(s, esi_opnd);
s = push(s, edi_opnd);
// Stack layout:
// +28 address of first src element to copy from
// +24 src offset (not needed)
// +20 address of first dst element to copy to
// +16 dst offset (not needed)
// +12 length
// +08 return ip
// +04 saved edi
// +00 saved esi
// Fetch src, dst, and length into esi, edi, and ecx
M_Base_Opnd m1(esp_reg, 28);
M_Base_Opnd m2(esp_reg, 20);
M_Base_Opnd m3(esp_reg, 12);
s = mov(s, esi_opnd, m1);
s = mov(s, edi_opnd, m2);
s = mov(s, ecx_opnd, m3);
s = mov(s, edx_opnd, ecx_opnd);
// Compute the number of words to copy, if none jump to code to copy last 2-bytes
// Note that ecx is never zero, the override ensures this
Imm_Opnd i1(1);
s = shift(s, shr_opc, ecx_opnd, i1);
Imm_Opnd i0(size_8, 0);
s = branch8(s, Condition_Z, i0);
char* patch1 = s-1;
// Is copy less than MOV_PAIR words?
Imm_Opnd imp(MOV_PAIR);
s = alu(s, cmp_opc, ecx_opnd, imp);
s = branch8(s, Condition_A, i0);
char* patch2 = s-1;
// Yes, jump into a sequence of move words
s = mov(s, eax_opnd, imp);
s = alu(s, sub_opc, eax_opnd, ecx_opnd);
s = alu(s, add_opc, eax_opnd, eax_opnd);
s = alu(s, add_opc, eax_opnd, eax_opnd);
s = alu(s, add_opc, eax_opnd, eax_opnd);
Imm_Opnd i_seq_start((int)POINTER_SIZE_INT(s)+7);
s = alu(s, add_opc, eax_opnd, i_seq_start) ;
s = jump(s, eax_opnd) ;
for(unsigned j = 0 ; j<MOV_PAIR ; j++) {
unsigned offset = (MOV_PAIR-j-1)*4;
M_Base_Opnd m4(esi_reg, offset);
M_Base_Opnd m5(edi_reg, offset);
s = mov(s, eax_opnd, m4);
s = mov(s, m5, eax_opnd);
if (j < MOV_PAIR - 1)
s = mov(s, ebp_opnd, ebp_opnd); // 2-byte nop sequence
}
// Adjust esi and edi
M_Index_Opnd m6(esi_reg, ecx_reg, 0, 2);
M_Index_Opnd m7(edi_reg, ecx_reg, 0, 2);
s = lea(s, esi_opnd, m6);
s = lea(s, edi_opnd, m7);
s = jump8(s, i0);
char* patch3 = s-1;
// No, do a rep MOVSD
assert(s-patch2-1 < 128);
*patch2 = (char)(s-patch2-1);
*s++ = (char)0xF3 ;
*s++ = (char)0xA5 ;
// In either case, now determine if there is an additional 2-bytes to copy
assert(s-patch3-1 < 128);
*patch3 = (char)(s-patch3-1);
s = test(s, edx_opnd, i1);
s = branch8(s, Condition_Z, i0);
char* patch4 = s-1;
// Do last 2-byte copy
assert(s-patch1-1 < 128);
*patch1 = (char)(s-patch1-1);
M_Base_Opnd m8(esi_reg, 0);
M_Base_Opnd m9(edi_reg, 0);
s = mov(s, eax_opnd, m8, size_16) ;
s = mov(s, m9, eax_opnd, size_16) ;
// Done
assert(s-patch4-1 < 128);
*patch4 = (char)(s-patch4-1);
s = pop(s, edi_opnd);
s = pop(s, esi_opnd);
Imm_Opnd i20(20);
s = ret(s, i20);
addr = (NativeCodePtr)stub;
assert(stub_size >= (unsigned)(s-stub));
DUMP_STUB(stub, "stub.fast_array_copy", s - stub);
return addr;
}
LilCodeStub* nso_char_array_copy(LilCodeStub* cs, Method_Handle)
{
Global_Env *env = VM_Global_State::loader_env;
POINTER_SIZE_INT length_offset = (POINTER_SIZE_INT)vector_length_offset();
POINTER_SIZE_INT body_offset =
(POINTER_SIZE_INT)vector_first_element_offset(VM_DATA_TYPE_CHAR);
NativeCodePtr custom_stub = fast_array_copy();
// Check all array copy error conditions and that both arrays are [C
cs = lil_parse_onto_end(cs,
"jc i4=0,ret;" // if length==0 return
"locals 2;"
"l0=i0;"
"jc l0=%0i,slowpath;" // src nonnull
"l1=i2;"
"jc l1=%1i,slowpath;" // dst nonnull
"ld l0,[l0+%2i:pint];"
"jc l0!=%3i,slowpath;" // src vtable==[C vtable
"ld l1,[l1+%4i:pint];"
"jc l0!=l1,slowpath;" // src vtable==dst vtable
"jc i4<0:g4,slowpath;" // length>0
"l1 = i1;"
"jc l1<0:g4,slowpath;" // src offset>=0
"ld l0,[i0+%5i:pint];"
"l1 = l1+i4;"
"jc l0:g4<l1,slowpath;" // src offset + length <= src length
"l1 = i3;"
"jc l1<0:g4,slowpath;" // dst offset>=0
"ld l0,[i2+%6i:pint];"
"l1 = l1+i4;"
"jc l0:g4<l1,slowpath;", // dst offset + length <= dst length
VM_Global_State::loader_env->managed_null,
VM_Global_State::loader_env->managed_null,
(POINTER_SIZE_INT)object_get_vtable_offset(),
env->ArrayOfChar_Class->get_vtable(),
(POINTER_SIZE_INT)object_get_vtable_offset(),
length_offset, length_offset);
assert(cs);
// At this point array copy will happen, now decide how to do it
cs = lil_parse_onto_end(cs,
"l0 = i1:pint<<1;" // l0 = first element of src to copy
"l0 = l0+i0:pint;"
"l0 = l0+%0i;"
"l1 = i0;"
"jc l1!=i2,nonoverlap;"
"l1 = i3:pint<<1;" // l1 = first element of dst to copy to
"l1 = l1+i2:pint;"
"l1 = l1+%1i;"
"out platform:pint,pint,pint:void;" // copy might overlap, call memmove
"o0 = l1;"
"o1 = l0;"
"o2 = i4:pint<<1;"
"locals 0;"
"call %2i;"
":ret;"
"ret;"
":nonoverlap;" // copy will not overlap, call custom stub
"i0 = l0:ref;"
"l1 = i3:pint<<1;" // l1 = first element of dst to copy to
"l1 = l1+i2:pint;"
"l1 = l1+%3i;"
"i2 = l1:ref;"
"tailcall %4i;"
":slowpath;" // slowpath, fall through to JNI stub, locals 0 is to satisfy LIL validity checker
"locals 0;",
body_offset, body_offset, memmove, body_offset, custom_stub);
assert(cs);
return cs;
}
#endif // _IA_32
// NSO table item
typedef struct
{
NativeStubOverride fun;
const char* class_name;
const char* method_name;
const char* method_desc;
} NSOLocalItem;
// Local NSO table for filling up env-local lookup table
NSOLocalItem local_NSO_table[] =
{
#ifdef _IA32_
{nso_char_array_copy,
#else
{nso_array_copy,
#endif
"java/lang/System", "arraycopy", "Ljava/lang/Object;ILjava/lang/Object;II)V"
},
{nso_get_class,
"java/lang/Object", "getClass", "()Ljava/lang/Class;"
},
{nso_readinternal,
"java/io/FileInputStream", "readInternal", "(I[BII)I"
},
{nso_system_currenttimemillis,
"java/lang/System", "currentTimeMillis", "()J"
},
{nso_newinstance,
"java/lang/Class", "newInstance", "()Ljava/lang/Object;"
},
};
// NSO table item
struct NSOTableItem
{
NativeStubOverride fun;
const String* class_name;
const String* method_name;
const String* method_desc;
};
// Initializes local NSO table for given environment
NSOTableItem* nso_init_lookup_table(String_Pool* pstrpool)
{
assert(pstrpool);
size_t item_count = sizeof(local_NSO_table)/sizeof(local_NSO_table[0]);
NSOTableItem* NSOTable = (NSOTableItem*)STD_MALLOC(sizeof(NSOTableItem)*(item_count+1));
assert(NSOTable != NULL);
int i;
for (i = 0; i < int(item_count); i++)
{
NSOTable[i].fun = local_NSO_table->fun;
NSOTable[i].class_name =
pstrpool->lookup(local_NSO_table->class_name);
NSOTable[i].method_name =
pstrpool->lookup(local_NSO_table->method_name);
NSOTable[i].method_desc =
pstrpool->lookup(local_NSO_table->method_desc);
}
NSOTable[i].fun = NULL;
return NSOTable;
}
// Frees memory occupied by NSO table
void nso_clear_lookup_table(NSOTableItem* NSOTable)
{
assert(NSOTable);
STD_FREE(NSOTable);
}
// Look for NSO method
NativeStubOverride nso_find_method_override(const Global_Env* ge,
const String* class_name, const String* name, const String* desc)
{
NSOTableItem* envNSOTable = ge->nsoTable;
for (unsigned i = 0; envNSOTable[i].fun; i++)
{
NSOTableItem* pitem = &envNSOTable[i];
if (pitem->class_name == class_name &&
pitem->method_name == name &&
pitem->method_desc == desc)
{
return pitem->fun;
}
}
return NULL;
}