This is an archive of the discontinued LLVM Phabricator instance.

[WinEH] Apply funclet operand bundles to nounwind intrinsics that lower to function calls
ClosedPublic

Authored by sgraenitz on Jun 20 2022, 5:47 AM.

Details

Summary

WinEHPrepare marks any function call from EH funclets as unreachable, if it's not a nounwind intrinsic and has no proper funclet bundle operand. This affects ARC intrinsics on Windows, because they are lowered to regular function calls in the PreISelIntrinsicLowering pass. It caused silent binary truncations and crashes during unwinding with the GNUstep ObjC runtime: https://github.com/gnustep/libobjc2/issues/222

This patch adds a new function llvm::IntrinsicInst::mayLowerToFunctionCall() that aims to collect all affected intrinsic IDs.

  • Clang CodeGen uses it to determine whether or not it must emit a funclet bundle operand.
  • PreISelIntrinsicLowering asserts that the function returns true for all ObjC runtime calls it lowers.
  • LLVM uses it to determine whether or not a funclet bundle operand must be propagated to inlined call sites.

Diff Detail

Event Timeline

sgraenitz created this revision.Jun 20 2022, 5:47 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 20 2022, 5:47 AM
sgraenitz requested review of this revision.Jun 20 2022, 5:47 AM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptJun 20 2022, 5:47 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
sgraenitz updated this revision to Diff 438425.Jun 20 2022, 9:04 AM

Add missing objc_sync_exit to fix failing test Transforms/PreISelIntrinsicLowering/objc-arc.ll

Running check-llvm and check-clang locally found no more failing tests. The issue is not limited to pre-ISel intrinsics (anymore) and I have to re-phrase a few comments. Apart from that, is there any more feedback? Thanks

theraven accepted this revision.Jul 24 2022, 6:11 AM

LGTM, a couple of extra comments would help.

llvm/include/llvm/IR/IntrinsicInst.h
110

Minor nit, should be /// for a doc comment.

llvm/lib/IR/IntrinsicInst.cpp
61

I'm curious why memcpy and friends don't need to be on this list. Do they get expanded at a different point? Or is it that the front end inserts a memcpy call and the optimiser that turns them into intrinsics and back preserves operand bundles? It would be a good idea to have a comment explaining why these specific ones are on the list and not other libc (or math library) functions that may appear in cleanup blocks. From the code in InlineFunction.cpp, it looks as if this is a problem only for intrinsics that may throw, which excludes most of the C standard ones?

This revision is now accepted and ready to land.Jul 24 2022, 6:11 AM
sgraenitz updated this revision to Diff 447667.Jul 26 2022, 6:42 AM
sgraenitz marked 2 inline comments as done.

Rebase and polish comments

Hi @theraven thanks for reviewing!

llvm/lib/IR/IntrinsicInst.cpp
61

The name mayLowerToFunctionCall was proposed in a precursor to this review: https://reviews.llvm.org/D127962#inline-1227485. I agree that the term might appear overly general at first. I still followed the advice because:
(1) This controls assignment and propagation of operand bundles, which is a very general concept.
(2) From the perspective of IR transformations, only a few intrinsics actually lower to function calls (the majority remains intrinsics).
(3) The approach might be applied to other intrinsics, e.g. statepoints

However, this is not obvious and because other readers might have the same question, I extended my comment in the header like this:

Check if the intrinsic might lower into a regular function call in the course of IR transformations

Do you think that makes it more clear?

For additional context
Right now the list only contains ObjC ARC intrinsics. These are subject to PreISelIntrinsicLowering which means they are lowered to regular function calls before we run exception handling passes:
https://github.com/llvm/llvm-project/blob/c73adbad6a9964e0700865b7c786cc6885898c68/llvm/lib/CodeGen/TargetPassConfig.cpp#L1114-L1118

WinEHPrepare is an exception handling pass and it accepts call instructions in EH funclets only if they:

  • target nounwind intrinsics or inline assembly
  • have a matching funclet bundle operand

It declares other calls as "implausible instructions" and marks them unreachable, which caused the truncation bug that this patch aims to fix.

All ObjC ARC intrinsics have the nounwind attribute. My understanding is that the only user-code that might run in turn of an invocation can be destructors -- and they don't throw per definition.

The reason libc functions like memcpy are not in this list is that they are lowered at a later point in time. In the context of WinEHPrepare (and opt in general) they are always intrinsics.

Pre-merge checks passed on Windows. Debian failures are unrelated. Locally on Ubuntu and macOS all tests pass. Getting ready to land this.

This revision was landed with ongoing or failed builds.Jul 26 2022, 8:55 AM
This revision was automatically updated to reflect the committed changes.