使用 llvm 和本地值编号算法擦除冗余表达式

Erasing redundant expression with llvm and local value numbering algorithm

本文关键字:算法 擦除 冗余 表达式 编号 llvm 使用      更新时间:2023-10-16

所以我的C代码是:

#include <stdio.h>
void main(){
int a, b,c, d;
b = 18, c = 112;
b = a - d;
d = a - d;
}

其部分 IR 是:

%5 = load i32, i32* %1, align 4
%6 = load i32, i32* %4, align 4
%7 = sub nsw i32 %5, %6
store i32 %7, i32* %2, align 4
%8 = load i32, i32* %1, align 4
%9 = load i32, i32* %4, align 4
%10 = sub nsw i32 %8, %9
store i32 %10, i32* %4, align 4

我已经实现了 LVN 算法来检测冗余表达式,即 d = a - d。现在为了优化,我需要操作指令并使其 d = b。我不确定如何使用 llvm 进行操作以及如何操作 IR。

我是llvm的新手,所以这可能是一个愚蠢的问题,但我真的很困惑。由于 llvm 在 IR 上工作,我知道当它看到"d = a - d"时,它将首先加载 a 和 d,但需要更改 IR 中的二进制操作和存储指令,以便 %4 从 %2 获取值。谁能帮我检查我是否正确理解了这一点,以及如何操纵 IR 来优化代码。

首先,让我们用一个不调用未定义行为(由于访问未初始化的变量)的程序替换您的示例程序,以便 UB 不会混淆问题:

void f(int a, int b, int c, int d){
b = a - d;
d = a - d;
// Code that uses b and d
}

(我还删除了这两个作业,因为它们没有任何影响,无论如何mem2reg后都会消失。

现在实际回答您的问题:大多数优化在mem2reg传递之后运行,这会在可能的情况下将内存访问转换为寄存器。这很重要,因为与内存位置不同,LLVM 寄存器只能从源中的单个点分配,因此mem2reg将代码转换为 SSA 形式,这是许多优化工作所必需的。

如果我们将mem2reg应用于示例代码,我们将得到:

define void @f(i32, i32, i32, i32) #0 {
%5 = sub nsw i32 %0, %3
%6 = sub nsw i32 %0, %3
; Code that uses b and d
}

所以现在我们应用您的分析来发现%6等同于%5。有了这些信息,我们可以删除%6的定义,并将所有出现的%6替换为%5(请注意,如果%5%6位于不同的基本块中,其中一个不支配另一个,这将更加复杂)。为此,您可以使用uses()方法找到%6的所有用法,该方法告诉您哪些指令%6为哪个操作数。然后,您可以将该操作数设置为对%5的引用。