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 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
//===--- LocalTypeData.h - Dominance-scoped type data -----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://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 "MetadataRequest.h"
#include "swift/AST/Type.h"
#include "llvm/ADT/STLExtras.h"
#include <utility>
namespace swift {
class TypeBase;
namespace irgen {
class DynamicMetadataRequest;
class MetadataResponse;
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:
static LocalTypeDataKey getKey(CanType type, LocalTypeDataKind index) {
return LocalTypeDataKey{ 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.
OperationCost cost() const;
/// Return the abstract cost of evaluating this cache entry.
OperationCost costForRequest(LocalTypeDataKey key,
DynamicMetadataRequest request) const;
/// Return whether following the metadata path immediately produces
/// a value that's statically known to satisfy the given request, or
/// whether a separate dynamic check is required.
bool immediatelySatisfies(LocalTypeDataKey key,
DynamicMetadataRequest request) 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 {
MetadataResponse Value;
ConcreteCacheEntry(DominancePoint point, bool isConditional,
MetadataResponse value)
: CacheEntry(Kind::Concrete, point, isConditional), Value(value) {}
OperationCost cost() const { return OperationCost::Free; }
OperationCost costForRequest(LocalTypeDataKey key,
DynamicMetadataRequest request) const;
bool immediatelySatisfies(LocalTypeDataKey key,
DynamicMetadataRequest request) const;
static bool classof(const CacheEntry *entry) {
return entry->getKind() == Kind::Concrete;
}
};
/// 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;
MetadataResponse Value;
public:
explicit AbstractSource(CanType type, ProtocolConformanceRef conformance,
llvm::Value *value)
: Type(type), Conformance(conformance.getOpaqueValue()),
Value(MetadataResponse::forComplete(value)) {
assert(Conformance != nullptr);
}
explicit AbstractSource(CanType type, MetadataResponse 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);
}
MetadataResponse 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;
unsigned State;
MetadataPath Path;
AbstractCacheEntry(DominancePoint point, bool isConditional,
unsigned sourceIndex, MetadataPath &&path,
MetadataState state)
: CacheEntry(Kind::Abstract, point, isConditional),
SourceIndex(sourceIndex), State(unsigned(state)),
Path(std::move(path)) {}
MetadataResponse follow(IRGenFunction &IGF, AbstractSource &source,
DynamicMetadataRequest request) const;
/// Return the natural state of the metadata that would be produced
/// by just following the path. If the path ends in calling a type
/// metadata accessor, it's okay to use MetadataState::Complete here
/// because it's just as cheap to produce that as anything else.
/// But if the path ends in just loading type metadata from somewhere,
/// this should be the presumed state of that type metadata so that
/// the costing accurately includes the cost of dynamically checking
/// the state of that metadata.
MetadataState getState() const {
return MetadataState(State);
}
OperationCost cost() const { return Path.cost(); }
OperationCost costForRequest(LocalTypeDataKey key,
DynamicMetadataRequest request) const;
bool immediatelySatisfies(LocalTypeDataKey key,
DynamicMetadataRequest request) const;
static bool classof(const CacheEntry *entry) {
return entry->getKind() == Kind::Abstract;
}
};
/// 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<LocalTypeDataKey, 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.
MetadataResponse tryGet(IRGenFunction &IGF, LocalTypeDataKey key,
bool allowAbstract, DynamicMetadataRequest request);
/// Whether the cached state was advanced or otherwise why not.
enum class StateAdvancement {
/// No entry whose state could be advanced was found.
NoEntry,
/// An entry was found whose state was already advanced at least as far as
/// the indicated state.
AlreadyAtLeast,
/// The state was advanced.
Advanced,
};
/// Advances the state cached for \p key to \p state within the active
/// dominance scope.
StateAdvancement advanceStateInScope(IRGenFunction &IGF, LocalTypeDataKey key,
MetadataState state);
/// Add a new concrete entry to the cache at the given definition point.
void addConcrete(DominancePoint point, bool isConditional,
LocalTypeDataKey key, MetadataResponse value) {
key = key.getCachingKey();
assert((key.Kind.isAnyTypeMetadata() ||
value.isStaticallyKnownComplete()) &&
"only type metadata can be added in a non-complete state");
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, MetadataResponse value);
void dump() const;
// Private details for ConditionalDominanceScope.
void eraseConditional(ArrayRef<LocalTypeDataKey> keys);
};
}
}
#endif
|