SysY2022语言定义中不包含无符号整数、结构体、移位操作,整数和浮点数均为32位,比赛测试样例不包含错误。鉴于SysY2022语言的特点,为了IR的简洁,对LLVM IR进行筛选和修改得到如下指令集
1. 终结符指令
2. 一元运算
3. 二元运算
4. 按位二元运算
5. 向量运算
6. 聚合操作
7. 内存访问和寻址操作
8. 转换操作
9. 其他操作
程序中的每个基本块都以“终结符”指令结束,该指令指示在当前块完成后应该执行哪个块。
概述:
“ret”指令用于将控制流(以及可选的值)从函数返回给调用者。
“ret”指令有两种形式:一种是返回一个值然后导致控制流,另一种只是导致控制流发生。
语法:
ret ; Return a value from a non-void function
ret void ; Return from void function
例子:
ret i32 5 ; Return an integer value of 5
ret void ; Return from a void function
概述:
“br”指令用于使控制流转移到当前函数中的不同基本块,条件分支
语法:
br i1 , label , label ; Conditional branch
i1
表示一个布尔值,为真跳转到
标签,为假跳转到
标签例子:
Test:
%cond = icmp eq i32 %a, %b
br i1 %cond, label %IfEqual, label %IfUnequal
IfEqual:
ret i32 1
IfUnequal:
ret i32 0
概述:
“jump”指令用于使控制流转移到当前函数中的不同基本块,无条件跳转指令
语法:
jump label ; Unconditional branch
标签例子:
br label %12
12
标签一元运算符需要单个操作数,对其执行操作并产生单个值。结果值与其操作数具有相同的类型。
概述:
“fneg”指令返回浮点数的相反数
语法:
= fneg
例子:
%8 = fneg float %7
%8
= - %7
二元运算符用于执行程序中的大部分计算。它们需要两个相同类型的操作数,对它们执行操作,并产生一个值。结果值与其操作数具有相同的类型。
概述:
“add”指令返回它的两个整数的和。
语法:
= add ,
= add nsw ,
nsw
代表“No Signed Wrap”。如果存在 nsw 关键字,则如果发生有符号溢出,则 add 的结果值是poison value
。例子:
%7 = add nsw i32 %5, %6
概述:
“add”指令返回它的两个浮点数的和。
语法:
= fadd ,
例子:
%14 = fadd float %8, %13
概述:
“sub”指令返回其两个整数的差。
“sub”指令用于表示大多数其他中间表示中存在的“neg”指令。
语法:
= sub ,
= sub nsw ,
例子:
%11 = sub nsw i32 %10, 1
概述:
“fsub”指令返回其两个浮点数的差。
语法:
= fsub ,
例子:
%8 = fsub float %6, %7
概述:
“mul”指令返回其两个整数的乘积。
语法:
= mul ,
= mul nsw ,
例子:
%6 = mul nsw i32 %4, %5
概述:
“mul”指令返回其两个浮点数的乘积。
语法:
= fmul ,
例子:
%54 = fmul double %53, 1.000000e+01
概述:
“div”指令返回两个整数的商。产生的值是向零舍入的两个操作数的有符号整数商。
语法:
= div ,
= div exact ,
exact
关键字,并发生了舍入(没有整除),则产生的结果值是poison value
例子:
%9 = div i32 %8, 2
概述:
“fdiv”指令返回两个浮点数的商。
语法:
= fdiv ,
例子:
%15 = fdiv float %14, 2.000000e+00
概述:
“rem”指令返回其两个整数的有符号除法的余数。
语法:
= rem ,
例子:
%4 = rem i32 %3, 3
%4
= %3
% 3按位二元运算符用于在程序中进行各种形式的位转换。位运算通常是非常高效的指令。它们需要两个相同类型的操作数,对它们执行操作产生一个与操作数的类型相同的结果值。
概述:
“xor”指令返回其两个整数的按位逻辑异或。
语法:
= xor ,
例子:
%5 = xor i1 %4, -1
%5
= ! %4
读取、写入和分配内存
概述:
“alloca”指令在当前执行函数的栈上分配内存,当这个函数返回给它的调用者时自动释放。
语法:
= alloca [, ] [, align ]
要分配的内存数量,分配的内存大小就是该数量*单个类型空间,如果没有指定,默认情况下该值为1。align
内存对齐值,表示分配的空间至少与该边界对齐例子:
%2 = alloca [10 x i32], align 4
概述:
“load”指令用于从内存中读取。
语法:
= load , * [, align ]
例子:
%7 = load i32, i32* %3, align 4
%3
是一个指针,表示将%7
赋值为%3
所指的地址的值概述:
“store”指令用于写入内存。
语法:
store , * [, align ]
例子:
store i32 0, i32* %1, align 4
%1
所指的地址概述:
“getelementptr”指令用于获取数组的子元素的地址。它只执行地址计算,不访问内存。
语法:
= getelementptr inbounds , * {, }*
是用作计算基础的类型。第二个参数*
是指针,是开始的基地址。其余参数用于指示聚合对象的哪些元素被索引。每个索引的解释取决于被索引到的类型。例子:
%110 = getelementptr inbounds [32 x [2 x i32]], [32 x [2 x i32]]* %2, i32 0, i32 30
%10 = getelementptr inbounds [6 x i32], [6 x i32]* @arr, i32 0, i32 %9
%2
的基地址上没有偏移,第二个索引表示偏移了30 x [2 x i32],返回指向%2
+ 30 x [2 x i32]的 [2 x i32]类型的指针%arr
的基地址上没有偏移,第二个索引表示偏移了%9
x i32,返回指向@arr
+ %9
x i32 的 i32类型的指针此类别中的指令是转换指令(强制转换),它们都采用单个操作数和类型。它们对操作数执行各种位转换
概述:
“ftoi”指令将其浮点操作数转换为最接近(向零舍入)的有符号整数值。如果该值不能适合 ,则结果是poison value。
语法:
= fptosi to
例子:
%35 = fptosi float %34 to i32
概述:
“itof”指令将其操作数解释为有符号整数,并将其转换为相应的浮点值。如果无法准确表示该值,则使用默认舍入模式对其进行舍入。
语法:
= sitofp to
例子:
%4 = sitofp i32 %3 to float
概述:
“icmp”指令根据两个整数或指针的比较返回一个整数(i32)。
语法:
= icmp ,
eq: equal
ne: not equal
gt: greater than
ge: greater or equal
lt: less than
le: less or equal
例子:
%21 = icmp eq i32* %20, %17
%20
等于%17
,则%21
为true
,否则为false
概述:
“fcmp”指令根据浮点数的比较返回一个整数(i32)。
语法:
= fcmp ,
例子:
%5 = fcmp lt float %4, 0.000000e+00
%4
小于0.000000e+00,则返回true,否则返回false概述:
“phi”指令用于实现 SSA 图中表示函数的 φ 节点。
在基本块的开头和 PHI 指令之间不能有非 phi 指令,即 PHI 指令必须在基本块中的第一个。
语法:
= phi [ , ], ...
指定。
代表可能的前置基本块,
代表该前置基本块带来的值例子:
%44 = phi i1 [ false, %32 ], [ %42, %36 ]
%44
的值可能会来自两个基本块:%32
或者%36
。来自%32
%36
的变量值是%42
。概述:
“call”指令代表一个简单的函数调用。“call”指令用于使控制流转移到指定函数,其传入参数绑定到指定值。根据被调用函数中的“ret”指令,控制流继续执行函数调用之后的指令,并且函数的返回值绑定到结果参数。
语法:
= call ()
例子:
%8 = call i32 @func(i32 %7)
@func
的返回值为i32,参数为i32,传递的实参为%7