IR 构建器
IRBuilder
是 LLVM 中间表示 (IR) 生成的主力。它允许您用 LLVM 指令填充函数的基本块。
一个 IRBuilder
内部维护一个当前基本块以及块指令列表中的一个指针。当添加新指令时,它会插入到该点,然后指针会前进到新指令之后。
一个 IRBuilder
也维护一个描述当前源位置的元数据引用,该元数据会附加到所有插入的指令上。
实例化
属性
IRBuilder
具有以下属性
实用工具
定位
以下 IRBuilder
方法帮助您移动当前指令指针
- IRBuilder.position_before(instruction)
定位到给定 instruction 的正前方。当前块也会更改为该指令的基本块。
- IRBuilder.position_after(instruction)
定位到给定 instruction 的正后方。当前块也会更改为该指令的基本块。
- IRBuilder.position_at_start(block)
定位到基本 block 的开头。
- IRBuilder.position_at_end(block)
定位到基本 block 的末尾。
以下上下文管理器允许您临时切换到另一个基本块,然后返回到您之前的位置。
- IRBuilder.goto_block(block)
将构建器定位在基本 block 的末尾(如果它未终止),或者在其终结符之前。
new_block = builder.append_basic_block('foo') with builder.goto_block(new_block): # Now the builder is at the end of *new_block* # ... add instructions # Now the builder has returned to its previous position
- IRBuilder.goto_entry_block()
与
goto_block()
相同,但使用当前函数的入口块。
流程控制辅助函数
以下上下文管理器使创建条件代码变得更容易。
- IRBuilder.if_then(pred, likely=None)
创建一个基本块,其执行取决于谓词 pred(一个
IntType(1)
类型的值)。为条件块之后的指令创建另一个基本块。当前基本块以基于 pred 的条件分支终止。当进入上下文管理器时,构建器定位在条件块的末尾。当退出上下文管理器时,构建器定位在延续块的开头。
如果 likely 不是
None
,它表示 pred 是否很可能为True
,并相应地发出元数据以指定分支权重。
- IRBuilder.if_else(pred, likely=None)
设置两个基本块,其执行取决于谓词 pred(一个
IntType(1)
类型的值)。likely 的含义与if_then()
中的相同。产生一对上下文管理器。它们中的每一个都充当一个
if_then()
上下文管理器——第一个用于 pred 为True
时执行的块,第二个用于 pred 为False
时执行的块。当退出上下文管理器时,构建器定位在一个新的延续块上,两个条件块都将跳转到该延续块。
典型用法
with builder.if_else(pred) as (then, otherwise): with then: # emit instructions for when the predicate is true with otherwise: # emit instructions for when the predicate is false # emit instructions following the if-else block
指令构建
以下方法在当前块的当前索引处插入一个新指令——一个 Instruction
实例。返回新指令。
指令的操作数几乎总是值。
这些方法中的许多还接受一个可选的 name 参数,指定结果值的局部 name。如果未给出,则自动生成唯一名称。
算术运算
在以下方法中,flags 参数是一个可选的字符串序列,用于修改指令的语义。示例包括浮点运算的快速数学标志,以及整数运算中溢出时的环绕是否可以忽略。
整数
- IRBuilder.shl(lhs, rhs, name='', flags=())
将 lhs 左移 rhs 位。
- IRBuilder.lshr(lhs, rhs, name=(), flags=())
将 lhs 逻辑右移 rhs 位。
- IRBuilder.ashr(lhs, rhs, name='', flags=())
将 lhs 算术(有符号)右移 rhs 位。
- IRBuilder.cttz(value, flag)
计算 value 中的尾随零位数。布尔型 flag 指示结果对
0
是否有定义。
- IRBuilder.ctlz(value, flag)
计算 value 中的前导零位数。布尔型 flag 指示结果对
0
是否有定义。
- IRBuilder.add(lhs, rhs, name='', flags=())
整数加法 lhs 和 rhs。
- IRBuilder.sadd_with_overflow(lhs, rhs, name='', flags=())
整数加法 lhs 和 rhs。返回一个
{ 结果, 溢出位 }
结构。
- IRBuilder.sub(lhs, rhs, name='', flags=())
整数减法 lhs 减去 rhs。
- IRBuilder.ssub_with_overflow(lhs, rhs, name='', flags=())
整数减法 lhs 减去 rhs。返回一个
{ 结果, 溢出位 }
结构。
- IRBuilder.mul(lhs, rhs, name='', flags=())
整数乘法 lhs 乘以 rhs。
- IRBuilder.smul_with_overflow(lhs, rhs, name='', flags=())
整数乘法 lhs 乘以 rhs。返回一个
{ 结果, 溢出位 }
结构。
- IRBuilder.sdiv(lhs, rhs, name='', flags=())
有符号整数除法 lhs 除以 rhs。
- IRBuilder.udiv(lhs, rhs, name='', flags=())
无符号整数除法 lhs 除以 rhs。
- IRBuilder.srem(lhs, rhs, name='', flags=())
有符号整数 lhs 除以 rhs 的余数。
- IRBuilder.urem(lhs, rhs, name='', flags=())
无符号整数 lhs 除以 rhs 的余数。
- IRBuilder.and_(lhs, rhs, name='', flags=())
位与 lhs 与 rhs。
- IRBuilder.or_(lhs, rhs, name='', flags=())
位或 lhs 与 rhs。
- IRBuilder.xor(lhs, rhs, name='', flags=())
位异或 lhs 与 rhs。
- IRBuilder.not_(value, name='')
位补 value。
- IRBuilder.neg(value, name='')
对 value 取反。
浮点数
- IRBuilder.fadd(lhs, rhs, name='', flags=())
浮点数加法 lhs 和 rhs。
- IRBuilder.fsub(lhs, rhs, name='', flags=())
浮点数减法 lhs 减去 rhs。
- IRBuilder.fmul(lhs, rhs, name='', flags=())
浮点数乘法 lhs 乘以 rhs。
- IRBuilder.fdiv(lhs, rhs, name='', flags=())
浮点数除法 lhs 除以 rhs。
- IRBuilder.frem(lhs, rhs, name='', flags=())
浮点数 lhs 除以 rhs 的余数。
- IRBuilder.fneg(arg, name='', flags=())
浮点数 arg 的取反。
类型转换
- IRBuilder.trunc(value, typ, name='')
将整数 value 截断为整数类型 typ。
- IRBuilder.zext(value, typ, name='')
将整数 value 零扩展到整数类型 typ。
- IRBuilder.sext(value, typ, name='')
将整数 value 符号扩展到整数类型 typ。
- IRBuilder.fptrunc(value, typ, name='')
将浮点数 value 截断(近似)为浮点类型 typ。
- IRBuilder.fpext(value, typ, name='')
将浮点数 value 扩展到浮点类型 typ。
- IRBuilder.fptosi(value, typ, name='')
将浮点数 value 转换为有符号整数类型 typ。
- IRBuilder.fptoui(value, typ, name='')
将浮点数 value 转换为无符号整数类型 typ。
- IRBuilder.sitofp(value, typ, name='')
将有符号整数 value 转换为浮点类型 typ。
- IRBuilder.uitofp(value, typ, name='')
将无符号整数 value 转换为浮点类型 typ。
- IRBuilder.ptrtoint(value, typ, name='')
将指针 value 转换为整数类型 typ。
- IRBuilder.inttoptr(value, typ, name='')
将整数 value 转换为指针类型 typ。
- IRBuilder.bitcast(value, typ, name='')
将指针 value 转换为指针类型 typ。
- IRBuilder.addrspacecast(value, typ, name='')
将指针 value 转换为不同地址空间的指针类型 typ。
比较
- IRBuilder.icmp_signed(cmpop, lhs, rhs, name='')
有符号整数 lhs 与 rhs 比较。字符串 cmpop 可以是
<
、<=
、==
、!=
、>=
或>
之一。
- IRBuilder.icmp_unsigned(cmpop, lhs, rhs, name='')
无符号整数 lhs 与 rhs 比较。字符串 cmpop 可以是
<
、<=
、==
、!=
、>=
或>
之一。
- IRBuilder.fcmp_ordered(cmpop, lhs, rhs, name='', flags=[])
浮点数 lhs 与 rhs 有序比较。
字符串 cmpop 可以是
<
、<=
、==
、!=
、>=
、>
、ord
或uno
之一。flags 列表可以包含
nnan
、ninf
、nsz
、arcp
和fast
中的任何一个,其中fast
暗示所有之前的标志。
- IRBuilder.fcmp_unordered(cmpop, lhs, rhs, name='', flags=[])
浮点数 lhs 与 rhs 无序比较。
字符串 cmpop 可以是
<
、<=
、==
、!=
、>=
、>
、ord
或uno
之一。flags 列表可以包含
nnan
、ninf
、nsz
、arcp
和fast
中的任何一个,其中fast
暗示所有之前的标志。
条件移动
- IRBuilder.select(cond, lhs, rhs, name='')
二选一——如果 cond 为真则为 lhs,否则为 rhs。
Phi
- IRBuilder.phi(typ, name='')
创建一个 phi 节点。要添加传入边及其值,请在返回值上使用
add_incoming()
方法。
聚合操作
- IRBuilder.insert_value(agg, value, index, name='')
通过在 index 处设置新 value 来构建聚合值 agg 的副本。index 的值类型可以与
extract_value()
中的相同。
向量操作
- IRBuilder.extract_element(vector, idx, name='')
返回位置 idx 处的 value。
- IRBuilder.insert_element(vector, value, idx, name='')
返回向量,其中
vector[idx]
被value
替换。如果 idx 大于或等于向量长度,则结果未定义。
- IRBuilder.shuffle_vector(vector1, vector2, mask, name='')
从 vector1 和 vector2 构建元素的排列。返回一个与 mask 长度相同的新向量。
vector1 和 vector2 必须具有相同的元素类型。
mask 必须是整数类型的常量向量。
内存
- IRBuilder.alloca(typ, size=None, name='')
为 typ 类型的大小值静态分配一个栈槽。如果未给出 size,则分配一个用于 1 个值的栈槽。
- IRBuilder.load(ptr, name='', align=None)
从指针 ptr 加载值。如果传递了 align,它应该是一个 Python 整数,指定保证的指针对齐。
- IRBuilder.store(value, ptr, align=None)
将 value 存储到指针 ptr。如果传递了 align,它应该是一个 Python 整数,指定保证的指针对齐。
- IRBuilder.load_atomic(ptr, ordering, align, name='')
作为给定 ordering 的原子操作,从指针 ptr 加载值。align 必须是一个 Python 整数,指定保证的指针对齐。
- IRBuilder.store_atomic(value, ptr, ordering, align)
作为给定 ordering 的原子操作,将 value 存储到指针 ptr。align 必须是一个 Python 整数,指定保证的指针对齐。
- IRBuilder.gep(ptr, indices, inbounds=False, name='')
getelementptr 指令。给定指向聚合值 ptr 的指针,计算由 indices 序列给出的内部元素的地址。
- llvmlite.ir.cmpxchg(ptr, cmp, val, ordering, failordering=None, name='')
在地址 ptr 进行原子比较并交换。
cmp 是用于与内容比较的值。
val 是要交换进去的新值。
可选的 ordering 和 failordering 指定了此指令的内存模型。
- llvmlite.ir.atomic_rmw(op, ptr, val, ordering, name='')
在地址 ptr 执行原子内存操作 op,操作数为 val。
字符串 op 指定操作——例如,
add
或sub
。可选的 ordering 指定了此指令的内存模型。
函数调用
- IRBuilder.call(fn, args, name='', cconv=None, tail=None, fastmath=(), attrs=(), arg_attrs=None)
调用函数 fn,参数为 args(一个值序列)。
cconv 是可选的调用约定。
tail 控制尾调用优化行为。它可以是以下之一:
None
(默认值):表示没有特定的尾调用优化行为。"tail"
:一个提示,表示调用应该进行尾调用优化,但可能被忽略。"musttail"
:表示为了程序正确性,调用必须进行尾调用优化。"notail"
:表示调用不应进行尾调用优化。
为了向后兼容以前的版本,还接受以下值:
False
等同于None
,表示没有特定行为。True
等同于"tail"
,表示建议进行尾调用优化。
fastmath 是一个字符串或字符串序列,用于指定快速数学标志的名称。
attrs 是一个字符串或字符串序列,表示要附加到调用站点的函数属性。
arg_attrs 是一个字典,将参数索引(常规整数,从零开始)映射到字符串或字符串序列,给出要附加到此调用站点相应参数的属性。如果字典中不存在某个索引,或者 arg_attrs 完全缺失,则不会为给定参数发出任何属性。
如果某些属性(例如
sret
)在函数声明中指定,则为了正确性,它们也必须在每个调用站点指定。(截至 LLVM 11,LLVM 语言参考中似乎没有明确指定这一点。)
分支
以下方法都是终结符
- IRBuilder.cbranch(cond, truebr, falsebr)
根据 cond(一个
IntType(1)
类型的值),条件跳转到 truebr 或 falsebr(均为Block
实例)。此指令是一个PredictableInstr
。
- IRBuilder.ret(value)
从当前函数返回 value。
- IRBuilder.ret_void()
从当前函数返回,不带返回值。
- IRBuilder.switch(value, default)
根据 value 切换到不同的块。如果未匹配到其他块,则 default 是要切换到的块。
要添加非默认目标,请在返回值上使用
add_case()
方法。
- IRBuilder.branch_indirect(address)
跳转到地址为 address 的基本块,address 是
IntType(8).as_pointer()
类型的值。要获取块地址,请使用
BlockAddress
常量。要添加所有可能的跳转目的地,请在返回值上使用
add_destination()
方法。
异常处理
- IRBuilder.invoke(fn, args, normal_to, unwind_to, name='', cconv=None, fastmath=(), attrs=(), arg_attrs=None)
调用函数 fn,参数为 args(一个值序列)。
如果函数 fn 正常返回,控制权将转移到 normal_to。否则,它将转移到 unwind_to,其第一个非 phi 指令必须是
LandingPad
。其余参数给出了在调用站点指定的附加属性;详见
call()
方法的描述。
- IRBuilder.landingpad(typ, personality, name='', cleanup=False)
描述此基本块可以处理哪些异常。
typ 指定了着陆点的返回类型。它是一个包含 2 个指针大小字段的结构体。
personality 指定了异常个性化函数。
cleanup 指定是否应始终将控制权转移到此着陆点,即使没有捕获到匹配的异常。
要添加着陆点子句,请在返回值上使用
add_clause()
方法。有两种着陆点子句:
一个
CatchClause
,它指定要捕获的单个异常的类型信息。类型信息是一个IntType(8).as_pointer().as_pointer()
类型的值;一个
FilterClause
,它指定一个类型信息数组。
每个着陆点必须包含至少一个子句,或者被标记为清理。
着陆点的语义完全由个性化函数决定。有关 LLVM 优化器中处理着陆点的详细信息,请参阅LLVM 中的异常处理。有关个性化函数实现的详细信息,请参阅Itanium 异常处理 ABI。
- IRBuilder.resume(landingpad)
恢复由 landingpad 捕获的异常。用于指示着陆点最终未捕获到异常,可能因为它只执行了清理。
内联汇编
- IRBuilder.asm(ftype, asm, constraint, args, side_effect, name='')
添加一个内联汇编调用指令。例如,这在
load_reg()
和store_reg()
中使用。参数
ftype 是一个函数类型,指定内联汇编调用的输入和输出。
asm 是内联汇编片段——例如,
"mov $2, $0\nadd $1, $0"
。x86 内联汇编使用 AT&T 语法。constraint 定义输入/输出约束——例如
=r,r,r
。args 是输入列表,作为 IR 值。
side_effect 是一个布尔值,指定此指令是否具有约束列表中不可见的副作用。
name 是返回的 LLVM 值的可选名称。
有关这些参数的更多信息,请参阅官方 LLVM 文档。
示例:在 x86 上添加两个 64 位值
fty = FunctionType(IntType(64), [IntType(64),IntType(64)]) add = builder.asm(fty, "mov $2, $0\nadd $1, $0", "=r,r,r", (arg_0, arg_1), True, name="asm_add")
- IRBuilder.load_reg(reg_type, reg_name, name='')
将寄存器值加载到 LLVM 值中。
示例:获取
rax
寄存器的值builder.load_reg(IntType(64), "rax")
- IRBuilder.store_reg(value, reg_type, reg_name, name='')
将 LLVM 值存储到寄存器中。
示例:将
0xAAAAAAAAAAAAAAAA
存储到rax
寄存器中builder.store_reg(Constant(IntType(64), 0xAAAAAAAAAAAAAAAA), IntType(64), "rax")