Add support for MOPS instructions

The MOPS extension to AArch64 adds support for instructions designed to
accelerate functions like memcpy and memset. Implement these in the assembler,
disassembler and simulator.
diff --git a/src/aarch64/assembler-aarch64.cc b/src/aarch64/assembler-aarch64.cc
index 93fa96d..d093271 100644
--- a/src/aarch64/assembler-aarch64.cc
+++ b/src/aarch64/assembler-aarch64.cc
@@ -2069,6 +2069,366 @@
   Emit(0xbac00000 | Rd(xd) | RnSP(xn) | RmSP(xm));
 }
 
+void Assembler::cpye(const Register& rd,
+                     const Register& rs,
+                     const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d800400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyen(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d80c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyern(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d808400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyewn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d804400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfe(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19800400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfen(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1980c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfern(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19808400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfewn(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19804400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfm(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19400400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfmn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1940c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfmrn(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19408400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfmwn(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19404400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfp(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19000400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfpn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1900c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfprn(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19008400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyfpwn(const Register& rd,
+                        const Register& rs,
+                        const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x19004400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpym(const Register& rd,
+                     const Register& rs,
+                     const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d400400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpymn(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d40c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpymrn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d408400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpymwn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d404400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyp(const Register& rd,
+                     const Register& rs,
+                     const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d000400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpypn(const Register& rd,
+                      const Register& rs,
+                      const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d00c400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpyprn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d008400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::cpypwn(const Register& rd,
+                       const Register& rs,
+                       const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero() && !rs.IsZero());
+
+  Emit(0x1d004400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::sete(const Register& rd,
+                     const Register& rn,
+                     const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c08400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::seten(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c0a400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setge(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc08400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setgen(const Register& rd,
+                       const Register& rn,
+                       const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc0a400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setgm(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc04400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setgmn(const Register& rd,
+                       const Register& rn,
+                       const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc06400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setgp(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc00400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setgpn(const Register& rd,
+                       const Register& rn,
+                       const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x1dc02400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setm(const Register& rd,
+                     const Register& rn,
+                     const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c04400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setmn(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c06400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setp(const Register& rd,
+                     const Register& rn,
+                     const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c00400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
+void Assembler::setpn(const Register& rd,
+                      const Register& rn,
+                      const Register& rs) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kMOPS));
+  VIXL_ASSERT(!AreAliased(rd, rn, rs));
+  VIXL_ASSERT(!rd.IsZero() && !rn.IsZero());
+
+  Emit(0x19c02400 | Rd(rd) | Rn(rn) | Rs(rs));
+}
+
 // NEON structure loads and stores.
 Instr Assembler::LoadStoreStructAddrModeField(const MemOperand& addr) {
   Instr addr_field = RnSP(addr.GetBaseRegister());
diff --git a/src/aarch64/assembler-aarch64.h b/src/aarch64/assembler-aarch64.h
index 1ba4c59..b3d408d 100644
--- a/src/aarch64/assembler-aarch64.h
+++ b/src/aarch64/assembler-aarch64.h
@@ -6942,6 +6942,114 @@
   // Compare with Tag.
   void cmpp(const Register& xn, const Register& xm) { subps(xzr, xn, xm); }
 
+  // Memory Copy.
+  void cpye(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads and writes non-temporal.
+  void cpyen(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads non-temporal.
+  void cpyern(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, writes non-temporal.
+  void cpyewn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only.
+  void cpyfe(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads and writes non-temporal.
+  void cpyfen(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads non-temporal.
+  void cpyfern(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, writes non-temporal.
+  void cpyfewn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only.
+  void cpyfm(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads and writes non-temporal.
+  void cpyfmn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads non-temporal.
+  void cpyfmrn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, writes non-temporal.
+  void cpyfmwn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only.
+  void cpyfp(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads and writes non-temporal.
+  void cpyfpn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, reads non-temporal.
+  void cpyfprn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy Forward-only, writes non-temporal.
+  void cpyfpwn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy.
+  void cpym(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads and writes non-temporal.
+  void cpymn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads non-temporal.
+  void cpymrn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, writes non-temporal.
+  void cpymwn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy.
+  void cpyp(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads and writes non-temporal.
+  void cpypn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, reads non-temporal.
+  void cpyprn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Copy, writes non-temporal.
+  void cpypwn(const Register& rd, const Register& rs, const Register& rn);
+
+  // Memory Set.
+  void sete(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set, non-temporal.
+  void seten(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting.
+  void setge(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting, non-temporal.
+  void setgen(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting.
+  void setgm(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting, non-temporal.
+  void setgmn(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting.
+  void setgp(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set with tag setting, non-temporal.
+  void setgpn(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set.
+  void setm(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set, non-temporal.
+  void setmn(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set.
+  void setp(const Register& rd, const Register& rn, const Register& rs);
+
+  // Memory Set, non-temporal.
+  void setpn(const Register& rd, const Register& rn, const Register& rs);
+
   // Emit generic instructions.
 
   // Emit raw instructions into the instruction stream.
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
index 692ca97..1adb37c 100644
--- a/src/aarch64/cpu-features-auditor-aarch64.cc
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -1758,6 +1758,48 @@
         {"subg_64_addsub_immtags"_h, CPUFeatures::kMTE},
         {"subps_64s_dp_2src"_h, CPUFeatures::kMTE},
         {"subp_64s_dp_2src"_h, CPUFeatures::kMTE},
+        {"cpyen_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyern_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyewn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpye_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfen_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfern_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfewn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfe_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfmn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfmrn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfmwn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfm_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfpn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfprn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfpwn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyfp_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpymn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpymrn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpymwn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpym_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpypn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyprn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpypwn_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"cpyp_cpy_memcms"_h, CPUFeatures::kMOPS},
+        {"seten_set_memcms"_h, CPUFeatures::kMOPS},
+        {"sete_set_memcms"_h, CPUFeatures::kMOPS},
+        {"setgen_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setge_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setgmn_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setgm_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setgpn_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setgp_set_memcms"_h,
+         CPUFeatures(CPUFeatures::kMOPS, CPUFeatures::kMTE)},
+        {"setmn_set_memcms"_h, CPUFeatures::kMOPS},
+        {"setm_set_memcms"_h, CPUFeatures::kMOPS},
+        {"setpn_set_memcms"_h, CPUFeatures::kMOPS},
+        {"setp_set_memcms"_h, CPUFeatures::kMOPS},
     };
 
     if (features.count(form_hash) > 0) {
diff --git a/src/aarch64/decoder-aarch64.cc b/src/aarch64/decoder-aarch64.cc
index a4e2989..c7825a0 100644
--- a/src/aarch64/decoder-aarch64.cc
+++ b/src/aarch64/decoder-aarch64.cc
@@ -183,6 +183,7 @@
     INSTANTIATE_TEMPLATE_M(00000010);
     INSTANTIATE_TEMPLATE_M(0000001f);
     INSTANTIATE_TEMPLATE_M(00000060);
+    INSTANTIATE_TEMPLATE_M(000000df);
     INSTANTIATE_TEMPLATE_M(00000100);
     INSTANTIATE_TEMPLATE_M(00000200);
     INSTANTIATE_TEMPLATE_M(00000400);
diff --git a/src/aarch64/decoder-constants-aarch64.h b/src/aarch64/decoder-constants-aarch64.h
index ddfdff6..81071ab 100644
--- a/src/aarch64/decoder-constants-aarch64.h
+++ b/src/aarch64/decoder-constants-aarch64.h
@@ -60,13 +60,6 @@
     },
   },
 
-  { "_ggvztl",
-    {30},
-    { {"0"_b, "bl_only_branch_imm"},
-      {"1"_b, "_qpzynz"},
-    },
-  },
-
   { "_ghmzhr",
     {20, 19, 18, 17, 16, 13, 12},
     { {"0000000"_b, "rbit_32_dp_1src"},
@@ -194,16 +187,6 @@
     },
   },
 
-  { "_gkxgsn",
-    {30, 23, 22, 11, 10},
-    { {"00000"_b, "stlur_32_ldapstl_unscaled"},
-      {"00100"_b, "ldapur_32_ldapstl_unscaled"},
-      {"01000"_b, "ldapursw_64_ldapstl_unscaled"},
-      {"10000"_b, "stlur_64_ldapstl_unscaled"},
-      {"10100"_b, "ldapur_64_ldapstl_unscaled"},
-    },
-  },
-
   { "_glgrjy",
     {23, 22, 20, 19, 18, 17, 16},
     { {"0000000"_b, "not_asimdmisc_r"},
@@ -503,6 +486,32 @@
     },
   },
 
+  { "_gxqjty",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlur_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapur_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursw_64_ldapstl_unscaled"},
+      {"100xx00"_b, "stlur_64_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapur_64_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfp_cpy_memcms"},
+      {"x000101"_b, "cpyfpwt_cpy_memcms"},
+      {"x001001"_b, "cpyfprt_cpy_memcms"},
+      {"x001101"_b, "cpyfpt_cpy_memcms"},
+      {"x010001"_b, "cpyfm_cpy_memcms"},
+      {"x010101"_b, "cpyfmwt_cpy_memcms"},
+      {"x011001"_b, "cpyfmrt_cpy_memcms"},
+      {"x011101"_b, "cpyfmt_cpy_memcms"},
+      {"x100001"_b, "cpyfe_cpy_memcms"},
+      {"x100101"_b, "cpyfewt_cpy_memcms"},
+      {"x101001"_b, "cpyfert_cpy_memcms"},
+      {"x101101"_b, "cpyfet_cpy_memcms"},
+      {"x110001"_b, "setp_set_memcms"},
+      {"x110101"_b, "setpt_set_memcms"},
+      {"x111001"_b, "setpn_set_memcms"},
+      {"x111101"_b, "setptn_set_memcms"},
+    },
+  },
+
   { "_gxslgq",
     {23, 22, 20, 19, 17, 16},
     { {"000010"_b, "scvtf_s32_float2fix"},
@@ -584,6 +593,23 @@
     },
   },
 
+  { "_gzysny",
+    {23, 22, 13, 12, 11, 10},
+    { {"000001"_b, "cpypn_cpy_memcms"},
+      {"000101"_b, "cpypwtn_cpy_memcms"},
+      {"001001"_b, "cpyprtn_cpy_memcms"},
+      {"001101"_b, "cpyptn_cpy_memcms"},
+      {"010001"_b, "cpymn_cpy_memcms"},
+      {"010101"_b, "cpymwtn_cpy_memcms"},
+      {"011001"_b, "cpymrtn_cpy_memcms"},
+      {"011101"_b, "cpymtn_cpy_memcms"},
+      {"100001"_b, "cpyen_cpy_memcms"},
+      {"100101"_b, "cpyewtn_cpy_memcms"},
+      {"101001"_b, "cpyertn_cpy_memcms"},
+      {"101101"_b, "cpyetn_cpy_memcms"},
+    },
+  },
+
   { "_hggmnk",
     {13, 12},
     { {"10"_b, "lslv_32_dp_2src"},
@@ -1162,13 +1188,6 @@
     },
   },
 
-  { "_jkpsxk",
-    {20},
-    { {"0"_b, "_kyygzs"},
-      {"1"_b, "msr_sr_systemmove"},
-    },
-  },
-
   { "_jkqktg",
     {20, 19, 18, 17, 16},
     { {"00000"_b, "sqneg_asimdmisc_r"},
@@ -1513,13 +1532,6 @@
     },
   },
 
-  { "_jyxszq",
-    {30, 4},
-    { {"0x"_b, "b_only_branch_imm"},
-      {"10"_b, "b_only_condbranch"},
-    },
-  },
-
   { "_jzjvtv",
     {19, 18, 17, 16, 4},
     { {"00000"_b, "brkbs_p_p_p_z"},
@@ -1729,6 +1741,31 @@
     },
   },
 
+  { "_kmljzt",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlurb_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapurb_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursb_64_ldapstl_unscaled"},
+      {"011xx00"_b, "ldapursb_32_ldapstl_unscaled"},
+      {"100xx00"_b, "stlurh_32_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapurh_32_ldapstl_unscaled"},
+      {"110xx00"_b, "ldapursh_64_ldapstl_unscaled"},
+      {"111xx00"_b, "ldapursh_32_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfpn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtn_cpy_memcms"},
+      {"x001101"_b, "cpyfptn_cpy_memcms"},
+      {"x010001"_b, "cpyfmn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtn_cpy_memcms"},
+      {"x100001"_b, "cpyfen_cpy_memcms"},
+      {"x100101"_b, "cpyfewtn_cpy_memcms"},
+      {"x101001"_b, "cpyfertn_cpy_memcms"},
+      {"x101101"_b, "cpyfetn_cpy_memcms"},
+    },
+  },
+
   { "_knkjnz",
     {30, 23, 22, 20, 13},
     { {"00001"_b, "ld1sh_z_p_bi_s32"},
@@ -1852,6 +1889,28 @@
     },
   },
 
+  { "_krgxtm",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlur_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapur_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursw_64_ldapstl_unscaled"},
+      {"100xx00"_b, "stlur_64_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapur_64_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfpn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtn_cpy_memcms"},
+      {"x001101"_b, "cpyfptn_cpy_memcms"},
+      {"x010001"_b, "cpyfmn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtn_cpy_memcms"},
+      {"x100001"_b, "cpyfen_cpy_memcms"},
+      {"x100101"_b, "cpyfewtn_cpy_memcms"},
+      {"x101001"_b, "cpyfertn_cpy_memcms"},
+      {"x101101"_b, "cpyfetn_cpy_memcms"},
+    },
+  },
+
   { "_krhrrr",
     {12, 10},
     { {"00"_b, "_xyzpvp"},
@@ -2051,13 +2110,6 @@
     },
   },
 
-  { "_kyygzs",
-    {19},
-    { {"0"_b, "_nnkyzr"},
-      {"1"_b, "sys_cr_systeminstrs"},
-    },
-  },
-
   { "_kyyzks",
     {13, 12},
     { {"00"_b, "sdiv_32_dp_2src"},
@@ -2367,6 +2419,27 @@
     },
   },
 
+  { "_lqzrkj",
+    {23, 22, 13, 12, 11, 10},
+    { {"000001"_b, "cpyprn_cpy_memcms"},
+      {"000101"_b, "cpypwtrn_cpy_memcms"},
+      {"001001"_b, "cpyprtrn_cpy_memcms"},
+      {"001101"_b, "cpyptrn_cpy_memcms"},
+      {"010001"_b, "cpymrn_cpy_memcms"},
+      {"010101"_b, "cpymwtrn_cpy_memcms"},
+      {"011001"_b, "cpymrtrn_cpy_memcms"},
+      {"011101"_b, "cpymtrn_cpy_memcms"},
+      {"100001"_b, "cpyern_cpy_memcms"},
+      {"100101"_b, "cpyewtrn_cpy_memcms"},
+      {"101001"_b, "cpyertrn_cpy_memcms"},
+      {"101101"_b, "cpyetrn_cpy_memcms"},
+      {"110001"_b, "setge_set_memcms"},
+      {"110101"_b, "setget_set_memcms"},
+      {"111001"_b, "setgen_set_memcms"},
+      {"111101"_b, "setgetn_set_memcms"},
+    },
+  },
+
   { "_lrjyhr",
     {23, 22, 20, 19, 13, 11},
     { {"0000x0"_b, "bic_asimdimm_l_hl"},
@@ -3088,6 +3161,35 @@
     },
   },
 
+  { "_mxgnhq",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlurb_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapurb_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursb_64_ldapstl_unscaled"},
+      {"011xx00"_b, "ldapursb_32_ldapstl_unscaled"},
+      {"100xx00"_b, "stlurh_32_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapurh_32_ldapstl_unscaled"},
+      {"110xx00"_b, "ldapursh_64_ldapstl_unscaled"},
+      {"111xx00"_b, "ldapursh_32_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfpwn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtwn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtwn_cpy_memcms"},
+      {"x001101"_b, "cpyfptwn_cpy_memcms"},
+      {"x010001"_b, "cpyfmwn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtwn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtwn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtwn_cpy_memcms"},
+      {"x100001"_b, "cpyfewn_cpy_memcms"},
+      {"x100101"_b, "cpyfewtwn_cpy_memcms"},
+      {"x101001"_b, "cpyfertwn_cpy_memcms"},
+      {"x101101"_b, "cpyfetwn_cpy_memcms"},
+      {"x110001"_b, "setm_set_memcms"},
+      {"x110101"_b, "setmt_set_memcms"},
+      {"x111001"_b, "setmn_set_memcms"},
+      {"x111101"_b, "setmtn_set_memcms"},
+    },
+  },
+
   { "_mxgykv",
     {19, 18, 17, 16},
     { {"0000"_b, "cntp_r_p_p"},
@@ -3179,6 +3281,12 @@
     },
   },
 
+  { "_mzkzll",
+    {18, 17, 16},
+    { {"011"_b, "_xjzgvk"},
+    },
+  },
+
   { "_mzqzhq",
     {23, 22, 20, 19, 11},
     { {"00000"_b, "mvni_asimdimm_m_sm"},
@@ -3368,6 +3476,14 @@
     },
   },
 
+  { "_nlhghl",
+    {7, 6, 4, 3, 2, 1, 0},
+    { {"0111111"_b, "clrex_bn_barriers"},
+      {"1011111"_b, "dsb_bo_barriers"},
+      {"1111111"_b, "isb_bi_barriers"},
+    },
+  },
+
   { "_nlkkyx",
     {23, 13, 12},
     { {"001"_b, "fmulx_asisdsame_only"},
@@ -3482,12 +3598,6 @@
     },
   },
 
-  { "_nnkyzr",
-    {18, 17, 16},
-    { {"011"_b, "_yvgqjx"},
-    },
-  },
-
   { "_nnllqy",
     {18, 17},
     { {"00"_b, "ld1_asisdlso_s1_1s"},
@@ -3507,6 +3617,14 @@
     },
   },
 
+  { "_nppmnk",
+    {30, 4},
+    { {"0x"_b, "b_only_branch_imm"},
+      {"10"_b, "b_only_condbranch"},
+      {"11"_b, "bc_only_condbranch"},
+    },
+  },
+
   { "_nqgqjh",
     {30, 23, 22, 20, 19},
     { {"0xxxx"_b, "bl_only_branch_imm"},
@@ -3547,6 +3665,13 @@
     },
   },
 
+  { "_nrmvkk",
+    {30},
+    { {"0"_b, "bl_only_branch_imm"},
+      {"1"_b, "_xzytrm"},
+    },
+  },
+
   { "_nrrmtx",
     {22, 13, 12},
     { {"000"_b, "swpa_64_memop"},
@@ -4027,6 +4152,32 @@
     },
   },
 
+  { "_prztqv",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlur_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapur_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursw_64_ldapstl_unscaled"},
+      {"100xx00"_b, "stlur_64_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapur_64_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfpwn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtwn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtwn_cpy_memcms"},
+      {"x001101"_b, "cpyfptwn_cpy_memcms"},
+      {"x010001"_b, "cpyfmwn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtwn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtwn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtwn_cpy_memcms"},
+      {"x100001"_b, "cpyfewn_cpy_memcms"},
+      {"x100101"_b, "cpyfewtwn_cpy_memcms"},
+      {"x101001"_b, "cpyfertwn_cpy_memcms"},
+      {"x101101"_b, "cpyfetwn_cpy_memcms"},
+      {"x110001"_b, "setm_set_memcms"},
+      {"x110101"_b, "setmt_set_memcms"},
+      {"x111001"_b, "setmn_set_memcms"},
+      {"x111101"_b, "setmtn_set_memcms"},
+    },
+  },
+
   { "_pslllp",
     {30, 23, 22, 20, 19, 12, 11},
     { {"0000000"_b, "movi_asimdimm_d_ds"},
@@ -4520,12 +4671,6 @@
     },
   },
 
-  { "_qpzynz",
-    {23, 22},
-    { {"00"_b, "_jkpsxk"},
-    },
-  },
-
   { "_qqpkkm",
     {9, 8, 7, 6, 5, 1, 0},
     { {"1111111"_b, "eretaa_64e_branch_reg"},
@@ -5055,6 +5200,27 @@
     },
   },
 
+  { "_rmxslj",
+    {23, 22, 13, 12, 11, 10},
+    { {"000001"_b, "cpyp_cpy_memcms"},
+      {"000101"_b, "cpypwt_cpy_memcms"},
+      {"001001"_b, "cpyprt_cpy_memcms"},
+      {"001101"_b, "cpypt_cpy_memcms"},
+      {"010001"_b, "cpym_cpy_memcms"},
+      {"010101"_b, "cpymwt_cpy_memcms"},
+      {"011001"_b, "cpymrt_cpy_memcms"},
+      {"011101"_b, "cpymt_cpy_memcms"},
+      {"100001"_b, "cpye_cpy_memcms"},
+      {"100101"_b, "cpyewt_cpy_memcms"},
+      {"101001"_b, "cpyert_cpy_memcms"},
+      {"101101"_b, "cpyet_cpy_memcms"},
+      {"110001"_b, "setgp_set_memcms"},
+      {"110101"_b, "setgpt_set_memcms"},
+      {"111001"_b, "setgpn_set_memcms"},
+      {"111101"_b, "setgptn_set_memcms"},
+    },
+  },
+
   { "_rnktts",
     {23, 22},
     { {"00"_b, "and_asimdsame_only"},
@@ -5124,6 +5290,13 @@
     },
   },
 
+  { "_rsrhnm",
+    {20},
+    { {"0"_b, "_vltzhv"},
+      {"1"_b, "msr_sr_systemmove"},
+    },
+  },
+
   { "_rsyhtj",
     {13, 12, 11, 10},
     { {"0001"_b, "ushl_asisdsame_only"},
@@ -5560,12 +5733,6 @@
     },
   },
 
-  { "_sphpkr",
-    {4, 3, 2, 1, 0},
-    { {"11111"_b, "_thsxvg"},
-    },
-  },
-
   { "_spjjkg",
     {23, 22, 13, 12, 11, 10},
     { {"0011x0"_b, "sudot_asimdelem_d"},
@@ -5590,6 +5757,35 @@
     },
   },
 
+  { "_spmsnv",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlurb_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapurb_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursb_64_ldapstl_unscaled"},
+      {"011xx00"_b, "ldapursb_32_ldapstl_unscaled"},
+      {"100xx00"_b, "stlurh_32_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapurh_32_ldapstl_unscaled"},
+      {"110xx00"_b, "ldapursh_64_ldapstl_unscaled"},
+      {"111xx00"_b, "ldapursh_32_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfprn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtrn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtrn_cpy_memcms"},
+      {"x001101"_b, "cpyfptrn_cpy_memcms"},
+      {"x010001"_b, "cpyfmrn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtrn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtrn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtrn_cpy_memcms"},
+      {"x100001"_b, "cpyfern_cpy_memcms"},
+      {"x100101"_b, "cpyfewtrn_cpy_memcms"},
+      {"x101001"_b, "cpyfertrn_cpy_memcms"},
+      {"x101101"_b, "cpyfetrn_cpy_memcms"},
+      {"x110001"_b, "sete_set_memcms"},
+      {"x110101"_b, "setet_set_memcms"},
+      {"x111001"_b, "seten_set_memcms"},
+      {"x111101"_b, "setetn_set_memcms"},
+    },
+  },
+
   { "_spzgkt",
     {23, 22, 13, 12, 11, 10},
     { {"0x1001"_b, "ucvtf_asimdshf_c"},
@@ -5800,18 +5996,6 @@
     },
   },
 
-  { "_thsxvg",
-    {11, 10, 9, 8, 7, 6},
-    { {"000010"_b, "ssbb_only_barriers"},
-      {"010010"_b, "pssbb_only_barriers"},
-      {"0x1010"_b, "dsb_bo_barriers"},
-      {"0xx110"_b, "dsb_bo_barriers"},
-      {"1xxx10"_b, "dsb_bo_barriers"},
-      {"xxxx01"_b, "clrex_bn_barriers"},
-      {"xxxx11"_b, "isb_bi_barriers"},
-    },
-  },
-
   { "_thvvzp",
     {18, 17, 12},
     { {"0x0"_b, "st1_asisdlsop_dx1_r1d"},
@@ -5909,6 +6093,27 @@
     },
   },
 
+  { "_tlyzkg",
+    {23, 22, 13, 12, 11, 10},
+    { {"000001"_b, "cpypwn_cpy_memcms"},
+      {"000101"_b, "cpypwtwn_cpy_memcms"},
+      {"001001"_b, "cpyprtwn_cpy_memcms"},
+      {"001101"_b, "cpyptwn_cpy_memcms"},
+      {"010001"_b, "cpymwn_cpy_memcms"},
+      {"010101"_b, "cpymwtwn_cpy_memcms"},
+      {"011001"_b, "cpymrtwn_cpy_memcms"},
+      {"011101"_b, "cpymtwn_cpy_memcms"},
+      {"100001"_b, "cpyewn_cpy_memcms"},
+      {"100101"_b, "cpyewtwn_cpy_memcms"},
+      {"101001"_b, "cpyertwn_cpy_memcms"},
+      {"101101"_b, "cpyetwn_cpy_memcms"},
+      {"110001"_b, "setgm_set_memcms"},
+      {"110101"_b, "setgmt_set_memcms"},
+      {"111001"_b, "setgmn_set_memcms"},
+      {"111101"_b, "setgmtn_set_memcms"},
+    },
+  },
+
   { "_tlzlrj",
     {17},
     { {"0"_b, "st2_asisdlso_b2_2b"},
@@ -6400,6 +6605,13 @@
     },
   },
 
+  { "_vltzhv",
+    {19},
+    { {"0"_b, "_mzkzll"},
+      {"1"_b, "sys_cr_systeminstrs"},
+    },
+  },
+
   { "_vlzrlm",
     {23, 22, 20, 19, 13, 11},
     { {"0000x0"_b, "mvni_asimdimm_l_sl"},
@@ -6896,6 +7108,32 @@
     },
   },
 
+  { "_xhzmls",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlur_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapur_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursw_64_ldapstl_unscaled"},
+      {"100xx00"_b, "stlur_64_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapur_64_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfprn_cpy_memcms"},
+      {"x000101"_b, "cpyfpwtrn_cpy_memcms"},
+      {"x001001"_b, "cpyfprtrn_cpy_memcms"},
+      {"x001101"_b, "cpyfptrn_cpy_memcms"},
+      {"x010001"_b, "cpyfmrn_cpy_memcms"},
+      {"x010101"_b, "cpyfmwtrn_cpy_memcms"},
+      {"x011001"_b, "cpyfmrtrn_cpy_memcms"},
+      {"x011101"_b, "cpyfmtrn_cpy_memcms"},
+      {"x100001"_b, "cpyfern_cpy_memcms"},
+      {"x100101"_b, "cpyfewtrn_cpy_memcms"},
+      {"x101001"_b, "cpyfertrn_cpy_memcms"},
+      {"x101101"_b, "cpyfetrn_cpy_memcms"},
+      {"x110001"_b, "sete_set_memcms"},
+      {"x110101"_b, "setet_set_memcms"},
+      {"x111001"_b, "seten_set_memcms"},
+      {"x111101"_b, "setetn_set_memcms"},
+    },
+  },
+
   { "_xjghst",
     {13, 12, 11, 10},
     { {"0000"_b, "_kvmrng"},
@@ -6922,6 +7160,17 @@
     },
   },
 
+  { "_xjzgvk",
+    {13, 12, 5},
+    { {"010"_b, "_tnzytv"},
+      {"011"_b, "_vmpnlv"},
+      {"100"_b, "_hhhqjk"},
+      {"101"_b, "_tkzqqp"},
+      {"110"_b, "_nlhghl"},
+      {"111"_b, "_spglxn"},
+    },
+  },
+
   { "_xkkggt",
     {17},
     { {"0"_b, "st4_asisdlsop_bx4_r4b"},
@@ -6957,6 +7206,35 @@
     },
   },
 
+  { "_xmtqph",
+    {30, 23, 22, 13, 12, 11, 10},
+    { {"000xx00"_b, "stlurb_32_ldapstl_unscaled"},
+      {"001xx00"_b, "ldapurb_32_ldapstl_unscaled"},
+      {"010xx00"_b, "ldapursb_64_ldapstl_unscaled"},
+      {"011xx00"_b, "ldapursb_32_ldapstl_unscaled"},
+      {"100xx00"_b, "stlurh_32_ldapstl_unscaled"},
+      {"101xx00"_b, "ldapurh_32_ldapstl_unscaled"},
+      {"110xx00"_b, "ldapursh_64_ldapstl_unscaled"},
+      {"111xx00"_b, "ldapursh_32_ldapstl_unscaled"},
+      {"x000001"_b, "cpyfp_cpy_memcms"},
+      {"x000101"_b, "cpyfpwt_cpy_memcms"},
+      {"x001001"_b, "cpyfprt_cpy_memcms"},
+      {"x001101"_b, "cpyfpt_cpy_memcms"},
+      {"x010001"_b, "cpyfm_cpy_memcms"},
+      {"x010101"_b, "cpyfmwt_cpy_memcms"},
+      {"x011001"_b, "cpyfmrt_cpy_memcms"},
+      {"x011101"_b, "cpyfmt_cpy_memcms"},
+      {"x100001"_b, "cpyfe_cpy_memcms"},
+      {"x100101"_b, "cpyfewt_cpy_memcms"},
+      {"x101001"_b, "cpyfert_cpy_memcms"},
+      {"x101101"_b, "cpyfet_cpy_memcms"},
+      {"x110001"_b, "setp_set_memcms"},
+      {"x110101"_b, "setpt_set_memcms"},
+      {"x111001"_b, "setpn_set_memcms"},
+      {"x111101"_b, "setptn_set_memcms"},
+    },
+  },
+
   { "_xmxpnx",
     {10},
     { {"0"_b, "sri_z_zzi"},
@@ -7299,6 +7577,12 @@
     },
   },
 
+  { "_xzytrm",
+    {23, 22},
+    { {"00"_b, "_rsrhnm"},
+    },
+  },
+
   { "_xzyxnr",
     {30, 23, 22, 11, 10},
     { {"10001"_b, "stg_64spost_ldsttags"},
@@ -7426,19 +7710,6 @@
     },
   },
 
-  { "_yjxshz",
-    {30, 23, 22, 11, 10},
-    { {"00000"_b, "stlurb_32_ldapstl_unscaled"},
-      {"00100"_b, "ldapurb_32_ldapstl_unscaled"},
-      {"01000"_b, "ldapursb_64_ldapstl_unscaled"},
-      {"01100"_b, "ldapursb_32_ldapstl_unscaled"},
-      {"10000"_b, "stlurh_32_ldapstl_unscaled"},
-      {"10100"_b, "ldapurh_32_ldapstl_unscaled"},
-      {"11000"_b, "ldapursh_64_ldapstl_unscaled"},
-      {"11100"_b, "ldapursh_32_ldapstl_unscaled"},
-    },
-  },
-
   { "_yjxvkp",
     {18, 17, 12},
     { {"0x0"_b, "st4_asisdlsop_dx4_r4d"},
@@ -7813,17 +8084,6 @@
     },
   },
 
-  { "_yvgqjx",
-    {13, 12, 5},
-    { {"010"_b, "_tnzytv"},
-      {"011"_b, "_vmpnlv"},
-      {"100"_b, "_hhhqjk"},
-      {"101"_b, "_tkzqqp"},
-      {"110"_b, "_sphpkr"},
-      {"111"_b, "_spglxn"},
-    },
-  },
-
   { "_yvhnlk",
     {30, 23, 22, 13, 12, 11, 10},
     { {"0001111"_b, "casp_cp32_ldstexcl"},
@@ -8624,11 +8884,14 @@
       {"001001100x"_b, "_zjslnr"},
       {"001001110x"_b, "_jpxgqh"},
       {"0010011x1x"_b, "_gkhhjm"},
-      {"0010100xxx"_b, "_jyxszq"},
+      {"0010100xxx"_b, "_nppmnk"},
       {"0010110xxx"_b, "_xqhgkk"},
       {"00101x1xxx"_b, "_zkqtrj"},
       {"0011000xxx"_b, "_qkyjhg"},
-      {"00110010xx"_b, "_yjxshz"},
+      {"0011001000"_b, "_xmtqph"},
+      {"0011001001"_b, "_mxgnhq"},
+      {"0011001010"_b, "_spmsnv"},
+      {"0011001011"_b, "_kmljzt"},
       {"0011010000"_b, "_zzrqlh"},
       {"0011010001"_b, "_qsrlql"},
       {"001101001x"_b, "_tnrrjk"},
@@ -8762,7 +9025,7 @@
       {"10100111xx"_b, "_rgjqzs"},
       {"10101000xx"_b, "_qmrgkn"},
       {"10101001xx"_b, "_jkxlnq"},
-      {"1010101000"_b, "_ggvztl"},
+      {"1010101000"_b, "_nrmvkk"},
       {"1010101001"_b, "_xlhjhx"},
       {"101010101x"_b, "_nqgqjh"},
       {"1010101100"_b, "_qsrtzz"},
@@ -8775,7 +9038,10 @@
       {"101011xx10"_b, "_hsjynv"},
       {"101011xxx1"_b, "_kmhtqp"},
       {"1011000xxx"_b, "_ylhxlt"},
-      {"10110010xx"_b, "_gkxgsn"},
+      {"1011001000"_b, "_gxqjty"},
+      {"1011001001"_b, "_prztqv"},
+      {"1011001010"_b, "_xhzmls"},
+      {"1011001011"_b, "_krgxtm"},
       {"1011001100"_b, "_xzmjxk"},
       {"1011001110"_b, "_ppqkym"},
       {"10110011x1"_b, "_xzyxnr"},
@@ -8831,10 +9097,15 @@
       {"11111001xx"_b, "_xprlgy"},
       {"1111101xxx"_b, "_hjgylh"},
       {"1x10000xxx"_b, "adrp_only_pcreladdr"},
+      {"x011101000"_b, "_rmxslj"},
+      {"x011101001"_b, "_tlyzkg"},
+      {"x011101010"_b, "_lqzrkj"},
+      {"x011101011"_b, "_gzysny"},
       {"x110110xxx"_b, "_zytrsq"},
       {"x110111xxx"_b, "_kxsysq"},
     },
   },
+
 };
 // clang-format on
 
diff --git a/src/aarch64/decoder-visitor-map-aarch64.h b/src/aarch64/decoder-visitor-map-aarch64.h
index e242964..b202247 100644
--- a/src/aarch64/decoder-visitor-map-aarch64.h
+++ b/src/aarch64/decoder-visitor-map-aarch64.h
@@ -2720,7 +2720,6 @@
       {"pacib1716_hi_hints"_h, &VISITORCLASS::VisitSystem},                    \
       {"pacibsp_hi_hints"_h, &VISITORCLASS::VisitSystem},                      \
       {"pacibz_hi_hints"_h, &VISITORCLASS::VisitSystem},                       \
-      {"pssbb_only_barriers"_h, &VISITORCLASS::VisitSystem},                   \
       {"sev_hi_hints"_h, &VISITORCLASS::VisitSystem},                          \
       {"sevl_hi_hints"_h, &VISITORCLASS::VisitSystem},                         \
       {"ssbb_only_barriers"_h, &VISITORCLASS::VisitSystem},                    \
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index 3b3f7a7..5b9dc10 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -47,7 +47,6 @@
       {"csdb_hi_hints"_h, &Disassembler::DisassembleNoArgs},
       {"dgh_hi_hints"_h, &Disassembler::DisassembleNoArgs},
       {"ssbb_only_barriers"_h, &Disassembler::DisassembleNoArgs},
-      {"pssbb_only_barriers"_h, &Disassembler::DisassembleNoArgs},
       {"esb_hi_hints"_h, &Disassembler::DisassembleNoArgs},
       {"isb_bi_barriers"_h, &Disassembler::DisassembleNoArgs},
       {"nop_hi_hints"_h, &Disassembler::DisassembleNoArgs},
@@ -695,6 +694,42 @@
        &Disassembler::Disassemble_XdSP_XnSP_uimm6_uimm4},
       {"subps_64s_dp_2src"_h, &Disassembler::Disassemble_Xd_XnSP_XmSP},
       {"subp_64s_dp_2src"_h, &Disassembler::Disassemble_Xd_XnSP_XmSP},
+      {"cpyen_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyern_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyewn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpye_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfen_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfern_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfewn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfe_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfmn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfmrn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfmwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfm_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfpn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfprn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfpwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyfp_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpymn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpymrn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpymwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpym_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpypn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyprn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpypwn_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"cpyp_cpy_memcms"_h, &Disassembler::DisassembleCpy},
+      {"seten_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"sete_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setgen_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setge_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setgmn_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setgm_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setgpn_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setgp_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setmn_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setm_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setpn_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"setp_set_memcms"_h, &Disassembler::DisassembleSet},
   };
   return &form_to_visitor;
 }  // NOLINT(readability/fn_size)
@@ -1983,11 +2018,23 @@
     case "hint_hm_hints"_h:
       form = "'IH";
       break;
-    case "dmb_bo_barriers"_h:
-    case "dsb_bo_barriers"_h:
+    case Hash("dmb_bo_barriers"):
       form = "'M";
       break;
-    case "sys_cr_systeminstrs"_h: {
+    case Hash("dsb_bo_barriers"): {
+      int crm = instr->GetCRm();
+      if (crm == 0) {
+        mnemonic = "ssbb";
+        form = "";
+      } else if (crm == 4) {
+        mnemonic = "pssbb";
+        form = "";
+      } else {
+        form = "'M";
+      }
+      break;
+    }
+    case Hash("sys_cr_systeminstrs"): {
       mnemonic = "dc";
       suffix = ", 'Xt";
 
@@ -5983,6 +6030,46 @@
   Format(instr, mnemonic_.c_str(), form);
 }
 
+void Disassembler::DisassembleCpy(const Instruction *instr) {
+  const char *form = "['Xd]!, ['Xs]!, 'Xn!";
+
+  int d = instr->GetRd();
+  int n = instr->GetRn();
+  int s = instr->GetRs();
+
+  // Aliased registers and sp/zr are disallowed.
+  if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31) || (s == 31)) {
+    form = NULL;
+  }
+
+  // Bits 31 and 30 must be zero.
+  if (instr->ExtractBits(31, 30)) {
+    form = NULL;
+  }
+
+  Format(instr, mnemonic_.c_str(), form);
+}
+
+void Disassembler::DisassembleSet(const Instruction *instr) {
+  const char *form = "['Xd]!, 'Xn!, 'Xs";
+
+  int d = instr->GetRd();
+  int n = instr->GetRn();
+  int s = instr->GetRs();
+
+  // Aliased registers are disallowed. Only Xs may be xzr.
+  if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31)) {
+    form = NULL;
+  }
+
+  // Bits 31 and 30 must be zero.
+  if (instr->ExtractBits(31, 30)) {
+    form = NULL;
+  }
+
+  Format(instr, mnemonic_.c_str(), form);
+}
+
 void Disassembler::ProcessOutput(const Instruction * /*instr*/) {
   // The base disasm does nothing more than disassembling into a buffer.
 }
diff --git a/src/aarch64/disasm-aarch64.h b/src/aarch64/disasm-aarch64.h
index 3af4f11..3345174 100644
--- a/src/aarch64/disasm-aarch64.h
+++ b/src/aarch64/disasm-aarch64.h
@@ -184,6 +184,9 @@
   void Disassemble_ZdaS_ZnB_ZmB(const Instruction* instr);
   void Disassemble_Vd4S_Vn16B_Vm16B(const Instruction* instr);
 
+  void DisassembleCpy(const Instruction* instr);
+  void DisassembleSet(const Instruction* instr);
+
   void DisassembleSVEShiftLeftImm(const Instruction* instr);
   void DisassembleSVEShiftRightImm(const Instruction* instr);
   void DisassembleSVEAddSubCarry(const Instruction* instr);
diff --git a/src/aarch64/instructions-aarch64.h b/src/aarch64/instructions-aarch64.h
index 542e96e..ec84e26 100644
--- a/src/aarch64/instructions-aarch64.h
+++ b/src/aarch64/instructions-aarch64.h
@@ -516,6 +516,65 @@
     return false;
   }
 
+  bool IsMOPSPrologueOf(const Instruction* instr, uint32_t mops_type) const {
+    VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
+                (mops_type == "cpy"_h));
+    const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
+    return GetInstructionBits() == instr->Mask(~(0x3U << op_lsb));
+  }
+
+  bool IsMOPSMainOf(const Instruction* instr, uint32_t mops_type) const {
+    VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
+                (mops_type == "cpy"_h));
+    const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
+    return GetInstructionBits() ==
+           (instr->Mask(~(0x3U << op_lsb)) | (0x1 << op_lsb));
+  }
+
+  bool IsMOPSEpilogueOf(const Instruction* instr, uint32_t mops_type) const {
+    VIXL_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
+                (mops_type == "cpy"_h));
+    const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
+    return GetInstructionBits() ==
+           (instr->Mask(~(0x3U << op_lsb)) | (0x2 << op_lsb));
+  }
+
+  template <uint32_t mops_type>
+  bool IsConsistentMOPSTriplet() const {
+    VIXL_STATIC_ASSERT((mops_type == "set"_h) || (mops_type == "setg"_h) ||
+                       (mops_type == "cpy"_h));
+
+    int64_t isize = static_cast<int64_t>(kInstructionSize);
+    const Instruction* prev2 = GetInstructionAtOffset(-2 * isize);
+    const Instruction* prev1 = GetInstructionAtOffset(-1 * isize);
+    const Instruction* next1 = GetInstructionAtOffset(1 * isize);
+    const Instruction* next2 = GetInstructionAtOffset(2 * isize);
+
+    // Use the encoding of the current instruction to determine the expected
+    // adjacent instructions. NB. this doesn't check if the nearby instructions
+    // are MOPS-type, but checks that they form a consistent triplet if they
+    // are. For example, 'mov x0, #0; mov x0, #512; mov x0, #1024' is a
+    // consistent triplet, but they are not MOPS instructions.
+    const int op_lsb = (mops_type == "cpy"_h) ? 22 : 14;
+    const uint32_t kMOPSOpfield = 0x3 << op_lsb;
+    const uint32_t kMOPSPrologue = 0;
+    const uint32_t kMOPSMain = 0x1 << op_lsb;
+    const uint32_t kMOPSEpilogue = 0x2 << op_lsb;
+    switch (Mask(kMOPSOpfield)) {
+      case kMOPSPrologue:
+        return next1->IsMOPSMainOf(this, mops_type) &&
+               next2->IsMOPSEpilogueOf(this, mops_type);
+      case kMOPSMain:
+        return prev1->IsMOPSPrologueOf(this, mops_type) &&
+               next1->IsMOPSEpilogueOf(this, mops_type);
+      case kMOPSEpilogue:
+        return prev2->IsMOPSPrologueOf(this, mops_type) &&
+               prev1->IsMOPSMainOf(this, mops_type);
+      default:
+        VIXL_ABORT_WITH_MSG("Undefined MOPS operation\n");
+    }
+  }
+
   static int GetImmBranchRangeBitwidth(ImmBranchType branch_type);
   VIXL_DEPRECATED(
       "GetImmBranchRangeBitwidth",
diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h
index 9b17555..af9aa52 100644
--- a/src/aarch64/macro-assembler-aarch64.h
+++ b/src/aarch64/macro-assembler-aarch64.h
@@ -7532,6 +7532,248 @@
   void Stzg(const Register& rt, const MemOperand& addr);
   void Ldg(const Register& rt, const MemOperand& addr);
 
+  void Cpye(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpye(rd, rs, rn);
+  }
+
+  void Cpyen(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyen(rd, rs, rn);
+  }
+
+  void Cpyern(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyern(rd, rs, rn);
+  }
+
+  void Cpyewn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyewn(rd, rs, rn);
+  }
+
+  void Cpyfe(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfe(rd, rs, rn);
+  }
+
+  void Cpyfen(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfen(rd, rs, rn);
+  }
+
+  void Cpyfern(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfern(rd, rs, rn);
+  }
+
+  void Cpyfewn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfewn(rd, rs, rn);
+  }
+
+  void Cpyfm(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfm(rd, rs, rn);
+  }
+
+  void Cpyfmn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfmn(rd, rs, rn);
+  }
+
+  void Cpyfmrn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfmrn(rd, rs, rn);
+  }
+
+  void Cpyfmwn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfmwn(rd, rs, rn);
+  }
+
+  void Cpyfp(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfp(rd, rs, rn);
+  }
+
+  void Cpyfpn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfpn(rd, rs, rn);
+  }
+
+  void Cpyfprn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfprn(rd, rs, rn);
+  }
+
+  void Cpyfpwn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyfpwn(rd, rs, rn);
+  }
+
+  void Cpym(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpym(rd, rs, rn);
+  }
+
+  void Cpymn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpymn(rd, rs, rn);
+  }
+
+  void Cpymrn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpymrn(rd, rs, rn);
+  }
+
+  void Cpymwn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpymwn(rd, rs, rn);
+  }
+
+  void Cpyp(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyp(rd, rs, rn);
+  }
+
+  void Cpypn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpypn(rd, rs, rn);
+  }
+
+  void Cpyprn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpyprn(rd, rs, rn);
+  }
+
+  void Cpypwn(const Register& rd, const Register& rs, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cpypwn(rd, rs, rn);
+  }
+
+  void Sete(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    sete(rd, rn, rs);
+  }
+
+  void Seten(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    seten(rd, rn, rs);
+  }
+
+  void Setge(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setge(rd, rn, rs);
+  }
+
+  void Setgen(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setgen(rd, rn, rs);
+  }
+
+  void Setgm(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setgm(rd, rn, rs);
+  }
+
+  void Setgmn(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setgmn(rd, rn, rs);
+  }
+
+  void Setgp(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setgp(rd, rn, rs);
+  }
+
+  void Setgpn(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setgpn(rd, rn, rs);
+  }
+
+  void Setm(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setm(rd, rn, rs);
+  }
+
+  void Setmn(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setmn(rd, rn, rs);
+  }
+
+  void Setp(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setp(rd, rn, rs);
+  }
+
+  void Setpn(const Register& rd, const Register& rn, const Register& rs) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    setpn(rd, rn, rs);
+  }
+
+// Macro assembler wrappers that package the MOPS instructions into a single
+// call.
+#define MOPS_LIST(V)  \
+  V(Set, set, )       \
+  V(Setn, set, n)     \
+  V(Setg, setg, )     \
+  V(Setgn, setg, n)   \
+  V(Cpy, cpy, )       \
+  V(Cpyn, cpy, n)     \
+  V(Cpyrn, cpy, rn)   \
+  V(Cpywn, cpy, wn)   \
+  V(Cpyf, cpyf, )     \
+  V(Cpyfn, cpyf, n)   \
+  V(Cpyfrn, cpyf, rn) \
+  V(Cpyfwn, cpyf, wn)
+
+#define DEFINE_MACRO_ASM_FUNC(MASM, ASMPREFIX, ASMSUFFIX)                 \
+  void MASM(const Register& ra, const Register& rb, const Register& rc) { \
+    ExactAssemblyScope scope(this, 3 * kInstructionSize);                 \
+    ASMPREFIX##p##ASMSUFFIX(ra, rb, rc);                                  \
+    ASMPREFIX##m##ASMSUFFIX(ra, rb, rc);                                  \
+    ASMPREFIX##e##ASMSUFFIX(ra, rb, rc);                                  \
+  }
+  MOPS_LIST(DEFINE_MACRO_ASM_FUNC)
+#undef DEFINE_MACRO_ASM_FUNC
+
   template <typename T>
   Literal<T>* CreateLiteralDestroyedWithPool(T value) {
     return new Literal<T>(value,
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index c98fb21..765ffa4 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -432,6 +432,42 @@
       {"subg_64_addsub_immtags"_h, &Simulator::SimulateMTEAddSubTag},
       {"subps_64s_dp_2src"_h, &Simulator::SimulateMTESubPointer},
       {"subp_64s_dp_2src"_h, &Simulator::SimulateMTESubPointer},
+      {"cpyen_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyern_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyewn_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpye_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyfen_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyfern_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyfewn_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyfe_cpy_memcms"_h, &Simulator::SimulateCpyE},
+      {"cpyfmn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpyfmrn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpyfmwn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpyfm_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpyfpn_cpy_memcms"_h, &Simulator::SimulateCpyFP},
+      {"cpyfprn_cpy_memcms"_h, &Simulator::SimulateCpyFP},
+      {"cpyfpwn_cpy_memcms"_h, &Simulator::SimulateCpyFP},
+      {"cpyfp_cpy_memcms"_h, &Simulator::SimulateCpyFP},
+      {"cpymn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpymrn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpymwn_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpym_cpy_memcms"_h, &Simulator::SimulateCpyM},
+      {"cpypn_cpy_memcms"_h, &Simulator::SimulateCpyP},
+      {"cpyprn_cpy_memcms"_h, &Simulator::SimulateCpyP},
+      {"cpypwn_cpy_memcms"_h, &Simulator::SimulateCpyP},
+      {"cpyp_cpy_memcms"_h, &Simulator::SimulateCpyP},
+      {"setp_set_memcms"_h, &Simulator::SimulateSetP},
+      {"setpn_set_memcms"_h, &Simulator::SimulateSetP},
+      {"setgp_set_memcms"_h, &Simulator::SimulateSetGP},
+      {"setgpn_set_memcms"_h, &Simulator::SimulateSetGP},
+      {"setm_set_memcms"_h, &Simulator::SimulateSetM},
+      {"setmn_set_memcms"_h, &Simulator::SimulateSetM},
+      {"setgm_set_memcms"_h, &Simulator::SimulateSetGM},
+      {"setgmn_set_memcms"_h, &Simulator::SimulateSetGM},
+      {"sete_set_memcms"_h, &Simulator::SimulateSetE},
+      {"seten_set_memcms"_h, &Simulator::SimulateSetE},
+      {"setge_set_memcms"_h, &Simulator::SimulateSetE},
+      {"setgen_set_memcms"_h, &Simulator::SimulateSetE},
   };
   return &form_to_visitor;
 }
@@ -1927,6 +1963,24 @@
   }
 }
 
+void Simulator::PrintMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value) {
+  fprintf(stream_,
+          "#               %s: %s0x%016" PRIxPTR " %s<- %s0x%02x%s",
+          clr_reg_name,
+          clr_memory_address,
+          dst,
+          clr_normal,
+          clr_reg_value,
+          value,
+          clr_normal);
+
+  fprintf(stream_,
+          " <- %s0x%016" PRIxPTR "%s\n",
+          clr_memory_address,
+          src,
+          clr_normal);
+}
+
 void Simulator::PrintRead(int rt_code,
                           PrintRegisterFormat format,
                           uintptr_t address) {
@@ -14036,22 +14090,6 @@
   }
 }
 
-void Simulator::Simulate_XtSP_XnSP_simm_excl(const Instruction* instr) {
-  uint64_t rn = ReadXRegister(instr->GetRn(), Reg31IsStackPointer);
-  USE(rn);
-
-  switch (form_hash_) {
-    case Hash("stg_64spre_ldsttags"):
-      break;
-    case Hash("stz2g_64spre_ldsttags"):
-      break;
-    case Hash("stzg_64spre_ldsttags"):
-      break;
-    default:
-      VIXL_UNIMPLEMENTED();
-  }
-}
-
 void Simulator::SimulateMTELoadTag(const Instruction* instr) {
   uint64_t rt = ReadXRegister(instr->GetRt());
   int offset = instr->GetImmLS() * static_cast<int>(kMTETagGranuleInBytes);
@@ -14069,6 +14107,145 @@
   WriteXRegister(instr->GetRt(), GetAddressWithAllocationTag(rt, tag));
 }
 
+void Simulator::SimulateCpyFP(const Instruction* instr) {
+  MOPSPHelper<"cpy"_h>(instr);
+  LogSystemRegister(NZCV);
+}
+
+void Simulator::SimulateCpyP(const Instruction* instr) {
+  MOPSPHelper<"cpy"_h>(instr);
+
+  int d = instr->GetRd();
+  int n = instr->GetRn();
+  int s = instr->GetRs();
+
+  // Determine copy direction. For cases in which direction is implementation
+  // defined, use forward.
+  bool is_backwards = false;
+  uint64_t xs = ReadXRegister(s);
+  uint64_t xd = ReadXRegister(d);
+  uint64_t xn = ReadXRegister(n);
+
+  // Ignore the top byte of addresses for comparisons. We can use xn as is,
+  // as it should have zero in bits 63:55.
+  uint64_t xs_tbi = ExtractUnsignedBitfield64(55, 0, xs);
+  uint64_t xd_tbi = ExtractUnsignedBitfield64(55, 0, xd);
+  VIXL_ASSERT(ExtractUnsignedBitfield64(63, 55, xn) == 0);
+  if ((xs_tbi < xd_tbi) && ((xs_tbi + xn) > xd_tbi)) {
+    is_backwards = true;
+    WriteXRegister(s, xs + xn);
+    WriteXRegister(d, xd + xn);
+  }
+
+  ReadNzcv().SetN(is_backwards ? 1 : 0);
+  LogSystemRegister(NZCV);
+}
+
+void Simulator::SimulateCpyM(const Instruction* instr) {
+  VIXL_ASSERT(instr->IsConsistentMOPSTriplet<"cpy"_h>());
+  VIXL_ASSERT(instr->IsMOPSMainOf(GetLastExecutedInstruction(), "cpy"_h));
+
+  int d = instr->GetRd();
+  int n = instr->GetRn();
+  int s = instr->GetRs();
+
+  uint64_t xd = ReadXRegister(d);
+  uint64_t xn = ReadXRegister(n);
+  uint64_t xs = ReadXRegister(s);
+  bool is_backwards = ReadN();
+
+  int step = 1;
+  if (is_backwards) {
+    step = -1;
+    xs--;
+    xd--;
+  }
+
+  while (xn--) {
+    uint8_t temp = MemRead<uint8_t>(xs);
+    MemWrite<uint8_t>(xd, temp);
+    LogMemTransfer(xd, xs, temp);
+    xs += step;
+    xd += step;
+  }
+
+  if (is_backwards) {
+    xs++;
+    xd++;
+  }
+
+  WriteXRegister(d, xd);
+  WriteXRegister(n, 0);
+  WriteXRegister(s, xs);
+}
+
+void Simulator::SimulateCpyE(const Instruction* instr) {
+  USE(instr);
+  VIXL_ASSERT(instr->IsConsistentMOPSTriplet<"cpy"_h>());
+  VIXL_ASSERT(instr->IsMOPSEpilogueOf(GetLastExecutedInstruction(), "cpy"_h));
+  // This implementation does nothing in the epilogue; all copying is completed
+  // in the "main" part.
+}
+
+void Simulator::SimulateSetP(const Instruction* instr) {
+  MOPSPHelper<"set"_h>(instr);
+  LogSystemRegister(NZCV);
+}
+
+void Simulator::SimulateSetM(const Instruction* instr) {
+  VIXL_ASSERT(instr->IsConsistentMOPSTriplet<"set"_h>());
+  VIXL_ASSERT(instr->IsMOPSMainOf(GetLastExecutedInstruction(), "set"_h));
+
+  uint64_t xd = ReadXRegister(instr->GetRd());
+  uint64_t xn = ReadXRegister(instr->GetRn());
+  uint64_t xs = ReadXRegister(instr->GetRs());
+
+  while (xn--) {
+    LogWrite(instr->GetRs(), GetPrintRegPartial(kPrintRegLaneSizeB), xd);
+    MemWrite<uint8_t>(xd++, xs);
+  }
+  WriteXRegister(instr->GetRd(), xd);
+  WriteXRegister(instr->GetRn(), 0);
+}
+
+void Simulator::SimulateSetE(const Instruction* instr) {
+  USE(instr);
+  VIXL_ASSERT(instr->IsConsistentMOPSTriplet<"set"_h>());
+  VIXL_ASSERT(instr->IsMOPSEpilogueOf(GetLastExecutedInstruction(), "set"_h));
+  // This implementation does nothing in the epilogue; all setting is completed
+  // in the "main" part.
+}
+
+void Simulator::SimulateSetGP(const Instruction* instr) {
+  MOPSPHelper<"setg"_h>(instr);
+
+  uint64_t xd = ReadXRegister(instr->GetRd());
+  uint64_t xn = ReadXRegister(instr->GetRn());
+
+  if ((xn > 0) && !IsAligned(xd, kMTETagGranuleInBytes)) {
+    VIXL_ALIGNMENT_EXCEPTION();
+  }
+
+  if (!IsAligned(xn, kMTETagGranuleInBytes)) {
+    VIXL_ALIGNMENT_EXCEPTION();
+  }
+
+  LogSystemRegister(NZCV);
+}
+
+void Simulator::SimulateSetGM(const Instruction* instr) {
+  uint64_t xd = ReadXRegister(instr->GetRd());
+  uint64_t xn = ReadXRegister(instr->GetRn());
+
+  int tag = GetAllocationTagFromAddress(xd);
+  while (xn) {
+    meta_data_.SetMTETag(xd, tag);
+    xd += 16;
+    xn -= 16;
+  }
+  SimulateSetM(instr);
+}
+
 void Simulator::DoTrace(const Instruction* instr) {
   VIXL_ASSERT((instr->Mask(ExceptionMask) == HLT) &&
               (instr->GetImmException() == kTraceOpcode));
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 1555922..61dd848 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -230,8 +230,7 @@
 
   template <typename T>
   void SetMTETag(T address, int tag, Instruction const* pc = nullptr) {
-    VIXL_ASSERT(
-        IsAligned(reinterpret_cast<uintptr_t>(address), kMTETagGranuleInBytes));
+    VIXL_ASSERT(IsAligned((uintptr_t)address, kMTETagGranuleInBytes));
     uint64_t key = GenerateMTEkey(address);
     MetaDataMTE* m = GetAttribute<MetaDataMTE*>(&metadata_mte_, key);
 
@@ -1267,6 +1266,8 @@
   bool PcIsInGuardedPage() const { return guard_pages_; }
   void SetGuardedPages(bool guard_pages) { guard_pages_ = guard_pages; }
 
+  const Instruction* GetLastExecutedInstruction() const { return last_instr_; }
+
   void ExecuteInstruction() {
     // The program counter should always be aligned.
     VIXL_ASSERT(IsWordAligned(pc_));
@@ -1379,15 +1380,24 @@
   void SimulateNEONFPMulByElementLong(const Instruction* instr);
   void SimulateNEONComplexMulByElement(const Instruction* instr);
   void SimulateNEONDotProdByElement(const Instruction* instr);
-
-  void Simulate_XdSP_XnSP_Xm(const Instruction* instr);
   void SimulateMTEAddSubTag(const Instruction* instr);
   void SimulateMTETagMaskInsert(const Instruction* instr);
   void SimulateMTESubPointer(const Instruction* instr);
   void SimulateMTELoadTag(const Instruction* instr);
   void SimulateMTEStoreTag(const Instruction* instr);
   void SimulateMTEStoreTagPair(const Instruction* instr);
-  void Simulate_XtSP_XnSP_simm_excl(const Instruction* instr);
+  void Simulate_XdSP_XnSP_Xm(const Instruction* instr);
+  void SimulateCpy(const Instruction* instr);
+  void SimulateCpyFP(const Instruction* instr);
+  void SimulateCpyP(const Instruction* instr);
+  void SimulateCpyM(const Instruction* instr);
+  void SimulateCpyE(const Instruction* instr);
+  void SimulateSetP(const Instruction* instr);
+  void SimulateSetM(const Instruction* instr);
+  void SimulateSetE(const Instruction* instr);
+  void SimulateSetGP(const Instruction* instr);
+  void SimulateSetGM(const Instruction* instr);
+
 
   // Integer register accessors.
 
@@ -2451,7 +2461,9 @@
   void LogPWrite(int rt_code, uintptr_t address) {
     if (ShouldTraceWrites()) PrintPWrite(rt_code, address);
   }
-
+  void LogMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value) {
+    if (ShouldTraceWrites()) PrintMemTransfer(dst, src, value);
+  }
   // Helpers for the above, where the access operation is parameterised.
   // - For loads, set op = "<-".
   // - For stores, set op = "->".
@@ -2463,6 +2475,7 @@
                     PrintRegisterFormat format,
                     const char* op,
                     uintptr_t address);
+  void PrintMemTransfer(uintptr_t dst, uintptr_t src, uint8_t value);
   // Simple, unpredicated SVE accesses always access the whole vector, and never
   // know the lane type, so these don't accept a `format`.
   void PrintZAccess(int rt_code, const char* op, uintptr_t address);
@@ -3118,6 +3131,46 @@
                                       AddrMode addr_mode);
   void NEONLoadStoreSingleStructHelper(const Instruction* instr,
                                        AddrMode addr_mode);
+  template <uint32_t mops_type>
+  void MOPSPHelper(const Instruction* instr) {
+    VIXL_ASSERT(instr->IsConsistentMOPSTriplet<mops_type>());
+
+    int d = instr->GetRd();
+    int n = instr->GetRn();
+    int s = instr->GetRs();
+
+    // Aliased registers and xzr are disallowed for Xd and Xn.
+    if ((d == n) || (d == s) || (n == s) || (d == 31) || (n == 31)) {
+      VisitUnallocated(instr);
+    }
+
+    // Additionally, Xs may not be xzr for cpy.
+    if ((mops_type == "cpy"_h) && (s == 31)) {
+      VisitUnallocated(instr);
+    }
+
+    // Bits 31 and 30 must be zero.
+    if (instr->ExtractBits(31, 30) != 0) {
+      VisitUnallocated(instr);
+    }
+
+    // Saturate copy count.
+    uint64_t xn = ReadXRegister(n);
+    int saturation_bits = (mops_type == "cpy"_h) ? 55 : 63;
+    if ((xn >> saturation_bits) != 0) {
+      xn = (UINT64_C(1) << saturation_bits) - 1;
+      if (mops_type == "setg"_h) {
+        // Align saturated value to granule.
+        xn &= ~UINT64_C(kMTETagGranuleInBytes - 1);
+      }
+      WriteXRegister(n, xn);
+    }
+
+    ReadNzcv().SetN(0);
+    ReadNzcv().SetZ(0);
+    ReadNzcv().SetC(1);  // Indicates "option B" implementation.
+    ReadNzcv().SetV(0);
+  }
 
   int64_t ShiftOperand(unsigned reg_size,
                        uint64_t value,
@@ -4950,7 +5003,7 @@
   const Instruction* pc_;
 
   // Pointer to the last simulated instruction, used for checking the validity
-  // of the current instruction with movprfx.
+  // of the current instruction with the previous instruction, such as movprfx.
   Instruction const* last_instr_;
 
   // Branch type register, used for branch target identification.
diff --git a/src/cpu-features.h b/src/cpu-features.h
index ebd0578..24781da 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -183,7 +183,9 @@
   /* Enhanced Counter Virtualization                                        */ \
   V(kECV,                 "ECV",                    "ecv")                     \
   /* Increased precision of Reciprocal Estimate and Square Root Estimate    */ \
-  V(kRPRES,               "RPRES",                  "rpres")
+  V(kRPRES,               "RPRES",                  "rpres")                   \
+  /* Memory operation instructions, for memcpy, memset                      */ \
+  V(kMOPS,                "Memory ops",             NULL)
 // clang-format on
 
 
diff --git a/test/aarch64/test-assembler-aarch64.cc b/test/aarch64/test-assembler-aarch64.cc
index 5b5a4ad..1ea4f1a 100644
--- a/test/aarch64/test-assembler-aarch64.cc
+++ b/test/aarch64/test-assembler-aarch64.cc
@@ -14001,6 +14001,345 @@
   }
 }
 
+TEST(mops_set) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  uint8_t dst[16];
+  memset(dst, 0x55, ArrayLength(dst));
+  uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
+
+  START();
+  __ Mov(x0, dst_addr);
+  __ Add(x1, x0, 1);
+  __ Mov(x2, 13);
+  __ Mov(x3, 0x1234aa);
+
+  // Set 13 bytes dst[1] onwards to 0xaa.
+  __ Setp(x1, x2, x3);
+  __ Setm(x1, x2, x3);
+  __ Sete(x1, x2, x3);
+
+  // x2 is now zero, so this should do nothing.
+  __ Setp(x1, x2, x3);
+  __ Setm(x1, x2, x3);
+  __ Sete(x1, x2, x3);
+
+  // Set dst[15] to zero using the masm helper.
+  __ Add(x1, x0, 15);
+  __ Mov(x2, 1);
+  __ Set(x1, x2, xzr);
+
+  // Load dst for comparison.
+  __ Ldp(x10, x11, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(dst_addr + 16, x1);
+    ASSERT_EQUAL_64(0, x2);
+    ASSERT_EQUAL_64(0x1234aa, x3);
+    ASSERT_EQUAL_64(0xaaaa'aaaa'aaaa'aa55, x10);
+    ASSERT_EQUAL_64(0x0055'aaaa'aaaa'aaaa, x11);
+  }
+}
+
+TEST(mops_setn) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  // In simulation, non-temporal set is handled by the same code as normal set,
+  // so only a basic test is required beyond that already provided above.
+
+  uint8_t dst[16] = {0x55};
+  uintptr_t dst_addr = reinterpret_cast<uintptr_t>(dst);
+
+  START();
+  __ Mov(x0, dst_addr);
+  __ Mov(x1, x0);
+  __ Mov(x2, 16);
+  __ Mov(x3, 0x42);
+  __ Setn(x1, x2, x3);
+  __ Ldp(x10, x11, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(dst_addr + 16, x1);
+    ASSERT_EQUAL_64(0, x2);
+    ASSERT_EQUAL_64(0x42, x3);
+    ASSERT_EQUAL_64(0x4242'4242'4242'4242, x10);
+    ASSERT_EQUAL_64(0x4242'4242'4242'4242, x11);
+  }
+}
+
+TEST(mops_setg) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS, CPUFeatures::kMTE);
+
+  uint8_t* dst_addr = nullptr;
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
+  const int dst_size = 32;
+  dst_addr = reinterpret_cast<uint8_t*>(
+      simulator.Mmap(NULL,
+                     dst_size * sizeof(uint8_t),
+                     PROT_READ | PROT_WRITE | PROT_MTE,
+                     MAP_PRIVATE | MAP_ANONYMOUS,
+                     -1,
+                     0));
+
+  VIXL_ASSERT(dst_addr != nullptr);
+  uint8_t* untagged_ptr = AddressUntag(dst_addr);
+  memset(untagged_ptr, 0xc9, dst_size);
+#else
+// TODO: Port the memory allocation to work on MTE supported platform natively.
+// Note that `CAN_RUN` prevents running in MTE-unsupported environments.
+#endif
+
+  START();
+  __ Mov(x0, reinterpret_cast<uint64_t>(dst_addr));
+  __ Gmi(x2, x0, xzr);
+  __ Irg(x1, x0, x2);  // Choose new tag for setg destination.
+  __ Mov(x2, 16);
+  __ Mov(x3, 0x42);
+  __ Setg(x1, x2, x3);
+
+  __ Ubfx(x4, x1, 56, 4);  // Extract new tag.
+  __ Bfi(x0, x4, 56, 4);   // Tag dst_addr so set region can be loaded.
+  __ Ldp(x10, x11, MemOperand(x0));
+
+  __ Mov(x0, reinterpret_cast<uint64_t>(dst_addr));
+  __ Ldp(x12, x13, MemOperand(x0, 16));  // Unset region has original tag.
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(0, x2);
+    ASSERT_EQUAL_64(0x42, x3);
+    ASSERT_EQUAL_64(0x4242'4242'4242'4242, x10);
+    ASSERT_EQUAL_64(0x4242'4242'4242'4242, x11);
+    ASSERT_EQUAL_64(0xc9c9'c9c9'c9c9'c9c9, x12);
+    ASSERT_EQUAL_64(0xc9c9'c9c9'c9c9'c9c9, x13);
+  }
+
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
+  simulator.Munmap(dst_addr, dst_size, PROT_MTE);
+#endif
+}
+
+TEST(mops_cpy) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  uint8_t buf[16];
+  uintptr_t buf_addr = reinterpret_cast<uintptr_t>(buf);
+
+  for (unsigned i = 0; i < ArrayLength(buf); i++) {
+    buf[i] = i;
+  }
+
+  START();
+  __ Mov(x0, buf_addr);
+
+  // Copy first eight bytes into second eight.
+  __ Mov(x2, x0);     // src = &buf[0]
+  __ Add(x3, x0, 8);  // dst = &buf[8]
+  __ Mov(x4, 8);      // count = 8
+  __ Cpyp(x3, x2, x4);
+  __ Cpym(x3, x2, x4);
+  __ Cpye(x3, x2, x4);
+  __ Ldp(x10, x11, MemOperand(x0));
+  __ Mrs(x20, NZCV);
+
+  // Copy first eight bytes to overlapping offset, causing reverse copy.
+  __ Mov(x5, x0);     // src = &buf[0]
+  __ Add(x6, x0, 4);  // dst = &buf[4]
+  __ Mov(x7, 8);      // count = 8
+  __ Cpy(x6, x5, x7);
+  __ Ldp(x12, x13, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(buf_addr + 8, x2);
+    ASSERT_EQUAL_64(buf_addr + 16, x3);
+    ASSERT_EQUAL_64(0, x4);
+    ASSERT_EQUAL_64(0x0706'0504'0302'0100, x10);
+    ASSERT_EQUAL_64(0x0706'0504'0302'0100, x11);
+    ASSERT_EQUAL_64(CFlag, x20);
+
+    ASSERT_EQUAL_64(buf_addr, x5);
+    ASSERT_EQUAL_64(buf_addr + 4, x6);
+    ASSERT_EQUAL_64(0, x7);
+    ASSERT_EQUAL_64(0x0302'0100'0302'0100, x12);
+    ASSERT_EQUAL_64(0x0706'0504'0706'0504, x13);
+    ASSERT_EQUAL_NZCV(NCFlag);
+  }
+}
+
+TEST(mops_cpyn) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  // In simulation, non-temporal cpy is handled by the same code as normal cpy,
+  // so only a basic test is required beyond that already provided above.
+
+  uint8_t buf[16];
+  uintptr_t buf_addr = reinterpret_cast<uintptr_t>(buf);
+
+  for (unsigned i = 0; i < ArrayLength(buf); i++) {
+    buf[i] = i;
+  }
+
+  START();
+  __ Mov(x0, buf_addr);
+
+  __ Add(x2, x0, 1);  // src = &buf[1]
+  __ Mov(x3, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpyn(x3, x2, x4);
+  __ Ldp(x10, x11, MemOperand(x0));
+
+  __ Add(x5, x0, 1);  // src = &buf[1]
+  __ Mov(x6, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpyrn(x6, x5, x4);
+  __ Ldp(x12, x13, MemOperand(x0));
+
+  __ Add(x7, x0, 1);  // src = &buf[1]
+  __ Mov(x8, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpywn(x8, x7, x4);
+  __ Ldp(x14, x15, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(buf_addr + 16, x2);
+    ASSERT_EQUAL_64(buf_addr + 15, x3);
+    ASSERT_EQUAL_64(0x0807'0605'0403'0201, x10);
+    ASSERT_EQUAL_64(0x0f0f'0e0d'0c0b'0a09, x11);
+
+    ASSERT_EQUAL_64(buf_addr + 16, x5);
+    ASSERT_EQUAL_64(buf_addr + 15, x6);
+    ASSERT_EQUAL_64(0x0908'0706'0504'0302, x12);
+    ASSERT_EQUAL_64(0x0f0f'0f0e'0d0c'0b0a, x13);
+
+    ASSERT_EQUAL_64(buf_addr + 16, x7);
+    ASSERT_EQUAL_64(buf_addr + 15, x8);
+    ASSERT_EQUAL_64(0x0a09'0807'0605'0403, x14);
+    ASSERT_EQUAL_64(0x0f0f'0f0f'0e0d'0c0b, x15);
+
+    ASSERT_EQUAL_64(0, x4);
+    ASSERT_EQUAL_NZCV(CFlag);
+  }
+}
+
+TEST(mops_cpyf) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  uint8_t buf[16];
+  uintptr_t buf_addr = reinterpret_cast<uintptr_t>(buf);
+
+  for (unsigned i = 0; i < ArrayLength(buf); i++) {
+    buf[i] = i;
+  }
+
+  // This test matches the cpy variant above, but using cpyf will result in a
+  // different answer for the overlapping copy.
+  START();
+  __ Mov(x0, buf_addr);
+
+  // Copy first eight bytes into second eight.
+  __ Mov(x2, x0);     // src = &buf[0]
+  __ Add(x3, x0, 8);  // dst = &buf[8]
+  __ Mov(x4, 8);      // count = 8
+  __ Cpyf(x3, x2, x4);
+  __ Ldp(x10, x11, MemOperand(x0));
+  __ Mrs(x20, NZCV);
+
+  // Copy first eight bytes to overlapping offset.
+  __ Mov(x5, x0);     // src = &buf[0]
+  __ Add(x6, x0, 4);  // dst = &buf[4]
+  __ Mov(x7, 8);      // count = 8
+  __ Cpyf(x6, x5, x7);
+  __ Ldp(x12, x13, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(buf_addr + 8, x2);
+    ASSERT_EQUAL_64(buf_addr + 16, x3);
+    ASSERT_EQUAL_64(0, x4);
+    ASSERT_EQUAL_64(0x0706'0504'0302'0100, x10);
+    ASSERT_EQUAL_64(0x0706'0504'0302'0100, x11);
+    ASSERT_EQUAL_64(CFlag, x20);
+
+    ASSERT_EQUAL_64(buf_addr + 8, x5);
+    ASSERT_EQUAL_64(buf_addr + 12, x6);
+    ASSERT_EQUAL_64(0, x7);
+    ASSERT_EQUAL_NZCV(CFlag);
+
+    // These results are not architecturally defined. They may change if the
+    // simulator is implemented in a different, but still architecturally
+    // correct, way.
+    ASSERT_EQUAL_64(0x0302'0100'0302'0100, x12);
+    ASSERT_EQUAL_64(0x0706'0504'0302'0100, x13);
+  }
+}
+
+TEST(mops_cpyfn) {
+  SETUP_WITH_FEATURES(CPUFeatures::kMOPS);
+
+  // In simulation, non-temporal cpy is handled by the same code as normal cpy,
+  // so only a basic test is required beyond that already provided above.
+
+  uint8_t buf[16];
+  uintptr_t buf_addr = reinterpret_cast<uintptr_t>(buf);
+
+  for (unsigned i = 0; i < ArrayLength(buf); i++) {
+    buf[i] = i;
+  }
+
+  START();
+  __ Mov(x0, buf_addr);
+
+  __ Add(x2, x0, 1);  // src = &buf[1]
+  __ Mov(x3, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpyfn(x3, x2, x4);
+  __ Ldp(x10, x11, MemOperand(x0));
+
+  __ Add(x5, x0, 1);  // src = &buf[1]
+  __ Mov(x6, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpyfrn(x6, x5, x4);
+  __ Ldp(x12, x13, MemOperand(x0));
+
+  __ Add(x7, x0, 1);  // src = &buf[1]
+  __ Mov(x8, x0);     // dst = &buf[0]
+  __ Mov(x4, 15);     // count = 15
+  __ Cpyfwn(x8, x7, x4);
+  __ Ldp(x14, x15, MemOperand(x0));
+  END();
+
+  if (CAN_RUN()) {
+    RUN();
+    ASSERT_EQUAL_64(buf_addr + 16, x2);
+    ASSERT_EQUAL_64(buf_addr + 15, x3);
+    ASSERT_EQUAL_64(0x0807'0605'0403'0201, x10);
+    ASSERT_EQUAL_64(0x0f0f'0e0d'0c0b'0a09, x11);
+
+    ASSERT_EQUAL_64(buf_addr + 16, x5);
+    ASSERT_EQUAL_64(buf_addr + 15, x6);
+    ASSERT_EQUAL_64(0x0908'0706'0504'0302, x12);
+    ASSERT_EQUAL_64(0x0f0f'0f0e'0d0c'0b0a, x13);
+
+    ASSERT_EQUAL_64(buf_addr + 16, x7);
+    ASSERT_EQUAL_64(buf_addr + 15, x8);
+    ASSERT_EQUAL_64(0x0a09'0807'0605'0403, x14);
+    ASSERT_EQUAL_64(0x0f0f'0f0f'0e0d'0c0b, x15);
+
+    ASSERT_EQUAL_64(0, x4);
+    ASSERT_EQUAL_NZCV(CFlag);
+  }
+}
+
 #ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
 // Test the pseudo-instructions that control CPUFeatures dynamically in the
 // Simulator. These are used by the test infrastructure itself, but in a fairly
diff --git a/test/aarch64/test-disasm-aarch64.cc b/test/aarch64/test-disasm-aarch64.cc
index ef33846..3ef49a1 100644
--- a/test/aarch64/test-disasm-aarch64.cc
+++ b/test/aarch64/test-disasm-aarch64.cc
@@ -3241,6 +3241,69 @@
   CLEANUP();
 }
 
+TEST(mops) {
+  SETUP();
+
+  COMPARE_MACRO(Cpyen(x4, x5, x6), "cpyen [x4]!, [x5]!, x6!");
+  COMPARE_MACRO(Cpyern(x7, x6, x1), "cpyern [x7]!, [x6]!, x1!");
+  COMPARE_MACRO(Cpyewn(x26, x27, x28), "cpyewn [x26]!, [x27]!, x28!");
+  COMPARE_MACRO(Cpye(x14, x15, x19), "cpye [x14]!, [x15]!, x19!");
+  COMPARE_MACRO(Cpyfen(x13, x1, x9), "cpyfen [x13]!, [x1]!, x9!");
+  COMPARE_MACRO(Cpyfern(x24, x2, x10), "cpyfern [x24]!, [x2]!, x10!");
+  COMPARE_MACRO(Cpyfewn(x12, x2, x11), "cpyfewn [x12]!, [x2]!, x11!");
+  COMPARE_MACRO(Cpyfe(x3, x9, x12), "cpyfe [x3]!, [x9]!, x12!");
+  COMPARE_MACRO(Cpyfmn(x7, x27, x13), "cpyfmn [x7]!, [x27]!, x13!");
+  COMPARE_MACRO(Cpyfmrn(x19, x9, x14), "cpyfmrn [x19]!, [x9]!, x14!");
+  COMPARE_MACRO(Cpyfmwn(x1, x11, x15), "cpyfmwn [x1]!, [x11]!, x15!");
+  COMPARE_MACRO(Cpyfm(x1, x13, x16), "cpyfm [x1]!, [x13]!, x16!");
+  COMPARE_MACRO(Cpyfpn(x3, x13, x17), "cpyfpn [x3]!, [x13]!, x17!");
+  COMPARE_MACRO(Cpyfprn(x18, x8, x17), "cpyfprn [x18]!, [x8]!, x17!");
+  COMPARE_MACRO(Cpyfpwn(x9, x29, x18), "cpyfpwn [x9]!, [x29]!, x18!");
+  COMPARE_MACRO(Cpyfp(x4, x3, x19), "cpyfp [x4]!, [x3]!, x19!");
+  COMPARE_MACRO(Cpymn(x5, x15, x20), "cpymn [x5]!, [x15]!, x20!");
+  COMPARE_MACRO(Cpymrn(x12, x22, x21), "cpymrn [x12]!, [x22]!, x21!");
+  COMPARE_MACRO(Cpymwn(x12, x1, x22), "cpymwn [x12]!, [x1]!, x22!");
+  COMPARE_MACRO(Cpym(x1, x10, x23), "cpym [x1]!, [x10]!, x23!");
+  COMPARE_MACRO(Cpypn(x3, x26, x25), "cpypn [x3]!, [x26]!, x25!");
+  COMPARE_MACRO(Cpyprn(x4, x14, x24), "cpyprn [x4]!, [x14]!, x24!");
+  COMPARE_MACRO(Cpypwn(x9, x29, x26), "cpypwn [x9]!, [x29]!, x26!");
+  COMPARE_MACRO(Cpyp(x0, x30, x28), "cpyp [x0]!, [x30]!, x28!");
+
+  COMPARE_MACRO(Seten(x6, x26, x27), "seten [x6]!, x26!, x27");
+  COMPARE_MACRO(Sete(x3, x23, x1), "sete [x3]!, x23!, x1");
+  COMPARE_MACRO(Setgen(x6, x16, x2), "setgen [x6]!, x16!, x2");
+  COMPARE_MACRO(Setge(x4, x24, x3), "setge [x4]!, x24!, x3");
+  COMPARE_MACRO(Setgmn(x9, x29, x4), "setgmn [x9]!, x29!, x4");
+  COMPARE_MACRO(Setgm(x30, x3, x5), "setgm [x30]!, x3!, x5");
+  COMPARE_MACRO(Setgpn(x11, x1, x6), "setgpn [x11]!, x1!, x6");
+  COMPARE_MACRO(Setgp(x1, x16, x7), "setgp [x1]!, x16!, x7");
+  COMPARE_MACRO(Setmn(x4, x14, x8), "setmn [x4]!, x14!, x8");
+  COMPARE_MACRO(Setm(x8, x7, x9), "setm [x8]!, x7!, x9");
+  COMPARE_MACRO(Setpn(x2, x22, x10), "setpn [x2]!, x22!, x10");
+  COMPARE_MACRO(Setp(x7, x17, x11), "setp [x7]!, x17!, x11");
+
+  // Check unallocated bit patterns.
+  COMPARE_PREFIX(dci(0x1d000422), "cpyp [x2]!, [x0]!, x1!");
+  COMPARE_PREFIX(dci(0xdd000422), "unallocated");  // sz != 0
+  COMPARE_PREFIX(dci(0x1d000442), "unallocated");  // Xd == Xn
+  COMPARE_PREFIX(dci(0x1d020422), "unallocated");  // Xd == Xs
+  COMPARE_PREFIX(dci(0x1d000402), "unallocated");  // Xn == Xs
+  COMPARE_PREFIX(dci(0x1d00043f), "unallocated");  // Xd == 31
+  COMPARE_PREFIX(dci(0x1d0007e2), "unallocated");  // Xn == 31
+  COMPARE_PREFIX(dci(0x1d1f0422), "unallocated");  // Xs == 31
+
+  COMPARE_PREFIX(dci(0x19c02424), "setpn [x4]!, x1!, x0");
+  COMPARE_PREFIX(dci(0xd9c02424), "unallocated");  // sz != 0
+  COMPARE_PREFIX(dci(0x19c0e424), "unallocated");  // op2 == 0xe
+  COMPARE_PREFIX(dci(0x19c02400), "unallocated");  // Xd == Xn
+  COMPARE_PREFIX(dci(0x19c02420), "unallocated");  // Xd == Xs
+  COMPARE_PREFIX(dci(0x19c02404), "unallocated");  // Xn == Xs
+  COMPARE_PREFIX(dci(0x19c0243f), "unallocated");  // Xd == 31
+  COMPARE_PREFIX(dci(0x19c027e4), "unallocated");  // Xn == 31
+
+  CLEANUP();
+}
+
 TEST(architecture_features) {
   SETUP();
 
diff --git a/tools/code_coverage.log b/tools/code_coverage.log
index 891ad27..b40ade0 100644
--- a/tools/code_coverage.log
+++ b/tools/code_coverage.log
@@ -9,4 +9,5 @@
 1650549095 82.93% 97.52% 95.33%
 1651138061 82.94% 97.52% 95.36%
 1653484786 82.79% 97.46% 95.51%
+1657272256 83.03% 97.50% 95.35%
 1657620989 82.93% 97.52% 95.33%