Skip to content

ARM64 Instructions — Arithmetic, Logical and Shift Operations

DodaTech Updated 2026-06-28 7 min read

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

ARM64 provides arithmetic, logical, and shift instructions with conditional execution, flexible operand options, and a uniform 4-byte encoding for all operations.

What You'll Learn

  • Arithmetic instructions (add, sub, mul, div)
  • Logical operations (and, orr, eor)
  • Shift and rotate operations
  • Conditional instructions
  • Multiply and divide variants

Why It Matters

ARM64 instructions are simple but powerful — each can include a shift or extend as part of the operation. Durga Antivirus Pro uses ARM64 NEON for signature scanning on Android devices.

Real-World Use

Mobile app development, embedded firmware, game engines on ARM platforms, and performance-critical code on Apple Silicon.

flowchart LR
    A["ARM64 Instructions"] --> B["Arithmetic"]
    A --> C["Logical"]
    A --> D["Shift"]
    A --> E["Multiply"]
    B --> F["add, sub, cmp"]
    C --> G["and, orr, eor"]
    D --> H["lsl, lsr, asr"]
    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

Basic Arithmetic

// Addition
add x0, x1, x2          // x0 = x1 + x2
add x0, x1, #42         // x0 = x1 + 42 (immediate)
adds x0, x1, x2         // x0 = x1 + x2, sets flags

// Subtraction
sub x0, x1, x2          // x0 = x1 - x2
sub x0, x1, #42         // x0 = x1 - 42
subs x0, x1, x2         // x0 = x1 - x2, sets flags

// Compare (subs but result discarded)
cmp x0, x1              // sets flags based on x0 - x1
cmp x0, #42             // sets flags based on x0 - 42

// Negative
neg x0, x1              // x0 = -x1

Arithmetic with Shift

// Shift as part of arithmetic — free!
add x0, x1, x2, lsl #3     // x0 = x1 + (x2 << 3)
sub x0, x1, x2, lsr #2     // x0 = x1 - (x2 >> 2)
add x0, x1, x2, asr #4     // x0 = x1 + (x2 >> 4, sign-extended)

// Equivalent to multiply by power of 2
add x0, x1, x2, lsl #3     // x0 = x1 + x2 * 8
sub x0, x1, x2, lsl #1     // x0 = x1 - x2 * 2

Multiply and Divide

// Basic multiply
mul x0, x1, x2          // x0 = x1 * x2
mneg x0, x1, x2         // x0 = -(x1 * x2)

// Multiply-accumulate
madd x0, x1, x2, x3     // x0 = x1 * x2 + x3
msub x0, x1, x2, x3     // x0 = x1 * x2 - x3

// Unsigned multiply high
umulh x0, x1, x2        // x0 = high 64 bits of x1*x2

// Divide
udiv x0, x1, x2         // x0 = x1 / x2 (unsigned)
sdiv x0, x1, x2         // x0 = x1 / x2 (signed)

// 64-bit multiply -> 128-bit
smull x0, w1, w2        // x0 = w1 * w2 (signed, 32x32->64)
umull x0, w1, w2        // x0 = w1 * w2 (unsigned, 32x32->64)

Logical Operations

// Bitwise
and x0, x1, x2          // x0 = x1 & x2
orr x0, x1, x2          // x0 = x1 | x2
eor x0, x1, x2          // x0 = x1 ^ x2 (xor)
bic x0, x1, x2          // x0 = x1 & ~x2 (bit clear)
orn x0, x1, x2          // x0 = x1 | ~x2
eon x0, x1, x2          // x0 = x1 ^ ~x2

// With immediate (shifted)
and x0, x1, #0xFF       // mask lower byte
orr x0, x1, #0xFF00     // set upper byte bits

// Logical with shift
and x0, x1, x2, lsl #8
orr x0, x1, x2, lsr #4

// Move/negate
mov x0, x1              // x0 = x1 (alias: orr x0, xzr, x1)
mvn x0, x1              // x0 = ~x1 (alias: orn x0, xzr, x1)

Shift Operations

// Logical shifts
lsl x0, x1, #5          // x0 = x1 << 5
lsr x0, x1, #5          // x0 = x1 >> 5 (zero fill)

// Arithmetic shift (preserves sign)
asr x0, x1, #5          // x0 = x1 >> 5 (sign extend)

// Rotate right
ror x0, x1, #5          // x0 = rotate right by 5

// Shift by register
lslv x0, x1, x2         // x0 = x1 << x2
lsrv x0, x1, x2
asrv x0, x1, x2
rorv x0, x1, x2

Conditional Instructions

// Conditional select
csel x0, x1, x2, eq     // if (eq) x0=x1 else x0=x2
csinc x0, x1, x2, ne    // if (ne) x0=x1 else x0=x2+1
csinv x0, x1, x2, ge    // if (ge) x0=x1 else x0=~x2
csneg x0, x1, x2, lt    // if (lt) x0=x1 else x0=-x2

// Conditional set
cset x0, eq             // x0 = (eq) ? 1 : 0
csetm x0, ne            // x0 = (ne) ? -1 : 0

// Conditional increment/decrement
cinc x0, x1, gt         // if (gt) x0=x1+1 else x0=x1
cinv x0, x1, le         // if (le) x0=~x1 else x0=x1

Flag Setting Instructions

// Instructions ending with 's' set flags
adds x0, x1, x2         // set N,Z,C,V flags
subs x0, x1, x2
ands x0, x1, x2         // sets N,Z,C (V=0 for and)

// Compare
cmp x0, x1              // alias: subs xzr, x0, x1
cmn x0, x1              // alias: adds xzr, x0, x1 (compare negative)
tst x0, x1              // alias: ands xzr, x0, x1 (bit test)

Common Mistakes

1. Forgetting ARM64 immediate limitations

Immediates are 12-bit values with optional 12-bit shift. Not all 32-bit values are encodable. Use movz/movk for arbitrary constants.

2. Using subs when cmp is cleaner

cmp x0, x1 is clearer than subs xzr, x0, x1. Both set flags the same way.

3. Not using shifts in arithmetic

ARM64 includes free shifts in most data-processing instructions. add x0, x1, x2, lsl #3 avoids a separate shift instruction.

4. Wrong register width

mul x0, w1, w2 is invalid. Multiply requires matching widths: mul x0, x1, x2 or smull x0, w1, w2.

5. Division by zero

ARM64 does not trap on division by zero. The result is 0. Check for zero divisor explicitly.

Practice Questions

1. What does add x0, x1, x2, lsl #3 compute?

x0 = x1 + (x2 << 3) = x1 + x2 * 8

2. How do you conditionally select between two values in ARM64?

csel x0, x1, x2, condition — selects x1 if condition is true, x2 otherwise.

3. What is the difference between lsr and asr?

lsr shifts right with zero fill (logical). asr shifts right with sign extension (arithmetic).

4. How do you set flags without keeping the result?

Use cmp for subtraction flags, cmn for addition flags, tst for AND flags.

Challenge: Write ARM64 code that computes the absolute difference of two numbers without branches.

Solution
// |x0 - x1| without branches
subs x2, x0, x1          // diff = x0 - x1, set flags
cneg x0, x2, mi          // if (negative) x0 = -x2 else x0 = x2

FAQ

{{< faq question="Does ARM64 have a dedicated shift instruction?" >}} Yes, lsl, lsr, asr, ror. But shifts are also available as part of most arithmetic/logical operations. {{< /faq >}}

{{< faq question="How do I load a large 64-bit constant?" >}} Use movz (move wide with zero), movn (move wide with NOT), and movk (move keep) in sequence. {{< /faq >}}

{{< faq question="Does ARM64 have popcount or bit reverse?" >}} Yes. cls (count leading sign bits), clz (count leading zeros), rbit (reverse bits), and cnt (popcount in NEON). {{< /faq >}}

{{< faq question="How many cycles does a multiply take on ARM64?" >}} Multiplication is typically 3-4 cycles. Multiply-accumulate (madd) is the same cost as multiply. {{< /faq >}}

{{< faq question="Can ARM64 do integer division in hardware?" >}} Yes, all ARM64 CPUs have hardware udiv and sdiv instructions, typically taking 4-12 cycles depending on operand size. {{< /faq >}}

Mini Project

Write an ARM64 function that computes the factorial of a number using multiply and conditional instructions.

.global _start

.section .text
factorial:
    mov x1, #1          // result = 1
loop:
    mul x1, x1, x0      // result *= n
    subs x0, x0, #1     // n-- and set flags
    bgt loop             // if n > 0 continue
    mov x0, x1           // return result
    ret

_start:
    mov x0, #5
    bl factorial
    // x0 = 120

    mov x8, #93
    svc #0

What's Next

Now that you understand ARM64 instructions, proceed to ARM64 memory operations.

Topic Description Link
ARM memory Memory access patterns {{< ref "23-arm-memory" >}}
ARM branches Conditional branches {{< ref "24-arm-branches" >}}
ARM intro ARM64 architecture {{< ref "21-arm-intro" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro