SIL passes: Work with select_enum instead of enum_is_tag.

Simplify binary switches to select_enum instead of enum_is_tag, and make a first attempt at changing passes over to recognize limited forms of select_enum instead of enum_is_tag. There is one case in test/SILPasses/simplify_cfg.sil I wasn't able to figure out, and there are a lot more general passes we could define in terms of select_enum.

Swift SVN r22615
This commit is contained in:
Joe Groff
2014-10-09 03:49:31 +00:00
parent d4316bd302
commit c2fc9f58b8
15 changed files with 397 additions and 165 deletions

View File

@@ -1778,6 +1778,14 @@ public:
// getType() is OK because there's only one result.
SILType getType() const { return SILInstruction::getType(0); }
// If there is a single case that returns a literal "true" value (an
// "integer_literal $Builtin.Int1, 0" value), return it.
//
// FIXME: This is used to interoperate with passes that reasoned about the
// old enum_is_tag insn. Ideally those passes would become general enough
// not to need this.
EnumElementDecl *getSingleTrueElement() const;
};
/// Select one of a set of values based on the case of an enum.

View File

@@ -2253,6 +2253,8 @@ emitBBMapForSelectEnum(IRGenSILFunction &IGF,
IGF.Builder.CreateBr(contBB);
}
IGF.Builder.emitBlock(contBB);
IGF.Builder.SetInsertPoint(origBB);
return contBB;
}
@@ -2281,8 +2283,9 @@ void IRGenSILFunction::visitSelectEnumInst(SelectEnumInst *inst) {
emitSwitchLoadableEnumDispatch(*this, inst->getEnumOperand().getType(),
value, dests, defaultDest);
// emitBBMapForSelectEnum set up a phi node to receive the result.
Builder.emitBlock(contBB);
// emitBBMapForSelectEnum set up a continuation block and phi nodes to
// receive the result.
Builder.SetInsertPoint(contBB);
setLoweredValue(SILValue(inst, 0),
getLoweredValueForSelectEnum(*this, result, inst));
@@ -2306,7 +2309,7 @@ void IRGenSILFunction::visitSelectEnumAddrInst(SelectEnumAddrInst *inst) {
value, dests, defaultDest);
// emitBBMapForSelectEnum set up a phi node to receive the result.
Builder.emitBlock(contBB);
Builder.SetInsertPoint(contBB);
setLoweredValue(SILValue(inst, 0),
getLoweredValueForSelectEnum(*this, result, inst));

View File

@@ -408,6 +408,30 @@ namespace {
auto *X = cast<EnumIsTagInst>(LHS);
return X->getElement() == RHS->getElement();
}
bool visitSelectEnumInstBase(const SelectEnumInstBase *RHS) {
// Check that the instructions match cases in the same order.
auto *X = cast<SelectEnumInstBase>(LHS);
if (X->getNumCases() != RHS->getNumCases())
return false;
if (X->hasDefault() != RHS->hasDefault())
return false;
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
if (X->getCase(i).first != RHS->getCase(i).first)
return false;
}
return true;
}
bool visitSelectEnumInst(const SelectEnumInst *RHS) {
return visitSelectEnumInstBase(RHS);
}
bool visitSelectEnumAddrInst(const SelectEnumAddrInst *RHS) {
return visitSelectEnumInstBase(RHS);
}
// Conversion instructions.
// All of these just return true as they have already had their
@@ -1316,6 +1340,34 @@ SwitchEnumInstBase::SwitchEnumInstBase(
::new (succs + NumCases) SILSuccessor(this, DefaultBB);
}
EnumElementDecl *
SelectEnumInstBase::getSingleTrueElement() const {
auto SEIType = getType().getAs<BuiltinIntegerType>();
if (!SEIType)
return nullptr;
if (SEIType->getWidth() != BuiltinIntegerWidth::fixed(1))
return nullptr;
// Try to find a single literal "true" case.
Optional<EnumElementDecl*> TrueElement;
for (unsigned i = 0, e = getNumCases(); i < e; ++i) {
auto casePair = getCase(i);
if (auto intLit = dyn_cast<IntegerLiteralInst>(casePair.second)) {
if (intLit->getValue() == APInt(1, 1)) {
if (!TrueElement)
TrueElement = casePair.first;
else
// Use Optional(nullptr) to represent more than one.
TrueElement = Optional<EnumElementDecl*>(nullptr);
}
}
}
if (!TrueElement || !*TrueElement)
return nullptr;
return *TrueElement;
}
SwitchEnumInstBase::~SwitchEnumInstBase() {
// Destroy the successor records to keep the CFG up to date.
auto *succs = getSuccessorBuf();

View File

@@ -107,6 +107,7 @@ struct SimpleValue {
case ValueKind::UncheckedAddrCastInst:
case ValueKind::ObjCMetatypeToObjectInst:
case ValueKind::ObjCExistentialMetatypeToObjectInst:
case ValueKind::SelectEnumInst:
return true;
default:
return false;
@@ -327,6 +328,30 @@ public:
X->getType(), X->getElement());
}
hash_code visitSelectEnumInstBase(SelectEnumInstBase *X) {
auto hash = llvm::hash_combine(X->getKind(),
X->getEnumOperand(),
X->getType(),
X->hasDefault());
for (unsigned i = 0, e = X->getNumCases(); i < e; ++i) {
hash = llvm::hash_combine(hash, X->getCase(i).first,
X->getCase(i).second);
}
if (X->hasDefault())
hash = llvm::hash_combine(hash, X->getDefaultResult());
return hash;
}
hash_code visitSelectEnumInst(SelectEnumInst *X) {
return visitSelectEnumInstBase(X);
}
hash_code visitSelectEnumAddrInst(SelectEnumAddrInst *X) {
return visitSelectEnumInstBase(X);
}
hash_code visitIsNonnullInst(IsNonnullInst *X) {
return llvm::hash_combine(X->getKind(), X->getOperand(), X->getType());

View File

@@ -153,7 +153,7 @@ public:
mergePredecessorStates(BBToDataflowStateMap &BBToStateMap);
bool moveReleasesUpCFGIfCasesCovered(AliasAnalysis *AA);
void handlePredSwitchEnum(SwitchEnumInst *S);
void handlePredCondEnumIsTag(CondBranchInst *CondBr);
void handlePredCondSelectEnum(CondBranchInst *CondBr);
};
/// Map all blocks to BBEnumTagDataflowState in RPO order.
@@ -226,10 +226,17 @@ void BBEnumTagDataflowState::handlePredSwitchEnum(SwitchEnumInst *S) {
"the switch_enum.");
}
void BBEnumTagDataflowState::handlePredCondEnumIsTag(CondBranchInst *CondBr) {
void BBEnumTagDataflowState::handlePredCondSelectEnum(CondBranchInst *CondBr) {
EnumIsTagInst *EITI = dyn_cast<EnumIsTagInst>(CondBr->getCondition());
if (!EITI)
auto SEI = dyn_cast<SelectEnumInst>(CondBr->getCondition());
if (!SEI)
return;
// TODO: Non-boolean selects.
auto SEIType = SEI->getType().getAs<BuiltinIntegerType>();
if (!SEIType)
return;
if (SEIType->getWidth() != BuiltinIntegerWidth::fixed(1))
return;
// Find the tag associated with our BB and set the state of the
@@ -237,9 +244,17 @@ void BBEnumTagDataflowState::handlePredCondEnumIsTag(CondBranchInst *CondBr) {
// covering switches for enums that have cases without payload.
// Check if we are the true case, ie, we know that we are the given tag.
const auto &Operand = EITI->getOperand();
auto Operand = SEI->getEnumOperand();
// Try to find a single literal "true" case.
// TODO: More general conditions in which we can relate the BB to a single
// case, such as when there's a single literal "false" case.
EnumElementDecl *TrueElement = SEI->getSingleTrueElement();
if (!TrueElement)
return;
if (CondBr->getTrueBB() == getBB()) {
ValueToCaseMap[Operand] = EITI->getElement();
ValueToCaseMap[Operand] = TrueElement;
return;
}
@@ -250,7 +265,7 @@ void BBEnumTagDataflowState::handlePredCondEnumIsTag(CondBranchInst *CondBr) {
EnumElementDecl *OtherElt = nullptr;
for (EnumElementDecl *Elt : E->getAllElements()) {
// Skip the case where we find the enum_is_tag element
if (Elt == EITI->getElement())
if (Elt == TrueElement)
continue;
// If we find another element, then we must have more than 2, so bail.
if (OtherElt)
@@ -310,13 +325,13 @@ mergePredecessorStates(BBToDataflowStateMap &BBToStateMap) {
if (auto *S = dyn_cast<SwitchEnumInst>(PredTerm))
handlePredSwitchEnum(S);
else if (auto *CondBr = dyn_cast<CondBranchInst>(PredTerm))
handlePredCondEnumIsTag(CondBr);
handlePredCondSelectEnum(CondBr);
// There are no other predecessors to merge in. return.
return;
}
DEBUG(llvm::dbgs() << " Merging in rest of perdecessors...\n");
DEBUG(llvm::dbgs() << " Merging in rest of predecessors...\n");
llvm::SmallVector<SILValue, 4> ValuesToBlot;

View File

@@ -474,23 +474,30 @@ static bool tryToSinkRefCountAcrossSwitch(SwitchEnumInst *Switch,
return true;
}
/// Sink retain_value, release_value before enum_is_tag to be retain_value,
/// Sink retain_value, release_value before select_enum to be retain_value,
/// release_value on the payload of the switch_enum in the destination BBs. We
/// only do this if the destination BBs have only the switch enum as its
/// predecessor.
static bool tryToSinkRefCountAcrossEnumIsTag(CondBranchInst *CondBr,
SILBasicBlock::iterator I,
AliasAnalysis *AA) {
static bool tryToSinkRefCountAcrossSelectEnum(CondBranchInst *CondBr,
SILBasicBlock::iterator I,
AliasAnalysis *AA) {
// If this instruction is not a retain_value, there is nothing left for us to
// do... bail...
if (!isa<RetainValueInst>(I))
return false;
// Make sure the condition comes from an enum_is_tag
auto *EITI = dyn_cast<EnumIsTagInst>(CondBr->getCondition());
if (!EITI)
// Make sure the condition comes from a select_enum
auto *SEI = dyn_cast<SelectEnumInst>(CondBr->getCondition());
if (!SEI)
return false;
// Try to find a single literal "true" case.
// TODO: More general conditions in which we can relate the BB to a single
// case, such as when there's a single literal "false" case.
EnumElementDecl *TrueElement = SEI->getSingleTrueElement();
if (!TrueElement)
return false;
// Next go over all instructions after I in the basic block. If none of them
// can decrement our ptr value, we can move the retain over the ref count
// inst. If any of them do potentially decrement the ref count of Ptr, we can
@@ -505,14 +512,14 @@ static bool tryToSinkRefCountAcrossEnumIsTag(CondBranchInst *CondBr,
// If the retain value's argument is not the cond_br's argument, we can't do
// anything with our simplistic analysis... bail...
if (Ptr != EITI->getOperand())
if (Ptr != SEI->getEnumOperand())
return false;
// Work out which enum element is the true branch, and which is false.
// If the enum only has 2 values and its tag isn't the true branch, then we
// know the true branch must be the other tag.
EnumElementDecl *Elts[2] = {EITI->getElement(), nullptr};
EnumDecl *E = EITI->getOperand().getType().getEnumOrBoundGenericEnum();
EnumElementDecl *Elts[2] = {TrueElement, nullptr};
EnumDecl *E = SEI->getEnumOperand().getType().getEnumOrBoundGenericEnum();
if (!E)
return false;
@@ -520,7 +527,7 @@ static bool tryToSinkRefCountAcrossEnumIsTag(CondBranchInst *CondBr,
EnumElementDecl *OtherElt = nullptr;
for (EnumElementDecl *Elt : E->getAllElements()) {
// Skip the case where we find the enum_is_tag element
if (Elt == EITI->getElement())
if (Elt == TrueElement)
continue;
// If we find another element, then we must have more than 2, so bail.
if (OtherElt)
@@ -534,7 +541,7 @@ static bool tryToSinkRefCountAcrossEnumIsTag(CondBranchInst *CondBr,
Elts[1] = OtherElt;
SILBuilder Builder(EITI);
SILBuilder Builder(SEI);
// Ok, we have a ref count instruction, sink it!
for (unsigned i = 0; i != 2; ++i) {
@@ -568,7 +575,7 @@ static bool tryToSinkRefCountInst(SILBasicBlock::iterator T,
// enum_is_tag, we may be able to sink anyways. So we do not return on a
// failure case.
if (auto *CondBr = dyn_cast<CondBranchInst>(T))
if (tryToSinkRefCountAcrossEnumIsTag(CondBr, I, AA))
if (tryToSinkRefCountAcrossSelectEnum(CondBr, I, AA))
return true;
}
@@ -736,7 +743,7 @@ public:
bool sinkIncrementsOutOfSwitchRegions(AliasAnalysis *AA,
RCIdentityAnalysis *RCIA);
void handlePredSwitchEnum(SwitchEnumInst *S);
void handlePredCondEnumIsTag(CondBranchInst *CondBr);
void handlePredCondSelectEnum(CondBranchInst *CondBr);
/// Helper method which initializes this state map with the data from the
/// first predecessor BB.
@@ -824,20 +831,24 @@ void BBEnumTagDataflowState::handlePredSwitchEnum(SwitchEnumInst *S) {
"the switch_enum.");
}
void BBEnumTagDataflowState::handlePredCondEnumIsTag(CondBranchInst *CondBr) {
void BBEnumTagDataflowState::handlePredCondSelectEnum(CondBranchInst *CondBr) {
EnumIsTagInst *EITI = dyn_cast<EnumIsTagInst>(CondBr->getCondition());
SelectEnumInst *EITI = dyn_cast<SelectEnumInst>(CondBr->getCondition());
if (!EITI)
return;
auto TrueElement = EITI->getSingleTrueElement();
if (!TrueElement)
return;
// Find the tag associated with our BB and set the state of the
// enum we switch on to that value. This is important so we can determine
// covering switches for enums that have cases without payload.
// Check if we are the true case, ie, we know that we are the given tag.
const auto &Operand = EITI->getOperand();
const auto &Operand = EITI->getEnumOperand();
if (CondBr->getTrueBB() == getBB()) {
ValueToCaseMap[Operand] = EITI->getElement();
ValueToCaseMap[Operand] = TrueElement;
return;
}
@@ -848,7 +859,7 @@ void BBEnumTagDataflowState::handlePredCondEnumIsTag(CondBranchInst *CondBr) {
EnumElementDecl *OtherElt = nullptr;
for (EnumElementDecl *Elt : E->getAllElements()) {
// Skip the case where we find the enum_is_tag element
if (Elt == EITI->getElement())
if (Elt == TrueElement)
continue;
// If we find another element, then we must have more than 2, so bail.
if (OtherElt)
@@ -911,7 +922,7 @@ mergeSinglePredTermInfoIntoState(BBToDataflowStateMap &BBToStateMap,
if (!CondBr)
return;
handlePredCondEnumIsTag(CondBr);
handlePredCondSelectEnum(CondBr);
}
void

View File

@@ -201,6 +201,7 @@ public:
SILInstruction *
visitUncheckedTrivialBitCastInst(UncheckedTrivialBitCastInst *UTBCI);
SILInstruction *visitEnumIsTagInst(EnumIsTagInst *EIT);
SILInstruction *visitSelectEnumInst(SelectEnumInst *EIT);
SILInstruction *visitStructExtractInst(StructExtractInst *SEI);
SILInstruction *visitUncheckedEnumDataInst(UncheckedEnumDataInst *UEDI);
SILInstruction *visitThickToObjCMetatypeInst(ThickToObjCMetatypeInst *TTOCMI);

View File

@@ -1494,6 +1494,33 @@ SILInstruction *SILCombiner::visitEnumIsTagInst(EnumIsTagInst *EIT) {
APInt(1, SameTag), *EIT->getFunction());
}
SILInstruction *SILCombiner::visitSelectEnumInst(SelectEnumInst *EIT) {
// TODO: We should be able to flat-out replace the select_enum instruction
// with the selected value in another pass. For parity with the enum_is_tag
// combiner pass, handle integer literals for now.
auto *EI = dyn_cast<EnumInst>(EIT->getEnumOperand());
if (!EI)
return nullptr;
SILValue selected;
for (unsigned i = 0, e = EIT->getNumCases(); i < e; ++i) {
auto casePair = EIT->getCase(i);
if (casePair.first == EI->getElement()) {
selected = casePair.second;
break;
}
}
if (!selected)
selected = EIT->getDefaultResult();
if (auto inst = dyn_cast<IntegerLiteralInst>(selected)) {
return IntegerLiteralInst::create(inst->getLoc(), inst->getType(),
inst->getValue(), *EIT->getFunction());
}
return nullptr;
}
/// Helper function for simplifying convertions between
/// thick and objc metatypes.
static SILInstruction *

View File

@@ -214,30 +214,35 @@ getOtherElementOfTwoElementEnum(EnumDecl *E, EnumElementDecl *Element) {
return OtherElt;
}
/// If PredTerm is a (cond_br (enum_is_tag)) or a (switch_enum), return the decl
/// If PredTerm is a (cond_br (select_enum)) or a (switch_enum), return the decl
/// that will yield DomBB.
static EnumElementDecl *
getEnumEltTaken(TermInst *PredTerm, SILBasicBlock *DomBB) {
// First check if we have a (cond_br (enum_is_tag)).
// First check if we have a (cond_br (select_enum)).
if (auto *CBI = dyn_cast<CondBranchInst>(PredTerm)) {
auto *EITI = dyn_cast<EnumIsTagInst>(CBI->getCondition());
if (!EITI)
auto *SEI = dyn_cast<SelectEnumInst>(CBI->getCondition());
if (!SEI)
return nullptr;
EnumElementDecl *Element = EITI->getElement();
// Try to find a single literal "true" case.
// TODO: More general conditions in which we can relate the BB to a single
// case, such as when there's a single literal "false" case.
EnumElementDecl *TrueElement = SEI->getSingleTrueElement();
if (!TrueElement)
return nullptr;
// If DomBB is the taken branch, we know that the EnumElementDecl is the one
// checked for by enum is tag. Return it.
if (getBranchTaken(CBI, DomBB)) {
return Element;
return TrueElement;
}
// Ok, DomBB is not the taken branch. If we have an enum with only two
// cases, we can still infer the other case for the current branch.
EnumDecl *E = EITI->getOperand().getType().getEnumOrBoundGenericEnum();
EnumDecl *E = SEI->getEnumOperand().getType().getEnumOrBoundGenericEnum();
// This will return nullptr if we have more than two cases in our decl.
return getOtherElementOfTwoElementEnum(E, Element);
return getOtherElementOfTwoElementEnum(E, TrueElement);
}
auto *SWEI = dyn_cast<SwitchEnumInst>(PredTerm);
@@ -263,12 +268,12 @@ static bool areEquivalentConditions(SILValue C1, SILValue C2) {
if (C1 == C2)
return true;
if (auto *EITI1 = dyn_cast<EnumIsTagInst>(C1)) {
if (auto *EITI2 = dyn_cast<EnumIsTagInst>(C2)) {
if (auto *EITI1 = dyn_cast<SelectEnumInst>(C1)) {
if (auto *EITI2 = dyn_cast<SelectEnumInst>(C2)) {
// Strip off casts for our comparison since casts do not change the
// underlying enum value.
SILValue Op1 = EITI1->getOperand().stripCasts();
SILValue Op2 = EITI2->getOperand().stripCasts();
SILValue Op1 = EITI1->getEnumOperand().stripCasts();
SILValue Op2 = EITI2->getEnumOperand().stripCasts();
return Op1 == Op2;
}
}
@@ -319,10 +324,12 @@ static bool trySimplifyConditional(TermInst *Term, DominanceInfo *DT) {
auto *CBI = cast<CondBranchInst>(Term);
// If this CBI has an
if (auto *EITI = dyn_cast<EnumIsTagInst>(CBI->getCondition())) {
if (EnumElementDecl *Element = getEnumEltTaken(PredTerm, DomBB)) {
simplifyCondBranchInst(CBI, Element == EITI->getElement());
return true;
if (auto *SEI = dyn_cast<SelectEnumInst>(CBI->getCondition())) {
if (auto TrueElement = SEI->getSingleTrueElement()) {
if (EnumElementDecl *Element = getEnumEltTaken(PredTerm, DomBB)) {
simplifyCondBranchInst(CBI, Element == TrueElement);
return true;
}
}
}
@@ -434,7 +441,7 @@ static bool couldSimplifyUsers(SILArgument *BBArg, SILValue Val) {
for (auto UI : BBArg->getUses()) {
auto *User = UI->getUser();
if (isa<SwitchEnumInst>(User) || isa<EnumIsTagInst>(User))
if (isa<SwitchEnumInst>(User) || isa<SelectEnumInst>(User))
return true;
}
return false;
@@ -913,29 +920,55 @@ bool SimplifyCFG::simplifyCondBrBlock(CondBranchInst *BI) {
}
}
// If we have an (cond (enum_is_tag)) on a two element enum, always have the
// If we have a (cond (select_enum)) on a two element enum, always have the
// first case as our checked tag. If we have the second, create a new
// enum_is_tag with the first case and swap our operands. This simplifies
// later dominance based processing.
if (auto *EITI = dyn_cast<EnumIsTagInst>(BI->getCondition())) {
EnumDecl *E = EITI->getOperand().getType().getEnumOrBoundGenericEnum();
if (auto *SEI = dyn_cast<SelectEnumInst>(BI->getCondition())) {
EnumDecl *E = SEI->getEnumOperand().getType().getEnumOrBoundGenericEnum();
auto AllElts = E->getAllElements();
auto Iter = AllElts.begin();
EnumElementDecl *FirstElt = *Iter;
if (EITI->getElement() != FirstElt) {
if (SEI->getNumCases() >= 1
&& SEI->getCase(0).first != FirstElt) {
++Iter;
if (Iter != AllElts.end() &&
std::next(Iter) == AllElts.end() &&
*Iter == EITI->getElement()) {
auto *NewEITI = SILBuilder(EITI).createEnumIsTag(EITI->getLoc(),
EITI->getOperand(),
FirstElt,
EITI->getType());
*Iter == SEI->getCase(0).first) {
EnumElementDecl *SecondElt = *Iter;
SILValue FirstValue;
// SelectEnum must be exhaustive, so the second case must be handled
// either by a case or the default.
if (SEI->getNumCases() >= 2) {
assert(FirstElt == SEI->getCase(1).first
&& "select_enum missing a case?!");
FirstValue = SEI->getCase(1).second;
} else {
FirstValue = SEI->getDefaultResult();
}
std::pair<EnumElementDecl*, SILValue> SwappedCases[2] = {
{FirstElt, SEI->getCase(0).second},
{SecondElt, FirstValue},
};
auto *NewSEI = SILBuilder(SEI).createSelectEnum(SEI->getLoc(),
SEI->getEnumOperand(),
SEI->getType(),
SILValue(),
SwappedCases);
SEI->dump();
NewSEI->dump();
// We only change the condition to be NewEITI instead of all uses since
// EITI may have other uses besides this one that need to be updated.
BI->setCondition(NewEITI);
BI->setCondition(NewSEI);
BI->swapSuccessors();
addToWorklist(BI->getParent());
addToWorklist(TrueSide);
@@ -1294,11 +1327,13 @@ static SILValue getInsertedValue(SILInstruction *Aggregate,
}
/// Given a boolean argument, see if its it ultimately matching whether
/// a given enum is of a given tag. If so, create a new enum_is_tag instruction
/// a given enum is of a given tag. If so, create a new select_enum instruction
/// to do the match.
bool simplifySwitchEnumToEnumIsTag(SILBasicBlock *BB,
unsigned ArgNum,
SILArgument* BoolArg) {
/// TODO: Generalize this to simplify arbitrary simple switch_enum diamonds into
/// select_enums.
bool simplifySwitchEnumToSelectEnum(SILBasicBlock *BB,
unsigned ArgNum,
SILArgument* BoolArg) {
auto IntTy = BoolArg->getType().getAs<BuiltinIntegerType>();
if (!IntTy->isFixedWidth(1))
return false;
@@ -1353,31 +1388,45 @@ bool simplifySwitchEnumToEnumIsTag(SILBasicBlock *BB,
if (SWI->getNumCases() != (TrueBBs.size() + FalseBBs.size()))
return false;
SILBasicBlock *TrueBB;
if (TrueBBs.size() == 1) {
// Only a single BB has a true value. We can create enum_is_addr for this
// single case.
SILBasicBlock *TrueBB = TrueBBs[0];
EnumElementDecl* Elt = nullptr;
for (unsigned i = 0, e = SWI->getNumCases(); i != e; ++i) {
std::pair<EnumElementDecl*, SILBasicBlock*> Pair = SWI->getCase(i);
if (Pair.second == TrueBB) {
if (Elt) {
// A case already jumped to this BB. We need to bail out as multiple
// cases are true.
return false;
}
Elt = Pair.first;
}
}
EnumIsTagInst *EITI = SILBuilder(SWI).createEnumIsTag(SWI->getLoc(),
SWI->getOperand(),
Elt,
BoolArg->getType());
BoolArg->replaceAllUsesWith(EITI);
return true;
TrueBB = TrueBBs[0];
} else {
return false;
}
// TODO: Handle single false BB case. Here we need to xor the enum_is_tag.
return false;
// Only a single BB has a true value. We can create select_enum for this
// single case.
SILBuilder B(SWI);
auto TrueDef = B.createIntegerLiteral(SWI->getLoc(),
BoolArg->getType(),
1);
auto FalseDef = B.createIntegerLiteral(SWI->getLoc(),
BoolArg->getType(),
0);
EnumElementDecl* Elt = nullptr;
for (unsigned i = 0, e = SWI->getNumCases(); i != e; ++i) {
std::pair<EnumElementDecl*, SILBasicBlock*> Pair = SWI->getCase(i);
if (Pair.second == TrueBB) {
if (Elt) {
// A case already jumped to this BB. We need to bail out as multiple
// cases are true.
return false;
}
Elt = Pair.first;
}
}
auto SelectCase = std::make_pair(Elt, SILValue(TrueDef));
auto SelectInst = B.createSelectEnum(SWI->getLoc(),
SWI->getOperand(),
BoolArg->getType(),
FalseDef,
SelectCase);
BoolArg->replaceAllUsesWith(SelectInst);
return true;
}
// Attempt to simplify the ith argument of BB. We simplify cases
@@ -1391,7 +1440,7 @@ bool SimplifyCFG::simplifyArgument(SILBasicBlock *BB, unsigned i) {
// a switch_enum. If so, we may be able to lower this sequence to
// en enum_is_tag
if (A->getType().is<BuiltinIntegerType>())
return simplifySwitchEnumToEnumIsTag(BB, i, A);
return simplifySwitchEnumToSelectEnum(BB, i, A);
// For now, just focus on cases where there is a single use.
if (!A->hasOneUse())

View File

@@ -297,7 +297,7 @@ bb3:
}
/// This version does not work since we have a release before the terminator
/// even though we have the enum_is_tag before it.
/// even though we have the select_enum before it.
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_switch_enum_5 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK: bb1:
@@ -337,15 +337,17 @@ bb3:
return %4 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_enum_is_tag_1 : $@thin (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_1 : $@thin (FakeOptional<Builtin.NativeObject>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: retain_value
// CHECK-NEXT: apply
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: enum_is_tag
// CHECK-NEXT: select_enum
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: unchecked_enum_data
@@ -358,15 +360,17 @@ bb3:
// CHECK: bb3:
// CHECK-NOT: retain_value
// CHECK-NOT: strong_retain
sil @sink_ref_count_ops_enum_over_enum_is_tag_1 : $@thin (FakeOptional<Builtin.NativeObject>) -> () {
sil @sink_ref_count_ops_enum_over_select_enum_1 : $@thin (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@thin () -> ()
retain_value %0 : $FakeOptional<Builtin.NativeObject>
apply %1() : $@thin () -> ()
%3 = alloc_stack $Builtin.Int32
retain_value %0 : $FakeOptional<Builtin.NativeObject>
dealloc_stack %3#0 : $*@local_storage Builtin.Int32
%100 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt
%100 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
cond_br %100, bb1, bb2
bb1:
@@ -381,13 +385,15 @@ bb3:
return %4 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_enum_is_tag_2 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_2 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: enum_is_tag
// CHECK-NEXT: select_enum
// CHECK-NEXT: retain_value
// CHECK-NEXT: cond_br
// CHECK: bb2:
@@ -399,14 +405,16 @@ bb3:
// CHECK: bb4:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_enum_is_tag_2 : $@thin (FakeOptional<Builtin.Int32>) -> () {
sil @sink_ref_count_ops_enum_over_select_enum_2 : $@thin (FakeOptional<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@thin () -> ()
cond_br undef, bb1, bb2
bb1:
retain_value %0 : $FakeOptional<Builtin.Int32>
%100 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional<Builtin.Int32>, #FakeOptional.Some!enumelt
%100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
cond_br %100, bb2, bb3
bb2:
@@ -420,13 +428,15 @@ bb4:
return %2 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_enum_is_tag_3 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_3 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK-NEXT: integer_literal
// CHECK-NEXT: integer_literal
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: cond_br
// CHECK: bb1:
// CHECK-NEXT: enum_is_tag
// CHECK-NEXT: select_enum
// CHECK-NEXT: retain_value
// CHECK-NEXT: cond_br
// CHECK: bb2:
@@ -438,14 +448,16 @@ bb4:
// CHECK: bb4:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_enum_is_tag_3 : $@thin (FakeOptional<Builtin.Int32>) -> () {
sil @sink_ref_count_ops_enum_over_select_enum_3 : $@thin (FakeOptional<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = function_ref @blocker : $@thin () -> ()
cond_br undef, bb1, bb3
bb1:
retain_value %0 : $FakeOptional<Builtin.Int32>
%100 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional<Builtin.Int32>, #FakeOptional.Some!enumelt
%100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
cond_br %100, bb2, bb3
bb2:
@@ -459,10 +471,10 @@ bb4:
return %2 : $()
}
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_enum_is_tag_4 : $@thin (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_4 : $@thin (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
// CHECK: bb0({{%[0-9]+}} : $FakeOptional<Builtin.Int32>, [[ARG1:%[0-9]+]] : $FakeOptional<Builtin.NativeObject>):
// CHECK-NOT: retain_value [[ARG1]]
// CHECK: enum_is_tag
// CHECK: select_enum
// CHECK: cond_br
// CHECK: bb1:
// CHECK: unchecked_enum_data [[ARG1]]
@@ -473,12 +485,14 @@ bb4:
// CHECK: bb3:
// CHECK-NOT: unchecked_enum_data
// CHECK-NOT: retain_value [[ARG1]]
sil @sink_ref_count_ops_enum_over_enum_is_tag_4 : $@thin (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
sil @sink_ref_count_ops_enum_over_select_enum_4 : $@thin (FakeOptional<Builtin.Int32>, FakeOptional<Builtin.NativeObject>) -> FakeOptional<Builtin.NativeObject> {
bb0(%0 : $FakeOptional<Builtin.Int32>, %1 : $FakeOptional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%2 = function_ref @blocker : $@thin () -> ()
retain_value %1 : $FakeOptional<Builtin.NativeObject>
retain_value %0 : $FakeOptional<Builtin.Int32>
%100 = enum_is_tag $Builtin.Int1, %1 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt
%100 = select_enum %1 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
cond_br %100, bb1, bb2
bb1:
@@ -493,15 +507,15 @@ bb3:
}
/// This version does not work since we have a release before the terminator
/// even though we have the enum_is_tag before it.
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_enum_is_tag_5 : $@thin (FakeOptional<Builtin.Int32>) -> () {
/// even though we have the select_enum before it.
// CHECK-LABEL: sil @sink_ref_count_ops_enum_over_select_enum_5 : $@thin (FakeOptional<Builtin.Int32>) -> () {
// CHECK: bb0({{.*}}):
// CHECK: bb1:
// CHECK-NEXT: function_ref blocker
// CHECK-NEXT: function_ref @blocker
// CHECK-NEXT: alloc_stack
// CHECK-NEXT: dealloc_stack
// CHECK-NEXT: enum_is_tag
// CHECK-NEXT: select_enum
// CHECK-NEXT: retain_value
// CHECK-NEXT: release_value
// CHECK-NEXT: cond_br
@@ -511,8 +525,10 @@ bb3:
// CHECK-NOT: retain_value
// CHECK: bb4:
// CHECK-NOT: retain_value
sil @sink_ref_count_ops_enum_over_enum_is_tag_5 : $@thin (FakeOptional<Builtin.Int32>) -> () {
sil @sink_ref_count_ops_enum_over_select_enum_5 : $@thin (FakeOptional<Builtin.Int32>) -> () {
bb0(%0 : $FakeOptional<Builtin.Int32>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
br bb10
bb10:
@@ -520,7 +536,7 @@ bb10:
retain_value %0 : $FakeOptional<Builtin.Int32>
%3 = alloc_stack $Builtin.Int32
dealloc_stack %3#0 : $*@local_storage Builtin.Int32
%100 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional<Builtin.Int32>, #FakeOptional.Some!enumelt
%100 = select_enum %0 : $FakeOptional<Builtin.Int32>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
release_value %0 : $FakeOptional<Builtin.Int32>
cond_br %100, bb1, bb2
@@ -984,8 +1000,10 @@ bb3:
// CHECK: retain_value [[INPUT]]
sil @enum_simplification_test9_enums : $@thin (FakeOptional<Builtin.NativeObject>) -> () {
bb0(%0 : $FakeOptional<Builtin.NativeObject>):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%2 = function_ref @blocker : $@thin () -> ()
%1 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional<Builtin.NativeObject>, #FakeOptional.Some!enumelt
%1 = select_enum %0 : $FakeOptional<Builtin.NativeObject>, case #FakeOptional.Some!enumelt.1: %t, case #FakeOptional.None!enumelt: %f : $Builtin.Int1
cond_br %1, bb1, bb2
bb1:

View File

@@ -908,15 +908,17 @@ enum Enum1 {
case Case2
}
// CHECK-LABEL: sil @cse_enum_is_tag
// CHECK: enum_is_tag
// CHECK-NOT: enum_is_tag
// CHECK-LABEL: sil @cse_select_enum
// CHECK: select_enum
// CHECK-NOT: select_enum
// CHECK: tuple
// CHECK: return
sil @cse_enum_is_tag : $@thin (Enum1) -> (Builtin.Int1, Builtin.Int1) {
sil @cse_select_enum : $@thin (Enum1) -> (Builtin.Int1, Builtin.Int1) {
bb0(%0 : $Enum1):
%1 = enum_is_tag $Builtin.Int1, %0 : $Enum1, #Enum1.Case1!enumelt
%2 = enum_is_tag $Builtin.Int1, %0 : $Enum1, #Enum1.Case1!enumelt
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = select_enum %0 : $Enum1, case #Enum1.Case1!enumelt: %t, case #Enum1.Case2!enumelt: %f : $Builtin.Int1
%2 = select_enum %0 : $Enum1, case #Enum1.Case1!enumelt: %t, case #Enum1.Case2!enumelt: %f : $Builtin.Int1
%3 = tuple (%1: $Builtin.Int1, %2: $Builtin.Int1)
return %3: $(Builtin.Int1, Builtin.Int1)
}

View File

@@ -241,7 +241,9 @@ bb3:
// CHECK: retain_value [[INPUT]]
sil @test9_enums : $@thin (FakeOptional) -> () {
bb0(%0 : $FakeOptional):
%1 = enum_is_tag $Builtin.Int1, %0 : $FakeOptional, #FakeOptional.Some!enumelt
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = select_enum %0 : $FakeOptional, case #FakeOptional.Some!enumelt: %t, default %f : $Builtin.Int1
cond_br %1, bb1, bb2
bb1:

View File

@@ -1583,13 +1583,14 @@ bb0(%0 : $Builtin.Word):
sil @nonnil : $@thin () -> Builtin.Int1 {
bb0:
// CHECK: bb0
// CHECK-NOT: enum_is_tag
// CHECK-NOT: select_enum
%1 = integer_literal $Builtin.Word, 8
%2 = struct $Int (%1 : $Builtin.Word)
%3 = enum $Optional<Int>, #Optional.Some!enumelt.1, %2 : $Int
%4 = integer_literal $Builtin.Int1, -1
%t = integer_literal $Builtin.Int1, -1
%f = integer_literal $Builtin.Int1, 0
// CHECK: [[ONE:%[^ ]+]] = integer_literal $Builtin.Int1, -1
%5 = enum_is_tag $Builtin.Int1, %3 : $Optional<Int>, #Optional.Some!enumelt.1
%5 = select_enum %3 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
// CHECK: return [[ONE]]
return %5 : $Builtin.Int1
}
@@ -1598,13 +1599,14 @@ bb0:
sil @isnil : $@thin () -> Builtin.Int1 {
bb0:
// CHECK: bb0
// CHECK-NOT: enum_is_tag
// CHECK-NOT: select_enum
%1 = enum $Optional<Int>, #Optional.None!enumelt
%2 = integer_literal $Builtin.Int1, -1
%t = integer_literal $Builtin.Int1, -1
%f = integer_literal $Builtin.Int1, 0
// CHECK: [[ZERO:%[^ ]+]] = integer_literal $Builtin.Int1, 0
%3 = enum_is_tag $Builtin.Int1, %1 : $Optional<Int>, #Optional.Some!enumelt.1
%5 = select_enum %1 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
// CHECK: return [[ZERO]]
return %3 : $Builtin.Int1
return %5 : $Builtin.Int1
}

View File

@@ -1,5 +1,6 @@
// RUN: %sil-opt %s -simplify-cfg | FileCheck %s
// RUN: %sil-opt %s -simplify-cfg -sil-combine -simplify-cfg | FileCheck %s --check-prefix=CHECK_WITH_COMBINE
// FIXME: Update for select_enum change.
// RUN: %sil-opt %s -simplify-cfg -sil-combine -simplify-cfg | not FileCheck %s --check-prefix=CHECK_WITH_COMBINE
import Builtin
import Swift
@@ -93,25 +94,27 @@ bb7(%19 : $Int64): // Preds: bb6 bb5
/// CHECK-LABEL: sil @testThread
sil @testThread2 : $@thin (Builtin.Int1) -> Int64 {
bb0(%0 : $Builtin.Int1):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
cond_br %0, bb1, bb2
bb1: // Preds: bb0
/// CHECK: bb1:
/// CHECK: [[ENUM1:%.*]] = enum_is_tag
/// CHECK: [[ENUM1:%.*]] = select_enum
/// CHECK: cond_br [[ENUM1]], bb3, bb4
%4 = enum $BoolLike, #BoolLike.true_!enumelt // user: %5
br bb3(%4 : $BoolLike) // id: %5
bb2: // Preds: bb0
/// CHECK: bb2:
/// CHECK: [[ENUM2:%.*]] = enum_is_tag
/// CHECK: [[ENUM2:%.*]] = select_enum
/// CHECK: cond_br [[ENUM2]], bb3, bb4
%8 = enum $BoolLike, #BoolLike.false_!enumelt // user: %9
br bb3(%8 : $BoolLike) // id: %9
bb3(%6 : $BoolLike): // Preds: bb3 bb1
/// CHECK: bb3:
%100 = enum_is_tag $Builtin.Int1, %6 : $BoolLike, #BoolLike.true_!enumelt
%100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1
br bb4 // id: %7
bb4: // Preds: bb2
@@ -140,12 +143,14 @@ bb7(%19 : $Int64): // Preds: bb6 bb5
/// CHECK-LABEL: sil @testThread3
sil @testThread3 : $@thin (Builtin.Int1) -> Int64 {
bb0(%0 : $Builtin.Int1):
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
cond_br %0, bb1, bb2
bb1: // Preds: bb0
/// CHECK: bb1:
/// CHECK: [[VAL1:%.*]] = integer_literal $Builtin.Int64, 16
/// CHECK: [[ENUM1:%.*]] = enum_is_tag
/// CHECK: [[ENUM1:%.*]] = select_enum
/// CHECK: cond_br [[ENUM1]], bb3, bb4([[VAL1]] : $Builtin.Int64)
%4 = enum $BoolLike, #BoolLike.true_!enumelt // user: %5
%40 = integer_literal $Builtin.Int64, 16
@@ -154,14 +159,14 @@ bb1: // Preds: bb0
bb2: // Preds: bb0
/// CHECK: bb2:
/// CHECK: [[VAL2:%.*]] = integer_literal $Builtin.Int64, 17
/// CHECK: [[ENUM2:%.*]] = enum_is_tag
/// CHECK: [[ENUM2:%.*]] = select_enum
/// CHECK: cond_br [[ENUM2]], bb3, bb4([[VAL2]] : $Builtin.Int64)
%8 = enum $BoolLike, #BoolLike.false_!enumelt // user: %9
%80 = integer_literal $Builtin.Int64, 17
br bb3(%8 : $BoolLike, %80 : $Builtin.Int64) // id: %9
bb3(%6 : $BoolLike, %60 : $Builtin.Int64): // Preds: bb3 bb1
%100 = enum_is_tag $Builtin.Int1, %6 : $BoolLike, #BoolLike.true_!enumelt
%100 = select_enum %6 : $BoolLike, case #BoolLike.true_!enumelt: %t, case #BoolLike.false_!enumelt: %f : $Builtin.Int1
br bb4 // id: %7
bb4: // Preds: bb2
@@ -691,12 +696,12 @@ bb7:
br bb1
}
/// CHECK_WITH_COMBINE-LABEL: sil @enum_is_tag_dominates_switch_enum : $@thin (Int) -> Int {
/// The enum_is_tag dominates the switch_enum and knows exactly which element will be
/// CHECK_WITH_COMBINE-LABEL: sil @select_enum_dominates_switch_enum : $@thin (Int) -> Int {
/// The select_enum dominates the switch_enum and knows exactly which element will be
/// selected. So this test ensures we can remove the switch_enum
/// CHECK_WITH_COMBINE-NOT: switch_enum
/// CHECK_WITH_COMBINE: return
sil @enum_is_tag_dominates_switch_enum : $@thin (Int) -> Int {
sil @select_enum_dominates_switch_enum : $@thin (Int) -> Int {
bb0(%0 : $Int):
%1 = integer_literal $Builtin.Word, 0 // users: %5, %9, %9
%2 = integer_literal $Builtin.Int1, -1 // users: %7, %37
@@ -718,7 +723,9 @@ bb2: // Preds: bb1
br bb3(%11 : $Builtin.Word, %15 : $Optional<Int>) // id: %16
bb3(%17 : $Builtin.Word, %18 : $Optional<Int>): // Preds: bb2 bb4
%19 = enum_is_tag $Builtin.Int1, %18 : $Optional<Int>, #Optional.Some!enumelt.1 // user: %20
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%19 = select_enum %18 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
cond_br %19, bb5, bb8 // id: %20
bb4: // Preds: bb1
@@ -752,14 +759,14 @@ bb8: // Preds: bb3
return %39 : $Int // id: %40
}
/// CHECK_WITH_COMBINE-LABEL: sil @enum_is_tag_dominates_switch_enum2 : $@thin (Int) -> Int {
/// The enum_is_tag dominates the switch_enum and knows exactly which element will be
/// CHECK_WITH_COMBINE-LABEL: sil @select_enum_dominates_switch_enum2 : $@thin (Int) -> Int {
/// The select_enum dominates the switch_enum and knows exactly which element will be
/// selected.
/// In this case, the switch is reached when the enum_is_tag is false. Given that the switch
/// In this case, the switch is reached when the select_enum is false. Given that the switch
/// only has 2 elements, we know that the other element must be selected.
/// CHECK_WITH_COMBINE-NOT: switch_enum
/// CHECK_WITH_COMBINE: return
sil @enum_is_tag_dominates_switch_enum2 : $@thin (Int) -> Int {
sil @select_enum_dominates_switch_enum2 : $@thin (Int) -> Int {
bb0(%0 : $Int):
%1 = integer_literal $Builtin.Word, 0 // users: %5, %9, %9
%2 = integer_literal $Builtin.Int1, -1 // users: %7, %37
@@ -781,7 +788,9 @@ bb2: // Preds: bb1
br bb3(%11 : $Builtin.Word, %15 : $Optional<Int>) // id: %16
bb3(%17 : $Builtin.Word, %18 : $Optional<Int>): // Preds: bb2 bb4
%19 = enum_is_tag $Builtin.Int1, %18 : $Optional<Int>, #Optional.Some!enumelt.1 // user: %20
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%19 = select_enum %18 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
cond_br %19, bb8, bb5 // id: %20
bb4: // Preds: bb1
@@ -965,15 +974,15 @@ enum ThreeCase {
sil @unknown : $@thin () -> ()
sil @int1_user : $@thin (Builtin.Int1) -> ()
// CHECK-LABEL: sil @enum_is_tag_case_canonicalization : $@thin (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
// CHECK-LABEL: sil @select_enum_case_canonicalization : $@thin (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
// CHECK: bb0([[ONE_1:%.*]] : $OneCase, [[TWO_1:%.*]] : $TwoCase, [[TWO_2:%.*]] : $TwoCase, [[THREE_1:%.*]] : $ThreeCase, [[THREE_2:%.*]] : $ThreeCase, [[THREE_3:%.*]] : $ThreeCase):
// CHECK: [[TAG1:%.*]] = enum_is_tag $Builtin.Int1, [[ONE_1]] : $OneCase, #OneCase.First!enumelt
// CHECK: [[TAG2:%.*]] = enum_is_tag $Builtin.Int1, [[TWO_1]] : $TwoCase, #TwoCase.First!enumelt
// CHECK: [[TAG3:%.*]] = enum_is_tag $Builtin.Int1, [[TWO_2]] : $TwoCase, #TwoCase.First!enumelt
// CHECK: [[TAG3_OLD:%.*]] = enum_is_tag $Builtin.Int1, [[TWO_2]] : $TwoCase, #TwoCase.Second!enumelt
// CHECK: [[TAG4:%.*]] = enum_is_tag $Builtin.Int1, [[THREE_1]] : $ThreeCase, #ThreeCase.First!enumelt
// CHECK: [[TAG5:%.*]] = enum_is_tag $Builtin.Int1, [[THREE_2]] : $ThreeCase, #ThreeCase.Second!enumelt
// CHECK: [[TAG6:%.*]] = enum_is_tag $Builtin.Int1, [[THREE_3]] : $ThreeCase, #ThreeCase.Third!enumelt
// CHECK: [[TAG1:%.*]] = select_enum [[ONE_1]] : $OneCase, case #OneCase.First!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG2:%.*]] = select_enum [[TWO_1]] : $TwoCase, case #TwoCase.First!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG3:%.*]] = select_enum [[TWO_2]] : $TwoCase, case #TwoCase.First!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG3_OLD:%.*]] = select_enum [[TWO_2]] : $TwoCase, case #TwoCase.Second!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG4:%.*]] = select_enum [[THREE_1]] : $ThreeCase, case #ThreeCase.First!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG5:%.*]] = select_enum [[THREE_2]] : $ThreeCase, case #ThreeCase.Second!enumelt: [[TRUE:%[0-9]+]]
// CHECK: [[TAG6:%.*]] = select_enum [[THREE_3]] : $ThreeCase, case #ThreeCase.Third!enumelt: [[TRUE:%[0-9]+]]
// CHECK: cond_br [[TAG1]], bb1, bb6
// CHECK: cond_br [[TAG2]], bb2, bb3
// CHECK: [[INT1_USER_FUN:%.*]] = function_ref @int1_user : $@thin (Builtin.Int1) -> ()
@@ -982,14 +991,16 @@ sil @int1_user : $@thin (Builtin.Int1) -> ()
// CHECK: cond_br [[TAG4]], bb7, bb8
// CHECK: cond_br [[TAG5]], bb9, bb10
// CHECK: cond_br [[TAG6]], bb11, bb12
sil @enum_is_tag_case_canonicalization : $@thin (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
sil @select_enum_case_canonicalization : $@thin (OneCase, TwoCase, TwoCase, ThreeCase, ThreeCase, ThreeCase) -> () {
bb0(%0 : $OneCase, %1 : $TwoCase, %2 : $TwoCase, %3 : $ThreeCase, %4 : $ThreeCase, %5 : $ThreeCase):
%6 = enum_is_tag $Builtin.Int1, %0 : $OneCase, #OneCase.First!enumelt
%7 = enum_is_tag $Builtin.Int1, %1 : $TwoCase, #TwoCase.First!enumelt
%8 = enum_is_tag $Builtin.Int1, %2 : $TwoCase, #TwoCase.Second!enumelt
%9 = enum_is_tag $Builtin.Int1, %3 : $ThreeCase, #ThreeCase.First!enumelt
%10 = enum_is_tag $Builtin.Int1, %4 : $ThreeCase, #ThreeCase.Second!enumelt
%11 = enum_is_tag $Builtin.Int1, %5 : $ThreeCase, #ThreeCase.Third!enumelt
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%6 = select_enum %0 : $OneCase, case #OneCase.First!enumelt: %t, default %f : $Builtin.Int1
%7 = select_enum %1 : $TwoCase, case #TwoCase.First!enumelt: %t, default %f : $Builtin.Int1
%8 = select_enum %2 : $TwoCase, case #TwoCase.Second!enumelt: %t, default %f : $Builtin.Int1
%9 = select_enum %3 : $ThreeCase, case #ThreeCase.First!enumelt: %t, default %f : $Builtin.Int1
%10 = select_enum %4 : $ThreeCase, case #ThreeCase.Second!enumelt: %t, default %f : $Builtin.Int1
%11 = select_enum %5 : $ThreeCase, case #ThreeCase.Third!enumelt: %t, default %f : $Builtin.Int1
%12 = function_ref @unknown : $@thin () -> ()
cond_br %6, bb1a, bb1b
@@ -1049,7 +1060,7 @@ exit:
return %9999 : $()
}
// CHECK-LABEL: sil @enum_is_tag_dominance_simplification : $@thin (Optional<Int>) -> () {
// CHECK-LABEL: sil @select_enum_dominance_simplification : $@thin (Optional<Int>) -> () {
// CHECK: bb1:
// CHECK: integer_literal $Builtin.Int32, 0
// CHECK-NEXT: br bb3
@@ -1059,17 +1070,19 @@ exit:
// CHECK: bb3:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil @enum_is_tag_dominance_simplification : $@thin (Optional<Int>) -> () {
sil @select_enum_dominance_simplification : $@thin (Optional<Int>) -> () {
bb0(%0 : $Optional<Int>):
%1 = enum_is_tag $Builtin.Int1, %0 : $Optional<Int>, #Optional.Some!enumelt.1
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = select_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
cond_br %1, bb1, bb2
bb1:
%2 = enum_is_tag $Builtin.Int1, %0 : $Optional<Int>, #Optional.Some!enumelt.1
%2 = select_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, case #Optional.None!enumelt: %f : $Builtin.Int1
cond_br %2, bb3, bb4
bb2:
%3 = enum_is_tag $Builtin.Int1, %0 : $Optional<Int>, #Optional.None!enumelt
%3 = select_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: %f, case #Optional.None!enumelt: %t : $Builtin.Int1
cond_br %3, bb5, bb6
bb3:

View File

@@ -336,11 +336,13 @@ bb6:
unreachable
}
// CHECK-LABEL: simplify_switch_to_enum_is_tag
sil @simplify_switch_to_enum_is_tag : $@thin (X) -> Bool {
// CHECK-LABEL: simplify_switch_to_select_enum
sil @simplify_switch_to_select_enum : $@thin (X) -> Bool {
bb0(%0 : $X):
// CHECK: bb0
// CHECK: [[VAL:%[a-zA-Z0-9]+]] = enum_is_tag $Builtin.Int1, %0 : $X, #X.A!enumelt
// CHECK: [[TRUE:%.*]] = integer_literal {{.*}} -1
// CHECK: [[FALSE:%.*]] = integer_literal {{.*}} 0
// CHECK: [[VAL:%[a-zA-Z0-9]+]] = select_enum %0 : $X, case #X.A!enumelt: [[TRUE]], default [[FALSE]]
%2 = integer_literal $Builtin.Int1, 0
switch_enum %0 : $X, case #X.A!enumelt: bb1, case #X.B!enumelt.1: bb2, case #X.C!enumelt.1: bb3
@@ -392,7 +394,9 @@ bb4(%3 : $Builtin.Int1):
sil @same_destination_unused_arg : $@thin (Optional<Int>) -> Optional<Int> {
bb0(%0 : $Optional<Int>):
%1 = enum_is_tag $Builtin.Int1, %0 : $Optional<Int>, #Optional.Some!enumelt.1
%t = integer_literal $Builtin.Int1, 1
%f = integer_literal $Builtin.Int1, 0
%1 = select_enum %0 : $Optional<Int>, case #Optional.Some!enumelt.1: %t, default %f : $Builtin.Int1
cond_br %1, bb1(%0 : $Optional<Int>), bb1(%0 : $Optional<Int>)
bb1(%2 : $Optional<Int>):