我喜欢涉及优化,主要是在空间环境中考虑算法流程。在尝试了几种不同的场景之后,这个场景似乎是我能想到的最好的场景。假设这也是必须的,因为在调用此过程时,有时会设置DF,有时则不设置。
OJBECTIVE :将DF返回给调用者。
00 55 push bp
01 89E5 mov bp, sp
03 9C pushfw
04 FD std
... Function/Subroutine body
05 58 pop ax ; Retrieve original flags
06 F6C404 test ah, DF ; Was DF already set
09 7506 jnz Exit ; If so, nothing to do
; Reset DF without altering state of any other flags
0B 9C pushfw
0C 8066FFFB and byte [bp-1], 0FBH ; Strip DF from MSB of EFLAGS
10 9D popfw
Exit:
11 C9 leave
12 C3 ret
排除实际上将成为主体一部分的STD
,这个解决目标的序言/结尾以18个字节为单位。
有没有人实现过比这更严格的方法?
答案 0 :(得分:3)
您有一个不错的选择:
要求DF清除函数入口/出口(私有帮助程序函数除外,它可以使用自定义调用约定)。想要使用std
的功能可以在cld
或call
之前使用ret
。
使用其他标记作为返回值的一部分非常简单:cld
和std
不会影响它们。
在极少数情况下,您需要在以递减顺序遍历的循环内进行函数调用,可能根本不使用lods
或其他字符串指令。它们不是魔术,偶尔dec si
/ mov al, [si]
或其他什么不是代码大小的灾难。或者它意味着循环中的cld
和std
,每个只有1个字节。
大多数情况下,你希望DF清除,以便你向上循环,在这种情况下你可以在循环内部进行函数调用而不会出现任何问题。 (当时不是所有,但这种设计对于常见情况是最好的,并且可以毫不费力地处理不常见的情况)。
一个相当不错的选择:
cld
或std
。所以这不是一个非常好的选择。一个平庸的选项:
cld
或std
,因为它不能假设任何内容,和保存/恢复FLAGS。 (除非你根据已知的调用者和他们放置DF的状态进行优化)。这个有优势的唯一一次是在一个包含函数调用的循环中,它也需要DF设置。
如果要在FLAGS的条件代码中返回状态以及保存/恢复DF,只需将 {{1>之后设置条件代码的指令放在FLAGS 中恢复来电者的DF 。
在条件代码中没有有趣状态的函数中,只需使用popf
即可恢复所有调用者FLAGS。你不需要将调用者的DF合并到当前函数的FLAGS中,而这些函数不会返回任何有趣的FLAGS。
在极少数情况下,您无法在最后一个标志设置指令之前轻松移动最后一个字符串指令,模拟字符串指令可能会更小。而不是inc或dec,使用popf
保持标记不变。 (SI和DI在16位寻址模式下均有效,因此lea si, [si +- 1]
可用于它们。)
lea
/ lods
/ stos
都不是神奇的,甚至不是movs
版本。如果您不关心性能(仅限代码大小),您可以使用the slow loop
instruction和备用寄存器(使用rep
保存/恢复)来模拟rep movs
而不触及标记/ push
如果需要)
pop
您的代码示例无法恢复来电者的DF,它会无条件地清除它。它相当于 0B 9C pushfw
0C 8066FFFB and byte [bp-1], 0FBH ; Strip DF from MSB of EFLAGS
10 9D popfw
。
要恢复来电者的DF,您需要从第一个cld
中提取DF位,并将其合并到最后pushf
的FLAGS值中。该函数,然后 pushf
。这显然是可能的,但效率明显低于代码大小(并且代码大小更大)。
另请注意,popf
速度很慢:在Haswell上它的速度为9 uops,并且每18个周期吞吐量一个。如果您只关心代码大小而不是性能,那么需要popf
/ pushf
的设计一定不会很糟糕,但在我看来,在进入/退出时要求DF清除将赢得大多数时候代码大小,以及性能。
这是32位和64位调用约定选择用于处理DF的内容,我也不明白为什么它在16位代码中也不能正常工作。