Skip to content

Commit 6f60a17

Browse files
authored
Merge pull request #34633 from gottesmm/pr-200203a48964c3efb090a7f4f2389b115a88b607
[ownership] Change switch_enum to be a true forwarding instructions.
2 parents 901bce2 + 264955c commit 6f60a17

File tree

20 files changed

+473
-319
lines changed

20 files changed

+473
-319
lines changed

include/swift/SIL/SILInstruction.h

Lines changed: 132 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
#include "swift/Strings.h"
4141
#include "llvm/ADT/APFloat.h"
4242
#include "llvm/ADT/APInt.h"
43+
#include "llvm/ADT/SmallPtrSet.h"
4344
#include "llvm/ADT/ilist.h"
4445
#include "llvm/ADT/ilist_node.h"
4546
#include "llvm/Support/TrailingObjects.h"
@@ -7318,6 +7319,20 @@ class TermInst : public NonValueInstruction {
73187319
}
73197320
};
73207321

7322+
class OwnershipForwardingTermInst : public TermInst {
7323+
ValueOwnershipKind ownershipKind;
7324+
7325+
protected:
7326+
OwnershipForwardingTermInst(SILInstructionKind kind,
7327+
SILDebugLocation debugLoc,
7328+
ValueOwnershipKind ownershipKind)
7329+
: TermInst(kind, debugLoc), ownershipKind(ownershipKind) {}
7330+
7331+
public:
7332+
ValueOwnershipKind getOwnershipKind() const { return ownershipKind; }
7333+
void setOwnershipKind(ValueOwnershipKind newKind) { ownershipKind = newKind; }
7334+
};
7335+
73217336
/// UnreachableInst - Position in the code which would be undefined to reach.
73227337
/// These are always implicitly generated, e.g. when falling off the end of a
73237338
/// function or after a no-return function call.
@@ -7806,9 +7821,9 @@ class SwitchValueInst final
78067821
}
78077822
};
78087823

7809-
/// Common implementation for the switch_enum and
7810-
/// switch_enum_addr instructions.
7811-
class SwitchEnumInstBase : public TermInst {
7824+
/// Common implementation for the switch_enum and switch_enum_addr instructions.
7825+
template <typename BaseTy>
7826+
class SwitchEnumInstBase : public BaseTy {
78127827
FixedOperandList<1> Operands;
78137828

78147829
// Tail-allocated after the SwitchEnumInst record are:
@@ -7837,48 +7852,97 @@ class SwitchEnumInstBase : public TermInst {
78377852
}
78387853

78397854
protected:
7855+
template <typename... Rest>
78407856
SwitchEnumInstBase(
78417857
SILInstructionKind Kind, SILDebugLocation DebugLoc, SILValue Operand,
78427858
SILBasicBlock *DefaultBB,
78437859
ArrayRef<std::pair<EnumElementDecl *, SILBasicBlock *>> CaseBBs,
7844-
Optional<ArrayRef<ProfileCounter>> Counts, ProfileCounter DefaultCount);
7860+
Optional<ArrayRef<ProfileCounter>> Counts, ProfileCounter DefaultCount,
7861+
Rest &&... rest)
7862+
: BaseTy(Kind, DebugLoc, std::forward<Rest>(rest)...),
7863+
Operands(this, Operand) {
7864+
SILInstruction::Bits.SEIBase.HasDefault = bool(DefaultBB);
7865+
SILInstruction::Bits.SEIBase.NumCases = CaseBBs.size();
7866+
// Initialize the case and successor arrays.
7867+
auto *cases = getCaseBuf();
7868+
auto *succs = getSuccessorBuf();
7869+
for (unsigned i = 0, size = CaseBBs.size(); i < size; ++i) {
7870+
cases[i] = CaseBBs[i].first;
7871+
if (Counts) {
7872+
::new (succs + i)
7873+
SILSuccessor(this, CaseBBs[i].second, Counts.getValue()[i]);
7874+
} else {
7875+
::new (succs + i) SILSuccessor(this, CaseBBs[i].second);
7876+
}
7877+
}
7878+
7879+
if (hasDefault()) {
7880+
::new (succs + getNumCases()) SILSuccessor(this, DefaultBB, DefaultCount);
7881+
}
7882+
}
78457883

7846-
template <typename SWITCH_ENUM_INST>
7884+
template <typename SWITCH_ENUM_INST, typename... RestTys>
78477885
static SWITCH_ENUM_INST *createSwitchEnum(
78487886
SILDebugLocation DebugLoc, SILValue Operand, SILBasicBlock *DefaultBB,
78497887
ArrayRef<std::pair<EnumElementDecl *, SILBasicBlock *>> CaseBBs,
78507888
SILFunction &F, Optional<ArrayRef<ProfileCounter>> Counts,
7851-
ProfileCounter DefaultCount);
7889+
ProfileCounter DefaultCount, RestTys &&... restArgs);
78527890

78537891
public:
78547892
/// Clean up tail-allocated successor records for the switch cases.
7855-
~SwitchEnumInstBase();
7893+
~SwitchEnumInstBase() {
7894+
// Destroy the successor records to keep the CFG up to date.
7895+
auto *succs = getSuccessorBuf();
7896+
for (unsigned i = 0, end = getNumCases() + hasDefault(); i < end; ++i) {
7897+
succs[i].~SILSuccessor();
7898+
}
7899+
}
78567900

78577901
SILValue getOperand() const { return Operands[0].get(); }
78587902

78597903
ArrayRef<Operand> getAllOperands() const { return Operands.asArray(); }
78607904
MutableArrayRef<Operand> getAllOperands() { return Operands.asArray(); }
78617905

7862-
SuccessorListTy getSuccessors() {
7906+
TermInst::SuccessorListTy getSuccessors() {
78637907
return MutableArrayRef<SILSuccessor>{getSuccessorBuf(),
78647908
static_cast<size_t>(getNumCases() + hasDefault())};
78657909
}
78667910

7867-
unsigned getNumCases() const {
7868-
return SILInstruction::Bits.SwitchEnumInstBase.NumCases;
7869-
}
7911+
unsigned getNumCases() const { return SILInstruction::Bits.SEIBase.NumCases; }
7912+
78707913
std::pair<EnumElementDecl*, SILBasicBlock*>
78717914
getCase(unsigned i) const {
78727915
assert(i < getNumCases() && "case out of bounds");
78737916
return {getCaseBuf()[i], getSuccessorBuf()[i].getBB()};
78747917
}
7918+
78757919
ProfileCounter getCaseCount(unsigned i) const {
78767920
assert(i < getNumCases() && "case out of bounds");
78777921
return getSuccessorBuf()[i].getCount();
78787922
}
78797923

78807924
// Swap the cases at indices \p i and \p j.
7881-
void swapCase(unsigned i, unsigned j);
7925+
void swapCase(unsigned i, unsigned j) {
7926+
assert(i < getNumCases() && "First index is out of bounds?!");
7927+
assert(j < getNumCases() && "Second index is out of bounds?!");
7928+
7929+
auto *succs = getSuccessorBuf();
7930+
7931+
// First grab our destination blocks.
7932+
SILBasicBlock *iBlock = succs[i].getBB();
7933+
SILBasicBlock *jBlock = succs[j].getBB();
7934+
7935+
// Then destroy the sil successors and reinitialize them with the new things
7936+
// that they are pointing at.
7937+
succs[i].~SILSuccessor();
7938+
::new (succs + i) SILSuccessor(this, jBlock);
7939+
succs[j].~SILSuccessor();
7940+
::new (succs + j) SILSuccessor(this, iBlock);
7941+
7942+
// Now swap our cases.
7943+
auto *cases = getCaseBuf();
7944+
std::swap(cases[i], cases[j]);
7945+
}
78827946

78837947
/// Return the block that will be branched to on the specified enum
78847948
/// case.
@@ -7893,22 +7957,69 @@ class SwitchEnumInstBase : public TermInst {
78937957
}
78947958

78957959
/// If the default refers to exactly one case decl, return it.
7896-
NullablePtr<EnumElementDecl> getUniqueCaseForDefault();
7960+
NullablePtr<EnumElementDecl> getUniqueCaseForDefault() {
7961+
auto enumValue = getOperand();
7962+
SILType enumType = enumValue->getType();
7963+
7964+
auto *f = SILInstruction::getFunction();
7965+
if (!enumType.isEffectivelyExhaustiveEnumType(f))
7966+
return nullptr;
7967+
7968+
EnumDecl *decl = enumType.getEnumOrBoundGenericEnum();
7969+
assert(decl && "switch_enum operand is not an enum");
7970+
7971+
SmallPtrSet<EnumElementDecl *, 4> unswitchedElts;
7972+
for (auto elt : decl->getAllElements())
7973+
unswitchedElts.insert(elt);
7974+
7975+
for (unsigned i = 0, e = getNumCases(); i != e; ++i) {
7976+
auto Entry = getCase(i);
7977+
unswitchedElts.erase(Entry.first);
7978+
}
7979+
7980+
if (unswitchedElts.size() == 1)
7981+
return *unswitchedElts.begin();
7982+
7983+
return nullptr;
7984+
}
78977985

78987986
/// If the given block only has one enum element decl matched to it,
78997987
/// return it.
7900-
NullablePtr<EnumElementDecl> getUniqueCaseForDestination(SILBasicBlock *BB);
7901-
7902-
bool hasDefault() const {
7903-
return SILInstruction::Bits.SwitchEnumInstBase.HasDefault;
7988+
NullablePtr<EnumElementDecl>
7989+
getUniqueCaseForDestination(SILBasicBlock *block) {
7990+
SILValue value = getOperand();
7991+
SILType enumType = value->getType();
7992+
EnumDecl *decl = enumType.getEnumOrBoundGenericEnum();
7993+
assert(decl && "switch_enum operand is not an enum");
7994+
(void)decl;
7995+
7996+
EnumElementDecl *eltDecl = nullptr;
7997+
for (unsigned i : range(getNumCases())) {
7998+
auto entry = getCase(i);
7999+
if (entry.second == block) {
8000+
if (eltDecl != nullptr)
8001+
return nullptr;
8002+
eltDecl = entry.first;
8003+
}
8004+
}
8005+
if (!eltDecl && hasDefault() && getDefaultBB() == block) {
8006+
return getUniqueCaseForDefault();
8007+
}
8008+
return eltDecl;
79048009
}
79058010

8011+
bool hasDefault() const { return SILInstruction::Bits.SEIBase.HasDefault; }
8012+
79068013
SILBasicBlock *getDefaultBB() const {
79078014
assert(hasDefault() && "doesn't have a default");
79088015
return getSuccessorBuf()[getNumCases()];
79098016
}
79108017

7911-
NullablePtr<SILBasicBlock> getDefaultBBOrNull() const;
8018+
NullablePtr<SILBasicBlock> getDefaultBBOrNull() const {
8019+
if (!hasDefault())
8020+
return nullptr;
8021+
return getDefaultBB();
8022+
}
79128023

79138024
ProfileCounter getDefaultCount() const {
79148025
assert(hasDefault() && "doesn't have a default");
@@ -7925,7 +8036,7 @@ class SwitchEnumInstBase : public TermInst {
79258036
/// passed into the corresponding destination block as an argument.
79268037
class SwitchEnumInst
79278038
: public InstructionBase<SILInstructionKind::SwitchEnumInst,
7928-
SwitchEnumInstBase> {
8039+
SwitchEnumInstBase<OwnershipForwardingTermInst>> {
79298040
friend SILBuilder;
79308041

79318042
private:
@@ -7937,7 +8048,7 @@ class SwitchEnumInst
79378048
Optional<ArrayRef<ProfileCounter>> CaseCounts,
79388049
ProfileCounter DefaultCount)
79398050
: InstructionBase(DebugLoc, Operand, DefaultBB, CaseBBs, CaseCounts,
7940-
DefaultCount) {}
8051+
DefaultCount, Operand.getOwnershipKind()) {}
79418052
static SwitchEnumInst *
79428053
create(SILDebugLocation DebugLoc, SILValue Operand, SILBasicBlock *DefaultBB,
79438054
ArrayRef<std::pair<EnumElementDecl *, SILBasicBlock *>> CaseBBs,
@@ -7948,7 +8059,7 @@ class SwitchEnumInst
79488059
/// A switch on an enum's discriminator in memory.
79498060
class SwitchEnumAddrInst
79508061
: public InstructionBase<SILInstructionKind::SwitchEnumAddrInst,
7951-
SwitchEnumInstBase> {
8062+
SwitchEnumInstBase<TermInst>> {
79528063
friend SILBuilder;
79538064

79548065
private:

include/swift/SIL/SILNode.h

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -362,12 +362,25 @@ class alignas(8) SILNode {
362362
IBWTO_BITFIELD(SwitchValueInst, TermInst, 1,
363363
HasDefault : 1
364364
);
365-
SWIFT_INLINE_BITFIELD_FULL(SwitchEnumInstBase, TermInst, 1+32,
365+
366+
// Special handling for SwitchEnumInstBase.
367+
//
368+
// We assume all subsequent SwitchEnumBit uses do not use any further bits.
369+
SWIFT_INLINE_BITFIELD(SEIBase, TermInst, 32-NumTermInstBits,
370+
// Does this switch enum inst have a default block.
366371
HasDefault : 1,
367-
: NumPadBits,
368-
NumCases : 32
372+
// Number of cases
373+
NumCases : 31 - NumTermInstBits;
374+
template <typename BaseTy>
375+
friend class SwitchEnumInstBase;
369376
);
370377

378+
#define SEIB_BITFIELD_EMPTY(T, U) \
379+
IBWTO_BITFIELD_EMPTY(T, U)
380+
381+
SEIB_BITFIELD_EMPTY(SwitchEnumInst, SEIBase);
382+
SEIB_BITFIELD_EMPTY(SwitchEnumAddrInst, SEIBase);
383+
371384
SWIFT_INLINE_BITFIELD_EMPTY(MultipleValueInstruction, SILInstruction);
372385

373386
SWIFT_INLINE_BITFIELD(BeginCOWMutationInst, MultipleValueInstruction, 1,

include/swift/SIL/SILType.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,10 @@ class SILType {
429429
SILType getEnumElementType(EnumElementDecl *elt, SILModule &M,
430430
TypeExpansionContext context) const;
431431

432+
/// Given that this is an enum type, return true if this type is effectively
433+
/// exhausted.
434+
bool isEffectivelyExhaustiveEnumType(SILFunction *f);
435+
432436
/// Given that this is a tuple type, return the lowered type of the
433437
/// given tuple element. The result will have the same value
434438
/// category as the base type.

0 commit comments

Comments
 (0)