Skip to content

Commit 11c7431

Browse files
committed
deinit-devirtualization: completely de-compose non-copyable destroys
Even if the destroyed value doesn't have a deinit. This fixes a false alarm when a non-copyable value ends its lifetime in a function with performance annotations. rdar://117002721
1 parent 5e0bcab commit 11c7431

File tree

5 files changed

+38
-5
lines changed

5 files changed

+38
-5
lines changed

SwiftCompilerSources/Sources/Optimizer/Utilities/Devirtualization.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func devirtualizeDeinits(of destroy: DestroyAddrInst, _ context: some MutatingCo
3030

3131
private func devirtualize(destroy: some DevirtualizableDestroy, _ context: some MutatingContext) -> Bool {
3232
let type = destroy.type
33-
guard type.isMoveOnly && type.selfOrAnyFieldHasValueDeinit(in: destroy.parentFunction) else {
33+
if !type.isMoveOnly {
3434
return true
3535
}
3636
precondition(type.isNominal, "non-copyable non-nominal types not supported, yet")
@@ -47,9 +47,12 @@ private func devirtualize(destroy: some DevirtualizableDestroy, _ context: some
4747
// the struct fields or enum cases.
4848
if type.isStruct {
4949
result = destroy.devirtualizeStructFields(context)
50-
} else {
51-
precondition(type.isEnum, "unknown nominal value type")
50+
} else if type.isEnum {
5251
result = destroy.devirtualizeEnumPayloads(context)
52+
} else {
53+
precondition(type.isClass, "unknown non-copyable type")
54+
// A class reference cannot be further de-composed.
55+
return true
5356
}
5457
}
5558
context.erase(instruction: destroy)

test/SILOptimizer/devirt_deinits.sil

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ import SwiftShims
4747
deinit
4848
}
4949

50+
@_moveOnly struct S5 {
51+
@_hasStorage let a: Int
52+
@_hasStorage let b: AnyObject
53+
}
54+
5055
// CHECK-LABEL: sil [ossa] @test_simple_struct :
5156
// CHECK: [[D:%.*]] = function_ref @s1_deinit
5257
// CHECK: apply [[D]](%0) : $@convention(method) (@owned S1) -> ()
@@ -72,6 +77,17 @@ bb0(%0 : @owned $StrWithoutDeinit):
7277
return %r : $()
7378
}
7479

80+
// CHECK-LABEL: sil [ossa] @test_no_deinit :
81+
// CHECK: ({{%.*}}, [[S2:%.*]]) = destructure_struct %0
82+
// CHECK-NEXT: destroy_value [[S2]]
83+
// CHECK-NEXT: tuple ()
84+
// CHECK: } // end sil function 'test_no_deinit'
85+
sil [ossa] @test_no_deinit : $@convention(thin) (@owned S5) -> () {
86+
bb0(%0 : @owned $S5):
87+
destroy_value %0 : $S5
88+
%r = tuple()
89+
return %r : $()
90+
}
7591
// CHECK-LABEL: sil [ossa] @test_indirect_deinit_arg :
7692
// CHECK: [[D:%.*]] = function_ref @s3_deinit
7793
// CHECK: [[S:%.*]] = alloc_stack $S3<Int>

test/SILOptimizer/moveonly_deinit_devirtualization.sil

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,9 @@ bb2(%4 : $Builtin.Int32):
284284

285285

286286
bb3(%6 : @owned $(Klass, Klass)):
287-
destroy_value %6 : $(Klass, Klass)
287+
(%7, %8) = destructure_tuple %6 : $(Klass, Klass)
288+
destroy_value %7 : $Klass
289+
destroy_value %8 : $Klass
288290
br bb6
289291

290292
bb4(%9 : $(Builtin.Int64, Builtin.Int64)):

test/SILOptimizer/moveonly_deinit_devirtualization_library_evolution.sil

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,10 @@ bb2:
285285

286286
bb3:
287287
%6 = unchecked_take_enum_data_addr %0 : $*NonTrivialMoveOnlyEnum, #NonTrivialMoveOnlyEnum.third
288-
destroy_addr %6 : $*(Klass, Klass)
288+
%7 = tuple_element_addr %6 : $*(Klass, Klass), 0
289+
%8 = tuple_element_addr %6 : $*(Klass, Klass), 1
290+
destroy_addr %7 : $*Klass
291+
destroy_addr %8 : $*Klass
289292
br bb6
290293

291294
bb4:

test/SILOptimizer/performance-annotations.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,3 +471,12 @@ func useOfExistentialNoRuntime() -> P {
471471
Str(x: 1) // expected-error {{Using type 'any P' can cause metadata allocation or locks}}
472472
}
473473

474+
public struct NonCopyable: ~Copyable {
475+
var value: Int
476+
}
477+
478+
@_noAllocation
479+
public func testNonCopyable(_ foo: consuming NonCopyable) {
480+
let _ = foo.value
481+
}
482+

0 commit comments

Comments
 (0)