Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Types

BitterASM has exactly one built-in type, int. Every other type is declared in BitterASM code, usually in a library:

KindDeclared withExample
Structstructan instruction format, bits<N>, Array<T, N>
EnumenumEndian { Little, Big }, Option<T>
Type aliastypetype Reg = bits<5>

Types can be generic: bits<8> and Array<int, 4> are instances of generic structs.

Why types matter

Types exist only at compile time. The compiler uses them to:

  • Check arguments. A macro that takes a Reg rejects anything that isn’t a Reg.
  • Pick overloads. mov rax, rbx and mov rax, 5 call different mov macros because rbx and 5 have different types. See Overloading.
  • Enforce rules. An invariant such as “fits in 8 bits” or “is even” is checked every time a value of the type is made.
  • Describe output. A struct’s fields tell an evaluator like bitter how to lay out bits. See Packing bytes with bitter.

No automatic conversions

A value never changes type on its own. To turn one type into another, convert it explicitly with as:

from std.binary import bits

macro emit_byte(b: bits<8>) {
    @emit b
}

emit_byte 65 as bits<8>
bits<8> { value: 65 }

Equality

== and != work on any two values: structs compare field by field, and enums compare variant and payload.

In this chapter