[fpc-pascal] Generic type conflicts
Sven Barth
pascaldragon at googlemail.com
Thu Nov 7 08:58:07 CET 2019
Am 07.11.2019 um 01:42 schrieb Ben Grasset via fpc-pascal:
> On Wed, Nov 6, 2019 at 7:33 PM Ben Grasset <operator97 at gmail.com
> <mailto:operator97 at gmail.com>> wrote:
>
> Encouraging typecasting (which cares only about the sizes of the
> types involved, nothing else) at the programmer level is far more
> error-prone in a variety of ways.
>
>
> Also: it's slower in many cases, because it tends to involve "if"
> statements that *remain* as if statements in the final generated
> assembly code, whereas a static check would allow for simply
> generating *only* the code for the path that's actually taken.
This is not true. If the compiler can prove at compile time that the
if-condition is constant then the branch that is not taken is removed at
the node level. It doesn't even remotely reach the code generation
phase. (I just checked that yesterday when I implemented IsManagedType()
and wondered about missing "unreachable code" warnings).
Take a look at the test for IsManagedType():
=== code begin ===
program tismngd1;
{$mode objfpc}
{$modeswitch advancedrecords}
uses
TypInfo;
var
gError: LongInt = 0;
function NextErrorCode: LongInt; inline;
begin
Inc(gError);
Result := gError;
end;
generic procedure TestType<T>(aIsMngd: Boolean); inline;
begin
if IsManagedType(T) <> aIsMngd then begin
Writeln('IsManagedType(', PTypeInfo(TypeInfo(T))^.Name, ') failure;
expected: ', aIsMngd, ', got: ', IsManagedType(T));
Halt(NextErrorCode);
end;
NextErrorCode;
end;
type
TTestLongInt = record
a: LongInt;
end;
TTestAnsiString = record
a: AnsiString;
end;
TTestManaged = record
a: LongInt;
class operator Initialize(var aTestManaged: TTestManaged);
end;
TTestObj = object
a: LongInt;
end;
TTestObjAnsiString = object
a: AnsiString;
end;
class operator TTestManaged.Initialize(var aTestManaged: TTestManaged);
begin
aTestManaged.a := 42;
end;
type
TProcVar = procedure;
TMethodVar = procedure of object;
TDynArrayLongInt = array of LongInt;
TStaticArrayLongInt = array[0..4] of LongInt;
TStaticArrayAnsiString = array[0..4] of AnsiString;
TEnum = (eOne, eTwo, eThree);
TSet = set of (sOne, sTwo, sThree);
begin
specialize TestType<LongInt>(False);
specialize TestType<Boolean>(False);
specialize TestType<ShortString>(False);
specialize TestType<AnsiString>(True);
specialize TestType<UnicodeString>(True);
specialize TestType<WideString>(True);
specialize TestType<Single>(False);
specialize TestType<TProcVar>(False);
specialize TestType<TMethodVar>(False);
specialize TestType<Pointer>(False);
specialize TestType<IInterface>(True);
specialize TestType<TObject>(False);
specialize TestType<TTestLongInt>(False);
specialize TestType<TTestAnsiString>(True);
specialize TestType<TTestManaged>(True);
specialize TestType<TTestObj>(False);
specialize TestType<TTestObjAnsiString>(True);
specialize TestType<TDynArrayLongInt>(True);
specialize TestType<TStaticArrayLongInt>(False);
specialize TestType<TStaticArrayAnsiString>(True);
specialize TestType<TEnum>(False);
specialize TestType<TSet>(False);
Writeln('Ok');
end.
=== code end ===
Thanks to the node level optimization I mentioned the assembly code of
the main function will look like this (in this case x86_64-win64):
=== code begin ===
.section .text.n_main,"ax"
.balign 16,0x90
.globl main
main:
.globl PASCALMAIN
PASCALMAIN:
.Lc122:
.Lc123:
# Temps allocated between rbp-8 and rbp+0
.seh_proc main
# [62] begin
pushq %rbp
.seh_pushreg %rbp
.Lc124:
.Lc125:
movq %rsp,%rbp
.Lc126:
leaq -48(%rsp),%rsp
.seh_stackalloc 48
movq %rbx,-8(%rbp)
.seh_savereg %rbx, 40
.seh_endprologue
call fpc_initializeunits
# [63] specialize TestType<LongInt>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [64] specialize TestType<Boolean>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [65] specialize TestType<ShortString>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [66] specialize TestType<AnsiString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [67] specialize TestType<UnicodeString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [68] specialize TestType<WideString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [69] specialize TestType<Single>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [70] specialize TestType<TProcVar>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [71] specialize TestType<TMethodVar>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [72] specialize TestType<Pointer>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [73] specialize TestType<IInterface>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [74] specialize TestType<TObject>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [75] specialize TestType<TTestLongInt>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [76] specialize TestType<TTestAnsiString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [77] specialize TestType<TTestManaged>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [78] specialize TestType<TTestObj>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [79] specialize TestType<TTestObjAnsiString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [80] specialize TestType<TDynArrayLongInt>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [81] specialize TestType<TStaticArrayLongInt>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [82] specialize TestType<TStaticArrayAnsiString>(True);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [83] specialize TestType<TEnum>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [84] specialize TestType<TSet>(False);
addl $1,TC_$P$TISMNGD1_$$_GERROR(%rip)
movl TC_$P$TISMNGD1_$$_GERROR(%rip),%eax
# [85] Writeln('Ok');
call fpc_get_output
movq %rax,%rbx
leaq _$TISMNGD1$_Ld3(%rip),%r8
movq %rbx,%rdx
movl $0,%ecx
call fpc_write_text_shortstr
call fpc_iocheck
movq %rbx,%rcx
call fpc_writeln_end
call fpc_iocheck
# [86] end.
call fpc_do_exit
movq -8(%rbp),%rbx
leaq (%rbp),%rsp
popq %rbp
ret
.seh_endproc
.Lc121:
=== code end ===
As you can see the compiler basically optimized everything except the
increment of the gError variable away, because it determined it can do
so. And this was *without* any explicit optimizations enabled.
Regards,
Sven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.freepascal.org/pipermail/fpc-pascal/attachments/20191107/d7d99fb9/attachment-0001.html>
More information about the fpc-pascal
mailing list