Clang 为什么在LLVM语言中有一些内在的东西?

Clang 为什么在LLVM语言中有一些内在的东西?,clang,llvm,Clang,Llvm,我发现在中有一些内在函数,例如llvm.memcpy,llvm.va_start 然而,我不知道他们为什么存在,为什么其他人不存在。例如,由于memcpy的原型在string.h中,为什么其他函数,如strcpy,不被视为内在函数 我注意到前端在某些情况下可能会生成特殊的内在函数调用。对于一个简单的情况: #include<string.h> int foo(void){ char str[10] = "str"; return 0; } llvm.memcpy在

我发现在中有一些内在函数,例如
llvm.memcpy
llvm.va_start

然而,我不知道他们为什么存在,为什么其他人不存在。例如,由于
memcpy
的原型在
string.h
中,为什么其他函数,如
strcpy
,不被视为内在函数

我注意到前端在某些情况下可能会生成特殊的内在函数调用。对于一个简单的情况:

#include<string.h>

int foo(void){
    char str[10] = "str";
    return 0;
}
llvm.memcpy
在IR中调用,但不在源代码中。但是,前端能否在没有这种内在特性的情况下生成LLVM IR

我还看到了一篇关于的文档,发现一些特殊函数,如
malloc
free
,包含在LLVM指令中(显然它们已经不存在了)


那么,llvm指令的设计理念是什么呢?

具有内在功能使得扩展llvm更容易,从而利用硬件的功能来执行专门的操作,否则这些操作必须在软件中进行编码

在某些CPU类型中,某些操作(如将数据从一个位置复制到另一个位置)可以完全由硬件执行,但在另一些CPU类型中,必须作为正常功能进行编码

使用这些内在函数允许LLVM输出对内在函数的调用,然后(由编码器)将内在函数转换为目标处理器最有效的形式——专用机器指令或对实际函数的调用

理论上,您可以使用单独的特殊IR指令来覆盖所有这些情况,但是这并不是很容易扩展的。随着时间的推移,必须创建的指令数量将显著增加

它说,在未来

几乎所有对LLVM的扩展都应该作为一个内在函数开始,然后在保证的情况下转换为一条指令


当然,您可以在没有memcpy的情况下完成示例中显示的操作-只是稍微难一点(好吧,可能不是只有4个字节,这可以通过四个单字节移动完成,比memcpy难不了多少-另一方面,如果您初始化str的字符串是128字节[并且
str
足够长以容纳它],一个128个单字节移动的序列将相当笨拙,生成循环也有点笨拙)

但是,内部函数的要点是允许编译器(后端)“理解发生了什么”,因为它将能够确定[至少对于常数]副本的大小,例如,生成两个32位移动,以将
“str”
值存储到
str
变量中。或者,如果金额较大,请调用real
memcpy
,或对中间大小进行循环

最后,简单的答案是“因为编译器可以生成比其他解决方案更好的代码”

我猜没有
strcpy
的原因是
strcpy
可以(更有效地)替换为
memcpy
用于常量字符串,如果字符串不是常量,
strcpy
memcpy
要复杂一点,因此对字符串进行内联优化没有那么大的好处

从理论上讲,所有类型的功能都是固有的,但这是必须进行的“成本/收益”分析——您获得了多少收益,以及编写代码需要多长时间才能做到这一点


[当然,我只是从我使用LLVM的经验中推断出这一点,我从在LLVM中实现内在函数的人那里不知道这一点]

不使用strcpy的另一个原因是它只对以null结尾的C样式字符串有用。LLVM不仅适用于C,许多其他语言(如Fortran、C++)倾向于使用已知长度的字符串,memcpy更有效。非常感谢!你能告诉我如果没有
llvm.memcpy
(伪)IR会是什么样子吗?本质上就是一个“将常量存储到str[0..3]中”的序列。
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
}