This is an archive of the discontinued LLVM Phabricator instance.

[SelectionDAG] Use __memcmpeq to replace bcmp and bool usage memcmp
Needs ReviewPublic

Authored by goldstein.w.n on Jun 9 2022, 5:09 PM.

Details

Summary

Add __memcmpeq as a LibCall enabled by cl::opt option:
'--with-builtin-memcmpeq'.

If enabled calls to bcmp will be replaced with memcmpeq and calls to
memcmp that only use the result as a boolean will be replaced
with
memcmpeq.

If not enabled this patch has no effect.

This is essentially a follow up to: https://reviews.llvm.org/D56593

In glibc we decided against adding an optimized bcmp:
https://marc.info/?t=163157542200002&r=1&w=3
because its not in a reserved namespace.

To get the functionality (and make the bool usage memcmp optimization
meaningful on GNU systems) we added __memcmpeq:
https://sourceware.org/git/?p=glibc.git;a=commit;h=44829b3ddb64e99e37343a0f25b2c082387d31a5

Which was release with GLIBC 2.35.

Unlike bcmp which is an alias to memcmp on all targets in GLIBC,
__memcmpeq is actually better optimized on some targets (x86 as
of writing this patch).

Diff Detail

Event Timeline

goldstein.w.n created this revision.Jun 9 2022, 5:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2022, 5:09 PM
goldstein.w.n requested review of this revision.Jun 9 2022, 5:09 PM
Herald added a project: Restricted Project. · View Herald TranscriptJun 9 2022, 5:09 PM

NB: Also have patch to add __memcmpeq as bonafide builtin in clang. Figure that's a bigger change but if people are interested LMK.

ps.
I posted this on monday to the mailing list but the moderators never approved. Is the only way to submit patches to llvm via phabricator?

goldstein.w.n added a comment.EditedJun 15 2022, 9:44 AM

It seems GCC may not end up sticking with -fextra-libc-function=. They seem to want to do this
just with just a check for if __memcmpeq is declared: https://gcc.gnu.org/pipermail/gcc-patches/2022-June/596595.html

Assuming GCC does stick with -fextra-libc-function= do you think llvm's side should keep the
--with-builtin-memcmpeq and clang should enable that if it sees '__memcmpeq' in
-fextra-libc-function=* or it should redirect all extra functions to llvm and let llvm handle them?

Ideally LLVM should just "enable" memcmpeq if glibc version is > XYZ. Not sure if there is a realiable way to do it. @efriedma ?

Ideally LLVM should just "enable" memcmpeq if glibc version is > XYZ. Not sure if there is a realiable way to do it. @efriedma ?

Wouldn't that almost definitionally only work if the file was being compiled statically? Otherwise you would
run into the issue that compiling on a system with glibc >= 2.35 produce an executable that wouldn't work
on a system with glibc < 2.35

Maybe new flag like -fglibc-version=ver ?

It could nicely fit as we already have -fbinutils-version and -fgnuc-version.

Wouldn't that almost definitionally only work if the file was being compiled statically? Otherwise you would
run into the issue that compiling on a system with glibc >= 2.35 produce an executable that wouldn't work
on a system with glibc < 2.35

There is absolutely no expectation of compatibility in that direction. A binary compiled against version X of glibc can run against any glibc version Y, where Y >= X -- but NOT the other way around.

Wouldn't that almost definitionally only work if the file was being compiled statically? Otherwise you would
run into the issue that compiling on a system with glibc >= 2.35 produce an executable that wouldn't work
on a system with glibc < 2.35

There is absolutely no expectation of compatibility in that direction. A binary compiled against version X of glibc can run against any glibc version Y, where Y >= X -- but NOT the other way around.

Okay, so how about the same system header check approach as GCC is planning to go with?

Or do you want to limit this to just GLIBC?

Maybe new flag like -fglibc-version=ver ?

It could nicely fit as we already have -fbinutils-version and -fgnuc-version.

That would work here. Although if other libc implementations implement
something similar it wouldn't scale too-well.

We already effectively have a platform version number on Mac/iOS/Android; adding a version number for another platform isn't a big deal. (Yes, it doesn't really scale in the sense that every libc needs to have some entries in TargetLibraryInfo.cpp, but we basically end up doing that anyway, in practice.)

The tricky thing is that there isn't any easy way to tell the glibc version number. The clang driver would have to dig through either system headers or the glibc library itself.

The alternative is that clang IR generation could check if __memcmpeq is declared somewhere in the file, and add some metadata to the module. That's also sort of ugly, but maybe easier to implement.

We already effectively have a platform version number on Mac/iOS/Android; adding a version number for another platform isn't a big deal. (Yes, it doesn't really scale in the sense that every libc needs to have some entries in TargetLibraryInfo.cpp, but we basically end up doing that anyway, in practice.)

The tricky thing is that there isn't any easy way to tell the glibc version number. The clang driver would have to dig through either system headers or the glibc library itself.

Parsing "libc.so.6" for ".gnu.version_d" wouldn't be an insane way to do it.

The alternative is that clang IR generation could check if __memcmpeq is declared somewhere in the file, and add some metadata to the module. That's also sort of ugly, but maybe easier to implement.

I like the idea of doing this at IR generation time.

What do you think about just using the clang AST and checking if there is a valid definition for __memcmpeq + isGNUEnvironment?
It's in the reserved namespace so as long as its not another libc re-purposing the symbol that should be safe.

That also seems to the direction GCC is going so it would be maximum compatibility.

[SelectionDAG]

I feel that [SimplifyLibCalls] is more appropriate. The main change is there.

-fglibc-version=ver

This seems fine to me. Agree with efriedma that every platform adding someway looks bad, but that is the current state.
I can see that a generic glibc version option can be useful. E.g. we had a workaround for exp10* in f5689f830414bdb60ce56ff37c182b6cebcedd0b.
Some Triple.isOSLinux() uses be better reword using a glibc version.
I think the hard thing is to decide a default value, which isn't clear in llvm-project's build and target requirement.

(From https://reviews.llvm.org/D56593#3586673)

-fbuiltin-__memcmpeq

https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html says "There is no corresponding -fbuiltin-function option".

If Clang decides to add this, it looks good and I am happy to implement it :)

What do you think about just using the clang AST and checking if there is a valid definition for __memcmpeq + isGNUEnvironment?

And if there is no declaration and somebody wrote “__builtin_memcmp(…) == 0” ? We sadly cannot use memcmpeq then, no?

What do you think about just using the clang AST and checking if there is a valid definition for __memcmpeq + isGNUEnvironment?

And if there is no declaration and somebody wrote “__builtin_memcmp(…) == 0” ? We sadly cannot use memcmpeq then, no?

No because the function may not exist.

But it will catch all the cases the function does exist w.o a commandline option. The reason I'm a bit partial to this is
if the optimization is guarded by a commandline option it see very little usage.

The only case the AST would miss is a build where memcmp was called w.o <string.h> being included. This seems
edge case enough to me its worth accepting.

In a few years time once GLIBC2.35 becomes the norm the replacement can become default like bcmp.

Is this still being looked at?

goldstein.w.n added a comment.EditedApr 11 2023, 4:05 PM

Is this still being looked at?

Sorry, its been a while.

This stalled when discussing the method of enabling this. I think the goal was to match GCC but AFAICT GCC never ended up implementing this.

But im not actively working on this at the moment.