Index: clang/lib/CodeGen/CGObjC.cpp =================================================================== --- clang/lib/CodeGen/CGObjC.cpp +++ clang/lib/CodeGen/CGObjC.cpp @@ -1111,6 +1111,21 @@ callee, ReturnValueSlot(), args); } +// emitCmdLoadForGetterSetterBody - Handle emitting local storage declarations +// for the `_cmd` argument that no longer exists for direct methods. +static llvm::Value *emitCmdLoadForGetterSetterBody(CodeGenFunction &CGF, + ObjCMethodDecl *MD) { + if (MD->isDirectMethod()) { + // Direct methods no longer have a `_cmd` argument, so storage must be + // emitted for it to be passed to the property helper. Since the `_cmd` + // argument was never being initialized by the caller before, still pass + // an uninitialized/undefined value here. + CGF.EmitVarDecl(*MD->getCmdDecl()); + } + + return CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(MD->getCmdDecl()), "cmd"); +} + void CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, @@ -1189,8 +1204,7 @@ // Return (ivar-type) objc_getProperty((id) self, _cmd, offset, true). // FIXME: Can't this be simpler? This might even be worse than the // corresponding gcc code. - llvm::Value *cmd = - Builder.CreateLoad(GetAddrOfLocalVar(getterMethod->getCmdDecl()), "cmd"); + llvm::Value *cmd = emitCmdLoadForGetterSetterBody(*this, getterMethod); llvm::Value *self = Builder.CreateBitCast(LoadObjCSelf(), VoidPtrTy); llvm::Value *ivarOffset = EmitIvarOffset(classImpl->getClassInterface(), ivar); @@ -1475,8 +1489,7 @@ // Emit objc_setProperty((id) self, _cmd, offset, arg, // , ). - llvm::Value *cmd = - Builder.CreateLoad(GetAddrOfLocalVar(setterMethod->getCmdDecl())); + llvm::Value *cmd = emitCmdLoadForGetterSetterBody(*this, setterMethod); llvm::Value *self = Builder.CreateBitCast(LoadObjCSelf(), VoidPtrTy); llvm::Value *ivarOffset = Index: clang/test/CodeGenObjC/direct-method.m =================================================================== --- clang/test/CodeGenObjC/direct-method.m +++ clang/test/CodeGenObjC/direct-method.m @@ -14,6 +14,7 @@ - (int)getInt __attribute__((objc_direct)); @property(direct, readonly) int intProperty; @property(direct, readonly) int intProperty2; +@property(direct, readonly) id objectProperty; @end @implementation Root @@ -167,6 +168,18 @@ @end // CHECK-LABEL: define hidden i32 @"\01-[Root intProperty]" +// Check the synthesized objectProperty calls objc_getProperty(); this also +// checks that the synthesized method accesses _cmd (or rather loads the +// selector, as it is an argument to objc_getProperty). +// CHECK-LABEL: define hidden i8* @"\01-[Root objectProperty]"( +// CHECK-LABEL: entry: +// CHECK-NEXT: [[RETVAL:%.*]] = alloca i8*, +// CHECK-NEXT: [[SELFADDR:%.*]] = alloca %0*, +// CHECK-NEXT: [[CMDVAL:%_cmd]] = alloca i8*, +// CHECK-LABEL: objc_direct_method.cont: +// CHECK: [[CMD:%.*]] = load i8*, i8** [[CMDVAL]], +// CHECK: call i8* @objc_getProperty({{.*}}, i8*{{.*}} [[CMD]], {{.*}}) + @interface Foo : Root { id __strong _cause_cxx_destruct; }