Skip to content

Commit ece63bb

Browse files
authored
Merge pull request #42104 from xedin/irgen-part-of-multi-opaque
[IRGen] Augment opaque type descriptor to support limited availability underlying types
2 parents 4494db9 + 4fe16f5 commit ece63bb

File tree

4 files changed

+544
-27
lines changed

4 files changed

+544
-27
lines changed

lib/IRGen/GenMeta.cpp

Lines changed: 327 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,39 +2044,19 @@ namespace {
20442044
addGenericSignature();
20452045
addUnderlyingTypeAndConformances();
20462046
}
2047-
2048-
void addUnderlyingTypeAndConformances() {
2049-
auto sig = O->getOpaqueInterfaceGenericSignature();
2050-
auto contextSig = O->getGenericSignature().getCanonicalSignature();
2051-
auto subs = *O->getUniqueUnderlyingTypeSubstitutions();
20522047

2053-
// Add the underlying types for each generic parameter.
2054-
for (auto genericParam : O->getOpaqueGenericParams()) {
2055-
auto underlyingType = Type(genericParam).subst(subs)->getCanonicalType(sig);
2056-
B.addRelativeAddress(
2057-
IGM.getTypeRef(underlyingType, contextSig,
2058-
MangledTypeRefRole::Metadata).first);
2048+
void addUnderlyingTypeAndConformances() {
2049+
for (unsigned index : indices(O->getOpaqueGenericParams())) {
2050+
B.addRelativeAddress(getUnderlyingTypeRef(index));
20592051
}
20602052

2061-
// Add witness tables for each of the conformance requirements.
2053+
auto sig = O->getOpaqueInterfaceGenericSignature();
20622054
for (const auto &req : sig.getRequirements()) {
2063-
auto proto = requiresWitnessTable(req);
2064-
if (!proto)
2065-
continue;
2066-
2067-
auto underlyingDependentType = req.getFirstType()->getCanonicalType();
2068-
auto underlyingType = underlyingDependentType.subst(subs)
2069-
->getCanonicalType();
2070-
auto underlyingConformance =
2071-
subs.lookupConformance(underlyingDependentType, proto);
2072-
auto witnessTableRef = IGM.emitWitnessTableRefString(
2073-
underlyingType, underlyingConformance,
2074-
contextSig,
2075-
/*setLowBit*/ false);
2076-
B.addRelativeAddress(witnessTableRef);
2055+
if (auto *proto = requiresWitnessTable(req))
2056+
B.addRelativeAddress(getWitnessTableRef(req, proto));
20772057
}
20782058
}
2079-
2059+
20802060
bool isUniqueDescriptor() {
20812061
switch (LinkEntity::forOpaqueTypeDescriptor(O)
20822062
.getLinkage(NotForDefinition)) {
@@ -2152,6 +2132,326 @@ namespace {
21522132

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

0 commit comments

Comments
 (0)