Skip to content

Commit b2c5a3e

Browse files
martinboehmezoecarver
authored andcommitted
Add a constructor thunk if required to add additional constructor
arguments. Also add more IR tests and make various other changes.
1 parent e606727 commit b2c5a3e

File tree

7 files changed

+182
-37
lines changed

7 files changed

+182
-37
lines changed

lib/ClangImporter/ImportDecl.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,14 +3439,19 @@ namespace {
34393439

34403440
bool hasReferenceableFields = !members.empty();
34413441

3442-
if (hasZeroInitializableStorage &&
3443-
!Impl.SwiftContext.LangOpts.EnableCXXInterop) {
3444-
// Add constructors for the struct.
3442+
const clang::CXXRecordDecl *cxxRecordDecl =
3443+
dyn_cast<clang::CXXRecordDecl>(decl);
3444+
if (hasZeroInitializableStorage && !cxxRecordDecl) {
3445+
// Add default constructor for the struct if compiling in C mode.
3446+
// If we're compiling for C++, we'll import the C++ default constructor
3447+
// (if there is one), so we don't need to synthesize one here.
34453448
ctors.push_back(createDefaultConstructor(Impl, result));
34463449
}
34473450

3451+
bool hasUserDeclaredConstructor =
3452+
cxxRecordDecl && cxxRecordDecl->hasUserDeclaredConstructor();
34483453
if (hasReferenceableFields && hasMemberwiseInitializer &&
3449-
!Impl.SwiftContext.LangOpts.EnableCXXInterop) {
3454+
!hasUserDeclaredConstructor) {
34503455
// The default zero initializer suppresses the implicit value
34513456
// constructor that would normally be formed, so we have to add that
34523457
// explicitly as well.
@@ -3893,7 +3898,6 @@ namespace {
38933898

38943899
AbstractFunctionDecl *result = nullptr;
38953900
if (auto *ctordecl = dyn_cast<clang::CXXConstructorDecl>(decl)) {
3896-
// TODO: Is failable, throws etc. correct?
38973901
DeclName ctorName(Impl.SwiftContext, DeclBaseName::createConstructor(),
38983902
bodyParams);
38993903
result = Impl.createDeclWithClangNode<ConstructorDecl>(

lib/IRGen/GenCall.cpp

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -430,7 +430,7 @@ namespace {
430430

431431
private:
432432
void expand(SILParameterInfo param);
433-
llvm::Type *addIndirectResult(bool noCapture = true);
433+
llvm::Type *addIndirectResult();
434434

435435
SILFunctionConventions getSILFuncConventions() const {
436436
return SILFunctionConventions(FnType, IGM.getSILModule());
@@ -484,8 +484,7 @@ llvm::Type *SignatureExpansion::addIndirectResult() {
484484
auto resultType = getSILFuncConventions().getSILResultType(
485485
IGM.getMaximalTypeExpansionContext());
486486
const TypeInfo &resultTI = IGM.getTypeInfo(resultType);
487-
addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet(),
488-
noCapture);
487+
addIndirectResultAttributes(IGM, Attrs, ParamIRTypes.size(), claimSRet());
489488
addPointerParameter(resultTI.getStorageType());
490489
return IGM.VoidTy;
491490
}
@@ -1272,6 +1271,16 @@ void SignatureExpansion::expandExternalSignatureTypes() {
12721271
SmallVector<clang::CanQualType,4> paramTys;
12731272
auto const &clangCtx = IGM.getClangASTContext();
12741273

1274+
bool formalIndirectResult = FnType->getNumResults() > 0 &&
1275+
FnType->getSingleResult().isFormalIndirect();
1276+
if (formalIndirectResult) {
1277+
auto resultType = getSILFuncConventions().getSingleSILResultType(
1278+
IGM.getMaximalTypeExpansionContext());
1279+
auto clangTy =
1280+
IGM.getClangASTContext().getPointerType(IGM.getClangType(resultType));
1281+
paramTys.push_back(clangTy);
1282+
}
1283+
12751284
switch (FnType->getRepresentation()) {
12761285
case SILFunctionTypeRepresentation::ObjCMethod: {
12771286
// ObjC methods take their 'self' argument first, followed by an
@@ -1336,17 +1345,8 @@ void SignatureExpansion::expandExternalSignatureTypes() {
13361345
}
13371346

13381347
// If we return indirectly, that is the first parameter type.
1339-
bool formalIndirect = FnType->getNumResults() > 0 &&
1340-
FnType->getSingleResult().isFormalIndirect();
1341-
if (formalIndirect || returnInfo.isIndirect()) {
1342-
// Specify "nocapture" if we're returning the result indirectly for
1343-
// low-level ABI reasons, as the called function never sees the implicit
1344-
// output parameter.
1345-
// On the other hand, if the result is a formal indirect result in SIL, that
1346-
// means that the Clang function has an explicit output parameter (e.g. it's
1347-
// a C++ constructor), which it might capture -- so don't specify
1348-
// "nocapture" in that case.
1349-
addIndirectResult(/* noCapture = */ returnInfo.isIndirect());
1348+
if (returnInfo.isIndirect()) {
1349+
addIndirectResult();
13501350
}
13511351

13521352
size_t firstParamToLowerNormally = 0;
@@ -1432,6 +1432,13 @@ void SignatureExpansion::expandExternalSignatureTypes() {
14321432
}
14331433
}
14341434

1435+
if (formalIndirectResult) {
1436+
// If the result is a formal indirect result in SIL, that means that the
1437+
// Clang function has an explicit output parameter (e.g. it's a C++
1438+
// constructor), which it might capture -- so don't specify "nocapture".
1439+
addIndirectResultAttributes(IGM, Attrs, 0, claimSRet(), /* nocapture = */ false);
1440+
}
1441+
14351442
if (returnInfo.isIndirect() || returnInfo.isIgnore()) {
14361443
ResultIRType = IGM.VoidTy;
14371444
} else {
@@ -2812,6 +2819,11 @@ static void externalizeArguments(IRGenFunction &IGF, const Callee &callee,
28122819
== SILFunctionTypeRepresentation::Block) {
28132820
// Ignore the physical block-object parameter.
28142821
firstParam += 1;
2822+
// Or the indirect result parameter.
2823+
} else if (fnType->getNumResults() > 0 &&
2824+
fnType->getSingleResult().isFormalIndirect()) {
2825+
// Ignore the indirect result parameter.
2826+
firstParam += 1;
28152827
}
28162828

28172829
for (unsigned i = firstParam, e = FI.arg_size(); i != e; ++i) {

lib/IRGen/GenDecl.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
#include "clang/AST/ASTContext.h"
3939
#include "clang/AST/DeclCXX.h"
4040
#include "clang/AST/GlobalDecl.h"
41+
#include "clang/CodeGen/CodeGenABITypes.h"
42+
#include "clang/CodeGen/ModuleBuilder.h"
4143
#include "llvm/ADT/SmallString.h"
4244
#include "llvm/IR/DerivedTypes.h"
4345
#include "llvm/IR/GlobalAlias.h"
@@ -2761,6 +2763,77 @@ void IRGenModule::emitDynamicReplacementOriginalFunctionThunk(SILFunction *f) {
27612763
IGF.Builder.CreateRet(Res);
27622764
}
27632765

2766+
/// If the calling convention for `ctor` doesn't match the calling convention
2767+
/// that we assumed for it when we imported it as `initializer`, emit and
2768+
/// return a thunk that conforms to the assumed calling convention. The thunk
2769+
/// is marked `alwaysinline`, so it doesn't generate any runtime overhead.
2770+
/// If the assumed calling convention was correct, just return `ctor`.
2771+
///
2772+
/// See also comments in CXXMethodConventions in SIL/IR/SILFunctionType.cpp.
2773+
static llvm::Constant *
2774+
emitCXXConstructorThunkIfNeeded(IRGenModule &IGM, SILFunction *initializer,
2775+
const clang::CXXConstructorDecl *ctor,
2776+
const LinkEntity &entity,
2777+
llvm::Constant *ctorAddress) {
2778+
Signature signature = IGM.getSignature(initializer->getLoweredFunctionType());
2779+
2780+
llvm::FunctionType *assumedFnType = signature.getType();
2781+
llvm::FunctionType *ctorFnType =
2782+
cast<llvm::FunctionType>(ctorAddress->getType()->getPointerElementType());
2783+
2784+
if (assumedFnType == ctorFnType) {
2785+
return ctorAddress;
2786+
}
2787+
2788+
// The thunk has private linkage, so it doesn't need to have a predictable
2789+
// mangled name -- we just need to make sure the name is unique.
2790+
llvm::SmallString<32> name;
2791+
llvm::raw_svector_ostream stream(name);
2792+
stream << "__swift_cxx_ctor";
2793+
entity.mangle(stream);
2794+
2795+
llvm::Function *thunk = llvm::Function::Create(
2796+
assumedFnType, llvm::Function::PrivateLinkage, name, &IGM.Module);
2797+
2798+
thunk->setCallingConv(llvm::CallingConv::C);
2799+
2800+
llvm::AttrBuilder attrBuilder;
2801+
IGM.constructInitialFnAttributes(attrBuilder);
2802+
attrBuilder.addAttribute(llvm::Attribute::AlwaysInline);
2803+
llvm::AttributeList attr = signature.getAttributes().addAttributes(
2804+
IGM.getLLVMContext(), llvm::AttributeList::FunctionIndex, attrBuilder);
2805+
thunk->setAttributes(attr);
2806+
2807+
IRGenFunction subIGF(IGM, thunk);
2808+
if (IGM.DebugInfo)
2809+
IGM.DebugInfo->emitArtificialFunction(subIGF, thunk);
2810+
2811+
SmallVector<llvm::Value *, 8> Args;
2812+
for (auto i = thunk->arg_begin(), e = thunk->arg_end(); i != e; ++i) {
2813+
auto *argTy = i->getType();
2814+
auto *paramTy = ctorFnType->getParamType(i - thunk->arg_begin());
2815+
if (paramTy != argTy)
2816+
Args.push_back(subIGF.coerceValue(i, paramTy, IGM.DataLayout));
2817+
else
2818+
Args.push_back(i);
2819+
}
2820+
2821+
clang::CodeGen::ImplicitCXXConstructorArgs implicitArgs =
2822+
clang::CodeGen::getImplicitCXXConstructorArgs(IGM.ClangCodeGen->CGM(),
2823+
ctor);
2824+
for (size_t i = 0; i < implicitArgs.Prefix.size(); ++i) {
2825+
Args.insert(Args.begin() + 1 + i, implicitArgs.Prefix[i]);
2826+
}
2827+
for (const auto &arg : implicitArgs.Suffix) {
2828+
Args.push_back(arg);
2829+
}
2830+
2831+
subIGF.Builder.CreateCall(ctorFnType, ctorAddress, Args);
2832+
subIGF.Builder.CreateRetVoid();
2833+
2834+
return thunk;
2835+
}
2836+
27642837
/// Find the entry point for a SIL function.
27652838
llvm::Function *IRGenModule::getAddrOfSILFunction(
27662839
SILFunction *f, ForDefinition_t forDefinition,
@@ -2789,6 +2862,11 @@ llvm::Function *IRGenModule::getAddrOfSILFunction(
27892862
if (auto clangDecl = f->getClangDecl()) {
27902863
auto globalDecl = getClangGlobalDeclForFunction(clangDecl);
27912864
clangAddr = getAddrOfClangGlobalDecl(globalDecl, forDefinition);
2865+
2866+
if (auto ctor = dyn_cast<clang::CXXConstructorDecl>(clangDecl)) {
2867+
clangAddr =
2868+
emitCXXConstructorThunkIfNeeded(*this, f, ctor, entity, clangAddr);
2869+
}
27922870
}
27932871

27942872
bool isDefinition = f->isDefinition();

test/Interop/Cxx/class/Inputs/cxx-constructors.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,18 @@ struct DefaultConstructorDeleted {
1717
};
1818

1919
struct ConstructorWithParam {
20-
ConstructorWithParam(int val) : x(val) {}
20+
ConstructorWithParam(int val) : x(val + 42) {}
2121
int x;
2222
};
23+
24+
struct Base {};
25+
26+
struct ArgType {
27+
int i = 42;
28+
};
29+
30+
struct HasVirtualBase : public virtual Base {
31+
HasVirtualBase() = delete;
32+
HasVirtualBase(ArgType Arg) {}
33+
int i;
34+
};

test/Interop/Cxx/class/cxx-constructors-executable.swift

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
1-
// RUN: %empty-directory(%t)
2-
// RUN: %target-build-swift %s -I %S/Inputs/ -o %t/cxx_interop -Xfrontend -enable-cxx-interop
3-
// RUN: %target-codesign %t/cxx_interop
4-
// RUN: %target-run %t/cxx_interop
1+
// RUN: %target-run-simple-swift(-I %S/Inputs/ -Xfrontend -enable-cxx-interop)
52
//
63
// REQUIRES: executable_test
74

@@ -29,9 +26,9 @@ CxxConstructorTestSuite.test("MemberOfClassType") {
2926
}
3027

3128
CxxConstructorTestSuite.test("ConstructorWithParam") {
32-
let instance = ConstructorWithParam(123456)
29+
let instance = ConstructorWithParam(2)
3330

34-
expectEqual(123456, instance.x)
31+
expectEqual(44, instance.x)
3532
}
3633

3734
runAllTests()
Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,42 @@
1-
// RUN: %target-swift-frontend -I %S/Inputs -enable-cxx-interop -emit-ir %s | %FileCheck %s
1+
// Target-specific tests for C++ constructor call code generation.
2+
3+
// RUN: %swift -module-name Swift -target x86_64-apple-macosx10.9 -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_X64
4+
// RUN: %swift -module-name Swift -target armv7-none-linux-androideabi -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=ITANIUM_ARM
5+
// RUN: %swift -module-name Swift -target x86_64-unknown-windows-msvc -dump-clang-diagnostics -I %S/Inputs -enable-cxx-interop -emit-ir %s -parse-stdlib -parse-as-library -disable-legacy-type-info | %FileCheck %s -check-prefix=MICROSOFT_X64
26

37
import CxxConstructors
48

5-
// Note:
6-
// - The `this` parameter should carry a `noalias` attribute, as it is
7-
// guaranteed that nothing will alias the object before it has been fully
8-
// constructed. It should also carry an `sret` attribute to indicate that this
9-
// is an out parameter for a structure being returned by the function.
10-
// - The `this` parameter should _not_ carry a `nocapture` attribute (unlike
11-
// Swift constructors that return their result indirectly) because the C++ has
12-
// explicit access to `this` and may capture it.
13-
// CHECK: call void @_ZN20ConstructorWithParamC2Ei(%struct.ConstructorWithParam* noalias sret %{{[0-9]+}}, i32 42)
14-
let _ = ConstructorWithParam(42)
9+
typealias Void = ()
10+
11+
public func createHasVirtualBase() -> HasVirtualBase {
12+
// - The `this` parameter should carry a `noalias` attribute, as it is
13+
// guaranteed that nothing will alias the object before it has been fully
14+
// constructed. It should also carry an `sret` attribute to indicate that
15+
// this is an out parameter for a structure being returned by the function.
16+
// Note that this doesn't apply on ABIs (Itanium ARM, Microsoft x64)
17+
// where we insert an (inlined) thunk; in this case, we're getting the
18+
// attributes of the constructor that was generated by Clang, which doesn't
19+
// insert these attributes.
20+
//
21+
// - The `this` parameter should _not_ carry a `nocapture` attribute (unlike
22+
// Swift constructors that return their result indirectly) because the C++
23+
// constructor has explicit access to `this` and may capture it.
24+
//
25+
// ITANIUM_X64: define swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"()
26+
// ITANIUM_X64-NOT: define
27+
// ITANIUM_X64: call void @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* noalias sret %{{[0-9]+}}, i32 %{{[0-9]+}})
28+
//
29+
// ITANIUM_ARM: define protected swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"()
30+
// To verify that the thunk is inlined, make sure there's no intervening
31+
// `define`, i.e. the call to the C++ constructor happens in
32+
// createHasVirtualBase(), not some later function.
33+
// ITANIUM_ARM-NOT: define
34+
// Note `this` return type.
35+
// ITANIUM_ARM: call %struct.HasVirtualBase* @_ZN14HasVirtualBaseC1E7ArgType(%struct.HasVirtualBase* %{{[0-9]+}}, [1 x i32] %{{[0-9]+}})
36+
//
37+
// MICROSOFT_X64: define dllexport swiftcc { i8*, i32 } @"$ss20createHasVirtualBaseSo0bcD0VyF"()
38+
// MICROSOFT_X64-NOT: define
39+
// Note `this` return type and implicit "most derived" argument.
40+
// MICROSOFT_X64: call %struct.HasVirtualBase* @"??0HasVirtualBase@@QEAA@UArgType@@@Z"(%struct.HasVirtualBase* %{{[0-9]+}}, i32 %{{[0-9]+}}, i32 1)
41+
return HasVirtualBase(ArgType())
42+
}

test/Interop/Cxx/class/cxx-constructors-module-interface.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,28 @@
77
// CHECK-NEXT: struct ImplicitDefaultConstructor {
88
// CHECK-NEXT: var x: Int32
99
// CHECK-NEXT: init()
10+
// CHECK-NEXT: init(x: Int32)
1011
// CHECK-NEXT: }
1112
// CHECK-NEXT: struct MemberOfClassType {
1213
// CHECK-NEXT: var member: ImplicitDefaultConstructor
1314
// CHECK-NEXT: init()
15+
// CHECK-NEXT: init(member: ImplicitDefaultConstructor)
1416
// CHECK-NEXT: }
1517
// CHECK-NEXT: struct DefaultConstructorDeleted {
1618
// CHECK-NEXT: }
1719
// CHECK-NEXT: struct ConstructorWithParam {
1820
// CHECK-NEXT: var x: Int32
1921
// CHECK-NEXT: init(_ val: Int32)
2022
// CHECK-NEXT: }
23+
// CHECK-NEXT: struct Base {
24+
// CHECK-NEXT: init()
25+
// CHECK-NEXT: }
26+
// CHECK-NEXT: struct ArgType {
27+
// CHECK-NEXT: var i: Int32
28+
// CHECK-NEXT: init()
29+
// CHECK-NEXT: init(i: Int32)
30+
// CHECK-NEXT: }
31+
// CHECK-NEXT: struct HasVirtualBase {
32+
// CHECK-NEXT: var i: Int32
33+
// CHECK-NEXT: init(_ Arg: ArgType)
34+
// CHECK-NEXT: }

0 commit comments

Comments
 (0)