2

汇编代码中无条件跳转之后的指令是否构成新基本块的开始?假设该指令不是可执行文件中任何分支的目标。

4

2 回答 2

3

由于计算分支的可能性,以及可能总是或从不遵循条件分支,不可能完美地确定什么构成基本块。相反,使用两个假设来估计基本块:

  1. 由于某些其他原因,计算出的分支只会转到作为基本块开始的位置,因此不需要知道它们的目标。
  2. 条件分支既可以被遵循也可以不被遵循。

使用维基百科的定义,一个基本块只能在开始时进入并在结束时退出,从开始到结束只有一条路径。因此,任何条件跳转指令都必须是基本块的结尾,因为它会创建两条路径。计算分支也是终点,因为它们可以产生许多不同的路径。

可以使用稍微不同的定义来讨论无条件跳转是否是基本块的结束。如果一个块中的所有代码在内存中必须是连续的,那么无条件跳转总是结束,除非它转到它之后的指令。否则,它遵循与非跳转指令相同的规则。

对于所有其他指令,如果将在它之后执行的指令是基本块的开始,那么它必须是一个结束。

块的最简单的开始是程序的入口点。此外,作为条件跳转目标的任何指令都是块的开始,因为它可能会或可能不会在跳转指令之后执行。如果一条指令是无条件跳转的目标并且基本块在内存中必须是连续的,那么它就是一个块的开始。否则,如果它是两个或多个无条件跳转的目标,或者它是一个的目标并且它之前的指令不是无条件跳转,那么它就是一个块的开始,因为它有多个路径进入它。

如果无条件跳转之后的指令不是任何其他跳转的目标,它仍可能被标记为基本块的开始,因为它的存在表明计算分支可能以它为目标。

这些基本块开始的规则本质上简化为“在基本块结束之后执行的指令是基本块的开始”。

一种更简单的方法,有时可能会产生更小的块,但通常是好的,任何跳转都是块的结尾,它之后的指令是块的开始,而任何跳转的目标是块的开始并且它之前的指令是一个结束。


如果有更高级别的代码可用,它可以用来更准确地确定块边界。例如,编译器生成的计算分支通常会有一组已知的可能目标。考虑以下 C 代码:

int i, j, k;
switch(i) {
    case 0:
        j++;
        // Flow into the next case
    case 1:
        k++;
}

这是使用计算分支的代码的一些可能的伪汇编:

jump    *(i+jumpTable)
add     1, j
add     1, k

跳转表将针对添加指令之一,因此它们都是基本块的开始。但是,这不能从装配中确定,因此使用第一个假设。第一个添加是基本块的开始,因为它是在跳转之后发生的。但是,没有针对第二个添加的已知分支,因此该分析不认为它是基本块的开始。

于 2013-07-16T23:27:52.957 回答
2

跟随无条件跳转并且不是任何分支或跳转目标的指令是死代码或只是数据。

于 2013-07-16T18:42:56.723 回答