C语言 为什么 LLVM 会分配冗余变量?



这是一个简单的C文件,其中包含枚举定义和main函数:

enum days {MON, TUE, WED, THU};
int main() {
enum days d;
d = WED;
return 0;
}

它转译为以下 LLVM IR:

define dso_local i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4
ret i32 0
}

%2显然是d变量,它被分配了 2。如果直接返回零,%1对应什么?

这个%1寄存器是由clang生成的,用于处理函数中的多个返回语句。假设您正在编写一个函数来计算整数的阶乘。取而代之的是这个

int factorial(int n){
int result;
if(n < 2)
result = 1;
else{
result = n * factorial(n-1);
}
return result;
}

你可能会这样做

int factorial(int n){
if(n < 2)
return 1;
return n * factorial(n-1);
}

为什么?因为 Clang 会插入为您保存返回值的result变量。耶。这就是该%1变量的原因。查看 ir 以获取代码的略微修改版本。

修改后的代码,

enum days {MON, TUE, WED, THU};
int main() {
enum days d;
d = WED;
if(d) return 1;
return 0;
}

红外

define dso_local i32 @main() #0 !dbg !15 {
%1 = alloca i32, align 4
%2 = alloca i32, align 4
store i32 0, i32* %1, align 4
store i32 2, i32* %2, align 4, !dbg !22
%3 = load i32, i32* %2, align 4, !dbg !23
%4 = icmp ne i32 %3, 0, !dbg !23
br i1 %4, label %5, label %6, !dbg !25
5:                                                ; preds = %0
store i32 1, i32* %1, align 4, !dbg !26
br label %7, !dbg !26
6:                                                ; preds = %0
store i32 0, i32* %1, align 4, !dbg !27
br label %7, !dbg !27
7:                                                ; preds = %6, %5
%8 = load i32, i32* %1, align 4, !dbg !28
ret i32 %8, !dbg !28
}

现在你看到%1让自己有用吧?大多数具有单个 return 语句的函数都会通过 llvm 的传递之一剥离此变量。

为什么这很重要——实际问题是什么?

我认为您正在寻找的更深层次的答案可能是:LLVM的架构基于相当简单的前端和许多传递。前端必须生成正确的代码,但它不一定是好的代码。他们可以做最简单的工作。

在这种情况下,Clang 生成了几条指令,结果这些指令不用于任何用途。这通常不是问题,因为LLVM的某些部分将摆脱多余的指令。Clang相信这种情况会发生。Clang 不需要避免发出死代码;它的实现可能侧重于正确性、简单性、可测试性等。

因为 Clang 已经完成了语法分析,但 LLVM 甚至还没有开始优化。

Clang前端生成的是IR(中间表示(而不是机器代码。这些变量是 SSA(单个静态分配(;它们还没有绑定到寄存器,实际上经过优化,永远不会是因为它们是多余的。

该代码是源的某种文字表示形式。这是叮当交给LLVM进行优化的东西。基本上,LLVM从那里开始,然后从那里进行优化。事实上,对于版本 10 和x86_64,llc -O2最终将生成:

main: # @main
xor eax, eax
ret

相关内容

  • 没有找到相关文章

最新更新