Assembly (x86-64, System V AMD64 ABI)
This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].
1 Assessment
This page assesses Assembly in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.
Core coupling 10/100 · Ecosystem coupling 58/100 · Engineering readiness 68/100
1.1 Core coupling
1.1.1 Type
- ❌ There is no type system: An operand is a register or a memory address of a fixed width, and the assembler does not check that an integer, a pointer, and an address are used consistently (T1)
- ❌ No sum types: An alternative must be encoded by hand in a register or in a memory layout the programmer invents (T4)
- ❌ No generics: A macro can repeat a sequence, but there is no type parameter through which a repeated sequence can be checked (T5)
- ❌ No namespaces: Every global symbol shares one flat namespace in the object file, so a collision is resolved by naming convention (T6)
- ❌ No null safety: A null pointer is a zero register value, and a load from it is an ordinary instruction (T10)
- ❌ No module boundary: Separate translation units share the same global symbol space and ABI, and the only encapsulation is the assembler’s local label (T11)
1.1.2 Construction
- ❌ Registers are global mutable state: Every instruction reads and writes the same fixed register file, so the meaning of a register depends on the whole instruction stream (C1)
- ❌ Mutability is the only mode: Nothing is immutable unless a section is marked read-only, and that is enforced by the loader rather than by a type (C2)
- ❌ Construction is allocation: Creating a value means writing a register or a memory cell that must already have been reserved (C3)
- ❌ No constructor or validation: A value is legal only because the programmer kept it legal, and nothing checks it (C4)
- ✅ Macros decouple a repeated sequence:
.macroin GAS and%macroin NASM let a reusable instruction sequence be named and expanded, so a repeated pattern is written once - ✅ Sections separate data from code:
.text,.data,.rodata, and.bssplace each kind of content in its own segment of the object file
1.1.3 Memory
- ❌ Every allocation and deallocation is manual: The stack frame, the
malloccall, and thefreecall are the programmer’s responsibility (M1) - ❌ No release tied to scope: There is no RAII; nothing runs at the end of a block (M3)
- ❌ No ownership model: A pointer may alias, dangle, or be freed twice, and the assembler does not see pointers as a type (M4)
- ✅ The stack is a structured allocation:
push/popand thersp/rbpframe give a scoped allocation with a defined lifetime for the duration of a call - ⚠️ The layout is the ABI’s, not the program’s: The System V AMD64 ABI fixes which registers are caller-saved and how the stack frame is formed, so the layout choices are constrained
1.1.4 Error
- ❌ There is no structured error path: No exception, no error type, and no return-result convention; a failed call is signalled by a flag or a register the caller must test (Er1)
- ❌ An error can be ignored by design: A non-zero value in
raxor a set carry flag does nothing unless the code branches on it (Er3) - ❌ No composable result type: An error cannot be returned as a value the caller’s code carries in a checked way (Er4)
- ✅ Flags give a structured condition test:
cmpandtestsetZF,CF,SF, andOF, and a conditional jump branches on a named condition - ⚠️ The recovery path is a jump target: Recovery is a branch to a label, so its structure exists only in the programmer’s discipline
1.1.5 Concurrency
- ❌ Shared mutable state is the default: Registers and memory are equally visible to every core, and there is no notion of private state (K2)
- ❌ Synchronisation is manual with no race detection: A lock is a
lockprefix on one instruction or a call into a library, and nothing analyses the program for a race (K3) - ❌ No structured concurrency: A thread is a
cloneorpthread_createcall, and nothing scopes or joins its lifetime (K4) - ✅ Atomic operations are instruction-level:
lock cmpxchg,lock xadd, andmfenceexpress a defined atomic primitive directly - ✅ The
lockprefix gives a defined ordering: It serialises the decorated instruction across the cores of the machine - ⚠️ The memory ordering is reasoned about by hand: The x86-64 model is comparatively strong, but nothing in the language states or checks the ordering the algorithm needs
1.1.6 Platform
- ❌ The program is bound to one instruction set: x86-64 opcodes do not run on AArch64 or RISC-V, so the source is not portable across architectures (P1)
- ❌ The calling convention is one ABI: A call must agree with the System V AMD64 register and stack rules, so the function interface is fit for one platform’s convention
- ✅ The object format is documented and stable: An ELF object file is linked with the platform linker through a published format
- ✅ The assembler is a choice within the target: GAS and NASM produce code for the same instruction set
- ⚠️ Windows uses a different x86-64 convention: The Microsoft x64 ABI differs from System V in its argument registers and its shadow space, so the source is bound to one operating-system convention as well
- ⚠️ Extension instructions need runtime dispatch: AVX-512 and the other vector extensions require a CPU feature check before they are used
1.1.7 Trade-offs (not coupling)
- Full machine control is the point: The absence of an abstraction layer is a deliberate removal, not a defect
- Assembly is a last resort by design: It is used where a higher-level language cannot express the operation
- A hand-written kernel can repay the discipline cost: The performance-critical kernel and the cryptographic primitive are the cases where it does
- Reverse engineering is a use, not an application: Disassembly is read, not maintained as a product
- The dialect choice is a surface choice: The Intel and AT&T notations name the same instruction set
- Inline assembly inside a host language is a scoping decision: What remains is a coupling to the compiler’s
asmsyntax
1.2 Ecosystem coupling
1.2.1 Performance
- ✅ The program is the machine code: There is no interpreter, JIT, or runtime between the source and the CPU, so the cost of an instruction is the instruction
- ✅ Instruction selection is under program control:
lea, fused operations, and vector instructions can be chosen for each site - ✅ There is no runtime start-up cost: A static executable needs no runtime initialisation beyond the loader
- ⚠️ The CPU optimises beyond the instruction stream: Out-of-order execution, branch prediction, and the micro-operation cache mean the source-order cost is not the measured cost
- ⚠️ A hand-written routine can lose to a compiler: Without the optimiser’s cross-block analysis, hand-scheduled and hand-allocated code can be slower than
-O2output
1.2.2 Ecosystem
- ❌ There is no package manager: A dependency is an object file or a linked library, resolved by the linker and the build script (Ec1)
- ✅ The assemblers are standard and maintained: GAS, NASM, and the LLVM assembler are widely available
- ✅ The code interoperates with C at the ABI: An assembly routine can be called from C and can call C
- ⚠️ The reusable corpus is small: Most assembly source is written for one program and its ABI, so few libraries exist as assembly source
1.2.3 Evolution
- ✅ The instruction set is specified and backward compatible: x86-64 adds encodings and keeps the earlier ones
- ✅ New extensions are opt-in: SSE, AVX, and AVX-512 code is gated by CPU feature detection rather than by the instruction set as a whole
- ✅ The manuals are public and maintained: The Intel SDM and the AMD APM document each instruction
- ⚠️ Assembler syntax is not standardised across tools: GAS and NASM disagree on directives, operand order, and macro syntax
- ⚠️ A microarchitecture change invalidates tuning: Code hand-scheduled for one CPU generation can be mistuned on the next
1.2.4 Domain
- ✅ The kernel-level operations are the native domain: Hardware access, interrupt entry, context switch, and boot code
- ✅ Some instructions are reachable only here: The special registers,
cpuid,rdtsc, and the privileged instructions have no higher-level equivalent - ✅ Vector kernels benefit: A hand-tuned SIMD routine writes exactly the instruction the target supports
- ❌ It is a poor fit above the machine: With no types, no data abstraction, and no error handling, ordinary application logic is expensive to write and to change (D2)
- ⚠️ Even kernel work is mostly C with small assembly islands: The fit is real but narrow
1.2.5 Learning
- ❌ The curve is extremely steep: The programmer must hold the register file, the instruction set, and the target ABI in mind at once
- ❌ There is no abstraction to learn instead of the machine: Nothing is hidden and nothing is named above the instruction level
- ✅ The reference material is complete and public: Intel and AMD document every instruction and every flag
- ✅ The machine gives immediate feedback: A debugger shows the registers and the memory directly
- ⚠️ Three bodies of knowledge are separate subjects: The instruction set, the ABI, and the toolchain must each be learned on its own
1.2.6 Syntax
- ⚠️ The Intel and AT&T dialects differ in the same instruction set: Operand order reverses and the register and constant prefixes differ, so the notation is a toolchain choice
- ⚠️ The directive syntax differs by assembler: GAS uses
.globland.section, NASM usesglobalandsection - ⚠️ Every operand form is spelled out:
%rax,$42, and8(%rbp)write the addressing mode at each site - ✅ The mnemonics are short and uniform:
mov,add, andjmpeach name one operation - ⚠️ A label is a fixed position in the code: A label names a location for a jump, a call, or a data symbol, so a transfer target is coupled to that position in the listing
1.2.7 Testing
- ❌ There is no test framework in the language: A test is a program that runs and compares, and the harness lives outside the assembler (G1)
- ❌ Debugging is low-level: The debugger exposes registers and raw memory, so a fault is found in machine state rather than in a named abstraction (G2)
- ✅ A single-step debugger is exact:
gdbandlldbcan run one instruction and show every register and flag - ✅ Repetition is deterministic: With no scheduler and no collector, the same input produces the same instruction sequence
- ⚠️ The usual check is a test in a higher-level language: The assembly routine is called from C and compared there
1.2.8 Security
- ❌ Every memory-safety class is open: A buffer overflow, a use-after-free, a double free, and an out-of-bounds write are all possible and nothing prevents them (Q1)
- ❌ Return-oriented programming is possible: The stack holds return addresses that are writable, so a memory write can redirect control flow
- ❌ There is no bounds checking and no guard page by default: An array index is an address calculation
- ⚠️ Stack canaries, ASLR, and CET are external mitigations: They come from the toolchain flags, the loader, and the operating system, and the source must cooperate with them
- ✅ Non-executable and write-or-execute pages are available: The operating system can mark the stack non-executable and enforce
W^X, which removes some code injection - ✅ The sandbox is the operating system’s: A program runs with the privileges the loader grants it, not with a language-level capability model
1.3 Engineering readiness
1.3.1 Reproducibility
- ✅ No charged mechanism in this dimension.
1.3.2 Supply chain
- ⚠️ Published packages carry no signed provenance by default.
1.3.3 Observability
- ⚠️ No stable tracing and metrics standard.
- ⚠️ The standard library has no structured logging.
1.3.4 Feedback
- ✅ No charged mechanism in this dimension.
1.3.5 Interop
- ⚠️ No maintained portable target.
1.3.6 Governance
- ⚠️ The specification is not public.
1.4 Profile
Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.
1.4.1 Energy and cost
What this axis asks: what does a running program cost in energy and time?
1.4.2 AI and agent readiness
What this axis asks: how well does a coding model and an agent work in this language?
- ❌ Sparse training data: models write it unreliably [8].
1.4.3 Safety certification
What this axis asks: is there a certified or standards-qualified path to a safety-critical system?
- ⚠️ No certified or standards-qualified path for safety-critical systems.
1.5 Scoring detail
1.5.1 Core coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Type | T1 |
dynamic no static check | ✅ | −15 |
T2 |
weak static | — | 0 | |
T3 |
dynamic/duck dispatch | — | 0 | |
T4 |
no sum types | ✅ | −4 | |
T5 |
no generics | ✅ | −4 | |
T6 |
no namespaces | ✅ | −8 | |
T7 |
type erasure/reflection | — | 0 | |
T8 |
operator overloading | — | 0 | |
T9 |
inheritance required for polymorphism | — | 0 | |
T10 |
no null-safety | ✅ | −8 | |
T11 |
no module boundary | ✅ | −8 | |
T12 |
no effect tracking | ✅ | −8 | |
T13 |
async/sync colouring | — | 0 | |
T14 |
unchecked foreign boundary | ✅ | −4 | |
| Type subtotal | −15 (−59) | |||
| Construction | C1 |
global mutable state by default | ✅ | −15 |
C2 |
no immutability by default | ✅ | −4 | |
C3 |
constructors coupled to allocation | ✅ | −4 | |
C4 |
no constructor/validation | ✅ | −4 | |
| Construction subtotal | −15 (−27) | |||
| Memory | M1 |
manual deallocation | ✅ | −15 |
M2 |
GC non-deterministic pauses | — | 0 | |
M3 |
no RAII | ✅ | −4 | |
M4 |
no ownership model | ✅ | −8 | |
| Memory subtotal | −15 (−27) | |||
| Error | Er1 |
no structured path | ✅ | −15 |
Er2 |
unchecked exceptions only | — | 0 | |
Er3 |
errors ignored/non-exhaustive | ✅ | −4 | |
Er4 |
no composable result type | ✅ | −4 | |
| Error subtotal | −15 (−23) | |||
| Concurrency | K1 |
GIL/no parallel | — | 0 |
K2 |
shared mutable state default | ✅ | −8 | |
K3 |
manual sync no race detection | ✅ | −8 | |
K4 |
no structured concurrency | ✅ | −4 | |
K5 |
no primitives | — | 0 | |
| Concurrency subtotal | −15 (−20) | |||
| Platform | P1 |
cannot function outside one vendor/domain | ✅ | −15 |
P2 |
single-vendor but other uses | — | 0 | |
P3 |
legacy/discontinued platform | — | 0 | |
| Platform subtotal | −15 |
Core Dimensions score: 10/100 — total cost = −90 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.2 Ecosystem coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Performance | R1 |
interpreted no AOT/JIT | — | 0 |
R2 |
GC pauses | — | 0 | |
R3 |
JIT warmup | — | 0 | |
| Performance subtotal | 0 | |||
| Ecosystem | Ec1 |
no package manager | ✅ | −4 |
Ec2 |
fragmented tooling | — | 0 | |
Ec3 |
small/niche/discontinued | — | 0 | |
Ec4 |
proprietary | — | 0 | |
Ec5 |
platform-locked tooling | — | 0 | |
Ec6 |
monoculture | — | 0 | |
Ec7 |
legacy burden | — | 0 | |
| Ecosystem subtotal | −4 | |||
| Evolution | V1 |
breaking changes | — | 0 |
V2 |
frozen | — | 0 | |
V3 |
discontinued | — | 0 | |
V4 |
glacial standards | — | 0 | |
| Evolution subtotal | 0 | |||
| Domain | D1 |
domain/vendor lock-in | — | 0 |
D2 |
poor domain fit | ✅ | −8 | |
D3 |
framework forces architecture | — | 0 | |
| Domain subtotal | −8 | |||
| Learning | L1 |
steep | — | 0 |
L2 |
large surface/multi-paradigm | — | 0 | |
L3 |
sparse docs | — | 0 | |
| Learning subtotal | 0 | |||
| Syntax | S1 |
verbose | — | 0 |
S2 |
inconsistent | — | 0 | |
S3 |
punctuation-heavy | — | 0 | |
| Syntax subtotal | 0 | |||
| Testing | G1 |
weak tooling | ✅ | −8 |
G2 |
poor debugging | ✅ | −8 | |
G3 |
runtime-only errors | — | 0 | |
| Testing subtotal | −15 (−16) | |||
| Security | Q1 |
memory-unsafe | ✅ | −15 |
Q2 |
dynamic/weak typing risk | — | 0 | |
Q3 |
known vulnerability/injection | — | 0 | |
| Security subtotal | −15 |
Meta Dimensions score: 58/100 — total cost = −42 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.3 Engineering readiness Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Reproducibility | Y1 |
build not reproducible | — | 0 |
Y2 |
dependencies not pinned | — | 0 | |
| Reproducibility subtotal | 0 | |||
| Supply chain | Y3 |
no signed provenance | ✅ | −8 |
Y4 |
registry without namespacing | — | 0 | |
| Supply chain subtotal | −8 | |||
| Observability | Y5 |
no stable tracing standard | ✅ | −8 |
Y6 |
no structured logging | ✅ | −4 | |
| Observability subtotal | −12 | |||
| Feedback | Y7 |
slow build | — | 0 |
Y8 |
no fast in-editor analysis | — | 0 | |
| Feedback subtotal | 0 | |||
| Interop | Y9 |
no foreign-function interface | — | 0 |
Y10 |
no portable target | ✅ | −4 | |
| Interop subtotal | −4 | |||
| Governance | Y11 |
single-vendor governance | — | 0 |
Y12 |
closed specification | ✅ | −8 | |
| Governance subtotal | −8 |
Readiness Dimensions score: 68/100 — total cost = −32 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
Verdict: Assembly’s design produces the lowest core score in this comparison, because it removes every separating construct rather than misplacing one. There is no type system, no release tied to scope, no structured error path, and no concurrency construct; the register file is global mutable state, and the machine is the only abstraction. What the language does give is exact control: the programmer chooses the instruction, the register, and the schedule, and the flags give a defined condition test. Main strengths: no runtime overhead, direct access to the privileged instructions, and a fully public specification. Main weaknesses: the source is bound to one instruction set and one ABI, every memory-safety class is open, and the work is expensive to learn, to test, and to maintain.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- On the Nature of Coupling: All Coupling as Shared Resource Access — Zenodo, 2026 — doi:10.5281/zenodo.21925122
- Coupling as a Consequence of Cohesion: Decomposition, the Coupling Floor, and Domain-Imposed Coupling — Zenodo, 2026 — doi:10.5281/zenodo.21925201
- On the Nature of Cohesion: Cohesion as Correctness Relative to a Partitioning Rule — Zenodo, 2026 — doi:10.5281/zenodo.21703193