0% found this document useful (0 votes)
6 views53 pages

Compiler2018 Big Picture

Compiler2018_big_picture

Uploaded by

kalapepe
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)
6 views53 pages

Compiler2018 Big Picture

Compiler2018_big_picture

Uploaded by

kalapepe
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
You are on page 1/ 53

Compiler 2018:

Big Picture
Lequn Chen
March 16, 2017
Why Compilers Course

• Improve programming skills


• ~10k loc

• Very important project experience in your CV


• programming skills
• perseverance
What Are Compilers

Compiler
Source Language Target Language
gcc
C Linux x86-64 ELF
javac
Java JVM Bytecode
your compiler
M* Linux x86-64 Assembly
What Are Virtual Machines
• Interpreter
• JIT Optimization focused on Code Generation

Java JVM on x86-64 Linux

Kotlin JVM on x86-64 Windows

JVM Bytecode
Scala JVM on x86-64 macOS

Clojure JVM on Embedded Systems

• Language Features
• Optimization based on High Level Semantics
not Low-Level Virtual Machine anymore

LLVM
• Code Generation
• Transforms and Optimizations

C/C++ x86-64

Rust RISC

LLVM IR
Swift ARM

Haskell MIPS

• Language Features
• Optimization based on High Level Semantics
not Low-Level Virtual Machine anymore

LLVM
• Code Generation
Front-end • Transforms and Optimizations

C/C++ x86-64

Back-end
Rust RISC

LLVM IR
Swift ARM

Haskell MIPS

• Language Features
• Optimization based on High Level Semantics
Compilers
• Overview
• Front-end → Intermediate Representation → Back-end
• More detail?
• Lexing
• Parsing
• Semantic Analysis
• IR Generation
• IR Optimization
• Code Generation
• Target-dependent Optimizations
About the Course
• Language: whatever you want

• Lexing and parsing library: whatever you want

• Source language: M* (C-and-Java-like)

• Target platform: Linux x86-64 Assembly in NASM

• Additional language features: whatever you want


• as long as it is compatible with the manual
• Optimizations: whatever you want
• as long as you can pass the tests
Lexing & Parsing
Source Code
while f3 < 100 {
f3 = f1 + f2;
f1, f2 = f2, f3;
}
Lexing
while f3 < 100 {
f3 = f1 + f2;
f1, f2 = f2, f3;
}

KEYWORD while
IDENTIFIER f3
SYMBOL <
LITERAL 100
SYMBOL {
IDENTIFIER f3
SYMBOL =
IDENTIFIER f1
SYMBOL +
IDENTIFIER f2
SYMBOL ;
IDENTIFIER f1
SYMBOL ,
IDENTIFIER f2
SYMBOL =
IDENTIFIER f2
SYMBOL ,
IDENTIFIER f3
SYMBOL ;
SYMBOL }
Parsing
while f3 < 100 {
f3 = f1 + f2;
f1, f2 = f2, f3;
}

KEYWORD while Abstract Syntax Tree


IDENTIFIER f3 WHILE
SYMBOL <
EXPRESSION BODY
LITERAL 100
SYMBOL { < STATEMENT STATEMENT
IDENTIFIER f3
SYMBOL = f3 100 ASSIGN UNPACKING
IDENTIFIER f1
SYMBOL + f3 EXPRESSION TARGET SOURCE

IDENTIFIER f2 + f1 f2 f2 f3
SYMBOL ;
IDENTIFIER f1 f1 f2
SYMBOL ,
IDENTIFIER f2
SYMBOL =
IDENTIFIER f2
SYMBOL ,
IDENTIFIER f3
SYMBOL ;
SYMBOL }
Syntax Error
while f3 < 100 {
f3 = f1 + f2;
f1, f2 = f2, f3;
}

KEYWORD while Abstract Syntax Tree


IDENTIFIER f3 WHILE
SYMBOL <
EXPRESSION BODY
LITERAL 100
SYMBOL { <
IDENTIFIER f3
SYMBOL = f3 100 ???
IDENTIFIER f1
SYMBOL +
IDENTIFIER f2
SYMBOL ;
IDENTIFIER f1
SYMBOL ,
IDENTIFIER f2
SYMBOL = Syntax Error: Expect loop body
IDENTIFIER f2
SYMBOL ,
IDENTIFIER f3
SYMBOL ;
SYMBOL }
Syntax Error
while f3 < 100 {
f3 = f1 + f2;
f1, f2 = f2, f3;
}

KEYWORD while Abstract Syntax Tree


IDENTIFIER f3 WHILE
SYMBOL <
EXPRESSION BODY
LITERAL 100
SYMBOL { < STATEMENT STATEMENT
IDENTIFIER f3
SYMBOL = f3 100 ASSIGN UNPACKING
IDENTIFIER f1
SYMBOL + f3 EXPRESSION TARGET SOURCE

IDENTIFIER f2 + f1 f2 f2 f3
SYMBOL ;
IDENTIFIER f1 f1 f2
SYMBOL ,
IDENTIFIER f2
SYMBOL = Syntax Error: Missing }
IDENTIFIER f2
SYMBOL ,
IDENTIFIER f3
SYMBOL ;
SYMBOL }
Parsing: Grammars
stmt: expr NEWLINE
| ID '=' expr NEWLINE
| NEWLINE
;

expr: <assoc=right> expr op='^' expr


| expr op=('*'|'/') expr a = 2
| expr op=('+'|'-') expr
| INT b = 3
| ID c = 4 + a * b
| '(' expr ')'
d = 5 - c * (3 + b) / a
MUL : '*';
DIV : '/';
e = a ^ (b + 10) ^ c ^ d
ADD : '+';
SUB : '-';
ID : Letter LetterOrDigit*
fragment Letter: [a-zA-Z_]
fragment Digit: [0-9]
fragment LetterOrDigit: Letter | Digit
NEWLINE: '\r'? '\n'
WS : [ \t]+ -> skip
Parsing: Grammars
Parsing: Grammars
Expr → Expr + Term Expr → Term Expr’
| Expr - Term Expr’ → + Term Expr’
LL ✘ LL ✔
| Term | - Term Expr’
LR ✔ | Ɛ LR ✔
Term → Term * Factor
| Term / Factor Term → Factor Term’
Term’ → * Factor Term’
Factor → ( Expr ) | / Factor Term’
| Integer | Ɛ

Factor → ( Expr )
| Integer

def Expr():
Expr()
match('+') Infinite Recursion!
Term()
Lexer & Parser?

• Usually, lexer and parser can be completely separated.

• However,

• vector<pair<int, int>>
Pragmatic Solution
• What to do
• Build AST
• Check syntax errors
• Use parser generators, especially, ANTLR 4
• Check https://github.com/antlr/grammars-v4
• Read if you want
• https://abcdabcd987.com/using-antlr4/
• https://abcdabcd987.com/notes-on-antlr4/
Challenge Yourself

• Hand-written lexer and parser


• Check Parsing Techniques: A Practical Guide
Semantic Analysis
Semantic Error
while f3 < 100 {
f3 = f1 + f4;
f1, f2 = f2, f3;
}

KEYWORD while Abstract Syntax Tree


IDENTIFIER f3 WHILE
SYMBOL <
EXPRESSION BODY
LITERAL 100
SYMBOL { < STATEMENT STATEMENT
IDENTIFIER f3
SYMBOL = f3 100 ASSIGN UNPACKING
IDENTIFIER f1
SYMBOL + f3 EXPRESSION TARGET SOURCE

IDENTIFIER f2 + f1 f2 f2 f3
SYMBOL ;
IDENTIFIER f1 f1 f4
SYMBOL ,
IDENTIFIER f2
SYMBOL = Semantic Error: f4 used before declaration
IDENTIFIER f2
SYMBOL ,
IDENTIFIER f3
SYMBOL ;
SYMBOL }
Language Features

• x, y = y, x

•c = sum(x * y for x in a for y in b)

• a.sort(key=lambda x: x[0])
Pragmatic Solution

• What to do
• Walk the AST tree
• Build symbol table
• Check all kinds of semantic errors
Challenge Yourself

• Add features to the language


• unpacking
• list comprehension
• lambda
• lifetimes
•…
IR Generation
IR: What & Why

• Intermediate Representation

• Focus less on the source language

• Pay more attention to the target platform

• Most of transformation and analysis are done in IR


IR Design

• IR design is closely related to


• Source language
• Target machine
• Transforms / Analysis
IR: Multiple Levels
• A compiler can use more than one IR, and of course, there
are more than one level.

• HIR/MIR: Carry more information. May have type system


similar to the source language. Higher level analysis &
transforms can be performed on. (Alias analysis works
better with type knowledge)
• point1.x => (LoadField point1 “x”)

• LIR: Closer to the target machine. Don’t have much type


information (General/FP Reg). Focus on code generation.
• point1.x => (LoadMem (Mem baseAddr 4))
IR: Multiple Levels
• A compiler can use more than one IR, and of course,
there are more than one level.

• LLVM: Low Level Virtual Machine


• Actually, its level is not that low.
• And it happens that LLVM use a single representation.

• The more information you own, the more chances you


have to do analysis and transforms.
LLVM IR
• LLVM: almost keep everything!

struct RT {
char A;
%struct.RT = type { i8, [10 x [20 x i32]], i8 }
int B[10][20];
%struct.ST = type { i32, double, %struct.RT }
char C;
};
define i32* @foo(%struct.ST* %s) {
struct ST {
entry:
int X;
%arrayidx = getelementptr inbounds %struct.ST,
double Y;
%struct.ST* %s, i64 1, i32 2, i32 1, i64 5, i64 13
struct RT Z;
ret i32* %arrayidx
};
}
int *foo(struct ST *s) {
return &s[1].Z.B[5][13];
}
IR Design: Structure
• Tree (the Tiger Book)
• ✘ I cannot understand it
• ✘ Hard to analyze and transform

• Linear (the Dragon Book)


• ✘ Hard to analyze and transform

• Control Flow Graph


• ✔ Easy to build CFG IR
• ✔ Further analysis and transformations need CFG
Control Flow Graph
if x > y

if x > y:
z = x
foo()
z = x z = y
else: foo() bar()
z = y
bar()
print(z)

print(z)

• Node: Basic Block

• BB: Straight-line piece of code without any jumps or jump targets

• Directed Edge: Jumps


Design: Memory Model?
• Memory-to-Memory
• Reg Alloc: What should be keep in registers?
• ✘ Lots of students wasted lots of time on it

• Register-to-Register:
• Unlimited virtual register
• Reg Alloc: What should be spilled to memory?
• ✔ Easy to understand
• ✔ Similar to the target platform
Design: Function?
• Should the “function” and “function call” concept
present in IR?

• I’m strongly in favor of it


• ✔ Simplify things
• Function call doesn’t split basic block
• In optimization’s language, “global” means inside a
function, not the whole program.
Debugging

• I printed my IR in LLVM’s format and run


• Painful!
• No direct memory arithmetic!

• I wrote my own interpreter


• https://github.com/abcdabcd987/LLIRInterpreter
• Life is much more easier!
Pragmatic Solution
• What to do
• Walk the AST tree
• Generate IR
• IR Design
• Use CFG IR. Don’t use tree IR or linear IR.
• Use register-to-register memory model
• Check senior students’ design for reference, for example
• https://github.com/abcdabcd987/LLIRInterpreter
Challenge Yourself

• Design your own IR


• Read for your reference if you want:
• https://speakerdeck.com/abcdabcd987/
compiler2016-by-abcdabcd987
Optimizations
Optimizations
• Loop optimizations
• Loop unrolling • Code generator optimization
• Software pipelining
• Register allocation
• Data-flow optimizations • Instruction selection
• Common subexpression • Instruction scheduling
elimination
• Constant folding and • Others
propagation • Dead code elimination
• Inlining
• SSA-based optimizations
• Global value numbering • …
• Sparse conditional constant
propagation
Register Allocation

• Register-to-register IR: infinite virtual registers

• Real machine: limited number of registers

• Register allocation: map virtual registers to real registers

• Spilling: which virtual registers should move to memory


Register Allocation
• Linear scan algorithm
• ✔ Sounds easier?
• ✔ Allocate faster
• ✘ Slightly worse run time
• Graph coloring algorithm
• ✘ Liveness analysis
• ✘ Write more lines of code
• ✔ Better run time performance
• ✔ Actually, not hard at all. Way simpler than lots of OI/ACM
algorithms.
Pragmatic Solution

• What to do
• Analyze and transform IR
• Graph coloring register allocation
• Inlining
Challenge Yourself

• Try all kinds of optimizations


Code Generation
Pragmatic Solution

• What to do
• Transform IR to target machine assembly
• Do it in a naïve way
Challenge Yourself

• Dive further into x86-64


• Instruction selection
• Instruction scheduling
Standard Library
Pragmatic Solution

• Use libc
Challenge Yourself

• Write your own standard library


• Write your own heap memory allocator
Wrap Up
Pragmatic Solution
• Use ANTLR 4. Imitate existing ANTLR 4 grammars.
• Use CFG IR. Use register-to-register model.
• Use graph coloring register allocation.
• Use libc
• Talk to classmates, TAs, senior students
• Ask for help. Don’t plagiarize others’ code.
Challenge Yourself

• And help others

You might also like