//===--- CIRGenExprCXX.cpp - Emit CIR Code for C++ expressions ------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This contains code dealing with code generation of C++ expressions // //===----------------------------------------------------------------------===// #include "CIRGenCXXABI.h" #include "CIRGenConstantEmitter.h" #include "CIRGenFunction.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/Basic/OperatorKinds.h" #include "clang/CIR/MissingFeatures.h" #include "llvm/Support/TrailingObjects.h" using namespace clang; using namespace clang::CIRGen; namespace { struct MemberCallInfo { RequiredArgs reqArgs; // Number of prefix arguments for the call. Ignores the `this` pointer. unsigned prefixSize; }; } // namespace static MemberCallInfo commonBuildCXXMemberOrOperatorCall( CIRGenFunction &cgf, const CXXMethodDecl *md, mlir::Value thisPtr, mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce, CallArgList &args, CallArgList *rtlArgs) { assert(ce == nullptr || isa(ce) || isa(ce)); assert(md->isInstance() && "Trying to emit a member or operator call expr on a static method!"); // Push the this ptr. const CXXRecordDecl *rd = cgf.cgm.getCXXABI().getThisArgumentTypeForMethod(md); args.add(RValue::get(thisPtr), cgf.getTypes().deriveThisType(rd, md)); // If there is an implicit parameter (e.g. VTT), emit it. if (implicitParam) { args.add(RValue::get(implicitParam), implicitParamTy); } const auto *fpt = md->getType()->castAs(); RequiredArgs required = RequiredArgs::getFromProtoWithExtraSlots(fpt, args.size()); unsigned prefixSize = args.size() - 1; // Add the rest of the call args if (rtlArgs) { // Special case: if the caller emitted the arguments right-to-left already // (prior to emitting the *this argument), we're done. This happens for // assignment operators. args.addFrom(*rtlArgs); } else if (ce) { // Special case: skip first argument of CXXOperatorCall (it is "this"). unsigned argsToSkip = isa(ce) ? 1 : 0; cgf.emitCallArgs(args, fpt, drop_begin(ce->arguments(), argsToSkip), ce->getDirectCallee()); } else { assert( fpt->getNumParams() == 0 && "No CallExpr specified for function with non-zero number of arguments"); } // return {required, prefixSize}; return {required, prefixSize}; } RValue CIRGenFunction::emitCXXMemberPointerCallExpr(const CXXMemberCallExpr *ce, ReturnValueSlot returnValue) { const BinaryOperator *bo = cast(ce->getCallee()->IgnoreParens()); const Expr *baseExpr = bo->getLHS(); const Expr *memFnExpr = bo->getRHS(); const auto *mpt = memFnExpr->getType()->castAs(); const auto *fpt = mpt->getPointeeType()->castAs(); // Emit the 'this' pointer. Address thisAddr = Address::invalid(); if (bo->getOpcode() == BO_PtrMemI) thisAddr = emitPointerWithAlignment(baseExpr); else thisAddr = emitLValue(baseExpr).getAddress(); assert(!cir::MissingFeatures::emitTypeCheck()); // Get the member function pointer. mlir::Value memFnPtr = emitScalarExpr(memFnExpr); // Resolve the member function pointer to the actual callee and adjust the // "this" pointer for call. mlir::Location loc = getLoc(ce->getExprLoc()); auto [/*mlir::Value*/ calleePtr, /*mlir::Value*/ adjustedThis] = builder.createGetMethod(loc, memFnPtr, thisAddr.getPointer()); // Prepare the call arguments. CallArgList argsList; argsList.add(RValue::get(adjustedThis), getContext().VoidPtrTy); emitCallArgs(argsList, fpt, ce->arguments()); RequiredArgs required = RequiredArgs::getFromProtoWithExtraSlots(fpt, 1); // Build the call. CIRGenCallee callee(fpt, calleePtr.getDefiningOp()); assert(!cir::MissingFeatures::opCallMustTail()); return emitCall(cgm.getTypes().arrangeCXXMethodCall(argsList, fpt, required, /*PrefixSize=*/0), callee, returnValue, argsList, nullptr, loc); } RValue CIRGenFunction::emitCXXMemberOrOperatorMemberCallExpr( const CallExpr *ce, const CXXMethodDecl *md, ReturnValueSlot returnValue, bool hasQualifier, NestedNameSpecifier qualifier, bool isArrow, const Expr *base) { assert(isa(ce) || isa(ce)); // Compute the object pointer. bool canUseVirtualCall = md->isVirtual() && !hasQualifier; const CXXMethodDecl *devirtualizedMethod = nullptr; assert(!cir::MissingFeatures::devirtualizeMemberFunction()); // Note on trivial assignment // -------------------------- // Classic codegen avoids generating the trivial copy/move assignment operator // when it isn't necessary, choosing instead to just produce IR with an // equivalent effect. We have chosen not to do that in CIR, instead emitting // trivial copy/move assignment operators and allowing later transformations // to optimize them away if appropriate. // C++17 demands that we evaluate the RHS of a (possibly-compound) assignment // operator before the LHS. CallArgList rtlArgStorage; CallArgList *rtlArgs = nullptr; if (auto *oce = dyn_cast(ce)) { if (oce->isAssignmentOp()) { rtlArgs = &rtlArgStorage; emitCallArgs(*rtlArgs, md->getType()->castAs(), drop_begin(ce->arguments(), 1), ce->getDirectCallee(), /*ParamsToSkip*/ 0); } } LValue thisPtr; if (isArrow) { LValueBaseInfo baseInfo; assert(!cir::MissingFeatures::opTBAA()); Address thisValue = emitPointerWithAlignment(base, &baseInfo); thisPtr = makeAddrLValue(thisValue, base->getType(), baseInfo); } else { thisPtr = emitLValue(base); } if (isa(md)) { cgm.errorNYI(ce->getSourceRange(), "emitCXXMemberOrOperatorMemberCallExpr: constructor call"); return RValue::get(nullptr); } if ((md->isTrivial() || (md->isDefaulted() && md->getParent()->isUnion())) && isa(md)) return RValue::get(nullptr); // Compute the function type we're calling const CXXMethodDecl *calleeDecl = devirtualizedMethod ? devirtualizedMethod : md; const CIRGenFunctionInfo *fInfo = nullptr; if (const auto *dtor = dyn_cast(calleeDecl)) fInfo = &cgm.getTypes().arrangeCXXStructorDeclaration( GlobalDecl(dtor, Dtor_Complete)); else fInfo = &cgm.getTypes().arrangeCXXMethodDeclaration(calleeDecl); cir::FuncType ty = cgm.getTypes().getFunctionType(*fInfo); assert(!cir::MissingFeatures::sanitizers()); assert(!cir::MissingFeatures::emitTypeCheck()); // C++ [class.virtual]p12: // Explicit qualification with the scope operator (5.1) suppresses the // virtual call mechanism. // // We also don't emit a virtual call if the base expression has a record type // because then we know what the type is. bool useVirtualCall = canUseVirtualCall && !devirtualizedMethod; if (const auto *dtor = dyn_cast(calleeDecl)) { assert(ce->arg_begin() == ce->arg_end() && "Destructor shouldn't have explicit parameters"); assert(returnValue.isNull() && "Destructor shouldn't have return value"); if (useVirtualCall) { cgm.getCXXABI().emitVirtualDestructorCall(*this, dtor, Dtor_Complete, thisPtr.getAddress(), cast(ce)); } else { GlobalDecl globalDecl(dtor, Dtor_Complete); CIRGenCallee callee; assert(!cir::MissingFeatures::appleKext()); if (!devirtualizedMethod) { callee = CIRGenCallee::forDirect( cgm.getAddrOfCXXStructor(globalDecl, fInfo, ty), globalDecl); } else { cgm.errorNYI(ce->getSourceRange(), "devirtualized destructor call"); return RValue::get(nullptr); } QualType thisTy = isArrow ? base->getType()->getPointeeType() : base->getType(); // CIRGen does not pass CallOrInvoke here (different from OG LLVM codegen) // because in practice it always null even in OG. emitCXXDestructorCall(globalDecl, callee, thisPtr.getPointer(), thisTy, /*implicitParam=*/nullptr, /*implicitParamTy=*/QualType(), ce); } return RValue::get(nullptr); } CIRGenCallee callee; if (useVirtualCall) { callee = CIRGenCallee::forVirtual(ce, md, thisPtr.getAddress(), ty); } else { assert(!cir::MissingFeatures::sanitizers()); if (getLangOpts().AppleKext) { cgm.errorNYI(ce->getSourceRange(), "emitCXXMemberOrOperatorMemberCallExpr: AppleKext"); return RValue::get(nullptr); } callee = CIRGenCallee::forDirect(cgm.getAddrOfFunction(calleeDecl, ty), GlobalDecl(calleeDecl)); } if (md->isVirtual()) { Address newThisAddr = cgm.getCXXABI().adjustThisArgumentForVirtualFunctionCall( *this, calleeDecl, thisPtr.getAddress(), useVirtualCall); thisPtr.setAddress(newThisAddr); } return emitCXXMemberOrOperatorCall( calleeDecl, callee, returnValue, thisPtr.getPointer(), /*ImplicitParam=*/nullptr, QualType(), ce, rtlArgs); } RValue CIRGenFunction::emitCXXOperatorMemberCallExpr(const CXXOperatorCallExpr *e, const CXXMethodDecl *md, ReturnValueSlot returnValue) { assert(md->isInstance() && "Trying to emit a member call expr on a static method!"); return emitCXXMemberOrOperatorMemberCallExpr( e, md, returnValue, /*HasQualifier=*/false, /*Qualifier=*/std::nullopt, /*IsArrow=*/false, e->getArg(0)); } RValue CIRGenFunction::emitCUDAKernelCallExpr(const CUDAKernelCallExpr *expr, ReturnValueSlot returnValue) { // Emit as a device kernel call if CUDA device code is to be generated. if (!getLangOpts().HIP && getLangOpts().CUDAIsDevice) cgm.errorNYI("CUDA Device side kernel call"); return cgm.getCUDARuntime().emitCUDAKernelCallExpr(*this, expr, returnValue); } RValue CIRGenFunction::emitCXXMemberOrOperatorCall( const CXXMethodDecl *md, const CIRGenCallee &callee, ReturnValueSlot returnValue, mlir::Value thisPtr, mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce, CallArgList *rtlArgs) { const auto *fpt = md->getType()->castAs(); CallArgList args; MemberCallInfo callInfo = commonBuildCXXMemberOrOperatorCall( *this, md, thisPtr, implicitParam, implicitParamTy, ce, args, rtlArgs); auto &fnInfo = cgm.getTypes().arrangeCXXMethodCall( args, fpt, callInfo.reqArgs, callInfo.prefixSize); assert((ce || currSrcLoc) && "expected source location"); mlir::Location loc = ce ? getLoc(ce->getExprLoc()) : *currSrcLoc; assert(!cir::MissingFeatures::opCallMustTail()); return emitCall(fnInfo, callee, returnValue, args, nullptr, loc); } static void emitNullBaseClassInitialization(CIRGenFunction &cgf, Address destPtr, const CXXRecordDecl *base) { if (base->isEmpty()) return; const ASTRecordLayout &layout = cgf.getContext().getASTRecordLayout(base); CharUnits nvSize = layout.getNonVirtualSize(); // We cannot simply zero-initialize the entire base sub-object if vbptrs are // present, they are initialized by the most derived class before calling the // constructor. SmallVector, 1> stores; stores.emplace_back(CharUnits::Zero(), nvSize); // Each store is split by the existence of a vbptr. // TODO(cir): This only needs handling for the MS CXXABI. assert(!cir::MissingFeatures::msabi()); // If the type contains a pointer to data member we can't memset it to zero. // Instead, create a null constant and copy it to the destination. // TODO: there are other patterns besides zero that we can usefully memset, // like -1, which happens to be the pattern used by member-pointers. // TODO: isZeroInitializable can be over-conservative in the case where a // virtual base contains a member pointer. mlir::TypedAttr nullConstantForBase = cgf.cgm.emitNullConstantForBase(base); if (!cgf.getBuilder().isNullValue(nullConstantForBase)) { cgf.cgm.errorNYI( base->getSourceRange(), "emitNullBaseClassInitialization: base constant is not null"); } else { // Otherwise, just memset the whole thing to zero. This is legal // because in LLVM, all default initializers (other than the ones we just // handled above) are guaranteed to have a bit pattern of all zeros. // TODO(cir): When the MS CXXABI is supported, we will need to iterate over // `stores` and create a separate memset for each one. For now, we know that // there will only be one store and it will begin at offset zero, so that // simplifies this code considerably. assert(stores.size() == 1 && "Expected only one store"); assert(stores[0].first == CharUnits::Zero() && "Expected store to begin at offset zero"); CIRGenBuilderTy builder = cgf.getBuilder(); mlir::Location loc = cgf.getLoc(base->getBeginLoc()); builder.createStore(loc, builder.getConstant(loc, nullConstantForBase), destPtr); } } void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e, AggValueSlot dest) { assert(!dest.isIgnored() && "Must have a destination!"); const CXXConstructorDecl *cd = e->getConstructor(); // If we require zero initialization before (or instead of) calling the // constructor, as can be the case with a non-user-provided default // constructor, emit the zero initialization now, unless destination is // already zeroed. if (e->requiresZeroInitialization() && !dest.isZeroed()) { switch (e->getConstructionKind()) { case CXXConstructionKind::Delegating: case CXXConstructionKind::Complete: emitNullInitialization(getLoc(e->getSourceRange()), dest.getAddress(), e->getType()); break; case CXXConstructionKind::VirtualBase: case CXXConstructionKind::NonVirtualBase: emitNullBaseClassInitialization(*this, dest.getAddress(), cd->getParent()); break; } } // If this is a call to a trivial default constructor, do nothing. if (cd->isTrivial() && cd->isDefaultConstructor()) return; // Elide the constructor if we're constructing from a temporary if (getLangOpts().ElideConstructors && e->isElidable()) { // FIXME: This only handles the simplest case, where the source object is // passed directly as the first argument to the constructor. This // should also handle stepping through implicit casts and conversion // sequences which involve two steps, with a conversion operator // follwed by a converting constructor. const Expr *srcObj = e->getArg(0); assert(srcObj->isTemporaryObject(getContext(), cd->getParent())); assert( getContext().hasSameUnqualifiedType(e->getType(), srcObj->getType())); emitAggExpr(srcObj, dest); return; } if (const ArrayType *arrayType = getContext().getAsArrayType(e->getType())) { assert(!cir::MissingFeatures::sanitizers()); emitCXXAggrConstructorCall(cd, arrayType, dest.getAddress(), e, false); } else { clang::CXXCtorType type = Ctor_Complete; bool forVirtualBase = false; bool delegating = false; switch (e->getConstructionKind()) { case CXXConstructionKind::Complete: type = Ctor_Complete; break; case CXXConstructionKind::Delegating: // We should be emitting a constructor; GlobalDecl will assert this type = curGD.getCtorType(); delegating = true; break; case CXXConstructionKind::VirtualBase: forVirtualBase = true; [[fallthrough]]; case CXXConstructionKind::NonVirtualBase: type = Ctor_Base; break; } emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e); } } static CharUnits calculateCookiePadding(CIRGenFunction &cgf, const CXXNewExpr *e) { if (!e->isArray()) return CharUnits::Zero(); // No cookie is required if the operator new[] being used is the // reserved placement operator new[]. if (e->getOperatorNew()->isReservedGlobalPlacementOperator()) return CharUnits::Zero(); return cgf.cgm.getCXXABI().getArrayCookieSize(e); } static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e, unsigned minElements, mlir::Value &numElements, mlir::Value &sizeWithoutCookie) { QualType type = e->getAllocatedType(); mlir::Location loc = cgf.getLoc(e->getSourceRange()); if (!e->isArray()) { CharUnits typeSize = cgf.getContext().getTypeSizeInChars(type); sizeWithoutCookie = cgf.getBuilder().getConstant( loc, cir::IntAttr::get(cgf.sizeTy, typeSize.getQuantity())); return sizeWithoutCookie; } // The width of size_t. unsigned sizeWidth = cgf.cgm.getDataLayout().getTypeSizeInBits(cgf.sizeTy); // The number of elements can be have an arbitrary integer type; // essentially, we need to multiply it by a constant factor, add a // cookie size, and verify that the result is representable as a // size_t. That's just a gloss, though, and it's wrong in one // important way: if the count is negative, it's an error even if // the cookie size would bring the total size >= 0. // // If the array size is constant, Sema will have prevented negative // values and size overflow. // Compute the constant factor. llvm::APInt arraySizeMultiplier(sizeWidth, 1); while (const ConstantArrayType *cat = cgf.getContext().getAsConstantArrayType(type)) { type = cat->getElementType(); arraySizeMultiplier *= cat->getSize(); } CharUnits typeSize = cgf.getContext().getTypeSizeInChars(type); llvm::APInt typeSizeMultiplier(sizeWidth, typeSize.getQuantity()); typeSizeMultiplier *= arraySizeMultiplier; // Figure out the cookie size. llvm::APInt cookieSize(sizeWidth, calculateCookiePadding(cgf, e).getQuantity()); // This will be a size_t. mlir::Value size; // Emit the array size expression. // We multiply the size of all dimensions for NumElements. // e.g for 'int[2][3]', ElemType is 'int' and NumElements is 6. const Expr *arraySize = *e->getArraySize(); mlir::Attribute constNumElements = ConstantEmitter(cgf.cgm, &cgf) .tryEmitAbstract(arraySize, arraySize->getType()); if (constNumElements) { // Get an APInt from the constant const llvm::APInt &count = mlir::cast(constNumElements).getValue(); [[maybe_unused]] unsigned numElementsWidth = count.getBitWidth(); bool hasAnyOverflow = false; // The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as // overflow, but that should never happen. The size argument is implicitly // cast to a size_t, so it can never be negative and numElementsWidth will // always equal sizeWidth. assert(!count.isNegative() && "Expected non-negative array size"); assert(numElementsWidth == sizeWidth && "Expected a size_t array size constant"); // Okay, compute a count at the right width. llvm::APInt adjustedCount = count.zextOrTrunc(sizeWidth); // Scale numElements by that. This might overflow, but we don't // care because it only overflows if allocationSize does too, and // if that overflows then we shouldn't use this. // This emits a constant that may not be used, but we can't tell here // whether it will be needed or not. numElements = cgf.getBuilder().getConstInt(loc, adjustedCount * arraySizeMultiplier); // Compute the size before cookie, and track whether it overflowed. bool overflow; llvm::APInt allocationSize = adjustedCount.umul_ov(typeSizeMultiplier, overflow); // Sema prevents us from hitting this case assert(!overflow && "Overflow in array allocation size"); // Add in the cookie, and check whether it's overflowed. if (cookieSize != 0) { // Save the current size without a cookie. This shouldn't be // used if there was overflow sizeWithoutCookie = cgf.getBuilder().getConstInt( loc, allocationSize.zextOrTrunc(sizeWidth)); allocationSize = allocationSize.uadd_ov(cookieSize, overflow); hasAnyOverflow |= overflow; } // On overflow, produce a -1 so operator new will fail if (hasAnyOverflow) { size = cgf.getBuilder().getConstInt(loc, llvm::APInt::getAllOnes(sizeWidth)); } else { size = cgf.getBuilder().getConstInt(loc, allocationSize); } } else { // Create a value for the variable number of elements numElements = cgf.emitScalarExpr(*e->getArraySize()); auto numElementsType = mlir::cast(numElements.getType()); unsigned numElementsWidth = numElementsType.getWidth(); // The number of elements can have an arbitrary integer type; // essentially, we need to multiply it by a constant factor, add a // cookie size, and verify that the result is representable as a // size_t. That's just a gloss, though, and it's wrong in one // important way: if the count is negative, it's an error even if // the cookie size would bring the total size >= 0. bool isSigned = (*e->getArraySize())->getType()->isSignedIntegerOrEnumerationType(); // There are up to five conditions we need to test for: // 1) if isSigned, we need to check whether numElements is negative; // 2) if numElementsWidth > sizeWidth, we need to check whether // numElements is larger than something representable in size_t; // 3) if minElements > 0, we need to check whether numElements is smaller // than that. // 4) we need to compute // sizeWithoutCookie := numElements * typeSizeMultiplier // and check whether it overflows; and // 5) if we need a cookie, we need to compute // size := sizeWithoutCookie + cookieSize // and check whether it overflows. mlir::Value hasOverflow; // If numElementsWidth > sizeWidth, then one way or another, we're // going to have to do a comparison for (2), and this happens to // take care of (1), too. if (numElementsWidth > sizeWidth) { llvm::APInt threshold = llvm::APInt::getOneBitSet(numElementsWidth, sizeWidth); // Use an unsigned comparison regardless of the sign of numElements. mlir::Value unsignedNumElements = numElements; if (isSigned) unsignedNumElements = cgf.getBuilder().createIntCast( numElements, cgf.getBuilder().getUIntNTy(numElementsWidth)); mlir::Value thresholdV = cgf.getBuilder().getConstInt(loc, threshold, /*isUnsigned=*/true); hasOverflow = cgf.getBuilder().createCompare( loc, cir::CmpOpKind::ge, unsignedNumElements, thresholdV); numElements = cgf.getBuilder().createIntCast( unsignedNumElements, mlir::cast(cgf.sizeTy)); // Otherwise, if we're signed, we want to sext up to size_t. } else if (isSigned) { if (numElementsWidth < sizeWidth) numElements = cgf.getBuilder().createIntCast( numElements, cgf.getBuilder().getSIntNTy(sizeWidth)); // If there's a non-1 type size multiplier, then we can do the // signedness check at the same time as we do the multiply // because a negative number times anything will cause an // unsigned overflow. Otherwise, we have to do it here. But at // least in this case, we can subsume the >= minElements check. if (typeSizeMultiplier == 1) hasOverflow = cgf.getBuilder().createCompare( loc, cir::CmpOpKind::lt, numElements, cgf.getBuilder().getConstInt(loc, numElements.getType(), minElements)); numElements = cgf.getBuilder().createIntCast( numElements, mlir::cast(cgf.sizeTy)); // Otherwise, zext up to size_t if necessary. } else if (numElementsWidth < sizeWidth) { numElements = cgf.getBuilder().createIntCast( numElements, mlir::cast(cgf.sizeTy)); } assert(numElements.getType() == cgf.sizeTy); if (minElements) { // Don't allow allocation of fewer elements than we have initializers. if (!hasOverflow) { mlir::Value minElementsV = cgf.getBuilder().getConstInt( loc, llvm::APInt(sizeWidth, minElements)); hasOverflow = cgf.getBuilder().createCompare(loc, cir::CmpOpKind::lt, numElements, minElementsV); } else if (numElementsWidth > sizeWidth) { // The other existing overflow subsumes this check. // We do an unsigned comparison, since any signed value < -1 is // taken care of either above or below. mlir::Value minElementsV = cgf.getBuilder().getConstInt( loc, llvm::APInt(sizeWidth, minElements)); hasOverflow = cgf.getBuilder().createOr( loc, hasOverflow, cgf.getBuilder().createCompare(loc, cir::CmpOpKind::lt, numElements, minElementsV)); } } size = numElements; // Multiply by the type size if necessary. This multiplier // includes all the factors for nested arrays. // // This step also causes numElements to be scaled up by the // nested-array factor if necessary. Overflow on this computation // can be ignored because the result shouldn't be used if // allocation fails. if (typeSizeMultiplier != 1) { mlir::Value tsmV = cgf.getBuilder().getConstInt(loc, typeSizeMultiplier); auto mulOp = cir::MulOverflowOp::create( cgf.getBuilder(), loc, mlir::cast(cgf.sizeTy), size, tsmV); if (hasOverflow) hasOverflow = cgf.getBuilder().createOr(loc, hasOverflow, mulOp.getOverflow()); else hasOverflow = mulOp.getOverflow(); size = mulOp.getResult(); // Also scale up numElements by the array size multiplier. if (arraySizeMultiplier != 1) { // If the base element type size is 1, then we can re-use the // multiply we just did. if (typeSize.isOne()) { assert(arraySizeMultiplier == typeSizeMultiplier); numElements = size; // Otherwise we need a separate multiply. } else { mlir::Value asmV = cgf.getBuilder().getConstInt(loc, arraySizeMultiplier); numElements = cgf.getBuilder().createMul(loc, numElements, asmV); } } } else { // numElements doesn't need to be scaled. assert(arraySizeMultiplier == 1); } // Add in the cookie size if necessary. if (cookieSize != 0) { sizeWithoutCookie = size; mlir::Value cookieSizeV = cgf.getBuilder().getConstInt(loc, cookieSize); auto addOp = cir::AddOverflowOp::create( cgf.getBuilder(), loc, mlir::cast(cgf.sizeTy), size, cookieSizeV); if (hasOverflow) hasOverflow = cgf.getBuilder().createOr(loc, hasOverflow, addOp.getOverflow()); else hasOverflow = addOp.getOverflow(); size = addOp.getResult(); } // If we had any possibility of dynamic overflow, make a select to // overwrite 'size' with an all-ones value, which should cause // operator new to throw. if (hasOverflow) { mlir::Value allOnes = cgf.getBuilder().getConstInt(loc, llvm::APInt::getAllOnes(sizeWidth)); size = cgf.getBuilder().createSelect(loc, hasOverflow, allOnes, size); } } if (cookieSize == 0) sizeWithoutCookie = size; else assert(sizeWithoutCookie && "didn't set sizeWithoutCookie?"); return size; } /// Emit a call to an operator new or operator delete function, as implicitly /// created by new-expressions and delete-expressions. static RValue emitNewDeleteCall(CIRGenFunction &cgf, const FunctionDecl *calleeDecl, const FunctionProtoType *calleeType, const CallArgList &args) { cir::CIRCallOpInterface callOrTryCall; cir::FuncOp calleePtr = cgf.cgm.getAddrOfFunction(calleeDecl); CIRGenCallee callee = CIRGenCallee::forDirect(calleePtr, GlobalDecl(calleeDecl)); RValue rv = cgf.emitCall(cgf.cgm.getTypes().arrangeFreeFunctionCall(args, calleeType), callee, ReturnValueSlot(), args, &callOrTryCall); /// C++1y [expr.new]p10: /// [In a new-expression,] an implementation is allowed to omit a call /// to a replaceable global allocation function. /// /// We model such elidable calls with the 'builtin' attribute. if (calleeDecl->isReplaceableGlobalAllocationFunction() && calleePtr && calleePtr->hasAttr(cir::CIRDialect::getNoBuiltinAttrName())) { callOrTryCall->setAttr(cir::CIRDialect::getBuiltinAttrName(), mlir::UnitAttr::get(callOrTryCall->getContext())); } return rv; } RValue CIRGenFunction::emitNewOrDeleteBuiltinCall(const FunctionProtoType *type, const CallExpr *callExpr, OverloadedOperatorKind op) { CallArgList args; emitCallArgs(args, type, callExpr->arguments()); // Find the allocation or deallocation function that we're calling. ASTContext &astContext = getContext(); assert(op == OO_New || op == OO_Delete); DeclarationName name = astContext.DeclarationNames.getCXXOperatorName(op); clang::DeclContextLookupResult lookupResult = astContext.getTranslationUnitDecl()->lookup(name); for (const NamedDecl *decl : lookupResult) { if (const auto *funcDecl = dyn_cast(decl)) { if (astContext.hasSameType(funcDecl->getType().getTypePtr(), type)) { if (sanOpts.has(SanitizerKind::AllocToken)) { // TODO: Set !alloc_token metadata. assert(!cir::MissingFeatures::allocToken()); cgm.errorNYI("Alloc token sanitizer not yet supported!"); } // Emit the call to operator new/delete. return emitNewDeleteCall(*this, funcDecl, type, args); } } } llvm_unreachable("predeclared global operator new/delete is missing"); } namespace { template struct PlacementArg { typename Traits::RValueTy argValue; QualType argType; }; /// A cleanup to call the given 'operator delete' function upon abnormal /// exit from a new expression. Templated on a traits type that deals with /// ensuring that the arguments dominate the cleanup if necessary. template class CallDeleteDuringNew final : public EHScopeStack::Cleanup, private llvm::TrailingObjects, PlacementArg> { using TrailingObj = llvm::TrailingObjects, PlacementArg>; friend TrailingObj; using TrailingObj::getTrailingObjects; /// Type used to hold llvm::Value*s. typedef typename Traits::ValueTy ValueTy; /// Type used to hold RValues. typedef typename Traits::RValueTy RValueTy; unsigned numPlacementArgs : 30; LLVM_PREFERRED_TYPE(AlignedAllocationMode) unsigned passAlignmentToPlacementDelete : 1; const FunctionDecl *operatorDelete; ValueTy ptr; ValueTy allocSize; CharUnits allocAlign; PlacementArg *getPlacementArgs() { return getTrailingObjects(); } void setPlacementArg(unsigned i, RValueTy argValue, QualType argType) { assert(i < numPlacementArgs && "index out of range"); getPlacementArgs()[i] = {argValue, argType}; } public: static size_t getExtraSize(size_t numPlacementArgs) { return TrailingObj::template additionalSizeToAlloc>( numPlacementArgs); } CallDeleteDuringNew(size_t numPlacementArgs, const FunctionDecl *operatorDelete, ValueTy ptr, ValueTy allocSize, const ImplicitAllocationParameters &iap, CharUnits allocAlign, const CallArgList *newArgs, unsigned numNonPlacementArgs, CIRGenFunction *cgf, mlir::Location loc) : numPlacementArgs(numPlacementArgs), passAlignmentToPlacementDelete(isAlignedAllocation(iap.PassAlignment)), operatorDelete(operatorDelete), ptr(ptr), allocSize(allocSize), allocAlign(allocAlign) { for (unsigned i = 0, n = numPlacementArgs; i != n; ++i) { const CallArg &arg = (*newArgs)[i + numNonPlacementArgs]; setPlacementArg(i, arg.getRValue(*cgf, loc), arg.ty); } } void emit(CIRGenFunction &cgf, Flags flags) override { const auto *fpt = operatorDelete->getType()->castAs(); CallArgList deleteArgs; unsigned firstNonTypeArg = 0; TypeAwareAllocationMode typeAwareDeallocation = TypeAwareAllocationMode::No; assert(!cir::MissingFeatures::typeAwareAllocation()); // The first argument after type-identity parameter (if any) is always // a void* (or C* for a destroying operator delete for class type C). deleteArgs.add(Traits::get(cgf, ptr), fpt->getParamType(firstNonTypeArg)); // Figure out what other parameters we should be implicitly passing. UsualDeleteParams params; if (numPlacementArgs) { // A placement deallocation function is implicitly passed an alignment // if the placement allocation function was, but is never passed a size. params.Alignment = alignedAllocationModeFromBool(passAlignmentToPlacementDelete); params.TypeAwareDelete = typeAwareDeallocation; params.Size = isTypeAwareAllocation(params.TypeAwareDelete); } else { // For a non-placement new-expression, 'operator delete' can take a // size and/or an alignment if it has the right parameters. params = operatorDelete->getUsualDeleteParams(); } assert(!params.DestroyingDelete && "should not call destroying delete in a new-expression"); // The second argument can be a std::size_t (for non-placement delete). if (params.Size) deleteArgs.add(Traits::get(cgf, allocSize), cgf.getContext().getSizeType()); // The next (second or third) argument can be a std::align_val_t, which // is an enum whose underlying type is std::size_t. // FIXME: Use the right type as the parameter type. Note that in a call // to operator delete(size_t, ...), we may not have it available. if (isAlignedAllocation(params.Alignment)) cgf.cgm.errorNYI("CallDeleteDuringNew: aligned allocation"); // Pass the rest of the arguments, which must match exactly. for (unsigned i = 0; i != numPlacementArgs; ++i) { auto arg = getPlacementArgs()[i]; deleteArgs.add(Traits::get(cgf, arg.argValue), arg.argType); } // Call 'operator delete'. emitNewDeleteCall(cgf, operatorDelete, fpt, deleteArgs); } }; } // namespace /// Enter a cleanup to call 'operator delete' if the initializer in a /// new-expression throws. static void enterNewDeleteCleanup(CIRGenFunction &cgf, const CXXNewExpr *e, Address newPtr, mlir::Value allocSize, CharUnits allocAlign, const CallArgList &newArgs) { unsigned numNonPlacementArgs = e->getNumImplicitArgs(); // If we're not inside a conditional branch, then the cleanup will // dominate and we can do the easier (and more efficient) thing. if (!cgf.isInConditionalBranch()) { struct DirectCleanupTraits { typedef mlir::Value ValueTy; typedef RValue RValueTy; static RValue get(CIRGenFunction &, ValueTy v) { return RValue::get(v); } static RValue get(CIRGenFunction &, RValueTy v) { return v; } }; typedef CallDeleteDuringNew DirectCleanup; assert(!cir::MissingFeatures::typeAwareAllocation()); cgf.ehStack.pushCleanupWithExtra( EHCleanup, e->getNumPlacementArgs(), e->getOperatorDelete(), newPtr.getPointer(), allocSize, e->implicitAllocationParameters(), allocAlign, &newArgs, numNonPlacementArgs, &cgf, cgf.getLoc(e->getSourceRange())); return; } cgf.cgm.errorNYI(e->getSourceRange(), "enterNewDeleteCleanup: conditional branch"); } static void storeAnyExprIntoOneUnit(CIRGenFunction &cgf, const Expr *init, QualType allocType, Address newPtr, AggValueSlot::Overlap_t mayOverlap) { // FIXME: Refactor with emitExprAsInit. switch (cgf.getEvaluationKind(allocType)) { case cir::TEK_Scalar: cgf.emitScalarInit(init, cgf.getLoc(init->getSourceRange()), cgf.makeAddrLValue(newPtr, allocType), false); return; case cir::TEK_Complex: cgf.emitComplexExprIntoLValue(init, cgf.makeAddrLValue(newPtr, allocType), /*isInit*/ true); return; case cir::TEK_Aggregate: { assert(!cir::MissingFeatures::aggValueSlotGC()); assert(!cir::MissingFeatures::sanitizers()); AggValueSlot slot = AggValueSlot::forAddr( newPtr, allocType.getQualifiers(), AggValueSlot::IsDestructed, AggValueSlot::IsNotAliased, mayOverlap, AggValueSlot::IsNotZeroed); cgf.emitAggExpr(init, slot); return; } } llvm_unreachable("bad evaluation kind"); } void CIRGenFunction::emitNewArrayInitializer( const CXXNewExpr *e, QualType elementType, mlir::Type elementTy, Address beginPtr, mlir::Value numElements, mlir::Value allocSizeWithoutCookie) { // If we have a type with trivial initialization and no initializer, // there's nothing to do. if (!e->hasInitializer()) return; Address curPtr = beginPtr; unsigned initListElements = 0; const Expr *init = e->getInitializer(); Address endOfInit = Address::invalid(); QualType::DestructionKind dtorKind = elementType.isDestructedType(); assert(!cir::MissingFeatures::cleanupDeactivationScope()); // Attempt to perform zero-initialization using memset. auto tryMemsetInitialization = [&]() -> bool { mlir::Location loc = numElements.getLoc(); // FIXME: If the type is a pointer-to-data-member under the Itanium ABI, // we can initialize with a memset to -1. if (!cgm.getTypes().isZeroInitializable(elementType)) return false; // Optimization: since zero initialization will just set the memory // to all zeroes, generate a single memset to do it in one shot. // Subtract out the size of any elements we've already initialized. auto remainingSize = allocSizeWithoutCookie; if (initListElements) { // We know this can't overflow; we check this when doing the allocation. unsigned initializedSize = getContext().getTypeSizeInChars(elementType).getQuantity() * initListElements; cir::ConstantOp initSizeOp = builder.getConstInt(loc, remainingSize.getType(), initializedSize); remainingSize = builder.createSub(loc, remainingSize, initSizeOp); } // Create the memset. mlir::Value castOp = builder.createPtrBitcast(curPtr.getPointer(), cgm.voidTy); builder.createMemSet(loc, castOp, builder.getConstInt(loc, cgm.uInt8Ty, 0), remainingSize); return true; }; const InitListExpr *ile = dyn_cast(init); const CXXParenListInitExpr *cplie = nullptr; const StringLiteral *sl = nullptr; const ObjCEncodeExpr *ocee = nullptr; const Expr *ignoreParen = nullptr; if (!ile) { ignoreParen = init->IgnoreParenImpCasts(); cplie = dyn_cast(ignoreParen); sl = dyn_cast(ignoreParen); ocee = dyn_cast(ignoreParen); } // If the initializer is an initializer list, first do the explicit elements. if (ile || cplie || sl || ocee) { // Initializing from a (braced) string literal is a special case; the init // list element does not initialize a (single) array element. if ((ile && ile->isStringLiteralInit()) || sl || ocee) { cgm.errorNYI(ile->getSourceRange(), "emitNewArrayInitializer: string literal init"); return; } ArrayRef initExprs = ile ? ile->inits() : cplie->getInitExprs(); initListElements = initExprs.size(); // If this is a multi-dimensional array new, we will initialize multiple // elements with each init list element. QualType allocType = e->getAllocatedType(); if (const ConstantArrayType *cat = dyn_cast_or_null( allocType->getAsArrayTypeUnsafe())) { (void)cat; cgm.errorNYI(ile->getSourceRange(), "emitNewArrayInitializer: constant array init"); return; } // Enter a partial-destruction Cleanup if necessary. if (dtorKind) { cgm.errorNYI(ile->getSourceRange(), "emitNewArrayInitializer: init requires dtor"); return; } CharUnits elementSize = getContext().getTypeSizeInChars(elementType); CharUnits startAlign = curPtr.getAlignment(); unsigned i = 0; for (const Expr *ie : initExprs) { // Tell the cleanup that it needs to destroy up to this // element. TODO: some of these stores can be trivially // observed to be unnecessary. if (endOfInit.isValid()) { cgm.errorNYI(ie->getSourceRange(), "emitNewArrayInitializer: update dtor cleanup ptr"); return; } // FIXME: If the last initializer is an incomplete initializer list for // an array, and we have an array filler, we can fold together the two // initialization loops. storeAnyExprIntoOneUnit(*this, ie, ie->getType(), curPtr, AggValueSlot::DoesNotOverlap); mlir::Location loc = getLoc(ie->getExprLoc()); mlir::Value castOp = builder.createPtrBitcast( curPtr.getPointer(), convertTypeForMem(allocType)); mlir::Value offsetOp = builder.getSignedInt(loc, 1, /*width=*/32); mlir::Value dataPtr = builder.createPtrStride(loc, castOp, offsetOp); curPtr = Address(dataPtr, curPtr.getElementType(), startAlign.alignmentAtOffset((++i) * elementSize)); } // The remaining elements are filled with the array filler expression. init = ile ? ile->getArrayFiller() : cplie->getArrayFiller(); // Extract the initializer for the individual array elements by pulling // out the array filler from all the nested initializer lists. This avoids // generating a nested loop for the initialization. while (init && init->getType()->isConstantArrayType()) { auto *subIle = dyn_cast(init); if (!subIle) break; assert(subIle->getNumInits() == 0 && "explicit inits in array filler?"); init = subIle->getArrayFiller(); } // Switch back to initializing one base element at a time. curPtr = curPtr.withElementType(builder, beginPtr.getElementType()); } // If all elements have already been initialized, skip any further // initialization. auto constOp = mlir::dyn_cast(numElements.getDefiningOp()); if (constOp) { auto constIntAttr = mlir::dyn_cast(constOp.getValue()); // Just skip out if the constant count is zero. if (constIntAttr && constIntAttr.getUInt() <= initListElements) return; } assert(init && "have trailing elements to initialize but no initializer"); // If this is a constructor call, try to optimize it out, and failing that // emit a single loop to initialize all remaining elements. if (const CXXConstructExpr *cce = dyn_cast(init)) { CXXConstructorDecl *ctor = cce->getConstructor(); if (ctor->isTrivial()) { // If new expression did not specify value-initialization, then there // is no initialization. if (!cce->requiresZeroInitialization()) return; cgm.errorNYI(cce->getSourceRange(), "emitNewArrayInitializer: trivial ctor zero-init"); return; } // Store the new Cleanup position for irregular Cleanups. // // FIXME: Share this cleanup with the constructor call emission rather than // having it create a cleanup of its own. if (endOfInit.isValid()) builder.createStore(getLoc(e->getSourceRange()), curPtr.emitRawPointer(), endOfInit); mlir::Type initType = convertType(cce->getType()); // Emit a constructor call loop to initialize the remaining elements. if (initListElements) { // If the number of elements is a constant, we will have already gotten // the constant op above. Here we use it to get the number of remaining // elements as a new constant. if (constOp) { auto constIntAttr = mlir::cast(constOp.getValue()); uint64_t numRemainingElements = constIntAttr.getUInt() - initListElements; numElements = builder.getConstInt(getLoc(e->getSourceRange()), numElements.getType(), numRemainingElements); // Currently, the AST gives us a pointer to the element type here // rather than an array. That's inconsistent with what it does // without an explicit initializer list, so we need to create an // array type here. That will decay back to a pointer when we lower // the cir.array.ctor op, but we need an array type for the initial // representation. if (!mlir::isa(initType)) initType = cir::ArrayType::get(initType, numRemainingElements); } else { cgm.errorNYI(e->getSourceRange(), "emitNewArrayInitializer: numRemainingElements with " "non-constant count"); return; } } curPtr = curPtr.withElementType(builder, initType); emitCXXAggrConstructorCall(ctor, numElements, curPtr, cce, /*newPointerIsChecked=*/true, cce->requiresZeroInitialization()); if (getContext().getTargetInfo().emitVectorDeletingDtors( getContext().getLangOpts())) { cgm.errorNYI(e->getSourceRange(), "emitNewArrayInitializer: emitVectorDeletingDtors"); } return; } // If this is value-initialization, we can usually use memset. if (isa(init)) { if (tryMemsetInitialization()) return; cgm.errorNYI(init->getSourceRange(), "emitNewArrayInitializer: implicit value init"); return; } cgm.errorNYI(init->getSourceRange(), "emitNewArrayInitializer: unsupported initializer"); return; } static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e, QualType elementType, mlir::Type elementTy, Address newPtr, mlir::Value numElements, mlir::Value allocSizeWithoutCookie) { assert(!cir::MissingFeatures::generateDebugInfo()); if (e->isArray()) { cgf.emitNewArrayInitializer(e, elementType, elementTy, newPtr, numElements, allocSizeWithoutCookie); } else if (const Expr *init = e->getInitializer()) { storeAnyExprIntoOneUnit(cgf, init, e->getAllocatedType(), newPtr, AggValueSlot::DoesNotOverlap); } } RValue CIRGenFunction::emitCXXDestructorCall( GlobalDecl dtor, const CIRGenCallee &callee, mlir::Value thisVal, QualType thisTy, mlir::Value implicitParam, QualType implicitParamTy, const CallExpr *ce) { const CXXMethodDecl *dtorDecl = cast(dtor.getDecl()); assert(!thisTy.isNull()); assert(thisTy->getAsCXXRecordDecl() == dtorDecl->getParent() && "Pointer/Object mixup"); assert(!cir::MissingFeatures::addressSpace()); CallArgList args; commonBuildCXXMemberOrOperatorCall(*this, dtorDecl, thisVal, implicitParam, implicitParamTy, ce, args, nullptr); assert((ce || dtor.getDecl()) && "expected source location provider"); assert(!cir::MissingFeatures::opCallMustTail()); return emitCall(cgm.getTypes().arrangeCXXStructorDeclaration(dtor), callee, ReturnValueSlot(), args, nullptr, ce ? getLoc(ce->getExprLoc()) : getLoc(dtor.getDecl()->getSourceRange())); } RValue CIRGenFunction::emitCXXPseudoDestructorExpr( const CXXPseudoDestructorExpr *expr) { QualType destroyedType = expr->getDestroyedType(); if (destroyedType.hasStrongOrWeakObjCLifetime()) { assert(!cir::MissingFeatures::objCLifetime()); cgm.errorNYI(expr->getExprLoc(), "emitCXXPseudoDestructorExpr: Objective-C lifetime is NYI"); } else { // C++ [expr.pseudo]p1: // The result shall only be used as the operand for the function call // operator (), and the result of such a call has type void. The only // effect is the evaluation of the postfix-expression before the dot or // arrow. emitIgnoredExpr(expr->getBase()); } return RValue::get(nullptr); } namespace { /// Calls the given 'operator delete' on a single object. struct CallObjectDelete final : EHScopeStack::Cleanup { mlir::Value ptr; const FunctionDecl *operatorDelete; QualType elementType; CallObjectDelete(mlir::Value ptr, const FunctionDecl *operatorDelete, QualType elementType) : ptr(ptr), operatorDelete(operatorDelete), elementType(elementType) {} void emit(CIRGenFunction &cgf, Flags flags) override { cgf.emitDeleteCall(operatorDelete, ptr, elementType); } }; } // namespace /// Emit the code for deleting a single object. static void emitObjectDelete(CIRGenFunction &cgf, const CXXDeleteExpr *de, Address ptr, QualType elementType) { // C++11 [expr.delete]p3: // If the static type of the object to be deleted is different from its // dynamic type, the static type shall be a base class of the dynamic type // of the object to be deleted and the static type shall have a virtual // destructor or the behavior is undefined. assert(!cir::MissingFeatures::emitTypeCheck()); const FunctionDecl *operatorDelete = de->getOperatorDelete(); assert(!operatorDelete->isDestroyingOperatorDelete()); // Find the destructor for the type, if applicable. If the // destructor is virtual, we'll just emit the vcall and return. const CXXDestructorDecl *dtor = nullptr; if (const auto *rd = elementType->getAsCXXRecordDecl()) { if (rd->hasDefinition() && !rd->hasTrivialDestructor()) { dtor = rd->getDestructor(); if (dtor->isVirtual()) { assert(!cir::MissingFeatures::devirtualizeDestructor()); cgf.cgm.getCXXABI().emitVirtualObjectDelete(cgf, de, ptr, elementType, dtor); return; } } } // Make sure that we call delete even if the dtor throws. // This doesn't have to a conditional cleanup because we're going // to pop it off in a second. cgf.ehStack.pushCleanup( NormalAndEHCleanup, ptr.getPointer(), operatorDelete, elementType); if (dtor) { cgf.emitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false, /*Delegating=*/false, ptr, elementType); } else if (elementType.getObjCLifetime()) { assert(!cir::MissingFeatures::objCLifetime()); cgf.cgm.errorNYI(de->getSourceRange(), "emitObjectDelete: ObjCLifetime"); } cgf.popCleanupBlock(); } void CIRGenFunction::emitCXXDeleteExpr(const CXXDeleteExpr *e) { const Expr *arg = e->getArgument(); Address ptr = emitPointerWithAlignment(arg); // Null check the pointer. // // We could avoid this null check if we can determine that the object // destruction is trivial and doesn't require an array cookie; we can // unconditionally perform the operator delete call in that case. For now, we // assume that deleted pointers are null rarely enough that it's better to // keep the branch. This might be worth revisiting for a -O0 code size win. assert(!cir::MissingFeatures::emitNullCheckForDeleteCalls()); cir::YieldOp thenYield; mlir::Value notNull = builder.createPtrIsNotNull(ptr.getPointer()); cir::IfOp::create(builder, getLoc(e->getExprLoc()), notNull, /*withElseRegion=*/false, /*thenBuilder=*/ [&](mlir::OpBuilder &b, mlir::Location loc) { thenYield = builder.createYield(loc); }); // Emit the rest of the CIR inside the if-op's then region, but restore the // insertion point to the point after the if when this function returns. mlir::OpBuilder::InsertionGuard guard(builder); builder.setInsertionPoint(thenYield); QualType deleteTy = e->getDestroyedType(); // A destroying operator delete overrides the entire operation of the // delete expression. if (e->getOperatorDelete()->isDestroyingOperatorDelete()) { cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: destroying operator delete"); return; } // We might be deleting a pointer to array. deleteTy = getContext().getBaseElementType(deleteTy); ptr = ptr.withElementType(builder, convertTypeForMem(deleteTy)); if (e->isArrayForm() && cgm.getASTContext().getTargetInfo().emitVectorDeletingDtors( cgm.getASTContext().getLangOpts())) { cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: emitVectorDeletingDtors"); } if (e->isArrayForm()) { const FunctionDecl *operatorDelete = e->getOperatorDelete(); cir::FuncOp operatorDeleteFn = cgm.getAddrOfFunction(operatorDelete); auto deleteFn = mlir::FlatSymbolRefAttr::get(operatorDeleteFn.getSymNameAttr()); UsualDeleteParams udp = operatorDelete->getUsualDeleteParams(); auto deleteParams = cir::UsualDeleteParamsAttr::get( builder.getContext(), udp.Size, isAlignedAllocation(udp.Alignment), isTypeAwareAllocation(udp.TypeAwareDelete), udp.DestroyingDelete); mlir::FlatSymbolRefAttr elementDtor; if (const auto *rd = deleteTy->getAsCXXRecordDecl()) { if (rd->hasDefinition() && !rd->hasTrivialDestructor()) { const CXXDestructorDecl *dtor = rd->getDestructor(); if (dtor->getType()->castAs()->canThrow()) cgm.errorNYI(e->getSourceRange(), "emitCXXDeleteExpr: throwing destructor"); cir::FuncOp dtorFn = cgm.getAddrOfCXXStructor(GlobalDecl(dtor, Dtor_Complete)); elementDtor = mlir::FlatSymbolRefAttr::get(builder.getContext(), dtorFn.getSymNameAttr()); } } cir::DeleteArrayOp::create(builder, ptr.getPointer().getLoc(), ptr.getPointer(), deleteFn, deleteParams, elementDtor); } else { emitObjectDelete(*this, e, ptr, deleteTy); } } mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) { // The element type being allocated. QualType allocType = getContext().getBaseElementType(e->getAllocatedType()); // 1. Build a call to the allocation function. FunctionDecl *allocator = e->getOperatorNew(); // If there is a brace-initializer, cannot allocate fewer elements than inits. unsigned minElements = 0; if (e->isArray() && e->hasInitializer()) { const InitListExpr *ile = dyn_cast(e->getInitializer()); if (ile && ile->isStringLiteralInit()) minElements = cast(ile->getType()->getAsArrayTypeUnsafe()) ->getSize() .getZExtValue(); else if (ile) minElements = ile->getNumInits(); } mlir::Value numElements = nullptr; mlir::Value allocSizeWithoutCookie = nullptr; mlir::Value allocSize = emitCXXNewAllocSize( *this, e, minElements, numElements, allocSizeWithoutCookie); CharUnits allocAlign = getContext().getTypeAlignInChars(allocType); // Emit the allocation call. Address allocation = Address::invalid(); CallArgList allocatorArgs; if (allocator->isReservedGlobalPlacementOperator()) { // If the allocator is a global placement operator, just // "inline" it directly. assert(e->getNumPlacementArgs() == 1); const Expr *arg = *e->placement_arguments().begin(); LValueBaseInfo baseInfo; allocation = emitPointerWithAlignment(arg, &baseInfo); // The pointer expression will, in many cases, be an opaque void*. // In these cases, discard the computed alignment and use the // formal alignment of the allocated type. if (baseInfo.getAlignmentSource() != AlignmentSource::Decl) allocation = allocation.withAlignment(allocAlign); // Set up allocatorArgs for the call to operator delete if it's not // the reserved global operator. if (e->getOperatorDelete() && !e->getOperatorDelete()->isReservedGlobalPlacementOperator()) { cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: reserved placement new with delete"); } } else { const FunctionProtoType *allocatorType = allocator->getType()->castAs(); unsigned paramsToSkip = 0; // The allocation size is the first argument. QualType sizeType = getContext().getSizeType(); allocatorArgs.add(RValue::get(allocSize), sizeType); ++paramsToSkip; if (allocSize != allocSizeWithoutCookie) { CharUnits cookieAlign = getSizeAlign(); // FIXME: Ask the ABI. allocAlign = std::max(allocAlign, cookieAlign); } // The allocation alignment may be passed as the second argument. if (e->passAlignment()) { cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: pass alignment"); } // FIXME: Why do we not pass a CalleeDecl here? emitCallArgs(allocatorArgs, allocatorType, e->placement_arguments(), AbstractCallee(), paramsToSkip); RValue rv = emitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs); // Set !heapallocsite metadata on the call to operator new. assert(!cir::MissingFeatures::generateDebugInfo()); // If this was a call to a global replaceable allocation function that does // not take an alignment argument, the allocator is known to produce storage // that's suitably aligned for any object that fits, up to a known // threshold. Otherwise assume it's suitably aligned for the allocated type. CharUnits allocationAlign = allocAlign; if (!e->passAlignment() && allocator->isReplaceableGlobalAllocationFunction()) { const TargetInfo &target = cgm.getASTContext().getTargetInfo(); unsigned allocatorAlign = llvm::bit_floor(std::min( target.getNewAlign(), getContext().getTypeSize(allocType))); allocationAlign = std::max( allocationAlign, getContext().toCharUnitsFromBits(allocatorAlign)); } mlir::Value allocPtr = rv.getValue(); allocation = Address( allocPtr, mlir::cast(allocPtr.getType()).getPointee(), allocationAlign); } // Emit a null check on the allocation result if the allocation // function is allowed to return null (because it has a non-throwing // exception spec or is the reserved placement new) and we have an // interesting initializer will be running sanitizers on the initialization. bool nullCheck = e->shouldNullCheckAllocation() && (!allocType.isPODType(getContext()) || e->hasInitializer()); assert(!cir::MissingFeatures::exprNewNullCheck()); if (nullCheck) cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: null check"); // If there's an operator delete, enter a cleanup to call it if an // exception is thrown. If we do this, we'll be creating the result pointer // inside a cleanup scope, either with a bitcast or an offset based on the // array cookie size. However, we need to return that pointer from outside // the cleanup scope, so we need to store it in a temporary variable. bool useNewDeleteCleanup = e->getOperatorDelete() && !e->getOperatorDelete()->isReservedGlobalPlacementOperator(); EHScopeStack::stable_iterator operatorDeleteCleanup; mlir::Operation *cleanupDominator = nullptr; if (useNewDeleteCleanup) { assert(!cir::MissingFeatures::typeAwareAllocation()); enterNewDeleteCleanup(*this, e, allocation, allocSize, allocAlign, allocatorArgs); operatorDeleteCleanup = ehStack.stable_begin(); cleanupDominator = cir::UnreachableOp::create(builder, getLoc(e->getSourceRange())) .getOperation(); } if (allocSize != allocSizeWithoutCookie) { assert(e->isArray()); allocation = cgm.getCXXABI().initializeArrayCookie( *this, allocation, numElements, e, allocType); } mlir::Type elementTy; if (e->isArray()) { // For array new, use the allocated type to handle multidimensional arrays // correctly elementTy = convertTypeForMem(e->getAllocatedType()); } else { elementTy = convertTypeForMem(allocType); } Address result = builder.createElementBitCast(getLoc(e->getSourceRange()), allocation, elementTy); // If we're inside a new delete cleanup, store the result pointer. Address resultPtr = Address::invalid(); if (useNewDeleteCleanup) { resultPtr = createTempAlloca(builder.getPointerTo(elementTy), result.getAlignment(), getLoc(e->getSourceRange()), "__new_result"); builder.createStore(getLoc(e->getSourceRange()), result.getPointer(), resultPtr); } // Passing pointer through launder.invariant.group to avoid propagation of // vptrs information which may be included in previous type. // To not break LTO with different optimizations levels, we do it regardless // of optimization level. if (cgm.getCodeGenOpts().StrictVTablePointers && allocator->isReservedGlobalPlacementOperator()) cgm.errorNYI(e->getSourceRange(), "emitCXXNewExpr: strict vtable pointers"); assert(!cir::MissingFeatures::sanitizers()); emitNewInitializer(*this, e, allocType, elementTy, result, numElements, allocSizeWithoutCookie); // Deactivate the 'operator delete' cleanup if we finished // initialization. if (useNewDeleteCleanup) { assert(operatorDeleteCleanup.isValid()); assert(resultPtr.isValid()); deactivateCleanupBlock(operatorDeleteCleanup, cleanupDominator); cleanupDominator->erase(); cir::LoadOp loadResult = builder.createLoad(getLoc(e->getSourceRange()), resultPtr); result = result.withPointer(loadResult.getResult()); } assert(!cir::MissingFeatures::exprNewNullCheck()); return result.getPointer(); } void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD, mlir::Value ptr, QualType deleteTy) { assert(!cir::MissingFeatures::deleteArray()); const auto *deleteFTy = deleteFD->getType()->castAs(); CallArgList deleteArgs; UsualDeleteParams params = deleteFD->getUsualDeleteParams(); auto paramTypeIt = deleteFTy->param_type_begin(); // Pass std::type_identity tag if present if (isTypeAwareAllocation(params.TypeAwareDelete)) cgm.errorNYI(deleteFD->getSourceRange(), "emitDeleteCall: type aware delete"); // Pass the pointer itself. QualType argTy = *paramTypeIt++; mlir::Value deletePtr = builder.createBitcast(ptr.getLoc(), ptr, convertType(argTy)); deleteArgs.add(RValue::get(deletePtr), argTy); // Pass the std::destroying_delete tag if present. if (params.DestroyingDelete) cgm.errorNYI(deleteFD->getSourceRange(), "emitDeleteCall: destroying delete"); // Pass the size if the delete function has a size_t parameter. if (params.Size) { QualType sizeType = *paramTypeIt++; CharUnits deleteTypeSize = getContext().getTypeSizeInChars(deleteTy); assert(mlir::isa(convertType(sizeType)) && "expected cir::IntType"); cir::ConstantOp size = builder.getConstInt( *currSrcLoc, convertType(sizeType), deleteTypeSize.getQuantity()); deleteArgs.add(RValue::get(size), sizeType); } // Pass the alignment if the delete function has an align_val_t parameter. if (isAlignedAllocation(params.Alignment)) cgm.errorNYI(deleteFD->getSourceRange(), "emitDeleteCall: aligned allocation"); assert(paramTypeIt == deleteFTy->param_type_end() && "unknown parameter to usual delete function"); // Emit the call to delete. emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs); } static mlir::Value emitDynamicCastToNull(CIRGenFunction &cgf, mlir::Location loc, QualType destTy) { mlir::Type destCIRTy = cgf.convertType(destTy); assert(mlir::isa(destCIRTy) && "result of dynamic_cast should be a ptr"); if (!destTy->isPointerType()) { mlir::Region *currentRegion = cgf.getBuilder().getBlock()->getParent(); /// C++ [expr.dynamic.cast]p9: /// A failed cast to reference type throws std::bad_cast cgf.cgm.getCXXABI().emitBadCastCall(cgf, loc); // The call to bad_cast will terminate the current block. Create a new block // to hold any follow up code. cgf.getBuilder().createBlock(currentRegion, currentRegion->end()); } return cgf.getBuilder().getNullPtr(destCIRTy, loc); } mlir::Value CIRGenFunction::emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce) { mlir::Location loc = getLoc(dce->getSourceRange()); cgm.emitExplicitCastExprType(dce, this); QualType destTy = dce->getTypeAsWritten(); QualType srcTy = dce->getSubExpr()->getType(); // C++ [expr.dynamic.cast]p7: // If T is "pointer to cv void," then the result is a pointer to the most // derived object pointed to by v. bool isDynCastToVoid = destTy->isVoidPointerType(); bool isRefCast = destTy->isReferenceType(); QualType srcRecordTy; QualType destRecordTy; if (isDynCastToVoid) { srcRecordTy = srcTy->getPointeeType(); // No destRecordTy. } else if (const PointerType *destPTy = destTy->getAs()) { srcRecordTy = srcTy->castAs()->getPointeeType(); destRecordTy = destPTy->getPointeeType(); } else { srcRecordTy = srcTy; destRecordTy = destTy->castAs()->getPointeeType(); } assert(srcRecordTy->isRecordType() && "source type must be a record type!"); assert(!cir::MissingFeatures::emitTypeCheck()); if (dce->isAlwaysNull()) return emitDynamicCastToNull(*this, loc, destTy); auto destCirTy = mlir::cast(convertType(destTy)); return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy, destCirTy, isRefCast, thisAddr); } static mlir::Value emitCXXTypeidFromVTable(CIRGenFunction &cgf, const Expr *e, mlir::Type typeInfoPtrTy, bool hasNullCheck) { Address thisPtr = cgf.emitLValue(e).getAddress(); QualType srcType = e->getType(); // C++ [class.cdtor]p4: // If the operand of typeid refers to the object under construction or // destruction and the static type of the operand is neither the constructor // or destructor’s class nor one of its bases, the behavior is undefined. assert(!cir::MissingFeatures::sanitizers()); if (hasNullCheck && cgf.cgm.getCXXABI().shouldTypeidBeNullChecked(srcType)) { mlir::Value isThisNull = cgf.getBuilder().createPtrIsNull(thisPtr.getPointer()); // We don't really care about the value, we just want to make sure the // 'true' side calls bad-type-id. cir::IfOp::create( cgf.getBuilder(), cgf.getLoc(e->getSourceRange()), isThisNull, /*withElseRegion=*/false, [&](mlir::OpBuilder &, mlir::Location loc) { cgf.cgm.getCXXABI().emitBadTypeidCall(cgf, loc); }); } return cgf.cgm.getCXXABI().emitTypeid(cgf, srcType, thisPtr, typeInfoPtrTy); } mlir::Value CIRGenFunction::emitCXXTypeidExpr(const CXXTypeidExpr *e) { mlir::Location loc = getLoc(e->getSourceRange()); mlir::Type resultType = cir::PointerType::get(convertType(e->getType())); QualType ty = e->isTypeOperand() ? e->getTypeOperand(getContext()) : e->getExprOperand()->getType(); // If the non-default global var address space is not default, we need to do // an address-space cast here. assert(!cir::MissingFeatures::addressSpace()); // C++ [expr.typeid]p2: // When typeid is applied to a glvalue expression whose type is a // polymorphic class type, the result refers to a std::type_info object // representing the type of the most derived object (that is, the dynamic // type) to which the glvalue refers. // If the operand is already most derived object, no need to look up vtable. if (!e->isTypeOperand() && e->isPotentiallyEvaluated() && !e->isMostDerived(getContext())) return emitCXXTypeidFromVTable(*this, e->getExprOperand(), resultType, e->hasNullCheck()); auto typeInfo = cast(cgm.getAddrOfRTTIDescriptor(loc, ty)); // `getAddrOfRTTIDescriptor` lies to us and always gives us a uint8ptr as its // type, however we need the value of the actual global to call the // get-global-op, so look it up here. auto typeInfoGlobal = cast(cgm.getGlobalValue(typeInfo.getSymbol().getValue())); auto getTypeInfo = cir::GetGlobalOp::create( builder, loc, builder.getPointerTo(typeInfoGlobal.getSymType()), typeInfoGlobal.getSymName()); // The ABI is just generating these sometimes as ptr to u8, but they are // simply a representation of the type_info. So we have to cast this, if // necessary (createBitcast is a noop if the types match). return builder.createBitcast(getTypeInfo, resultType); }