Skip to content

[IRGen] Use generic value witnesses for prespecialized types. #36765

New issue

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

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

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ class ASTContext final {
/// Get the runtime availability of support for differentiation.
AvailabilityContext getDifferentiationAvailability();

/// Get the runtime availability of getters and setters of multi payload enum
/// tag single payloads.
AvailabilityContext getMultiPayloadEnumTagSinglePayload();

/// Get the runtime availability of features introduced in the Swift 5.2
/// compiler for the target platform.
AvailabilityContext getSwift52Availability();
Expand All @@ -749,6 +753,10 @@ class ASTContext final {
/// compiler for the target platform.
AvailabilityContext getSwift55Availability();

/// Get the runtime availability of features introduced in the Swift 5.6
/// compiler for the target platform.
AvailabilityContext getSwift56Availability();

/// Get the runtime availability of features that have been introduced in the
/// Swift compiler for future versions of the target platform.
AvailabilityContext getSwiftFutureAvailability();
Expand Down
18 changes: 18 additions & 0 deletions include/swift/Runtime/Enum.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ void swift_storeEnumTagMultiPayload(OpaqueValue *value,
const EnumMetadata *enumType,
unsigned whichCase);

/// The unspecialized getEnumTagSinglePayload value witness to be used by the
/// VWTs for for specialized generic enums that are multi-payload.
///
/// Runtime availability: Swift 5.6
SWIFT_RUNTIME_EXPORT
unsigned swift_getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value,
uint32_t numExtraCases,
const Metadata *enumType);

/// The unspecialized storeEnumTagSinglePayload value witness to be used by the
/// VWTs for for specialized generic enums that are multi-payload.
///
/// Runtime availability: Swift 5.6
SWIFT_RUNTIME_EXPORT
void swift_storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value,
uint32_t index,
uint32_t numExtraCases,
const Metadata *enumType);
}

#endif
23 changes: 23 additions & 0 deletions include/swift/Runtime/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,29 @@ FUNCTION(AutoDiffAllocateSubcontext,
ARGS(RefCountedPtrTy, SizeTy),
ATTRS(NoUnwind, ArgMemOnly))

// SWIFT_RUNTIME_EXPORT
// unsigned swift_getMultiPayloadEnumTagSinglePayload(const OpaqueValue *value,
// uint32_t numExtraCases,
// const Metadata *enumType)
FUNCTION(GetMultiPayloadEnumTagSinglePayload,
swift_getMultiPayloadEnumTagSinglePayload, C_CC,
MultiPayloadEnumTagSinglePayloadAvailability,
RETURNS(Int32Ty),
ARGS(OpaquePtrTy, Int32Ty, TypeMetadataPtrTy),
ATTRS(NoUnwind))

// SWIFT_RUNTIME_EXPORT
// void swift_storeMultiPayloadEnumTagSinglePayload(OpaqueValue *value,
// uint32_t index,
// uint32_t numExtraCases,
// const Metadata *enumType);
FUNCTION(StoreMultiPayloadEnumTagSinglePayload,
swift_storeMultiPayloadEnumTagSinglePayload, C_CC,
MultiPayloadEnumTagSinglePayloadAvailability,
RETURNS(VoidTy),
ARGS(OpaquePtrTy, Int32Ty, Int32Ty, TypeMetadataPtrTy),
ATTRS(NoUnwind))

#undef RETURNS
#undef ARGS
#undef ATTRS
Expand Down
7 changes: 7 additions & 0 deletions lib/AST/Availability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,10 @@ AvailabilityContext ASTContext::getDifferentiationAvailability() {
return getSwiftFutureAvailability();
}

AvailabilityContext ASTContext::getMultiPayloadEnumTagSinglePayload() {
return getSwift56Availability();
}

AvailabilityContext ASTContext::getSwift52Availability() {
auto target = LangOpts.Target;

Expand Down Expand Up @@ -416,6 +420,9 @@ AvailabilityContext ASTContext::getSwift55Availability() {
return getSwiftFutureAvailability();
}

AvailabilityContext ASTContext::getSwift56Availability() {
return getSwiftFutureAvailability();
}

AvailabilityContext ASTContext::getSwiftFutureAvailability() {
auto target = LangOpts.Target;
Expand Down
186 changes: 126 additions & 60 deletions lib/IRGen/GenValueWitness.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include "IRGenDebugInfo.h"
#include "IRGenFunction.h"
#include "IRGenModule.h"
#include "MetadataLayout.h"
#include "StructLayout.h"
#include "TypeInfo.h"

Expand Down Expand Up @@ -815,15 +816,72 @@ static llvm::Constant *getMemCpyFunction(IRGenModule &IGM,
});
}

namespace {
struct BoundGenericTypeCharacteristics {
SILType concreteType;
const TypeInfo *TI;
FixedPacking packing;
};

ValueWitnessFlags getValueWitnessFlags(const TypeInfo *TI, SILType concreteType,
FixedPacking packing) {
ValueWitnessFlags flags;

// If we locally know that the type has fixed layout, we can emit
// meaningful flags for it.
if (auto *fixedTI = dyn_cast<FixedTypeInfo>(TI)) {
assert(packing == FixedPacking::OffsetZero ||
packing == FixedPacking::Allocate);
bool isInline = packing == FixedPacking::OffsetZero;
bool isBitwiseTakable =
fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal);
assert(isBitwiseTakable || !isInline);
flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue())
.withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal))
.withInlineStorage(isInline)
.withBitwiseTakable(isBitwiseTakable);
} else {
flags = flags.withIncomplete(true);
}

if (concreteType.getEnumOrBoundGenericEnum())
flags = flags.withEnumWitnesses(true);

return flags;
}

unsigned getExtraInhabitantCount(const TypeInfo *TI, IRGenModule &IGM) {
unsigned value = 0;
if (auto *fixedTI = dyn_cast<FixedTypeInfo>(TI)) {
value = fixedTI->getFixedExtraInhabitantCount(IGM);
}
return value;
}

void addSize(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) {
if (auto staticSize = TI->getStaticSize(IGM))
return B.add(staticSize);
// Just fill in 0 here if the type can't be statically laid out.
return B.addSize(Size(0));
}

void addStride(ConstantStructBuilder &B, const TypeInfo *TI, IRGenModule &IGM) {
if (auto value = TI->getStaticStride(IGM))
return B.add(value);

// Just fill in null here if the type can't be statically laid out.
return B.addSize(Size(0));
}
} // end anonymous namespace

/// Find a witness to the fact that a type is a value type.
/// Always adds an i8*.
static void addValueWitness(IRGenModule &IGM,
ConstantStructBuilder &B,
ValueWitness index,
FixedPacking packing,
CanType abstractType,
SILType concreteType,
const TypeInfo &concreteTI) {
static void addValueWitness(IRGenModule &IGM, ConstantStructBuilder &B,
ValueWitness index, FixedPacking packing,
CanType abstractType, SILType concreteType,
const TypeInfo &concreteTI,
const Optional<BoundGenericTypeCharacteristics>
boundGenericCharacteristics = llvm::None) {
auto addFunction = [&](llvm::Constant *fn) {
fn = llvm::ConstantExpr::getBitCast(fn, IGM.Int8PtrTy);
B.addSignedPointer(fn, IGM.getOptions().PointerAuth.ValueWitnesses, index);
Expand Down Expand Up @@ -880,57 +938,51 @@ static void addValueWitness(IRGenModule &IGM,
goto standard;

case ValueWitness::Size: {
if (auto value = concreteTI.getStaticSize(IGM))
return B.add(value);

// Just fill in 0 here if the type can't be statically laid out.
return B.addSize(Size(0));
if (boundGenericCharacteristics)
return addSize(B, boundGenericCharacteristics->TI, IGM);
return addSize(B, &concreteTI, IGM);
}

case ValueWitness::Flags: {
ValueWitnessFlags flags;

// If we locally know that the type has fixed layout, we can emit
// meaningful flags for it.
if (auto *fixedTI = dyn_cast<FixedTypeInfo>(&concreteTI)) {
assert(packing == FixedPacking::OffsetZero ||
packing == FixedPacking::Allocate);
bool isInline = packing == FixedPacking::OffsetZero;
bool isBitwiseTakable =
fixedTI->isBitwiseTakable(ResilienceExpansion::Maximal);
assert(isBitwiseTakable || !isInline);
flags = flags.withAlignment(fixedTI->getFixedAlignment().getValue())
.withPOD(fixedTI->isPOD(ResilienceExpansion::Maximal))
.withInlineStorage(isInline)
.withBitwiseTakable(isBitwiseTakable);
} else {
flags = flags.withIncomplete(true);
}

if (concreteType.getEnumOrBoundGenericEnum())
flags = flags.withEnumWitnesses(true);

return B.addInt32(flags.getOpaqueValue());
if (boundGenericCharacteristics)
return B.addInt32(
getValueWitnessFlags(boundGenericCharacteristics->TI,
boundGenericCharacteristics->concreteType,
boundGenericCharacteristics->packing)
.getOpaqueValue());
return B.addInt32(getValueWitnessFlags(&concreteTI, concreteType, packing)
.getOpaqueValue());
}

case ValueWitness::ExtraInhabitantCount: {
unsigned value = 0;
if (auto *fixedTI = dyn_cast<FixedTypeInfo>(&concreteTI)) {
value = fixedTI->getFixedExtraInhabitantCount(IGM);
}
return B.addInt32(value);
if (boundGenericCharacteristics)
return B.addInt32(
getExtraInhabitantCount(boundGenericCharacteristics->TI, IGM));
return B.addInt32(getExtraInhabitantCount(&concreteTI, IGM));
}

case ValueWitness::Stride: {
if (auto value = concreteTI.getStaticStride(IGM))
return B.add(value);

// Just fill in null here if the type can't be statically laid out.
return B.addSize(Size(0));
if (boundGenericCharacteristics)
return addStride(B, boundGenericCharacteristics->TI, IGM);
return addStride(B, &concreteTI, IGM);
}

case ValueWitness::GetEnumTagSinglePayload:
case ValueWitness::GetEnumTagSinglePayload: {
if (boundGenericCharacteristics)
if (auto *enumDecl = boundGenericCharacteristics->concreteType
.getEnumOrBoundGenericEnum())
if (IGM.getMetadataLayout(enumDecl).hasPayloadSizeOffset())
return B.add(llvm::ConstantExpr::getBitCast(
IGM.getGetMultiPayloadEnumTagSinglePayloadFn(), IGM.Int8PtrTy));
goto standard;
}
case ValueWitness::StoreEnumTagSinglePayload: {
if (boundGenericCharacteristics)
if (auto *enumDecl = boundGenericCharacteristics->concreteType
.getEnumOrBoundGenericEnum())
if (IGM.getMetadataLayout(enumDecl).hasPayloadSizeOffset())
return B.add(llvm::ConstantExpr::getBitCast(
IGM.getStoreMultiPayloadEnumTagSinglePayloadFn(), IGM.Int8PtrTy));
goto standard;
}

Expand All @@ -949,7 +1001,7 @@ static void addValueWitness(IRGenModule &IGM,
buildValueWitnessFunction(IGM, fn, index, packing, abstractType,
concreteType, concreteTI);
addFunction(fn);
}
}

static bool shouldAddEnumWitnesses(CanType abstractType) {
// Needs to handle UnboundGenericType.
Expand All @@ -964,22 +1016,21 @@ static llvm::StructType *getValueWitnessTableType(IRGenModule &IGM,
}

/// Collect the value witnesses for a particular type.
static void addValueWitnesses(IRGenModule &IGM,
ConstantStructBuilder &B,
FixedPacking packing,
CanType abstractType,
SILType concreteType,
const TypeInfo &concreteTI) {
static void addValueWitnesses(IRGenModule &IGM, ConstantStructBuilder &B,
FixedPacking packing, CanType abstractType,
SILType concreteType, const TypeInfo &concreteTI,
const Optional<BoundGenericTypeCharacteristics>
boundGenericCharacteristics = llvm::None) {
for (unsigned i = 0; i != NumRequiredValueWitnesses; ++i) {
addValueWitness(IGM, B, ValueWitness(i), packing,
abstractType, concreteType, concreteTI);
addValueWitness(IGM, B, ValueWitness(i), packing, abstractType,
concreteType, concreteTI, boundGenericCharacteristics);
}
if (shouldAddEnumWitnesses(abstractType)) {
for (auto i = unsigned(ValueWitness::First_EnumValueWitness);
i <= unsigned(ValueWitness::Last_EnumValueWitness);
++i) {
addValueWitness(IGM, B, ValueWitness(i), packing,
abstractType, concreteType, concreteTI);
addValueWitness(IGM, B, ValueWitness(i), packing, abstractType,
concreteType, concreteTI, boundGenericCharacteristics);
}
}
}
Expand All @@ -995,6 +1046,19 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM,
ConstantStructBuilder &B,
CanType abstractType,
bool &canBeConstant) {
Optional<BoundGenericTypeCharacteristics> boundGenericCharacteristics;
if (auto boundGenericType = dyn_cast<BoundGenericType>(abstractType)) {
CanType concreteFormalType = getFormalTypeInContext(abstractType);

auto concreteLoweredType = IGM.getLoweredType(concreteFormalType);
const auto *boundConcreteTI = &IGM.getTypeInfo(concreteLoweredType);
auto packing = boundConcreteTI->getFixedPacking(IGM);
boundGenericCharacteristics = {concreteLoweredType, boundConcreteTI,
packing};

abstractType =
boundGenericType->getDecl()->getDeclaredType()->getCanonicalType();
}
CanType concreteFormalType = getFormalTypeInContext(abstractType);

auto concreteLoweredType = IGM.getLoweredType(concreteFormalType);
Expand All @@ -1003,10 +1067,12 @@ static void addValueWitnessesForAbstractType(IRGenModule &IGM,

// For now, assume that we never have any interest in dynamically
// changing the value witnesses for something that's fixed-layout.
canBeConstant = concreteTI.isFixedSize();
canBeConstant = boundGenericCharacteristics
? boundGenericCharacteristics->TI->isFixedSize()
: concreteTI.isFixedSize();

addValueWitnesses(IGM, B, packing, abstractType,
concreteLoweredType, concreteTI);
addValueWitnesses(IGM, B, packing, abstractType, concreteLoweredType,
concreteTI, boundGenericCharacteristics);
}

static constexpr uint64_t sizeAndAlignment(Size size, Alignment alignment) {
Expand Down
11 changes: 10 additions & 1 deletion lib/IRGen/IRGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,15 @@ namespace RuntimeConstants {
}
return RuntimeAvailability::AlwaysAvailable;
}

RuntimeAvailability
MultiPayloadEnumTagSinglePayloadAvailability(ASTContext &context) {
auto featureAvailability = context.getMultiPayloadEnumTagSinglePayload();
if (!isDeploymentAvailabilityContainedIn(context, featureAvailability)) {
return RuntimeAvailability::ConditionallyAvailable;
}
return RuntimeAvailability::AlwaysAvailable;
}
} // namespace RuntimeConstants

// We don't use enough attributes to justify generalizing the
Expand Down Expand Up @@ -1653,7 +1662,7 @@ bool IRGenModule::useDllStorage() { return ::useDllStorage(Triple); }

bool IRGenModule::shouldPrespecializeGenericMetadata() {
auto canPrespecializeTarget =
(Triple.isOSDarwin() || Triple.isTvOS() ||
(Triple.isOSDarwin() ||
(Triple.isOSLinux() && !(Triple.isARM() && Triple.isArch32Bit())));
if (canPrespecializeTarget && isStandardLibrary()) {
return true;
Expand Down
Loading