HNUSTC 编译器14天速通后记

前情提要

对我校编译原理没有实验课,没有课程设计,就是纯纯的理论很不爽,在与老师沟通后开始尝试写一个用于教学目的的编译器,在此之前我已经实现了正则表达式的解析和LL文法的分析,对于实现一个编译器很有兴趣。本着跟计算机打交道这么多年,连一个编译器都没摸透过的遗憾心理开始了编译器速通计划。

先花了一个星期确定技术栈上,最终我们敲定了先使用lex+yacc,然后借助llvm的框架快速搭建一个以llvm ir为目标代码的编译器。语言选定为C98的一个子集,去除了一些无关紧要的特性,保留了结构体等比较重要的特性。

然后是漫长的看各种手册实现的过程,在不断的思考-实现-碰壁循环中最终花费大约两个星期实现了一个最简单的编译器,但是相比原计划还是削减了一些重要的特性,比如结构体和函数指针。最终代码量总和在1k行左右,编译中的各个阶段均能独立运行输出调试结果。拿libreoj上的两道题跑了两个简单的测试,能过应该就没问题了。

开源以后再说吧

简介

HNUSTC语言是C语言的一个相差极小的子集(),去除了C语言一些对于学习编译原理无关紧要的特性。本参考手册提供了HNUSTC的语言文法定义以便同学们能够更轻松的实现HNUSTC编译器。以下是HNUSTC语言相比C语言(C98)特性差别的一个快速概览。

  • 只考虑单文件(单编译单元)编译
  • 自动引入标准库
  • 没有预处理器(意味着不能使用assert等依赖宏的组件)
  • 没有结构体和函数指针
  • 没有inlineregistergotoexterncaseunionstaticconstgotovolatiledefaultdotypedef
  • 没有内嵌汇编
  • 没有函数指针
  • 简化的字面量形式,简化的变量初始化形式
  • ixx表示xx位有符号整数
  • 没有函数声明,函数定义顺序不影响编译执行结果
  • 局部变量定义只允许出现在函数定义之后
  • 没有显式类型转换和泛化的隐式转换

词法分析

以下用正则表达式描述了HNUSTC的单词定义。;开头的行为注释。

在词法分析阶段进行简单的字面量翻译。

注释

HNUSTC支持单行和多行注释,词法分析器遇到时将会忽略。

关键字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
;流程控制
while
for
if
else
return
;基本类型
i8 i16 i32 i64 i128
f32 f64
void
;特殊字面量
NULL
;类型操作
sizeof

为了实现方便放弃实现无符号整形。删除了C中令人困惑的类型long int,使用ixx表示xx位有符号整形,f32表示floatf64表示double

运算符

按优先级排序,越前面优先级越高。

第一列描述了结合性,如果没有则顺延上一个描述了结合性的行的结合性。[a-z]描述了其他表达式。

{...}为注释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
从左到右 a(b){函数调用} a[b]{数组}
从右到左 +a{单元加} -a{单元减} !a ~a *a &a sizeof a
从左到右 a*b a/b a%b
a+b a-b
a<<b a>>b
a<b a<=b a>b a>=b
a==b a!=b
a&b
a^b
a|b
a&&b
a||b
从右到左 a=b
从左到右 a,b

为了实现方便,我们去除了++,--以及如+=各种组合赋值运算符,还有三元运算符?:

运算符优先级不等于求值顺序。

标识符

以下定义了一个标识符。

1
[a-zA-Z_][a-zA-Z_0-9]*

标识符不能与现有关键字相同。

字面量
1
2
3
4
5
6
7
8
9
10
11
12
;字符串字面量
".*?[^\\]"
;字符字面量
'.*?[^\\]'
;整形字面量(十进制)
[0-9]+
;整形字面量(十六进制)
0x[0-9A-Fa-f]+
;浮点字面量(十进制)
[0-9]+\.?[0-9]*[Ee][\+-]?[0-9]+|\.[0-9]+([Ee][\+-]?[0-9]+)?
;NULL字面量
NULL

相比C,为了实现方便,HNUSTC去除了八进制字面量并简化了浮点字面量的定义。

整形字面量均为i32,如果不能完整表示则提升至最少能表示的整形,浮点字面量均为f64

在C中,NULL由宏定义,因为HNUSTC没有宏所以将NULL提升为字面量,等价与0

语法分析

在词法分析完成后,ply生成了一个由单词token组成的串,每一个token还附带一个额外的的值,可以通过定义函数直接处理字面量,接下来语法分析阶段将这些token归约为对应的文法,并利用这个额外的值构建语法分析树。

这里给出了HNUSTC语法的顶层设计

1
2
3
4
5
6
7
8
9
10
11
12
13
程序 program
|
定义 declare/函数定义 declare_func
|
语句 statement | |
| - 复合语句 compound_statements-----------|
| - 选择语句 selection_statements----------|
| - 表达式语句 expression_statements-------|
| - 循环语句 iterator_statements-----------|
| - 跳转语句 jump_statements---------------|
|
|
表达式 expression-----------------------------------------|

程序由定义或者函数定义组成,函数定义引入复合语句。

1
2
3
program : declare program  
| declare_func program
| empty

empty表示空token

定义 declare

HNUSTC使用定义引入变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
declare : specifiers declarators_and_initializers ";"

specifiers : TYPENAME

declarators_and_initializers : declarator_and_initializer COMMA declarators_and_initializers
| declarator_and_initializer

declarator_and_initializer : declarator initializer
| declarator

initializer : ASSIGN nocomma_expression

declarator : STAR declarator %prec dec_ptr

declarator : "(" declarator ")"

declarator : declarator "[" INTEGERINSTANT "]" %prec dec_array

declarator : IDENT

其中specifiersdeclarator共同确定了标识符的类型。

其中declarator使用dec_ptrdec_array并指明优先级是为了防止诸如int *a[3];的歧义发生,此时优先使用dec_array,那么从里至外为a,长度为3的数组,指针,inta的含义为包含三个指针int的数组。

HNUSTC这么做的动机是对着a反向进行declarator的操作,最终类型会变成specifiers。比如*(a[0])int类型。

背后原理跟编译原理有关,如果按照这种文法直接生成语法分析树的话,一边遍历树一边push节点进栈,最终栈顶就是顶层的类型,要对这种值运算的话就可以很方便的进行类型计算。

函数定义 declare_func
1
2
3
4
5
6
7
8
9
10
11
12
declare_func : parameter "(" parameters ")" "{" function_body "}"

parameters : parameter COMMA parameters
| parameter

parameter : type_name

type_name : specifiers declarator

initializer : ASSIGN nocomma_expression

function_body : compound_statements

函数定义不是语句,只能在程序顶层使用。

语句 statement

共五种语句:复合语句、选择语句、表达式语句、循环语句、跳转语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
statements : "{" compound_statements "}" 
| expression_statements
| selection_statements
| iterator_statements
| jump_statements

compound_statements : statements_or_declare compound_statements
| statements_or_declare

statements_or_declare : statements
| declare

expression_statements : expression ";"
| ";"

selection_statements : IF "(" expression ")" statements %prec THEN
| IF "(" expression ")" statements ELSE statements

iterator_statements : WHILE "(" expression ")" statements

iterator_statements : FOR "(" expression ";" expression ";" expression ")" statements

jump_statements : BREAK ";"
| CONTINUE ";"
| RETURN expression ";"
| RETURN ";"

为了解决if-if-else二义性问题,这里介绍一种方法。使用yacc的%prec语法将ifif-else看成两个运算符THENELSE的文法。接下来就是解决二义性

if-if-else二义性:if(if else) 还是 (if if) else

表达式 expression
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
nocomma_expression : IDENT "(" expression ")" %prec CALL

expression : nocomma_expression COMMA expression
| nocomma_expression

nocomma_expression : "(" expression ")"

nocomma_expression : nocomma_expression "[" nocomma_expression "]"

nocomma_expression : nocomma_expression STAR nocomma_expression
| nocomma_expression DIV nocomma_expression
| nocomma_expression MOD nocomma_expression
| nocomma_expression PLUS nocomma_expression
| nocomma_expression MINUS nocomma_expression
| nocomma_expression LSHIFT nocomma_expression
| nocomma_expression RSHIFT nocomma_expression
| nocomma_expression LESS nocomma_expression
| nocomma_expression LESSEQ nocomma_expression
| nocomma_expression GREATER nocomma_expression
| nocomma_expression GREATEREQ nocomma_expression
| nocomma_expression EQ nocomma_expression
| nocomma_expression NEQ nocomma_expression
| nocomma_expression AMPERSAND nocomma_expression
| nocomma_expression BITOR nocomma_expression
| nocomma_expression BITXOR nocomma_expression
| nocomma_expression LOGICOR nocomma_expression
| nocomma_expression LOGICAND nocomma_expression

nocomma_expression : nocomma_expression ASSIGN nocomma_expression
| nocomma_expression PLUSASSIGN nocomma_expression
| nocomma_expression MINUSASSIGN nocomma_expression
| nocomma_expression MULTIASSIGN nocomma_expression
| nocomma_expression DIVIASSIGN nocomma_expression
| nocomma_expression MODASSIGN nocomma_expression
| nocomma_expression RSHIFTASSIGN nocomma_expression
| nocomma_expression LSHIFTASSIGN nocomma_expression
| nocomma_expression BITANDASSIGN nocomma_expression
| nocomma_expression BITXORASSIGN nocomma_expression
| nocomma_expression BITORASSIGN nocomma_expression

nocomma_expression : nocomma_expression QUES nocomma_expression COLON nocomma_expression %prec QUESTION

nocomma_expression : PLUS nocomma_expression %prec UPLUS
| MINUS nocomma_expression %prec UMINUS
| LOGICNOT nocomma_expression
| BITNOT nocomma_expression
| STAR nocomma_expression %prec DEREF
| AMPERSAND nocomma_expression %prec ADDRESS
| SIZEOF nocomma_expression

nocomma_expression : instant_value
| IDENT

instant_value : STRINGINSTANT
| CHARINSTANT
| INTEGERINSTANT
| FLOATINSTANT
| NULL

因为逗号表达式在函数调用中被认为是传参,为了防止其造成的二义性,所以表达式分为两类,不是逗号表达式的表达式nocomma_expression和包含逗号表达式的表达式expression,在需要使用表达式时按情况使用这两类。

解决二义性

LR语法分析器遇到无法解决的二义性被称为移入-规约冲突

对于这个文法

1
2
3
表达式 : 表达式 + 表达式
| 表达式 * 表达式
| 数字

下面描述了LR语法分析器分析3*4+5时遇到移入规约冲突的情景

1
2
3
4
5
6
7
8
步数  符号栈                  输入Token               步骤
---- --------------------- --------------------- -------------------------------
1 $ 3 * 4 + 5$ 移入 3
2 $ 3 * 4 + 5$ 归约 表达式 : 数字
3 $ 表达式 * 4 + 5$ 移入 *
4 $ 表达式 * 4 + 5$ 移入 4
5 $ 表达式 * 4 + 5$ 归约: 表达式 : 数字
6 $ 表达式 * 表达式 + 5$ 移入规约冲突

此时归约最终会变成(3*4)+5,此时移入最终就会变成3*(4+5)yacc通过引入规则的优先级解决了这个问题。

如果表达式 + 表达式的优先级比表达式 * 表达式低,那么在遇到移入归约冲突时,有两种情况:

  • 符号栈中已经可以归约表达式 * 表达式,移入+可以继续构造表达式 + 表达式

    此时优先进行归约

  • 符号栈中已经可以归约表达式 + 表达式,移入*可以继续构造表达式 * 表达式

    此时优先进行移入

如你所见,一个规则的优先级其实就是与冲突的规则的第一个不同终结符的优先级,在遇到这种表达式规则时退化为运算符的优先级。也可以通过%prec指定一个规则的优先级等于优先级表上的哪个token的优先级。

除了优先级还有结合性之分,左结合优先进行符号栈中栈底方向的归约,右结合优先进行符号栈中栈顶方向的归约。

yacc优先级

排在越上面的优先级越低,同行是相同优先级,第一列表明了操作符的结合性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
right PLUSASSIGN MINUSASSIGN MULTIASSIGN DIVIASSIGN MODASSIGN RSHIFTASSIGN LSHIFTASSIGN BITANDASSIGN BITXORASSIGN BITORASSIGN ASSIGN 
right QUESTION
left LOGICOR
left LOGICAND
left BITOR
left BITXOR
left AMPERSAND
left EQ NEQ
left LESS LESSEQ GREATER GREATEREQ
left LSHIFT RSHIFT
left PLUS MINUS
left STAR DIV MOD
right UPLUS UMINUS LOGICNOT BITNOT DEREF ADDRESS SIZEOF
left CALL [
right THEN ELSE
right dec_ptr
left dec_array
python-yacc的缺陷

在语法分析中,如果一个需要进行优先级分析的规则的优先级token没有裸露到优先级表上就会导致预期外的错误。

1
2
3
exp : exp * exp %prec TIMES
....
| * exp %prec DEREF

需要去掉任意一个$prec才能正常处理。

1
2
3
exp : exp * exp
....
| * exp %prec DEREF
语法分析树的生成

不能简单的根据语法分析的结果生成语法分析树,诸如int a,b;的结构完全可以分解成int a;int b;方便后续的实现。基于同样的理由,我们把表达式转换为逆波兰式,在之后的语义分析就能借助栈很轻松的处理,而不是编写递归函数。

借助python的tuple和namedtuple,我们可以很轻松的实现这一过程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from collections import namedtuple
declare = namedtuple('declare',['type','ini'])
array_type = namedtuple('array_type',['size'])
pointer_type = namedtuple('pointer_type',[])
function_type = namedtuple('function_type',['paras'])
#compound_statements = (statement...)
whileStatement = namedtuple('whileStatement',['exp','state'])
forStatement = namedtuple('forStatement',['iniexp','condexp','iterexp','state'])
ifStatement = namedtuple('ifStatement',['exp','then_state','else_state'])
expStatement = namedtuple('expStatement',['exp'])
breakStatement = namedtuple('breakStatement',[])
returnStatement = namedtuple('returnStatement',['exp'])
continueStatement = namedtuple('continueStatement',[])
instantvalue=namedtuple('instantvalue',['value'])
variable=namedtuple('variable',['name','type','value'])

具体实现细节此时忽略,只谈类型的表示,也就是上述代码的type字段。

类型 type 的表示

在HNUSTC中,任何一个标识符和字面量都具有类型信息和名字信息。每一个type都是这样的一个tuple。

1
(名字,类型修饰,...,类型修饰,基本类型)

类型修饰是上文定义的array_typepointer_typefunction_type的一个类型。以下是一个典型的函数type

1
('test', function_type(paras=(('a', 'i32'), ('b', 'i32'))), 'i32')

中间代码生成

LLVM项目介绍

LLVM项目是模块化和可复用的编译器和工具链技术的集合。尽管它的名字叫LLVM,但它与传统的虚拟机没有什么关系。“LLVM”本身不是首字母缩略词;这是项目的全称。

LLVM IR介绍

LLVM 中间代码表示被设计为以三种不同的形式使用:作为内存上的编译器中间表示、作为磁盘上的字节码表示(适合JIT编译器快速加载)以及作为人类可读的汇编语言表示。 这允许LLVM为高效的编译器转换和分析提供强大的中间表示,同时提供一种自然的方式来调试和可视化转换。 三种不同形式的LLVM代码都是等价的。

LLVM 笔记
LLVM抽象机

为LLVM IR设计的LLVM抽象机是一层更贴近计算机硬件并进行合理抽象的概念上的机器,理解LLVM IR抽象机有助于我们理解LLVM IR的执行过程。

LLVM IR抽象机相比C语言抽象机有以下不同:

  • LLVM IR抽象机是对现实世界机器的抽象
  • 一个LLVM IR程序由(模块-函数/全局变量-块-指令)组成
  • 变量存放在寄存器上,寄存器的数量是无限的
  • 可以使用loadstore系列指令进行寄存器和内存间的数据传送,无法直接操作内存的一切对象
  • 对寄存器的操作满足静态单赋值形式
  • 寄存器均有类型属性,一般指令所要求的操作数类型必须相同
以LLVM IR为目标代码相比汇编的目标代码简化了什么
  • 完整静态类型检查

    LLVM自带了一套类型系统,如果使用LLVM库构建LLVM IR,那么会静态检查类型。而汇编的类型系统是隐含的,直到运行时才能发现类型错误。

  • 一些由LLVM后端维护的算法

    比如寄存器分配,后端优化等

对于本科生,这两点弊端均可以接受,而这换来的是对编译器前端工作的完全认识。

使用LLVM IR的优点
  • LLVM IR保留了大部分汇编语言的性质,一是不至于和之后学的汇编语言产生内容上的重叠,二是能为汇编语言做出一个良好的铺垫
  • 让学生能接触到一线编译器的中间代码
  • 能让学生体会到后端优化和前端优化的必要性
具体实现简记

因为使用了python的llvmlite库,不考虑额外优化的话可以很快的搭出编译器。

隐式转换

为了方便我们直接借用LLVM IR的类型系统作为我们的类型表示。在要使用标识符表示的左值时load进寄存器,这个过程被称为load。指定一个目标类型进行类型转换称为cast

在HNUSTC中类型存在三种隐式转换

  1. 传递函数参数以及赋值时的转换
  2. 进行表达式计算时的转换
  3. ifwhilefor等语句中出现的条件表达式的转换

对于1,在load之外还需要进行数组的指针退化操作。对于2,除了进行1的操作外还需要进行类型提升的操作。对于3,除了进行1的操作外还需要进行!=0的判断。

因为HNUST没有强制转换,所有指针都能互相进行隐式转换。

表达式到ir转换递归函数

因为在语法分析后得到的是一个逆波兰式,我们使用栈就能很轻松的实现从表达式到ir的转换。

为了避免&aload a,我们使用rvalue属性标记存放在寄存器上的指针是不是一个需要load的值。因为增加了属性,所以在栈上需要浅拷贝一下。

语句到ir转换递归函数

因为语句会创建新的块,所以语句需要一点复杂的操作,得益于python是引用传递,这一部分得以轻松完成。

在进入一个转换函数的时候,需要额外进行栈指针的保存,在退出一个转换函数时如果没有return掉就恢复栈指针。如果遇到了breakcontinue等语句提前结束块或者正常结束块时就恢复栈指针。

使用printf和scanf

直接全局声明printf和scanf就行,llc出来中间文件之后由clang链接进C库,printf和scanf自然就导入进来了。

其他工具

使用opt+dot工具可以生成控制块流程图

1
2
opt -dot-cfg foo.ll -disable-output -enable-new-pm=0
dot -Tpng -o test_main.png <dotfile>

最终结果展示

A+B Problem
代码
1
2
3
4
5
6
i32 main(){
i32 a,b;
scanf("%d%d",&a,&b);
printf("%d",a+b);
return 0;
}
抽象语法树
1
(declare(type=('main', function_type(paras=()), 'i32'), ini=(declare(type=('a', 'i32'), ini=()), declare(type=('b', 'i32'), ini=()), expStatement(exp=('f(', 'scanf', instantvalue(value='%d%d'), 'a', '&ADDRESS', 'b', '&ADDRESS', 'f)')), expStatement(exp=('f(', 'printf', instantvalue(value='%d'), 'a', 'b', '+', 'f)')), returnStatement(exp=(instantvalue(value=0),)))),)
LLVM IR代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
; ModuleID = ""
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare i8* @"llvm.stacksave"()

declare void @"llvm.stackrestore"(i8* %".1")

declare i32 @"printf"(i8* %".1", ...)

declare i32 @"scanf"(i8* %".1", ...)

define i32 @"main"() noinline optnone
{
entry:
%".2" = alloca i32
%".3" = alloca i32
%".4" = getelementptr [5 x i8], [5 x i8]* @"90bd7ee6-dc40-11ec-b16d-b025aa3ae7d8", i64 0, i64 0
%".5" = call i32 (i8*, ...) @"scanf"(i8* %".4", i32* %".2", i32* %".3")
%".6" = load i32, i32* %".2"
%".7" = load i32, i32* %".3"
%".8" = add i32 %".6", %".7"
%".9" = getelementptr [3 x i8], [3 x i8]* @"90bd8c9c-dc40-11ec-b16d-b025aa3ae7d8", i64 0, i64 0
%".10" = call i32 (i8*, ...) @"printf"(i8* %".9", i32 %".8")
ret i32 0
}

@"90bd7ee6-dc40-11ec-b16d-b025aa3ae7d8" = global [5 x i8] c"%d%d\00"
@"90bd8c9c-dc40-11ec-b16d-b025aa3ae7d8" = global [3 x i8] c"%d\00"
控制块流程图

斐波那契(递归版)
代码
1
2
3
4
5
6
7
8
9
10
i32 fib(i32 n){
if (n<=2) return 1;
return fib(n-1)+fib(n-2);
}
i32 main(){
i32 n;
scanf("%d",&n);
printf("%d",fib(n));
return 0;
}
抽象语法树
1
(declare(type=('fib', function_type(paras=(('n', 'i32'),)), 'i32'), ini=(ifStatement(exp=('n', instantvalue(value=2), '<='), then_state=(returnStatement(exp=(instantvalue(value=1),)),), else_state=()), returnStatement(exp=('f(', 'fib', 'n', instantvalue(value=1), '-', 'f)', 'f(', 'fib', 'n', instantvalue(value=2), '-', 'f)', '+')))), declare(type=('main', function_type(paras=()), 'i32'), ini=(declare(type=('n', 'i32'), ini=()), expStatement(exp=('f(', 'scanf', instantvalue(value='%d'), 'n', '&ADDRESS', 'f)')), expStatement(exp=('f(', 'printf', instantvalue(value='%d'), 'f(', 'fib', 'n', 'f)', 'f)')), returnStatement(exp=(instantvalue(value=0),)))))
LLVM IR代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
; ModuleID = ""
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare i8* @"llvm.stacksave"()

declare void @"llvm.stackrestore"(i8* %".1")

declare i32 @"printf"(i8* %".1", ...)

declare i32 @"scanf"(i8* %".1", ...)

define i32 @"fib"(i32 %"n")
{
entry:
%".3" = alloca i32
store i32 %"n", i32* %".3"
br label %".5"
.5:
%".9" = load i32, i32* %".3"
%".10" = icmp sle i32 %".9", 2
%".11" = sext i1 %".10" to i32
%".12" = icmp ne i32 %".11", 0
br i1 %".12", label %".6", label %".7"
.6:
%".14" = call i8* @"llvm.stacksave"()
ret i32 1
.7:
%".16" = load i32, i32* %".3"
%".17" = sub i32 %".16", 1
%".18" = call i32 @"fib"(i32 %".17")
%".19" = load i32, i32* %".3"
%".20" = sub i32 %".19", 2
%".21" = call i32 @"fib"(i32 %".20")
%".22" = add i32 %".18", %".21"
ret i32 %".22"
}

define i32 @"main"() noinline optnone
{
entry:
%".2" = alloca i32
%".3" = getelementptr [3 x i8], [3 x i8]* @"7bd32084-dc41-11ec-9cef-b025aa3ae7d8", i64 0, i64 0
%".4" = call i32 (i8*, ...) @"scanf"(i8* %".3", i32* %".2")
%".5" = load i32, i32* %".2"
%".6" = call i32 @"fib"(i32 %".5")
%".7" = getelementptr [3 x i8], [3 x i8]* @"7bd32aca-dc41-11ec-9cef-b025aa3ae7d8", i64 0, i64 0
%".8" = call i32 (i8*, ...) @"printf"(i8* %".7", i32 %".6")
ret i32 0
}

@"7bd32084-dc41-11ec-9cef-b025aa3ae7d8" = global [3 x i8] c"%d\00"
@"7bd32aca-dc41-11ec-9cef-b025aa3ae7d8" = global [3 x i8] c"%d\00"
控制块流程图

斐波那契(循环版)
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
i32 main(){
i32 a[3],n;
a[0]=a[1]=1;
scanf("%d",&n);
while (n>2){
a[2]=a[0]+a[1];
a[0]=a[1];
a[1]=a[2];
n=n-1;
}
printf("%d",a[n-1]);
return 0;
}
抽象语法树
1
(declare(type=('main', function_type(paras=()), 'i32'), ini=(declare(type=('a', array_type(size=3), 'i32'), ini=()), declare(type=('n', 'i32'), ini=()), expStatement(exp=('a', instantvalue(value=0), '[]', 'a', instantvalue(value=1), '[]', instantvalue(value=1), '=', '=')), expStatement(exp=('f(', 'scanf', instantvalue(value='%d'), 'n', '&ADDRESS', 'f)')), whileStatement(exp=('n', instantvalue(value=2), '>'), state=(expStatement(exp=('a', instantvalue(value=2), '[]', 'a', instantvalue(value=0), '[]', 'a', instantvalue(value=1), '[]', '+', '=')), expStatement(exp=('a', instantvalue(value=0), '[]', 'a', instantvalue(value=1), '[]', '=')), expStatement(exp=('a', instantvalue(value=1), '[]', 'a', instantvalue(value=2), '[]', '=')), expStatement(exp=('n', 'n', instantvalue(value=1), '-', '=')))), expStatement(exp=('f(', 'printf', instantvalue(value='%d'), 'a', 'n', instantvalue(value=1), '-', '[]', 'f)')), returnStatement(exp=(instantvalue(value=0),)))),)
LLVM IR代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
; ModuleID = ""
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare i8* @"llvm.stacksave"()

declare void @"llvm.stackrestore"(i8* %".1")

declare i32 @"printf"(i8* %".1", ...)

declare i32 @"scanf"(i8* %".1", ...)

define i32 @"main"() noinline optnone
{
entry:
%".2" = alloca [3 x i32]
%".3" = alloca i32
%".4" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 0
%".5" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 1
store i32 1, i32* %".5"
%".7" = load i32, i32* %".5"
store i32 %".7", i32* %".4"
%".9" = getelementptr [3 x i8], [3 x i8]* @"dee5ae62-dc41-11ec-bae3-b025aa3ae7d8", i64 0, i64 0
%".10" = call i32 (i8*, ...) @"scanf"(i8* %".9", i32* %".3")
br label %".11"
.11:
%".15" = load i32, i32* %".3"
%".16" = icmp sgt i32 %".15", 2
%".17" = sext i1 %".16" to i32
%".18" = icmp ne i32 %".17", 0
br i1 %".18", label %".12", label %".13"
.12:
%".20" = call i8* @"llvm.stacksave"()
%".21" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 2
%".22" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 0
%".23" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 1
%".24" = load i32, i32* %".22"
%".25" = load i32, i32* %".23"
%".26" = add i32 %".24", %".25"
store i32 %".26", i32* %".21"
%".28" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 0
%".29" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 1
%".30" = load i32, i32* %".29"
store i32 %".30", i32* %".28"
%".32" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 1
%".33" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 2
%".34" = load i32, i32* %".33"
store i32 %".34", i32* %".32"
%".36" = load i32, i32* %".3"
%".37" = sub i32 %".36", 1
store i32 %".37", i32* %".3"
call void @"llvm.stackrestore"(i8* %".20")
br label %".11"
.13:
%".41" = load i32, i32* %".3"
%".42" = sub i32 %".41", 1
%".43" = getelementptr [3 x i32], [3 x i32]* %".2", i64 0, i32 %".42"
%".44" = load i32, i32* %".43"
%".45" = getelementptr [3 x i8], [3 x i8]* @"dee5c226-dc41-11ec-bae3-b025aa3ae7d8", i64 0, i64 0
%".46" = call i32 (i8*, ...) @"printf"(i8* %".45", i32 %".44")
ret i32 0
}

@"dee5ae62-dc41-11ec-bae3-b025aa3ae7d8" = global [3 x i8] c"%d\00"
@"dee5c226-dc41-11ec-bae3-b025aa3ae7d8" = global [3 x i8] c"%d\00"
控制块流程图

LOJ#2318. 「NOIP2017」宝藏
代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
i32 min(i32 a, i32 b) {
if (a >= b)
return b;
return a;
}

i32 n, m, fun[100000], dis[20], val[20][20];

void dfs(i32 o) {
i32 i, j;
for (i = 1; i <= n; i = i + 1)
if (1 << (i - 1) & o)
for (j = 1; j <= n; j = j + 1)
if (!((1 << (j - 1)) & o) && val[i][j] != 1e8)
if (fun[o | (1 << (j - 1))] > fun[o] + dis[i] * val[i][j]) {
i32 temp = dis[j];
dis[j] = dis[i] + 1;
fun[o | (1 << (j - 1))] = fun[o] + dis[i] * val[i][j];
dfs(o | (1 << (j - 1)));
dis[j] = temp;
}
}
i32 main() {
scanf("%d %d", &n, &m);
i32 i, j;
for (i = 1; i <= n; i = i + 1)
for (j = 1; j <= n; j = j + 1)
val[i][j] = 1e8;
for (i = 1; i <= m; i = i + 1) {
i32 u, v, l;
scanf("%d %d %d", &u, &v, &l);
val[u][v] = min(val[u][v], l);
val[v][u] = min(val[v][u], l);
}
i32 ret = 1e8, sum;
for (sum = 1; sum <= n; sum = sum + 1) {
for (i = 1; i <= n; i = i + 1)
dis[i] = 1e8;
for (i = 1; i <= (1 << n) - 1; i = i + 1)
fun[i] = 1e8;
dis[sum] = 1;
fun[1 << (sum - 1)] = 0;
dfs(1 << (sum - 1));
ret = min(ret, fun[(1 << n) - 1]);
}
printf("%d", ret);
return 0;
}
抽象语法树
1
(declare(type=('min', function_type(paras=(('a', 'i32'), ('b', 'i32'))), 'i32'), ini=(ifStatement(exp=('a', 'b', '>='), then_state=(returnStatement(exp=('b',)),), else_state=()), returnStatement(exp=('a',)))), declare(type=('n', 'i32'), ini=()), declare(type=('m', 'i32'), ini=()), declare(type=('fun', array_type(size=100000), 'i32'), ini=()), declare(type=('dis', array_type(size=20), 'i32'), ini=()), declare(type=('val', array_type(size=20), array_type(size=20), 'i32'), ini=()), declare(type=('dfs', function_type(paras=(('o', 'i32'),)), 'void'), ini=(declare(type=('i', 'i32'), ini=()), declare(type=('j', 'i32'), ini=()), forStatement(iniexp=('i', instantvalue(value=1), '='), condexp=('i', 'n', '<='), iterexp=('i', 'i', instantvalue(value=1), '+', '='), state=(ifStatement(exp=(instantvalue(value=1), '(', 'i', instantvalue(value=1), '-', ')', '<<', 'o', '&'), then_state=(forStatement(iniexp=('j', instantvalue(value=1), '='), condexp=('j', 'n', '<='), iterexp=('j', 'j', instantvalue(value=1), '+', '='), state=(ifStatement(exp=('(', '(', instantvalue(value=1), '(', 'j', instantvalue(value=1), '-', ')', '<<', ')', 'o', '&', ')', '!', 'val', 'i', '[]', 'j', '[]', instantvalue(value=100000000.0), '!=', '&&'), then_state=(ifStatement(exp=('fun', 'o', '(', instantvalue(value=1), '(', 'j', instantvalue(value=1), '-', ')', '<<', ')', '|', '[]', 'fun', 'o', '[]', 'dis', 'i', '[]', 'val', 'i', '[]', 'j', '[]', '*', '+', '>'), then_state=(declare(type=('temp', 'i32'), ini=('dis', 'j', '[]')), expStatement(exp=('dis', 'j', '[]', 'dis', 'i', '[]', instantvalue(value=1), '+', '=')), expStatement(exp=('fun', 'o', '(', instantvalue(value=1), '(', 'j', instantvalue(value=1), '-', ')', '<<', ')', '|', '[]', 'fun', 'o', '[]', 'dis', 'i', '[]', 'val', 'i', '[]', 'j', '[]', '*', '+', '=')), expStatement(exp=('f(', 'dfs', 'o', '(', instantvalue(value=1), '(', 'j', instantvalue(value=1), '-', ')', '<<', ')', '|', 'f)')), expStatement(exp=('dis', 'j', '[]', 'temp', '='))), else_state=()),), else_state=()),)),), else_state=()),)))), declare(type=('main', function_type(paras=()), 'i32'), ini=(expStatement(exp=('f(', 'scanf', instantvalue(value='%d %d'), 'n', '&ADDRESS', 'm', '&ADDRESS', 'f)')), declare(type=('i', 'i32'), ini=()), declare(type=('j', 'i32'), ini=()), forStatement(iniexp=('i', instantvalue(value=1), '='), condexp=('i', 'n', '<='), iterexp=('i', 'i', instantvalue(value=1), '+', '='), state=(forStatement(iniexp=('j', instantvalue(value=1), '='), condexp=('j', 'n', '<='), iterexp=('j', 'j', instantvalue(value=1), '+', '='), state=(expStatement(exp=('val', 'i', '[]', 'j', '[]', instantvalue(value=100000000.0), '=')),)),)), forStatement(iniexp=('i', instantvalue(value=1), '='), condexp=('i', 'm', '<='), iterexp=('i', 'i', instantvalue(value=1), '+', '='), state=(declare(type=('u', 'i32'), ini=()), declare(type=('v', 'i32'), ini=()), declare(type=('l', 'i32'), ini=()), expStatement(exp=('f(', 'scanf', instantvalue(value='%d %d %d'), 'u', '&ADDRESS', 'v', '&ADDRESS', 'l', '&ADDRESS', 'f)')), expStatement(exp=('val', 'u', '[]', 'v', '[]', 'f(', 'min', 'val', 'u', '[]', 'v', '[]', 'l', 'f)', '=')), expStatement(exp=('val', 'v', '[]', 'u', '[]', 'f(', 'min', 'val', 'v', '[]', 'u', '[]', 'l', 'f)', '=')))), declare(type=('ret', 'i32'), ini=(instantvalue(value=100000000.0),)), declare(type=('sum', 'i32'), ini=()), forStatement(iniexp=('sum', instantvalue(value=1), '='), condexp=('sum', 'n', '<='), iterexp=('sum', 'sum', instantvalue(value=1), '+', '='), state=(forStatement(iniexp=('i', instantvalue(value=1), '='), condexp=('i', 'n', '<='), iterexp=('i', 'i', instantvalue(value=1), '+', '='), state=(expStatement(exp=('dis', 'i', '[]', instantvalue(value=100000000.0), '=')),)), forStatement(iniexp=('i', instantvalue(value=1), '='), condexp=('i', '(', instantvalue(value=1), 'n', '<<', ')', instantvalue(value=1), '-', '<='), iterexp=('i', 'i', instantvalue(value=1), '+', '='), state=(expStatement(exp=('fun', 'i', '[]', instantvalue(value=100000000.0), '=')),)), expStatement(exp=('dis', 'sum', '[]', instantvalue(value=1), '=')), expStatement(exp=('fun', instantvalue(value=1), '(', 'sum', instantvalue(value=1), '-', ')', '<<', '[]', instantvalue(value=0), '=')), expStatement(exp=('f(', 'dfs', instantvalue(value=1), '(', 'sum', instantvalue(value=1), '-', ')', '<<', 'f)')), expStatement(exp=('ret', 'f(', 'min', 'ret', 'fun', '(', instantvalue(value=1), 'n', '<<', ')', instantvalue(value=1), '-', '[]', 'f)', '=')))), expStatement(exp=('f(', 'printf', instantvalue(value='%d'), 'ret', 'f)')), returnStatement(exp=(instantvalue(value=0),)))))
LLVM IR代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
; ModuleID = ""
target triple = "x86_64-unknown-linux-gnu"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

declare i8* @"llvm.stacksave"()

declare void @"llvm.stackrestore"(i8* %".1")

declare i32 @"printf"(i8* %".1", ...)

declare i32 @"scanf"(i8* %".1", ...)

define i32 @"min"(i32 %"a", i32 %"b")
{
entry:
%".4" = alloca i32
store i32 %"a", i32* %".4"
%".6" = alloca i32
store i32 %"b", i32* %".6"
br label %".8"
.8:
%".12" = load i32, i32* %".4"
%".13" = load i32, i32* %".6"
%".14" = icmp sge i32 %".12", %".13"
%".15" = sext i1 %".14" to i32
%".16" = icmp ne i32 %".15", 0
br i1 %".16", label %".9", label %".10"
.9:
%".18" = call i8* @"llvm.stacksave"()
%".19" = load i32, i32* %".6"
ret i32 %".19"
.10:
%".21" = load i32, i32* %".4"
ret i32 %".21"
}

@"n" = dso_local global i32 0
@"m" = dso_local global i32 0
@"fun" = dso_local global [100000 x i32] zeroinitializer
@"dis" = dso_local global [20 x i32] zeroinitializer
@"val" = dso_local global [20 x [20 x i32]] zeroinitializer
define void @"dfs"(i32 %"o")
{
entry:
%".3" = alloca i32
store i32 %"o", i32* %".3"
%".5" = alloca i32
%".6" = alloca i32
store i32 1, i32* %".5"
br label %".8"
.8:
%".12" = load i32, i32* %".5"
%".13" = load i32, i32* @"n"
%".14" = icmp sle i32 %".12", %".13"
%".15" = sext i1 %".14" to i32
%".16" = icmp ne i32 %".15", 0
br i1 %".16", label %".9", label %".10"
.9:
%".18" = call i8* @"llvm.stacksave"()
br label %".19"
.10:
ret void
.19:
%".23" = load i32, i32* %".5"
%".24" = sub i32 %".23", 1
%".25" = shl i32 1, %".24"
%".26" = load i32, i32* %".3"
%".27" = and i32 %".25", %".26"
%".28" = icmp ne i32 %".27", 0
br i1 %".28", label %".20", label %".21"
.20:
%".30" = call i8* @"llvm.stacksave"()
store i32 1, i32* %".6"
br label %".32"
.21:
call void @"llvm.stackrestore"(i8* %".18")
%".149" = load i32, i32* %".5"
%".150" = add i32 %".149", 1
store i32 %".150", i32* %".5"
br label %".8"
.32:
%".36" = load i32, i32* %".6"
%".37" = load i32, i32* @"n"
%".38" = icmp sle i32 %".36", %".37"
%".39" = sext i1 %".38" to i32
%".40" = icmp ne i32 %".39", 0
br i1 %".40", label %".33", label %".34"
.33:
%".42" = call i8* @"llvm.stacksave"()
br label %".43"
.34:
call void @"llvm.stackrestore"(i8* %".30")
br label %".21"
.43:
%".47" = load i32, i32* %".6"
%".48" = sub i32 %".47", 1
%".49" = shl i32 1, %".48"
%".50" = load i32, i32* %".3"
%".51" = and i32 %".49", %".50"
%".52" = trunc i32 %".51" to i1
%".53" = xor i1 %".52", -1
%".54" = load i32, i32* %".5"
%".55" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".54"
%".56" = load i32, i32* %".6"
%".57" = getelementptr [20 x i32], [20 x i32]* %".55", i64 0, i32 %".56"
%".58" = load i32, i32* %".57"
%".59" = sitofp i32 %".58" to double
%".60" = fcmp one double %".59", 0x4197d78400000000
%".61" = and i1 %".53", %".60"
%".62" = sext i1 %".61" to i32
%".63" = icmp ne i32 %".62", 0
br i1 %".63", label %".44", label %".45"
.44:
%".65" = call i8* @"llvm.stacksave"()
br label %".66"
.45:
call void @"llvm.stackrestore"(i8* %".42")
%".142" = load i32, i32* %".6"
%".143" = add i32 %".142", 1
store i32 %".143", i32* %".6"
br label %".32"
.66:
%".70" = load i32, i32* %".6"
%".71" = sub i32 %".70", 1
%".72" = shl i32 1, %".71"
%".73" = load i32, i32* %".3"
%".74" = or i32 %".73", %".72"
%".75" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".74"
%".76" = load i32, i32* %".3"
%".77" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".76"
%".78" = load i32, i32* %".5"
%".79" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".78"
%".80" = load i32, i32* %".5"
%".81" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".80"
%".82" = load i32, i32* %".6"
%".83" = getelementptr [20 x i32], [20 x i32]* %".81", i64 0, i32 %".82"
%".84" = load i32, i32* %".79"
%".85" = load i32, i32* %".83"
%".86" = mul i32 %".84", %".85"
%".87" = load i32, i32* %".77"
%".88" = add i32 %".87", %".86"
%".89" = load i32, i32* %".75"
%".90" = icmp sgt i32 %".89", %".88"
%".91" = sext i1 %".90" to i32
%".92" = icmp ne i32 %".91", 0
br i1 %".92", label %".67", label %".68"
.67:
%".94" = call i8* @"llvm.stacksave"()
%".95" = alloca i32
%".96" = load i32, i32* %".6"
%".97" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".96"
%".98" = load i32, i32* %".97"
store i32 %".98", i32* %".95"
%".100" = load i32, i32* %".6"
%".101" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".100"
%".102" = load i32, i32* %".5"
%".103" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".102"
%".104" = load i32, i32* %".103"
%".105" = add i32 %".104", 1
store i32 %".105", i32* %".101"
%".107" = load i32, i32* %".6"
%".108" = sub i32 %".107", 1
%".109" = shl i32 1, %".108"
%".110" = load i32, i32* %".3"
%".111" = or i32 %".110", %".109"
%".112" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".111"
%".113" = load i32, i32* %".3"
%".114" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".113"
%".115" = load i32, i32* %".5"
%".116" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".115"
%".117" = load i32, i32* %".5"
%".118" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".117"
%".119" = load i32, i32* %".6"
%".120" = getelementptr [20 x i32], [20 x i32]* %".118", i64 0, i32 %".119"
%".121" = load i32, i32* %".116"
%".122" = load i32, i32* %".120"
%".123" = mul i32 %".121", %".122"
%".124" = load i32, i32* %".114"
%".125" = add i32 %".124", %".123"
store i32 %".125", i32* %".112"
%".127" = load i32, i32* %".6"
%".128" = sub i32 %".127", 1
%".129" = shl i32 1, %".128"
%".130" = load i32, i32* %".3"
%".131" = or i32 %".130", %".129"
call void @"dfs"(i32 %".131")
%".133" = load i32, i32* %".6"
%".134" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".133"
%".135" = load i32, i32* %".95"
store i32 %".135", i32* %".134"
call void @"llvm.stackrestore"(i8* %".94")
br label %".68"
.68:
call void @"llvm.stackrestore"(i8* %".65")
br label %".45"
}

define i32 @"main"() noinline optnone
{
entry:
%".2" = getelementptr [6 x i8], [6 x i8]* @"49c07884-dc42-11ec-ac15-b025aa3ae7d8", i64 0, i64 0
%".3" = call i32 (i8*, ...) @"scanf"(i8* %".2", i32* @"n", i32* @"m")
%".4" = alloca i32
%".5" = alloca i32
store i32 1, i32* %".4"
br label %".7"
.7:
%".11" = load i32, i32* %".4"
%".12" = load i32, i32* @"n"
%".13" = icmp sle i32 %".11", %".12"
%".14" = sext i1 %".13" to i32
%".15" = icmp ne i32 %".14", 0
br i1 %".15", label %".8", label %".9"
.8:
%".17" = call i8* @"llvm.stacksave"()
store i32 1, i32* %".5"
br label %".19"
.9:
store i32 1, i32* %".4"
br label %".47"
.19:
%".23" = load i32, i32* %".5"
%".24" = load i32, i32* @"n"
%".25" = icmp sle i32 %".23", %".24"
%".26" = sext i1 %".25" to i32
%".27" = icmp ne i32 %".26", 0
br i1 %".27", label %".20", label %".21"
.20:
%".29" = call i8* @"llvm.stacksave"()
%".30" = load i32, i32* %".4"
%".31" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".30"
%".32" = load i32, i32* %".5"
%".33" = getelementptr [20 x i32], [20 x i32]* %".31", i64 0, i32 %".32"
%".34" = fptosi double 0x4197d78400000000 to i32
store i32 %".34", i32* %".33"
call void @"llvm.stackrestore"(i8* %".29")
%".37" = load i32, i32* %".5"
%".38" = add i32 %".37", 1
store i32 %".38", i32* %".5"
br label %".19"
.21:
call void @"llvm.stackrestore"(i8* %".17")
%".42" = load i32, i32* %".4"
%".43" = add i32 %".42", 1
store i32 %".43", i32* %".4"
br label %".7"
.47:
%".51" = load i32, i32* %".4"
%".52" = load i32, i32* @"m"
%".53" = icmp sle i32 %".51", %".52"
%".54" = sext i1 %".53" to i32
%".55" = icmp ne i32 %".54", 0
br i1 %".55", label %".48", label %".49"
.48:
%".57" = call i8* @"llvm.stacksave"()
%".58" = alloca i32
%".59" = alloca i32
%".60" = alloca i32
%".61" = getelementptr [9 x i8], [9 x i8]* @"49c0ae3a-dc42-11ec-ac15-b025aa3ae7d8", i64 0, i64 0
%".62" = call i32 (i8*, ...) @"scanf"(i8* %".61", i32* %".58", i32* %".59", i32* %".60")
%".63" = load i32, i32* %".58"
%".64" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".63"
%".65" = load i32, i32* %".59"
%".66" = getelementptr [20 x i32], [20 x i32]* %".64", i64 0, i32 %".65"
%".67" = load i32, i32* %".58"
%".68" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".67"
%".69" = load i32, i32* %".59"
%".70" = getelementptr [20 x i32], [20 x i32]* %".68", i64 0, i32 %".69"
%".71" = load i32, i32* %".60"
%".72" = load i32, i32* %".70"
%".73" = call i32 @"min"(i32 %".72", i32 %".71")
store i32 %".73", i32* %".66"
%".75" = load i32, i32* %".59"
%".76" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".75"
%".77" = load i32, i32* %".58"
%".78" = getelementptr [20 x i32], [20 x i32]* %".76", i64 0, i32 %".77"
%".79" = load i32, i32* %".59"
%".80" = getelementptr [20 x [20 x i32]], [20 x [20 x i32]]* @"val", i64 0, i32 %".79"
%".81" = load i32, i32* %".58"
%".82" = getelementptr [20 x i32], [20 x i32]* %".80", i64 0, i32 %".81"
%".83" = load i32, i32* %".60"
%".84" = load i32, i32* %".82"
%".85" = call i32 @"min"(i32 %".84", i32 %".83")
store i32 %".85", i32* %".78"
call void @"llvm.stackrestore"(i8* %".57")
%".88" = load i32, i32* %".4"
%".89" = add i32 %".88", 1
store i32 %".89", i32* %".4"
br label %".47"
.49:
%".92" = alloca i32
%".93" = fptosi double 0x4197d78400000000 to i32
store i32 %".93", i32* %".92"
%".95" = alloca i32
store i32 1, i32* %".95"
br label %".97"
.97:
%".101" = load i32, i32* %".95"
%".102" = load i32, i32* @"n"
%".103" = icmp sle i32 %".101", %".102"
%".104" = sext i1 %".103" to i32
%".105" = icmp ne i32 %".104", 0
br i1 %".105", label %".98", label %".99"
.98:
%".107" = call i8* @"llvm.stacksave"()
store i32 1, i32* %".4"
br label %".109"
.99:
%".177" = load i32, i32* %".92"
%".178" = getelementptr [3 x i8], [3 x i8]* @"49c118f2-dc42-11ec-ac15-b025aa3ae7d8", i64 0, i64 0
%".179" = call i32 (i8*, ...) @"printf"(i8* %".178", i32 %".177")
ret i32 0
.109:
%".113" = load i32, i32* %".4"
%".114" = load i32, i32* @"n"
%".115" = icmp sle i32 %".113", %".114"
%".116" = sext i1 %".115" to i32
%".117" = icmp ne i32 %".116", 0
br i1 %".117", label %".110", label %".111"
.110:
%".119" = call i8* @"llvm.stacksave"()
%".120" = load i32, i32* %".4"
%".121" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".120"
%".122" = fptosi double 0x4197d78400000000 to i32
store i32 %".122", i32* %".121"
call void @"llvm.stackrestore"(i8* %".119")
%".125" = load i32, i32* %".4"
%".126" = add i32 %".125", 1
store i32 %".126", i32* %".4"
br label %".109"
.111:
store i32 1, i32* %".4"
br label %".130"
.130:
%".134" = load i32, i32* @"n"
%".135" = shl i32 1, %".134"
%".136" = sub i32 %".135", 1
%".137" = load i32, i32* %".4"
%".138" = icmp sle i32 %".137", %".136"
%".139" = sext i1 %".138" to i32
%".140" = icmp ne i32 %".139", 0
br i1 %".140", label %".131", label %".132"
.131:
%".142" = call i8* @"llvm.stacksave"()
%".143" = load i32, i32* %".4"
%".144" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".143"
%".145" = fptosi double 0x4197d78400000000 to i32
store i32 %".145", i32* %".144"
call void @"llvm.stackrestore"(i8* %".142")
%".148" = load i32, i32* %".4"
%".149" = add i32 %".148", 1
store i32 %".149", i32* %".4"
br label %".130"
.132:
%".152" = load i32, i32* %".95"
%".153" = getelementptr [20 x i32], [20 x i32]* @"dis", i64 0, i32 %".152"
store i32 1, i32* %".153"
%".155" = load i32, i32* %".95"
%".156" = sub i32 %".155", 1
%".157" = shl i32 1, %".156"
%".158" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".157"
store i32 0, i32* %".158"
%".160" = load i32, i32* %".95"
%".161" = sub i32 %".160", 1
%".162" = shl i32 1, %".161"
call void @"dfs"(i32 %".162")
%".164" = load i32, i32* @"n"
%".165" = shl i32 1, %".164"
%".166" = sub i32 %".165", 1
%".167" = getelementptr [100000 x i32], [100000 x i32]* @"fun", i64 0, i32 %".166"
%".168" = load i32, i32* %".167"
%".169" = load i32, i32* %".92"
%".170" = call i32 @"min"(i32 %".169", i32 %".168")
store i32 %".170", i32* %".92"
call void @"llvm.stackrestore"(i8* %".107")
%".173" = load i32, i32* %".95"
%".174" = add i32 %".173", 1
store i32 %".174", i32* %".95"
br label %".97"
}

@"49c07884-dc42-11ec-ac15-b025aa3ae7d8" = global [6 x i8] c"%d %d\00"
@"49c0ae3a-dc42-11ec-ac15-b025aa3ae7d8" = global [9 x i8] c"%d %d %d\00"
@"49c118f2-dc42-11ec-ac15-b025aa3ae7d8" = global [3 x i8] c"%d\00"
控制块流程图