View Issue Details
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0023862||FPC||Compiler||public||2013-02-08 21:17||2015-12-09 16:33|
|Reporter||Maciej Izak||Assigned To||Florian|
|Fixed in Version||3.0.1|
|Summary||0023862: Inline and "Procedure too complex, it requires too many registers"|
|Description||Can't compile attached unit (in Delphi it's ok).|
When I delete all inline keywords from unit it's compile.
|Tags||No tags attached.|
|Fixed in Revision||32621|
Cromis.AnyValue.pas.zip (5,375 bytes)
||If you open the cpu window in Delphi, is it actually inlined? Because inline is a compiler hint, i.e. try to inline in Delphi - but do not if it is not possible -, not a compiler *must* inline.|
Yes, for example any function in function TAnyValue.Equal in "case FValueType of" is inlined (checked in Delphi XE2 cpu window). In FPC i have fatal error for the same function (TAnyValue.Equal):
Cromis.AnyValue.pas(947,44) Fatal: Procedure too complex, it requires too many registers
Even without any inline 2.6.2 dies on macosx with long procedure .... eg:
AList := TStringList.Create;
with AList do
Add('Some string .......');
Add('Some string .......xxxxxxxxxxxxxxxxxxxxxxxx');
Add('Some string .......yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy');
// ...etc... cca 10000 Add() with const string
// 2.6.2 on mac intel stops with "Fatal: Procedure too complex, it requires too many registers" ... while linux/win works fine
That's because on Mac OS X, by default PIC code is generated (because it's required by shared libraries, and since OS X 10.9 also for executables submitted to the AppStore), which requires more registers. If you make it even more complex or compile explicitly with -Cg, it will also fail on Linux.
In general, the only thing you can do is split up the procedure, it's a limitation of the register allocator. The upper limit for virtual registers may be increased in the future at some point, but it's not considered to be a critical problem.
||I did it already (splitted proc) ... thanks.|
This issue is a classic case of "entity expansion attack". With every procedure declared as inline and calling several other inlined procedures, the total amount of inline expansions grows exponentially and any complexity limit can be reached very fast.
The compiler needs mechanism to limit the inlining depth/complexity.
||Actually, I though it limits inline expansion depth?|
||Only for recursive calls: an inlined routine cannot get inlined again into itself.|
In response to Florian's question:
I couldn't find any code responsible for that.
(At first, I thought it enters an infinite loop with A inlining B and B inlining A. But fortunately forward calls to functions from same unit are not inlined, because compiler had not yet parsed body of following functions. However it can be possible to trigger infinite recursion scenario using two units.)
> However it can be possible to trigger infinite recursion scenario using two units.
No, because the po_inline flag is unset while processing the inlined body of a function (ncal.pas:4130)
Maybe make a limit on expansion depth weighted? IOW from a non-inlined procedure you can only inline a certain weight, and if that is reached (directly or indirectly) it stops inlining?
The advantage would be that cheap (oneliner) functions are treated more leniently, while they probably yield the largest benefit, and the least register pressure?
And if a complex procedure does match the left over weight, some simple procedure might still be inlined.
||Florian has been working on an auto-inlining patch from time to time. That's unrelated to this problem though, and even with limitations you could still easily run into the limit of the register allocator.|
||I have implemented a patch which prevents limits code grow by inline expansion which fixes this example. Nevertheless, one can still construct examples which trigger again the "procedure too complex error", but it should be harder which "real world"-code now.|
|2013-02-08 21:17||Maciej Izak||New Issue|
|2013-02-08 21:17||Maciej Izak||File Added: Cromis.AnyValue.pas.zip|
|2013-02-09 12:54||Thaddy de Koning||Note Added: 0065593|
|2013-02-10 11:23||Maciej Izak||Note Added: 0065615|
|2014-01-14 17:25||Zeljan Rikalo||Note Added: 0072430|
|2014-01-14 20:04||Jonas Maebe||Note Added: 0072434|
|2014-01-14 20:04||Jonas Maebe||Priority||high => normal|
|2014-01-14 20:04||Jonas Maebe||Severity||crash => feature|
|2014-01-19 11:42||Zeljan Rikalo||Note Added: 0072537|
|2014-01-19 14:35||Sergei Gorelkin||Note Added: 0072541|
|2014-01-19 16:15||Florian||Note Added: 0072545|
|2014-01-19 17:23||Jonas Maebe||Note Added: 0072548|
|2014-01-19 17:33||Sergei Gorelkin||Note Added: 0072549|
|2014-01-19 17:34||Sergei Gorelkin||Note Edited: 0072549||View Revisions|
|2014-01-19 17:40||Jonas Maebe||Note Added: 0072550|
|2015-01-06 21:56||Sven Barth||Relationship added||related to 0027206|
|2015-01-13 16:01||Marco van de Voort||Note Added: 0080348|
|2015-01-13 18:08||Jonas Maebe||Note Added: 0080356|
|2015-01-17 14:55||Florian||Fixed in Revision||=> 29495|
|2015-01-17 14:55||Florian||Note Added: 0080468|
|2015-01-17 14:55||Florian||Status||new => resolved|
|2015-01-17 14:55||Florian||Fixed in Version||=> 3.1.1|
|2015-01-17 14:55||Florian||Resolution||open => fixed|
|2015-01-17 14:55||Florian||Assigned To||=> Florian|
|2015-12-09 16:33||Jonas Maebe||Fixed in Revision||29495 => 32621|
|2015-12-09 16:33||Jonas Maebe||Fixed in Version||3.1.1 => 3.0.1|