You are not logged in.
Pages: 1
> code uses explicit pointer types,
It means nothing if they are not checked
> with strong typing
and you said you disabled typing check (as $T- }
Cannot link those two claims together
@ab
it maybe would not be a big issue if it changes loop for dec/jnz - apart from i COULD be dependent on the certain opcodes and their lengths.
However delphi exchanged look for an opposite instractions seq: dec cx / JZ ... ensuring loop would exit immediately
PS: after rereading my own report. Actually not even dec/jz, but just a jz without dec
Thanks. Perhaps if you also blogged about which patterns in DCC trigger the most weird code generation lapses, it also would be of benefit.
OTOH i feel likeyou tend to "old school" programing, avoiding language features that missed from D5/D7 era... Delphi tries to be more high-level, and you have choses your own ground and don't move nether up nor down.
I think that avoiding type safety is not a thing for Pascal. So in my project i do the opposite and enforce T+ everywhere, like other safety net measures. And where i need to violate it, i do it by explicit typecasts or absolute vars.
But probably Delphi developers do not set T+ either, otherwise i wopuld not say how could they let that craze pass below radars
But how can it be that dynamic arrays would work correctly in Delphi ? I think we missed some details. What if we would just change the index?
SetLength(dmDBXSaveErr, 3);
{$T-} p_e := @dmDBXSaveErr;
{$T-} p_e := @dmDBXSaveErr[1];
{$T+} p_e := @dmDBXSaveErr[1];
{$T-} p_e := @@dmDBXSaveErr[1];
//remember? this compiles fine... or should i say "this at least compiles" ?
{$T+} p_e := @@dmDBXSaveErr[0];
// SUDDENLY!
{$T+} p_e := @@dmDBXSaveErr[1]; // [DCC Error] E2010 Incompatible types: '_PEventErr' and 'Pointer'
Type compatibility is different for 0th and 1st elements
CurrencyFrames.pas.681: {$T-} p_e := @dmDBXSaveErr[1];
0AB5EA85 8B45F4 mov eax,[ebp-$0c]
0AB5EA88 8B8060030000 mov eax,[eax+$00000360] // so we're fetching the address of the 0th element....
0AB5EA8E 8B4008 mov eax,[eax+$08] // and screw it overwriting it by 1st element value
0AB5EA91 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.683: {$T+} p_e := @dmDBXSaveErr[1]; // same as above
0AB5EA94 8B45F4 mov eax,[ebp-$0c]
0AB5EA97 8B8060030000 mov eax,[eax+$00000360]
0AB5EA9D 8B4008 mov eax,[eax+$08]
0AB5EAA0 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.685: {$T-} p_e := @@dmDBXSaveErr[1];
0AB5EAA3 8B45F4 mov eax,[ebp-$0c]
0AB5EAA6 8B8060030000 mov eax,[eax+$00000360] // so we're fetching the address of the 0th element....
0AB5EAAC 83C008 add eax,$08 // and just correctly shift it to the next element!
0AB5EAAF 8945F8 mov [ebp-$08],eax // but as usual, to get a correct valuewe had to kill type safety :-/
Oh, yes... about dynamic arrays... wpould they be any different from static ones ?
type _EventsErr = array { [1 .. 3] } of TDataSetErrorEvent; // dynamic - one more indirection level
SetLength(dmDBXSaveErr, 3);
{$T+}
p_e := @dmDBXSaveErr; // [DCC Error] E2010 Incompatible types: '_PEventErr' and 'Pointer'
{$T-} p_e := @dmDBXSaveErr;
{$T-} p_e := @dmDBXSaveErr[0];
{$T+} p_e := @dmDBXSaveErr[0];
{$T-} p_e := @@dmDBXSaveErr[0];
{$T+} p_e := @@dmDBXSaveErr[0];
CurrencyFrames.pas.679: {$T-} p_e := @dmDBXSaveErr;
094FEA7A 8B45F4 mov eax,[ebp-$0c]
094FEA7D 0560030000 add eax,$00000360
094FEA82 8945F8 mov [ebp-$08],eax
// Here we obtain the pointer to the DynArray controlling structure
// It is correct but is not what we want
// Just want to notice that DCC - both CodeGen and TypeChecker - worked coherently here
CurrencyFrames.pas.681: {$T-} p_e := @dmDBXSaveErr[0];
094FEA85 8B45F4 mov eax,[ebp-$0c]
094FEA88 8B8060030000 mov eax,[eax+$00000360] // here we obtain the cell address we crave for...
094FEA8E 8B00 mov eax,[eax] // and screw it, replacing with the VALUE of the cell instead
094FEA90 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.683: {$T+} p_e := @dmDBXSaveErr[0]; // same as above
094FEA93 8B45F4 mov eax,[ebp-$0c]
094FEA96 8B8060030000 mov eax,[eax+$00000360]
094FEA9C 8B00 mov eax,[eax]
094FEA9E 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.685: {$T-} p_e := @@dmDBXSaveErr[0];
094FEAA1 8B45F4 mov eax,[ebp-$0c]
094FEAA4 8B8060030000 mov eax,[eax+$00000360] // With dynamic array we have a correct working code...
094FEAAA 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.687: {$T+} p_e := @@dmDBXSaveErr[0];
094FEAAD 8B45F4 mov eax,[ebp-$0c]
094FEAB0 8B8060030000 mov eax,[eax+$00000360] // ...that even can be compiled without killing type safety!!!
094FEAB6 8945F8 mov [ebp-$08],eax
Outcome of pointers math
{$T-}
p_e := @dmDBXSaveErr;
p_e := @@dmDBXSaveErr[1];
p_e := @dmDBXSaveErr[1];
{$T+}
p_e := @dmDBXSaveErr[1];
CurrencyFrames.pas.678: p_e := @dmDBXSaveErr; {$T-}
092FEA3D 8B45F4 mov eax,[ebp-$0c]
092FEA40 0560030000 add eax,$00000360 // Correct result, but type-unsafe
092FEA45 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.679: p_e := @@dmDBXSaveErr[1]; {$T-}
092FEA48 8B45F4 mov eax,[ebp-$0c]
092FEA4B 0560030000 add eax,$00000360 // Correct result, but type-unsafe
092FEA50 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.681: p_e := @dmDBXSaveErr[1]; {$T-}
092FEA53 8B45F4 mov eax,[ebp-$0c]
092FEA56 8B8060030000 mov eax,[eax+$00000360] // WRONG!!! Value instead of address! but "type-safe"
092FEA5C 8945F8 mov [ebp-$08],eax
CurrencyFrames.pas.683: p_e := @dmDBXSaveErr[1]; {$T+}
092FEA5F 8B45F4 mov eax,[ebp-$0c]
092FEA62 8B8060030000 mov eax,[eax+$00000360] // WRONG!!! Value instead of address! but "type-safe"
092FEA68 8945F8 mov [ebp-$08],eax
Then i tried to address Watches window - it failed to calculate values like @dmDbxSaveErr[0] and
@@dmDbxSaveErr[0]
I made one of the arrays dynamic - just to see if it counts - and suddenly Wathes started to work with both dynamic and static arrays
Trying to use Pointer Math instead
var p_n: _PEventNtf; p_e: _PEventErr;
function Cell: _PEventNtf;
begin
Result := p_n; // here we moved compiler scizoidness into the main fun body
Inc( p_n );
end;
function Err: _PEventErr;
begin
Result := p_e;
Inc( p_e );
end;
begin
inherited Loaded;
{$T+}
p_n := @dmDBXSaveNtf; // [DCC Error] E2010 Incompatible types: '_PEventNtf' and 'Pointer'
p_n := @@dmDBXSaveNtf; // [DCC Error] E2036 Variable required
p_e := @dmDBXSaveErr[1];
p_e := @@dmDBXSaveErr[1]; // [DCC Error] E2010 Incompatible types: '_PEventErr' and 'Pointer'
{$T-}
p_n := @dmDBXSaveNtf;
p_n := @@dmDBXSaveNtf; // [DCC Error] E2036 Variable required
p_e := @dmDBXSaveErr[1];
p_e := @@dmDBXSaveErr[1];
Cell^ := ...
So... is procedure of object variable even a procedure ???
...or how i learned not to worry about type safety and hate the code generator.
PS: also featuring: different elements' types in static and dynamic arrays of the same element type
-----------
Working with legacy code i met a TFrame that used to intercept events of a dataset in another DataModule but did not nilled them on its destruction.
Wanted to make it safer and DRY i set to only change the whole list of event handlers in one procedure, and just call it twice.
...i thought that "procedure of object" is one of procedure types. Yet DCC XE2 has a varying opinions on the topic.
------------
type
_PEventNtf = ^TDataSetNotifyEvent; // iniially was an internal type inside the TFrame1, made it global - no difference
_PEventErr = ^TDataSetErrorEvent;
TFrame1 = class(TFrameBase)
....
type _EventsNtf = array [1 .. 10] of TDataSetNotifyEvent;
var dmDBXSaveNtf: _EventsNtf;
type _EventsErr = array [1 .. 3] of TDataSetErrorEvent;
var dmDBXSaveErr: _EventsErr;
// потом эти массивы одной функцией обмениваются с соотв. датасетами
// так что нельзя забыть восстановить то, что перехватывали
...
{$T+, D+} // $O- and $O+ generate the same code here
procedure TFrame1.Loaded;
var i_n, i_e: integer;
function Cell: _PEventNtf;
begin
Inc(i_n);
Result := @dmDBXSaveNtf[i_n]; // compiles and looks innocent...
end;
function Err: _PEventErr;
begin
Inc(i_e);
{$T-} // forcing to compile the next line: incorrect pascal sources generates the correct binary code
Result := @@dmDBXSaveErr[i_e];
end;
begin
inherited Loaded;
i_n := 0; i_e := 0;
Cell^ := quSQLQuery1AfterOpen;
Cell^ := quSQLQuery1AfterClose;
etc
And the 1st Cell^ := ... line suddenly fallen with attempt at writing @000004 :-[...]
It turned out, the function really did returned nil
CurrencyFrames.pas.648: begin // nested fn Cell
0AACE9F4 55 push ebp
0AACE9F5 8BEC mov ebp,esp
0AACE9F7 51 push ecx
CurrencyFrames.pas.649: Inc(i_n);
0AACE9F8 8B4508 mov eax,[ebp+$08]
0AACE9FB FF40FC inc dword ptr [eax-$04]
CurrencyFrames.pas.650: Result := @dmDBXSaveNtf[i_n];
0AACE9FE 8B4508 mov eax,[ebp+$08]
0AACEA01 8B40FC mov eax,[eax-$04]
0AACEA04 8B5508 mov edx,[ebp+$08]
0AACEA07 8B52F8 mov edx,[edx-$08]
0AACEA0A 8B84C208030000 mov eax,[edx+eax*8+$0308] // reading the value inside a cell instead of taking address
0AACEA11 8945FC mov [ebp-$04],eax
CurrencyFrames.pas.651: end;
0AACEA14 8B45FC mov eax,[ebp-$04]
0AACEA17 59 pop ecx
0AACEA18 5D pop ebp
0AACEA19 C3 ret
0AACEA1A 8BC0 mov eax,eax // gap filler
CurrencyFrames.pas.653: begin // nested fn Err
0AACEA1C 55 push ebp
0AACEA1D 8BEC mov ebp,esp
0AACEA1F 51 push ecx
CurrencyFrames.pas.654: Inc(i_e);
0AACEA20 8B4508 mov eax,[ebp+$08]
0AACEA23 FF40F4 inc dword ptr [eax-$0c]
CurrencyFrames.pas.656: Result := @@dmDBXSaveErr[i_e]; // line forced by {$T-}
0AACEA26 8B4508 mov eax,[ebp+$08]
0AACEA29 8B40F4 mov eax,[eax-$0c]
0AACEA2C 8B5508 mov edx,[ebp+$08]
0AACEA2F 8B52F8 mov edx,[edx-$08]
0AACEA32 8D84C258030000 lea eax,[edx+eax*8+$0358] // correctly taking the address of the array cell itself!
0AACEA39 8945FC mov [ebp-$04],eax
CurrencyFrames.pas.657: end;
0AACEA3C 8B45FC mov eax,[ebp-$04]
0AACEA3F 59 pop ecx
0AACEA40 5D pop ebp
0AACEA41 C3 ret
Well, in post-XE Delphi you cannot have neither small nor fast EXE
In XE2 assembler is screwed, sometimes it just inverts the logic in your code
http://qc.embarcadero.com/wc/qcmain.aspx?d=105876
and still not-fixed http://qc.embarcadero.com/wc/qcmain.aspx?d=105877
I wonder if this one was tried as an interfaced object, with RefCount-controlledFluent Style API (chined method calls)
In the classic OO world:
record = memory structure
object = memory structure with a method
AFAIR that was not the case in C++
class constructors are cool
AFAIR they can be applied to records, but not to objects :-)
What a shame that Codegear introduced an out-of-box unicode-ready Delphi 10 years after Microsoft has fully supported it in Windows 2000... When everybody who needed unicode uses TntComponents
Sorry for bumping old thread.
I think lack of Unicode in Delphi was more vocal than wide request.
While it was pity, it is still too much exagerrated.
Those who needed it had Tnt, but when Tnt ceased and were sold out, were there developers to frk and continue TnT as free project ? no.
Maybe there was some large widget library, who choosed Tnt mechanics as a base to grow their large wiget sets unicode-aware ? no.
So, frankly, no one cared mostly.
1st version of Windows NT - NT 3.1 - was in 1993. It was not in 1999. If Delphi needed Unicode support - they could start as early as Delphi 2 release - but back then next to no one though about Unicode, every one though about RAM economy. It was RAM prices going down as well as obsoletion of Windows 9x/ME familiy and MS Unicode Services for Win98 that made Unicode a feasible "least common". And it was not until somewhere around 2005, that old Windows line-up could be completely forgotten.
Yes, Delphi is a bit late to jump onto Unicode train. But they did not planned to make two EXE's from same sources, ANSI and Unicode, they wanted one-time swtch. And they wanted all 3rd-party VCL ecosystem to switch as well. And it would rather not done too early.
The idea "make apps smaller" is too generic. "let's make good to everyone"
iI talked about more techy description, how and to which extent this ismade.
> KOL is not compatible with the IDE designer
KOL is not, but KOL-MCK is.
And KOL w/o MCK existed for very short time far far ago.
Last KOL release was yeas ago, but news on the site shows changes as recent as Feb 2012.
Though probably KOL-MCK is more ambitious and requires more work.
The problem maybe is that both projects are little known
GDAL get Delphi Access Licence;
RCS checks for Delphi Client/Server Licence;
RPR checks for Delphi Professional Licence.
I'd re-phrase.
GDAL stands for GetDelphiAccessLicence;
RCS stands for RequireClientServer;
RPR stands for RequireProfessionalLicence.
Last two are named quite along VCL habits :-)
About protection force and patcher - why bother? go torrents and download the most full-featured Delphi version for free.
You can have legal Delphi and your app, or you can get contrafact ones. But semi-illegal Delphi is ... like semi-pregnant girl.
That is not the fencing, but rather a warning label "Do not tresspass"
It's simplicity makes it rock-stable, not suddenly turning mad like more Artificialy Idiotic protections, like those used by PC Games.
But it's GUI is kinda spaghetti. Unreal Commander, having no need for backward compatibility, is sometimes easier and more straightlined to use.
Though in general TC seems to be more featured.
by chance found LVCL http://bouchez.info/lvcl.html
But it references LightVCL for description, which is in French.
So i did not saw, what were the ideas behind it.
Especially if it uses non-optimised, slow and fat DFM :-)
I wonder if anyone compared LVCL to KOL-MCK.
I remember using D5 to make 2KB DLL plugin - only to find that MS OE 6 / Win2K cannot load it :-)
talking about performance, how would you like this ? good, it is legacy compatibility bridge. Pure insanity....
Main.pas.49: lblJvIPAddressValuesAddress.Caption := IntToStr(AddressValues.Address);
00563B20 8B45F8 mov eax,[ebp-$08]
00563B23 8945E0 mov [ebp-$20],eax
00563B26 8B45E0 mov eax,[ebp-$20]
00563B29 8945F4 mov [ebp-$0c],eax
00563B2C 8B45F4 mov eax,[ebp-$0c]
...
Main.pas.50: lblJvIPAddressValuesValue1.Caption := IntToStr(AddressValues.Value1);
00563B52 8B45F8 mov eax,[ebp-$08]
00563B55 8945DC mov [ebp-$24],eax
00563B58 8B45DC mov eax,[ebp-$24]
00563B5B 8945F0 mov [ebp-$10],eax
00563B5E 8B45F0 mov eax,[ebp-$10]
00563B61 8945D4 mov [ebp-$2c],eax
00563B64 8B45D4 mov eax,[ebp-$2c]
00563B67 BA04000000 mov edx,$00000004
....
No, really, that is getting weird. IS it depending on call-site ?
Integer, with both {$O-} and {$O+}
fmMain.pas.1216: x := -3;
0075EFC3 C745F4FDFFFFFF mov [ebp-$0c],$fffffffd
fmMain.pas.1217: x := x div 2;
0075EFCA B902000000 mov ecx,$00000002
0075EFCF 8B45F4 mov eax,[ebp-$0c]
0075EFD2 99 cdq
0075EFD3 F7F9 idiv ecx
0075EFD5 8945F4 mov [ebp-$0c],eax
Cardinal:
fmMain.pas.1214: x := +3;
0075F013 C745F403000000 mov [ebp-$0c],$00000003
fmMain.pas.1215: x := x div 2;
0075F01A B902000000 mov ecx,$00000002
0075F01F 8B45F4 mov eax,[ebp-$0c]
0075F022 33D2 xor edx,edx
0075F024 F7F1 div ecx
0075F026 8945F4 mov [ebp-$0c],eax
fmMain.pas.1217: x := +3;
0075F029 C745F403000000 mov [ebp-$0c],$00000003
x: cardinal; y: integer;
fmMain.pas.1214: x := +3;
0075EFDA C745F403000000 mov [ebp-$0c],$00000003
fmMain.pas.1215: x := x shr 2;
0075EFE1 C16DF402 shr dword ptr [ebp-$0c],$02
fmMain.pas.1217: y := -3;
0075EFE5 C745F0FDFFFFFF mov [ebp-$10],$fffffffd
fmMain.pas.1218: y := y shr 2;
0075EFEC C16DF002 shr dword ptr [ebp-$10],$02
fmMain.pas.1221: x := x + y;
0075EFF0 8B45F0 mov eax,[ebp-$10]
0075EFF3 0145F4 add [ebp-$0c],eax
Which is arguably wrong on signed values.
PS since you made speed-optimized RTL, wanna have a bit of thrill and awe ?
Like "a bit of functional programming does not hurt in any quantities" ?
https://forums.embarcadero.com/thread.j … eID=414610
> to handle CF in case of negative number
that means SHL - "*2". And only if range checking on.
In case of "div 2" CF would be oddity bit. IOW only need checking in "mod 2". Which probably be taken as "and 1". "Shr" 1 for "mod 2" or Odd(x) or Even(x) seems to be too clever trick for DCC.
Truly, i can't think of single practical use of CF after shift, that has any difference between signed and unsigned.
Also i ain't sure that CF is used at all for opcodes other than RCR/RCL. Just do not remember.
http://en.wikibooks.org/wiki/X86_Assemb … and_Rotate - this tells they are not, but i do not know how authoritative this source is. I remember well RCL but don't remember SCL at all. Though by logic it should exist for 64-bit integers in 32-bit mode and 32-bit integers in 16-bit one.
SHR - unsigned divizion, zero-padded
SAR - signed division, sign-padded
Looking retrospectively, i like that shortcut typo of mine. :-)
"Unzigned" would be even better though. Missed chance for perfection.
I have no access to Delphi 5 now, but at least i chanced to find respective line in XE2-produced 32-bit code.
TPoint is made of signed integers, isn't it ?
So, that's not just inconsistent but plain wrong :-)
Perhaps You've just forgotten {$O+} in DEBUG profile ?
Mmmm, in XE2 it still uses SAR even in $O- mode. If would not forget at spring should re0check in D5. I just can't believe in such inconsistency as a primary feature, not a bug in some particular compiler build.
That's inconsistent.
Year ago (when all modern x86 architectures except for Bulldozer were already in place) you gave suggestion to replace div with shr
Now u say it is irrelevant.
And i still have notebook with Pentium M processor, which, if i remeber right, is rooted in old PentiumPro :-)
fixed typos
i would not search for TurboPascal 5.5 or 7; and Virtual Pascal proves nothing here, but i feel like i saw SAR opcode in generated code.
Anyway, given that RTL/VCL strongly dislikes Cardinal and such, it must be EXTREMELY lame limitation to use SHR but not SAR
Is there QC on this ?
" You use some div 32 or div 256: you'd better use shl 5 and shl 8, which will be faster when working with integer/NativeInt. "
" For the "div 2", replace it with "shl 1". Or use cardinals (the compiler will replace "aCardinal div 2" by "aCardinal shl 1", but it won't do it for an integer, because it must check if the integer is not <0).
For unsigned integers, div 2=shl 1, div 4=shl 2, div 8=shl 3, and so on... "
???
1) Ain't shift-left a multiplication, while shift-right being division ? 0100 = 4, 0010 = 2
2) AFAIR 80386 assembler there is three instructions:
SHL - multiplication, sign bit is lost (preempted into overflow signal)
SHR - unsigned divizion, zero-padded
SAR - signed division, sign-padded
So would compiler refuse to optimize integer div 2 ?
Pages: 1