Modern ISAs are fundamentally compiler targets, meaning architectural design must align with compiler optimization strategies.

Translation Pipeline

  • Compiler: Translates higher-level source code (.c) into symbolic assembly (.s), assigning variables to registers and organizing memory allocations.
  • Assembler: Converts assembly into a binary object file (.o). Expands pseudoinstructions (simplified mnemonics) into one or more real machine instructions. Generates a symbol table mapping labels to their calculated memory addresses for use by the linker.
  • Linker: Stitches independently assembled object modules and pre-compiled library routines into a single executable. Patches all internal and external address references and relocates each module to its absolute memory mapping.
  • Loader: Transfers the finalized executable from storage to main memory. Initializes the stack pointer and general-purpose registers, then jumps to a startup routine that invokes the program’s main entry point.
  • Java Path: Java source compiles to hardware-independent bytecodes. A software interpreter (the JVM) executes bytecodes on any host. Just-In-Time (JIT) compilers translate frequently executed bytecodes into native machine language at runtime for near-native performance.

Translating high-level logic into machine code introduces the need for standardized communication between discrete subroutines.

Compiler Phases

  • Front End: Language-dependent parsing.
  • High-Level Optimizer: Procedure inlining and loop transformations.
  • Global Optimizer: Global and local optimizations, plus register allocation.
  • Code Generator: Machine-dependent instruction selection.

Basic Blocks

A basic block is a maximal sequence of instructions with no internal branches and no internal branch targets. Compilers decompose a program into a control-flow graph of basic blocks connected by branch edges. This structure enables instruction scheduling and optimization within each block, and data-flow analysis (liveness, reaching definitions) across the graph. Branches connect these blocks to construct loops and if-else structures.

Register Allocation by Graph Coloring

  • Graph coloring maps an unlimited number of virtual registers to a limited set of physical registers.
  • Graph coloring is highly effective but requires all registers to be identical, unreserved, and orthogonal.
  • When the graph cannot be colored (more live values than physical registers), the compiler spills the least-used variables to memory, inserting load/store instructions around their uses.

ISA Design Guidelines for Compilers

  • Provide Regularity (Orthogonality): Operations, data types, and addressing modes should be independent and universally applicable to avoid limiting compiler choices.
  • Provide Primitives, Not Solutions: Avoid highly complex instructions tailored for a single high-level language feature, as compilers struggle to match exact use cases.
  • Simplify Trade-offs: Make the performance costs of alternative code sequences obvious to the compiler.
  • Do Not Bind Constants at Runtime: Do not force the hardware to interpret values dynamically if the compiler can resolve them statically.

Applying these compiler-friendly guidelines and foundational principles results in streamlined, highly efficient architectures like RISC-V.