This is an archive of the discontinued LLVM Phabricator instance.

Fix behavior of ifuncs with 'used' extern "C" static functions
ClosedPublic

Authored by erichkeane on Mar 28 2022, 11:52 AM.

Details

Summary

We expect that extern "C" static functions to be usable in things like
inline assembly, as well as ifuncs:
See the bug report here: https://github.com/llvm/llvm-project/issues/54549

However, we were diagnosing this as 'not defined', because the
ifunc's attempt to look up its resolver would generate a declared IR
function.

Additionally, as background, the way we allow these static extern "C"
functions to work in inline assembly is by making an alias with the C
mangling in MOST situations to the version we emit with
internal-linkage/mangling.

The problem here was multi-fold: First- We generated the alias after the
ifunc was checked, so the function by that name didn't exist yet.
Second, the ifunc's generation caused a symbol to exist under the name
of the alias already (the declared function above), which suppressed the
alias generation.

This patch fixes all of this by moving the checking of ifuncs/CFE aliases
until AFTER we have generated the extern-C alias. Then, it does a
'fixup' around the GlobalIFunc to make sure we correct the reference.

Diff Detail

Event Timeline

erichkeane created this revision.Mar 28 2022, 11:52 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 28 2022, 11:52 AM
erichkeane requested review of this revision.Mar 28 2022, 11:52 AM
aaron.ballman added a subscriber: rjmccall.

CodeGen is not my wheelhouse, so I've added @rjmccall in case he spots something I've missed. This seems reasonable to me though.

clang/lib/CodeGen/CodeGenModule.cpp
6351
6458
erichkeane marked an inline comment as done.

Do the two comment fixups aaron suggested.

tahonermann added inline comments.Mar 29 2022, 12:05 PM
clang/lib/CodeGen/CodeGenModule.cpp
6352

An example of why the names might not match would be helpful here.

6356–6358

This comment doesn't state what the challenge is when there is more than one use. Is the concern that there may be multiple ifuncs that have the same associated resolver? What might cause that to happen? (presumably multiple functions declared with the ifunc attribute that name the same resolver).

A test that exercises this case seems appropriate.

clang/test/SemaCXX/externc-ifunc-resolver.cpp
9

There is considerable subtlety between these two tests and, unfortunately, the diagnostic isn't particularly helpful.

If I'm following correctly, the other test passes because an unmangled name is specified for the ifunc attribute and that is resolved against the emitted alias.

In this test, the same code is present as the other test, but with an additional declaration that doesn't seem relevant; one would not expect lookup for "resolve_foo" in the ifunc attribute to include the NS namespace. Is the error generated because two aliases for "resolve_foo" are emitted? If so, that seems like something that should be diagnosed somewhere. Or perhaps an alias should not be emitted for used functions defined at namespace scope.

erichkeane added inline comments.Mar 29 2022, 12:16 PM
clang/lib/CodeGen/CodeGenModule.cpp
6356–6358

I think the 'multiple ifuncs' isn't actually an issue, it is more that "I have no idea what else could use this/create this". Thinking now, the multiple 'ifuncs' seems completely doable now. I'll update the patch to handle multiples here.

clang/test/SemaCXX/externc-ifunc-resolver.cpp
9

If I'm following correctly, the other test passes because an unmangled name is specified for the ifunc attribute and that is resolved against the emitted alias.

Yes, this is correct.

There is considerable subtlety between these two tests and, unfortunately, the diagnostic isn't particularly helpful.

Yeah, this is unfortunate. However, when it is diagnosed causes some problems in that we don't have the real name of the declaration available anymore. I'm not intending to improve the quality of the diagnostic here though.

Is the error generated because two aliases for "resolve_foo" are emitted?

It is because two aliases WOULD be emitted, so we choose not to.

If so, that seems like something that should be diagnosed somewhere.

Maybe? Probably? I don't see the 'used' alias functionality as a part of this patch, I'm trying to keep that behavior as much as it possibly exists now.

Or perhaps an alias should not be emitted for used functions defined at namespace scope.

This we cannot do without breaking code that depends on it already.

Going through @tahonermann 's comments to attempt to allow multiple ifunc references, I ended up down the rabbit hole.

Turns out there are ways for there to be a ConstantExpr bitcast in the way that needs to be corrected (the 'first' one is never created this way, as the type is derived directly from the ifunc's type). I did a significant refactor to attempt to make this as clean as possible.

Note that the diagnostics for ifuncs are still bad, but I make no attempts to fix that here.

FWIW, I'm seeing a precommit CI failure on Windows:

Failed Tests (1):
  Clang :: SemaCXX/externc-ifunc-resolver.cpp

May as well also fix up the clang-format issues in the review.

clang/lib/CodeGen/CodeGenModule.cpp
6352

Name appears to be entirely unused?

erichkeane marked an inline comment as done.

Applied clang-format, removed 'Name' as Aaron found is no longer used, fixed the Sema test to not run on Windows triple, where ifunc is not available

fix a typo in the 'triple' .

tahonermann added inline comments.Mar 31 2022, 8:42 PM
clang/lib/CodeGen/CodeGenModule.cpp
6381–6382

This comment can only be understood in the context of this commit with the FIXME comment noted in the relevant test case. I suggest:

// This user is one we don't know how to handle, so fail redirection. This
// will result in an ifunc retaining a resolver name that will ultimately fail
// to be resolved to a defined function.
6424–6437

I suggest moving the declaration of ExistingElem after the break on !Val given that the former is not relevant if Val is null.

I think the !Val check is worth a comment here. I suggest:

// If Val is null, that implies that there were multiple declarations that each
// had a claim to the unmangled name. In this case, generation of the alias
// is suppressed. See CodeGenModule::MaybeHandleStaticInExternC().

The lack of checking for an existing element with the desired alias name was a pre-existing oversight, yes?

6430–6431

This comment seems inconsistent with the code. If there are no ifuncs involved, then why call CheckAndReplaceExternCIFuncs()?

clang/lib/CodeGen/CodeGenModule.h
1579–1581

Grammar is off in the last sentence.

The comment doesn't really explain this function's purpose. I suggest:

/// Helper function for EmitStaticExternCAliases() to redirect ifuncs that have a resolver
/// name that matches 'Elem' to instead resolve to the name of 'CppFunc'. This
/// redirection is necessary in cases where 'Elem' has a name that will be emitted as
/// an alias of the name bound to 'CppFunc'; ifuncs may not reference aliases. Redirection
/// is only performed if 'Elem' is only used by ifuncs in which case, 'Elem' is destroyed..
/// 'true' is returned If redirection is successful, and 'false' is returned otherwise.
clang/test/SemaCXX/externc-ifunc-resolver.cpp
10–14

Thanks for adding this comment; it is helpful.

erichkeane updated this revision to Diff 419741.Apr 1 2022, 6:39 AM
erichkeane marked 9 inline comments as done.

Fix all the things @tahonermann found.

clang/lib/CodeGen/CodeGenModule.cpp
6424–6437

SGTM, thanks!

6430–6431

Woops! Artifact of a previous implementation of this. Replaced the comment.

tahonermann added inline comments.Apr 1 2022, 12:46 PM
clang/lib/CodeGen/CodeGenModule.cpp
6426

You retyped my suggested comment instead of copy/paste? Or is the "hte" just intended to test my attention to detail? 😜

clang/lib/CodeGen/CodeGenModule.h
1579–1581

The parent comment is marked as done, but no change appears to have been applied.

erichkeane marked 2 inline comments as done.Apr 1 2022, 12:53 PM
erichkeane added inline comments.
clang/lib/CodeGen/CodeGenModule.cpp
6426

Ugh, even worse. I read your suggestion and was like, "I don't like that, it reads funny, let me try my own version". THEN, I typed in MY version of it just to find out I'd basically typed the exact wording(I put the 'See...' line earlier), so I just copy/pasted the change. For the worse... yay me.

clang/lib/CodeGen/CodeGenModule.h
1579–1581

Woops, forgot to git-add the file! Done again!

erichkeane marked 2 inline comments as done.
tahonermann accepted this revision.Apr 1 2022, 12:59 PM

Looks good to me! I left one final comment about a double period that was my fault.

clang/lib/CodeGen/CodeGenModule.h
1584

Double period; that was my badness that you dutifully copy pasted :)

This revision is now accepted and ready to land.Apr 1 2022, 12:59 PM
This revision was landed with ongoing or failed builds.Apr 1 2022, 1:01 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptApr 1 2022, 1:01 PM