Skip to content

[sil-cse] Add CSE support for open_existential_ref #3456

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ class SILInstruction : public ValueBase,public llvm::ilist_node<SILInstruction>{
/// Return the array of opened archetypes operands for this instruction.
MutableArrayRef<Operand> getOpenedArchetypeOperands();

/// Returns true if a given kind of instruciton may have opened archetype
/// operands.
bool mayHaveOpenedArchetypeOperands() const;

unsigned getNumOperands() const { return getAllOperands().size(); }

unsigned getNumOpenedArchetypeOperands() const {
Expand Down
26 changes: 26 additions & 0 deletions lib/SIL/SILInstruction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,10 @@ namespace {
}

bool visitMarkDependenceInst(const MarkDependenceInst *RHS) {
return true;
}

bool visitOpenExistentialRefInst(const OpenExistentialRefInst *RHS) {
return true;
}

Expand Down Expand Up @@ -704,6 +708,23 @@ namespace {
return {}; \
return I->getOpenedArchetypeOperands(); \
}
#include "swift/SIL/SILNodes.def"
};

class MayHaveOpenedArchetypeOperandsAccessor
: public SILVisitor<MayHaveOpenedArchetypeOperandsAccessor,
bool> {
public:
#define VALUE(CLASS, PARENT) \
bool visit##CLASS(const CLASS *I) { \
llvm_unreachable("accessing non-instruction " #CLASS); \
}
#define INST(CLASS, PARENT, MEMBEHAVIOR, RELEASINGBEHAVIOR) \
bool visit##CLASS(const CLASS *I) { \
return IMPLEMENTS_METHOD(CLASS, SILInstruction, \
getOpenedArchetypeOperands, \
ArrayRef<Operand>() const); \
}
#include "swift/SIL/SILNodes.def"
};
} // end anonymous namespace
Expand All @@ -725,6 +746,11 @@ MutableArrayRef<Operand> SILInstruction::getOpenedArchetypeOperands() {
return OpenedArchetypeOperandsMutableAccessor().visit(this);
}

bool SILInstruction::mayHaveOpenedArchetypeOperands() const {
return MayHaveOpenedArchetypeOperandsAccessor().visit(
const_cast<SILInstruction *>(this));
}

/// getOperandNumber - Return which operand this is in the operand list of the
/// using instruction.
unsigned Operand::getOperandNumber() const {
Expand Down
138 changes: 132 additions & 6 deletions lib/SILOptimizer/Transforms/CSE.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,21 @@ class HashVisitor : public SILInstructionVisitor<HashVisitor, llvm::hash_code> {
Operands.begin(),
Operands.end()));
}

hash_code visitMarkDependenceInst(MarkDependenceInst *X) {
OperandValueArrayRef Operands(X->getAllOperands());
return llvm::hash_combine(
X->getKind(), X->getType(),
llvm::hash_combine_range(Operands.begin(), Operands.end()));
}

hash_code visitOpenExistentialRefInst(OpenExistentialRefInst *X) {
auto ArchetypeTy = cast<ArchetypeType>(X->getType().getSwiftRValueType());
auto ConformsTo = ArchetypeTy->getConformsTo();
return llvm::hash_combine(
X->getKind(), X->getOperand(),
llvm::hash_combine_range(ConformsTo.begin(), ConformsTo.end()));
}
};
} // end anonymous namespace

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

if (isa<OpenExistentialRefInst>(LHSI) && isa<OpenExistentialRefInst>(RHSI)) {
if (LHSI->getNumOperands() != RHSI->getNumOperands())
return false;

// Check operands.
for (unsigned i = 0, e = LHSI->getNumOperands(); i != e; ++i)
if (LHSI->getOperand(i) != RHSI->getOperand(i))
return false;

// Consider the types of two open_existential_ref instructions to be equal,
// if the sets of protocols they conform to are equal.
auto LHSArchetypeTy =
cast<ArchetypeType>(LHSI->getType().getSwiftRValueType());
auto LHSConformsTo = LHSArchetypeTy->getConformsTo();
auto RHSArchetypeTy =
cast<ArchetypeType>(RHSI->getType().getSwiftRValueType());
auto RHSConformsTo = RHSArchetypeTy->getConformsTo();
return LHSConformsTo == RHSConformsTo;
}
return LHSI->getKind() == RHSI->getKind() && LHSI->isIdenticalTo(RHSI);
}

//===----------------------------------------------------------------------===//
// CSE Interface
//===----------------------------------------------------------------------===//

namespace {
namespace swift {

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

bool processNode(DominanceInfoNode *Node);
bool processOpenExistentialRef(SILInstruction *Inst, ValueBase *V,
SILBasicBlock::iterator &I);
};
} // end anonymous namespace

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

namespace {
// A very simple cloner for cloning instructions inside
// the same function. The only interesting thing it does
// is remapping the archetypes when it is required.
class InstructionCloner : public SILCloner<InstructionCloner> {
friend class SILCloner<InstructionCloner>;
friend class SILVisitor<InstructionCloner>;
SILInstruction *Result = nullptr;
public:
InstructionCloner(SILFunction *F) : SILCloner(*F) {}

static SILInstruction *doIt(SILInstruction *I) {
InstructionCloner TC(I->getFunction());
return TC.clone(I);
}

SILInstruction *clone(SILInstruction *I) {
visit(I);
return Result;
}

void postProcess(SILInstruction *Orig, SILInstruction *Cloned) {
assert(Orig->getFunction() == &getBuilder().getFunction() &&
"cloning between functions is not supported");

Result = Cloned;
SILCloner<InstructionCloner>::postProcess(Orig, Cloned);
}
SILValue remapValue(SILValue Value) {
return Value;
}
SILBasicBlock *remapBasicBlock(SILBasicBlock *BB) { return BB; }
};
}

/// Handle CSE of open_existential_ref instructions.
/// Returns true if uses of open_existential_ref can
/// be replaced by a dominating instruction.
/// \Inst is the open_existential_ref instruction
/// \V is the dominating open_existential_ref instruction
/// \I is the iterator referring to the current instruction.
bool CSE::processOpenExistentialRef(SILInstruction *Inst, ValueBase *V,
SILBasicBlock::iterator &I) {
assert(isa<OpenExistentialRefInst>(Inst));
llvm::SmallSetVector<SILInstruction *, 16> Candidates;
// Collect all candidates that may contain opened archetypes
// that need to be replaced.
for (auto Use : Inst->getUses()) {
auto User = Use->getUser();
if (User->mayHaveOpenedArchetypeOperands()) {
if (canHandle(User)) {
auto It = AvailableValues->begin(User);
if (It != AvailableValues->end()) {
return false;
}
}
Candidates.insert(User);
}
}
// Now process candidates.
auto OldOpenedArchetype = getOpenedArchetypeOf(Inst);
auto NewOpenedArchetype = getOpenedArchetypeOf(dyn_cast<SILInstruction>(V));
// TODO: Move it to CSE instance to avoid recreating it every time?
SILOpenedArchetypesTracker OpenedArchetypesTracker(*Inst->getFunction());
// Register the new archetype to be used.
OpenedArchetypesTracker.registerOpenedArchetypes(dyn_cast<SILInstruction>(V));
// Use a cloner. It makes copying the instruction and remaping of
// opened archetypes trivial.
InstructionCloner Cloner(I->getFunction());
Cloner.registerOpenedExistentialRemapping(
OldOpenedArchetype->castTo<ArchetypeType>(), NewOpenedArchetype);
auto &Builder = Cloner.getBuilder();
Builder.setOpenedArchetypesTracker(&OpenedArchetypesTracker);

// Now clone each candidate and replace the opened archetype
// by a dominating one.
for (auto Candidate : Candidates) {
Builder.getOpenedArchetypes().addOpenedArchetypeOperands(
Candidate->getOpenedArchetypeOperands());
Builder.setInsertionPoint(Candidate);
auto NewI = Cloner.clone(Candidate);
Candidate->replaceAllUsesWith(NewI);
if (I == Candidate->getIterator())
I = NewI->getIterator();
eraseFromParentWithDebugInsts(Candidate, I);
}
return true;
}

bool CSE::processNode(DominanceInfoNode *Node) {
SILBasicBlock *BB = Node->getBlock();
bool Changed = false;
Expand Down Expand Up @@ -572,11 +691,17 @@ bool CSE::processNode(DominanceInfoNode *Node) {
// instruction has an available value. If so, use it.
if (ValueBase *V = AvailableValues->lookup(Inst)) {
DEBUG(llvm::dbgs() << "SILCSE CSE: " << *Inst << " to: " << *V << '\n');
Inst->replaceAllUsesWith(V);
Inst->eraseFromParent();
Changed = true;
++NumCSE;
continue;
// Instructions producing a new opened archetype need a special handling,
// because replacing these intructions may require a replacement
// of the opened archetype type operands in some of the uses.
if (!isa<OpenExistentialRefInst>(Inst) ||
processOpenExistentialRef(Inst, V, I)) {
Inst->replaceAllUsesWith(V);
Inst->eraseFromParent();
Changed = true;
++NumCSE;
continue;
}
}

// Otherwise, just remember that this value is available.
Expand Down Expand Up @@ -687,6 +812,7 @@ bool CSE::canHandle(SILInstruction *Inst) {
case ValueKind::ThinFunctionToPointerInst:
case ValueKind::PointerToThinFunctionInst:
case ValueKind::MarkDependenceInst:
case ValueKind::OpenExistentialRefInst:
return true;
default:
return false;
Expand Down
20 changes: 17 additions & 3 deletions lib/Serialization/SerializeSIL.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
#include "Serialization.h"
#include "swift/Strings.h"
#include "swift/AST/Module.h"
#include "swift/SIL/CFG.h"
#include "swift/SIL/SILArgument.h"
#include "swift/SIL/SILModule.h"
#include "swift/SIL/SILUndef.h"
#include "swift/SILOptimizer/Utils/Generics.h"

#include "llvm/ADT/MapVector.h"
#include "llvm/ADT/PostOrderIterator.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringExtras.h"
Expand Down Expand Up @@ -390,7 +392,10 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) {
// Assign a value ID to each SILInstruction that has value and to each basic
// block argument.
unsigned ValueID = 0;
for (const SILBasicBlock &BB : F) {
llvm::ReversePostOrderTraversal<SILFunction *> RPOT(
const_cast<SILFunction *>(&F));
for (auto Iter = RPOT.begin(), E = RPOT.end(); Iter != E; ++Iter) {
auto &BB = **Iter;
BasicBlockMap.insert(std::make_pair(&BB, BasicID++));

for (auto I = BB.bbarg_begin(), E = BB.bbarg_end(); I != E; ++I)
Expand All @@ -401,8 +406,17 @@ void SILSerializer::writeSILFunction(const SILFunction &F, bool DeclOnly) {
ValueIDs[&SI] = ++ValueID;
}

for (const SILBasicBlock &BB : F)
writeSILBasicBlock(BB);
// Write SIL basic blocks in the RPOT order
// to make sure that instructions defining open archetypes
// are serialized before instructions using those opened
// archetypes.
unsigned SerializedBBNum = 0;
for (auto Iter = RPOT.begin(), E = RPOT.end(); Iter != E; ++Iter) {
auto *BB = *Iter;
writeSILBasicBlock(*BB);
SerializedBBNum++;
}
assert(BasicID == SerializedBBNum && "Wrong number of BBs was serialized");
}

void SILSerializer::writeSILBasicBlock(const SILBasicBlock &BB) {
Expand Down
8 changes: 4 additions & 4 deletions test/ClangModules/serialization-sil.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,36 @@ public func testPartialApply(_ obj: Test) {
// CHECK-LABEL: @_TF4Test16testPartialApplyFPSo4Test_T_ : $@convention(thin) (@owned Test) -> () {
if let curried1 = obj.normalObject {
// CHECK: dynamic_method_br [[CURRIED1_OBJ:%.+]] : $@opened([[CURRIED1_EXISTENTIAL:.+]]) Test, #Test.normalObject!1.foreign, [[CURRIED1_TRUE:[^,]+]], [[CURRIED1_FALSE:[^,]+]]
// CHECK: [[CURRIED1_FALSE]]:
// CHECK: [[CURRIED1_TRUE]]([[CURRIED1_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject):
// CHECK: [[CURRIED1_PARTIAL:%.+]] = partial_apply [[CURRIED1_METHOD]]([[CURRIED1_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject
// CHECK: [[CURRIED1_THUNK:%.+]] = function_ref @_TTRXFo__oPs9AnyObject__XFo_iT__iPS___ : $@convention(thin) (@in (), @owned @callee_owned () -> @owned AnyObject) -> @out AnyObject
// CHECK: = partial_apply [[CURRIED1_THUNK]]([[CURRIED1_PARTIAL]]) : $@convention(thin) (@in (), @owned @callee_owned () -> @owned AnyObject) -> @out AnyObject
// CHECK: [[CURRIED1_FALSE]]:
curried1()
}
if let curried2 = obj.innerPointer {
// CHECK: dynamic_method_br [[CURRIED2_OBJ:%.+]] : $@opened([[CURRIED2_EXISTENTIAL:.+]]) Test, #Test.innerPointer!1.foreign, [[CURRIED2_TRUE:[^,]+]], [[CURRIED2_FALSE:[^,]+]]
// CHECK: [[CURRIED2_FALSE]]:
// CHECK: [[CURRIED2_TRUE]]([[CURRIED2_METHOD:%.+]] : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutablePointer<()>):
// CHECK: [[CURRIED2_PARTIAL:%.+]] = partial_apply [[CURRIED2_METHOD]]([[CURRIED2_OBJ]]) : $@convention(objc_method) (@opened([[CURRIED2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutablePointer<()>
// CHECK: [[CURRIED2_THUNK:%.+]] = function_ref @_TTRXFo__dGSpT___XFo_iT__iGSpT___ : $@convention(thin) (@in (), @owned @callee_owned () -> UnsafeMutablePointer<()>) -> @out UnsafeMutablePointer<()>
// CHECK: = partial_apply [[CURRIED2_THUNK]]([[CURRIED2_PARTIAL]]) : $@convention(thin) (@in (), @owned @callee_owned () -> UnsafeMutablePointer<()>) -> @out UnsafeMutablePointer<()>
// CHECK: [[CURRIED2_FALSE]]:
curried2()
}
if let prop1 = obj.normalObjectProp {
// CHECK: dynamic_method_br [[PROP1_OBJ:%.+]] : $@opened([[PROP1_EXISTENTIAL:.+]]) Test, #Test.normalObjectProp!getter.1.foreign, [[PROP1_TRUE:[^,]+]], [[PROP1_FALSE:[^,]+]]
// CHECK: [[PROP1_FALSE]]:
// CHECK: [[PROP1_TRUE]]([[PROP1_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject):
// CHECK: [[PROP1_PARTIAL:%.+]] = partial_apply [[PROP1_METHOD]]([[PROP1_OBJ]]) : $@convention(objc_method) (@opened([[PROP1_EXISTENTIAL]]) Test) -> @autoreleased AnyObject
// CHECK: = apply [[PROP1_PARTIAL]]() : $@callee_owned () -> @owned AnyObject
// CHECK: [[PROP1_FALSE]]:
_ = prop1
}
if let prop2 = obj.innerPointerProp {
// CHECK: dynamic_method_br [[PROP2_OBJ:%.+]] : $@opened([[PROP2_EXISTENTIAL:.+]]) Test, #Test.innerPointerProp!getter.1.foreign, [[PROP2_TRUE:[^,]+]], [[PROP2_FALSE:[^,]+]]
// CHECK: [[PROP2_FALSE]]:
// CHECK: [[PROP2_TRUE]]([[PROP2_METHOD:%.+]] : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutablePointer<()>):
// CHECK: [[PROP2_PARTIAL:%.+]] = partial_apply [[PROP2_METHOD]]([[PROP2_OBJ]]) : $@convention(objc_method) (@opened([[PROP2_EXISTENTIAL]]) Test) -> @unowned_inner_pointer UnsafeMutablePointer<()>
// CHECK: = apply [[PROP2_PARTIAL]]() : $@callee_owned () -> UnsafeMutablePointer<()>
// CHECK: [[PROP2_FALSE]]:
_ = prop2
}
} // CHECK: {{^}$}}
48 changes: 48 additions & 0 deletions test/SILOptimizer/cse.sil
Original file line number Diff line number Diff line change
Expand Up @@ -1319,3 +1319,51 @@ bb0(%0 : $*Builtin.Int64, %1 : $Builtin.NativeObject):
%6 = tuple(%4 : $Builtin.Int64, %5 : $Builtin.Int64)
return %6 : $(Builtin.Int64, Builtin.Int64)
}

protocol Proto : class {
func doThis()
func doThat()
}

// Check that all open_existential_ref instructions are CSEd
// CHECK-LABEL: sil @cse_open_existential : $@convention(thin) (@guaranteed Proto, Bool) -> ()
// CHECK: bb0
// CHECK: %[[OPENED_EXISTENTIAL:[0-9]+]] = open_existential_ref %{{[0-9]+}} : $Proto to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto
// CHECK: %[[WM1:[0-9]+]] = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThis!1
// CHECK: apply %[[WM1]]{{.*}}(%[[OPENED_EXISTENTIAL]])
// CHECK: cond_br
// CHECK: bb1:
// CHECK-NEXT: %[[WM2:[0-9]+]] = witness_method $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto, #Proto.doThat!1
// CHECK-NEXT: apply %[[WM2]]{{.*}}(%[[OPENED_EXISTENTIAL]])
// CHECK-NEXT: br
// CHECK: bb2:
// CHECK-NEXT: apply %[[WM1]]{{.*}}(%[[OPENED_EXISTENTIAL]])
// CHECK-NEXT: br
// CHECK: bb3:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @cse_open_existential : $@convention(thin) (@guaranteed Proto, Bool) -> () {
bb0(%0 : $Proto, %1 : $Bool):
%4 = open_existential_ref %0 : $Proto to $@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto
%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) -> ()
%6 = apply %5<@opened("1B68354A-4796-11E6-B7DF-B8E856428C60") Proto>(%4) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
%7 = struct_extract %1 : $Bool, #Bool._value
cond_br %7, bb1, bb2

bb1:
%9 = open_existential_ref %0 : $Proto to $@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto
%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) -> ()
%11 = apply %10<@opened("1B685052-4796-11E6-B7DF-B8E856428C60") Proto>(%9) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
br bb3

bb2: // Preds: bb0
%13 = open_existential_ref %0 : $Proto to $@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto
%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) -> ()
%15 = apply %14<@opened("1B6851A6-4796-11E6-B7DF-B8E856428C60") Proto>(%13) : $@convention(witness_method) <τ_0_0 where τ_0_0 : Proto> (@guaranteed τ_0_0) -> ()
br bb3

bb3:
%17 = tuple ()
return %17 : $()
}

Loading