Download Introduction to LLVM Compiler Framework and Infrastructure and more Lecture notes Compiler Design in PDF only on Docsity!
The LLVM Compiler Framework
and Infrastructure
(Part 1)
Presented by Gennady Pekhimenko Substantial portions courtesy of Olatunji Ruwase, Chris Lattner, Vikram Adve, and David Koes
LLVM Compiler System
ļ® The LLVM Compiler Infrastructure
ļ¶ Provides reusable components for building compilers
ļ¶ Reduce the time/cost to build a new compiler
ļ¶ Build static compilers, JITs, trace-based optimizers, ...
ļ® The LLVM Compiler Framework
ļ¶ End-to-end compilers using the LLVM infrastructure
ļ¶ C and C++ are robust and aggressive:
ļ® Java, Scheme and others are in development
ļ¶ Emit C code or native code for X86, Sparc, PowerPC
Tutorial Overview
ļ® Introduction to the running example
ļ® LLVM C/C++ Compiler Overview
ļ¶ High-level view of an example LLVM compiler
ļ® The LLVM Virtual Instruction Set
ļ¶ IR overview and type-system
ļ® The Pass Manager
ļ® Important LLVM Tools
ļ¶ opt, code generator, JIT, test suite, bugpoint
ļ® Assignment Overview
Running example: arg promotion
Consider use of by-reference parameters:
int callee(const int &X) {
return X+1;
int caller() {
return callee(4);
int callee(const int *X) {
return *X+1; // memory load
int caller() {
int tmp; // stack object
tmp = 4; // memory store
return callee(&tmp);
compiles to
ļ¼ Eliminated load in callee
ļ¼ Eliminated store in caller
ļ¼ Eliminated stack slot for ātmpā
int callee(int X) {
return X+1;
int caller() {
return callee(4);
We want:
Tutorial Overview
ļ® Introduction to the running example
ļ® LLVM C/C++ Compiler Overview
ļ¶ High-level view of an example LLVM compiler
ļ® The LLVM Virtual Instruction Set
ļ¶ IR overview and type-system
ļ® The Pass Manager
ļ® Important LLVM Tools
ļ¶ opt, code generator, JIT, test suite, bugpoint
ļ® Assignment Overview
The LLVM C/C++ Compiler
ļ® From the high level, it is a standard compiler:
ļ¶ Compatible with standard makefiles
ļ¶ Uses GCC 4.2 C and C++ parser
ļ® Distinguishing features:
ļ¶ Uses LLVM optimizers, not GCC optimizers
ļ¶ .o files contain LLVM IR/bytecode, not machine code
ļ¶ Executable can be bytecode (JITād) or machine code
llvmg++ -emit-llvm
C file llvmgcc -emit-llvm
C++ file
.o file
.o file
llvm linker executable
Compile Time Link Time
Looking into events at link-time
.o file
.o file
llvm linker executable
Native Code Backend Native executable
āllcā C Code Backend
C Compiler Native executable āllc āmarch=cā NOTE: Produces very ugly C. Officially deprecated, but still works fairly well.
āgccā
Link in native .o files and libraries here
LLVM
Linker
Link-time Optimizer .bc file for LLVM JIT
.o file
.o file
20 LLVM Analysis & Optimization Passes
Optionally āinternalizesā: marks most functions as internal, to improve IPO
Perfect place for argument promotion optimization! 10
Goals of the compiler design
ļ® Analyze and optimize as early as possible:
ļ¶ Compile-time opts reduce modify-rebuild-execute cycle
ļ¶ Compile-time optimizations reduce work at link-time
(by shrinking the program)
ļ® All IPA/IPO make an open-world assumption
ļ¶ Thus, they all work on libraries and at compile-time
ļ¶ āInternalizeā pass enables āwhole programā optzn
ļ® One IR (without lowering) for analysis & optzn
ļ¶ Compile-time optzns can be run at link-time too!
ļ¶ The same IR is used as input to the JIT
IR design is the key to these goals!
Goals of LLVM IR
ļ® Easy to produce, understand, and define!
ļ® Language- and Target-Independent
ļ¶ AST-level IR (e.g. ANDF, UNCOL) is not very feasible
ļ® Every analysis/xform must know about āallā languages
ļ® One IR for analysis and optimization
ļ¶ IR must be able to support aggressive IPO, loop opts,
scalar opts, ⦠high- and low-level optimization!
ļ® Optimize as much as early as possible
ļ¶ Canāt postpone everything until link or runtime
ļ¶ No lowering in the IR!
LLVM Instruction Set Overview
ļ® Low-level and target-independent semantics
ļ¶ RISC-like three address code
ļ¶ Infinite virtual register set in SSA form
ļ¶ Simple, low-level control flow constructs
ļ¶ Load/store instructions with typed-pointers
ļ® IR has text, binary, and in-memory forms
for (i = 0; i < N;
++i) Sum(&A[i], &P);
loop: ; preds = %bb0, %loop %i.1 = phi i32 [ 0, %bb0 ], [ %i.2, %loop ] %AiAddr = getelementptr float %A, i32 %i. call void @Sum(float %AiAddr, %pair %P) %i.2 = add i32 %i.1, 1 %exitcond = icmp eq i32 %i.1, %N br i1 %exitcond, label %outloop, label %loop** 14
LLVM Type System Details
ļ® The entire type system consists of:
ļ¶ Primitives: label, void, float, integer, ā¦
ļ® Arbitrary bitwidth integers (i1, i32, i64)
ļ¶ Derived: pointer, array, structure, function
ļ¶ No high-level types: type-system is language neutral!
ļ® Type system allows arbitrary casts:
ļ¶ Allows expressing weakly-typed languages, like C
ļ¶ Front-ends can implement safe languages
ļ¶ Also easy to define a type-safe subset of LLVM
See also: docs/LangRef.html
Lowering source-level types to LLVM
ļ® Source language types are lowered:
ļ¶ Rich type systems expanded to simple type system
ļ¶ Implicit & abstract types are made explicit & concrete
ļ® Examples of lowering:
ļ¶ References turn into pointers: T& ļ T*
ļ¶ Complex numbers: complex float ļ { float, float }
ļ¶ Bitfields: struct X { int Y:4; int Z:2; } ļ { i32 }
ļ¶ Inheritance: class T : S { int X; } ļ { S, i32 }
ļ¶ Methods: class T { void foo(); } ļ void foo(T)*
ļ® Same idea as lowering to machine code
Our example, compiled to LLVM
int callee(const int *X) {
return *X+1; // load
int caller() {
int T; // on stack
T = 4; // store
return callee(&T);
internal int %callee(int* %X) {
%tmp.1 = load int* %X
%tmp.2 = add int %tmp.1, 1
ret int %tmp.
int %caller() {
%T = alloca int
store int 4, int* %T
%tmp.3 = call int %callee(int* %T)
ret int %tmp.
Stack allocation is
explicit in LLVM
All loads/stores are
explicit in the LLVM
representation
Linker āinternalizesā
most functions in most
cases
Our example, desired transformation
internal int %callee(int %X.val) {
%tmp.2 = add int %X.val, 1
ret int %tmp.
int %caller() {
%T = alloca int
store int 4, int* %T
%tmp.1 = load int %T*
%tmp.3 = call int %callee(%tmp.1)
ret int %tmp.
internal int %callee(int* %X) {
%tmp.1 = load int* %X
%tmp.2 = add int %tmp.1, 1
ret int %tmp.
int %caller() {
%T = alloca int
store int 4, int* %T
%tmp.3 = call int %callee(int* %T)
ret int %tmp.
Change the prototype
for the function
Insert load instructions
into all callers
Update all call sites of
ācalleeā
Other transformation
(-mem2reg) cleans up
the rest
int %caller() {
%tmp.3 = call int %callee(int 4)
ret int %tmp.