LLVM IR(四)——IR常量(Constants)

可以转载,请注明出处!

4.1 简单常量(Simple Constants)

Boolean constants
布尔类型有两个i1类型的有效常量,分别是truefalse

Integer constants
整数值就是整型常量,比如(12、3、456),负数也算整型常量。说白了也就是一个整型字面量。

Floating-point constants
浮点常量使用标准十进制表示法(例如123.421),指数表示法(例如1.23421e+2)或更精确的十六进制表示法。汇编器需要浮点常量的精确十进制值。例如,汇编程序接受1.25,但拒绝1.3,因为1.3是二进制中的重复小数。浮点常量必须具有浮点类型。

Null pointer constants
标识符null被识别为空指针常量,并且必须是指针类型。

Token constants
标识符none被识别为空的标记常量,并且必须是标记类型。

常量的一个非直观符号是浮点常量的十六进制形式。例如,形式为double 0x432ff973cafa8000等同于double 4.5e+15(前者不太好阅读)。唯一需要十六进制浮点常量的是必须发出浮点常量但不能用合理数目的十进制浮点数表示的浮点常量数字。例如,上面提到的1.3、NaN’s、infinities以及其他特殊值用IEEE十六进制格式表示,这样汇编和反汇编才不会导致常量中的任何位发生更改。

当使用十六进制形式时,bfloathalffloatdouble类型的常量使用上面显示的16位数字表示(匹配IEEE754表示的double)。However,bfloathalffloat值必须能够精确地分别表示为bfloatIEEE 754 half和IEEE 754 float

long double始终使用十六进制格式,并且有三种形式:x86使用的80位格式0xK表示long double,K代表20个十六进制数字(一个16进制占4位,20个刚好80位,丝滑);PowerPC使用的128位格式(两个相邻的双精度)0xM,M代表32个十六进制数字;IEEE 128位格式0xL,L代表32个十六进制数字。long double只有在你的目标上匹配long double格式时才有效。IEEE16位格式(半精度)和bfloat表示为0xH,H代表4个十六进制数字,所有的十六进制格式都是big-endian(左边是符号位)。

4.2 复合常量(Complex Constants)

复合常量就是由一些简单常量和较小复杂常量(类比咋们过程的package中含有数组这一结构)组合而成。

结构常量
结构常量和结构体在形式上看很相像(废话是吧,那肯定很像),都是一对{}括起来一列元素,元素之间用“,”隔开,不同就在于结构体只有类型,结构常量在类型后面跟了具体的值。
例如:{ i32 4, float 17.0, i32* @G },其中@G被声明为@G = external global i32。结构常量必须具有对应的结构体(也就是说你要先定义一个结构体,再根据结构体去定义结构常量),并且元素的数量和类型必须与对应的结构体相匹配。

数组常量
没错那句废话又来了,数组常量和数组类型在结构形式上看很相像。都是用[]将一堆元素括起来,元素之间用逗号隔开,for example:[ i32 42, i32 11, i32 74 ]。数组常量必须具有对应的数组类型,并且元素的数量与类型必须要与对应的数组类型相匹配。
但是有的同志搞特殊啊,字符数组常量可以用双引号引一个字符串,然后在前面加一个前缀字符c,比如全局变量介绍中提到的一个全局常量:c"Hello World\0A\00"。这个跟C语言中定义字符串很像,没有字符串类型,就一个字符数组而已,不过这里有一个前缀c。

向量(Vector)常量
形式很像、类型要匹配,不多哔哔直接for example:< i32 42, i32 11, i32 74, i32 100 >

零初始化
zeroinitializer,用这个字符串,可以将任何类型的值初始化为零,数组结构体啥的都可以。

元数据节点
元数据节点是一个没有类型的常量元组。元数据可以引用常量值,例如:!{!0, !{!2, !0}, !"test"}!{!0, i32 0, i8* @global, i64 (i64)* @function, !"str"}。与其他类型的常量不同,它经常被解释为指令流的一部分,元数据是一个附加额外信息的地方,例如调试信息。

4.3 全局变量和函数的地址(Global Variable and Function Addresses)

全局变量和函数的地址总是隐式有效的(链接时候)常量,当全局标识符作为指针类型使用是,该常量将会被显示引用。

例如:

@X = global i32 17
@Y = global i32 42
@Z = global [2 x i32*] [ i32* @X, i32* @Y ]

这部分其实说的是一个地址常量,也就是地址值,上面@Z就是一个指针数组。

4.4 未定义值(Undefined Values)

字符串undef可以被用于任何需要常量的地方,并表明值的使用者会收到一个未指定的位模式(bit-pattern)。

未定义值(undefined values)可以是任何类型(除了labelvoid),可以在任何允许使用常量的地方使用。它之所以有用是因为,可以向编译器表明,无论使用什么值,程序的定义都是莫得问题。

这是我对于undefined values的理解:undef可以表示一个一定范围内(由与之相应的类型决定,i1就只能是0或1,i2就是0-3中的任意一个)的数,该数的每个二进制位是0还是1不确定,可以随意改变。所以undef的代言是,别问我是什么星座的,我百搭!

看几个例子就能明白:

  %A = add %X, undef	;加法运算
  %B = sub %X, undef	;减法运算
  %C = xor %X, undef	;异或操作
Safe:
  %A = undef
  %B = undef
  %C = undef

解释一下 %A = add %X, undef,由于这里是一个加法运算,定值X的取值不管是多少,整个运算的结果都会受到undef的影响。假设X的值为1,undef不管是0还是1都会影响到A的值;再假设X的值是0,undef不管是0还是1也还是会影响到A的值。所以说,这个加法运算的结果我们是不能确定其值的,所以结果值 %A = undef。另外两个同理。

再看这个例子:

  %A = or %X, undef
  %B = and %X, undef
Safe:
  %A = -1
  %B = 0
Safe:
  %A = %X  ;; By choosing undef as 0
  %B = %X  ;; By choosing undef as -1
Unsafe:
  %A = undef
  %B = undef

解释一下%A = or %X, undef,这是一个按位或运算,当X的值的所有位都为1的时候,不管undef如何取值,对输出的结果都么有影响,值永远为111...111,所以%A = undef肯定是不可行的;再说111...111,二进制的最高位是符号位,0代表正数,1代表负数,负数在内存中以补码的形式存在,将补码111...111换成原码就是100...001,值是-1,所以 %A = -1是莫得问题;至于 %A = %X 我有点没太明白,undef本就是一个可变的值,当其值为0的时候, %A = %X 是成立,但是人家本身就是可变的,难搞哦!另一个按位与运行算更好理解。

官方文档中还有好几个例子,感觉用法很高级,建议自己看一看,但是归根结底还是玩的这个理。

4.5 有害值(Poison Values)

这个要翻译成有害值、不健康值、还是毒药值?那就叫识货吧,哈哈哈!感觉怎么翻译都不合适,后面直接说poison values。来大郎,喝药药!

为了方便投机性(speculative )执行,当提供非法操作数时,许多指令不会立即引起未定义行为,而是返回一个poison value。所以产生这个值,就是代码有问题,但程序并不会终止,依旧执行。

官网的解释,并附加了一个例子,自己看吧!
在这里插入图片描述

举例:

entry:
  %poison = sub nuw i32 0, 1           ; Results in a poison value.
  %still_poison = and i32 %poison, 0   ; 0, but also poison.
  %poison_yet_again = getelementptr i32, i32* @h, i32 %still_poison
  store i32 0, i32* %poison_yet_again  ; Undefined behavior due to
                                       ; store to poison.

  store i32 %poison, i32* @g           ; Poison value stored to memory.
  %poison2 = load i32, i32* @g         ; Poison value loaded back from memory.

  %narrowaddr = bitcast i32* @g to i16*
  %wideaddr = bitcast i32* @g to i64*
  %poison3 = load i16, i16* %narrowaddr ; Returns a poison value.
  %poison4 = load i64, i64* %wideaddr   ; Returns a poison value.

  %cmp = icmp slt i32 %poison, 0       ; Returns a poison value.
  br i1 %cmp, label %end, label %end   ; undefined behavior

end:
4.6 基本块地址(Addresses of Basic Blocks)

语法:

blockaddress(@function, %block)

blockaddress常量在指定的函数中计算指定的基本块的地址,并且总是有一个i8*类型。取出入口块的地址是非法的。

该值仅在作为indirectbr指令或callbr指令的操作数、或者与null做比较时定义了行为。标签地址之间的指针相等性测试(两个指针值是否相等)会导致未定义的行为,不过与null进行比较是可以的,而且没有标签等于空指针。

只要位(bits)没有被检查,该值就可以作为一个不透明的(opaque )指针大小的值进行传递。只要在使用indirectbr指令或callbr指令之前重新构造原始值,就可以对这些值执行ptrtoint转换(ptrtoint...to...是一个转换指令,将指针值转换成其他类型的值)和算术运算。

Finally,将值用于内联汇编(inline assembly)的操作数时,一些目标可能会提供已定义的语义,但是这是特定于目标的。

4.7 常量表达式(Constant Expressions)

常量表达式是用来允许包含其他常量的表达式作为常量使用。这句话的意思是expression+constant这一结构作为一个constant来使用,就叫常量表达式。常量表达式可以是任何的first class类型,可以涉及任何没有副作用(比如loadcall指令就不支持)的LLVM操作。

官网上有很多常量表达式操作的语法,可以自己看,只要明白上面这段话,那些语法就都ok。对于常量表达式的操作,没有新的指令,全都是操作变量的指令在这里用来操作常量,比如icmp、trunc、select等等。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yelvens

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值