/*
 * Copyright (c) 2003, 2025, 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.
 *
 */

#include "asm/macroAssembler.hpp"
#include "gc/shared/barrierSet.hpp"
#include "gc/shared/barrierSetAssembler.hpp"
#include "oops/objArrayKlass.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "stubGenerator_x86_64.hpp"
#ifdef COMPILER2
#include "opto/c2_globals.hpp"
#endif
#if INCLUDE_JVMCI
#include "jvmci/jvmci_globals.hpp"
#endif

#define __ _masm->

#define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8)

#ifdef PRODUCT
#define BLOCK_COMMENT(str) /* nothing */
#else
#define BLOCK_COMMENT(str) __ block_comment(str)
#endif // PRODUCT

#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")

#ifdef PRODUCT
#define INC_COUNTER_NP(counter, rscratch) ((void)0)
#else
#define INC_COUNTER_NP(counter, rscratch) \
BLOCK_COMMENT("inc_counter " #counter); \
inc_counter_np(_masm, counter, rscratch);

static void inc_counter_np(MacroAssembler* _masm, uint& counter, Register rscratch) {
  __ incrementl(ExternalAddress((address)&counter), rscratch);
}

#if COMPILER2_OR_JVMCI
static uint& get_profile_ctr(int shift) {
  if (shift == 0) {
    return SharedRuntime::_jbyte_array_copy_ctr;
  } else if (shift == 1) {
    return SharedRuntime::_jshort_array_copy_ctr;
  } else if (shift == 2) {
    return SharedRuntime::_jint_array_copy_ctr;
  } else {
    assert(shift == 3, "");
    return SharedRuntime::_jlong_array_copy_ctr;
  }
}
#endif // COMPILER2_OR_JVMCI
#endif // !PRODUCT

void StubGenerator::generate_arraycopy_stubs() {
  address entry;
  address entry_jbyte_arraycopy;
  address entry_jshort_arraycopy;
  address entry_jint_arraycopy;
  address entry_oop_arraycopy;
  address entry_jlong_arraycopy;
  address entry_checkcast_arraycopy;

  StubRoutines::_jbyte_disjoint_arraycopy  = generate_disjoint_byte_copy(&entry);
  StubRoutines::_jbyte_arraycopy           = generate_conjoint_byte_copy(entry, &entry_jbyte_arraycopy);

  StubRoutines::_jshort_disjoint_arraycopy = generate_disjoint_short_copy(&entry);
  StubRoutines::_jshort_arraycopy          = generate_conjoint_short_copy(entry, &entry_jshort_arraycopy);

  StubRoutines::_jint_disjoint_arraycopy   = generate_disjoint_int_oop_copy(StubGenStubId::jint_disjoint_arraycopy_id, &entry);
  StubRoutines::_jint_arraycopy            = generate_conjoint_int_oop_copy(StubGenStubId::jint_arraycopy_id, entry, &entry_jint_arraycopy);

  StubRoutines::_jlong_disjoint_arraycopy  = generate_disjoint_long_oop_copy(StubGenStubId::jlong_disjoint_arraycopy_id, &entry);
  StubRoutines::_jlong_arraycopy           = generate_conjoint_long_oop_copy(StubGenStubId::jlong_arraycopy_id, entry, &entry_jlong_arraycopy);
  if (UseCompressedOops) {
    StubRoutines::_oop_disjoint_arraycopy  = generate_disjoint_int_oop_copy(StubGenStubId::oop_disjoint_arraycopy_id, &entry);
    StubRoutines::_oop_arraycopy           = generate_conjoint_int_oop_copy(StubGenStubId::oop_arraycopy_id, entry, &entry_oop_arraycopy);
    StubRoutines::_oop_disjoint_arraycopy_uninit  = generate_disjoint_int_oop_copy(StubGenStubId::oop_disjoint_arraycopy_uninit_id, &entry);
    StubRoutines::_oop_arraycopy_uninit           = generate_conjoint_int_oop_copy(StubGenStubId::oop_arraycopy_uninit_id, entry, nullptr);
  } else {
    StubRoutines::_oop_disjoint_arraycopy  = generate_disjoint_long_oop_copy(StubGenStubId::oop_disjoint_arraycopy_id, &entry);
    StubRoutines::_oop_arraycopy           = generate_conjoint_long_oop_copy(StubGenStubId::oop_arraycopy_id, entry, &entry_oop_arraycopy);
    StubRoutines::_oop_disjoint_arraycopy_uninit  = generate_disjoint_long_oop_copy(StubGenStubId::oop_disjoint_arraycopy_uninit_id, &entry);
    StubRoutines::_oop_arraycopy_uninit           = generate_conjoint_long_oop_copy(StubGenStubId::oop_arraycopy_uninit_id, entry, nullptr);
  }

  StubRoutines::_checkcast_arraycopy        = generate_checkcast_copy(StubGenStubId::checkcast_arraycopy_id, &entry_checkcast_arraycopy);
  StubRoutines::_checkcast_arraycopy_uninit = generate_checkcast_copy(StubGenStubId::checkcast_arraycopy_uninit_id, nullptr);

  StubRoutines::_unsafe_arraycopy    = generate_unsafe_copy(entry_jbyte_arraycopy,
                                                            entry_jshort_arraycopy,
                                                            entry_jint_arraycopy,
                                                            entry_jlong_arraycopy);
  StubRoutines::_generic_arraycopy   = generate_generic_copy(entry_jbyte_arraycopy,
                                                             entry_jshort_arraycopy,
                                                             entry_jint_arraycopy,
                                                             entry_oop_arraycopy,
                                                             entry_jlong_arraycopy,
                                                             entry_checkcast_arraycopy);

  StubRoutines::_jbyte_fill = generate_fill(StubGenStubId::jbyte_fill_id);
  StubRoutines::_jshort_fill = generate_fill(StubGenStubId::jshort_fill_id);
  StubRoutines::_jint_fill = generate_fill(StubGenStubId::jint_fill_id);
  StubRoutines::_arrayof_jbyte_fill = generate_fill(StubGenStubId::arrayof_jbyte_fill_id);
  StubRoutines::_arrayof_jshort_fill = generate_fill(StubGenStubId::arrayof_jshort_fill_id);
  StubRoutines::_arrayof_jint_fill = generate_fill(StubGenStubId::arrayof_jint_fill_id);

  StubRoutines::_unsafe_setmemory = generate_unsafe_setmemory(StubRoutines::_jbyte_fill);

  // We don't generate specialized code for HeapWord-aligned source
  // arrays, so just use the code we've already generated
  StubRoutines::_arrayof_jbyte_disjoint_arraycopy  = StubRoutines::_jbyte_disjoint_arraycopy;
  StubRoutines::_arrayof_jbyte_arraycopy           = StubRoutines::_jbyte_arraycopy;

  StubRoutines::_arrayof_jshort_disjoint_arraycopy = StubRoutines::_jshort_disjoint_arraycopy;
  StubRoutines::_arrayof_jshort_arraycopy          = StubRoutines::_jshort_arraycopy;

  StubRoutines::_arrayof_jint_disjoint_arraycopy   = StubRoutines::_jint_disjoint_arraycopy;
  StubRoutines::_arrayof_jint_arraycopy            = StubRoutines::_jint_arraycopy;

  StubRoutines::_arrayof_jlong_disjoint_arraycopy  = StubRoutines::_jlong_disjoint_arraycopy;
  StubRoutines::_arrayof_jlong_arraycopy           = StubRoutines::_jlong_arraycopy;

  StubRoutines::_arrayof_oop_disjoint_arraycopy    = StubRoutines::_oop_disjoint_arraycopy;
  StubRoutines::_arrayof_oop_arraycopy             = StubRoutines::_oop_arraycopy;

  StubRoutines::_arrayof_oop_disjoint_arraycopy_uninit    = StubRoutines::_oop_disjoint_arraycopy_uninit;
  StubRoutines::_arrayof_oop_arraycopy_uninit             = StubRoutines::_oop_arraycopy_uninit;
}


// Verify that a register contains clean 32-bits positive value
// (high 32-bits are 0) so it could be used in 64-bits shifts.
//
//  Input:
//    Rint  -  32-bits value
//    Rtmp  -  scratch
//
void StubGenerator::assert_clean_int(Register Rint, Register Rtmp) {
#ifdef ASSERT
  Label L;
  assert_different_registers(Rtmp, Rint);
  __ movslq(Rtmp, Rint);
  __ cmpq(Rtmp, Rint);
  __ jcc(Assembler::equal, L);
  __ stop("high 32-bits of int value are not 0");
  __ bind(L);
#endif
}


//  Generate overlap test for array copy stubs
//
//  Input:
//     c_rarg0 - from
//     c_rarg1 - to
//     c_rarg2 - element count
//
//  Output:
//     rax   - &from[element count - 1]
//
void StubGenerator::array_overlap_test(address no_overlap_target, Label* NOLp, Address::ScaleFactor sf) {
  const Register from     = c_rarg0;
  const Register to       = c_rarg1;
  const Register count    = c_rarg2;
  const Register end_from = rax;

  __ cmpptr(to, from);
  __ lea(end_from, Address(from, count, sf, 0));
  if (NOLp == nullptr) {
    RuntimeAddress no_overlap(no_overlap_target);
    __ jump_cc(Assembler::belowEqual, no_overlap);
    __ cmpptr(to, end_from);
    __ jump_cc(Assembler::aboveEqual, no_overlap);
  } else {
    __ jcc(Assembler::belowEqual, (*NOLp));
    __ cmpptr(to, end_from);
    __ jcc(Assembler::aboveEqual, (*NOLp));
  }
}


// Copy big chunks forward
//
// Inputs:
//   end_from     - source arrays end address
//   end_to       - destination array end address
//   qword_count  - 64-bits element count, negative
//   tmp1         - scratch
//   L_copy_bytes - entry label
//   L_copy_8_bytes  - exit  label
//
void StubGenerator::copy_bytes_forward(Register end_from, Register end_to,
                                       Register qword_count, Register tmp1,
                                       Register tmp2, Label& L_copy_bytes,
                                       Label& L_copy_8_bytes, DecoratorSet decorators,
                                       BasicType type) {
  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
  DEBUG_ONLY(__ stop("enter at entry label, not here"));
  Label L_loop;
  __ align(OptoLoopAlignment);
  if (UseUnalignedLoadStores) {
    Label L_end;
    __ BIND(L_loop);
    if (UseAVX >= 2) {
      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(end_from, qword_count, Address::times_8, -56),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(end_to, qword_count, Address::times_8, -56), xmm0,
                        tmp1, tmp2, xmm1);

      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(end_from, qword_count, Address::times_8, -24),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(end_to, qword_count, Address::times_8, -24), xmm0,
                        tmp1, tmp2, xmm1);
    } else {
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -56),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -56), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -40),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -40), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -24),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -24), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -8),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -8), xmm0,
                        tmp1, tmp2, xmm1);
    }

    __ BIND(L_copy_bytes);
    __ addptr(qword_count, 8);
    __ jcc(Assembler::lessEqual, L_loop);
    __ subptr(qword_count, 4);  // sub(8) and add(4)
    __ jcc(Assembler::greater, L_end);
    // Copy trailing 32 bytes
    if (UseAVX >= 2) {
      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(end_from, qword_count, Address::times_8, -24),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(end_to, qword_count, Address::times_8, -24), xmm0,
                        tmp1, tmp2, xmm1);
    } else {
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -24),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -24), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(end_from, qword_count, Address::times_8, -8),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(end_to, qword_count, Address::times_8, -8), xmm0,
                        tmp1, tmp2, xmm1);
    }
    __ addptr(qword_count, 4);
    __ BIND(L_end);
  } else {
    // Copy 32-bytes per iteration
    __ BIND(L_loop);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(end_from, qword_count, Address::times_8, -24),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(end_to, qword_count, Address::times_8, -24), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(end_from, qword_count, Address::times_8, -16),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(end_to, qword_count, Address::times_8, -16), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(end_from, qword_count, Address::times_8, -8),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(end_to, qword_count, Address::times_8, -8), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(end_from, qword_count, Address::times_8, 0),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(end_to, qword_count, Address::times_8, 0), tmp1,
                      tmp2);

    __ BIND(L_copy_bytes);
    __ addptr(qword_count, 4);
    __ jcc(Assembler::lessEqual, L_loop);
  }
  __ subptr(qword_count, 4);
  __ jcc(Assembler::less, L_copy_8_bytes); // Copy trailing qwords
}


// Copy big chunks backward
//
// Inputs:
//   from         - source arrays address
//   dest         - destination array address
//   qword_count  - 64-bits element count
//   tmp1         - scratch
//   L_copy_bytes - entry label
//   L_copy_8_bytes  - exit  label
//
void StubGenerator::copy_bytes_backward(Register from, Register dest,
                                        Register qword_count, Register tmp1,
                                        Register tmp2, Label& L_copy_bytes,
                                        Label& L_copy_8_bytes, DecoratorSet decorators,
                                        BasicType type) {
  BarrierSetAssembler* bs = BarrierSet::barrier_set()->barrier_set_assembler();
  DEBUG_ONLY(__ stop("enter at entry label, not here"));
  Label L_loop;
  __ align(OptoLoopAlignment);
  if (UseUnalignedLoadStores) {
    Label L_end;
    __ BIND(L_loop);
    if (UseAVX >= 2) {
      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(from, qword_count, Address::times_8, 32),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(dest, qword_count, Address::times_8, 32), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(from, qword_count, Address::times_8, 0),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(dest, qword_count, Address::times_8, 0), xmm0,
                        tmp1, tmp2, xmm1);
    } else {
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 48),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 48), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 32),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 32), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 16),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 16), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 0),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 0), xmm0,
                        tmp1, tmp2, xmm1);
    }

    __ BIND(L_copy_bytes);
    __ subptr(qword_count, 8);
    __ jcc(Assembler::greaterEqual, L_loop);

    __ addptr(qword_count, 4);  // add(8) and sub(4)
    __ jcc(Assembler::less, L_end);
    // Copy trailing 32 bytes
    if (UseAVX >= 2) {
      bs->copy_load_at(_masm, decorators, type, 32,
                       xmm0, Address(from, qword_count, Address::times_8, 0),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 32,
                        Address(dest, qword_count, Address::times_8, 0), xmm0,
                        tmp1, tmp2, xmm1);
    } else {
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 16),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 16), xmm0,
                        tmp1, tmp2, xmm1);
      bs->copy_load_at(_masm, decorators, type, 16,
                       xmm0, Address(from, qword_count, Address::times_8, 0),
                       tmp1, xmm1);
      bs->copy_store_at(_masm, decorators, type, 16,
                        Address(dest, qword_count, Address::times_8, 0), xmm0,
                        tmp1, tmp2, xmm1);
    }
    __ subptr(qword_count, 4);
    __ BIND(L_end);
  } else {
    // Copy 32-bytes per iteration
    __ BIND(L_loop);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(from, qword_count, Address::times_8, 24),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(dest, qword_count, Address::times_8, 24), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(from, qword_count, Address::times_8, 16),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(dest, qword_count, Address::times_8, 16), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(from, qword_count, Address::times_8, 8),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(dest, qword_count, Address::times_8, 8), tmp1,
                      tmp2);
    bs->copy_load_at(_masm, decorators, type, 8,
                     tmp1, Address(from, qword_count, Address::times_8, 0),
                     tmp2);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(dest, qword_count, Address::times_8, 0), tmp1,
                      tmp2);

    __ BIND(L_copy_bytes);
    __ subptr(qword_count, 4);
    __ jcc(Assembler::greaterEqual, L_loop);
  }
  __ addptr(qword_count, 4);
  __ jcc(Assembler::greater, L_copy_8_bytes); // Copy trailing qwords
}

#if COMPILER2_OR_JVMCI

// Note: Following rules apply to AVX3 optimized arraycopy stubs:-
// - If target supports AVX3 features (BW+VL+F) then implementation uses 32 byte vectors (YMMs)
//   for both special cases (various small block sizes) and aligned copy loop. This is the
//   default configuration.
// - If copy length is above AVX3Threshold, then implementation use 64 byte vectors (ZMMs)
//   for main copy loop (and subsequent tail) since bulk of the cycles will be consumed in it.
// - If user forces MaxVectorSize=32 then above 4096 bytes its seen that REP MOVs shows a
//   better performance for disjoint copies. For conjoint/backward copy vector based
//   copy performs better.
// - If user sets AVX3Threshold=0, then special cases for small blocks sizes operate over
//   64 byte vector registers (ZMMs).

// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
//
// Side Effects:
//   disjoint_copy_avx3_masked is set to the no-overlap entry point
//   used by generate_conjoint_[byte/int/short/long]_copy().
//
address StubGenerator::generate_disjoint_copy_avx3_masked(StubGenStubId stub_id, address* entry) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  int shift;
  bool is_oop;
  bool dest_uninitialized;

  switch (stub_id) {
  case jbyte_disjoint_arraycopy_id:
    shift = 0;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jshort_disjoint_arraycopy_id:
    shift = 1;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jint_disjoint_arraycopy_id:
    shift = 2;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jlong_disjoint_arraycopy_id:
    shift = 3;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case oop_disjoint_arraycopy_id:
    shift = (UseCompressedOops ? 2 : 3);
    is_oop = true;
    dest_uninitialized = false;
    break;
  case oop_disjoint_arraycopy_uninit_id:
    shift = (UseCompressedOops ? 2 : 3);
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  int avx3threshold = VM_Version::avx3_threshold();
  bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0);
  const int large_threshold = 2621440; // 2.5 MB
  Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry;
  Label L_repmovs, L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64;
  Label L_copy_large, L_finish;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register temp1       = r8;
  const Register temp2       = r11;
  const Register temp3       = rax;
  const Register temp4       = rcx;
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
     // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  BasicType type_vec[] = { T_BYTE,  T_SHORT,  T_INT,   T_LONG};
  BasicType type = is_oop ? T_OBJECT : type_vec[shift];

  setup_argument_regs(type);

  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }
  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
  bs->arraycopy_prologue(_masm, decorators, type, from, to, count);

  {
    // Type(shift)           byte(0), short(1), int(2),   long(3)
    int loop_size[]        = { 192,     96,       48,      24};
    int threshold[]        = { 4096,    2048,     1024,    512};

    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // 'from', 'to' and 'count' are now valid

    // temp1 holds remaining count and temp4 holds running count used to compute
    // next address offset for start of to/from addresses (temp4 * scale).
    __ mov64(temp4, 0);
    __ movq(temp1, count);

    // Zero length check.
    __ BIND(L_tail);
    __ cmpq(temp1, 0);
    __ jcc(Assembler::lessEqual, L_exit);

    // Special cases using 32 byte [masked] vector copy operations.
    arraycopy_avx3_special_cases(xmm1, k2, from, to, temp1, shift,
                                 temp4, temp3, use64byteVector, L_entry, L_exit);

    // PRE-MAIN-POST loop for aligned copy.
    __ BIND(L_entry);

    if (MaxVectorSize == 64) {
      __ movq(temp2, temp1);
      __ shlq(temp2, shift);
      __ cmpq(temp2, large_threshold);
      __ jcc(Assembler::greaterEqual, L_copy_large);
    }
    if (avx3threshold != 0) {
      __ cmpq(count, threshold[shift]);
      if (MaxVectorSize == 64) {
        // Copy using 64 byte vectors.
        __ jcc(Assembler::greaterEqual, L_pre_main_post_64);
      } else {
        assert(MaxVectorSize < 64, "vector size should be < 64 bytes");
        // REP MOVS offer a faster copy path.
        __ jcc(Assembler::greaterEqual, L_repmovs);
      }
    }

    if ((MaxVectorSize < 64)  || (avx3threshold != 0)) {
      // Partial copy to make dst address 32 byte aligned.
      __ movq(temp2, to);
      __ andq(temp2, 31);
      __ jcc(Assembler::equal, L_main_pre_loop);

      __ negptr(temp2);
      __ addq(temp2, 32);
      if (shift) {
        __ shrq(temp2, shift);
      }
      __ movq(temp3, temp2);
      copy32_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift);
      __ movq(temp4, temp2);
      __ movq(temp1, count);
      __ subq(temp1, temp2);

      __ cmpq(temp1, loop_size[shift]);
      __ jcc(Assembler::less, L_tail);

      __ BIND(L_main_pre_loop);
      __ subq(temp1, loop_size[shift]);

      // Main loop with aligned copy block size of 192 bytes at 32 byte granularity.
      __ align32();
      __ BIND(L_main_loop);
         copy64_avx(to, from, temp4, xmm1, false, shift, 0);
         copy64_avx(to, from, temp4, xmm1, false, shift, 64);
         copy64_avx(to, from, temp4, xmm1, false, shift, 128);
         __ addptr(temp4, loop_size[shift]);
         __ subq(temp1, loop_size[shift]);
         __ jcc(Assembler::greater, L_main_loop);

      __ addq(temp1, loop_size[shift]);

      // Tail loop.
      __ jmp(L_tail);

      __ BIND(L_repmovs);
        __ movq(temp2, temp1);
        // Swap to(RSI) and from(RDI) addresses to comply with REP MOVs semantics.
        __ movq(temp3, to);
        __ movq(to,  from);
        __ movq(from, temp3);
        // Save to/from for restoration post rep_mov.
        __ movq(temp1, to);
        __ movq(temp3, from);
        if(shift < 3) {
          __ shrq(temp2, 3-shift);     // quad word count
        }
        __ movq(temp4 , temp2);        // move quad ward count into temp4(RCX).
        __ rep_mov();
        __ shlq(temp2, 3);             // convert quad words into byte count.
        if(shift) {
          __ shrq(temp2, shift);       // type specific count.
        }
        // Restore original addresses in to/from.
        __ movq(to, temp3);
        __ movq(from, temp1);
        __ movq(temp4, temp2);
        __ movq(temp1, count);
        __ subq(temp1, temp2);         // tailing part (less than a quad ward size).
        __ jmp(L_tail);
    }

    if (MaxVectorSize > 32) {
      __ BIND(L_pre_main_post_64);
      // Partial copy to make dst address 64 byte aligned.
      __ movq(temp2, to);
      __ andq(temp2, 63);
      __ jcc(Assembler::equal, L_main_pre_loop_64bytes);

      __ negptr(temp2);
      __ addq(temp2, 64);
      if (shift) {
        __ shrq(temp2, shift);
      }
      __ movq(temp3, temp2);
      copy64_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift, 0 , true);
      __ movq(temp4, temp2);
      __ movq(temp1, count);
      __ subq(temp1, temp2);

      __ cmpq(temp1, loop_size[shift]);
      __ jcc(Assembler::less, L_tail64);

      __ BIND(L_main_pre_loop_64bytes);
      __ subq(temp1, loop_size[shift]);

      // Main loop with aligned copy block size of 192 bytes at
      // 64 byte copy granularity.
      __ align32();
      __ BIND(L_main_loop_64bytes);
         copy64_avx(to, from, temp4, xmm1, false, shift, 0 , true);
         copy64_avx(to, from, temp4, xmm1, false, shift, 64, true);
         copy64_avx(to, from, temp4, xmm1, false, shift, 128, true);
         __ addptr(temp4, loop_size[shift]);
         __ subq(temp1, loop_size[shift]);
         __ jcc(Assembler::greater, L_main_loop_64bytes);

      __ addq(temp1, loop_size[shift]);
      // Zero length check.
      __ jcc(Assembler::lessEqual, L_exit);

      __ BIND(L_tail64);

      // Tail handling using 64 byte [masked] vector copy operations.
      use64byteVector = true;
      arraycopy_avx3_special_cases(xmm1, k2, from, to, temp1, shift,
                                   temp4, temp3, use64byteVector, L_entry, L_exit);
    }
    __ BIND(L_exit);
  }

  __ BIND(L_finish);
  address ucme_exit_pc = __ pc();
  // When called from generic_arraycopy r11 contains specific values
  // used during arraycopy epilogue, re-initializing r11.
  if (is_oop) {
    __ movq(r11, shift == 3 ? count : to);
  }
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, count);
  restore_argument_regs(type);
  INC_COUNTER_NP(get_profile_ctr(shift), rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  if (MaxVectorSize == 64) {
    __ BIND(L_copy_large);
      UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc);
      arraycopy_avx3_large(to, from, temp1, temp2, temp3, temp4, count, xmm1, xmm2, xmm3, xmm4, shift);
    __ jmp(L_finish);
  }
  return start;
}

void StubGenerator::arraycopy_avx3_large(Register to, Register from, Register temp1, Register temp2,
                                         Register temp3, Register temp4, Register count,
                                         XMMRegister xmm1, XMMRegister xmm2, XMMRegister xmm3,
                                         XMMRegister xmm4, int shift) {

  // Type(shift)           byte(0), short(1), int(2),   long(3)
  int loop_size[]        = { 256,     128,       64,      32};
  int threshold[]        = { 4096,    2048,     1024,    512};

  Label L_main_loop_large;
  Label L_tail_large;
  Label L_exit_large;
  Label L_entry_large;
  Label L_main_pre_loop_large;
  Label L_pre_main_post_large;

  assert(MaxVectorSize == 64, "vector length != 64");
  __ BIND(L_entry_large);

  __ BIND(L_pre_main_post_large);
  // Partial copy to make dst address 64 byte aligned.
  __ movq(temp2, to);
  __ andq(temp2, 63);
  __ jcc(Assembler::equal, L_main_pre_loop_large);

  __ negptr(temp2);
  __ addq(temp2, 64);
  if (shift) {
    __ shrq(temp2, shift);
  }
  __ movq(temp3, temp2);
  copy64_masked_avx(to, from, xmm1, k2, temp3, temp4, temp1, shift, 0, true);
  __ movq(temp4, temp2);
  __ movq(temp1, count);
  __ subq(temp1, temp2);

  __ cmpq(temp1, loop_size[shift]);
  __ jcc(Assembler::less, L_tail_large);

  __ BIND(L_main_pre_loop_large);
  __ subq(temp1, loop_size[shift]);

  // Main loop with aligned copy block size of 256 bytes at 64 byte copy granularity.
  __ align32();
  __ BIND(L_main_loop_large);
  copy256_avx3(to, from, temp4, xmm1, xmm2, xmm3, xmm4, shift, 0);
  __ addptr(temp4, loop_size[shift]);
  __ subq(temp1, loop_size[shift]);
  __ jcc(Assembler::greater, L_main_loop_large);
  // fence needed because copy256_avx3 uses non-temporal stores
  __ sfence();

  __ addq(temp1, loop_size[shift]);
  // Zero length check.
  __ jcc(Assembler::lessEqual, L_exit_large);
  __ BIND(L_tail_large);
  // Tail handling using 64 byte [masked] vector copy operations.
  __ cmpq(temp1, 0);
  __ jcc(Assembler::lessEqual, L_exit_large);
  arraycopy_avx3_special_cases_256(xmm1, k2, from, to, temp1, shift,
                               temp4, temp3, L_exit_large);
  __ BIND(L_exit_large);
}

// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
//
address StubGenerator::generate_conjoint_copy_avx3_masked(StubGenStubId stub_id, address* entry, address nooverlap_target) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  int shift;
  bool is_oop;
  bool dest_uninitialized;

  switch (stub_id) {
  case jbyte_arraycopy_id:
    shift = 0;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jshort_arraycopy_id:
    shift = 1;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jint_arraycopy_id:
    shift = 2;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case jlong_arraycopy_id:
    shift = 3;
    is_oop = false;
    dest_uninitialized = false;
    break;
  case oop_arraycopy_id:
    shift = (UseCompressedOops ? 2 : 3);
    is_oop = true;
    dest_uninitialized = false;
    break;
  case oop_arraycopy_uninit_id:
    shift = (UseCompressedOops ? 2 : 3);
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  int avx3threshold = VM_Version::avx3_threshold();
  bool use64byteVector = (MaxVectorSize > 32) && (avx3threshold == 0);

  Label L_main_pre_loop, L_main_pre_loop_64bytes, L_pre_main_post_64;
  Label L_main_loop, L_main_loop_64bytes, L_tail, L_tail64, L_exit, L_entry;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register temp1       = r8;
  const Register temp2       = rcx;
  const Register temp3       = r11;
  const Register temp4       = rax;
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
     // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  array_overlap_test(nooverlap_target, (Address::ScaleFactor)(shift));

  BasicType type_vec[] = { T_BYTE,  T_SHORT,  T_INT,   T_LONG};
  BasicType type = is_oop ? T_OBJECT : type_vec[shift];

  setup_argument_regs(type);

  DecoratorSet decorators = IN_HEAP | IS_ARRAY;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }
  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
  bs->arraycopy_prologue(_masm, decorators, type, from, to, count);
  {
    // Type(shift)       byte(0), short(1), int(2),   long(3)
    int loop_size[]   = { 192,     96,       48,      24};
    int threshold[]   = { 4096,    2048,     1024,    512};

    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // 'from', 'to' and 'count' are now valid

    // temp1 holds remaining count.
    __ movq(temp1, count);

    // Zero length check.
    __ BIND(L_tail);
    __ cmpq(temp1, 0);
    __ jcc(Assembler::lessEqual, L_exit);

    __ mov64(temp2, 0);
    __ movq(temp3, temp1);
    // Special cases using 32 byte [masked] vector copy operations.
    arraycopy_avx3_special_cases_conjoint(xmm1, k2, from, to, temp2, temp3, temp1, shift,
                                          temp4, use64byteVector, L_entry, L_exit);

    // PRE-MAIN-POST loop for aligned copy.
    __ BIND(L_entry);

    if ((MaxVectorSize > 32) && (avx3threshold != 0)) {
      __ cmpq(temp1, threshold[shift]);
      __ jcc(Assembler::greaterEqual, L_pre_main_post_64);
    }

    if ((MaxVectorSize < 64)  || (avx3threshold != 0)) {
      // Partial copy to make dst address 32 byte aligned.
      __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0));
      __ andq(temp2, 31);
      __ jcc(Assembler::equal, L_main_pre_loop);

      if (shift) {
        __ shrq(temp2, shift);
      }
      __ subq(temp1, temp2);
      copy32_masked_avx(to, from, xmm1, k2, temp2, temp1, temp3, shift);

      __ cmpq(temp1, loop_size[shift]);
      __ jcc(Assembler::less, L_tail);

      __ BIND(L_main_pre_loop);

      // Main loop with aligned copy block size of 192 bytes at 32 byte granularity.
      __ align32();
      __ BIND(L_main_loop);
         copy64_avx(to, from, temp1, xmm1, true, shift, -64);
         copy64_avx(to, from, temp1, xmm1, true, shift, -128);
         copy64_avx(to, from, temp1, xmm1, true, shift, -192);
         __ subptr(temp1, loop_size[shift]);
         __ cmpq(temp1, loop_size[shift]);
         __ jcc(Assembler::greater, L_main_loop);

      // Tail loop.
      __ jmp(L_tail);
    }

    if (MaxVectorSize > 32) {
      __ BIND(L_pre_main_post_64);
      // Partial copy to make dst address 64 byte aligned.
      __ leaq(temp2, Address(to, temp1, (Address::ScaleFactor)(shift), 0));
      __ andq(temp2, 63);
      __ jcc(Assembler::equal, L_main_pre_loop_64bytes);

      if (shift) {
        __ shrq(temp2, shift);
      }
      __ subq(temp1, temp2);
      copy64_masked_avx(to, from, xmm1, k2, temp2, temp1, temp3, shift, 0 , true);

      __ cmpq(temp1, loop_size[shift]);
      __ jcc(Assembler::less, L_tail64);

      __ BIND(L_main_pre_loop_64bytes);

      // Main loop with aligned copy block size of 192 bytes at
      // 64 byte copy granularity.
      __ align32();
      __ BIND(L_main_loop_64bytes);
         copy64_avx(to, from, temp1, xmm1, true, shift, -64 , true);
         copy64_avx(to, from, temp1, xmm1, true, shift, -128, true);
         copy64_avx(to, from, temp1, xmm1, true, shift, -192, true);
         __ subq(temp1, loop_size[shift]);
         __ cmpq(temp1, loop_size[shift]);
         __ jcc(Assembler::greater, L_main_loop_64bytes);

      // Zero length check.
      __ cmpq(temp1, 0);
      __ jcc(Assembler::lessEqual, L_exit);

      __ BIND(L_tail64);

      // Tail handling using 64 byte [masked] vector copy operations.
      use64byteVector = true;
      __ mov64(temp2, 0);
      __ movq(temp3, temp1);
      arraycopy_avx3_special_cases_conjoint(xmm1, k2, from, to, temp2, temp3, temp1, shift,
                                            temp4, use64byteVector, L_entry, L_exit);
    }
    __ BIND(L_exit);
  }
  address ucme_exit_pc = __ pc();
  // When called from generic_arraycopy r11 contains specific values
  // used during arraycopy epilogue, re-initializing r11.
  if(is_oop) {
    __ movq(r11, count);
  }
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, count);
  restore_argument_regs(type);
  INC_COUNTER_NP(get_profile_ctr(shift), rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}

void StubGenerator::arraycopy_avx3_special_cases(XMMRegister xmm, KRegister mask, Register from,
                                                 Register to, Register count, int shift,
                                                 Register index, Register temp,
                                                 bool use64byteVector, Label& L_entry, Label& L_exit) {
  Label L_entry_64, L_entry_96, L_entry_128;
  Label L_entry_160, L_entry_192;

  int size_mat[][6] = {
  /* T_BYTE */ {32 , 64,  96 , 128 , 160 , 192 },
  /* T_SHORT*/ {16 , 32,  48 , 64  , 80  , 96  },
  /* T_INT  */ {8  , 16,  24 , 32  , 40  , 48  },
  /* T_LONG */ {4  ,  8,  12 , 16  , 20  , 24  }
  };

  // Case A) Special case for length less than equal to 32 bytes.
  __ cmpq(count, size_mat[shift][0]);
  __ jccb(Assembler::greater, L_entry_64);
  copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift);
  __ jmp(L_exit);

  // Case B) Special case for length less than equal to 64 bytes.
  __ BIND(L_entry_64);
  __ cmpq(count, size_mat[shift][1]);
  __ jccb(Assembler::greater, L_entry_96);
  copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 0, use64byteVector);
  __ jmp(L_exit);

  // Case C) Special case for length less than equal to 96 bytes.
  __ BIND(L_entry_96);
  __ cmpq(count, size_mat[shift][2]);
  __ jccb(Assembler::greater, L_entry_128);
  copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector);
  __ subq(count, 64 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 64);
  __ jmp(L_exit);

  // Case D) Special case for length less than equal to 128 bytes.
  __ BIND(L_entry_128);
  __ cmpq(count, size_mat[shift][3]);
  __ jccb(Assembler::greater, L_entry_160);
  copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector);
  copy32_avx(to, from, index, xmm, shift, 64);
  __ subq(count, 96 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 96);
  __ jmp(L_exit);

  // Case E) Special case for length less than equal to 160 bytes.
  __ BIND(L_entry_160);
  __ cmpq(count, size_mat[shift][4]);
  __ jccb(Assembler::greater, L_entry_192);
  copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector);
  copy64_avx(to, from, index, xmm, false, shift, 64, use64byteVector);
  __ subq(count, 128 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 128);
  __ jmp(L_exit);

  // Case F) Special case for length less than equal to 192 bytes.
  __ BIND(L_entry_192);
  __ cmpq(count, size_mat[shift][5]);
  __ jcc(Assembler::greater, L_entry);
  copy64_avx(to, from, index, xmm, false, shift, 0, use64byteVector);
  copy64_avx(to, from, index, xmm, false, shift, 64, use64byteVector);
  copy32_avx(to, from, index, xmm, shift, 128);
  __ subq(count, 160 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, index, temp, shift, 160);
  __ jmp(L_exit);
}

void StubGenerator::arraycopy_avx3_special_cases_256(XMMRegister xmm, KRegister mask, Register from,
                                                     Register to, Register count, int shift, Register index,
                                                     Register temp, Label& L_exit) {
  Label L_entry_64, L_entry_128, L_entry_192, L_entry_256;

  int size_mat[][4] = {
  /* T_BYTE */ {64, 128, 192, 256},
  /* T_SHORT*/ {32, 64 , 96 , 128},
  /* T_INT  */ {16, 32 , 48 ,  64},
  /* T_LONG */ { 8, 16 , 24 ,  32}
  };

  assert(MaxVectorSize == 64, "vector length != 64");
  // Case A) Special case for length less than or equal to 64 bytes.
  __ BIND(L_entry_64);
  __ cmpq(count, size_mat[shift][0]);
  __ jccb(Assembler::greater, L_entry_128);
  copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 0, true);
  __ jmp(L_exit);

  // Case B) Special case for length less than or equal to 128 bytes.
  __ BIND(L_entry_128);
  __ cmpq(count, size_mat[shift][1]);
  __ jccb(Assembler::greater, L_entry_192);
  copy64_avx(to, from, index, xmm, false, shift, 0, true);
  __ subq(count, 64 >> shift);
  copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 64, true);
  __ jmp(L_exit);

  // Case C) Special case for length less than or equal to 192 bytes.
  __ BIND(L_entry_192);
  __ cmpq(count, size_mat[shift][2]);
  __ jcc(Assembler::greater, L_entry_256);
  copy64_avx(to, from, index, xmm, false, shift, 0, true);
  copy64_avx(to, from, index, xmm, false, shift, 64, true);
  __ subq(count, 128 >> shift);
  copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 128, true);
  __ jmp(L_exit);

  // Case D) Special case for length less than or equal to 256 bytes.
  __ BIND(L_entry_256);
  copy64_avx(to, from, index, xmm, false, shift, 0, true);
  copy64_avx(to, from, index, xmm, false, shift, 64, true);
  copy64_avx(to, from, index, xmm, false, shift, 128, true);
  __ subq(count, 192 >> shift);
  copy64_masked_avx(to, from, xmm, mask, count, index, temp, shift, 192, true);
  __ jmp(L_exit);
}

void StubGenerator::arraycopy_avx3_special_cases_conjoint(XMMRegister xmm, KRegister mask, Register from,
                                                           Register to, Register start_index, Register end_index,
                                                           Register count, int shift, Register temp,
                                                           bool use64byteVector, Label& L_entry, Label& L_exit) {
  Label L_entry_64, L_entry_96, L_entry_128;
  Label L_entry_160, L_entry_192;
  bool avx3 = (MaxVectorSize > 32) && (VM_Version::avx3_threshold() == 0);

  int size_mat[][6] = {
  /* T_BYTE */ {32 , 64,  96 , 128 , 160 , 192 },
  /* T_SHORT*/ {16 , 32,  48 , 64  , 80  , 96  },
  /* T_INT  */ {8  , 16,  24 , 32  , 40  , 48  },
  /* T_LONG */ {4  ,  8,  12 , 16  , 20  , 24  }
  };

  // Case A) Special case for length less than equal to 32 bytes.
  __ cmpq(count, size_mat[shift][0]);
  __ jccb(Assembler::greater, L_entry_64);
  copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  __ jmp(L_exit);

  // Case B) Special case for length less than equal to 64 bytes.
  __ BIND(L_entry_64);
  __ cmpq(count, size_mat[shift][1]);
  __ jccb(Assembler::greater, L_entry_96);
  if (avx3) {
     copy64_masked_avx(to, from, xmm, mask, count, start_index, temp, shift, 0, true);
  } else {
     copy32_avx(to, from, end_index, xmm, shift, -32);
     __ subq(count, 32 >> shift);
     copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  }
  __ jmp(L_exit);

  // Case C) Special case for length less than equal to 96 bytes.
  __ BIND(L_entry_96);
  __ cmpq(count, size_mat[shift][2]);
  __ jccb(Assembler::greater, L_entry_128);
  copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector);
  __ subq(count, 64 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  __ jmp(L_exit);

  // Case D) Special case for length less than equal to 128 bytes.
  __ BIND(L_entry_128);
  __ cmpq(count, size_mat[shift][3]);
  __ jccb(Assembler::greater, L_entry_160);
  copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector);
  copy32_avx(to, from, end_index, xmm, shift, -96);
  __ subq(count, 96 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  __ jmp(L_exit);

  // Case E) Special case for length less than equal to 160 bytes.
  __ BIND(L_entry_160);
  __ cmpq(count, size_mat[shift][4]);
  __ jccb(Assembler::greater, L_entry_192);
  copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector);
  copy64_avx(to, from, end_index, xmm, true, shift, -128, use64byteVector);
  __ subq(count, 128 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  __ jmp(L_exit);

  // Case F) Special case for length less than equal to 192 bytes.
  __ BIND(L_entry_192);
  __ cmpq(count, size_mat[shift][5]);
  __ jcc(Assembler::greater, L_entry);
  copy64_avx(to, from, end_index, xmm, true, shift, -64, use64byteVector);
  copy64_avx(to, from, end_index, xmm, true, shift, -128, use64byteVector);
  copy32_avx(to, from, end_index, xmm, shift, -160);
  __ subq(count, 160 >> shift);
  copy32_masked_avx(to, from, xmm, mask, count, start_index, temp, shift);
  __ jmp(L_exit);
}

void StubGenerator::copy256_avx3(Register dst, Register src, Register index, XMMRegister xmm1,
                                XMMRegister xmm2, XMMRegister xmm3, XMMRegister xmm4,
                                int shift, int offset) {
  if (MaxVectorSize == 64) {
    Address::ScaleFactor scale = (Address::ScaleFactor)(shift);
    __ prefetcht0(Address(src, index, scale, offset + 0x200));
    __ prefetcht0(Address(src, index, scale, offset + 0x240));
    __ prefetcht0(Address(src, index, scale, offset + 0x280));
    __ prefetcht0(Address(src, index, scale, offset + 0x2C0));

    __ prefetcht0(Address(src, index, scale, offset + 0x400));
    __ prefetcht0(Address(src, index, scale, offset + 0x440));
    __ prefetcht0(Address(src, index, scale, offset + 0x480));
    __ prefetcht0(Address(src, index, scale, offset + 0x4C0));

    __ evmovdquq(xmm1, Address(src, index, scale, offset), Assembler::AVX_512bit);
    __ evmovdquq(xmm2, Address(src, index, scale, offset + 0x40), Assembler::AVX_512bit);
    __ evmovdquq(xmm3, Address(src, index, scale, offset + 0x80), Assembler::AVX_512bit);
    __ evmovdquq(xmm4, Address(src, index, scale, offset + 0xC0), Assembler::AVX_512bit);

    __ evmovntdquq(Address(dst, index, scale, offset), xmm1, Assembler::AVX_512bit);
    __ evmovntdquq(Address(dst, index, scale, offset + 0x40), xmm2, Assembler::AVX_512bit);
    __ evmovntdquq(Address(dst, index, scale, offset + 0x80), xmm3, Assembler::AVX_512bit);
    __ evmovntdquq(Address(dst, index, scale, offset + 0xC0), xmm4, Assembler::AVX_512bit);
  }
}

void StubGenerator::copy64_masked_avx(Register dst, Register src, XMMRegister xmm,
                                       KRegister mask, Register length, Register index,
                                       Register temp, int shift, int offset,
                                       bool use64byteVector) {
  BasicType type[] = { T_BYTE,  T_SHORT,  T_INT,   T_LONG};
  assert(MaxVectorSize >= 32, "vector length should be >= 32");
  if (!use64byteVector) {
    copy32_avx(dst, src, index, xmm, shift, offset);
    __ subptr(length, 32 >> shift);
    copy32_masked_avx(dst, src, xmm, mask, length, index, temp, shift, offset+32);
  } else {
    Address::ScaleFactor scale = (Address::ScaleFactor)(shift);
    assert(MaxVectorSize == 64, "vector length != 64");
    __ mov64(temp, -1L);
    __ bzhiq(temp, temp, length);
    __ kmovql(mask, temp);
    __ evmovdqu(type[shift], mask, xmm, Address(src, index, scale, offset), false, Assembler::AVX_512bit);
    __ evmovdqu(type[shift], mask, Address(dst, index, scale, offset), xmm, true, Assembler::AVX_512bit);
  }
}


void StubGenerator::copy32_masked_avx(Register dst, Register src, XMMRegister xmm,
                                       KRegister mask, Register length, Register index,
                                       Register temp, int shift, int offset) {
  assert(MaxVectorSize >= 32, "vector length should be >= 32");
  BasicType type[] = { T_BYTE,  T_SHORT,  T_INT,   T_LONG};
  Address::ScaleFactor scale = (Address::ScaleFactor)(shift);
  __ mov64(temp, -1L);
  __ bzhiq(temp, temp, length);
  __ kmovql(mask, temp);
  __ evmovdqu(type[shift], mask, xmm, Address(src, index, scale, offset), false, Assembler::AVX_256bit);
  __ evmovdqu(type[shift], mask, Address(dst, index, scale, offset), xmm, true, Assembler::AVX_256bit);
}


void StubGenerator::copy32_avx(Register dst, Register src, Register index, XMMRegister xmm,
                                int shift, int offset) {
  assert(MaxVectorSize >= 32, "vector length should be >= 32");
  Address::ScaleFactor scale = (Address::ScaleFactor)(shift);
  __ vmovdqu(xmm, Address(src, index, scale, offset));
  __ vmovdqu(Address(dst, index, scale, offset), xmm);
}


void StubGenerator::copy64_avx(Register dst, Register src, Register index, XMMRegister xmm,
                                bool conjoint, int shift, int offset, bool use64byteVector) {
  assert(MaxVectorSize == 64 || MaxVectorSize == 32, "vector length mismatch");
  if (!use64byteVector) {
    if (conjoint) {
      copy32_avx(dst, src, index, xmm, shift, offset+32);
      copy32_avx(dst, src, index, xmm, shift, offset);
    } else {
      copy32_avx(dst, src, index, xmm, shift, offset);
      copy32_avx(dst, src, index, xmm, shift, offset+32);
    }
  } else {
    Address::ScaleFactor scale = (Address::ScaleFactor)(shift);
    __ evmovdquq(xmm, Address(src, index, scale, offset), Assembler::AVX_512bit);
    __ evmovdquq(Address(dst, index, scale, offset), xmm, Assembler::AVX_512bit);
  }
}

#endif // COMPILER2_OR_JVMCI


// Arguments:
//   entry     - location for return of (post-push) entry
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries,
// we let the hardware handle it.  The one to eight bytes within words,
// dwords or qwords that span cache line boundaries will still be loaded
// and stored atomically.
//
// Side Effects:
//   entry is set to the no-overlap entry point
//   used by generate_conjoint_byte_copy().
//
address StubGenerator::generate_disjoint_byte_copy(address* entry) {
  StubGenStubId stub_id = StubGenStubId::jbyte_disjoint_arraycopy_id;
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
#if COMPILER2_OR_JVMCI
  if (VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_disjoint_copy_avx3_masked(stub_id, entry);
  }
#endif
  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();
  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;

  Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes;
  Label L_copy_byte, L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register byte_count  = rcx;
  const Register qword_count = count;
  const Register end_from    = from; // source array end address
  const Register end_to      = to;   // destination array end address
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
     // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  setup_arg_regs(); // from => rdi, to => rsi, count => rdx
                    // r9 and r10 may be used to save non-volatile registers

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(byte_count, count);
    __ shrptr(count, 3); // count => qword_count

    // Copy from low to high addresses.  Use 'to' as scratch.
    __ lea(end_from, Address(from, qword_count, Address::times_8, -8));
    __ lea(end_to,   Address(to,   qword_count, Address::times_8, -8));
    __ negptr(qword_count); // make the count negative
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(end_from, qword_count, Address::times_8, 8));
    __ movq(Address(end_to, qword_count, Address::times_8, 8), rax);
    __ increment(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);

    // Check for and copy trailing dword
  __ BIND(L_copy_4_bytes);
    __ testl(byte_count, 4);
    __ jccb(Assembler::zero, L_copy_2_bytes);
    __ movl(rax, Address(end_from, 8));
    __ movl(Address(end_to, 8), rax);

    __ addptr(end_from, 4);
    __ addptr(end_to, 4);

    // Check for and copy trailing word
  __ BIND(L_copy_2_bytes);
    __ testl(byte_count, 2);
    __ jccb(Assembler::zero, L_copy_byte);
    __ movw(rax, Address(end_from, 8));
    __ movw(Address(end_to, 8), rax);

    __ addptr(end_from, 2);
    __ addptr(end_to, 2);

    // Check for and copy trailing byte
  __ BIND(L_copy_byte);
    __ testl(byte_count, 1);
    __ jccb(Assembler::zero, L_exit);
    __ movb(rax, Address(end_from, 8));
    __ movb(Address(end_to, 8), rax);
  }
__ BIND(L_exit);
  address ucme_exit_pc = __ pc();
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jbyte_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    UnsafeMemoryAccessMark umam(this, !aligned, false, ucme_exit_pc);
    // Copy in multi-bytes chunks
    copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_BYTE);
    __ jmp(L_copy_4_bytes);
  }
  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//   nooverlap_target - entry to branch to if no overlap detected
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4-, 2-, or 1-byte boundaries,
// we let the hardware handle it.  The one to eight bytes within words,
// dwords or qwords that span cache line boundaries will still be loaded
// and stored atomically.
//
address StubGenerator::generate_conjoint_byte_copy(address nooverlap_target, address* entry) {
  StubGenStubId stub_id = StubGenStubId::jbyte_arraycopy_id;
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
#if COMPILER2_OR_JVMCI
  if (VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target);
  }
#endif
  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();
  DecoratorSet decorators = IN_HEAP | IS_ARRAY;

  Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_copy_2_bytes;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register byte_count  = rcx;
  const Register qword_count = count;

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  array_overlap_test(nooverlap_target, Address::times_1);
  setup_arg_regs(); // from => rdi, to => rsi, count => rdx
                    // r9 and r10 may be used to save non-volatile registers

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(byte_count, count);
    __ shrptr(count, 3);   // count => qword_count

    // Copy from high to low addresses.

    // Check for and copy trailing byte
    __ testl(byte_count, 1);
    __ jcc(Assembler::zero, L_copy_2_bytes);
    __ movb(rax, Address(from, byte_count, Address::times_1, -1));
    __ movb(Address(to, byte_count, Address::times_1, -1), rax);
    __ decrement(byte_count); // Adjust for possible trailing word

    // Check for and copy trailing word
  __ BIND(L_copy_2_bytes);
    __ testl(byte_count, 2);
    __ jcc(Assembler::zero, L_copy_4_bytes);
    __ movw(rax, Address(from, byte_count, Address::times_1, -2));
    __ movw(Address(to, byte_count, Address::times_1, -2), rax);

    // Check for and copy trailing dword
  __ BIND(L_copy_4_bytes);
    __ testl(byte_count, 4);
    __ jcc(Assembler::zero, L_copy_bytes);
    __ movl(rax, Address(from, qword_count, Address::times_8));
    __ movl(Address(to, qword_count, Address::times_8), rax);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(from, qword_count, Address::times_8, -8));
    __ movq(Address(to, qword_count, Address::times_8, -8), rax);
    __ decrement(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);
  }
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jbyte_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // Copy in multi-bytes chunks
    copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_BYTE);
  }
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jbyte_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we
// let the hardware handle it.  The two or four words within dwords
// or qwords that span cache line boundaries will still be loaded
// and stored atomically.
//
// Side Effects:
//   entry is set to the no-overlap entry point
//   used by generate_conjoint_short_copy().
//
address StubGenerator::generate_disjoint_short_copy(address *entry) {
  StubGenStubId stub_id = StubGenStubId::jshort_disjoint_arraycopy_id;
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
#if COMPILER2_OR_JVMCI
  if (VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_disjoint_copy_avx3_masked(stub_id, entry);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();
  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;

  Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes,L_copy_2_bytes,L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register word_count  = rcx;
  const Register qword_count = count;
  const Register end_from    = from; // source array end address
  const Register end_to      = to;   // destination array end address
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  setup_arg_regs(); // from => rdi, to => rsi, count => rdx
                    // r9 and r10 may be used to save non-volatile registers

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(word_count, count);
    __ shrptr(count, 2); // count => qword_count

    // Copy from low to high addresses.  Use 'to' as scratch.
    __ lea(end_from, Address(from, qword_count, Address::times_8, -8));
    __ lea(end_to,   Address(to,   qword_count, Address::times_8, -8));
    __ negptr(qword_count);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(end_from, qword_count, Address::times_8, 8));
    __ movq(Address(end_to, qword_count, Address::times_8, 8), rax);
    __ increment(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);

    // Original 'dest' is trashed, so we can't use it as a
    // base register for a possible trailing word copy

    // Check for and copy trailing dword
  __ BIND(L_copy_4_bytes);
    __ testl(word_count, 2);
    __ jccb(Assembler::zero, L_copy_2_bytes);
    __ movl(rax, Address(end_from, 8));
    __ movl(Address(end_to, 8), rax);

    __ addptr(end_from, 4);
    __ addptr(end_to, 4);

    // Check for and copy trailing word
  __ BIND(L_copy_2_bytes);
    __ testl(word_count, 1);
    __ jccb(Assembler::zero, L_exit);
    __ movw(rax, Address(end_from, 8));
    __ movw(Address(end_to, 8), rax);
  }
__ BIND(L_exit);
  address ucme_exit_pc = __ pc();
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jshort_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    UnsafeMemoryAccessMark umam(this, !aligned, false, ucme_exit_pc);
    // Copy in multi-bytes chunks
    copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_SHORT);
    __ jmp(L_copy_4_bytes);
  }

  return start;
}


address StubGenerator::generate_fill(StubGenStubId stub_id) {
  BasicType t;
  bool aligned;

  switch (stub_id) {
  case jbyte_fill_id:
    t = T_BYTE;
    aligned = false;
    break;
  case jshort_fill_id:
    t = T_SHORT;
    aligned = false;
    break;
  case jint_fill_id:
    t = T_INT;
    aligned = false;
    break;
  case arrayof_jbyte_fill_id:
    t = T_BYTE;
    aligned = true;
    break;
  case arrayof_jshort_fill_id:
    t = T_SHORT;
    aligned = true;
    break;
  case arrayof_jint_fill_id:
    t = T_INT;
    aligned = true;
    break;
  default:
    ShouldNotReachHere();
  }

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  BLOCK_COMMENT("Entry:");

  const Register to       = c_rarg0;  // destination array address
  const Register value    = c_rarg1;  // value
  const Register count    = c_rarg2;  // elements count
  __ mov(r11, count);

  __ enter(); // required for proper stackwalking of RuntimeStub frame

  {
    // Add set memory mark to protect against unsafe accesses faulting
    UnsafeMemoryAccessMark umam(this, ((t == T_BYTE) && !aligned), true);
    __ generate_fill(t, aligned, to, value, r11, rax, xmm0);
  }

  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//   nooverlap_target - entry to branch to if no overlap detected
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4- or 2-byte boundaries, we
// let the hardware handle it.  The two or four words within dwords
// or qwords that span cache line boundaries will still be loaded
// and stored atomically.
//
address StubGenerator::generate_conjoint_short_copy(address nooverlap_target, address *entry) {
  StubGenStubId stub_id = StubGenStubId::jshort_arraycopy_id;
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
#if COMPILER2_OR_JVMCI
  if (VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();
  DecoratorSet decorators = IN_HEAP | IS_ARRAY;

  Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register word_count  = rcx;
  const Register qword_count = count;

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  array_overlap_test(nooverlap_target, Address::times_2);
  setup_arg_regs(); // from => rdi, to => rsi, count => rdx
                    // r9 and r10 may be used to save non-volatile registers

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(word_count, count);
    __ shrptr(count, 2); // count => qword_count

    // Copy from high to low addresses.  Use 'to' as scratch.

    // Check for and copy trailing word
    __ testl(word_count, 1);
    __ jccb(Assembler::zero, L_copy_4_bytes);
    __ movw(rax, Address(from, word_count, Address::times_2, -2));
    __ movw(Address(to, word_count, Address::times_2, -2), rax);

   // Check for and copy trailing dword
  __ BIND(L_copy_4_bytes);
    __ testl(word_count, 2);
    __ jcc(Assembler::zero, L_copy_bytes);
    __ movl(rax, Address(from, qword_count, Address::times_8));
    __ movl(Address(to, qword_count, Address::times_8), rax);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(from, qword_count, Address::times_8, -8));
    __ movq(Address(to, qword_count, Address::times_8, -8), rax);
    __ decrement(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);
  }
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jshort_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !aligned, true);
    // Copy in multi-bytes chunks
    copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, T_SHORT);
  }
  restore_arg_regs();
  INC_COUNTER_NP(SharedRuntime::_jshort_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Arguments:
//   stub_id   - unqiue id for stub to generate
//   entry     - location for return of (post-push) entry
//   is_oop    - true => oop array, so generate store check code
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4-byte boundaries, we let
// the hardware handle it.  The two dwords within qwords that span
// cache line boundaries will still be loaded and stored atomically.
//
// Side Effects:
//   disjoint_int_copy_entry is set to the no-overlap entry point
//   used by generate_conjoint_int_oop_copy().
//
address StubGenerator::generate_disjoint_int_oop_copy(StubGenStubId stub_id, address* entry) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  bool is_oop;
  bool dest_uninitialized;
  switch (stub_id) {
  case StubGenStubId::jint_disjoint_arraycopy_id:
    is_oop = false;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_disjoint_arraycopy_id:
    assert(UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_disjoint_arraycopy_uninit_id:
    assert(UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
#if COMPILER2_OR_JVMCI
  if ((!is_oop || bs->supports_avx3_masked_arraycopy()) && VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_disjoint_copy_avx3_masked(stub_id, entry);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  Label L_copy_bytes, L_copy_8_bytes, L_copy_4_bytes, L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register dword_count = rcx;
  const Register qword_count = count;
  const Register end_from    = from; // source array end address
  const Register end_to      = to;   // destination array end address
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  setup_arg_regs_using_thread(); // from => rdi, to => rsi, count => rdx
                                 // r9 is used to save r15_thread

  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }

  BasicType type = is_oop ? T_OBJECT : T_INT;
  bs->arraycopy_prologue(_masm, decorators, type, from, to, count);

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(dword_count, count);
    __ shrptr(count, 1); // count => qword_count

    // Copy from low to high addresses.  Use 'to' as scratch.
    __ lea(end_from, Address(from, qword_count, Address::times_8, -8));
    __ lea(end_to,   Address(to,   qword_count, Address::times_8, -8));
    __ negptr(qword_count);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(end_from, qword_count, Address::times_8, 8));
    __ movq(Address(end_to, qword_count, Address::times_8, 8), rax);
    __ increment(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);

    // Check for and copy trailing dword
  __ BIND(L_copy_4_bytes);
    __ testl(dword_count, 1); // Only byte test since the value is 0 or 1
    __ jccb(Assembler::zero, L_exit);
    __ movl(rax, Address(end_from, 8));
    __ movl(Address(end_to, 8), rax);
  }
__ BIND(L_exit);
  address ucme_exit_pc = __ pc();
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count);
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(SharedRuntime::_jint_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ vzeroupper();
  __ xorptr(rax, rax); // return 0
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, false, ucme_exit_pc);
    // Copy in multi-bytes chunks
    copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT);
    __ jmp(L_copy_4_bytes);
  }

  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//   nooverlap_target - entry to branch to if no overlap detected
//   is_oop  - true => oop array, so generate store check code
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
// If 'from' and/or 'to' are aligned on 4-byte boundaries, we let
// the hardware handle it.  The two dwords within qwords that span
// cache line boundaries will still be loaded and stored atomically.
//
address StubGenerator::generate_conjoint_int_oop_copy(StubGenStubId stub_id, address nooverlap_target, address *entry) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  bool is_oop;
  bool dest_uninitialized;
  switch (stub_id) {
  case StubGenStubId::jint_arraycopy_id:
    is_oop = false;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_arraycopy_id:
    assert(UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_arraycopy_uninit_id:
    assert(UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
#if COMPILER2_OR_JVMCI
  if ((!is_oop || bs->supports_avx3_masked_arraycopy()) && VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  Label L_copy_bytes, L_copy_8_bytes, L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register count       = rdx;  // elements count
  const Register dword_count = rcx;
  const Register qword_count = count;

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
     // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  array_overlap_test(nooverlap_target, Address::times_4);
  setup_arg_regs_using_thread(); // from => rdi, to => rsi, count => rdx
                                 // r9 is used to save r15_thread

  DecoratorSet decorators = IN_HEAP | IS_ARRAY;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }

  BasicType type = is_oop ? T_OBJECT : T_INT;
  // no registers are destroyed by this call
  bs->arraycopy_prologue(_masm, decorators, type, from, to, count);

  assert_clean_int(count, rax); // Make sure 'count' is clean int.
  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // 'from', 'to' and 'count' are now valid
    __ movptr(dword_count, count);
    __ shrptr(count, 1); // count => qword_count

    // Copy from high to low addresses.  Use 'to' as scratch.

    // Check for and copy trailing dword
    __ testl(dword_count, 1);
    __ jcc(Assembler::zero, L_copy_bytes);
    __ movl(rax, Address(from, dword_count, Address::times_4, -4));
    __ movl(Address(to, dword_count, Address::times_4, -4), rax);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    __ movq(rax, Address(from, qword_count, Address::times_8, -8));
    __ movq(Address(to, qword_count, Address::times_8, -8), rax);
    __ decrement(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);
  }
  if (is_oop) {
    __ jmp(L_exit);
  }
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(SharedRuntime::_jint_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // Copy in multi-bytes chunks
    copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_INT);
  }

__ BIND(L_exit);
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, dword_count);
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(SharedRuntime::_jint_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ xorptr(rax, rax); // return 0
  __ vzeroupper();
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
 // Side Effects:
//   disjoint_oop_copy_entry or disjoint_long_copy_entry is set to the
//   no-overlap entry point used by generate_conjoint_long_oop_copy().
//
address StubGenerator::generate_disjoint_long_oop_copy(StubGenStubId stub_id, address *entry) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  bool is_oop;
  bool dest_uninitialized;
  switch (stub_id) {
  case StubGenStubId::jlong_disjoint_arraycopy_id:
    is_oop = false;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_disjoint_arraycopy_id:
    assert(!UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_disjoint_arraycopy_uninit_id:
    assert(!UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
#if COMPILER2_OR_JVMCI
  if ((!is_oop || bs->supports_avx3_masked_arraycopy()) && VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize >= 32) {
    return generate_disjoint_copy_avx3_masked(stub_id, entry);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  Label L_copy_bytes, L_copy_8_bytes, L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register qword_count = rdx;  // elements count
  const Register end_from    = from; // source array end address
  const Register end_to      = rcx;  // destination array end address
  const Register saved_count = r11;
  // End pointers are inclusive, and if count is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  // Save no-overlap entry point for generate_conjoint_long_oop_copy()
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  setup_arg_regs_using_thread(); // from => rdi, to => rsi, count => rdx
                                   // r9 is used to save r15_thread
  // 'from', 'to' and 'qword_count' are now valid

  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_DISJOINT;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }

  BasicType type = is_oop ? T_OBJECT : T_LONG;
  bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count);
  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);

    // Copy from low to high addresses.  Use 'to' as scratch.
    __ lea(end_from, Address(from, qword_count, Address::times_8, -8));
    __ lea(end_to,   Address(to,   qword_count, Address::times_8, -8));
    __ negptr(qword_count);
    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    bs->copy_load_at(_masm, decorators, type, 8,
                     rax, Address(end_from, qword_count, Address::times_8, 8),
                     r10);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(end_to, qword_count, Address::times_8, 8), rax,
                      r10);
    __ increment(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);
  }
  if (is_oop) {
    __ jmp(L_exit);
  } else {
    restore_arg_regs_using_thread();
    INC_COUNTER_NP(SharedRuntime::_jlong_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
    __ xorptr(rax, rax); // return 0
    __ vzeroupper();
    __ leave(); // required for proper stackwalking of RuntimeStub frame
    __ ret(0);
  }

  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);
    // Copy in multi-bytes chunks
    copy_bytes_forward(end_from, end_to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG);
  }

  __ BIND(L_exit);
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, qword_count);
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(is_oop ? SharedRuntime::_oop_array_copy_ctr :
                          SharedRuntime::_jlong_array_copy_ctr,
                 rscratch1); // Update counter after rscratch1 is free
  __ vzeroupper();
  __ xorptr(rax, rax); // return 0
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Arguments:
//   entry     - location for return of (post-push) entry
//   nooverlap_target - entry to branch to if no overlap detected
//   is_oop  - true => oop array, so generate store check code
//
// Inputs:
//   c_rarg0   - source array address
//   c_rarg1   - destination array address
//   c_rarg2   - element count, treated as ssize_t, can be zero
//
address StubGenerator::generate_conjoint_long_oop_copy(StubGenStubId stub_id, address nooverlap_target, address *entry) {
  // aligned is always false -- x86_64 always uses the unaligned code
  const bool aligned = false;
  bool is_oop;
  bool dest_uninitialized;
  switch (stub_id) {
  case StubGenStubId::jlong_arraycopy_id:
    is_oop = false;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_arraycopy_id:
    assert(!UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = false;
    break;
  case StubGenStubId::oop_arraycopy_uninit_id:
    assert(!UseCompressedOops, "inconsistent oop copy size!");
    is_oop = true;
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
#if COMPILER2_OR_JVMCI
  if ((!is_oop || bs->supports_avx3_masked_arraycopy()) && VM_Version::supports_avx512vlbw() && VM_Version::supports_bmi2() && MaxVectorSize  >= 32) {
    return generate_conjoint_copy_avx3_masked(stub_id, entry, nooverlap_target);
  }
#endif

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  Label L_copy_bytes, L_copy_8_bytes, L_exit;
  const Register from        = rdi;  // source array address
  const Register to          = rsi;  // destination array address
  const Register qword_count = rdx;  // elements count
  const Register saved_count = rcx;

  __ enter(); // required for proper stackwalking of RuntimeStub frame
  assert_clean_int(c_rarg2, rax);    // Make sure 'count' is clean int.

  if (entry != nullptr) {
    *entry = __ pc();
    // caller can pass a 64-bit byte count here (from Unsafe.copyMemory)
    BLOCK_COMMENT("Entry:");
  }

  array_overlap_test(nooverlap_target, Address::times_8);
  setup_arg_regs_using_thread(); // from => rdi, to => rsi, count => rdx
                                 // r9 is used to save r15_thread
  // 'from', 'to' and 'qword_count' are now valid

  DecoratorSet decorators = IN_HEAP | IS_ARRAY;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }
  if (aligned) {
    decorators |= ARRAYCOPY_ALIGNED;
  }

  BasicType type = is_oop ? T_OBJECT : T_LONG;
  bs->arraycopy_prologue(_masm, decorators, type, from, to, qword_count);
  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);

    __ jmp(L_copy_bytes);

    // Copy trailing qwords
  __ BIND(L_copy_8_bytes);
    bs->copy_load_at(_masm, decorators, type, 8,
                     rax, Address(from, qword_count, Address::times_8, -8),
                     r10);
    bs->copy_store_at(_masm, decorators, type, 8,
                      Address(to, qword_count, Address::times_8, -8), rax,
                      r10);
    __ decrement(qword_count);
    __ jcc(Assembler::notZero, L_copy_8_bytes);
  }
  if (is_oop) {
    __ jmp(L_exit);
  } else {
    restore_arg_regs_using_thread();
    INC_COUNTER_NP(SharedRuntime::_jlong_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
    __ xorptr(rax, rax); // return 0
    __ vzeroupper();
    __ leave(); // required for proper stackwalking of RuntimeStub frame
    __ ret(0);
  }
  {
    // UnsafeMemoryAccess page error: continue after unsafe access
    UnsafeMemoryAccessMark umam(this, !is_oop && !aligned, true);

    // Copy in multi-bytes chunks
    copy_bytes_backward(from, to, qword_count, rax, r10, L_copy_bytes, L_copy_8_bytes, decorators, is_oop ? T_OBJECT : T_LONG);
  }
  __ BIND(L_exit);
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, qword_count);
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(is_oop ? SharedRuntime::_oop_array_copy_ctr :
                          SharedRuntime::_jlong_array_copy_ctr,
                 rscratch1); // Update counter after rscratch1 is free
  __ vzeroupper();
  __ xorptr(rax, rax); // return 0
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


// Helper for generating a dynamic type check.
// Smashes no registers.
void StubGenerator::generate_type_check(Register sub_klass,
                                        Register super_check_offset,
                                        Register super_klass,
                                        Label& L_success) {
  assert_different_registers(sub_klass, super_check_offset, super_klass);

  BLOCK_COMMENT("type_check:");

  Label L_miss;

  __ check_klass_subtype_fast_path(sub_klass, super_klass, noreg,        &L_success, &L_miss, nullptr,
                                   super_check_offset);
  __ check_klass_subtype_slow_path(sub_klass, super_klass, noreg, noreg, &L_success, nullptr);

  // Fall through on failure!
  __ BIND(L_miss);
}

//
//  Generate checkcasting array copy stub
//
//  Input:
//    c_rarg0   - source array address
//    c_rarg1   - destination array address
//    c_rarg2   - element count, treated as ssize_t, can be zero
//    c_rarg3   - size_t ckoff (super_check_offset)
// not Win64
//    c_rarg4   - oop ckval (super_klass)
// Win64
//    rsp+40    - oop ckval (super_klass)
//
//  Output:
//    rax ==  0  -  success
//    rax == -1^K - failure, where K is partial transfer count
//
address StubGenerator::generate_checkcast_copy(StubGenStubId stub_id, address *entry) {

  bool dest_uninitialized;
  switch (stub_id) {
  case StubGenStubId::checkcast_arraycopy_id:
    dest_uninitialized = false;
    break;
  case StubGenStubId::checkcast_arraycopy_uninit_id:
    dest_uninitialized = true;
    break;
  default:
    ShouldNotReachHere();
  }

  Label L_load_element, L_store_element, L_do_card_marks, L_done;

  // Input registers (after setup_arg_regs)
  const Register from        = rdi;   // source array address
  const Register to          = rsi;   // destination array address
  const Register length      = rdx;   // elements count
  const Register ckoff       = rcx;   // super_check_offset
  const Register ckval       = r8;    // super_klass

  // Registers used as temps (r13, r14 are save-on-entry)
  const Register end_from    = from;  // source array end address
  const Register end_to      = r13;   // destination array end address
  const Register count       = rdx;   // -(count_remaining)
  const Register r14_length  = r14;   // saved copy of length
  // End pointers are inclusive, and if length is not zero they point
  // to the last unit copied:  end_to[0] := end_from[0]

  const Register rax_oop    = rax;    // actual oop copied
  const Register r11_klass  = r11;    // oop._klass

  //---------------------------------------------------------------
  // Assembler stub will be used for this call to arraycopy
  // if the two arrays are subtypes of Object[] but the
  // destination array type is not equal to or a supertype
  // of the source type.  Each element must be separately
  // checked.

  __ align(CodeEntryAlignment);
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  __ enter(); // required for proper stackwalking of RuntimeStub frame

#ifdef ASSERT
  // caller guarantees that the arrays really are different
  // otherwise, we would have to make conjoint checks
  { Label L;
    array_overlap_test(L, TIMES_OOP);
    __ stop("checkcast_copy within a single array");
    __ bind(L);
  }
#endif //ASSERT

  setup_arg_regs_using_thread(4); // from => rdi, to => rsi, length => rdx
                                  // ckoff => rcx, ckval => r8
                                  // r9 is used to save r15_thread
#ifdef _WIN64
  // last argument (#4) is on stack on Win64
  __ movptr(ckval, Address(rsp, 6 * wordSize));
#endif

  // Caller of this entry point must set up the argument registers.
  if (entry != nullptr) {
    *entry = __ pc();
    BLOCK_COMMENT("Entry:");
  }

  // allocate spill slots for r13, r14
  enum {
    saved_r13_offset,
    saved_r14_offset,
    saved_r10_offset,
    saved_rbp_offset
  };
  __ subptr(rsp, saved_rbp_offset * wordSize);
  __ movptr(Address(rsp, saved_r13_offset * wordSize), r13);
  __ movptr(Address(rsp, saved_r14_offset * wordSize), r14);
  __ movptr(Address(rsp, saved_r10_offset * wordSize), r10);

#ifdef ASSERT
    Label L2;
    __ get_thread_slow(r14);
    __ cmpptr(r15_thread, r14);
    __ jcc(Assembler::equal, L2);
    __ stop("StubRoutines::call_stub: r15_thread is modified by call");
    __ bind(L2);
#endif // ASSERT

  // check that int operands are properly extended to size_t
  assert_clean_int(length, rax);
  assert_clean_int(ckoff, rax);

#ifdef ASSERT
  BLOCK_COMMENT("assert consistent ckoff/ckval");
  // The ckoff and ckval must be mutually consistent,
  // even though caller generates both.
  { Label L;
    int sco_offset = in_bytes(Klass::super_check_offset_offset());
    __ cmpl(ckoff, Address(ckval, sco_offset));
    __ jcc(Assembler::equal, L);
    __ stop("super_check_offset inconsistent");
    __ bind(L);
  }
#endif //ASSERT

  // Loop-invariant addresses.  They are exclusive end pointers.
  Address end_from_addr(from, length, TIMES_OOP, 0);
  Address   end_to_addr(to,   length, TIMES_OOP, 0);
  // Loop-variant addresses.  They assume post-incremented count < 0.
  Address from_element_addr(end_from, count, TIMES_OOP, 0);
  Address   to_element_addr(end_to,   count, TIMES_OOP, 0);

  DecoratorSet decorators = IN_HEAP | IS_ARRAY | ARRAYCOPY_CHECKCAST | ARRAYCOPY_DISJOINT;
  if (dest_uninitialized) {
    decorators |= IS_DEST_UNINITIALIZED;
  }

  BasicType type = T_OBJECT;
  size_t element_size = UseCompressedOops ? 4 : 8;

  BarrierSetAssembler *bs = BarrierSet::barrier_set()->barrier_set_assembler();
  bs->arraycopy_prologue(_masm, decorators, type, from, to, count);

  // Copy from low to high addresses, indexed from the end of each array.
  __ lea(end_from, end_from_addr);
  __ lea(end_to,   end_to_addr);
  __ movptr(r14_length, length);        // save a copy of the length
  assert(length == count, "");          // else fix next line:
  __ negptr(count);                     // negate and test the length
  __ jcc(Assembler::notZero, L_load_element);

  // Empty array:  Nothing to do.
  __ xorptr(rax, rax);                  // return 0 on (trivial) success
  __ jmp(L_done);

  // ======== begin loop ========
  // (Loop is rotated; its entry is L_load_element.)
  // Loop control:
  //   for (count = -count; count != 0; count++)
  // Base pointers src, dst are biased by 8*(count-1),to last element.
  __ align(OptoLoopAlignment);

  __ BIND(L_store_element);
  bs->copy_store_at(_masm,
                    decorators,
                    type,
                    element_size,
                    to_element_addr,
                    rax_oop,
                    r10);
  __ increment(count);               // increment the count toward zero
  __ jcc(Assembler::zero, L_do_card_marks);

  // ======== loop entry is here ========
  __ BIND(L_load_element);
  bs->copy_load_at(_masm,
                   decorators,
                   type,
                   element_size,
                   rax_oop,
                   from_element_addr,
                   r10);
  __ testptr(rax_oop, rax_oop);
  __ jcc(Assembler::zero, L_store_element);

  __ load_klass(r11_klass, rax_oop, rscratch1);// query the object klass
  generate_type_check(r11_klass, ckoff, ckval, L_store_element);
  // ======== end loop ========

  // It was a real error; we must depend on the caller to finish the job.
  // Register rdx = -1 * number of *remaining* oops, r14 = *total* oops.
  // Emit GC store barriers for the oops we have copied (r14 + rdx),
  // and report their number to the caller.
  assert_different_registers(rax, r14_length, count, to, end_to, rcx, rscratch1);
  Label L_post_barrier;
  __ addptr(r14_length, count);     // K = (original - remaining) oops
  __ movptr(rax, r14_length);       // save the value
  __ notptr(rax);                   // report (-1^K) to caller (does not affect flags)
  __ jccb(Assembler::notZero, L_post_barrier);
  __ jmp(L_done); // K == 0, nothing was copied, skip post barrier

  // Come here on success only.
  __ BIND(L_do_card_marks);
  __ xorptr(rax, rax);              // return 0 on success

  __ BIND(L_post_barrier);
  bs->arraycopy_epilogue(_masm, decorators, type, from, to, r14_length);

  // Common exit point (success or failure).
  __ BIND(L_done);
  __ movptr(r13, Address(rsp, saved_r13_offset * wordSize));
  __ movptr(r14, Address(rsp, saved_r14_offset * wordSize));
  __ movptr(r10, Address(rsp, saved_r10_offset * wordSize));
  restore_arg_regs_using_thread();
  INC_COUNTER_NP(SharedRuntime::_checkcast_array_copy_ctr, rscratch1); // Update counter after rscratch1 is free
  __ leave(); // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}


//  Generate 'unsafe' array copy stub
//  Though just as safe as the other stubs, it takes an unscaled
//  size_t argument instead of an element count.
//
//  Input:
//    c_rarg0   - source array address
//    c_rarg1   - destination array address
//    c_rarg2   - byte count, treated as ssize_t, can be zero
//
// Examines the alignment of the operands and dispatches
// to a long, int, short, or byte copy loop.
//
address StubGenerator::generate_unsafe_copy(address byte_copy_entry, address short_copy_entry,
                                            address int_copy_entry, address long_copy_entry) {

  Label L_long_aligned, L_int_aligned, L_short_aligned;

  // Input registers (before setup_arg_regs)
  const Register from        = c_rarg0;  // source array address
  const Register to          = c_rarg1;  // destination array address
  const Register size        = c_rarg2;  // byte count (size_t)

  // Register used as a temp
  const Register bits        = rax;      // test copy of low bits

  __ align(CodeEntryAlignment);
  StubGenStubId stub_id = StubGenStubId::unsafe_arraycopy_id;
  StubCodeMark mark(this, stub_id);
  address start = __ pc();

  __ enter(); // required for proper stackwalking of RuntimeStub frame

  // bump this on entry, not on exit:
  INC_COUNTER_NP(SharedRuntime::_unsafe_array_copy_ctr, rscratch1);

  __ mov(bits, from);
  __ orptr(bits, to);
  __ orptr(bits, size);

  __ testb(bits, BytesPerLong-1);
  __ jccb(Assembler::zero, L_long_aligned);

  __ testb(bits, BytesPerInt-1);
  __ jccb(Assembler::zero, L_int_aligned);

  __ testb(bits, BytesPerShort-1);
  __ jump_cc(Assembler::notZero, RuntimeAddress(byte_copy_entry));

  __ BIND(L_short_aligned);
  __ shrptr(size, LogBytesPerShort); // size => short_count
  __ jump(RuntimeAddress(short_copy_entry));

  __ BIND(L_int_aligned);
  __ shrptr(size, LogBytesPerInt); // size => int_count
  __ jump(RuntimeAddress(int_copy_entry));

  __ BIND(L_long_aligned);
  __ shrptr(size, LogBytesPerLong); // size => qword_count
  __ jump(RuntimeAddress(long_copy_entry));

  return start;
}


// Static enum for helper
enum USM_TYPE {USM_SHORT, USM_DWORD, USM_QUADWORD};
// Helper for generate_unsafe_setmemory
//
// Atomically fill an array of memory using 2-, 4-, or 8-byte chunks
static void do_setmemory_atomic_loop(USM_TYPE type, Register dest,
                                     Register size, Register wide_value,
                                     Register tmp, Label& L_exit,
                                     MacroAssembler *_masm) {
  Label L_Loop, L_Tail, L_TailLoop;

  int shiftval = 0;
  int incr = 0;

  switch (type) {
    case USM_SHORT:
      shiftval = 1;
      incr = 16;
      break;
    case USM_DWORD:
      shiftval = 2;
      incr = 32;
      break;
    case USM_QUADWORD:
      shiftval = 3;
      incr = 64;
      break;
  }

  // At this point, we know the lower bits of size are zero
  __ shrq(size, shiftval);
  // size now has number of X-byte chunks (2, 4 or 8)

  // Number of (8*X)-byte chunks into tmp
  __ movq(tmp, size);
  __ shrq(tmp, 3);
  __ jccb(Assembler::zero, L_Tail);

  __ BIND(L_Loop);

  // Unroll 8 stores
  for (int i = 0; i < 8; i++) {
    switch (type) {
      case USM_SHORT:
        __ movw(Address(dest, (2 * i)), wide_value);
        break;
      case USM_DWORD:
        __ movl(Address(dest, (4 * i)), wide_value);
        break;
      case USM_QUADWORD:
        __ movq(Address(dest, (8 * i)), wide_value);
        break;
    }
  }
  __ addq(dest, incr);
  __ decrementq(tmp);
  __ jccb(Assembler::notZero, L_Loop);

  __ BIND(L_Tail);

  // Find number of remaining X-byte chunks
  __ andq(size, 0x7);

  // If zero, then we're done
  __ jccb(Assembler::zero, L_exit);

  __ BIND(L_TailLoop);

    switch (type) {
      case USM_SHORT:
        __ movw(Address(dest, 0), wide_value);
        break;
      case USM_DWORD:
        __ movl(Address(dest, 0), wide_value);
        break;
      case USM_QUADWORD:
        __ movq(Address(dest, 0), wide_value);
        break;
    }
  __ addq(dest, incr >> 3);
  __ decrementq(size);
  __ jccb(Assembler::notZero, L_TailLoop);
}

//  Generate 'unsafe' set memory stub
//  Though just as safe as the other stubs, it takes an unscaled
//  size_t (# bytes) argument instead of an element count.
//
//  Input:
//    c_rarg0   - destination array address
//    c_rarg1   - byte count (size_t)
//    c_rarg2   - byte value
//
// Examines the alignment of the operands and dispatches
// to an int, short, or byte fill loop.
//
address StubGenerator::generate_unsafe_setmemory(address unsafe_byte_fill) {
  __ align(CodeEntryAlignment);
  StubGenStubId stub_id = StubGenStubId::unsafe_setmemory_id;
  StubCodeMark mark(this, stub_id);
  address start = __ pc();
  __ enter();   // required for proper stackwalking of RuntimeStub frame

  assert(unsafe_byte_fill != nullptr, "Invalid call");

  // bump this on entry, not on exit:
  INC_COUNTER_NP(SharedRuntime::_unsafe_set_memory_ctr, rscratch1);

  {
    Label L_exit, L_fillQuadwords, L_fillDwords, L_fillBytes;

    const Register dest = c_rarg0;
    const Register size = c_rarg1;
    const Register byteVal = c_rarg2;
    const Register wide_value = rax;
    const Register rScratch1 = r10;

    assert_different_registers(dest, size, byteVal, wide_value, rScratch1);

    //     fill_to_memory_atomic(unsigned char*, unsigned long, unsigned char)

    __ testq(size, size);
    __ jcc(Assembler::zero, L_exit);

    // Propagate byte to full Register
    __ movzbl(rScratch1, byteVal);
    __ mov64(wide_value, 0x0101010101010101ULL);
    __ imulq(wide_value, rScratch1);

    // Check for pointer & size alignment
    __ movq(rScratch1, dest);
    __ orq(rScratch1, size);

    __ testb(rScratch1, 7);
    __ jcc(Assembler::equal, L_fillQuadwords);

    __ testb(rScratch1, 3);
    __ jcc(Assembler::equal, L_fillDwords);

    __ testb(rScratch1, 1);
    __ jcc(Assembler::notEqual, L_fillBytes);

    // Fill words
    {
      UnsafeMemoryAccessMark umam(this, true, true);

      // At this point, we know the lower bit of size is zero and a
      // multiple of 2
      do_setmemory_atomic_loop(USM_SHORT, dest, size, wide_value, rScratch1,
                               L_exit, _masm);
    }
    __ jmpb(L_exit);

    __ BIND(L_fillQuadwords);

    // Fill QUADWORDs
    {
      UnsafeMemoryAccessMark umam(this, true, true);

      // At this point, we know the lower 3 bits of size are zero and a
      // multiple of 8
      do_setmemory_atomic_loop(USM_QUADWORD, dest, size, wide_value, rScratch1,
                               L_exit, _masm);
    }
    __ BIND(L_exit);

    __ leave();   // required for proper stackwalking of RuntimeStub frame
    __ ret(0);

    __ BIND(L_fillDwords);

    // Fill DWORDs
    {
      UnsafeMemoryAccessMark umam(this, true, true);

      // At this point, we know the lower 2 bits of size are zero and a
      // multiple of 4
      do_setmemory_atomic_loop(USM_DWORD, dest, size, wide_value, rScratch1,
                               L_exit, _masm);
    }
    __ jmpb(L_exit);

    __ BIND(L_fillBytes);
    // Set up for tail call to previously generated byte fill routine
    // Parameter order is (ptr, byteVal, size)
    __ xchgq(c_rarg1, c_rarg2);
    __ leave();    // Clear effect of enter()
    __ jump(RuntimeAddress(unsafe_byte_fill));
  }

  return start;
}

// Perform range checks on the proposed arraycopy.
// Kills temp, but nothing else.
// Also, clean the sign bits of src_pos and dst_pos.
void StubGenerator::arraycopy_range_checks(Register src,     // source array oop (c_rarg0)
                                           Register src_pos, // source position (c_rarg1)
                                           Register dst,     // destination array oo (c_rarg2)
                                           Register dst_pos, // destination position (c_rarg3)
                                           Register length,
                                           Register temp,
                                           Label& L_failed) {
  BLOCK_COMMENT("arraycopy_range_checks:");

  //  if (src_pos + length > arrayOop(src)->length())  FAIL;
  __ movl(temp, length);
  __ addl(temp, src_pos);             // src_pos + length
  __ cmpl(temp, Address(src, arrayOopDesc::length_offset_in_bytes()));
  __ jcc(Assembler::above, L_failed);

  //  if (dst_pos + length > arrayOop(dst)->length())  FAIL;
  __ movl(temp, length);
  __ addl(temp, dst_pos);             // dst_pos + length
  __ cmpl(temp, Address(dst, arrayOopDesc::length_offset_in_bytes()));
  __ jcc(Assembler::above, L_failed);

  // Have to clean up high 32-bits of 'src_pos' and 'dst_pos'.
  // Move with sign extension can be used since they are positive.
  __ movslq(src_pos, src_pos);
  __ movslq(dst_pos, dst_pos);

  BLOCK_COMMENT("arraycopy_range_checks done");
}


//  Generate generic array copy stubs
//
//  Input:
//    c_rarg0    -  src oop
//    c_rarg1    -  src_pos (32-bits)
//    c_rarg2    -  dst oop
//    c_rarg3    -  dst_pos (32-bits)
// not Win64
//    c_rarg4    -  element count (32-bits)
// Win64
//    rsp+40     -  element count (32-bits)
//
//  Output:
//    rax ==  0  -  success
//    rax == -1^K - failure, where K is partial transfer count
//
address StubGenerator::generate_generic_copy(address byte_copy_entry, address short_copy_entry,
                                             address int_copy_entry, address oop_copy_entry,
                                             address long_copy_entry, address checkcast_copy_entry) {

  Label L_failed, L_failed_0, L_objArray;
  Label L_copy_shorts, L_copy_ints, L_copy_longs;

  // Input registers
  const Register src        = c_rarg0;  // source array oop
  const Register src_pos    = c_rarg1;  // source position
  const Register dst        = c_rarg2;  // destination array oop
  const Register dst_pos    = c_rarg3;  // destination position
#ifndef _WIN64
  const Register length     = c_rarg4;
  const Register rklass_tmp = r9;  // load_klass
#else
  const Address  length(rsp, 7 * wordSize);  // elements count is on stack on Win64
  const Register rklass_tmp = rdi;  // load_klass
#endif

  { int modulus = CodeEntryAlignment;
    int target  = modulus - 5; // 5 = sizeof jmp(L_failed)
    int advance = target - (__ offset() % modulus);
    if (advance < 0)  advance += modulus;
    if (advance > 0)  __ nop(advance);
  }
  StubGenStubId stub_id = StubGenStubId::generic_arraycopy_id;
  StubCodeMark mark(this, stub_id);

  // Short-hop target to L_failed.  Makes for denser prologue code.
  __ BIND(L_failed_0);
  __ jmp(L_failed);
  assert(__ offset() % CodeEntryAlignment == 0, "no further alignment needed");

  __ align(CodeEntryAlignment);
  address start = __ pc();

  __ enter(); // required for proper stackwalking of RuntimeStub frame

#ifdef _WIN64
  __ push(rklass_tmp); // rdi is callee-save on Windows
#endif

  // bump this on entry, not on exit:
  INC_COUNTER_NP(SharedRuntime::_generic_array_copy_ctr, rscratch1);

  //-----------------------------------------------------------------------
  // Assembler stub will be used for this call to arraycopy
  // if the following conditions are met:
  //
  // (1) src and dst must not be null.
  // (2) src_pos must not be negative.
  // (3) dst_pos must not be negative.
  // (4) length  must not be negative.
  // (5) src klass and dst klass should be the same and not null.
  // (6) src and dst should be arrays.
  // (7) src_pos + length must not exceed length of src.
  // (8) dst_pos + length must not exceed length of dst.
  //

  //  if (src == nullptr) return -1;
  __ testptr(src, src);         // src oop
  size_t j1off = __ offset();
  __ jccb(Assembler::zero, L_failed_0);

  //  if (src_pos < 0) return -1;
  __ testl(src_pos, src_pos); // src_pos (32-bits)
  __ jccb(Assembler::negative, L_failed_0);

  //  if (dst == nullptr) return -1;
  __ testptr(dst, dst);         // dst oop
  __ jccb(Assembler::zero, L_failed_0);

  //  if (dst_pos < 0) return -1;
  __ testl(dst_pos, dst_pos); // dst_pos (32-bits)
  size_t j4off = __ offset();
  __ jccb(Assembler::negative, L_failed_0);

  // The first four tests are very dense code,
  // but not quite dense enough to put four
  // jumps in a 16-byte instruction fetch buffer.
  // That's good, because some branch predicters
  // do not like jumps so close together.
  // Make sure of this.
  guarantee(((j1off ^ j4off) & ~15) != 0, "I$ line of 1st & 4th jumps");

  // registers used as temp
  const Register r11_length    = r11; // elements count to copy
  const Register r10_src_klass = r10; // array klass

  //  if (length < 0) return -1;
  __ movl(r11_length, length);        // length (elements count, 32-bits value)
  __ testl(r11_length, r11_length);
  __ jccb(Assembler::negative, L_failed_0);

  __ load_klass(r10_src_klass, src, rklass_tmp);
#ifdef ASSERT
  //  assert(src->klass() != nullptr);
  {
    BLOCK_COMMENT("assert klasses not null {");
    Label L1, L2;
    __ testptr(r10_src_klass, r10_src_klass);
    __ jcc(Assembler::notZero, L2);   // it is broken if klass is null
    __ bind(L1);
    __ stop("broken null klass");
    __ bind(L2);
    __ load_klass(rax, dst, rklass_tmp);
    __ cmpq(rax, 0);
    __ jcc(Assembler::equal, L1);     // this would be broken also
    BLOCK_COMMENT("} assert klasses not null done");
  }
#endif

  // Load layout helper (32-bits)
  //
  //  |array_tag|     | header_size | element_type |     |log2_element_size|
  // 32        30    24            16              8     2                 0
  //
  //   array_tag: typeArray = 0x3, objArray = 0x2, non-array = 0x0
  //

  const int lh_offset = in_bytes(Klass::layout_helper_offset());

  // Handle objArrays completely differently...
  const jint objArray_lh = Klass::array_layout_helper(T_OBJECT);
  __ cmpl(Address(r10_src_klass, lh_offset), objArray_lh);
  __ jcc(Assembler::equal, L_objArray);

  //  if (src->klass() != dst->klass()) return -1;
  __ load_klass(rax, dst, rklass_tmp);
  __ cmpq(r10_src_klass, rax);
  __ jcc(Assembler::notEqual, L_failed);

  const Register rax_lh = rax;  // layout helper
  __ movl(rax_lh, Address(r10_src_klass, lh_offset));

  //  if (!src->is_Array()) return -1;
  __ cmpl(rax_lh, Klass::_lh_neutral_value);
  __ jcc(Assembler::greaterEqual, L_failed);

  // At this point, it is known to be a typeArray (array_tag 0x3).
#ifdef ASSERT
  {
    BLOCK_COMMENT("assert primitive array {");
    Label L;
    __ cmpl(rax_lh, (Klass::_lh_array_tag_type_value << Klass::_lh_array_tag_shift));
    __ jcc(Assembler::greaterEqual, L);
    __ stop("must be a primitive array");
    __ bind(L);
    BLOCK_COMMENT("} assert primitive array done");
  }
#endif

  arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length,
                         r10, L_failed);

  // TypeArrayKlass
  //
  // src_addr = (src + array_header_in_bytes()) + (src_pos << log2elemsize);
  // dst_addr = (dst + array_header_in_bytes()) + (dst_pos << log2elemsize);
  //

  const Register r10_offset = r10;    // array offset
  const Register rax_elsize = rax_lh; // element size

  __ movl(r10_offset, rax_lh);
  __ shrl(r10_offset, Klass::_lh_header_size_shift);
  __ andptr(r10_offset, Klass::_lh_header_size_mask);   // array_offset
  __ addptr(src, r10_offset);           // src array offset
  __ addptr(dst, r10_offset);           // dst array offset
  BLOCK_COMMENT("choose copy loop based on element size");
  __ andl(rax_lh, Klass::_lh_log2_element_size_mask); // rax_lh -> rax_elsize

#ifdef _WIN64
  __ pop(rklass_tmp); // Restore callee-save rdi
#endif

  // next registers should be set before the jump to corresponding stub
  const Register from     = c_rarg0;  // source array address
  const Register to       = c_rarg1;  // destination array address
  const Register count    = c_rarg2;  // elements count

  // 'from', 'to', 'count' registers should be set in such order
  // since they are the same as 'src', 'src_pos', 'dst'.

  __ cmpl(rax_elsize, 0);
  __ jccb(Assembler::notEqual, L_copy_shorts);
  __ lea(from, Address(src, src_pos, Address::times_1, 0));// src_addr
  __ lea(to,   Address(dst, dst_pos, Address::times_1, 0));// dst_addr
  __ movl2ptr(count, r11_length); // length
  __ jump(RuntimeAddress(byte_copy_entry));

__ BIND(L_copy_shorts);
  __ cmpl(rax_elsize, LogBytesPerShort);
  __ jccb(Assembler::notEqual, L_copy_ints);
  __ lea(from, Address(src, src_pos, Address::times_2, 0));// src_addr
  __ lea(to,   Address(dst, dst_pos, Address::times_2, 0));// dst_addr
  __ movl2ptr(count, r11_length); // length
  __ jump(RuntimeAddress(short_copy_entry));

__ BIND(L_copy_ints);
  __ cmpl(rax_elsize, LogBytesPerInt);
  __ jccb(Assembler::notEqual, L_copy_longs);
  __ lea(from, Address(src, src_pos, Address::times_4, 0));// src_addr
  __ lea(to,   Address(dst, dst_pos, Address::times_4, 0));// dst_addr
  __ movl2ptr(count, r11_length); // length
  __ jump(RuntimeAddress(int_copy_entry));

__ BIND(L_copy_longs);
#ifdef ASSERT
  {
    BLOCK_COMMENT("assert long copy {");
    Label L;
    __ cmpl(rax_elsize, LogBytesPerLong);
    __ jcc(Assembler::equal, L);
    __ stop("must be long copy, but elsize is wrong");
    __ bind(L);
    BLOCK_COMMENT("} assert long copy done");
  }
#endif
  __ lea(from, Address(src, src_pos, Address::times_8, 0));// src_addr
  __ lea(to,   Address(dst, dst_pos, Address::times_8, 0));// dst_addr
  __ movl2ptr(count, r11_length); // length
  __ jump(RuntimeAddress(long_copy_entry));

  // ObjArrayKlass
__ BIND(L_objArray);
  // live at this point:  r10_src_klass, r11_length, src[_pos], dst[_pos]

  Label L_plain_copy, L_checkcast_copy;
  //  test array classes for subtyping
  __ load_klass(rax, dst, rklass_tmp);
  __ cmpq(r10_src_klass, rax); // usual case is exact equality
  __ jcc(Assembler::notEqual, L_checkcast_copy);

  // Identically typed arrays can be copied without element-wise checks.
  arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length,
                         r10, L_failed);

  __ lea(from, Address(src, src_pos, TIMES_OOP,
               arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // src_addr
  __ lea(to,   Address(dst, dst_pos, TIMES_OOP,
               arrayOopDesc::base_offset_in_bytes(T_OBJECT))); // dst_addr
  __ movl2ptr(count, r11_length); // length
__ BIND(L_plain_copy);
#ifdef _WIN64
  __ pop(rklass_tmp); // Restore callee-save rdi
#endif
  __ jump(RuntimeAddress(oop_copy_entry));

__ BIND(L_checkcast_copy);
  // live at this point:  r10_src_klass, r11_length, rax (dst_klass)
  {
    // Before looking at dst.length, make sure dst is also an objArray.
    __ cmpl(Address(rax, lh_offset), objArray_lh);
    __ jcc(Assembler::notEqual, L_failed);

    // It is safe to examine both src.length and dst.length.
    arraycopy_range_checks(src, src_pos, dst, dst_pos, r11_length,
                           rax, L_failed);

    const Register r11_dst_klass = r11;
    __ load_klass(r11_dst_klass, dst, rklass_tmp); // reload

    // Marshal the base address arguments now, freeing registers.
    __ lea(from, Address(src, src_pos, TIMES_OOP,
                 arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
    __ lea(to,   Address(dst, dst_pos, TIMES_OOP,
                 arrayOopDesc::base_offset_in_bytes(T_OBJECT)));
    __ movl(count, length);           // length (reloaded)
    Register sco_temp = c_rarg3;      // this register is free now
    assert_different_registers(from, to, count, sco_temp,
                               r11_dst_klass, r10_src_klass);
    assert_clean_int(count, sco_temp);

    // Generate the type check.
    const int sco_offset = in_bytes(Klass::super_check_offset_offset());
    __ movl(sco_temp, Address(r11_dst_klass, sco_offset));
    assert_clean_int(sco_temp, rax);
    generate_type_check(r10_src_klass, sco_temp, r11_dst_klass, L_plain_copy);

    // Fetch destination element klass from the ObjArrayKlass header.
    int ek_offset = in_bytes(ObjArrayKlass::element_klass_offset());
    __ movptr(r11_dst_klass, Address(r11_dst_klass, ek_offset));
    __ movl(  sco_temp,      Address(r11_dst_klass, sco_offset));
    assert_clean_int(sco_temp, rax);

#ifdef _WIN64
    __ pop(rklass_tmp); // Restore callee-save rdi
#endif

    // the checkcast_copy loop needs two extra arguments:
    assert(c_rarg3 == sco_temp, "#3 already in place");
    // Set up arguments for checkcast_copy_entry.
    setup_arg_regs_using_thread(4);
    __ movptr(r8, r11_dst_klass);  // dst.klass.element_klass, r8 is c_rarg4 on Linux/Solaris
    __ jump(RuntimeAddress(checkcast_copy_entry));
  }

__ BIND(L_failed);
#ifdef _WIN64
  __ pop(rklass_tmp); // Restore callee-save rdi
#endif
  __ xorptr(rax, rax);
  __ notptr(rax); // return -1
  __ leave();   // required for proper stackwalking of RuntimeStub frame
  __ ret(0);

  return start;
}

#undef __
