Skip to content

Commit fedf86f

Browse files
authored
Merge pull request swiftlang#63809 from hyp/eng/exception1
[interop] add itanium ABI support for trapping on uncaught exceptions when making a foreign call
2 parents 6f28e27 + e704e94 commit fedf86f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+1005
-45
lines changed

lib/IRGen/CallEmission.h

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,25 @@ class CallEmission {
6363
/// RemainingArgsForCallee, at least between calls.
6464
bool EmittedCall;
6565

66+
/// The basic block to which the call to a potentially throwing foreign
67+
/// function should jump to continue normal execution of the program.
68+
llvm::BasicBlock *invokeNormalDest = nullptr;
69+
70+
/// The basic block to which the call to a potentially throwing foreign
71+
/// function should jump to in case an exception has been thrown during the
72+
/// invocation of the call.
73+
llvm::BasicBlock *invokeUnwindDest = nullptr;
74+
6675
virtual void setFromCallee();
6776
void emitToUnmappedMemory(Address addr);
6877
void emitToUnmappedExplosion(Explosion &out);
69-
virtual void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) = 0;
78+
virtual void emitCallToUnmappedExplosion(llvm::CallBase *call,
79+
Explosion &out) = 0;
7080
void emitYieldsToExplosion(Explosion &out);
7181
virtual FunctionPointer getCalleeFunctionPointer() = 0;
72-
llvm::CallInst *emitCallSite();
82+
llvm::CallBase *emitCallSite();
7383

74-
virtual llvm::CallInst *createCall(const FunctionPointer &fn,
84+
virtual llvm::CallBase *createCall(const FunctionPointer &fn,
7585
ArrayRef<llvm::Value *> args) = 0;
7686

7787
CallEmission(IRGenFunction &IGF, llvm::Value *selfValue, Callee &&callee)
@@ -105,7 +115,7 @@ class CallEmission {
105115
bool isOutlined);
106116
void emitToExplosion(Explosion &out, bool isOutlined);
107117

108-
llvm::CallInst *emitCoroutineAsOrdinaryFunction() {
118+
llvm::CallBase *emitCoroutineAsOrdinaryFunction() {
109119
assert(IsCoroutine);
110120
IsCoroutine = false;
111121

lib/IRGen/Callee.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,14 @@ namespace irgen {
331331
llvm::Type *awaitSignature = nullptr;
332332
bool useSignature = false;
333333

334+
// True when this function pointer points to a non-throwing foreign
335+
// function.
336+
bool isForeignNoThrow = false;
337+
338+
// True when this function pointer points to a foreign function that traps
339+
// on exception in the always_inline thunk.
340+
bool foreignCallCatchesExceptionInThunk = false;
341+
334342
explicit FunctionPointer(Kind kind, llvm::Value *value,
335343
const Signature &signature)
336344
: FunctionPointer(kind, value, PointerAuthInfo(), signature) {}
@@ -491,6 +499,24 @@ namespace irgen {
491499
bool shouldSuppressPolymorphicArguments() const {
492500
return kind.shouldSuppressPolymorphicArguments();
493501
}
502+
503+
void setForeignNoThrow() { isForeignNoThrow = true; }
504+
505+
bool canThrowForeignException() const {
506+
return getForeignInfo().canThrow && !isForeignNoThrow;
507+
}
508+
509+
void setForeignCallCatchesExceptionInThunk() {
510+
foreignCallCatchesExceptionInThunk = true;
511+
}
512+
513+
bool doesForeignCallCatchExceptionInThunk() {
514+
return foreignCallCatchesExceptionInThunk;
515+
}
516+
517+
bool shouldUseInvoke() const {
518+
return canThrowForeignException() && !foreignCallCatchesExceptionInThunk;
519+
}
494520
};
495521

496522
class Callee {

lib/IRGen/GenCall.cpp

Lines changed: 102 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,9 @@ Signature SignatureExpansion::getSignature() {
19821982
result.Attributes = Attrs;
19831983
using ExtraData = Signature::ExtraData;
19841984
if (FnType->getLanguage() == SILFunctionLanguage::C) {
1985+
// This is a potentially throwing function. The use of 'noexcept' /
1986+
// 'nothrow' is applied at the call site in the \c FunctionPointer class.
1987+
ForeignInfo.canThrow = IGM.isForeignExceptionHandlingEnabled();
19851988
result.ExtraDataKind = ExtraData::kindForMember<ForeignFunctionInfo>();
19861989
result.ExtraDataStorage.emplace<ForeignFunctionInfo>(result.ExtraDataKind,
19871990
ForeignInfo);
@@ -2227,9 +2230,10 @@ class SyncCallEmission final : public CallEmission {
22272230
index, IGF.IGM.getMaximalTypeExpansionContext());
22282231
}
22292232

2230-
llvm::CallInst *createCall(const FunctionPointer &fn,
2233+
llvm::CallBase *createCall(const FunctionPointer &fn,
22312234
ArrayRef<llvm::Value *> args) override {
2232-
return IGF.Builder.CreateCall(fn, Args);
2235+
return IGF.Builder.CreateCallOrInvoke(fn, Args, invokeNormalDest,
2236+
invokeUnwindDest);
22332237
}
22342238

22352239
void begin() override { super::begin(); }
@@ -2375,7 +2379,8 @@ class SyncCallEmission final : public CallEmission {
23752379
}
23762380
super::setArgs(adjusted, isOutlined, witnessMetadata);
23772381
}
2378-
void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override {
2382+
void emitCallToUnmappedExplosion(llvm::CallBase *call,
2383+
Explosion &out) override {
23792384
// Bail out immediately on a void result.
23802385
llvm::Value *result = call;
23812386
if (result->getType()->isVoidTy())
@@ -2678,7 +2683,8 @@ class AsyncCallEmission final : public CallEmission {
26782683
saveValue(resumeParentField, fnVal, isOutlined);
26792684
}
26802685
}
2681-
void emitCallToUnmappedExplosion(llvm::CallInst *call, Explosion &out) override {
2686+
void emitCallToUnmappedExplosion(llvm::CallBase *call,
2687+
Explosion &out) override {
26822688
// Bail out on a void result type.
26832689
auto &IGM = IGF.IGM;
26842690
llvm::Value *result = call;
@@ -2770,7 +2776,7 @@ class AsyncCallEmission final : public CallEmission {
27702776
return IGF.getCalleeErrorResultSlot(errorType);
27712777
}
27722778

2773-
llvm::CallInst *createCall(const FunctionPointer &fn,
2779+
llvm::CallBase *createCall(const FunctionPointer &fn,
27742780
ArrayRef<llvm::Value *> args) override {
27752781
auto &IGM = IGF.IGM;
27762782
auto &Builder = IGF.Builder;
@@ -2900,7 +2906,7 @@ void CallEmission::emitToUnmappedMemory(Address result) {
29002906
}
29012907

29022908
/// The private routine to ultimately emit a call or invoke instruction.
2903-
llvm::CallInst *CallEmission::emitCallSite() {
2909+
llvm::CallBase *CallEmission::emitCallSite() {
29042910
assert(state == State::Emitting);
29052911
assert(LastArgWritten == 0);
29062912
assert(!EmittedCall);
@@ -2921,8 +2927,16 @@ llvm::CallInst *CallEmission::emitCallSite() {
29212927
Args[i] = IGF.coerceValue(Args[i], paramTy, IGF.IGM.DataLayout);
29222928
}
29232929

2924-
// TODO: exceptions!
2930+
if (fn.canThrowForeignException()) {
2931+
if (!fn.doesForeignCallCatchExceptionInThunk()) {
2932+
invokeNormalDest = IGF.createBasicBlock("invoke.cont");
2933+
invokeUnwindDest = IGF.createExceptionUnwindBlock();
2934+
} else
2935+
IGF.setCallsThunksWithForeignExceptionTraps();
2936+
}
29252937
auto call = createCall(fn, Args);
2938+
if (invokeNormalDest)
2939+
IGF.Builder.emitBlock(invokeNormalDest);
29262940

29272941
// Make coroutines calls opaque to LLVM analysis.
29282942
if (IsCoroutine) {
@@ -2984,8 +2998,9 @@ assertTypesInByValAndStructRetAttributes(llvm::FunctionType *fnType,
29842998
return attrList;
29852999
}
29863000

2987-
llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn,
2988-
ArrayRef<llvm::Value*> args) {
3001+
llvm::CallBase *IRBuilder::CreateCallOrInvoke(
3002+
const FunctionPointer &fn, ArrayRef<llvm::Value *> args,
3003+
llvm::BasicBlock *invokeNormalDest, llvm::BasicBlock *invokeUnwindDest) {
29893004
assert(fn.getKind() == FunctionPointer::Kind::Function);
29903005
SmallVector<llvm::OperandBundleDef, 1> bundles;
29913006

@@ -2999,8 +3014,13 @@ llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn,
29993014

30003015
assert(!isTrapIntrinsic(fn.getRawPointer()) && "Use CreateNonMergeableTrap");
30013016
auto fnTy = cast<llvm::FunctionType>(fn.getFunctionType());
3002-
llvm::CallInst *call =
3003-
IRBuilderBase::CreateCall(fnTy, fn.getRawPointer(), args, bundles);
3017+
llvm::CallBase *call;
3018+
if (!fn.shouldUseInvoke())
3019+
call = IRBuilderBase::CreateCall(fnTy, fn.getRawPointer(), args, bundles);
3020+
else
3021+
call =
3022+
IRBuilderBase::CreateInvoke(fnTy, fn.getRawPointer(), invokeNormalDest,
3023+
invokeUnwindDest, args, bundles);
30043024

30053025
llvm::AttributeList attrs = fn.getAttributes();
30063026
// If a parameter of a function is SRet, the corresponding argument should be
@@ -3024,6 +3044,13 @@ llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn,
30243044
return call;
30253045
}
30263046

3047+
llvm::CallInst *IRBuilder::CreateCall(const FunctionPointer &fn,
3048+
ArrayRef<llvm::Value *> args) {
3049+
assert(!fn.shouldUseInvoke());
3050+
return cast<llvm::CallInst>(CreateCallOrInvoke(
3051+
fn, args, /*invokeNormalDest=*/nullptr, /*invokeUnwindDest=*/nullptr));
3052+
}
3053+
30273054
/// Emit the result of this call to memory.
30283055
void CallEmission::emitToMemory(Address addr,
30293056
const LoadableTypeInfo &indirectedResultTI,
@@ -4416,6 +4443,40 @@ void IRGenFunction::emitBBForReturn() {
44164443
CurFn->getBasicBlockList().push_back(ReturnBB);
44174444
}
44184445

4446+
llvm::BasicBlock *IRGenFunction::createExceptionUnwindBlock() {
4447+
auto *result = createBasicBlock("exception.unwind");
4448+
IRBuilder::SavedInsertionPointRAII insertRAII(Builder, result);
4449+
4450+
// Create a catch-all landing pad.
4451+
// FIXME: Refactor Clang/lib/CodeGen to call into Clang here.
4452+
// FIXME: MSVC support.
4453+
llvm::LandingPadInst *lpad = Builder.CreateLandingPad(
4454+
llvm::StructType::get(IGM.Int8PtrTy, IGM.Int32Ty), 0);
4455+
lpad->addClause(llvm::ConstantPointerNull::get(IGM.Int8PtrTy));
4456+
4457+
// The trap with a message informs the user that the exception hasn't
4458+
// been caught. The trap creates a new debug inline frame for the message,
4459+
// so ensure that the current debug location is preserved.
4460+
auto oldDebugLoc = Builder.getCurrentDebugLocation();
4461+
emitTrap(IGM.Context.LangOpts.EnableObjCInterop
4462+
? "unhandled C++ / Objective-C exception"
4463+
: "unhandled C++ exception",
4464+
/*Unreachable=*/true);
4465+
Builder.SetCurrentDebugLocation(oldDebugLoc);
4466+
4467+
ExceptionUnwindBlocks.push_back(result);
4468+
return result;
4469+
}
4470+
4471+
void IRGenFunction::createExceptionTrapScope(
4472+
llvm::function_ref<void(llvm::BasicBlock *, llvm::BasicBlock *)>
4473+
invokeEmitter) {
4474+
auto *invokeNormalDest = createBasicBlock("invoke.cont");
4475+
auto *invokeUnwindDest = createExceptionUnwindBlock();
4476+
invokeEmitter(invokeNormalDest, invokeUnwindDest);
4477+
Builder.emitBlock(invokeNormalDest);
4478+
}
4479+
44194480
/// Emit the prologue for the function.
44204481
void IRGenFunction::emitPrologue() {
44214482
// Set up the IRBuilder.
@@ -4466,12 +4527,42 @@ bool IRGenFunction::emitBranchToReturnBB() {
44664527
return true;
44674528
}
44684529

4530+
llvm::Function *IRGenModule::getForeignExceptionHandlingPersonalityFunc() {
4531+
if (foreignExceptionHandlingPersonalityFunc)
4532+
return foreignExceptionHandlingPersonalityFunc;
4533+
foreignExceptionHandlingPersonalityFunc = llvm::Function::Create(
4534+
llvm::FunctionType::get(Int32Ty, true), llvm::Function::ExternalLinkage,
4535+
"__gxx_personality_v0", getModule());
4536+
return foreignExceptionHandlingPersonalityFunc;
4537+
}
4538+
4539+
bool IRGenModule::isForeignExceptionHandlingEnabled() const {
4540+
// FIXME: Support exceptions on windows MSVC.
4541+
if (Triple.isWindowsMSVCEnvironment())
4542+
return false;
4543+
const auto &clangLangOpts =
4544+
Context.getClangModuleLoader()->getClangASTContext().getLangOpts();
4545+
return Context.LangOpts.EnableCXXInterop && clangLangOpts.Exceptions &&
4546+
!clangLangOpts.IgnoreExceptions;
4547+
}
4548+
44694549
/// Emit the epilogue for the function.
44704550
void IRGenFunction::emitEpilogue() {
44714551
if (EarliestIP != AllocaIP)
44724552
EarliestIP->eraseFromParent();
44734553
// Destroy the alloca insertion point.
44744554
AllocaIP->eraseFromParent();
4555+
// Add exception unwind blocks and additional exception handling info
4556+
// if needed.
4557+
if (!ExceptionUnwindBlocks.empty() ||
4558+
callsAnyAlwaysInlineThunksWithForeignExceptionTraps) {
4559+
// The function should have an unwind table when catching exceptions.
4560+
CurFn->addFnAttr(llvm::Attribute::getWithUWTableKind(
4561+
*IGM.LLVMContext, llvm::UWTableKind::Default));
4562+
CurFn->setPersonalityFn(IGM.getForeignExceptionHandlingPersonalityFunc());
4563+
}
4564+
for (auto *bb : ExceptionUnwindBlocks)
4565+
CurFn->getBasicBlockList().push_back(bb);
44754566
}
44764567

44774568
std::pair<Address, Size>

lib/IRGen/GenDecl.cpp

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3252,12 +3252,35 @@ llvm::Constant *swift::irgen::emitCXXConstructorThunkIfNeeded(
32523252
Args.push_back(arg);
32533253
}
32543254

3255-
subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args);
3255+
auto *call =
3256+
emitCXXConstructorCall(subIGF, ctor, ctorFnType, ctorAddress, Args);
3257+
if (isa<llvm::InvokeInst>(call))
3258+
IGM.emittedForeignFunctionThunksWithExceptionTraps.insert(thunk);
32563259
subIGF.Builder.CreateRetVoid();
32573260

32583261
return thunk;
32593262
}
32603263

3264+
llvm::CallBase *swift::irgen::emitCXXConstructorCall(
3265+
IRGenFunction &IGF, const clang::CXXConstructorDecl *ctor,
3266+
llvm::FunctionType *ctorFnType, llvm::Constant *ctorAddress,
3267+
llvm::ArrayRef<llvm::Value *> args) {
3268+
bool canThrow = IGF.IGM.isForeignExceptionHandlingEnabled();
3269+
if (auto *fpt = ctor->getType()->getAs<clang::FunctionProtoType>()) {
3270+
if (fpt->isNothrow())
3271+
canThrow = false;
3272+
}
3273+
if (!canThrow)
3274+
return IGF.Builder.CreateCall(ctorFnType, ctorAddress, args);
3275+
llvm::CallBase *result;
3276+
IGF.createExceptionTrapScope([&](llvm::BasicBlock *invokeNormalDest,
3277+
llvm::BasicBlock *invokeUnwindDest) {
3278+
result = IGF.Builder.createInvoke(ctorFnType, ctorAddress, args,
3279+
invokeNormalDest, invokeUnwindDest);
3280+
});
3281+
return result;
3282+
}
3283+
32613284
StackProtectorMode IRGenModule::shouldEmitStackProtector(SILFunction *f) {
32623285
const SILOptions &opts = IRGen.SIL.getOptions();
32633286
return (opts.EnableStackProtection && f->needsStackProtection()) ?

lib/IRGen/GenDecl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ namespace llvm {
2929
class AttributeList;
3030
class Function;
3131
class FunctionType;
32+
class CallBase;
3233
}
3334
namespace swift {
3435
namespace irgen {
@@ -69,6 +70,12 @@ namespace irgen {
6970
emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, Signature signature,
7071
const clang::CXXConstructorDecl *ctor,
7172
StringRef name, llvm::Constant *ctorAddress);
73+
74+
llvm::CallBase *emitCXXConstructorCall(IRGenFunction &IGF,
75+
const clang::CXXConstructorDecl *ctor,
76+
llvm::FunctionType *ctorFnType,
77+
llvm::Constant *ctorAddress,
78+
llvm::ArrayRef<llvm::Value *> args);
7279
}
7380
}
7481

lib/IRGen/GenStruct.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ namespace {
579579
auto callee = cast<llvm::Function>(clangFnAddr->stripPointerCasts());
580580
Signature signature = IGF.IGM.getSignature(fnType);
581581
std::string name = "__swift_cxx_copy_ctor" + callee->getName().str();
582+
auto *origClangFnAddr = clangFnAddr;
582583
clangFnAddr = emitCXXConstructorThunkIfNeeded(
583584
IGF.IGM, signature, copyConstructor, name, clangFnAddr);
584585
callee = cast<llvm::Function>(clangFnAddr);
@@ -588,7 +589,19 @@ namespace {
588589
src = IGF.coerceValue(src, callee->getFunctionType()->getParamType(1),
589590
IGF.IGM.DataLayout);
590591
}
591-
IGF.Builder.CreateCall(callee->getFunctionType(), callee, {dest, src});
592+
llvm::Value *args[] = {dest, src};
593+
if (clangFnAddr == origClangFnAddr) {
594+
// Ensure we can use 'invoke' to trap on uncaught exceptions when
595+
// calling original copy constructor without going through the thunk.
596+
emitCXXConstructorCall(IGF, copyConstructor, callee->getFunctionType(),
597+
callee, args);
598+
return;
599+
}
600+
// Check if we're calling a thunk that traps on exception thrown from copy
601+
// constructor.
602+
if (IGF.IGM.emittedForeignFunctionThunksWithExceptionTraps.count(callee))
603+
IGF.setCallsThunksWithForeignExceptionTraps();
604+
IGF.Builder.CreateCall(callee->getFunctionType(), callee, args);
592605
}
593606

594607
public:
@@ -653,6 +666,23 @@ namespace {
653666
IGF.IGM.DataLayout);
654667
args.push_back(implicitParam);
655668
}
669+
bool canThrow = false;
670+
if (IGF.IGM.isForeignExceptionHandlingEnabled()) {
671+
if (auto *fpt =
672+
destructor->getType()->getAs<clang::FunctionProtoType>()) {
673+
if (!fpt->isNothrow())
674+
canThrow = true;
675+
}
676+
}
677+
if (canThrow) {
678+
IGF.createExceptionTrapScope([&](llvm::BasicBlock *invokeNormalDest,
679+
llvm::BasicBlock *invokeUnwindDest) {
680+
IGF.Builder.createInvoke(destructorFnAddr->getFunctionType(),
681+
destructorFnAddr, args, invokeNormalDest,
682+
invokeUnwindDest);
683+
});
684+
return;
685+
}
656686

657687
IGF.Builder.CreateCall(destructorFnAddr->getFunctionType(),
658688
destructorFnAddr, args);

0 commit comments

Comments
 (0)