跳转至

Light IR

Light IR 简介

本课程以 Cminusf 语言为源语言,从 LLVM IR 中裁剪出了适用于教学的精简的 IR 子集,并将其命名为 Light IR。同时依据 LLVM 的设计,为 Light IR 提供了配套简化的 C++ 库,用于生成 IR。

如下是一段 C 语言代码 easy.c 与 其对应的 IR 文件 easy.ll 示例。

  • easy.c:
int main(){
  int a;
  int b;
  a = 1;
  b = 2;
  return a + b;
}
  • easy.ll:
; 整个 .ll 文件称为 module
; ModuleID = 'easy.c'
; ...
; module 中至少有一个 main function
define dso_local i32 @main() #0 {
  ; 此处 main function 仅有 1  basicblock
  ; basicblock 由一系列 instruction 组成
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %3 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 1, i32* %2, align 4
  store i32 2, i32* %3, align 4
  %4 = load i32, i32* %2, align 4
  %5 = load i32, i32* %3, align 4
  %6 = add nsw i32 %4, %5
  ret i32 %6
}
; ...

Light IR 指令

Light IR 指令假设

Light IR 指令从 LLVM IR 中裁剪得到,因此保留了 LLVM IR 如下的指令规范

  • 采用 3 地址的方式
  • %2 = add i32 %0, %1
  • 无限寄存器 + 静态单赋值形式
  • IR 中的变量均代表了一个虚拟寄存器,并且数量无上限
  • 每个虚拟寄存器只被赋值一次
  • 强类型系统

  • 每个操作数都具备自身的类型,分为基本类型,以及组合类型

    基本类型:

    • i1:1 位宽的整数类型
    • i32:32 位宽的整数类型
    • float:单精度浮点数类型
    • label: 基本块的标识符类型

    组合类型:

    • 指针类型:<type> *,例如 i32*, [10 x i32*]
    • 数组类型:[n x <type>],例如 [10 x i32], [10 x [10 x i32]]
    • 函数类型:<ret-type>@(<arg-type>),由函数返回值类型与参数类型列表组合成的类型

    组合类型可以嵌套,例如前面的 [10 x [10 x i32]] 就是嵌套后的类型。

Light IR 指令详解

Terminator Instructions

retbr 都是 Terminator Instructions 也就是终止指令,在 llvm 基本块的定义里,基本块是单进单出的,因此只能有一条终止指令(retbr)。

Ret
  • 概念:返回指令。用于将控制流(以及可选的值)从函数返回给调用者。ret指令有两种形式:一种返回值,然后终结函数,另一种仅终结函数。
  • 格式
  • ret <type> <value>
  • ret void
  • 例子:
  • ret i32 %0
  • ret void
Br
  • 概念:跳转指令。用于使控制流转移到当前功能中的另一个基本块。该指令有两种形式,分别对应于条件分支和无条件分支。
  • 格式:
  • br i1 <cond>, label <iftrue>, label <iffalse>
  • br label <dest>
  • 例子:
  • br i1 %cond, label %truebb, label %falsebb
  • br label %bb

Standard Binary Instructions

Add FAdd
  • 概念:add指令返回其两个i32类型的操作数之和,返回值为i32类型,fadd指令返回其两个float类型的操作数之和,返回值为float类型。
  • 格式:
  • <result> = add <type> <op1>, <op2>
  • <result> = fadd <type> <op1>, <op2>
  • 例子:
  • %2 = add i32 %1, %0
  • %2 = fadd float %1, %0
Sub FSub
  • 概念:sub指令返回其两个i32类型的操作数之差,返回值为i32类型,fsub指令返回其两个float类型的操作数之差,返回值为float类型。
  • 格式与例子与addfadd类似
Mul FMul
  • 概念:mul指令返回其两个i32类型的操作数之积,返回值为i32类型,fmul指令返回其两个float类型的操作数之积,返回值为float类型。
  • 格式与例子与addfadd类似
SDiv FDiv
  • 概念:sdiv指令返回其两个i32类型的操作数之商,返回值为i32类型,fdiv指令返回其两个float类型的操作数之商,返回值为float类型。
  • 格式与例子与addfadd类似

Memory Instructions

Alloca
  • 概念: alloca指令在当前执行函数的栈帧(Stack Frame)上分配内存。
  • 格式:<result> = alloca <type>
  • 例子:
  • %ptr = alloca i32
  • %ptr = alloca [10 x i32]
Load
  • 概念:load指令用于从内存中读取。
  • 格式:<result> = load <type>, <type>* <pointer>
  • 例子:%val = load i32, i32* %ptr
Store
  • 概念:store指令用于写入内存。
  • 格式:store <type> <value>, <type>* <pointer>
  • 例子:store i32 3, i32* %ptr

CastInst

ZExt
  • 概念:zext指令将其操作数 扩展为type2类型。
  • 格式:<result> = zext <type> <value> to <type2>
  • 例子:%1 = zext i1 %0 to i32
FpToSi
  • 概念:fptosi指令将浮点值转换为type2(整数)类型。
  • 格式:<result> = fptosi <type> <value> to <type2>
  • 例子:%Y = fptosi float 1.0E-247 to i32
SiToFp
  • 概念:sitofp指令将有符号整数转换为type2(浮点数)类型。
  • 格式:<result> = sitofp <type> <value> to <type2>
  • 例子:%X = sitofp i32 257 to float

Other Instructions

ICmp FCmp
  • 概念:icmp指令根据两个整数的比较返回布尔值,fcmp指令根据两个浮点数的比较返回布尔值。
  • 格式:
  • <result> = icmp <cond> <type> <op1>, <op2>
    • <cond> = eq | ne | sgt | sge | slt | sle
  • <result> = fcmp <cond> <type> <op1>, <op2>
    • <cond> = eq | ne | ugt | uge | ult | ule
  • 例子:i1 %2 = icmp sge i32 %0, %1
Call
  • 概念:call指令用于使控制流转移到指定的函数,其传入参数绑定到指定的值。在被调用函数中执行ret指令后,如果被调用函数返回值不为 void 类型,控制流程将在函数调用后继续执行该指令,并且该函数的返回值绑定到result参数。
  • 格式:
  • <result> = call <return ty> <func name>(<function args>)
  • call <func name>(<function args>)
  • 例子:
  • %0 = call i32 @func( i32 %1, i32* %0)
  • call @func( i32 %arg)
GetElementPtr
  • 概念:getelementptr指令用于获取数组结构的元素的地址。它 仅执行地址计算 ,并且不访问内存。
  • 格式
  • 数组:<result> = getelementptr <type>, <type>* <ptrval>, i32 0, i32 <idx>
  • 指针(仅在数组作为函数参数时存在):<result> = getelementptr <type>, <type>* <ptrval>, i32 <idx>
  • 参数解释:getelementptr 后面的两个 <type> 一定是相同的。<ptrval> 表示指针,<idx> 表示索引(也即数组下标)
  • 例子:
  • %2 = getelementptr [10 x i32], [10 x i32]* %1, i32 0, i32 %0
  • %2 = getelementptr i32, i32* %1 i32 %0
  • 额外阅读:The Often Misunderstood GEP Instruction