您可以编写自己的内存分配例程,在堆中分配对齐的数据。您可以指定自己的对齐大小(不仅是 16 字节,还有 32 字节、64 字节等等......):
procedure GetMemAligned(const bits: Integer; const src: Pointer;
const SrcSize: Integer; out DstAligned, DstUnaligned: Pointer;
out DstSize: Integer);
var
Bytes: NativeInt;
i: NativeInt;
begin
if src <> nil then
begin
i := NativeInt(src);
i := i shr bits;
i := i shl bits;
if i = NativeInt(src) then
begin
// the source is already aligned, nothing to do
DstAligned := src;
DstUnaligned := src;
DstSize := SrcSize;
Exit;
end;
end;
Bytes := 1 shl bits;
DstSize := SrcSize + Bytes;
GetMem(DstUnaligned, DstSize);
FillChar(DstUnaligned^, DstSize, 0);
i := NativeInt(DstUnaligned) + Bytes;
i := i shr bits;
i := i shl bits;
DstAligned := Pointer(i);
if src <> nil then
Move(src^, DstAligned^, SrcSize);
end;
procedure FreeMemAligned(const src: Pointer; var DstUnaligned: Pointer;
var DstSize: Integer);
begin
if src <> DstUnaligned then
begin
if DstUnaligned <> nil then
FreeMem(DstUnaligned, DstSize);
end;
DstUnaligned := nil;
DstSize := 0;
end;
然后使用指针和过程作为第三个参数来返回结果。
你也可以使用函数,但不是很明显。
type
PVector^ = TVector;
TVector = packed array [1..4] of Single;
然后以这种方式分配这些对象:
const
SizeAligned = SizeOf(TVector);
var
DataUnaligned, DataAligned: Pointer;
SizeUnaligned: Integer;
V1: PVector;
begin
GetMemAligned(4 {align by 4 bits, i.e. by 16 bytes}, nil, SizeAligned, DataAligned, DataUnaligned, SizeUnaligned);
V1 := DataAligned;
// now you can work with your vector via V1^ - it is aligned by 16 bytes and stays in the heap
FreeMemAligned(nil, DataUnaligned, SizeUnaligned);
end;
正如您所指出的,我们已经传递nil
给 GetMemAligned 和 FreeMemAligned - 当我们想要对齐现有数据时需要此参数,例如,我们作为函数参数接收的数据。
只需在汇编例程中使用直接寄存器名称而不是参数名称。使用寄存器调用约定时,您不会搞砸任何事情 - 否则您可能会在不知道使用的参数名称只是寄存器的别名的情况下修改寄存器。
在 Win64 下,使用 Microsoft 调用约定,第一个参数始终作为 RCX 传递,第二个 - RDX,第三个 R8,第四个 - R9,其余在堆栈中。函数在 RAX 中返回结果。但是如果一个函数返回一个结构(“记录”)结果,它不会在 RAX 中返回,而是在一个隐式参数中,按地址返回。调用后,您的函数可能会修改以下寄存器:RAX,RCX,RDX,R8,R9,R10,R11。其余的应保留。有关更多详细信息,请参阅https://msdn.microsoft.com/en-us/library/ms235286.aspx。
在 Win32 下,使用 Delphi 寄存器调用约定,调用在 EAX 中传递第一个参数,在 EDX 中传递第二个参数,在 ECX 中传递第三个参数,然后在堆栈中传递
下表总结了这些差异:
64 32
--- ---
1) rcx eax
2) rdx edx
3) r8 ecx
4) r9 stack
因此,您的函数将如下所示(32 位):
procedure add4(const a, b: TVector; out Result: TVector); register; assembler;
asm
movaps xmm0, [eax]
movaps xmm1, [edx]
addps xmm0, xmm1
movaps [ecx], xmm0
end;
64位以下;
procedure add4(const a, b: TVector; out Result: TVector); register; assembler;
asm
movaps xmm0, [rcx]
movaps xmm1, [rdx]
addps xmm0, xmm1
movaps [r8], xmm0
end;
顺便说一句,根据微软的说法,64 位调用约定中的浮点参数直接在 XMM 寄存器中传递:第一个在 XMM0 中,第二个在 XMM1 中,第三个在 XMM2 中,第四个在 XMM3 中,然后在堆栈中。所以你可以通过值传递它们,而不是通过引用。