Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2019-03

Re: Successful implementation of inline support forpure assembler routines on x86

16 messages · 9 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

J. Gareth Moreton

Of course, my worry now is that we've submitted so many patches and issues that we'll just be building an ever-growing back-log that may never be cleared.  It also depends on what Florian's own vision for the future of Free Pascal is, I think.
Gareth aka. Kit

On Sat 16/03/19 17:05 , "J. Gareth Moreton"
sent:
Normally Florian or another administrator will say if the patch has been applied and mark the ticket as "resolved" if they're happy.  Once they are added I will have a play around.

Admittedly one thing I'm also waiting on is the node XML dump feature ( https://bugs.freepascal.org/view.php?id=35017 ), since that will allow me to see how procedures are constructed at the intermediate level, plus I sort of need it in order to work out what's going on with https://bugs.freepascal.org/view.php?id=32913 , something I've vowed to fix but has been sitting dormant for ages now - I'll have to apologise to David Hawk (the reporter) afterwards.

Gareth aka. Kit

On Sat 16/03/19 16:07 , Ryan Joseph sent:

On Mar 15, 2019, at 9:37 AM, Sven Barth via fpc-devel wrote: That could maybe be managed once the support for constants as parameter

for generics is added (note: I don't know right now how SHUFPS works, so take the following as pseudo code):

I fixed the patch for constants in generics and uploaded again (https://bugs.freepascal.org/view.php?id=35140). [2] Not sure if they added it yet or they were waiting for my to fix things. Let me know if that patch is in the correct format so I can fix the other one for multi-helpers.

Regards,
Ryan Joseph

Reply by email

Ben Grasset
to J. Gareth Moreton

Inlining of pure assembler functions would actually be immediately, specifically useful to me! I've been having a go at improving FPC scores on "BenchmarksGames", and was so far successful with Binary Trees simple by throwing a really good threading library at it, however, there are some benchmarks that simply can't be fixed without either proper intrinsics or user-specifiable inlinable ASM methods. I have a working re-implmentation of NBody (that is just a direct rewrite of the Rust implementation) where I've implemented __m128 and __m128d as records with static nostackframe assembler "class functions", however it's just not fast enough to be competitive due to the inability to inline any of the assembler methods.

Something along these lines is absolutely sorely lacking in FPC currently, don't let anyone tell you otherwise.

PM J. Gareth Moreton wrote 63 lines
Of course, my worry now is that we've submitted so many patches and issues that we'll just be building an ever-growing back-log that may never be cleared. It also depends on what Florian's own vision for the future of Free Pascal is, I think. Gareth aka. Kit On Sat 16/03/19 17:05 , "J. Gareth Moreton" sent: Normally Florian or another administrator will say if the patch has been applied and mark the ticket as "resolved" if they're happy. Once they are added I will have a play around. Admittedly one thing I'm also waiting on is the node XML dump feature ( https://bugs.freepascal.org/view.php?id=35017 ), since that will allow me to see how procedures are constructed at the intermediate level, plus I sort of need it in order to work out what's going on with https://bugs.freepascal.org/view.php?id=32913 , something I've vowed to fix but has been sitting dormant for ages now - I'll have to apologise to David Hawk (the reporter) afterwards. Gareth aka. Kit On Sat 16/03/19 16:07 , Ryan Joseph sent: > On Mar 15, 2019, at 9:37 AM, Sven Barth via fpc-devel < > wrote: > > That could maybe be managed once the support for constants as parameter for generics is added (note: I don't know right now how SHUFPS works, so take the following as pseudo code): > I fixed the patch for constants in generics and uploaded again ( https://bugs.freepascal.org/view.php?id=35140). Not sure if they added it yet or they were waiting for my to fix things. Let me know if that patch is in the correct format so I can fix the other one for multi-helpers. Regards, Ryan Joseph _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel"> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel"> http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel _______________________________________________ fpc-devel maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply by email

Ryan Joseph
to Ben Grasset
Show quoted text 5 lines
On Mar 16, 2019, at 9:54 PM, Ben Grasset wrote: Inlining of pure assembler functions would actually be immediately, specifically useful to me! I've been having a go at improving FPC scores on "BenchmarksGames", and was so far successful with Binary Trees simple by throwing a really good threading library at it, however, there are some benchmarks that simply can't be fixed without either proper intrinsics or user-specifiable inlinable ASM methods. I have a working re-implmentation of NBody (that is just a direct rewrite of the Rust implementation) where I've implemented __m128 and __m128d as records with static nostackframe assembler "class functions", however it's just not fast enough to be competitive due to the inability to inline any of the assembler methods. Something along these lines is absolutely sorely lacking in FPC currently, don't let anyone tell you otherwise.

Sounds exciting progress for FPC. Btw what happened to the development of “pure” function modifier that would make it possible to use functions in compile time expressions? I was pretty excited about what could be done with that also.

Regards,
Ryan Joseph

Reply by email

J. Gareth Moreton

Hi Florian,

I think the main thing is that Object
Pascal has always supported the ability to
drop into assembly language, unlike C++
which requires a dialect-specific
extension and is not allowed at all under
Microsoft Visual C++ 64-bit. Allowing
certain assembler routines to be inlined
seems like a logical extension using
language semantics that already exist.

Part of it may be preference but I think
some people like the fine degree of
control that assembly language offers,
while intrinsics, I find, can quickly get
somewhat untidy and confusing, especially
with instructions like CPUID that read and
write to specific registers (although my
code forbids that instruction because EBX
is non-volatile).

The other thing... no matter how good the
compiler is, there are some situations
where assembly language will always
perform better.

I suppose I would like to ask the
community more than anything. Is this a
feature that you'd like to see and use?

I hoped the way that I designed the patch
helps to alleviate the can of worms by
simply not allowing inline if the platform
doesn't have the ability to support it
yet. "CanInline" simply returns False
unless overridden.

Gareth aka. Kit

On Sun 17/03/19 17:37 , Florian Klämpfl
sent:

Am 15.03.19 um 11:32 schrieb J. Gareth

Moreton:

Show quoted text 6 lines
> Hi everyone, > > Sorry for the slightly long-winded

title. 

This was something I've been > working on for the past few weeks to

allow the

use of "inline" with > certain x86 assembler routines,

subject to

restrictions for safety > reasons.  I have successfully written

code

that implements this support > while ensuring it doesn't affect

platforms with

a different assembly > languages.  Regression tests have

passed,

as have some new tests, some > of which test modified RTL assembler

functions

Show quoted text 6 lines
that have had "inline" > added to their list of directives. As said before, I have a lot of concerns

against this as it opens a can

of worms hard to handle: * using inline assembler is always the

worst solution to do something,

Show quoted text 8 lines
it normally means either: - the compiler misses a certain feature - the compiler is generating bad code * intrinsics provide a much better way

to achieve exactly what this

approach aims at: - they enable the use of all registers

as the compiler does register

allocation - constraints on instructions (like a

certain operand needs to be a

Show quoted text 6 lines
constant) can be easily forced Is there any advantage over intrinsics,

I missed so far?

Show quoted text 6 lines
> > There was a previous concern about it

breaking

too much and it being > impossible to implement on all

platforms - the

latter point may be true, > but through the careful use of virtual

methods

within the compiler, it > should be possible to extend the

inline support

Show quoted text 6 lines
one platform at a time > without breaking the others. > > All the details, including a PDF

specification

that explains the design > and implementation choices, a couple

of existing

tests that touch on the > functionality, along with any new

tests,

limitations or problems found, > can be found here:

https://bugs.freepascal.org/view.php?
id=35232

> > I hope it proves to be insightful and

useful.

Show quoted text 8 lines
> > Gareth aka. Kit > > P.S. Though this feature can be used

for

implementing intrinsics, it is > not a direct replacement for them

because

instructions like SHUFPS > cannot be flexibly encoded due to its

immediate

operand (i.e. it has to > be a raw number... it can't take its

value from

a parameter). Well, with intrinsics, this can be

easily done.

Reply by email

J. Gareth Moreton

The other thing is that it will benefit
cross-platform functions like Trunc and
ReadWriteBarrier better, which call only
single assembler commands. One can simply
add "inline" to their platform-specific
implementation for the performance gains
without having to rewrite the routines to
use intrinsics.

Gareth aka. Kit

Reply by email

Florian Klämpfl
to Ben Grasset: Inlining of pure assembler functions would actually be immediately, specifically useful…
Ben Grasset wrote 14 lines
Inlining of pure assembler functions would actually be immediately, specifically useful to me! I've been having a go at improving FPC scores on "BenchmarksGames", and was so far successful with Binary Trees simple by throwing a really good threading library at it, however, there are some benchmarks that simply can't be fixed without either proper intrinsics or user-specifiable inlinable ASM methods. I have a working re-implmentation of NBody (that is just a direct rewrite of the Rust implementation) where I've implemented __m128 and __m128d as records with static nostackframe assembler "class functions", however it's just not fast enough to be competitive due to the inability to inline any of the assembler methods. Something along these lines is absolutely sorely lacking in FPC currently, don't let anyone tell you otherwise.

How is it better than intrinsics support (similiar to gcc/icc etc.)?

Reply by email

Jonas Maebe
to J. Gareth Moreton: I think the main thing is that Object Pascal has always supported the ability to drop…
J. Gareth Moreton wrote
Part of it may be preference but I think some people like the fine degree of control that assembly language offers,

That is absolutely correct. That is both its strength and its weakness. The weakness is that it is impossible to integrate such code safely in compiler-generated code without the programmer saying exactly what that code does (in terms of constraints, like GCC supports: https://gcc.gnu.org/onlinedocs/gcc/Constraints.html ).

E.g., at least the following issues exist with your patch, but that's not because your code is of bad quality. It's simply that it is impossible to fully analyse inline assembly and determine it to be safe:
* you forbid modifying the stack, but loading the stack pointer in another register and then modifying the stack through this other register is not caught (or e.g. loading a value from memory that happens to point to the stack)
* you skip over db/dw/dd/dq directives, even though these can also be used to encode instructions (often ones not (yet) supported by the compiler). There may be more assembler directives like that that could influence the code.

Additionally, your remark regarding memory barriers is a bit dangerous: these instructions must not only act as memory barriers to the processor, but also to the compiler. I.e., the compiler must not be allowed to optimise certain things across such a barrier (e.g. (re)move memory reads or writes), because then the barrier will no longer serve its purpose. That is the main reason why marking them as "they change everything" should probably stay for the foreseeable future.

The performance overhead of memory barriers is also many times greater than that of a call/return, so I don't think it will actually matter that much (although it would still be better to get rid of the call/return than not, of course -- provided the compiler can be told to not optimise anything across it).

I thought I sent a mail in the previous thread about this, but I can't find it anymore so maybe I did not. What I though I said before, is that I think that inlining pure assembler functions is something that should never be done. A pure assembler function, especially with "nostackframe", is the programmer literally telling the compiler "you have absolutely no business messing with this code".

On the other hand, if you have a regular function with an inline assembler block, then inlining becomes a whole lot more feasible. Especially if you add support for GCC-like constraints. Then there is no issue with the assembler code expecting arguments in certain registers, possibly returning in the middle of the block, messing up the stack etc, because you simply cannot do that in this scenario. This means you don't have to (try) to check for this either. And there is already rudimentary support for specifying constraints in this case (which registers get modified).

It would be much less of a quick win (e.g. because the compiler does not support passing variables in registers to assembler blocks right now), but in the long run it would be fully supportable and much more maintainable. It would also require much less target-specific support, because it would not require trying to figure out what the assembler block is doing.

That said: for optimal performance, you will usually still want intrinsics rather than inline assembly, simply because the compiler can then be taught to reason about them, and perform constant propagation through them (and potentially eliminate them altogether).

Jonas

Reply by email

Florian Klämpfl
to J. Gareth Moreton: I think the main thing is that Object Pascal has always supported the ability to drop…
J. Gareth Moreton wrote 8 lines
Hi Florian, I think the main thing is that Object Pascal has always supported the ability to drop into assembly language, unlike C++ which requires a dialect-specific extension and is not allowed at all under Microsoft Visual C++ 64-bit.

Actually, the policy of FPC is to avoid assembler as much as possible.

Show quoted text 10 lines
Allowing certain assembler routines to be inlined seems like a logical extension using language semantics that already exist. Part of it may be preference but I think some people like the fine degree of control that assembly language offers, while intrinsics > I find, can quickly get somewhat untidy and confusing,

I cannot see how inlining does this better?

Show quoted text 5 lines
especially with instructions like CPUID that read and write to specific registers (although my code forbids that instruction because EBX is non-volatile).

CPUID is that slow, it makes no sense to inline it.

Show quoted text 5 lines
The other thing... no matter how good the compiler is, there are some situations where assembly language will always perform better.

I doubt this as inline assembler pure routines use always fixed registers. Intrinsics don't do so.

Show quoted text 10 lines
I suppose I would like to ask the community more than anything. Is this a feature that you'd like to see and use? I hoped the way that I designed the patch helps to alleviate the can of worms by simply not allowing inline if the platform doesn't have the ability to support it yet.

But this is exactly the can of worms. People will request that we support it on all CPUs.

"CanInline" simply returns False unless overridden.

Reply by email

Martok
to Florian Klämpfl: How is it better than intrinsics support (similiar to gcc/icc etc.)?
Florian Klämpfl wrote
How is it better than intrinsics support (similiar to gcc/icc etc.)?

It *exists*?

Remember how long it took to get PopCnt support? How about the rest of the BMI? TBM? AES-NI? Newer AVX?

Implementation of these would be a lot easier if they could be written in code, even with native-pascal implementations as drop-ins for platforms that don't support them (or use other mnemonics). Those would just be normal declarations (in the RTL or in a package), and not require modifying the compiler for all platforms.
It would also be easier to maintain, as there wouldn't be the need for new inlinennodes or entirely different node types.

Regards, Martok

Reply by email

Sven Barth
to Martok

Martok schrieb am So., 17. März 2019, 21:47:

Show quoted text 8 lines
Am 17.03.2019 um 18:57 schrieb Florian Klämpfl: > How is it better than intrinsics support (similiar to gcc/icc etc.)? It *exists*? Remember how long it took to get PopCnt support? How about the rest of the BMI? TBM? AES-NI? Newer AVX?

You are aware that the assembler reader needs to support new instructions as well?

Regards,
Sven

Reply by email

Florian Klämpfl
to Martok: It *exists*? Remember how long it took to get PopCnt support? How about the rest of the…
Martok wrote 5 lines
Am 17.03.2019 um 18:57 schrieb Florian Klämpfl: > How is it better than intrinsics support (similiar to gcc/icc etc.)? It *exists*? Remember how long it took to get PopCnt support?

PopCnt is not really an intrinsic as it has a fallback counter part and works on all platforms. Intrinsic means that it is really mapped directly to the CPU instruction without any fallbacks.

As the branch of Jeppe shows, it is pretty easy, just requires some continuous work.

How about the rest of the BMI? > TBM? AES-NI? Newer AVX?

See above.

Reply by email

Jeppe Johansen
to Florian Klämpfl
Florian Klämpfl wrote 13 lines
Am 17.03.19 um 21:47 schrieb Martok: > Am 17.03.2019 um 18:57 schrieb Florian Klämpfl: >> How is it better than intrinsics support (similiar to gcc/icc etc.)? > It *exists*? > > Remember how long it took to get PopCnt support? PopCnt is not really an intrinsic as it has a fallback counter part and works on all platforms. Intrinsic means that it is really mapped directly to the CPU instruction without any fallbacks. As the branch of Jeppe shows, it is pretty easy, just requires some continuous work.

As far as I recall there were some issues in argument passing and handling the new __mm128 type that were a little annoying. But it might be fairly easy to bring back up to speed again.

> How about the rest of the BMI? > TBM? AES-NI? Newer AVX? See above.

Agreed if that's the route that's taken. It's my feeling that the newer extensions took a much more regular/orthogonal route than the old weird MMX and SSE forms.

Reply by email

Ben Grasset
to Florian Klämpfl: How is it better than intrinsics support (similiar to gcc/icc etc.)?
PM Florian Klämpfl wrote
How is it better than intrinsics support (similiar to gcc/icc etc.)?

Well, it wouldn't be better than a literal equivalent to those intriniscs, if that's what we're talking about. By which I mean, like, say how in Clang/GCC (or languages such as Rust that use LLVM), if you do _mm_loadl_pd or whatever, that translates not to a function call but directly to the "inlined" assembler instructions (at least in release builds.)

Reply by email

Ben Grasset
to Ben Grasset

To clarify my last message, what I had said previously was that FPC needs *either* inlinable assembler *or* intrinsics. Just basically something that ultimately can be called as a "normal" Pascal function, but that does *not* end up as an un-inlined function call. I have no preference as to how exactly it's done under the hood.

Reply by email

Marco van de Voort
to Florian Klämpfl: How is it better than intrinsics support (similiar to gcc/icc etc.)?

Op 3/17/2019 om 6:57 PM schreef Florian Klämpfl:

Show quoted text 6 lines
> Something along these lines is absolutely sorely lacking in FPC > currently, don't let anyone tell you otherwise. How is it better than intrinsics support (similiar to gcc/icc etc.)?

Intrinsics are common, and prepared for you by compiler devels.

 Inline assembler blocks are something you use as a programmer as soon as you reuse a block a lot. An example in the embedded world, e.g. as an interrupt prologue or epilogue, things like prologues for SPI operations (lowering/raising CS and enabling/disabling interrupts, padded with appropriate nops if needed if the slave is slower than the master).

Example PIC32MK/MZ (=MIPS) which has quite some different interrupt prologues depending on what features are used (shadow registers and the like).

These are now gcc inline macros.

Reply by email

Florian Klämpfl
to Ben Grasset: On Sun, Mar 17, 2019 at 1:57 PM Florian Klämpfl Well, it wouldn't be better than a…
Ben Grasset wrote 9 lines
On Sun, Mar 17, 2019 at 1:57 PM Florian Klämpfl < > wrote: How is it better than intrinsics support (similiar to gcc/icc etc.)? Well, it wouldn't be better than a literal equivalent to those intriniscs, if that's what we're talking about. By which I mean, like, say how in Clang/GCC (or languages such as Rust that use LLVM), if you do _mm_loadl_pd or whatever, that translates not to a function call but directly to the "inlined" assembler instructions (at least in release builds.) 

Yes, that's what I mean. So far I have not seen a single advantage of inlining pure assembler routines over such intrinsics.

Reply by email