c -为什么Clang在LLVM IR中复制常量数组?



我用这段代码来演示:

int
main(int argc, char** argv)
{
char a[8] = {65, 66, 67, 68, 69, 70, 71, 72};
printf("a %s", a);
}

我期望的是这个数组将作为常量1放在某个位置和我们只是复制指针,而不是它的内容。以下是Clang在- 0级别优化时所做的:

@__const.main.a = private unnamed_addr constant [8 x i8] c"ABCDEFGH", align 1
@.str = private unnamed_addr constant [5 x i8] c"a %s0", align 1
; Function Attrs: noinline nounwind uwtable
define dso_local i32 @main(i32 noundef %0, i8** noundef %1) #0 {
%3 = alloca i32, align 4
%4 = alloca i8**, align 8
%5 = alloca [8 x i8], align 1
store i32 %0, i32* %3, align 4
store i8** %1, i8*** %4, align 8
%6 = bitcast [8 x i8]* %5 to i8*
call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %6, i8* align 1 getelementptr inbounds ([8 x i8], [8 x i8]* @__const.main.a, i32 0, i32 0), i64 8, i1 false)
%7 = getelementptr inbounds [8 x i8], [8 x i8]* %5, i64 0, i64 0
%8 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i8* noundef %7)
ret i32 0
}
; Function Attrs: argmemonly nofree nounwind willreturn
declare void @llvm.memcpy.p0i8.p0i8.i64(i8* noalias nocapture writeonly, i8* noalias nocapture readonly, i64, i1 immarg) #1

它创建%5,然后使用"llvm.memcpy.p0i8. p0i64 "内部函数转换成%5。为什么会这样?

我把生成的llvm ir文件再次发送给clang,在01级优化clang产生了以下内容:

@.str = private unnamed_addr constant [5 x i8] c"a %s0", align 1
; Function Attrs: nofree noinline nounwind uwtable
define dso_local i32 @main(i32 noundef %0, i8** nocapture noundef readnone %1) local_unnamed_addr #0 {
%3 = alloca i64, align 8
store i64 5208208757389214273, i64* %3, align 8
%4 = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i64 0, i64 0), i64* noundef nonnull %3)
ret i32 0
}

这对我来说比0级优化更有意义。我也测试过巨大的结构体,Clang仍然使用llvm。请务必复制内容。考虑'char a[8]'作为'char**'有什么错呢?因为没有复制和移动东西,如果不引用它将被删除。

我的猜测是,在- 0级别,Clang编译器必须遵守C语言的定义设计。由于创建的类型是'char a[8]',那么它必须'alloca' [8 x i8]。但是当您看到llvm ir文件上的优化时,就会发现llvm.memcpy.p0i8.p0i8。I64 '做了一些神奇的事情来优化代码。

为什么clang在- 0级别复制它?我该怎么做呢?

您声明了一个局部变量,Clang总是为它生成一个alloca。然后用数据填充它,数据是一个聚合,因此Clang将聚合数据发送给一个全局变量和一个内存变量来复制字节。注意,char a[8]是可变的,如果你的代码有a[5]++;,那么它必须只改变这个本地数据的副本,原始数据必须仍然存在,以便下次函数运行时,变量再次用原始值初始化。

您可以指出您的代码不会改变a的内容,但是clang不会这样工作。它尽可能简单地将每个Clang AST节点转换为LLVM IR。这种分析留给LLVM的优化。

最新更新