LLVM IR
Introduction
概念
LLVM 對 code 的表示有三種,三種格式是等價的
in-memory compiler IR
on-disk bitcode representation (suitable for fast loading by a Just-In-Time compiler) (
.bc
)human readable assembly language representation (
.ll
)
+--------------------+ +---------------------+
| | llvm-dis | |
| | ---------------> | |
| LLVM Bitcode (.bc) | | LLVM Assembly (.ll) |
| | <--------------- | |
| | llvm-as | |
+--------------------+ +---------------------+
簡單列表
- LLVM identifiers
global :
@
(functions, global variables)local :
%
(register names, types)
- Comments
;
(直到該行結束)
- Linkage Types
private
internal
available_externally
linkonce
weak
common
appending
extern_weak
linkonce_odr, weak_odr
external
Calling Conventions
編譯參數
-S -emit-llvm
: 生出 LLVM IR (FILENAME.ll
)
LLVM IR 範例
Hello World
LLVM IR :
@.str = constant [12 x i8] c"Hello World\00"
declare i32 @puts(i8*)
define i32 @main() {
%var = getelementptr [12 x i8], [12 x i8]* @.str, i64 0, i64 0
call i32 @puts(i8* %var)
ret i32 0
}
User Input
C :
#include <stdio.h>
int main() {
int value;
scanf("%d", &value);
printf("%d\n", value);
return 0;
}
LLVM IR :
@.str.0 = constant [3 x i8] c"%d\00"
@.str.1 = constant [4 x i8] c"%d\0A\00"
declare i32 @scanf(i8*, ...)
declare i32 @printf(i8*, ...)
define i32 @main() {
%ptr = alloca i32 ; i32*:ptr
call i32 (i8*, ...) @scanf(i8* getelementptr ([3 x i8], [3 x i8]* @.str.0, i32 0, i32 0),
i32* %ptr)
%value = load i32, i32* %ptr ; i32:value = *ptr
call i32 (i8*, ...) @printf(i8* getelementptr ([4 x i8], [4 x i8]* @.str.1, i32 0, i32 0),
i32 %value)
ret i32 0
}
Fibonacci
SSA (Static single assignment form)
Φ (Phi) function
+----------+
| x1 < 3 ? |
+----------+
| |
| |
+-----+ +---+
| |
| |
v v
+--------+ +--------+
| x2 = 1 | | x3 = 2 |
+--------+ +--------+
| |
| |
+-------+------+
|
v
+----------------+
| x4 = Φ(x2, x3) |
+----------------+
LLVM IR Type
First Class Types
Single Value Types
Vector Type
SIMD
Syntax
< <# elements> x <elementtype> >
Example
<8 x float> ; Vector of 8 32-bit floating-point values.
LLVM IR Generated Example
Sanitizer
C :
#include <stdio.h>
int main() {
int a[4];
printf("%d\n", a[4]);
return 0;
}
Build :
$ clang -fsanitize=address -S -emit-llvm test.c
LLVM IR :
; ModuleID = 'test.c'
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@.str = internal unnamed_addr constant { [4 x i8], [60 x i8] } { [4 x i8] c"%d\0A\00", [60 x i8] zeroinitializer }, align 32
@llvm.global_ctors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 1, void ()* @asan.module_ctor }]
@__asan_option_detect_stack_use_after_return = external global i32
@__asan_gen_ = private unnamed_addr constant [12 x i8] c"1 32 16 1 a\00", align 1
@__asan_gen_.1 = private constant [7 x i8] c"test.c\00", align 1
@__asan_gen_.2 = private unnamed_addr constant [17 x i8] c"<string literal>\00", align 1
@__asan_gen_.3 = private unnamed_addr constant [7 x i8] c"test.c\00", align 1
@__asan_gen_.4 = private unnamed_addr constant { [7 x i8]*, i32, i32 } { [7 x i8]* @__asan_gen_.3, i32 5, i32 12 }
@0 = internal global [1 x { i64, i64, i64, i64, i64, i64, i64 }] [{ i64, i64, i64, i64, i64, i64, i64 } { i64 ptrtoint ({ [4 x i8], [60 x i8] }* @.str to i64), i64 4, i64 64, i64 ptrtoint ([17 x i8]* @__asan_gen_.2 to i64), i64 ptrtoint ([7 x i8]* @__asan_gen_.1 to i64), i64 0, i64 ptrtoint ({ [7 x i8]*, i32, i32 }* @__asan_gen_.4 to i64) }]
@llvm.global_dtors = appending global [1 x { i32, void ()* }] [{ i32, void ()* } { i32 1, void ()* @asan.module_dtor }]
; Function Attrs: nounwind sanitize_address uwtable
define i32 @main(i32 %argc, i8** %argv) #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
%3 = alloca i8**, align 8
%4 = load i32, i32* @__asan_option_detect_stack_use_after_return
%5 = icmp ne i32 %4, 0
br i1 %5, label %6, label %8
; <label>:6 ; preds = %0
%7 = call i64 @__asan_stack_malloc_0(i64 64)
br label %8
; <label>:8 ; preds = %0, %6
%9 = phi i64 [ 0, %0 ], [ %7, %6 ]
%10 = icmp eq i64 %9, 0
br i1 %10, label %11, label %13
; <label>:11 ; preds = %8
%MyAlloca = alloca i8, i64 64, align 32
%12 = ptrtoint i8* %MyAlloca to i64
br label %13
; <label>:13 ; preds = %8, %11
%14 = phi i64 [ %9, %8 ], [ %12, %11 ]
%15 = add i64 %14, 32
%16 = inttoptr i64 %15 to [4 x i32]*
%17 = inttoptr i64 %14 to i64*
store i64 1102416563, i64* %17
%18 = add i64 %14, 8
%19 = inttoptr i64 %18 to i64*
store i64 ptrtoint ([12 x i8]* @__asan_gen_ to i64), i64* %19
%20 = add i64 %14, 16
%21 = inttoptr i64 %20 to i64*
store i64 ptrtoint (i32 (i32, i8**)* @main to i64), i64* %21
%22 = lshr i64 %14, 3
%23 = add i64 %22, 2147450880
%24 = add i64 %23, 0
%25 = inttoptr i64 %24 to i64*
store i64 -868350299093208591, i64* %25
store i32 0, i32* %1
store i32 %argc, i32* %2, align 4
store i8** %argv, i8*** %3, align 8
%26 = getelementptr inbounds [4 x i32], [4 x i32]* %16, i32 0, i64 4
%27 = ptrtoint i32* %26 to i64
%28 = lshr i64 %27, 3
%29 = add i64 %28, 2147450880
%30 = inttoptr i64 %29 to i8*
%31 = load i8, i8* %30
%32 = icmp ne i8 %31, 0
br i1 %32, label %33, label %39, !prof !3
; <label>:33 ; preds = %13
%34 = and i64 %27, 7
%35 = add i64 %34, 3
%36 = trunc i64 %35 to i8
%37 = icmp sge i8 %36, %31
br i1 %37, label %38, label %39
; <label>:38 ; preds = %33
call void @__asan_report_load4(i64 %27)
call void asm sideeffect "", ""()
unreachable
; <label>:39 ; preds = %33, %13
%40 = load i32, i32* %26, align 4
%41 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ({ [4 x i8], [60 x i8] }, { [4 x i8], [60 x i8] }* @.str, i32 0, i32 0, i32 0), i32 %40)
store i64 1172321806, i64* %17
%42 = icmp ne i64 %9, 0
br i1 %42, label %43, label %50
; <label>:43 ; preds = %39
%44 = add i64 %23, 0
%45 = inttoptr i64 %44 to i64*
store i64 -723401728380766731, i64* %45
%46 = add i64 %9, 56
%47 = inttoptr i64 %46 to i64*
%48 = load i64, i64* %47
%49 = inttoptr i64 %48 to i8*
store i8 0, i8* %49
br label %53
; <label>:50 ; preds = %39
%51 = add i64 %23, 0
%52 = inttoptr i64 %51 to i64*
store i64 0, i64* %52
br label %53
; <label>:53 ; preds = %50, %43
ret i32 0
}
declare i32 @printf(i8*, ...) #1
define internal void @asan.module_ctor() {
call void @__asan_init_v5()
call void @__asan_register_globals(i64 ptrtoint ([1 x { i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 1)
ret void
}
declare void @__asan_init_v5()
declare void @__asan_report_load_n(i64, i64)
declare void @__asan_loadN(i64, i64)
declare void @__asan_report_load1(i64)
declare void @__asan_load1(i64)
declare void @__asan_report_load2(i64)
declare void @__asan_load2(i64)
declare void @__asan_report_load4(i64)
declare void @__asan_load4(i64)
declare void @__asan_report_load8(i64)
declare void @__asan_load8(i64)
declare void @__asan_report_load16(i64)
declare void @__asan_load16(i64)
declare void @__asan_report_store_n(i64, i64)
declare void @__asan_storeN(i64, i64)
declare void @__asan_report_store1(i64)
declare void @__asan_store1(i64)
declare void @__asan_report_store2(i64)
declare void @__asan_store2(i64)
declare void @__asan_report_store4(i64)
declare void @__asan_store4(i64)
declare void @__asan_report_store8(i64)
declare void @__asan_store8(i64)
declare void @__asan_report_store16(i64)
declare void @__asan_store16(i64)
declare void @__asan_report_exp_load_n(i64, i64, i32)
declare void @__asan_exp_loadN(i64, i64, i32)
declare void @__asan_report_exp_load1(i64, i32)
declare void @__asan_exp_load1(i64, i32)
declare void @__asan_report_exp_load2(i64, i32)
declare void @__asan_exp_load2(i64, i32)
declare void @__asan_report_exp_load4(i64, i32)
declare void @__asan_exp_load4(i64, i32)
declare void @__asan_report_exp_load8(i64, i32)
declare void @__asan_exp_load8(i64, i32)
declare void @__asan_report_exp_load16(i64, i32)
declare void @__asan_exp_load16(i64, i32)
declare void @__asan_report_exp_store_n(i64, i64, i32)
declare void @__asan_exp_storeN(i64, i64, i32)
declare void @__asan_report_exp_store1(i64, i32)
declare void @__asan_exp_store1(i64, i32)
declare void @__asan_report_exp_store2(i64, i32)
declare void @__asan_exp_store2(i64, i32)
declare void @__asan_report_exp_store4(i64, i32)
declare void @__asan_exp_store4(i64, i32)
declare void @__asan_report_exp_store8(i64, i32)
declare void @__asan_exp_store8(i64, i32)
declare void @__asan_report_exp_store16(i64, i32)
declare void @__asan_exp_store16(i64, i32)
declare i8* @__asan_memmove(i8*, i8*, i64)
declare i8* @__asan_memcpy(i8*, i8*, i64)
declare i8* @__asan_memset(i8*, i32, i64)
declare void @__asan_handle_no_return()
declare void @__sanitizer_ptr_cmp(i64, i64)
declare void @__sanitizer_ptr_sub(i64, i64)
declare i64 @__asan_stack_malloc_0(i64)
declare void @__asan_stack_free_0(i64, i64)
declare i64 @__asan_stack_malloc_1(i64)
declare void @__asan_stack_free_1(i64, i64)
declare i64 @__asan_stack_malloc_2(i64)
declare void @__asan_stack_free_2(i64, i64)
declare i64 @__asan_stack_malloc_3(i64)
declare void @__asan_stack_free_3(i64, i64)
declare i64 @__asan_stack_malloc_4(i64)
declare void @__asan_stack_free_4(i64, i64)
declare i64 @__asan_stack_malloc_5(i64)
declare void @__asan_stack_free_5(i64, i64)
declare i64 @__asan_stack_malloc_6(i64)
declare void @__asan_stack_free_6(i64, i64)
declare i64 @__asan_stack_malloc_7(i64)
declare void @__asan_stack_free_7(i64, i64)
declare i64 @__asan_stack_malloc_8(i64)
declare void @__asan_stack_free_8(i64, i64)
declare i64 @__asan_stack_malloc_9(i64)
declare void @__asan_stack_free_9(i64, i64)
declare i64 @__asan_stack_malloc_10(i64)
declare void @__asan_stack_free_10(i64, i64)
declare void @__asan_poison_stack_memory(i64, i64)
declare void @__asan_unpoison_stack_memory(i64, i64)
declare void @__asan_alloca_poison(i64, i64)
declare void @__asan_allocas_unpoison(i64, i64)
declare void @__asan_before_dynamic_init(i64)
declare void @__asan_after_dynamic_init()
declare void @__asan_register_globals(i64, i64)
declare void @__asan_unregister_globals(i64, i64)
define internal void @asan.module_dtor() {
call void @__asan_unregister_globals(i64 ptrtoint ([1 x { i64, i64, i64, i64, i64, i64, i64 }]* @0 to i64), i64 1)
ret void
}
attributes #0 = { nounwind sanitize_address uwtable "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+sse,+sse2" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.asan.globals = !{!0}
!llvm.ident = !{!2}
!0 = !{[4 x i8]* getelementptr inbounds ({ [4 x i8], [60 x i8] }, { [4 x i8], [60 x i8] }* @.str, i32 0, i32 0), !1, !"<string literal>", i1 false, i1 false}
!1 = !{!"test.c", i32 5, i32 12}
!2 = !{!"clang version 3.7.0 (tags/RELEASE_370/final)"}
!3 = !{!"branch_weights", i32 1, i32 100000}