,

Contents ยท ISAs (x86-64, ARMv8, RISC-V)


What is an ISA? Contract between HW and SW

  • Defines registers, instruction set, memory model, exceptions, and privileged architecture.
  • Microarchitectures implement the ISA with different pipelines, caches, predictors.
  • Binary compatibility hinges on stable ISA/ABI across generations.

Instruction formats and encoding

  • x86-64: variable-length CISC encoding with prefixes, opcodes, ModR/M, SIB, displacement/immediate.
  • ARMv8/RISC-V: fixed or few fixed lengths (AArch64 32-bit, RISC-V 32-bit with 16-bit C extension).
  • Endianness: x86 little-endian; ARM bi-endian (AArch64 typically little); RISC-V little.
RISC-V R-type: funct7 | rs2 | rs1 | funct3 | rd | opcode

Registers, calling conventions, and ABIs

  • x86-64 SysV: rdi,rsi,rdx,rcx,r8,r9 args; Win64 differs; callee-saved vs caller-saved sets.
  • AArch64: x0..x7 args, x19..x28 callee-saved; SP, FP, LR, PC conventions.
  • RISC-V: a0..a7 args, s0..s11 callee-saved; ABI dictates stack and varargs.
// C function attributes for multiversioning (GCC)
__attribute__((target("avx2"))) int f(int* a){ /* ... */ }
__attribute__((target("default"))) int f(int* a){ /* scalar */ }

Privilege levels and exception model

  • x86: ring model (0..3), system instructions, MSRs, syscalls/sysenter.
  • ARMv8: EL0..EL3 exception levels; SPSR/ESR/ELR; SVC/SMC for traps.
  • RISC-V: M/S/U privilege; CSRs, traps, and delegation.
Syscall ABIs differ across OS/ISA; libc abstracts most differences.

Memory models: TSO, RCsc, barriers

  • x86-64 TSO: strong ordering, store buffer; fences for I/O and rare reorderings.
  • ARMv8/RISC-V: weaker models require explicit barriers for cross-core ordering.
  • Language-level atomics map to ISA fences (mfence, dmb, fence rw,rw).
#include 
atomic_thread_fence(memory_order_seq_cst);

x86-64 overview (legacy, SSE/AVX, system)

  • Legacy baggage (x87, segmented addressing) coexists with modern SSE/AVX/AVX-512.
  • CPUID for feature discovery; XSAVE/XRSTOR for context switch management.
  • System: paging, long mode, VMX/SMX; fast syscalls via SYSCALL/SYSRET.
#include 
int has_avx2(){ unsigned a,b,c,d; __cpuid_count(7,0,a,b,c,d); return (b & (1<<5))!=0; }

ARMv8-A overview (AArch64, NEON/SVE)

  • AArch64: 31 general-purpose 64-bit regs, separate SIMD/FP regs; PC not directly accessible.
  • NEON SIMD and optional SVE (scalable vectors) with predicate-first design.
  • Feature discovery via ID registers and OS HWCAPs; pointer authentication (PAC) optional.
// Linux: query HWCAPs for NEON/SVE
#include 
unsigned long caps = getauxval(AT_HWCAP);

RISC-V overview (RV64I + extensions)

  • Modular ISA: base I plus extensions (M,A,F,D,C,V,B,K...). Profiles define common sets.
  • Simple, fixed encodings; open standard with many implementations.
  • Toolchain uses -march to select extensions (e.g., rv64gc, rv64gcv).
# GCC/Clang flags
-march=rv64gc -mabi=lp64d

Feature detection and multiversioning

  • Runtime dispatch selects best code path based on CPUID/HWCAP or IFUNC resolvers.
  • Ship fat binaries or use function multiversioning for hot kernels.
  • Maintain scalar fallback for maximum compatibility.
// x86 IFUNC (GNU) example sketch
__attribute__((ifunc("resolve"))) int dot(const float*,const float*);

Compilers, flags, and intrinsics

  • Use -march/-mtune to target ISA and microarchitecture; profile-guided optimizations (PGO) help.
  • Intrinsics headers: immintrin.h (x86), arm_neon.h (NEON), arm_sve.h (SVE), riscv_vector.h (RVV).
  • Disassemble and inspect codegen with objdump/llvm-objdump; verify vector width and instruction mix.
clang -O3 -march=x86-64-v3 -S -o - foo.c | less

Exercises

  1. Write a small function in C and inspect its assembly on x86-64 vs AArch64; note calling convention differences.
  2. Add multiversioning (AVX2/NEON/scalar) to a dot product; dispatch at runtime.
  3. Experiment with -march/-mtune flags on your CPU and measure speedups.
ISAs define the contract; performance and portability come from knowing the knobs and honoring the ABI.