Skip to content
17 changes: 17 additions & 0 deletions SwiftCompilerSources/Sources/AST/Conformance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,23 @@ public struct Conformance: CustomStringConvertible, Hashable, NoReflectionChildr
assert(isConcrete)
return bridged.getAssociatedConformance(assocType.bridged, proto.bridged).conformance
}

// This is a less precise definition of "resilient conformance" because it's
// only used for SIL optimizations -- where it's okay to think a conformance
// is resilient even if IRGen will decide it's not (see
// IRGenModule::isResilientConformance).
public func isResilientConformance(currentModule: ModuleDecl) -> Bool {
let conformanceModule = self.protocol.parentModule
let protocolModule = self.protocol.parentModule

// If the protocol and the conformance are both in the current module,
// they're not resilient.
if conformanceModule == currentModule && protocolModule == currentModule {
return false
}

return true
}
}

public struct ConformanceArray : RandomAccessCollection, CustomReflectable {
Expand Down
2 changes: 2 additions & 0 deletions SwiftCompilerSources/Sources/AST/Declarations.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ final public class ClassDecl: NominalTypeDecl {

final public class ProtocolDecl: NominalTypeDecl {
public var requiresClass: Bool { bridged.ProtocolDecl_requiresClass() }

public var isInvertible: Bool { bridged.ProtocolDecl_isInvertible() }
}

final public class BuiltinTupleDecl: NominalTypeDecl {}
Expand Down
21 changes: 21 additions & 0 deletions SwiftCompilerSources/Sources/Optimizer/Utilities/OptUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,27 @@ extension Instruction {
}
case let gvi as GlobalValueInst:
return context.canMakeStaticObjectReadOnly(objectType: gvi.type)

// Metatypes (and upcasts of them to existentials) can be used as static initializers for SE-0492, as long as they
// do not require an accessor to reference the metadata (i.e. are not generic, not resilient and not a class).
// See also irgen::isCanonicalCompleteTypeMetadataStaticallyAddressable.
case let mti as MetatypeInst:
let silType = mti.type.loweredInstanceTypeOfMetatype(in: parentFunction)
let instanceType = mti.type.canonicalType.instanceTypeOfMetatype
if !mti.type.isGenericAtAnyLevel,
!instanceType.isClass,
let nominal = instanceType.nominal,
!nominal.isGenericAtAnyLevel,
silType.isFixedABI(in: mti.parentFunction) {
return true
}
return false
case let iemi as InitExistentialMetatypeInst:
let isAnyConformanceResilient = iemi.conformances.contains {
!$0.protocol.isInvertible && $0.isResilientConformance(currentModule: context.currentModuleContext)
}
return !iemi.type.isGenericAtAnyLevel && !isAnyConformanceResilient

case is StructInst,
is TupleInst,
is EnumInst,
Expand Down
4 changes: 4 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Instruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -901,6 +901,10 @@ class OpenExistentialBoxValueInst : SingleValueInstruction, UnaryInstruction {}
final public
class InitExistentialMetatypeInst : SingleValueInstruction, UnaryInstruction {
public var metatype: Value { operand.value }

public var conformances: ConformanceArray {
ConformanceArray(bridged: bridged.InitExistentialMetatypeInst_getConformances())
}
}

final public
Expand Down
4 changes: 4 additions & 0 deletions SwiftCompilerSources/Sources/SIL/Type.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ public struct Type : TypeProperties, CustomStringConvertible, NoReflectionChildr
bridged.isAddressableForDeps(function.bridged)
}

public func isFixedABI(in function: Function) -> Bool {
bridged.isFixedABI(function.bridged)
}

/// If this is a raw layout type, returns the substituted like-type.
public var rawLayoutSubstitutedLikeType: AST.`Type`? {
.init(bridgedOrNil: bridged.getRawLayoutSubstitutedLikeType())
Expand Down
1 change: 1 addition & 0 deletions include/swift/AST/ASTBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ struct BridgedDeclObj {
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedASTType Class_getSuperclass() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedDeclObj Class_getDestructor() const;
BRIDGED_INLINE bool ProtocolDecl_requiresClass() const;
BRIDGED_INLINE bool ProtocolDecl_isInvertible() const;
BRIDGED_INLINE bool AbstractFunction_isOverridden() const;
BRIDGED_INLINE bool Destructor_isIsolated() const;
BRIDGED_INLINE bool EnumElementDecl_hasAssociatedValues() const;
Expand Down
4 changes: 4 additions & 0 deletions include/swift/AST/ASTBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,10 @@ bool BridgedDeclObj::ProtocolDecl_requiresClass() const {
return getAs<swift::ProtocolDecl>()->requiresClass();
}

bool BridgedDeclObj::ProtocolDecl_isInvertible() const {
return getAs<swift::ProtocolDecl>()->getInvertibleProtocolKind() != std::nullopt;
}

bool BridgedDeclObj::AbstractFunction_isOverridden() const {
return getAs<swift::AbstractFunctionDecl>()->isOverridden();
}
Expand Down
2 changes: 2 additions & 0 deletions include/swift/SIL/SILBridging.h
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ struct BridgedType {
BRIDGED_INLINE bool isExactSuperclassOf(BridgedType t) const;
BRIDGED_INLINE bool isMarkedAsImmortal() const;
BRIDGED_INLINE bool isAddressableForDeps(BridgedFunction f) const;
BRIDGED_INLINE bool isFixedABI(BridgedFunction f) const;
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedLikeType() const;
BRIDGED_INLINE SWIFT_IMPORT_UNSAFE BridgedASTType getRawLayoutSubstitutedCountType() const;
BRIDGED_INLINE bool mayHaveCustomDeinit(BridgedFunction f) const;
Expand Down Expand Up @@ -797,6 +798,7 @@ struct BridgedInstruction {
BRIDGED_INLINE bool IndexAddrInst_needsStackProtection() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialRefInst_getConformances() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedCanType InitExistentialRefInst_getFormalConcreteType() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedConformanceArray InitExistentialMetatypeInst_getConformances() const;
BRIDGED_INLINE bool OpenExistentialAddr_isImmutable() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar GlobalAccessInst_getGlobal() const;
SWIFT_IMPORT_UNSAFE BRIDGED_INLINE BridgedGlobalVar AllocGlobalInst_getGlobal() const;
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILBridgingImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,10 @@ bool BridgedType::isAddressableForDeps(BridgedFunction f) const {
return unbridged().isAddressableForDeps(*f.getFunction());
}

bool BridgedType::isFixedABI(BridgedFunction f) const {
return unbridged().isFixedABI(*f.getFunction());
}

BridgedASTType BridgedType::getRawLayoutSubstitutedLikeType() const {
return {unbridged().getRawLayoutSubstitutedLikeType().getPointer()};
}
Expand Down Expand Up @@ -1249,6 +1253,10 @@ BridgedCanType BridgedInstruction::InitExistentialRefInst_getFormalConcreteType(
return getAs<swift::InitExistentialRefInst>()->getFormalConcreteType();
}

BridgedConformanceArray BridgedInstruction::InitExistentialMetatypeInst_getConformances() const {
return {getAs<swift::InitExistentialMetatypeInst>()->getConformances()};
}

bool BridgedInstruction::OpenExistentialAddr_isImmutable() const {
switch (getAs<swift::OpenExistentialAddrInst>()->getAccessKind()) {
case swift::OpenedExistentialAccess::Immutable: return true;
Expand Down
9 changes: 9 additions & 0 deletions include/swift/SIL/SILBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2155,6 +2155,11 @@ class SILBuilder {
createInitExistentialMetatype(SILLocation Loc, SILValue metatype,
SILType existentialType,
ArrayRef<ProtocolConformanceRef> conformances) {
if (isInsertingIntoGlobal()) {
return insert(InitExistentialMetatypeInst::create(
getSILDebugLocation(Loc), existentialType, metatype, conformances,
getModule()));
}
return insert(InitExistentialMetatypeInst::create(
getSILDebugLocation(Loc), existentialType, metatype, conformances,
&getFunction()));
Expand Down Expand Up @@ -2289,6 +2294,10 @@ class SILBuilder {
}

MetatypeInst *createMetatype(SILLocation Loc, SILType Metatype) {
if (isInsertingIntoGlobal()) {
return insert(MetatypeInst::create(getSILDebugLocation(Loc), Metatype,
getModule()));
}
return insert(MetatypeInst::create(getSILDebugLocation(Loc), Metatype,
&getFunction()));
}
Expand Down
8 changes: 8 additions & 0 deletions include/swift/SIL/SILInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -7464,6 +7464,9 @@ class MetatypeInst final
ArrayRef<SILValue> TypeDependentOperands)
: NullaryInstructionWithTypeDependentOperandsBase(DebugLoc,
TypeDependentOperands, Metatype) {}

static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype,
SILModule &Mod);

static MetatypeInst *create(SILDebugLocation DebugLoc, SILType Metatype,
SILFunction *F);
Expand Down Expand Up @@ -8141,6 +8144,11 @@ class InitExistentialMetatypeInst final
ArrayRef<SILValue> TypeDependentOperands,
ArrayRef<ProtocolConformanceRef> conformances);

static InitExistentialMetatypeInst *
create(SILDebugLocation DebugLoc, SILType existentialMetatypeType,
SILValue metatype, ArrayRef<ProtocolConformanceRef> conformances,
SILModule &mod);

static InitExistentialMetatypeInst *
create(SILDebugLocation DebugLoc, SILType existentialMetatypeType,
SILValue metatype, ArrayRef<ProtocolConformanceRef> conformances,
Expand Down
24 changes: 24 additions & 0 deletions lib/IRGen/GenConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@
#include "Explosion.h"
#include "GenConstant.h"
#include "GenEnum.h"
#include "GenExistential.h"
#include "GenIntegerLiteral.h"
#include "GenStruct.h"
#include "GenTuple.h"
#include "MetadataRequest.h"
#include "TypeInfo.h"
#include "StructLayout.h"
#include "Callee.h"
Expand All @@ -31,9 +33,11 @@
#include "swift/IRGen/Linking.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Range.h"
#include "swift/SIL/SILInstruction.h"
#include "swift/SIL/SILModule.h"
#include "llvm/Analysis/ConstantFolding.h"
#include "llvm/Support/BLAKE3.h"
#include "llvm/Support/ErrorHandling.h"

using namespace swift;
using namespace irgen;
Expand Down Expand Up @@ -414,6 +418,26 @@ Explosion irgen::emitConstantValue(IRGenModule &IGM, SILValue operand,
assert(ti.isFixedSize(expansion));
Address addr = IGM.getAddrOfSILGlobalVariable(var, ti, NotForDefinition);
return addr.getAddress();
} else if (auto *mti = dyn_cast<MetatypeInst>(operand)) {
auto metaTy = mti->getType().castTo<MetatypeType>();
auto type = metaTy.getInstanceType();
ASSERT(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type));
return IGM.getAddrOfTypeMetadata(type);
} else if (auto *iemi = dyn_cast<InitExistentialMetatypeInst>(operand)) {
auto *mti =
dyn_cast<MetatypeInst>(iemi->getOperand().getDefiningInstruction());
ASSERT(mti != nullptr && "couldn't constant fold initializer expression");

auto metaTy = mti->getType().castTo<MetatypeType>();
auto type = metaTy.getInstanceType();
ASSERT(isCanonicalCompleteTypeMetadataStaticallyAddressable(IGM, type));
llvm::Constant *metadata = IGM.getAddrOfTypeMetadata(type);

Explosion result;
emitExistentialMetatypeContainer(IGM, result, iemi->getType(), metadata,
iemi->getOperand()->getType(),
iemi->getConformances());
return result;
} else if (auto *atp = dyn_cast<AddressToPointerInst>(operand)) {
auto *val = emitConstantValue(IGM, atp->getOperand()).claimNextConstant();
return val;
Expand Down
5 changes: 5 additions & 0 deletions lib/IRGen/GenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
#include "swift/AST/ModuleDependencies.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/Pattern.h"
#include "swift/AST/PrettyStackTrace.h"
#include "swift/AST/ProtocolConformance.h"
#include "swift/AST/TypeMemberVisitor.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/Mangler.h"
#include "swift/Basic/PrettyStackTrace.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Demangling/ManglingMacros.h"
#include "swift/IRGen/Linking.h"
Expand Down Expand Up @@ -2931,6 +2933,9 @@ Address IRGenModule::getAddrOfSILGlobalVariable(SILGlobalVariable *var,
llvm::Constant *IRGenModule::getGlobalInitValue(SILGlobalVariable *var,
llvm::Type *storageType,
Alignment alignment) {
PrettyStackTraceStringAction trace(
"constant-folding init value for SIL global", var->getName());

if (var->isInitializedObject()) {
StructLayout *layout = StaticObjectLayouts[var].layout.get();
ObjectInst *oi = cast<ObjectInst>(var->getStaticInitializerValue());
Expand Down
34 changes: 34 additions & 0 deletions lib/IRGen/GenExistential.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2033,6 +2033,40 @@ void irgen::emitExistentialMetatypeContainer(IRGenFunction &IGF,
});
}

/// Emit an existential metatype container from a metatype value as an
/// explosion of llvm::Constant values (suitable for static initialization).
void irgen::emitExistentialMetatypeContainer(
IRGenModule &IGM, Explosion &out, SILType outType, llvm::Constant *metatype,
SILType metatypeType, ArrayRef<ProtocolConformanceRef> conformances) {
assert(outType.is<ExistentialMetatypeType>());
auto &destTI = IGM.getTypeInfo(outType).as<ExistentialMetatypeTypeInfo>();
out.add(metatype);

auto srcType = metatypeType.castTo<MetatypeType>().getInstanceType();
auto destType = outType.castTo<ExistentialMetatypeType>().getInstanceType();
while (auto destMetatypeType = dyn_cast<ExistentialMetatypeType>(destType)) {
destType = destMetatypeType.getInstanceType();
srcType = cast<AnyMetatypeType>(srcType).getInstanceType();
}

// Emit the witness table pointers as constants.
for (auto protocol : destTI.getStoredProtocols()) {
// Find the corresponding conformance
ProtocolConformanceRef conformance;
for (auto conf : conformances) {
if (conf.getProtocol() == protocol) {
conformance = conf;
break;
}
}
assert(conformance.isConcrete() && "missing conformance");

// Emit witness table constant
auto table = IGM.getAddrOfWitnessTable(conformance.getConcrete());
out.add(table);
}
}

void irgen::emitMetatypeOfOpaqueExistential(IRGenFunction &IGF, Address buffer,
SILType type, Explosion &out) {
assert(type.isExistentialType());
Expand Down
25 changes: 15 additions & 10 deletions lib/IRGen/GenExistential.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace irgen {
class Address;
class Explosion;
class IRGenFunction;
class IRGenModule;

/// Emit the metadata and witness table initialization for an allocated
/// opaque existential container.
Expand All @@ -44,16 +45,20 @@ namespace irgen {
SILType loweredSrcType,
ArrayRef<ProtocolConformanceRef> conformances);

/// Emit an existential metatype container from a metatype value
/// as an explosion.
void emitExistentialMetatypeContainer(IRGenFunction &IGF,
Explosion &out,
SILType outType,
llvm::Value *metatype,
SILType metatypeType,
ArrayRef<ProtocolConformanceRef> conformances);


/// Emit an existential metatype container from a metatype value as an
/// explosion.
void emitExistentialMetatypeContainer(
IRGenFunction &IGF, Explosion &out, SILType outType,
llvm::Value *metatype, SILType metatypeType,
ArrayRef<ProtocolConformanceRef> conformances);

/// Emit an existential metatype container from a metatype value as an
/// explosion of llvm::Constant values (suitable for static initialization).
void emitExistentialMetatypeContainer(
IRGenModule &IGM, Explosion &out, SILType outType,
llvm::Constant *metatype, SILType metatypeType,
ArrayRef<ProtocolConformanceRef> conformances);

/// Emit a class existential container from a class instance value
/// as an explosion.
void emitClassExistentialContainer(IRGenFunction &IGF,
Expand Down
18 changes: 18 additions & 0 deletions lib/SIL/IR/SILInstructions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2327,6 +2327,17 @@ InitExistentialMetatypeInst::InitExistentialMetatypeInst(
getTrailingObjects<ProtocolConformanceRef>());
}

InitExistentialMetatypeInst *InitExistentialMetatypeInst::create(
SILDebugLocation Loc, SILType existentialMetatypeType, SILValue metatype,
ArrayRef<ProtocolConformanceRef> conformances, SILModule &M) {
unsigned size = totalSizeToAlloc<swift::Operand, ProtocolConformanceRef>(
1, conformances.size());
void *buffer = M.allocateInst(size, alignof(InitExistentialMetatypeInst));
return ::new (buffer) InitExistentialMetatypeInst(
Loc, existentialMetatypeType, metatype,
{}, conformances);
}

InitExistentialMetatypeInst *InitExistentialMetatypeInst::create(
SILDebugLocation Loc, SILType existentialMetatypeType, SILValue metatype,
ArrayRef<ProtocolConformanceRef> conformances, SILFunction *F) {
Expand Down Expand Up @@ -2737,6 +2748,13 @@ CheckedCastBranchInst *CheckedCastBranchInst::create(
Target2Count, forwardingOwnershipKind, preservesOwnership);
}

MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty,
SILModule &Mod) {
auto Size = totalSizeToAlloc<swift::Operand>(1);
auto Buffer = Mod.allocateInst(Size, alignof(MetatypeInst));
return ::new (Buffer) MetatypeInst(Loc, Ty, {});
}

MetatypeInst *MetatypeInst::create(SILDebugLocation Loc, SILType Ty,
SILFunction *F) {
SILModule &Mod = F->getModule();
Expand Down
Loading