为什么LLVM语言中有一些内在函数?

时间:2014-08-14 07:54:07

标签: clang llvm

我发现LLVM中有一些内在函数,例如llvm.memcpyllvm.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指令中包含了一些特殊函数,如mallocfree(显然它们不再存在)。

那么llvm指令的设计是什么?

2 个答案:

答案 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的扩展都应该作为内部函数启动,然后在有保证的情况下转换为指令。