Skip to content

[5.9] More batched changes #66196

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion include/swift/SIL/SILMoveOnlyDeinit.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ class SILMoveOnlyDeinit final : public SILAllocated<SILMoveOnlyDeinit> {

SILFunction *getImplementation() const { return funcImpl; }

bool isSerialized() const { return serialized; }
IsSerialized_t isSerialized() const {
return serialized ? IsSerialized : IsNotSerialized;
}
void setSerialized(IsSerialized_t inputSerialized) {
serialized = inputSerialized ? 1 : 0;
}

void print(llvm::raw_ostream &os, bool verbose) const;
void dump() const;
Expand Down
18 changes: 12 additions & 6 deletions lib/IRGen/GenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2887,19 +2887,25 @@ static bool tryEmitDeinitCall(IRGenFunction &IGF,
llvm::function_ref<void ()> indirectCleanup) {
auto ty = T.getASTType();
auto nominal = ty->getAnyNominal();

// We are only concerned with move-only type deinits here.
if (!nominal || !nominal->getValueTypeDestructor()) {
return false;
}

auto deinit = IGF.getSILModule().lookUpMoveOnlyDeinit(nominal);
assert(deinit && "type has a deinit declared in AST but SIL deinit record is not present!");


auto deinitTable = IGF.getSILModule().lookUpMoveOnlyDeinit(nominal);

// If we do not have a deinit table, call the value witness instead.
if (!deinitTable) {
irgen::emitDestroyCall(IGF, T, indirect());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to call indirectCleanup after the call to indirect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will prepare a small patch on main that will fix this. Why don't you give the +1 and then I can get TimK to +1 this and I can take care of this tonight/over the weekend.

return true;
}

// The deinit should take a single value parameter of the nominal type, either
// by @owned or indirect @in convention.
auto deinitFn = IGF.IGM.getAddrOfSILFunction(deinit->getImplementation(),
auto deinitFn = IGF.IGM.getAddrOfSILFunction(deinitTable->getImplementation(),
NotForDefinition);
auto deinitTy = deinit->getImplementation()->getLoweredFunctionType();
auto deinitTy = deinitTable->getImplementation()->getLoweredFunctionType();
auto deinitFP = FunctionPointer::forDirect(IGF.IGM, deinitFn,
nullptr, deinitTy);
assert(deinitTy->getNumParameters() == 1
Expand Down
5 changes: 0 additions & 5 deletions lib/SILGen/SILGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,11 +1079,6 @@ void SILGenModule::emitFunctionDefinition(SILDeclRef constant, SILFunction *f) {
preEmitFunction(constant, f, loc);
PrettyStackTraceSILFunction X("silgen emitDeallocatingDestructor", f);
SILGenFunction(*this, *f, dd).emitDeallocatingDestructor(dd);

// If we have a move only type, create the table for this type.
if (nom->isMoveOnly())
SILMoveOnlyDeinit::create(f->getModule(), nom, IsNotSerialized, f);

postEmitFunction(constant, f);
return;
}
Expand Down
3 changes: 3 additions & 0 deletions lib/SILGen/SILGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,9 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor<SILGenModule> {
SILFunction *jvp, SILFunction *vjp,
const DeclAttribute *diffAttr);

/// Emit a deinit table for a noncopyable type.
void emitNonCopyableTypeDeinitTable(NominalTypeDecl *decl);

/// Known functions for bridging.
SILDeclRef getStringToNSStringFn();
SILDeclRef getNSStringToStringFn();
Expand Down
22 changes: 22 additions & 0 deletions lib/SILGen/SILGenType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,21 @@ void SILGenModule::emitDefaultWitnessTable(ProtocolDecl *protocol) {
defaultWitnesses->convertToDefinition(builder.DefaultWitnesses);
}

void SILGenModule::emitNonCopyableTypeDeinitTable(NominalTypeDecl *nom) {
auto *dd = nom->getValueTypeDestructor();
if (!dd)
return;

SILDeclRef constant(dd, SILDeclRef::Kind::Deallocator);
SILFunction *f = getFunction(constant, NotForDefinition);
auto serialized = IsSerialized_t::IsNotSerialized;
bool nomIsPublic = nom->getEffectiveAccess() >= AccessLevel::Public;
// We only serialize the deinit if the type is public and not resilient.
if (nomIsPublic && !nom->isResilient())
serialized = IsSerialized;
SILMoveOnlyDeinit::create(f->getModule(), nom, serialized, f);
}

namespace {

/// An ASTVisitor for generating SIL from method declarations
Expand Down Expand Up @@ -1100,6 +1115,13 @@ class SILGenType : public TypeMemberVisitor<SILGenType> {
genVTable.emitVTable();
}

// If this is a nominal type that is move only, emit a deinit table for it.
if (auto *nom = dyn_cast<NominalTypeDecl>(theType)) {
if (nom->isMoveOnly()) {
SGM.emitNonCopyableTypeDeinitTable(nom);
}
}

// Build a default witness table if this is a protocol that needs one.
if (auto protocol = dyn_cast<ProtocolDecl>(theType)) {
if (!protocol->isObjC() && protocol->isResilient()) {
Expand Down
4 changes: 4 additions & 0 deletions lib/SILOptimizer/UtilityPasses/SerializeSILPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,10 @@ class SerializeSILPass : public SILModuleTransform {
for (auto &VT : M.getVTables()) {
VT->setSerialized(IsNotSerialized);
}

for (auto &Deinit : M.getMoveOnlyDeinits()) {
Deinit->setSerialized(IsNotSerialized);
}
}

public:
Expand Down
24 changes: 24 additions & 0 deletions test/IRGen/Inputs/moveonly_split_module_source_input.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@

class Klass {
var name = "John"
}

#if TEST_LIBRARY_EVOLUTION
public struct MoveOnly : ~Copyable {
var k = Klass()
var k2 = Klass()
deinit {
print("==> I am in the deinit resiliently!")
print("==> My name is \(k.name)")
}
}
#else
struct MoveOnly : ~Copyable {
var k = Klass()
var k2 = Klass()
deinit {
print("==> I am in the deinit!")
print("==> My name is \(k.name)")
}
}
#endif
30 changes: 30 additions & 0 deletions test/IRGen/moveonly_split_module_source_deinit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// RUN: %empty-directory(%t)
// RUN: %target-swiftc_driver -emit-module -module-name server -emit-module-path %t/server.swiftmodule %s %S/Inputs/moveonly_split_module_source_input.swift
// RUN: %target-swift-frontend -module-name server -primary-file %s %S/Inputs/moveonly_split_module_source_input.swift -emit-ir -emit-module-path %t/server.swiftmodule | %FileCheck %s -check-prefix=REFERRING_MODULE
// RUN: %target-swift-frontend -module-name server %s -primary-file %S/Inputs/moveonly_split_module_source_input.swift -emit-ir -emit-module-path %t/server.swiftmodule | %FileCheck %s -check-prefix=DEFINING_MODULE

// Make sure we call the deinit through the value witness table in the other module.

// REFERRING_MODULE-LABEL: define swiftcc void @"$s6serverAAV4mainyyKFZ"(%swift.refcounted* swiftself %0, %swift.error** noalias nocapture swifterror dereferenceable(8) %1) #0 {
// REFERRING_MODULE: [[SERVER:%.*]] = alloca %T6server8MoveOnlyV
// REFERRING_MODULE: [[VALUE_WITNESS_TABLE:%.*]] = getelementptr inbounds i8*, i8** %"$s6server8MoveOnlyVN.valueWitnesses"
// REFERRING_MODULE: [[VALUE_WITNESS:%.*]] = load i8*, i8** [[VALUE_WITNESS_TABLE]]
// REFERRING_MODULE: [[DESTROY:%.*]] = bitcast i8* [[VALUE_WITNESS]]
// REFERRING_MODULE: [[CAST_SERVER:%.*]] = bitcast %T6server8MoveOnlyV* [[SERVER]]
// REFERRING_MODULE: call void [[DESTROY]]({{%.*}} [[CAST_SERVER]], {{%.*}} @"$s6server8MoveOnlyVN")

// Make sure that in the other module, we do call the deinit directly from the value witness.
// DEFINING_MODULE-LABEL: define internal void @"$s6server8MoveOnlyVwxx"(%swift.opaque* noalias %object, %swift.type* %MoveOnly) {{.*}} {
// DEFINING_MODULE: [[SELF:%.*]] = bitcast %swift.opaque* [[ARG:%.*]] to %T6server8MoveOnlyV*
// DEFINING_MODULE: [[VAR:%.*]] = getelementptr inbounds {{%.*}}, {{%.*}}* [[SELF]]
// DEFINING_MODULE: [[LOADED_VAR:%.*]] = load {{%.*}}*, {{%.*}}** [[VAR]],
// DEFINING_MODULE: [[VAR2:%.*]] = getelementptr inbounds {{%.*}}, {{%.*}}* [[SELF]]
// DEFINING_MODULE: [[LOADED_VAR2:%.*]] = load {{%.*}}*, {{%.*}}** [[VAR2]],
// DEFINING_MODULE: call swiftcc void @"$s6server8MoveOnlyVfD"({{%.*}}* [[LOADED_VAR]], {{%.*}}* [[LOADED_VAR2]])
@main
public struct server {
public static func main() throws {
let server = MoveOnly()
_ = server
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift-dylib(%t/%target-library-name(MoveOnlySplit)) -enable-library-evolution %S/Inputs/moveonly_split_module_source_input.swift -emit-module -emit-module-path %t/MoveOnlySplit.swiftmodule -module-name MoveOnlySplit -DTEST_LIBRARY_EVOLUTION
// RUN: %target-codesign %t/%target-library-name(MoveOnlySplit)

// RUN: %target-build-swift %s -lMoveOnlySplit -I %t -L %t -o %t/main %target-rpath(%t)
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main %t/%target-library-name(MoveOnlySplit) | %FileCheck -check-prefix=CHECK-LIBRARY-EVOLUTION %s

// REQUIRES: executable_test

import MoveOnlySplit

func main() {
let server = MoveOnly() // CHECK-LIBRARY-EVOLUTION: ==> I am in the deinit resiliently!
}

main()
19 changes: 19 additions & 0 deletions test/Interpreter/Inputs/moveonly_split_module_source_input.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@

#if TEST_LIBRARY_EVOLUTION
public struct MoveOnly : ~Copyable {
var name = "John"
public init() {}
deinit {
print("==> I am in the deinit resiliently!")
print("==> My name is: \(name)!")
}
}
#else
struct MoveOnly : ~Copyable {
var name = "John"
deinit {
print("==> I am in the deinit!")
print("==> My name is: \(name)!")
}
}
#endif
16 changes: 16 additions & 0 deletions test/Interpreter/moveonly_split_module_source_deinit.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Normal test.

// RUN: %empty-directory(%t)
// RUN: %target-swiftc_driver -emit-module -module-name server -emit-module-path %t/server.swiftmodule %s %S/Inputs/moveonly_split_module_source_input.swift
// RUN: %target-swiftc_driver -emit-executable -module-name server -emit-module-path %t/server.swiftmodule %s %S/Inputs/moveonly_split_module_source_input.swift -o %t/server -Xlinker -add_ast_path -Xlinker %t/server.swiftmodule -Xlinker -alias -Xlinker _server_main -Xlinker _main
// RUN: %target-codesign %t/server
// RUN: %target-run %t/server | %FileCheck %s

// REQUIRES: executable_test

@main
public struct server {
public static func main() throws {
let server = MoveOnly() // CHECK: ==> I am in the deinit!
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// RUN: %empty-directory(%t)
// RUN: %target-build-swift-dylib(%t/%target-library-name(MoveOnlySplit)) -enable-library-evolution %S/Inputs/moveonly_split_module_source_input.swift -emit-module -emit-module-path %t/MoveOnlySplit.swiftmodule -module-name MoveOnlySplit -DTEST_LIBRARY_EVOLUTION
// RUN: %target-codesign %t/%target-library-name(MoveOnlySplit)

// RUN: %target-build-swift %s -lMoveOnlySplit -I %t -L %t -o %t/main %target-rpath(%t)
// RUN: %target-codesign %t/main
// RUN: %target-run %t/main %t/%target-library-name(MoveOnlySplit) | %FileCheck -check-prefix=CHECK-LIBRARY-EVOLUTION %s

// REQUIRES: executable_test

import MoveOnlySplit

func main() {
let server = MoveOnly() // CHECK-LIBRARY-EVOLUTION: ==> I am in the deinit resiliently!
}

main()
7 changes: 4 additions & 3 deletions test/SILOptimizer/moveonly_lifetime.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// RUN: %target-swift-emit-sil -sil-verify-all -module-name moveonly_lifetime -o /dev/null -Xllvm -sil-print-canonical-module -Onone -verify -enable-experimental-feature MoveOnlyClasses %s 2>&1 | %FileCheck %s

@_moveOnly
class C {}
struct C : ~Copyable {
deinit {}
}

@_silgen_name("getC")
func getC() -> C
Expand Down Expand Up @@ -37,7 +38,7 @@ func something()
// CHECK: apply [[BORROW_C]]([[INSTANCE]])
//
// TODO: Once we maximize lifetimes this should be below something.
// CHECK: [[DESTROY_C:%[^,]+]] = function_ref @$s17moveonly_lifetime1CCfD
// CHECK: [[DESTROY_C:%[^,]+]] = function_ref @$s17moveonly_lifetime1CVfD
// CHECK: [[INSTANCE:%.*]] = load [take] [[STACK]]
// CHECK: apply [[DESTROY_C]]([[INSTANCE]])
//
Expand Down
4 changes: 3 additions & 1 deletion test/Serialization/moveonly_deinit.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// RUN: %empty-directory(%t)
// TODO: re-enable the simplification passes once rdar://104875010 is fixed
// RUN: %target-swift-frontend -enable-experimental-feature MoveOnlyEnumDeinits -Xllvm -sil-disable-pass=simplification -g -emit-module -module-name OtherModule %S/Inputs/moveonly_deinit.swift -emit-module-path %t/OtherModule.swiftmodule
// RUN: %target-swift-frontend -enable-experimental-feature MoveOnlyEnumDeinits -Xllvm -sil-disable-pass=simplification -g -I %t %s -emit-silgen
// RUN: %target-sil-opt -enable-experimental-feature MoveOnlyEnumDeinits %t/OtherModule.swiftmodule | %FileCheck -check-prefix=CHECK-SERIALIZED %s

// Make sure we can deserialize deinits of both enums and structs.

import OtherModule

// CHECK-SERIALIZED: sil_moveonlydeinit [serialized] MoveOnlyStruct {
// CHECK-SERIALIZED: sil_moveonlydeinit [serialized] MoveOnlyEnum {
let s = MoveOnlyStruct(desc: 5)
let e = MoveOnlyEnum.lhs(5)