blob: 4f8e5cee5793a4a679cc95ef3790fb7eb98ff58a [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
*/
//
// Functions needed to compile a method.
// For methods without the "native" attribute, we invoke a JIT, for those that
// have the "native" attribute, we locate the native function and generate the
// appropriate stub.
//
//MVM
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#define LOG_DOMAIN "vm.core"
#include "cxxlog.h"
#include "open/types.h"
#include "open/vm_type_access.h"
#include "open/vm_field_access.h"
#include "open/vm_method_access.h"
#include "open/vm_class_manipulation.h"
#include "Class.h"
#include "environment.h"
#include "m2n.h"
#include "../m2n_ipf_internal.h"
#include "exceptions.h"
#include "jit_intf.h"
#include "jit_intf_cpp.h"
#include "merced.h"
#include "jit_runtime_support.h"
#include "object_layout.h"
#include "nogc.h"
#include "open/gc.h"
#include "vm_threads.h"
#include "ini.h"
#define METHOD_NAME_BUF_SIZE 512
#include "vm_ipf.h"
#include "compile.h"
#include "Code_Emitter.h"
#include "lil.h"
#include "lil_code_generator.h"
#include "stub_code_utils.h"
#include "dump.h"
#include "vm_stats.h"
void emit_hashcode_override(Emitter_Handle eh, Method *method);
void emit_arraycopy_override(Emitter_Handle eh, Method *method);
void emit_system_currenttimemillis_override(Emitter_Handle eh, Method *method);
void emit_readinternal_override(Emitter_Handle eh, Method *method);
void flush_hw_cache(U_8* addr, size_t len)
{
for(unsigned int i = 0; i < len; i += 32) {
flush_cache_line((void*)&(addr[i]));
}
} //flush_hw_cache
/* BEGIN UTILITIES TO CONVERT BETWEEN MANAGED AND UNMANAGED NULLS */
// Convert a reference in register "reg", if null, from a managed null (heap_base) to an unmanaged one (NULL/0). Uses SCRATCH_GENERAL_REG.
void gen_convert_managed_to_unmanaged_null_ipf(Emitter_Handle emitter, unsigned reg) {
assert(reg != SCRATCH_GENERAL_REG);
assert(reg != SCRATCH_GENERAL_REG2); // because of the call to increment_stats_counter()
if (VM_Global_State::loader_env->compress_references) {
Merced_Code_Emitter *mce = (Merced_Code_Emitter *)emitter;
#ifdef VM_STATS
increment_stats_counter(*mce, &VM_Statistics::get_vm_stats().num_convert_null_m2u);
#endif
gen_compare_to_managed_null(*mce, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, reg, SCRATCH_GENERAL_REG);
mce->ipf_mov(reg, 0, SCRATCH_PRED_REG);
}
}
// Convert a reference in "reg", if null, from an unmanaged null (NULL/0) to an managed one (heap_base).
// If a null must be translated, will rewrite rewrite "reg".
void gen_convert_unmanaged_to_managed_null_ipf(Emitter_Handle emitter, unsigned reg)
{
assert(reg != SCRATCH_GENERAL_REG); // because of the call to increment_stats_counter()
assert(reg != SCRATCH_GENERAL_REG2); // because of the call to increment_stats_counter()
if (VM_Global_State::loader_env->compress_references) {
Merced_Code_Emitter *mce = (Merced_Code_Emitter *)emitter;
#ifdef VM_STATS
increment_stats_counter(*mce, &VM_Statistics::get_vm_stats().num_convert_null_u2m);
#endif
mce->ipf_cmp(icmp_eq, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, reg, 0);
emit_mov_imm_compactor(*mce, reg, (uint64)VM_Global_State::loader_env->heap_base, SCRATCH_PRED_REG);
}
} //gen_convert_unmanaged_to_managed_null_ipf
/* END UTILITIES TO CONVERT BETWEEN MANAGED AND UNMANAGED NULLS */
/* BEGIN SUPPORT FOR STUB OVERRIDE CODE SEQUENCES */
// Override for the RNI method java_lang_Class.newInstance(Java_java_lang_Class *clss).
static void emit_newinstance_override(Emitter_Handle eh, Method *method) {
Merced_Code_Emitter *mce = (Merced_Code_Emitter *)eh;
Merced_Code_Emitter &emitter = *mce;
// Emit code to examine the argument class (a java_lang_Class instance) and allocate an instance using fast inline code
// if the class is instantiatable (e.g., is not an array or primitive), no constructor must be invoked, and no GC is required.
Global_Env *env = VM_Global_State::loader_env;
unsigned current_offset = 1;
unsigned limit_offset = 1;
if (//env->use_inline_newinstance_sequence &&
gc_supports_frontier_allocation(&current_offset, &limit_offset)) {
// Get the struct Class* that corresponds to the java_lang_Class reference argument.
// (p1) g1 = in0 + env->vm_class_offset // address of the struct Class* field in a java_lang_Class instance
// (p1) g7 = [g1] // struct Class* corresponding to the argument java_lang_Class
assert(env->vm_class_offset != 0); // else VM bootstrapping isn't finished and this offset is unknown
emitter.ipf_adds(SCRATCH_GENERAL_REG, (int)(env->vm_class_offset), IN_REG0);
emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, SCRATCH_GENERAL_REG7, SCRATCH_GENERAL_REG);
// Check that the argument class is instantiatable. Note: we use Void_Class here but any class will do.
// g1 = g7 + offset_is_fast_allocation_possible // address of (clss->m_is_fast_allocation_possible)
// g2 = [g1] // clss->m_is_fast_allocation_possible
// p1 = (g2 != 0) // p1 = is clss->m_is_fast_allocation_possible?
size_t offset_is_fast_allocation_possible = env->Void_Class->get_offset_of_fast_allocation_flag();
emitter.ipf_adds(SCRATCH_GENERAL_REG, (int)offset_is_fast_allocation_possible, SCRATCH_GENERAL_REG7);
emitter.ipf_ld(int_mem_size_1, mem_ld_none, mem_none, SCRATCH_GENERAL_REG2, SCRATCH_GENERAL_REG);
emitter.ipf_cmp(icmp_ne, cmp_none, SCRATCH_PRED_REG, SCRATCH_PRED_REG2, 0, SCRATCH_GENERAL_REG2);
// The following instructions are executed only if p1 (i.e., if clss->m_is_fast_allocation_possible).
// This is a significantly modified version of the inline code in get_vm_rt_new_with_thread_pointer_compactor().
// Do the fast path without creating a frame. This sequence uses predicated instructions rather than branching,
// although I wonder if it has become too long for this to be effective.
//
// (p1) g1 = r4 + offset_gc_local // address of current
// (p1) g2 = r4 + offset_gc_local+sizeof(void*) // address of limit
// (p1) r8 = [g1] // current
// (p1) g3 = [g2] // limit
//
// (p1) g5 = g7 + offset_instance_data_size // address of the class's size
// (p1) g6 = [g5] // size
// (p1) g4 = r8 + g6 // current+size
// (p1) p3 = (g4 <= g3) // p3 = is (current+size <= limit)?
//
// (p3) g2 = g7 + offset_allocation_handle // address of clss->allocation_handle (a compressed/uncompressed vtable pointer)
// (p3) g3 = [g2] // vtable (which is compressed if vm_vtables_are_compressed())
// (p3) [r8] = g3 // write vtable to the newly allocated object
// (p3) [g1] = g4 // set new current value
// (p3) return
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;
emitter.ipf_adds(SCRATCH_GENERAL_REG, (int)current_offset, THREAD_PTR_REG, SCRATCH_PRED_REG);
emitter.ipf_adds(SCRATCH_GENERAL_REG2, (int)limit_offset, THREAD_PTR_REG, SCRATCH_PRED_REG);
emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, RETURN_VALUE_REG, SCRATCH_GENERAL_REG, SCRATCH_PRED_REG);
emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, SCRATCH_GENERAL_REG3, SCRATCH_GENERAL_REG2, SCRATCH_PRED_REG);
emitter.ipf_adds(SCRATCH_GENERAL_REG5, (int)offset_instance_data_size, SCRATCH_GENERAL_REG7, SCRATCH_PRED_REG);
emitter.ipf_ld(int_mem_size_4, mem_ld_none, mem_none, SCRATCH_GENERAL_REG6, SCRATCH_GENERAL_REG5, SCRATCH_PRED_REG);
emitter.ipf_add(SCRATCH_GENERAL_REG4, RETURN_VALUE_REG, SCRATCH_GENERAL_REG6, SCRATCH_PRED_REG);
// Note the use of "cmp_unc" to unconditionally set both SCRATCH_PRED_REG3 and SCRATCH_PRED_REG4 to 0 (false) beforehand.
emitter.ipf_cmp(icmp_le, cmp_unc, SCRATCH_PRED_REG3, SCRATCH_PRED_REG4, SCRATCH_GENERAL_REG4, SCRATCH_GENERAL_REG3,
/*cmp4*/ false, SCRATCH_PRED_REG);
emitter.ipf_adds(SCRATCH_GENERAL_REG2, (int)offset_allocation_handle, SCRATCH_GENERAL_REG7, SCRATCH_PRED_REG3);
emitter.ipf_ld(int_mem_size_8, mem_ld_none, mem_none, SCRATCH_GENERAL_REG3, SCRATCH_GENERAL_REG2, SCRATCH_PRED_REG3);
if (vm_is_vtable_compressed())
{
emitter.ipf_st(int_mem_size_4, mem_st_none, mem_none, RETURN_VALUE_REG, SCRATCH_GENERAL_REG3, SCRATCH_PRED_REG3);
}
else
{
emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, RETURN_VALUE_REG, SCRATCH_GENERAL_REG3, SCRATCH_PRED_REG3);
}
emitter.ipf_st(int_mem_size_8, mem_st_none, mem_none, SCRATCH_GENERAL_REG, SCRATCH_GENERAL_REG4, SCRATCH_PRED_REG3);
emitter.ipf_brret(br_many, br_sptk, br_none, BRANCH_RETURN_LINK_REG, SCRATCH_PRED_REG3);
}
}
static void emit_convertToByteArray_override(Emitter_Handle eh, Method *method) {
Merced_Code_Emitter *mce = (Merced_Code_Emitter *)eh;
Merced_Code_Emitter &emitter = *mce;
const int v0 = RETURN_VALUE_REG;
const int carray = IN_REG0;
const int barray = IN_REG1;
const int first = IN_REG2;
const int sc1 = SCRATCH_GENERAL_REG;
const int sc2 = SCRATCH_GENERAL_REG2;
const int sc3 = SCRATCH_GENERAL_REG3;
const int sc4 = SCRATCH_GENERAL_REG4;
const int sc5 = SCRATCH_GENERAL_REG5;
const int sc6 = SCRATCH_GENERAL_REG6;
const int sc7 = SCRATCH_GENERAL_REG7;
const int sc8 = SCRATCH_GENERAL_REG8;
const int p1 = SCRATCH_PRED_REG;
const int p2 = SCRATCH_PRED_REG2;
const int p3 = SCRATCH_PRED_REG3;
const int p4 = SCRATCH_PRED_REG4;
const int length_offset = vector_length_offset();
const int first_char_offset = vector_first_element_offset(VM_DATA_TYPE_CHAR);
const int first_byte_offset = vector_first_element_offset(VM_DATA_TYPE_INT8);
const int target_loop = 2;
const int target_end = 3;
const int target_fail = 4;
const int target_loopExit = 5;
// v0 = barray
// sc1 = &barray->length
// sc2 = [sc1]
// cmp p1,p2 = (sc2 == 0)
// cmp p3,p4 = (sc2 == 1)
// (p1) br.ret
// sc2 = sc2 - 2
// sc3 = ar.lc
// ar.lc = sc2
// sc4 = &carray[0];
// sc5 = first<<1 + sc4
// sc6 = &barray[0];
// sc7 = 0xff;
// sc8 = [sc5],2
// (p3) br.cond loopExit
// loop:
// [sc6],1 = sc8
// cmp p1,p2 = (sc8 > sc7)
// (p1) br.cond fail
// sc8 = [sc5],2
// br.cloop loop
// loopExit:
// [sc6],1 = sc8
// cmp p1,p2 = (sc8 > sc7)
// (p1) br.cond fail
// end:
// ar.lc = sc3
// br.ret
// fail:
// v0 = 0
// br end
emitter.ipf_mov(v0, barray);
emitter.ipf_adds(sc1, length_offset, barray);
emitter.ipf_ld(int_mem_size_4, mem_ld_none, mem_none, sc2, sc1);
emitter.ipf_cmp(icmp_eq, cmp_none, p1, p2, sc2, 0);
emitter.ipf_cmpi(icmp_eq, cmp_none, p3, p4, 1, sc2);
emitter.ipf_brret(br_many, br_spnt, br_none, BRANCH_RETURN_LINK_REG, p1);
emitter.ipf_adds(sc2, -2, sc2);
emitter.ipf_mfap(sc3, AR_lc);
emitter.ipf_mtap(AR_lc, sc2);
emitter.ipf_adds(sc4, first_char_offset, carray);
emitter.ipf_shladd(sc5, first, 1, sc4);
emitter.ipf_adds(sc6, first_byte_offset, barray);
emitter.ipf_movi(sc7, 0xff);
emitter.ipf_ld_inc_imm(int_mem_size_2, mem_ld_none, mem_none, sc8, sc5, 2);
emitter.ipf_br(br_cond, br_few, br_spnt, br_none, target_loopExit, p3);
emitter.set_target(target_loop);
emitter.ipf_st_inc_imm(int_mem_size_1, mem_st_none, mem_none, sc6, sc8, 1);
emitter.ipf_cmp(icmp_gt, cmp_none, p1, p2, sc8, sc7);
emitter.ipf_br(br_cond, br_few, br_spnt, br_none, target_fail, p1);
emitter.ipf_ld_inc_imm(int_mem_size_2, mem_ld_none, mem_none, sc8, sc5, 2);
emitter.ipf_br(br_cloop, br_few, br_sptk, br_none, target_loop);
emitter.set_target(target_loopExit);
emitter.ipf_st_inc_imm(int_mem_size_1, mem_st_none, mem_none, sc6, sc8, 1);
emitter.ipf_cmp(icmp_gt, cmp_none, p1, p2, sc8, sc7);
emitter.ipf_br(br_cond, br_few, br_spnt, br_none, target_fail, p1);
emitter.set_target(target_end);
emitter.ipf_mtap(AR_lc, sc3);
emitter.ipf_brret(br_many, br_spnt, br_none, BRANCH_RETURN_LINK_REG);
emitter.set_target(target_fail);
emitter.ipf_mov(v0, 0);
emitter.ipf_br(br_cond, br_few, br_sptk, br_none, target_end);
}
static unsigned default_override_size(Method *m)
{
LDIE(51, "Not implemented");
return 0;
}
static Stub_Override_Entry _stub_override_entries_base[] = {
{"java/lang/Class", "newInstance", "()Ljava/lang/Object;", emit_newinstance_override, default_override_size},
{"java/lang/VMSystem", "identityHashCode", "(Ljava/lang/Object;)I", emit_hashcode_override, default_override_size},
{"java/lang/VMSystem", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", emit_arraycopy_override, default_override_size},
{"java/lang/System", "currentTimeMillis", "()J", emit_system_currenttimemillis_override, default_override_size},
{"java/lang/String", "convertToByteArray0", "([C[BI)[B", emit_convertToByteArray_override, default_override_size},
{"java/io/FileInputStream", "readInternal", "(I[BII)I", emit_readinternal_override, default_override_size}
};
Stub_Override_Entry *stub_override_entries = &(_stub_override_entries_base[0]);
int sizeof_stub_override_entries = sizeof(_stub_override_entries_base) / sizeof(_stub_override_entries_base[0]);
/* END SUPPORT FOR STUB OVERRIDE CODE SEQUENCES */
void compile_flush_generated_code_block(U_8* b, size_t sz) {
flush_hw_cache(b, sz);
}
extern "C" void do_mf_asm();
extern "C" void do_mf() {
do_mf_asm();
}
void compile_flush_generated_code() {
sync_i_cache();
do_mf();
}
// 20030711: This function might not be compressed references safe
static void protect_value_type(Class_Handle c, uint64* start, GcFrame* gc) {
DIE(("It is supposed that the function is never called"));
assert(!hythread_is_suspend_enabled());
unsigned num_fields = class_num_instance_fields_recursive(c);
for(unsigned i=0; i<num_fields; i++) {
Field_Handle f = class_get_instance_field_recursive(c, i);
Type_Info_Handle ftih = field_get_type_info(f);
unsigned offset = 0;
// field_get_offset_unboxed asserts 0 anyway
switch (type_info_get_type(ftih)) {
case VM_DATA_TYPE_CLASS:
case VM_DATA_TYPE_ARRAY:
assert((offset&7) == 0);
gc->add_object((ManagedObject**)(start+(offset>>3)));
break;
case VM_DATA_TYPE_MP:
assert((offset&7) == 0);
gc->add_managed_pointer((ManagedPointer*)(start+(offset>>3)));
break;
case VM_DATA_TYPE_VALUE:
{
// This should never cause loading
Class_Handle fc = type_info_get_class(ftih);
assert(fc);
assert((offset&7) == 0);
protect_value_type(fc, start+(offset>>3), gc);
break;
}
default:
// Ignore everything else
break;
}
}
}
void compile_protect_arguments(Method_Handle method, GcFrame* gc) {
assert(!hythread_is_suspend_enabled());
Method_Signature_Handle msh = method_get_signature(method);
if (msh == NULL) {
return;
}
assert(msh);
unsigned num_args = method_args_get_number(msh);
M2nFrame* m2nf = m2n_get_last_frame();
unsigned cur_word = 0;
for(unsigned i=0; i<num_args; i++) {
Type_Info_Handle tih = method_args_get_type_info(msh, i);
switch (type_info_get_type(tih)) {
case VM_DATA_TYPE_INT64:
case VM_DATA_TYPE_UINT64:
case VM_DATA_TYPE_F8:
case VM_DATA_TYPE_INT8:
case VM_DATA_TYPE_UINT8:
case VM_DATA_TYPE_INT16:
case VM_DATA_TYPE_UINT16:
case VM_DATA_TYPE_INT32:
case VM_DATA_TYPE_UINT32:
case VM_DATA_TYPE_INTPTR:
case VM_DATA_TYPE_UINTPTR:
case VM_DATA_TYPE_F4:
case VM_DATA_TYPE_BOOLEAN:
case VM_DATA_TYPE_CHAR:
case VM_DATA_TYPE_UP:
cur_word++;
break;
case VM_DATA_TYPE_CLASS:
case VM_DATA_TYPE_ARRAY:
gc->add_object((ManagedObject**)m2n_get_arg_word(m2nf, cur_word));
cur_word++;
break;
case VM_DATA_TYPE_MP:
gc->add_managed_pointer((ManagedPointer*)m2n_get_arg_word(m2nf, cur_word));
break;
case VM_DATA_TYPE_VALUE:
{
// 20030711: Must verify this with the calling convention
LDIE(52, "Unexpected data type");
// This should never cause loading
Class_Handle c = type_info_get_class(tih);
assert(c);
protect_value_type(c, m2n_get_arg_word(m2nf, cur_word), gc);
break;
}
default:
LDIE(52, "Unexpected data type");
}
}
}
void patch_code_with_threads_suspended(U_8* code_block, U_8* new_code, size_t size) {
// Check that the code being modified is one or more complete bundles on IPF.
assert((((size_t)code_block) % 16) == 0); // else did not start at a possible bundle address
// 20030203 Check for current restrictions on code patching
assert(size == 16); // currently support exactly one bundle
// Check that the original bundle had the form: <template>, <slot 0 inst>, brl <offset0> or <template>, <slot 0 inst>, <nop.i>, br <offset0>
uint64 original_bundle_low = *((uint64 *)code_block);
unsigned UNUSED original_template = (unsigned)(original_bundle_low & 0x1F);
assert((original_template == 4) || (original_template == 5) ||
(original_template == 16) || (original_template == 17)); // else not mlx or mlxS or mib or mibS template
// Should check for a brl instruction - need help on its encoding
uint64 UNUSED original_inst0 = (original_bundle_low >> 5) & 0x1FfFfFfFfFf;
// Similarly, check that the new bundle has the same form
uint64 new_bundle_low = *((uint64 *)code_block);
unsigned UNUSED new_template = (unsigned)(new_bundle_low & 0x1F);
assert(original_template == new_template);
// Should check for a brl instruction
uint64 UNUSED new_inst0 = (original_bundle_low >> 5) & 0x1FfFfFfFfFf;
assert(original_inst0 == new_inst0);
memcpy(code_block, new_code, size);
}
/* BEGIN COMPILE-ME STUBS */
static NativeCodePtr compile_get_compile_me_generic() {
static NativeCodePtr addr = NULL;
if (!addr) {
NativeCodePtr (*p_jitter)(Method*) = compile_me;
void (*p_rethrow)() = exn_rethrow_if_pending;
LilCodeStub* cs = lil_parse_code_stub(
"entry 1:managed:arbitrary;"
"push_m2n 0, %0i;"
"m2n_save_all;"
"out platform:pint:pint;"
"o0=sp0;"
"call %1i;"
"locals 1;"
"l0 = r;"
"out platform::void;"
"call %2i;"
"pop_m2n;"
"tailcall l0;",
FRAME_COMPILATION, p_jitter, p_rethrow);
assert(cs && lil_is_valid(cs));
addr = LilCodeGenerator::get_platform()->compile(cs);
DUMP_STUB(addr, "compile_me_generic", lil_cs_get_code_size(cs));
lil_free_code_stub(cs);
}
return addr;
}
NativeCodePtr compile_gen_compile_me(Method_Handle method) {
LilCodeStub * cs = lil_parse_code_stub(
"entry 0:managed:arbitrary;"
"std_places 1;"
"sp0=%0i;"
"tailcall %1i;",
method, lil_npc_to_fp(compile_get_compile_me_generic()));
assert(cs && lil_is_valid(cs));
NativeCodePtr addr = LilCodeGenerator::get_platform()->compile(cs);
#ifndef NDEBUG
static unsigned done = 0;
// dump first 10 compileme stubs
if (dump_stubs && ++done <= 10) {
char * buf;
const char * c = class_get_name(method_get_class(method));
const char * m = method_get_name(method);
const char * d = method_get_descriptor(method);
size_t sz = strlen(c)+strlen(m)+strlen(d)+12;
buf = (char *)STD_MALLOC(sz);
sprintf(buf, "compileme.%s.%s%s", c, m, d);
assert(strlen(buf) < sz);
DUMP_STUB(addr, buf, lil_cs_get_code_size(cs));
STD_FREE(buf);
}
#endif
lil_free_code_stub(cs);
return addr;
}
/* END COMPILE-ME STUBS */