从汇编代码和骨架 C 导出数组的大小

deriving size of array from assembly code and skeleton C

我正在尝试改进我的汇编编程,我遇到了这个导出函数中参数值的练习,但我不确定我应该如何使用给定的汇编代码来完成它.

这是令我感到困惑的汇编代码(尝试注释某些行):

arrayfunc:
    leaq    15992(%rdx),%rax // get 1999th element frm Array2
    leaq    -8(%rdx),%r10 //start of Array2
    movq    %rcx,%r9    // store address of Array1 in rcx into r9
.L2:
    leaq    -400(%rdx), %r8 //Array2 - 50longs? but why minus 50longs
    movq    %r9,%rdx    //move address in Array1[i][j] into rdx
.L3: //inner loop
    movslq  (%rdx),%rcx //move value in Array1[i][j] into rcx
    subq    ,%rax     // increment j so becomes Array2[M-1-i][N-1-2j]
    addq    ,%rdx     //increment address to Array1[i][2j]
    movq    %rcx,8(%rax)// what does this line do
    cmpq    %r8,%rax    //compare j<N
    jne .L3
    addq    0,%r9    //Not sure what this line does with the 200
    cmpq    %r10,%rax
    jne .L2
    ret

这是给出的 C 代码:

void arrayfunc(int Array1[M][N], long Array2[M][N])
{
    long i,j;
    for(i=0;i<M;++i)
        for(j=0;j<N;++j)
        {
            Array2[M-1-i][N-1-j] = Array1[i][j];
        }
}

有人可以教我如何正确解释汇编,以便我可以准确地推导出 M 和 N 的值吗?我在解释这些台词时遇到了困难(不确定我是否评论正确,但有些台词我真的不确定所指示的情况)

请帮助我更好地理解这个 asm(注释代码会有很大帮助),因为我真的不知道如何找到 M 和 N 值。

感谢任何帮助。

由于这些代码中存在一些错误,这变得更加困难。第三个 leaq 只有一个操作数,因此缺少目标寄存器。 MN 是常量,否则会有涉及变量(可能还有乘法)的代码用于索引(没有),但是 C 代码说 ++M,这会不允许在常量上使用(应该改为 ++i)。

由于 MN 是常数,所以 Array2[M-1][N-1] 处的元素是 Array2 的常数偏移量(指数组的最后一个元素) .由于它是在循环中使用的,代码在所谓的 loop invariant code motion 中计算该地址——一种优化技术,将一些 fixed/constant 计算重新定位到循环之外,预先执行而不是重复相同的事情循环的每次迭代。

Array2[M-1] 部分,我们导出 (M-1)*N 以获得最后一行的偏移量。从 [N-1] 部分,我们添加到 N-1,然后将整个事情乘以 8,因为在 Array2.

中每长 8 个字节

然后通过公式 ((M-1)*N+N-1)*8 和简化的 (M*N-1)*8M*N*8-8 计算索引的常量部分的完整偏移量。因此 15992 = M*N*8-816000 = M*N*82000 = M*N.

外循环每次迭代向前推进 200 字节,这对应于递增的 i,用于 Array1 的第一个索引位置。由于 Array1 的第一个索引的 +1 映射到 200 字节,因此 Array1 的行的大小(以元素而非字节计)为 200/450,因此 N=50.

因为 N=50 我们可以推断 2000=M*50 因此 2000/50=40=M.


基本上,一种方法是搜索代码以了解其计算方式 Array2[M-1-i][N-1-j]。这是关键 b/c 它是使用 M.

的汇编代码中的表达式

(Array1[i][j] 可能涉及 N,但不涉及 M — 但这里已经优化,author/compiler 识别访问模式是顺序的,因此不需要 i*N+j,只需一个 运行 值,增量为 4)。

这不是微不足道的,因为已经应用了优化技术;这些将计算分散到代码的不同部分,而不是像人们可能期望的那样一起出现在一个地方。变量也被删除(或大量修改),用索引和循环控制变量代替指针。

这一行:movq %rcx,8(%rax)// what does this line do 将赋值写入 Array2 的内存,基本上是 Array2[][]=... 中的 = 运算符。一旦理解了这一点,我们就可以向后推理找到整个索引计算,其中部分展开,各种常量组合在一起。

(另一种方法是弄清楚 i<Mj<N 是如何完成的,尽管由于这些循环控制变量已更改为有利于指针,因此分析很重要,并且包括一些以上分析。)

循环一读一写,在C语言和汇编语言中都是如此。所以内存写一定是Array2的一个元素赋值,内存读movslq (%rdx),%rcx一定是从Array1.

取一个元素

请注意,进一步优化可能会显着改变一些事情,例如循环展开和向量寄存器的使用。