2013-04-04 17 views
7

Ben aşağıdaki çalıştırmak çalışıyordum,Delphi XE3'te align-data-move SSE nasıl kullanılır?

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; 

Hafıza yeri 16-align olduğu gibi bildiğim kadarıyla, movaps güvenilebilir, movaps üzerinde erişim ihlali verir. movup (hizalama gerekmez) olursa sorun olmaz.

Bu yüzden sorum, Delphi XE3'te, {$ CODEALIGN} bu durumda çalışmıyor gibi görünüyor.

DÜZENLEME

Çok garip ... Ben şu çalıştı.

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. 

Eğer 'windows kullan' eklenmezse, her şey yolunda. Eğer 'window' (pencere kullan) 'ise, c: = add4 (a, b) istisnası atıyor ancak testinde().

Bunu kim açıklayabilir?

DÜZENLEME hepsi şimdi bana mantıklı. Delphi XE3 için sonuçlar - 64 bit X64 de

  1. yığın çerçeveler (gereken şekilde) 16-bayt, {$ CODEALIGN 16} 16 bayta proc/eğlence için kod hizalar ayarlanır vardır.
  2. dinamik dizi v1 önce tam sayı Var bildirmek durumunda
  3. Ancak yığın değişkenler , örneğin, her zaman 16-bayt hizalı değildir SetMinimumBlockAlignment (mba16byte) kullanılarak 16 hizalamak için ayarlanabilir yığın yaşamaktadır Yukarıdaki örnekte v2, örn. test(), örnek çalışmayacak.
+1

'CODEALIGN', _code_ öğelerini hizalar. Eğer _data_ 'ı hizalamak isterseniz,' ALIGN' direktifini kullanabilirsiniz. – Michael

+1

Aynı zamanda {ALIGN 16} denedim ve işe yaramıyor. –

cevap

2

Verilerinizi 16 bayt olarak hizalamanız gerekir. Bu biraz dikkat ve dikkat gerektirir. Yığın ayırıcısının 16 bayta hizalandığından emin olabilirsiniz. Ancak, derleyicinizin yığınınızdaki değişkenleri hizaladığından emin olamazsınız, çünkü diziniz öğelerin büyüklüğü olan 4'lük bir hizalama özelliğine sahiptir. Ve diğer yapılarda beyan edilen değişkenlerin de 4 bayt hizalaması olacaktır. Temizlemek için zor bir engel.

Sorununuzu derleyicinin geçerli sürümlerinde çözebileceğinizi sanmıyorum. En azından yutmak için çok acı bir ilaç olduğunu tahmin ettiğim tahsis edilen değişkenlerden vazgeçmediğin sürece.Harici bir assembler ile şansınız olabilir. U

+0

Tamam ... C++ 'da bunu yapmak daha kolay mı? –

+1

MS derleyici, 64 bit satır içi asm'ye izin vermiyor. Gcc'nin yapmasını beklerim. Ve eminim ki gcc size yığın değişkenlerini hizalama yeteneğini verecektir. –

+0

Çok garip davranış, lütfen yeni düzenlemelerime bakın. –

1

bunu kullanın 16 baytlık hizalama ile tahsis bellek yöneticisi dahili yapmak:

SetMinimumBlockAlignment(mba16Byte); 

Ayrıca olarak bildiğim kadarıyla, hem "kayıt" ve "montajcı" dir gereksiz direktifleri yüzden Bunları kodunuzdan atlayabilirsiniz.

-

Düzenleme: Bu x64 içindir söz. Sadece x64 için derlenmiş Delphi XE2'de aşağıdakileri denedim ve burada çalışıyor.

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. 
+0

Çözümünüzü denedim ve işe yaramıyor. Gördüğüm kadarıyla, SetMinimumBlockAlignment uygulamasının 64-bit altında bir etkisi yoktur, bir yorumu vardır {16 baytlık hizalama 64-bit altında gereklidir.}. –

+0

@DoctorLai Düzenleme bölümüne bakın. Senin için çalışmıyor mu? –

+0

hayır, bilgisayarımda çalışmıyor, Erişim İhlali istisnası atar. –

0

Hizalanmış verileri yığına ayıran kendi bellek ayırma rutinlerinizi yazabilirsiniz.

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; 

Sonra sonuca dönmek için üçüncü bir argüman olarak işaretçileri ve prosedürleri kullanın: Kendi hizalama boyutu (sadece 16 byte değil, aynı zamanda 32 bayt, böylece 64 byte ve ...) belirleyebilirsiniz.

Ayrıca işlevleri de kullanabilirsiniz, ancak bu belli değildir. Bu parametre gereklidir biz mevcut verileri, örneğin hizalamak istiyorum -

işaret ettiğiniz gibi, GetMemAligned ve FreeMemAligned için nil geçti
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; 

:

type 
    PVector^ = TVector; 
    TVector = packed array [1..4] of Single; 

Sonra bu şekilde bu nesneleri tahsis Örneğin, bir işlev argümanı olarak aldığımız

Yalnızca montaj yordamlarındaki parametre adları yerine düz kayıt adlarını kullanın. Kayıt arama çağrısını kullanırken bununla hiçbir şey karıştırmayacaksınız - aksi halde, kullanılan parametre adlarının kayıtlar için yalnızca takma adlar olduğunu bilmeden yazmaçları değiştirme riski vardır.

Win64 altında, Microsoft çağrı kuralı ile, ilk parametre her zaman RCX, ikinci - RDX, üçüncü R8, dördüncü - R9, geri kalanı yığın halinde geçirilir. Bir işlev sonucu RAX içinde döndürür. Ancak bir işlev bir yapı ("kayıt") sonucu döndürürse, RAX içinde değil, örtülü bir argüman olarak, adrese döndürülür. Aşağıdaki kayıtlar, çağrının ardından aşağıdakiler tarafından değiştirilebilir: RAX, RCX, RDX, R8, R9, R10, R11. Gerisi korunmalıdır. Daha fazla ayrıntı için https://msdn.microsoft.com/en-us/library/ms235286.aspx'a bakın.

  64  32 
     --- --- 
    1) rcx eax 
    2) rdx edx 
    3) r8 ecx 
    4) r9 stack 
: Delphi yazmacı çağırma ile Win32 altında

, bir çağrı yığını birinci, ikinci EDX içinde eax'a parametre, ECX üçüncü ve geri kalanı geçer

Aşağıdaki tablo farklar özetlenmiştir

Yani, fonksiyon bu (32-bit) gibi görünecektir:

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; 

altında 64 bit; Microsoft'a göre arada

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 bit çağrı kuralı içinde kayan nokta argümanlar XMM kayıtları doğrudan geçirilir: İlk XMM0 içinde, ikinci XMM1, XMM3 içinde XMM2 üçüncü ve dördüncü, ve yığın halinde dinlenin. Böylece referansla değil, değerlere göre iletebilirsiniz.