Skip to content

Commit 52d6ae3

Browse files
committed
DeinitDevirtualizer: don't erase the destroy instruction if the the destroyed value can't be de-virtualized
This can happen if the destroyed type is resilient. Fixes a verifier crash.
1 parent a5ef2b3 commit 52d6ae3

File tree

2 files changed

+31
-18
lines changed

2 files changed

+31
-18
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,28 +39,25 @@ private func devirtualize(destroy: some DevirtualizableDestroy, _ context: some
3939
return true
4040
}
4141

42-
let result: Bool
4342
if type.nominal.hasValueDeinit && !destroy.shouldDropDeinit {
4443
guard let deinitFunc = context.lookupDeinit(ofNominal: type.nominal) else {
4544
return false
4645
}
4746
destroy.createDeinitCall(to: deinitFunc, context)
48-
result = true
49-
} else {
50-
// If there is no deinit to be called for the original type we have to recursively visit
51-
// the struct fields or enum cases.
52-
if type.isStruct {
53-
result = destroy.devirtualizeStructFields(context)
54-
} else if type.isEnum {
55-
result = destroy.devirtualizeEnumPayloads(context)
56-
} else {
57-
precondition(type.isClass, "unknown non-copyable type")
58-
// A class reference cannot be further de-composed.
59-
return true
60-
}
47+
context.erase(instruction: destroy)
48+
return true
6149
}
62-
context.erase(instruction: destroy)
63-
return result
50+
// If there is no deinit to be called for the original type we have to recursively visit
51+
// the struct fields or enum cases.
52+
if type.isStruct {
53+
return destroy.devirtualizeStructFields(context)
54+
}
55+
if type.isEnum {
56+
return destroy.devirtualizeEnumPayloads(context)
57+
}
58+
precondition(type.isClass, "unknown non-copyable type")
59+
// A class reference cannot be further de-composed.
60+
return true
6461
}
6562

6663
// Used to dispatch devirtualization tasks to `destroy_value` and `destroy_addr`.
@@ -79,6 +76,10 @@ private extension DevirtualizableDestroy {
7976
guard let cases = type.getEnumCases(in: parentFunction) else {
8077
return false
8178
}
79+
defer {
80+
context.erase(instruction: self)
81+
}
82+
8283
if cases.allPayloadsAreTrivial(in: parentFunction) {
8384
let builder = Builder(before: self, context)
8485
builder.createEndLifetime(of: operand.value)
@@ -122,11 +123,15 @@ extension DestroyValueInst : DevirtualizableDestroy {
122123
}
123124

124125
fileprivate func devirtualizeStructFields(_ context: some MutatingContext) -> Bool {
125-
let builder = Builder(before: self, context)
126-
127126
guard let fields = type.getNominalFields(in: parentFunction) else {
128127
return false
129128
}
129+
130+
defer {
131+
context.erase(instruction: self)
132+
}
133+
134+
let builder = Builder(before: self, context)
130135
if fields.allFieldsAreTrivial(in: parentFunction) {
131136
builder.createEndLifetime(of: operand.value)
132137
return true
@@ -193,6 +198,9 @@ extension DestroyAddrInst : DevirtualizableDestroy {
193198
guard let fields = type.getNominalFields(in: parentFunction) else {
194199
return false
195200
}
201+
defer {
202+
context.erase(instruction: self)
203+
}
196204
if fields.allFieldsAreTrivial(in: parentFunction) {
197205
builder.createEndLifetime(of: operand.value)
198206
return true

test/Interpreter/moveonly_swiftskell.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717
// RUN: %target-run %t/E %t/%target-library-name(Swiftskell) \
1818
// RUN: | %FileCheck %s --implicit-check-not destroy
1919

20+
// RUN: %target-build-swift -O -I%t -L%t -lSwiftskell -parse-as-library %s \
21+
// RUN: -module-name Opt -o %t/Opt %target-rpath(%t)
22+
// RUN: %target-codesign %t/Opt
23+
// RUN: %target-run %t/Opt %t/%target-library-name(Swiftskell) | %FileCheck %s --implicit-check-not destroy
24+
2025
// REQUIRES: executable_test
2126

2227
// Temporarily disable for back-deployment (rdar://128544927)

0 commit comments

Comments
 (0)