inlining functions depending on implementation only functions

classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

inlining functions depending on implementation only functions

Benito van der Zander
Hi,

after updating from fpc 3.1 to fpc 3.3, I am getting a lot of "function
was not inlined" warnings, e.g. when an inline function depends on a
function not declared in the interface part like:

unit inlinetest;

{$mode objfpc}{$H+}

interface

uses
   Classes, SysUtils;


function strContains(const str, searched: string): boolean; inline;

implementation

function strContainsFrom(const str, searched: string; from: SizeInt):
boolean;
begin
   result:=Pos(searched, str, from) > 0;
end;


function strContains(const str, searched: string): boolean; inline;
begin
   result := strContainsFrom(str, searched, 1);
end;

end.



Is that supposed to happen?

Fpc 3.1 did not show any warning in this case (although now that I
investigate it, fpc 3.1 also did not seem to inline it despite not
showing the warning)


Bye,

Benito


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions depending on implementation only functions

wkitty42
On 12/29/18 9:16 AM, Benito van der Zander wrote:
> Fpc 3.1 did not show any warning in this case (although now that I
> investigate it, fpc 3.1 also did not seem to inline it despite not showing
> the warning)

i think, but am not totally sure, that the addition of the warning is a recently
added feature specifically to note that a function was not inlined for some
reason...


--
  NOTE: No off-list assistance is given without prior approval.
        *Please keep mailing list traffic on the list unless*
        *a signed and pre-paid contract is in effect with us.*
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions depending on implementation only functions

Free Pascal - General mailing list
In reply to this post by Benito van der Zander
Am Sa., 29. Dez. 2018, 15:23 hat Benito van der Zander <[hidden email]> geschrieben:
Hi,

after updating from fpc 3.1 to fpc 3.3, I am getting a lot of "function
was not inlined" warnings, e.g. when an inline function depends on a
function not declared in the interface part like:

unit inlinetest;

{$mode objfpc}{$H+}

interface

uses
   Classes, SysUtils;


function strContains(const str, searched: string): boolean; inline;

implementation

function strContainsFrom(const str, searched: string; from: SizeInt):
boolean;
begin
   result:=Pos(searched, str, from) > 0;
end;


function strContains(const str, searched: string): boolean; inline;
begin
   result := strContainsFrom(str, searched, 1);
end;

end.



Is that supposed to happen?

Fpc 3.1 did not show any warning in this case (although now that I
investigate it, fpc 3.1 also did not seem to inline it despite not
showing the warning)

Correct. FPC 3.1.1 did neither warn nor inline in this case, 3.3.1 at least warns (I think 3.2 already warns as well). 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Benito van der Zander
Hi,

and why is it not inlining the count and append call of this string builder? It is not using any implementation only function

unit inlinetest;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils,math;

type TStrBuilder = object
protected
  next, bufferend: pchar; //next empty pchar and first pos after the string
public
  buffer: pstring;
  procedure init(abuffer:pstring);
  procedure final;
  function count: SizeInt; inline;
  procedure reserveadd(delta: SizeInt);
  procedure append(c: char); inline;
  procedure append(const s: RawByteString); inline;
  procedure append(const p: pchar; const l: SizeInt); inline;
end;

implementation

procedure TStrBuilder.init(abuffer: pstring);
begin
  buffer := abuffer;
  SetLength(buffer^, 16); //use setlength to create a new string

  next := pchar(buffer^);
  bufferend := next + length(buffer^);
end;

procedure TStrBuilder.final;
begin
  if next <> bufferend then begin
    setlength(buffer^, count);  // !!! Note: Call to subroutine "function TStrBuilder.count:Int64;" marked as inline is not inlined
    next := pchar(buffer^) + length(buffer^);
    bufferend := next;
  end;
end;

function TStrBuilder.count: SizeInt;
begin
  result := next - pointer(buffer^);
end;


procedure TStrBuilder.reserveadd(delta: SizeInt);
var
  oldlen: SizeInt;
begin
  if next + delta > bufferend then begin
    oldlen := count;
    SetLength(buffer^, max(2*length(buffer^), oldlen + delta));
    next := pchar(buffer^) + oldlen;
    bufferend := pchar(buffer^) + length(buffer^);
  end;
end;

procedure TStrBuilder.append(c: char);
begin
  if next >= bufferend then reserveadd(1);
  next^ := c;
  inc(next);
end;

procedure TStrBuilder.append(const s: RawByteString);
begin
  append(pchar(pointer(s)), length(s)); // !!! inlinetest.pas(71,3) Note: Call to subroutine "procedure TStrBuilder.append(const p:PChar;const l:Int64);" marked as inline is not inlined
end;



procedure TStrBuilder.append(const p: pchar; const l: SizeInt); inline;
begin
  if l <= 0 then exit;
  if next + l > bufferend then reserveadd(l);
  move(p^, next^, l);
  inc(next, l);
end;



end.



Bye,
Benito 

Am 29.12.18 um 20:31 schrieb Sven Barth via fpc-pascal:
Am Sa., 29. Dez. 2018, 15:23 hat Benito van der Zander <[hidden email]> geschrieben:
Hi,

after updating from fpc 3.1 to fpc 3.3, I am getting a lot of "function
was not inlined" warnings, e.g. when an inline function depends on a
function not declared in the interface part like:

unit inlinetest;

{$mode objfpc}{$H+}

interface

uses
   Classes, SysUtils;


function strContains(const str, searched: string): boolean; inline;

implementation

function strContainsFrom(const str, searched: string; from: SizeInt):
boolean;
begin
   result:=Pos(searched, str, from) > 0;
end;


function strContains(const str, searched: string): boolean; inline;
begin
   result := strContainsFrom(str, searched, 1);
end;

end.



Is that supposed to happen?

Fpc 3.1 did not show any warning in this case (although now that I
investigate it, fpc 3.1 also did not seem to inline it despite not
showing the warning)

Correct. FPC 3.1.1 did neither warn nor inline in this case, 3.3.1 at least warns (I think 3.2 already warns as well). 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Jonas Maebe-3
On 01/01/19 22:38, Benito van der Zander wrote:
> and why is it not inlining the count and append call of this string
> builder? It is not using any implementation only function

Routines can only be inlined if they are called after their
implementation has been parsed. FPC compiles everything in a single pass.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Benito van der Zander
Hi Jonas,

That is reasonable. Perhaps the hint should mention that?

But why is this still not inlined::

type TStrBuilder = object
  procedure append(const p: pchar; const l: SizeInt); inline;
  procedure append(const s: RawByteString);
end;

implementation

procedure TStrBuilder.append(const p: pchar; const l: SizeInt); inline;
begin
end;


procedure TStrBuilder.append(const s: RawByteString);
begin
  append(pchar(pointer(s)), length(s)); //inlinetest.pas(24,3) Note: Call to subroutine "procedure TStrBuilder.append(const p:PChar;const l:Int64);" marked as inline is not inlined
end;



Best,
Benito 

Am 01.01.19 um 22:41 schrieb Jonas Maebe:
On 01/01/19 22:38, Benito van der Zander wrote:
and why is it not inlining the count and append call of this string builder? It is not using any implementation only function

Routines can only be inlined if they are called after their implementation has been parsed. FPC compiles everything in a single pass.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Jonas Maebe-3
On 2019-01-02 00:19, Benito van der Zander wrote:

> procedure TStrBuilder.append(const s: RawByteString);
> begin
> append(pchar(pointer(s)), length(s)); //inlinetest.pas(24,3) Note: Call
> to subroutine "procedure TStrBuilder.append(const p:PChar;const
> l:Int64);" marked as inline is not inlined
> end;

The compiler does not support inlining calls with parameters that cast a
managed type to an unmanaged type at this time.

Regarding the reason why something cannot be inlined: sometimes you can
get more information with -vd, but not always (and even then the reason
may be vague).

I would personally be in favour of removing all of the inlining hints,
because they are specific to a particular compiler version and mainly
cause people to waste time on premature optimisation (or on what they
think is an optimisation, but in many cases slows down things due to
increased instruction cache pressure). Especially in this case: there is
nothing potentially wrong with such a call, nor is the method in general
not inlinable, so there is no way to get rid of the hint other than by
removing the inline directive altogether (or by adding a version of that
routine with a different name that is not declared as "inline"). This
only promotes complicating code without any potential improvement of
productivity or clarity of the code/programmer intent.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Benito van der Zander
Hi,

procedure TStrBuilder.append(const s: RawByteString);
begin
append(pchar(pointer(s)), length(s)); //inlinetest.pas(24,3) Note: Call to subroutine "procedure TStrBuilder.append(const p:PChar;const l:Int64);" marked as inline is not inlined
end;

this seems to help

procedure TStrBuilder.append(const s: RawByteString);
var p: pchar;
begin 
   p := pchar(pointer(s));
   append(p, length(s));
end;


I would personally be in favour of removing all of the inlining hints, because they are specific to a particular compiler version and mainly cause people to waste time on premature optimisation (or on what they think is an optimisation, but in many cases slows down things due to increased instruction cache pressure).

After updating from 3.1 to 3.3 I only have to look at around one thousand hints




The one-pass thing is probably the reason it now complains about all inline functions in dependency cycles, unit A uses unit B that uses unit A. Then unit A can't inline something unit B.  Any way around that?



Best,
Benito 

Am 02.01.19 um 00:41 schrieb Jonas Maebe:
On 2019-01-02 00:19, Benito van der Zander wrote:

procedure TStrBuilder.append(const s: RawByteString);
begin
append(pchar(pointer(s)), length(s)); //inlinetest.pas(24,3) Note: Call to subroutine "procedure TStrBuilder.append(const p:PChar;const l:Int64);" marked as inline is not inlined
end;

The compiler does not support inlining calls with parameters that cast a managed type to an unmanaged type at this time.

Regarding the reason why something cannot be inlined: sometimes you can get more information with -vd, but not always (and even then the reason may be vague).

I would personally be in favour of removing all of the inlining hints, because they are specific to a particular compiler version and mainly cause people to waste time on premature optimisation (or on what they think is an optimisation, but in many cases slows down things due to increased instruction cache pressure). Especially in this case: there is nothing potentially wrong with such a call, nor is the method in general not inlinable, so there is no way to get rid of the hint other than by removing the inline directive altogether (or by adding a version of that routine with a different name that is not declared as "inline"). This only promotes complicating code without any potential improvement of productivity or clarity of the code/programmer intent.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Jonas Maebe-3
On 03/01/19 00:10, Benito van der Zander wrote:
> The one-pass thing is probably the reason it now complains about all
> inline functions in dependency cycles, unit A uses unit B that uses unit
> A. Then unit A can't inline something unit B. Any way around that?

Not besides breaking your dependency cycles.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

denisgolovan


07.01.2019, 00:45, "Jonas Maebe" <[hidden email]>:
> Not besides breaking your dependency cycles.

Sorry, but for necro-posting, but shouldn't WPO is supposed to help to inline functions as well?

--
Regards,
Denis Golovan

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Jonas Maebe-3
On 12/01/19 12:47, denisgolovan wrote:
>
> 07.01.2019, 00:45, "Jonas Maebe" <[hidden email]>:
>> Not besides breaking your dependency cycles.
>
> Sorry, but for necro-posting, but shouldn't WPO is supposed to help to inline functions as well?

Not at this time (unless you use the LLVM backend). However, what you
actually can do, is manually recompile all units of your program
multiple times. While this won't help with inline functions called
before they are parsed in those same units, it will allow inlining of
function bodies in other units that were not available during the first
compilation due to dependency cycles.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

denisgolovan


12.01.2019, 14:53, "Jonas Maebe" <[hidden email]>:
> Not at this time (unless you use the LLVM backend).

Could you give some hints on how to enable WPO/LTO under LLVM backend?
http://wiki.freepascal.org/LLVM does not seem to mention anything on that.

> However, what you actually can do, is manually recompile all units of your program
> multiple times. While this won't help with inline functions called
> before they are parsed in those same units, it will allow inlining of
> function bodies in other units that were not available during the first
> compilation due to dependency cycles.

Eh.
It does not look too practical, right?
Unless there is some magical receipt for that :)

--
Regards,
Denis Golovan

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Jonas Maebe-3
On 12/01/19 13:05, denisgolovan wrote:
>
>
> 12.01.2019, 14:53, "Jonas Maebe" <[hidden email]>:
>> Not at this time (unless you use the LLVM backend).
>
> Could you give some hints on how to enable WPO/LTO under LLVM backend?
> http://wiki.freepascal.org/LLVM does not seem to mention anything on that.

It's not yet integrated in the compiler, so you have to do it manually
right now:
a) compile everything with -al (including the RTL etc; add OPT="-a" to
the make command)
b) go in all the unit directories, and assemble the files to bitcode
using something like this:

for file in *.ll; do
   clang -emit-llvm -O -c $file
done

c) compile your program with -a -s and do the same as in step b) for
your program
d) edit the generated link.res file, and replace all references to unit
files ending in ".o" with the same files ending in ".bc"

If you used a custom clang rather than the one installed by default on
your system, you will also have to specify the path to the libLTO that
the linker should use. You have to do this on the ld command line in
ppas.sh. You can find the correct parameter by compiling a test
C-program with "clang -flto -### test.c", as this will print the ld
invocation to use. E.g. on macOS, it will be something like

-lto_library
/Volumes/imac/devel/clang+llvm-7.0.0-x86_64-apple-darwin/lib/libLTO.dylib"

On Linux, it's something like

-plugin
/home/jmaebe/local/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/../lib/LLVMgold.so
-plugin-opt=O2

(or a different optimization level)

>> However, what you actually can do, is manually recompile all units of your program
>> multiple times. While this won't help with inline functions called
>> before they are parsed in those same units, it will allow inlining of
>> function bodies in other units that were not available during the first
>> compilation due to dependency cycles.
>
> Eh.
> It does not look too practical, right?

That's why I mentioned it's a manual process.


Jonas
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Free Pascal - General mailing list
In reply to this post by denisgolovan
Am Sa., 12. Jan. 2019, 13:05 hat denisgolovan <[hidden email]> geschrieben:
> However, what you actually can do, is manually recompile all units of your program
> multiple times. While this won't help with inline functions called
> before they are parsed in those same units, it will allow inlining of
> function bodies in other units that were not available during the first
> compilation due to dependency cycles.

Eh.
It does not look too practical, right?
Unless there is some magical receipt for that :)

With WPO you need to compile at least twice as well as the first pass only collects information and acts on it in the second pass. 

Regards, 
Sven 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

denisgolovan
In reply to this post by Jonas Maebe-3


12.01.2019, 15:32, "Jonas Maebe" <[hidden email]>:

> It's not yet integrated in the compiler, so you have to do it manually
> right now:
> a) compile everything with -al (including the RTL etc; add OPT="-a" to
> the make command)
> b) go in all the unit directories, and assemble the files to bitcode
> using something like this:
>
> for file in *.ll; do
>    clang -emit-llvm -O -c $file
> done
>
> c) compile your program with -a -s and do the same as in step b) for
> your program
> d) edit the generated link.res file, and replace all references to unit
> files ending in ".o" with the same files ending in ".bc"
>
> If you used a custom clang rather than the one installed by default on
> your system, you will also have to specify the path to the libLTO that
> the linker should use. You have to do this on the ld command line in
> ppas.sh. You can find the correct parameter by compiling a test
> C-program with "clang -flto -### test.c", as this will print the ld
> invocation to use. E.g. on macOS, it will be something like
>
> -lto_library
> /Volumes/imac/devel/clang+llvm-7.0.0-x86_64-apple-darwin/lib/libLTO.dylib"
>
> On Linux, it's something like
>
> -plugin
> /home/jmaebe/local/clang+llvm-7.0.0-x86_64-linux-gnu-ubuntu-16.04/bin/../lib/LLVMgold.so
> -plugin-opt=O2
>
> (or a different optimization level)

Thanks, but I expected something like rebuilding compiler and adding some option to Lazarus project :)
So far it looks like it's not far away from manually recompilation of each and every module.

--
Regards,
Denis Golovan

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

denisgolovan
In reply to this post by Free Pascal - General mailing list
 
 
12.01.2019, 16:03, "Sven Barth via fpc-pascal" <[hidden email]>:
With WPO you need to compile at least twice as well as the first pass only collects information and acts on it in the second pass.
 
Is it possible to make Lazarus do that automatically?
According to Jonas it's a bit too unpractical.
 
-- 
Regards,
Denis Golovan
 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

Benito van der Zander
In reply to this post by denisgolovan
Hi,

 something that appears  to help is to put units in the interface uses rather than the implementation uses.

When the unit mentioned in the interface uses, uses the first unit in its implementation uses, it can inline the functions from the first unit. Although I would have expected it to do the opposite.

Unfortunately that does not help if the second unit needs to use types from the first unit in its interface
   
Best,
Benito 

Am 12.01.19 um 13:05 schrieb denisgolovan:

12.01.2019, 14:53, "Jonas Maebe" [hidden email]:
Not at this time (unless you use the LLVM backend). 
Could you give some hints on how to enable WPO/LTO under LLVM backend?
http://wiki.freepascal.org/LLVM does not seem to mention anything on that.

However, what you actually can do, is manually recompile all units of your program
multiple times. While this won't help with inline functions called
before they are parsed in those same units, it will allow inlining of
function bodies in other units that were not available during the first
compilation due to dependency cycles.
Eh.
It does not look too practical, right?
Unless there is some magical receipt for that :)


_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: inlining functions

denisgolovan
 
 
12.01.2019, 19:44, "Benito van der Zander" <[hidden email]>:
Hi,
 

 something that appears  to help is to put units in the interface uses rather than the implementation uses.

When the unit mentioned in the interface uses, uses the first unit in its implementation uses, it can inline the functions from the first unit. Although I would have expected it to do the opposite.
 
Unfortunately that does not help if the second unit needs to use types from the first unit in its interface
   
Best,
Benito
 
Yes, I got it.
The problem is that circular dependencies are too often happen in practice.
The discussion is about how to make compiler inline function automatically even when circular dependencies exist.
 
-- 
Regards,
Denis Golovan
 

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal