Skip to content

Commit 638e4b0

Browse files
committed
IRGen/Runtime: Use relative addresses in nominal type descriptors.
Decrease the size of nominal type descriptors and make them true-const by relative-addressing the other metadata they need to reference, which should all be included in the same image as the descriptor itself. Relative-referencing string constants exposes a bug in the Apple linker, which crashes when resolving relative relocations to coalesceable symbols (rdar://problem/22674524); work around this for now by revoking the `unnamed_addr`-ness of string constants that we take relative references to. (I haven't tested whether GNU ld or gold also have this problem on Linux; it may be possible to conditionalize the workaround to only apply to Darwin targets for now.)
1 parent c62274c commit 638e4b0

File tree

16 files changed

+260
-105
lines changed

16 files changed

+260
-105
lines changed

include/swift/Basic/RelativePointer.h

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace swift {
2626
/// A relative reference to an object stored in memory. The reference may be
2727
/// direct or indirect, and uses the low bit of the (assumed at least
2828
/// 2-byte-aligned) pointer to differentiate.
29-
template<typename ValueTy>
29+
template<typename ValueTy, bool Nullable = false>
3030
class RelativeIndirectablePointer {
3131
private:
3232
/// The relative offset of the pointer's memory from the `this` pointer.
@@ -44,7 +44,12 @@ class RelativeIndirectablePointer {
4444
RelativeIndirectablePointer &operator=(const RelativeIndirectablePointer &)
4545
= delete;
4646

47+
public:
4748
const ValueTy *get() const & {
49+
// Check for null.
50+
if (Nullable && RelativeOffset == 0)
51+
return nullptr;
52+
4853
// The pointer is offset relative to `this`.
4954
auto base = reinterpret_cast<intptr_t>(this);
5055
intptr_t address = base + (RelativeOffset & ~1);
@@ -57,8 +62,7 @@ class RelativeIndirectablePointer {
5762
return reinterpret_cast<const ValueTy *>(address);
5863
}
5964
}
60-
61-
public:
65+
6266
operator const ValueTy* () const & {
6367
return get();
6468
}
@@ -75,7 +79,7 @@ class RelativeIndirectablePointer {
7579
/// A relative reference to a function, intended to reference private metadata
7680
/// functions for the current executable or dynamic library image from
7781
/// position-independent constant data.
78-
template<typename T>
82+
template<typename T, bool Nullable>
7983
class RelativeDirectPointerImpl {
8084
private:
8185
/// The relative offset of the function's entry point from *this.
@@ -96,7 +100,11 @@ class RelativeDirectPointerImpl {
96100
using PointerTy = T*;
97101

98102
PointerTy get() const & {
99-
// The function entry point is addressed relative to `this`.
103+
// Check for null.
104+
if (Nullable && RelativeOffset == 0)
105+
return nullptr;
106+
107+
// The value is addressed relative to `this`.
100108
auto base = reinterpret_cast<intptr_t>(this);
101109
intptr_t absolute = base + RelativeOffset;
102110
return reinterpret_cast<PointerTy>(absolute);
@@ -109,12 +117,14 @@ class RelativeDirectPointerImpl {
109117
};
110118

111119
/// A direct relative reference to an object.
112-
template<typename T>
120+
template<typename T, bool Nullable = true>
113121
class RelativeDirectPointer :
114-
private RelativeDirectPointerImpl<T>
122+
private RelativeDirectPointerImpl<T, Nullable>
115123
{
116-
using super = RelativeDirectPointerImpl<T>;
124+
using super = RelativeDirectPointerImpl<T, Nullable>;
117125
public:
126+
using super::get;
127+
118128
operator typename super::PointerTy() const & {
119129
return this->get();
120130
}
@@ -132,12 +142,14 @@ class RelativeDirectPointer :
132142

133143
/// A specialization of RelativeDirectPointer for function pointers,
134144
/// allowing for calls.
135-
template<typename RetTy, typename...ArgTy>
136-
class RelativeDirectPointer<RetTy (ArgTy...)> :
137-
private RelativeDirectPointerImpl<RetTy (ArgTy...)>
145+
template<typename RetTy, typename...ArgTy, bool Nullable>
146+
class RelativeDirectPointer<RetTy (ArgTy...), Nullable> :
147+
private RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable>
138148
{
139-
using super = RelativeDirectPointerImpl<RetTy (ArgTy...)>;
149+
using super = RelativeDirectPointerImpl<RetTy (ArgTy...), Nullable>;
140150
public:
151+
using super::get;
152+
141153
operator typename super::PointerTy() const & {
142154
return this->get();
143155
}
@@ -149,5 +161,44 @@ class RelativeDirectPointer<RetTy (ArgTy...)> :
149161
using super::isNull;
150162
};
151163

164+
/// A direct relative reference to an aligned object, with an additional
165+
/// tiny integer value crammed into its low bits.
166+
template<typename PointeeTy, typename IntTy>
167+
class RelativeDirectPointerIntPair {
168+
int32_t RelativeOffsetPlusInt;
169+
170+
/// RelativePointers should appear in statically-generated metadata. They
171+
/// shouldn't be constructed or copied.
172+
RelativeDirectPointerIntPair() = delete;
173+
RelativeDirectPointerIntPair(RelativeDirectPointerIntPair &&) = delete;
174+
RelativeDirectPointerIntPair(const RelativeDirectPointerIntPair &) = delete;
175+
RelativeDirectPointerIntPair &operator=(RelativeDirectPointerIntPair &&)
176+
= delete;
177+
RelativeDirectPointerIntPair &operator=(const RelativeDirectPointerIntPair&)
178+
= delete;
179+
180+
static int32_t getMask() {
181+
static_assert(alignof(PointeeTy) >= alignof(int32_t),
182+
"pointee alignment must be at least 32 bit");
183+
184+
return alignof(int32_t) - 1;
185+
}
186+
187+
public:
188+
using ValueTy = PointeeTy;
189+
using PointerTy = PointeeTy*;
190+
191+
PointerTy getPointer() const & {
192+
// The value is addressed relative to `this`.
193+
auto base = reinterpret_cast<intptr_t>(this);
194+
intptr_t absolute = base + (RelativeOffsetPlusInt & ~getMask());
195+
return reinterpret_cast<PointerTy>(absolute);
196+
}
197+
198+
IntTy getInt() const & {
199+
return IntTy(RelativeOffsetPlusInt & getMask());
200+
}
201+
};
202+
152203
}
153204

include/swift/Runtime/Metadata.h

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,10 +1213,8 @@ struct EnumTypeDescriptor;
12131213
/// Common information about all nominal types. For generic types, this
12141214
/// descriptor is shared for all instantiations of the generic type.
12151215
struct NominalTypeDescriptor {
1216-
/// The kind of nominal type descriptor.
1217-
NominalTypeKind Kind;
12181216
/// The mangled name of the nominal type, with no generic parameters.
1219-
const char *Name;
1217+
RelativeDirectPointer<char> Name;
12201218

12211219
/// The following fields are kind-dependent.
12221220
union {
@@ -1236,12 +1234,12 @@ struct NominalTypeDescriptor {
12361234

12371235
/// The field names. A doubly-null-terminated list of strings, whose
12381236
/// length and order is consistent with that of the field offset vector.
1239-
const char *FieldNames;
1237+
RelativeDirectPointer<char, /*nullable*/ true> FieldNames;
12401238

12411239
/// The field type vector accessor. Returns a pointer to an array of
12421240
/// type metadata references whose order is consistent with that of the
12431241
/// field offset vector.
1244-
const FieldType *(*GetFieldTypes)(const Metadata *Self);
1242+
RelativeDirectPointer<const FieldType * (const Metadata *)> GetFieldTypes;
12451243

12461244
/// True if metadata records for this type have a field offset vector for
12471245
/// its stored properties.
@@ -1260,12 +1258,12 @@ struct NominalTypeDescriptor {
12601258

12611259
/// The field names. A doubly-null-terminated list of strings, whose
12621260
/// length and order is consistent with that of the field offset vector.
1263-
const char *FieldNames;
1261+
RelativeDirectPointer<char, /*nullable*/ true> FieldNames;
12641262

12651263
/// The field type vector accessor. Returns a pointer to an array of
12661264
/// type metadata references whose order is consistent with that of the
12671265
/// field offset vector.
1268-
const FieldType *(*GetFieldTypes)(const Metadata *Self);
1266+
RelativeDirectPointer<const FieldType * (const Metadata *)> GetFieldTypes;
12691267

12701268
/// True if metadata records for this type have a field offset vector for
12711269
/// its stored properties.
@@ -1283,11 +1281,11 @@ struct NominalTypeDescriptor {
12831281
/// The names of the cases. A doubly-null-terminated list of strings,
12841282
/// whose length is NumNonEmptyCases + NumEmptyCases. Cases are named in
12851283
/// tag order, non-empty cases first, followed by empty cases.
1286-
const char *CaseNames;
1284+
RelativeDirectPointer<char, /*nullable*/ true> CaseNames;
12871285
/// The field type vector accessor. Returns a pointer to an array of
12881286
/// type metadata references whose order is consistent with that of the
12891287
/// CaseNames. Only types for payload cases are provided.
1290-
const FieldType *(*GetCaseTypes)(const Metadata *Self);
1288+
RelativeDirectPointer<const FieldType * (const Metadata *)> GetCaseTypes;
12911289

12921290
uint32_t getNumPayloadCases() const {
12931291
return NumPayloadCasesAndPayloadSizeOffset & 0x00FFFFFFU;
@@ -1308,10 +1306,20 @@ struct NominalTypeDescriptor {
13081306
} Enum;
13091307
};
13101308

1309+
RelativeDirectPointerIntPair<GenericMetadata, NominalTypeKind>
1310+
GenericMetadataPatternAndKind;
1311+
13111312
/// A pointer to the generic metadata pattern that is used to instantiate
1312-
/// instances of this type. Null if the type is not generic.
1313-
GenericMetadata *GenericMetadataPattern;
1314-
1313+
/// instances of this type. Zero if the type is not generic.
1314+
GenericMetadata *getGenericMetadataPattern() const {
1315+
return const_cast<GenericMetadata*>(
1316+
GenericMetadataPatternAndKind.getPointer());
1317+
}
1318+
1319+
NominalTypeKind getKind() const {
1320+
return GenericMetadataPatternAndKind.getInt();
1321+
}
1322+
13151323
/// The generic parameter descriptor header. This describes how to find and
13161324
/// parse the generic parameter vector in metadata records for this nominal
13171325
/// type.
@@ -1515,7 +1523,7 @@ struct ClassMetadata : public HeapMetadata {
15151523
/// Get a pointer to the field type vector, if present, or null.
15161524
const FieldType *getFieldTypes() const {
15171525
assert(isTypeMetadata());
1518-
auto *getter = Description->Class.GetFieldTypes;
1526+
auto *getter = Description->Class.GetFieldTypes.get();
15191527
if (!getter)
15201528
return nullptr;
15211529

@@ -1693,7 +1701,7 @@ struct StructMetadata : public Metadata {
16931701

16941702
/// Get a pointer to the field type vector, if present, or null.
16951703
const FieldType *getFieldTypes() const {
1696-
auto *getter = Description->Struct.GetFieldTypes;
1704+
auto *getter = Description->Struct.GetFieldTypes.get();
16971705
if (!getter)
16981706
return nullptr;
16991707

lib/IRGen/GenDecl.cpp

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2701,17 +2701,34 @@ Address IRGenFunction::createFixedSizeBufferAlloca(const llvm::Twine &name) {
27012701
///
27022702
/// \returns an i8* with a null terminator; note that embedded nulls
27032703
/// are okay
2704-
llvm::Constant *IRGenModule::getAddrOfGlobalString(StringRef data) {
2704+
///
2705+
/// FIXME: willBeRelativelyAddressed is only needed to work around an ld64 bug
2706+
/// resolving relative references to coalesceable symbols.
2707+
/// It should be removed when fixed. rdar://problem/22674524
2708+
llvm::Constant *IRGenModule::getAddrOfGlobalString(StringRef data,
2709+
bool willBeRelativelyAddressed) {
27052710
// Check whether this string already exists.
27062711
auto &entry = GlobalStrings[data];
2707-
if (entry) return entry;
2712+
if (entry.second) {
2713+
// FIXME: Clear unnamed_addr if the global will be relative referenced
2714+
// to work around an ld64 bug. rdar://problem/22674524
2715+
if (willBeRelativelyAddressed)
2716+
entry.first->setUnnamedAddr(false);
2717+
return entry.second;
2718+
}
27082719

27092720
// If not, create it. This implicitly adds a trailing null.
27102721
auto init = llvm::ConstantDataArray::getString(LLVMContext, data);
27112722
auto global = new llvm::GlobalVariable(Module, init->getType(), true,
27122723
llvm::GlobalValue::PrivateLinkage,
27132724
init);
2714-
global->setUnnamedAddr(true);
2725+
// FIXME: ld64 crashes resolving relative references to coalesceable symbols.
2726+
// rdar://problem/22674524
2727+
// If we intend to relatively address this string, don't mark it with
2728+
// unnamed_addr to prevent it from going into the cstrings section and getting
2729+
// coalesced.
2730+
if (!willBeRelativelyAddressed)
2731+
global->setUnnamedAddr(true);
27152732

27162733
// Drill down to make an i8*.
27172734
auto zero = llvm::ConstantInt::get(SizeTy, 0);
@@ -2720,7 +2737,7 @@ llvm::Constant *IRGenModule::getAddrOfGlobalString(StringRef data) {
27202737
global->getValueType(), global, indices);
27212738

27222739
// Cache and return.
2723-
entry = address;
2740+
entry = {global, address};
27242741
return address;
27252742
}
27262743

lib/IRGen/GenEnum.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ llvm::Constant *EnumImplStrategy::emitCaseNames() const {
158158
fieldNames.push_back('\0');
159159
}
160160
// The final null terminator is provided by getAddrOfGlobalString.
161-
return IGM.getAddrOfGlobalString(fieldNames);
161+
return IGM.getAddrOfGlobalString(fieldNames,
162+
/*willBeRelativelyAddressed*/ true);
162163
}
163164

164165
unsigned EnumImplStrategy::getPayloadSizeForMetadata() const {
@@ -1048,7 +1049,7 @@ namespace {
10481049
// C enums have arbitrary values and we don't preserve the mapping
10491050
// between the case and raw value at runtime, so don't emit any
10501051
// case names at all so that reflection can give up in this case.
1051-
return llvm::ConstantPointerNull::get(IGM.Int8PtrTy);
1052+
return nullptr;
10521053
}
10531054
};
10541055

0 commit comments

Comments
 (0)