diff options
Diffstat (limited to 'clang/lib/CIR/CodeGen')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenBuilder.h | 14 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXX.cpp | 59 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 32 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenClass.cpp | 51 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 6 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp | 46 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 80 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 193 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 23 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 4 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 35 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/EHScopeStack.h | 3 |
15 files changed, 518 insertions, 43 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index 58345b4..25afe8b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -122,6 +122,11 @@ public: return getPointerTo(cir::VPtrType::get(getContext())); } + cir::FuncType getFuncType(llvm::ArrayRef<mlir::Type> params, mlir::Type retTy, + bool isVarArg = false) { + return cir::FuncType::get(params, retTy, isVarArg); + } + /// Get a CIR record kind from a AST declaration tag. cir::RecordType::RecordKind getRecordKind(const clang::TagTypeKind kind) { switch (kind) { @@ -372,6 +377,15 @@ public: return cir::BinOp::create(*this, loc, cir::BinOpKind::Div, lhs, rhs); } + mlir::Value createDynCast(mlir::Location loc, mlir::Value src, + cir::PointerType destType, bool isRefCast, + cir::DynamicCastInfoAttr info) { + auto castKind = + isRefCast ? cir::DynamicCastKind::Ref : cir::DynamicCastKind::Ptr; + return cir::DynamicCastOp::create(*this, loc, destType, castKind, src, info, + /*relative_layout=*/false); + } + Address createBaseClassAddr(mlir::Location loc, Address addr, mlir::Type destType, unsigned offset, bool assumeNotNull) { diff --git a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp index d5b35c2..274d11b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXX.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "CIRGenCXXABI.h" #include "CIRGenFunction.h" #include "CIRGenModule.h" @@ -95,7 +96,63 @@ static void emitDeclDestroy(CIRGenFunction &cgf, const VarDecl *vd, return; } - cgf.cgm.errorNYI(vd->getSourceRange(), "global with destructor"); + // If not constant storage we'll emit this regardless of NeedsDtor value. + CIRGenBuilderTy &builder = cgf.getBuilder(); + + // Prepare the dtor region. + mlir::OpBuilder::InsertionGuard guard(builder); + mlir::Block *block = builder.createBlock(&addr.getDtorRegion()); + CIRGenFunction::LexicalScope lexScope{cgf, addr.getLoc(), + builder.getInsertionBlock()}; + lexScope.setAsGlobalInit(); + builder.setInsertionPointToStart(block); + + CIRGenModule &cgm = cgf.cgm; + QualType type = vd->getType(); + + // Special-case non-array C++ destructors, if they have the right signature. + // Under some ABIs, destructors return this instead of void, and cannot be + // passed directly to __cxa_atexit if the target does not allow this + // mismatch. + const CXXRecordDecl *record = type->getAsCXXRecordDecl(); + bool canRegisterDestructor = + record && (!cgm.getCXXABI().hasThisReturn( + GlobalDecl(record->getDestructor(), Dtor_Complete)) || + cgm.getCXXABI().canCallMismatchedFunctionType()); + + // If __cxa_atexit is disabled via a flag, a different helper function is + // generated elsewhere which uses atexit instead, and it takes the destructor + // directly. + cir::FuncOp fnOp; + if (record && (canRegisterDestructor || cgm.getCodeGenOpts().CXAAtExit)) { + if (vd->getTLSKind()) + cgm.errorNYI(vd->getSourceRange(), "TLS destructor"); + assert(!record->hasTrivialDestructor()); + assert(!cir::MissingFeatures::openCL()); + CXXDestructorDecl *dtor = record->getDestructor(); + // In LLVM OG codegen this is done in registerGlobalDtor, but CIRGen + // relies on LoweringPrepare for further decoupling, so build the + // call right here. + auto gd = GlobalDecl(dtor, Dtor_Complete); + fnOp = cgm.getAddrAndTypeOfCXXStructor(gd).second; + cgf.getBuilder().createCallOp( + cgf.getLoc(vd->getSourceRange()), + mlir::FlatSymbolRefAttr::get(fnOp.getSymNameAttr()), + mlir::ValueRange{cgm.getAddrOfGlobalVar(vd)}); + } else { + cgm.errorNYI(vd->getSourceRange(), "array destructor"); + } + assert(fnOp && "expected cir.func"); + cgm.getCXXABI().registerGlobalDtor(vd, fnOp, nullptr); + + builder.setInsertionPointToEnd(block); + if (block->empty()) { + block->erase(); + // Don't confuse lexical cleanup. + builder.clearInsertionPoint(); + } else { + builder.create<cir::YieldOp>(addr.getLoc()); + } } cir::FuncOp CIRGenModule::codegenCXXStructor(GlobalDecl gd) { diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index 2465a68..06f41cd 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -54,6 +54,12 @@ public: Address thisAddr, const CXXRecordDecl *classDecl, const CXXRecordDecl *baseClassDecl) = 0; + virtual mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc, + QualType srcRecordTy, + QualType destRecordTy, + cir::PointerType destCIRTy, + bool isRefCast, Address src) = 0; + public: /// Similar to AddedStructorArgs, but only notes the number of additional /// arguments. @@ -149,6 +155,14 @@ public: /// Loads the incoming C++ this pointer as it was passed by the caller. mlir::Value loadIncomingCXXThis(CIRGenFunction &cgf); + /// Get the implicit (second) parameter that comes after the "this" pointer, + /// or nullptr if there is isn't one. + virtual mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf, + const CXXDestructorDecl *dd, + CXXDtorType type, + bool forVirtualBase, + bool delegating) = 0; + /// Emit constructor variants required by this ABI. virtual void emitCXXConstructors(const clang::CXXConstructorDecl *d) = 0; @@ -160,6 +174,14 @@ public: bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) = 0; + /// Emit code to force the execution of a destructor during global + /// teardown. The default implementation of this uses atexit. + /// + /// \param dtor - a function taking a single pointer argument + /// \param addr - a pointer to pass to the destructor function. + virtual void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor, + mlir::Value addr) = 0; + /// Checks if ABI requires extra virtual offset for vtable field. virtual bool isVirtualOffsetNeededForVTableField(CIRGenFunction &cgf, @@ -233,6 +255,16 @@ public: return false; } + /// Returns true if the target allows calling a function through a pointer + /// with a different signature than the actual function (or equivalently, + /// bitcasting a function or function pointer to a different function type). + /// In principle in the most general case this could depend on the target, the + /// calling convention, and the actual types of the arguments and return + /// value. Here it just means whether the signature mismatch could *ever* be + /// allowed; in other words, does the target do strict checking of signatures + /// for all calls. + virtual bool canCallMismatchedFunctionType() const { return true; } + /// Gets the mangle context. clang::MangleContext &getMangleContext() { return *mangleContext; } diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index d9ebf19..485b2c8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -126,6 +126,30 @@ static bool isInitializerOfDynamicClass(const CXXCtorInitializer *baseInit) { } namespace { +/// Call the destructor for a direct base class. +struct CallBaseDtor final : EHScopeStack::Cleanup { + const CXXRecordDecl *baseClass; + bool baseIsVirtual; + CallBaseDtor(const CXXRecordDecl *base, bool baseIsVirtual) + : baseClass(base), baseIsVirtual(baseIsVirtual) {} + + void emit(CIRGenFunction &cgf) override { + const CXXRecordDecl *derivedClass = + cast<CXXMethodDecl>(cgf.curFuncDecl)->getParent(); + + const CXXDestructorDecl *d = baseClass->getDestructor(); + // We are already inside a destructor, so presumably the object being + // destroyed should have the expected type. + QualType thisTy = d->getFunctionObjectParameterType(); + assert(cgf.currSrcLoc && "expected source location"); + Address addr = cgf.getAddressOfDirectBaseInCompleteClass( + *cgf.currSrcLoc, cgf.loadCXXThisAddress(), derivedClass, baseClass, + baseIsVirtual); + cgf.emitCXXDestructorCall(d, Dtor_Base, baseIsVirtual, + /*delegating=*/false, addr, thisTy); + } +}; + /// A visitor which checks whether an initializer uses 'this' in a /// way which requires the vtable to be properly set. struct DynamicThisUseChecker @@ -891,12 +915,6 @@ public: assert(!cir::MissingFeatures::ehCleanupFlags()); cgf.emitDestroy(lv.getAddress(), field->getType(), destroyer); } - - // This is a placeholder until EHCleanupScope is implemented. - size_t getSize() const override { - assert(!cir::MissingFeatures::ehCleanupScope()); - return sizeof(DestroyField); - } }; } // namespace @@ -928,8 +946,21 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd, if (dtorType == Dtor_Complete) { assert(!cir::MissingFeatures::sanitizers()); - if (classDecl->getNumVBases()) - cgm.errorNYI(dd->getSourceRange(), "virtual base destructor cleanups"); + // We push them in the forward order so that they'll be popped in + // the reverse order. + for (const CXXBaseSpecifier &base : classDecl->vbases()) { + auto *baseClassDecl = base.getType()->castAsCXXRecordDecl(); + + if (baseClassDecl->hasTrivialDestructor()) { + // Under SanitizeMemoryUseAfterDtor, poison the trivial base class + // memory. For non-trival base classes the same is done in the class + // destructor. + assert(!cir::MissingFeatures::sanitizers()); + } else { + ehStack.pushCleanup<CallBaseDtor>(NormalAndEHCleanup, baseClassDecl, + /*baseIsVirtual=*/true); + } + } return; } @@ -948,8 +979,8 @@ void CIRGenFunction::enterDtorCleanups(const CXXDestructorDecl *dd, if (baseClassDecl->hasTrivialDestructor()) assert(!cir::MissingFeatures::sanitizers()); else - cgm.errorNYI(dd->getSourceRange(), - "non-trivial base destructor cleanups"); + ehStack.pushCleanup<CallBaseDtor>(NormalAndEHCleanup, baseClassDecl, + /*baseIsVirtual=*/false); } assert(!cir::MissingFeatures::sanitizers()); diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 563a753..039d290 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -695,12 +695,6 @@ struct DestroyObject final : EHScopeStack::Cleanup { void emit(CIRGenFunction &cgf) override { cgf.emitDestroy(addr, type, destroyer); } - - // This is a placeholder until EHCleanupScope is implemented. - size_t getSize() const override { - assert(!cir::MissingFeatures::ehCleanupScope()); - return sizeof(DestroyObject); - } }; } // namespace diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index be94890..f416571 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1185,10 +1185,16 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) { case CK_BuiltinFnToFnPtr: llvm_unreachable("builtin functions are handled elsewhere"); + case CK_Dynamic: { + LValue lv = emitLValue(e->getSubExpr()); + Address v = lv.getAddress(); + const auto *dce = cast<CXXDynamicCastExpr>(e); + return makeNaturalAlignAddrLValue(emitDynamicCast(v, dce), e->getType()); + } + // These are never l-values; just use the aggregate emission code. case CK_NonAtomicToAtomic: case CK_AtomicToNonAtomic: - case CK_Dynamic: case CK_ToUnion: case CK_BaseToDerived: case CK_AddressSpaceConversion: diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 4eb8ca8..97c0944 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -463,12 +463,6 @@ struct CallObjectDelete final : EHScopeStack::Cleanup { void emit(CIRGenFunction &cgf) override { cgf.emitDeleteCall(operatorDelete, ptr, elementType); } - - // This is a placeholder until EHCleanupScope is implemented. - size_t getSize() const override { - assert(!cir::MissingFeatures::ehCleanupScope()); - return sizeof(CallObjectDelete); - } }; } // namespace @@ -728,3 +722,43 @@ void CIRGenFunction::emitDeleteCall(const FunctionDecl *deleteFD, // Emit the call to delete. emitNewDeleteCall(*this, deleteFD, deleteFTy, deleteArgs); } + +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<PointerType>()) { + srcRecordTy = srcTy->castAs<PointerType>()->getPointeeType(); + destRecordTy = destPTy->getPointeeType(); + } else { + srcRecordTy = srcTy; + destRecordTy = destTy->castAs<ReferenceType>()->getPointeeType(); + } + + assert(srcRecordTy->isRecordType() && "source type must be a record type!"); + assert(!cir::MissingFeatures::emitTypeCheck()); + + if (dce->isAlwaysNull()) { + cgm.errorNYI(dce->getSourceRange(), "emitDynamicCastToNull"); + return {}; + } + + auto destCirTy = mlir::cast<cir::PointerType>(convertType(destTy)); + return cgm.getCXXABI().emitDynamicCast(*this, loc, srcRecordTy, destRecordTy, + destCirTy, isRefCast, thisAddr); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp index 59aa257..89e9ec4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp @@ -500,6 +500,26 @@ private: bool appendBitField(const FieldDecl *field, uint64_t fieldOffset, cir::IntAttr ci, bool allowOverwrite = false); + /// Applies zero-initialization to padding bytes before and within a field. + /// \param layout The record layout containing field offset information. + /// \param fieldNo The field index in the record. + /// \param field The field declaration. + /// \param allowOverwrite Whether to allow overwriting existing values. + /// \param sizeSoFar The current size processed, updated by this function. + /// \param zeroFieldSize Set to true if the field has zero size. + /// \returns true on success, false if padding could not be applied. + bool applyZeroInitPadding(const ASTRecordLayout &layout, unsigned fieldNo, + const FieldDecl &field, bool allowOverwrite, + CharUnits &sizeSoFar, bool &zeroFieldSize); + + /// Applies zero-initialization to trailing padding bytes in a record. + /// \param layout The record layout containing size information. + /// \param allowOverwrite Whether to allow overwriting existing values. + /// \param sizeSoFar The current size processed. + /// \returns true on success, false if padding could not be applied. + bool applyZeroInitPadding(const ASTRecordLayout &layout, bool allowOverwrite, + CharUnits &sizeSoFar); + bool build(InitListExpr *ile, bool allowOverwrite); bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase, const CXXRecordDecl *vTableClass, CharUnits baseOffset); @@ -548,6 +568,49 @@ bool ConstRecordBuilder::appendBitField(const FieldDecl *field, allowOverwrite); } +bool ConstRecordBuilder::applyZeroInitPadding( + const ASTRecordLayout &layout, unsigned fieldNo, const FieldDecl &field, + bool allowOverwrite, CharUnits &sizeSoFar, bool &zeroFieldSize) { + uint64_t startBitOffset = layout.getFieldOffset(fieldNo); + CharUnits startOffset = + cgm.getASTContext().toCharUnitsFromBits(startBitOffset); + if (sizeSoFar < startOffset) { + if (!appendBytes(sizeSoFar, computePadding(cgm, startOffset - sizeSoFar), + allowOverwrite)) + return false; + } + + if (!field.isBitField()) { + CharUnits fieldSize = + cgm.getASTContext().getTypeSizeInChars(field.getType()); + sizeSoFar = startOffset + fieldSize; + zeroFieldSize = fieldSize.isZero(); + } else { + const CIRGenRecordLayout &rl = + cgm.getTypes().getCIRGenRecordLayout(field.getParent()); + const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(&field); + uint64_t endBitOffset = startBitOffset + info.size; + sizeSoFar = cgm.getASTContext().toCharUnitsFromBits(endBitOffset); + if (endBitOffset % cgm.getASTContext().getCharWidth() != 0) + sizeSoFar++; + zeroFieldSize = info.size == 0; + } + return true; +} + +bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout, + bool allowOverwrite, + CharUnits &sizeSoFar) { + CharUnits totalSize = layout.getSize(); + if (sizeSoFar < totalSize) { + if (!appendBytes(sizeSoFar, computePadding(cgm, totalSize - sizeSoFar), + allowOverwrite)) + return false; + } + sizeSoFar = totalSize; + return true; +} + bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { RecordDecl *rd = ile->getType() ->castAs<clang::RecordType>() @@ -562,11 +625,9 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { if (cxxrd->getNumBases()) return false; - if (cgm.shouldZeroInitPadding()) { - assert(!cir::MissingFeatures::recordZeroInitPadding()); - cgm.errorNYI(rd->getSourceRange(), "zero init padding"); - return false; - } + const bool zeroInitPadding = cgm.shouldZeroInitPadding(); + bool zeroFieldSize = false; + CharUnits sizeSoFar = CharUnits::Zero(); unsigned elementNo = 0; for (auto [index, field] : llvm::enumerate(rd->fields())) { @@ -596,7 +657,10 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { continue; } - assert(!cir::MissingFeatures::recordZeroInitPadding()); + if (zeroInitPadding && + !applyZeroInitPadding(layout, index, *field, allowOverwrite, sizeSoFar, + zeroFieldSize)) + return false; // When emitting a DesignatedInitUpdateExpr, a nested InitListExpr // represents additional overwriting of our current constant value, and not @@ -641,8 +705,8 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) { } } - assert(!cir::MissingFeatures::recordZeroInitPadding()); - return true; + return !zeroInitPadding || + applyZeroInitPadding(layout, allowOverwrite, sizeSoFar); } namespace { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 7edd83e..637f9ef 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -1916,6 +1916,11 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { return builder.createIntToPtr(middleVal, destCIRTy); } + case CK_Dynamic: { + Address v = cgf.emitPointerWithAlignment(subExpr); + const auto *dce = cast<CXXDynamicCastExpr>(ce); + return cgf.emitDynamicCast(v, dce); + } case CK_ArrayToPointerDecay: return cgf.emitArrayToPointerDecay(subExpr).getPointer(); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index db2adc2..7a606ee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1312,6 +1312,8 @@ public: mlir::LogicalResult emitDoStmt(const clang::DoStmt &s); + mlir::Value emitDynamicCast(Address thisAddr, const CXXDynamicCastExpr *dce); + /// Emit an expression as an initializer for an object (variable, field, etc.) /// at the given location. The expression is not necessarily the normal /// initializer for the object, and the address is not necessarily diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 0418174..9e490c6d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -59,7 +59,11 @@ public: void addImplicitStructorParams(CIRGenFunction &cgf, QualType &resTy, FunctionArgList ¶ms) override; - + mlir::Value getCXXDestructorImplicitParam(CIRGenFunction &cgf, + const CXXDestructorDecl *dd, + CXXDtorType type, + bool forVirtualBase, + bool delegating) override; void emitCXXConstructors(const clang::CXXConstructorDecl *d) override; void emitCXXDestructors(const clang::CXXDestructorDecl *d) override; void emitCXXStructor(clang::GlobalDecl gd) override; @@ -68,6 +72,8 @@ public: CXXDtorType type, bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) override; + void registerGlobalDtor(const VarDecl *vd, cir::FuncOp dtor, + mlir::Value addr) override; void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) override; void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) override; @@ -116,6 +122,16 @@ public: Address thisAddr, const CXXRecordDecl *classDecl, const CXXRecordDecl *baseClassDecl) override; + // The traditional clang CodeGen emits calls to `__dynamic_cast` directly into + // LLVM in the `emitDynamicCastCall` function. In CIR, `dynamic_cast` + // expressions are lowered to `cir.dyn_cast` ops instead of calls to runtime + // functions. So during CIRGen we don't need the `emitDynamicCastCall` + // function that clang CodeGen has. + mlir::Value emitDynamicCast(CIRGenFunction &cgf, mlir::Location loc, + QualType srcRecordTy, QualType destRecordTy, + cir::PointerType destCIRTy, bool isRefCast, + Address src) override; + /**************************** RTTI Uniqueness ******************************/ protected: /// Returns true if the ABI requires RTTI type_info objects to be unique @@ -1492,11 +1508,8 @@ void CIRGenItaniumCXXABI::emitDestructorCall( CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type, bool forVirtualBase, bool delegating, Address thisAddr, QualType thisTy) { GlobalDecl gd(dd, type); - if (needsVTTParameter(gd)) { - cgm.errorNYI(dd->getSourceRange(), "emitDestructorCall: VTT"); - } - - mlir::Value vtt = nullptr; + mlir::Value vtt = + getCXXDestructorImplicitParam(cgf, dd, type, forVirtualBase, delegating); ASTContext &astContext = cgm.getASTContext(); QualType vttTy = astContext.getPointerType(astContext.VoidPtrTy); assert(!cir::MissingFeatures::appleKext()); @@ -1507,6 +1520,34 @@ void CIRGenItaniumCXXABI::emitDestructorCall( vttTy, nullptr); } +void CIRGenItaniumCXXABI::registerGlobalDtor(const VarDecl *vd, + cir::FuncOp dtor, + mlir::Value addr) { + if (vd->isNoDestroy(cgm.getASTContext())) + return; + + if (vd->getTLSKind()) { + cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: TLS"); + return; + } + + // HLSL doesn't support atexit. + if (cgm.getLangOpts().HLSL) { + cgm.errorNYI(vd->getSourceRange(), "registerGlobalDtor: HLSL"); + return; + } + + // The default behavior is to use atexit. This is handled in lowering + // prepare. Nothing to be done for CIR here. +} + +mlir::Value CIRGenItaniumCXXABI::getCXXDestructorImplicitParam( + CIRGenFunction &cgf, const CXXDestructorDecl *dd, CXXDtorType type, + bool forVirtualBase, bool delegating) { + GlobalDecl gd(dd, type); + return cgf.getVTTParameter(gd, forVirtualBase, delegating); +} + // The idea here is creating a separate block for the throw with an // `UnreachableOp` as the terminator. So, we branch from the current block // to the throw block and create a block for the remaining operations. @@ -1796,3 +1837,143 @@ mlir::Value CIRGenItaniumCXXABI::getVirtualBaseClassOffset( } return vbaseOffset; } + +static cir::FuncOp getBadCastFn(CIRGenFunction &cgf) { + // Prototype: void __cxa_bad_cast(); + + // TODO(cir): set the calling convention of the runtime function. + assert(!cir::MissingFeatures::opFuncCallingConv()); + + cir::FuncType fnTy = + cgf.getBuilder().getFuncType({}, cgf.getBuilder().getVoidTy()); + return cgf.cgm.createRuntimeFunction(fnTy, "__cxa_bad_cast"); +} + +// TODO(cir): This could be shared with classic codegen. +static CharUnits computeOffsetHint(ASTContext &astContext, + const CXXRecordDecl *src, + const CXXRecordDecl *dst) { + CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + + // If Dst is not derived from Src we can skip the whole computation below and + // return that Src is not a public base of Dst. Record all inheritance paths. + if (!dst->isDerivedFrom(src, paths)) + return CharUnits::fromQuantity(-2ULL); + + unsigned numPublicPaths = 0; + CharUnits offset; + + // Now walk all possible inheritance paths. + for (const CXXBasePath &path : paths) { + if (path.Access != AS_public) // Ignore non-public inheritance. + continue; + + ++numPublicPaths; + + for (const CXXBasePathElement &pathElement : path) { + // If the path contains a virtual base class we can't give any hint. + // -1: no hint. + if (pathElement.Base->isVirtual()) + return CharUnits::fromQuantity(-1ULL); + + if (numPublicPaths > 1) // Won't use offsets, skip computation. + continue; + + // Accumulate the base class offsets. + const ASTRecordLayout &L = + astContext.getASTRecordLayout(pathElement.Class); + offset += L.getBaseClassOffset( + pathElement.Base->getType()->getAsCXXRecordDecl()); + } + } + + // -2: Src is not a public base of Dst. + if (numPublicPaths == 0) + return CharUnits::fromQuantity(-2ULL); + + // -3: Src is a multiple public base type but never a virtual base type. + if (numPublicPaths > 1) + return CharUnits::fromQuantity(-3ULL); + + // Otherwise, the Src type is a unique public nonvirtual base type of Dst. + // Return the offset of Src from the origin of Dst. + return offset; +} + +static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) { + // Prototype: + // void *__dynamic_cast(const void *sub, + // global_as const abi::__class_type_info *src, + // global_as const abi::__class_type_info *dst, + // std::ptrdiff_t src2dst_offset); + + mlir::Type voidPtrTy = cgf.getBuilder().getVoidPtrTy(); + mlir::Type rttiPtrTy = cgf.getBuilder().getUInt8PtrTy(); + mlir::Type ptrDiffTy = cgf.convertType(cgf.getContext().getPointerDiffType()); + + // TODO(cir): mark the function as nowind willreturn readonly. + assert(!cir::MissingFeatures::opFuncNoUnwind()); + assert(!cir::MissingFeatures::opFuncWillReturn()); + assert(!cir::MissingFeatures::opFuncReadOnly()); + + // TODO(cir): set the calling convention of the runtime function. + assert(!cir::MissingFeatures::opFuncCallingConv()); + + cir::FuncType FTy = cgf.getBuilder().getFuncType( + {voidPtrTy, rttiPtrTy, rttiPtrTy, ptrDiffTy}, voidPtrTy); + return cgf.cgm.createRuntimeFunction(FTy, "__dynamic_cast"); +} + +static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf, + mlir::Location loc, + QualType srcRecordTy, + QualType destRecordTy) { + auto srcRtti = mlir::cast<cir::GlobalViewAttr>( + cgf.cgm.getAddrOfRTTIDescriptor(loc, srcRecordTy)); + auto destRtti = mlir::cast<cir::GlobalViewAttr>( + cgf.cgm.getAddrOfRTTIDescriptor(loc, destRecordTy)); + + cir::FuncOp runtimeFuncOp = getItaniumDynamicCastFn(cgf); + cir::FuncOp badCastFuncOp = getBadCastFn(cgf); + auto runtimeFuncRef = mlir::FlatSymbolRefAttr::get(runtimeFuncOp); + auto badCastFuncRef = mlir::FlatSymbolRefAttr::get(badCastFuncOp); + + const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl(); + const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl(); + CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl); + + mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType()); + auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity()); + + return cir::DynamicCastInfoAttr::get(srcRtti, destRtti, runtimeFuncRef, + badCastFuncRef, offsetHintAttr); +} + +mlir::Value CIRGenItaniumCXXABI::emitDynamicCast(CIRGenFunction &cgf, + mlir::Location loc, + QualType srcRecordTy, + QualType destRecordTy, + cir::PointerType destCIRTy, + bool isRefCast, Address src) { + bool isCastToVoid = destRecordTy.isNull(); + assert((!isCastToVoid || !isRefCast) && "cannot cast to void reference"); + + if (isCastToVoid) { + cgm.errorNYI(loc, "emitDynamicCastToVoid"); + return {}; + } + + // If the destination is effectively final, the cast succeeds if and only + // if the dynamic type of the pointer is exactly the destination type. + if (destRecordTy->getAsCXXRecordDecl()->isEffectivelyFinal() && + cgf.cgm.getCodeGenOpts().OptimizationLevel > 0) { + cgm.errorNYI(loc, "emitExactDynamicCast"); + return {}; + } + + cir::DynamicCastInfoAttr castInfo = + emitDynamicCastInfo(cgf, loc, srcRecordTy, destRecordTy); + return cgf.getBuilder().createDynCast(loc, src.getPointer(), destCIRTy, + isRefCast, castInfo); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 910c8a9..fe1ea56 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2079,6 +2079,29 @@ CIRGenModule::createCIRBuiltinFunction(mlir::Location loc, StringRef name, return fnOp; } +cir::FuncOp CIRGenModule::createRuntimeFunction(cir::FuncType ty, + StringRef name, mlir::ArrayAttr, + [[maybe_unused]] bool isLocal, + bool assumeConvergent) { + if (assumeConvergent) + errorNYI("createRuntimeFunction: assumeConvergent"); + if (isLocal) + errorNYI("createRuntimeFunction: local"); + + cir::FuncOp entry = getOrCreateCIRFunction(name, ty, GlobalDecl(), + /*forVtable=*/false); + + if (entry) { + // TODO(cir): set the attributes of the function. + assert(!cir::MissingFeatures::setLLVMFunctionFEnvAttributes()); + assert(!cir::MissingFeatures::opFuncCallingConv()); + assert(!cir::MissingFeatures::opGlobalDLLImportExport()); + entry.setDSOLocal(true); + } + + return entry; +} + mlir::SymbolTable::Visibility CIRGenModule::getMLIRVisibility(cir::GlobalOp op) { // MLIR doesn't accept public symbols declarations (only diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index c6a6681..f627bae 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -480,6 +480,10 @@ public: cir::FuncType ty, const clang::FunctionDecl *fd); + cir::FuncOp createRuntimeFunction(cir::FuncType ty, llvm::StringRef name, + mlir::ArrayAttr = {}, bool isLocal = false, + bool assumeConvergent = false); + static constexpr const char *builtinCoroId = "__builtin_coro_id"; /// Given a builtin id for a function like "__builtin_fabsf", return a diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp index 94d856b..84f5977 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -327,9 +327,40 @@ cir::GlobalLinkageKind CIRGenModule::getVTableLinkage(const CXXRecordDecl *rd) { llvm_unreachable("Should not have been asked to emit this"); } } + // -fapple-kext mode does not support weak linkage, so we must use + // internal linkage. + if (astContext.getLangOpts().AppleKext) + return cir::GlobalLinkageKind::InternalLinkage; + + auto discardableODRLinkage = cir::GlobalLinkageKind::LinkOnceODRLinkage; + auto nonDiscardableODRLinkage = cir::GlobalLinkageKind::WeakODRLinkage; + if (rd->hasAttr<DLLExportAttr>()) { + // Cannot discard exported vtables. + discardableODRLinkage = nonDiscardableODRLinkage; + } else if (rd->hasAttr<DLLImportAttr>()) { + // Imported vtables are available externally. + discardableODRLinkage = cir::GlobalLinkageKind::AvailableExternallyLinkage; + nonDiscardableODRLinkage = + cir::GlobalLinkageKind::AvailableExternallyLinkage; + } + + switch (rd->getTemplateSpecializationKind()) { + case TSK_Undeclared: + case TSK_ExplicitSpecialization: + case TSK_ImplicitInstantiation: + return discardableODRLinkage; + + case TSK_ExplicitInstantiationDeclaration: { + errorNYI(rd->getSourceRange(), + "getVTableLinkage: explicit instantiation declaration"); + return cir::GlobalLinkageKind::ExternalLinkage; + } + + case TSK_ExplicitInstantiationDefinition: + return nonDiscardableODRLinkage; + } - errorNYI(rd->getSourceRange(), "getVTableLinkage: no key function"); - return cir::GlobalLinkageKind::ExternalLinkage; + llvm_unreachable("Invalid TemplateSpecializationKind!"); } cir::GlobalOp CIRGenVTables::getAddrOfVTT(const CXXRecordDecl *rd) { diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h index 66c1f76..67a72f5 100644 --- a/clang/lib/CIR/CodeGen/EHScopeStack.h +++ b/clang/lib/CIR/CodeGen/EHScopeStack.h @@ -108,9 +108,6 @@ public: /// // \param flags cleanup kind. virtual void emit(CIRGenFunction &cgf) = 0; - - // This is a placeholder until EHScope is implemented. - virtual size_t getSize() const = 0; }; private: |