slavapestov created this revision. slavapestov added reviewers: rjmccall, dexonsmith, aaron.ballman. Herald added a project: clang. Herald added a subscriber: cfe-commits. dexonsmith edited reviewers, added: erik.pilkington, arphaman; removed: dexonsmith. dexonsmith added a comment.
+Erik and Alex, can you take a look at this? This CL lands the Clang-side support for a new Objective-C runtime feature that adds support for dynamically-allocated metadata. What follows is a basic description of this mechanism: In Swift, the class metadata contains an Objective-C class object as a prefix, followed by a vtable, generic parameters, and field offsets. If the inheritance hierarchy of a class crosses a resilience boundary, we cannot statically emit the entire metadata. Instead, it is lazily built at runtime when first needed. In order for such classes to be accessed from Clang, the Objective-C runtime will support a new mechanism whereby Swift can instead emit a "class stub" object. The class stub object is used by Clang in places where a class reference is normally emitted: - Classrefs emitted when messaging the class - Categories A class stub contains an "isa pointer" which is a scalar value 1 followed by a function pointer which realizes the real class metadata. A reference to a class stub from a classref also has its least significant pointer set to 1. The Objective-C runtime distinguishes a class stub from a real class using this fake "isa pointer". Instead of directly loading from a classref, Clang instead calls `objc_loadClassref()`; this function checks if the classref contains a class stub, and if so, it calls the function pointer and stores the result back into the classref to fast path future loads. Note that Clang already supports a objc_runtime_visible attribute. The objc_class_stub attribute can be thought of as a generalization of objc_runtime_visible. Whereas objc_runtime_visible replaces the classref load with a runtime lookup of a class by string, the class stub mechanism is more efficient because the result of the lookup is cached. Also, objc_runtime_visible does not support categories to be defined on the class, whereas objc_class_stub does. Repository: rC Clang https://reviews.llvm.org/D59628 Files: include/clang/Basic/Attr.td include/clang/Basic/AttrDocs.td lib/CodeGen/CGObjCMac.cpp lib/Sema/SemaDeclAttr.cpp test/CodeGenObjC/class-stubs.m
Index: test/CodeGenObjC/class-stubs.m =================================================================== --- /dev/null +++ test/CodeGenObjC/class-stubs.m @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | \ +// RUN: FileCheck %s + +__attribute__((objc_class_stub)) +@interface A ++ (void) classMethod; +@end + +int main() { + [A classMethod]; +} + +// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = private global i8* inttoptr (i64 add (i64 ptrtoint (%struct._class_t* @"OBJC_CLASS_$_A" to i64), i64 1) to i8*), section "__DATA,__objc_classrefs,regular,no_dead_strip", align 8 + + +// CHECK-LABEL: define i32 @main() +// CHECK-NEXT: entry: +// CHECK-NEXT: [[CLASS:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_REFERENCES_$_") +// CHECK-NEXT: [[SELECTOR:%.*]] = load i8*, i8** @OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[RECEIVER:%.*]] = bitcast %struct._class_t* [[CLASS]] to i8* +// CHECK-NEXT: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to void (i8*, i8*)*)(i8* [[RECEIVER]], i8* [[SELECTOR]]) +// CHECK-NEXT: ret i32 0 + Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7123,6 +7123,9 @@ case ParsedAttr::AT_ObjCSubclassingRestricted: handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, AL); break; + case ParsedAttr::AT_ObjCClassStub: + handleSimpleAttribute<ObjCClassStubAttr>(S, D, AL); + break; case ParsedAttr::AT_ObjCCompleteDefinition: handleSimpleAttribute<ObjCCompleteDefinitionAttr>(S, D, AL); break; Index: lib/CodeGen/CGObjCMac.cpp =================================================================== --- lib/CodeGen/CGObjCMac.cpp +++ lib/CodeGen/CGObjCMac.cpp @@ -726,6 +726,25 @@ "objc_begin_catch"); } + /// Class objc_loadClassref (void *) + /// + /// Loads from a classref. For Objective-C stub classes, this invokes the + /// initialization callback stored inside the stub. For all other classes + /// this simply dereferences the pointer. + llvm::Constant *getLoadClassrefFn() const { + // FIXME: Other attributes? + + // Add the non-lazy-bind attribute, since objc_loadClassref is likely to + // be called a lot. + llvm::Type *params[] = { Int8PtrPtrTy }; + return CGM.CreateRuntimeFunction( + llvm::FunctionType::get(ClassnfABIPtrTy, params, false), + "objc_loadClassref", + llvm::AttributeList::get(CGM.getLLVMContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::NonLazyBind)); + } + llvm::StructType *EHTypeTy; llvm::Type *EHTypePtrTy; @@ -7247,12 +7266,23 @@ llvm::Constant *ClassGV; if (ID) { ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition); + + if (ID->hasAttr<ObjCClassStubAttr>()) { + ClassGV = llvm::ConstantExpr::getPtrToInt(ClassGV, CGM.IntPtrTy); + + // Classrefs pointing at Objective-C stub classes have the least + // significant bit set to 1. + auto *Tag = llvm::ConstantInt::get(CGM.IntPtrTy, 1); + ClassGV = llvm::ConstantExpr::getAdd(ClassGV, Tag); + ClassGV = llvm::ConstantExpr::getIntToPtr(ClassGV, + ObjCTypes.Int8PtrTy); + } } else { ClassGV = GetClassGlobal((getClassSymbolPrefix() + II->getName()).str(), NotForDefinition); } - Entry = new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + Entry = new llvm::GlobalVariable(CGM.getModule(), ClassGV->getType(), false, llvm::GlobalValue::PrivateLinkage, ClassGV, "OBJC_CLASSLIST_REFERENCES_$_"); Entry->setAlignment(Align.getQuantity()); @@ -7260,6 +7290,14 @@ "regular,no_dead_strip")); CGM.addCompilerUsedGlobal(Entry); } + + if (ID->hasAttr<ObjCClassStubAttr>()) { + // Classrefs pointing at Objective-C stub classes must be loaded by calling + // a special runtime function. + return CGF.EmitNounwindRuntimeCall( + ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result"); + } + return CGF.Builder.CreateAlignedLoad(Entry, Align); } Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -1112,6 +1112,13 @@ }]; } +def ObjCClassStubDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +This attribute specifies that the Objective-C class to which it applies has dynamically-allocated metadata. Classes annotated with this attribute cannot be subclassed. + }]; +} + def ObjCBoxableDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -1795,6 +1795,12 @@ let Documentation = [ObjCRuntimeVisibleDocs]; } +def ObjCClassStub: Attr { + let Spellings = [Clang<"objc_class_stub">]; + let Subjects = SubjectList<[ObjCInterface], ErrorDiag>; + let Documentation = [ObjCClassStubDocs]; +} + def ObjCBoxable : Attr { let Spellings = [Clang<"objc_boxable">]; let Subjects = SubjectList<[Record], ErrorDiag>;
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits