Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

根据修改意见修改书稿内容(ConstantFold,CSE,DCE) #280

Merged
merged 1 commit into from
Aug 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 03Compiler/03Frontend/07ConstantFold.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ dis.dis("day_sec=24*60*60")

上述的 CPython 的字节码表明,python 在对 day_sec 赋值是直接加载计算结果 86400,相比于 3 次载入数据和两次乘法,更加地高效。

表达式 e 可进行常量折叠,当且仅当表达式 e 的所有子表达式都是常量。而子表达式被判断为常量通常需要常量传播的帮助。
表达式可进行常量折叠,当且仅当表达式的所有子表达式都是常量。而子表达式被判断为常量通常需要常量传播的帮助。

举个例子:

Expand Down
4 changes: 2 additions & 2 deletions 03Compiler/03Frontend/08CSE.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ $$

- 如果 $b_i$ 只有一个后继节点 $b_j$,则将 $Insert(i,j)$ 中的表达式插入到 $b_i$ 的出口处。
- 如果 $b_j$ 只有一个前趋节点 $b_i$,则将 $Insert(i,j)$ 中的表达式插入到 $b_j$ 的入口处。
- 若前两个条件都不满足,则在边 {i->j} 上新建一个基本块,将 $Insert(i,j)$ 中的表达式插入到该基本块中。
- 若前两个条件都不满足,则在边 ${b_i\rightarrow b_j}$ 上新建一个基本块,将 $Insert(i,j)$ 中的表达式插入到该基本块中。

需要注意的是,由于插入的是新的赋值语句,所以插入后,有一些表达式冗余了。比如对于一个基本块的向上展示的表达式。因此编译器对每个基本块都需要维护一个删除集合,记为 $Delete(i)$。其定义如下:

Expand All @@ -166,7 +166,7 @@ AI 编译器中公共子表达式消除采取相同的思路,区别在于 AI

![second](images/08CSE02.png)

图中 Op3 和 Op4 都经过了相同的图结构{{Op1,Op2},{Op1->Op2}}, AI 编译器会将相同子图的所有不同输出都连接到同一个子图上,然后会在后续的死代码消除中删除其他相同的子图,从而达到简化计算图的目的。减少计算开销。
图中 Op3 和 Op4 都经过了相同的图结构$\{\{Op1,Op2\},Op1\rightarrow Op2\}$, AI 编译器会将相同子图的所有不同输出都连接到同一个子图上,然后会在后续的死代码消除中删除其他相同的子图,从而达到简化计算图的目的。减少计算开销。

## 公共子表达式实现案例

Expand Down
8 changes: 4 additions & 4 deletions 03Compiler/03Frontend/09DCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,15 @@ $$

![控制依赖](images/09DCE01.png)

如上图,$B_4$ 和 $B_5$ 的控制依赖点为 $B_3$ ,以 $B_4$ 为例,从 $B_3$ 到出口节点 $B_8$,存在非空路径{$B_3$->$B_4$->$B_6$->$B_8$}经过 $B_4$,又存在非空路径{$B_3$->$B_5$->$B_6$->$B_8$}不经过 $B_4$,所以 $B_4$ 的控制依赖点为 $B_3$ , $B_5$ 同理。
如上图,$B_4$ 和 $B_5$ 的控制依赖点为 $B_3$ ,以 $B_4$ 为例,从 $B_3$ 到出口节点 $B_8$,存在非空路径$\{B_3\rightarrow B_4\rightarrow B_6\rightarrow B_8\}$经过 $B_4$,又存在非空路径$\{B_3\rightarrow B_5\rightarrow B_6\rightarrow B_8\}$不经过 $B_4$,所以 $B_4$ 的控制依赖点为 $B_3$ , $B_5$ 同理。

在传统编译器中,对于某个关键操作 i ,假设 i:v <- x op y,如果操作 x 和 y 的定义没有被标记上有用,则将其标记为有用操作。然后遍历关机操作 d 所在块的控制依赖块,将控制依赖块的条件转移操作标记为有用。在标记操作完成后,需要将无用的条件转移操作转化为跳转操作,跳转到该基本块的第一个含有用操作的后支配节点。因为当一个基本块的条件转移操作没有被标记为有用,那么从其后继结点一直到其直接的后向支配者结点,都不包含有用操作。如果其直接后继节点不包含有用的操作,也是同样。由于出口节点是一定含有有用操作,所以上述操作在向后查找的过程中,一定会停止。
在传统编译器中,对于某个关键操作 i ,假设 i:v = x op y,如果操作 x 和 y 的定义没有被标记上有用,则将其标记为有用操作。然后遍历关机操作 d 所在块的控制依赖块,将控制依赖块的条件转移操作标记为有用。在标记操作完成后,需要将无用的条件转移操作转化为跳转操作,跳转到该基本块的第一个含有用操作的后支配节点。因为当一个基本块的条件转移操作没有被标记为有用,那么从其后继结点一直到其直接的后向支配者结点,都不包含有用操作。如果其直接后继节点不包含有用的操作,也是同样。由于出口节点是一定含有有用操作,所以上述操作在向后查找的过程中,一定会停止。

## AI 编译器中的死代码消除

AI 编译器通常是通过分析计算图,找到无用的计算节点或不可达的计算节点,然后消除这些节点。

在计算图中,不可达节点是指从输入节点通过图中的有向边无法到达的节点。如下图,计算图中有 A,B,C 三个算子,假设三个算子都不是输入节点。不存在一条路径从输入节点到 B 节点,所以 B 节点是不可达节点,AI 编译器会删除该节点,并删除其到可达节点的边,即边 B ->C
在计算图中,不可达节点是指从输入节点通过图中的有向边无法到达的节点。如下图,计算图中有 A,B,C 三个算子,假设三个算子都不是输入节点。不存在一条路径从输入节点到 B 节点,所以 B 节点是不可达节点,AI 编译器会删除该节点,并删除其到可达节点的边,即边 $B \rightarrow C$

![不可达计算节点](images/09DCE02.png)

Expand All @@ -139,7 +139,7 @@ AI 编译器通常是通过分析计算图,找到无用的计算节点或不

## 死代码消除案例

以 Golang 为例,简单了解一下传统编译器是死代码消除的一种实现
以 Golang 为例,简单了解一下AI编译器是死代码消除的一种实现

1. 通过控制流分析,找到可达的基本块(ReachableBlocks 函数)。编译器通过深度优先搜索,从入口基本块开始遍历控制流图。将遍历到的所有基本块都标记为可达。
2. 从不可达的基本块中删除出边。遍历控制流图中的所有基本块,如果某个基本块 $b_n$ 未被标记为可达,则遍历 $b_n$ 的所有后继节点,如果后继节点是可达的,则删除 $b_n$ 到该节点的边。
Expand Down