AArch64: report NZCV access for MRS/MSR of NZCV (#3002)

MRS Xt, NZCV reads (and MSR NZCV, Xt writes) the same flags every
flag-setting instruction implicitly defines, but the generated
implicit register lists cannot express it: MRS/MSR are single generic
instructions whose system register is an immediate operand, so LLVM's
static Uses/Defs do not depend on it. The semantic link is already
half-modeled: AArch64_check_updates_flags() sets update_flags for
MSR NZCV, yet neither direction surfaces in the detail register lists
or cs_regs_access().

Add the aliased NZCV register to the implicit lists when MRS reads or
MSR writes it. The 128-bit pair forms MRRS/MSRR are deliberately
excluded: NZCV is not a valid 128-bit system register, so those
encodings are UNDEFINED rather than flag accesses (an exhaustive
2^32-word cross-check against an external NZCV-liveness model caught
exactly this over-match in an earlier revision of the patch).

Add detail tests covering both directions, a TPIDR_EL0 control case,
and exact-match register lists for MRRS/MSRR.
This commit is contained in:
Andrew Gaul 2026-07-23 01:13:58 +09:00 committed by GitHub
parent 4eb90672f3
commit d770dd020c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 106 additions and 0 deletions

View file

@ -379,6 +379,40 @@ static void AArch64_check_updates_flags(MCInst *MI)
#endif // CAPSTONE_DIET
}
/// Surfaces system-register accesses which alias an architectural
/// register Capstone models. MRS Xt, NZCV reads (and MSR NZCV, Xt
/// writes) the same flags every flag-setting instruction implicitly
/// defines, but the generated implicit register lists cannot express
/// it: MRS/MSR are single generic instructions whose system register
/// is an immediate operand, so LLVM's static Uses/Defs do not depend
/// on it. Add the aliased register to the implicit lists so
/// cs_regs_access() reports it like any other NZCV reader/writer.
/// The 128-bit pair forms MRRS/MSRR are deliberately excluded: NZCV
/// is not a valid 128-bit system register, so those encodings are
/// UNDEFINED rather than flag accesses.
static void AArch64_add_sysreg_alias_access(MCInst *MI)
{
#ifndef CAPSTONE_DIET
if (!detail_is_set(MI))
return;
const unsigned opcode = MCInst_getOpcode(MI);
if (opcode != AArch64_MRS && opcode != AArch64_MSR)
return;
cs_detail *detail = get_detail(MI);
for (int i = 0; i < detail->aarch64.op_count; ++i) {
const cs_aarch64_op *op = &detail->aarch64.operands[i];
if (op->type != AARCH64_OP_SYSREG)
continue;
if (op->sysop.sub_type == AARCH64_OP_REG_MRS &&
op->sysop.reg.sysreg == AARCH64_SYSREG_NZCV)
map_add_implicit_read(MI, AARCH64_REG_NZCV);
else if (op->sysop.sub_type == AARCH64_OP_REG_MSR &&
op->sysop.reg.sysreg == AARCH64_SYSREG_NZCV)
map_add_implicit_write(MI, AARCH64_REG_NZCV);
}
#endif // CAPSTONE_DIET
}
static aarch64_shifter id_to_shifter(unsigned Opcode)
{
switch (Opcode) {
@ -934,6 +968,7 @@ void AArch64_printer(MCInst *MI, SStream *O, void * /* MCRegisterInfo* */ info)
AArch64_check_post_index_am(MI, O);
}
AArch64_check_updates_flags(MI);
AArch64_add_sysreg_alias_access(MI);
map_set_alias_id(MI, O, insn_alias_mnem_map,
ARR_SIZE(insn_alias_mnem_map) - 1);
int syntax_opt = MI->csh->syntax;

View file

@ -1614,3 +1614,74 @@ test_cases:
mem_index: x2
access: CS_AC_READ
regs_read: [x1, x2]
-
# MRS/MSR of NZCV access the same flags every flag-setting
# instruction implicitly defines, so cs_regs_access() must report
# them. Other system registers (TPIDR_EL0 control case) are
# unaffected, and the 128-bit pair forms MRRS/MSRR must NOT gain
# an NZCV access: NZCV is not a valid 128-bit system register, so
# those encodings are UNDEFINED rather than flag accesses.
input:
bytes: [ 0x00, 0x42, 0x3b, 0xd5, 0x00, 0x42, 0x1b, 0xd5, 0x42, 0xd0, 0x3b, 0xd5, 0x00, 0x42, 0x7b, 0xd5, 0x00, 0x42, 0x5b, 0xd5 ]
arch: "CS_ARCH_AARCH64"
options: [ "CS_OPT_DETAIL" ]
address: 0x0
expected:
insns:
-
asm_text: "mrs x0, NZCV"
details:
aarch64:
operands:
-
type: AARCH64_OP_REG
reg: x0
access: CS_AC_WRITE
-
type: AARCH64_OP_SYSREG
sub_type: AARCH64_OP_REG_MRS
sys_raw_val: 0xda10
cc: AArch64CC_Invalid
regs_read: [ nzcv ]
regs_write: [ x0 ]
-
asm_text: "msr NZCV, x0"
details:
aarch64:
operands:
-
type: AARCH64_OP_SYSREG
sub_type: AARCH64_OP_REG_MSR
sys_raw_val: 0xda10
-
type: AARCH64_OP_REG
reg: x0
access: CS_AC_READ
cc: AArch64CC_Invalid
regs_read: [ x0 ]
regs_write: [ nzcv ]
-
asm_text: "mrs x2, TPIDR_EL0"
details:
aarch64:
operands:
-
type: AARCH64_OP_REG
reg: x2
access: CS_AC_WRITE
-
type: AARCH64_OP_SYSREG
sub_type: AARCH64_OP_REG_MRS
sys_raw_val: 0xde82
cc: AArch64CC_Invalid
regs_write: [ x2 ]
-
asm_text: "mrrs x0, x1, NZCV"
details:
# Exact-match register lists: x0/x1 only, no NZCV read.
regs_write: [ x0, x1 ]
-
asm_text: "msrr NZCV, x0, x1"
details:
# Exact-match register lists: x0/x1 only, no NZCV write.
regs_read: [ x0, x1 ]