]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Bug 487418 - [arm64] Cleanup M{SR,RS} instructions (part 2)
authorPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 16 May 2026 09:16:36 +0000 (11:16 +0200)
committerPaul Floyd <pjfloyd@wanadoo.fr>
Sat, 16 May 2026 11:29:42 +0000 (13:29 +0200)
Extracts system register shift and mask macros to a new VEX public
header libvex_guest_arm64_sysregs.h

These macros are used in both VEX dirty helpers and coregrind
VG_(machine_get_hwcaps).

Mostly this is just refactoring the code. It should be more robust
in the face of changes concerning future ARM CPU features. The
id_aa64pfr0_el1 dirty helper has changed a bit - the code was wrong
but the output was right (for what we currently support).

VEX/Makefile.am
VEX/priv/guest_arm64_helpers.c
VEX/pub/libvex_guest_arm64_sysregs.h [new file with mode: 0644]
coregrind/m_machine.c

index ca50e0df2e47478a69b1551fc91fec90b134395a..59ce63c15d2b48aa5015d6380d0c5286957570de 100644 (file)
@@ -23,6 +23,7 @@ pkginclude_HEADERS = \
        pub/libvex_guest_ppc64.h \
        pub/libvex_guest_arm.h \
        pub/libvex_guest_arm64.h \
+       pub/libvex_guest_arm64_sysregs.h \
        pub/libvex_guest_s390x.h \
        pub/libvex_guest_mips32.h \
        pub/libvex_guest_mips64.h \
index 7bd99ec5fa26cda6aa8af2f51ce0ac28821b38fb..d0f8b24b500d5bd39485e5da591f246f1506afd6 100644 (file)
@@ -29,6 +29,7 @@
 #include "libvex_basictypes.h"
 #include "libvex_emnote.h"
 #include "libvex_guest_arm64.h"
+#include "libvex_guest_arm64_sysregs.h"
 #include "libvex_ir.h"
 #include "libvex.h"
 
@@ -776,6 +777,7 @@ ULong arm64g_calc_crc32cx ( ULong acc, ULong bits )
    return crc;
 }
 
+
 /* CALLED FROM GENERATED CODE */
 /* DIRTY HELPER (non-referentially-transparent) */
 /* Horrible hack.  On non-arm64 platforms, return 0. */
@@ -828,27 +830,18 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64PFR0_EL1 ( void )
    ULong w = 0x5555555555555555ULL; /* overwritten */
    __asm__ __volatile__("mrs %0, id_aa64pfr0_el1" : "=r"(w));
 
-   // The control word uses the following nibbles (as seen on RPi)
-   // unsupported unless indicated
-   // 0 to 3 - EL0 to EL3 exception level handling
-   // 4 - FP includes half-precision (partial support)
-   // 5 - AdvSIMD also includes haf-precision
-
-   /* If half-precision fp is present we fall back to normal
-      half precision implementation because of missing support in the emulation.
-      If no AdvSIMD and FP are implemented, we preserve the value */
-   w = (w >> 16);
-   w &= 0xff;
-   switch(w) {
-     case 0x01:
-       w = 0x0;
-       break;
-     case 0xff:
-       w = (0xFF<<16);
-       break;
-     default:
-       w = 0x0;
-       break;
+   MASK_SYSTEM_REGISTER_FIELDS(w,
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64PFR0_EL0_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64PFR0_EL1_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64PFR0_FP_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64PFR0_ADVSIMD_SHIFT));
+
+   if (SYSTEM_REGISTER_FIELD(w, ID_AA64PFR0_FP_SHIFT) != ID_AA64PFR0_FP_NOT_PRESENT) {
+      CLAMP_REGISTER_FIELD_INPLACE(w, ID_AA64PFR0_FP_SHIFT, ID_AA64PFR0_FP_NHP_SUPPORTED);
+   }
+
+   if (SYSTEM_REGISTER_FIELD(w, ID_AA64PFR0_ADVSIMD_SHIFT) != ID_AA64PFR0_ADVSIMD_NOT_PRESENT) {
+      CLAMP_REGISTER_FIELD_INPLACE(w, ID_AA64PFR0_ADVSIMD_SHIFT, ID_AA64PFR0_ADVSIMD_NHP_SUPPORTED);
    }
 
    return w;
@@ -880,8 +873,23 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64MMFR1_EL1 ( void )
    ULong w = 0x5555555555555555ULL; /* overwritten */
    __asm__ __volatile__("mrs %0, id_aa64mmfr1_el1" : "=r"(w));
 
-   /* Clear VH and HAFDBS bits */
-   w &= ~(0xF0F);
+   // FIXME PJF we were just filtering out ID_AA64MMFR1_HAFDB and ID_AA64MMFR1_VH
+   // do we really support all of these?
+   MASK_SYSTEM_REGISTER_FIELDS(w,
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_VMIDBITS_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_HPDS_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_LO_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_PAN_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_SPECSEI_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_XNX_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_TWED_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_ETS_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_HCX_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_AFP_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_NTLBPA_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_TIDCP1_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64MMFR1_CMOW_SHIFT));
+
    return w;
 #  else
    return 0ULL;
@@ -897,23 +905,14 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR0_EL1 ( void )
    ULong w = 0x5555555555555555ULL; /* overwritten */
    __asm__ __volatile__("mrs %0, id_aa64isar0_el1" : "=r"(w));
 
-   // In the mask below, nibbles are (higher nibbles all unsupported)
-   // 0 - RES0
-   // 1 - AES
-   // 2 - SHA1
-   // 3 - SHA2
-   // 4 - CRC32
-   // 5 - Atomic bits
-   // 6 - TME (unsupported)
-   // 7 - RDM
-   // 8 - SHA3 (unsupported)
-   // 9 - SM3 (unsupported)
-   // 10 - SM4 (unsupported)
-   // 11 - DP
-
-   //     10
-   //     109876543210
-   w &= 0xF000F0FFFFFF;
+   MASK_SYSTEM_REGISTER_FIELDS(w,
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_AES_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_SHA1_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_SHA2_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_CRC32_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_ATOMICS_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_RDM_SHIFT) |
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR0_DP_SHIFT));
 
    return w;
 #  else
@@ -930,8 +929,8 @@ ULong arm64g_dirtyhelper_MRS_ID_AA64ISAR1_EL1 ( void )
    ULong w = 0x5555555555555555ULL; /* overwritten */
    __asm__ __volatile__("mrs %0, id_aa64isar1_el1" : "=r"(w));
 
-   // only nibble 0 DBP
-   w &= 0xF;
+   MASK_SYSTEM_REGISTER_FIELDS(w,
+                               MAKE_SYSTEM_REGISTER_MASK_FIELD(ID_AA64ISAR1_DPB_SHIFT));
 
    return w;
 #  else
diff --git a/VEX/pub/libvex_guest_arm64_sysregs.h b/VEX/pub/libvex_guest_arm64_sysregs.h
new file mode 100644 (file)
index 0000000..8b9f497
--- /dev/null
@@ -0,0 +1,137 @@
+/*---------------------------------------------------------------*/
+/*--- begin                      libvex_guest_arm64_sysregs.h ---*/
+/*---------------------------------------------------------------*/
+
+/*
+   This file is part of Valgrind, a dynamic binary instrumentation
+   framework.
+
+   Copyright (C) 2026 Paul Floyd
+      pjfloyd@wanadoo.fr
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 3 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+   The GNU General Public License is contained in the file COPYING.
+*/
+
+#ifndef __LIBVEX_PUB_GUEST_ARM64_SYSREGS_H
+#define __LIBVEX_PUB_GUEST_ARM64_SYSREGS_H
+
+/*---------------------------------------------------------------*/
+/*--- arm64 system register field definitions                 ---*/
+/*---------------------------------------------------------------*/
+
+/* These definitions are used when reading system registers
+ * to mask unsupported fields. They are also used by Valgrind
+ * during startup when determining hardware capabilities. */
+
+/* ID_AA64ISAR0_EL1 Instruction set attribute register 0 fields */
+#define ID_AA64ISAR0_FHM_SHIFT            48
+#define ID_AA64ISAR0_DP_SHIFT             44
+#define ID_AA64ISAR0_SM4_SHIFT            40
+#define ID_AA64ISAR0_SM3_SHIFT            36
+#define ID_AA64ISAR0_SHA3_SHIFT           32
+#define ID_AA64ISAR0_RDM_SHIFT            28
+#define ID_AA64ISAR0_ATOMICS_SHIFT        20
+#define ID_AA64ISAR0_CRC32_SHIFT          16
+#define ID_AA64ISAR0_SHA2_SHIFT           12
+#define ID_AA64ISAR0_SHA1_SHIFT           8
+#define ID_AA64ISAR0_AES_SHIFT            4
+#define ID_AA64ISAR0_RES0_SHIFT           0
+/* Field values */
+#define ID_AA64ISAR0_FHM_SUPPORTED        0x1
+#define ID_AA64ISAR0_DP_SUPPORTED         0x1
+#define ID_AA64ISAR0_SM4_SUPPORTED        0x1
+#define ID_AA64ISAR0_SM3_SUPPORTED        0x1
+#define ID_AA64ISAR0_SHA3_SUPPORTED       0x1
+#define ID_AA64ISAR0_RDM_SUPPORTED        0x1
+#define ID_AA64ISAR0_ATOMICS_SUPPORTED    0x2
+
+/* ID_AA64ISAR1_EL1 Instruction set attribute register 1 fields */
+#define ID_AA64ISAR1_I8MM_SHIFT           52
+#define ID_AA64ISAR1_BF16_SHIFT           44
+#define ID_AA64ISAR1_DPB_SHIFT             0
+/* Field values */
+#define ID_AA64ISAR1_I8MM_SUPPORTED       0x1
+#define ID_AA64ISAR1_BF16_SUPPORTED       0x1
+#define ID_AA64ISAR1_DPBCVAP_SUPPORTED    0x1
+#define ID_AA64ISAR1_DPBCVADP_SUPPORTED   0x2
+
+/* ID_AA64PFR0_EL1 Processor feature register 0 fields */
+#define ID_AA64PFR0_CSV3_SHIFT            60
+#define ID_AA64PFR0_CSV2_SHIFT            56
+#define ID_AA64PFR0_RME_SHIFT             52
+#define ID_AA64PFR0_DIT_SHIFT             48
+#define ID_AA64PFR0_AMU_SHIFT             44
+#define ID_AA64PFR0_MPAM_SHIFT            40
+#define ID_AA64PFR0_SEL2_SHIFT            36
+#define ID_AA64PFR0_SVE_SHIFT             32
+#define ID_AA64PFR0_RAS_SHIFT             28
+#define ID_AA64PFR0_GIC_SHIFT             24
+#define ID_AA64PFR0_ADVSIMD_SHIFT         20
+#define ID_AA64PFR0_FP_SHIFT              16
+#define ID_AA64PFR0_EL3_SHIFT             12
+#define ID_AA64PFR0_EL2_SHIFT             8
+#define ID_AA64PFR0_EL1_SHIFT             4
+#define ID_AA64PFR0_EL0_SHIFT             0
+/* Field values */
+#define ID_AA64PFR0_FP_NHP_SUPPORTED      0x0 /* FP but no half precision */
+#define ID_AA64PFR0_FP_HP_SUPPORTED       0x1 /* FP and half precision */
+#define ID_AA64PFR0_FP_NOT_PRESENT        0xf /* no FP present */
+#define ID_AA64PFR0_ADVSIMD_NHP_SUPPORTED 0x0
+#define ID_AA64PFR0_ADVSIMD_HP_SUPPORTED  0x1
+#define ID_AA64PFR0_ADVSIMD_NOT_PRESENT   0xf
+
+/* ID_AA64MMFR1_EL1 memory model feature register */
+#define ID_AA64MMFR1_RES0_SHIFT           60
+#define ID_AA64MMFR1_CMOW_SHIFT           56
+#define ID_AA64MMFR1_TIDCP1_SHIFT         52
+#define ID_AA64MMFR1_NTLBPA_SHIFT         48
+#define ID_AA64MMFR1_AFP_SHIFT            44
+#define ID_AA64MMFR1_HCX_SHIFT            40
+#define ID_AA64MMFR1_ETS_SHIFT            36
+#define ID_AA64MMFR1_TWED_SHIFT           32
+#define ID_AA64MMFR1_XNX_SHIFT            28
+#define ID_AA64MMFR1_SPECSEI_SHIFT        24
+#define ID_AA64MMFR1_PAN_SHIFT            20
+#define ID_AA64MMFR1_LO_SHIFT             16
+#define ID_AA64MMFR1_HPDS_SHIFT           12
+#define ID_AA64MMFR1_VH_SHIFT             8
+#define ID_AA64MMFR1_VMIDBITS_SHIFT       4
+#define ID_AA64MMFR1_HAFDBS_SHIFT         0
+
+#define SYSTEM_REGISTER_FIELD(val, shift) ((((val) >> (shift)) & 0xfULL))
+
+/* Feature support is specified in nibbles. That gives 16 possible
+ * levels. Usually 0 means no support. After that each successive
+ * level is a superset of the previous one. That means that if we do
+ * not fully support a level then we need to clamp to the previous
+ * level. */
+#define CLAMP_REGISTER_FIELD_INPLACE(val, shift, limit) \
+   do {                                                 \
+      if (SYSTEM_REGISTER_FIELD(val, shift) > (ULong)(limit)) { \
+         val &= ~(0xfULL << (shift)); \
+         val |= ((ULong)(limit) << (shift)); \
+      } \
+   } while (0)
+
+#define MAKE_SYSTEM_REGISTER_MASK_FIELD(shift) (0xfULL << (shift))
+#define MASK_SYSTEM_REGISTER_FIELDS(val, mask) (val) &= (mask)
+
+#endif /* ifndef __LIBVEX_PUB_GUEST_ARM64_SYSREGS_H */
+
+
+/*---------------------------------------------------------------*/
+/*---                            libvex_guest_arm64_sysregs.h ---*/
+/*---------------------------------------------------------------*/
index 15227efcf97a1732a93d453b02083dda9b7090bc..0feb0bdbeabf3a178a41e9eaaefa354da3a0f466 100644 (file)
@@ -40,6 +40,8 @@
 #include "pub_core_libcsignal.h"   // for ppc32 messing with SIGILL and SIGFPE
 #include "pub_core_debuglog.h"
 
+#include "libvex_guest_arm64_sysregs.h"
+
 
 #define INSTR_PTR(regs)    ((regs).vex.VG_INSTR_PTR)
 #define STACK_PTR(regs)    ((regs).vex.VG_STACK_PTR)
@@ -1859,100 +1861,58 @@ Bool VG_(machine_get_hwcaps)( void )
      if (is_base_v8)
         return True;
 
-     /* ID_AA64ISAR0_EL1 Instruction set attribute register 0 fields */
-     #define ID_AA64ISAR0_FHM_SHIFT            48
-     #define ID_AA64ISAR0_DP_SHIFT             44
-     #define ID_AA64ISAR0_SM4_SHIFT            40
-     #define ID_AA64ISAR0_SM3_SHIFT            36
-     #define ID_AA64ISAR0_SHA3_SHIFT           32
-     #define ID_AA64ISAR0_RDM_SHIFT            28
-     #define ID_AA64ISAR0_ATOMICS_SHIFT        20
-     /* Field values */
-     #define ID_AA64ISAR0_FHM_SUPPORTED        0x1
-     #define ID_AA64ISAR0_DP_SUPPORTED         0x1
-     #define ID_AA64ISAR0_SM4_SUPPORTED        0x1
-     #define ID_AA64ISAR0_SM3_SUPPORTED        0x1
-     #define ID_AA64ISAR0_SHA3_SUPPORTED       0x1
-     #define ID_AA64ISAR0_RDM_SUPPORTED        0x1
-     #define ID_AA64ISAR0_ATOMICS_SUPPORTED    0x2
-
-     /* ID_AA64ISAR1_EL1 Instruction set attribute register 1 fields */
-     #define ID_AA64ISAR1_I8MM_SHIFT           52
-     #define ID_AA64ISAR1_BF16_SHIFT           44
-     #define ID_AA64ISAR1_DPB_SHIFT             0
-     /* Field values */
-     #define ID_AA64ISAR1_I8MM_SUPPORTED       0x1
-     #define ID_AA64ISAR1_BF16_SUPPORTED       0x1
-     #define ID_AA64ISAR1_DPBCVAP_SUPPORTED    0x1
-     #define ID_AA64ISAR1_DPBCVADP_SUPPORTED   0x2
-
-     /* ID_AA64PFR0_EL1 Processor feature register 0 fields */
-     #define ID_AA64PFR0_VFP16_SHIFT           20
-     #define ID_AA64PFR0_FP16_SHIFT            16
-     /* Field values */
-     #define ID_AA64PFR0_VFP16_SUPPORTED       0x1
-     #define ID_AA64PFR0_FP16_SUPPORTED        0x1
-
-     #define get_cpu_ftr(id) ({                                             \
-         unsigned long val;                                                 \
+
+     #define get_cpu_ftr(id, val) ({                                             \
          asm("mrs %0, "#id : "=r" (val));                                   \
          VG_(debugLog)(1, "machine", "ARM64: %-20s: 0x%016lx\n", #id, val); \
      })
-     get_cpu_ftr(ID_AA64ISAR0_EL1);
-     get_cpu_ftr(ID_AA64ISAR1_EL1);
-     get_cpu_ftr(ID_AA64PFR0_EL1);
-
-     #define get_ftr(id, ftr, fval, have_ftr) ({                           \
-         unsigned long rval;                                               \
-         asm("mrs %0, "#id : "=r" (rval));                                 \
-         have_ftr = (fval & ((rval >> ftr) & 0xf)) >= fval ? True : False; \
-     })
+
+     unsigned long isar0;
+     unsigned long isar1;
+     unsigned long pfr0;
+
+     get_cpu_ftr(ID_AA64ISAR0_EL1, isar0);
+     get_cpu_ftr(ID_AA64ISAR1_EL1, isar1);
+     get_cpu_ftr(ID_AA64PFR0_EL1, pfr0);
 
      /* Read ID_AA64ISAR0_EL1 attributes */
 
      /* FHM indicates support for FMLAL and FMLSL instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_FHM_SHIFT,
-             ID_AA64ISAR0_FHM_SUPPORTED, have_fhm);
+     have_fhm = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_FHM_SHIFT) >= ID_AA64ISAR0_FHM_SUPPORTED;
 
      /* DP indicates support for UDOT and SDOT instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_DP_SHIFT,
-             ID_AA64ISAR0_DP_SUPPORTED, have_dp);
+     have_dp = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_DP_SHIFT) >= ID_AA64ISAR0_DP_SUPPORTED;
 
      /* SM4 indicates support for SM4E and SM4EKEY instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM4_SHIFT,
-             ID_AA64ISAR0_SM4_SUPPORTED, have_sm4);
+     have_sm4 = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_SM4_SHIFT) >= ID_AA64ISAR0_SM4_SUPPORTED;
 
      /* SM3 indicates support for SM3SS1, SM3TT1A, SM3TT1B, SM3TT2A, * SM3TT2B,
       * SM3PARTW1, and SM3PARTW2 instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_SM3_SHIFT,
-             ID_AA64ISAR0_SM3_SUPPORTED, have_sm3);
+     have_sm3 = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_SM3_SHIFT) >= ID_AA64ISAR0_SM3_SUPPORTED;
 
      /* SHA3 indicates support for EOR3, RAX1, XAR, and BCAX instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_SHA3_SHIFT,
-             ID_AA64ISAR0_SHA3_SUPPORTED, have_sha3);
+     have_sha3 = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_SHA3_SHIFT) >= ID_AA64ISAR0_SHA3_SUPPORTED;
 
      /* RDM indicates support for SQRDMLAH and SQRDMLSH instructions.
       * Mandatory from v8.1 onwards.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_RDM_SHIFT,
-             ID_AA64ISAR0_RDM_SUPPORTED, have_rdm);
+     have_rdm = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_RDM_SHIFT) >= ID_AA64ISAR0_RDM_SUPPORTED;
 
      /* v8.1 ATOMICS indicates support for LDADD, LDCLR, LDEOR, LDSET, LDSMAX,
       * LDSMIN, LDUMAX, LDUMIN, CAS, CASP, and SWP instructions.
       * Mandatory from v8.1 onwards.
       */
-     get_ftr(ID_AA64ISAR0_EL1, ID_AA64ISAR0_ATOMICS_SHIFT,
-             ID_AA64ISAR0_ATOMICS_SUPPORTED, have_atomics);
+     have_atomics = SYSTEM_REGISTER_FIELD(isar0, ID_AA64ISAR0_ATOMICS_SHIFT) >= ID_AA64ISAR0_ATOMICS_SUPPORTED;
 
      /* Read ID_AA64ISAR1_EL1 attributes */
 
@@ -1960,41 +1920,41 @@ Bool VG_(machine_get_hwcaps)( void )
       * instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR1_EL1, ID_AA64ISAR1_I8MM_SHIFT,
-             ID_AA64ISAR1_I8MM_SUPPORTED, have_i8mm);
+     have_i8mm = SYSTEM_REGISTER_FIELD(isar1, ID_AA64ISAR1_I8MM_SHIFT) >= ID_AA64ISAR1_I8MM_SUPPORTED;
 
      /* BF16 indicates support for BFDOT, BFMLAL, BFMLAL2, BFMMLA, BFCVT, and
       * BFCVT2 instructions.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR1_EL1, ID_AA64ISAR1_BF16_SHIFT,
-             ID_AA64ISAR1_BF16_SUPPORTED, have_bf16);
+     have_bf16 = SYSTEM_REGISTER_FIELD(isar1, ID_AA64ISAR1_BF16_SHIFT) >= ID_AA64ISAR1_BF16_SUPPORTED;
 
      /* DPB indicates support for DC CVAP instruction.
       * Mandatory for v8.2 onwards.
       */
-     get_ftr(ID_AA64ISAR1_EL1, ID_AA64ISAR1_DPB_SHIFT,
-             ID_AA64ISAR1_DPBCVAP_SUPPORTED, have_dpbcvap);
+     have_dpbcvap = SYSTEM_REGISTER_FIELD(isar1, ID_AA64ISAR1_DPB_SHIFT) >= ID_AA64ISAR1_DPBCVAP_SUPPORTED;
 
      /* DPB indicates support for DC CVADP instruction.
       * Optional for v8.2.
       */
-     get_ftr(ID_AA64ISAR1_EL1, ID_AA64ISAR1_DPB_SHIFT,
-             ID_AA64ISAR1_DPBCVADP_SUPPORTED, have_dpbcvadp);
+     have_dpbcvadp = SYSTEM_REGISTER_FIELD(isar1, ID_AA64ISAR1_DPB_SHIFT) >= ID_AA64ISAR1_DPBCVADP_SUPPORTED;
 
      /* Read ID_AA64PFR0_EL1 attributes */
 
      /* VFP16 indicates support for half-precision vector arithmetic.
-      * Optional for v8.2. Must be the same value as FP16.
+      * Optional for v8.2. Must be the same value as FP16. fp and
+      * advsimd are different to the usual isa/fp in that 0 means
+      * that the base features are present and 0xf means that the
+      * features are absent. Normally 0 means that the feature is absent.
       */
-     get_ftr(ID_AA64PFR0_EL1, ID_AA64PFR0_VFP16_SHIFT,
-             ID_AA64PFR0_VFP16_SUPPORTED, have_vfp16);
+     unsigned long advsimd = SYSTEM_REGISTER_FIELD(pfr0, ID_AA64PFR0_ADVSIMD_SHIFT);
+     have_vfp16 = advsimd >= ID_AA64PFR0_ADVSIMD_HP_SUPPORTED && advsimd != ID_AA64PFR0_ADVSIMD_NOT_PRESENT;
 
      /* FP16 indicates support for half-precision scalar arithmetic.
       * Optional for v8.2. Must be the same value as VFP16.
       */
-     get_ftr(ID_AA64PFR0_EL1, ID_AA64PFR0_FP16_SHIFT,
-             ID_AA64PFR0_FP16_SUPPORTED, have_fp16);
+     unsigned long fp = SYSTEM_REGISTER_FIELD(pfr0, ID_AA64PFR0_FP_SHIFT);
+     vg_assert(fp == advsimd);
+     have_fp16 = fp >= ID_AA64PFR0_FP_HP_SUPPORTED && fp != ID_AA64PFR0_FP_NOT_PRESENT;
 
      if (have_fhm)        vai.hwcaps |= VEX_HWCAPS_ARM64_FHM;
      if (have_dpbcvap)    vai.hwcaps |= VEX_HWCAPS_ARM64_DPBCVAP;
@@ -2010,7 +1970,6 @@ Bool VG_(machine_get_hwcaps)( void )
      if (have_vfp16)      vai.hwcaps |= VEX_HWCAPS_ARM64_VFP16;
 
      #undef get_cpu_ftr
-     #undef get_ftr
 
      return True;
    }