将 ASM 与非 asm 代码结合(或需要 SwapInt64 ASM 函数)
Combining ASM with non-asm code (or SwapInt64 ASM function needed)
我需要处理来自旧 Mac 时代(旧摩托罗拉 CPU)的文件。字节是大端字节序,所以我有一个函数可以将 Int64 和 Intel 小端字节序交换。该函数是 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,编译器都会正确地 compile/call 适当的函数。
你的提议非常好。这是一个相当合理的做法。
如果你想在 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 实现。
如果您追求的是性能,那么在无法内联的单独例程中交换字节的方法有点愚蠢。
假设您有一个数据块并且其中的所有 dword/qwords 都需要更改字节顺序的更好方法。
这看起来像这样。
对于双字
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.
如果数据是小端(例如 32 或 64 位 x86 mac,现代 arm),则使用 LE 变体,如果源数据(例如,磁盘文件)是大端,则使用 BE格式。
根据使用的架构,交换或 "nothing" 将被内联,对于单次转换通常是相当理想的。对于面向块的解决方案,请参阅发布的 SSE 代码(或 Agner Fog 的代码)
我需要处理来自旧 Mac 时代(旧摩托罗拉 CPU)的文件。字节是大端字节序,所以我有一个函数可以将 Int64 和 Intel 小端字节序交换。该函数是 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,编译器都会正确地 compile/call 适当的函数。
你的提议非常好。这是一个相当合理的做法。
如果你想在 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 实现。
如果您追求的是性能,那么在无法内联的单独例程中交换字节的方法有点愚蠢。
假设您有一个数据块并且其中的所有 dword/qwords 都需要更改字节顺序的更好方法。
这看起来像这样。
对于双字
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.
如果数据是小端(例如 32 或 64 位 x86 mac,现代 arm),则使用 LE 变体,如果源数据(例如,磁盘文件)是大端,则使用 BE格式。
根据使用的架构,交换或 "nothing" 将被内联,对于单次转换通常是相当理想的。对于面向块的解决方案,请参阅发布的 SSE 代码(或 Agner Fog 的代码)