【问题标题】:Delphi XE2 assembly德尔福 XE2 总成
【发布时间】:2012-02-17 00:40:32
【问题描述】:

我有以下在 Delphi 2006 中工作的函数,但在 Delphi XE2 下它在处理 RET 时会给出访问冲突错误或特权指令错误。

function Q_TrimChar(const S: string; Ch: Char): string;
asm
        PUSH    ESI
        MOV     ESI,ECX
        TEST    EAX,EAX
        JE      @@qt
        MOV     ECX,[EAX-4]
        TEST    ECX,ECX
        JE      @@qt
        PUSH    EBX
        PUSH    EDI
        MOV     EBX,EAX
        MOV     EDI,EDX
        XOR     EDX,EDX
        MOV     EAX,ESI
        CALL    System.@LStrFromPCharLen
        MOV     EDX,EDI
        MOV     ECX,[EBX-4]
@@lp1:  CMP     DL,BYTE PTR [EBX]
        JNE     @@ex1
        INC     EBX
        DEC     ECX
        JNE     @@lp1
        MOV     EDX,[ESI]
        JMP     @@wq
@@ex1:  DEC     ECX
@@lp2:  CMP     DL,BYTE PTR [EBX+ECX]
        JNE     @@ex2
        DEC     ECX
        JMP     @@lp2
@@ex2:  MOV     EDI,[ESI]
        LEA     EDX,[EDI+ECX+1]
@@lp3:  MOV     AL,BYTE PTR [EBX+ECX]
        MOV     BYTE PTR [EDI+ECX],AL
        DEC     ECX
        JNS     @@lp3
@@wq:   MOV     EAX,[ESI]
        MOV     BYTE PTR [EDX],0
        SUB     EDX,EAX
        MOV     [EAX-4],EDX
        POP     EDI
        POP     EBX
        POP     ESI
        RET
@@qt:   MOV     EAX,ESI
        CALL    System.@LStrClr
        POP     ESI
end;

我不太了解组装。有什么问题?

【问题讨论】:

  • 顺便说一下,这个 asm 代码的编码非常糟糕。例如,如果 s 字符串只是 Ch 的一个字符,它将生成一个 AV,恕我直言。忘记它,并在他的回答中使用 Mike 提供的第二个帕斯卡版本。
  • 我投票决定关闭,因为它的定义非常狭窄。尽管有人似乎已经明确回答了这个问题,这完全令人印象深刻,但这种问题对网站的价值就像零一样。

标签: delphi assembly delphi-xe2 delphi-2006 basm


【解决方案1】:

我完全同意 David 的建议,即在 Pascal 中简单地编写代码并支持该答案。除非分析表明这是一个真正的瓶颈,否则就不需要 ASM。这里有两个版本。第一个更容易阅读,但第二个更高效:

function Q_TrimChar(const S: string; Ch: Char): string;
begin
  result := S;
  while (result <> '') and (result[1] = Ch) do Delete(Result, 1, 1);
  while (result <> '') and (result[Length(Result)] = Ch) do Delete(Result, Length(Result), 1);
end;

function Q_TrimChar(const S: string; Ch: Char): string;
var
  First, Last : integer;
begin
  First := 1;
  Last := Length(S);
  while (First < Last) and (S[First] = Ch) do inc(First);
  while (Last >= First) and (S[Last] = Ch) do Dec(Last);
  Result := copy(S, First, Last-First+1);
end;

【讨论】:

  • +1 很好地完成了代码实际所做的工作。第二个版本更好。对此的明显优化是在 First=1 和 Last=Length(S) 时不调用复制。
  • +1。良好的编译器生成的字符串操作例程很难被击败,尤其是在这种简单的情况下。我不认为原始的汇编器比这里提供的第二个帕斯卡变体更快!
  • @CosminPrund 你说得对,第二个版本比原来的 asm 版本错误更少,并且性能相同 - 甚至可能更好,因为恕我直言,asm 版本有错误(例如,如果S=Ch)。有关关闭算法的真正优化 asm 版本,请参阅 this unit 中的 asm 和 pascal 中的 function Trim(const S: RawUTF8): RawUTF8; - 我使用了 John O'Harrow 的汇编器,针对 Delphi 2009/2010/XE/XE2 进行了修改。
【解决方案2】:

Delphi 2006 使用单字节 ANSI 字符,因此 stringAnsiStringCharAnsiChar。在 Delphi 2009 及更高版本上,使用两个字节的 Unicode 字符。这个函数不可能在两个编译器上都工作。

即使是使用 AnsiString 和 AnsiChar 的标准 hack 也不起作用。这个函数对 RTL 实现的假设很可能在现代 Delphi 中不再有效。

我会在 Pascal 中重新编写这个函数并让编译器完成这项工作。这不仅是解决您当前问题的最快方法,而且如果您选择解决这个问题,它还可以帮助您克服 64 位编译的障碍。

【讨论】:

  • 所提供代码中的主要问题绝对不是字符串/AnsiString 问题,而是整个 asm 块本身,由于寄存器布局不同,它在 x64 中根本不起作用 自 Delphi 2009 以来 LStrFromPCharLen API 的新签名。
  • @Arnaud 我发现这种批评相当薄弱。 x64 不是重点。 OP 肯定在编译 32 位。该代码在 32 位 XE2 中不起作用。它也不适用于 32 位 2010 或 XE。另外,我评论了 x64 位,所以我已经涵盖了。至于主要问题,我根本不买。代码的任何一个问题都意味着它不起作用。因此,代码存在多个缺陷。如果一个答案未能识别出每一个问题,这是否值得一票否决?显而易见的结论是需要帕斯卡转换。无需过度分析损坏的代码。
  • 代码的主要问题,尽管AnsiString 使用,是关于LStrFromPCharLen API 更改 - 它预计自 Delphi 2009 以来的代码页。
  • @Arnaud 所以你认为如果没有做出改变,那么 asm 就可以了。它会神奇地处理两个字节字符吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多