Skip to content

Commit 4fe16f5

Browse files
committed
[IRGen] Augment opaque type descriptor to support limited availability underlying types
If particular opaque type descriptor has multiple conditionally available underlying types, emit a special type and witness reference accessors that would be called at runtime to determine actual underlying type.
1 parent 1c582bf commit 4fe16f5

File tree

2 files changed

+502
-27
lines changed

2 files changed

+502
-27
lines changed

lib/IRGen/GenMeta.cpp

Lines changed: 327 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,39 +2036,19 @@ namespace {
20362036
addGenericSignature();
20372037
addUnderlyingTypeAndConformances();
20382038
}
2039-
2040-
void addUnderlyingTypeAndConformances() {
2041-
auto sig = O->getOpaqueInterfaceGenericSignature();
2042-
auto contextSig = O->getGenericSignature().getCanonicalSignature();
2043-
auto subs = *O->getUniqueUnderlyingTypeSubstitutions();
20442039

2045-
// Add the underlying types for each generic parameter.
2046-
for (auto genericParam : O->getOpaqueGenericParams()) {
2047-
auto underlyingType = Type(genericParam).subst(subs)->getCanonicalType(sig);
2048-
B.addRelativeAddress(
2049-
IGM.getTypeRef(underlyingType, contextSig,
2050-
MangledTypeRefRole::Metadata).first);
2040+
void addUnderlyingTypeAndConformances() {
2041+
for (unsigned index : indices(O->getOpaqueGenericParams())) {
2042+
B.addRelativeAddress(getUnderlyingTypeRef(index));
20512043
}
20522044

2053-
// Add witness tables for each of the conformance requirements.
2045+
auto sig = O->getOpaqueInterfaceGenericSignature();
20542046
for (const auto &req : sig.getRequirements()) {
2055-
auto proto = requiresWitnessTable(req);
2056-
if (!proto)
2057-
continue;
2058-
2059-
auto underlyingDependentType = req.getFirstType()->getCanonicalType();
2060-
auto underlyingType = underlyingDependentType.subst(subs)
2061-
->getCanonicalType();
2062-
auto underlyingConformance =
2063-
subs.lookupConformance(underlyingDependentType, proto);
2064-
auto witnessTableRef = IGM.emitWitnessTableRefString(
2065-
underlyingType, underlyingConformance,
2066-
contextSig,
2067-
/*setLowBit*/ false);
2068-
B.addRelativeAddress(witnessTableRef);
2047+
if (auto *proto = requiresWitnessTable(req))
2048+
B.addRelativeAddress(getWitnessTableRef(req, proto));
20692049
}
20702050
}
2071-
2051+
20722052
bool isUniqueDescriptor() {
20732053
switch (LinkEntity::forOpaqueTypeDescriptor(O)
20742054
.getLinkage(NotForDefinition)) {
@@ -2144,6 +2124,326 @@ namespace {
21442124

21452125
return O->getOpaqueGenericParams().size() + numWitnessTables;
21462126
}
2127+
2128+
private:
2129+
llvm::Constant *getUnderlyingTypeRef(unsigned opaqueParamIdx) const {
2130+
2131+
// If this opaque declaration has a unique set of substitutions,
2132+
// we can simply emit a direct type reference.
2133+
if (auto unique = O->getUniqueUnderlyingTypeSubstitutions()) {
2134+
auto sig = O->getOpaqueInterfaceGenericSignature();
2135+
auto contextSig = O->getGenericSignature().getCanonicalSignature();
2136+
2137+
auto *genericParam = O->getOpaqueGenericParams()[opaqueParamIdx];
2138+
auto underlyingType =
2139+
Type(genericParam).subst(*unique)->getCanonicalType(sig);
2140+
return IGM
2141+
.getTypeRef(underlyingType, contextSig,
2142+
MangledTypeRefRole::Metadata)
2143+
.first;
2144+
}
2145+
2146+
// Otherwise, we have to go through a metadata accessor to
2147+
// fetch underlying type at runtime.
2148+
2149+
// There are one or more underlying types with limited
2150+
// availability and one universally available one. This
2151+
// requires us to build a metadata accessor.
2152+
auto substitutionSet = O->getConditionallyAvailableSubstitutions();
2153+
assert(!substitutionSet.empty());
2154+
2155+
UnderlyingTypeAccessor accessor(IGM, O, opaqueParamIdx);
2156+
return accessor.emit(substitutionSet);
2157+
}
2158+
2159+
llvm::Constant *getWitnessTableRef(const Requirement &req,
2160+
ProtocolDecl *protocol) {
2161+
auto contextSig = O->getGenericSignature().getCanonicalSignature();
2162+
auto underlyingDependentType = req.getFirstType()->getCanonicalType();
2163+
2164+
if (auto unique = O->getUniqueUnderlyingTypeSubstitutions()) {
2165+
auto underlyingType =
2166+
underlyingDependentType.subst(*unique)->getCanonicalType();
2167+
auto underlyingConformance =
2168+
unique->lookupConformance(underlyingDependentType, protocol);
2169+
2170+
return IGM.emitWitnessTableRefString(underlyingType,
2171+
underlyingConformance, contextSig,
2172+
/*setLowBit*/ false);
2173+
}
2174+
2175+
WitnessTableAccessor accessor(IGM, O, req, protocol);
2176+
return accessor.emit(O->getConditionallyAvailableSubstitutions());
2177+
}
2178+
2179+
class AbstractMetadataAccessor {
2180+
protected:
2181+
IRGenModule &IGM;
2182+
2183+
/// The opaque type declaration for this accessor.
2184+
OpaqueTypeDecl *O;
2185+
2186+
public:
2187+
AbstractMetadataAccessor(IRGenModule &IGM, OpaqueTypeDecl *O)
2188+
: IGM(IGM), O(O) {}
2189+
2190+
virtual ~AbstractMetadataAccessor() {}
2191+
2192+
/// The unique symbol this accessor would be reachable by at runtime.
2193+
virtual std::string getSymbol() const = 0;
2194+
2195+
/// The result type for this accessor. This type would have
2196+
/// to match a type produced by \c getResultValue.
2197+
virtual llvm::Type *getResultType() const = 0;
2198+
2199+
/// Produce a result value based on the given set of substitutions.
2200+
virtual llvm::Value *
2201+
getResultValue(IRGenFunction &IGF, GenericEnvironment *genericEnv,
2202+
SubstitutionMap substitutions) const = 0;
2203+
2204+
llvm::Constant *
2205+
emit(ArrayRef<OpaqueTypeDecl::ConditionallyAvailableSubstitutions *>
2206+
substitutionSet) {
2207+
auto getInt32Constant =
2208+
[&](Optional<unsigned> value) -> llvm::ConstantInt * {
2209+
return llvm::ConstantInt::get(IGM.Int32Ty, value.getValueOr(0));
2210+
};
2211+
2212+
auto symbol = getSymbol();
2213+
2214+
auto *accessor = getAccessorFn(symbol);
2215+
{
2216+
IRGenFunction IGF(IGM, accessor);
2217+
2218+
if (IGM.DebugInfo)
2219+
IGM.DebugInfo->emitArtificialFunction(IGF, accessor);
2220+
2221+
auto signature = O->getGenericSignature().getCanonicalSignature();
2222+
auto *genericEnv = signature.getGenericEnvironment();
2223+
2224+
// Prepare contextual replacements.
2225+
{
2226+
SmallVector<GenericRequirement, 4> requirements;
2227+
2228+
enumerateGenericSignatureRequirements(
2229+
signature,
2230+
[&](GenericRequirement req) { requirements.push_back(req); });
2231+
2232+
auto bindingsBufPtr = IGF.collectParameters().claimNext();
2233+
2234+
bindFromGenericRequirementsBuffer(
2235+
IGF, requirements,
2236+
Address(bindingsBufPtr, IGM.getPointerAlignment()),
2237+
MetadataState::Complete, [&](CanType t) {
2238+
return genericEnv ? genericEnv->mapTypeIntoContext(t)
2239+
->getCanonicalType()
2240+
: t;
2241+
});
2242+
}
2243+
2244+
SmallVector<llvm::BasicBlock *, 4> conditionalTypes;
2245+
2246+
// Pre-allocate a basic block per condition, so there it's
2247+
// possible to jump between conditions.
2248+
for (unsigned index : indices(substitutionSet)) {
2249+
conditionalTypes.push_back(
2250+
IGF.createBasicBlock((index < substitutionSet.size() - 1)
2251+
? "conditional-" + llvm::utostr(index)
2252+
: "universal"));
2253+
}
2254+
2255+
// Jump straight to the first conditional type block.
2256+
IGF.Builder.CreateBr(conditionalTypes.front());
2257+
2258+
// For each conditionally available substitution
2259+
// (the last one is universal):
2260+
// - check all of the conditions via `isOSVersionAtLeast`
2261+
// - if all checks are true - emit a return of a result value.
2262+
for (unsigned i = 0; i < substitutionSet.size() - 1; ++i) {
2263+
auto *underlyingTy = substitutionSet[i];
2264+
2265+
IGF.Builder.emitBlock(conditionalTypes[i]);
2266+
2267+
auto returnTypeBB =
2268+
IGF.createBasicBlock("result-" + llvm::utostr(i));
2269+
2270+
// Emit a #available condition check, if it's `false` -
2271+
// jump to the next conditionally available type.
2272+
auto conditions = underlyingTy->getAvailability();
2273+
2274+
SmallVector<llvm::BasicBlock *, 4> conditionBlocks;
2275+
for (unsigned condIndex : indices(conditions)) {
2276+
// cond-<type_idx>-<cond_index>
2277+
conditionBlocks.push_back(IGF.createBasicBlock(
2278+
"cond-" + llvm::utostr(i) + "-" + llvm::utostr(condIndex)));
2279+
}
2280+
2281+
// Jump to the first condition.
2282+
IGF.Builder.CreateBr(conditionBlocks.front());
2283+
2284+
for (unsigned condIndex : indices(conditions)) {
2285+
const auto &condition = conditions[condIndex];
2286+
2287+
assert(condition.hasLowerEndpoint());
2288+
2289+
auto version = condition.getLowerEndpoint();
2290+
auto *major = getInt32Constant(version.getMajor());
2291+
auto *minor = getInt32Constant(version.getMinor());
2292+
auto *patch = getInt32Constant(version.getSubminor());
2293+
2294+
IGF.Builder.emitBlock(conditionBlocks[condIndex]);
2295+
2296+
auto isAtLeast =
2297+
IGF.emitTargetOSVersionAtLeastCall(major, minor, patch);
2298+
2299+
auto success = IGF.Builder.CreateICmpNE(
2300+
isAtLeast, llvm::Constant::getNullValue(IGM.Int32Ty));
2301+
2302+
auto nextCondOrRet = condIndex == conditions.size() - 1
2303+
? returnTypeBB
2304+
: conditionBlocks[condIndex + 1];
2305+
2306+
IGF.Builder.CreateCondBr(success, nextCondOrRet,
2307+
conditionalTypes[i + 1]);
2308+
}
2309+
2310+
{
2311+
IGF.Builder.emitBlock(returnTypeBB);
2312+
IGF.Builder.CreateRet(getResultValue(
2313+
IGF, genericEnv, underlyingTy->getSubstitutions()));
2314+
}
2315+
}
2316+
2317+
IGF.Builder.emitBlock(conditionalTypes.back());
2318+
auto universal = substitutionSet.back();
2319+
2320+
assert(universal->getAvailability().size() == 1 &&
2321+
universal->getAvailability()[0].isEmpty());
2322+
2323+
IGF.Builder.CreateRet(
2324+
getResultValue(IGF, genericEnv, universal->getSubstitutions()));
2325+
}
2326+
2327+
return getAddrOfMetadataAccessor(symbol, accessor);
2328+
}
2329+
2330+
private:
2331+
llvm::Function *getAccessorFn(std::string symbol) const {
2332+
auto fnTy = llvm::FunctionType::get(getResultType(), {IGM.Int8PtrTy},
2333+
/*vararg*/ false);
2334+
2335+
auto *accessor = llvm::Function::Create(
2336+
fnTy, llvm::GlobalValue::PrivateLinkage, symbol, IGM.getModule());
2337+
2338+
accessor->setAttributes(IGM.constructInitialAttributes());
2339+
2340+
return accessor;
2341+
}
2342+
2343+
llvm::Constant *
2344+
getAddrOfMetadataAccessor(std::string symbol,
2345+
llvm::Function *accessor) const {
2346+
return IGM.getAddrOfStringForMetadataRef(
2347+
symbol, /*align*/ 2,
2348+
/*low bit*/ false, [&](ConstantInitBuilder &B) {
2349+
// Form the mangled name with its relative reference.
2350+
auto S = B.beginStruct();
2351+
2352+
S.setPacked(true);
2353+
S.add(llvm::ConstantInt::get(IGM.Int8Ty, 255));
2354+
S.add(llvm::ConstantInt::get(IGM.Int8Ty, 9));
2355+
S.addRelativeAddress(accessor);
2356+
2357+
// And a null terminator!
2358+
S.addInt(IGM.Int8Ty, 0);
2359+
2360+
return S.finishAndCreateFuture();
2361+
});
2362+
}
2363+
};
2364+
2365+
class UnderlyingTypeAccessor final : public AbstractMetadataAccessor {
2366+
/// The index of the generic parameter accessor is going
2367+
/// to retrieve the underlying type for.
2368+
unsigned OpaqueParamIndex;
2369+
2370+
public:
2371+
UnderlyingTypeAccessor(IRGenModule &IGM, OpaqueTypeDecl *O,
2372+
unsigned opaqueParamIndex)
2373+
: AbstractMetadataAccessor(IGM, O),
2374+
OpaqueParamIndex(opaqueParamIndex) {}
2375+
2376+
std::string getSymbol() const override {
2377+
IRGenMangler mangler;
2378+
return mangler.mangleSymbolNameForUnderlyingTypeAccessorString(
2379+
O, OpaqueParamIndex);
2380+
}
2381+
2382+
llvm::Type *getResultType() const override {
2383+
return IGM.TypeMetadataPtrTy;
2384+
}
2385+
2386+
llvm::Value *
2387+
getResultValue(IRGenFunction &IGF, GenericEnvironment *genericEnv,
2388+
SubstitutionMap substitutions) const override {
2389+
auto type =
2390+
Type(O->getOpaqueGenericParams()[OpaqueParamIndex])
2391+
.subst(substitutions)
2392+
->getCanonicalType(O->getOpaqueInterfaceGenericSignature());
2393+
2394+
type = genericEnv
2395+
? genericEnv->mapTypeIntoContext(type)->getCanonicalType()
2396+
: type;
2397+
2398+
return IGF.emitTypeMetadataRef(type);
2399+
}
2400+
};
2401+
2402+
class WitnessTableAccessor final : public AbstractMetadataAccessor {
2403+
/// The requirement itself.
2404+
const Requirement &R;
2405+
2406+
/// Protocol requirement.
2407+
ProtocolDecl *P;
2408+
2409+
public:
2410+
WitnessTableAccessor(IRGenModule &IGM, OpaqueTypeDecl *O,
2411+
const Requirement &requirement, ProtocolDecl *P)
2412+
: AbstractMetadataAccessor(IGM, O), R(requirement), P(P) {}
2413+
2414+
std::string getSymbol() const override {
2415+
IRGenMangler mangler;
2416+
return mangler.mangleSymbolNameForUnderlyingWitnessTableAccessorString(
2417+
O, R, P);
2418+
}
2419+
2420+
llvm::Type *getResultType() const override {
2421+
return IGM.WitnessTablePtrTy;
2422+
}
2423+
2424+
llvm::Value *
2425+
getResultValue(IRGenFunction &IGF, GenericEnvironment *genericEnv,
2426+
SubstitutionMap substitutions) const override {
2427+
auto underlyingDependentType = R.getFirstType()->getCanonicalType();
2428+
2429+
auto underlyingType =
2430+
underlyingDependentType.subst(substitutions)->getCanonicalType();
2431+
auto underlyingConformance =
2432+
substitutions.lookupConformance(underlyingDependentType, P);
2433+
2434+
if (underlyingType->hasTypeParameter()) {
2435+
auto sig = genericEnv->getGenericSignature();
2436+
underlyingConformance = underlyingConformance.subst(
2437+
underlyingType, QueryInterfaceTypeSubstitutions(genericEnv),
2438+
LookUpConformanceInSignature(sig.getPointer()));
2439+
2440+
underlyingType = genericEnv->mapTypeIntoContext(underlyingType)
2441+
->getCanonicalType();
2442+
}
2443+
2444+
return emitWitnessTableRef(IGF, underlyingType, underlyingConformance);
2445+
}
2446+
};
21472447
};
21482448
} // end anonymous namespace
21492449

0 commit comments

Comments
 (0)