From: Michael Wyman Date: Tue, 11 Oct 2022 21:32:21 +0000 (-0700) Subject: Fix assert in generated `direct` property getter/setters due to removal of `_cmd... X-Git-Tag: upstream/17.0.6~30897 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=1fbb6d8b34dec217179d8e46d8c5d8e243047045;p=platform%2Fupstream%2Fllvm.git Fix assert in generated `direct` property getter/setters due to removal of `_cmd` parameter. This fixes a bug from https://reviews.llvm.org/D131424 that removed the implicit `_cmd` parameter as an argument to `objc_direct` method implementations. In many cases the generated getter/setter will call `objc_getProperty` or `objc_setProperty`, both of which require the selector of the getter/setter; since `_cmd` didn't automatically have backing storage, attempting to load the address asserted. For direct property generated getters/setters, this now passes an undefined/uninitialized/poison value as the `_cmd` argument to `objc_getProperty`/`objc_setProperty`. Prior to removing the `_cmd` argument from the ABI of direct methods, it was left uninitialized/undefined; although references within hand-implemented methods would load the selector in the method prologue, generated getters/setters never did and just forwarded the undefined value that was passed as the argument. This change keeps the generated code mostly similar to before, passing an uninitialized/undefined/poison value; for setters, the value argument may be moved to another register. Added a test that triggers the assert prior to the implementation code. Differential Revision: https://reviews.llvm.org/D135091 --- diff --git a/clang/lib/CodeGen/CGObjC.cpp b/clang/lib/CodeGen/CGObjC.cpp index bbd300e..7f23be0 100644 --- a/clang/lib/CodeGen/CGObjC.cpp +++ b/clang/lib/CodeGen/CGObjC.cpp @@ -25,6 +25,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Analysis/ObjCARCUtil.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InlineAsm.h" using namespace clang; @@ -1111,6 +1112,25 @@ static void emitCPPObjectAtomicGetterCall(CodeGenFunction &CGF, callee, ReturnValueSlot(), args); } +// emitCmdValueForGetterSetterBody - Handle emitting the load necessary for +// the `_cmd` selector argument for getter/setter bodies. For direct methods, +// this returns an undefined/poison value; this matches behavior prior to `_cmd` +// being removed from the direct method ABI as the getter/setter caller would +// never load one. For non-direct methods, this emits a load of the implicit +// `_cmd` storage. +static llvm::Value *emitCmdValueForGetterSetterBody(CodeGenFunction &CGF, + ObjCMethodDecl *MD) { + if (MD->isDirectMethod()) { + // Direct methods do not have a `_cmd` argument. Emit an undefined/poison + // value. This will be passed to objc_getProperty/objc_setProperty, which + // has not appeared bothered by the `_cmd` argument being undefined before. + llvm::Type *selType = CGF.ConvertType(CGF.getContext().getObjCSelType()); + return llvm::PoisonValue::get(selType); + } + + return CGF.Builder.CreateLoad(CGF.GetAddrOfLocalVar(MD->getCmdDecl()), "cmd"); +} + void CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, const ObjCPropertyImplDecl *propImpl, @@ -1189,8 +1209,7 @@ CodeGenFunction::generateObjCGetterBody(const ObjCImplementationDecl *classImpl, // 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 = emitCmdValueForGetterSetterBody(*this, getterMethod); llvm::Value *self = Builder.CreateBitCast(LoadObjCSelf(), VoidPtrTy); llvm::Value *ivarOffset = EmitIvarOffset(classImpl->getClassInterface(), ivar); @@ -1475,8 +1494,7 @@ CodeGenFunction::generateObjCSetterBody(const ObjCImplementationDecl *classImpl, // Emit objc_setProperty((id) self, _cmd, offset, arg, // , ). - llvm::Value *cmd = - Builder.CreateLoad(GetAddrOfLocalVar(setterMethod->getCmdDecl())); + llvm::Value *cmd = emitCmdValueForGetterSetterBody(*this, setterMethod); llvm::Value *self = Builder.CreateBitCast(LoadObjCSelf(), VoidPtrTy); llvm::Value *ivarOffset = diff --git a/clang/test/CodeGenObjC/direct-method.m b/clang/test/CodeGenObjC/direct-method.m index 2583de1..5d317f4 100644 --- a/clang/test/CodeGenObjC/direct-method.m +++ b/clang/test/CodeGenObjC/direct-method.m @@ -14,6 +14,7 @@ __attribute__((objc_root_class)) - (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,15 @@ __attribute__((objc_root_class)) @end // CHECK-LABEL: define hidden i32 @"\01-[Root intProperty]" +// Check the synthesized objectProperty calls objc_getProperty(); this also +// checks that the synthesized method passes undef for the `cmd` argument. +// CHECK-LABEL: define hidden i8* @"\01-[Root objectProperty]"( +// CHECK-LABEL: objc_direct_method.cont: +// CHECK-NEXT: [[SELFVAL:%.*]] = load {{.*}} %self.addr, +// CHECK-NEXT: [[SELF:%.*]] = bitcast {{.*}} [[SELFVAL]] to i8* +// CHECK-NEXT: [[IVAR:%.*]] = load {{.*}} @"OBJC_IVAR_$_Root._objectProperty", +// CHECK-NEXT: call i8* @objc_getProperty(i8* noundef [[SELF]], i8* noundef poison, i64 noundef [[IVAR]], {{.*}}) + @interface Foo : Root { id __strong _cause_cxx_destruct; }