gpt4 book ai didi

delphi - 如何在 Delphi 汇编器中将短条件跳转与分支目标对齐与 `.align` 协调起来?

转载 作者:行者123 更新时间:2023-12-03 15:32:06 25 4
gpt4 key购买 nike

如何在 Delphi 汇编器中协调短条件跳转与分支目标对齐?

我正在使用 Delphi 版本 10.2 Tokyo,用于 32 位和 64 位程序集,完全使用程序集编写一些函数。

如果我不使用.align,编译器会正确编码short条件跳转指令(由1字节操作码组成的2字节指令074h 和 1 字节相对偏移量 -+ 直至 07Fh)。但是,如果我放置一个 .align,即使小到 .align 4 - 所有条件跳转指令都位于 .align 之前,并且目标位于 .align 之后.align - 在这种情况下,所有这些指令都变成 6 字节指令,而不是应有的 2 字节指令。只有位于 .align 之后的指令仍被正确编码为 2 字节 short

Delphi 汇编器不接受“短”前缀。

如何在 Delphi 汇编器中使用 .align 协调短条件跳转和分支目标对齐?

这是一个示例程序 - 请注意中间有一个.align

    procedure Test; assembler;
label
label1, label2, label3;
asm
mov al, 1
cmp al, 2
je label1
je label2
je label3
label1:
mov al, 3
cmp al, 4
je label1
je label2
je label3
mov al, 5
.align 4
label2:
cmp al, 6
je label1
je label2
je label3
mov al, 7
cmp al, 8
je label1
je label2
je label3
label3:
end;

下面是它的编码方式 - 位于 align 之前的条件跳转,指向 label2 和 label3(在 align 之后)被编码为 6 字节指令(这是 64 位 CPU 目标):

0041C354 B001          mov al,$01      //   mov     al, 1
0041C356 3C02 cmp al,$02 // cmp al, 2
0041C358 740C jz $0041c366 // je label1
0041C35A 0F841C000000 jz $0041c37c // je label2
0041C360 0F8426000000 jz $0041c38c // je label3
0041C366 B003 mov al,$03 //label1: mov al, 3
0041C368 3C04 cmp al,$04 // cmp al, 4
0041C36A 74FA jz $0041c366 // je label1
0041C36C 0F840A000000 jz $0041c37c // je label2
0041C372 0F8414000000 jz $0041c38c // je label3
0041C378 B005 mov al,$05 // mov al, 5
0041C37A 8BC0 mov eax,eax // <-- a 2-byte dummy instruction, inserted by ".align 4" (almost a 2-byte NOP)
0041C37C 3C06 cmp al,$06 //label2: cmp al, 6
0041C37E 74E6 jz $0041c366 // je label1
0041C380 74FA jz $0041c37c // je label2
0041C382 7408 jz $0041c38c // je label3
0041C384 B007 mov al,$07 // mov al, 7
0041C386 3C08 cmp al,$08 // cmp al, 8
0041C388 74DC jz $0041c366 // je label1
0041C38A 74F0 jz $0041c37c // je label2
0041C38C C3 ret // label3:

但是如果我删除 .align - 所有指令都具有正确的大小 - 就像以前一样只有 2 个字节:

0041C354 B001          mov al,$01      //   mov     al, 1
0041C356 3C02 cmp al,$02 // cmp al, 2
0041C358 7404 jz $0041c35e // je label1
0041C35A 740E jz $0041c36a // je label2
0041C35C 741C jz $0041c37a // je label3
0041C35E B003 mov al,$03 //label1: mov al, 3
0041C360 3C04 cmp al,$04 // cmp al, 4
0041C362 74FA jz $0041c35e // je label1
0041C364 7404 jz $0041c36a // je label2
0041C366 7412 jz $0041c37a // je label3
0041C368 B005 mov al,$05 // mov al, 5
0041C36A 3C06 cmp al,$06 //.align 4 label2:cmp al, 6
0041C36C 74F0 jz $0041c35e // je label1
0041C36E 74FA jz $0041c36a // je label2
0041C370 7408 jz $0041c37a // je label3
0041C372 B007 mov al,$07 // mov al, 7
0041C374 3C08 cmp al,$08 // cmp al, 8
0041C376 74E6 jz $0041c35e // je label1
0041C378 74F0 jz $0041c36a // je label2
0041C37A C3 ret // je label3
// label3:

返回条件跳转指令:如何在 Delphi 汇编器中使用 .align 协调短条件跳转和分支目标对齐?

我承认在 SkyLake 及更高版本的处理器上对齐分支目标的好处很小,并且我知道我可以避免使用 .align - 它还会节省代码大小。但我想知道如何使用 Delphi 汇编器通过 align 生成短跳转。此问题不仅在 64 位目标中存在,在 32 位目标中也存在。

最佳答案

除非您的汇编器可以选择进行更好的分支位移优化(这可能需要重复传递),否则您可能会运气不好。 (当然,您可以自己手动完成所有对齐,但每次更改任何内容都必须重新完成。)

或者您可以使用不同的汇编器进行汇编。但正如我所料,这是非常不可取的because you lose access to Delphi-specific stuff like object layout for things declared outside of the asm 。 (感谢@Rudy 的评论。)

您可以在 Delphi 汇编器中编写一些函数,并在那里尽可能多地执行 Delphi 特定的操作。在另一个汇编器中编写关键循环部分,hexdump 将其机器代码输出转储到放入 Delphi 程序集中间的 db 伪指令中。

如果每个函数的开头至少与函数内的任何内容一样对齐,那么这可以正常工作,但您可能最终会浪费指令或将常量放入寄存器中供 NASM 部分使用,这可能会更糟而不仅仅是拥有更长的分支。

<小时/>

Only the instructions that are located after the .align remain correctly encoded as 2-byte short

这不太准确。第一个 je label1 看起来不错,它位于 .align 之前。

看起来任何跨越尚未评估的 .align 指令的分支都会为 rel32 留下空间,并且汇编程序永远不会回来并修复它。其他所有情况似乎都很好:向后分支跨越 .align,以及向前分支不跨越 .align

<小时/>

分支位移优化不是一个简单的问题,特别是当有 .align 指令时。不过,这似乎是一个真正次优的实现。

相关:Why is the "start small" algorithm for branch displacement not optimal?有关汇编器用于分支位移优化的算法的更多信息。即使优秀的汇编器也可能无法做出最佳选择,尤其是当存在 .align 指令时。

关于delphi - 如何在 Delphi 汇编器中将短条件跳转与分支目标对齐与 `.align` 协调起来?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45112065/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com