Skip to content

ARM64 Branches — Conditional Execution and Control Flow

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about ARM64 Branches. We cover key concepts, practical examples, and best practices to help you master this topic.

ARM64 branches use b/bl/br instructions with condition codes (eq, ne, lt, gt) and conditional select instructions for branchless control flow optimization.

What You'll Learn

  • Unconditional and conditional branches
  • Compare and branch instructions
  • Branch with link (function calls)
  • Conditional select (branchless)
  • Table branches

Why It Matters

Branches affect performance through branch prediction. ARM64 offers conditional select for branchless code. Durga Antivirus Pro uses branchless techniques in ARM64 signature scanning for consistent performance.

Real-World Use

Control flow in any program, branchless cryptography, switch statements, function dispatch, and loop constructs.

flowchart LR
    A["ARM64 Branches"] --> B["Unconditional"]
    A --> C["Conditional"]
    A --> D["Compare + Branch"]
    A --> E["Conditional Select"]
    B --> F["b, bl, br"]
    C --> G["b.eq, b.ne"]
    D --> H["cbz, cbnz"]
    E --> I["csel, csinc"]
    style A fill:#2563eb,stroke:#2563eb,color:#fff
    style B fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style C fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style D fill:#dbeafe,stroke:#2563eb,color:#1e40af
    style E fill:#dbeafe,stroke:#2563eb,color:#1e40af

Unconditional Branches

// Simple branch (up to +/-128MB)
b label                 // branch to label
b label2                // branch backwards also possible

// Branch with link (call)
bl function             // save return address in X30, branch
ret                     // return (branch to X30)

// Branch to register
br x0                   // branch to address in x0 (indirect)
blr x1                  // call through x1 (indirect call with link)

// Return
ret                     // alias: br x30
ret x1                  // return to address in x1

Condition Codes

Code Condition Flags Meaning
eq Equal Z=1 ==
ne Not equal Z=0 !=
hs/cs Higher/same C=1 Unsigned >=
lo/cc Lower C=0 Unsigned <
mi Minus/negative N=1 < 0
pl Plus/positive N=0 >= 0
vs Overflow V=1 Overflow
vc No overflow V=0 No overflow
hi Higher C=1 & Z=0 Unsigned >
ls Lower/same C=0 or Z=1 Unsigned <=
ge Greater/equal N==V Signed >=
lt Less than N!=V Signed <
gt Greater than Z=0 & N==V Signed >
le Less/equal Z=1 or N!=V Signed <=

Conditional Branches

cmp x0, x1
b.eq equal_label         // branch if x0 == x1
b.ne not_equal           // branch if x0 != x1
b.lt less_than           // branch if signed x0 < x1
b.le less_equal          // branch if signed x0 <= x1
b.gt greater_than        // branch if signed x0 > x1
b.ge greater_equal       // branch if signed x0 >= x1

// Unsigned comparisons
cmp x0, x1
b.hi higher              // branch if unsigned x0 > x1
b.ls lower_same          // branch if unsigned x0 <= x1

// Single flag tests
tst x0, #1
b.eq even                // branch if x0 is even

Compare and Branch

// Combine compare and branch (zero/non-zero)
cbz x0, zero_label       // branch if x0 == 0
cbnz x0, non_zero        // branch if x0 != 0

// Combined compare and branch (32-bit)
cbz w0, zero_label
cbnz w0, non_zero

// These save one instruction vs cmp + b.eq

Test and Branch

// Test bit and branch
tbz x0, #3, bit_clear    // branch if bit 3 of x0 is 0
tbnz x0, #3, bit_set     // branch if bit 3 of x0 is 1

// Useful for testing flags packed in a register
// Without needing to shift/mask first

Loop Constructs

// while (x0 > 0) { ... x0--; }
while_loop:
    cmp x0, #0
    b.le while_done
    // loop body
    sub x0, x0, #1
    b while_loop
while_done:

// for (i = 0; i < n; i++)
    mov x1, #0           // i = 0
for_loop:
    cmp x1, x0           // compare i with n
    b.ge for_done
    // loop body using x1 as index
    add x1, x1, #1       // i++
    b for_loop
for_done:

Switch Statements

// Simple jump table
switch:
    cmp x0, #3
    b.hi default_case     // if x0 > 3, go to default

    adr x1, jump_table
    ldr x2, [x1, x0, lsl #3]  // load target address
    br x2

jump_table:
    .quad case_0
    .quad case_1
    .quad case_2
    .quad case_3

case_0: // ...
case_1: // ...
case_2: // ...
case_3: // ...
default_case: // ...

Branchless Code with csel

// Branchless absolute value
// int abs(int x) { return x < 0 ? -x : x; }
abs:
    cmp x0, #0
    cneg x0, x0, lt      // if (lt) x0 = -x0
    ret

// Branchless min/max
min:
    cmp x0, x1
    csel x0, x0, x1, le  // if (le) x0=x0 else x0=x1
    ret

max:
    cmp x0, x1
    csel x0, x0, x1, ge  // if (ge) x0=x0 else x0=x1
    ret

Common Mistakes

1. Using signed conditions for unsigned values

After cmp, signed conditions (lt, gt) interpret values as signed. Use hi, ls for unsigned.

2. Forgetting the condition suffix

b eq is correct. beq is a different encoding (same function, both work in ARM64).

3. Branch range limits

Conditional branches have +/- 1MB range. Unconditional branches have +/- 128MB. Use br for indirect jumps.

4. cbz vs cmp/b.eq performance

cbz is preferred for zero-tests — it decodes in a single instruction. For other conditions, use cmp + b.cond.

5. Not using conditional select for simple branches

csel avoids branch misprediction penalties. Use it for simple 2-way selections.

Practice Questions

1. What register does bl store the return address in?

X30 (Link Register). ret branches to X30.

2. How do you branch if x0 is zero in ARM64?

cbz x0, label (single instruction) or cmp x0, #0; b.eq label (two instructions).

3. What is the difference between b.lt and b.lo?

b.lt is signed less-than. b.lo is unsigned lower (C=0). They use different flag conditions.

4. How do you implement max(a, b) without a branch?

cmp x0, x1; csel x0, x0, x1, ge — selects x0 if x0 >= x1, else x1.

Challenge: Write a branchless function that clamps a value between 0 and 100.

Solution
// int clamp(int x) { return x < 0 ? 0 : (x > 100 ? 100 : x); }
clamp:
    cmp x0, #0
    csel x0, xzr, x0, lt   // if (lt) x0 = 0
    cmp x0, #100
    csel x0, x0, #100, le  // if (le) keep x0 else x0 = 100
    ret

FAQ

{{< faq question="Does ARM64 have a conditional branch on carry?" >}} Yes. b.hs (higher/same = carry set) and b.lo (lower = carry clear) test the carry flag. {{< /faq >}}

{{< faq question="What is the branch penalty on ARM64?" >}} ARM CPUs have shorter pipelines than x86. A mispredicted branch costs ~10 cycles (vs ~15-20 on x86). {{< /faq >}}

{{< faq question="Can I branch to a 32-bit address in ARM64?" >}} Yes, use br with a register holding the target address. b and bl use PC-relative offsets. {{< /faq >}}

{{< faq question="What is a tail call in ARM64?" >}} A function call made as the last operation, reusing the caller's return address: b function instead of bl function; ret. {{< /faq >}}

{{< faq question="How do I branch to a computed address?" >} Load the address into a register and use br x0. Common for virtual method dispatch and switch tables. {{< /faq >}}

Mini Project

Write a binary search implementation using ARM64 conditional branches.

// int binary_search(int* arr, int len, int target)
// x0 = arr, x1 = len, x2 = target
binary_search:
    mov x3, #0           // low = 0
    sub x4, x1, #1       // high = len - 1

loop:
    cmp x3, x4
    b.gt not_found       // low > high -> not found

    add x5, x3, x4
    lsr x5, x5, #1       // mid = (low + high) >> 1

    ldr w6, [x0, x5, lsl #2]  // arr[mid]
    cmp w6, w2
    b.eq found
    b.lt go_right        // arr[mid] < target

    sub x4, x5, #1       // high = mid - 1
    b loop

go_right:
    add x3, x5, #1       // low = mid + 1
    b loop

found:
    mov x0, x5           // return mid
    ret

not_found:
    mov x0, #-1
    ret

What's Next

Now that you understand ARM64 branches, proceed to ARM64 functions.

Topic Description Link
ARM functions Functions and ABI {{< ref "25-arm-functions" >}}
ARM NEON SIMD processing {{< ref "26-arm-neon" >}}
ARM syscalls Linux syscalls {{< ref "27-arm-syscalls" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro