Path: blob/main/contrib/llvm-project/clang/lib/CodeGen/CGObjCRuntime.cpp
35233 views
//==- CGObjCRuntime.cpp - Interface to Shared Objective-C Runtime Features ==//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This abstract class defines the interface for Objective-C runtime-specific9// code generation. It provides some concrete helper methods for functionality10// shared between all (or most) of the Objective-C runtimes supported by clang.11//12//===----------------------------------------------------------------------===//1314#include "CGObjCRuntime.h"15#include "CGCXXABI.h"16#include "CGCleanup.h"17#include "CGRecordLayout.h"18#include "CodeGenFunction.h"19#include "CodeGenModule.h"20#include "clang/AST/RecordLayout.h"21#include "clang/AST/StmtObjC.h"22#include "clang/CodeGen/CGFunctionInfo.h"23#include "clang/CodeGen/CodeGenABITypes.h"24#include "llvm/IR/Instruction.h"25#include "llvm/Support/SaveAndRestore.h"2627using namespace clang;28using namespace CodeGen;2930uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM,31const ObjCInterfaceDecl *OID,32const ObjCIvarDecl *Ivar) {33return CGM.getContext().lookupFieldBitOffset(OID, nullptr, Ivar) /34CGM.getContext().getCharWidth();35}3637uint64_t CGObjCRuntime::ComputeIvarBaseOffset(CodeGen::CodeGenModule &CGM,38const ObjCImplementationDecl *OID,39const ObjCIvarDecl *Ivar) {40return CGM.getContext().lookupFieldBitOffset(OID->getClassInterface(), OID,41Ivar) /42CGM.getContext().getCharWidth();43}4445unsigned CGObjCRuntime::ComputeBitfieldBitOffset(46CodeGen::CodeGenModule &CGM,47const ObjCInterfaceDecl *ID,48const ObjCIvarDecl *Ivar) {49return CGM.getContext().lookupFieldBitOffset(ID, ID->getImplementation(),50Ivar);51}5253LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF,54const ObjCInterfaceDecl *OID,55llvm::Value *BaseValue,56const ObjCIvarDecl *Ivar,57unsigned CVRQualifiers,58llvm::Value *Offset) {59// Compute (type*) ( (char *) BaseValue + Offset)60QualType InterfaceTy{OID->getTypeForDecl(), 0};61QualType ObjectPtrTy =62CGF.CGM.getContext().getObjCObjectPointerType(InterfaceTy);63QualType IvarTy =64Ivar->getUsageType(ObjectPtrTy).withCVRQualifiers(CVRQualifiers);65llvm::Value *V = BaseValue;66V = CGF.Builder.CreateInBoundsGEP(CGF.Int8Ty, V, Offset, "add.ptr");6768if (!Ivar->isBitField()) {69LValue LV = CGF.MakeNaturalAlignRawAddrLValue(V, IvarTy);70return LV;71}7273// We need to compute an access strategy for this bit-field. We are given the74// offset to the first byte in the bit-field, the sub-byte offset is taken75// from the original layout. We reuse the normal bit-field access strategy by76// treating this as an access to a struct where the bit-field is in byte 0,77// and adjust the containing type size as appropriate.78//79// FIXME: Note that currently we make a very conservative estimate of the80// alignment of the bit-field, because (a) it is not clear what guarantees the81// runtime makes us, and (b) we don't have a way to specify that the struct is82// at an alignment plus offset.83//84// Note, there is a subtle invariant here: we can only call this routine on85// non-synthesized ivars but we may be called for synthesized ivars. However,86// a synthesized ivar can never be a bit-field, so this is safe.87uint64_t FieldBitOffset =88CGF.CGM.getContext().lookupFieldBitOffset(OID, nullptr, Ivar);89uint64_t BitOffset = FieldBitOffset % CGF.CGM.getContext().getCharWidth();90uint64_t AlignmentBits = CGF.CGM.getTarget().getCharAlign();91uint64_t BitFieldSize = Ivar->getBitWidthValue(CGF.getContext());92CharUnits StorageSize = CGF.CGM.getContext().toCharUnitsFromBits(93llvm::alignTo(BitOffset + BitFieldSize, AlignmentBits));94CharUnits Alignment = CGF.CGM.getContext().toCharUnitsFromBits(AlignmentBits);9596// Allocate a new CGBitFieldInfo object to describe this access.97//98// FIXME: This is incredibly wasteful, these should be uniqued or part of some99// layout object. However, this is blocked on other cleanups to the100// Objective-C code, so for now we just live with allocating a bunch of these101// objects.102CGBitFieldInfo *Info = new (CGF.CGM.getContext()) CGBitFieldInfo(103CGBitFieldInfo::MakeInfo(CGF.CGM.getTypes(), Ivar, BitOffset, BitFieldSize,104CGF.CGM.getContext().toBits(StorageSize),105CharUnits::fromQuantity(0)));106107Address Addr =108Address(V, llvm::Type::getIntNTy(CGF.getLLVMContext(), Info->StorageSize),109Alignment);110111return LValue::MakeBitfield(Addr, *Info, IvarTy,112LValueBaseInfo(AlignmentSource::Decl),113TBAAAccessInfo());114}115116namespace {117struct CatchHandler {118const VarDecl *Variable;119const Stmt *Body;120llvm::BasicBlock *Block;121llvm::Constant *TypeInfo;122/// Flags used to differentiate cleanups and catchalls in Windows SEH123unsigned Flags;124};125126struct CallObjCEndCatch final : EHScopeStack::Cleanup {127CallObjCEndCatch(bool MightThrow, llvm::FunctionCallee Fn)128: MightThrow(MightThrow), Fn(Fn) {}129bool MightThrow;130llvm::FunctionCallee Fn;131132void Emit(CodeGenFunction &CGF, Flags flags) override {133if (MightThrow)134CGF.EmitRuntimeCallOrInvoke(Fn);135else136CGF.EmitNounwindRuntimeCall(Fn);137}138};139}140141void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF,142const ObjCAtTryStmt &S,143llvm::FunctionCallee beginCatchFn,144llvm::FunctionCallee endCatchFn,145llvm::FunctionCallee exceptionRethrowFn) {146// Jump destination for falling out of catch bodies.147CodeGenFunction::JumpDest Cont;148if (S.getNumCatchStmts())149Cont = CGF.getJumpDestInCurrentScope("eh.cont");150151bool useFunclets = EHPersonality::get(CGF).usesFuncletPads();152153CodeGenFunction::FinallyInfo FinallyInfo;154if (!useFunclets)155if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt())156FinallyInfo.enter(CGF, Finally->getFinallyBody(),157beginCatchFn, endCatchFn, exceptionRethrowFn);158159SmallVector<CatchHandler, 8> Handlers;160161162// Enter the catch, if there is one.163if (S.getNumCatchStmts()) {164for (const ObjCAtCatchStmt *CatchStmt : S.catch_stmts()) {165const VarDecl *CatchDecl = CatchStmt->getCatchParamDecl();166167Handlers.push_back(CatchHandler());168CatchHandler &Handler = Handlers.back();169Handler.Variable = CatchDecl;170Handler.Body = CatchStmt->getCatchBody();171Handler.Block = CGF.createBasicBlock("catch");172Handler.Flags = 0;173174// @catch(...) always matches.175if (!CatchDecl) {176auto catchAll = getCatchAllTypeInfo();177Handler.TypeInfo = catchAll.RTTI;178Handler.Flags = catchAll.Flags;179// Don't consider any other catches.180break;181}182183Handler.TypeInfo = GetEHType(CatchDecl->getType());184}185186EHCatchScope *Catch = CGF.EHStack.pushCatch(Handlers.size());187for (unsigned I = 0, E = Handlers.size(); I != E; ++I)188Catch->setHandler(I, { Handlers[I].TypeInfo, Handlers[I].Flags }, Handlers[I].Block);189}190191if (useFunclets)192if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) {193CodeGenFunction HelperCGF(CGM, /*suppressNewContext=*/true);194if (!CGF.CurSEHParent)195CGF.CurSEHParent = cast<NamedDecl>(CGF.CurFuncDecl);196// Outline the finally block.197const Stmt *FinallyBlock = Finally->getFinallyBody();198HelperCGF.startOutlinedSEHHelper(CGF, /*isFilter*/false, FinallyBlock);199200// Emit the original filter expression, convert to i32, and return.201HelperCGF.EmitStmt(FinallyBlock);202203HelperCGF.FinishFunction(FinallyBlock->getEndLoc());204205llvm::Function *FinallyFunc = HelperCGF.CurFn;206207208// Push a cleanup for __finally blocks.209CGF.pushSEHCleanup(NormalAndEHCleanup, FinallyFunc);210}211212213// Emit the try body.214CGF.EmitStmt(S.getTryBody());215216// Leave the try.217if (S.getNumCatchStmts())218CGF.popCatchScope();219220// Remember where we were.221CGBuilderTy::InsertPoint SavedIP = CGF.Builder.saveAndClearIP();222223// Emit the handlers.224for (unsigned I = 0, E = Handlers.size(); I != E; ++I) {225CatchHandler &Handler = Handlers[I];226227CGF.EmitBlock(Handler.Block);228229CodeGenFunction::LexicalScope Cleanups(CGF, Handler.Body->getSourceRange());230SaveAndRestore RevertAfterScope(CGF.CurrentFuncletPad);231if (useFunclets) {232llvm::Instruction *CPICandidate = Handler.Block->getFirstNonPHI();233if (auto *CPI = dyn_cast_or_null<llvm::CatchPadInst>(CPICandidate)) {234CGF.CurrentFuncletPad = CPI;235CPI->setOperand(2, CGF.getExceptionSlot().emitRawPointer(CGF));236CGF.EHStack.pushCleanup<CatchRetScope>(NormalCleanup, CPI);237}238}239240llvm::Value *RawExn = CGF.getExceptionFromSlot();241242// Enter the catch.243llvm::Value *Exn = RawExn;244if (beginCatchFn)245Exn = CGF.EmitNounwindRuntimeCall(beginCatchFn, RawExn, "exn.adjusted");246247if (endCatchFn) {248// Add a cleanup to leave the catch.249bool EndCatchMightThrow = (Handler.Variable == nullptr);250251CGF.EHStack.pushCleanup<CallObjCEndCatch>(NormalAndEHCleanup,252EndCatchMightThrow,253endCatchFn);254}255256// Bind the catch parameter if it exists.257if (const VarDecl *CatchParam = Handler.Variable) {258llvm::Type *CatchType = CGF.ConvertType(CatchParam->getType());259llvm::Value *CastExn = CGF.Builder.CreateBitCast(Exn, CatchType);260261CGF.EmitAutoVarDecl(*CatchParam);262EmitInitOfCatchParam(CGF, CastExn, CatchParam);263}264265CGF.ObjCEHValueStack.push_back(Exn);266CGF.EmitStmt(Handler.Body);267CGF.ObjCEHValueStack.pop_back();268269// Leave any cleanups associated with the catch.270Cleanups.ForceCleanup();271272CGF.EmitBranchThroughCleanup(Cont);273}274275// Go back to the try-statement fallthrough.276CGF.Builder.restoreIP(SavedIP);277278// Pop out of the finally.279if (!useFunclets && S.getFinallyStmt())280FinallyInfo.exit(CGF);281282if (Cont.isValid())283CGF.EmitBlock(Cont.getBlock());284}285286void CGObjCRuntime::EmitInitOfCatchParam(CodeGenFunction &CGF,287llvm::Value *exn,288const VarDecl *paramDecl) {289290Address paramAddr = CGF.GetAddrOfLocalVar(paramDecl);291292switch (paramDecl->getType().getQualifiers().getObjCLifetime()) {293case Qualifiers::OCL_Strong:294exn = CGF.EmitARCRetainNonBlock(exn);295[[fallthrough]];296297case Qualifiers::OCL_None:298case Qualifiers::OCL_ExplicitNone:299case Qualifiers::OCL_Autoreleasing:300CGF.Builder.CreateStore(exn, paramAddr);301return;302303case Qualifiers::OCL_Weak:304CGF.EmitARCInitWeak(paramAddr, exn);305return;306}307llvm_unreachable("invalid ownership qualifier");308}309310namespace {311struct CallSyncExit final : EHScopeStack::Cleanup {312llvm::FunctionCallee SyncExitFn;313llvm::Value *SyncArg;314CallSyncExit(llvm::FunctionCallee SyncExitFn, llvm::Value *SyncArg)315: SyncExitFn(SyncExitFn), SyncArg(SyncArg) {}316317void Emit(CodeGenFunction &CGF, Flags flags) override {318CGF.EmitNounwindRuntimeCall(SyncExitFn, SyncArg);319}320};321}322323void CGObjCRuntime::EmitAtSynchronizedStmt(CodeGenFunction &CGF,324const ObjCAtSynchronizedStmt &S,325llvm::FunctionCallee syncEnterFn,326llvm::FunctionCallee syncExitFn) {327CodeGenFunction::RunCleanupsScope cleanups(CGF);328329// Evaluate the lock operand. This is guaranteed to dominate the330// ARC release and lock-release cleanups.331const Expr *lockExpr = S.getSynchExpr();332llvm::Value *lock;333if (CGF.getLangOpts().ObjCAutoRefCount) {334lock = CGF.EmitARCRetainScalarExpr(lockExpr);335lock = CGF.EmitObjCConsumeObject(lockExpr->getType(), lock);336} else {337lock = CGF.EmitScalarExpr(lockExpr);338}339lock = CGF.Builder.CreateBitCast(lock, CGF.VoidPtrTy);340341// Acquire the lock.342CGF.Builder.CreateCall(syncEnterFn, lock)->setDoesNotThrow();343344// Register an all-paths cleanup to release the lock.345CGF.EHStack.pushCleanup<CallSyncExit>(NormalAndEHCleanup, syncExitFn, lock);346347// Emit the body of the statement.348CGF.EmitStmt(S.getSynchBody());349}350351/// Compute the pointer-to-function type to which a message send352/// should be casted in order to correctly call the given method353/// with the given arguments.354///355/// \param method - may be null356/// \param resultType - the result type to use if there's no method357/// \param callArgs - the actual arguments, including implicit ones358CGObjCRuntime::MessageSendInfo359CGObjCRuntime::getMessageSendInfo(const ObjCMethodDecl *method,360QualType resultType,361CallArgList &callArgs) {362unsigned ProgramAS = CGM.getDataLayout().getProgramAddressSpace();363364llvm::PointerType *signatureType =365llvm::PointerType::get(CGM.getLLVMContext(), ProgramAS);366367// If there's a method, use information from that.368if (method) {369const CGFunctionInfo &signature =370CGM.getTypes().arrangeObjCMessageSendSignature(method, callArgs[0].Ty);371372const CGFunctionInfo &signatureForCall =373CGM.getTypes().arrangeCall(signature, callArgs);374375return MessageSendInfo(signatureForCall, signatureType);376}377378// There's no method; just use a default CC.379const CGFunctionInfo &argsInfo =380CGM.getTypes().arrangeUnprototypedObjCMessageSend(resultType, callArgs);381382return MessageSendInfo(argsInfo, signatureType);383}384385bool CGObjCRuntime::canMessageReceiverBeNull(CodeGenFunction &CGF,386const ObjCMethodDecl *method,387bool isSuper,388const ObjCInterfaceDecl *classReceiver,389llvm::Value *receiver) {390// Super dispatch assumes that self is non-null; even the messenger391// doesn't have a null check internally.392if (isSuper)393return false;394395// If this is a direct dispatch of a class method, check whether the class,396// or anything in its hierarchy, was weak-linked.397if (classReceiver && method && method->isClassMethod())398return isWeakLinkedClass(classReceiver);399400// If we're emitting a method, and self is const (meaning just ARC, for now),401// and the receiver is a load of self, then self is a valid object.402if (auto curMethod =403dyn_cast_or_null<ObjCMethodDecl>(CGF.CurCodeDecl)) {404auto self = curMethod->getSelfDecl();405if (self->getType().isConstQualified()) {406if (auto LI = dyn_cast<llvm::LoadInst>(receiver->stripPointerCasts())) {407llvm::Value *selfAddr = CGF.GetAddrOfLocalVar(self).emitRawPointer(CGF);408if (selfAddr == LI->getPointerOperand()) {409return false;410}411}412}413}414415// Otherwise, assume it can be null.416return true;417}418419bool CGObjCRuntime::isWeakLinkedClass(const ObjCInterfaceDecl *ID) {420do {421if (ID->isWeakImported())422return true;423} while ((ID = ID->getSuperClass()));424425return false;426}427428void CGObjCRuntime::destroyCalleeDestroyedArguments(CodeGenFunction &CGF,429const ObjCMethodDecl *method,430const CallArgList &callArgs) {431CallArgList::const_iterator I = callArgs.begin();432for (auto i = method->param_begin(), e = method->param_end();433i != e; ++i, ++I) {434const ParmVarDecl *param = (*i);435if (param->hasAttr<NSConsumedAttr>()) {436RValue RV = I->getRValue(CGF);437assert(RV.isScalar() &&438"NullReturnState::complete - arg not on object");439CGF.EmitARCRelease(RV.getScalarVal(), ARCImpreciseLifetime);440} else {441QualType QT = param->getType();442auto *RT = QT->getAs<RecordType>();443if (RT && RT->getDecl()->isParamDestroyedInCallee()) {444RValue RV = I->getRValue(CGF);445QualType::DestructionKind DtorKind = QT.isDestructedType();446switch (DtorKind) {447case QualType::DK_cxx_destructor:448CGF.destroyCXXObject(CGF, RV.getAggregateAddress(), QT);449break;450case QualType::DK_nontrivial_c_struct:451CGF.destroyNonTrivialCStruct(CGF, RV.getAggregateAddress(), QT);452break;453default:454llvm_unreachable("unexpected dtor kind");455break;456}457}458}459}460}461462llvm::Constant *463clang::CodeGen::emitObjCProtocolObject(CodeGenModule &CGM,464const ObjCProtocolDecl *protocol) {465return CGM.getObjCRuntime().GetOrEmitProtocol(protocol);466}467468std::string CGObjCRuntime::getSymbolNameForMethod(const ObjCMethodDecl *OMD,469bool includeCategoryName) {470std::string buffer;471llvm::raw_string_ostream out(buffer);472CGM.getCXXABI().getMangleContext().mangleObjCMethodName(OMD, out,473/*includePrefixByte=*/true,474includeCategoryName);475return buffer;476}477478479