/* * Copyright (c) 2012, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// Hook each parm in order. Stop looking at the first NULL. if (parm0 != NULL) { call->init_req(TypeFunc::Parms+0, parm0); if (parm1 != NULL) { call->init_req(TypeFunc::Parms+1, parm1); if (parm2 != NULL) { call->init_req(TypeFunc::Parms+2, parm2); if (parm3 != NULL) { call->init_req(TypeFunc::Parms+3, parm3); if (parm4 != NULL) { call->init_req(TypeFunc::Parms+4, parm4); if (parm5 != NULL) { call->init_req(TypeFunc::Parms+5, parm5); if (parm6 != NULL) { call->init_req(TypeFunc::Parms+6, parm6); if (parm7 != NULL) { call->init_req(TypeFunc::Parms+7, parm7); /* close each nested if ===> */ } } } } } } } }
assert(call->in(call->req()-1) != NULL, "must initialize all parms");
return call;
}
//------------------------------generate_guard--------------------------- // Helper function for generating guarded fast-slow graph structures. // The given 'test', if true, guards a slow path. If the test fails // then a fast path can be taken. (We generally hope it fails.) // In all cases, GraphKit::control() is updated to the fast path. // The returned value represents the control for the slow path. // The return value is never 'top'; it is either a valid control // or NULL if it is obvious that the slow path can never be taken. // Also, if region and the slow control are not NULL, the slow edge // is appended to the region.
Node* PhaseMacroExpand::generate_guard(Node** ctrl, Node* test, RegionNode* region, float true_prob) { if ((*ctrl)->is_top()) { // Already short circuited. return NULL;
} // Build an if node and its projections. // If test is true we take the slow path, which we assume is uncommon. if (_igvn.type(test) == TypeInt::ZERO) { // The slow branch is never taken. No need to build this guard. return NULL;
}
IfNode* iff = new IfNode(*ctrl, test, true_prob, COUNT_UNKNOWN);
transform_later(iff);
Node* if_slow = new IfTrueNode(iff);
transform_later(if_slow);
if (region != NULL) {
region->add_req(if_slow);
}
Node* if_fast = new IfFalseNode(iff);
transform_later(if_fast);
const TypeVect * vt = TypeVect::make(type, lane_count);
Node* mm = (*mem)->memory_at(C->get_alias_index(src_adr_type));
Node* masked_load = new LoadVectorMaskedNode(inline_block, mm, src_start,
src_adr_type, vt, mask_gen);
transform_later(masked_load);
mm = (*mem)->memory_at(C->get_alias_index(adr_type));
Node* masked_store = new StoreVectorMaskedNode(inline_block, mm, dst_start,
masked_load, adr_type, mask_gen);
transform_later(masked_store);
// Convergence region for inline_block and stub_block.
*exit_block = new RegionNode(3);
transform_later(*exit_block);
(*exit_block)->init_req(1, inline_block);
*result_memory = new PhiNode(*exit_block, Type::MEMORY, adr_type);
transform_later(*result_memory);
(*result_memory)->init_req(1, masked_store);
// if the offsets are the same, we can treat the memory regions as // disjoint, because either the memory regions are in different arrays, // or they are identical (which we can treat as disjoint.) We can also // treat a copy with a destination index less that the source index // as disjoint since a low->high copy will work correctly in this case. if (src_offset_inttype != NULL && src_offset_inttype->is_con() &&
dest_offset_inttype != NULL && dest_offset_inttype->is_con()) { // both indices are constants int s_offs = src_offset_inttype->get_con(); int d_offs = dest_offset_inttype->get_con(); int element_size = type2aelembytes(t);
aligned = ((arrayOopDesc::base_offset_in_bytes(t) + s_offs * element_size) % HeapWordSize == 0) &&
((arrayOopDesc::base_offset_in_bytes(t) + d_offs * element_size) % HeapWordSize == 0); if (s_offs >= d_offs) disjoint = true;
} elseif (src_offset == dest_offset && src_offset != NULL) { // This can occur if the offsets are identical non-constants.
disjoint = true;
}
// See if this is the initialization of a newly-allocated array. // If so, we will take responsibility here for initializing it to zero. // (Note: Because tightly_coupled_allocation performs checks on the // out-edges of the dest, we need to avoid making derived pointers // from it until we have checked its uses.) if (ReduceBulkZeroing
&& !(UseTLAB && ZeroTLAB) // pointless if already zeroed
&& basic_elem_type != T_CONFLICT // avoid corner case
&& !src->eqv_uncast(dest)
&& alloc != NULL
&& _igvn.find_int_con(alloc->in(AllocateNode::ALength), 1) > 0) {
assert(ac->is_alloc_tightly_coupled(), "sanity"); // acopy to uninitialized tightly coupled allocations // needs zeroing outside the copy range // and the acopy itself will be to uninitialized memory
acopy_to_uninitialized = true; if (alloc->maybe_set_complete(&_igvn)) { // "You break it, you buy it."
InitializeNode* init = alloc->initialization();
assert(init->is_complete(), "we just did this");
init->set_complete_with_arraycopy();
assert(dest->is_CheckCastPP(), "sanity");
assert(dest->in(0)->in(0) == init, "dest pinned");
adr_type = TypeRawPtr::BOTTOM; // all initializations are into raw memory // From this point on, every exit path is responsible for // initializing any non-copied parts of the object to zero. // Also, if this flag is set we make sure that arraycopy interacts properly // with G1, eliding pre-barriers. See CR 6627983.
dest_needs_zeroing = true;
} else { // dest_need_zeroing = false;
}
} else { // No zeroing elimination needed here.
alloc = NULL;
acopy_to_uninitialized = false; //original_dest = dest; //dest_needs_zeroing = false;
}
uint alias_idx = C->get_alias_index(adr_type);
// Results are placed here: enum { fast_path = 1, // normal void-returning assembly stub
checked_path = 2, // special assembly stub with cleanup
slow_call_path = 3, // something went wrong; call the VM
zero_path = 4, // bypass when length of copy is zero
bcopy_path = 5, // copy primitive array by 64-bit blocks
PATH_LIMIT = 6
};
RegionNode* result_region = new RegionNode(PATH_LIMIT);
PhiNode* result_i_o = new PhiNode(result_region, Type::ABIO);
PhiNode* result_memory = new PhiNode(result_region, Type::MEMORY, adr_type);
assert(adr_type != TypePtr::BOTTOM, "must be RawMem or a T[] slice");
transform_later(result_region);
transform_later(result_i_o);
transform_later(result_memory);
// (6) length must not be negative. if (!length_never_negative) {
generate_negative_guard(&local_ctrl, copy_length, slow_region);
}
// copy_length is 0. if (dest_needs_zeroing) {
assert(!local_ctrl->is_top(), "no ctrl?");
Node* dest_length = alloc->in(AllocateNode::ALength); if (copy_length->eqv_uncast(dest_length)
|| _igvn.find_int_con(dest_length, 1) <= 0) { // There is no zeroing to do. No need for a secondary raw memory barrier.
} else { // Clear the whole thing since there are no source elements to copy.
generate_clear_array(local_ctrl, local_mem,
adr_type, dest, basic_elem_type,
intcon(0), NULL,
alloc->in(AllocateNode::AllocSize)); // Use a secondary InitializeNode as raw memory barrier. // Currently it is needed only on this path since other // paths have stub or runtime calls as raw memory barriers.
MemBarNode* mb = MemBarNode::make(C, Op_Initialize,
Compile::AliasIdxRaw,
top());
transform_later(mb);
mb->set_req(TypeFunc::Control,local_ctrl);
mb->set_req(TypeFunc::Memory, local_mem->memory_at(Compile::AliasIdxRaw));
local_ctrl = transform_later(new ProjNode(mb, TypeFunc::Control));
local_mem->set_memory_at(Compile::AliasIdxRaw, transform_later(new ProjNode(mb, TypeFunc::Memory)));
InitializeNode* init = mb->as_Initialize();
init->set_complete(&_igvn); // (there is no corresponding AllocateNode)
}
}
// Present the results of the fast call.
result_region->init_req(zero_path, local_ctrl);
result_i_o ->init_req(zero_path, local_io);
result_memory->init_req(zero_path, local_mem->memory_at(alias_idx));
}
if (!(*ctrl)->is_top() && dest_needs_zeroing) { // We have to initialize the *uncopied* part of the array to zero. // The copy destination is the slice dest[off..off+len]. The other slices // are dest_head = dest[0..off] and dest_tail = dest[off+len..dest.length].
Node* dest_size = alloc->in(AllocateNode::AllocSize);
Node* dest_length = alloc->in(AllocateNode::ALength);
Node* dest_tail = transform_later( new AddINode(dest_offset, copy_length));
// If there is a head section that needs zeroing, do it now. if (_igvn.find_int_con(dest_offset, -1) != 0) {
generate_clear_array(*ctrl, mem,
adr_type, dest, basic_elem_type,
intcon(0), dest_offset,
NULL);
}
// Next, perform a dynamic check on the tail length. // It is often zero, and we can win big if we prove this. // There are two wins: Avoid generating the ClearArray // with its attendant messy index arithmetic, and upgrade // the copy to a more hardware-friendly word size of 64 bits.
Node* tail_ctl = NULL; if (!(*ctrl)->is_top() && !dest_tail->eqv_uncast(dest_length)) {
Node* cmp_lt = transform_later( new CmpINode(dest_tail, dest_length) );
Node* bol_lt = transform_later( new BoolNode(cmp_lt, BoolTest::lt) );
tail_ctl = generate_slow_guard(ctrl, bol_lt, NULL);
assert(tail_ctl != NULL || !(*ctrl)->is_top(), "must be an outcome");
}
// At this point, let's assume there is no tail. if (!(*ctrl)->is_top() && alloc != NULL && basic_elem_type != T_OBJECT) { // There is no tail. Try an upgrade to a 64-bit copy. bool didit = false;
{
Node* local_ctrl = *ctrl, *local_io = *io;
MergeMemNode* local_mem = MergeMemNode::make(mem);
transform_later(local_mem);
didit = generate_block_arraycopy(&local_ctrl, &local_mem, local_io,
adr_type, basic_elem_type, alloc,
src, src_offset, dest, dest_offset,
dest_size, acopy_to_uninitialized); if (didit) { // Present the results of the block-copying fast call.
result_region->init_req(bcopy_path, local_ctrl);
result_i_o ->init_req(bcopy_path, local_io);
result_memory->init_req(bcopy_path, local_mem->memory_at(alias_idx));
}
} if (didit) {
*ctrl = top(); // no regular fast path
}
}
BasicType copy_type = basic_elem_type;
assert(basic_elem_type != T_ARRAY, "caller must fix this"); if (!(*ctrl)->is_top() && copy_type == T_OBJECT) { // If src and dest have compatible element types, we can copy bits. // Types S[] and D[] are compatible if D is a supertype of S. // // If they are not, we will use checked_oop_disjoint_arraycopy, // which performs a fast optimistic per-oop check, and backs off // further to JVM_ArrayCopy on the first per-oop check that fails. // (Actually, we don't move raw bits only; the GC requires card marks.)
// We don't need a subtype check for validated copies and Object[].clone() bool skip_subtype_check = ac->is_arraycopy_validated() || ac->is_copyof_validated() ||
ac->is_copyofrange_validated() || ac->is_clone_oop_array(); if (!skip_subtype_check) { // Get the klass* for both src and dest
Node* src_klass = ac->in(ArrayCopyNode::SrcKlass);
Node* dest_klass = ac->in(ArrayCopyNode::DestKlass);
assert(src_klass != NULL && dest_klass != NULL, "should have klasses");
// Generate the subtype check. // This might fold up statically, or then again it might not. // // Non-static example: Copying List<String>.elements to a new String[]. // The backing store for a List<String> is always an Object[], // but its elements are always type String, if the generic types // are correct at the source level. // // Test S[] against D[], not S against D, because (probably) // the secondary supertype cache is less busy for S[] than S. // This usually only matters when D is an interface.
Node* not_subtype_ctrl = Phase::gen_subtype_check(src_klass, dest_klass, ctrl, mem, _igvn); // Plug failing path into checked_oop_disjoint_arraycopy if (not_subtype_ctrl != top()) {
Node* local_ctrl = not_subtype_ctrl;
MergeMemNode* local_mem = MergeMemNode::make(mem);
transform_later(local_mem);
// (At this point we can assume disjoint_bases, since types differ.) int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset());
Node* p1 = basic_plus_adr(dest_klass, ek_offset);
Node* n1 = LoadKlassNode::make(_igvn, NULL, C->immutable_memory(), p1, TypeRawPtr::BOTTOM);
Node* dest_elem_klass = transform_later(n1);
Node* cv = generate_checkcast_arraycopy(&local_ctrl, &local_mem,
adr_type,
dest_elem_klass,
src, src_offset, dest, dest_offset,
ConvI2X(copy_length), acopy_to_uninitialized); if (cv == NULL) cv = intcon(-1); // failure (no stub available)
checked_control = local_ctrl;
checked_i_o = *io;
checked_mem = local_mem->memory_at(alias_idx);
checked_value = cv;
}
} // At this point we know we do not need type checks on oop stores.
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2(); if (!bs->array_copy_requires_gc_barriers(alloc != NULL, copy_type, false, false, BarrierSetC2::Expansion)) { // If we do not need gc barriers, copy using the jint or jlong stub.
copy_type = LP64_ONLY(UseCompressedOops ? T_INT : T_LONG) NOT_LP64(T_INT);
assert(type2aelembytes(basic_elem_type) == type2aelembytes(copy_type), "sizes agree");
}
}
bool is_partial_array_copy = false; if (!(*ctrl)->is_top()) { // Generate the fast path, if possible.
Node* local_ctrl = *ctrl;
MergeMemNode* local_mem = MergeMemNode::make(mem);
transform_later(local_mem);
is_partial_array_copy = generate_unchecked_arraycopy(&local_ctrl, &local_mem,
adr_type, copy_type, disjoint_bases,
src, src_offset, dest, dest_offset,
ConvI2X(copy_length), acopy_to_uninitialized);
// Present the results of the fast call.
result_region->init_req(fast_path, local_ctrl);
result_i_o ->init_req(fast_path, *io);
result_memory->init_req(fast_path, local_mem->memory_at(alias_idx));
}
// Here are all the slow paths up to this point, in one bundle:
assert(slow_region != NULL, "allocated on entry");
slow_control = slow_region;
DEBUG_ONLY(slow_region = (RegionNode*)badAddress);
*ctrl = checked_control; if (!(*ctrl)->is_top()) { // Clean up after the checked call. // The returned value is either 0 or -1^K, // where K = number of partially transferred array elements.
Node* cmp = new CmpINode(checked_value, intcon(0));
transform_later(cmp);
Node* bol = new BoolNode(cmp, BoolTest::eq);
transform_later(bol);
IfNode* iff = new IfNode(*ctrl, bol, PROB_MAX, COUNT_UNKNOWN);
transform_later(iff);
// If it is 0, we are done, so transfer to the end.
Node* checks_done = new IfTrueNode(iff);
transform_later(checks_done);
result_region->init_req(checked_path, checks_done);
result_i_o ->init_req(checked_path, checked_i_o);
result_memory->init_req(checked_path, checked_mem);
// If it is not zero, merge into the slow call.
*ctrl = new IfFalseNode(iff);
transform_later(*ctrl);
RegionNode* slow_reg2 = new RegionNode(3);
PhiNode* slow_i_o2 = new PhiNode(slow_reg2, Type::ABIO);
PhiNode* slow_mem2 = new PhiNode(slow_reg2, Type::MEMORY, adr_type);
transform_later(slow_reg2);
transform_later(slow_i_o2);
transform_later(slow_mem2);
slow_reg2 ->init_req(1, slow_control);
slow_i_o2 ->init_req(1, slow_i_o);
slow_mem2 ->init_req(1, slow_mem);
slow_reg2 ->init_req(2, *ctrl);
slow_i_o2 ->init_req(2, checked_i_o);
slow_mem2 ->init_req(2, checked_mem);
if (alloc != NULL) { // We'll restart from the very beginning, after zeroing the whole thing. // This can cause double writes, but that's OK since dest is brand new. // So we ignore the low 31 bits of the value returned from the stub.
} else { // We must continue the copy exactly where it failed, or else // another thread might see the wrong number of writes to dest.
Node* checked_offset = new XorINode(checked_value, intcon(-1));
Node* slow_offset = new PhiNode(slow_reg2, TypeInt::INT);
transform_later(checked_offset);
transform_later(slow_offset);
slow_offset->init_req(1, intcon(0));
slow_offset->init_req(2, checked_offset);
// Adjust the arguments by the conditionally incoming offset.
Node* src_off_plus = new AddINode(src_offset, slow_offset);
transform_later(src_off_plus);
Node* dest_off_plus = new AddINode(dest_offset, slow_offset);
transform_later(dest_off_plus);
Node* length_minus = new SubINode(copy_length, slow_offset);
transform_later(length_minus);
// Tweak the node variables to adjust the code produced below:
src_offset = src_off_plus;
dest_offset = dest_off_plus;
copy_length = length_minus;
}
}
*ctrl = slow_control; if (!(*ctrl)->is_top()) {
Node* local_ctrl = *ctrl, *local_io = slow_i_o;
MergeMemNode* local_mem = MergeMemNode::make(mem);
transform_later(local_mem);
// Generate the slow path, if needed.
local_mem->set_memory_at(alias_idx, slow_mem);
// mem no longer guaranteed to stay a MergeMemNode
Node* out_mem = mem;
DEBUG_ONLY(mem = NULL);
// The memory edges above are precise in order to model effects around // array copies accurately to allow value numbering of field loads around // arraycopy. Such field loads, both before and after, are common in Java // collections and similar classes involving header/array data structures. // // But with low number of register or when some registers are used or killed // by arraycopy calls it causes registers spilling on stack. See 6544710. // The next memory barrier is added to avoid it. If the arraycopy can be // optimized away (which it can, sometimes) then we can manually remove // the membar also. // // Do not let reads from the cloned object float above the arraycopy. if (alloc != NULL && !alloc->initialization()->does_not_escape()) { // Do not let stores that initialize this object be reordered with // a subsequent store that would make this object accessible by // other threads.
insert_mem_bar(ctrl, &out_mem, Op_MemBarStoreStore);
} else {
insert_mem_bar(ctrl, &out_mem, Op_MemBarCPUOrder);
}
if (is_partial_array_copy) {
assert((*ctrl)->is_Proj(), "MemBar control projection");
assert((*ctrl)->in(0)->isa_MemBar(), "MemBar node");
(*ctrl)->in(0)->isa_MemBar()->set_trailing_partial_array_copy();
}
#ifdef ASSERT const TypeOopPtr* dest_t = _igvn.type(dest)->is_oopptr(); if (dest_t->is_known_instance() && !is_partial_array_copy) {
ArrayCopyNode* ac = NULL;
assert(ArrayCopyNode::may_modify(dest_t, (*ctrl)->in(0)->as_MemBar(), &_igvn, ac), "dependency on arraycopy lost");
assert(ac == NULL, "no arraycopy anymore");
} #endif
return out_mem;
}
// Helper for initialization of arrays, creating a ClearArray. // It writes zero bits in [start..end), within the body of an array object. // The memory effects are all chained onto the 'adr_type' alias category. // // Since the object is otherwise uninitialized, we are free // to put a little "slop" around the edges of the cleared area, // as long as it does not go back into the array's header, // or beyond the array end within the heap. // // The lower edge can be rounded down to the nearest jint and the // upper edge can be rounded up to the nearest MinObjAlignmentInBytes. // // Arguments: // adr_type memory slice where writes are generated // dest oop of the destination array // basic_elem_type element type of the destination // slice_idx array index of first element to store // slice_len number of elements to store (or NULL) // dest_size total size in bytes of the array object // // Exactly one of slice_len or dest_size must be non-NULL. // If dest_size is non-NULL, zeroing extends to the end of the object. // If slice_len is non-NULL, the slice_idx value must be a constant. void PhaseMacroExpand::generate_clear_array(Node* ctrl, MergeMemNode* merge_mem, const TypePtr* adr_type,
Node* dest,
BasicType basic_elem_type,
Node* slice_idx,
Node* slice_len,
Node* dest_size) { // one or the other but not both of slice_len and dest_size:
assert((slice_len != NULL? 1: 0) + (dest_size != NULL? 1: 0) == 1, ""); if (slice_len == NULL) slice_len = top(); if (dest_size == NULL) dest_size = top();
uint alias_idx = C->get_alias_index(adr_type);
// operate on this memory slice:
Node* mem = merge_mem->memory_at(alias_idx); // memory slice to operate on
// scaling and rounding of indexes: int scale = exact_log2(type2aelembytes(basic_elem_type)); int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type); int clear_low = (-1 << scale) & (BytesPerInt - 1); int bump_bit = (-1 << scale) & BytesPerInt;
bool PhaseMacroExpand::generate_block_arraycopy(Node** ctrl, MergeMemNode** mem, Node* io, const TypePtr* adr_type,
BasicType basic_elem_type,
AllocateNode* alloc,
Node* src, Node* src_offset,
Node* dest, Node* dest_offset,
Node* dest_size, bool dest_uninitialized) { // See if there is an advantage from block transfer. int scale = exact_log2(type2aelembytes(basic_elem_type)); if (scale >= LogBytesPerLong) returnfalse; // it is already a block transfer
// Look at the alignment of the starting offsets. int abase = arrayOopDesc::base_offset_in_bytes(basic_elem_type);
intptr_t src_off_con = (intptr_t) _igvn.find_int_con(src_offset, -1);
intptr_t dest_off_con = (intptr_t) _igvn.find_int_con(dest_offset, -1); if (src_off_con < 0 || dest_off_con < 0) { // At present, we can only understand constants. returnfalse;
}
// Helper function; generates code for the slow case. // We make a call to a runtime method which emulates the native method, // but without the native wrapper overhead.
MergeMemNode* PhaseMacroExpand::generate_slow_arraycopy(ArrayCopyNode *ac,
Node** ctrl, Node* mem, Node** io, const TypePtr* adr_type,
Node* src, Node* src_offset,
Node* dest, Node* dest_offset,
Node* copy_length, bool dest_uninitialized) {
assert(!dest_uninitialized, "Invariant");
// When src is negative and arraycopy is before an infinite loop,_callprojs.fallthrough_ioproj // could be NULL. Skip clone and update NULL fallthrough_ioproj. if (_callprojs.fallthrough_ioproj != NULL) {
*io = _callprojs.fallthrough_ioproj->clone();
transform_later(*io);
} else {
*io = NULL;
}
address copyfunc_addr = StubRoutines::checkcast_arraycopy(dest_uninitialized); if (copyfunc_addr == NULL) { // Stub was not generated, go slow path. return NULL;
}
// Pick out the parameters required to perform a store-check // for the target array. This is an optimistic check. It will // look in each non-null element's class, at the desired klass's // super_check_offset, for the desired klass. int sco_offset = in_bytes(Klass::super_check_offset_offset());
Node* p3 = basic_plus_adr(dest_elem_klass, sco_offset);
Node* n3 = new LoadINode(NULL, *mem /*memory(p3)*/, p3, _igvn.type(p3)->is_ptr(), TypeInt::INT, MemNode::unordered);
Node* check_offset = ConvI2X(transform_later(n3));
Node* check_value = dest_elem_klass;
assert(ac->is_arraycopy() || ac->is_arraycopy_validated(), "should be an arraycopy");
// Compile time checks. If any of these checks cannot be verified at compile time, // we do not make a fast path for this call. Instead, we let the call remain as it // is. The checks we choose to mandate at compile time are: // // (1) src and dest are arrays. const Type* src_type = src->Value(&_igvn); const Type* dest_type = dest->Value(&_igvn); const TypeAryPtr* top_src = src_type->isa_aryptr(); const TypeAryPtr* top_dest = dest_type->isa_aryptr();
if (src_elem == T_CONFLICT || dest_elem == T_CONFLICT) { // Conservatively insert a memory barrier on all memory slices. // Do not let writes into the source float below the arraycopy.
{
Node* mem = ac->in(TypeFunc::Memory);
insert_mem_bar(&ctrl, &mem, Op_MemBarCPUOrder);
// Call StubRoutines::generic_arraycopy stub.
Node* mem = generate_arraycopy(ac, NULL, &ctrl, merge_mem, &io,
TypeRawPtr::BOTTOM, T_CONFLICT,
src, src_offset, dest, dest_offset, length, // If a negative length guard was generated for the ArrayCopyNode, // the length of the array can never be negative. false, ac->has_negative_length_guard()); return;
}
assert(!ac->is_arraycopy_validated() || (src_elem == dest_elem && dest_elem != T_VOID), "validated but different basic types");
// (2) src and dest arrays must have elements of the same BasicType // Figure out the size and type of the elements we will be copying. if (src_elem != dest_elem || dest_elem == T_VOID) { // The component types are not the same or are not recognized. Punt. // (But, avoid the native method wrapper to JVM_ArrayCopy.)
{
Node* mem = ac->in(TypeFunc::Memory);
merge_mem = generate_slow_arraycopy(ac, &ctrl, mem, &io, TypePtr::BOTTOM, src, src_offset, dest, dest_offset, length, false);
}
//--------------------------------------------------------------------------- // We will make a fast path for this call to arraycopy.
// We have the following tests left to perform: // // (3) src and dest must not be null. // (4) src_offset must not be negative. // (5) dest_offset must not be negative. // (6) length must not be negative. // (7) src_offset + length must not exceed length of src. // (8) dest_offset + length must not exceed length of dest. // (9) each element of an oop array must be assignable
{
Node* mem = ac->in(TypeFunc::Memory);
merge_mem = MergeMemNode::make(mem);
transform_later(merge_mem);
}
RegionNode* slow_region = new RegionNode(1);
transform_later(slow_region);
if (!ac->is_arraycopy_validated()) { // (3) operands must not be null // We currently perform our null checks with the null_check routine. // This means that the null exceptions will be reported in the caller // rather than (correctly) reported inside of the native arraycopy call. // This should be corrected, given time. We do our null check with the // stack pointer restored. // null checks done library_call.cpp
// (4) src_offset must not be negative.
generate_negative_guard(&ctrl, src_offset, slow_region);
// (5) dest_offset must not be negative.
generate_negative_guard(&ctrl, dest_offset, slow_region);
// (6) length must not be negative (moved to generate_arraycopy()). // generate_negative_guard(length, slow_region);
// (7) src_offset + length must not exceed length of src.
Node* alen = ac->in(ArrayCopyNode::SrcLen);
assert(alen != NULL, "need src len");
generate_limit_guard(&ctrl,
src_offset, length,
alen,
slow_region);
// (8) dest_offset + length must not exceed length of dest.
alen = ac->in(ArrayCopyNode::DestLen);
assert(alen != NULL, "need dest len");
generate_limit_guard(&ctrl,
dest_offset, length,
alen,
slow_region);
// (9) each element of an oop array must be assignable // The generate_arraycopy subroutine checks this.
} // This is where the memory effects are placed: const TypePtr* adr_type = NULL; if (ac->_dest_type != TypeOopPtr::BOTTOM) {
adr_type = ac->_dest_type->add_offset(Type::OffsetBot)->is_ptr();
} else {
adr_type = TypeAryPtr::get_array_body_type(dest_elem);
}
generate_arraycopy(ac, alloc, &ctrl, merge_mem, &io,
adr_type, dest_elem,
src, src_offset, dest, dest_offset, length, // If a negative length guard was generated for the ArrayCopyNode, // the length of the array can never be negative. false, ac->has_negative_length_guard(), slow_region);
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.9 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.