用于Win64发行版的Delphi 10.2下的FillChar和StringOfChar [英] FillChar and StringOfChar under Delphi 10.2 for Win64 Release Target

查看:128
本文介绍了用于Win64发行版的Delphi 10.2下的FillChar和StringOfChar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Delphi 10.2 Pascal编程语言中的特定编程问题有疑问。

I have a question about a specific programming problem in Delphi 10.2 Pascal programming language.

在基于发布的CPU的Win64 Release中,StringOfChar和FillChar不能正常工作在2012年之前。

The StringOfChar and FillChar don’t work properly under Win64 Release build on CPUs released before year 2012.


  • FillChar的预期结果只是在给定内存缓冲区中重复8位字符的简单序列。

  • Expected result of FillChar is just plain sequence of just repeating 8-bit characters in a given memory buffer.

StringOfChar的预期结果相同,但是结果存储在字符串类型内。

Expected result of StringOfChar is the same, but the result is stored inside a string type.

但是,实际上,当我用10.2版本的Delphi编译在10.2之前可在Delphi中工作的应用程序时,为Win64编译的我们的应用程序将无法在一年前发布的CPU上正常工作2012。

But, in fact, when I compile our applications that worked in Delphi prior to 10.2 by the 10.2 version of Delp our applications compiled for Win64 stop working properly on CPUs released before year 2012.

StringOfChar和FillChar不能正常工作-尽管返回的是重复模式,但它们返回的是不同字符的字符串-不仅是与

The StringOfChar and FillChar don’t work properly – they return a string of different characters, although in a repeating pattern – not just a sequence of the same character as they should.

这里是足以演示的最小代码 问题。请注意,序列的长度至少应为16个字符,并且该字符不应为nul(#0)。代码如下:

Here is the minimal code enough to demonstrate the issue. Please note that the length of the sequence should be at least 16 characters, and the character should not be nul (#0). The code is below:

procedure TestStringOfChar;
var
  a: AnsiString;
  ac: AnsiChar;
begin
  ac := #1;
  a := StringOfChar(ac, 43);
  if a <> #1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1#1 then
  begin
    raise Exception.Create('ANSI StringOfChar Failed!!');
  end;
end;

我知道StackOverflow上有很多Delphi程序员。您是否遇到相同的问题?如果是,您如何解决?解决办法是什么?顺便说一句,我已经联系了Delphi的开发人员,但是他们没有确认也不否认这个问题,所以远。我正在使用Embarcadero Delphi 10.2版本25.0.26309.314。

I know that there are lots of Delphi programmers at StackOverflow. Are you experiencing the same problem? If yes, how you resolve it? What is the solution? By the way, I have contacted the developers of Delphi but they didn’t confirm nor deny the issue so far. I'm using Embarcadero Delphi 10.2 Version 25.0.26309.314.

更新:

如果您的CPU是2012年或之后生产的,则在调用StringOfChar重现该问题之前,还应包括以下几行:

If your CPU is manufactured in 2012 or later, additionally include the following lines before calling StringOfChar to reproduce the issue:

const
  ERMSBBit    = 1 shl 9; //$0200
begin
  CPUIDTable[7].EBX := CPUIDTable[7].EBX and not ERMSBBit;

关于 2017年4月RAD Studio 10.2工具链问题修补程序-尝试使用它,也可以尝试不使用-它没有帮助。无论修复程序如何,都存在该问题。

As about the April 2017 RAD Studio 10.2 Hotfix for Toolchain Issues - have tried with it and without it - it didn't help. The issue exists regardless of the Hotfix.

更新#2

Embarcadero有在08 / Aug / 17 6:03 PM确认并解决了此问题。因此,在Delphi 10.2 Tokyo Release 1(2017年8月8日发布)中,此错误已修复。

Embarcadero has confirmed and resolved this issue on 08/Aug/17 6:03 PM. So, in Delphi 10.2 Tokyo Release 1 (released on August 8, 2017) this bug is fixed.

推荐答案

StringOfChar(A:AnsiChar,count)在后台使用FillChar。

StringOfChar(A: AnsiChar, count) uses FillChar under the hood.

您可以使用以下代码解决此问题:

You can use the following code to fix the issue:

(*******************************************************
 System.FastSystem
 A fast drop-in addition to speed up function in system.pas
 It should compile and run in XE2 and beyond.
 Alpha version 0.5, fully tested in Win64
 (c) Copyright 2016 J. Bontes
   This Source Code Form is subject to the terms of the
   Mozilla Public License, v. 2.0.
   If a copy of the MPL was not distributed with this file,
   You can obtain one at http://mozilla.org/MPL/2.0/.
********************************************************
FillChar code is an altered version FillCharsse2 SynCommons.pas
which is part of Synopse framework by Arnaud Bouchez
********************************************************
Changelog
0.5 Initial version:
********************************************************)

unit FastSystem;

interface

procedure FillChar(var Dest; Count: NativeInt; Value: ansichar); inline; overload;
procedure FillChar(var Dest; Count: NativeInt; Value: Byte); overload;
procedure FillMemory(Destination: Pointer; Length: NativeUInt; Fill: Byte); inline;
{$EXTERNALSYM FillMemory}
procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;
{$EXTERNALSYM ZeroMemory}

implementation

procedure FillChar(var Dest; Count: NativeInt; Value: ansichar); inline; overload;
begin
  FillChar(Dest, Count, byte(Value));
end;

procedure FillMemory(Destination: Pointer; Length: NativeUInt; Fill: Byte);
begin
  FillChar(Destination^, Length, Fill);
end;

procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;
begin
  FillChar(Destination^, Length, 0);
end;

//This code is 3x faster than System.FillChar on x64.

{$ifdef CPUX64}
procedure FillChar(var Dest; Count: NativeInt; Value: Byte);
//rcx = dest
//rdx=count
//r8b=value
asm
              .noframe
              .align 16
              movzx r8,r8b           //There's no need to optimize for count <= 3
              mov rax,$0101010101010101
              mov r9d,edx
              imul rax,r8            //fill rax with value.
              cmp rdx,59             //Use simple code for small blocks.
              jl  @Below32
@Above32:     mov r11,rcx
              mov r8b,7              //code shrink to help alignment.
              lea r9,[rcx+rdx]       //r9=end of array
              sub rdx,8
              rep mov [rcx],rax
              add rcx,8
              and r11,r8             //and 7 See if dest is aligned
              jz @tail
@NotAligned:  xor rcx,r11            //align dest
              lea rdx,[rdx+r11]
@tail:        test r9,r8             //and 7 is tail aligned?
              jz @alignOK
@tailwrite:   mov [r9-8],rax         //no, we need to do a tail write
              and r9,r8              //and 7
              sub rdx,r9             //dec(count, tailcount)
@alignOK:     mov r10,rdx
              and edx,(32+16+8)      //count the partial iterations of the loop
              mov r8b,64             //code shrink to help alignment.
              mov r9,rdx
              jz @Initloop64
@partialloop: shr r9,1              //every instruction is 4 bytes
              lea r11,[rip + @partial +(4*7)] //start at the end of the loop
              sub r11,r9            //step back as needed
              add rcx,rdx            //add the partial loop count to dest
              cmp r10,r8             //do we need to do more loops?
              jmp r11                //do a partial loop
@Initloop64:  shr r10,6              //any work left?
              jz @done               //no, return
              mov rdx,r10
              shr r10,(19-6)         //use non-temporal move for > 512kb
              jnz @InitFillHuge
@Doloop64:    add rcx,r8
              dec edx
              mov [rcx-64+00H],rax
              mov [rcx-64+08H],rax
              mov [rcx-64+10H],rax
              mov [rcx-64+18H],rax
              mov [rcx-64+20H],rax
              mov [rcx-64+28H],rax
              mov [rcx-64+30H],rax
              mov [rcx-64+38H],rax
              jnz @DoLoop64
@done:        rep ret
              //db $66,$66,$0f,$1f,$44,$00,$00 //nop7
@partial:     mov [rcx-64+08H],rax
              mov [rcx-64+10H],rax
              mov [rcx-64+18H],rax
              mov [rcx-64+20H],rax
              mov [rcx-64+28H],rax
              mov [rcx-64+30H],rax
              mov [rcx-64+38H],rax
              jge @Initloop64        //are we done with all loops?
              rep ret
              db $0F,$1F,$40,$00
@InitFillHuge:
@FillHuge:    add rcx,r8
              dec rdx
              db $48,$0F,$C3,$41,$C0 // movnti  [rcx-64+00H],rax
              db $48,$0F,$C3,$41,$C8 // movnti  [rcx-64+08H],rax
              db $48,$0F,$C3,$41,$D0 // movnti  [rcx-64+10H],rax
              db $48,$0F,$C3,$41,$D8 // movnti  [rcx-64+18H],rax
              db $48,$0F,$C3,$41,$E0 // movnti  [rcx-64+20H],rax
              db $48,$0F,$C3,$41,$E8 // movnti  [rcx-64+28H],rax
              db $48,$0F,$C3,$41,$F0 // movnti  [rcx-64+30H],rax
              db $48,$0F,$C3,$41,$F8 // movnti  [rcx-64+38H],rax
              jnz @FillHuge
@donefillhuge:mfence
              rep ret
              db $0F,$1F,$44,$00,$00  //db $0F,$1F,$40,$00
@Below32:     and  r9d,not(3)
              jz @SizeIs3
@FillTail:    sub   edx,4
              lea   r10,[rip + @SmallFill + (15*4)]
              sub   r10,r9
              jmp   r10
@SmallFill:   rep mov [rcx+56], eax
              rep mov [rcx+52], eax
              rep mov [rcx+48], eax
              rep mov [rcx+44], eax
              rep mov [rcx+40], eax
              rep mov [rcx+36], eax
              rep mov [rcx+32], eax
              rep mov [rcx+28], eax
              rep mov [rcx+24], eax
              rep mov [rcx+20], eax
              rep mov [rcx+16], eax
              rep mov [rcx+12], eax
              rep mov [rcx+08], eax
              rep mov [rcx+04], eax
              mov [rcx],eax
@Fallthough:  mov [rcx+rdx],eax  //unaligned write to fix up tail
              rep ret

@SizeIs3:     shl edx,2           //r9 <= 3  r9*4
              lea r10,[rip + @do3 + (4*3)]
              sub r10,rdx
              jmp r10
@do3:         rep mov [rcx+2],al
@do2:         mov [rcx],ax
              ret
@do1:         mov [rcx],al
              rep ret
@do0:         rep ret
end;
{$endif}

解决问题的最简单方法是下载Mormot 并包含 SynCommon.pas 到您的项目中。这会将System.FillChar修补到上面的代码中,并且还包括其他一些性能改进。

The easiest way to fix your issue is to Download Mormot and include SynCommon.pas into your project. This will patch System.FillChar to the above code and include a couple of other performance improvements as well.

请注意,您不需要所有的Mormot,只需要SynCommons本身。

Note that you don't need all of Mormot, just SynCommons by itself.

这篇关于用于Win64发行版的Delphi 10.2下的FillChar和StringOfChar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆