Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/assembly/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Assembly x86的简单寄存器分配方案_Assembly_Compiler Construction_X86_Allocation_Cpu Registers - Fatal编程技术网

Assembly x86的简单寄存器分配方案

Assembly x86的简单寄存器分配方案,assembly,compiler-construction,x86,allocation,cpu-registers,Assembly,Compiler Construction,X86,Allocation,Cpu Registers,我正在写一个简单的玩具编译器,我来到了生成机器的部分 代码(本例中为x86-32程序集)。这就是我现在所拥有的: 鉴于转让声明: d:=(a-b)+(c-a)-(d+b)*(c+1) 我首先生成以下中间代码(三元组形式的3个地址代码): 我正在使用一个中间代码,希望以后能执行一些优化 在上面。现在,我不操纵3AC并直接从它生成程序集 我的寄存器使用方案如下:我执行所有算术运算 使用EAX并将中间结果保存在其他寄存器EBX、ECX和EDX中。对于 例如,从前面的3AC生成以下组件: mov

我正在写一个简单的玩具编译器,我来到了生成机器的部分 代码(本例中为x86-32程序集)。这就是我现在所拥有的:

鉴于转让声明: d:=(a-b)+(c-a)-(d+b)*(c+1)

我首先生成以下中间代码(三元组形式的3个地址代码):

我正在使用一个中间代码,希望以后能执行一些优化 在上面。现在,我不操纵3AC并直接从它生成程序集

我的寄存器使用方案如下:我执行所有算术运算 使用EAX并将中间结果保存在其他寄存器EBX、ECX和EDX中。对于 例如,从前面的3AC生成以下组件:

mov     eax, a
sub     eax, b      ; eax = (0)
mov     ebx, eax    ; ebx = (0) & eax = free
mov     eax, c
sub     eax, a      ; eax = (1)
add     ebx, eax    ; ebx = (2) & eax = free
mov     eax, d
add     eax, b      ; eax = (3)
mov     ecx, eax    ; ecx = (3) & eax = free
mov     eax, c
add     eax, 1      ; eax = (4)
imul    ecx         ; eax = (5) & ecx = free
sub     ebx, eax    ; ebx = (6) & eax = free
mov     eax, ebx    ; eax = (6) & ebx = free
mov     d, eax
我的问题是:当我需要泄漏EAX的结果但所有 寄存器正忙(EBX、ECX和EDX处于临时状态)。我应该保存文件吗 堆栈中EAX的值,并在以后恢复?如果是这样,我应该保留吗 在每个函数的堆栈框架中为额外的临时变量留出一些额外的空间

我再说一遍,这只是我现在的想法。如果有其他简单的 分配寄存器的方案我想知道(我知道 更复杂的解决方案包括图形着色等,但我只是在寻找一些东西
这很简单。)

如果您的数据多于寄存器,并且推送了多余的数据,那么您必须在使用它之前将其弹出。如果您最终没有使用它(由于分支),那么无论如何您都必须弹出它

因此,您甚至没有使用数据就在推送和弹出

您将把数据推入堆栈,并在需要时将其弹出

您还将抛出保存在将弹出数据移动到的寄存器中的数据

您必须让编译器记住堆栈的深度,并确保从函数返回时堆栈的深度是正确的

在函数之前或之后简单地使用本地存储并不容易。您可以将mov(e)(可能带有偏移量)推到bpxchg(可能带有偏移量)进出本地存储区域,而无需推送

您可以在任何时候从函数中Ret(urn),而无需弹出与您推送的数据量相同的数据,只需将其丢弃在本地存储器中即可

显然,这支持几乎无限数量的“寄存器”非常容易,而推和弹出变成了“3x3(或更多)滑块拼图”游戏

这些滑块难题(以及Rubic的立方体)用螺丝刀解决起来更快(除了世界纪录冠军)。只要把你想要的东西撕成碎片(访问任何内存位置),然后按照你的意愿把它重新组装起来(不要在Ret之前把东西弹出来)——“河内之塔”式的东西不需要来回滑动

使用本地变量而不是堆栈(除非您只缺少一个或两个寄存器,否则推送和弹出的智能例程可能比内存访问更快;就像几乎解决的立方体比用螺丝刀打开更快地解决一样)


将有一个屈服点在2或3(寄存器短于您想要的),其中局部变量的推送和弹出速度比推送和弹出速度要快,这取决于代码以及它如何被洗牌(优化)。

而不是总是将结果计算到EAX中,考虑将结果计算到目标位置,该目标位置可以是寄存器或内存位置

在伪代码中:

for each 3AC instruction I
   Look up the set S of places that hold operands of I
   R = allocate_place(I)  // register or memory for the result
   Emit code that uses S and puts the result of I into R
      // code emitted differs depending on whether R, S are registers or memory
   free_places S
您将使用一个分配器,根据可用的内容提供寄存器名或临时内存位置。分配器保留一个“反向映射”,允许在上面查找指令的每个操作数所在的位置。分配器可以使用多种策略。最简单的方法是先用完所有寄存器,然后开始分配内存

请注意,当生成整个函数的代码时,分配器将知道该函数总共需要多少临时内存位置。函数前导码必须在创建堆栈帧时设置这些。您需要一种机制来“回补”具有正确数量的位置的前导。这有多种可能性。问问你是否需要想法。然后,在继续编译下一个函数之前重置分配器

上述算法在使用其值时立即释放相应的资源(寄存器或内存位置),因为您的简单代码生成器允许此不变量。如果您消除了公共子表达式或进行了其他优化,那么决定何时释放寄存器会变得更加复杂,因为它的值可能会被多次使用


嵌入在表达式中的函数调用会引发其他有趣的情况,需要仔细考虑。如何保存寄存器?

因为这是一个玩具编译器,所以您不必太担心。您可以使用一个简单的
推送
弹出
,只有在必要时才不会中断堆栈。此外,这可能会有所帮助:。哦,还请记住,如果您不需要完整的32位,您可以将它们拆分,然后使用
ah
/
al
bh
/
bl
,等等。您可以用另一种方式来执行此操作。在堆栈框架中为每个变量分配一个插槽,然后开始尝试不使用它们。显而易见的好处是,你总能找到一种方法来泄漏寄存器。谢谢,这正是我想要的。
for each 3AC instruction I
   Look up the set S of places that hold operands of I
   R = allocate_place(I)  // register or memory for the result
   Emit code that uses S and puts the result of I into R
      // code emitted differs depending on whether R, S are registers or memory
   free_places S