Skip to content

Commit ca7254c

Browse files
committed
IRGen: Preliminary support for resilient enums
Resilient enums are manipulated as opaque values. Clients are still allowed to assume physical case indices and case payload types for now -- we might add a level of indirection here, which would require designing a new case dispatch mechanism. Resilient enums are never constructed directly, only by calling case constructor functions. Case constructors already get emitted, however they're [transparent] -- this will change in a subsequent patch. We could save on code size by emitting an InjectEnumTag value witness function that can construct any case given a physical case number, rather than emitting constructors for each case, but for now going through case constructor functions will suffice.
1 parent c06f335 commit ca7254c

File tree

7 files changed

+521
-17
lines changed

7 files changed

+521
-17
lines changed

lib/IRGen/GenEnum.cpp

Lines changed: 342 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@
9292
// for the payload, resulting in a fixed-size lowering for recursive
9393
// enums.
9494
//
95+
// For all lowerings except ResilientEnumImplStrategy, the primary enum
96+
// operations are open-coded at usage sites. Resilient enums are accessed
97+
// by invoking the value witnesses for these operations.
9598
//
9699
//===----------------------------------------------------------------------===//
97100

@@ -112,6 +115,7 @@
112115
#include "IRGenModule.h"
113116
#include "LoadableTypeInfo.h"
114117
#include "NonFixedTypeInfo.h"
118+
#include "ResilientTypeInfo.h"
115119
#include "GenMeta.h"
116120
#include "GenProto.h"
117121
#include "GenType.h"
@@ -4047,6 +4051,305 @@ namespace {
40474051
return result;
40484052
}
40494053
};
4054+
4055+
class ResilientEnumImplStrategy final
4056+
: public EnumImplStrategy
4057+
{
4058+
public:
4059+
ResilientEnumImplStrategy(IRGenModule &IGM,
4060+
unsigned NumElements,
4061+
std::vector<Element> &&WithPayload,
4062+
std::vector<Element> &&WithNoPayload)
4063+
: EnumImplStrategy(IGM, Opaque, IsFixedSize,
4064+
NumElements,
4065+
std::move(WithPayload),
4066+
std::move(WithNoPayload))
4067+
{ }
4068+
4069+
TypeInfo *completeEnumTypeLayout(TypeConverter &TC,
4070+
SILType Type,
4071+
EnumDecl *theEnum,
4072+
llvm::StructType *enumTy) override;
4073+
4074+
void destructiveProjectDataForLoad(IRGenFunction &IGF,
4075+
SILType T,
4076+
Address enumAddr) const override {
4077+
emitDestructiveProjectEnumDataCall(IGF, T, enumAddr.getAddress());
4078+
}
4079+
4080+
void storeTag(IRGenFunction &IGF,
4081+
SILType T,
4082+
Address enumAddr,
4083+
EnumElementDecl *Case) const override {
4084+
llvm_unreachable("resilient enums cannot be constructed directly");
4085+
}
4086+
4087+
llvm::Value *
4088+
emitIndirectCaseTest(IRGenFunction &IGF, SILType T,
4089+
Address enumAddr,
4090+
EnumElementDecl *Case) const override {
4091+
llvm::Value *tag = emitGetEnumTagCall(IGF, T, enumAddr.getAddress());
4092+
llvm::Value *expectedTag = llvm::ConstantInt::get(IGF.IGM.Int32Ty,
4093+
getTagIndex(Case));
4094+
return IGF.Builder.CreateICmpEQ(tag, expectedTag);
4095+
}
4096+
4097+
void emitIndirectSwitch(IRGenFunction &IGF,
4098+
SILType T,
4099+
Address enumAddr,
4100+
ArrayRef<std::pair<EnumElementDecl*,
4101+
llvm::BasicBlock*>> dests,
4102+
llvm::BasicBlock *defaultDest) const override {
4103+
// Switch on the tag value.
4104+
llvm::Value *tag = emitGetEnumTagCall(IGF, T, enumAddr.getAddress());
4105+
4106+
// Create a map of the destination blocks for quicker lookup.
4107+
llvm::DenseMap<EnumElementDecl*,llvm::BasicBlock*> destMap(dests.begin(),
4108+
dests.end());
4109+
4110+
// Create an unreachable branch for unreachable switch defaults.
4111+
auto &C = IGF.IGM.getLLVMContext();
4112+
auto *unreachableBB = llvm::BasicBlock::Create(C);
4113+
4114+
// If there was no default branch in SIL, use the unreachable branch as
4115+
// the default.
4116+
if (!defaultDest)
4117+
defaultDest = unreachableBB;
4118+
4119+
auto *tagSwitch = IGF.Builder.CreateSwitch(tag, defaultDest, NumElements);
4120+
4121+
unsigned tagIndex = 0;
4122+
4123+
// Payload tags come first.
4124+
for (auto &elt : ElementsWithPayload) {
4125+
auto found = destMap.find(elt.decl);
4126+
if (found != destMap.end()) {
4127+
auto tagVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, tagIndex);
4128+
tagSwitch->addCase(tagVal, found->second);
4129+
}
4130+
++tagIndex;
4131+
}
4132+
4133+
// Next come empty tags.
4134+
for (auto &elt : ElementsWithNoPayload) {
4135+
auto found = destMap.find(elt.decl);
4136+
if (found != destMap.end()) {
4137+
auto tagVal = llvm::ConstantInt::get(IGF.IGM.Int32Ty, tagIndex);
4138+
tagSwitch->addCase(tagVal, found->second);
4139+
}
4140+
++tagIndex;
4141+
}
4142+
4143+
assert(tagIndex == NumElements);
4144+
4145+
// Delete the unreachable default block if we didn't use it, or emit it
4146+
// if we did.
4147+
if (unreachableBB->use_empty()) {
4148+
delete unreachableBB;
4149+
} else {
4150+
IGF.Builder.emitBlock(unreachableBB);
4151+
IGF.Builder.CreateUnreachable();
4152+
}
4153+
}
4154+
4155+
void assignWithCopy(IRGenFunction &IGF, Address dest, Address src,
4156+
SILType T)
4157+
const override {
4158+
emitAssignWithCopyCall(IGF, T,
4159+
dest.getAddress(), src.getAddress());
4160+
}
4161+
4162+
void assignWithTake(IRGenFunction &IGF, Address dest, Address src,
4163+
SILType T)
4164+
const override {
4165+
emitAssignWithTakeCall(IGF, T,
4166+
dest.getAddress(), src.getAddress());
4167+
}
4168+
4169+
void initializeWithCopy(IRGenFunction &IGF, Address dest, Address src,
4170+
SILType T)
4171+
const override {
4172+
emitInitializeWithCopyCall(IGF, T,
4173+
dest.getAddress(), src.getAddress());
4174+
}
4175+
4176+
void initializeWithTake(IRGenFunction &IGF, Address dest, Address src,
4177+
SILType T)
4178+
const override {
4179+
emitInitializeWithTakeCall(IGF, T,
4180+
dest.getAddress(), src.getAddress());
4181+
}
4182+
4183+
void destroy(IRGenFunction &IGF, Address addr, SILType T)
4184+
const override {
4185+
emitDestroyCall(IGF, T, addr.getAddress());
4186+
}
4187+
4188+
// \group Operations for loadable enums
4189+
4190+
ClusteredBitVector
4191+
getTagBitsForPayloads() const override {
4192+
llvm_unreachable("resilient enums are always indirect");
4193+
}
4194+
4195+
ClusteredBitVector
4196+
getBitPatternForNoPayloadElement(EnumElementDecl *theCase)
4197+
const override {
4198+
llvm_unreachable("resilient enums are always indirect");
4199+
}
4200+
4201+
ClusteredBitVector
4202+
getBitMaskForNoPayloadElements() const override {
4203+
llvm_unreachable("resilient enums are always indirect");
4204+
}
4205+
4206+
void initializeFromParams(IRGenFunction &IGF, Explosion &params,
4207+
Address dest, SILType T) const override {
4208+
llvm_unreachable("resilient enums are always indirect");
4209+
}
4210+
4211+
void emitValueInjection(IRGenFunction &IGF,
4212+
EnumElementDecl *elt,
4213+
Explosion &params,
4214+
Explosion &out) const override {
4215+
llvm_unreachable("resilient enums are always indirect");
4216+
}
4217+
4218+
llvm::Value *
4219+
emitValueCaseTest(IRGenFunction &IGF, Explosion &value,
4220+
EnumElementDecl *Case) const override {
4221+
llvm_unreachable("resilient enums are always indirect");
4222+
}
4223+
4224+
void emitValueSwitch(IRGenFunction &IGF,
4225+
Explosion &value,
4226+
ArrayRef<std::pair<EnumElementDecl*,
4227+
llvm::BasicBlock*>> dests,
4228+
llvm::BasicBlock *defaultDest) const override {
4229+
llvm_unreachable("resilient enums are always indirect");
4230+
}
4231+
4232+
void emitValueProject(IRGenFunction &IGF,
4233+
Explosion &inValue,
4234+
EnumElementDecl *theCase,
4235+
Explosion &out) const override {
4236+
llvm_unreachable("resilient enums are always indirect");
4237+
}
4238+
4239+
void getSchema(ExplosionSchema &schema) const override {
4240+
llvm_unreachable("resilient enums are always indirect");
4241+
}
4242+
4243+
unsigned getExplosionSize() const override {
4244+
llvm_unreachable("resilient enums are always indirect");
4245+
}
4246+
4247+
void loadAsCopy(IRGenFunction &IGF, Address addr,
4248+
Explosion &e) const override {
4249+
llvm_unreachable("resilient enums are always indirect");
4250+
}
4251+
4252+
void loadAsTake(IRGenFunction &IGF, Address addr,
4253+
Explosion &e) const override {
4254+
llvm_unreachable("resilient enums are always indirect");
4255+
}
4256+
4257+
void assign(IRGenFunction &IGF, Explosion &e,
4258+
Address addr) const override {
4259+
llvm_unreachable("resilient enums are always indirect");
4260+
}
4261+
4262+
void initialize(IRGenFunction &IGF, Explosion &e,
4263+
Address addr) const override {
4264+
llvm_unreachable("resilient enums are always indirect");
4265+
}
4266+
4267+
void reexplode(IRGenFunction &IGF, Explosion &src,
4268+
Explosion &dest) const override {
4269+
llvm_unreachable("resilient enums are always indirect");
4270+
}
4271+
4272+
void copy(IRGenFunction &IGF, Explosion &src, Explosion &dest)
4273+
const override {
4274+
llvm_unreachable("resilient enums are always indirect");
4275+
}
4276+
4277+
void consume(IRGenFunction &IGF, Explosion &src) const override {
4278+
llvm_unreachable("resilient enums are always indirect");
4279+
}
4280+
4281+
void fixLifetime(IRGenFunction &IGF, Explosion &src) const override {
4282+
llvm_unreachable("resilient enums are always indirect");
4283+
}
4284+
4285+
void packIntoEnumPayload(IRGenFunction &IGF,
4286+
EnumPayload &outerPayload,
4287+
Explosion &src,
4288+
unsigned offset) const override {
4289+
llvm_unreachable("resilient enums are always indirect");
4290+
}
4291+
4292+
void unpackFromEnumPayload(IRGenFunction &IGF,
4293+
const EnumPayload &outerPayload,
4294+
Explosion &dest,
4295+
unsigned offset) const override {
4296+
llvm_unreachable("resilient enums are always indirect");
4297+
}
4298+
4299+
/// \group Operations for emitting type metadata
4300+
4301+
llvm::Value *
4302+
emitGetEnumTag(IRGenFunction &IGF, SILType T, Address addr)
4303+
const override {
4304+
llvm_unreachable("resilient enums cannot be defined");
4305+
}
4306+
4307+
bool needsPayloadSizeInMetadata() const override {
4308+
llvm_unreachable("resilient enums cannot be defined");
4309+
}
4310+
4311+
void initializeMetadata(IRGenFunction &IGF,
4312+
llvm::Value *metadata,
4313+
llvm::Value *vwtable,
4314+
SILType T) const override {
4315+
llvm_unreachable("resilient enums cannot be defined");
4316+
}
4317+
4318+
/// \group Extra inhabitants
4319+
4320+
bool mayHaveExtraInhabitants(IRGenModule &) const override {
4321+
return true;
4322+
}
4323+
4324+
llvm::Value *getExtraInhabitantIndex(IRGenFunction &IGF,
4325+
Address src,
4326+
SILType T) const override {
4327+
return emitGetExtraInhabitantIndexCall(IGF, T, src.getAddress());
4328+
}
4329+
4330+
void storeExtraInhabitant(IRGenFunction &IGF,
4331+
llvm::Value *index,
4332+
Address dest,
4333+
SILType T) const override {
4334+
emitStoreExtraInhabitantCall(IGF, T, index, dest.getAddress());
4335+
}
4336+
4337+
APInt
4338+
getFixedExtraInhabitantMask(IRGenModule &IGM) const override {
4339+
llvm_unreachable("resilient enum is not fixed size");
4340+
}
4341+
4342+
unsigned getFixedExtraInhabitantCount(IRGenModule &IGM) const override {
4343+
llvm_unreachable("resilient enum is not fixed size");
4344+
}
4345+
4346+
APInt
4347+
getFixedExtraInhabitantValue(IRGenModule &IGM,
4348+
unsigned bits,
4349+
unsigned index) const override {
4350+
llvm_unreachable("resilient enum is not fixed size");
4351+
}
4352+
};
40504353
} // end anonymous namespace
40514354

40524355
EnumImplStrategy *EnumImplStrategy::get(TypeConverter &TC,
@@ -4122,6 +4425,18 @@ EnumImplStrategy *EnumImplStrategy::get(TypeConverter &TC,
41224425
+ elementsWithNoPayload.size()
41234426
&& "not all elements accounted for");
41244427

4428+
// Resilient enums are manipulated as opaque values, except we still
4429+
// make the following assumptions:
4430+
// 1) Physical case indices won't change
4431+
// 2) The indirect-ness of cases won't change
4432+
// 3) Payload types won't change in a non-resilient way
4433+
if (TC.IGM.isResilient(theEnum, ResilienceScope::Component)) {
4434+
return new ResilientEnumImplStrategy(TC.IGM,
4435+
numElements,
4436+
std::move(elementsWithPayload),
4437+
std::move(elementsWithNoPayload));
4438+
}
4439+
41254440
// Enums imported from Clang or marked with @objc use C-compatible layout.
41264441
if (theEnum->hasClangNode() || theEnum->isObjC()) {
41274442
assert(elementsWithPayload.size() == 0 && "C enum with payload?!");
@@ -4331,6 +4646,16 @@ namespace {
43314646
IsBitwiseTakable_t bt)
43324647
: EnumTypeInfoBase(strategy, irTy, align, pod, bt) {}
43334648
};
4649+
4650+
/// TypeInfo for dynamically-sized enum types.
4651+
class ResilientEnumTypeInfo
4652+
: public EnumTypeInfoBase<ResilientTypeInfo<ResilientEnumTypeInfo>>
4653+
{
4654+
public:
4655+
ResilientEnumTypeInfo(EnumImplStrategy &strategy,
4656+
llvm::Type *irTy)
4657+
: EnumTypeInfoBase(strategy, irTy) {}
4658+
};
43344659
} // end anonymous namespace
43354660

43364661
const EnumImplStrategy &
@@ -4789,12 +5114,26 @@ MultiPayloadEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC,
47895114
return completeDynamicLayout(TC, Type, theEnum, enumTy);
47905115
}
47915116

5117+
TypeInfo *
5118+
ResilientEnumImplStrategy::completeEnumTypeLayout(TypeConverter &TC,
5119+
SILType Type,
5120+
EnumDecl *theEnum,
5121+
llvm::StructType *enumTy) {
5122+
return registerEnumTypeInfo(new ResilientEnumTypeInfo(*this, enumTy));
5123+
}
5124+
47925125
const TypeInfo *TypeConverter::convertEnumType(TypeBase *key, CanType type,
47935126
EnumDecl *theEnum) {
4794-
llvm::StructType *convertedStruct = IGM.createNominalType(theEnum);
5127+
llvm::StructType *storageType;
5128+
5129+
// Resilient enum types lower down to the same opaque type.
5130+
if (IGM.isResilient(theEnum, ResilienceScope::Component))
5131+
storageType = cast<llvm::StructType>(IGM.OpaquePtrTy->getElementType());
5132+
else
5133+
storageType = IGM.createNominalType(theEnum);
47955134

47965135
// Create a forward declaration for that type.
4797-
addForwardDecl(key, convertedStruct);
5136+
addForwardDecl(key, storageType);
47985137

47995138
SILType loweredTy = SILType::getPrimitiveAddressType(type);
48005139

@@ -4803,7 +5142,7 @@ const TypeInfo *TypeConverter::convertEnumType(TypeBase *key, CanType type,
48035142

48045143
// Create the TI.
48055144
auto *ti = strategy->completeEnumTypeLayout(*this, loweredTy,
4806-
theEnum, convertedStruct);
5145+
theEnum, storageType);
48075146
// Assert that the layout query functions for fixed-layout enums work, for
48085147
// LLDB's sake.
48095148
#ifndef NDEBUG

0 commit comments

Comments
 (0)