Skip to content

Commit 06bfef2

Browse files
authored
Merge pull request #63572 from hyp/eng/vtable2
[interop][SwiftToCxx] dispatch virtual calls via thunks for resilient…
2 parents 0b58685 + 9e32761 commit 06bfef2

File tree

6 files changed

+159
-48
lines changed

6 files changed

+159
-48
lines changed

include/swift/IRGen/IRABIDetailsProvider.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,9 @@ class IRABIDetailsProvider {
254254
/// A direct call can be made to the underlying function.
255255
Direct,
256256
/// An indirect call that can be made via a static offset in a vtable.
257-
IndirectVTableStaticOffset
257+
IndirectVTableStaticOffset,
258+
/// The call should be made via the provided thunk function.
259+
Thunk
258260
};
259261

260262
static MethodDispatchInfo direct() {
@@ -265,18 +267,27 @@ class IRABIDetailsProvider {
265267
return MethodDispatchInfo(Kind::IndirectVTableStaticOffset, bitOffset);
266268
}
267269

270+
static MethodDispatchInfo thunk(std::string thunkName) {
271+
return MethodDispatchInfo(Kind::Thunk, 0, thunkName);
272+
}
273+
268274
Kind getKind() const { return kind; }
269275
size_t getStaticBitOffset() const {
270276
assert(kind == Kind::IndirectVTableStaticOffset);
271277
return bitOffset;
272278
}
279+
StringRef getThunkSymbolName() const {
280+
assert(kind == Kind::Thunk);
281+
return thunkName;
282+
}
273283

274284
private:
275-
constexpr MethodDispatchInfo(Kind kind, size_t bitOffset)
276-
: kind(kind), bitOffset(bitOffset) {}
285+
MethodDispatchInfo(Kind kind, size_t bitOffset, std::string thunkName = "")
286+
: kind(kind), bitOffset(bitOffset), thunkName(thunkName) {}
277287

278288
Kind kind;
279289
size_t bitOffset;
290+
std::string thunkName;
280291
};
281292

282293
Optional<MethodDispatchInfo>

lib/IRGen/IRABIDetailsProvider.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/IRGenOptions.h"
3030
#include "swift/AST/ParameterList.h"
3131
#include "swift/AST/Types.h"
32+
#include "swift/IRGen/Linking.h"
3233
#include "swift/SIL/SILFunctionBuilder.h"
3334
#include "swift/SIL/SILModule.h"
3435
#include "swift/Subsystems.h"
@@ -216,6 +217,12 @@ class IRABIDetailsProviderImpl {
216217
auto *parentClass = dyn_cast<ClassDecl>(funcDecl->getDeclContext());
217218
if (!parentClass)
218219
return MethodDispatchInfo::direct();
220+
// Resilient indirect calls should go through a thunk.
221+
if (parentClass->hasResilientMetadata())
222+
return MethodDispatchInfo::thunk(
223+
LinkEntity::forDispatchThunk(
224+
SILDeclRef(const_cast<AbstractFunctionDecl *>(funcDecl)))
225+
.mangleAsString());
219226
auto &layout = IGM.getMetadataLayout(parentClass);
220227
if (!isa<ClassMetadataLayout>(layout))
221228
return {};

lib/PrintAsClang/DeclAndTypePrinter.cpp

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,6 +1356,47 @@ class DeclAndTypePrinter::Implementation
13561356
LoweredFunctionSignature signature;
13571357
};
13581358

1359+
/// Print the C function declaration that represents the given native Swift
1360+
/// function, or its dispatch thunk.
1361+
ClangRepresentation printCFunctionWithLoweredSignature(
1362+
AbstractFunctionDecl *FD, const FunctionSwiftABIInformation &funcABI,
1363+
Type resultTy, AnyFunctionType *funcTy, StringRef symbolName,
1364+
StringRef comment = "") {
1365+
std::string cRepresentationString;
1366+
llvm::raw_string_ostream cRepresentationOS(cRepresentationString);
1367+
cRepresentationOS << "SWIFT_EXTERN ";
1368+
1369+
DeclAndTypeClangFunctionPrinter funcPrinter(
1370+
cRepresentationOS, owningPrinter.prologueOS, owningPrinter.typeMapping,
1371+
owningPrinter.interopContext, owningPrinter);
1372+
1373+
auto representation = funcPrinter.printFunctionSignature(
1374+
FD, funcABI.getSignature(), symbolName, resultTy,
1375+
DeclAndTypeClangFunctionPrinter::FunctionSignatureKind::CFunctionProto);
1376+
if (representation.isUnsupported())
1377+
return representation;
1378+
1379+
os << cRepresentationOS.str();
1380+
// Swift functions can't throw exceptions, we can only
1381+
// throw them from C++ when emitting C++ inline thunks for the Swift
1382+
// functions.
1383+
if (!funcTy->isThrowing())
1384+
os << " SWIFT_NOEXCEPT";
1385+
if (!funcABI.useCCallingConvention())
1386+
os << " SWIFT_CALL";
1387+
printAvailability(FD);
1388+
os << ';';
1389+
if (funcABI.useMangledSymbolName()) {
1390+
// add a comment with a demangled function name.
1391+
os << " // ";
1392+
if (!comment.empty())
1393+
os << comment << ' ';
1394+
FD->getName().print(os);
1395+
}
1396+
os << "\n";
1397+
return representation;
1398+
}
1399+
13591400
// Print out the extern C Swift ABI function signature.
13601401
Optional<FunctionSwiftABIInformation>
13611402
printSwiftABIFunctionSignatureAsCxxFunction(
@@ -1378,47 +1419,33 @@ class DeclAndTypePrinter::Implementation
13781419
auto resultTy =
13791420
getForeignResultType(FD, funcTy, asyncConvention, errorConvention);
13801421

1381-
std::string cRepresentationString;
1382-
llvm::raw_string_ostream cRepresentationOS(cRepresentationString);
1383-
13841422
auto signature = owningPrinter.interopContext.getIrABIDetails()
13851423
.getFunctionLoweredSignature(FD);
13861424
// FIXME: Add a note saying that this func is unsupported.
13871425
if (!signature)
13881426
return None;
13891427
FunctionSwiftABIInformation funcABI(FD, *signature);
13901428

1391-
cRepresentationOS << "SWIFT_EXTERN ";
1392-
1393-
DeclAndTypeClangFunctionPrinter funcPrinter(
1394-
cRepresentationOS, owningPrinter.prologueOS, owningPrinter.typeMapping,
1395-
owningPrinter.interopContext, owningPrinter);
1396-
1397-
auto representation = funcPrinter.printFunctionSignature(
1398-
FD, funcABI.getSignature(), funcABI.getSymbolName(), resultTy,
1399-
DeclAndTypeClangFunctionPrinter::FunctionSignatureKind::CFunctionProto);
1400-
if (representation.isUnsupported()) {
1429+
auto representation = printCFunctionWithLoweredSignature(
1430+
FD, funcABI, resultTy, funcTy, funcABI.getSymbolName());
1431+
if (representation.isUnsupported())
14011432
// FIXME: Emit remark about unemitted declaration.
14021433
return None;
1403-
}
14041434

1405-
os << cRepresentationOS.str();
1406-
// Swift functions can't throw exceptions, we can only
1407-
// throw them from C++ when emitting C++ inline thunks for the Swift
1408-
// functions.
1409-
// FIXME: Support throwing exceptions for Swift errors.
1410-
if (!funcTy->isThrowing())
1411-
os << " SWIFT_NOEXCEPT";
1412-
if (!funcABI.useCCallingConvention())
1413-
os << " SWIFT_CALL";
1414-
printAvailability(FD);
1415-
os << ';';
1416-
if (funcABI.useMangledSymbolName()) {
1417-
// add a comment with a demangled function name.
1418-
os << " // ";
1419-
FD->getName().print(os);
1435+
if (selfTypeDeclContext && !isa<ConstructorDecl>(FD)) {
1436+
if (auto dispatchInfo = owningPrinter.interopContext.getIrABIDetails()
1437+
.getMethodDispatchInfo(FD)) {
1438+
// Emit the C signature for the dispatch thunk.
1439+
if (dispatchInfo->getKind() ==
1440+
IRABIDetailsProvider::MethodDispatchInfo::Kind::Thunk) {
1441+
auto thunkRepresentation = printCFunctionWithLoweredSignature(
1442+
FD, funcABI, resultTy, funcTy, dispatchInfo->getThunkSymbolName(),
1443+
"dispatch thunk for");
1444+
assert(!thunkRepresentation.isUnsupported());
1445+
}
1446+
}
14201447
}
1421-
os << "\n";
1448+
14221449
return funcABI;
14231450
}
14241451

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,21 +1098,27 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
10981098
os << " void* _ctx = nullptr;\n";
10991099
}
11001100
Optional<StringRef> indirectFunctionVar;
1101-
if (dispatchInfo &&
1102-
dispatchInfo->getKind() !=
1103-
IRABIDetailsProvider::MethodDispatchInfo::Kind::Direct) {
1104-
assert(dispatchInfo->getKind() == IRABIDetailsProvider::MethodDispatchInfo::
1105-
Kind::IndirectVTableStaticOffset);
1106-
auto vtableBitOffset = dispatchInfo->getStaticBitOffset();
1107-
1108-
os << "void ***selfPtr_ = reinterpret_cast<void ***>( "
1109-
"::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));\n";
1110-
os << "void **vtable_ = *selfPtr_;\n";
1111-
os << "using FType = decltype(" << cxx_synthesis::getCxxImplNamespaceName()
1112-
<< "::" << swiftSymbolName << ");\n";
1113-
os << "FType *fptr_ = reinterpret_cast<FType *>(*(vtable_ + "
1114-
<< (vtableBitOffset / 8) << "));\n"; // FIXME: not 8
1115-
indirectFunctionVar = StringRef("fptr_");
1101+
using DispatchKindTy = IRABIDetailsProvider::MethodDispatchInfo::Kind;
1102+
if (dispatchInfo) {
1103+
switch (dispatchInfo->getKind()) {
1104+
case DispatchKindTy::Direct:
1105+
break;
1106+
case DispatchKindTy::IndirectVTableStaticOffset:
1107+
os << "void ***selfPtr_ = reinterpret_cast<void ***>( "
1108+
"::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));"
1109+
"\n";
1110+
os << "void **vtable_ = *selfPtr_;\n";
1111+
os << "using FType = decltype("
1112+
<< cxx_synthesis::getCxxImplNamespaceName() << "::" << swiftSymbolName
1113+
<< ");\n";
1114+
os << "FType *fptr_ = reinterpret_cast<FType *>(*(vtable_ + "
1115+
<< (dispatchInfo->getStaticBitOffset() / 8) << "));\n";
1116+
indirectFunctionVar = StringRef("fptr_");
1117+
break;
1118+
case DispatchKindTy::Thunk:
1119+
swiftSymbolName = dispatchInfo->getThunkSymbolName();
1120+
break;
1121+
}
11161122
}
11171123
auto printCallToCFunc = [&](Optional<StringRef> additionalParam) {
11181124
if (indirectFunctionVar)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/swift-class-virtual-method-dispatch.swift -typecheck -module-name Class -clang-header-expose-decls=all-public -emit-clang-header-path %t/class.h -enable-library-evolution
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-class-execution.o
6+
// RUN: %target-interop-build-swift %S/swift-class-virtual-method-dispatch.swift -o %t/swift-class-execution -Xlinker %t/swift-class-execution.o -module-name Class -Xfrontend -entry-point-function-name -Xfrontend swiftMain -enable-library-evolution
7+
8+
// RUN: %target-codesign %t/swift-class-execution
9+
// RUN: %target-run %t/swift-class-execution | %FileCheck %S/swift-class-virtual-method-dispatch-execution.cpp
10+
11+
// FIXME: pointer signing support.
12+
// UNSUPPORTED: CPU=arm64e
13+
14+
// REQUIRES: executable_test
15+
16+
#include "swift-class-virtual-method-dispatch-execution.cpp"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %S/swift-class-virtual-method-dispatch.swift -typecheck -module-name Class -clang-header-expose-decls=all-public -emit-clang-header-path %t/class.h -enable-library-evolution
3+
// RUN: %FileCheck %s < %t/class.h
4+
5+
// RUN: %check-interop-cxx-header-in-clang(%t/class.h)
6+
7+
// note: implemented in swift-class-virtual-method-dispatch.swift
8+
9+
// CHECK: void BaseClass::virtualMethod() {
10+
// CHECK-NEXT: return _impl::$s5Class04BaseA0C13virtualMethodyyFTj(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
11+
// CHECK-NEXT: }
12+
13+
// CHECK: swift::Int BaseClass::virtualMethodIntInt(swift::Int x) {
14+
// CHECK-NEXT: return _impl::$s5Class04BaseA0C016virtualMethodIntE0yS2iFTj(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
15+
// CHECK-NEXT: }
16+
17+
// CHECK: swift::Int BaseClass::finalMethodInBase(swift::Int x) {
18+
// CHECK-NEXT: return _impl::$s5Class04BaseA0C013finalMethodInB0yS2iF(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
19+
// CHECK-NEXT: }
20+
21+
// CHECK: void DerivedClass::virtualMethod() {
22+
// CHECK-NEXT: return _impl::$s5Class04BaseA0C13virtualMethodyyFTj(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
23+
// CHECK-NEXT: }
24+
25+
// CHECK: swift::Int DerivedClass::virtualMethodIntInt(swift::Int x) {
26+
// CHECK-NEXT: return _impl::$s5Class04BaseA0C016virtualMethodIntE0yS2iFTj(x, ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
27+
// CHECK-NEXT: }
28+
29+
// CHECK: BaseClass DerivedClass::virtualMethodInDerived(const BaseClass& x) {
30+
// CHECK-NEXT: return _impl::_impl_BaseClass::makeRetained(_impl::$s5Class07DerivedA0C015virtualMethodInB0yAA04BaseA0CAFFTj(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
31+
// CHECK-NEXT: }
32+
33+
// CHECK: void DerivedDerivedClass::virtualMethod() {
34+
// CHECK-NEXT: return _impl::$s5Class07DerivedbA0C13virtualMethodyyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
35+
// CHECK-NEXT: }
36+
37+
// CHECK: BaseClass DerivedDerivedClass::virtualMethodInDerived(const BaseClass& x) {
38+
// CHECK-NEXT: return _impl::_impl_BaseClass::makeRetained(_impl::$s5Class07DerivedbA0C015virtualMethodInB0yAA04BaseA0CAFF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(x), ::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this)));
39+
// CHECK-NEXT: }
40+
41+
// CHECK: void DerivedDerivedClass::methodInDerivedDerived() {
42+
// CHECK-NEXT: return _impl::$s5Class07DerivedbA0C08methodInbB0yyF(::swift::_impl::_impl_RefCountedClass::getOpaquePointer(*this));
43+
// CHECK-NEXT: }
44+

0 commit comments

Comments
 (0)