mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
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:
@@ -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.
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 *
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>):
|
||||
|
||||
Reference in New Issue
Block a user