将ASM与非asm代码(或需要SwapInt64 ASM功能)相结合

时间:2015-10-18 11:39:14

标签: delphi assembly pascal inline-assembly

我需要处理来自旧Mac时代的文件(旧摩托罗拉CPU)。字节是大端,所以我有一个函数交换和Int64到英特尔小端。该函数是ASM,适用于32位CPU但不适用于64位。对于64位,我有一个不同于ASM的不同功能。我想用IFDEF组合这些功能。我可以这样做吗?这会有问题吗?

interface 

function SwapInt64(Value: Int64): Int64; assembler;  

implementation

{$IFDEF CPUx86}        
function SwapInt64(Value: Int64): Int64; assembler;        { Does not work on 64 bit }                                                                      { 
asm
 MOV     EDX,[DWORD PTR EBP + 12]
 MOV     EAX,[DWORD PTR EBP + 8]
 BSWAP   EAX
 XCHG    EAX,EDX
 BSWAP   EAX
end;

{$else}

 function SwapInt64 (Value: Int64): Int64;
 var P: PInteger;
 begin
  Result: = (Value shl 32) or (Value shr 32);
  P: = @Result;
  P ^: = (Swap (P ^) shl 16) or (Swap (P ^ shr 16));
  Inc (P);
  P ^: = (Swap (P ^) shl 16) or (Swap (P ^ shr 16));
 end;
{$ENDIF}

我认为编译器将正确编译/调用适当的函数,无论是ASM还是Pascal。

3 个答案:

答案 0 :(得分:6)

你提出的建议完全没问题。这是一种非常合理的方法。

如果你想在asm中进行64位交换,对于x64来说,这非常简单:

function SwapInt64(Value: Int64): Int64;
asm
  MOV    RAX,RCX
  BSWAP  RAX
end;

使用条件将其与32位版本相结合,正如您在问题中所做的那样。

function SwapInt64(Value: Int64): Int64;
{$IF Defined(CPUX86)}
asm
 MOV     EDX,[DWORD PTR EBP + 12]
 MOV     EAX,[DWORD PTR EBP + 8]
 BSWAP   EAX
 XCHG    EAX,EDX
 BSWAP   EAX
end;
{$ELSEIF Defined(CPUX64)}
asm
  MOV    RAX,RCX
  BSWAP  RAX
end;
{$ELSE}
  {$Message Fatal 'Unsupported architecture'}
{$ENDIF}

或者在{$ELSE}块中包含Pascal实现。

答案 1 :(得分:5)

如果性能与您所追求的一样,将字节交换为无法内联的单独例程的方法有点愚蠢。

更好的方法来假设你有一个数据块,并且其中的所有双字/ qword都需要更改其字节序。

这看起来像这样。

对于dwords

function SwapDWords(var Data; size: cardinal): boolean;
{ifdef CPUX64}
asm
  //Data in RCX, Size in EDX
  xor EAX,EAX //failure
  test EDX,3 
  jz @MultipleOf4
@error:
  ret
@MultipleOf4
  neg EDX    //Count up instead of down
  jz  @done
  ADD RCX,RDX      
@loop
  mov R8d, [RCX+RDX]
  bswap R8d
  mov [RCX+RDX],R8d  
  add RDX,4   //add is faster than inc on modern processors
  jnz @loop
@done:
  inc EAX  //success
  ret
end;

对于qwords

function SwapQWords(var Data; size: cardinal): boolean;
{ifdef CPUX64}
asm
  //Data in RCX, Size in EDX
  xor EAX,EAX //failure
  test EDX,7 
  jz @MultipleOf8
@error:
  ret
@MultipleOf8
  neg EDX    //Count up instead of down
  jz  @done
  ADD RCX,RDX      
@loop
  mov R8, [RCX+RDX]
  bswap R8
  mov [RCX+RDX],R8
  add RDX,8   //add is faster than inc on modern processors
  jnz @loop
@done:
  inc EAX  //success
  ret
end;

如果你已经在64位,那么你有SSE2,并且可以使用128位SSE寄存器。
现在,您可以一次处理4个双字,有效地展开循环4次。 请参阅:http://www.asmcommunity.net/forums/topic/?id=29743

   movntpd xmm5,[RCX+RDX]  //non-temporal move to avoid polluting the cache
   movdqu xmm0, xmm5
   movdqu xmm1, xmm5
   pxor    xmm5, xmm5
   punpckhbw xmm0, xmm5 ; interleave '0' with bytes of original
   punpcklbw xmm1, xmm5 ;  so they become words
   pshuflw xmm0, xmm0, 27 ; swap the words by shuffling
   pshufhw xmm0, xmm0, 27 ;//27 = B00_01_10_11
   pshuflw xmm1, xmm1, 27
   pshufhw xmm1, xmm1, 27
   packuswb xmm1, xmm0 ; make the words back into bytes.
   movntpd [RCX+RDX], xmm1  //non-temporal move to keep the cache clean.

答案 2 :(得分:1)

只需使用LEToN()或BEtoN()

即可

如果数据是小端(如32或64位x86 mac,现代手臂),则使用LE变量,如果源数据(例如来自磁盘的文件)采用大端格式,则使用BE。

根据所使用的体系结构,内联交换或“无”将被内联,通常是单个转换的最佳选择。对于面向块的解决方案,请参阅发布的SSE代码(或Agner Fog's)