Skip to content

Commit 0c909c2

Browse files
authored
Merge pull request #59482 from hyp/eng/emit-resilient-structs
[interop][SwiftToCxx] Add support for emitting resilient struct bindings
2 parents 541aa8d + b8ee625 commit 0c909c2

File tree

11 files changed

+433
-21
lines changed

11 files changed

+433
-21
lines changed

lib/PrintAsClang/ClangSyntaxPrinter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ using namespace cxx_synthesis;
1818

1919
StringRef cxx_synthesis::getCxxImplNamespaceName() { return "_impl"; }
2020

21+
StringRef cxx_synthesis::getCxxOpaqueStorageClassName() {
22+
return "OpaqueStorage";
23+
}
24+
2125
bool ClangSyntaxPrinter::isClangKeyword(StringRef name) {
2226
static const llvm::DenseSet<StringRef> keywords = [] {
2327
llvm::DenseSet<StringRef> set;

lib/PrintAsClang/ClangSyntaxPrinter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ namespace cxx_synthesis {
2929
/// module in C++.
3030
StringRef getCxxImplNamespaceName();
3131

32+
/// Return the name of the C++ class inside of `swift::_impl`
33+
/// namespace that holds an opaque value, like a resilient struct.
34+
StringRef getCxxOpaqueStorageClassName();
35+
3236
} // end namespace cxx_synthesis
3337

3438
class ClangSyntaxPrinter {

lib/PrintAsClang/PrintAsClang.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx,
103103
"#include <cstddef>\n"
104104
"#include <cstdbool>\n"
105105
"#include <cstring>\n";
106+
out << "#include <stdlib.h>\n";
107+
out << "#if defined(_WIN32)\n#include <malloc.h>\n#endif\n";
106108
},
107109
[&] {
108110
out << "#include <stdint.h>\n"

lib/PrintAsClang/PrintClangFunction.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,14 @@ bool isKnownType(Type t, PrimitiveTypeMapping &typeMapping,
5151
return getKnownTypeInfo(typeDecl, typeMapping, languageMode) != None;
5252
}
5353

54+
bool isResilientType(Type t) {
55+
if (auto *typeAliasType = dyn_cast<TypeAliasType>(t.getPointer()))
56+
return isResilientType(typeAliasType->getSinglyDesugaredType());
57+
else if (auto *nominalType = t->getNominalOrBoundGenericNominal())
58+
return nominalType->isResilient();
59+
return false;
60+
}
61+
5462
bool isKnownCxxType(Type t, PrimitiveTypeMapping &typeMapping) {
5563
return isKnownType(t, typeMapping, OutputLanguageMode::Cxx);
5664
}
@@ -135,7 +143,8 @@ class CFunctionSignatureTypePrinter
135143
// FIXME: Handle optional structures.
136144
if (typeUseKind == FunctionSignatureTypeUse::ParamType) {
137145
if (languageMode != OutputLanguageMode::Cxx &&
138-
interopContext.getIrABIDetails().shouldPassIndirectly(ST)) {
146+
(SD->isResilient() ||
147+
interopContext.getIrABIDetails().shouldPassIndirectly(ST))) {
139148
if (modifiersDelegate.prefixIndirectParamValueTypeInC)
140149
(*modifiersDelegate.prefixIndirectParamValueTypeInC)(os);
141150
// FIXME: it would be nice to print out the C struct type here.
@@ -198,7 +207,8 @@ void DeclAndTypeClangFunctionPrinter::printFunctionSignature(
198207
bool isIndirectReturnType =
199208
kind == FunctionSignatureKind::CFunctionProto &&
200209
!isKnownCType(resultTy, typeMapping) &&
201-
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
210+
(isResilientType(resultTy) ||
211+
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy));
202212
if (!isIndirectReturnType) {
203213
OptionalTypeKind retKind;
204214
Type objTy;
@@ -285,7 +295,8 @@ void DeclAndTypeClangFunctionPrinter::printCxxToCFunctionParameterUse(
285295
if (auto *structDecl = type->getStructOrBoundGenericStruct()) {
286296
ClangValueTypePrinter(os, cPrologueOS, typeMapping, interopContext)
287297
.printParameterCxxToCUseScaffold(
288-
interopContext.getIrABIDetails().shouldPassIndirectly(type),
298+
structDecl->isResilient() ||
299+
interopContext.getIrABIDetails().shouldPassIndirectly(type),
289300
structDecl, namePrinter, isInOut,
290301
/*isSelf=*/paramRole &&
291302
*paramRole == AdditionalParam::Role::Self);
@@ -352,6 +363,7 @@ void DeclAndTypeClangFunctionPrinter::printCxxThunkBody(
352363
if (!isKnownCxxType(resultTy, typeMapping)) {
353364
if (auto *structDecl = resultTy->getStructOrBoundGenericStruct()) {
354365
bool isIndirect =
366+
structDecl->isResilient() ||
355367
interopContext.getIrABIDetails().shouldReturnIndirectly(resultTy);
356368
ClangValueTypePrinter valueTypePrinter(os, cPrologueOS, typeMapping,
357369
interopContext);

lib/PrintAsClang/PrintClangValueType.cpp

Lines changed: 58 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,18 @@ void printCTypeMetadataTypeFunction(raw_ostream &os,
7676
void ClangValueTypePrinter::printValueTypeDecl(
7777
const NominalTypeDecl *typeDecl,
7878
llvm::function_ref<void(void)> bodyPrinter) {
79-
auto typeSizeAlign =
80-
interopContext.getIrABIDetails().getTypeSizeAlignment(typeDecl);
81-
if (!typeSizeAlign) {
82-
// FIXME: handle non-fixed layout structs.
83-
return;
84-
}
85-
if (typeSizeAlign->size == 0) {
86-
// FIXME: How to represent 0 sized structs?
87-
return;
79+
llvm::Optional<IRABIDetailsProvider::SizeAndAlignment> typeSizeAlign;
80+
if (!typeDecl->isResilient()) {
81+
82+
typeSizeAlign =
83+
interopContext.getIrABIDetails().getTypeSizeAlignment(typeDecl);
84+
assert(typeSizeAlign && "unknown layout for non-resilient type!");
85+
if (typeSizeAlign->size == 0) {
86+
// FIXME: How to represent 0 sized structs?
87+
return;
88+
}
8889
}
90+
bool isOpaqueLayout = !typeSizeAlign.hasValue();
8991

9092
ClangSyntaxPrinter printer(os);
9193

@@ -138,6 +140,11 @@ void ClangValueTypePrinter::printValueTypeDecl(
138140
os << " auto *vwTable = ";
139141
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
140142
os << ";\n";
143+
if (isOpaqueLayout) {
144+
os << " _storage = ";
145+
printer.printSwiftImplQualifier();
146+
os << cxx_synthesis::getCxxOpaqueStorageClassName() << "(vwTable);\n";
147+
}
141148
os << " vwTable->initializeWithCopy(_getOpaquePointer(), const_cast<char "
142149
"*>(other._getOpaquePointer()), metadata._0);\n";
143150
os << " }\n";
@@ -156,23 +163,55 @@ void ClangValueTypePrinter::printValueTypeDecl(
156163
// Print out private default constructor.
157164
os << " inline ";
158165
printer.printBaseName(typeDecl);
159-
os << "() {}\n";
166+
if (isOpaqueLayout) {
167+
os << "(";
168+
printer.printSwiftImplQualifier();
169+
os << "ValueWitnessTable * _Nonnull vwTable) : _storage(vwTable) {}\n";
170+
} else {
171+
os << "() {}\n";
172+
}
160173
// Print out '_make' function which returns an unitialized instance for
161174
// passing to Swift.
162175
os << " static inline ";
163176
printer.printBaseName(typeDecl);
164-
os << " _make() { return ";
165-
printer.printBaseName(typeDecl);
166-
os << "(); }\n";
177+
os << " _make() {";
178+
if (isOpaqueLayout) {
179+
os << "\n";
180+
os << " auto metadata = " << cxx_synthesis::getCxxImplNamespaceName()
181+
<< "::";
182+
printer.printSwiftTypeMetadataAccessFunctionCall(typeMetadataFuncName);
183+
os << ";\n";
184+
os << " return ";
185+
printer.printBaseName(typeDecl);
186+
os << "(";
187+
printer.printValueWitnessTableAccessFromTypeMetadata("metadata");
188+
os << ");\n }\n";
189+
} else {
190+
os << " return ";
191+
printer.printBaseName(typeDecl);
192+
os << "(); }\n";
193+
}
167194
// Print out the private accessors to the underlying Swift value storage.
168195
os << " inline const char * _Nonnull _getOpaquePointer() const { return "
169-
"_storage; }\n";
170-
os << " inline char * _Nonnull _getOpaquePointer() { return _storage; }\n";
196+
"_storage";
197+
if (isOpaqueLayout)
198+
os << ".getOpaquePointer()";
199+
os << "; }\n";
200+
os << " inline char * _Nonnull _getOpaquePointer() { return _storage";
201+
if (isOpaqueLayout)
202+
os << ".getOpaquePointer()";
203+
os << "; }\n";
171204
os << "\n";
172205

173206
// Print out the storage for the value type.
174-
os << " alignas(" << typeSizeAlign->alignment << ") ";
175-
os << "char _storage[" << typeSizeAlign->size << "];\n";
207+
os << " ";
208+
if (isOpaqueLayout) {
209+
printer.printSwiftImplQualifier();
210+
os << cxx_synthesis::getCxxOpaqueStorageClassName() << " _storage;\n";
211+
} else {
212+
os << "alignas(" << typeSizeAlign->alignment << ") ";
213+
os << "char _storage[" << typeSizeAlign->size << "];\n";
214+
}
176215
// Wrap up the value type.
177216
os << " friend class " << cxx_synthesis::getCxxImplNamespaceName() << "::";
178217
printCxxImplClassName(os, typeDecl);
@@ -209,7 +248,8 @@ void ClangValueTypePrinter::printValueTypeDecl(
209248
os << "};\n";
210249
});
211250

212-
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
251+
if (!isOpaqueLayout)
252+
printCValueTypeStorageStruct(cPrologueOS, typeDecl, *typeSizeAlign);
213253
}
214254

215255
/// Print the name of the C stub struct for passing/returning a value type

lib/PrintAsClang/PrintSwiftToClangCoreScaffold.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,63 @@ static void printTypeMetadataResponseType(SwiftToClangInteropContext &ctx,
118118
funcSig.parameterTypes[0]);
119119
}
120120

121+
static void printOpaqueAllocFee(raw_ostream &os) {
122+
os << R"text(inline void * _Nonnull opaqueAlloc(size_t size, size_t align) {
123+
#if defined(_WIN32)
124+
void *r = _aligned_malloc(size, align);
125+
#else
126+
if (align < sizeof(void *)) align = sizeof(void *);
127+
void *r = nullptr;
128+
int res = posix_memalign(&r, align, size);
129+
(void)res;
130+
#endif
131+
return r;
132+
}
133+
inline void opaqueFree(void * _Nonnull p) {
134+
#if defined(_WIN32)
135+
_aligned_free(p);
136+
#else
137+
free(p);
138+
#endif
139+
}
140+
)text";
141+
}
142+
143+
static void printSwiftResilientStorageClass(raw_ostream &os) {
144+
auto name = cxx_synthesis::getCxxOpaqueStorageClassName();
145+
static_assert(TargetValueWitnessFlags<uint64_t>::AlignmentMask ==
146+
TargetValueWitnessFlags<uint32_t>::AlignmentMask,
147+
"alignment mask doesn't match");
148+
os << "/// Container for an opaque Swift value, like resilient struct.\n";
149+
os << "class " << name << " {\n";
150+
os << "public:\n";
151+
os << " inline " << name << "() noexcept : storage(nullptr) { }\n";
152+
os << " inline " << name
153+
<< "(ValueWitnessTable * _Nonnull vwTable) noexcept : storage("
154+
"reinterpret_cast<char *>(opaqueAlloc(vwTable->size, (vwTable->flags &"
155+
<< TargetValueWitnessFlags<uint64_t>::AlignmentMask << ") + 1))) { }\n";
156+
os << " inline " << name << "(" << name
157+
<< "&& other) noexcept : storage(other.storage) { other.storage = "
158+
"nullptr; }\n";
159+
os << " inline " << name << "(const " << name << "&) noexcept = delete;\n";
160+
os << " inline ~" << name
161+
<< "() noexcept { if (storage) { opaqueFree(static_cast<char "
162+
"* _Nonnull>(storage)); } }\n";
163+
os << " void operator =(" << name
164+
<< "&& other) noexcept { auto temp = storage; storage = other.storage; "
165+
"other.storage = temp; }\n";
166+
os << " void operator =(const " << name << "&) noexcept = delete;\n";
167+
os << " inline char * _Nonnull getOpaquePointer() noexcept { return "
168+
"static_cast<char "
169+
"* _Nonnull>(storage); }\n";
170+
os << " inline const char * _Nonnull getOpaquePointer() const noexcept { "
171+
"return "
172+
"static_cast<char * _Nonnull>(storage); }\n";
173+
os << "private:\n";
174+
os << " char * _Nullable storage;\n";
175+
os << "};\n";
176+
}
177+
121178
void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
122179
PrimitiveTypeMapping &typeMapping,
123180
raw_ostream &os) {
@@ -130,6 +187,10 @@ void swift::printSwiftToClangCoreScaffold(SwiftToClangInteropContext &ctx,
130187
os << "\n";
131188
printValueWitnessTable(os);
132189
});
190+
os << "\n";
191+
printOpaqueAllocFee(os);
192+
os << "\n";
193+
printSwiftResilientStorageClass(os);
133194
});
134195
});
135196
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#if defined(_WIN32) || defined(WIN32)
2+
#define SDK_STDLIB_H
3+
#endif
4+
5+
#ifndef SDK_STDLIB_H
6+
#define SDK_STDLIB_H
7+
8+
#include <stdint.h>
9+
10+
typedef long ldiv_t;
11+
typedef long long lldiv_t;
12+
13+
int posix_memalign(void **, size_t, size_t);
14+
void free(void *);
15+
16+
ldiv_t ldiv(long int, long int);
17+
lldiv_t lldiv(long long int, long long int);
18+
19+
#endif // SDK_STDLIB_H

test/Interop/SwiftToCxx/core/swift-impl-defs-in-cxx.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,41 @@
5151
// CHECK-NEXT: }
5252
// CHECK-NEXT: #endif
5353
// CHECK-EMPTY:
54+
// CHECK-NEXT: inline void * _Nonnull opaqueAlloc(size_t size, size_t align) {
55+
// CHECK-NEXT: #if defined(_WIN32)
56+
// CHECK-NEXT: void *r = _aligned_malloc(size, align);
57+
// CHECK-NEXT: #else
58+
// CHECK-NEXT: if (align < sizeof(void *)) align = sizeof(void *);
59+
// CHECK-NEXT: void *r = nullptr;
60+
// CHECK-NEXT: int res = posix_memalign(&r, align, size);
61+
// CHECK-NEXT: (void)res;
62+
// CHECK-NEXT: #endif
63+
// CHECK-NEXT: return r;
64+
// CHECK-NEXT: }
65+
// CHECK-NEXT: inline void opaqueFree(void * _Nonnull p) {
66+
// CHECK-NEXT: #if defined(_WIN32)
67+
// CHECK-NEXT: _aligned_free(p);
68+
// CHECK-NEXT: #else
69+
// CHECK-NEXT: free(p);
70+
// CHECK-NEXT: #endif
71+
// CHECK-NEXT: }
72+
// CHECK-EMPTY:
73+
// CHECK-NEXT: /// Container for an opaque Swift value, like resilient struct.
74+
// CHECK-NEXT: class OpaqueStorage {
75+
// CHECK-NEXT: public:
76+
// CHECK-NEXT: inline OpaqueStorage() noexcept : storage(nullptr) { }
77+
// CHECK-NEXT: inline OpaqueStorage(ValueWitnessTable * _Nonnull vwTable) noexcept : storage(reinterpret_cast<char *>(opaqueAlloc(vwTable->size, (vwTable->flags &255) + 1))) { }
78+
// CHECK-NEXT: inline OpaqueStorage(OpaqueStorage&& other) noexcept : storage(other.storage) { other.storage = nullptr; }
79+
// CHECK-NEXT: inline OpaqueStorage(const OpaqueStorage&) noexcept = delete;
80+
// CHECK-NEXT: inline ~OpaqueStorage() noexcept { if (storage) { opaqueFree(static_cast<char * _Nonnull>(storage)); } }
81+
// CHECK-NEXT: void operator =(OpaqueStorage&& other) noexcept { auto temp = storage; storage = other.storage; other.storage = temp; }
82+
// CHECK-NEXT: void operator =(const OpaqueStorage&) noexcept = delete;
83+
// CHECK-NEXT: inline char * _Nonnull getOpaquePointer() noexcept { return static_cast<char * _Nonnull>(storage); }
84+
// CHECK-NEXT: inline const char * _Nonnull getOpaquePointer() const noexcept { return static_cast<char * _Nonnull>(storage); }
85+
// CHECK-NEXT: private:
86+
// CHECK-NEXT: char * _Nullable storage;
87+
// CHECK-NEXT: };
88+
// CHECK-EMPTY:
5489
// CHECK-NEXT: } // namespace _impl
5590
// CHECK-EMPTY:
5691
// CHECK-EMPTY:
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-swift-frontend %S/resilient-struct-in-cxx.swift -enable-library-evolution -typecheck -module-name Structs -clang-header-expose-public-decls -emit-clang-header-path %t/structs.h
4+
5+
// RUN: %target-interop-build-clangxx -c %s -I %t -o %t/swift-structs-execution.o
6+
7+
// RUN: %target-interop-build-swift %S/resilient-struct-in-cxx.swift -enable-library-evolution -o %t/swift-structs-execution -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain
8+
9+
// RUN: %target-codesign %t/swift-structs-execution
10+
// RUN: %target-run %t/swift-structs-execution | %FileCheck --check-prefixes=CHECK,CURRENT %s
11+
12+
// RUN: %target-interop-build-swift %S/resilient-struct-in-cxx.swift -enable-library-evolution -o %t/swift-structs-execution-new -Xlinker %t/swift-structs-execution.o -module-name Structs -Xfrontend -entry-point-function-name -Xfrontend swiftMain -D CHANGE_LAYOUT
13+
14+
// RUN: %target-codesign %t/swift-structs-execution-new
15+
// RUN: %target-run %t/swift-structs-execution-new | %FileCheck --check-prefixes=CHECK,CHANGE %s
16+
17+
// REQUIRES: executable_test
18+
19+
#include <assert.h>
20+
#include "structs.h"
21+
22+
int main() {
23+
using namespace Structs;
24+
25+
auto largeStruct = createLargeStruct(11);
26+
assert(largeStruct.getX1() == 11);
27+
largeStruct.dump();
28+
// CHECK: x.1 = 11, .2 = -11, .3 = 22, .4 = 7, .5 = 0
29+
30+
auto smallStruct = largeStruct.getFirstSmallStruct();
31+
assert(smallStruct.getX() == 65);
32+
smallStruct.dump();
33+
// CHECK: find - small dump
34+
// CURRENT-NEXT: x = 65
35+
// CHANGE-NEXT: x&y = 65&0
36+
37+
auto copySmallStruct = smallStruct;
38+
mutateSmall(copySmallStruct);
39+
copySmallStruct.dump();
40+
// CHECK: find - small dump
41+
// CURRENT-NEXT: x = 66
42+
// CHANGE-NEXT: x&y = 0&65
43+
44+
printSmallAndLarge(smallStruct, largeStruct);
45+
// CHECK: find - small dump
46+
// CURRENT-NEXT: x = 65
47+
// CHANGE-NEXT: x&y = 65&0
48+
// CHECK-NEXT: x.1 = 11, .2 = -11, .3 = 22, .4 = 7, .5 = 0
49+
50+
{
51+
auto structWithRefCountStoredProp =
52+
createStructWithRefCountStoredProp();
53+
structWithRefCountStoredProp.dump();
54+
{
55+
StructWithRefCountStoredProp copy(structWithRefCountStoredProp);
56+
}
57+
structWithRefCountStoredProp.dump();
58+
}
59+
// CHECK-NEXT: create RefCountedClass 0
60+
// CHECK-NEXT: storedRef = 0
61+
// CHECK-NEXT: storedRef = 0
62+
// CHECK-NEXT: destroy RefCountedClass 0
63+
return 0;
64+
}

0 commit comments

Comments
 (0)