Add support for AXFLAG and XAFLAG instructions.

Change-Id: I95867fa8adb6eeefe49dc5e0f56ed51b4d060a1d
diff --git a/src/aarch64/assembler-aarch64.cc b/src/aarch64/assembler-aarch64.cc
index 060681b..04bd226 100644
--- a/src/aarch64/assembler-aarch64.cc
+++ b/src/aarch64/assembler-aarch64.cc
@@ -2549,6 +2549,18 @@
 }
 
 
+void Assembler::axflag() {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kAXFlag));
+  Emit(AXFLAG);
+}
+
+
+void Assembler::xaflag() {
+  VIXL_ASSERT(CPUHas(CPUFeatures::kAXFlag));
+  Emit(XAFLAG);
+}
+
+
 void Assembler::clrex(int imm4) { Emit(CLREX | CRm(imm4)); }
 
 
diff --git a/src/aarch64/assembler-aarch64.h b/src/aarch64/assembler-aarch64.h
index d67824d..309b49a 100644
--- a/src/aarch64/assembler-aarch64.h
+++ b/src/aarch64/assembler-aarch64.h
@@ -2126,6 +2126,14 @@
   // Invert carry flag [Armv8.4].
   void cfinv();
 
+  // Convert floating-point condition flags from alternative format to Arm
+  // format [Armv8.5].
+  void xaflag();
+
+  // Convert floating-point condition flags from Arm format to alternative
+  // format [Armv8.5].
+  void axflag();
+
   // System instruction.
   void sys(int op1, int crn, int crm, int op2, const Register& xt = xzr);
 
diff --git a/src/aarch64/constants-aarch64.h b/src/aarch64/constants-aarch64.h
index 3f6dc58..16f2715 100644
--- a/src/aarch64/constants-aarch64.h
+++ b/src/aarch64/constants-aarch64.h
@@ -779,7 +779,9 @@
   SystemPStateFixed = 0xD5004000,
   SystemPStateFMask = 0xFFF8F000,
   SystemPStateMask  = 0xFFFFF0FF,
-  CFINV             = SystemPStateFixed | 0x0000001F
+  CFINV             = SystemPStateFixed | 0x0000001F,
+  XAFLAG            = SystemPStateFixed | 0x0000003F,
+  AXFLAG            = SystemPStateFixed | 0x0000005F
 };
 
 enum SystemHintOp {
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
index c0cd5a9..d3639d3 100644
--- a/src/aarch64/cpu-features-auditor-aarch64.cc
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -1086,6 +1086,10 @@
       case CFINV:
         scope.Record(CPUFeatures::kFlagM);
         break;
+      case AXFLAG:
+      case XAFLAG:
+        scope.Record(CPUFeatures::kAXFlag);
+        break;
     }
   }
 }
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index e976603..2161043 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -2069,6 +2069,14 @@
         mnemonic = "cfinv";
         form = NULL;
         break;
+      case AXFLAG:
+        mnemonic = "axflag";
+        form = NULL;
+        break;
+      case XAFLAG:
+        mnemonic = "xaflag";
+        form = NULL;
+        break;
     }
   } else if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) {
     switch (instr->Mask(SystemPAuthMask)) {
diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h
index a1de4dd..8051bd1 100644
--- a/src/aarch64/macro-assembler-aarch64.h
+++ b/src/aarch64/macro-assembler-aarch64.h
@@ -2007,6 +2007,16 @@
     SingleEmissionCheckScope guard(this);
     cfinv();
   }
+  void Axflag() {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    axflag();
+  }
+  void Xaflag() {
+    VIXL_ASSERT(allow_macro_instructions_);
+    SingleEmissionCheckScope guard(this);
+    xaflag();
+  }
   void Sys(int op1, int crn, int crm, int op2, const Register& rt = xzr) {
     VIXL_ASSERT(allow_macro_instructions_);
     SingleEmissionCheckScope guard(this);
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index c1d32a9..1c1d52e 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -3732,6 +3732,24 @@
       case CFINV:
         ReadNzcv().SetC(!ReadC());
         break;
+      case AXFLAG:
+        ReadNzcv().SetN(0);
+        ReadNzcv().SetZ(ReadNzcv().GetZ() | ReadNzcv().GetV());
+        ReadNzcv().SetC(ReadNzcv().GetC() & ~ReadNzcv().GetV());
+        ReadNzcv().SetV(0);
+        break;
+      case XAFLAG: {
+        // Can't set the flags in place due to the logical dependencies.
+        uint32_t n = (~ReadNzcv().GetC() & ~ReadNzcv().GetZ()) & 1;
+        uint32_t z = ReadNzcv().GetZ() & ReadNzcv().GetC();
+        uint32_t c = ReadNzcv().GetC() | ReadNzcv().GetZ();
+        uint32_t v = ~ReadNzcv().GetC() & ReadNzcv().GetZ();
+        ReadNzcv().SetN(n);
+        ReadNzcv().SetZ(z);
+        ReadNzcv().SetC(c);
+        ReadNzcv().SetV(v);
+        break;
+      }
     }
   } else if (instr->Mask(SystemPAuthFMask) == SystemPAuthFixed) {
     // Check BType allows PACI[AB]SP instructions.
diff --git a/src/cpu-features.h b/src/cpu-features.h
index 88b6914..fffef44 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -96,7 +96,9 @@
   /* Data-independent timing (for selected instructions).                   */ \
   V(kDIT,                 "DIT",                    "dit")                     \
   /* Branch target identification.                                          */ \
-  V(kBTI,                 "BTI",                    NULL)
+  V(kBTI,                 "BTI",                    NULL)                      \
+  /* Flag manipulation instructions: {AX,XA}FLAG                            */ \
+  V(kAXFlag,              "AXFlag",                 NULL)
 // clang-format on
 
 
diff --git a/test/aarch64/test-assembler-aarch64.cc b/test/aarch64/test-assembler-aarch64.cc
index 38fee97..8f0a87f 100644
--- a/test/aarch64/test-assembler-aarch64.cc
+++ b/test/aarch64/test-assembler-aarch64.cc
@@ -15058,6 +15058,70 @@
 }
 
 
+TEST(axflag_xaflag) {
+  // The AXFLAG and XAFLAG instructions are designed for converting the FP
+  // conditional flags from Arm format to an alternate format efficiently.
+  // There are only 4 cases which are relevant for this conversion but we test
+  // the behaviour for all 16 cases anyway. The 4 important cases are labelled
+  // below.
+  StatusFlags expected_x[16] = {NoFlag,
+                                ZFlag,
+                                CFlag,  // Greater than
+                                ZFlag,  // Unordered
+                                ZFlag,
+                                ZFlag,
+                                ZCFlag,  // Equal to
+                                ZFlag,
+                                NoFlag,  // Less than
+                                ZFlag,
+                                CFlag,
+                                ZFlag,
+                                ZFlag,
+                                ZFlag,
+                                ZCFlag,
+                                ZFlag};
+  StatusFlags expected_a[16] = {NFlag,  // Less than
+                                NFlag,
+                                CFlag,  // Greater than
+                                CFlag,
+                                CVFlag,  // Unordered
+                                CVFlag,
+                                ZCFlag,  // Equal to
+                                ZCFlag,
+                                NFlag,
+                                NFlag,
+                                CFlag,
+                                CFlag,
+                                CVFlag,
+                                CVFlag,
+                                ZCFlag,
+                                ZCFlag};
+
+  for (unsigned i = 0; i < 16; i++) {
+    SETUP_WITH_FEATURES(CPUFeatures::kAXFlag);
+
+    START();
+    __ Mov(x0, i << Flags_offset);
+    __ Msr(NZCV, x0);
+    __ Axflag();
+    __ Mrs(x1, NZCV);
+    __ Msr(NZCV, x0);
+    __ Xaflag();
+    __ Mrs(x2, NZCV);
+    END();
+
+#ifdef VIXL_INCLUDE_SIMULATOR_AARCH64
+    RUN();
+    ASSERT_EQUAL_32(expected_x[i], w1);
+    ASSERT_EQUAL_32(expected_a[i], w2);
+#else
+    USE(expected_x, expected_a);
+#endif  // VIXL_INCLUDE_SIMULATOR_AARCH64
+    TEARDOWN();
+  }
+}
+
+
 TEST(system_msr) {
   // All FPCR fields that must be implemented: AHP, DN, FZ, RMode
   const uint64_t fpcr_core = 0x07c00000;
diff --git a/test/aarch64/test-disasm-aarch64.cc b/test/aarch64/test-disasm-aarch64.cc
index 0d7642f..5fb98d2 100644
--- a/test/aarch64/test-disasm-aarch64.cc
+++ b/test/aarch64/test-disasm-aarch64.cc
@@ -3212,6 +3212,8 @@
   SETUP();
 
   COMPARE(cfinv(), "cfinv");
+  COMPARE(axflag(), "axflag");
+  COMPARE(xaflag(), "xaflag");
 
   CLEANUP();
 }
diff --git a/test/test-api.cc b/test/test-api.cc
index f033271..d3a6201 100644
--- a/test/test-api.cc
+++ b/test/test-api.cc
@@ -392,7 +392,7 @@
       // Armv8.4
       "RCpc (imm), FlagM, USCAT, FHM, DIT, "
       // Armv8.5
-      "BTI",
+      "BTI, AXFlag",
       CPUFeatures::All());
 }