HomeARM CourseDay 11
DAY 11 · THE INSTRUCTION SET

Flags & Conditional Execution

By EcrioniX · Updated Jun 6, 2026

On Day 8 the S suffix set the flags. Today we use them — and meet ARM's most distinctive trick: almost any instruction can run conditionally. It's how ARM does if without always paying for a branch.

1. Recap: the flags drive decisions

From Day 3, the CPSR holds four condition flags: N (negative), Z (zero), C (carry), V (overflow). Instructions with the S suffix (or CMP/TST) set them. Now we read them to decide what runs.

2. Condition codes

A two-letter condition code names a test on the flags. Here are the ones you'll actually use:

CodeMeaningType
EQ / NEequal / not equal (Z=1 / Z=0)any
GE / LT≥ / < (signed)signed
GT / LE> / ≤ (signed)signed
HI / LS> / ≤ (unsigned)unsigned
HS / LO≥ / < (unsigned) (=CS/CC)unsigned
MI / PLnegative / positive-or-zerosign
VS / VCoverflow set / clearoverflow
ALalways (the default)

💡 Signed vs unsigned — don't mix them up

Comparing signed integers? Use GT/LT/GE/LE. Comparing addresses, sizes, or unsigned values? Use HI/LS/HS/LO. Picking the wrong family is a classic bug — e.g. an address comparison that breaks when the top bit is set.

3. Conditional branches (the familiar use)

The everyday use is on branches: do the compare, then branch on the result.

CMP r0, #10 ; set flags from r0 - 10 BEQ equal ; branch if r0 == 10 (Z set) BGT bigger ; branch if r0 > 10 (signed) BHI bigger_u ; branch if r0 > 10 (unsigned)

4. ARM's signature: conditional everything

Here's what makes classic ARM special. You can append a condition code to almost any instruction, not just branches. If the condition is false, the instruction simply does nothing (a no-op) — no branch needed:

; if (r0 == 0) r1 = 1; else r1 = 2; — with NO branches CMP r0, #0 MOVEQ r1, #1 ; runs only if Z=1 (r0 was 0) MOVNE r1, #2 ; runs only if Z=0

Both moves are fetched; the CPU executes the one whose condition holds and skips the other. No branch means no pipeline flush (Day 7) — for a short if, that's faster and tighter.

A neat example — branchless GCD

; while (r0 != r1) subtract smaller from larger gcd: CMP r0, r1 SUBGT r0, r0, r1 ; if r0 > r1: r0 -= r1 SUBLT r1, r1, r0 ; if r0 < r1: r1 -= r0 BNE gcd ; loop until equal

The whole conditional body is two predicated instructions — elegant and flush-free.

5. Combine with the S suffix carefully

A conditional instruction can also set flags by adding S (e.g. ADDEQS → written ADDSEQ). Order matters in syntax, and over-using flag-setting inside a conditional chain can break the next condition — keep chains short and deliberate.

6. Thumb and A64

✅ The mental model

Compute → set flags (S / CMP) → react. React with a conditional branch, or — ARM's trick — a conditional instruction that runs only when the flags agree. Short ifs become a couple of predicated instructions with no branch penalty.

🎯 Day 11 takeaways

Quick check

  1. To compare two memory addresses, do you use BGT or BHI?
  2. What does MOVNE r1, #2 do when Z=1?
  3. Why can predicating a short if be faster than branching?

FAQ

What is conditional execution?

Appending a condition code so an instruction runs only when the flags match (e.g. ADDEQ); otherwise it's a no-op. ARM's signature feature.

Signed vs unsigned conditions?

Signed: GT/LT/GE/LE. Unsigned: HI/LS/HS/LO. Use unsigned for addresses and sizes.

Does A64 still have it?

A64 keeps conditional branches and conditional-select (CSEL) but dropped general predication of every instruction.

Previous
← Day 10: Load/store & addressing

← Back to the full course roadmap