如何使用对齐
我试图运行以下,
type
Vector = array [1..4] of Single;
{$CODEALIGN 16}
function add4(const a, b: Vector): Vector; register; assembler;
asm
movaps xmm0, [a]
movaps xmm1, [b]
addps xmm0, xmm1
movaps [@result], xmm0
end;
它提供了对movaps的访问冲突,据我所知,如果内存位置是16对齐的,则可以信任这些movaps。 如果movups(不需要对齐),它工作没问题。
所以我的问题是,在Delphi XE3中,{$ CODEALIGN}在这种情况下似乎不起作用。
编辑
很奇怪......我尝试了以下。
program Project3;
{$APPTYPE CONSOLE}
uses
windows; // if not using windows, no errors at all
type
Vector = array [1..4] of Single;
function add4(const a, b: Vector): Vector;
asm
movaps xmm0, [a]
movaps xmm1, [b]
addps xmm0, xmm1
movaps [@result], xmm0
end;
procedure test();
var
v1, v2: vector;
begin
v1[1] := 1;
v2[1] := 1;
v1 := add4(v1,v2); // this works
end;
var
a, b, c: Vector;
begin
{$ifndef cpux64}
{$MESSAGE FATAL 'this example is for x64 target only'}
{$else}
test();
c := add4(a, b); // throw out AV here
{$endif}
end.
如果没有添加“使用窗口”,一切都很好。 如果'使用窗口',那么它将在c:= add4(a,b)处抛出异常,但不在test()中。
谁能解释这一点?
现在编辑它对我来说是有意义的。 德尔福XE3-64位的结论是
你需要你的数据是16字节对齐的。 这需要一些照顾和关注。 您可以确保堆分配器对齐到16个字节。 但是你不能确保编译器将16字节对齐你的堆栈分配的变量,因为你的数组的对齐属性是4,它的元素的大小。 并且在其他结构中声明的任何变量也将有4个字节对齐。 这是一个艰难的障碍要清除。
我不认为你可以在当前可用版本的编译器中解决你的问题。 至少不要,除非你放弃堆栈分配的变量,我猜那是一个可以吞咽的药片。 外部汇编程序可能会带来一些好运。
您可以编写自己的内存分配例程,以在堆中分配对齐的数据。 您可以指定自己的对齐大小(不仅是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下,使用微软调用约定,第一个参数总是作为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;
顺便说一下,根据Microsoft的说法,64位调用约定中的浮点参数直接传递到XMM寄存器:XMM0中的第一个,XMM1中的第二个,XMM2中的第三个,以及XMM3中的第四个,并且依次堆栈。 所以你可以通过价值传递它们,而不是通过引用。
使用它可以使内置内存管理器以16字节对齐方式分配:
SetMinimumBlockAlignment(mba16Byte);
此外,据我所知,“注册”和“汇编器”都是冗余指令,所以你可以跳过你的代码。
-
编辑:你提到这是为x64。 我只是在为X64编译的Delphi XE2中尝试了以下内容,并且在此处可用。
program Project3;
type
Vector = array [1..4] of Single;
function add4(const a, b: Vector): Vector;
asm
movaps xmm0, [a]
movaps xmm1, [b]
addps xmm0, xmm1
movaps [@result], xmm0
end;
procedure f();
var
v1,v2 : vector;
begin
v1[1] := 1;
v2[1] := 1;
v1 := add4(v1,v2);
end;
begin
{$ifndef cpux64}
{$MESSAGE FATAL 'this example is for x64 target only'}
{$else}
f();
{$endif}
end.
链接地址: http://www.djcxy.com/p/12613.html
上一篇: How to use align
下一篇: Create a table with column names derived from row values of another table