Chapter 5: Intermediate Representations
Chapter Overview
Definition: Intermediate Representation (IR) is a central data structure in a compiler that bridges the source and target programs.
Importance: The design and properties of IR directly affect the cost and effectiveness of compilation.
Types of IR: This chapter focuses on various IR forms used in compilers:
Graphical IR
Linear IRs
Symbol tables
5.1 Introduction
Passes in Compilers: Compilers are organized into a series of passes; they derive information about source code during each pass and must convey this information to subsequent passes through an IR.
Role of IR: The IR is the definitive form of the program in the compiler during translation, rather than the original source code.
Significance of IR Properties: Properties of an IR affect how the compiler reads, writes, and navigates the code.
Compiler Uses: Many modern compilers use multiple IRs at different stages of a single compilation.
Choosing IR: An appropriate IR must be selected based on:
Source language characteristics
Target machine properties
Application requirements
5.1.1 A Taxonomy of Intermediate Representations
Dimensions of IR: IRs can be categorized along three axes:
Structural Organization:
Graphical IRs: Represent knowledge using graphs (nodes and edges).
Example: Parse trees represent the structure of the source code.
Linear IRs: Represent sequential operations and resemble assembly code.
Example: IL code is a form of linear IR.
Hybrid IRs: Combine features of both tree-like and linear representations.
Level of Abstraction: IRs can represent operations at varying levels, from high-level abstractions to low-level, machine-like representations.
Naming Discipline: Strategies for naming intermediate values can affect optimization and the efficiency of code generation.
5.2 Graphical IRs
General Characteristics: Graphical IRs use nodes and edges; they differ in abstraction levels, relationship to code, and graph structure.
5.2.1 Syntax-Related Trees
Parse Trees: Include nodes for every grammar symbol in the derivation and reflect the syntactic structure of the program. They provide complete grammatical information, vital for parsing but can be large.
Example grammar and parse tree showing operation of a multiplication.
Transformations: Transforming the grammar into simpler structures reduces nodes, leading to Abstract Syntax Trees (ASTs), which retain essential meanings but omit extraneous nodes.
5.2.2 Storage Efficiency and Graphical Representations
Limitations of Large ASTs: Large trees can consume memory, complicating compilation processes. Efforts to minimize AST size, such as combining fields and reducing node complexity, have proven beneficial.
DAGs (Directed Acyclic Graphs): A DAG is a reduced form of an AST that eliminates repeated subexpressions through shared nodes, enhancing storage efficiency.
5.2.3 Control-Flow Graphs (CFG)
CFG Definition: Each node represents a basic block, and edges denote control transfers between blocks. They formalize execution paths through code segments and allow analysis for optimization.
Example of a CFG for a simple loop structure.
5.2.4 Data-Dependence Graphs
Data-Dependence Graph: Encodes the relationships between definitions and uses of values, indicating dependencies and sequencing constraints in operations.
Example connections between computational nodes in a code fragment.
5.2.5 Call Graph
Call Graph Definition: Represents calling relationships among procedures, critical for interprocedural analysis and optimization.
Complexities of Call Graphs: Handling separate compilation and ambiguities in procedure calls can complicate the building of accurate call graphs.
5.3 Linear IRs
Linear Representation: Linear IRs depict a sequence of operations, mirroring both source and target code architecture. Common forms include bytecode and three-address code.
Stack-Machine Code: A one-address code manipulating operands through stack operations. It is compact but imposes a transitory nature on results.
Three-Address Code: A three-argument format for operations (target, operand1, operand2). Highly flexible and compatible with many processors, allowing effective optimization opportunities.
5.4 Mapping Values to Names
Naming Disciplines: The effective naming of temporary values has implications for optimization, code generation, and efficiency.
Static Single-Assignment Form (SSA): A favored discipline that ensures every variable is assigned once, using φ-functions to merge values from differing paths in control flow.
5.5 Symbol Tables
Purpose of Symbol Tables: Used for storing various characteristics of identifiers, such as variable names, types, and memory locations. They manage scope and make name lookup efficient during compilation.
Implementation: A hash table structure is often used for quick access, with specific insertion and lookup functions. Nested scopes can also be managed effectively.
5.6 Summary and Perspective
Impact of IR Design: The choice of IR influences compiler structure, design efficiency, and the outcome of the compiled program, demanding careful consideration in implementation. Multiple IRs may be suited for different tasks within a single compilation cycle, reflecting the evolving nature of modern software development processes and languages.