forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathLocalTypeData.h
255 lines (209 loc) · 7.66 KB
/
LocalTypeData.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
//===--- LocalTypeData.h - Dominance-scoped type data -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines types relating to the local caching of type data,
// such as type metadata, value witness tables, and protocol witness tables.
//
// Type data may be cached concretely, meaning that it was already fully
// computed, or abstractly, meaning that we remember how to recreate it
// but haven't actually done so yet.
//
// Type data may be cached at different points within a function.
// Some of these points may not dominate all possible use sites.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_IRGEN_LOCALTYPEDATA_H
#define SWIFT_IRGEN_LOCALTYPEDATA_H
#include "LocalTypeDataKind.h"
#include "DominancePoint.h"
#include "MetadataPath.h"
#include "swift/AST/Type.h"
#include "llvm/ADT/STLExtras.h"
#include <utility>
namespace swift {
class TypeBase;
namespace irgen {
class FulfillmentMap;
enum IsExact_t : bool;
/// A cache of local type data.
///
/// Basic design considerations:
///
/// - We want to be able to efficiently look up a key and find something.
/// Generally this will find something from the entry block. We shouldn't
/// have to scan all the dominating points first.
///
/// - We don't expect to have multiple definitions for a key very often.
/// Therefore, given a collision, it should be okay to scan a list and
/// ask whether each is acceptable.
class LocalTypeDataCache {
public:
using Key = LocalTypeDataKey;
static Key getKey(CanType type, LocalTypeDataKind index) {
return { type, index };
}
private:
struct CacheEntry {
enum class Kind {
Concrete, Abstract
};
DominancePoint DefinitionPoint;
private:
enum { KindMask = 0x1, ConditionalMask = 0x2 };
llvm::PointerIntPair<CacheEntry*, 2, unsigned> NextAndFlags;
public:
Kind getKind() const {
return Kind(NextAndFlags.getInt() & KindMask);
}
CacheEntry *getNext() const { return NextAndFlags.getPointer(); }
void setNext(CacheEntry *next) { NextAndFlags.setPointer(next); }
/// Return the abstract cost of evaluating this cache entry.
unsigned cost() const;
/// Destruct and deallocate this cache entry.
void erase() const;
bool isConditional() const {
return NextAndFlags.getInt() & ConditionalMask;
}
protected:
CacheEntry(Kind kind, DominancePoint point, bool isConditional)
: DefinitionPoint(point),
NextAndFlags(nullptr,
unsigned(kind) | (isConditional ? ConditionalMask : 0)) {
}
~CacheEntry() = default;
};
/// A concrete entry in the cache, which directly stores the desired value.
struct ConcreteCacheEntry : CacheEntry {
llvm::Value *Value;
ConcreteCacheEntry(DominancePoint point, bool isConditional,
llvm::Value *value)
: CacheEntry(Kind::Concrete, point, isConditional), Value(value) {}
unsigned cost() const { return 0; }
};
/// A source of concrete data from which abstract cache entries can be
/// derived.
class AbstractSource {
public:
enum class Kind {
TypeMetadata,
ProtocolWitnessTable,
};
private:
CanType Type;
void *Conformance;
llvm::Value *Value;
public:
explicit AbstractSource(CanType type, ProtocolConformanceRef conformance,
llvm::Value *value)
: Type(type), Conformance(conformance.getOpaqueValue()), Value(value) {
assert(Conformance != nullptr);
}
explicit AbstractSource(CanType type, llvm::Value *value)
: Type(type), Conformance(nullptr), Value(value) {}
Kind getKind() const {
return (Conformance ? Kind::ProtocolWitnessTable : Kind::TypeMetadata);
}
CanType getType() const {
return Type;
}
ProtocolConformanceRef getProtocolConformance() const {
assert(Conformance && "not a protocol conformance");
return ProtocolConformanceRef::getFromOpaqueValue(Conformance);
}
llvm::Value *getValue() const {
return Value;
}
};
/// An abstract entry in the cache, which requires some amount of
/// non-trivial evaluation to derive the desired value.
struct AbstractCacheEntry : CacheEntry {
unsigned SourceIndex;
MetadataPath Path;
AbstractCacheEntry(DominancePoint point, bool isConditional,
unsigned sourceIndex, MetadataPath &&path)
: CacheEntry(Kind::Abstract, point, isConditional),
SourceIndex(sourceIndex), Path(std::move(path)) {}
llvm::Value *follow(IRGenFunction &IGF, AbstractSource &source) const;
unsigned cost() const { return Path.cost(); }
};
/// The linked list of cache entries corresponding to a particular key.
struct CacheEntryChain {
CacheEntry *Root;
explicit CacheEntryChain(CacheEntry *root = nullptr) : Root(root) {}
CacheEntryChain(const CacheEntryChain &other) = delete;
CacheEntryChain &operator=(const CacheEntryChain &other) = delete;
CacheEntryChain(CacheEntryChain &&other) : Root(other.Root) {
other.Root = nullptr;
}
CacheEntryChain &operator=(CacheEntryChain &&other) {
Root = other.Root;
other.Root = nullptr;
return *this;
}
void push_front(CacheEntry *entry) {
entry->setNext(Root);
Root = entry;
}
void eraseEntry(CacheEntry *prev, CacheEntry *entry) {
if (prev) {
assert(prev->getNext() == entry);
prev->setNext(entry->getNext());
} else {
assert(Root == entry);
Root = entry->getNext();
}
entry->erase();
}
/// Delete the linked list.
~CacheEntryChain() {
auto next = Root;
while (next) {
auto cur = next;
next = cur->getNext();
cur->erase();
}
}
};
llvm::DenseMap<Key, CacheEntryChain> Map;
std::vector<AbstractSource> AbstractSources;
void addAbstractForFulfillments(IRGenFunction &IGF,
FulfillmentMap &&fulfillments,
llvm::function_ref<AbstractSource()> createSource);
public:
LocalTypeDataCache() = default;
LocalTypeDataCache(const LocalTypeDataCache &other) = delete;
LocalTypeDataCache &operator=(const LocalTypeDataCache &other) = delete;
/// Load the value from cache if possible. This may require emitting
/// code if the value is cached abstractly.
llvm::Value *tryGet(IRGenFunction &IGF, Key key, bool allowAbstract = true);
/// Load the value from cache, asserting its presence.
llvm::Value *get(IRGenFunction &IGF, Key key) {
auto result = tryGet(IGF, key);
assert(result && "get() on unmapped entry?");
return result;
}
/// Add a new concrete entry to the cache at the given definition point.
void addConcrete(DominancePoint point, bool isConditional,
Key key, llvm::Value *value) {
auto newEntry = new ConcreteCacheEntry(point, isConditional, value);
Map[key].push_front(newEntry);
}
/// Add entries based on what can be fulfilled from the given type metadata.
void addAbstractForTypeMetadata(IRGenFunction &IGF, CanType type,
IsExact_t isExact, llvm::Value *metadata);
void dump() const;
// Private details for ConditionalDominanceScope.
void eraseConditional(ArrayRef<LocalTypeDataKey> keys);
};
}
}
#endif