Skip to content

Commit cfdc943

Browse files
committed
[sil-cse] Add CSE support for open_existential_ref
1 parent e9dca65 commit cfdc943

File tree

3 files changed

+184
-6
lines changed

3 files changed

+184
-6
lines changed

lib/SIL/SILInstruction.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,10 @@ namespace {
620620
}
621621

622622
bool visitMarkDependenceInst(const MarkDependenceInst *RHS) {
623+
return true;
624+
}
625+
626+
bool visitOpenExistentialRefInst(const OpenExistentialRefInst *RHS) {
623627
return true;
624628
}
625629

lib/SILOptimizer/Transforms/CSE.cpp

Lines changed: 132 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -362,12 +362,21 @@ class HashVisitor : public SILInstructionVisitor<HashVisitor, llvm::hash_code> {
362362
Operands.begin(),
363363
Operands.end()));
364364
}
365+
365366
hash_code visitMarkDependenceInst(MarkDependenceInst *X) {
366367
OperandValueArrayRef Operands(X->getAllOperands());
367368
return llvm::hash_combine(
368369
X->getKind(), X->getType(),
369370
llvm::hash_combine_range(Operands.begin(), Operands.end()));
370371
}
372+
373+
hash_code visitOpenExistentialRefInst(OpenExistentialRefInst *X) {
374+
auto ArchetypeTy = cast<ArchetypeType>(X->getType().getSwiftRValueType());
375+
auto ConformsTo = ArchetypeTy->getConformsTo();
376+
return llvm::hash_combine(
377+
X->getKind(), X->getOperand(),
378+
llvm::hash_combine_range(ConformsTo.begin(), ConformsTo.end()));
379+
}
371380
};
372381
} // end anonymous namespace
373382

@@ -381,14 +390,33 @@ bool llvm::DenseMapInfo<SimpleValue>::isEqual(SimpleValue LHS,
381390
if (LHS.isSentinel() || RHS.isSentinel())
382391
return LHSI == RHSI;
383392

393+
if (isa<OpenExistentialRefInst>(LHSI) && isa<OpenExistentialRefInst>(RHSI)) {
394+
if (LHSI->getNumOperands() != RHSI->getNumOperands())
395+
return false;
396+
397+
// Check operands.
398+
for (unsigned i = 0, e = LHSI->getNumOperands(); i != e; ++i)
399+
if (LHSI->getOperand(i) != RHSI->getOperand(i))
400+
return false;
401+
402+
// Consider the types of two open_existential_ref instructions to be equal,
403+
// if the sets of protocols they conform to are equal.
404+
auto LHSArchetypeTy =
405+
cast<ArchetypeType>(LHSI->getType().getSwiftRValueType());
406+
auto LHSConformsTo = LHSArchetypeTy->getConformsTo();
407+
auto RHSArchetypeTy =
408+
cast<ArchetypeType>(RHSI->getType().getSwiftRValueType());
409+
auto RHSConformsTo = RHSArchetypeTy->getConformsTo();
410+
return LHSConformsTo == RHSConformsTo;
411+
}
384412
return LHSI->getKind() == RHSI->getKind() && LHSI->isIdenticalTo(RHSI);
385413
}
386414

387415
//===----------------------------------------------------------------------===//
388416
// CSE Interface
389417
//===----------------------------------------------------------------------===//
390418

391-
namespace {
419+
namespace swift {
392420

393421
/// CSE - This pass does a simple depth-first walk over the dominator tree,
394422
/// eliminating trivially redundant instructions and using simplifyInstruction
@@ -476,6 +504,8 @@ class CSE {
476504
};
477505

478506
bool processNode(DominanceInfoNode *Node);
507+
bool processOpenExistentialRef(SILInstruction *Inst, ValueBase *V,
508+
SILBasicBlock::iterator &I);
479509
};
480510
} // end anonymous namespace
481511

@@ -525,6 +555,95 @@ bool CSE::processFunction(SILFunction &Fm, DominanceInfo *DT) {
525555
return Changed;
526556
}
527557

558+
namespace {
559+
// A very simple cloner for cloning instructions inside
560+
// the same function. The only interesting thing it does
561+
// is remapping the archetypes when it is required.
562+
class InstructionCloner : public SILCloner<InstructionCloner> {
563+
friend class SILCloner<InstructionCloner>;
564+
friend class SILVisitor<InstructionCloner>;
565+
SILInstruction *Result = nullptr;
566+
public:
567+
InstructionCloner(SILFunction *F) : SILCloner(*F) {}
568+
569+
static SILInstruction *doIt(SILInstruction *I) {
570+
InstructionCloner TC(I->getFunction());
571+
return TC.clone(I);
572+
}
573+
574+
SILInstruction *clone(SILInstruction *I) {
575+
visit(I);
576+
return Result;
577+
}
578+
579+
void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
580+
assert(Orig->getFunction() == &getBuilder().getFunction() &&
581+
"cloning between functions is not supported");
582+
583+
Result = Cloned;
584+
SILCloner<InstructionCloner>::postProcess(Orig, Cloned);
585+
}
586+
SILValue remapValue(SILValue Value) {
587+
return Value;
588+
}
589+
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
590+
};
591+
}
592+
593+
/// Handle CSE of open_existential_ref instructions.
594+
/// Returns true if uses of open_existential_ref can
595+
/// be replaced by a dominating instruction.
596+
/// \Inst is the open_existential_ref instruction
597+
/// \V is the dominating open_existential_ref instruction
598+
/// \I is the iterator referring to the current instruction.
599+
bool CSE::processOpenExistentialRef(SILInstruction *Inst, ValueBase *V,
600+
SILBasicBlock::iterator &I) {
601+
assert(isa<OpenExistentialRefInst>(Inst));
602+
llvm::SmallSetVector<SILInstruction *, 16> Candidates;
603+
// Collect all candidates that may contain opened archetypes
604+
// that need to be replaced.
605+
for (auto Use : Inst->getUses()) {
606+
auto User = Use->getUser();
607+
if (User->mayHaveOpenedArchetypeOperands()) {
608+
if (canHandle(User)) {
609+
auto It = AvailableValues->begin(User);
610+
if (It != AvailableValues->end()) {
611+
return false;
612+
}
613+
}
614+
Candidates.insert(User);
615+
}
616+
}
617+
// Now process candidates.
618+
auto OldOpenedArchetype = getOpenedArchetypeOf(Inst);
619+
auto NewOpenedArchetype = getOpenedArchetypeOf(dyn_cast<SILInstruction>(V));
620+
// TODO: Move it to CSE instance to avoid recreating it every time?
621+
SILOpenedArchetypesTracker OpenedArchetypesTracker(*Inst->getFunction());
622+
// Register the new archetype to be used.
623+
OpenedArchetypesTracker.registerOpenedArchetypes(dyn_cast<SILInstruction>(V));
624+
// Use a cloner. It makes copying the instruction and remaping of
625+
// opened archetypes trivial.
626+
InstructionCloner Cloner(I->getFunction());
627+
Cloner.registerOpenedExistentialRemapping(
628+
OldOpenedArchetype->castTo<ArchetypeType>(), NewOpenedArchetype);
629+
auto &Builder = Cloner.getBuilder();
630+
Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);
631+
632+
// Now clone each candidate and replace the opened archetype
633+
// by a dominating one.
634+
for (auto Candidate : Candidates) {
635+
Builder.getOpenedArchetypes().addOpenedArchetypeOperands(
636+
Candidate->getOpenedArchetypeOperands());
637+
Builder.setInsertionPoint(Candidate);
638+
auto NewI = Cloner.clone(Candidate);
639+
Candidate->replaceAllUsesWith(NewI);
640+
if (I == Candidate->getIterator())
641+
I = NewI->getIterator();
642+
eraseFromParentWithDebugInsts(Candidate, I);
643+
}
644+
return true;
645+
}
646+
528647
bool CSE::processNode(DominanceInfoNode *Node) {
529648
SILBasicBlock *BB = Node->getBlock();
530649
bool Changed = false;
@@ -572,11 +691,17 @@ bool CSE::processNode(DominanceInfoNode *Node) {
572691
// instruction has an available value. If so, use it.
573692
if (ValueBase *V = AvailableValues->lookup(Inst)) {
574693
DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << " to: " << *V << '\n');
575-
Inst->replaceAllUsesWith(V);
576-
Inst->eraseFromParent();
577-
Changed = true;
578-
++NumCSE;
579-
continue;
694+
// Instructions producing a new opened archetype need a special handling,
695+
// because replacing these intructions may require a replacement
696+
// of the opened archetype type operands in some of the uses.
697+
if (!isa<OpenExistentialRefInst>(Inst) ||
698+
processOpenExistentialRef(Inst, V, I)) {
699+
Inst->replaceAllUsesWith(V);
700+
Inst->eraseFromParent();
701+
Changed = true;
702+
++NumCSE;
703+
continue;
704+
}
580705
}
581706

582707
// Otherwise, just remember that this value is available.
@@ -687,6 +812,7 @@ bool CSE::canHandle(SILInstruction *Inst) {
687812
case ValueKind::ThinFunctionToPointerInst:
688813
case ValueKind::PointerToThinFunctionInst:
689814
case ValueKind::MarkDependenceInst:
815+
case ValueKind::OpenExistentialRefInst:
690816
return true;
691817
default:
692818
return false;

test/SILOptimizer/cse.sil

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1319,3 +1319,51 @@ bb0(%0 : $*Builtin.Int64, %1 : $Builtin.NativeObject):
13191319
%6 = tuple(%4 : $Builtin.Int64, %5 : $Builtin.Int64)
13201320
return %6 : $(Builtin.Int64, Builtin.Int64)
13211321
}
1322+
1323+
protocol Proto : class {
1324+
func doThis()
1325+
func doThat()
1326+
}
1327+
1328+
// Check that all open_existential_ref instructions are CSEd
1329+
// CHECK-LABEL: sil @cse_open_existential : $@convention(thin) (@guaranteed Proto, Bool) -> ()
1330+
// CHECK: bb0
1331+
// CHECK: %[[OPENED_EXISTENTIAL:[0-9]+]] = open_existential_ref %{{[0-9]+}} : $Proto to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto
1332+
// CHECK: %[[WM1:[0-9]+]] = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThis!1
1333+
// CHECK: apply %[[WM1]]{{.*}}(%[[OPENED_EXISTENTIAL]])
1334+
// CHECK: cond_br
1335+
// CHECK: bb1:
1336+
// CHECK-NEXT: %[[WM2:[0-9]+]] = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThat!1
1337+
// CHECK-NEXT: apply %[[WM2]]{{.*}}(%[[OPENED_EXISTENTIAL]])
1338+
// CHECK-NEXT: br
1339+
// CHECK: bb2:
1340+
// CHECK-NEXT: apply %[[WM1]]{{.*}}(%[[OPENED_EXISTENTIAL]])
1341+
// CHECK-NEXT: br
1342+
// CHECK: bb3:
1343+
// CHECK-NEXT: tuple
1344+
// CHECK-NEXT: return
1345+
sil @cse_open_existential : $@convention(thin) (@guaranteed Proto, Bool) -> () {
1346+
bb0(%0 : $Proto, %1 : $Bool):
1347+
%4 = open_existential_ref %0 : $Proto to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto
1348+
%5 = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThis!1, %4 : $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1349+
%6 = apply %5<@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto>(%4) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1350+
%7 = struct_extract %1 : $Bool, #Bool._value
1351+
cond_br %7, bb1, bb2
1352+
1353+
bb1:
1354+
%9 = open_existential_ref %0 : $Proto to $@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto
1355+
%10 = witness_method $@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThat!1, %9 : $@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1356+
%11 = apply %10<@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto>(%9) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1357+
br bb3
1358+
1359+
bb2: // Preds: bb0
1360+
%13 = open_existential_ref %0 : $Proto to $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
1361+
%14 = witness_method $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThis!1, %13 : $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1362+
%15 = apply %14<@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto>(%13) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
1363+
br bb3
1364+
1365+
bb3:
1366+
%17 = tuple ()
1367+
return %17 : $()
1368+
}
1369+

0 commit comments

Comments
 (0)