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>; 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: 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()) { + 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()) { + // 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: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -7123,6 +7123,9 @@ case ParsedAttr::AT_ObjCSubclassingRestricted: handleSimpleAttribute(S, D, AL); break; + case ParsedAttr::AT_ObjCClassStub: + handleSimpleAttribute(S, D, AL); + break; case ParsedAttr::AT_ObjCCompleteDefinition: handleSimpleAttribute(S, D, AL); break; 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 +