小编Sid*_*esh的帖子

如何影响Android/ARM目标的Delphi XEx代码生成?

更新2017-05-17.我不再为出现此问题的公司工作,也无法访问Delphi XEx.当我在那里时,问题通过迁移到混合FPC + GCC(Pascal + C)来解决,NEON内在函数用于某些例程,它们有所不同.(强烈建议使用FPC + GCC,因为它可以使用标准工具,特别是Valgrind.)如果有人能够通过可靠的示例演示他们如何实际能够从Delphi XEx生成优化的ARM代码,我很高兴接受答案.


Embarcadero的Delphi编译器使用LLVM后端为Android设备生成本机ARM代码.我有大量的Pascal代码需要编译到Android应用程序中,我想知道如何使Delphi生成更高效的代码.现在,我甚至都没有谈论自动SIMD优化等高级功能,只是关于生成合理的代码.当然必须有一种方法将参数传递给LLVM端,或以某种方式影响结果?通常,任何编译器都会有很多选项来影响代码编译和优化,但是Delphi的ARM目标似乎只是"优化开/关"就是这样.

LLVM应该能够产生合理紧密且合理的代码,但似乎Delphi以一种奇怪的方式使用它的设施.Delphi希望非常频繁地使用堆栈,它通常只利用处理器的寄存器r0-r3作为临时变量.也许是最疯狂的,似乎是将正常的32位整数加载为四个1字节的加载操作.如何让Delphi产生更好的ARM代码,而且没有逐字节麻烦的Android?

起初我认为逐字节加载是用于从big-endian交换字节顺序,但事实并非如此,它实际上只是加载一个带有4个单字节加载的32位数字.*可能是加载完整的32位而不进行未对齐的字大小的内存加载.(是否应该避免这是另一回事,这将暗示整个事情是编译器错误)*

让我们来看看这个简单的函数:

function ReadInteger(APInteger : PInteger) : Integer;
begin
  Result := APInteger^;
end;
Run Code Online (Sandbox Code Playgroud)

即使启用了优化,带有更新包1的Delphi XE7以及XE6也会为该功能生成以下ARM汇编代码:

Disassembly of section .text._ZN16Uarmcodetestform11ReadIntegerEPi:

00000000 <_ZN16Uarmcodetestform11ReadIntegerEPi>:
   0:   b580        push    {r7, lr}
   2:   466f        mov r7, sp
   4:   b083        sub sp, #12
   6:   9002        str r0, [sp, #8]
   8:   78c1        ldrb    r1, [r0, #3]
   a:   7882        ldrb    r2, [r0, #2]
   c:   ea42 2101   orr.w   r1, r2, r1, lsl #8
  10:   7842        ldrb    r2, [r0, #1] …
Run Code Online (Sandbox Code Playgroud)

delphi android arm llvm android-ndk

263
推荐指数
1
解决办法
1万
查看次数

什么与指针相关的东西在Delphi XE8的移动编译器中不起作用?

Embarcadero的docwiki页面基于LLVM的Delphi编译器列出了Delphi XE8中的几种语言更改.其中一颗子弹说:

基于LLVM的Delphi编译器不支持使用指针.

这在实践中究竟意味着什么?以前在Delphi XE7中使用哪些与指针相关的东西,在Delphi XE8中不再有效?我似乎无法在Embarcadero的网站上找到对此的深入解释.例如,据说包含更多信息的从桌面迁移Delphi代码到Mobile的页面没有提到"Pointer"这个词.

delphi pointers llvm delphi-xe8

5
推荐指数
1
解决办法
634
查看次数

使用Default()赋值在Delphi中初始化记录变量是否安全?

将默认值(TMyRecord)分配给TMyRecord的变量称为内部调用Finalize,然后将内存清零,就像FillChar那样.例如,在以下问题的答案中已经说过,我确实测试过分配Default()确实会导致调用例如System._FinalizeRecord

如何在Delphi中正确释放包含各种类型的记录?

Initialize(),Default()和FillChar()之间的区别

我的问题是,即使在Delphi没有自动调用Initialize的情况下,初始化这样的记录是否总是安全的?对我来说,在未初始化的记录变量上调用Finalize似乎没有意义.在初始化之前,必须假定内存包含随机垃圾.在这种情况下,我对托管类型特别感兴趣,这些托管类型是动态分配内存的指针,Finalize例程应该通过减少它们的引用计数来完成,等等.在许多情况下,Delphi会自动生成对Initialize的调用,以确保其托管类型保持可管理状态.但不总是.

这是一个示例,说明了一个有问题的案例.正如下面的答案所述,你不应该使用GetMem来分配包含这样的托管类型的记录,但是让我们假设某人做了,然后尝试使用Default()赋值作为初始化

type
  TMyRecord = record
    s1, s2, s3 : String;
  end;
  PMyRecord = ^TMyRecord;

var
  pr : PMyRecord;

begin
  GetMem(pr, SizeOf(TMyRecord)); 
  pr^ := Default(TMyRecord);
...
Run Code Online (Sandbox Code Playgroud)

我故意使用GetMem()而不是New(),因为据我所知,GetMem()返回的内存不应该自动归零,并且编译器不应该自动调用Initialize.那么在这种情况下,使用默认分配初始化记录不是不安全吗?

在大卫接受的答案中,他正在使用一种漂亮的Clear方法记录类型 如何在Delphi中正确释放包含各种类型的记录? 让我们添加一个

  TMyRecord = record
    s1, s2, s3 : String;
    procedure Clear;
  end;
...
procedure TMyRecord.Clear;
begin
  Self := Default(TMyRecord);
end;
Run Code Online (Sandbox Code Playgroud)

现在,Clear例程应该完全无法知道记录是否位于堆栈或堆上,并且是否已在其上调用Initialize.

delphi

1
推荐指数
1
解决办法
492
查看次数

标签 统计

delphi ×3

llvm ×2

android ×1

android-ndk ×1

arm ×1

delphi-xe8 ×1

pointers ×1