Recent

Author Topic: TypeInfo() - what is it?  (Read 717 times)

beria

  • Jr. Member
  • **
  • Posts: 70
TypeInfo() - what is it?
« on: November 30, 2022, 03:32:31 pm »
As I understood, from the point of view of the description - TypeInfo() (https://www.freepascal.org/docs-html/rtl/system/typeinfo.html) is a compiler function, which works only at compile time and returns a pointer to a variable type.  That is, it does not generate any binary code in the program. The nearest analogue of this function which works this way is IsConstValue().
But in reality a simple example shows that it is not so and it generates unnecessary sets of comparing predefined constants with predictable result...
An example for the procedure

 
Code: Pascal  [Select][+][-]
  1.  procedure test40(a: ptrUint); inline;
  2.   begin
  3.     if typeinfo(a) = typeinfo(BYTE) then Writeln('BYTE');
  4.   end;

 in the program to call test40(i), where i-ptrUint; in theory there should be no code, as, by the way, IsConstValue() works, generating code, without any conditional overflows, depending on whether the argument is a constant...
I have it generated by calling test40 ...

    ; Register edx allocated
            mov edx,dword RTTI_$SYSTEM_$$_LONGWORD
    ; Register eax,eflags allocated
            cmp edx,dword RTTI_$SYSTEM_$$_BYTE
    register eax,edx released
            jne ..@j235
    ; Register eflags released
    ; Register eax,ecx,edx allocated

Which makes no sense, because they are always compare different constants which are always predefined before the start of the program.

... Or maybe I'm missing something in the implementation of this function, and it's still not an internal compiler function, as it's written in the description, or FPC somehow allows you to change the variable's type, which is totally insane to me?

ASerge

  • Hero Member
  • *****
  • Posts: 2049
Re: TypeInfo() - what is it?
« Reply #1 on: November 30, 2022, 10:14:41 pm »
Or maybe I'm missing something in the implementation of this function, and it's still not an internal compiler function, as it's written in the description, or FPC somehow allows you to change the variable's type, which is totally insane to me?
According to the link that you yourself indicated, it is written "If no type information was yet generated for the type, this statement will ensure that type information is available, i.e. the result is always non-nil".
Thus metadata is always generated when using TypeInfo.
Use GetTypeKind:
Code: Pascal  [Select][+][-]
  1. procedure test40(a: PtrUint); inline;
  2. begin
  3.   if GetTypeKind(a) = GetTypeKind(Byte) then
  4.     Writeln('BYTE');
  5. end;

beria

  • Jr. Member
  • **
  • Posts: 70
Re: TypeInfo() - what is it?
« Reply #2 on: November 30, 2022, 11:07:43 pm »
Thank you very much. I had your option first, and it works and is very good, but it turns out that I think due to an implementation error, ALL integer types, regardless of their size in bytes, which is what I need, are the same for GetTypeKind...
The code below gives the same values.... So this built-in compiler function is a priori for me and my tasks, not working...

writeln(GetTypeKind(byte));
writeln(GetTypeKind(longint));
......

And I don't think you're right about mandatory code generation for TypeOf.... TypeOf() generates, as you can see from the code and description, just the value of the constant, like "Роinter", like IsConstValue() of the constant type "boolean" Nothing else.  And the compiler itself should, in theory, optimize them... But it always works only in the case of IsConstValue() and does not in the case of TypeOf(). I still don't know why I have to make a zoo of functions to decode a binary stream depending on the size of an integer argument in bytes instead of one universal function...

Here is a code sample optimizing constants in FPC:

Code: Pascal  [Select][+][-]
  1. const
  2.   const1 = 1;
  3.   const2 = 2;
  4.  
  5. begin
  6. if const1 = const2 then write ('test');
  7. end.

This program construction does not insert anything anywhere, neither comparing constants with predefined result, nor calling function "write"..... It should be exactly the same in theory, but it doesn't work...
« Last Edit: November 30, 2022, 11:09:19 pm by beria »

PascalDragon

  • Hero Member
  • *****
  • Posts: 4870
  • Compiler Developer
Re: TypeInfo() - what is it?
« Reply #3 on: November 30, 2022, 11:26:43 pm »
Thank you very much. I had your option first, and it works and is very good, but it turns out that I think due to an implementation error, ALL integer types, regardless of their size in bytes, which is what I need, are the same for GetTypeKind...

GetTypeKind returns a TTypeKind and for these all integer types are in fact tkInteger. They only differ by the OrdType which is part of the TTypeInfo returned by the TypeInfo intrinsic, but which isn't covered by GetTypeKind. It's intention is to optimize the main lookup of types, not all of them.

And I don't think you're right about mandatory code generation for TypeOf.... TypeOf() generates, as you can see from the code and description, just the value of the constant, like "Роinter", like IsConstValue() of the constant type "boolean" Nothing else.  And the compiler itself should, in theory, optimize them... But it always works only in the case of IsConstValue() and does not in the case of TypeOf(). I still don't know why I have to make a zoo of functions to decode a binary stream depending on the size of an integer argument in bytes instead of one universal function...

The compiler currently simply does not treat the pointers returned by TypeInfo as constant. FPC 3.3.1 however will at least optimize comparisons between two TypeInfo invocations that contain types:

Code: Pascal  [Select][+][-]
  1. if TypeInfo(PtrUInt) = TypeInfo(Byte) then Writeln('BYTE')

This will evaluate to False at compile time. I'd need to check whether it is really safe to extend this further.

beria

  • Jr. Member
  • **
  • Posts: 70
Re: TypeInfo() - what is it?
« Reply #4 on: December 01, 2022, 11:17:38 am »


Code: Pascal  [Select][+][-]
  1. if TypeInfo(PtrUInt) = TypeInfo(Byte) then Writeln('BYTE')

This will evaluate to False at compile time. I'd need to check whether it is really safe to extend this further.

I would say that the safety of such an approach follows directly from the whole paradigm of the Pascal language with a pre-description of all types.... But the procedure below is not working yet because it generates unnecessary code... By the way, for "generics" the same restrictions and code bloat.

Code: Pascal  [Select][+][-]
  1.  procedure test41(a: ptrUint); inline;
  2.   begin
  3.     if TypeInfo(a) = TypeInfo(uint8) then Writeln('1')
  4.     else if TypeInfo(a) = TypeInfo(uint16) then Writeln('2')
  5.     else if TypeInfo(a) = TypeInfo(uint32) then Writeln('4')
  6.     else if TypeInfo(a) = TypeInfo(uint64) then Writeln('8');
  7.   end;
       
ps: there is a hope in the near future to wait at least beta of new functionality, what with pleasure I will test in the real and working project?  I don't think it will be very difficult because, at first glance, I just saw how TypeInfo is implemented in the source code of FPC, just there artificially limited functionality, as opposed to many other built-in compiler functions, which also return a constant.
pps: and on a side note, is there any way for "{$mode objfpc}" to use the simpler and more obvious "generics" syntax from the Delphi compatibility relim, just that I don't use?



PascalDragon

  • Hero Member
  • *****
  • Posts: 4870
  • Compiler Developer
Re: TypeInfo() - what is it?
« Reply #5 on: December 01, 2022, 10:09:56 pm »
I would say that the safety of such an approach follows directly from the whole paradigm of the Pascal language with a pre-description of all types.... But the procedure below is not working yet because it generates unnecessary code...

“not working yet” and “generates unnecessary code” are two different points. The code will behave the same no matter if the compiler generates “unnecessary” code or not.

Code: Pascal  [Select][+][-]
  1.  procedure test41(a: ptrUint); inline;
  2.   begin
  3.     if TypeInfo(a) = TypeInfo(uint8) then Writeln('1')
  4.     else if TypeInfo(a) = TypeInfo(uint16) then Writeln('2')
  5.     else if TypeInfo(a) = TypeInfo(uint32) then Writeln('4')
  6.     else if TypeInfo(a) = TypeInfo(uint64) then Writeln('8');
  7.   end;

I don't know whether this is what you desire, but this will only ever differ if you compile for different platforms. But in that case you could just as well use a type instead of the variable for TypeInfo and thus profit from the already available optimization.
       
ps: there is a hope in the near future to wait at least beta of new functionality, what with pleasure I will test in the real and working project?  I don't think it will be very difficult because, at first glance, I just saw how TypeInfo is implemented in the source code of FPC, just there artificially limited functionality, as opposed to many other built-in compiler functions, which also return a constant.

The important part is to make sure that there really wouldn't be a corner case.

pps: and on a side note, is there any way for "{$mode objfpc}" to use the simpler and more obvious "generics" syntax from the Delphi compatibility relim, just that I don't use?

No, there is not. In the future there will likely be a modeswitch for that, but not until mode ObjFPC supports some features that it currently lacks that mode Delphi supports. Please note however that with the Delphi syntax currently you'll be less able to write complex expressions using inline specializations.

beria

  • Jr. Member
  • **
  • Posts: 70
Re: TypeInfo() - what is it?
« Reply #6 on: December 01, 2022, 11:09:58 pm »
I would say that the safety of such an approach follows directly from the whole paradigm of the Pascal language with a pre-description of all types.... But the procedure below is not working yet because it generates unnecessary code...

“not working yet” and “generates unnecessary code” are two different points. The code will behave the same no matter if the compiler generates “unnecessary” code or not.
[/quote]

The problem is purely in the ultimate optimization. This is the code of an extremely complex and multiplatform telemetry data decoder, I optimized it to the limit, including assembler inserts where necessary, because it is called many millions of times in real time.  So even a set of redundant and unoptimized constant comparison operations is, by measurements, up to minus 5% of performance. At the moment I have different procedures for all dimensions of integer arguments, for this reason, which is very confusing and complicated, but if everything will work as it should, they can be removed in one and universal, which code will be dynamically generated, depending on the type of application.  Exactly the same thing I already did for the variant when the argument of the procedure is a constant. I used to have separate procedures for them too, but beautifully and stably working IsConstValue() helped me a lot.  By the way, it would be very good to rewrite the Move procedure using it.  So, I'm just for a large number of such procedures for the compiler itself, not for the language.
Right now I have something like:
Code: Pascal  [Select][+][-]
  1.   procedure Decoder(a: uint64); inline;
  2.   procedure Decoder(a: uint32); inline;
  3.   procedure Decoder(a: uint16); inline;
  4.   procedure Decoder(a: uint8); inline;

 ...and having to make a mandatory type change for variables with a sign. It's very annoying...

Something like this...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 10479
  • FPC developer.
Re: TypeInfo() - what is it?
« Reply #7 on: December 01, 2022, 11:22:23 pm »
Do you actually have an example where it matters? Since the code you post will first typecast to ptruint, and only then get the typeinfo of ptruint.

beria

  • Jr. Member
  • **
  • Posts: 70
Re: TypeInfo() - what is it?
« Reply #8 on: December 01, 2022, 11:35:57 pm »
Do you actually have an example where it matters? Since the code you post will first typecast to ptruint, and only then get the typeinfo of ptruint.
That's why I write that now it doesn't work and you have to do everything in other ways... But I want it to work like any other compiler function and to get dynamic branching within a procedure...
« Last Edit: December 01, 2022, 11:56:09 pm by beria »

PascalDragon

  • Hero Member
  • *****
  • Posts: 4870
  • Compiler Developer
Re: TypeInfo() - what is it?
« Reply #9 on: December 02, 2022, 05:39:19 pm »
I would say that the safety of such an approach follows directly from the whole paradigm of the Pascal language with a pre-description of all types.... But the procedure below is not working yet because it generates unnecessary code...

“not working yet” and “generates unnecessary code” are two different points. The code will behave the same no matter if the compiler generates “unnecessary” code or not.

The problem is purely in the ultimate optimization. This is the code of an extremely complex and multiplatform telemetry data decoder, I optimized it to the limit, including assembler inserts where necessary, because it is called many millions of times in real time.  So even a set of redundant and unoptimized constant comparison operations is, by measurements, up to minus 5% of performance. At the moment I have different procedures for all dimensions of integer arguments, for this reason, which is very confusing and complicated, but if everything will work as it should, they can be removed in one and universal, which code will be dynamically generated, depending on the type of application.  Exactly the same thing I already did for the variant when the argument of the procedure is a constant. I used to have separate procedures for them too, but beautifully and stably working IsConstValue() helped me a lot.  By the way, it would be very good to rewrite the Move procedure using it.  So, I'm just for a large number of such procedures for the compiler itself, not for the language.
Right now I have something like:
Code: Pascal  [Select][+][-]
  1.   procedure Decoder(a: uint64); inline;
  2.   procedure Decoder(a: uint32); inline;
  3.   procedure Decoder(a: uint16); inline;
  4.   procedure Decoder(a: uint8); inline;

 ...and having to make a mandatory type change for variables with a sign. It's very annoying...

Something like this...

I still don't get what you're trying to achieve. If you retrieve the type information from a PtrUInt parameter then it will always be PtrUInt. It won't change depending on what you pass in. For that you'd need to use generics which wouldn't be a runtime difference either.

And I also don't get your remark regarding Move.

Thaddy

  • Hero Member
  • *****
  • Posts: 12565
Re: TypeInfo() - what is it?
« Reply #10 on: January 14, 2023, 06:18:06 pm »
Or maybe I'm missing something in the implementation of this function, and it's still not an internal compiler function, as it's written in the description, or FPC somehow allows you to change the variable's type, which is totally insane to me?
Use GetTypeKind:
Code: Pascal  [Select][+][-]
  1. procedure test40(a: PtrUint); inline;
  2. begin
  3.   if GetTypeKind(a) = GetTypeKind(Byte) then
  4.     Writeln('BYTE');
  5. end;

No, this would only work for your expected result on x86_64 and not on x86_32 a.k.a i386.
Code: Pascal  [Select][+][-]
  1. var a:ptruint;
  2. begin
  3.  writeln(GetTypeKind(a) = GetTypeKind(Byte));  // returns true on 32 bit and false on 64 bit
  4. end.

On 32 bit GetTypeKind(a) returns tkInteger as does GetTypeKind(byte)
On 64 bit GetTypeKind(a) returns tkQword.............

Btw: tkInteger is correct for 32 bit. It does not have to explain signed or not.


« Last Edit: January 14, 2023, 06:32:37 pm by Thaddy »
Writing code in an overly complex way is usually merely a complete misunderstanding of the goal you are trying to achieve.

 

TinyPortal © 2005-2018