Question Details

No question body available.

Tags

assembly conditional-statements arm64 instructions

Answers (3)

April 15, 2026 Score: 4 Rep: 147 Quality: Medium Completeness: 80%

After reading the pseudo-code here:

https://developer.arm.com/documentation/ddi0487/mb/-Part-J-Architectural-Pseudocode/-Chapter-J1-A-profile-Architecture-Pseudocode/-J1-4-Shared-pseudocode/-J1-4-508-ConditionHolds?lang=en#aslfuncconditionholds1


// ConditionHolds()
// ================
// Return TRUE iff COND currently holds
func ConditionHolds(cond : bits(4)) => boolean
begin
    // Evaluate base condition.
    var result : boolean;
    case cond[3:1] of
        when ‘000’ => result = (PSTATE.Z == ‘1’);                          // EQ or NE
        when ‘001’ => result = (PSTATE.C == ‘1’);                          // CS or CC
        when ‘010’ => result = (PSTATE.N == ‘1’);                          // MI or PL
        when ‘011’ => result = (PSTATE.V == ‘1’);                          // VS or VC
        when ‘100’ => result = (PSTATE.C == ‘1’ && PSTATE.Z == ‘0’);       // HI or LS
        when ‘101’ => result = (PSTATE.N == PSTATE.V);                     // GE or LT
        when ‘110’ => result = (PSTATE.N == PSTATE.V && PSTATE.Z == ‘0’);  // GT or LE
        when ‘111’ => result = TRUE;                                       // AL
    end;
    // Condition flag values in the set ‘111x’ indicate always true
    // Otherwise, invert condition if necessary.
    if cond[0] == ‘1’ && cond != ‘1111’ then
        result = !result;
    end;
    return result;
end;

It could be seen from this line:

if cond[0] == ‘1’ && cond != ‘1111’ then

That nv (0b1111) condition is treated differently by design, and suggests that it behaves as al (0b1110) condition, which is always taken.

April 15, 2026 Score: 2 Rep: 63,474 Quality: Medium Completeness: 80%

Indeed, the NV condition code counterintuitively means "Always". This is confirmed by the Architecture Reference Manual's table of condition codes in Section C1.2.4 (Table C1-1) which shows the mnemonics AL (1110) and NV (1111) both as meaning "Always": Snippet from ARM Table C1-1 showing NV condition code meaning The footnote on NV says "The Condition code NV exists only to provide a valid disassembly of the 0b1111 encoding, otherwise its behavior is identical to AL".


This raises the question of why it's done this way. The following is my guess at an explanation.

For all the other condition codes, the low bit of the encoding serves to logically invert the condition. For instance, 0010 is "carry set" and 0011 is "carry clear". Since 1110 is "always", you would indeed think that 1111 would be "never", and it must require some extra circuitry to special-case this.

I think the explanation comes from AArch32. In the much older A32 instruction set, most instructions support conditional execution, with the top 4 bits indicating the condition. AArch64 chose the same encodings that A32 was already using, which makes sense because then they could use the same decoding logic.

In A32, 1110 does indeed mean "Always", e.g. the instruction should be executed unconditionally, and the low bit does generally invert the condition. But 1111 is an exception: it's actually not allowed at all as a condition code for an A32 instruction. Instead, 1111 is used for a group of more specialized instructions that do not support conditional execution, and are always executed unconditionally: some ASIMD instructions, hints and memory barriers, and a few "miscellaneous" system instructions. This way they can fit more instructions into the 32-bit encoding space.

(The encoding section of the manual shows some instruction classes as allowing any value in the condition field, such as branches. However, even in this case, using 1111 actually changes the behavior. For example, 0xeb002c2a encodes bl .+0xb0b0 (with condition code 1110, "always"), but changing the condition field to 1111 results in 0xfb002c2a which is blx .+0xb0ba; i.e. an interworking branch that switches to Thumb mode, and with an extra bit used for the immediate relative address.)

So in A32, using 1111 in the condition field of a conditional instruction wouldn't cause the instruction to execute "never"; instead, it would execute (unconditionally) a completely different instruction. Thus it makes a certain amount of sense that the hardware understands 1111 as "this instruction will definitely be executed", and that applying the same logic to AArch64 conditional branches would suggest 1111 meaning "this branch is always taken".

Now there's no particular reason to ever use 1111 in an AArch64 conditional branch (or conditional select, etc) because it's entirely redundant with 1110 which is the intended "always" condition. However, Arm generally has a philosophy for their AArch64 assembly language that every valid encoding should be possible to specify unambiguously in assembly language. If for some reason you want to assemble a branch instruction with condition 1111, there needs to be an assembler mnemonic for it. Likewise, if a disassembler should encounter such an instruction, it needs to output a mnemonic that distinguishes it from a 1110 encoding, even though both would execute identically. (That's what footnote (b) of the table is saying.)

It seems that in choosing the mnemonic for 1111, Arm chose the one that fits the encoding scheme's logic (i.e. the inverse of 1110 AL), even though its actual functionality is the exact opposite. Or maybe it was a sort of joke. Regardless, it's not really intended to be used.

April 15, 2026 Score: 1 Rep: 380,410 Quality: Medium Completeness: 80%

b.nv's mnemonic stands for "Branch Never", but this is misleading, it's actually always taken in AArch64, as you found in your answer. Raymond Chen omitted the NV condition from his table in https://devblogs.microsoft.com/oldnewthing/20220815-00/?p=106975 , instead just mentioning it in a footnote.

If for some reason you want a never-taken conditional branch that still counts for the performance-counter event BRCONDRETIRED, use cbnz on the zero register: cbnz xzr, foo.

(There is no encoding of cbz/cbnz with the stack pointer, only with the zero register. They share the same register-number, with the interpretation depending on the opcode. I'm a bit surprised since testing for a non-zero stack pointer is something you might plausibly do, but XZR's value is fixed at 0 so any fixed-predicate branch on it isn't actually conditional. I tried with GAS and Clang and they both only accept XZR or WZR, not SP.)


In AArch32 state (or for ARMv7 and earlier), there is no bnv. https://azeria-labs.com/arm-conditional-execution-and-branching-part-6/ describes a NV condition as "never executed" for ARM mode (but not for Thumb). However, neither GAS nor clang -target arm accept bnv or addnv, nor even it nv in Thumb mode.

In AArch32 state, any instruction can be predicated, but there is no "NV" condition. As Nate explained, the 1111 encoding for the condition bits instead makes it a different instruction, giving the instruction set room to cram in some more opcodes which don't support predication, e.g. NEON SIMD.