【问题标题】:Delphi 10.3.1 compiler generates code that issues an exception when compiled to 64 bitsDelphi 10.3.1 编译器生成的代码在编译为 64 位时会发出异常
【发布时间】:2019-09-05 14:37:56
【问题描述】:

以下代码仅在编译为 64 位时才会在 Delphi 10.3.1 中生成异常 (c0000005 ACCESS_VIOLATION)。

但是,相同的代码在 Delphi 10.3.1 中编译为 32 位时不会产生异常。此外,在 Delphi 10.2.3 中编译为 32 位或 64 位时它不会失败。

program CrashOn64;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyBaseClass = class
  protected
    procedure Setup(aParams: array of const); virtual;
  public
  end;

type
  TMyWorkClass = class(TMyBaseClass)
  protected
    procedure DoSetup; virtual;
  public
    procedure Setup(aParams: array of const); override;
  end;

{ TMyBaseClass }

procedure TMyBaseClass.Setup(aParams: array of const);
begin
end;

{ TMyWorkClass }

procedure TMyWorkClass.DoSetup;
begin
  inherited;   
end;

procedure TMyWorkClass.Setup(aParams: array of const);
begin
  inherited;
  DoSetup
end;

// main

var
  myClass: TMyWorkClass;
begin
  try
    myClass:=TMyWorkClass.Create;
    try
      myClass.Setup([123]); // <-- Crash on Windows 64-bit
      writeln('OK!')
    finally
      myClass.Free
    end
  except
    on E: Exception do Writeln(E.ClassName, ': ', E.Message);
  end;

  readln; // Wait for Enter key
end.

问题似乎在于参数类型为array of const。如果我们将array of const更改为array of integer,64位代码仍然会失败,因此新的Delphi编译器似乎存在参数数量未知的数组问题。我们找到了通过为array of integer 创建类型来避免编译器错误的技巧,但这个技巧不适用于我们需要的array of const

这是在 Delphi 10.3.1 中根据 CPU 视图为 64 位生成的汇编代码:

CrashOn64.dpr.41: inherited;
0000000000428888 488B7528         mov rsi,[rbp+$28]
000000000042888C 488D7D20         lea rdi,[rbp+$20]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,$1fffffffffffffff <<< What????????
000000000042889A F348A5           rep movsq                     <<< Crashes here.
000000000042889D A5               movsd
000000000042889E 66A5             movsw
00000000004288A0 A4               movsb
00000000004288A1 488B4D50         mov rcx,[rbp+$50]
00000000004288A5 488D5520         lea rdx,[rbp+$20]
00000000004288A9 448B4560         mov r8d,[rbp+$60]
00000000004288AD E8CEFEFFFF       call TMyBaseClass.Setup

这是在 Delphi 10.2.3 中为相同功能生成的 64 位代码:

CrashOn64.dpr.41: inherited;
0000000000427329 488B4D50         mov rcx,[rbp+$50]
000000000042732D 488B5528         mov rdx,[rbp+$28]
0000000000427331 448B4560         mov r8d,[rbp+$60]
0000000000427335 E8E6FEFFFF       call TMyBaseClass.Setup

这是 Delphi 10.3.1 中的 64 位编译器错误还是我们遗漏了什么?有什么解决方法吗?

【问题讨论】:

  • 这肯定是一个编译器错误。我认为您需要向 QualityPortal 提交报告。
  • 还有一个不升级到 10.3.1 的理由,谢谢
  • @NasreddineGalfout,由于对高 DPI 支持的一些改进,我们进行了升级,第一印象总体上不错,但这个问题似乎是个大问题。
  • FWIW,东京代码也复制了数组,但该代码列在开始行下,而不是在继承下。东京使用正确的尺寸。 但这是 Rio 的一个错误,应该报告
  • 哦不,请不要另一个打开数组错误!不久前,我在 Delphi 5 中发现了一个讨厌的 codegen 错误,当在某些条件下使用开放数组时,它会破坏调用堆栈,并且该错误极大地影响了 Indy(它仍然支持 Delphi 5)并且是一个主要的 PITA 解决方案,到了“修复”如此丑陋的地步,我不想公开发布它。如果有 new 开放数组错误,我需要重新检查 Indy :-( Indy 中使用了相当多的开放数组。

标签: delphi delphi-10.3-rio


【解决方案1】:

这是一个错误,应该报告1。如问题中所述,对于每种类型的 open array,它都会失败。

一种解决方法是在方法中将数组定义为const

procedure Setup(const aParams: array of const); 

将打开的数组声明为const,通过引用传递数组,而没有const,它将作为副本按值传递。在这种情况下,Rio 版本会失败。


1 被举报为:Access violation when calling an inherited function with an open array parameter in Rio

【讨论】:

  • 我注意到质量门户上的报告日期为 3 月 8 日。
  • @pep 这并不奇怪。 Emb 不擅长解决问题。我有一个他们声称已在东京修复的错误,但我为他们创建的测试代码仍然失败。
【解决方案2】:

这个错误更广泛,它不处理过程/函数的 VARed 短字符串。

consider this SIMPLE code...
procedure Copyit(var s: shortstring); // VAR is important, pass by value ok
begin
   debugform(s); // break point here
end;

procedure TForm1.Button1Click(Sender: TObject);
var
stuff: shortstring;
begin
   stuff:= 'This is a demo string';
   CopyIt(stuff);
end;

在 32 位模式下,它会生成您期望的代码。 在 64 位模式下,它生成的代码完全如上所示, rcx 设置为 $1FFFFFFFFFFFFFFFF 的巨大内存移动!!!

【讨论】:

    猜你喜欢
    • 2021-11-14
    • 2014-06-24
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 2014-01-12
    相关资源
    最近更新 更多