Add support for CSSC instructions (#69)

Add support for CSSC instructions (abs, cnt, ctz, smax, smin, umax, umin) to all
components, and refactor some of the code nearby.
diff --git a/src/aarch64/assembler-aarch64.cc b/src/aarch64/assembler-aarch64.cc
index df17813..604ec46 100644
--- a/src/aarch64/assembler-aarch64.cc
+++ b/src/aarch64/assembler-aarch64.cc
@@ -2429,6 +2429,52 @@
   Emit(0x19c02400 | Rd(rd) | Rn(rn) | Rs(rs));
 }
 
+void Assembler::abs(const Register& rd, const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
+  VIXL_ASSERT(rd.IsSameSizeAndType(rn));
+
+  Emit(0x5ac02000 | SF(rd) | Rd(rd) | Rn(rn));
+}
+
+void Assembler::cnt(const Register& rd, const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
+  VIXL_ASSERT(rd.IsSameSizeAndType(rn));
+
+  Emit(0x5ac01c00 | SF(rd) | Rd(rd) | Rn(rn));
+}
+
+void Assembler::ctz(const Register& rd, const Register& rn) {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kCSSC));
+  VIXL_ASSERT(rd.IsSameSizeAndType(rn));
+
+  Emit(0x5ac01800 | SF(rd) | Rd(rd) | Rn(rn));
+}
+
+#define MINMAX(V)                        \
+  V(smax, 0x11c00000, 0x1ac06000, true)  \
+  V(smin, 0x11c80000, 0x1ac06800, true)  \
+  V(umax, 0x11c40000, 0x1ac06400, false) \
+  V(umin, 0x11cc0000, 0x1ac06c00, false)
+
+#define VIXL_DEFINE_ASM_FUNC(FN, IMMOP, REGOP, SIGNED)                     \
+  void Assembler::FN(const Register& rd,                                   \
+                     const Register& rn,                                   \
+                     const Operand& op) {                                  \
+    VIXL_ASSERT(rd.IsSameSizeAndType(rn));                                 \
+    Instr i = SF(rd) | Rd(rd) | Rn(rn);                                    \
+    if (op.IsImmediate()) {                                                \
+      int64_t imm = op.GetImmediate();                                     \
+      i |= SIGNED ? ImmField<17, 10>(imm) : ImmUnsignedField<17, 10>(imm); \
+      Emit(IMMOP | i);                                                     \
+    } else {                                                               \
+      VIXL_ASSERT(op.IsPlainRegister());                                   \
+      VIXL_ASSERT(op.GetRegister().IsSameSizeAndType(rd));                 \
+      Emit(REGOP | i | Rm(op.GetRegister()));                              \
+    }                                                                      \
+  }
+MINMAX(VIXL_DEFINE_ASM_FUNC)
+#undef VIXL_DEFINE_ASM_FUNC
+
 // 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 0d0387f..fbf5a84 100644
--- a/src/aarch64/assembler-aarch64.h
+++ b/src/aarch64/assembler-aarch64.h
@@ -7050,6 +7050,27 @@
   // Memory Set, non-temporal.
   void setpn(const Register& rd, const Register& rn, const Register& rs);
 
+  // Absolute value.
+  void abs(const Register& rd, const Register& rn);
+
+  // Count bits.
+  void cnt(const Register& rd, const Register& rn);
+
+  // Count Trailing Zeros.
+  void ctz(const Register& rd, const Register& rn);
+
+  // Signed Maximum.
+  void smax(const Register& rd, const Register& rn, const Operand& op);
+
+  // Signed Minimum.
+  void smin(const Register& rd, const Register& rn, const Operand& op);
+
+  // Unsigned Maximum.
+  void umax(const Register& rd, const Register& rn, const Operand& op);
+
+  // Unsigned Minimum.
+  void umin(const Register& rd, const Register& rn, const Operand& op);
+
   // Emit generic instructions.
 
   // Emit raw instructions into the instruction stream.
diff --git a/src/aarch64/cpu-aarch64.cc b/src/aarch64/cpu-aarch64.cc
index fba0dfd..3b70cfc 100644
--- a/src/aarch64/cpu-aarch64.cc
+++ b/src/aarch64/cpu-aarch64.cc
@@ -81,6 +81,8 @@
 
 const IDRegister::Field AA64ISAR2::kWFXT(0);
 const IDRegister::Field AA64ISAR2::kRPRES(4);
+const IDRegister::Field AA64ISAR2::kMOPS(16);
+const IDRegister::Field AA64ISAR2::kCSSC(52);
 
 const IDRegister::Field AA64MMFR0::kECV(60);
 
@@ -195,6 +197,8 @@
   CPUFeatures f;
   if (Get(kWFXT) >= 2) f.Combine(CPUFeatures::kWFXT);
   if (Get(kRPRES) >= 1) f.Combine(CPUFeatures::kRPRES);
+  if (Get(kMOPS) >= 1) f.Combine(CPUFeatures::kMOPS);
+  if (Get(kCSSC) >= 1) f.Combine(CPUFeatures::kCSSC);
   return f;
 }
 
diff --git a/src/aarch64/cpu-aarch64.h b/src/aarch64/cpu-aarch64.h
index 7ec3a25..d5a5f8c 100644
--- a/src/aarch64/cpu-aarch64.h
+++ b/src/aarch64/cpu-aarch64.h
@@ -171,6 +171,8 @@
  private:
   static const Field kWFXT;
   static const Field kRPRES;
+  static const Field kMOPS;
+  static const Field kCSSC;
 };
 
 class AA64MMFR0 : public IDRegister {
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
index 1adb37c..e426932 100644
--- a/src/aarch64/cpu-features-auditor-aarch64.cc
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -1800,6 +1800,28 @@
         {"setm_set_memcms"_h, CPUFeatures::kMOPS},
         {"setpn_set_memcms"_h, CPUFeatures::kMOPS},
         {"setp_set_memcms"_h, CPUFeatures::kMOPS},
+        {"abs_32_dp_1src"_h, CPUFeatures::kCSSC},
+        {"abs_64_dp_1src"_h, CPUFeatures::kCSSC},
+        {"cnt_32_dp_1src"_h, CPUFeatures::kCSSC},
+        {"cnt_64_dp_1src"_h, CPUFeatures::kCSSC},
+        {"ctz_32_dp_1src"_h, CPUFeatures::kCSSC},
+        {"ctz_64_dp_1src"_h, CPUFeatures::kCSSC},
+        {"smax_32_dp_2src"_h, CPUFeatures::kCSSC},
+        {"smax_64_dp_2src"_h, CPUFeatures::kCSSC},
+        {"smin_32_dp_2src"_h, CPUFeatures::kCSSC},
+        {"smin_64_dp_2src"_h, CPUFeatures::kCSSC},
+        {"umax_32_dp_2src"_h, CPUFeatures::kCSSC},
+        {"umax_64_dp_2src"_h, CPUFeatures::kCSSC},
+        {"umin_32_dp_2src"_h, CPUFeatures::kCSSC},
+        {"umin_64_dp_2src"_h, CPUFeatures::kCSSC},
+        {"smax_32_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"smax_64_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"smin_32_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"smin_64_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"umax_32u_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"umax_64u_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"umin_32u_minmax_imm"_h, CPUFeatures::kCSSC},
+        {"umin_64u_minmax_imm"_h, CPUFeatures::kCSSC},
     };
 
     if (features.count(form_hash) > 0) {
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index 24d20df..3592752 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -730,6 +730,28 @@
       {"setm_set_memcms"_h, &Disassembler::DisassembleSet},
       {"setpn_set_memcms"_h, &Disassembler::DisassembleSet},
       {"setp_set_memcms"_h, &Disassembler::DisassembleSet},
+      {"abs_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"abs_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"cnt_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"cnt_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"ctz_32_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"ctz_64_dp_1src"_h, &Disassembler::VisitDataProcessing1Source},
+      {"smax_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"smax_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"smin_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"smin_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"umax_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"umax_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"umin_32_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"umin_64_dp_2src"_h, &Disassembler::VisitDataProcessing2Source},
+      {"smax_32_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"smax_64_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"smin_32_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"smin_64_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"umax_32u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"umax_64u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"umin_32u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
+      {"umin_64u_minmax_imm"_h, &Disassembler::DisassembleMinMaxImm},
   };
   return &form_to_visitor;
 }  // NOLINT(readability/fn_size)
@@ -1416,6 +1438,10 @@
   Format(instr, mnemonic, form);
 }
 
+void Disassembler::DisassembleMinMaxImm(const Instruction *instr) {
+  const char *suffix = (instr->ExtractBit(18) == 0) ? "'s1710" : "'u1710";
+  FormatWithDecodedMnemonic(instr, "'Rd, 'Rn, #", suffix);
+}
 
 void Disassembler::VisitCompareBranch(const Instruction *instr) {
   FormatWithDecodedMnemonic(instr, "'Rt, 'TImmCmpa");
diff --git a/src/aarch64/disasm-aarch64.h b/src/aarch64/disasm-aarch64.h
index 3345174..cc941bb 100644
--- a/src/aarch64/disasm-aarch64.h
+++ b/src/aarch64/disasm-aarch64.h
@@ -186,6 +186,7 @@
 
   void DisassembleCpy(const Instruction* instr);
   void DisassembleSet(const Instruction* instr);
+  void DisassembleMinMaxImm(const Instruction* instr);
 
   void DisassembleSVEShiftLeftImm(const Instruction* instr);
   void DisassembleSVEShiftRightImm(const Instruction* instr);
diff --git a/src/aarch64/macro-assembler-aarch64.cc b/src/aarch64/macro-assembler-aarch64.cc
index a48931b..94e153a 100644
--- a/src/aarch64/macro-assembler-aarch64.cc
+++ b/src/aarch64/macro-assembler-aarch64.cc
@@ -1411,6 +1411,33 @@
   Add(rd, rn, operand, SetFlags);
 }
 
+#define MINMAX(V)        \
+  V(Smax, smax, IsInt8)  \
+  V(Smin, smin, IsInt8)  \
+  V(Umax, umax, IsUint8) \
+  V(Umin, umin, IsUint8)
+
+#define VIXL_DEFINE_MASM_FUNC(MASM, ASM, RANGE)      \
+  void MacroAssembler::MASM(const Register& rd,      \
+                            const Register& rn,      \
+                            const Operand& op) {     \
+    VIXL_ASSERT(allow_macro_instructions_);          \
+    if (op.IsImmediate()) {                          \
+      int64_t imm = op.GetImmediate();               \
+      if (!RANGE(imm)) {                             \
+        UseScratchRegisterScope temps(this);         \
+        Register temp = temps.AcquireSameSizeAs(rd); \
+        Mov(temp, imm);                              \
+        MASM(rd, rn, temp);                          \
+        return;                                      \
+      }                                              \
+    }                                                \
+    SingleEmissionCheckScope guard(this);            \
+    ASM(rd, rn, op);                                 \
+  }
+MINMAX(VIXL_DEFINE_MASM_FUNC)
+#undef VIXL_DEFINE_MASM_FUNC
+
 void MacroAssembler::St2g(const Register& rt, const MemOperand& addr) {
   VIXL_ASSERT(allow_macro_instructions_);
   SingleEmissionCheckScope guard(this);
diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h
index f0663ee..78c9e9a 100644
--- a/src/aarch64/macro-assembler-aarch64.h
+++ b/src/aarch64/macro-assembler-aarch64.h
@@ -7777,6 +7777,29 @@
   MOPS_LIST(DEFINE_MACRO_ASM_FUNC)
 #undef DEFINE_MACRO_ASM_FUNC
 
+  void Abs(const Register& rd, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    abs(rd, rn);
+  }
+
+  void Cnt(const Register& rd, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    cnt(rd, rn);
+  }
+
+  void Ctz(const Register& rd, const Register& rn) {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    ctz(rd, rn);
+  }
+
+  void Smax(const Register& rd, const Register& rn, const Operand& op);
+  void Smin(const Register& rd, const Register& rn, const Operand& op);
+  void Umax(const Register& rd, const Register& rn, const Operand& op);
+  void Umin(const Register& rd, const Register& rn, const Operand& op);
+
   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 275840a..7c7dc1c 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -468,6 +468,28 @@
       {"seten_set_memcms"_h, &Simulator::SimulateSetE},
       {"setge_set_memcms"_h, &Simulator::SimulateSetE},
       {"setgen_set_memcms"_h, &Simulator::SimulateSetE},
+      {"abs_32_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"abs_64_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"cnt_32_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"cnt_64_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"ctz_32_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"ctz_64_dp_1src"_h, &Simulator::VisitDataProcessing1Source},
+      {"smax_32_dp_2src"_h, &Simulator::SimulateSignedMinMax},
+      {"smax_64_dp_2src"_h, &Simulator::SimulateSignedMinMax},
+      {"smin_32_dp_2src"_h, &Simulator::SimulateSignedMinMax},
+      {"smin_64_dp_2src"_h, &Simulator::SimulateSignedMinMax},
+      {"smax_32_minmax_imm"_h, &Simulator::SimulateSignedMinMax},
+      {"smax_64_minmax_imm"_h, &Simulator::SimulateSignedMinMax},
+      {"smin_32_minmax_imm"_h, &Simulator::SimulateSignedMinMax},
+      {"smin_64_minmax_imm"_h, &Simulator::SimulateSignedMinMax},
+      {"umax_32_dp_2src"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umax_64_dp_2src"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umin_32_dp_2src"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umin_64_dp_2src"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umax_32u_minmax_imm"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umax_64u_minmax_imm"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umin_32u_minmax_imm"_h, &Simulator::SimulateUnsignedMinMax},
+      {"umin_64u_minmax_imm"_h, &Simulator::SimulateUnsignedMinMax},
   };
   return &form_to_visitor;
 }
@@ -5225,112 +5247,102 @@
 }
 
 
-#define PAUTH_MODES_REGISTER_CONTEXT(V) \
-  V(IA, kPACKeyIA, kInstructionPointer) \
-  V(IB, kPACKeyIB, kInstructionPointer) \
-  V(DA, kPACKeyDA, kDataPointer)        \
-  V(DB, kPACKeyDB, kDataPointer)
-
-#define PAUTH_MODES_ZERO_CONTEXT(V)      \
-  V(IZA, kPACKeyIA, kInstructionPointer) \
-  V(IZB, kPACKeyIB, kInstructionPointer) \
-  V(DZA, kPACKeyDA, kDataPointer)        \
-  V(DZB, kPACKeyDB, kDataPointer)
+#define PAUTH_MODES_REGISTER_CONTEXT(V)   \
+  V(i, a, kPACKeyIA, kInstructionPointer) \
+  V(i, b, kPACKeyIB, kInstructionPointer) \
+  V(d, a, kPACKeyDA, kDataPointer)        \
+  V(d, b, kPACKeyDB, kDataPointer)
 
 void Simulator::VisitDataProcessing1Source(const Instruction* instr) {
   unsigned dst = instr->GetRd();
   unsigned src = instr->GetRn();
+  Reg31Mode r31_pac = Reg31IsStackPointer;
 
-  switch (instr->Mask(DataProcessing1SourceMask)) {
-#define DEFINE_PAUTH_FUNCS(SUFFIX, KEY, D)                  \
-  case PAC##SUFFIX: {                                       \
-    uint64_t mod = ReadXRegister(src, Reg31IsStackPointer); \
-    uint64_t ptr = ReadXRegister(dst);                      \
-    WriteXRegister(dst, AddPAC(ptr, mod, KEY, D));          \
-    break;                                                  \
-  }                                                         \
-  case AUT##SUFFIX: {                                       \
-    uint64_t mod = ReadXRegister(src, Reg31IsStackPointer); \
-    uint64_t ptr = ReadXRegister(dst);                      \
-    WriteXRegister(dst, AuthPAC(ptr, mod, KEY, D));         \
-    break;                                                  \
+  switch (form_hash_) {
+#define DEFINE_PAUTH_FUNCS(SUF0, SUF1, KEY, D)      \
+  case "pac" #SUF0 "z" #SUF1 "_64z_dp_1src"_h:      \
+    VIXL_ASSERT(src == kZeroRegCode);               \
+    r31_pac = Reg31IsZeroRegister;                  \
+    VIXL_FALLTHROUGH();                             \
+  case "pac" #SUF0 #SUF1 "_64p_dp_1src"_h: {        \
+    uint64_t mod = ReadXRegister(src, r31_pac);     \
+    uint64_t ptr = ReadXRegister(dst);              \
+    WriteXRegister(dst, AddPAC(ptr, mod, KEY, D));  \
+    break;                                          \
+  }                                                 \
+  case "aut" #SUF0 "z" #SUF1 "_64z_dp_1src"_h:      \
+    VIXL_ASSERT(src == kZeroRegCode);               \
+    r31_pac = Reg31IsZeroRegister;                  \
+    VIXL_FALLTHROUGH();                             \
+  case "aut" #SUF0 #SUF1 "_64p_dp_1src"_h: {        \
+    uint64_t mod = ReadXRegister(src, r31_pac);     \
+    uint64_t ptr = ReadXRegister(dst);              \
+    WriteXRegister(dst, AuthPAC(ptr, mod, KEY, D)); \
+    break;                                          \
   }
-
     PAUTH_MODES_REGISTER_CONTEXT(DEFINE_PAUTH_FUNCS)
 #undef DEFINE_PAUTH_FUNCS
 
-#define DEFINE_PAUTH_FUNCS(SUFFIX, KEY, D)          \
-  case PAC##SUFFIX: {                               \
-    if (src != kZeroRegCode) {                      \
-      VIXL_UNIMPLEMENTED();                         \
-    }                                               \
-    uint64_t ptr = ReadXRegister(dst);              \
-    WriteXRegister(dst, AddPAC(ptr, 0x0, KEY, D));  \
-    break;                                          \
-  }                                                 \
-  case AUT##SUFFIX: {                               \
-    if (src != kZeroRegCode) {                      \
-      VIXL_UNIMPLEMENTED();                         \
-    }                                               \
-    uint64_t ptr = ReadXRegister(dst);              \
-    WriteXRegister(dst, AuthPAC(ptr, 0x0, KEY, D)); \
-    break;                                          \
-  }
-
-    PAUTH_MODES_ZERO_CONTEXT(DEFINE_PAUTH_FUNCS)
-#undef DEFINE_PAUTH_FUNCS
-
-    case XPACI:
-      if (src != kZeroRegCode) {
-        VIXL_UNIMPLEMENTED();
-      }
+    case "xpaci_64z_dp_1src"_h:
       WriteXRegister(dst, StripPAC(ReadXRegister(dst), kInstructionPointer));
       break;
-    case XPACD:
-      if (src != kZeroRegCode) {
-        VIXL_UNIMPLEMENTED();
-      }
+    case "xpacd_64z_dp_1src"_h:
       WriteXRegister(dst, StripPAC(ReadXRegister(dst), kDataPointer));
       break;
-    case RBIT_w:
+    case "rbit_32_dp_1src"_h:
       WriteWRegister(dst, ReverseBits(ReadWRegister(src)));
       break;
-    case RBIT_x:
+    case "rbit_64_dp_1src"_h:
       WriteXRegister(dst, ReverseBits(ReadXRegister(src)));
       break;
-    case REV16_w:
+    case "rev16_32_dp_1src"_h:
       WriteWRegister(dst, ReverseBytes(ReadWRegister(src), 1));
       break;
-    case REV16_x:
+    case "rev16_64_dp_1src"_h:
       WriteXRegister(dst, ReverseBytes(ReadXRegister(src), 1));
       break;
-    case REV_w:
+    case "rev_32_dp_1src"_h:
       WriteWRegister(dst, ReverseBytes(ReadWRegister(src), 2));
       break;
-    case REV32_x:
+    case "rev32_64_dp_1src"_h:
       WriteXRegister(dst, ReverseBytes(ReadXRegister(src), 2));
       break;
-    case REV_x:
+    case "rev_64_dp_1src"_h:
       WriteXRegister(dst, ReverseBytes(ReadXRegister(src), 3));
       break;
-    case CLZ_w:
+    case "clz_32_dp_1src"_h:
       WriteWRegister(dst, CountLeadingZeros(ReadWRegister(src)));
       break;
-    case CLZ_x:
+    case "clz_64_dp_1src"_h:
       WriteXRegister(dst, CountLeadingZeros(ReadXRegister(src)));
       break;
-    case CLS_w:
+    case "cls_32_dp_1src"_h:
       WriteWRegister(dst, CountLeadingSignBits(ReadWRegister(src)));
       break;
-    case CLS_x:
+    case "cls_64_dp_1src"_h:
       WriteXRegister(dst, CountLeadingSignBits(ReadXRegister(src)));
       break;
-    default:
-      VIXL_UNIMPLEMENTED();
+    case "abs_32_dp_1src"_h:
+      WriteWRegister(dst, Abs(ReadWRegister(src)));
+      break;
+    case "abs_64_dp_1src"_h:
+      WriteXRegister(dst, Abs(ReadXRegister(src)));
+      break;
+    case "cnt_32_dp_1src"_h:
+      WriteWRegister(dst, CountSetBits(ReadWRegister(src)));
+      break;
+    case "cnt_64_dp_1src"_h:
+      WriteXRegister(dst, CountSetBits(ReadXRegister(src)));
+      break;
+    case "ctz_32_dp_1src"_h:
+      WriteWRegister(dst, CountTrailingZeros(ReadWRegister(src)));
+      break;
+    case "ctz_64_dp_1src"_h:
+      WriteXRegister(dst, CountTrailingZeros(ReadXRegister(src)));
+      break;
   }
 }
 
-
 uint32_t Simulator::Poly32Mod2(unsigned n, uint64_t data, uint32_t poly) {
   VIXL_ASSERT((n > 32) && (n <= 64));
   for (unsigned i = (n - 1); i >= 32; i--) {
@@ -5507,6 +5519,81 @@
   WriteRegister(reg_size, instr->GetRd(), result);
 }
 
+void Simulator::SimulateSignedMinMax(const Instruction* instr) {
+  int32_t wn = ReadWRegister(instr->GetRn());
+  int32_t wm = ReadWRegister(instr->GetRm());
+  int64_t xn = ReadXRegister(instr->GetRn());
+  int64_t xm = ReadXRegister(instr->GetRm());
+  int32_t imm = instr->ExtractSignedBits(17, 10);
+  int dst = instr->GetRd();
+
+  switch (form_hash_) {
+    case "smax_64_minmax_imm"_h:
+    case "smin_64_minmax_imm"_h:
+      xm = imm;
+      break;
+    case "smax_32_minmax_imm"_h:
+    case "smin_32_minmax_imm"_h:
+      wm = imm;
+      break;
+  }
+
+  switch (form_hash_) {
+    case "smax_32_minmax_imm"_h:
+    case "smax_32_dp_2src"_h:
+      WriteWRegister(dst, std::max(wn, wm));
+      break;
+    case "smax_64_minmax_imm"_h:
+    case "smax_64_dp_2src"_h:
+      WriteXRegister(dst, std::max(xn, xm));
+      break;
+    case "smin_32_minmax_imm"_h:
+    case "smin_32_dp_2src"_h:
+      WriteWRegister(dst, std::min(wn, wm));
+      break;
+    case "smin_64_minmax_imm"_h:
+    case "smin_64_dp_2src"_h:
+      WriteXRegister(dst, std::min(xn, xm));
+      break;
+  }
+}
+
+void Simulator::SimulateUnsignedMinMax(const Instruction* instr) {
+  uint64_t xn = ReadXRegister(instr->GetRn());
+  uint64_t xm = ReadXRegister(instr->GetRm());
+  uint32_t imm = instr->ExtractBits(17, 10);
+  int dst = instr->GetRd();
+
+  switch (form_hash_) {
+    case "umax_64u_minmax_imm"_h:
+    case "umax_32u_minmax_imm"_h:
+    case "umin_64u_minmax_imm"_h:
+    case "umin_32u_minmax_imm"_h:
+      xm = imm;
+      break;
+  }
+
+  switch (form_hash_) {
+    case "umax_32u_minmax_imm"_h:
+    case "umax_32_dp_2src"_h:
+      xn &= 0xffff'ffff;
+      xm &= 0xffff'ffff;
+      VIXL_FALLTHROUGH();
+    case "umax_64u_minmax_imm"_h:
+    case "umax_64_dp_2src"_h:
+      WriteXRegister(dst, std::max(xn, xm));
+      break;
+    case "umin_32u_minmax_imm"_h:
+    case "umin_32_dp_2src"_h:
+      xn &= 0xffff'ffff;
+      xm &= 0xffff'ffff;
+      VIXL_FALLTHROUGH();
+    case "umin_64u_minmax_imm"_h:
+    case "umin_64_dp_2src"_h:
+      WriteXRegister(dst, std::min(xn, xm));
+      break;
+  }
+}
 
 void Simulator::VisitDataProcessing3Source(const Instruction* instr) {
   unsigned reg_size = instr->GetSixtyFourBits() ? kXRegSize : kWRegSize;
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 1321c4b..c5cc894 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -1397,6 +1397,8 @@
   void SimulateSetE(const Instruction* instr);
   void SimulateSetGP(const Instruction* instr);
   void SimulateSetGM(const Instruction* instr);
+  void SimulateSignedMinMax(const Instruction* instr);
+  void SimulateUnsignedMinMax(const Instruction* instr);
 
 
   // Integer register accessors.
diff --git a/src/cpu-features.h b/src/cpu-features.h
index 962ad52..97eb661 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -200,7 +200,8 @@
   V(kWFXT,                "WFXT",                   "wfxt")                    \
   /* Extended BFloat16 instructions                                         */ \
   V(kEBF16,               "EBF16",                  "ebf16")                   \
-  V(kSVE_EBF16,           "EBF16 (SVE)",            "sveebf16")
+  V(kSVE_EBF16,           "EBF16 (SVE)",            "sveebf16")                \
+  V(kCSSC,                "CSSC",                   "cssc")
 // clang-format on
 
 
diff --git a/src/utils-vixl.h b/src/utils-vixl.h
index b6c8455..7af9483 100644
--- a/src/utils-vixl.h
+++ b/src/utils-vixl.h
@@ -291,6 +291,17 @@
   return ~value + 1;
 }
 
+// An absolute operation for signed integers that is defined for results outside
+// the representable range. Specifically, Abs(MIN_INT) is MIN_INT.
+template <typename T>
+T Abs(T val) {
+  // TODO: this static assertion is for signed integer inputs, as that's the
+  // only type tested. However, the code should work for all numeric inputs.
+  // Remove the assertion and this comment when more tests are available.
+  VIXL_STATIC_ASSERT(std::is_signed<T>::value && std::is_integral<T>::value);
+  return ((val >= -std::numeric_limits<T>::max()) && (val < 0)) ? -val : val;
+}
+
 // Convert unsigned to signed numbers in a well-defined way (using two's
 // complement representations).
 inline int64_t RawbitsToInt64(uint64_t bits) {