The ISA: The Contract Between Software and Hardware
An instruction set architecture is the specification a compiler targets and a processor promises to honour: which instructions exist, which registers they name, how memory behaves, and how addresses are formed. Everything about how that promise is kept is deliberately outside it.
Software view, hardware view
The gap between what you wrote and what the machine does is where this whole domain lives.
What the contract specifies
An ISA specifies the things software must be able to rely on. Which instructions exist and what each computes. How many architectural registers there are and what they are called. How addresses are formed — the addressing modes (Addressing Modes: How an Index Becomes an Address). What sizes and alignments of memory access are permitted. And, critically for multi-threaded code, what ordering guarantees memory operations have (Hardware Memory Models Are Not Language Memory Models).
That last item is easy to overlook and is genuinely part of the contract. Whether one core is guaranteed to observe another core's writes in the order they were issued is an architectural property, not an implementation detail, and it differs between instruction sets in ways that make correct concurrent code non-portable at the assembly level.
The contract's value is compatibility across time. A binary compiled years ago runs on a processor designed years later because both sides honour the same specification — and honouring it is precisely what allows the newer processor to be internally nothing like the older one.
| Concern | In the ISA? | Why |
|---|---|---|
| Which instructions exist | Yes | Compilers must know what they may emit |
| Number and names of architectural registers | Yes | Instructions encode register names directly |
| Addressing modes | Yes | Address formation is part of instruction semantics |
| Memory ordering guarantees | Yes | Concurrent code correctness depends on it |
| Pipeline depth, cache sizes, execution width | No | Implementation choices — see ISA vs Microarchitecture: The Distinction Everything Depends On |
| Which register holds a function's first argument | No — that is ABI | A software convention layered on top of the ISA |
| How a system call is made | Partly — the instruction is ISA; the numbering is OS | The contract stops at the instruction |
Calling conventions are ABI, not ISA
This distinction is worth isolating because it is confused constantly. The ISA says a register exists and what instructions can do with it. It does not say that it holds a function's first argument, or that another register must be preserved across calls, or how a struct returned by value is passed. Those are application binary interface decisions — conventions that let separately compiled code interoperate.
The evidence that these are separable is that the same ISA carries different ABIs. Which registers are argument-passing, which are callee-saved and how the stack is aligned at a call boundary differ between platforms that share an instruction set entirely. Code compiled for one will not correctly interoperate with code compiled for the other, despite every instruction being valid on both machines.
The practical payoff arrives when reading disassembly. The instruction semantics come from the ISA manual; the meaning of *which* register a value was placed in comes from the ABI document for that platform. Reaching for the wrong one is a reliable way to misread a function prologue.
mov %rdi, %rax ; ISA says: copy one 64-bit register to another.
; ABI says: rdi held the first integer argument,
; and rax is where the return value goes.
; => "return the first argument"
; Same instruction, different platform ABI on the same ISA:
; - a different register may carry the first argument
; - a different set of registers may be callee-saved
; - stack alignment requirements at the call may differ
;
; The instruction is valid either way. The *meaning* is not in the ISA.Why the contract is drawn where it is
The boundary is chosen to maximise what implementers may change. Everything software genuinely needs to depend on is inside; everything else is left out deliberately, so that processor designers can restructure the machine freely between generations without invalidating any existing binary.
This is the same principle as an API contract in software — and it fails in the same way when the boundary leaks. Software that depends on unspecified behaviour, such as the timing of particular instructions or the size of a cache, works on the machine it was developed on and breaks on the next one. That is not the hardware betraying the contract; it is software having relied on something the contract never promised.
It also explains why ISAs accumulate extensions rather than being redesigned. Removing an instruction breaks binaries; adding one does not. The result is instruction sets that grow monotonically and carry decades of history, which is a compatibility success story that happens to look like accumulated mess.
- In the contract: instructions, architectural registers, addressing modes, memory ordering.
- Outside it: caches, pipeline depth, execution width, instruction timing.
- Adjacent to it: the ABI, which is a software convention on top of the ISA.
- Why: so implementations can change freely without breaking existing binaries.
- The failure mode: depending on unspecified behaviour, which works until the next machine.
| Depending on… | Portable across generations? | Why |
|---|---|---|
| An instruction existing | Yes, within the declared baseline | It is in the contract |
| Memory ordering guarantees | Yes, within one ISA | Architectural — but differs between ISAs |
| An instruction's cycle count | No | Microarchitectural; changes between generations |
| A specific cache size | No | Not in the contract at all |
| Argument register placement | Only within one ABI | A platform convention, not the ISA |
Key points
- The ISA specifies instructions, architectural registers, addressing modes and memory ordering — what software may rely on.
- It deliberately omits caches, pipeline depth, width and timing, so implementations can change freely.
- Calling conventions are ABI, not ISA: the same instruction set carries different ABIs on different platforms.
- The contract exists to make binaries portable across generations and vendors.
- Depending on unspecified behaviour works on the machine you tested and breaks on the next one.
Follow the mechanism
The path through the machine, hop by hop — and the conclusions it invites that are wrong.
- 1Source → compiler: code is lowered to instructions drawn from the target ISA's defined set.
- 2Compiler → ABI: argument placement, callee-saved registers and stack alignment follow the platform convention, not the ISA.
- 3Binary → processor: the processor decodes instructions according to the architectural specification it implements.
- 4Processor → microarchitecture: how those instructions are actually executed is unconstrained by the contract.
- 5Retirement → architectural state: results become visible in the order and form the ISA promises, whatever happened internally.
- • "It ran on my machine, so it is a valid binary for this ISA." It may require extensions your deployment target lacks.
- • "The ISA tells me how fast this instruction is." It defines what the instruction computes. Cost is a microarchitectural property.
- • "Calling conventions are part of the instruction set." They are ABI, and they differ between platforms sharing the same ISA.
Consequences, controls and cost
- • A binary compiled years ago runs on hardware designed years later, because both honour the same published contract.
- • Code that depends on instruction timing or cache size is not portable even across processors of the same ISA.
- • Object files built with different ABIs on the same ISA cannot be linked together correctly, despite every instruction being valid.
- • Target the ISA baseline your deployment actually guarantees rather than the machine you happen to build on.
- • Depend only on documented architectural behaviour; treat observed timing as measurement, never as specification.
- • When reading disassembly, use the ISA manual for semantics and the platform ABI document for register meaning.
- • Use compiler flags to select instruction set extensions explicitly, so binaries do not silently require newer hardware.
- • Inspect a binary for the instruction set extensions it actually uses before deploying to hardware with a different baseline.
- • Compare disassembly against the ISA manual for semantics and the platform ABI for register roles when a function boundary looks wrong.
- • Verify at load time, or at build time, that the required extensions are present rather than discovering it as an illegal instruction fault.
- • Targeting a conservative ISA baseline maximises portability and leaves newer instructions — and their performance — unused.
- • Targeting a newer baseline produces better code and a binary that will not start on older hardware.
Scope
§224 — what these claims are specific to.
- ISA-SPECIFICWhat is included in the contract differs between instruction sets; memory ordering guarantees in particular vary substantially between x86-64 and AArch64.
- ABI-SPECIFICCalling conventions, callee-saved register sets and stack alignment are platform decisions layered on the ISA and differ between operating systems on identical hardware.
Misconceptions
Where the rest of this lives
Which ISA baseline and which extensions a compiler targets is a build-time decision that determines both the instructions emitted and the minimum hardware the binary will start on.