Recent

Author Topic: Why cannot this be inlined?  (Read 1830 times)

Edson

  • Hero Member
  • *****
  • Posts: 1295
Why cannot this be inlined?
« on: November 27, 2023, 05:00:58 pm »
I read some old post about the problem of showing a lot of hints about "Call to subroutine ... marked as inline is not inlined" and altouhg it's annoying (more considering most of them come from LCL units), and I know compiler is not obligated to inline what it don't want to inline, moreover I can suppress the hints with a directive, I would like to know why this simple method cannot be inlined?:

Code: Pascal  [Select][+][-]
  1. procedure TMirList.ClearInsertMode; INLINE;
  2. begin
  3.   insPoint := -1;
  4. end;
  5.  
« Last Edit: November 27, 2023, 05:02:30 pm by Edson »
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9682
  • Debugger - SynEdit - and more
    • wiki
Re: Why cannot this be inlined?
« Reply #1 on: November 27, 2023, 05:36:24 pm »
From distant memory... There was something with IIRC global vars. Maybe... So what is "InsPoint"?

And if it is that, did you try fpc 3.3.1? Afaik, with 3.3.1 the mentioned issue is gone.

Edson

  • Hero Member
  • *****
  • Posts: 1295
Re: Why cannot this be inlined?
« Reply #2 on: November 27, 2023, 06:10:42 pm »
From distant memory... There was something with IIRC global vars. Maybe... So what is "InsPoint"?

And if it is that, did you try fpc 3.3.1? Afaik, with 3.3.1 the mentioned issue is gone.

Hi Martin.
"InsPoint is a global field of TMirList class:
Code: Pascal  [Select][+][-]
  1.   TMirList = class
  2.   public
  3.     insPoint: Integer;      
  4.   ...
  5.  
I've updated to Lazarus 2.2.6 and FPC 3.2.2 and I still have the same hint.
I've checked the assembler windows too, and I see in fact compiler ignores the INLINE and creates a function call.
How do I update my current Lazarus to FPC 3.3.1?
« Last Edit: November 27, 2023, 06:14:17 pm by Edson »
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9682
  • Debugger - SynEdit - and more
    • wiki
Re: Why cannot this be inlined?
« Reply #3 on: November 27, 2023, 06:33:12 pm »
First of all, I don't know if "fields" are a problem. Could be, but all I recall are "global vars" => outside a class.

Another point: "ClearInsertMode" is not virtual?

-------
Fpc 3.3.1 (aka trunk) is a development version. There is no installer. FpcUpDeluxe can install it for you. But as it is a development version it may not be as stable as the released version (you may run into other issues)




also I just noticed:
Code: Pascal  [Select][+][-]
  1. procedure TMirList.ClearInsertMode; INLINE;

That is in the implementation section.

What abount
Code: Pascal  [Select][+][-]
  1. type
  2. TMirList = class
  3.   procedure ClearInsertMode; INLINE;  //  << is there an inline here too??
  4.  

If not that may explain it.


If you don't have an inline at the declaration (in the interface section), then calls to ClearInsertMode are only inlined if
- they are from the same unit
- from a line, below the code of ClearInsertMode
- ... if at all


Edson

  • Hero Member
  • *****
  • Posts: 1295
Re: Why cannot this be inlined?
« Reply #4 on: November 27, 2023, 07:41:50 pm »
My function "ClearInsertMode" is the most simple. Not virtual. No Override. Declared as INLINE in header:

Code: Pascal  [Select][+][-]
  1.   TMirList = class
  2.   public
  3.     insPoint: Integer;      //Insert point for instructions
  4.     ...
  5.   private  
  6.     procedure ClearInsertMode; inline;
  7.     ...
  8.   end;

And I use "ClearInsertMode" only inside the class

Maybe I need to wait for Fpc 3.3.1 to be stable. I don't like to use trunk versions.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9682
  • Debugger - SynEdit - and more
    • wiki
Re: Why cannot this be inlined?
« Reply #5 on: November 28, 2023, 02:10:55 am »
ok, I took the time, and put your code snippets into a small sample prog.
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}
  3. type
  4.   TMirList = class
  5.   public
  6.     insPoint: Integer;      //Insert point for instructions
  7.   private
  8.     procedure ClearInsertMode; inline;
  9.   end;
  10.  
  11. procedure TMirList.ClearInsertMode;
  12. begin
  13.   insPoint := -1;
  14. end;
  15.  
  16. var
  17.   a: TMirList;
  18. begin
  19.   a := TMirList.Create;
  20.   a.ClearInsertMode;
  21. end.
  22.  

Compiled with fpc 3.2.2
run in the debugger
assembler shows it is inlined.

So, I can not reproduce

Win 10 - 64bit

Code: ASM  [Select][+][-]
  1. C:\Users\martin\AppData\Local\Temp\project1.lpr:19  a := TMirList.Create;
  2. 00000001000015FE B801000000               mov eax,$00000001
  3. 0000000100001603 488D0DF6E90000           lea rcx,[rip+$0000E9F6]
  4. 000000010000160A 4889C2                   mov rdx,rax
  5. 000000010000160D E8FE2B0000               call +$00002BFE    # $0000000100004210 SYSTEM$_$TOBJECT_$__$$_CREATE$$TOBJECT
  6. 0000000100001612 488905F7290100           mov [rip+$000129F7],rax
  7. C:\Users\martin\AppData\Local\Temp\project1.lpr:20  a.ClearInsertMode;
  8. 0000000100001619 C74008FFFFFFFF           mov [rax+$08],$FFFFFFFF
  9. C:\Users\martin\AppData\Local\Temp\project1.lpr:21  end.
  10. 0000000100001620 E8BB400000               call +$000040BB    # $00000001000056E0 FPC_DO_EXIT
  11. 0000000100001625 90                       nop
  12. 0000000100001626 488D6500                 lea rsp,[rbp+$00]
  13. 000000010000162A 5D                       pop rbp
  14. 000000010000162B C3                       ret
  15.  

Edson

  • Hero Member
  • *****
  • Posts: 1295
Re: Why cannot this be inlined?
« Reply #6 on: November 28, 2023, 04:11:27 pm »
OK. It's some crazy.

I've simplified my unit. This is the version where I get a NOT INLINED procedure ClearInsertMode():

Code: Pascal  [Select][+][-]
  1. unit MirList;
  2. {$mode ObjFPC}{$H+}
  3. interface
  4. uses
  5.   Classes, SysUtils;
  6. type
  7.   TMirList = class
  8.   public
  9.     insPoint: Integer;      //Insert point for instructions
  10.     procedure ConvertBody;
  11.     procedure ClearInsertMode; inline;
  12.   end;
  13.  
  14. implementation
  15. procedure TMirList.ConvertBody;
  16. begin
  17.     ClearInsertMode;
  18. end;
  19. procedure TMirList.ClearInsertMode;
  20. begin
  21.   insPoint := -1;
  22. end;
  23.  
  24. end.
  25.  

But if I implement first ClearInsertMode(), it's INLINED  :o:

Code: Pascal  [Select][+][-]
  1. unit MirList;
  2. {$mode ObjFPC}{$H+}
  3. interface
  4. uses
  5.   Classes, SysUtils;
  6. type
  7.   TMirList = class
  8.   public
  9.     insPoint: Integer;      //Insert point for instructions
  10.     procedure ConvertBody;
  11.     procedure ClearInsertMode; inline;
  12.   end;
  13.  
  14. implementation
  15. procedure TMirList.ClearInsertMode;
  16. begin
  17.   insPoint := -1;
  18. end;
  19. procedure TMirList.ConvertBody;
  20. begin
  21.     ClearInsertMode;
  22. end;
  23.  
  24. end.
  25.  

Can someone explain this?
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9682
  • Debugger - SynEdit - and more
    • wiki
Re: Why cannot this be inlined?
« Reply #7 on: November 28, 2023, 05:13:42 pm »
Ah, yes. afaik, it can only be inlined, if the body has already been compiled.

Not tested, but
- the order of the declaration in the class should not matter
- the order of the code occurring in the source does matter. Only code that has been parsed, can be inlined into other code.

Edson

  • Hero Member
  • *****
  • Posts: 1295
Re: Why cannot this be inlined?
« Reply #8 on: November 28, 2023, 05:27:19 pm »
Ah, yes. afaik, it can only be inlined, if the body has already been compiled.

Not tested, but
- the order of the declaration in the class should not matter
- the order of the code occurring in the source does matter. Only code that has been parsed, can be inlined into other code.

I understand. If this is a compiler limitation, it's OK. Although it should be documented. I don't remember to have seen this in some Free Pascal document.  :-\

Maybe the LCL code should consider this limitation, too.
Lazarus 2.2.6 - FPC 3.2.2 - x86_64-win64 on Windows 10

PascalDragon

  • Hero Member
  • *****
  • Posts: 5424
  • Compiler Developer
Re: Why cannot this be inlined?
« Reply #9 on: November 28, 2023, 09:16:30 pm »
Ah, yes. afaik, it can only be inlined, if the body has already been compiled.

Not tested, but
- the order of the declaration in the class should not matter
- the order of the code occurring in the source does matter. Only code that has been parsed, can be inlined into other code.

I understand. If this is a compiler limitation, it's OK.

Yes, that is indeed the case. Inside the same unit the body needs to be available for inlining. Across units it usually works (exceptions might occur in cycle situations - and of course there might be other reasons why code isn't inlined). The same also holds true for generics:

Code: Pascal  [Select][+][-]
  1. unit uinline;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. generic function Test<T>: SizeInt; inline;
  8.  
  9. function Foo: SizeInt;
  10. function Bar: SizeInt;
  11.  
  12. implementation
  13.  
  14. function Foo: SizeInt;
  15. begin
  16.   // this won't be inlined
  17.   Result := specialize Test<LongInt>();
  18. end;
  19.  
  20. generic function Test<T>: SizeInt; inline;
  21. begin
  22.   Result := SizeOf(T);
  23. end;
  24.  
  25. function Bar: SizeInt;
  26. begin
  27.   // this will be inlined
  28.   Result := specialize Test<LongInt>();
  29. end;
  30.  
  31. end.

Although it should be documented. I don't remember to have seen this in some Free Pascal document.  :-\

It's an implementation detail and we tend not to document these by choice as users should not rely on them (though to be fair: this specific restriction is very unlikely to change, considering we won't turn the parser into a multi pass one 😅)

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9682
  • Debugger - SynEdit - and more
    • wiki
Re: Why cannot this be inlined?
« Reply #10 on: November 28, 2023, 09:35:27 pm »
It's an implementation detail and we tend not to document these by choice as users should not rely on them (though to be fair: this specific restriction is very unlikely to change, considering we won't turn the parser into a multi pass one 😅)

I don't think "implementation detail" should be a reason for this case not to be documented.

After all the "implementation detail" is that is hasn't been implemented. (regardless of whether it ever will).
However, missing implementation are not something to be relied on anyway. If it said "not implemented yet" no user can relay on "I can write inline here, as it will be ignored", as the "yet" says that this can change.

ASerge

  • Hero Member
  • *****
  • Posts: 2196
Re: Why cannot this be inlined?
« Reply #11 on: November 28, 2023, 09:37:57 pm »
Although it should be documented.
+1
Like in Delphi. From Using the inline Directive: Within a unit, the body for an inline function should be defined before calls to the function are made. Otherwise, the body of the function, which is not known to the compiler when it reaches the call site, cannot be expanded inline.

Stefan Glienke

  • New Member
  • *
  • Posts: 23
Re: Why cannot this be inlined?
« Reply #12 on: November 29, 2023, 05:05:12 pm »
At least FPC has a hint that it's not inlined - Delphi is just totally silent about it %)
Maybe FPC could add the reason to the hint.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5424
  • Compiler Developer
Re: Why cannot this be inlined?
« Reply #13 on: December 01, 2023, 10:29:01 pm »
It's an implementation detail and we tend not to document these by choice as users should not rely on them (though to be fair: this specific restriction is very unlikely to change, considering we won't turn the parser into a multi pass one 😅)

I don't think "implementation detail" should be a reason for this case not to be documented.

After all the "implementation detail" is that is hasn't been implemented. (regardless of whether it ever will).
However, missing implementation are not something to be relied on anyway. If it said "not implemented yet" no user can relay on "I can write inline here, as it will be ignored", as the "yet" says that this can change.

Well, then someone file a documentation bug... 🤷‍♀️

At least FPC has a hint that it's not inlined - Delphi is just totally silent about it %)

Though it can lead to quite some noise as well... 🤪

Maybe FPC could add the reason to the hint.

Considering that FPC prints the reason for many others already, that's fair. Fell free to file a bug report. :)

 

TinyPortal © 2005-2018