[WIP] Emit nominal type access functions for imported types.

Emit nominal type access functions for imported types. These access
functions work with non-unique metadata references, so they perform
uniquing through the runtime on first access.

Fixes rdar://problem/36430234.
This commit is contained in:
Doug Gregor
2018-01-19 23:11:50 -08:00
parent eb4f9a3d4c
commit 817c0b368e
5 changed files with 136 additions and 37 deletions

View File

@@ -451,7 +451,7 @@ static bool hasRequiredTypeMetadataAccessFunction(NominalTypeDecl *typeDecl) {
case FormalLinkage::PublicNonUnique:
case FormalLinkage::HiddenNonUnique:
return false;
return true;
}
llvm_unreachable("bad formal linkage");
@@ -1340,7 +1340,10 @@ emitInPlaceTypeMetadataAccessFunctionBody(IRGenFunction &IGF,
CanNominalType type,
llvm::Constant *cacheVariable,
InPlaceMetadataInitializer &&initializer) {
llvm::Constant *metadata = IGF.IGM.getAddrOfTypeMetadata(type, false);
llvm::Constant *metadata =
IGF.IGM.requiresForeignTypeMetadata(type)
? IGF.IGM.getAddrOfForeignTypeMetadataCandidate(type)
: IGF.IGM.getAddrOfTypeMetadata(type, false);
// We might not have interesting initialization to do.
assert((cacheVariable == nullptr) ==
@@ -4607,8 +4610,9 @@ static llvm::Value *
emitInPlaceValueTypeMetadataInitialization(IRGenFunction &IGF,
CanNominalType type,
llvm::Value *metadata) {
// All the value types are basically similar.
assert(isa<StructType>(type) || isa<EnumType>(type));
// All the value types are basically similar, as are foreign types.
assert(isa<StructType>(type) || isa<EnumType>(type) ||
IGF.IGM.requiresForeignTypeMetadata(type));
// Set up the value witness table if it's dependent.
SILType loweredType = IGF.IGM.getLoweredType(AbstractionPattern(type), type);
@@ -5081,6 +5085,22 @@ namespace {
}
Size getOffsetOfAddressPoint() const { return AddressPoint; }
void createMetadataAccessFunction() {
auto type = cast<NominalType>(asImpl().getTargetType());
(void) getTypeMetadataAccessFunction(IGM, type, ForDefinition,
[&](IRGenFunction &IGF,
llvm::Constant *cacheVariable) {
return emitInPlaceTypeMetadataAccessFunctionBody(IGF, type,
cacheVariable,
[&](IRGenFunction &IGF, llvm::Value *candidate) {
auto metadata = uniqueForeignTypeMetadataRef(IGF, candidate);
return emitInPlaceValueTypeMetadataInitialization(IGF, type,
metadata);
});
});
}
};
class ForeignClassMetadataBuilder;
@@ -5140,8 +5160,17 @@ namespace {
}
void addNominalTypeDescriptor() {
auto descriptor = ClassNominalTypeDescriptorBuilder(this->IGM, Target).emit();
B.add(descriptor);
// FIXME: Go through getAddrOfNominalTypeDescriptor once it's no
// longer required to define the nominal type descriptor.
auto entity = LinkEntity::forNominalTypeDescriptor(Target);
auto descriptor =
IGM.getAddrOfLLVMVariableOrGOTEquivalent(
entity,
IGM.getPointerAlignment(),
IGM.ClassNominalTypeDescriptorTy);
assert(!descriptor.isIndirect() && "Should be defined here");
B.add(descriptor.getDirectValue());
}
void noteStartOfSuperClass() { }
@@ -5225,6 +5254,18 @@ namespace {
};
} // end anonymous namespace
bool IRGenModule::requiresForeignTypeMetadata(CanType type) {
if (NominalTypeDecl *nominal = type->getAnyNominal()) {
if (auto *clas = dyn_cast<ClassDecl>(nominal)) {
return clas->getForeignClassKind() == ClassDecl::ForeignKind::CFType;
}
return isa<ClangModuleUnit>(nominal->getModuleScopeContext());
}
return false;
}
llvm::Constant *
IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
// What we save in GlobalVars is actually the offsetted value.
@@ -5236,10 +5277,33 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
ConstantInitBuilder builder(*this);
auto init = builder.beginStruct();
init.setPacked(true);
// Local function to create the global variable for the foreign type
// metadata candidate.
Size addressPoint;
llvm::Constant *result = nullptr;
auto createCandidateVariable = [&] {
auto definition = init.finishAndCreateFuture();
// Create the global variable.
LinkInfo link = LinkInfo::get(*this, entity, ForDefinition);
auto var =
createVariable(*this, link, definition.getType(),
getPointerAlignment());
definition.installInGlobal(var);
// Apply the offset.
result = llvm::ConstantExpr::getBitCast(var, Int8PtrTy);
result = llvm::ConstantExpr::getInBoundsGetElementPtr(
Int8Ty, result, getSize(addressPoint));
result = llvm::ConstantExpr::getBitCast(result, TypeMetadataPtrTy);
// Only remember the offset.
GlobalVars[entity] = result;
};
// Compute the constant initializer and the offset of the type
// metadata candidate within it.
Size addressPoint;
if (auto classType = dyn_cast<ClassType>(type)) {
assert(!classType.getParent());
auto classDecl = classType->getDecl();
@@ -5248,6 +5312,11 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
ForeignClassMetadataBuilder builder(*this, classDecl, init);
builder.layout();
addressPoint = builder.getOffsetOfAddressPoint();
ClassNominalTypeDescriptorBuilder(*this, classDecl).emit();
createCandidateVariable();
maybeEmitNominalTypeMetadataAccessFunction(classDecl, builder);
} else if (auto structType = dyn_cast<StructType>(type)) {
auto structDecl = structType->getDecl();
assert(isa<ClangModuleUnit>(structDecl->getModuleScopeContext()));
@@ -5255,6 +5324,9 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
ForeignStructMetadataBuilder builder(*this, structDecl, init);
builder.layout();
addressPoint = builder.getOffsetOfAddressPoint();
createCandidateVariable();
maybeEmitNominalTypeMetadataAccessFunction(structDecl, builder);
} else if (auto enumType = dyn_cast<EnumType>(type)) {
auto enumDecl = enumType->getDecl();
assert(enumDecl->hasClangNode());
@@ -5262,31 +5334,15 @@ IRGenModule::getAddrOfForeignTypeMetadataCandidate(CanType type) {
ForeignEnumMetadataBuilder builder(*this, enumDecl, init);
builder.layout();
addressPoint = builder.getOffsetOfAddressPoint();
createCandidateVariable();
maybeEmitNominalTypeMetadataAccessFunction(enumDecl, builder);
} else {
llvm_unreachable("foreign metadata for unexpected type?!");
}
auto definition = init.finishAndCreateFuture();
// Create the global variable.
LinkInfo link = LinkInfo::get(*this, entity, ForDefinition);
auto var =
createVariable(*this, link, definition.getType(), getPointerAlignment());
definition.installInGlobal(var);
// Apply the offset.
llvm::Constant *result = var;
result = llvm::ConstantExpr::getBitCast(result, Int8PtrTy);
result = llvm::ConstantExpr::getInBoundsGetElementPtr(
Int8Ty, result, getSize(addressPoint));
result = llvm::ConstantExpr::getBitCast(result, TypeMetadataPtrTy);
// Only remember the offset.
GlobalVars[entity] = result;
if (NominalTypeDecl *Nominal = type->getAnyNominal()) {
if (NominalTypeDecl *Nominal = type->getAnyNominal())
addLazyConformances(Nominal);
}
return result;
}

View File

@@ -2104,17 +2104,10 @@ void IRGenModule::emitSILWitnessTable(SILWitnessTable *wt) {
// Trigger the lazy emission of the foreign type metadata.
CanType conformingType = Conf->getType()->getCanonicalType();
if (NominalTypeDecl *Nominal = conformingType->getAnyNominal()) {
if (auto *clas = dyn_cast<ClassDecl>(Nominal)) {
if (clas->isForeign())
getAddrOfForeignTypeMetadataCandidate(conformingType);
} else if (isa<ClangModuleUnit>(Nominal->getModuleScopeContext())) {
getAddrOfForeignTypeMetadataCandidate(conformingType);
}
}
if (requiresForeignTypeMetadata(conformingType))
(void)getAddrOfForeignTypeMetadataCandidate(conformingType);
}
/// True if a function's signature in LLVM carries polymorphic parameters.
/// Generic functions and protocol witnesses carry polymorphic parameters.
bool irgen::hasPolymorphicParameters(CanSILFunctionType ty) {

View File

@@ -305,6 +305,23 @@ IRGenModule::IRGenModule(IRGenerator &irgen,
NominalTypeDescriptorPtrTy
= NominalTypeDescriptorTy->getPointerTo(DefaultAS);
ClassNominalTypeDescriptorTy =
llvm::StructType::get(LLVMContext, {
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int32Ty,
Int16Ty,
Int16Ty,
Int32Ty,
}, /*packed=*/true);
MethodDescriptorStructTy
= createStructType(*this, "swift.method_descriptor", {
RelativeAddressTy,

View File

@@ -500,6 +500,7 @@ public:
llvm::PointerType *ProtocolConformanceDescriptorPtrTy;
llvm::StructType *NominalTypeDescriptorTy;
llvm::PointerType *NominalTypeDescriptorPtrTy;
llvm::StructType *ClassNominalTypeDescriptorTy;
llvm::StructType *MethodDescriptorStructTy; /// %swift.method_descriptor
llvm::StructType *TypeMetadataRecordTy;
llvm::PointerType *TypeMetadataRecordPtrTy;
@@ -1080,6 +1081,10 @@ public:
llvm::Constant *getAddrOfTypeMetadataLazyCacheVariable(CanType type,
ForDefinition_t forDefinition);
llvm::Constant *getAddrOfForeignTypeMetadataCandidate(CanType concreteType);
/// Determine whether the given type requires foreign type metadata.
bool requiresForeignTypeMetadata(CanType type);
llvm::Constant *getAddrOfClassMetadataBaseOffset(ClassDecl *D,
ForDefinition_t forDefinition);
llvm::Constant *getAddrOfNominalTypeDescriptor(NominalTypeDecl *D,

View File

@@ -4,12 +4,14 @@
import StdlibUnittest
import Foundation
import CoreFoundation
let DemangleToMetadataTests = TestSuite("DemangleToMetadataObjC")
@objc class C : NSObject { }
@objc enum E: Int { case a }
@objc protocol P1 { }
protocol P2 { }
DemangleToMetadataTests.test("@objc classes") {
expectEqual(type(of: C()), _typeByMangledName("4main1CC")!)
@@ -40,5 +42,31 @@ DemangleToMetadataTests.test("Classes that don't exist") {
expectNil(_typeByMangledName("4main4BoomC"))
}
// FIXME: Shouldn't need this?
extension CFArray: P2 { }
DemangleToMetadataTests.test("CoreFoundation classes") {
expectEqual(CFArray.self, _typeByMangledName("So10CFArrayRefa")!)
}
DemangleToMetadataTests.test("Imported error types") {
expectEqual(URLError.self, _typeByMangledName("10Foundation8URLErrorV")!)
expectEqual(URLError.Code.self,
_typeByMangledName("10Foundation8URLErrorV4CodeV")!)
}
DemangleToMetadataTests.test("Imported swift_wrapper types") {
expectEqual(URLFileResourceType.self,
_typeByMangledName("So21NSURLFileResourceTypea")!)
}
extension URLSessionTask.State: P2 { }
DemangleToMetadataTests.test("Imported enum types") {
// FIXME: Should work
// expectEqual(NSURLSessionTask.State.self,
// _typeByMangledName("So21NSURLSessionTaskStateV")!)
}
runAllTests()