Check CPU features in the Simulator and Disassembler.

This is implemented using a new decoder visitor, CPUFeaturesAuditor. This
visitor holds a set of features required by the last-decoded instruction, a set
of features seen since construction (or 'ResetSeenFeatures()'), and a set of
features that are considered to be available. This visitor can be used by
itself, but its main purpose is to implement Simulator and Disassembler support
for CPUFeatures:

- The Simulator creates and attaches this visitor automatically, much like it
  does for the disassembly trace, so existing simulation use-cases require no
  special user interaction. The Simulator checks that each encountered
  instruction only uses available features.

  The Simulator also supports dynamic configuration of features, in a manner
  similar to the trace controls.

- The Disassembler does not directly interact with the CPUFeaturesAuditor, but
  the PrintDisassembler can be configured to annotate each instruction with
  required features that are missing from GetAvailableFeatures().

  The PrintDisassembler is used to implement Simulator trace, so a simulation
  failure due to missing feature support will be immediately preceeded by a
  corresponding PrintDisassembler annotation (when trace is enabled).

  No existing test uses the PrintDisassembler in this way, so this patch also
  adds 'TRACE_' tests to check that the features are appropriately formatted.

Change-Id: I3dc0e22f0a5bd3287ba1870539e018ff4a768a4b
diff --git a/src/aarch64/cpu-features-auditor-aarch64.cc b/src/aarch64/cpu-features-auditor-aarch64.cc
new file mode 100644
index 0000000..68fae51
--- /dev/null
+++ b/src/aarch64/cpu-features-auditor-aarch64.cc
@@ -0,0 +1,813 @@
+// Copyright 2018, VIXL authors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of Arm Limited nor the names of its contributors may be
+//     used to endorse or promote products derived from this software without
+//     specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "cpu-features.h"
+#include "globals-vixl.h"
+#include "utils-vixl.h"
+#include "decoder-aarch64.h"
+
+#include "cpu-features-auditor-aarch64.h"
+
+namespace vixl {
+namespace aarch64 {
+
+// Every instruction must update last_instruction_, even if only to clear it,
+// and every instruction must also update seen_ once it has been fully handled.
+// This scope makes that simple, and allows early returns in the decode logic.
+class CPUFeaturesAuditor::RecordInstructionFeaturesScope {
+ public:
+  explicit RecordInstructionFeaturesScope(CPUFeaturesAuditor* auditor)
+      : auditor_(auditor) {
+    auditor_->last_instruction_ = CPUFeatures::None();
+  }
+  ~RecordInstructionFeaturesScope() {
+    auditor_->seen_.Combine(auditor_->last_instruction_);
+  }
+
+  void Record(CPUFeatures::Feature feature0,
+              CPUFeatures::Feature feature1 = CPUFeatures::kNone,
+              CPUFeatures::Feature feature2 = CPUFeatures::kNone,
+              CPUFeatures::Feature feature3 = CPUFeatures::kNone) {
+    auditor_->last_instruction_.Combine(feature0, feature1, feature2, feature3);
+  }
+
+  // If exactly one of a or b is known to be available, record it. Otherwise,
+  // record both. This is intended for encodings that can be provided by two
+  // different features.
+  void RecordOneOrBothOf(CPUFeatures::Feature a, CPUFeatures::Feature b) {
+    bool hint_a = auditor_->available_.Has(a);
+    bool hint_b = auditor_->available_.Has(b);
+    if (hint_a && !hint_b) {
+      Record(a);
+    } else if (hint_b && !hint_a) {
+      Record(b);
+    } else {
+      Record(a, b);
+    }
+  }
+
+ private:
+  CPUFeaturesAuditor* auditor_;
+};
+
+void CPUFeaturesAuditor::LoadStoreHelper(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  switch (instr->Mask(LoadStoreMask)) {
+    case LDR_b:
+    case LDR_q:
+    case STR_b:
+    case STR_q:
+      scope.Record(CPUFeatures::kNEON);
+      return;
+    case LDR_h:
+    case LDR_s:
+    case LDR_d:
+    case STR_h:
+    case STR_s:
+    case STR_d:
+      scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON);
+      return;
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::LoadStorePairHelper(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  switch (instr->Mask(LoadStorePairMask)) {
+    case LDP_q:
+    case STP_q:
+      scope.Record(CPUFeatures::kNEON);
+      return;
+    case LDP_s:
+    case LDP_d:
+    case STP_s:
+    case STP_d: {
+      scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON);
+      return;
+    }
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitAddSubExtended(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitAddSubImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitAddSubShifted(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitAddSubWithCarry(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitBitfield(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitCompareBranch(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitConditionalBranch(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitConditionalCompareImmediate(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitConditionalCompareRegister(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitConditionalSelect(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitCrypto2RegSHA(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitCrypto3RegSHA(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitCryptoAES(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitDataProcessing1Source(const Instruction* instr) {
+  USE(instr);
+  RecordInstructionFeaturesScope scope(this);
+}
+
+void CPUFeaturesAuditor::VisitDataProcessing2Source(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  switch (instr->Mask(DataProcessing2SourceMask)) {
+    case CRC32B:
+    case CRC32H:
+    case CRC32W:
+    case CRC32X:
+    case CRC32CB:
+    case CRC32CH:
+    case CRC32CW:
+    case CRC32CX:
+      scope.Record(CPUFeatures::kCRC32);
+      return;
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitDataProcessing3Source(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitException(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitExtract(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPCompare(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPConditionalCompare(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPConditionalSelect(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPDataProcessing1Source(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  if (instr->Mask(FPDataProcessing1SourceMask) == FMOV_h) {
+    scope.Record(CPUFeatures::kFPHalf);
+  }
+}
+
+void CPUFeaturesAuditor::VisitFPDataProcessing2Source(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPDataProcessing3Source(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPFixedPointConvert(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitFPImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  if (instr->Mask(FPImmediateMask) == FMOV_h_imm) {
+    scope.Record(CPUFeatures::kFPHalf);
+  }
+}
+
+void CPUFeaturesAuditor::VisitFPIntegerConvert(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require FP.
+  scope.Record(CPUFeatures::kFP);
+  switch (instr->Mask(FPIntegerConvertMask)) {
+    case FMOV_hw:
+    case FMOV_wh:
+    case FMOV_xh:
+    case FMOV_hx:
+      scope.Record(CPUFeatures::kFPHalf);
+      return;
+    case FMOV_d1_x:
+    case FMOV_x_d1:
+      scope.Record(CPUFeatures::kNEON);
+      return;
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitLoadLiteral(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  switch (instr->Mask(LoadLiteralMask)) {
+    case LDR_s_lit:
+    case LDR_d_lit:
+      scope.RecordOneOrBothOf(CPUFeatures::kFP, CPUFeatures::kNEON);
+      return;
+    case LDR_q_lit:
+      scope.Record(CPUFeatures::kNEON);
+      return;
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitLoadStoreExclusive(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  switch (instr->Mask(LoadStoreExclusiveMask)) {
+    case CAS_w:
+    case CASA_w:
+    case CASL_w:
+    case CASAL_w:
+    case CAS_x:
+    case CASA_x:
+    case CASL_x:
+    case CASAL_x:
+    case CASB:
+    case CASAB:
+    case CASLB:
+    case CASALB:
+    case CASH:
+    case CASAH:
+    case CASLH:
+    case CASALH:
+    case CASP_w:
+    case CASPA_w:
+    case CASPL_w:
+    case CASPAL_w:
+    case CASP_x:
+    case CASPA_x:
+    case CASPL_x:
+    case CASPAL_x:
+      scope.Record(CPUFeatures::kAtomics);
+      return;
+    case STLLRB:
+    case LDLARB:
+    case STLLRH:
+    case LDLARH:
+    case STLLR_w:
+    case LDLAR_w:
+    case STLLR_x:
+    case LDLAR_x:
+      scope.Record(CPUFeatures::kLORegions);
+      return;
+    default:
+      // No special CPU features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePairNonTemporal(
+    const Instruction* instr) {
+  LoadStorePairHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePairOffset(const Instruction* instr) {
+  LoadStorePairHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePairPostIndex(const Instruction* instr) {
+  LoadStorePairHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePairPreIndex(const Instruction* instr) {
+  LoadStorePairHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePostIndex(const Instruction* instr) {
+  LoadStoreHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStorePreIndex(const Instruction* instr) {
+  LoadStoreHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStoreRegisterOffset(
+    const Instruction* instr) {
+  LoadStoreHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStoreUnscaledOffset(
+    const Instruction* instr) {
+  LoadStoreHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLoadStoreUnsignedOffset(
+    const Instruction* instr) {
+  LoadStoreHelper(instr);
+}
+
+void CPUFeaturesAuditor::VisitLogicalImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitLogicalShifted(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitMoveWideImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEON2RegMisc(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEON2RegMiscFPMask)) {
+    case NEON_FABS:
+    case NEON_FNEG:
+    case NEON_FSQRT:
+    case NEON_FCVTL:
+    case NEON_FCVTN:
+    case NEON_FCVTXN:
+    case NEON_FRINTI:
+    case NEON_FRINTX:
+    case NEON_FRINTA:
+    case NEON_FRINTM:
+    case NEON_FRINTN:
+    case NEON_FRINTP:
+    case NEON_FRINTZ:
+    case NEON_FCVTNS:
+    case NEON_FCVTNU:
+    case NEON_FCVTPS:
+    case NEON_FCVTPU:
+    case NEON_FCVTMS:
+    case NEON_FCVTMU:
+    case NEON_FCVTZS:
+    case NEON_FCVTZU:
+    case NEON_FCVTAS:
+    case NEON_FCVTAU:
+    case NEON_SCVTF:
+    case NEON_UCVTF:
+    case NEON_FRSQRTE:
+    case NEON_FRECPE:
+    case NEON_FCMGT_zero:
+    case NEON_FCMGE_zero:
+    case NEON_FCMEQ_zero:
+    case NEON_FCMLE_zero:
+    case NEON_FCMLT_zero:
+      scope.Record(CPUFeatures::kFP);
+      return;
+    default:
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEON3Different(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEON3Same(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  if (instr->Mask(NEON3SameFPFMask) == NEON3SameFPFixed) {
+    scope.Record(CPUFeatures::kFP);
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEON3SameExtra(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  if ((instr->Mask(NEON3SameExtraFCMLAMask) == NEON_FCMLA) ||
+      (instr->Mask(NEON3SameExtraFCADDMask) == NEON_FCADD)) {
+    scope.Record(CPUFeatures::kFP, CPUFeatures::kFcma);
+  } else {
+    switch (instr->Mask(NEON3SameExtraMask)) {
+      case NEON_SDOT:
+      case NEON_UDOT:
+        scope.Record(CPUFeatures::kDotProduct);
+        return;
+      case NEON_SQRDMLAH:
+      case NEON_SQRDMLSH:
+        scope.Record(CPUFeatures::kRDM);
+        return;
+      default:
+        // No additional features.
+        return;
+    }
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONAcrossLanes(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  if (instr->Mask(NEONAcrossLanesFPFMask) == NEONAcrossLanesFPFixed) {
+    scope.Record(CPUFeatures::kFP);
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONByIndexedElement(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONByIndexedElementMask)) {
+    case NEON_SDOT_byelement:
+    case NEON_UDOT_byelement:
+      scope.Record(CPUFeatures::kDotProduct);
+      return;
+    case NEON_SQRDMLAH_byelement:
+    case NEON_SQRDMLSH_byelement:
+      scope.Record(CPUFeatures::kRDM);
+      return;
+    default:
+      // Fall through to check other FP instructions.
+      break;
+  }
+  switch (instr->Mask(NEONByIndexedElementFPMask)) {
+    case NEON_FMLA_byelement:
+    case NEON_FMLS_byelement:
+    case NEON_FMUL_byelement:
+    case NEON_FMULX_byelement:
+      scope.Record(CPUFeatures::kFP);
+      return;
+    default:
+      switch (instr->Mask(NEONByIndexedElementFPComplexMask)) {
+        case NEON_FCMLA_byelement:
+          scope.Record(CPUFeatures::kFP, CPUFeatures::kFcma);
+          return;
+      }
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONCopy(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONExtract(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONLoadStoreMultiStruct(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONLoadStoreMultiStructPostIndex(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONLoadStoreSingleStruct(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONLoadStoreSingleStructPostIndex(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONModifiedImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  if (instr->GetNEONCmode() == 0xf) {
+    // FMOV (vector, immediate), double-, single- or half-precision.
+    scope.Record(CPUFeatures::kFP);
+    if (instr->ExtractBit(11)) scope.Record(CPUFeatures::kNEONHalf);
+  }
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONPerm(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONScalar2RegMisc(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONScalar2RegMiscFPMask)) {
+    case NEON_FRECPE_scalar:
+    case NEON_FRECPX_scalar:
+    case NEON_FRSQRTE_scalar:
+    case NEON_FCMGT_zero_scalar:
+    case NEON_FCMGE_zero_scalar:
+    case NEON_FCMEQ_zero_scalar:
+    case NEON_FCMLE_zero_scalar:
+    case NEON_FCMLT_zero_scalar:
+    case NEON_SCVTF_scalar:
+    case NEON_UCVTF_scalar:
+    case NEON_FCVTNS_scalar:
+    case NEON_FCVTNU_scalar:
+    case NEON_FCVTPS_scalar:
+    case NEON_FCVTPU_scalar:
+    case NEON_FCVTMS_scalar:
+    case NEON_FCVTMU_scalar:
+    case NEON_FCVTZS_scalar:
+    case NEON_FCVTZU_scalar:
+    case NEON_FCVTAS_scalar:
+    case NEON_FCVTAU_scalar:
+    case NEON_FCVTXN_scalar:
+      scope.Record(CPUFeatures::kFP);
+      return;
+    default:
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONScalar3Diff(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONScalar3Same(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  if (instr->Mask(NEONScalar3SameFPFMask) == NEONScalar3SameFPFixed) {
+    scope.Record(CPUFeatures::kFP);
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONScalar3SameExtra(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON and RDM.
+  scope.Record(CPUFeatures::kNEON, CPUFeatures::kRDM);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONScalarByIndexedElement(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONScalarByIndexedElementMask)) {
+    case NEON_SQDMULL_byelement_scalar:
+    case NEON_SQDMLAL_byelement_scalar:
+    case NEON_SQDMLSL_byelement_scalar:
+    case NEON_SQDMULH_byelement_scalar:
+    case NEON_SQRDMULH_byelement_scalar:
+      // No additional features.
+      return;
+    case NEON_SQRDMLAH_byelement_scalar:
+    case NEON_SQRDMLSH_byelement_scalar:
+      scope.Record(CPUFeatures::kRDM);
+      return;
+    default:
+      // FMUL, FMLA, FMLS, FMULX
+      scope.Record(CPUFeatures::kFP);
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONScalarCopy(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitNEONScalarPairwise(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONScalarPairwiseMask)) {
+    case NEON_FADDP_scalar:
+    case NEON_FMAXP_scalar:
+    case NEON_FMAXNMP_scalar:
+    case NEON_FMINP_scalar:
+    case NEON_FMINNMP_scalar:
+      scope.Record(CPUFeatures::kFP);
+      return;
+    default:
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONScalarShiftImmediate(
+    const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONScalarShiftImmediateMask)) {
+    case NEON_FCVTZS_imm_scalar:
+    case NEON_FCVTZU_imm_scalar:
+    case NEON_SCVTF_imm_scalar:
+    case NEON_UCVTF_imm_scalar:
+      scope.Record(CPUFeatures::kFP);
+      // If immh is 0b001x then the data type is FP16, and requires kNEONHalf.
+      if ((instr->GetImmNEONImmh() & 0xe) == 0x2) {
+        scope.Record(CPUFeatures::kNEONHalf);
+      }
+      return;
+    default:
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONShiftImmediate(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  switch (instr->Mask(NEONShiftImmediateMask)) {
+    case NEON_SCVTF_imm:
+    case NEON_UCVTF_imm:
+    case NEON_FCVTZS_imm:
+    case NEON_FCVTZU_imm:
+      scope.Record(CPUFeatures::kFP);
+      // If immh is 0b001x then the data type is FP16, and requires kNEONHalf.
+      if ((instr->GetImmNEONImmh() & 0xe) == 0x2) {
+        scope.Record(CPUFeatures::kNEONHalf);
+      }
+      return;
+    default:
+      // No additional features.
+      return;
+  }
+}
+
+void CPUFeaturesAuditor::VisitNEONTable(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  // All of these instructions require NEON.
+  scope.Record(CPUFeatures::kNEON);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitPCRelAddressing(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitSystem(const Instruction* instr) {
+  USE(instr);
+  RecordInstructionFeaturesScope scope(this);
+}
+
+void CPUFeaturesAuditor::VisitTestBranch(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitUnallocated(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitUnconditionalBranch(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+void CPUFeaturesAuditor::VisitUnconditionalBranchToRegister(
+    const Instruction* instr) {
+  USE(instr);
+  RecordInstructionFeaturesScope scope(this);
+}
+
+void CPUFeaturesAuditor::VisitUnimplemented(const Instruction* instr) {
+  RecordInstructionFeaturesScope scope(this);
+  USE(instr);
+}
+
+
+}  // namespace aarch64
+}  // namespace vixl
diff --git a/src/aarch64/cpu-features-auditor-aarch64.h b/src/aarch64/cpu-features-auditor-aarch64.h
new file mode 100644
index 0000000..23aec06
--- /dev/null
+++ b/src/aarch64/cpu-features-auditor-aarch64.h
@@ -0,0 +1,125 @@
+// Copyright 2018, VIXL authors
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+//   * Redistributions of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//   * Redistributions in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//   * Neither the name of Arm Limited nor the names of its contributors may be
+//     used to endorse or promote products derived from this software without
+//     specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND
+// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef VIXL_AARCH64_CPU_FEATURES_AUDITOR_AARCH64_H_
+#define VIXL_AARCH64_CPU_FEATURES_AUDITOR_AARCH64_H_
+
+#include <iostream>
+
+#include "cpu-features.h"
+#include "decoder-aarch64.h"
+
+namespace vixl {
+namespace aarch64 {
+
+// This visitor records the CPU features that each decoded instruction requires.
+// It provides:
+//  - the set of CPU features required by the most recently decoded instruction,
+//  - a cumulative set of encountered CPU features,
+//  - an optional list of 'available' CPU features.
+//
+// Primarily, this allows the Disassembler and Simulator to share the same CPU
+// features logic. However, it can be used standalone to scan code blocks for
+// CPU features.
+class CPUFeaturesAuditor : public DecoderVisitor {
+ public:
+  // Construction arguments:
+  //   - If a decoder is specified, the CPUFeaturesAuditor automatically
+  //     registers itself as a visitor. Otherwise, this can be done manually.
+  //
+  //   - If an `available` features list is provided, it is used as a hint in
+  //     cases where instructions may be provided by multiple separate features.
+  //     An example of this is FP&SIMD loads and stores: some of these are used
+  //     in both FP and integer SIMD code. If exactly one of those features is
+  //     in `available` when one of these instructions is encountered, then the
+  //     auditor will record that feature. Otherwise, it will record _both_
+  //     features.
+  explicit CPUFeaturesAuditor(
+      Decoder* decoder, const CPUFeatures& available = CPUFeatures::None())
+      : available_(available), decoder_(decoder) {
+    if (decoder_ != NULL) decoder_->AppendVisitor(this);
+  }
+
+  explicit CPUFeaturesAuditor(
+      const CPUFeatures& available = CPUFeatures::None())
+      : available_(available), decoder_(NULL) {}
+
+  virtual ~CPUFeaturesAuditor() {
+    if (decoder_ != NULL) decoder_->RemoveVisitor(this);
+  }
+
+  void ResetSeenFeatures() {
+    seen_ = CPUFeatures::None();
+    last_instruction_ = CPUFeatures::None();
+  }
+
+  // Query or set available CPUFeatures.
+  const CPUFeatures& GetAvailableFeatures() const { return available_; }
+  void SetAvailableFeatures(const CPUFeatures& available) {
+    available_ = available;
+  }
+
+  // Query CPUFeatures seen since construction (or the last call to `Reset()`).
+  const CPUFeatures& GetSeenFeatures() const { return seen_; }
+
+  // Query CPUFeatures from the last instruction visited by this auditor.
+  const CPUFeatures& GetInstructionFeatures() const {
+    return last_instruction_;
+  }
+
+  bool InstructionIsAvailable() const {
+    return available_.Has(last_instruction_);
+  }
+
+  // The common CPUFeatures interface operates on the available_ list.
+  CPUFeatures* GetCPUFeatures() { return &available_; }
+  void SetCPUFeatures(const CPUFeatures& available) {
+    SetAvailableFeatures(available);
+  }
+
+// Declare all Visitor functions.
+#define DECLARE(A) \
+  virtual void Visit##A(const Instruction* instr) VIXL_OVERRIDE;
+  VISITOR_LIST(DECLARE)
+#undef DECLARE
+
+ private:
+  class RecordInstructionFeaturesScope;
+
+  void LoadStoreHelper(const Instruction* instr);
+  void LoadStorePairHelper(const Instruction* instr);
+
+  CPUFeatures seen_;
+  CPUFeatures last_instruction_;
+  CPUFeatures available_;
+
+  Decoder* decoder_;
+};
+
+}  // namespace aarch64
+}  // namespace vixl
+
+#endif  // VIXL_AARCH64_CPU_FEATURES_AUDITOR_AARCH64_H_
diff --git a/src/aarch64/disasm-aarch64.cc b/src/aarch64/disasm-aarch64.cc
index 100b23e..b8865d0 100644
--- a/src/aarch64/disasm-aarch64.cc
+++ b/src/aarch64/disasm-aarch64.cc
@@ -25,6 +25,7 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <cstdlib>
+#include <sstream>
 
 #include "disasm-aarch64.h"
 #include "utils-aarch64.h"
@@ -32,6 +33,7 @@
 namespace vixl {
 namespace aarch64 {
 
+
 Disassembler::Disassembler() {
   buffer_size_ = 256;
   buffer_ = reinterpret_cast<char *>(malloc(buffer_size_));
@@ -5175,11 +5177,38 @@
 
 
 void PrintDisassembler::ProcessOutput(const Instruction *instr) {
-  fprintf(stream_,
-          "0x%016" PRIx64 "  %08" PRIx32 "\t\t%s\n",
-          reinterpret_cast<uint64_t>(instr),
-          instr->GetInstructionBits(),
-          GetOutput());
+  int bytes_printed = fprintf(stream_,
+                              "0x%016" PRIx64 "  %08" PRIx32 "\t\t%s",
+                              reinterpret_cast<uint64_t>(instr),
+                              instr->GetInstructionBits(),
+                              GetOutput());
+  if (cpu_features_auditor_ != NULL) {
+    CPUFeatures needs = cpu_features_auditor_->GetInstructionFeatures();
+    needs.Remove(cpu_features_auditor_->GetAvailableFeatures());
+    if (needs != CPUFeatures::None()) {
+      // Try to align annotations. This value is arbitrary, but based on looking
+      // good with most instructions. Note that, for historical reasons, the
+      // disassembly itself is printed with tab characters, so bytes_printed is
+      // _not_ equivalent to the number of occupied screen columns. However, the
+      // prefix before the tabs is always the same length, so the annotation
+      // indentation does not change from one line to the next.
+      const int indent_to = 70;
+      // Always allow some space between the instruction and the annotation.
+      const int min_pad = 2;
+
+      int pad = std::max(min_pad, (indent_to - bytes_printed));
+      fprintf(stream_, "%*s", pad, "");
+
+      std::stringstream features;
+      features << needs;
+      fprintf(stream_,
+              "%s%s%s",
+              cpu_features_prefix_,
+              features.str().c_str(),
+              cpu_features_suffix_);
+    }
+  }
+  fprintf(stream_, "\n");
 }
 
 }  // namespace aarch64
diff --git a/src/aarch64/disasm-aarch64.h b/src/aarch64/disasm-aarch64.h
index 0ae9331..825ca27 100644
--- a/src/aarch64/disasm-aarch64.h
+++ b/src/aarch64/disasm-aarch64.h
@@ -30,6 +30,7 @@
 #include "../globals-vixl.h"
 #include "../utils-vixl.h"
 
+#include "cpu-features-auditor-aarch64.h"
 #include "decoder-aarch64.h"
 #include "instructions-aarch64.h"
 #include "operands-aarch64.h"
@@ -168,12 +169,40 @@
 
 class PrintDisassembler : public Disassembler {
  public:
-  explicit PrintDisassembler(FILE* stream) : stream_(stream) {}
+  explicit PrintDisassembler(FILE* stream)
+      : cpu_features_auditor_(NULL),
+        cpu_features_prefix_("// Needs: "),
+        cpu_features_suffix_(""),
+        stream_(stream) {}
   void DisassembleBuffer(const Instruction* start, uint64_t size);
 
+  // If a CPUFeaturesAuditor is specified, it will be used to annotate
+  // disassembly. The CPUFeaturesAuditor is expected to visit the instructions
+  // _before_ the disassembler, such that the CPUFeatures information is
+  // available when the disassembler is called.
+  void RegisterCPUFeaturesAuditor(CPUFeaturesAuditor const* auditor) {
+    cpu_features_auditor_ = auditor;
+  }
+
+  // Set the prefix to appear before the CPU features annotations.
+  void SetCPUFeaturesPrefix(const char* prefix) {
+    VIXL_ASSERT(prefix != NULL);
+    cpu_features_prefix_ = prefix;
+  }
+
+  // Set the suffix to appear after the CPU features annotations.
+  void SetCPUFeaturesSuffix(const char* suffix) {
+    VIXL_ASSERT(suffix != NULL);
+    cpu_features_suffix_ = suffix;
+  }
+
  protected:
   virtual void ProcessOutput(const Instruction* instr) VIXL_OVERRIDE;
 
+  CPUFeaturesAuditor const* cpu_features_auditor_;
+  const char* cpu_features_prefix_;
+  const char* cpu_features_suffix_;
+
  private:
   FILE* stream_;
 };
diff --git a/src/aarch64/macro-assembler-aarch64.cc b/src/aarch64/macro-assembler-aarch64.cc
index 15cfe84..f13d15a 100644
--- a/src/aarch64/macro-assembler-aarch64.cc
+++ b/src/aarch64/macro-assembler-aarch64.cc
@@ -2753,6 +2753,79 @@
 }
 
 
+void MacroAssembler::SetSimulatorCPUFeatures(const CPUFeatures& features) {
+  ConfigureSimulatorCPUFeaturesHelper(features, kSetCPUFeaturesOpcode);
+}
+
+
+void MacroAssembler::EnableSimulatorCPUFeatures(const CPUFeatures& features) {
+  ConfigureSimulatorCPUFeaturesHelper(features, kEnableCPUFeaturesOpcode);
+}
+
+
+void MacroAssembler::DisableSimulatorCPUFeatures(const CPUFeatures& features) {
+  ConfigureSimulatorCPUFeaturesHelper(features, kDisableCPUFeaturesOpcode);
+}
+
+
+void MacroAssembler::ConfigureSimulatorCPUFeaturesHelper(
+    const CPUFeatures& features, DebugHltOpcode action) {
+  VIXL_ASSERT(allow_macro_instructions_);
+  VIXL_ASSERT(generate_simulator_code_);
+
+  typedef ConfigureCPUFeaturesElementType ElementType;
+  VIXL_ASSERT(CPUFeatures::kNumberOfFeatures <=
+              std::numeric_limits<ElementType>::max());
+
+  size_t count = features.Count();
+
+  size_t preamble_length = kConfigureCPUFeaturesListOffset;
+  size_t list_length = (count + 1) * sizeof(ElementType);
+  size_t padding_length = AlignUp(list_length, kInstructionSize) - list_length;
+
+  size_t total_length = preamble_length + list_length + padding_length;
+
+  // Check the overall code size as well as the size of each component.
+  ExactAssemblyScope guard_total(this, total_length);
+
+  {  // Preamble: the opcode itself.
+    ExactAssemblyScope guard_preamble(this, preamble_length);
+    hlt(action);
+  }
+  {  // A kNone-terminated list of features.
+    ExactAssemblyScope guard_list(this, list_length);
+    for (CPUFeatures::const_iterator it = features.begin();
+         it != features.end();
+         ++it) {
+      dc(static_cast<ElementType>(*it));
+    }
+    dc(static_cast<ElementType>(CPUFeatures::kNone));
+  }
+  {  // Padding for instruction alignment.
+    ExactAssemblyScope guard_padding(this, padding_length);
+    for (size_t size = 0; size < padding_length; size += sizeof(ElementType)) {
+      // The exact value is arbitrary.
+      dc(static_cast<ElementType>(CPUFeatures::kNone));
+    }
+  }
+}
+
+void MacroAssembler::SaveSimulatorCPUFeatures() {
+  VIXL_ASSERT(allow_macro_instructions_);
+  VIXL_ASSERT(generate_simulator_code_);
+  SingleEmissionCheckScope guard(this);
+  hlt(kSaveCPUFeaturesOpcode);
+}
+
+
+void MacroAssembler::RestoreSimulatorCPUFeatures() {
+  VIXL_ASSERT(allow_macro_instructions_);
+  VIXL_ASSERT(generate_simulator_code_);
+  SingleEmissionCheckScope guard(this);
+  hlt(kRestoreCPUFeaturesOpcode);
+}
+
+
 void UseScratchRegisterScope::Open(MacroAssembler* masm) {
   VIXL_ASSERT(masm_ == NULL);
   VIXL_ASSERT(masm != NULL);
diff --git a/src/aarch64/macro-assembler-aarch64.h b/src/aarch64/macro-assembler-aarch64.h
index 0a4d02e..1dc0c0e 100644
--- a/src/aarch64/macro-assembler-aarch64.h
+++ b/src/aarch64/macro-assembler-aarch64.h
@@ -3172,6 +3172,14 @@
   // the output data.
   void AnnotateInstrumentation(const char* marker_name);
 
+  // Enable or disable CPU features dynamically. This mechanism allows users to
+  // strictly check the use of CPU features in different regions of code.
+  void SetSimulatorCPUFeatures(const CPUFeatures& features);
+  void EnableSimulatorCPUFeatures(const CPUFeatures& features);
+  void DisableSimulatorCPUFeatures(const CPUFeatures& features);
+  void SaveSimulatorCPUFeatures();
+  void RestoreSimulatorCPUFeatures();
+
   LiteralPool* GetLiteralPool() { return &literal_pool_; }
 
 // Support for simulated runtime calls.
@@ -3317,6 +3325,9 @@
                                                 GetCursorOffset());
   }
 
+  void ConfigureSimulatorCPUFeaturesHelper(const CPUFeatures& features,
+                                           DebugHltOpcode action);
+
   // Tell whether any of the macro instruction can be used. When false the
   // MacroAssembler will assert if a method which can emit a variable number
   // of instructions is called.
@@ -3548,6 +3559,51 @@
   }
 };
 
+
+// Like CPUFeaturesScope, but also generate Simulation pseudo-instructions to
+// control a Simulator's CPUFeatures dynamically.
+//
+// One major difference from CPUFeaturesScope is that this scope cannot offer
+// a writable "CPUFeatures* GetCPUFeatures()", because every write to the
+// features needs a corresponding macro instruction.
+class SimulationCPUFeaturesScope {
+ public:
+  explicit SimulationCPUFeaturesScope(
+      MacroAssembler* masm,
+      CPUFeatures::Feature feature0 = CPUFeatures::kNone,
+      CPUFeatures::Feature feature1 = CPUFeatures::kNone,
+      CPUFeatures::Feature feature2 = CPUFeatures::kNone,
+      CPUFeatures::Feature feature3 = CPUFeatures::kNone)
+      : masm_(masm),
+        cpu_features_scope_(masm, feature0, feature1, feature2, feature3) {
+    masm_->SaveSimulatorCPUFeatures();
+    masm_->EnableSimulatorCPUFeatures(
+        CPUFeatures(feature0, feature1, feature2, feature3));
+  }
+
+  SimulationCPUFeaturesScope(MacroAssembler* masm, const CPUFeatures& other)
+      : masm_(masm), cpu_features_scope_(masm, other) {
+    masm_->SaveSimulatorCPUFeatures();
+    masm_->EnableSimulatorCPUFeatures(other);
+  }
+
+  ~SimulationCPUFeaturesScope() { masm_->RestoreSimulatorCPUFeatures(); }
+
+  const CPUFeatures* GetCPUFeatures() const {
+    return cpu_features_scope_.GetCPUFeatures();
+  }
+
+  void SetCPUFeatures(const CPUFeatures& cpu_features) {
+    cpu_features_scope_.SetCPUFeatures(cpu_features);
+    masm_->SetSimulatorCPUFeatures(cpu_features);
+  }
+
+ private:
+  MacroAssembler* masm_;
+  CPUFeaturesScope cpu_features_scope_;
+};
+
+
 // Variadic templating is only available from C++11.
 #ifdef VIXL_HAS_MACROASSEMBLER_RUNTIME_CALL_SUPPORT
 
diff --git a/src/aarch64/simulator-aarch64.cc b/src/aarch64/simulator-aarch64.cc
index 51b8fd7..a23d57e 100644
--- a/src/aarch64/simulator-aarch64.cc
+++ b/src/aarch64/simulator-aarch64.cc
@@ -62,7 +62,8 @@
 }
 
 
-Simulator::Simulator(Decoder* decoder, FILE* stream) {
+Simulator::Simulator(Decoder* decoder, FILE* stream)
+    : cpu_features_auditor_(decoder, CPUFeatures::All()) {
   // Ensure that shift operations act as the simulator expects.
   VIXL_ASSERT((static_cast<int32_t>(-1) >> 1) == -1);
   VIXL_ASSERT((static_cast<uint32_t>(-1) >> 1) == 0x7fffffff);
@@ -74,7 +75,17 @@
   decoder_->AppendVisitor(this);
 
   stream_ = stream;
+
   print_disasm_ = new PrintDisassembler(stream_);
+  // The Simulator and Disassembler share the same available list, held by the
+  // auditor. The Disassembler only annotates instructions with features that
+  // are _not_ available, so registering the auditor should have no effect
+  // unless the simulator is about to abort (due to missing features). In
+  // practice, this means that with trace enabled, the simulator will crash just
+  // after the disassembler prints the instruction, with the missing features
+  // enumerated.
+  print_disasm_->RegisterCPUFeaturesAuditor(&cpu_features_auditor_);
+
   SetColouredTrace(false);
   trace_parameters_ = LOG_NONE;
 
@@ -273,6 +284,14 @@
   clr_warning_message = value ? COLOUR(YELLOW) : "";
   clr_printf = value ? COLOUR(GREEN) : "";
   clr_branch_marker = value ? COLOUR(GREY) COLOUR_HIGHLIGHT : "";
+
+  if (value) {
+    print_disasm_->SetCPUFeaturesPrefix("// Needs: " COLOUR_BOLD(RED));
+    print_disasm_->SetCPUFeaturesSuffix(COLOUR(NORMAL));
+  } else {
+    print_disasm_->SetCPUFeaturesPrefix("// Needs: ");
+    print_disasm_->SetCPUFeaturesSuffix("");
+  }
 }
 
 
@@ -3194,6 +3213,17 @@
         case kRuntimeCallOpcode:
           DoRuntimeCall(instr);
           return;
+        case kSetCPUFeaturesOpcode:
+        case kEnableCPUFeaturesOpcode:
+        case kDisableCPUFeaturesOpcode:
+          DoConfigureCPUFeatures(instr);
+          return;
+        case kSaveCPUFeaturesOpcode:
+          DoSaveCPUFeatures(instr);
+          return;
+        case kRestoreCPUFeaturesOpcode:
+          DoRestoreCPUFeatures(instr);
+          return;
         default:
           HostBreakpoint();
           return;
@@ -5708,6 +5738,66 @@
 }
 #endif
 
+
+void Simulator::DoConfigureCPUFeatures(const Instruction* instr) {
+  VIXL_ASSERT(instr->Mask(ExceptionMask) == HLT);
+
+  typedef ConfigureCPUFeaturesElementType ElementType;
+  VIXL_ASSERT(CPUFeatures::kNumberOfFeatures <=
+              std::numeric_limits<ElementType>::max());
+
+  // k{Set,Enable,Disable}CPUFeatures have the same parameter encoding.
+
+  size_t element_size = sizeof(ElementType);
+  size_t offset = kConfigureCPUFeaturesListOffset;
+
+  // Read the kNone-terminated list of features.
+  CPUFeatures parameters;
+  while (true) {
+    ElementType feature = Memory::Read<ElementType>(instr + offset);
+    offset += element_size;
+    if (feature == CPUFeatures::kNone) break;
+    parameters.Combine(static_cast<CPUFeatures::Feature>(feature));
+  }
+
+  switch (instr->GetImmException()) {
+    case kSetCPUFeaturesOpcode:
+      SetCPUFeatures(parameters);
+      break;
+    case kEnableCPUFeaturesOpcode:
+      GetCPUFeatures()->Combine(parameters);
+      break;
+    case kDisableCPUFeaturesOpcode:
+      GetCPUFeatures()->Remove(parameters);
+      break;
+    default:
+      VIXL_UNREACHABLE();
+      break;
+  }
+
+  WritePc(instr->GetInstructionAtOffset(AlignUp(offset, kInstructionSize)));
+}
+
+
+void Simulator::DoSaveCPUFeatures(const Instruction* instr) {
+  VIXL_ASSERT((instr->Mask(ExceptionMask) == HLT) &&
+              (instr->GetImmException() == kSaveCPUFeaturesOpcode));
+  USE(instr);
+
+  saved_cpu_features_.push_back(*GetCPUFeatures());
+}
+
+
+void Simulator::DoRestoreCPUFeatures(const Instruction* instr) {
+  VIXL_ASSERT((instr->Mask(ExceptionMask) == HLT) &&
+              (instr->GetImmException() == kRestoreCPUFeaturesOpcode));
+  USE(instr);
+
+  SetCPUFeatures(saved_cpu_features_.back());
+  saved_cpu_features_.pop_back();
+}
+
+
 }  // namespace aarch64
 }  // namespace vixl
 
diff --git a/src/aarch64/simulator-aarch64.h b/src/aarch64/simulator-aarch64.h
index 8461f98..f63f0c2 100644
--- a/src/aarch64/simulator-aarch64.h
+++ b/src/aarch64/simulator-aarch64.h
@@ -27,10 +27,14 @@
 #ifndef VIXL_AARCH64_SIMULATOR_AARCH64_H_
 #define VIXL_AARCH64_SIMULATOR_AARCH64_H_
 
+#include <vector>
+
 #include "../globals-vixl.h"
 #include "../utils-vixl.h"
 
+#include "cpu-features.h"
 #include "abi-aarch64.h"
+#include "cpu-features-auditor-aarch64.h"
 #include "disasm-aarch64.h"
 #include "instructions-aarch64.h"
 #include "instrument-aarch64.h"
@@ -688,9 +692,18 @@
     // The program counter should always be aligned.
     VIXL_ASSERT(IsWordAligned(pc_));
     pc_modified_ = false;
+
+    // decoder_->Decode(...) triggers at least the following visitors:
+    //  1. The CPUFeaturesAuditor (`cpu_features_auditor_`).
+    //  2. The PrintDisassembler (`print_disasm_`), if enabled.
+    //  3. The Simulator (`this`).
+    // User can add additional visitors at any point, but the Simulator requires
+    // that the ordering above is preserved.
     decoder_->Decode(pc_);
     IncrementPc();
     LogAllWrittenRegisters();
+
+    VIXL_CHECK(cpu_features_auditor_.InstructionIsAvailable());
   }
 
 // Declare all Visitor functions.
@@ -1539,6 +1552,22 @@
     print_exclusive_access_warning_ = false;
   }
 
+  // The common CPUFeatures interface with the set of available features.
+
+  CPUFeatures* GetCPUFeatures() {
+    return cpu_features_auditor_.GetCPUFeatures();
+  }
+
+  void SetCPUFeatures(const CPUFeatures& cpu_features) {
+    cpu_features_auditor_.SetCPUFeatures(cpu_features);
+  }
+
+  // The set of features that the simulator has encountered.
+  const CPUFeatures& GetSeenFeatures() {
+    return cpu_features_auditor_.GetSeenFeatures();
+  }
+  void ResetSeenFeatures() { cpu_features_auditor_.ResetSeenFeatures(); }
+
 // Runtime call emulation support.
 // It requires VIXL's ABI features, and C++11 or greater.
 // Also, the initialisation of the tuples in RuntimeCall(Non)Void is incorrect
@@ -2964,6 +2993,12 @@
   // Pseudo Printf instruction
   void DoPrintf(const Instruction* instr);
 
+  // Pseudo-instructions to configure CPU features dynamically.
+  void DoConfigureCPUFeatures(const Instruction* instr);
+
+  void DoSaveCPUFeatures(const Instruction* instr);
+  void DoRestoreCPUFeatures(const Instruction* instr);
+
 // Simulate a runtime call.
 #ifndef VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT
   VIXL_NO_RETURN_IN_DEBUG_MODE
@@ -3106,6 +3141,9 @@
   // Indicates whether the exclusive-access warning has been printed.
   bool print_exclusive_access_warning_;
   void PrintExclusiveAccessWarning();
+
+  CPUFeaturesAuditor cpu_features_auditor_;
+  std::vector<CPUFeatures> saved_cpu_features_;
 };
 
 #if defined(VIXL_HAS_SIMULATED_RUNTIME_CALL_SUPPORT) && __cplusplus < 201402L
diff --git a/src/aarch64/simulator-constants-aarch64.h b/src/aarch64/simulator-constants-aarch64.h
index b6a4241..6631043 100644
--- a/src/aarch64/simulator-constants-aarch64.h
+++ b/src/aarch64/simulator-constants-aarch64.h
@@ -45,16 +45,22 @@
 // Each debug pseudo instruction is represented by a HLT instruction. The HLT
 // immediate field is used to identify the type of debug pseudo instruction.
 
-enum DebugHltOpcodes {
+enum DebugHltOpcode {
   kUnreachableOpcode = 0xdeb0,
   kPrintfOpcode,
   kTraceOpcode,
   kLogOpcode,
   kRuntimeCallOpcode,
+  kSetCPUFeaturesOpcode,
+  kEnableCPUFeaturesOpcode,
+  kDisableCPUFeaturesOpcode,
+  kSaveCPUFeaturesOpcode,
+  kRestoreCPUFeaturesOpcode,
   // Aliases.
   kDebugHltFirstOpcode = kUnreachableOpcode,
   kDebugHltLastOpcode = kLogOpcode
 };
+VIXL_DEPRECATED("DebugHltOpcode", typedef DebugHltOpcode DebugHltOpcodes);
 
 // Each pseudo instruction uses a custom encoding for additional arguments, as
 // described below.
@@ -140,7 +146,7 @@
 const unsigned kLogParamsOffset = 1 * kInstructionSize;
 const unsigned kLogLength = 2 * kInstructionSize;
 
-// Runtime call simulation - kRuntimeCall
+// Runtime call simulation - kRuntimeCallOpcode
 enum RuntimeCallType { kCallRuntime, kTailCallRuntime };
 
 const unsigned kRuntimeCallWrapperOffset = 1 * kInstructionSize;
@@ -151,6 +157,35 @@
 const unsigned kRuntimeCallTypeOffset =
     kRuntimeCallFunctionOffset + kRuntimeCallAddressSize;
 const unsigned kRuntimeCallLength = kRuntimeCallTypeOffset + sizeof(uint32_t);
+
+// Enable or disable CPU features - kSetCPUFeaturesOpcode
+//                                - kEnableCPUFeaturesOpcode
+//                                - kDisableCPUFeaturesOpcode
+//  - parameter[...]: A list of `CPUFeatures::Feature`s, encoded as
+//    ConfigureCPUFeaturesElementType and terminated with CPUFeatures::kNone.
+//  - [Padding to align to kInstructionSize.]
+//
+// 'Set' completely overwrites the existing CPU features.
+// 'Enable' and 'Disable' update the existing CPU features.
+//
+// These mechanisms allows users to strictly check the use of CPU features in
+// different regions of code.
+//
+// These have no effect on the set of 'seen' features (as reported by
+// CPUFeaturesAuditor::HasSeen(...)).
+typedef uint8_t ConfigureCPUFeaturesElementType;
+const unsigned kConfigureCPUFeaturesListOffset = 1 * kInstructionSize;
+
+// Save or restore CPU features - kSaveCPUFeaturesOpcode
+//                              - kRestoreCPUFeaturesOpcode
+//
+// These mechanisms provide a stack-like mechanism for preserving the CPU
+// features, or restoring the last-preserved features. These pseudo-instructions
+// take no arguments.
+//
+// These have no effect on the set of 'seen' features (as reported by
+// CPUFeaturesAuditor::HasSeen(...)).
+
 }  // namespace aarch64
 }  // namespace vixl
 
diff --git a/src/cpu-features.cc b/src/cpu-features.cc
index 7843eeb..8edd1ff 100644
--- a/src/cpu-features.cc
+++ b/src/cpu-features.cc
@@ -136,6 +136,8 @@
   return (features_ & mask) == mask;
 }
 
+size_t CPUFeatures::Count() const { return CountSetBits(features_); }
+
 std::ostream& operator<<(std::ostream& os, CPUFeatures::Feature feature) {
   // clang-format off
   switch (feature) {
diff --git a/src/cpu-features.h b/src/cpu-features.h
index f3b3b4b..e5c68ab 100644
--- a/src/cpu-features.h
+++ b/src/cpu-features.h
@@ -93,18 +93,29 @@
 //   - When the Assembler is asked to assemble an instruction, it asserts (in
 //     debug mode) that the necessary features are available.
 //
-//   - TODO: The Simulator assumes that all features are available by default,
-//     but it should be possible to configure it to either warn or fail if the
-//     simulated code uses features that are not enabled.
-//
-//   - TODO: The Disassembler disassembles all instructions that it knows, but
-//     it could be (optionally) configured to warn if the code uses features or
-//     instructions that are not available.
-//
 //   - TODO: The MacroAssembler relies on the Assembler's assertions, but in
 //     some cases it may be useful for macros to generate a fall-back sequence
 //     in case features are not available.
 //
+//   - The Simulator assumes by default that all features are available, but it
+//     is possible to configure it to fail if the simulated code uses features
+//     that are not enabled.
+//
+//     The Simulator also offers pseudo-instructions to allow features to be
+//     enabled and disabled dynamically. This is useful when you want to ensure
+//     that some features are constrained to certain areas of code.
+//
+//   - The base Disassembler knows nothing about CPU features, but the
+//     PrintDisassembler can be configured to annotate its output with warnings
+//     about unavailable features. The Simulator uses this feature when
+//     instruction trace is enabled.
+//
+//   - The Decoder-based components -- the Simulator and PrintDisassembler --
+//     rely on a CPUFeaturesAuditor visitor. This visitor keeps a list of
+//     features actually encountered so that a large block of code can be
+//     examined (either directly or through simulation), and the required
+//     features analysed later.
+//
 // Expected usage:
 //
 //     // By default, VIXL uses CPUFeatures::AArch64LegacyBaseline(), for
@@ -228,6 +239,15 @@
            Feature feature2 = kNone,
            Feature feature3 = kNone) const;
 
+  // Return the number of enabled features.
+  size_t Count() const;
+
+  // Check for equivalence.
+  bool operator==(const CPUFeatures& other) const {
+    return Has(other) && other.Has(*this);
+  }
+  bool operator!=(const CPUFeatures& other) const { return !(*this == other); }
+
   typedef CPUFeaturesConstIterator const_iterator;
 
   const_iterator begin() const;