0% found this document useful (0 votes)
2 views105 pages

Module - 6 & 7 Compiler

The document discusses the design of code generators in compiler design, focusing on issues such as instruction selection, register allocation, and evaluation order. It outlines the inputs to the code generator, target program architectures, and the importance of producing correct code. Additionally, it covers various aspects of run-time environments, storage allocation, and parallel computing techniques to enhance performance.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views105 pages

Module - 6 & 7 Compiler

The document discusses the design of code generators in compiler design, focusing on issues such as instruction selection, register allocation, and evaluation order. It outlines the inputs to the code generator, target program architectures, and the importance of producing correct code. Additionally, it covers various aspects of run-time environments, storage allocation, and parallel computing techniques to enhance performance.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

BCSE307L

COMPILER DESIGN
Issues in the Design of a Code Generator
They are dependent on specifics of

the intermediate representation,

the target language, and

the run-time system.

Tasks such as instruction selection, register allocation and

assignment, and instruction ordering are encountered in the design of

almost all code generators.


Issues in the Design of a Code Generator

The most important criterion for a code generator is that it

produce correct code.


1. Input to the Code Generator

Intermediate representation of the source program produced by the front end,

along with information in the symbol table.

IR include three-address representations such as quadruples, triples, indirect

triples.

Virtual machine representations such as bytecodes and stack-machine code;

Linear representations such as postfix notation and graphical representations

such as syntax trees and DAG's.


1. Input to the Code Generator

We also assume that all syntactic and static semantic errors have

been detected,

The necessary type checking has taken place, and that type

conversion operators have been inserted wherever necessary.

We assume that the front end has scanned, parsed, and translated

the source program into a relatively low-level IR.


2. The Target Program

The most common target-machine architectures are RISC (reduced

instruction set computer), CISC (complex instruction set computer),

and stack based.

RISC machine typically has many registers, three-address instructions,

simple addressing modes, and a relatively simple instruction-set

architecture.
2. The Target Program

CISC machine typically has few registers, two-address

instructions, a variety of addressing modes, several register

classes, variable-length instructions, and instructions with side

effects.
2. The Target Program

Stack-based machine, operations are done by pushing operands

onto a stack and then performing the operations on the

operands at the top of the stack.

Stack-based architectures were revived with the introduction of

the Java Virtual Machine (JVM).

The JVM is a software interpreter for Java bytecodes, an

intermediate language produced by Java compilers.


2. The Target Program

Producing an absolute machine-language program as output has

the advantage that it can be placed in a fixed location in

memory and immediately executed.

Producing a relocatable machine-language program (often called

an object module) as output allows subprograms to be compiled

separately.
2. The Target Program

A set of relocatable object modules can be linked together and

loaded for execution by a linking loader.

If the target machine does not handle relocation automatically, the

compiler must provide explicit relocation information to the

loader to link the separately compiled program modules.


3. Instruction Selection

The code generator must map the IR program into a code sequence that

can be executed by the target machine.

The complexity of performing this mapping is determined by a factors

such as

the level of the IR

the nature of the instruction-set architecture

the desired quality of the generated code.


3. Instruction Selection

Instruction speeds and machine idioms are other important

factors.

For example, every three-address statement of the form x = y + z,

where x, y, and z are statically allocated, can be translated into

the code sequence.


3. Instruction Selection
3. Instruction Selection
3. Instruction Selection

If the target machine has an "increment" instruction (INC), then

the three-address statement a = a + 1 may be implemented more

efficiently by the single instruction INC


4. Register Allocation

A key problem in code generation is deciding what values to hold

in what registers.

Registers are the fastest computational unit on the target machine,

but we usually do not have enough of them to hold all values.

Instructions involving register operands are invariably shorter

and faster than those involving operands in memory.


4. Register Allocation

The use of registers is often subdivided into two sub-problems:

1. Register allocation: during which we select the set of variables

that will reside in registers at each point in the program.

2. Register assignment: during which we pick the specific register

that a variable will reside in.


4. Register Allocation

Finding an optimal assignment of registers to variables is difficult, even

with single-register machines.

Mathematically, the problem is NP-complete.

Certain machines require register-pairs (an even and next odd

numbered register) for some operands and results.

For example, on some machines, integer multiplication and integer

division involve register pairs.


5. Evaluation Order

The order in which computations are performed can affect the

efficiency of the target code.

Picking a best order in the general case is a difficult NP-complete

problem.
The Target Language

Our target computer models a three-address machine with

load and store operations,

computation operations,

jump operations, and

conditional jumps.
1. A Simple Target Machine Model

We assume the following kinds of instructions are available:

Load operations: The instruction LD dst, addr loads the value in

location addr into location dst. This instruction denotes the

assignment dst = addr. The most common form of this

instruction is LD r, x which loads the value in location x into

register r.
1. A Simple Target Machine Model

Store operations: The instruction ST x, r stores the value in

register r into the location x. This instruction denotes the

assignment x = r.
1. A Simple Target Machine Model

Computation operations of the form OP dst, src1, src2, where OP

is a operator like ADD or SUB, and dst, srcl , and src2 are

locations, not necessarily distinct.

ADD R1, R2, R3 // R1 = R2 + R3

INC X // X = X + 1
1. A Simple Target Machine Model

Unconditional jumps: The instruction BR L causes control to

branch to the machine instruction with label L. (BR stands for

branch.)

Conditional jumps of the form Bcond r, L, where r is a register, L

is a label, and cond stands for any of the common tests on

values in the register r. BLTZ r, L


1. A Simple Target Machine Model

We assume our target machine has a variety of addressing modes:

MODE Form Address

AbsoluteM M M

Register R R R

Indexed C(R) C + Contents (R)

Indirect Register *R Contents(R)

Indirect Index *C(R) Contents (C + Contents (R))

Immediate #C NA
1. A Simple Target Machine Model

1) Absolute:

MOV a, R0

Contents(a)  R0
1. A Simple Target Machine Model

2) Register

MOV R0 , a

Contents(R0)  a
1. A Simple Target Machine Model

3) Indexed C(R) C + Contents (R)

MOV 4(R0), a

Contents (4 + Contents(R0))  a
1. A Simple Target Machine Model

4) Indirect Register *R Contents(R)

MOV *R0, a

Contents (Contents (R0))  a


1. A Simple Target Machine Model

5) Indirect Index *C(R) Contents (C + Contents (R))

MOV *4(R0), a

Contents (Contents (4+ Contents(R0)))  a


2. Program and Instruction Costs
Mode Form Address Address
Cost
Absolute M M 1
Register R R 0
Indexed C(R) C+Contents(R) 1
Indirect *R Contents(R) 0
Register
Indirect *C(R) Contents (C + 1
Indexed Contentes(R))
Immediate #C NA 1
2. Program and Instruction Costs

1 + Cost of source & Destination addressing Mode.

MOV R0 , R1

MOV R0, M

ADD #1, R0

SUB 2(R0), * 8 (R1)


2. Program and Instruction Costs

A= B + C

A, B, C distinct memory locations

MOV b, R0

ADD C, R0

MOV R0, A

Cost = 1 +1+ 0=2 + 2 + 2 = 6


2. Program and Instruction Costs

A=B+C

A, B, C distinct memory locations

MOV B, A

ADD C, A

Cost = 1 +1+ 1=3 +3 = 6


Register Allocation and Assignment
Instructions involving only register operands are faster than

those involving memory operands.

Efficient utilization of registers is vitally important in

generating good code.

In a program, what values should reside in registers (register

allocation) and in which register each value should reside

(register assignment).
Register Allocation and Assignment

Register allocation and assignment is to assign specific values

in the target program to certain registers.

Decide to assign

base addresses to one group of registers,

arithmetic computations to another,

the top of the stack to a fixed register, and so on….


1. Global Register Allocation
Registers is used to hold values for the duration of a single

basic block.

All live variables were stored at the end of each block.

Programs spend most of their time in inner loops.

A natural approach to global register assignment is to try to

keep a frequently used value in a fixed register throughout

a loop.
2. Usage Counts

Assume that the savings to be realized by keeping a

variable x in a register for the duration of a loop L is

one unit of cost for each reference

we count a savings of one for each use of x in loop L that

is not preceded by an assignment to x in the same

block.
2. Usage Counts

use(x, B) is the number of times x is used in B prior to any

definition of x;

live(x, B) is 1 if x is live on exit from B and is assigned a

value in B, and live(x, B) is 0 otherwise.


2. Usage Counts
2. Usage Counts

use(a, B1) + 2 * live (a, B1) = 0 + (2 * 1) = 2

use(a, B2) + 2 * live (a, B2) = 1 + (2 * 0) = 1

use(a, B3) + 2 * live (a, B3) = 1 + (2 * 0) = 1

use(a, B4) + 2 * live (a, B4) = 0 + (2 * 0) = 0

Total = 4
3. Register Assignment for Outer Loops

If an outer loop L1 contains an inner loop L2, the names

allocated registers in L2 need not be allocated registers

in L1 - L2.

If we choose to allocate x a register in L2 but not L1, we

must load x on entrance to L2 and store x on exit from

L2.
4. Register Allocation by Graph Coloring

When a register is needed for a computation but all

available registers are in use.

The contents of one of the used registers must be stored

(spilled) into a memory location in order to free up a

register.
4. Register Allocation by Graph Coloring
Graph coloring is a simple, systematic technique for allocating

registers and managing register spills.

Two passes are used:

Target-machine  instructions are selected

Register-interference graph is constructed in which the nodes are

symbolic registers and an edge connects two nodes if one is live

at a point where the other is defined.


4. Register Allocation by Graph Coloring

k-coloring of G by assigning n a color not assigned to any

of its neighbors.
Run-Time Environments

The compiler creates and manages a run-time

environment in which it assumes its target programs

are being executed.


Run-Time Environments
The environment deals with a variety of issues:

The layout and allocation of storage locations

Mechanisms used by the target program to access variables,

The linkages between procedures,

The mechanisms for passing parameters,

The interfaces to the operating system,

input/output devices, and

other programs.
Storage Organization

The operating system maps the logical addresses into

physical addresses, which are usually spread

throughout memory.

The run-time representation of an object program in the

logical address space consists of data and program

areas.
Storage Organization
Storage Organization

The run-time storage comes in blocks of contiguous bytes,

where a byte is the smallest unit of addressable

memory.
Static Versus Dynamic Storage Allocation
The two adjectives static and dynamic distinguish between

compile time and run time, respectively.

A storage-allocation decision is static, if it can be made by the

compiler looking only at the text of the program, not at

what the program does when it executes.

Conversely, a decision is dynamic if it can be decided only

while the program is running.


Static Versus Dynamic Storage Allocation

Stack storage: Names local to a procedure are allocated

space on a stack.

The stack supports the normal call/return policy for

procedures.
Static Versus Dynamic Storage Allocation

Heap storage: Data that may outlive the call to the

procedure that created it is usually allocated on a

"heap" of reusable storage.

To support heap management, "garbage collection"

enables the run-time system to detect useless data

elements and reuse their storage.


Stack Allocation of Space

void fact(int n)
void main()
{

{ int x // line 1; If(n==0) || (n==1) return 1; // line 4

int x=n; // line 5


x=fact(4); // line 2
int y=fact(n-1)); // line 6
printf(x); // line 3
return x*y; // line 7

} }
Stack Allocation of Space

Each time a procedure is called, space for its local

variables is pushed onto a stack, and when the

procedure terminates, that space is popped off the

stack.
Activation Trees

Each time a procedure is called, space for its local

variables is pushed onto a stack, and when the

procedure terminates, that space is popped off the

stack.
Activation Trees
Activation Trees
Activation Trees
Activation Records

Procedure calls and returns are usually managed by a

run-time stack called the control stack.

Each live activation has an activation record (sometimes

called a frame).
Activation Records
Activation Records
Activation Records
Activation Records
Activation Records
Module 7

78
Parallel Computers

• Parallelism: Doing multiple things at a time


• Things: instructions, operations, tasks
• Main Goal
– Improve performance (Execution time or task throughput)
• Other Goals
– Reduce power consumption
– Improve cost efficiency and scalability, reduce complexity
– Improve dependability: Redundant execution in space

79
Types of Parallelism and How to Exploit Them
• Instruction Level Parallelism
– Different instructions within a stream can be executed in parallel
– Pipelining, out-of-order execution, Dataflow

• Data Parallelism
– Different pieces of data can be operated on in parallel
– Vector processing, array processing

• Task Level Parallelism


– Different “tasks/threads” can be executed in parallel
– Multithreading
– Multiprocessing (multi-core)

80
Task-Level Parallelism: Creating Tasks
• Partition a single problem into multiple related tasks (threads)
– Explicitly: Parallel programming
• Easy when tasks are natural in the problem
– Web/database queries

• Difficult when natural task boundaries are unclear

– Transparently/implicitly: Thread level speculation


• Partition a single thread speculatively

• Run many independent tasks (processes) together


– Easy when there are many processes
• Batch simulations, different users, cloud computing workloads

– Does not improve the performance of a single task

81
Automatic Parallelization

82
• Automatic parallelization, also auto parallelization, or auto
parallelization refers to converting sequential code into multi-
threaded and/or vectorized code in order to use multiple
processors simultaneously in a shared-memory multiprocessor
(SMP) machine.
• Automatic parallelization technique
– Parse
– Analyze
– Schedule
– Code Generation

83
void loop_test(int u) { #pragma loop(hint_parallel(8))
for (int i=0; i<u; ++i) for (int i=0; i<upper_bound(); ++i)
A[i] = B[i] * C[i]; A[i] = B[i] * C[i];
}
The function upper_bound() might change every time
If u could be a small value, the
compiler won't automatically it's called. Because the upper bound cannot be known,
parallelize this loop. the compiler can emit a diagnostic message that
explains why it can't parallelize this loop. The
following example demonstrates a loop that can be
parallelized, a loop that cannot be parallelized, the
compiler syntax to use at the command prompt, and
the compiler output for each command line option:

84
Optimizations for cache locality and
vectorization

85
Optimizations for cache locality
• Optimizations for cache locality and vectorization
are crucial for improving the performance of
programs, especially in modern computing
architectures where memory access and data
parallelism play significant roles.
• Some common techniques used by compilers to
optimize for cache locality and vectorization:

86
• Loop Tiling/Blocking: This technique divides large loops into smaller
blocks that fit into the cache more efficiently. By processing data in smaller
chunks, it reduces cache misses and enhances cache locality.

87
• Loop Interchange: Reordering loop nests to improve spatial
locality, ensuring that consecutive memory accesses are more
likely to access nearby memory locations.
• Loop Unrolling: This optimization technique duplicates loop
bodies to reduce loop overhead and expose more opportunities
for instruction-level parallelism and vectorization.

88
• Data Alignment: Aligning data structures and arrays to
cache line boundaries ensures that data elements are
fetched in optimal-sized chunks, reducing memory
access latency.
• Prefetching: Compiler-generated or hardware-assisted
prefetching techniques can be employed to bring data
into the cache before it is actually accessed by the
program, reducing cache misses.

89
Vectorization
• vectorization, in parallel computing, is a special
case of automatic parallelization, where a
computer program is converted from a scalar
implementation, which processes a single pair of
operands at a time, to a vector implementation,
which processes one operation on multiple pairs
of operands at once.

90
Vectorization

• Exploiting SIMD (Single Instruction, Multiple Data) instructions


to perform parallel computations on multiple data elements
simultaneously. This can significantly enhance performance by
reducing the number of instructions executed per data element.
• Compilers automatically detect opportunities for vectorization
and generate SIMD instructions when appropriate. This can be
augmented by providing hints to the compiler through attributes.

91
• For example, modern conventional computers, including
specialized supercomputers, typically have vector operations that
simultaneously perform operations such as the following four additions
(via SIMD hardware):

Here is an example of such a loop, written in C:


• A vectorizing compiler transforms such loops into sequences of vector operations.
• These vector operations perform additions on blocks of elements from the arrays a,
b and c.

92
Software Pipelining

93
94
95
96
97
98
99
100
101
102
103
104
105

You might also like