Skip to content

Commit 6ff3b91

Browse files
committed
SimplifyDestructure: support simplifying a destructure_struct with a copy_value in place
Replace ``` %s = struct (%0, %1, %2) %c = copy_value %s // can also be a chain of multiple copies (%3, %4, %5) = destructure_struct %c ``` with ``` %c0 = copy_value %0 %c1 = copy_value %1 %c2 = copy_value %2 %s = struct (%0, %1, %2) // keep the original struct ``` and replace the results %3, %4, %5 with %c0, %c1, %c2, respectively.
1 parent 2c84de3 commit 6ff3b91

File tree

2 files changed

+118
-8
lines changed

2 files changed

+118
-8
lines changed

SwiftCompilerSources/Sources/Optimizer/InstructionSimplification/SimplifyDestructure.swift

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,55 @@ extension DestructureTupleInst : OnoneSimplifyable {
3131
extension DestructureStructInst : OnoneSimplifyable {
3232
func simplify(_ context: SimplifyContext) {
3333

34-
// Eliminate the redundant instruction pair
35-
// ```
36-
// %s = struct (%0, %1, %2)
37-
// (%3, %4, %5) = destructure_struct %s
38-
// ```
39-
// and replace the results %3, %4, %5 with %0, %1, %2, respectively
40-
//
41-
if let str = self.struct as? StructInst {
34+
switch self.struct {
35+
case let str as StructInst:
36+
// Eliminate the redundant instruction pair
37+
// ```
38+
// %s = struct (%0, %1, %2)
39+
// (%3, %4, %5) = destructure_struct %s
40+
// ```
41+
// and replace the results %3, %4, %5 with %0, %1, %2, respectively
42+
//
4243
tryReplaceConstructDestructPair(construct: str, destruct: self, context)
44+
45+
case let copy as CopyValueInst:
46+
// Similar to the pattern above, but with a copy_value:
47+
// Replace
48+
// ```
49+
// %s = struct (%0, %1, %2)
50+
// %c = copy_value %s // can also be a chain of multiple copies
51+
// (%3, %4, %5) = destructure_struct %c
52+
// ```
53+
// with
54+
// ```
55+
// %c0 = copy_value %0
56+
// %c1 = copy_value %1
57+
// %c2 = copy_value %2
58+
// %s = struct (%0, %1, %2) // keep the original struct
59+
// ```
60+
// and replace the results %3, %4, %5 with %c0, %c1, %c2, respectively.
61+
//
62+
// This transformation has the advantage that we can do it even if the `struct` instruction
63+
// has other uses than the `copy_value`.
64+
//
65+
if copy.uses.singleUse?.instruction == self,
66+
let structInst = copy.fromValue.lookThroughCopy as? StructInst,
67+
structInst.parentBlock == self.parentBlock
68+
{
69+
for (result, operand) in zip(self.results, structInst.operands) {
70+
if operand.value.type.isTrivial(in: parentFunction) {
71+
result.uses.replaceAll(with: operand.value, context)
72+
} else {
73+
let builder = Builder(before: structInst, context)
74+
let copiedOperand = builder.createCopyValue(operand: operand.value)
75+
result.uses.replaceAll(with: copiedOperand, context)
76+
}
77+
}
78+
context.erase(instruction: self)
79+
context.erase(instruction: copy)
80+
}
81+
default:
82+
break
4383
}
4484
}
4585
}

test/SILOptimizer/simplify_destructure_struct.sil

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,74 @@ bb0(%0 : @owned $String, %1 : $Int):
7070
return %4 : $String
7171
}
7272

73+
// CHECK-LABEL: sil [ossa] @forward_with_copy :
74+
// CHECK: %2 = copy_value %0
75+
// CHECK: %3 = struct $S (%0 : $String, %1 : $Int)
76+
// CHECK: fix_lifetime %1
77+
// CHECK: destroy_value %3
78+
// CHECK: return %2
79+
// CHECK: } // end sil function 'forward_with_copy'
80+
sil [ossa] @forward_with_copy : $@convention(thin) (@owned String, Int) -> @owned String {
81+
bb0(%0 : @owned $String, %1 : $Int):
82+
%2 = struct $S (%0 : $String, %1 : $Int)
83+
%3 = copy_value %2 : $S
84+
(%4, %5) = destructure_struct %3 : $S
85+
fix_lifetime %5 : $Int
86+
destroy_value %2 : $S
87+
return %4 : $String
88+
}
89+
90+
// CHECK-LABEL: sil [ossa] @forward_with_two_copies :
91+
// CHECK: %2 = copy_value %0
92+
// CHECK: %3 = struct $S (%0 : $String, %1 : $Int)
93+
// CHECK: %4 = copy_value %3
94+
// CHECK: fix_lifetime %1
95+
// CHECK: destroy_value %3
96+
// CHECK: destroy_value %4
97+
// CHECK: return %2
98+
// CHECK: } // end sil function 'forward_with_two_copies'
99+
sil [ossa] @forward_with_two_copies : $@convention(thin) (@owned String, Int) -> @owned String {
100+
bb0(%0 : @owned $String, %1 : $Int):
101+
%2 = struct $S (%0 : $String, %1 : $Int)
102+
%3 = copy_value %2 : $S
103+
%4 = copy_value %3 : $S
104+
(%5, %6) = destructure_struct %4 : $S
105+
fix_lifetime %6 : $Int
106+
destroy_value %2 : $S
107+
destroy_value %3 : $S
108+
return %5 : $String
109+
}
110+
111+
// CHECK-LABEL: sil [ossa] @copy_has_other_uses :
112+
// CHECK: destructure_struct
113+
// CHECK: } // end sil function 'copy_has_other_uses'
114+
sil [ossa] @copy_has_other_uses : $@convention(thin) (@owned String, Int) -> @owned String {
115+
bb0(%0 : @owned $String, %1 : $Int):
116+
%2 = struct $S (%0 : $String, %1 : $Int)
117+
%3 = copy_value %2 : $S
118+
fix_lifetime %3 : $S
119+
(%5, %6) = destructure_struct %3 : $S
120+
fix_lifetime %6 : $Int
121+
destroy_value %2 : $S
122+
return %5 : $String
123+
}
124+
125+
// CHECK-LABEL: sil [ossa] @different_basic_block :
126+
// CHECK: bb2:
127+
// CHECK: destructure_struct
128+
// CHECK: } // end sil function 'different_basic_block'
129+
sil [ossa] @different_basic_block : $@convention(thin) (@owned String, Int) -> @owned String {
130+
bb0(%0 : @owned $String, %1 : $Int):
131+
%2 = struct $S (%0 : $String, %1 : $Int)
132+
%3 = copy_value %2 : $S
133+
cond_br undef, bb1, bb2
134+
bb1:
135+
destroy_value %3 : $S
136+
unreachable
137+
bb2:
138+
(%4, %5) = destructure_struct %3 : $S
139+
fix_lifetime %5 : $Int
140+
destroy_value %2 : $S
141+
return %4 : $String
142+
}
73143

0 commit comments

Comments
 (0)