The ARM Cortex series of chips support conditional execution of instructions using the Compare and Branch instructions CBZ and CBNZ. These allow you to efficiently compare a register against zero and conditionally branch based on the result. This provides a way to implement conditional logic and flow control without having to explicitly compare and branch, saving instructions. CBZ and CBNZ are very useful for writing efficient ARM assembly code.
What are CBZ and CBNZ?
CBZ and CBNZ stand for Compare and Branch on Zero and Compare and Branch on Non Zero. As the names suggest:
- CBZ compares a register against zero and branches if the comparison is true (register equals zero)
- CBNZ compares a register against zero and branches if the comparison is false (register does not equal zero)
So CBZ branches if the register contains zero, while CBNZ branches if the register contains a non-zero value. Both instructions perform the comparison implicitly as part of the branch instruction, avoiding the need for a separate CMP instruction.
CBZ and CBNZ Syntax
The syntax for CBZ and CBNZ is: CBZ Rn, label CBNZ Rn, label
Where:
- CBZ or CBNZ is the instruction mnemonic
- Rn is the register being compared against zero
- label is the label to branch to if the condition is true
Some examples: CBZ R1, done ; Branch to done if R1 is zero CBNZ R2, not_zero ; Branch to not_zero if R2 is not zero
How CBZ and CBNZ Work
CBZ and CBNZ work by performing an implicit comparison of the specified register against zero and branching based on the result:
- The value in the register Rn is compared against zero
- If the comparison matches the condition (zero for CBZ, non-zero for CBNZ), branch to the specified label
- If the comparison does NOT match, execution continues to the next instruction
So CBZ branches if Rn equals zero, while CBNZ branches if Rn does not equal zero. This avoids having to explicitly compare using CMP and then branch – it is done in one instruction.
Registers Supported
CBZ and CBNZ work with the following registers:
- ARM core registers R0-R12
- Stack pointer R13 (SP)
So you can efficiently compare and branch on any general purpose register, including the stack pointer.
Conditional Flags
An important thing to note is that CBZ and CBNZ do NOT set conditional flags in the APSR register. So you cannot follow them with other conditional instructions that depend on flags, like IT or BCC.
Range of Branch Offsets
CBZ and CBNZ support both forward and backward branches. The branch offset range is:
- Backward branches: -1MB to -4 bytes
- Forward branches: +4 bytes to +1MB
So you can branch to a label up to 1MB ahead or behind the current PC. This allows for efficient conditional branching without the need to first load a target address into a register.
Conditional Execution
CBZ and CBNZ allow you to conditionally execute code based on a register’s value. Some examples: // Branch over code if R1 is non-zero CBZ R1, done … code to execute if R1 was zero … done: // Branch to error if R2 is zero CBNZ R2, no_error … error handling code … no_error:
This allows you to avoid explicitly comparing and branching, saving instructions. CBZ/CBNZ essentially combine a compare and branch in one instruction.
Usage Examples
Here are some examples of how to use CBZ and CBNZ for conditional branching in ARM assembly:
Looping Based on Counter
loop: … code … SUB R1, R1, #1 ; Decrement counter CBNZ R1, loop ; Loop back if not zero
CBNZ can be used to loop back as long as the counter R1 is non-zero. Once R1 hits zero, the loop ends.
Error Checking
… BL func ; Call function CBZ R0, error ; Branch to error if result is zero … error: … handle error …
CBZ can branch to error handling code if the function result in R0 is zero, indicating an error.
Optional Code Blocks
CBZ R3, skip_block ; Skip block if R3 is zero … optional code … skip_block:
Here CBZ is used to optionally skip over a block of code if a controlling register R3 is zero.
Boolean Tests
CMP R4, #1 CBNZ R4, is_true ; Branch if R4 was not zero … code for false case … is_true: … code for true case …
CBNZ can be used after a CMP to branch based on the result of a comparison, implementing an if/else type structure.
Advantages of CBZ and CBNZ
Some key advantages of using CBZ and CBNZ:
- More efficient than explicit compare and branch
- Avoid flag setting overhead
- Encapsulate compare and branch in one instruction
- Support forward and backward branching
- Work on all general purpose registers including SP
This makes them ideal for conditional branching in performance-critical ARM assembly code.
Summary
CBZ and CBNZ are very useful ARM instructions that allow conditional branching based on testing a register against zero.
Key points:
- CBZ branches if the register contains zero
- CBNZ branches if the register contains non-zero
- No need to explicitly compare and branch
- Do not set conditional flags
- Support large branch ranges
- Useful for conditional code blocks, error checking, loops
CBZ and CBNZ help optimize conditional logic and flow control without the overhead of separate compare and branch instructions.
Related Instructions
Here are some other ARM instructions related to CBZ and CBNZ:
CMP – Compare
Performs a comparison that sets flags for conditional branching: CMP R0, R1 ; Compare R0 to R1 BGT done ; Branch if greater than
IT – If-Then
Conditionally executes the next 1-4 instructions based on flags: CMP R2, #5 IT GT ADDGT R0, R1, R2 ; Conditionally add SUBGT R3, R4, #1 ; Conditionally sub
B.cond – Conditional Branch
Branches based on conditional flags like LT, GT, EQ: CMP R1, R2 BLT target ; Branch if less than
TST – Test Bits
Performs bitwise AND to set flags for conditional code: TST R5, #0x1 BEQ zero ; Branch if bit was zero
These provide other ways to perform conditional testing and branching in ARM assembly.
Conclusion
CBZ and CBNZ are very efficient conditional branch instructions supported on ARM Cortex chips. They allow you to compare a register against zero and branch based on the result in one instruction. This avoids explicit compare and branch sequences.
CBZ and CBNZ are useful for conditionally executing code blocks, error checking, early exits, loops, and other conditional logic constructs in ARM assembly. Proper utilization of these instructions can optimize performance-critical sections of ARM code.