目标程序:
test5.f90:
integer,target::target(2)=(/1,2/)
logical:: l1,l2
type t
integer,pointer :: ptr
end type
type(t) a(2)
a(1)%ptr => target(1)
a(2)%ptr => target(2)
l1 = associated(a(1)%ptr,target(1))
l2 = associated(a(2)%ptr,target(2))
end
LLVM IR程序:
test5.ll:
; ModuleID = '/tmp/test5-63c6ca.ll'
source_filename = "/tmp/test5-63c6ca.ll"
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
target triple = "aarch64-unknown-linux-gnu"
%struct.BSS1 = type <{ [16 x i8] }>
%struct.STATICS1 = type <{ [8 x i8] }>
%struct.ld.MAIN_t_td_ = type <{ [1 x %struct.ld.memtype], [7 x i64] }>
%struct.ld.memtype = type <{ [6 x i64], i8* }>
%structMAIN_t_td_ = type <{ [8 x i64], [6 x i8*], [9 x i8] }>
@.BSS1 = internal global %struct.BSS1 zeroinitializer, align 32
@.STATICS1 = internal global %struct.STATICS1 <{ [8 x i8] c"\01\00\00\00\02\00\00\00" }>, align 16
@.C360_MAIN_ = internal constant i32 25
@.C345_MAIN_ = internal constant i64 2
@.C325_MAIN_ = internal constant i64 1
@.C359_MAIN_ = internal constant i64 4
@.C355_MAIN_ = internal constant i64 25
@.C323_MAIN_ = internal constant i64 0
@.C322_MAIN_ = internal constant i32 0
@"MAIN$t$td$ld" = global %struct.ld.MAIN_t_td_ <{ [1 x %struct.ld.memtype] [%struct.ld.memtype <{ [6 x i64] [i64 80, i64 0, i64 0, i64 4, i64 -1, i64 0], i8* null }>], [7 x i64] [i64 0, i64 0, i64 0, i64 0, i64 -1, i64 0, i64 0] }>
@MAIN_t_td_ = global %structMAIN_t_td_ <{ [8 x i64] [i64 43, i64 33, i64 0, i64 8, i64 0, i64 0, i64 0, i64 0], [6 x i8*] [i8* null, i8* bitcast (%structMAIN_t_td_* @MAIN_t_td_ to i8*), i8* null, i8* null, i8* null, i8* bitcast (%struct.ld.MAIN_t_td_* @"MAIN$t$td$ld" to i8*)], [9 x i8] c"MAIN$t$td" }>
define void @MAIN_() !dbg !5 {
L.entry:
%"target$sd1_358" = alloca [16 x i64], align 8
%"ptr$sd_350" = alloca [1 x i64], align 8
%l1_346 = alloca i32, align 4
%l2_347 = alloca i32, align 4
%0 = bitcast i32* @.C322_MAIN_ to i8*, !dbg !8
%1 = bitcast void ()* @fort_init to void (i8*)*, !dbg !8
call void %1(i8* %0), !dbg !8
%2 = bitcast [16 x i64]* %"target$sd1_358" to i8*, !dbg !8
%3 = bitcast i64* @.C323_MAIN_ to i8*, !dbg !8
%4 = bitcast i64* @.C355_MAIN_ to i8*, !dbg !8
%5 = bitcast i64* @.C359_MAIN_ to i8*, !dbg !8
%6 = bitcast i64* @.C325_MAIN_ to i8*, !dbg !8
%7 = bitcast i64* @.C345_MAIN_ to i8*, !dbg !8
%8 = bitcast void ()* @f90_template1_i8 to void (i8*, i8*, i8*, i8*, i8*, i8*)*, !dbg !8
call void %8(i8* %2, i8* %3, i8* %4, i8* %5, i8* %6, i8* %7), !dbg !8
%9 = bitcast [16 x i64]* %"target$sd1_358" to i8*, !dbg !8
%10 = bitcast void ()* @f90_set_intrin_type_i8 to void (i8*, i32)*, !dbg !8
call void %10(i8* %9, i32 25), !dbg !8
br label %L.LB1_372
L.LB1_372: ; preds = %L.entry
%11 = bitcast %struct.STATICS1* @.STATICS1 to i8*, !dbg !9
%12 = bitcast %struct.BSS1* @.BSS1 to i8**, !dbg !9
store i8* %11, i8** %12, align 8, !dbg !9
%13 = bitcast %struct.STATICS1* @.STATICS1 to i8*, !dbg !10
%14 = getelementptr i8, i8* %13, i64 4, !dbg !10
%15 = bitcast %struct.BSS1* @.BSS1 to i8*, !dbg !10
%16 = getelementptr i8, i8* %15, i64 8, !dbg !10
%17 = bitcast i8* %16 to i8**, !dbg !10
store i8* %14, i8** %17, align 8, !dbg !10
%18 = bitcast %struct.BSS1* @.BSS1 to i8**, !dbg !11
%19 = load i8*, i8** %18, align 8, !dbg !11
%20 = bitcast [1 x i64]* %"ptr$sd_350" to i8*, !dbg !11
%21 = bitcast %struct.STATICS1* @.STATICS1 to i8*, !dbg !11
%22 = bitcast i64* @.C355_MAIN_ to i8*, !dbg !11
%23 = bitcast i32 ()* @fort_associated_t_i8 to i32 (i8*, i8*, i8*, i8*)*, !dbg !11
%24 = call i32 %23(i8* %19, i8* %20, i8* %21, i8* %22), !dbg !11
store i32 %24, i32* %l1_346, align 4, !dbg !11
%25 = bitcast %struct.BSS1* @.BSS1 to i8*, !dbg !12
%26 = getelementptr i8, i8* %25, i64 8, !dbg !12
%27 = bitcast i8* %26 to i8**, !dbg !12
%28 = load i8*, i8** %27, align 8, !dbg !12
%29 = bitcast [1 x i64]* %"ptr$sd_350" to i8*, !dbg !12
%30 = bitcast %struct.STATICS1* @.STATICS1 to i8*, !dbg !12
%31 = getelementptr i8, i8* %30, i64 4, !dbg !12
%32 = bitcast i64* @.C355_MAIN_ to i8*, !dbg !12
%33 = bitcast i32 ()* @fort_associated_t_i8 to i32 (i8*, i8*, i8*, i8*)*, !dbg !12
%34 = call i32 %33(i8* %28, i8* %29, i8* %31, i8* %32), !dbg !12
store i32 %34, i32* %l2_347, align 4, !dbg !12
ret void, !dbg !13
}
declare i32 @fort_associated_t_i8()
declare void @f90_set_intrin_type_i8()
declare void @f90_template1_i8()
declare void @fort_init()
!llvm.module.flags = !{!0, !1}
!llvm.dbg.cu = !{!2}
!0 = !{i32 2, !"Dwarf Version", i32 4}
!1 = !{i32 2, !"Debug Info Version", i32 3}
!2 = distinct !DICompileUnit(language: DW_LANG_Fortran90, file: !3, producer: " F90 Flang - 1.5 2017-05-01", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !4, globals: !4, imports: !4)
!3 = !DIFile(filename: "test5.f90", directory: "/home/luoxudong/work/test")
!4 = !{}
!5 = distinct !DISubprogram(name: "MAIN", scope: !2, file: !3, line: 1, type: !6, scopeLine: 1, spFlags: DISPFlagDefinition | DISPFlagMainSubprogram, unit: !2)
!6 = !DISubroutineType(cc: DW_CC_program, types: !7)
!7 = !{null}
!8 = !DILocation(line: 1, column: 1, scope: !5)
!9 = !DILocation(line: 7, column: 1, scope: !5)
!10 = !DILocation(line: 8, column: 1, scope: !5)
!11 = !DILocation(line: 9, column: 1, scope: !5)
!12 = !DILocation(line: 10, column: 1, scope: !5)
!13 = !DILocation(line: 11, column: 1, scope: !5)
基本概念
下面,我们对test5.ll逐行解释一些比较基本的概念。
注释
首先,第一行;ModuleID是注释,这是一个注释。在LLVM IR中,注释以;开头,并一直延伸到行尾。所以在LLVM IR中,并没有像C语言中的/* comment block */这样的注释块,而全都类似于// comment line这样的注释行。
目标数据分布和平台
第二行和第三行的target datalayout和target triple,则是注明了目标汇编代码的数据分布和平台。我们之前提到过,LLVM是一个面向多平台的深度定制化编译器后端,而我们LLVM IR的目的,则是让LLVM后端根据IR代码生成相应平台的汇编代码。所以,我们需要在IR代码中指明我们需要生成哪一个平台的代码,也就是target triple字段。类似地,我们还需要定制数据的大小端序、对齐形式等需求,所以我们也需要指明target datalayout字段。关于这两个字段的值的详细情况,我们可以参考Data Layout和Target Triple这两个官方文档。我们可以对照官方文档,解释我们在linux-gnu上得到的结果:
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
表示:
- e: 小端序
- m:e: 符号表中使用Mach-O格式的name mangling(这玩意儿我一直不知道中文是啥,就是把程序中的标识符经过处理得到可执行文件中的符号表中的符号)
- i64:64: 将i64类型的变量采用64比特的ABI对齐
- i128:128: 将i128类型的变量采用128比特的ABI对齐
- n32:64: 目标CPU的原生整型包含32比特和64比特
- S128: 栈以128比特自然对齐
ssa策略(static single assignment):
LLVM IR严格遵守ssa策略,即每个变量只能被赋值一次。
基本数据类型:
整形(iN)、空类型(void)、浮点型(float、double等);N可以为1、8、16、32、64、128,i1的值为true或false,整形默认按有符号补码形式储存,整形的有无符号是体现在操作指令上而非类型上。
%1 = udiv i8 -6, 2 ; get (256 - 6) / 2 = 125
%2 = sdiv i8 -6, 2 ; get (-6) / 2 = -3
转换指令:
- trunc to:去除高位
- zext to:无符号填充
- sext to:有符号填充
指针:
- ptrtoint:将指针转化为整形
- inttoptr:将整形转化为指针