Assembly 将递归性转换为汇编 我试图在汇编中翻译一个C++递归代码。我在32位模式下工作 这是C++代码。< /P> 1 extern "C" void output (unsigned); 2 extern "C" void parcours (unsigned[], unsigned, unsigned = 0); 3 4 void parcours (unsigned v[], unsigned n, unsigned k) { 5 while (k < n) { 6 output(v[k]); 7 parcours(v, n, 2*k+1); 8 k = 2*k+2; 9 } 10 } 1外部“C”无效输出(无符号); 2个外部“C”无效地块(无符号[],无符号,无符号=0); 3. 4个无效地块(无符号v[],无符号n,无符号k){ 5时(k这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途 您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是-S(-fverbose asm也很有用)。对于Microsoft编译器,此选项为
下面是我翻译它的尝试。我正在努力将递归C++(或任何其他高级语言)翻译成程序集。如果您能更正此代码并指出我的错误,我将不胜感激Assembly 将递归性转换为汇编 我试图在汇编中翻译一个C++递归代码。我在32位模式下工作 这是C++代码。< /P> 1 extern "C" void output (unsigned); 2 extern "C" void parcours (unsigned[], unsigned, unsigned = 0); 3 4 void parcours (unsigned v[], unsigned n, unsigned k) { 5 while (k < n) { 6 output(v[k]); 7 parcours(v, n, 2*k+1); 8 k = 2*k+2; 9 } 10 } 1外部“C”无效输出(无符号); 2个外部“C”无效地块(无符号[],无符号,无符号=0); 3. 4个无效地块(无符号v[],无符号n,无符号k){ 5时(k这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途 您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是-S(-fverbose asm也很有用)。对于Microsoft编译器,此选项为,assembly,x86,nasm,Assembly,X86,Nasm,下面是我翻译它的尝试。我正在努力将递归C++(或任何其他高级语言)翻译成程序集。如果您能更正此代码并指出我的错误,我将不胜感激 %include "io.inc" section .text global start start: mov edi, [ebp+8] ;address of the first element of the array mov eax, [ebp+12] ;n mov edx, [ebp+16] ;k parcou
%include "io.inc"
section .text
global start
start:
mov edi, [ebp+8] ;address of the first element of the array
mov eax, [ebp+12] ;n
mov edx, [ebp+16] ;k
parcours:
enter 0,0 ;stack frame creation
push edx ;we save k before the modification
shl edx,1
add edx,1 ;2*k+1
loop:
cmp edx,eax
jb endloop ;if k < n
push [edi+edx] ;we push for the output
call output
pop ebx
call parcours
shl edx,1 ;2*k
add edx,2 ;2*k+2
jmp loop
endloop:
pop edx
leave ;stack frame destruction
ret
%包括“io.inc”
第节.案文
全球启动
开始:
移动电子数据交换[ebp+8];数组的第一个元素的地址
mov-eax,[ebp+12];N
移动edx,[ebp+16];K
地块:
输入0,0;堆栈帧创建
推动edx;我们在修改前保存了k
shl edx,1
加edx,1;2*k+1
循环:
cmp-edx,eax
jb端环;如果k > > P>这是专门为C++代码转换成汇编而设计的工具。这类工具被称为“编译器”,这正是它们的主要用途
您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是
-S
(-fverbose asm
也很有用)。对于Microsoft编译器,此选项为
或者,您可以编译代码,然后使用类似于objdump
(Gnu)或dumpbin
(MS)的工具来显示目标代码的程序集列表。(如果你用调试符号编译,这有很好的效果,在汇编指令中交织相应的C++语句,至少是编译器的能力最好的,因为不一定有1到1的映射,更不用说为了优化目的而指令重新排序的问题)。
两者都有相同的效果。你可以这样做:
extern "C" void output (unsigned);
extern "C" void parcours (unsigned[], unsigned, unsigned = 0);
void parcours (unsigned v[], unsigned n, unsigned k) {
while (k < n) {
output(v[k]);
parcours(v, n, 2*k+1);
k = 2*k+2;
}
}
这是优化编译器为parcours
函数生成的汇编语言代码。从这里开始,您需要做两件事:
仔细阅读代码,确保您理解每条指令的作用、编译器发出指令的原因以及函数作为一个整体是如何工作的。像计算机一样跟踪代码,确保理解代码
在其周围编写一个包装器,其中包含汇编程序通常需要的内容,如section.text
和入口点
请注意,递归只需从parcours
函数中再次调用parcours
函数即可实现。C++和汇编的唯一区别在于C++使用了代码> PARCORs(v,n,2 *k+ 1);<代码>,而程序集使用一系列推送
指令(用于参数),然后是调用包
与您编写的代码相比,此代码有什么不同之处
首先,正如评论中所讨论的,它遵循一种标准的调用约定,其中非易失性寄存器保留在顶部(使用一系列push
指令),并在底部恢复(使用一系列相应的pop
指令)。参数在堆栈上以标准方式从右向左传递
此外,优化编译器(毫不奇怪)比您更擅长生成最佳代码序列。特别是,他们知道使用enter
和leave
创建/销毁堆栈帧的速度很慢,因此他们直接操作堆栈指针(esp
),并且仅在需要时/在需要时才这样做。另一个例子是,他们知道shl reg,1
相当于add reg,reg
,但后者速度更快。他们知道一些可爱的小技巧,比如leaeax,[ebx+1]
是一种有效的方法,可以在ebx
的值上加1,同时将结果存储在eax
中,而无需修改ebx
不过,最重要的是,它们有簿记算法,可以在执行递归函数调用时保持堆栈的正确平衡,这是您在尝试时无法正确执行的操作,这就是导致分段错误的原因
<>我将让你做一个详细的逐行比较C++编译器生成的尝试。这是学习如何编写汇编代码的一种非常有效的方法。就像使用调试器逐步检查代码一样,可以准确地看到代码偏离轨道的地方。现在尝试这样做还不算太晚,并将结果与这个正确的代码进行比较。< P>,有这样的工具,专门用于将C++代码转换成程序集的目的。这类工具被称为“编译器”,这正是它们的主要用途
您所做的是通过编译器运行C++代码,以及适当的标志,使它能够给您中间的汇编语言表示。在Gnu风格的编译器中,如GCC和Clang,这将是
-S
(-fverbose asm
也很有用)。对于Microsoft编译器,此选项为
或者,您可以编译代码,然后使用类似于objdump
(Gnu)或dumpbin
(MS)的工具来显示目标代码的程序集列表。(如果您使用调试符号编译,那么在汇编指令中,将相应的C++语句交织在一起,至少对编译器的AB最好,这是很好的效果。
parcours:
push edi
push esi
push ebx
mov esi, DWORD PTR [esp+20]
mov ebx, DWORD PTR [esp+24]
mov edi, DWORD PTR [esp+16]
cmp ebx, esi
jnb .L1
.L3:
sub esp, 12
push DWORD PTR [edi+ebx*4]
add ebx, ebx
call output
lea eax, [ebx+1]
add esp, 12
add ebx, 2
push eax
push esi
push edi
call parcours
add esp, 16
cmp esi, ebx
ja .L3
.L1:
pop ebx
pop esi
pop edi
ret