在本次 IR(中间代码)生成实验中,你被希望完成一个 IR 生成器,接受来自 sysu-parser
或 clang -cc1 -ast-dump=json
的语法树输入,产生 LLVM IR。预期的代码行数为 1500 行,预期的完成时间为 36 小时 ~ 108 小时。
$ ( export PATH=$HOME/sysu/bin:$PATH \
CPATH=$HOME/sysu/include:$CPATH \
LIBRARY_PATH=$HOME/sysu/lib:$LIBRARY_PATH \
LD_LIBRARY_PATH=$HOME/sysu/lib:$LD_LIBRARY_PATH &&
clang -E tester/functional/000_main.sysu.c |
clang -cc1 -O0 -S -emit-llvm )
; ModuleID = '-'
source_filename = "-"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"
; Function Attrs: noinline nounwind optnone
define i32 @main() #0 {
entry:
%retval = alloca i32, align 4
store i32 0, i32* %retval, align 4
ret i32 3
}
attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="none" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+cx8,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{!"Debian clang version 11.0.1-2"}
注意,你的输出不必与 clang -cc1 -O0 -S -emit-llvm
完全相同,只要你的 LLVM IR 在经过编译后与其有相同的输出与返回值。
$ ( export PATH=$HOME/sysu/bin:$PATH \
CPATH=$HOME/sysu/include:$CPATH \
LIBRARY_PATH=$HOME/sysu/lib:$LIBRARY_PATH \
LD_LIBRARY_PATH=$HOME/sysu/lib:$LD_LIBRARY_PATH &&
clang -E tester/functional/000_main.sysu.c |
clang -cc1 -ast-dump=json |
sysu-generator )
; ModuleID = '-'
source_filename = "-"
define i32 @main() {
entry:
ret i32 3
}
至此一个初级的 SYsU 编译器就完成了!你可以使用 lli
JIT 地执行编译出来的代码。
$ ( export PATH=$HOME/sysu/bin:$PATH \
CPATH=$HOME/sysu/include:$CPATH \
LIBRARY_PATH=$HOME/sysu/lib:$LIBRARY_PATH \
LD_LIBRARY_PATH=$HOME/sysu/lib:$LD_LIBRARY_PATH &&
sysu-preprocessor tester/functional/000_main.sysu.c |
sysu-grammar |
sysu-generator |
lli --load=libsysy.so --load=libsysu.so ) # 该输出来自运行时库的计时统计
TOTAL: 0H-0M-0S-0us
$ echo $? # 在 Unix & Linux 中,可以通过 echo $? 来查看最后运行的命令的返回值对 256 取模后的结果。
3
如果你使用了来自 LLVM 的其他组件,你需要将其加入本目录下 CMakeLists.txt
中的 llvm_map_components_to_libnames
,否则可能无法通过编译。你可以终端执行 llvm-config --components
,查看所有的 LLVM 组件名称。然而,不得使用任何封装好的库从 C 语言源码直接获得 LLVM IR,如 libclang
。
你可以像这样,借助 opt -dot-cfg
选项,生成一个输入的 CFG 可视化。终端执行下述指令,将在目录下生成 .main.dot
文件,对应源代码中 main
函数的 CFG 图。
( export PATH=$HOME/sysu/bin:$PATH \
CPATH=$HOME/sysu/include:$CPATH \
LIBRARY_PATH=$HOME/sysu/lib:$LIBRARY_PATH \
LD_LIBRARY_PATH=$HOME/sysu/lib:$LD_LIBRARY_PATH &&
clang -E tester/functional/027_if2.sysu.c |
clang -cc1 -O0 -S -emit-llvm |
opt -dot-cfg )
你可以将 .main.dot
中的内容复制到这里,在浏览器中查看其可视化。
本实验的评分分为两部分:基础部分和挑战部分。
你需要提交一份实验报告,简要记录你的实验过程、遇到的难点以及解决的方法,并在报告中附上排行榜的上榜截图;助教会定期检查排行榜上的代码。本次实验仅要求功能完全正确,因此只要成功进入排行榜,本次实验即视为通过,排行榜上的成绩与性能对本次实验没有影响。
注意:你需要保证你的代码可以在评测机环境中正确工作。
本次实验的评测项目为 benchmark_generator_and_optimizer_[0-1]
。benchmark_generator_and_optimizer_0
仅用于证明模板(代码与评测脚本)可以正确工作,不计入成绩;其他评测项详见评测脚本以了解检查算法,但不得修改评测逻辑而投机取巧。你也可以像这样调用评测脚本,单独执行其中的一个评测项。
( export PATH=$HOME/sysu/bin:$PATH \
CPATH=$HOME/sysu/include:$CPATH \
LIBRARY_PATH=$HOME/sysu/lib:$LIBRARY_PATH \
LD_LIBRARY_PATH=$HOME/sysu/lib:$LD_LIBRARY_PATH &&
sysu-compiler --unittest=benchmark_generator_and_optimizer_1 "**/*.sysu.c" )
评测机单次评测总时限为一小时,评测时会将 generator
、optimizer
目录以外的内容替换成本仓库中的内容,运行时长超过 2 分钟的编译结果会被跳过。因此本次实验中评测系统没有给出满分是正常的情况,助教的示例提交(基于 clang -O0
)得分为 425/429,性能分约为 12.2%,评测花费了约二十五分钟。
此外,因为评测集群的并发量有限(至多可以同时评测十六份提交)。请大家先在本地测试通过所有非 tester/third_party
目录下的算例后再提交。
不要投机取巧,即使(可能)没有人在看着你。
本节给出一些挑战方向供参考。
clang
导出的语法树大小为 8.9G,助教这里直接给出了压缩后的语法树。然而由于 llvm::json::parse
的性能过弱,在服务器上运行了十五分钟才解析完(对比 python 的 json.loads
仅花费八秒)。你可以使用编译原理课程上学到的知识实现一个高性能的 json 渲染库,并与 RapidJSON 等高性能的 json 库进行性能对比。