0% found this document useful (0 votes)
10 views

Compiler Design 1-1

Uploaded by

Santhosh Reddy
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views

Compiler Design 1-1

Uploaded by

Santhosh Reddy
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 27

Introduction to Compiler Design

Irfan Rasool
What is a Compiler?
A compiler is a specialized software that converts code written in a high-level programming
language (such as C, C++, Java) into machine code (binary or assembly language), which
can be directly executed by a computer's processor.

The process of compiling ensures that human-readable instructions (source code) are
translated into the low-level instructions that the machine can understand and execute.

For example:

● Source language: C++ (high-level)


● Target language: x86 Assembly or Binary (low-level)
What is a Compiler?
Key Characteristics of a Compiler

● One-Time Translation: A compiler typically translates the entire program in one go,
unlike an interpreter that processes code line by line during runtime.
● Generates Executable: Once the code is compiled, it produces an executable file that
can be run independently of the source code and the compiler.
● Error Checking: During the compilation process, the compiler checks the source code
for syntactic and semantic errors before generating the executable. These errors must
be fixed by the programmer before the program can run successfully.
Where We Use Compilers
● Parsers for HTML in web browser
● Interpreters for javascript/flash
● Machine code generation for high level languages
● Software testing
● Program optimization
● Malicious code detection
● Design of new computer architectures
○ Compiler-in-the-loop hardware development
● Hardware synthesis: VHDL to RTL translation
● Compiled simulation
○ Used to simulate designs written in VHDL
○ No interpretation of design, hence faster
Why Do We Need Compilers?
Compilers are essential for several reasons:

● Portability: A program written in a high-level language can be compiled to run on


different hardware architectures, making the software more portable across different
systems.
● Optimization: Compilers can optimize code in ways that make it more efficient,
reducing resource consumption (CPU, memory) and increasing execution speed.
● Error Checking: Compilers help identify errors during the development phase, catching
syntax and semantic issues before the program is run.
Complexity of Compiler
● A compiler is possibly the most complex system software and writing it is a substantial
exercise in software engineering
● The complexity arises from the fact that it is required to map a programmer’s
requirements (in a HLL program) to architectural details
● It uses algorithms and techniques from a very large number of areas in computer
science
● Translates intricate theory into practice - enables tool building
Nature of Compiler Algorithms
Draws results from mathematical logic, lattice theory, linear algebra, probability, etc.

● type checking, static analysis, dependence analysis and loop parallelization, cache
analysis, etc.

Makes practical application of

● Greedy algorithms - register allocation


● Heuristic search - list scheduling
● Graph algorithms - dead code elimination, register allocation
● Dynamic programming - instruction selection
● Optimization techniques - instruction scheduling
● Finite automata - lexical analysis
● Pushdown automata - parsing
● Fixed point algorithms - data-flow analysis
● Complex data structures - symbol tables, parse trees, data dependence graphs
● Computer architecture - machine code generation
Language Processing System
Compiler Overview and Structural Phases of Compiler
1. Lexical Analysis
2. Syntax Analysis
3. Semantic Analysis
4. Intermediate Code Generation
5. Code Optimization
6. Code Generation
7. Linking and Assembly
Lexical Analysis
Converts source code into a stream of tokens.

Functions:

● Removes whitespace and comments.


● Identifies keywords, operators, and identifiers.
● Outputs a token stream for the syntax analyzer.

Example:

● Input: int a = 5;
● Output: INT_KEYWORD IDENTIFIER ASSIGNMENT_OPERATOR CONSTANT SEMICOLON
Syntax Analysis (Parsing)
Converts token stream into a parse tree or abstract syntax tree (AST).

Functions:

● Ensures the program follows the grammar rules of the language.


● Produces an AST representing the syntactical structure.

Parsing Techniques:

● Top-Down Parsing (LL)


● Bottom-Up Parsing (LR)
Semantic Analysis
Ensures semantic correctness of the source code.

Functions:

● Type checking (e.g., int vs. float).


● Scope resolution (ensuring variables are declared before use).
● Consistency checks (e.g., function argument types).

Example:

● Input: int a; float b = a + 5;


● Output: Type mismatch error (int + float).
Intermediate Code Generation
Generates an intermediate code (IR) between high-level source and low-level machine code.

Purpose: Makes the compiler portable across different architectures.

Forms of IR:

● Three-Address Code (TAC)


● Control Flow Graphs (CFG)
● Abstract Syntax Tree (AST)
Code Optimization
Refines the intermediate code to improve performance.

Goals:

● Reduce instruction count.


● Improve memory utilization.
● Decrease execution time.

Optimization Techniques:

● Constant folding.
● Dead code elimination.
● Loop unrolling.
Code Generation
Translates optimized intermediate code into machine code or assembly.

Challenges:

● Register allocation.
● Instruction selection for the target architecture.

Example:

● A high-level expression like x = y + z is translated to assembly:


● MOV R1, y
● ADD R1, z
● MOV x, R1
Linking and Assembly
Finally, the machine code generated for individual modules (if the program is split across
multiple files) is linked together with external libraries and system resources.

The code is then assembled into a final executable binary file, ready to run on the target
machine.
Error Handling
Types of Errors:

● Lexical Errors: Misuse of symbols, unrecognized tokens.


● Syntax Errors: Mismatched parentheses, incorrect statement structure.
● Semantic Errors: Type mismatches, undeclared variables.

Error Recovery Strategies:

● Panic-mode recovery.
● Phrase-level recovery.
Compiler vs. Interpreter
● Compiler:
○ Translates the entire program at once.
○ Produces an independent executable.
○ Faster execution after compilation but slower to compile.
● Interpreter:
○ Translates and executes code line-by-line.
○ Does not produce an independent executable.
○ Slower execution time but faster to start running.
● Compilers generate machine code, whereas interpreters interpret
intermediate code
● Interpreters are easier to write and can provide better error
messages (symbol table is still available)
● Interpreters are at least 5 times slower than machine code
generated by compilers
● Interpreters also require much more memory than machine code
generated by compilers
● While both compilers and interpreters translate high-level code
into machine-understandable instructions, they differ in how they
do this.
Types of Compilers
There are various types of compilers depending on the target language, source language, or
the system they are designed for:

● Single-pass Compilers: These compilers go through the source code once and produce
machine code. They are typically fast but less powerful.
● Multi-pass Compilers: These compilers go through the source code multiple times (in
different passes), performing deeper analysis and optimizations.
● Cross Compilers: Compilers that generate machine code for a different architecture
than the one the compiler is running on.
● Just-in-Time (JIT) Compilers: A combination of an interpreter and compiler. JIT
compiles code during runtime for faster execution (used in environments like Java and
.NET).
Applications of Compilers
1. Translation of High-Level Languages to Machine Code

One of the primary applications of compilers is translating high-level programming


languages (such as C, C++, Java, Python) into machine code that the processor can execute
directly. This translation allows human-readable code to be efficiently executed by
computers.

● Cross-platform Development: Compilers allow code to be written once in a high-level


language and then compiled to run on different hardware architectures, including x86,
ARM, and PowerPC. This ensures portability and flexibility in software development.
Applications of Compilers
2. Optimizing Program Performance
Compilers are crucial in optimizing the code to improve its performance. The optimization
phase of a compiler transforms code into a more efficient version without altering its
intended behavior.
● Code Optimization Techniques:
○ Loop Optimization: Reducing the overhead in loops by techniques like loop
unrolling or strength reduction.
○ Dead Code Elimination: Removing portions of the code that are never executed or
have no effect on the program.
○ Inlining Functions: Reducing function call overhead by inlining small functions
directly into the calling code.
○ Constant Folding: Precomputing constant expressions during compilation to avoid
recalculation during runtime.
These optimizations are especially useful in real-time systems, game development, and
high-performance computing, where speed and efficiency are critical.
Applications of Compilers
3. Facilitating Multi-language Interoperability

Compilers enable different programming languages to work together by providing


interoperability between them. For example, compilers can allow cross-language function
calls and enable shared data structures between languages.

Example Applications:

● Java Native Interface (JNI): Java programs can call native C or C++ code for tasks
requiring high performance, like hardware interfacing or system-level operations.
● .NET Framework: The Common Language Runtime (CLR) in .NET compiles code from
various languages (C#, VB.NET, F#) into a common intermediate language (CIL),
enabling them to work seamlessly together.
Applications of Compilers
4. Enabling Just-in-Time (JIT) Compilation

Just-in-Time (JIT) compilers are used in managed runtime environments like the Java Virtual Machine (JVM)
and .NET's Common Language Runtime (CLR). JIT compilation involves compiling code during runtime rather
than before execution, offering several advantages:

● Performance Optimization at Runtime: JIT compilers can make optimizations based on the program’s
execution environment, such as CPU architecture, memory availability, and typical data patterns.
● Adaptive Execution: The compiler can optimize code paths that are frequently used while keeping
rarely used code in a less optimized state. This is particularly effective in dynamic languages like
JavaScript or Java.

JIT compilers are heavily used in environments that require a balance between flexibility and performance,
such as:

● Web Browsers (JavaScript JIT in Chrome’s V8 engine).


● Virtual Machines (JVM in Java and Dalvik/ART in Android).
Applications of Compilers
5. Compilers in Embedded Systems
Embedded systems (such as those in smartphones, IoT devices, and automotive control systems)
require compilers to generate highly optimized machine code due to limited hardware resources.
Embedded systems often have:
● Limited memory.
● Lower processing power.
● Real-time constraints.
Compilers designed for embedded systems optimize for size, power consumption, and real-time
performance. They are responsible for:
● Cross-compilation: Generating machine code for the embedded device's architecture on a
development machine.
● Memory Management: Ensuring minimal memory footprint to prevent system overload.
● Real-time Efficiency: Ensuring that time-sensitive tasks are executed within strict deadlines
(e.g., in automotive control systems).
Applications of Compilers
6. Compiler-Assisted Software Development Tools
Compilers serve as the backbone of many Integrated Development Environments (IDEs) and
tools used by developers to build, debug, and maintain software applications. They provide
real-time feedback on code correctness, errors, and potential performance improvements.

● Static Analysis Tools: Many compilers can perform static code analysis during the
compilation process. This helps in detecting:
○ Syntax errors.
○ Type mismatches.
○ Potentially dangerous code patterns (such as buffer overflows or memory leaks).
● Integrated Debugging: Compilers generate debugging information that allows tools like
gdb or Visual Studio Debugger to step through source code, monitor variables, and set
breakpoints.
○ These features enhance developer productivity and help ensure that software is both correct
and efficient.
Applications of Compilers
7. Supporting Parallel and Distributed Computing

Compilers for parallel computing and distributed systems translate code into machine
instructions that can execute efficiently on multiple processors or distributed systems. They
play a key role in:

● Multithreading: Compilers can automatically parallelize certain code segments (e.g.,


loops) to run on multiple threads, taking advantage of modern multi-core CPUs.
● Distributed Processing: Some compilers target distributed systems, generating code
that can efficiently share data and resources across different machines in a network
(e.g., MPI compilers).

Applications in High-Performance Computing (HPC):

● Scientific computing, simulations, and machine learning benefit from compilers that
generate parallel code to exploit supercomputing resources.
Applications of Compilers
8. Dynamic Language Compilation

In languages like Python, JavaScript, and Ruby, compilers play a key role in improving
execution efficiency through techniques like Just-in-Time (JIT) compilation and
ahead-of-time (AOT) compilation.

● Ahead-of-Time Compilation (AOT): Some compilers generate bytecode or machine


code from dynamic languages before runtime, improving the startup performance and
reducing runtime overhead.
● Just-in-Time Compilation (JIT): Dynamic languages that are interpreted (such as
JavaScript) often utilize JIT compilers to convert frequently executed code paths into
machine code, optimizing performance during execution.

You might also like