Delphi 10.3.1 编译器生成的代码在编译为 64 位时会发出异常

Delphi 10.3.1 compiler generates code that issues an exception when compiled to 64 bits

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

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

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.

这是根据 CPU 视图 :

在 Delphi 10.3.1 中为 64 位生成的汇编代码
CrashOn64.dpr.41: inherited;
0000000000428888 488B7528         mov rsi,[rbp+]
000000000042888C 488D7D20         lea rdi,[rbp+]
0000000000428890 48B9FFFFFFFFFFFFFF1F mov rcx,fffffffffffffff <<< What????????
000000000042889A F348A5           rep movsq                     <<< Crashes here.
000000000042889D A5               movsd
000000000042889E 66A5             movsw
00000000004288A0 A4               movsb
00000000004288A1 488B4D50         mov rcx,[rbp+]
00000000004288A5 488D5520         lea rdx,[rbp+]
00000000004288A9 448B4560         mov r8d,[rbp+]
00000000004288AD E8CEFEFFFF       call TMyBaseClass.Setup

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

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

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

这是一个错误,应该报告1。如问题中所述,每种类型的 open array.

都失败

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

procedure Setup(const aParams: array of const); 

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


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

此错误更为广泛,它不处理 procedures/functions 的 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 的巨大内存移动!!!