aarch64-pg
4.6 · Conditionals in ARMv8 AArch64 Assembly

Conditionals in ARMv8 AArch64 Assembly

note

The Assembly language discussed in this lesson refers specifically to ARMv8 AArch64 Assembly.

prerequisite

Before starting this lesson, you should have a basic understanding of the following prerequisite lessons:

Every program, from simple scripts to complex operating systems, must make decisions. Whether determining if a user entered the correct password, checking whether a value exceeds a limit, or choosing between different execution paths, conditional logic is a fundamental part of programming. In high-level languages, these decisions are hidden behind convenient constructs such as if and else statements, allowing programmers to control program behaviour with minimal effort. However, implementing the same decision-making process in ARMv8 AArch64 Assembly is not as simple.

In high-level programming languages, such as C, conditional logic can be implemented using built-in language constructs (if constructs). For example:

#include <stdio.h>int main() {    int a = 6;    int b = 7;    if (a > b) {        printf("XX\n");    } else {        printf("XY\n");    }    return 0;}

The code above compares the values stored in the variables a and b. "XX" will be printed if the value of a is greater than b; otherwise, "XY" is printed.

At the Assembly level, the processor does not understand high-level abstractions such as if and else. Instead, conditional behaviour is created by directly controlling the flow of execution through comparisons, condition codes, and branch instructions. This lesson explores how ARMv8 AArch64 Assembly implements conditional logic and reveals the low-level mechanisms that allow programs to make decisions.

The Big Picture

Conditional constructs in Assembly can be achieved by comparing values and changing the program's execution flow based on the resulting condition flags. The process can be summarised as follows:

  1. Compare two values using the cmp instruction.
  2. Use a branch instruction (b.cond) to jump to a specific label when the condition is satisfied.

note

b.cond is not an actual Assembly instruction. The cond part represents a placeholder for a condition code. Replacing cond with a specific condition code creates the actual branch instruction, such as b.eq, b.ne, or b.gt. See the b.cond entry in the Reference for an interactive breakdown of every condition code.

More about cmp and b.cond

The cmp instruction is the compare instruction. It compares the contents of two registers, or the contents of a register with an immediate value, and serves as the foundation for conditional logic in Assembly.

Comparing two values is essentially a way of checking whether a particular condition is met. However, the cmp instruction only performs the comparison; it does not take any action based on the result.

Instead, the comparison updates the condition flags in the background. These flags record information about the result of the comparison.

Instructions that use a condition code, such as b.cond, examine these condition flags. If the specified condition code (cond) is met, b.cond branches to the specified label. Otherwise, execution continues with the instruction immediately following b.cond.

Implementing Conditionals in Assembly

The above C code can be translated into Assembly as follows:

    // Pseudo-ops and directives    ..main:    // Program prologue    ..    mov     x19, 6              // int a = 6;    mov     x20, 7              // int b = 7;    cmp     x19, x20            // Compare a and b: if (a > b)    b.gt    greaterThan         // Jump to `greaterThan` if the condition code is met    // else block    // If the condition code is not met, execution continues with the instruction    // immediately following the conditional branch.    ldr     x0, =fmt2           // Load the address of the "XY" string    bl      printf              // Call printf    b       end                 // Branch to the label `end`    // Branching to the label `end` is necessary here.    // Otherwise, execution would continue into the code under the `greaterThan`    // label and execute the if block after completing the else block.greaterThan:                    // `greaterThan` is a label    ldr     x0, =fmt1           // Load the address of the "XX" string    bl      printf              // Call printf    // Branching to the label `end` is not necessary here because execution    // naturally continues to the instruction immediately following this block,    // which is the `end` label.end:                            // `end` is a label    // Code block after the if-else construct    ..    // Program epilogue
Open in playground

The assembly order does not have to match the order of the original if-else statement. In this example, the else block is placed first because b.gt only jumps when the condition is true. If the condition is false, execution naturally continues to the next instruction, which is the else block. After completing the else block, an unconditional branch (b end) to the label end is used to skip over the if block and prevent both blocks from executing.

The b.gt instruction checks whether the first value being compared is greater than the second value. In this case, it checks the condition flags updated by cmp x19, x20 to determine whether the value in x19 is greater than the value in x20.

The Complete Assembly Implementation

The code below demonstrates the complete process of implementing conditionals in Assembly, including the pseudo-ops, function prologue, and function epilogue.

loading editor...
figure 4.6.1runnable — step it and watch the registersOpen in playground