# HG changeset patch # User roland # Date 1301824854 -7200 # Node ID 13bc79b5c9c8ce81e9ab520df8785278eec6a3d2 # Parent 08eb13460b3a3993015fac452c49223adef20179 7033154: Improve C1 arraycopy performance Summary: better static analysis. Take advantage of array copy stubs. Reviewed-by: never diff -r 08eb13460b3a -r 13bc79b5c9c8 src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp --- a/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/cpu/sparc/vm/c1_LIRAssembler_sparc.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -2065,20 +2065,36 @@ // the known type isn't loaded since the code sanity checks // in debug mode and the type isn't required when we know the exact type // also check that the type is an array type. - // We also, for now, always call the stub if the barrier set requires a - // write_ref_pre barrier (which the stub does, but none of the optimized - // cases currently does). - if (op->expected_type() == NULL || - Universe::heap()->barrier_set()->has_write_ref_pre_barrier()) { + if (op->expected_type() == NULL) { __ mov(src, O0); __ mov(src_pos, O1); __ mov(dst, O2); __ mov(dst_pos, O3); __ mov(length, O4); - __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::arraycopy)); - - __ br_zero(Assembler::less, false, Assembler::pn, O0, *stub->entry()); - __ delayed()->nop(); + address copyfunc_addr = StubRoutines::generic_arraycopy(); + + if (copyfunc_addr == NULL) { // Use C version if stub was not generated + __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::arraycopy)); + } else { +#ifndef PRODUCT + if (PrintC1Statistics) { + address counter = (address)&Runtime1::_generic_arraycopystub_cnt; + __ inc_counter(counter, G1, G3); + } +#endif + __ call_VM_leaf(tmp, copyfunc_addr); + } + + if (copyfunc_addr != NULL) { + __ xor3(O0, -1, tmp); + __ sub(length, tmp, length); + __ add(src_pos, tmp, src_pos); + __ br_zero(Assembler::less, false, Assembler::pn, O0, *stub->entry()); + __ delayed()->add(dst_pos, tmp, dst_pos); + } else { + __ br_zero(Assembler::less, false, Assembler::pn, O0, *stub->entry()); + __ delayed()->nop(); + } __ bind(*stub->continuation()); return; } @@ -2135,20 +2151,143 @@ __ delayed()->nop(); } +#ifndef _LP64 + __ sra(dst_pos, 0, dst_pos); //higher 32bits must be null + __ sra(src_pos, 0, src_pos); //higher 32bits must be null +#endif + + int shift = shift_amount(basic_type); + if (flags & LIR_OpArrayCopy::type_check) { - if (UseCompressedOops) { - // We don't need decode because we just need to compare - __ lduw(src, oopDesc::klass_offset_in_bytes(), tmp); - __ lduw(dst, oopDesc::klass_offset_in_bytes(), tmp2); - __ cmp(tmp, tmp2); - __ br(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + // We don't know the array types are compatible + if (basic_type != T_OBJECT) { + // Simple test for basic type arrays + if (UseCompressedOops) { + // We don't need decode because we just need to compare + __ lduw(src, oopDesc::klass_offset_in_bytes(), tmp); + __ lduw(dst, oopDesc::klass_offset_in_bytes(), tmp2); + __ cmp(tmp, tmp2); + __ br(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + } else { + __ ld_ptr(src, oopDesc::klass_offset_in_bytes(), tmp); + __ ld_ptr(dst, oopDesc::klass_offset_in_bytes(), tmp2); + __ cmp(tmp, tmp2); + __ brx(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + } + __ delayed()->nop(); } else { - __ ld_ptr(src, oopDesc::klass_offset_in_bytes(), tmp); - __ ld_ptr(dst, oopDesc::klass_offset_in_bytes(), tmp2); - __ cmp(tmp, tmp2); - __ brx(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + // For object arrays, if src is a sub class of dst then we can + // safely do the copy. + address copyfunc_addr = StubRoutines::checkcast_arraycopy(); + + Label cont, slow; + assert_different_registers(tmp, tmp2, G3, G1); + + __ load_klass(src, G3); + __ load_klass(dst, G1); + + __ check_klass_subtype_fast_path(G3, G1, tmp, tmp2, &cont, copyfunc_addr == NULL ? stub->entry() : &slow, NULL); + + __ call(Runtime1::entry_for(Runtime1::slow_subtype_check_id), relocInfo::runtime_call_type); + __ delayed()->nop(); + + __ cmp(G3, 0); + if (copyfunc_addr != NULL) { // use stub if available + // src is not a sub class of dst so we have to do a + // per-element check. + __ br(Assembler::notEqual, false, Assembler::pt, cont); + __ delayed()->nop(); + + __ bind(slow); + + int mask = LIR_OpArrayCopy::src_objarray|LIR_OpArrayCopy::dst_objarray; + if ((flags & mask) != mask) { + // Check that at least both of them object arrays. + assert(flags & mask, "one of the two should be known to be an object array"); + + if (!(flags & LIR_OpArrayCopy::src_objarray)) { + __ load_klass(src, tmp); + } else if (!(flags & LIR_OpArrayCopy::dst_objarray)) { + __ load_klass(dst, tmp); + } + int lh_offset = klassOopDesc::header_size() * HeapWordSize + + Klass::layout_helper_offset_in_bytes(); + + __ lduw(tmp, lh_offset, tmp2); + + jint objArray_lh = Klass::array_layout_helper(T_OBJECT); + __ set(objArray_lh, tmp); + __ cmp(tmp, tmp2); + __ br(Assembler::notEqual, false, Assembler::pt, *stub->entry()); + __ delayed()->nop(); + } + + Register src_ptr = O0; + Register dst_ptr = O1; + Register len = O2; + Register chk_off = O3; + Register super_k = O4; + + __ add(src, arrayOopDesc::base_offset_in_bytes(basic_type), src_ptr); + if (shift == 0) { + __ add(src_ptr, src_pos, src_ptr); + } else { + __ sll(src_pos, shift, tmp); + __ add(src_ptr, tmp, src_ptr); + } + + __ add(dst, arrayOopDesc::base_offset_in_bytes(basic_type), dst_ptr); + if (shift == 0) { + __ add(dst_ptr, dst_pos, dst_ptr); + } else { + __ sll(dst_pos, shift, tmp); + __ add(dst_ptr, tmp, dst_ptr); + } + LP64_ONLY( __ sra(length, 0, length)); //higher 32bits must be null + __ mov(length, len); + __ load_klass(dst, tmp); + + int ek_offset = (klassOopDesc::header_size() * HeapWordSize + + objArrayKlass::element_klass_offset_in_bytes()); + __ ld_ptr(tmp, ek_offset, super_k); + + int sco_offset = (klassOopDesc::header_size() * HeapWordSize + + Klass::super_check_offset_offset_in_bytes()); + __ lduw(super_k, sco_offset, chk_off); + + __ call_VM_leaf(tmp, copyfunc_addr); + +#ifndef PRODUCT + if (PrintC1Statistics) { + Label failed; + __ br_notnull(O0, false, Assembler::pn, failed); + __ delayed()->nop(); + __ inc_counter((address)&Runtime1::_arraycopy_checkcast_cnt, G1, G3); + __ bind(failed); + } +#endif + + __ br_null(O0, false, Assembler::pt, *stub->continuation()); + __ delayed()->xor3(O0, -1, tmp); + +#ifndef PRODUCT + if (PrintC1Statistics) { + __ inc_counter((address)&Runtime1::_arraycopy_checkcast_attempt_cnt, G1, G3); + } +#endif + + __ sub(length, tmp, length); + __ add(src_pos, tmp, src_pos); + __ br(Assembler::always, false, Assembler::pt, *stub->entry()); + __ delayed()->add(dst_pos, tmp, dst_pos); + + __ bind(cont); + } else { + __ br(Assembler::equal, false, Assembler::pn, *stub->entry()); + __ delayed()->nop(); + __ bind(cont); + } } - __ delayed()->nop(); } #ifdef ASSERT @@ -2207,14 +2346,18 @@ } #endif - int shift = shift_amount(basic_type); +#ifndef PRODUCT + if (PrintC1Statistics) { + address counter = Runtime1::arraycopy_count_address(basic_type); + __ inc_counter(counter, G1, G3); + } +#endif Register src_ptr = O0; Register dst_ptr = O1; Register len = O2; __ add(src, arrayOopDesc::base_offset_in_bytes(basic_type), src_ptr); - LP64_ONLY(__ sra(src_pos, 0, src_pos);) //higher 32bits must be null if (shift == 0) { __ add(src_ptr, src_pos, src_ptr); } else { @@ -2223,7 +2366,6 @@ } __ add(dst, arrayOopDesc::base_offset_in_bytes(basic_type), dst_ptr); - LP64_ONLY(__ sra(dst_pos, 0, dst_pos);) //higher 32bits must be null if (shift == 0) { __ add(dst_ptr, dst_pos, dst_ptr); } else { @@ -2231,18 +2373,14 @@ __ add(dst_ptr, tmp, dst_ptr); } - if (basic_type != T_OBJECT) { - if (shift == 0) { - __ mov(length, len); - } else { - __ sll(length, shift, len); - } - __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::primitive_arraycopy)); - } else { - // oop_arraycopy takes a length in number of elements, so don't scale it. - __ mov(length, len); - __ call_VM_leaf(tmp, CAST_FROM_FN_PTR(address, Runtime1::oop_arraycopy)); - } + bool disjoint = (flags & LIR_OpArrayCopy::overlapping) == 0; + bool aligned = (flags & LIR_OpArrayCopy::unaligned) == 0; + const char *name; + address entry = StubRoutines::select_arraycopy_function(basic_type, aligned, disjoint, name, false); + + // arraycopy stubs takes a length in number of elements, so don't scale it. + __ mov(length, len); + __ call_VM_leaf(tmp, entry); __ bind(*stub->continuation()); } diff -r 08eb13460b3a -r 13bc79b5c9c8 src/cpu/x86/vm/c1_LIRAssembler_x86.cpp --- a/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/cpu/x86/vm/c1_LIRAssembler_x86.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -3102,7 +3102,7 @@ BasicType basic_type = default_type != NULL ? default_type->element_type()->basic_type() : T_ILLEGAL; if (basic_type == T_ARRAY) basic_type = T_OBJECT; - // if we don't know anything or it's an object array, just go through the generic arraycopy + // if we don't know anything, just go through the generic arraycopy if (default_type == NULL) { Label done; // save outgoing arguments on stack in case call to System.arraycopy is needed @@ -3123,7 +3123,9 @@ store_parameter(src, 4); NOT_LP64(assert(src == rcx && src_pos == rdx, "mismatch in calling convention");) - address entry = CAST_FROM_FN_PTR(address, Runtime1::arraycopy); + address C_entry = CAST_FROM_FN_PTR(address, Runtime1::arraycopy); + + address copyfunc_addr = StubRoutines::generic_arraycopy(); // pass arguments: may push as this is not a safepoint; SP must be fix at each safepoint #ifdef _LP64 @@ -3141,11 +3143,29 @@ // Allocate abi space for args but be sure to keep stack aligned __ subptr(rsp, 6*wordSize); store_parameter(j_rarg4, 4); - __ call(RuntimeAddress(entry)); + if (copyfunc_addr == NULL) { // Use C version if stub was not generated + __ call(RuntimeAddress(C_entry)); + } else { +#ifndef PRODUCT + if (PrintC1Statistics) { + __ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt)); + } +#endif + __ call(RuntimeAddress(copyfunc_addr)); + } __ addptr(rsp, 6*wordSize); #else __ mov(c_rarg4, j_rarg4); - __ call(RuntimeAddress(entry)); + if (copyfunc_addr == NULL) { // Use C version if stub was not generated + __ call(RuntimeAddress(C_entry)); + } else { +#ifndef PRODUCT + if (PrintC1Statistics) { + __ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt)); + } +#endif + __ call(RuntimeAddress(copyfunc_addr)); + } #endif // _WIN64 #else __ push(length); @@ -3153,13 +3173,28 @@ __ push(dst); __ push(src_pos); __ push(src); - __ call_VM_leaf(entry, 5); // removes pushed parameter from the stack + + if (copyfunc_addr == NULL) { // Use C version if stub was not generated + __ call_VM_leaf(C_entry, 5); // removes pushed parameter from the stack + } else { +#ifndef PRODUCT + if (PrintC1Statistics) { + __ incrementl(ExternalAddress((address)&Runtime1::_generic_arraycopystub_cnt)); + } +#endif + __ call_VM_leaf(copyfunc_addr, 5); // removes pushed parameter from the stack + } #endif // _LP64 __ cmpl(rax, 0); __ jcc(Assembler::equal, *stub->continuation()); + if (copyfunc_addr != NULL) { + __ mov(tmp, rax); + __ xorl(tmp, -1); + } + // Reload values from the stack so they are where the stub // expects them. __ movptr (dst, Address(rsp, 0*BytesPerWord)); @@ -3167,6 +3202,12 @@ __ movptr (length, Address(rsp, 2*BytesPerWord)); __ movptr (src_pos, Address(rsp, 3*BytesPerWord)); __ movptr (src, Address(rsp, 4*BytesPerWord)); + + if (copyfunc_addr != NULL) { + __ subl(length, tmp); + __ addl(src_pos, tmp); + __ addl(dst_pos, tmp); + } __ jmp(*stub->entry()); __ bind(*stub->continuation()); @@ -3226,10 +3267,6 @@ __ testl(dst_pos, dst_pos); __ jcc(Assembler::less, *stub->entry()); } - if (flags & LIR_OpArrayCopy::length_positive_check) { - __ testl(length, length); - __ jcc(Assembler::less, *stub->entry()); - } if (flags & LIR_OpArrayCopy::src_range_check) { __ lea(tmp, Address(src_pos, length, Address::times_1, 0)); @@ -3242,15 +3279,190 @@ __ jcc(Assembler::above, *stub->entry()); } + if (flags & LIR_OpArrayCopy::length_positive_check) { + __ testl(length, length); + __ jcc(Assembler::less, *stub->entry()); + __ jcc(Assembler::zero, *stub->continuation()); + } + +#ifdef _LP64 + __ movl2ptr(src_pos, src_pos); //higher 32bits must be null + __ movl2ptr(dst_pos, dst_pos); //higher 32bits must be null +#endif + if (flags & LIR_OpArrayCopy::type_check) { - if (UseCompressedOops) { - __ movl(tmp, src_klass_addr); - __ cmpl(tmp, dst_klass_addr); + // We don't know the array types are compatible + if (basic_type != T_OBJECT) { + // Simple test for basic type arrays + if (UseCompressedOops) { + __ movl(tmp, src_klass_addr); + __ cmpl(tmp, dst_klass_addr); + } else { + __ movptr(tmp, src_klass_addr); + __ cmpptr(tmp, dst_klass_addr); + } + __ jcc(Assembler::notEqual, *stub->entry()); } else { - __ movptr(tmp, src_klass_addr); - __ cmpptr(tmp, dst_klass_addr); + // For object arrays, if src is a sub class of dst then we can + // safely do the copy. + Label cont, slow; + + __ push(src); + __ push(dst); + + __ load_klass(src, src); + __ load_klass(dst, dst); + + __ check_klass_subtype_fast_path(src, dst, tmp, &cont, &slow, NULL); + + __ push(src); + __ push(dst); + __ call(RuntimeAddress(Runtime1::entry_for(Runtime1::slow_subtype_check_id))); + __ pop(dst); + __ pop(src); + + __ cmpl(src, 0); + __ jcc(Assembler::notEqual, cont); + + __ bind(slow); + __ pop(dst); + __ pop(src); + + address copyfunc_addr = StubRoutines::checkcast_arraycopy(); + if (copyfunc_addr != NULL) { // use stub if available + // src is not a sub class of dst so we have to do a + // per-element check. + + int mask = LIR_OpArrayCopy::src_objarray|LIR_OpArrayCopy::dst_objarray; + if ((flags & mask) != mask) { + // Check that at least both of them object arrays. + assert(flags & mask, "one of the two should be known to be an object array"); + + if (!(flags & LIR_OpArrayCopy::src_objarray)) { + __ load_klass(tmp, src); + } else if (!(flags & LIR_OpArrayCopy::dst_objarray)) { + __ load_klass(tmp, dst); + } + int lh_offset = klassOopDesc::header_size() * HeapWordSize + + Klass::layout_helper_offset_in_bytes(); + Address klass_lh_addr(tmp, lh_offset); + jint objArray_lh = Klass::array_layout_helper(T_OBJECT); + __ cmpl(klass_lh_addr, objArray_lh); + __ jcc(Assembler::notEqual, *stub->entry()); + } + +#ifndef _LP64 + // save caller save registers + store_parameter(rax, 2); + store_parameter(rcx, 1); + store_parameter(rdx, 0); + + __ movptr(tmp, dst_klass_addr); + __ movptr(tmp, Address(tmp, objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc))); + __ push(tmp); + __ movl(tmp, Address(tmp, Klass::super_check_offset_offset_in_bytes() + sizeof(oopDesc))); + __ push(tmp); + __ push(length); + __ lea(tmp, Address(dst, dst_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + __ push(tmp); + __ lea(tmp, Address(src, src_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + __ push(tmp); + + __ call_VM_leaf(copyfunc_addr, 5); +#else + __ movl2ptr(length, length); //higher 32bits must be null + + // save caller save registers: copy them to callee save registers + __ mov(rbx, rdx); + __ mov(r13, r8); + __ mov(r14, r9); +#ifndef _WIN64 + store_parameter(rsi, 1); + store_parameter(rcx, 0); + // on WIN64 other incoming parameters are in rdi and rsi saved + // across the call +#endif + + __ lea(c_rarg0, Address(src, src_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + assert_different_registers(c_rarg0, dst, dst_pos, length); + __ lea(c_rarg1, Address(dst, dst_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); + assert_different_registers(c_rarg1, dst, length); + + __ mov(c_rarg2, length); + assert_different_registers(c_rarg2, dst); + +#ifdef _WIN64 + // Allocate abi space for args but be sure to keep stack aligned + __ subptr(rsp, 6*wordSize); + __ load_klass(c_rarg3, dst); + __ movptr(c_rarg3, Address(c_rarg3, objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc))); + store_parameter(c_rarg3, 4); + __ movl(c_rarg3, Address(c_rarg3, Klass::super_check_offset_offset_in_bytes() + sizeof(oopDesc))); + __ call(RuntimeAddress(copyfunc_addr)); + __ addptr(rsp, 6*wordSize); +#else + __ load_klass(c_rarg4, dst); + __ movptr(c_rarg4, Address(c_rarg4, objArrayKlass::element_klass_offset_in_bytes() + sizeof(oopDesc))); + __ movl(c_rarg3, Address(c_rarg4, Klass::super_check_offset_offset_in_bytes() + sizeof(oopDesc))); + __ call(RuntimeAddress(copyfunc_addr)); +#endif + +#endif + +#ifndef PRODUCT + if (PrintC1Statistics) { + Label failed; + __ testl(rax, rax); + __ jcc(Assembler::notZero, failed); + __ incrementl(ExternalAddress((address)&Runtime1::_arraycopy_checkcast_cnt)); + __ bind(failed); + } +#endif + + __ testl(rax, rax); + __ jcc(Assembler::zero, *stub->continuation()); + +#ifndef PRODUCT + if (PrintC1Statistics) { + __ incrementl(ExternalAddress((address)&Runtime1::_arraycopy_checkcast_attempt_cnt)); + } +#endif + + __ mov(tmp, rax); + + __ xorl(tmp, -1); + +#ifndef _LP64 + // restore caller save registers + assert_different_registers(tmp, rdx, rcx, rax); // result of stub will be lost + __ movptr(rdx, Address(rsp, 0*BytesPerWord)); + __ movptr(rcx, Address(rsp, 1*BytesPerWord)); + __ movptr(rax, Address(rsp, 2*BytesPerWord)); +#else + // restore caller save registers + __ mov(rdx, rbx); + __ mov(r8, r13); + __ mov(r9, r14); +#ifndef _WIN64 + assert_different_registers(tmp, rdx, r8, r9, rcx, rsi); // result of stub will be lost + __ movptr(rcx, Address(rsp, 0*BytesPerWord)); + __ movptr(rsi, Address(rsp, 1*BytesPerWord)); +#else + assert_different_registers(tmp, rdx, r8, r9); // result of stub will be lost +#endif +#endif + + __ subl(length, tmp); + __ addl(src_pos, tmp); + __ addl(dst_pos, tmp); + } + + __ jmp(*stub->entry()); + + __ bind(cont); + __ pop(dst); + __ pop(src); } - __ jcc(Assembler::notEqual, *stub->entry()); } #ifdef ASSERT @@ -3291,16 +3503,16 @@ } #endif - if (shift_amount > 0 && basic_type != T_OBJECT) { - __ shlptr(length, shift_amount); +#ifndef PRODUCT + if (PrintC1Statistics) { + __ incrementl(ExternalAddress(Runtime1::arraycopy_count_address(basic_type))); } +#endif #ifdef _LP64 assert_different_registers(c_rarg0, dst, dst_pos, length); - __ movl2ptr(src_pos, src_pos); //higher 32bits must be null __ lea(c_rarg0, Address(src, src_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); assert_different_registers(c_rarg1, length); - __ movl2ptr(dst_pos, dst_pos); //higher 32bits must be null __ lea(c_rarg1, Address(dst, dst_pos, scale, arrayOopDesc::base_offset_in_bytes(basic_type))); __ mov(c_rarg2, length); @@ -3311,11 +3523,12 @@ store_parameter(tmp, 1); store_parameter(length, 2); #endif // _LP64 - if (basic_type == T_OBJECT) { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, Runtime1::oop_arraycopy), 0); - } else { - __ call_VM_leaf(CAST_FROM_FN_PTR(address, Runtime1::primitive_arraycopy), 0); - } + + bool disjoint = (flags & LIR_OpArrayCopy::overlapping) == 0; + bool aligned = (flags & LIR_OpArrayCopy::unaligned) == 0; + const char *name; + address entry = StubRoutines::select_arraycopy_function(basic_type, aligned, disjoint, name, false); + __ call_VM_leaf(entry, 0); __ bind(*stub->continuation()); } diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_GraphBuilder.cpp --- a/src/share/vm/c1/c1_GraphBuilder.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_GraphBuilder.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -2824,7 +2824,7 @@ int idx = 0; if (!method()->is_static()) { // we should always see the receiver - state->store_local(idx, new Local(objectType, idx)); + state->store_local(idx, new Local(method()->holder(), objectType, idx)); idx = 1; } @@ -2836,7 +2836,7 @@ // don't allow T_ARRAY to propagate into locals types if (basic_type == T_ARRAY) basic_type = T_OBJECT; ValueType* vt = as_ValueType(basic_type); - state->store_local(idx, new Local(vt, idx)); + state->store_local(idx, new Local(type, vt, idx)); idx += type->size(); } diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_Instruction.cpp --- a/src/share/vm/c1/c1_Instruction.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_Instruction.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -135,6 +135,33 @@ } +ciType* Local::exact_type() const { + ciType* type = declared_type(); + + // for primitive arrays, the declared type is the exact type + if (type->is_type_array_klass()) { + return type; + } else if (type->is_instance_klass()) { + ciInstanceKlass* ik = (ciInstanceKlass*)type; + if (ik->is_loaded() && ik->is_final() && !ik->is_interface()) { + return type; + } + } else if (type->is_obj_array_klass()) { + ciObjArrayKlass* oak = (ciObjArrayKlass*)type; + ciType* base = oak->base_element_type(); + if (base->is_instance_klass()) { + ciInstanceKlass* ik = base->as_instance_klass(); + if (ik->is_loaded() && ik->is_final()) { + return type; + } + } else if (base->is_primitive_type()) { + return type; + } + } + return NULL; +} + + ciType* LoadIndexed::exact_type() const { ciType* array_type = array()->exact_type(); if (array_type == NULL) { @@ -189,16 +216,21 @@ return ciTypeArrayKlass::make(elt_type()); } - ciType* NewObjectArray::exact_type() const { return ciObjArrayKlass::make(klass()); } +ciType* NewArray::declared_type() const { + return exact_type(); +} ciType* NewInstance::exact_type() const { return klass(); } +ciType* NewInstance::declared_type() const { + return exact_type(); +} ciType* CheckCast::declared_type() const { return klass(); @@ -349,6 +381,11 @@ if (state() != NULL) state()->values_do(f); } +ciType* Invoke::declared_type() const { + ciType *t = _target->signature()->return_type(); + assert(t->basic_type() != T_VOID, "need return value of void method?"); + return t; +} // Implementation of Contant intx Constant::hash() const { diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_Instruction.hpp --- a/src/share/vm/c1/c1_Instruction.hpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_Instruction.hpp Sun Apr 03 12:00:54 2011 +0200 @@ -621,16 +621,21 @@ LEAF(Local, Instruction) private: int _java_index; // the local index within the method to which the local belongs + ciType* _declared_type; public: // creation - Local(ValueType* type, int index) + Local(ciType* declared, ValueType* type, int index) : Instruction(type) , _java_index(index) + , _declared_type(declared) {} // accessors int java_index() const { return _java_index; } + ciType* declared_type() const { return _declared_type; } + ciType* exact_type() const; + // generic virtual void input_values_do(ValueVisitor* f) { /* no values */ } }; @@ -1146,6 +1151,8 @@ BasicTypeList* signature() const { return _signature; } ciMethod* target() const { return _target; } + ciType* declared_type() const; + // Returns false if target is not loaded bool target_is_final() const { return check_flag(TargetIsFinalFlag); } bool target_is_loaded() const { return check_flag(TargetIsLoadedFlag); } @@ -1187,6 +1194,7 @@ // generic virtual bool can_trap() const { return true; } ciType* exact_type() const; + ciType* declared_type() const; }; @@ -1208,6 +1216,8 @@ virtual bool needs_exception_state() const { return false; } + ciType* declared_type() const; + // generic virtual bool can_trap() const { return true; } virtual void input_values_do(ValueVisitor* f) { StateSplit::input_values_do(f); f->visit(&_length); } @@ -1397,6 +1407,7 @@ vmIntrinsics::ID _id; Values* _args; Value _recv; + int _nonnull_state; // mask identifying which args are nonnull public: // preserves_state can be set to true for Intrinsics @@ -1417,6 +1428,7 @@ , _id(id) , _args(args) , _recv(NULL) + , _nonnull_state(AllBits) { assert(args != NULL, "args must exist"); ASSERT_VALUES @@ -1442,6 +1454,23 @@ Value receiver() const { assert(has_receiver(), "must have receiver"); return _recv; } bool preserves_state() const { return check_flag(PreservesStateFlag); } + bool arg_needs_null_check(int i) { + if (i >= 0 && i < (int)sizeof(_nonnull_state) * BitsPerByte) { + return is_set_nth_bit(_nonnull_state, i); + } + return true; + } + + void set_arg_needs_null_check(int i, bool check) { + if (i >= 0 && i < (int)sizeof(_nonnull_state) * BitsPerByte) { + if (check) { + _nonnull_state |= nth_bit(i); + } else { + _nonnull_state &= ~(nth_bit(i)); + } + } + } + // generic virtual bool can_trap() const { return check_flag(CanTrapFlag); } virtual void input_values_do(ValueVisitor* f) { diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_LIR.hpp --- a/src/share/vm/c1/c1_LIR.hpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_LIR.hpp Sun Apr 03 12:00:54 2011 +0200 @@ -1215,7 +1215,11 @@ src_range_check = 1 << 5, dst_range_check = 1 << 6, type_check = 1 << 7, - all_flags = (1 << 8) - 1 + overlapping = 1 << 8, + unaligned = 1 << 9, + src_objarray = 1 << 10, + dst_objarray = 1 << 11, + all_flags = (1 << 12) - 1 }; LIR_OpArrayCopy(LIR_Opr src, LIR_Opr src_pos, LIR_Opr dst, LIR_Opr dst_pos, LIR_Opr length, LIR_Opr tmp, diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_LIRGenerator.cpp --- a/src/share/vm/c1/c1_LIRGenerator.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_LIRGenerator.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -706,6 +706,38 @@ } } +static Value maxvalue(IfOp* ifop) { + switch (ifop->cond()) { + case If::eql: return NULL; + case If::neq: return NULL; + case If::lss: // x < y ? x : y + case If::leq: // x <= y ? x : y + if (ifop->x() == ifop->tval() && + ifop->y() == ifop->fval()) return ifop->y(); + return NULL; + + case If::gtr: // x > y ? y : x + case If::geq: // x >= y ? y : x + if (ifop->x() == ifop->tval() && + ifop->y() == ifop->fval()) return ifop->y(); + return NULL; + + } +} + +static ciType* phi_declared_type(Phi* phi) { + ciType* t = phi->operand_at(0)->declared_type(); + if (t == NULL) { + return NULL; + } + for(int i = 1; i < phi->operand_count(); i++) { + if (t != phi->operand_at(i)->declared_type()) { + return NULL; + } + } + return t; +} + void LIRGenerator::arraycopy_helper(Intrinsic* x, int* flagsp, ciArrayKlass** expected_typep) { Instruction* src = x->argument_at(0); Instruction* src_pos = x->argument_at(1); @@ -715,12 +747,20 @@ // first try to identify the likely type of the arrays involved ciArrayKlass* expected_type = NULL; - bool is_exact = false; + bool is_exact = false, src_objarray = false, dst_objarray = false; { ciArrayKlass* src_exact_type = as_array_klass(src->exact_type()); ciArrayKlass* src_declared_type = as_array_klass(src->declared_type()); + Phi* phi; + if (src_declared_type == NULL && (phi = src->as_Phi()) != NULL) { + src_declared_type = as_array_klass(phi_declared_type(phi)); + } ciArrayKlass* dst_exact_type = as_array_klass(dst->exact_type()); ciArrayKlass* dst_declared_type = as_array_klass(dst->declared_type()); + if (dst_declared_type == NULL && (phi = dst->as_Phi()) != NULL) { + dst_declared_type = as_array_klass(phi_declared_type(phi)); + } + if (src_exact_type != NULL && src_exact_type == dst_exact_type) { // the types exactly match so the type is fully known is_exact = true; @@ -744,17 +784,60 @@ if (expected_type == NULL) expected_type = dst_exact_type; if (expected_type == NULL) expected_type = src_declared_type; if (expected_type == NULL) expected_type = dst_declared_type; + + src_objarray = (src_exact_type && src_exact_type->is_obj_array_klass()) || (src_declared_type && src_declared_type->is_obj_array_klass()); + dst_objarray = (dst_exact_type && dst_exact_type->is_obj_array_klass()) || (dst_declared_type && dst_declared_type->is_obj_array_klass()); } // if a probable array type has been identified, figure out if any // of the required checks for a fast case can be elided. int flags = LIR_OpArrayCopy::all_flags; + + if (!src_objarray) + flags &= ~LIR_OpArrayCopy::src_objarray; + if (!dst_objarray) + flags &= ~LIR_OpArrayCopy::dst_objarray; + + if (!x->arg_needs_null_check(0)) + flags &= ~LIR_OpArrayCopy::src_null_check; + if (!x->arg_needs_null_check(2)) + flags &= ~LIR_OpArrayCopy::dst_null_check; + + if (expected_type != NULL) { - // try to skip null checks - if (src->as_NewArray() != NULL) + Value length_limit = NULL; + + IfOp* ifop = length->as_IfOp(); + if (ifop != NULL) { + // look for expressions like min(v, a.length) which ends up as + // x > y ? y : x or x >= y ? y : x + if ((ifop->cond() == If::gtr || ifop->cond() == If::geq) && + ifop->x() == ifop->fval() && + ifop->y() == ifop->tval()) { + length_limit = ifop->y(); + } + } + + // try to skip null checks and range checks + NewArray* src_array = src->as_NewArray(); + if (src_array != NULL) { flags &= ~LIR_OpArrayCopy::src_null_check; - if (dst->as_NewArray() != NULL) + if (length_limit != NULL && + src_array->length() == length_limit && + is_constant_zero(src_pos)) { + flags &= ~LIR_OpArrayCopy::src_range_check; + } + } + + NewArray* dst_array = dst->as_NewArray(); + if (dst_array != NULL) { flags &= ~LIR_OpArrayCopy::dst_null_check; + if (length_limit != NULL && + dst_array->length() == length_limit && + is_constant_zero(dst_pos)) { + flags &= ~LIR_OpArrayCopy::dst_range_check; + } + } // check from incoming constant values if (positive_constant(src_pos)) @@ -788,6 +871,28 @@ } } + IntConstant* src_int = src_pos->type()->as_IntConstant(); + IntConstant* dst_int = dst_pos->type()->as_IntConstant(); + if (src_int && dst_int) { + int s_offs = src_int->value(); + int d_offs = dst_int->value(); + if (src_int->value() >= dst_int->value()) { + flags &= ~LIR_OpArrayCopy::overlapping; + } + if (expected_type != NULL) { + BasicType t = expected_type->element_type()->basic_type(); + int element_size = type2aelembytes(t); + if (((arrayOopDesc::base_offset_in_bytes(t) + s_offs * element_size) % HeapWordSize == 0) && + ((arrayOopDesc::base_offset_in_bytes(t) + d_offs * element_size) % HeapWordSize == 0)) { + flags &= ~LIR_OpArrayCopy::unaligned; + } + } + } else if (src_pos == dst_pos || is_constant_zero(dst_pos)) { + // src and dest positions are the same, or dst is zero so assume + // nonoverlapping copy. + flags &= ~LIR_OpArrayCopy::overlapping; + } + if (src == dst) { // moving within a single array so no type checks are needed if (flags & LIR_OpArrayCopy::type_check) { diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_Optimizer.cpp --- a/src/share/vm/c1/c1_Optimizer.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_Optimizer.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -644,7 +644,7 @@ void NullCheckVisitor::do_InstanceOf (InstanceOf* x) {} void NullCheckVisitor::do_MonitorEnter (MonitorEnter* x) { nce()->handle_AccessMonitor(x); } void NullCheckVisitor::do_MonitorExit (MonitorExit* x) { nce()->handle_AccessMonitor(x); } -void NullCheckVisitor::do_Intrinsic (Intrinsic* x) { nce()->clear_last_explicit_null_check(); } +void NullCheckVisitor::do_Intrinsic (Intrinsic* x) { nce()->handle_Intrinsic(x); } void NullCheckVisitor::do_BlockBegin (BlockBegin* x) {} void NullCheckVisitor::do_Goto (Goto* x) {} void NullCheckVisitor::do_If (If* x) {} @@ -1023,6 +1023,12 @@ void NullCheckEliminator::handle_Intrinsic(Intrinsic* x) { if (!x->has_receiver()) { + if (x->id() == vmIntrinsics::_arraycopy) { + for (int i = 0; i < x->number_of_arguments(); i++) { + x->set_arg_needs_null_check(i, !set_contains(x->argument_at(i))); + } + } + // Be conservative clear_last_explicit_null_check(); return; diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_Runtime1.cpp --- a/src/share/vm/c1/c1_Runtime1.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_Runtime1.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -103,7 +103,10 @@ int Runtime1::_generic_arraycopy_cnt = 0; int Runtime1::_primitive_arraycopy_cnt = 0; int Runtime1::_oop_arraycopy_cnt = 0; +int Runtime1::_generic_arraycopystub_cnt = 0; int Runtime1::_arraycopy_slowcase_cnt = 0; +int Runtime1::_arraycopy_checkcast_cnt = 0; +int Runtime1::_arraycopy_checkcast_attempt_cnt = 0; int Runtime1::_new_type_array_slowcase_cnt = 0; int Runtime1::_new_object_array_slowcase_cnt = 0; int Runtime1::_new_instance_slowcase_cnt = 0; @@ -119,6 +122,32 @@ int Runtime1::_throw_incompatible_class_change_error_count = 0; int Runtime1::_throw_array_store_exception_count = 0; int Runtime1::_throw_count = 0; + +static int _byte_arraycopy_cnt = 0; +static int _short_arraycopy_cnt = 0; +static int _int_arraycopy_cnt = 0; +static int _long_arraycopy_cnt = 0; +static int _oop_arraycopy_cnt = 0; + +address Runtime1::arraycopy_count_address(BasicType type) { + switch (type) { + case T_BOOLEAN: + case T_BYTE: return (address)&_byte_arraycopy_cnt; + case T_CHAR: + case T_SHORT: return (address)&_short_arraycopy_cnt; + case T_FLOAT: + case T_INT: return (address)&_int_arraycopy_cnt; + case T_DOUBLE: + case T_LONG: return (address)&_long_arraycopy_cnt; + case T_ARRAY: + case T_OBJECT: return (address)&_oop_arraycopy_cnt; + default: + ShouldNotReachHere(); + return NULL; + } +} + + #endif // Simple helper to see if the caller of a runtime stub which @@ -1229,9 +1258,17 @@ tty->print_cr(" _handle_wrong_method_cnt: %d", SharedRuntime::_wrong_method_ctr); tty->print_cr(" _ic_miss_cnt: %d", SharedRuntime::_ic_miss_ctr); tty->print_cr(" _generic_arraycopy_cnt: %d", _generic_arraycopy_cnt); + tty->print_cr(" _generic_arraycopystub_cnt: %d", _generic_arraycopystub_cnt); + tty->print_cr(" _byte_arraycopy_cnt: %d", _byte_arraycopy_cnt); + tty->print_cr(" _short_arraycopy_cnt: %d", _short_arraycopy_cnt); + tty->print_cr(" _int_arraycopy_cnt: %d", _int_arraycopy_cnt); + tty->print_cr(" _long_arraycopy_cnt: %d", _long_arraycopy_cnt); tty->print_cr(" _primitive_arraycopy_cnt: %d", _primitive_arraycopy_cnt); - tty->print_cr(" _oop_arraycopy_cnt: %d", _oop_arraycopy_cnt); + tty->print_cr(" _oop_arraycopy_cnt (C): %d", Runtime1::_oop_arraycopy_cnt); + tty->print_cr(" _oop_arraycopy_cnt (stub): %d", _oop_arraycopy_cnt); tty->print_cr(" _arraycopy_slowcase_cnt: %d", _arraycopy_slowcase_cnt); + tty->print_cr(" _arraycopy_checkcast_cnt: %d", _arraycopy_checkcast_cnt); + tty->print_cr(" _arraycopy_checkcast_attempt_cnt:%d", _arraycopy_checkcast_attempt_cnt); tty->print_cr(" _new_type_array_slowcase_cnt: %d", _new_type_array_slowcase_cnt); tty->print_cr(" _new_object_array_slowcase_cnt: %d", _new_object_array_slowcase_cnt); diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/c1/c1_Runtime1.hpp --- a/src/share/vm/c1/c1_Runtime1.hpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/c1/c1_Runtime1.hpp Sun Apr 03 12:00:54 2011 +0200 @@ -94,7 +94,10 @@ static int _generic_arraycopy_cnt; static int _primitive_arraycopy_cnt; static int _oop_arraycopy_cnt; + static int _generic_arraycopystub_cnt; static int _arraycopy_slowcase_cnt; + static int _arraycopy_checkcast_cnt; + static int _arraycopy_checkcast_attempt_cnt; static int _new_type_array_slowcase_cnt; static int _new_object_array_slowcase_cnt; static int _new_instance_slowcase_cnt; @@ -174,7 +177,8 @@ static void trace_block_entry(jint block_id); #ifndef PRODUCT - static address throw_count_address() { return (address)&_throw_count; } + static address throw_count_address() { return (address)&_throw_count; } + static address arraycopy_count_address(BasicType type); #endif // directly accessible leaf routine diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/opto/library_call.cpp --- a/src/share/vm/opto/library_call.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/opto/library_call.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -4292,81 +4292,6 @@ return true; } - -// constants for computing the copy function -enum { - COPYFUNC_UNALIGNED = 0, - COPYFUNC_ALIGNED = 1, // src, dest aligned to HeapWordSize - COPYFUNC_CONJOINT = 0, - COPYFUNC_DISJOINT = 2 // src != dest, or transfer can descend -}; - -// Note: The condition "disjoint" applies also for overlapping copies -// where an descending copy is permitted (i.e., dest_offset <= src_offset). -static address -select_arraycopy_function(BasicType t, bool aligned, bool disjoint, const char* &name, bool dest_uninitialized) { - int selector = - (aligned ? COPYFUNC_ALIGNED : COPYFUNC_UNALIGNED) + - (disjoint ? COPYFUNC_DISJOINT : COPYFUNC_CONJOINT); - -#define RETURN_STUB(xxx_arraycopy) { \ - name = #xxx_arraycopy; \ - return StubRoutines::xxx_arraycopy(); } - -#define RETURN_STUB_PARM(xxx_arraycopy, parm) { \ - name = #xxx_arraycopy; \ - return StubRoutines::xxx_arraycopy(parm); } - - switch (t) { - case T_BYTE: - case T_BOOLEAN: - switch (selector) { - case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_arraycopy); - case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_disjoint_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_disjoint_arraycopy); - } - case T_CHAR: - case T_SHORT: - switch (selector) { - case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_arraycopy); - case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_disjoint_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_disjoint_arraycopy); - } - case T_INT: - case T_FLOAT: - switch (selector) { - case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_arraycopy); - case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_disjoint_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_disjoint_arraycopy); - } - case T_DOUBLE: - case T_LONG: - switch (selector) { - case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_arraycopy); - case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_disjoint_arraycopy); - case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_disjoint_arraycopy); - } - case T_ARRAY: - case T_OBJECT: - switch (selector) { - case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB_PARM(oop_arraycopy, dest_uninitialized); - case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB_PARM(arrayof_oop_arraycopy, dest_uninitialized); - case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB_PARM(oop_disjoint_arraycopy, dest_uninitialized); - case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB_PARM(arrayof_oop_disjoint_arraycopy, dest_uninitialized); - } - default: - ShouldNotReachHere(); - return NULL; - } - -#undef RETURN_STUB -#undef RETURN_STUB_PARM -} - //------------------------------basictype2arraycopy---------------------------- address LibraryCallKit::basictype2arraycopy(BasicType t, Node* src_offset, @@ -4399,7 +4324,7 @@ disjoint = true; } - return select_arraycopy_function(t, aligned, disjoint, name, dest_uninitialized); + return StubRoutines::select_arraycopy_function(t, aligned, disjoint, name, dest_uninitialized); } diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/runtime/stubRoutines.cpp --- a/src/share/vm/runtime/stubRoutines.cpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/runtime/stubRoutines.cpp Sun Apr 03 12:00:54 2011 +0200 @@ -433,3 +433,77 @@ #undef RETURN_STUB } + +// constants for computing the copy function +enum { + COPYFUNC_UNALIGNED = 0, + COPYFUNC_ALIGNED = 1, // src, dest aligned to HeapWordSize + COPYFUNC_CONJOINT = 0, + COPYFUNC_DISJOINT = 2 // src != dest, or transfer can descend +}; + +// Note: The condition "disjoint" applies also for overlapping copies +// where an descending copy is permitted (i.e., dest_offset <= src_offset). +address +StubRoutines::select_arraycopy_function(BasicType t, bool aligned, bool disjoint, const char* &name, bool dest_uninitialized) { + int selector = + (aligned ? COPYFUNC_ALIGNED : COPYFUNC_UNALIGNED) + + (disjoint ? COPYFUNC_DISJOINT : COPYFUNC_CONJOINT); + +#define RETURN_STUB(xxx_arraycopy) { \ + name = #xxx_arraycopy; \ + return StubRoutines::xxx_arraycopy(); } + +#define RETURN_STUB_PARM(xxx_arraycopy, parm) { \ + name = #xxx_arraycopy; \ + return StubRoutines::xxx_arraycopy(parm); } + + switch (t) { + case T_BYTE: + case T_BOOLEAN: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jbyte_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jbyte_disjoint_arraycopy); + } + case T_CHAR: + case T_SHORT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jshort_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jshort_disjoint_arraycopy); + } + case T_INT: + case T_FLOAT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jint_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jint_disjoint_arraycopy); + } + case T_DOUBLE: + case T_LONG: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_arraycopy); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB(jlong_disjoint_arraycopy); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB(arrayof_jlong_disjoint_arraycopy); + } + case T_ARRAY: + case T_OBJECT: + switch (selector) { + case COPYFUNC_CONJOINT | COPYFUNC_UNALIGNED: RETURN_STUB_PARM(oop_arraycopy, dest_uninitialized); + case COPYFUNC_CONJOINT | COPYFUNC_ALIGNED: RETURN_STUB_PARM(arrayof_oop_arraycopy, dest_uninitialized); + case COPYFUNC_DISJOINT | COPYFUNC_UNALIGNED: RETURN_STUB_PARM(oop_disjoint_arraycopy, dest_uninitialized); + case COPYFUNC_DISJOINT | COPYFUNC_ALIGNED: RETURN_STUB_PARM(arrayof_oop_disjoint_arraycopy, dest_uninitialized); + } + default: + ShouldNotReachHere(); + return NULL; + } + +#undef RETURN_STUB +#undef RETURN_STUB_PARM +} diff -r 08eb13460b3a -r 13bc79b5c9c8 src/share/vm/runtime/stubRoutines.hpp --- a/src/share/vm/runtime/stubRoutines.hpp Sat Apr 02 10:54:15 2011 -0700 +++ b/src/share/vm/runtime/stubRoutines.hpp Sun Apr 03 12:00:54 2011 +0200 @@ -282,6 +282,8 @@ static address addr_fpu_subnormal_bias2() { return (address)&_fpu_subnormal_bias2; } + static address select_arraycopy_function(BasicType t, bool aligned, bool disjoint, const char* &name, bool dest_uninitialized); + static address jbyte_arraycopy() { return _jbyte_arraycopy; } static address jshort_arraycopy() { return _jshort_arraycopy; } static address jint_arraycopy() { return _jint_arraycopy; }