在Xcode中导入并使用LLVM

LLVM是一个自由软件项目,它是一种编译器基础设施,以C++写成。其发端源于2000年伊利诺伊大学厄巴纳-香槟分校(UIUC)的维克拉姆·艾夫(Vikram Adve)与其第一个博士生克里斯·拉特纳(Chris Lattner)的研究,彼时他们想要为所有静态及动态语言创造出动态的编译技术。


LLVM的命名最早源自于底层虚拟机(Low Level Virtual Machine)的首字母缩写,但现在这个项目的范围早已大大超越其最初的意思。当前,LLVM已经发展成为被用于开发从编译器前端到后端的“一套模块及可重用的编译器及工具链技术的集合”("collection of modular and reusable compiler and toolchain technologies")。

----------------------------------------------------------------------------------------------------------------------------------------

LLVM是一个非常庞大的项目。有时,为了构建自己的编译器或者编写pass,你都可能需要编辑LLVM项目的源码。但是如果只是在命令行+文本编辑器的层面上处理这些任务,工作效率都会大打折扣。要想更加高效的使用或学习LLVM,最好还是将其导入一个IDE。而在mac OS 上,Xcode无疑是最佳选择。

首先,你需要follow文献【3】中的步骤创建必要的用来存储项目源码的文件夹,然后将项目源码正确地下载到指定路径下。接下来,在... .../llvm/build文件夹下面执行如下命令:
  • cmake -G Xcode CMAKE_BUILD_TYPE="Debug" ../../llvm
如下图所示:


LLVM uses CMake, which supports a few build systems, such as NMake, GNU/Make, Visual Studio, Xcode, etc. 上面的命令会帮助我们创建 the project for Xcode。直到出现下面图示的情况,表明我们已经得到了一个名为LLVM.xcodeproj的Xcode项目文件。


你可以在文件夹中双击打开这个项目文件,也可以在命令行中直接使用命令open LLVM.xcodeproj来实现同样的功能。这里你可以选择自动创建Schemes。


然后,indexing will take a while(注意Xcode上面的进度条), once the project indexing is over you can build the clang. But before, probably, you need to cleanup the list of targets/schemes. 因为整个LLVM会生成非常非常多的目标文件,但是Xcode在Run的时候只能执行其中的一个。例如,我们只设定clang为我们的目标文件。于是,你需要单击【ALL_BUILD】(它位于下图中Xcode界面里上方红圈所框出的位置,因为我们已经自己调整了Schemes,所以图中的【ALL_BUILD】就变成了【clang】)。在点开的下拉菜单中选择【Manage Schemes…】,you’ll see a huge list of available targets, you don’t need most of them so feel free to hide ‘useless’ ones by unchecking ‘Show’ flag,如下图所示,我们只选择clang:

当你在Xcode中Build项目时,通常Build+Run是被连在一起的。所以为了能够确定你Build好的项目可以执行,你需要为其正确地Run做一些准备。具体来说,如果你在命令行中直接输入clang,那么由于没有指定参数,系统肯定会报错。所以,接下来,你要为程序指定执行参数。单击【clang]】,然后从下拉菜单里选择【Edit scheme】,你便会看到如下图所示的对话框。然后在Arguments passed on launch中,增加一个参数 -v,如下图所示:


现在已经万事俱备了,单击Xcode界面上的“三角”,程序会开始Build,第一次Build可能需要很长一段时间。这一步完成后,程序会自动执行 clang -v 这样一个命令,于是你可以从下图所示的输出窗口中看到结果,当前的clang 版本是 6.0.0


最后,我们在Xcode中试用一下LLVM里的其他工具。为此你需要在你期望的位置(例如Desktop)上建立一个测试文件,例如在文章《在LLVM中编写pass的详细教程(2)》中提供的名为test.c的文件。然后为了执行下面这句命令同样的功能(Flag -Os makes sure the produced IR is shortest and should be easiest to read),

  • clang -Os -S -emit-llvm test.c -o test.ll
你需要在Arguments passed on launch中,把执行参数改为:

  • -Os -S -emit-llvm [此处需输入完整的路径]/test.c -o [此处需输入完整的路径]/test.ll

现在,你便得到了一个IR文件,其中的部分内容如下:

; Function Attrs: norecurse nounwind optsize readnone ssp uwtable
define i32 @add(i32 %a, i32 %b) local_unnamed_addr #0 {
entry:
  %add = add nsw i32 %b, %a
  ret i32 %add
}

; Function Attrs: nounwind optsize ssp uwtable
define i32 @main() local_unnamed_addr #1 {
entry:
  %a = alloca i32, align 4
  %0 = bitcast i32* %a to i8*
  call void @llvm.lifetime.start.p0i8(i64 4, i8* nonnull %0) #4
  %call = call i32 (i8*, ...) @scanf(i8* getelementptr inbounds 
          ([3 x i8], [3 x i8]* @.str, i64 0, i64 0), i32* nonnull %a) #5
  %1 = load i32, i32* %a, align 4, !tbaa !3
  %cmp = icmp sgt i32 %1, -1
  br i1 %cmp, label %if.then, label %if.else

if.then:                                          ; preds = %entry
  %add.i = add nsw i32 %1, 7
  %call2 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds 
           ([4 x i8], [4 x i8]* @.str.1, i64 0, i64 0), i32 %add.i) #5
  br label %if.end

if.else:                                          ; preds = %entry
  %puts = call i32 @puts(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @str, i64 0, i64 0))
  br label %if.end

if.end:                                           ; preds = %if.else, %if.then
  call void @llvm.lifetime.end.p0i8(i64 4, i8* nonnull %0) #4
  ret i32 0
}
下面我们来使用一下LLVM中另外一个常用的工具llc。为此,跟之前的操作类似,在下拉菜单中选择【Manage Schemes…】,我们取消clang,而只勾选llc。然后在Arguments passed on launch中,把执行参数改为:
  • -O0 [此处需输入完整的路径]/test.ll -march=x86-64 -o [此处需输入完整的路径]/test-x86-64.s

Given IR we can use a backend to generate a machine code for a real CPU. 上这条命令会产生一个名为test-x86-64.s的文件,里面记录的就是64位X86架构下的汇编代码。我们截取其中的部分,列出如下:

addl	%edi, %esi
movl	%esi, %eax
popq	%rbp
retq
或者如果你希望为32位X86架构下的CPU产生汇编代码,你可以使用下面的参数:

  • -O0 [此处需输入完整的路径]/test.ll -march=x86 -o [此处需输入完整的路径]/test-x86.s

上这条命令会产生一个名为test-x86.s的文件。我们截取其中的部分,列出如下:

movl	12(%ebp), %eax
movl	8(%ebp), %ecx
addl	%ecx, %eax
popl	%ebp
retl
How about ARM? 你可以试试下面的参数:

  • -O0 [此处需输入完整的路径]/test.ll -march=arm -o [此处需输入完整的路径]/test-arm.s

上这条命令会产生一个名为test-arm.s的文件。我们截取其中的部分,列出如下:

mov	r3, r0
add	r0, r1, r0
str	r3, [sp, #4]            
str	r2, [sp]
add	sp, sp, #8
mov	pc, lr



【参考文献与推荐阅读材料】

  1. https://idea.popcount.org/2013-07-24-ir-is-better-than-assembly/
  2. https://lowlevelbits.org/getting-started-with-llvm/clang-on-os-x/
  3. http://blog.csdn.net/baimafujinji/article/details/78598658


(本文完)
发布了358 篇原创文章 · 获赞 4283 · 访问量 419万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览