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

Reply via email to