Autor Téma: Prekladac mproblemy s prekladem inferovaneho typu polozky generickeho open array  (Přečteno 1634 krát)

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Nekdy v listopadu jsem se ptal, zda nekdo nema nejaky napad, jak implementovat serializer/deserializer viz zde.

Tenkrat jsem to implementoval, napsal k tomu unit testy a vse fungovalo. Ted jsem se k projektu vratil, ze budu pokracovat a zacal jsem spusteni tech unit testu, z nichz mi nektere neprosly.

Kdyz jsem patral, co se deje, tak jsem zjistil, ze Delphi 10.2 Tokyo 32bit spatne preklada predani hodnoty polozky z generickeho open array, konkretne u typu int64/uint64. Pro kod
Kód: Delphi [Vybrat]
  1. for idx := 0 to Length(AArray) - 1 do
  2.   PutInt64(AStream, PInt64(@AArray[idx])^);
prelozi chybne predani polozky pole jako (eax adresa 1. elementu pole, edx je index polozy idx):
Kód: ASM [Vybrat]
  1. push dword ptr [eax+edx*2+$04]
  2. push dword ptr [eax+edx*2]

pricemz spravne ma byt
Kód: ASM [Vybrat]
  1. push dword ptr [eax+edx*8+$04]
  2. push dword ptr [eax+edx*8]

Tak jsem sel vyrobit SSCCE v podobe console application, a tam je preklad spravne, stejne jako v inverzni funkci GetArray<T> v aplikaci. U ostatnich typu se zda, ze Delphi typ inferuje spravne i to spravne prelozi tj. bere edx*sizeof(T)

Ale co je nejhorsi, ze kdyz jsem se v tom zacal hrabat, tak to Delphi najednou zacal prekladat spatne jeste jinak jako  :o
Kód: ASM [Vybrat]
  1. push dword ptr [eax+edx+$04]
  2. push dword ptr [eax+edx]

Co je jeste divnejsi, kdyz zmenim implementaci toho cyklu s uzitim ukazatele na pole int64, tak to v aplikaci prelozi stejne blbe se (sizeof<T>=1), zatimco v SSCCE je to prelozene spravne.
Kód: Delphi [Vybrat]
  1. p64a := @AArray[0];
  2. for idx := 0 to Length(AArray) - 1 do
  3.   PutInt64(AStream, p64a^[idx]);

Nakonec jsem se uchylil k "rucnimu" prekladu tech cyklu:
Kód: Delphi [Vybrat]
  1. p64 := @AArray[0];
  2. for idx := 0 to Length(AArray) - 1 do
  3. begin
  4.   PutInt64(AStream, p64^);
  5.   inc(p64, 1);
  6. end;

Ale nemam z toho dobry pocit, protoze ta puvodni implementace fungovala v listopadu taky dobre.

--

Nevidi nekdo, jestli nepracuju s nejakymi mylnymi predpoklady? Nebo co muze byt pricinou toho, ze Delphi preklad je ovlivnen vetrem na Marsu.
Poslal bych dotaz na SO, kde je prece jenom vetsi auditorium, ale tam by urcite chteli SSCCE a ten se mi nedari vytvorit resp. v opravdu minimalni verzi se chyba nezopakuje.

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2407
  • Karma: 103
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Takovy hloupy dotaz, neni to ve funkci, ktera má více parametrů (> 3).
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Takovy hloupy dotaz, neni to ve funkci, ktera má více parametrů (> 3).
nevim, jestli rozumim dotazu, ale signatura metody, ktera dostane to genericke open array vypada:
Kód: Delphi [Vybrat]
  1. class procedure PutSimpleArray<T>(AStream: TStream; const AArray: array of T);
  2. class function GetSimpleArray<T>(AStream: TStream): TArray<T>;

a pro kazdy typ na zaklade introspekce se volaji metody se dvema parametry, napr.
Kód: Delphi [Vybrat]
  1. class procedure PutUInt64(AStream: TStream; AValue: qword);
  2. class function GetUInt64(AStream: TStream): qword;
  3.  

s tim, ze jak jsem psal, u Get se to neprojevuje a preklad vypada OK.


Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2407
  • Karma: 103
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
A nemuzes vytvořit konzolovou aplikaci s tou tvoji jednotkou serializace misto nového SSCCE?
Nebo tam se to taky neprojevuje?
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
A nemuzes vytvořit konzolovou aplikaci s tou tvoji jednotkou serializace misto nového SSCCE?
No pro sebe ji vytvorit muzu, protoze to zacne chtit dalsi basalni jednotky z frameworku. Ale pak z toho udelat SSCCE, ktery by se dal publikovat by byla asi dost alchymie...

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
A nemuzes vytvořit konzolovou aplikaci s tou tvoji jednotkou serializace misto nového SSCCE?
Tak jsem presunul vse podstatne ze serializeru do consolove aplikace a chybu se povedlo zopakovat, shodou okolnosti taky jednou prelozeno jako eax+edx+..., jindy jako eax+edx*2+.... A dokonce to jednou prelozilo i spravne. Zkratka vitr na Marsu. Ze to nefunguje poznas tak, ze si nechas zobrazit obsah pole arrayOfQWords,h (hexadecimalne) a/nebo ze vtrasujes dovnitr PutSimpleArray a na volani PutUInt64 si zobrazis CPU okno. Spravne prelozene to ma byt:
Kód: Delphi [Vybrat]
  1. push dword ptr [eax+edx*8+$04]
  2. push dword ptr [eax+edx*8]

Consolovku jsem chtel pripojit, ale nedovoli to .dproj  ani zpravu delsi jak 20000 znaku, kdyz jsem to chtel dat do code >:(

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 4584
  • Karma: 40
    • Verze Delphi: XE7 professional
Skús ju dať na úložisko. Možno si ju stiahnu. Aj keď tomu veľmi neverím.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Skús ju dať na úložisko. Možno si ju stiahnu. Aj keď tomu veľmi neverím.
Pribalil jsem ji jako .zip


Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Pribalil jsem ji jako .zip

Jenže tam chybí .dpr ;-)

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2407
  • Karma: 103
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Pribalil jsem ji jako .zip

Jenže tam chybí .dpr ;-)

Resp. je tam jen dproj
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Pribalil jsem ji jako .zip
Jenže tam chybí .dpr ;-)
Sorry, tak snad ted.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Porad jsem premital, jak je mozne, ze Delphi vypocet adresy polozky pole preklada natvrdo tj. eax+edx*8+... a zjistil jsem, ze zrejme z duvodu rychlosti vytvari pro kazde T vlastni preklad. Kdyz napisu dummy metodu:
Kód: Delphi [Vybrat]
  1. class procedure TAny.DoSometing<T>(const AArray: array of T);
  2. var
  3.   Idx: integer;
  4.   v: T;
  5. begin
  6.   for Idx := 0 to length(AArray)-1 do
  7.     v := AArray[idx];
  8. end;
  9.  
a pak ji zavolam napr.
Kód: Delphi [Vybrat]
  1. TAny.DoSometing<int64>(arrayOfInt64s);
  2. TAny.DoSometing<int16>(arrayOfInt16s);
  3.  
a vtrasuju dovnitr, tak v prvnim pripade se dostanu na preklad:
Kód: ASM [Vybrat]
  1. SSCCE_ArrayOfInt64.dpr.26: v := AArray[idx];
  2. 004D102F 8B45F8           mov eax,[ebp-$08]
  3. 004D1032 8B55F0           mov edx,[ebp-$10]
  4. 004D1035 8B0CD0           mov ecx,[eax+edx*8]
  5. 004D1038 894DE8           mov [ebp-$18],ecx
  6. 004D103B 8B4CD004         mov ecx,[eax+edx*8+$04]
  7. 004D103F 894DEC           mov [ebp-$14],ecx
  8. 004D1042 FF45F0           inc dword ptr [ebp-$10]
resp. v pripade int16 na
Kód: ASM [Vybrat]
  1. SSCCE_ArrayOfInt64.dpr.26: v := AArray[idx];
  2. 004D1073 8B45F8           mov eax,[ebp-$08]
  3. 004D1076 8B55F0           mov edx,[ebp-$10]
  4. 004D1079 668B0450         mov ax,[eax+edx*2]
  5. 004D107D 668945EE         mov [ebp-$12],ax
  6. 004D1081 FF45F0           inc dword ptr [ebp-$10]

viz rozdilne hodnoty adres kodu v pameti. Preklady jsou spravne a z toho mi vychazi, ze by to mela byt v Delphi legalni konstrukce, takze nechci po Delphi nemozne, akorat v pripade te metody s case se jim preklad nekdy nejak nepovede...
« Poslední změna: 15-01-2019, 13:41:36 od pf1957 »

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Na zaklade poznatku, ze Delphi preklada samostatne kazde T jeste musim zkontrolovat, jestli preklad neni v poradku, ale spatne to urci, jakou metodu to ma zavolat.
Tak ne, vola to spravnou alternativu prekladu, ale ta pro int64 je chybna
« Poslední změna: 15-01-2019, 13:29:06 od pf1957 »

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Tak v Delphi 10.3 to je taky špatně:
Kód: Delphi [Vybrat]
  1. 004D6992 FF741004         push dword ptr [eax+edx+$04]
  2. 004D6996 FF3410           push dword ptr [eax+edx]

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Na zaklade poznatku, ze Delphi preklada samostatne kazde T jeste musim zkontrolovat, jestli preklad neni v poradku, ale spatne to urci, jakou metodu to ma zavolat.

sizeof(AArray[0]) tam vrací 8.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Výsledky jsou fakt vtipné.

Doplnil jsem:
Kód: Delphi [Vybrat]
  1. procedure Test_qword(a:qword);
  2.  begin
  3.  if a=-1 then halt;
  4. end;
  5.  
  6. procedure Test_int64(a:int64);
  7.  begin
  8.  if a=-1 then halt;
  9. end;
  10.  

a volání:

Kód: Delphi [Vybrat]
  1.         Test_qword(PInt64(@AArray[idx])^);
  2.         Test_int64(PInt64(@AArray[idx])^);
  3.  
Volání Test_qword je občas dobře, občas špatně (*1, *2...).
Volání Test_int64 je zatím vždy dobře (*8 ).


Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Tak v Delphi 10.3 to je taky špatně:
Kód: Delphi [Vybrat]
  1. 004D6992 FF741004         push dword ptr [eax+edx+$04]
  2. 004D6996 FF3410           push dword ptr [eax+edx]
Diky za vyzkouseni

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Volání Test_qword je občas dobře, občas špatně (*1, *2...).
Volání Test_int64 je zatím vždy dobře (*8 ).
Kdyz uz to mas upravene, mohl bys pls jeste vyzkouset misto qword uint64? Ono by to tedy melo byt totez.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Kdyz uz to mas upravene, mohl bys pls jeste vyzkouset misto qword uint64? Ono by to tedy melo byt totez.

Když v deklaraci procedury použiju UInt64 a volám to
Kód: Delphi [Vybrat]
  1. Test_quint(PInt64(@AArray[idx])^)
tak to nefunguje.

Pokud ale volám takto:
Kód: Delphi [Vybrat]
  1. Test_quint(PUInt64(@AArray[idx])^)
tak funguje (aspoň těch pár kompilací, co jsem zkusil).

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Ja jsem tedy rozlisil int64/uint64 a to se zda, ze na tech par prekladech taky funguje:
Kód: Delphi [Vybrat]
  1.     tkInt64:
  2.       begin
  3.         if ptd.MinInt64Value > ptd.MaxInt64Value then
  4.           for idx := 0 to Length(AArray) - 1 do
  5.             PutUInt64(AStream, PUInt64(@AArray[idx])^)
  6.         else
  7.           for idx := 0 to Length(AArray) - 1 do
  8.             PutInt64(AStream, PInt64(@AArray[idx])^);
  9.       end;
  10.  

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
ale stejne si to netroufnu takhle nechat, protoze v tom listopadu to opakovane fungovalo nekolik dni, co jsem delal na unit testech a zdalo se to OK. Radsi udelam pro kazdy typ ten "rucni" preklad a udelam vypocet adresy elementu primo nezavisty na T:
Kód: Delphi [Vybrat]
  1.         p64 := @AArray[0];
  2.         for idx := 0 to Length(AArray) - 1 do
  3.         begin
  4.           PutUInt64(AStream, p64^);
  5.           inc(p64, 1);
  6.         end;
  7.  

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Uděláš na to prosím report a pošleš sem link, abychom tam přidali hlas? Tohle mi připadá jako dost zásadní chyba, tak se třeba uráčí ji opravit rychle...

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Uděláš na to prosím report a pošleš sem link, abychom tam přidali hlas? Tohle mi připadá jako dost zásadní chyba, tak se třeba uráčí ji opravit rychle...
Kdyz uz jsem s tim stravil tolik casu, tak asi jo, jen vidim problem v tom, ze se mi stale nevede vytvorit SSCCE na par radku, kde bych to zopakoval.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Kdyz uz jsem s tim stravil tolik casu, tak asi jo, jen vidim problem v tom, ze se mi stale nevede vytvorit SSCCE na par radku, kde bych to zopakoval.

Podívám se, jestli mi to půjde nějak vypreparovat.

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
Kód: Delphi [Vybrat]
  1. program SSCCE_Payload;
  2.  
  3. {$APPTYPE CONSOLE}
  4. {$R *.res}
  5.  
  6. uses
  7.   System.Classes, System.Types, System.TypInfo, System.Rtti,
  8.   System.SysUtils, System.AnsiStrings, Generics.Collections,
  9.   WinApi.Windows;
  10.  
  11. type
  12.   qword = UInt64;
  13.  
  14.   TPayloadBuilder = class
  15.   public
  16.     class var Endian: TEndian;
  17.   public
  18.     // Simple types
  19.     class procedure PutUInt64(AStream: TStream; const AValue: qword);
  20.     // Collections
  21.     class procedure PutSimpleArray<T>(AStream: TStream;
  22.       const AArray: array of T);
  23.  
  24.   end;
  25.  
  26. { TPayloadBuilder }
  27.  
  28. procedure Test_quint(a:UInt64);
  29.  begin
  30. end;
  31.  
  32. procedure Test_int64(a:int64);
  33.  begin
  34. end;
  35.  
  36. class procedure TPayloadBuilder.PutSimpleArray<T>(AStream: TStream;
  37.   const AArray: array of T);
  38. var
  39.   idx: NativeInt;
  40.   x:pointer;
  41.   d:qword;
  42.  begin
  43.  for idx := 0 to Length(AArray) - 1 do begin
  44.    PutUInt64(AStream, PInt64(@AArray[idx])^); // error:   push dword ptr [eax+edx*2+$04]; push dword ptr [eax+edx*2]
  45.    Test_quint(PInt64(@AArray[idx])^); // error:   push dword ptr [eax+edx+$04]; push dword ptr [eax+edx]
  46.    Test_quint(PUInt64(@AArray[2])^);
  47.    Test_int64(PInt64(@AArray[idx])^);
  48.    d:=PInt64(@AArray[idx])^;
  49.    x:=@AArray[idx];
  50.    PutUInt64(AStream, PInt64(x)^);
  51.  end;
  52. end;
  53.  
  54. class procedure TPayloadBuilder.PutUInt64(AStream: TStream; const AValue: qword);
  55. begin
  56. end;
  57.  
  58. var
  59.   arrayOfQWords: TArray<qword>;
  60.   stream: TMemoryStream;
  61.  
  62. begin
  63.   try
  64.     arrayOfQWords := [$100000000, $100000001, $100000002, $100000003,
  65.       $100000004, $100000005, $100000006, $100000007, $100000008, $100000009];
  66.  
  67.     stream := TMemoryStream.Create;
  68.     try
  69.       TPayloadBuilder.PutSimpleArray<qword>(stream, arrayOfQWords);
  70.     finally
  71.       stream.Free;
  72.     end;
  73.  
  74.   except
  75.     on E: Exception do
  76.       Writeln(E.ClassName, ': ', E.Message);
  77.   end;
  78.  
  79. end.

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2407
  • Karma: 103
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Ja jsem tedy rozlisil int64/uint64 a to se zda, ze na tech par prekladech taky funguje:
Kód: Delphi [Vybrat]
  1.     tkInt64:
  2.       begin
  3.         if ptd.MinInt64Value > ptd.MaxInt64Value then
  4.           for idx := 0 to Length(AArray) - 1 do
  5.             PutUInt64(AStream, PUInt64(@AArray[idx])^)
  6.         else
  7.           for idx := 0 to Length(AArray) - 1 do
  8.             PutInt64(AStream, PInt64(@AArray[idx])^);
  9.       end;
  10.  

Co jsem zkousel tak to fungovalo pokud
qword = UInt64; a  PutUInt64(AStream, PUInt64(@AArray[idx])^);
a
qword = Int64; a  PutUInt64(AStream, PInt64(@AArray[idx])^);

jakmile jsem to zacal mixoval (jako unsigned 64bit se signed), tak to zacal byt problem, jako by dochazelo k nejake konverzi. Ale je to podivne
Embarcadero MVP - Czech republic

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
sem to zacal mixoval (jako unsigned 64bit se signed), tak to zacal byt problem, jako by dochazelo k nejake konverzi. Ale je to podivne
Divne to je, protoze ten chybny preklad se tyka jen vypoctu adresy elementu pole tj.
Kód: Delphi [Vybrat]
  1. AArray[idx]
a ten by mel byt intaktni s ohledem na dalsi (vnejsi) typecast. A nejvic divne mi prijde, ze to preklada pokazde trochu jinak, zahrnujic i spravnou verzi :o

Offline vandrovnik

  • Guru
  • *****
  • Příspěvků: 847
  • Karma: 45
    • Verze Delphi: 10.3
A nejvic divne mi prijde, ze to preklada pokazde trochu jinak, zahrnujic i spravnou verzi :o

Aneb využití random() v kompilátoru :-)

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Aneb využití random() v kompilátoru :-)
Diky za ten SSCCE. Zajimave je, ze i kdyz z nej vyhazuju dal, tak se chyba projevuje, zatimco ve skoro identickem SSCCE, ktery jsem psal od zacatku, se chyba nevyskytuje :(

Offline Stanislav Hruška

  • Padawan
  • ******
  • Příspěvků: 4584
  • Karma: 40
    • Verze Delphi: XE7 professional
Len tak pre každý prípad.
V takýchto prípadoch pre istotu "likvidujem" prípadné neviditeľné nežiadané znaky skopírovaním obsahu celej jednotky do texťáku (PsPad). Tam to, po prípadnom skontrolovaní, opäť skopírujem a vložím späť do jednotky. Ale je pravda, že som to už véééľmi dlho nepotreboval.
Delphi XE7, FireBird
Expert na kladenie nejasne formulovaných otázok.

Offline pf1957

  • Padawan
  • ******
  • Příspěvků: 2625
  • Karma: 133
    • Verze Delphi: D2007, XE3, DX10
Excellent
Rated 1 time
Uděláš na to prosím report a pošleš sem link, abychom tam přidali hlas? Tohle mi připadá jako dost zásadní chyba, tak se třeba uráčí ji opravit rychle...
Tak jsem to reportoval RSP-23355