Skip to content

Commit 0691490

Browse files
committed
Track whether types are fixed-ABI and ABI-accessible at the SIL level.
Fixed-ABI means that we can do value operations on the type without any metadata: value-allocations, copies, and destroys. It's currently equivalent to being fixed-size, but (1) being fixed-size isn't useful by itself at the SIL level and (2) you can imagine resilience or generics micro-optimizations where there's like an attribute that tells us the size of a type without actually telling us how to copy it. All types are fixed-ABI except: - layout-unconstrained generic types, - resilient value types, and - value types which contain a subobject of such a type (except within indirect enum cases). ABI-accessible means that we can perform value operations at all. We might not be able to if the type is not fixed-ABI and it is private to a different file (in non-WMO builds) or internal to a different module, because in such cases we will not be able to access its metadata. In general, we can't use such types `T` directly, but we may be able to use types `C` that contain such types as subobjects. Furthermore, we may be reasonably exposed to SIL that performs operations that treat `C` as non-opaque, e.g. if `C` is frozen (as it will be by default for modules in Swift 5). We can still achieve correctness in these cases as long as we don't either: - inline code that contains value operations on `T` or - attempt to recursively expand a value operation on `T` into value operations on its subobjects. The SIL optimizer currently never tries to expand value operations on objects in memory. However, IRGen always recursively expands value operations on frozen types; that will be fixed in a follow-up patch. The SIL verification that I've added here is definitely incomplete.
1 parent ba1a52f commit 0691490

File tree

5 files changed

+378
-250
lines changed

5 files changed

+378
-250
lines changed

include/swift/SIL/SILModule.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,14 @@ class SILModule {
634634
PGOReader = std::move(IPR);
635635
}
636636

637+
/// Can value operations (copies and destroys) on the given lowered type
638+
/// be performed in this module?
639+
bool isTypeABIAccessible(SILType type);
640+
641+
/// Can type metadata for the given formal type be fetched in
642+
/// the given module?
643+
bool isTypeMetadataAccessible(CanType type);
644+
637645
/// \brief Run the SIL verifier to make sure that all Functions follow
638646
/// invariants.
639647
void verify() const;

include/swift/SIL/TypeLowering.h

Lines changed: 116 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -96,35 +96,120 @@ enum IsDependent_t : unsigned {
9696
IsNotDependent = false,
9797
IsDependent = true
9898
};
99-
99+
100+
/// Is a lowered SIL type trivial? That is, are copies ultimately just
101+
/// bit-copies, and it takes no work to destroy a value?
102+
enum IsTrivial_t : bool {
103+
IsNotTrivial = false,
104+
IsTrivial = true
105+
};
106+
107+
/// Is a lowered SIL type fixed-ABI? That is, can the current context
108+
/// assign it a fixed size and alignment and perform value operations on it
109+
/// (such as copies, destroys, constructions, and projections) without
110+
/// metadata?
111+
///
112+
/// Note that a fully concrete type can be non-fixed-ABI without being
113+
/// itself resilient if it contains a subobject which is not fixed-ABI.
114+
///
115+
/// Also note that we're only concerned with the external value ABI here:
116+
/// resilient class types are still fixed-ABI, indirect enum cases do not
117+
/// affect the fixed-ABI-ness of the enum, and so on.
118+
enum IsFixedABI_t : bool {
119+
IsNotFixedABI = false,
120+
IsFixedABI = true
121+
};
122+
123+
/// Is a lowered SIL type address-only? That is, is the current context
124+
/// required to keep the value in memory for some reason?
125+
///
126+
/// A type might be address-only because:
127+
///
128+
/// - it is not fixed-size (e.g. because it is a resilient type) and
129+
/// therefore cannot be loaded into a statically-boundable set of
130+
/// registers; or
131+
///
132+
/// - it is semantically bound to memory, either because its storage
133+
/// address is used by the language runtime to implement its semantics
134+
/// (as with a weak reference) or because its representation is somehow
135+
/// address-dependent (as with something like a relative pointer).
136+
///
137+
/// An address-only type can be fixed-layout and/or trivial.
138+
/// A non-fixed-layout type is always address-only.
139+
enum IsAddressOnly_t : bool {
140+
IsNotAddressOnly = false,
141+
IsAddressOnly = true
142+
};
143+
144+
/// Is this type somewhat like a reference-counted type?
145+
enum IsReferenceCounted_t : bool {
146+
IsNotReferenceCounted = false,
147+
IsReferenceCounted = true
148+
};
149+
100150
/// Extended type information used by SIL.
101151
class TypeLowering {
102152
public:
103-
enum IsTrivial_t : bool { IsNotTrivial, IsTrivial };
104-
enum IsAddressOnly_t : bool { IsNotAddressOnly, IsAddressOnly };
105-
enum IsReferenceCounted_t : bool {
106-
IsNotReferenceCounted,
107-
IsReferenceCounted
153+
class RecursiveProperties {
154+
// These are chosen so that bitwise-or merges the flags properly.
155+
enum : unsigned {
156+
NonTrivialFlag = 1 << 0,
157+
NonFixedABIFlag = 1 << 1,
158+
AddressOnlyFlag = 1 << 2,
159+
};
160+
161+
uint8_t Flags;
162+
public:
163+
/// Construct a default RecursiveProperties, which corresponds to
164+
/// a trivial, loadable, fixed-layout type.
165+
constexpr RecursiveProperties() : Flags(0) {}
166+
167+
constexpr RecursiveProperties(IsTrivial_t isTrivial,
168+
IsFixedABI_t isFixedABI,
169+
IsAddressOnly_t isAddressOnly)
170+
: Flags((isTrivial ? 0U : NonTrivialFlag) |
171+
(isAddressOnly ? AddressOnlyFlag : 0U) |
172+
(isFixedABI ? 0U : NonFixedABIFlag)) {}
173+
174+
static constexpr RecursiveProperties forReference() {
175+
return {IsNotTrivial, IsFixedABI, IsNotAddressOnly};
176+
}
177+
178+
static constexpr RecursiveProperties forOpaque() {
179+
return {IsNotTrivial, IsNotFixedABI, IsAddressOnly};
180+
}
181+
182+
void addSubobject(RecursiveProperties other) {
183+
Flags |= other.Flags;
184+
}
185+
186+
IsTrivial_t isTrivial() const {
187+
return IsTrivial_t((Flags & NonTrivialFlag) == 0);
188+
}
189+
IsFixedABI_t isFixedABI() const {
190+
return IsFixedABI_t((Flags & NonFixedABIFlag) == 0);
191+
}
192+
IsAddressOnly_t isAddressOnly() const {
193+
return IsAddressOnly_t((Flags & AddressOnlyFlag) != 0);
194+
}
195+
196+
void setNonTrivial() { Flags |= NonTrivialFlag; }
197+
void setNonFixedABI() { Flags |= NonFixedABIFlag; }
198+
void setAddressOnly() { Flags |= AddressOnlyFlag; }
108199
};
109200

110201
private:
111202
/// The SIL type of values with this Swift type.
112203
SILType LoweredType;
113204

114-
enum : unsigned {
115-
IsTrivialFlag = 0x1,
116-
IsAddressOnlyFlag = 0x2,
117-
IsReferenceCountedFlag = 0x4,
118-
};
119-
unsigned Flags;
205+
RecursiveProperties Properties;
206+
unsigned ReferenceCounted : 1;
120207

121208
protected:
122-
TypeLowering(SILType type, IsTrivial_t isTrivial,
123-
IsAddressOnly_t isAddressOnly,
209+
TypeLowering(SILType type, RecursiveProperties properties,
124210
IsReferenceCounted_t isRefCounted)
125-
: LoweredType(type), Flags((isTrivial ? IsTrivialFlag : 0U) |
126-
(isAddressOnly ? IsAddressOnlyFlag : 0U) |
127-
(isRefCounted ? IsReferenceCountedFlag : 0U)) {}
211+
: LoweredType(type), Properties(properties),
212+
ReferenceCounted(isRefCounted) {}
128213

129214
public:
130215
TypeLowering(const TypeLowering &) = delete;
@@ -143,30 +228,41 @@ class TypeLowering {
143228
/// This is independent of whether the SIL result is address type.
144229
bool isFormallyReturnedIndirectly() const { return isAddressOnly(); }
145230

231+
RecursiveProperties getRecursiveProperties() const {
232+
return Properties;
233+
}
234+
146235
/// isAddressOnly - Returns true if the type is an address-only type. A type
147236
/// is address-only if it is a resilient value type, or if it is a fragile
148237
/// value type with a resilient member. In either case, the full layout of
149238
/// values of the type is unavailable to the compiler.
150239
bool isAddressOnly() const {
151-
return Flags & IsAddressOnlyFlag;
240+
return Properties.isAddressOnly();
152241
}
153242
/// isLoadable - Returns true if the type is loadable, in other words, its
154243
/// full layout is available to the compiler. This is the inverse of
155244
/// isAddressOnly.
156245
bool isLoadable() const {
157246
return !isAddressOnly();
158247
}
248+
249+
/// isFixedABI - Returns true if the type has a known fixed layout.
250+
/// If this is true, value operations on the type can be performed even if
251+
/// the type is inaccessible.
252+
bool isFixedABI() const {
253+
return Properties.isFixedABI();
254+
}
159255

160256
/// Returns true if the type is trivial, meaning it is a loadable
161257
/// value type with no reference type members that require releasing.
162258
bool isTrivial() const {
163-
return Flags & IsTrivialFlag;
259+
return Properties.isTrivial();
164260
}
165261

166262
/// Returns true if the type is a scalar reference-counted reference, which
167263
/// can be retained and released.
168264
bool isReferenceCounted() const {
169-
return Flags & IsReferenceCountedFlag;
265+
return ReferenceCounted;
170266
}
171267

172268
/// getLoweredType - Get the type used to represent values of the Swift type

lib/SIL/SIL.cpp

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,101 @@ swift::getLinkageForProtocolConformance(const NormalProtocolConformance *C,
105105
return (definition ? SILLinkage::Public : SILLinkage::PublicExternal);
106106
}
107107
}
108+
109+
bool SILModule::isTypeMetadataAccessible(CanType type) {
110+
assert(type->isLegalFormalType());
111+
112+
return !type.findIf([&](CanType type) {
113+
// Note that this function returns true if the type is *illegal* to use.
114+
115+
// Ignore non-nominal types.
116+
auto decl = type.getNominalOrBoundGenericNominal();
117+
if (!decl)
118+
return false;
119+
120+
// Check whether the declaration is inaccessible from the current context.
121+
switch (getDeclLinkage(decl)) {
122+
123+
// Public declarations are accessible from everywhere.
124+
case FormalLinkage::PublicUnique:
125+
case FormalLinkage::PublicNonUnique:
126+
return false;
127+
128+
// Hidden declarations are inaccessible from different modules.
129+
case FormalLinkage::HiddenUnique:
130+
return (decl->getModuleContext() != getSwiftModule());
131+
132+
// Private declarations are inaccessible from different files unless
133+
// this is WMO and we're in the same module.
134+
case FormalLinkage::Private: {
135+
// The only time we don't have an associated DC is in the
136+
// integrated REPL, where we also don't have a concept of other
137+
// source files within the current module.
138+
if (!AssociatedDeclContext)
139+
return (decl->getModuleContext() != getSwiftModule());
140+
141+
// The associated DC should be either a SourceFile or, in WMO mode,
142+
// a ModuleDecl. In the WMO modes, IRGen will ensure that private
143+
// declarations are usable throughout the module. Therefore, in
144+
// either case we just need to make sure that the declaration comes
145+
// from within the associated DC.
146+
auto declDC = decl->getDeclContext();
147+
return !(declDC == AssociatedDeclContext ||
148+
declDC->isChildContextOf(AssociatedDeclContext));
149+
}
150+
}
151+
llvm_unreachable("bad linkage");
152+
});
153+
}
154+
155+
/// Answer whether IRGen's emitTypeMetadataForLayout can fetch metadata for
156+
/// a type, which is the necessary condition for being able to do value
157+
/// operations on the type using dynamic metadata.
158+
static bool isTypeMetadataForLayoutAccessible(SILModule &M, SILType type) {
159+
// Look through types that aren't necessarily legal formal types:
160+
161+
// - tuples
162+
if (auto tupleType = type.getAs<TupleType>()) {
163+
for (auto index : indices(tupleType.getElementTypes())) {
164+
if (!isTypeMetadataForLayoutAccessible(M, type.getTupleElementType(index)))
165+
return false;
166+
}
167+
return true;
168+
}
169+
170+
// - optionals
171+
if (auto objType = type.getOptionalObjectType()) {
172+
return isTypeMetadataForLayoutAccessible(M, objType);
173+
}
174+
175+
// - function types
176+
if (type.is<SILFunctionType>())
177+
return true;
178+
179+
// - metatypes
180+
if (type.is<AnyMetatypeType>())
181+
return true;
182+
183+
// Otherwise, check that we can fetch the type metadata.
184+
return M.isTypeMetadataAccessible(type.getSwiftRValueType());
185+
186+
}
187+
188+
/// Can we perform value operations on the given type? We have no way
189+
/// of doing value operations on resilient-layout types from other modules
190+
/// that are ABI-private to their defining module. But if the type is not
191+
/// ABI-private, we can always at least fetch its metadata and use the
192+
/// value witness table stored there.
193+
bool SILModule::isTypeABIAccessible(SILType type) {
194+
// Fixed-ABI types can have value operations done without metadata.
195+
if (Types.getTypeLowering(type).isFixedABI())
196+
return true;
197+
198+
assert(!type.is<ReferenceStorageType>() &&
199+
!type.is<SILFunctionType>() &&
200+
!type.is<AnyMetatypeType>() &&
201+
"unexpected SIL lowered-only type with non-fixed layout");
202+
203+
// Otherwise, we need to be able to fetch layout-metadata for the type.
204+
return isTypeMetadataForLayoutAccessible(*this, type);
205+
}

lib/SIL/SILVerifier.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1819,6 +1819,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
18191819
"Dest address should be lvalue");
18201820
require(SI->getDest()->getType() == SI->getSrc()->getType(),
18211821
"Store operand type and dest type mismatch");
1822+
require(F.getModule().isTypeABIAccessible(SI->getDest()->getType()),
1823+
"cannot directly copy type with inaccessible ABI");
18221824
}
18231825

18241826
void checkRetainValueInst(RetainValueInst *I) {
@@ -2241,6 +2243,8 @@ class SILVerifier : public SILVerifierBase<SILVerifier> {
22412243
void checkDestroyAddrInst(DestroyAddrInst *DI) {
22422244
require(DI->getOperand()->getType().isAddress(),
22432245
"Operand of destroy_addr must be address");
2246+
require(F.getModule().isTypeABIAccessible(DI->getOperand()->getType()),
2247+
"cannot directly destroy type with inaccessible ABI");
22442248
}
22452249

22462250
void checkBindMemoryInst(BindMemoryInst *BI) {

0 commit comments

Comments
 (0)