Skip to content

Commit 3ab4484

Browse files
committed
SILOptimizer: Add destructure peepholes
``` (%7, ...) = destructure_tuple %6 : $(A, ...) tuple_addr_constructor [assign] %9 : $*(A, ...) with (%7 : $A, ...) => store [assign] %6 to %9 ``` and ``` (tuple (destructure_tuple %t)) => %t ```
1 parent 328b869 commit 3ab4484

File tree

4 files changed

+125
-7
lines changed

4 files changed

+125
-7
lines changed

lib/SILOptimizer/Mandatory/LowerTupleAddrConstructor.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,54 @@ using namespace swift;
2222

2323
namespace {
2424

25+
static bool peepholeTupleDestructorOperand(TupleAddrConstructorInst *ctor) {
26+
27+
// (%7, ...) = destructure_tuple %6 : $(A, ...)
28+
// tuple_addr_constructor [assign] %9 : $*(A, ...) with (%7 : $A, ...)
29+
// =>
30+
// store [assign] %6 to %9
31+
32+
auto numTupleElts = ctor->getNumElements();
33+
if (ctor->getNumElements() == 0)
34+
return false;
35+
36+
auto multiVal = dyn_cast<MultipleValueInstructionResult>(ctor->getElement(0));
37+
if (!multiVal) {
38+
return false;
39+
}
40+
auto destructure = dyn_cast<DestructureTupleInst>(multiVal->getParent());
41+
if (!destructure) {
42+
return false;
43+
}
44+
45+
if (destructure->getNumResults() != numTupleElts) {
46+
return false;
47+
}
48+
49+
for (unsigned i = 0; i < numTupleElts; ++i) {
50+
if (destructure->getResult(i) != ctor->getElement(i)) {
51+
return false;
52+
}
53+
if (!destructure->getResult(i)->getSingleUse()) {
54+
return false;
55+
}
56+
}
57+
if (ctor->getDest()->getType().getObjectType() !=
58+
destructure->getOperand()->getType())
59+
return false;
60+
61+
// Okay now we can peephole this to an assign.
62+
SILBuilderWithScope b(ctor);
63+
b.emitStoreValueOperation(ctor->getLoc(),
64+
destructure->getOperand(), ctor->getDest(),
65+
bool(ctor->isInitializationOfDest()) ?
66+
StoreOwnershipQualifier::Init
67+
: StoreOwnershipQualifier::Assign);
68+
ctor->eraseFromParent();
69+
destructure->eraseFromParent();
70+
return true;
71+
}
72+
2573
class LowerTupleAddrConstructorTransform : public SILFunctionTransform {
2674
void run() override {
2775
SILFunction *function = getFunction();
@@ -36,6 +84,14 @@ class LowerTupleAddrConstructorTransform : public SILFunctionTransform {
3684
if (!inst)
3785
continue;
3886

87+
// (tuple_addr_constructor [assign/init] %addr,
88+
// (destructure_tuple %tuple))
89+
// ->
90+
// (store [assign/init] %tuple to %addr)
91+
if (peepholeTupleDestructorOperand(inst)) {
92+
continue;
93+
}
94+
3995
SILBuilderWithScope builder(inst);
4096

4197
unsigned count = 0;

lib/SILOptimizer/Mandatory/OwnershipModelEliminator.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ struct OwnershipModelEliminatorVisitor
140140
bool visitApplyInst(ApplyInst *ai);
141141

142142
void splitDestroy(DestroyValueInst *destroy);
143+
bool peepholeTupleConstructorUser(DestructureTupleInst *dti);
143144
bool visitDestroyValueInst(DestroyValueInst *dvi);
144145
bool visitDeallocBoxInst(DeallocBoxInst *dbi) {
145146
if (!dbi->isDeadEnd())
@@ -712,8 +713,50 @@ bool OwnershipModelEliminatorVisitor::visitDestructureStructInst(
712713
return true;
713714
}
714715

716+
bool OwnershipModelEliminatorVisitor::peepholeTupleConstructorUser(DestructureTupleInst *dti) {
717+
auto destructureResults = dti->getResults();
718+
TupleInst *ti = nullptr;
719+
for (unsigned index : indices(destructureResults)) {
720+
SILValue result = destructureResults[index];
721+
// We must have a single use of the destructure value.
722+
auto *use = result->getSingleUse();
723+
if (!use) {
724+
ti = nullptr;
725+
break;
726+
}
727+
// The user must be a single tuple constructor.
728+
auto *tupleUsr = dyn_cast<TupleInst>(use->getUser());
729+
if (!tupleUsr || (ti && ti != tupleUsr)) {
730+
ti = nullptr;
731+
break;
732+
}
733+
// Indices of destructure and tuple must match.
734+
if (use->getOperandNumber() != index) {
735+
ti = nullptr;
736+
break;
737+
}
738+
ti = tupleUsr;
739+
}
740+
if (!ti)
741+
return false;
742+
743+
if (ti->getType() != dti->getOperand()->getType())
744+
return false;
745+
746+
ti->replaceAllUsesWith(dti->getOperand());
747+
eraseInstruction(ti);
748+
eraseInstruction(dti);
749+
return true;
750+
}
751+
715752
bool OwnershipModelEliminatorVisitor::visitDestructureTupleInst(
716753
DestructureTupleInst *dti) {
754+
755+
// (tuple (destructure)) -> (id)
756+
if (peepholeTupleConstructorUser(dti)) {
757+
return true;
758+
}
759+
717760
splitDestructure(dti, dti->getOperand());
718761
return true;
719762
}

test/SILOptimizer/ownership_model_eliminator.sil

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ struct PairOfInt {
2020
var rhs : Builtin.Int32
2121
}
2222

23+
struct ContainsTuple {
24+
var tuple : (Builtin.Int32, Builtin.Int32)
25+
}
26+
2327
// CHECK-LABEL: sil @load : $@convention(thin) (@in Builtin.NativeObject, @in Builtin.Int32) -> () {
2428
// CHECK: bb0([[ARG1:%[0-9]+]] : $*Builtin.NativeObject, [[ARG2:%[0-9]+]] : $*Builtin.Int32):
2529
// CHECK: [[LOAD2:%[0-9]+]] = load [[ARG1]] : $*Builtin.NativeObject
@@ -456,3 +460,21 @@ exit:
456460
%retval = tuple ()
457461
return %retval : $()
458462
}
463+
464+
// CHECK-LABEL: sil @destructure_tuple_peephole : $@convention(thin) (ContainsTuple) -> () {
465+
// CHECK: bb0([[ARG:%.*]] : $ContainsTuple):
466+
// CHECK: [[S:%.*]] = alloc_stack $(Builtin.Int32, Builtin.Int32)
467+
// CHECK: [[T:%.*]] = struct_extract [[ARG]] : $ContainsTuple, #ContainsTuple.tuple
468+
// CHECK: store [[T]] to [[S]] : $*(Builtin.Int32, Builtin.Int32)
469+
// CHECK: } // end sil function 'destructure_tuple_peephole'
470+
sil [ossa] @destructure_tuple_peephole : $@convention(thin) (ContainsTuple) -> () {
471+
entry(%arg : $ContainsTuple):
472+
%stk = alloc_stack $(Builtin.Int32, Builtin.Int32)
473+
%tpl = struct_extract %arg : $ContainsTuple, #ContainsTuple.tuple
474+
(%1, %2) = destructure_tuple %tpl : $(Builtin.Int32, Builtin.Int32)
475+
%3 = tuple (%1 : $Builtin.Int32, %2 : $Builtin.Int32)
476+
store %3 to [trivial] %stk : $*(Builtin.Int32, Builtin.Int32)
477+
dealloc_stack %stk : $*(Builtin.Int32, Builtin.Int32)
478+
%retval = tuple ()
479+
return %retval : $()
480+
}

test/Serialization/transparent-std.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,10 @@ func test_foo(x: Builtin.Int1, y: Builtin.Int1) -> Builtin.Int1 {
2020
// SIL: [[TUP:%.*]] = tuple ([[ARG0]] : $Builtin.Int64, [[ARG1]] : $Builtin.NativeObject)
2121
// SIL: retain_value [[TUP]]
2222
// SIL: [[CASTED_PTR:%.*]] = pointer_to_address [[ARG2]]
23-
// SIL: [[CASTED_PTR_0:%.*]] = tuple_element_addr [[CASTED_PTR]] : $*(Builtin.Int64, Builtin.NativeObject), 0
24-
// SIL: store [[ARG0]] to [[CASTED_PTR_0]]
25-
// SIL: [[CASTED_PTR_1:%.*]] = tuple_element_addr [[CASTED_PTR]] : $*(Builtin.Int64, Builtin.NativeObject), 1
26-
// SIL: [[OLD_VALUE:%.*]] = load [[CASTED_PTR_1]]
27-
// SIL: store [[ARG1]] to [[CASTED_PTR_1]]
28-
// SIL: strong_release [[OLD_VALUE]]
29-
// SIL: } // end sil function '$s19def_transparent_std12assign_tuple1x1yyBi64__Bot_BptF'
23+
// SIL: [[OLD_VALUE:%.*]] = load [[CASTED_PTR]]
24+
// SIL: store [[TUP]] to [[CASTED_PTR]]
25+
// SIL: release_value [[OLD_VALUE]]
26+
// SIL: } // end sil function '$s19def_transparent_std12assign_tuple1x1yyBi64__Bot_BptF'
3027
func test_tuple(x: (Builtin.Int64, Builtin.NativeObject),
3128
y: Builtin.RawPointer) {
3229
assign_tuple(x: x, y: y)

0 commit comments

Comments
 (0)