Skip to content

Commit

Permalink
Merge pull request chenzomi12#280 from 1190201122/main
Browse files Browse the repository at this point in the history
根据修改意见修改书稿内容(ConstantFold,CSE,DCE)
  • Loading branch information
chenzomi12 authored Aug 18, 2024
2 parents ed1dd95 + 8df20d5 commit 1b2ca0f
Show file tree
Hide file tree
Showing 3 changed files with 7 additions and 7 deletions.
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

0 comments on commit 1b2ca0f

Please sign in to comment.