string CustomDiag = customDiag;
}
-class LangOpt<string name, bit negated = 0> {
+class LangOpt<string name, code customCode = [{}]> {
string Name = name;
- bit Negated = negated;
+
+ // A custom predicate, written as an expression evaluated in a context with
+ // "LangOpts" bound.
+ code CustomCode = customCode;
}
def MicrosoftExt : LangOpt<"MicrosoftExt">;
def Borland : LangOpt<"Borland">;
def CUDA : LangOpt<"CUDA">;
-def COnly : LangOpt<"CPlusPlus", 1>;
+def COnly : LangOpt<"COnly", "!LangOpts.CPlusPlus">;
def CPlusPlus : LangOpt<"CPlusPlus">;
def OpenCL : LangOpt<"OpenCL">;
def RenderScript : LangOpt<"RenderScript">;
def ObjC : LangOpt<"ObjC">;
def BlocksSupported : LangOpt<"Blocks">;
def ObjCAutoRefCount : LangOpt<"ObjCAutoRefCount">;
+def ObjCNonFragileRuntime : LangOpt<"ObjCNonFragileRuntime",
+ "LangOpts.ObjCRuntime.allowsClassStubs()">;
// Language option for CMSE extensions
def Cmse : LangOpt<"Cmse">;
let Documentation = [ObjCRuntimeVisibleDocs];
}
+def ObjCClassStub : Attr {
+ let Spellings = [Clang<"objc_class_stub">];
+ let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
+ let Documentation = [ObjCClassStubDocs];
+ let LangOpts = [ObjCNonFragileRuntime];
+}
+
def ObjCBoxable : Attr {
let Spellings = [Clang<"objc_boxable">];
let Subjects = SubjectList<[Record], ErrorDiag>;
}];
}
+def ObjCClassStubDocs : Documentation {
+ let Category = DocCatType;
+ let Content = [{
+This attribute specifies that the Objective-C class to which it applies is
+instantiated at runtime.
+
+Unlike ``__attribute__((objc_runtime_visible))``, a class having this attribute
+still has a "class stub" that is visible to the linker. This allows categories
+to be defined. Static message sends with the class as a receiver use a special
+access pattern to ensure the class is lazily instantiated from the class stub.
+
+Classes annotated with this attribute cannot be subclassed and cannot have
+implementations defined for them. This attribute is intended for use in
+Swift-generated headers for classes defined in Swift.
+
+Adding or removing this attribute to a class is an ABI-breaking change.
+ }];
+}
+
def ObjCBoxableDocs : Documentation {
let Category = DocCatDecl;
let Content = [{
def err_restricted_superclass_mismatch : Error<
"cannot subclass a class that was declared with the "
"'objc_subclassing_restricted' attribute">;
+def err_class_stub_subclassing_mismatch : Error<
+ "'objc_class_stub' attribute cannot be specified on a class that does not "
+ "have the 'objc_subclassing_restricted' attribute">;
+def err_implementation_of_class_stub : Error<
+ "cannot declare implementation of a class declared with the "
+ "'objc_class_stub' attribute">;
def warn_objc_root_class_missing : Warning<
"class %0 defined without specifying a base class">,
InGroup<ObjCRootClass>;
}
}
+ /// Returns true if this Objective-C runtime supports Objective-C class
+ /// stubs.
+ bool allowsClassStubs() const {
+ switch (getKind()) {
+ case FragileMacOSX:
+ case GCC:
+ case GNUstep:
+ case ObjFW:
+ return false;
+ case MacOSX:
+ case iOS:
+ case WatchOS:
+ return true;
+ }
+ }
+
/// Try to parse an Objective-C runtime specification from the given
/// string.
///
"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::FunctionCallee getLoadClassrefFn() const {
+ // Add the non-lazy-bind attribute, since objc_loadClassref is likely to
+ // be called a lot.
+ //
+ // Also it is safe to make it readnone, since we never load or store the
+ // classref except by calling this function.
+ llvm::Type *params[] = { Int8PtrPtrTy };
+ llvm::FunctionCallee F = CGM.CreateRuntimeFunction(
+ llvm::FunctionType::get(ClassnfABIPtrTy, params, false),
+ "objc_loadClassref",
+ llvm::AttributeList::get(CGM.getLLVMContext(),
+ llvm::AttributeList::FunctionIndex,
+ {llvm::Attribute::NonLazyBind,
+ llvm::Attribute::ReadNone,
+ llvm::Attribute::NoUnwind}));
+ if (!CGM.getTriple().isOSBinFormatCOFF())
+ cast<llvm::Function>(F.getCallee())->setLinkage(
+ llvm::Function::ExternalWeakLinkage);
+
+ return F;
+ }
+
llvm::StructType *EHTypeTy;
llvm::Type *EHTypePtrTy;
/// DefinedCategories - List of defined categories.
SmallVector<llvm::GlobalValue*, 16> DefinedCategories;
+ /// DefinedStubCategories - List of defined categories on class stubs.
+ SmallVector<llvm::GlobalValue*, 16> DefinedStubCategories;
+
/// DefinedNonLazyCategories - List of defined "non-lazy" categories.
SmallVector<llvm::GlobalValue*, 16> DefinedNonLazyCategories;
bool isMetaclass,
ForDefinition_t isForDefinition);
+ llvm::Constant *GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID);
+
+ llvm::Value *EmitLoadOfClassRef(CodeGenFunction &CGF,
+ const ObjCInterfaceDecl *ID,
+ llvm::GlobalVariable *Entry);
+
/// EmitClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy,
/// for the given class reference.
llvm::Value *EmitClassRef(CodeGenFunction &CGF,
std::string str =
StringClass.empty() ? "OBJC_CLASS_$_NSConstantString"
: "OBJC_CLASS_$_" + StringClass;
- auto GV = GetClassGlobal(str, NotForDefinition);
+ llvm::Constant *GV = GetClassGlobal(str, NotForDefinition);
// Make sure the result is of the correct type.
auto V = llvm::ConstantExpr::getBitCast(GV, CGM.IntTy->getPointerTo());
AddModuleClassList(DefinedCategories, "OBJC_LABEL_CATEGORY_$",
GetSectionName("__objc_catlist",
"regular,no_dead_strip"));
+ AddModuleClassList(DefinedStubCategories, "OBJC_LABEL_STUB_CATEGORY_$",
+ GetSectionName("__objc_catlist2",
+ "regular,no_dead_strip"));
AddModuleClassList(DefinedNonLazyCategories, "OBJC_LABEL_NONLAZY_CATEGORY_$",
GetSectionName("__objc_nlcatlist",
"regular,no_dead_strip"));
llvm::GlobalVariable *GCATV =
finishAndCreateGlobal(values, ExtCatName.str(), CGM);
CGM.addCompilerUsedGlobal(GCATV);
- DefinedCategories.push_back(GCATV);
+ if (Interface->hasAttr<ObjCClassStubAttr>())
+ DefinedStubCategories.push_back(GCATV);
+ else
+ DefinedCategories.push_back(GCATV);
// Determine if this category is also "non-lazy".
if (ImplementationIsNonLazy(OCD))
return GV;
}
+llvm::Constant *
+CGObjCNonFragileABIMac::GetClassGlobalForClassRef(const ObjCInterfaceDecl *ID) {
+ llvm::Constant *ClassGV = GetClassGlobal(ID, /*metaclass*/ false,
+ NotForDefinition);
+
+ if (!ID->hasAttr<ObjCClassStubAttr>())
+ return ClassGV;
+
+ ClassGV = llvm::ConstantExpr::getPointerCast(ClassGV, ObjCTypes.Int8PtrTy);
+
+ // Stub classes are pointer-aligned. Classrefs pointing at stub classes
+ // must set the least significant bit set to 1.
+ auto *Idx = llvm::ConstantInt::get(CGM.Int32Ty, 1);
+ return llvm::ConstantExpr::getGetElementPtr(CGM.Int8Ty, ClassGV, Idx);
+}
+
+llvm::Value *
+CGObjCNonFragileABIMac::EmitLoadOfClassRef(CodeGenFunction &CGF,
+ const ObjCInterfaceDecl *ID,
+ llvm::GlobalVariable *Entry) {
+ if (ID && ID->hasAttr<ObjCClassStubAttr>()) {
+ // Classrefs pointing at Objective-C stub classes must be loaded by calling
+ // a special runtime function.
+ return CGF.EmitRuntimeCall(
+ ObjCTypes.getLoadClassrefFn(), Entry, "load_classref_result");
+ }
+
+ CharUnits Align = CGF.getPointerAlign();
+ return CGF.Builder.CreateAlignedLoad(Entry, Align);
+}
+
llvm::Value *
CGObjCNonFragileABIMac::EmitClassRefFromId(CodeGenFunction &CGF,
IdentifierInfo *II,
const ObjCInterfaceDecl *ID) {
- CharUnits Align = CGF.getPointerAlign();
llvm::GlobalVariable *&Entry = ClassReferences[II];
if (!Entry) {
llvm::Constant *ClassGV;
if (ID) {
- ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition);
+ ClassGV = GetClassGlobalForClassRef(ID);
} else {
ClassGV = GetClassGlobal((getClassSymbolPrefix() + II->getName()).str(),
NotForDefinition);
+ assert(ClassGV->getType() == ObjCTypes.ClassnfABIPtrTy &&
+ "classref was emitted with the wrong type?");
}
std::string SectionName =
GetSectionName("__objc_classrefs", "regular,no_dead_strip");
Entry = new llvm::GlobalVariable(
- CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false,
+ CGM.getModule(), ClassGV->getType(), false,
getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV,
"OBJC_CLASSLIST_REFERENCES_$_");
- Entry->setAlignment(Align.getQuantity());
- Entry->setSection(SectionName);
+ Entry->setAlignment(CGF.getPointerAlign().getQuantity());
+ if (!ID || !ID->hasAttr<ObjCClassStubAttr>())
+ Entry->setSection(SectionName);
+
CGM.addCompilerUsedGlobal(Entry);
}
- return CGF.Builder.CreateAlignedLoad(Entry, Align);
+
+ return EmitLoadOfClassRef(CGF, ID, Entry);
}
llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CodeGenFunction &CGF,
llvm::Value *
CGObjCNonFragileABIMac::EmitSuperClassRef(CodeGenFunction &CGF,
const ObjCInterfaceDecl *ID) {
- CharUnits Align = CGF.getPointerAlign();
llvm::GlobalVariable *&Entry = SuperClassReferences[ID->getIdentifier()];
if (!Entry) {
- auto ClassGV = GetClassGlobal(ID, /*metaclass*/ false, NotForDefinition);
+ llvm::Constant *ClassGV = GetClassGlobalForClassRef(ID);
std::string SectionName =
GetSectionName("__objc_superrefs", "regular,no_dead_strip");
Entry = new llvm::GlobalVariable(
- CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, false,
+ CGM.getModule(), ClassGV->getType(), false,
getLinkageTypeForObjCMetadata(CGM, SectionName), ClassGV,
"OBJC_CLASSLIST_SUP_REFS_$_");
- Entry->setAlignment(Align.getQuantity());
+ Entry->setAlignment(CGF.getPointerAlign().getQuantity());
Entry->setSection(SectionName);
CGM.addCompilerUsedGlobal(Entry);
}
- return CGF.Builder.CreateAlignedLoad(Entry, Align);
+
+ return EmitLoadOfClassRef(CGF, ID, Entry);
}
/// EmitMetaClassRef - Return a Value * of the address of _class_t
case ParsedAttr::AT_ObjCSubclassingRestricted:
handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, AL);
break;
+ case ParsedAttr::AT_ObjCClassStub:
+ handleSimpleAttribute<ObjCClassStubAttr>(S, D, AL);
+ break;
case ParsedAttr::AT_ObjCExplicitProtocolImpl:
handleObjCSuppresProtocolAttr(S, D, AL);
break;
}
}
+ if (IDecl->hasAttr<ObjCClassStubAttr>())
+ Diag(IC->getLocation(), diag::err_implementation_of_class_stub);
+
if (LangOpts.ObjCRuntime.isNonFragile()) {
while (IDecl->getSuperClass()) {
DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass());
Diag(Super->getLocation(), diag::note_class_declared);
}
}
+
+ if (IntfDecl->hasAttr<ObjCClassStubAttr>() &&
+ !IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>())
+ Diag(IntfDecl->getLocation(), diag::err_class_stub_subclassing_mismatch);
}
DiagnoseVariableSizedIvars(*this, OCD);
if (isInterfaceDeclKind) {
--- /dev/null
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -Wno-objc-root-class -emit-llvm -o - %s | FileCheck %s
+
+// -- classref for the message send in main()
+//
+// The class is declared with objc_class_stub, so LSB of the class pointer
+// must be set to 1.
+//
+// CHECK-LABEL: @"OBJC_CLASSLIST_REFERENCES_$_" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Base" to i8*), i32 1), align 8
+
+// -- classref for the super message send in anotherClassMethod()
+//
+// Metaclasses do not use the "stub" mechanism and are referenced statically.
+//
+// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_" = internal global %struct._class_t* @"OBJC_METACLASS_$_Derived", section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8
+
+// -- classref for the super message send in anotherInstanceMethod()
+//
+// The class is declared with objc_class_stub, so LSB of the class pointer
+// must be set to 1.
+//
+// CHECK-LABEL: @"OBJC_CLASSLIST_SUP_REFS_$_.1" = internal global i8* getelementptr (i8, i8* bitcast (%struct._class_t* @"OBJC_CLASS_$_Derived" to i8*), i32 1), section "__DATA,__objc_superrefs,regular,no_dead_strip", align 8
+
+// -- category list for class stubs goes in __objc_catlist2.
+//
+// CHECK-LABEL: @"OBJC_LABEL_STUB_CATEGORY_$" = internal global [1 x i8*] [i8* bitcast (%struct._category_t* @"_OBJC_$_CATEGORY_Derived_$_MyCategory" to i8*)], section "__DATA,__objc_catlist2,regular,no_dead_strip", align 8
+
+__attribute__((objc_class_stub))
+__attribute__((objc_subclassing_restricted))
+@interface Base
++ (void) classMethod;
+- (void) instanceMethod;
+@end
+
+__attribute__((objc_class_stub))
+__attribute__((objc_subclassing_restricted))
+@interface Derived : Base
+@end
+
+int main() {
+ [Base classMethod];
+}
+// 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
+
+// CHECK-LABEL: declare extern_weak %struct._class_t* @objc_loadClassref(i8**)
+// CHECK-SAME: [[ATTRLIST:#.*]]
+
+@implementation Derived (MyCategory)
+
++ (void) anotherClassMethod {
+ [super classMethod];
+}
+// CHECK-LABEL: define internal void @"\01+[Derived(MyCategory) anotherClassMethod]"(i8* %self, i8* %_cmd) #0 {
+// CHECK-NEXT: entry:
+// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8
+// CHECK: [[METACLASS_REF:%.*]] = load %struct._class_t*, %struct._class_t** @"OBJC_CLASSLIST_SUP_REFS_$_", align 8
+// CHECK: [[CAST_METACLASS_REF:%.*]] = bitcast %struct._class_t* [[METACLASS_REF]] to i8*
+// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1
+// CHECK: store i8* [[CAST_METACLASS_REF]], i8** [[DEST]], align 8
+// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}})
+// CHECK: ret void
+
+- (void) anotherInstanceMethod {
+ [super instanceMethod];
+}
+// CHECK-LABEL: define internal void @"\01-[Derived(MyCategory) anotherInstanceMethod]"(%0* %self, i8* %_cmd) #0 {
+// CHECK-NEXT: entry:
+// CHECK: [[SUPER:%.*]] = alloca %struct._objc_super, align 8
+// CHECK: [[CLASS_REF:%.*]] = call %struct._class_t* @objc_loadClassref(i8** @"OBJC_CLASSLIST_SUP_REFS_$_.1")
+// CHECK: [[CAST_CLASS_REF:%.*]] = bitcast %struct._class_t* [[CLASS_REF]] to i8*
+// CHECK: [[DEST:%.*]] = getelementptr inbounds %struct._objc_super, %struct._objc_super* [[SUPER]], i32 0, i32 1
+// CHECK: store i8* [[CAST_CLASS_REF]], i8** [[DEST]], align 8
+// CHECK: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper2 to void (%struct._objc_super*, i8*)*)(%struct._objc_super* [[SUPER]], i8* {{%.*}})
+// CHECK: ret void
+
+@end
+
+// -- calls to objc_loadClassRef() are readnone
+// CHECK: attributes [[ATTRLIST]] = { nounwind nonlazybind readnone }
// CHECK-NEXT: ObjCBridge (SubjectMatchRule_record, SubjectMatchRule_type_alias)
// CHECK-NEXT: ObjCBridgeMutable (SubjectMatchRule_record)
// CHECK-NEXT: ObjCBridgeRelated (SubjectMatchRule_record)
+// CHECK-NEXT: ObjCClassStub (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCDesignatedInitializer (SubjectMatchRule_objc_method)
// CHECK-NEXT: ObjCException (SubjectMatchRule_objc_interface)
// CHECK-NEXT: ObjCExplicitProtocolImpl (SubjectMatchRule_objc_protocol)
--- /dev/null
+// RUN: %clang -target i386-apple-darwin -fsyntax-only -Xclang -verify %s
+// RUN: %clang -target i386-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s
+
+@interface NSObject
+@end
+
+__attribute__((objc_class_stub)) // expected-warning {{'objc_class_stub' attribute ignored}}
+__attribute__((objc_subclassing_restricted))
+@interface StubClass : NSObject
+@end
--- /dev/null
+// RUN: %clang -target x86_64-apple-darwin -fsyntax-only -Xclang -verify %s
+// RUN: %clang -target x86_64-apple-darwin -x objective-c++ -fsyntax-only -Xclang -verify %s
+
+@interface NSObject
+@end
+
+__attribute__((objc_class_stub))
+@interface MissingSubclassingRestrictedAttribute : NSObject // expected-error {{'objc_class_stub' attribute cannot be specified on a class that does not have the 'objc_subclassing_restricted' attribute}}
+@end
+
+__attribute__((objc_class_stub))
+__attribute__((objc_subclassing_restricted))
+@interface ValidClassStubAttribute : NSObject
+@end
+
+@implementation ValidClassStubAttribute // expected-error {{cannot declare implementation of a class declared with the 'objc_class_stub' attribute}}
+@end
+
+@implementation ValidClassStubAttribute (MyCategory)
+@end
+
+__attribute__((objc_class_stub(123))) // expected-error {{'objc_class_stub' attribute takes no arguments}}
+@interface InvalidClassStubAttribute : NSObject
+@end
+
+__attribute__((objc_class_stub)) // expected-error {{'objc_class_stub' attribute only applies to Objective-C interfaces}}
+int cannotHaveObjCClassStubAttribute() {}
return true;
}
+static std::string GenerateTestExpression(ArrayRef<Record *> LangOpts) {
+ std::string Test;
+
+ for (auto *E : LangOpts) {
+ if (!Test.empty())
+ Test += " || ";
+
+ const StringRef Code = E->getValueAsString("CustomCode");
+ if (!Code.empty()) {
+ Test += "(";
+ Test += Code;
+ Test += ")";
+ } else {
+ Test += "LangOpts.";
+ Test += E->getValueAsString("Name");
+ }
+ }
+
+ if (Test.empty())
+ return "true";
+
+ return Test;
+}
+
std::string
PragmaClangAttributeSupport::generateStrictConformsTo(const Record &Attr,
raw_ostream &OS) {
// rules if the specific language options are specified.
std::vector<Record *> LangOpts = Rule.getLangOpts();
OS << " MatchRules.push_back(std::make_pair(" << Rule.getEnumValue()
- << ", /*IsSupported=*/";
- if (!LangOpts.empty()) {
- for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) {
- const StringRef Part = (*I)->getValueAsString("Name");
- if ((*I)->getValueAsBit("Negated"))
- OS << "!";
- OS << "LangOpts." << Part;
- if (I + 1 != E)
- OS << " || ";
- }
- } else
- OS << "true";
- OS << "));\n";
+ << ", /*IsSupported=*/" << GenerateTestExpression(LangOpts)
+ << "));\n";
}
}
OS << "}\n\n";
if (LangOpts.empty())
return "defaultDiagnoseLangOpts";
- // Generate the test condition, as well as a unique function name for the
- // diagnostic test. The list of options should usually be short (one or two
- // options), and the uniqueness isn't strictly necessary (it is just for
- // codegen efficiency).
- std::string FnName = "check", Test;
- for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I) {
- const StringRef Part = (*I)->getValueAsString("Name");
- if ((*I)->getValueAsBit("Negated")) {
- FnName += "Not";
- Test += "!";
- }
- Test += "S.LangOpts.";
- Test += Part;
- if (I + 1 != E)
- Test += " || ";
- FnName += Part;
- }
+ // Generate a unique function name for the diagnostic test. The list of
+ // options should usually be short (one or two options), and the
+ // uniqueness isn't strictly necessary (it is just for codegen efficiency).
+ std::string FnName = "check";
+ for (auto I = LangOpts.begin(), E = LangOpts.end(); I != E; ++I)
+ FnName += (*I)->getValueAsString("Name");
FnName += "LangOpts";
// If this code has already been generated, simply return the previous
return *I;
OS << "static bool " << FnName << "(Sema &S, const ParsedAttr &Attr) {\n";
- OS << " if (" << Test << ")\n";
+ OS << " auto &LangOpts = S.LangOpts;\n";
+ OS << " if (" << GenerateTestExpression(LangOpts) << ")\n";
OS << " return true;\n\n";
OS << " S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) ";
OS << "<< Attr.getName();\n";