diff a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
--- a/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
+++ b/src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp
@@ -1,7 +1,8 @@
 /*
  * Copyright (c) 2018, 2021, Red Hat, Inc. All rights reserved.
+ * Copyright Amazon.com Inc. 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.
@@ -29,10 +30,11 @@
 #include "gc/shenandoah/shenandoahHeap.inline.hpp"
 #include "gc/shenandoah/shenandoahHeapRegion.hpp"
 #include "gc/shenandoah/shenandoahRuntime.hpp"
 #include "gc/shenandoah/shenandoahThreadLocalData.hpp"
 #include "gc/shenandoah/heuristics/shenandoahHeuristics.hpp"
+#include "gc/shenandoah/mode/shenandoahMode.hpp"
 #include "interpreter/interpreter.hpp"
 #include "runtime/javaThread.hpp"
 #include "runtime/sharedRuntime.hpp"
 #include "utilities/macros.hpp"
 #ifdef COMPILER1
@@ -118,10 +120,33 @@
                                                        Register src, Register dst, Register count) {
 
   bool dest_uninitialized = (decorators & IS_DEST_UNINITIALIZED) != 0;
 
   if (is_reference_type(type)) {
+    if (ShenandoahCardBarrier) {
+      bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0;
+      bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0;
+      bool obj_int = type == T_OBJECT LP64_ONLY(&& UseCompressedOops);
+
+      // We need to save the original element count because the array copy stub
+      // will destroy the value and we need it for the card marking barrier.
+#ifdef _LP64
+      if (!checkcast) {
+        if (!obj_int) {
+          // Save count for barrier
+          __ movptr(r11, count);
+        } else if (disjoint) {
+          // Save dst in r11 in the disjoint case
+          __ movq(r11, dst);
+        }
+      }
+#else
+      if (disjoint) {
+        __ mov(rdx, dst);          // save 'to'
+      }
+#endif
+    }
 
     if ((ShenandoahSATBBarrier && !dest_uninitialized) || ShenandoahLoadRefBarrier) {
 #ifdef _LP64
       Register thread = r15_thread;
 #else
@@ -138,25 +163,25 @@
       __ push(thread);
       __ get_thread(thread);
 #endif
       assert_different_registers(src, dst, count, thread);
 
-      Label done;
+      Label L_done;
       // Short-circuit if count == 0.
       __ testptr(count, count);
-      __ jcc(Assembler::zero, done);
+      __ jcc(Assembler::zero, L_done);
 
       // Avoid runtime call when not active.
       Address gc_state(thread, in_bytes(ShenandoahThreadLocalData::gc_state_offset()));
       int flags;
       if (ShenandoahSATBBarrier && dest_uninitialized) {
         flags = ShenandoahHeap::HAS_FORWARDED;
       } else {
         flags = ShenandoahHeap::HAS_FORWARDED | ShenandoahHeap::MARKING;
       }
       __ testb(gc_state, flags);
-      __ jcc(Assembler::zero, done);
+      __ jcc(Assembler::zero, L_done);
 
       save_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false);
 
 #ifdef _LP64
       assert(src == rdi, "expected");
@@ -172,17 +197,47 @@
                         src, dst, count);
       }
 
       restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ false);
 
-      __ bind(done);
+      __ bind(L_done);
       NOT_LP64(__ pop(thread);)
     }
   }
 
 }
 
+void ShenandoahBarrierSetAssembler::arraycopy_epilogue(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
+                                                       Register src, Register dst, Register count) {
+
+  if (ShenandoahCardBarrier && is_reference_type(type)) {
+    bool checkcast = (decorators & ARRAYCOPY_CHECKCAST) != 0;
+    bool disjoint = (decorators & ARRAYCOPY_DISJOINT) != 0;
+    bool obj_int = type == T_OBJECT LP64_ONLY(&& UseCompressedOops);
+    Register tmp = rax;
+
+#ifdef _LP64
+    if (!checkcast) {
+      if (!obj_int) {
+        // Save count for barrier
+        count = r11;
+      } else if (disjoint) {
+        // Use the saved dst in the disjoint case
+        dst = r11;
+      }
+    } else {
+      tmp = rscratch1;
+    }
+#else
+    if (disjoint) {
+      __ mov(dst, rdx); // restore 'to'
+    }
+#endif
+    gen_write_ref_array_post_barrier(masm, decorators, dst, count, tmp);
+  }
+}
+
 void ShenandoahBarrierSetAssembler::shenandoah_write_barrier_pre(MacroAssembler* masm,
                                                                  Register obj,
                                                                  Register pre_val,
                                                                  Register thread,
                                                                  Register tmp,
@@ -553,10 +608,53 @@
 
     restore_machine_state(masm, /* handle_gpr = */ true, /* handle_fp = */ true);
   }
 }
 
+void ShenandoahBarrierSetAssembler::store_check(MacroAssembler* masm, Register obj) {
+  assert(ShenandoahCardBarrier, "Should have been checked by caller");
+
+  // Does a store check for the oop in register obj. The content of
+  // register obj is destroyed afterwards.
+
+  ShenandoahBarrierSet* ctbs = ShenandoahBarrierSet::barrier_set();
+  CardTable* ct = ctbs->card_table();
+
+  __ shrptr(obj, CardTable::card_shift());
+
+  Address card_addr;
+
+  // The calculation for byte_map_base is as follows:
+  // byte_map_base = _byte_map - (uintptr_t(low_bound) >> card_shift);
+  // So this essentially converts an address to a displacement and it will
+  // never need to be relocated. On 64-bit however the value may be too
+  // large for a 32-bit displacement.
+  intptr_t byte_map_base = (intptr_t)ct->byte_map_base();
+  if (__ is_simm32(byte_map_base)) {
+    card_addr = Address(noreg, obj, Address::times_1, byte_map_base);
+  } else {
+    // By doing it as an ExternalAddress 'byte_map_base' could be converted to a rip-relative
+    // displacement and done in a single instruction given favorable mapping and a
+    // smarter version of as_Address. However, 'ExternalAddress' generates a relocation
+    // entry and that entry is not properly handled by the relocation code.
+    AddressLiteral cardtable((address)byte_map_base, relocInfo::none);
+    Address index(noreg, obj, Address::times_1);
+    card_addr = __ as_Address(ArrayAddress(cardtable, index), rscratch1);
+  }
+
+  int dirty = CardTable::dirty_card_val();
+  if (UseCondCardMark) {
+    Label L_already_dirty;
+    __ cmpb(card_addr, dirty);
+    __ jccb(Assembler::equal, L_already_dirty);
+    __ movb(card_addr, dirty);
+    __ bind(L_already_dirty);
+  } else {
+    __ movb(card_addr, dirty);
+  }
+}
+
 void ShenandoahBarrierSetAssembler::store_at(MacroAssembler* masm, DecoratorSet decorators, BasicType type,
               Address dst, Register val, Register tmp1, Register tmp2, Register tmp3) {
 
   bool on_oop = is_reference_type(type);
   bool in_heap = (decorators & IN_HEAP) != 0;
@@ -590,11 +688,17 @@
                                    rthread /* thread */,
                                    tmp3  /* tmp */,
                                    val != noreg /* tosca_live */,
                                    false /* expand_call */);
     }
+
     BarrierSetAssembler::store_at(masm, decorators, type, Address(tmp1, 0), val, noreg, noreg, noreg);
+    if (val != noreg) {
+      if (ShenandoahCardBarrier) {
+        store_check(masm, tmp1);
+      }
+    }
     NOT_LP64(imasm->restore_bcp());
   } else {
     BarrierSetAssembler::store_at(masm, decorators, type, dst, val, tmp1, tmp2, tmp3);
   }
 }
@@ -785,10 +889,67 @@
     __ movptr(res, 1);
     __ bind(exit);
   }
 }
 
+#ifdef PRODUCT
+#define BLOCK_COMMENT(str) /* nothing */
+#else
+#define BLOCK_COMMENT(str) __ block_comment(str)
+#endif
+
+#define BIND(label) bind(label); BLOCK_COMMENT(#label ":")
+
+#define TIMES_OOP (UseCompressedOops ? Address::times_4 : Address::times_8)
+
+void ShenandoahBarrierSetAssembler::gen_write_ref_array_post_barrier(MacroAssembler* masm, DecoratorSet decorators,
+                                                                     Register addr, Register count,
+                                                                     Register tmp) {
+  assert(ShenandoahCardBarrier, "Should have been checked by caller");
+
+  ShenandoahBarrierSet* bs = ShenandoahBarrierSet::barrier_set();
+  CardTable* ct = bs->card_table();
+  intptr_t disp = (intptr_t) ct->byte_map_base();
+
+  Label L_loop, L_done;
+  const Register end = count;
+  assert_different_registers(addr, end);
+
+  // Zero count? Nothing to do.
+  __ testl(count, count);
+  __ jccb(Assembler::zero, L_done);
+
+#ifdef _LP64
+  __ leaq(end, Address(addr, count, TIMES_OOP, 0));  // end == addr+count*oop_size
+  __ subptr(end, BytesPerHeapOop); // end - 1 to make inclusive
+  __ shrptr(addr, CardTable::card_shift());
+  __ shrptr(end, CardTable::card_shift());
+  __ subptr(end, addr); // end --> cards count
+
+  __ mov64(tmp, disp);
+  __ addptr(addr, tmp);
+
+  __ BIND(L_loop);
+  __ movb(Address(addr, count, Address::times_1), 0);
+  __ decrement(count);
+  __ jccb(Assembler::greaterEqual, L_loop);
+#else
+  __ lea(end, Address(addr, count, Address::times_ptr, -wordSize));
+  __ shrptr(addr, CardTable::card_shift());
+  __ shrptr(end,  CardTable::card_shift());
+  __ subptr(end, addr); // end --> count
+
+  __ BIND(L_loop);
+  Address cardtable(addr, count, Address::times_1, disp);
+  __ movb(cardtable, 0);
+  __ decrement(count);
+  __ jccb(Assembler::greaterEqual, L_loop);
+#endif
+
+  __ BIND(L_done);
+}
+
 #undef __
 
 #ifdef COMPILER1
 
 #define __ ce->masm()->
