我发现LLVM中有一些内在函数,例如llvm.memcpy
,llvm.va_start
。
但是我不知道它们为什么存在以及为什么其他人不存在。例如,由于memcpy
的原型位于string.h
内,为什么其他函数(如strcpy
)不会被视为内在函数?
我注意到在某些情况下前端可能会生成特殊的内部函数调用。对于一个简单的案例:
#include<string.h>
int foo(void){
char str[10] = "str";
return 0;
}
clang生成的foo
的llvm IR是:
define i32 @foo() #0 {
entry:
%str = alloca [10 x i8], align 1
%0 = bitcast [10 x i8]* %str to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* %0, i8* getelementptr inbounds ([10 x i8]* @foo.str, i32 0, i32 0), i64 10, i32 1, i1 false)
ret i32 0
}
llvm.memcpy
在IR中调用,但不在源代码中。但前端是否可以生成没有这种内在的LLVM IR?
我还发现了一个关于a much earlier version of llvm language reference的文档,发现LLVM指令中包含了一些特殊函数,如malloc
,free
(显然它们不再存在)。
那么llvm指令的设计是什么?
答案 0 :(得分:4)
当然,你可以做你的例子显示的没有memcpy - 只是有点困难(好吧,也许不只有4个字节,这可以在四个单字节移动中完成,而不是比memcpy更难 - 另一方面,如果你初始化str的字符串是128字节[和str
适当长到足以容纳它],128个单字节移动的序列将相当尴尬,并且生成循环也有点笨拙)。
然而,内在函数的要点是允许编译器(后端)“理解发生了什么”,因为它将能够确定[至少为常量]副本的大小,例如, ,生成两个32位移动以将"str"
值存储到str
变量中。或者,如果金额很大,请调用实际memcpy
,或为中间大小设置循环。
最后,简单的答案是“因为编译器可以生成比替代解决方案更好的代码”。
我猜测,没有strcpy
的原因是strcpy
可以用memcpy
替换(更有效)用于常量字符串,并且如果字符串不是常量的话,strcpy
无论如何都比memcpy
复杂一点,因此对内联优化没有多大帮助。
从理论上讲,所有类型的功能都可以是内在的,但这是一项必须完成的“成本/收益”分析 - 您获得了多少,以及编写代码需要多长时间。< / p>
[当然,我只是从我使用LLVM的经验中推断出这一点,我不知道在LLVM中实现内部函数的人]
答案 1 :(得分:2)
具有内部函数可以更容易地扩展LLVM,以利用硬件的功能来执行必须在软件中编码的专门操作。
某些操作(例如将数据从一个地方复制到另一个地方)可以完全由某些CPU类型的硬件执行,但在其他操作中必须编码为正常功能。
使用这些内部函数允许LLVM输出对内部函数的调用,然后将其转换(由编码器)转换为目标处理器的最有效形式 - 专用机器指令或调用实际函数。
理论上,您可以使用单独的特殊IR指令来涵盖所有这些情况,但这些指令不是非常可扩展的。随着时间的推移,必须创建的指令数量会显着增加。
在LLVM docs中,它说
几乎所有LLVM的扩展都应该作为内部函数启动,然后在有保证的情况下转换为指令。