从汇编代码和骨架 C 导出数组的大小
Posted
技术标签:
【中文标题】从汇编代码和骨架 C 导出数组的大小【英文标题】:deriving size of array from assembly code and skeleton C 【发布时间】:2021-11-27 09:35:12 【问题描述】:我正在尝试改进我的汇编编程,并且我遇到了这个用于推导此函数中参数值的练习,但我不确定我应该如何使用给定的汇编代码来做这件事。
这是我感到困惑的汇编代码(尝试评论一些行):
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 $8,%rax // increment j so becomes Array2[M-1-i][N-1-2j]
addq $4,%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 $200,%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];
有人可以教我如何正确解释 asm,以便我可以准确地得出 M 和 N 的值吗?我在解释这些行时遇到了困难(不确定我是否评论正确,但有些行我真的不确定发生了什么)
请帮助我更好地理解这个 asm(注释代码会很有帮助),因为我真的不知道如何找到 M 和 N 值。
感谢任何和所有的帮助。
【问题讨论】:
【参考方案1】:由于这些代码中存在一些错误,这一点变得更加困难。第三个leaq
只有一个操作数,所以缺少一个目标寄存器。 M
和 N
是常量,否则会有涉及用于索引的变量(可能还有乘法)的代码(没有),但 C 代码显示 ++M
,这在常量上是不允许的(这应该是 ++i)。
由于M
和N
是常量,所以Array2[M-1][N-1]
处的元素是Array2
的常量偏移量(指的是数组的最后一个元素)。由于这是在循环中使用的,因此代码会在所谓的loop invariant code motion 中计算该地址——这是一种优化技术,可以将一些固定/常数计算移出循环,提前完成,而不是在每次迭代时重复相同的事情。循环。
从Array2[M-1]
部分,我们派生(M-1)*N
以获取最后一行的偏移量。从[N-1]
部分,我们添加到N-1
,然后将整个乘以8,因为Array2
中每长有8 个字节。
然后通过公式((M-1)*N+N-1)*8
计算索引中该常量部分的完整偏移量,并简化为(M*N-1)*8
和M*N*8-8
。因此15992 = M*N*8-8
和16000 = M*N*8
和2000 = M*N
。
外部循环每次迭代都会向前步进200
字节,这对应于递增的i
,用于Array1
的第一个索引位置。由于Array1
的第一个索引的+1
映射到200
字节,所以Array1
行的大小(以元素而不是字节为单位)为200/4
或50
,因此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
——但这里已经优化,作者/编译器识别访问模式是顺序的,所以不需要i*N+j
,只是一个运行值以 4 为增量)。
这不是微不足道的,因为已经应用了优化技术;这些将计算分散到代码的不同部分,而不是像人们可能期望的那样一起出现在一个地方。变量也被消除(或大量修改),用索引和循环控制变量代替指针。
这一行:movq %rcx,8(%rax)// what does this line do
将赋值写入Array2
的内存,基本上是Array2[][]=...
中的=
运算符。一旦理解了这一点,我们就可以向后推理以找到整个索引计算,其中部分展开并组合了各种常量。
(另一种方法是弄清楚i<M
和j<N
是如何完成的,尽管由于这些循环控制变量已更改为有利于指针,因此分析并非易事,并包括上述一些分析。)
在 C 语言和汇编语言中,循环具有一次读取和一次写入。因此,内存写入必须是赋值给Array2
的一个元素,而内存读取movslq (%rdx),%rcx
必须是从Array1
取一个元素。
请注意,进一步的优化可能会显着改变一些事情,例如循环展开和向量寄存器的使用。
【讨论】:
以上是关于从汇编代码和骨架 C 导出数组的大小的主要内容,如果未能解决你的问题,请参考以下文章
需要骨架代码从 PythonWin 调用 Excel VBA