diff options
Diffstat (limited to 'clang/lib/CIR')
21 files changed, 1391 insertions, 125 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index f855bda..73c9fb9 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -408,21 +408,23 @@ public: } mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType, - mlir::Value dstAddr, mlir::Type storageType, + Address dstAddr, mlir::Type storageType, mlir::Value src, const CIRGenBitFieldInfo &info, - bool isLvalueVolatile, bool useVolatile) { - return create<cir::SetBitfieldOp>(loc, resultType, dstAddr, storageType, - src, info.name, info.size, info.offset, - info.isSigned, isLvalueVolatile); + bool isLvalueVolatile) { + return create<cir::SetBitfieldOp>( + loc, resultType, dstAddr.getPointer(), storageType, src, info.name, + info.size, info.offset, info.isSigned, isLvalueVolatile, + dstAddr.getAlignment().getAsAlign().value()); } mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType, - mlir::Value addr, mlir::Type storageType, + Address addr, mlir::Type storageType, const CIRGenBitFieldInfo &info, - bool isLvalueVolatile, bool useVolatile) { - return create<cir::GetBitfieldOp>(loc, resultType, addr, storageType, - info.name, info.size, info.offset, - info.isSigned, isLvalueVolatile); + bool isLvalueVolatile) { + return create<cir::GetBitfieldOp>( + loc, resultType, addr.getPointer(), storageType, info.name, info.size, + info.offset, info.isSigned, isLvalueVolatile, + addr.getAlignment().getAsAlign().value()); } }; diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 61d1c54..ef136f8 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -121,6 +121,13 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, return RValue::get(nullptr); } + case Builtin::BI__builtin_assume_separate_storage: { + mlir::Value value0 = emitScalarExpr(e->getArg(0)); + mlir::Value value1 = emitScalarExpr(e->getArg(1)); + builder.create<cir::AssumeSepStorageOp>(loc, value0, value1); + return RValue::get(nullptr); + } + case Builtin::BI__builtin_complex: { mlir::Value real = emitScalarExpr(e->getArg(0)); mlir::Value imag = emitScalarExpr(e->getArg(1)); diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index eb079b8..5929568 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -75,6 +75,11 @@ public: /// Emit dtor variants required by this ABI. virtual void emitCXXDestructors(const clang::CXXDestructorDecl *d) = 0; + virtual void emitDestructorCall(CIRGenFunction &cgf, + const CXXDestructorDecl *dd, CXXDtorType type, + bool forVirtualBase, bool delegating, + Address thisAddr, QualType thisTy) = 0; + /// Returns true if the given destructor type should be emitted as a linkonce /// delegating thunk, regardless of whether the dtor is defined in this TU or /// not. diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp index 8da832d..67d8988 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCXXExpr.cpp @@ -246,6 +246,29 @@ static void emitNewInitializer(CIRGenFunction &cgf, const CXXNewExpr *e, } } +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<CXXMethodDecl>(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())); +} + /// 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, diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 8667bb6..50cca0e 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -12,6 +12,7 @@ #include "CIRGenCXXABI.h" #include "CIRGenFunction.h" +#include "CIRGenValue.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" @@ -311,6 +312,116 @@ void CIRGenFunction::emitInitializerForField(FieldDecl *field, LValue lhs, assert(!cir::MissingFeatures::requiresCleanups()); } +/// Emit a loop to call a particular constructor for each of several members +/// of an array. +/// +/// \param ctor the constructor to call for each element +/// \param arrayType the type of the array to initialize +/// \param arrayBegin an arrayType* +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed +void CIRGenFunction::emitCXXAggrConstructorCall( + const CXXConstructorDecl *ctor, const clang::ArrayType *arrayType, + Address arrayBegin, const CXXConstructExpr *e, bool newPointerIsChecked, + bool zeroInitialize) { + QualType elementType; + mlir::Value numElements = emitArrayLength(arrayType, elementType, arrayBegin); + emitCXXAggrConstructorCall(ctor, numElements, arrayBegin, e, + newPointerIsChecked, zeroInitialize); +} + +/// Emit a loop to call a particular constructor for each of several members +/// of an array. +/// +/// \param ctor the constructor to call for each element +/// \param numElements the number of elements in the array; +/// may be zero +/// \param arrayBase a T*, where T is the type constructed by ctor +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed +void CIRGenFunction::emitCXXAggrConstructorCall( + const CXXConstructorDecl *ctor, mlir::Value numElements, Address arrayBase, + const CXXConstructExpr *e, bool newPointerIsChecked, bool zeroInitialize) { + // It's legal for numElements to be zero. This can happen both + // dynamically, because x can be zero in 'new A[x]', and statically, + // because of GCC extensions that permit zero-length arrays. There + // are probably legitimate places where we could assume that this + // doesn't happen, but it's not clear that it's worth it. + + // Optimize for a constant count. + auto constantCount = dyn_cast<cir::ConstantOp>(numElements.getDefiningOp()); + if (constantCount) { + auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue()); + // Just skip out if the constant count is zero. + if (constIntAttr && constIntAttr.getUInt() == 0) + return; + } else { + // Otherwise, emit the check. + cgm.errorNYI(e->getSourceRange(), "dynamic-length array expression"); + } + + auto arrayTy = mlir::cast<cir::ArrayType>(arrayBase.getElementType()); + mlir::Type elementType = arrayTy.getElementType(); + cir::PointerType ptrToElmType = builder.getPointerTo(elementType); + + // Tradional LLVM codegen emits a loop here. CIR lowers to a loop as part of + // LoweringPrepare. + + // The alignment of the base, adjusted by the size of a single element, + // provides a conservative estimate of the alignment of every element. + // (This assumes we never start tracking offsetted alignments.) + // + // Note that these are complete objects and so we don't need to + // use the non-virtual size or alignment. + QualType type = getContext().getTypeDeclType(ctor->getParent()); + CharUnits eltAlignment = arrayBase.getAlignment().alignmentOfArrayElement( + getContext().getTypeSizeInChars(type)); + + // Zero initialize the storage, if requested. + if (zeroInitialize) + emitNullInitialization(*currSrcLoc, arrayBase, type); + + // C++ [class.temporary]p4: + // There are two contexts in which temporaries are destroyed at a different + // point than the end of the full-expression. The first context is when a + // default constructor is called to initialize an element of an array. + // If the constructor has one or more default arguments, the destruction of + // every temporary created in a default argument expression is sequenced + // before the construction of the next array element, if any. + { + assert(!cir::MissingFeatures::runCleanupsScope()); + + // Evaluate the constructor and its arguments in a regular + // partial-destroy cleanup. + if (getLangOpts().Exceptions && + !ctor->getParent()->hasTrivialDestructor()) { + cgm.errorNYI(e->getSourceRange(), "partial array cleanups"); + } + + // Emit the constructor call that will execute for every array element. + mlir::Value arrayOp = + builder.createPtrBitcast(arrayBase.getPointer(), arrayTy); + builder.create<cir::ArrayCtor>( + *currSrcLoc, arrayOp, [&](mlir::OpBuilder &b, mlir::Location loc) { + mlir::BlockArgument arg = + b.getInsertionBlock()->addArgument(ptrToElmType, loc); + Address curAddr = Address(arg, elementType, eltAlignment); + assert(!cir::MissingFeatures::sanitizers()); + auto currAVS = AggValueSlot::forAddr( + curAddr, type.getQualifiers(), AggValueSlot::IsDestructed, + AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap, + AggValueSlot::IsNotZeroed); + emitCXXConstructorCall(ctor, Ctor_Complete, + /*ForVirtualBase=*/false, + /*Delegating=*/false, currAVS, e); + builder.create<cir::YieldOp>(loc); + }); + } + + if (constantCount.use_empty()) + constantCount.erase(); +} + void CIRGenFunction::emitDelegateCXXConstructorCall( const CXXConstructorDecl *ctor, CXXCtorType ctorType, const FunctionArgList &args, SourceLocation loc) { @@ -369,6 +480,19 @@ void CIRGenFunction::emitImplicitAssignmentOperatorBody(FunctionArgList &args) { s->getStmtClassName()); } +void CIRGenFunction::destroyCXXObject(CIRGenFunction &cgf, Address addr, + QualType type) { + const RecordType *rtype = type->castAs<RecordType>(); + const CXXRecordDecl *record = cast<CXXRecordDecl>(rtype->getDecl()); + const CXXDestructorDecl *dtor = record->getDestructor(); + // TODO(cir): Unlike traditional codegen, CIRGen should actually emit trivial + // dtors which shall be removed on later CIR passes. However, only remove this + // assertion after we have a test case to exercise this path. + assert(!dtor->isTrivial()); + cgf.emitCXXDestructorCall(dtor, Dtor_Complete, /*forVirtualBase*/ false, + /*delegating=*/false, addr, type); +} + void CIRGenFunction::emitDelegatingCXXConstructorCall( const CXXConstructorDecl *ctor, const FunctionArgList &args) { assert(ctor->isDelegatingConstructor()); @@ -392,6 +516,14 @@ void CIRGenFunction::emitDelegatingCXXConstructorCall( } } +void CIRGenFunction::emitCXXDestructorCall(const CXXDestructorDecl *dd, + CXXDtorType type, + bool forVirtualBase, bool delegating, + Address thisAddr, QualType thisTy) { + cgm.getCXXABI().emitDestructorCall(*this, dd, type, forVirtualBase, + delegating, thisAddr, thisTy); +} + Address CIRGenFunction::getAddressOfBaseClass( Address value, const CXXRecordDecl *derived, llvm::iterator_range<CastExpr::path_const_iterator> path, diff --git a/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp new file mode 100644 index 0000000..be21ce9 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCleanup.cpp @@ -0,0 +1,69 @@ +//===--- CIRGenCleanup.cpp - Bookkeeping and code emission for cleanups ---===// +// +// 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 file contains code dealing with the IR generation for cleanups +// and related information. +// +// A "cleanup" is a piece of code which needs to be executed whenever +// control transfers out of a particular scope. This can be +// conditionalized to occur only on exceptional control flow, only on +// normal control flow, or both. +// +//===----------------------------------------------------------------------===// + +#include "CIRGenFunction.h" + +#include "clang/CIR/MissingFeatures.h" + +using namespace clang; +using namespace clang::CIRGen; + +//===----------------------------------------------------------------------===// +// CIRGenFunction cleanup related +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// EHScopeStack +//===----------------------------------------------------------------------===// + +void EHScopeStack::Cleanup::anchor() {} + +static mlir::Block *getCurCleanupBlock(CIRGenFunction &cgf) { + mlir::OpBuilder::InsertionGuard guard(cgf.getBuilder()); + mlir::Block *cleanup = + cgf.curLexScope->getOrCreateCleanupBlock(cgf.getBuilder()); + return cleanup; +} + +/// Pops a cleanup block. If the block includes a normal cleanup, the +/// current insertion point is threaded through the cleanup, as are +/// any branch fixups on the cleanup. +void CIRGenFunction::popCleanupBlock() { + assert(!ehStack.cleanupStack.empty() && "cleanup stack is empty!"); + mlir::OpBuilder::InsertionGuard guard(builder); + std::unique_ptr<EHScopeStack::Cleanup> cleanup = + ehStack.cleanupStack.pop_back_val(); + + assert(!cir::MissingFeatures::ehCleanupFlags()); + mlir::Block *cleanupEntry = getCurCleanupBlock(*this); + builder.setInsertionPointToEnd(cleanupEntry); + cleanup->emit(*this); +} + +/// Pops cleanup blocks until the given savepoint is reached. +void CIRGenFunction::popCleanupBlocks(size_t oldCleanupStackDepth) { + assert(!cir::MissingFeatures::ehstackBranches()); + + assert(ehStack.getStackDepth() >= oldCleanupStackDepth); + + // Pop cleanup blocks until we reach the base stack depth for the + // current scope. + while (ehStack.getStackDepth() > oldCleanupStackDepth) { + popCleanupBlock(); + } +} diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index afbe92a..a28ac3c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -183,8 +183,8 @@ void CIRGenFunction::emitAutoVarCleanups( const VarDecl &d = *emission.Variable; // Check the type for a cleanup. - if (d.needsDestruction(getContext())) - cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup"); + if (QualType::DestructionKind dtorKind = d.needsDestruction(getContext())) + emitAutoVarTypeCleanup(emission, dtorKind); assert(!cir::MissingFeatures::opAllocaPreciseLifetime()); @@ -648,3 +648,96 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs, assert(!cir::MissingFeatures::sanitizers()); } + +/// Immediately perform the destruction of the given object. +/// +/// \param addr - the address of the object; a type* +/// \param type - the type of the object; if an array type, all +/// objects are destroyed in reverse order +/// \param destroyer - the function to call to destroy individual +/// elements +void CIRGenFunction::emitDestroy(Address addr, QualType type, + Destroyer *destroyer) { + if (getContext().getAsArrayType(type)) + cgm.errorNYI("emitDestroy: array type"); + + return destroyer(*this, addr, type); +} + +CIRGenFunction::Destroyer * +CIRGenFunction::getDestroyer(QualType::DestructionKind kind) { + switch (kind) { + case QualType::DK_none: + llvm_unreachable("no destroyer for trivial dtor"); + case QualType::DK_cxx_destructor: + return destroyCXXObject; + case QualType::DK_objc_strong_lifetime: + case QualType::DK_objc_weak_lifetime: + case QualType::DK_nontrivial_c_struct: + cgm.errorNYI("getDestroyer: other destruction kind"); + return nullptr; + } + llvm_unreachable("Unknown DestructionKind"); +} + +namespace { +struct DestroyObject final : EHScopeStack::Cleanup { + DestroyObject(Address addr, QualType type, + CIRGenFunction::Destroyer *destroyer) + : addr(addr), type(type), destroyer(destroyer) {} + + Address addr; + QualType type; + CIRGenFunction::Destroyer *destroyer; + + void emit(CIRGenFunction &cgf) override { + cgf.emitDestroy(addr, type, destroyer); + } +}; +} // namespace + +/// Enter a destroy cleanup for the given local variable. +void CIRGenFunction::emitAutoVarTypeCleanup( + const CIRGenFunction::AutoVarEmission &emission, + QualType::DestructionKind dtorKind) { + assert(dtorKind != QualType::DK_none); + + // Note that for __block variables, we want to destroy the + // original stack object, not the possibly forwarded object. + Address addr = emission.getObjectAddress(*this); + + const VarDecl *var = emission.Variable; + QualType type = var->getType(); + + CleanupKind cleanupKind = NormalAndEHCleanup; + CIRGenFunction::Destroyer *destroyer = nullptr; + + switch (dtorKind) { + case QualType::DK_none: + llvm_unreachable("no cleanup for trivially-destructible variable"); + + case QualType::DK_cxx_destructor: + // If there's an NRVO flag on the emission, we need a different + // cleanup. + if (emission.NRVOFlag) { + cgm.errorNYI(var->getSourceRange(), "emitAutoVarTypeCleanup: NRVO"); + return; + } + // Otherwise, this is handled below. + break; + + case QualType::DK_objc_strong_lifetime: + case QualType::DK_objc_weak_lifetime: + case QualType::DK_nontrivial_c_struct: + cgm.errorNYI(var->getSourceRange(), + "emitAutoVarTypeCleanup: other dtor kind"); + return; + } + + // If we haven't chosen a more specific destroyer, use the default. + if (!destroyer) + destroyer = getDestroyer(dtorKind); + + assert(!cir::MissingFeatures::ehCleanupFlags()); + ehStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 51da48d..7ff5f26 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -333,13 +333,12 @@ mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, Address ptr = dst.getBitFieldAddress(); assert(!cir::MissingFeatures::armComputeVolatileBitfields()); - const bool useVolatile = false; mlir::Value dstAddr = dst.getAddress().getPointer(); - return builder.createSetBitfield(dstAddr.getLoc(), resLTy, dstAddr, + return builder.createSetBitfield(dstAddr.getLoc(), resLTy, ptr, ptr.getElementType(), src.getValue(), info, - dst.isVolatileQualified(), useVolatile); + dst.isVolatileQualified()); } RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) { @@ -352,8 +351,7 @@ RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) { assert(!cir::MissingFeatures::armComputeVolatileBitfields()); mlir::Value field = builder.createGetBitfield( - getLoc(loc), resLTy, ptr.getPointer(), ptr.getElementType(), info, - lv.isVolatile(), false); + getLoc(loc), resLTy, ptr, ptr.getElementType(), info, lv.isVolatile()); assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck() && "NYI"); return RValue::get(field); } @@ -366,7 +364,10 @@ Address CIRGenFunction::getAddrOfBitFieldStorage(LValue base, cir::PointerType fieldPtr = cir::PointerType::get(fieldType); cir::GetMemberOp sea = getBuilder().createGetMember( loc, fieldPtr, base.getPointer(), field->getName(), index); - return Address(sea, CharUnits::One()); + auto rec = cast<cir::RecordType>(base.getAddress().getElementType()); + CharUnits offset = CharUnits::fromQuantity( + rec.getElementOffset(cgm.getDataLayout().layout, index)); + return Address(sea, base.getAlignment().alignmentAtOffset(offset)); } LValue CIRGenFunction::emitLValueForBitField(LValue base, @@ -662,7 +663,8 @@ LValue CIRGenFunction::emitUnaryOpLValue(const UnaryOperator *e) { } case UO_PreInc: case UO_PreDec: { - bool isInc = e->isIncrementOp(); + cir::UnaryOpKind kind = + e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec; LValue lv = emitLValue(e->getSubExpr()); assert(e->isPrefix() && "Prefix operator in unexpected state!"); @@ -671,7 +673,7 @@ LValue CIRGenFunction::emitUnaryOpLValue(const UnaryOperator *e) { cgm.errorNYI(e->getSourceRange(), "UnaryOp complex inc/dec"); lv = LValue(); } else { - emitScalarPrePostIncDec(e, lv, isInc, /*isPre=*/true); + emitScalarPrePostIncDec(e, lv, kind, /*isPre=*/true); } return lv; @@ -1053,6 +1055,67 @@ LValue CIRGenFunction::emitMemberExpr(const MemberExpr *e) { llvm_unreachable("Unhandled member declaration!"); } +/// Evaluate an expression into a given memory location. +void CIRGenFunction::emitAnyExprToMem(const Expr *e, Address location, + Qualifiers quals, bool isInit) { + // FIXME: This function should take an LValue as an argument. + switch (getEvaluationKind(e->getType())) { + case cir::TEK_Complex: { + LValue lv = makeAddrLValue(location, e->getType()); + emitComplexExprIntoLValue(e, lv, isInit); + return; + } + + case cir::TEK_Aggregate: { + emitAggExpr(e, AggValueSlot::forAddr(location, quals, + AggValueSlot::IsDestructed_t(isInit), + AggValueSlot::IsAliased_t(!isInit), + AggValueSlot::MayOverlap)); + return; + } + + case cir::TEK_Scalar: { + RValue rv = RValue::get(emitScalarExpr(e)); + LValue lv = makeAddrLValue(location, e->getType()); + emitStoreThroughLValue(rv, lv); + return; + } + } + + llvm_unreachable("bad evaluation kind"); +} + +LValue CIRGenFunction::emitCompoundLiteralLValue(const CompoundLiteralExpr *e) { + if (e->isFileScope()) { + cgm.errorNYI(e->getSourceRange(), "emitCompoundLiteralLValue: FileScope"); + return {}; + } + + if (e->getType()->isVariablyModifiedType()) { + cgm.errorNYI(e->getSourceRange(), + "emitCompoundLiteralLValue: VariablyModifiedType"); + return {}; + } + + Address declPtr = createMemTemp(e->getType(), getLoc(e->getSourceRange()), + ".compoundliteral"); + const Expr *initExpr = e->getInitializer(); + LValue result = makeAddrLValue(declPtr, e->getType(), AlignmentSource::Decl); + + emitAnyExprToMem(initExpr, declPtr, e->getType().getQualifiers(), + /*Init*/ true); + + // Block-scope compound literals are destroyed at the end of the enclosing + // scope in C. + if (!getLangOpts().CPlusPlus && e->getType().isDestructedType()) { + cgm.errorNYI(e->getSourceRange(), + "emitCompoundLiteralLValue: non C++ DestructedType"); + return {}; + } + + return result; +} + LValue CIRGenFunction::emitCallExprLValue(const CallExpr *e) { RValue rv = emitCallExpr(e); @@ -1594,37 +1657,38 @@ void CIRGenFunction::emitCXXConstructExpr(const CXXConstructExpr *e, return; } - if (getContext().getAsArrayType(e->getType())) { - cgm.errorNYI(e->getSourceRange(), "emitCXXConstructExpr: array type"); - 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: - // This should just set 'forVirtualBase' to true and fall through, but - // virtual base class support is otherwise missing, so this needs to wait - // until it can be tested. - cgm.errorNYI(e->getSourceRange(), - "emitCXXConstructExpr: virtual base constructor"); - return; - case CXXConstructionKind::NonVirtualBase: - type = Ctor_Base; - break; - } + 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: + // This should just set 'forVirtualBase' to true and fall through, but + // virtual base class support is otherwise missing, so this needs to wait + // until it can be tested. + cgm.errorNYI(e->getSourceRange(), + "emitCXXConstructExpr: virtual base constructor"); + return; + case CXXConstructionKind::NonVirtualBase: + type = Ctor_Base; + break; + } - emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e); + emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e); + } } RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp index 0a22771..7f2e2ce 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -34,11 +34,20 @@ public: } mlir::Value emitLoadOfLValue(LValue lv, SourceLocation loc); + /// Store the specified real/imag parts into the /// specified value pointer. void emitStoreOfComplex(mlir::Location loc, mlir::Value val, LValue lv, bool isInit); + /// Emit a cast from complex value Val to DestType. + mlir::Value emitComplexToComplexCast(mlir::Value value, QualType srcType, + QualType destType, SourceLocation loc); + + /// Emit a cast from scalar value Val to DestType. + mlir::Value emitScalarToComplexCast(mlir::Value value, QualType srcType, + QualType destType, SourceLocation loc); + mlir::Value VisitAbstractConditionalOperator(const AbstractConditionalOperator *e); mlir::Value VisitArraySubscriptExpr(Expr *e); @@ -52,28 +61,33 @@ public: mlir::Value VisitGenericSelectionExpr(GenericSelectionExpr *e); mlir::Value VisitImplicitCastExpr(ImplicitCastExpr *e); mlir::Value VisitInitListExpr(const InitListExpr *e); + + mlir::Value VisitCompoundLiteralExpr(CompoundLiteralExpr *e) { + return emitLoadOfLValue(e); + } + mlir::Value VisitImaginaryLiteral(const ImaginaryLiteral *il); mlir::Value VisitParenExpr(ParenExpr *e); mlir::Value VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *e); - mlir::Value VisitPrePostIncDec(const UnaryOperator *e, bool isInc, + mlir::Value VisitPrePostIncDec(const UnaryOperator *e, cir::UnaryOpKind op, bool isPre); mlir::Value VisitUnaryPostDec(const UnaryOperator *e) { - return VisitPrePostIncDec(e, false, false); + return VisitPrePostIncDec(e, cir::UnaryOpKind::Dec, false); } mlir::Value VisitUnaryPostInc(const UnaryOperator *e) { - return VisitPrePostIncDec(e, true, false); + return VisitPrePostIncDec(e, cir::UnaryOpKind::Inc, false); } mlir::Value VisitUnaryPreDec(const UnaryOperator *e) { - return VisitPrePostIncDec(e, false, true); + return VisitPrePostIncDec(e, cir::UnaryOpKind::Dec, true); } mlir::Value VisitUnaryPreInc(const UnaryOperator *e) { - return VisitPrePostIncDec(e, true, true); + return VisitPrePostIncDec(e, cir::UnaryOpKind::Inc, true); } mlir::Value VisitUnaryDeref(const Expr *e); @@ -159,14 +173,110 @@ LValue ComplexExprEmitter::emitBinAssignLValue(const BinaryOperator *e, mlir::Value ComplexExprEmitter::emitCast(CastKind ck, Expr *op, QualType destTy) { switch (ck) { + case CK_Dependent: + llvm_unreachable("dependent type must be resolved before the CIR codegen"); + case CK_NoOp: case CK_LValueToRValue: return Visit(op); - default: - break; + + case CK_AtomicToNonAtomic: + case CK_NonAtomicToAtomic: + case CK_UserDefinedConversion: { + cgf.cgm.errorNYI( + "ComplexExprEmitter::emitCast Atmoic & UserDefinedConversion"); + return {}; } - cgf.cgm.errorNYI("ComplexType Cast"); - return {}; + + case CK_LValueBitCast: { + cgf.cgm.errorNYI("ComplexExprEmitter::emitCast CK_LValueBitCast"); + return {}; + } + + case CK_LValueToRValueBitCast: { + LValue sourceLVal = cgf.emitLValue(op); + Address addr = sourceLVal.getAddress().withElementType( + builder, cgf.convertTypeForMem(destTy)); + LValue destLV = cgf.makeAddrLValue(addr, destTy); + assert(!cir::MissingFeatures::opTBAA()); + return emitLoadOfLValue(destLV, op->getExprLoc()); + } + + case CK_BitCast: + case CK_BaseToDerived: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_Dynamic: + case CK_ToUnion: + case CK_ArrayToPointerDecay: + case CK_FunctionToPointerDecay: + case CK_NullToPointer: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_MemberPointerToBoolean: + case CK_ReinterpretMemberPointer: + case CK_ConstructorConversion: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_PointerToBoolean: + case CK_ToVoid: + case CK_VectorSplat: + case CK_IntegralCast: + case CK_BooleanToSignedIntegral: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_ARCProduceObject: + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: + case CK_ARCExtendBlockObject: + case CK_CopyAndAutoreleaseBlockObject: + case CK_BuiltinFnToFnPtr: + case CK_ZeroToOCLOpaqueType: + case CK_AddressSpaceConversion: + case CK_IntToOCLSampler: + case CK_FloatingToFixedPoint: + case CK_FixedPointToFloating: + case CK_FixedPointCast: + case CK_FixedPointToBoolean: + case CK_FixedPointToIntegral: + case CK_IntegralToFixedPoint: + case CK_MatrixCast: + case CK_HLSLVectorTruncation: + case CK_HLSLArrayRValue: + case CK_HLSLElementwiseCast: + case CK_HLSLAggregateSplatCast: + llvm_unreachable("invalid cast kind for complex value"); + + case CK_FloatingRealToComplex: + case CK_IntegralRealToComplex: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + return emitScalarToComplexCast(cgf.emitScalarExpr(op), op->getType(), + destTy, op->getExprLoc()); + } + + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: { + assert(!cir::MissingFeatures::cgFPOptionsRAII()); + return emitComplexToComplexCast(Visit(op), op->getType(), destTy, + op->getExprLoc()); + } + } + + llvm_unreachable("unknown cast resulting in complex value"); } mlir::Value ComplexExprEmitter::emitConstant( @@ -202,6 +312,49 @@ void ComplexExprEmitter::emitStoreOfComplex(mlir::Location loc, mlir::Value val, builder.createStore(loc, val, destAddr); } +mlir::Value ComplexExprEmitter::emitComplexToComplexCast(mlir::Value val, + QualType srcType, + QualType destType, + SourceLocation loc) { + if (srcType == destType) + return val; + + // Get the src/dest element type. + QualType srcElemTy = srcType->castAs<ComplexType>()->getElementType(); + QualType destElemTy = destType->castAs<ComplexType>()->getElementType(); + + cir::CastKind castOpKind; + if (srcElemTy->isFloatingType() && destElemTy->isFloatingType()) + castOpKind = cir::CastKind::float_complex; + else if (srcElemTy->isFloatingType() && destElemTy->isIntegerType()) + castOpKind = cir::CastKind::float_complex_to_int_complex; + else if (srcElemTy->isIntegerType() && destElemTy->isFloatingType()) + castOpKind = cir::CastKind::int_complex_to_float_complex; + else if (srcElemTy->isIntegerType() && destElemTy->isIntegerType()) + castOpKind = cir::CastKind::int_complex; + else + llvm_unreachable("unexpected src type or dest type"); + + return builder.createCast(cgf.getLoc(loc), castOpKind, val, + cgf.convertType(destType)); +} + +mlir::Value ComplexExprEmitter::emitScalarToComplexCast(mlir::Value val, + QualType srcType, + QualType destType, + SourceLocation loc) { + cir::CastKind castOpKind; + if (srcType->isFloatingType()) + castOpKind = cir::CastKind::float_to_complex; + else if (srcType->isIntegerType()) + castOpKind = cir::CastKind::int_to_complex; + else + llvm_unreachable("unexpected src type"); + + return builder.createCast(cgf.getLoc(loc), castOpKind, val, + cgf.convertType(destType)); +} + mlir::Value ComplexExprEmitter::VisitAbstractConditionalOperator( const AbstractConditionalOperator *e) { mlir::Value condValue = Visit(e->getCond()); @@ -355,9 +508,10 @@ mlir::Value ComplexExprEmitter::VisitSubstNonTypeTemplateParmExpr( } mlir::Value ComplexExprEmitter::VisitPrePostIncDec(const UnaryOperator *e, - bool isInc, bool isPre) { + cir::UnaryOpKind op, + bool isPre) { LValue lv = cgf.emitLValue(e->getSubExpr()); - return cgf.emitComplexPrePostIncDec(e, lv, isInc, isPre); + return cgf.emitComplexPrePostIncDec(e, lv, op, isPre); } mlir::Value ComplexExprEmitter::VisitUnaryDeref(const Expr *e) { @@ -449,12 +603,15 @@ mlir::Value CIRGenFunction::emitComplexExpr(const Expr *e) { } mlir::Value CIRGenFunction::emitComplexPrePostIncDec(const UnaryOperator *e, - LValue lv, bool isInc, + LValue lv, + cir::UnaryOpKind op, bool isPre) { + assert(op == cir::UnaryOpKind::Inc || + op == cir::UnaryOpKind::Dec && "Invalid UnaryOp kind for ComplexType"); + mlir::Value inVal = emitLoadOfComplex(lv, e->getExprLoc()); mlir::Location loc = getLoc(e->getExprLoc()); - auto opKind = isInc ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec; - mlir::Value incVal = builder.createUnaryOp(loc, opKind, inVal); + mlir::Value incVal = builder.createUnaryOp(loc, op, inVal); // Store the updated result through the lvalue. emitStoreOfComplex(loc, incVal, lv, /*isInit=*/false); @@ -467,6 +624,15 @@ mlir::Value CIRGenFunction::emitComplexPrePostIncDec(const UnaryOperator *e, return isPre ? incVal : inVal; } +void CIRGenFunction::emitComplexExprIntoLValue(const Expr *e, LValue dest, + bool isInit) { + assert(e && getComplexType(e->getType()) && + "Invalid complex expression to emit"); + ComplexExprEmitter emitter(*this); + mlir::Value value = emitter.Visit(const_cast<Expr *>(e)); + emitter.emitStoreOfComplex(getLoc(e->getExprLoc()), value, dest, isInit); +} + mlir::Value CIRGenFunction::emitLoadOfComplex(LValue src, SourceLocation loc) { return ComplexExprEmitter(*this).emitLoadOfLValue(src, loc); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index 9e13b4c..2523b0f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -88,6 +88,10 @@ public: // Utilities //===--------------------------------------------------------------------===// + mlir::Value emitComplexToScalarConversion(mlir::Location loc, + mlir::Value value, CastKind kind, + QualType destTy); + mlir::Value emitPromotedValue(mlir::Value result, QualType promotionType) { return builder.createFloatingCast(result, cgf.convertType(promotionType)); } @@ -233,6 +237,10 @@ public: mlir::Value VisitMemberExpr(MemberExpr *e); + mlir::Value VisitCompoundLiteralExpr(CompoundLiteralExpr *e) { + return emitLoadOfLValue(e); + } + mlir::Value VisitInitListExpr(InitListExpr *e); mlir::Value VisitExplicitCastExpr(ExplicitCastExpr *e) { @@ -383,22 +391,22 @@ public: // Unary Operators. mlir::Value VisitUnaryPostDec(const UnaryOperator *e) { LValue lv = cgf.emitLValue(e->getSubExpr()); - return emitScalarPrePostIncDec(e, lv, false, false); + return emitScalarPrePostIncDec(e, lv, cir::UnaryOpKind::Dec, false); } mlir::Value VisitUnaryPostInc(const UnaryOperator *e) { LValue lv = cgf.emitLValue(e->getSubExpr()); - return emitScalarPrePostIncDec(e, lv, true, false); + return emitScalarPrePostIncDec(e, lv, cir::UnaryOpKind::Inc, false); } mlir::Value VisitUnaryPreDec(const UnaryOperator *e) { LValue lv = cgf.emitLValue(e->getSubExpr()); - return emitScalarPrePostIncDec(e, lv, false, true); + return emitScalarPrePostIncDec(e, lv, cir::UnaryOpKind::Dec, true); } mlir::Value VisitUnaryPreInc(const UnaryOperator *e) { LValue lv = cgf.emitLValue(e->getSubExpr()); - return emitScalarPrePostIncDec(e, lv, true, true); + return emitScalarPrePostIncDec(e, lv, cir::UnaryOpKind::Inc, true); } mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv, - bool isInc, bool isPre) { + cir::UnaryOpKind kind, bool isPre) { if (cgf.getLangOpts().OpenMP) cgf.cgm.errorNYI(e->getSourceRange(), "inc/dec OpenMP"); @@ -427,7 +435,7 @@ public: // -> bool = ((int)bool + 1 != 0) // An interesting aspect of this is that increment is always true. // Decrement does not have this property. - if (isInc && type->isBooleanType()) { + if (kind == cir::UnaryOpKind::Inc && type->isBooleanType()) { value = builder.getTrue(cgf.getLoc(e->getExprLoc())); } else if (type->isIntegerType()) { QualType promotedType; @@ -458,7 +466,7 @@ public: assert(!cir::MissingFeatures::sanitizers()); if (e->canOverflow() && type->isSignedIntegerOrEnumerationType()) { - value = emitIncDecConsiderOverflowBehavior(e, value, isInc); + value = emitIncDecConsiderOverflowBehavior(e, value, kind); } else { cir::UnaryOpKind kind = e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec; @@ -480,7 +488,7 @@ public: // For everything else, we can just do a simple increment. mlir::Location loc = cgf.getLoc(e->getSourceRange()); CIRGenBuilderTy &builder = cgf.getBuilder(); - int amount = (isInc ? 1 : -1); + int amount = kind == cir::UnaryOpKind::Inc ? 1 : -1; mlir::Value amt = builder.getSInt32(amount, loc); assert(!cir::MissingFeatures::sanitizers()); value = builder.createPtrStride(loc, value, amt); @@ -500,8 +508,8 @@ public: if (mlir::isa<cir::SingleType, cir::DoubleType>(value.getType())) { // Create the inc/dec operation. // NOTE(CIR): clang calls CreateAdd but folds this to a unary op - cir::UnaryOpKind kind = - (isInc ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec); + assert(kind == cir::UnaryOpKind::Inc || + kind == cir::UnaryOpKind::Dec && "Invalid UnaryOp kind"); value = emitUnaryOp(e, kind, value); } else { cgf.cgm.errorNYI(e->getSourceRange(), "Unary inc/dec other fp type"); @@ -532,9 +540,9 @@ public: mlir::Value emitIncDecConsiderOverflowBehavior(const UnaryOperator *e, mlir::Value inVal, - bool isInc) { - cir::UnaryOpKind kind = - e->isIncrementOp() ? cir::UnaryOpKind::Inc : cir::UnaryOpKind::Dec; + cir::UnaryOpKind kind) { + assert(kind == cir::UnaryOpKind::Inc || + kind == cir::UnaryOpKind::Dec && "Invalid UnaryOp kind"); switch (cgf.getLangOpts().getSignedOverflowBehavior()) { case LangOptions::SOB_Defined: return emitUnaryOp(e, kind, inVal, /*nsw=*/false); @@ -1121,7 +1129,7 @@ LValue ScalarExprEmitter::emitCompoundAssignLValue( // 'An assignment expression has the value of the left operand after the // assignment...'. if (lhsLV.isBitField()) - cgf.cgm.errorNYI(e->getSourceRange(), "store through bitfield lvalue"); + cgf.emitStoreThroughBitfieldLValue(RValue::get(result), lhsLV); else cgf.emitStoreThroughLValue(RValue::get(result), lhsLV); @@ -1131,6 +1139,31 @@ LValue ScalarExprEmitter::emitCompoundAssignLValue( return lhsLV; } +mlir::Value ScalarExprEmitter::emitComplexToScalarConversion(mlir::Location lov, + mlir::Value value, + CastKind kind, + QualType destTy) { + cir::CastKind castOpKind; + switch (kind) { + case CK_FloatingComplexToReal: + castOpKind = cir::CastKind::float_complex_to_real; + break; + case CK_IntegralComplexToReal: + castOpKind = cir::CastKind::int_complex_to_real; + break; + case CK_FloatingComplexToBoolean: + castOpKind = cir::CastKind::float_complex_to_bool; + break; + case CK_IntegralComplexToBoolean: + castOpKind = cir::CastKind::int_complex_to_bool; + break; + default: + llvm_unreachable("invalid complex-to-scalar cast kind"); + } + + return builder.createCast(lov, castOpKind, value, cgf.convertType(destTy)); +} + mlir::Value ScalarExprEmitter::emitPromoted(const Expr *e, QualType promotionType) { e = e->IgnoreParens(); @@ -1754,6 +1787,15 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { ce->getExprLoc(), opts); } + case CK_FloatingComplexToReal: + case CK_IntegralComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_IntegralComplexToBoolean: { + mlir::Value value = cgf.emitComplexExpr(subExpr); + return emitComplexToScalarConversion(cgf.getLoc(ce->getExprLoc()), value, + kind, destTy); + } + case CK_FloatingRealToComplex: case CK_FloatingComplexCast: case CK_IntegralRealToComplex: @@ -2147,8 +2189,9 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( } mlir::Value CIRGenFunction::emitScalarPrePostIncDec(const UnaryOperator *e, - LValue lv, bool isInc, + LValue lv, + cir::UnaryOpKind kind, bool isPre) { return ScalarExprEmitter(*this, builder) - .emitScalarPrePostIncDec(e, lv, isInc, isPre); + .emitScalarPrePostIncDec(e, lv, kind, isPre); } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index e532b9d..b4b95d6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -26,7 +26,11 @@ namespace clang::CIRGen { CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, bool suppressNewContext) - : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) {} + : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder) { + ehStack.setCGF(this); + currentCleanupStackDepth = 0; + assert(ehStack.getStackDepth() == 0); +} CIRGenFunction::~CIRGenFunction() {} @@ -227,6 +231,14 @@ void CIRGenFunction::LexicalScope::cleanup() { CIRGenBuilderTy &builder = cgf.builder; LexicalScope *localScope = cgf.curLexScope; + auto applyCleanup = [&]() { + if (performCleanup) { + // ApplyDebugLocation + assert(!cir::MissingFeatures::generateDebugInfo()); + forceCleanup(); + } + }; + if (returnBlock != nullptr) { // Write out the return block, which loads the value from `__retval` and // issues the `cir.return`. @@ -235,32 +247,42 @@ void CIRGenFunction::LexicalScope::cleanup() { (void)emitReturn(*returnLoc); } - mlir::Block *curBlock = builder.getBlock(); - if (isGlobalInit() && !curBlock) - return; - if (curBlock->mightHaveTerminator() && curBlock->getTerminator()) - return; - - // Get rid of any empty block at the end of the scope. - bool entryBlock = builder.getInsertionBlock()->isEntryBlock(); - if (!entryBlock && curBlock->empty()) { - curBlock->erase(); - if (returnBlock != nullptr && returnBlock->getUses().empty()) - returnBlock->erase(); - return; - } - - // Reached the end of the scope. - { + auto insertCleanupAndLeave = [&](mlir::Block *insPt) { mlir::OpBuilder::InsertionGuard guard(builder); - builder.setInsertionPointToEnd(curBlock); + builder.setInsertionPointToEnd(insPt); + + // If we still don't have a cleanup block, it means that `applyCleanup` + // below might be able to get us one. + mlir::Block *cleanupBlock = localScope->getCleanupBlock(builder); + + // Leverage and defers to RunCleanupsScope's dtor and scope handling. + applyCleanup(); + + // If we now have one after `applyCleanup`, hook it up properly. + if (!cleanupBlock && localScope->getCleanupBlock(builder)) { + cleanupBlock = localScope->getCleanupBlock(builder); + builder.create<cir::BrOp>(insPt->back().getLoc(), cleanupBlock); + if (!cleanupBlock->mightHaveTerminator()) { + mlir::OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointToEnd(cleanupBlock); + builder.create<cir::YieldOp>(localScope->endLoc); + } + } if (localScope->depth == 0) { // Reached the end of the function. if (returnBlock != nullptr) { - if (returnBlock->getUses().empty()) + if (returnBlock->getUses().empty()) { returnBlock->erase(); - else { + } else { + // Thread return block via cleanup block. + if (cleanupBlock) { + for (mlir::BlockOperand &blockUse : returnBlock->getUses()) { + cir::BrOp brOp = mlir::cast<cir::BrOp>(blockUse.getOwner()); + brOp.setSuccessor(cleanupBlock); + } + } + builder.create<cir::BrOp>(*returnLoc, returnBlock); return; } @@ -268,13 +290,50 @@ void CIRGenFunction::LexicalScope::cleanup() { emitImplicitReturn(); return; } - // Reached the end of a non-function scope. Some scopes, such as those - // used with the ?: operator, can return a value. - if (!localScope->isTernary() && !curBlock->mightHaveTerminator()) { + + // End of any local scope != function + // Ternary ops have to deal with matching arms for yielding types + // and do return a value, it must do its own cir.yield insertion. + if (!localScope->isTernary() && !insPt->mightHaveTerminator()) { !retVal ? builder.create<cir::YieldOp>(localScope->endLoc) : builder.create<cir::YieldOp>(localScope->endLoc, retVal); } + }; + + // If a cleanup block has been created at some point, branch to it + // and set the insertion point to continue at the cleanup block. + // Terminators are then inserted either in the cleanup block or + // inline in this current block. + mlir::Block *cleanupBlock = localScope->getCleanupBlock(builder); + if (cleanupBlock) + insertCleanupAndLeave(cleanupBlock); + + // Now deal with any pending block wrap up like implicit end of + // scope. + + mlir::Block *curBlock = builder.getBlock(); + if (isGlobalInit() && !curBlock) + return; + if (curBlock->mightHaveTerminator() && curBlock->getTerminator()) + return; + + // Get rid of any empty block at the end of the scope. + bool entryBlock = builder.getInsertionBlock()->isEntryBlock(); + if (!entryBlock && curBlock->empty()) { + curBlock->erase(); + if (returnBlock != nullptr && returnBlock->getUses().empty()) + returnBlock->erase(); + return; } + + // If there's a cleanup block, branch to it, nothing else to do. + if (cleanupBlock) { + builder.create<cir::BrOp>(curBlock->back().getLoc(), cleanupBlock); + return; + } + + // No pre-existent cleanup block, emit cleanup code and yield/return. + insertCleanupAndLeave(curBlock); } cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) { @@ -408,7 +467,19 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, } } -void CIRGenFunction::finishFunction(SourceLocation endLoc) {} +void CIRGenFunction::finishFunction(SourceLocation endLoc) { + // Pop any cleanups that might have been associated with the + // parameters. Do this in whatever block we're currently in; it's + // important to do this before we enter the return block or return + // edges will be *really* confused. + // TODO(cir): Use prologueCleanupDepth here. + bool hasCleanups = ehStack.getStackDepth() != currentCleanupStackDepth; + if (hasCleanups) { + assert(!cir::MissingFeatures::generateDebugInfo()); + // FIXME(cir): should we clearInsertionPoint? breaks many testcases + popCleanupBlocks(currentCleanupStackDepth); + } +} mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) { auto result = mlir::LogicalResult::success(); @@ -593,11 +664,12 @@ void CIRGenFunction::emitDestructorBody(FunctionArgList &args) { assert(!cir::MissingFeatures::dtorCleanups()); - // TODO(cir): A complete destructor is supposed to call the base destructor. - // Since we have to emit both dtor kinds we just fall through for now and. - // As long as we don't support virtual bases this should be functionally - // equivalent. - assert(!cir::MissingFeatures::completeDtors()); + if (!isTryBody) { + QualType thisTy = dtor->getFunctionObjectParameterType(); + emitCXXDestructorCall(dtor, Dtor_Base, /*forVirtualBase=*/false, + /*delegating=*/false, loadCXXThisAddress(), thisTy); + break; + } // Fallthrough: act like we're in the base variant. [[fallthrough]]; @@ -698,6 +770,8 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { return emitStringLiteralLValue(cast<StringLiteral>(e)); case Expr::MemberExprClass: return emitMemberExpr(cast<MemberExpr>(e)); + case Expr::CompoundLiteralExprClass: + return emitCompoundLiteralLValue(cast<CompoundLiteralExpr>(e)); case Expr::BinaryOperatorClass: return emitBinaryOperatorLValue(cast<BinaryOperator>(e)); case Expr::CompoundAssignOperatorClass: { @@ -805,4 +879,48 @@ bool CIRGenFunction::shouldNullCheckClassCastValue(const CastExpr *ce) { return true; } +/// Computes the length of an array in elements, as well as the base +/// element type and a properly-typed first element pointer. +mlir::Value +CIRGenFunction::emitArrayLength(const clang::ArrayType *origArrayType, + QualType &baseType, Address &addr) { + const clang::ArrayType *arrayType = origArrayType; + + // If it's a VLA, we have to load the stored size. Note that + // this is the size of the VLA in bytes, not its size in elements. + if (isa<VariableArrayType>(arrayType)) { + assert(cir::MissingFeatures::vlas()); + cgm.errorNYI(*currSrcLoc, "VLAs"); + return builder.getConstInt(*currSrcLoc, SizeTy, 0); + } + + uint64_t countFromCLAs = 1; + QualType eltType; + + auto cirArrayType = mlir::dyn_cast<cir::ArrayType>(addr.getElementType()); + + while (cirArrayType) { + assert(isa<ConstantArrayType>(arrayType)); + countFromCLAs *= cirArrayType.getSize(); + eltType = arrayType->getElementType(); + + cirArrayType = + mlir::dyn_cast<cir::ArrayType>(cirArrayType.getElementType()); + + arrayType = getContext().getAsArrayType(arrayType->getElementType()); + assert((!cirArrayType || arrayType) && + "CIR and Clang types are out-of-sync"); + } + + if (arrayType) { + // From this point onwards, the Clang array type has been emitted + // as some other type (probably a packed struct). Compute the array + // size, and just emit the 'begin' expression as a bitcast. + cgm.errorNYI(*currSrcLoc, "length for non-array underlying types"); + } + + baseType = eltType; + return builder.getConstInt(*currSrcLoc, SizeTy, countFromCLAs); +} + } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 9541f4f..4891c74 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -18,6 +18,7 @@ #include "CIRGenModule.h" #include "CIRGenTypeCache.h" #include "CIRGenValue.h" +#include "EHScopeStack.h" #include "Address.h" @@ -61,6 +62,9 @@ public: /// The compiler-generated variable that holds the return value. std::optional<mlir::Value> fnRetAlloca; + /// Tracks function scope overall cleanup handling. + EHScopeStack ehStack; + /// CXXThisDecl - When generating code for a C++ member function, /// this will hold the implicit 'this' declaration. ImplicitParamDecl *cxxabiThisDecl = nullptr; @@ -595,14 +599,65 @@ public: FunctionArgList args, clang::SourceLocation loc, clang::SourceLocation startLoc); + /// Takes the old cleanup stack size and emits the cleanup blocks + /// that have been added. + void popCleanupBlocks(size_t oldCleanupStackDepth); + void popCleanupBlock(); + + /// Enters a new scope for capturing cleanups, all of which + /// will be executed once the scope is exited. + class RunCleanupsScope { + size_t cleanupStackDepth, oldCleanupStackDepth; + + protected: + bool performCleanup; + + private: + RunCleanupsScope(const RunCleanupsScope &) = delete; + void operator=(const RunCleanupsScope &) = delete; + + protected: + CIRGenFunction &cgf; + + /// Enter a new cleanup scope. + explicit RunCleanupsScope(CIRGenFunction &cgf) + : performCleanup(true), cgf(cgf) { + cleanupStackDepth = cgf.ehStack.getStackDepth(); + oldCleanupStackDepth = cgf.currentCleanupStackDepth; + cgf.currentCleanupStackDepth = cleanupStackDepth; + } + + /// Exit this cleanup scope, emitting any accumulated cleanups. + ~RunCleanupsScope() { + if (performCleanup) + forceCleanup(); + } + + /// Force the emission of cleanups now, instead of waiting + /// until this object is destroyed. + void forceCleanup() { + assert(performCleanup && "Already forced cleanup"); + { + mlir::OpBuilder::InsertionGuard guard(cgf.getBuilder()); + cgf.popCleanupBlocks(cleanupStackDepth); + performCleanup = false; + cgf.currentCleanupStackDepth = oldCleanupStackDepth; + } + } + }; + + // Cleanup stack depth of the RunCleanupsScope that was pushed most recently. + size_t currentCleanupStackDepth; + +public: /// Represents a scope, including function bodies, compound statements, and /// the substatements of if/while/do/for/switch/try statements. This class /// handles any automatic cleanup, along with the return value. - struct LexicalScope { + struct LexicalScope : public RunCleanupsScope { private: - // TODO(CIR): This will live in the base class RunCleanupScope once that - // class is upstreamed. - CIRGenFunction &cgf; + // Block containing cleanup code for things initialized in this + // lexical context (scope). + mlir::Block *cleanupBlock = nullptr; // Points to the scope entry block. This is useful, for instance, for // helping to insert allocas before finalizing any recursive CodeGen from @@ -632,8 +687,8 @@ public: unsigned depth = 0; LexicalScope(CIRGenFunction &cgf, mlir::Location loc, mlir::Block *eb) - : cgf(cgf), entryBlock(eb), parentScope(cgf.curLexScope), beginLoc(loc), - endLoc(loc) { + : RunCleanupsScope(cgf), entryBlock(eb), parentScope(cgf.curLexScope), + beginLoc(loc), endLoc(loc) { assert(entryBlock && "LexicalScope requires an entry block"); cgf.curLexScope = this; @@ -671,6 +726,27 @@ public: void setAsSwitch() { scopeKind = Kind::Switch; } void setAsTernary() { scopeKind = Kind::Ternary; } + // Lazy create cleanup block or return what's available. + mlir::Block *getOrCreateCleanupBlock(mlir::OpBuilder &builder) { + if (cleanupBlock) + return cleanupBlock; + cleanupBlock = createCleanupBlock(builder); + return cleanupBlock; + } + + mlir::Block *getCleanupBlock(mlir::OpBuilder &builder) { + return cleanupBlock; + } + + mlir::Block *createCleanupBlock(mlir::OpBuilder &builder) { + // Create the cleanup block but dont hook it up around just yet. + mlir::OpBuilder::InsertionGuard guard(builder); + mlir::Region *r = builder.getBlock() ? builder.getBlock()->getParent() + : &cgf.curFn->getRegion(0); + cleanupBlock = builder.createBlock(r); + return cleanupBlock; + } + // --- // Return handling. // --- @@ -721,6 +797,12 @@ public: LexicalScope *curLexScope = nullptr; + typedef void Destroyer(CIRGenFunction &cgf, Address addr, QualType ty); + + static Destroyer destroyCXXObject; + + Destroyer *getDestroyer(clang::QualType::DestructionKind kind); + /// ---------------------- /// CIR emit functions /// ---------------------- @@ -757,10 +839,17 @@ public: RValue emitAnyExpr(const clang::Expr *e, AggValueSlot aggSlot = AggValueSlot::ignored()); + /// Emits the code necessary to evaluate an arbitrary expression into the + /// given memory location. + void emitAnyExprToMem(const Expr *e, Address location, Qualifiers quals, + bool isInitializer); + /// Similarly to emitAnyExpr(), however, the result will always be accessible /// even if no aggregate location is provided. RValue emitAnyExprToTemp(const clang::Expr *e); + mlir::Value emitArrayLength(const clang::ArrayType *arrayType, + QualType &baseType, Address &addr); LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e); Address emitArrayToPointerDecay(const Expr *array); @@ -774,6 +863,8 @@ public: void emitAutoVarCleanups(const AutoVarEmission &emission); void emitAutoVarInit(const AutoVarEmission &emission); + void emitAutoVarTypeCleanup(const AutoVarEmission &emission, + clang::QualType::DestructionKind dtorKind); void emitBaseInitializer(mlir::Location loc, const CXXRecordDecl *classDecl, CXXCtorInitializer *baseInit); @@ -828,8 +919,12 @@ public: mlir::Value emitCheckedArgForAssume(const Expr *e); LValue emitCompoundAssignmentLValue(const clang::CompoundAssignOperator *e); + LValue emitCompoundLiteralLValue(const CompoundLiteralExpr *e); void emitConstructorBody(FunctionArgList &args); + + void emitDestroy(Address addr, QualType type, Destroyer *destroyer); + void emitDestructorBody(FunctionArgList &args); mlir::LogicalResult emitContinueStmt(const clang::ContinueStmt &s); @@ -837,6 +932,16 @@ public: void emitCXXConstructExpr(const clang::CXXConstructExpr *e, AggValueSlot dest); + void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + const clang::ArrayType *arrayType, + Address arrayBegin, const CXXConstructExpr *e, + bool newPointerIsChecked, + bool zeroInitialize = false); + void emitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + mlir::Value numElements, Address arrayBase, + const CXXConstructExpr *e, + bool newPointerIsChecked, + bool zeroInitialize); void emitCXXConstructorCall(const clang::CXXConstructorDecl *d, clang::CXXCtorType type, bool forVirtualBase, bool delegating, AggValueSlot thisAVS, @@ -847,6 +952,15 @@ public: bool delegating, Address thisAddr, CallArgList &args, clang::SourceLocation loc); + void emitCXXDestructorCall(const CXXDestructorDecl *dd, CXXDtorType type, + bool forVirtualBase, bool delegating, + Address thisAddr, QualType thisTy); + + RValue emitCXXDestructorCall(GlobalDecl dtor, const CIRGenCallee &callee, + mlir::Value thisVal, QualType thisTy, + mlir::Value implicitParam, + QualType implicitParamTy, const CallExpr *e); + mlir::LogicalResult emitCXXForRangeStmt(const CXXForRangeStmt &s, llvm::ArrayRef<const Attr *> attrs); @@ -911,7 +1025,7 @@ public: mlir::Value emitScalarExpr(const clang::Expr *e); mlir::Value emitScalarPrePostIncDec(const UnaryOperator *e, LValue lv, - bool isInc, bool isPre); + cir::UnaryOpKind kind, bool isPre); /// Build a debug stoppoint if we are emitting debug info. void emitStopPoint(const Stmt *s); @@ -930,8 +1044,10 @@ public: /// returning the result. mlir::Value emitComplexExpr(const Expr *e); + void emitComplexExprIntoLValue(const Expr *e, LValue dest, bool isInit); + mlir::Value emitComplexPrePostIncDec(const UnaryOperator *e, LValue lv, - bool isInc, bool isPre); + cir::UnaryOpKind op, bool isPre); LValue emitComplexAssignmentLValue(const BinaryOperator *e); diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 1496d87..e5e4c68 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -46,6 +46,11 @@ public: void emitCXXDestructors(const clang::CXXDestructorDecl *d) override; void emitCXXStructor(clang::GlobalDecl gd) override; + void emitDestructorCall(CIRGenFunction &cgf, const CXXDestructorDecl *dd, + CXXDtorType type, bool forVirtualBase, + bool delegating, Address thisAddr, + QualType thisTy) override; + bool useThunkForDtorVariant(const CXXDestructorDecl *dtor, CXXDtorType dt) const override { // Itanium does not emit any destructor variant as an inline thunk. @@ -108,8 +113,6 @@ static StructorCIRGen getCIRGenToUse(CIRGenModule &cgm, GlobalDecl aliasDecl; if (const auto *dd = dyn_cast<CXXDestructorDecl>(md)) { - // The assignment is correct here, but other support for this is NYI. - cgm.errorNYI(md->getSourceRange(), "getCIRGenToUse: dtor"); aliasDecl = GlobalDecl(dd, Dtor_Complete); } else { const auto *cd = cast<CXXConstructorDecl>(md); @@ -240,6 +243,25 @@ bool CIRGenItaniumCXXABI::needsVTTParameter(GlobalDecl gd) { return false; } +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; + ASTContext &astContext = cgm.getASTContext(); + QualType vttTy = astContext.getPointerType(astContext.VoidPtrTy); + assert(!cir::MissingFeatures::appleKext()); + CIRGenCallee callee = + CIRGenCallee::forDirect(cgm.getAddrOfCXXStructor(gd), gd); + + cgf.emitCXXDestructorCall(gd, callee, thisAddr.getPointer(), thisTy, vtt, + vttTy, nullptr); +} + CIRGenCXXABI *clang::CIRGen::CreateCIRGenItaniumCXXABI(CIRGenModule &cgm) { switch (cgm.getASTContext().getCXXABIKind()) { case TargetCXXABI::GenericItanium: diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 9193f6f..21bee33 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -409,7 +409,10 @@ mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) { } auto *retBlock = curLexScope->getOrCreateRetBlock(*this, loc); + // This should emit a branch through the cleanup block if one exists. builder.create<cir::BrOp>(loc, retBlock); + if (ehStack.getStackDepth() != currentCleanupStackDepth) + cgm.errorNYI(s.getSourceRange(), "return with cleanup stack"); builder.createBlock(builder.getBlock()->getParent()); return mlir::success(); diff --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt index 03ea60c..ca3a329 100644 --- a/clang/lib/CIR/CodeGen/CMakeLists.txt +++ b/clang/lib/CIR/CodeGen/CMakeLists.txt @@ -11,6 +11,7 @@ add_clang_library(clangCIR CIRGenBuilder.cpp CIRGenCall.cpp CIRGenClass.cpp + CIRGenCleanup.cpp CIRGenCXX.cpp CIRGenCXXABI.cpp CIRGenCXXExpr.cpp diff --git a/clang/lib/CIR/CodeGen/EHScopeStack.h b/clang/lib/CIR/CodeGen/EHScopeStack.h new file mode 100644 index 0000000..22750ac --- /dev/null +++ b/clang/lib/CIR/CodeGen/EHScopeStack.h @@ -0,0 +1,99 @@ +//===-- EHScopeStack.h - Stack for cleanup CIR generation -------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// These classes should be the minimum interface required for other parts of +// CIR CodeGen to emit cleanups. The implementation is in CIRGenCleanup.cpp and +// other implemenentation details that are not widely needed are in +// CIRGenCleanup.h. +// +// TODO(cir): this header should be shared between LLVM and CIR codegen. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H +#define CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H + +#include "llvm/ADT/SmallVector.h" + +namespace clang::CIRGen { + +class CIRGenFunction; + +enum CleanupKind : unsigned { + /// Denotes a cleanup that should run when a scope is exited using exceptional + /// control flow (a throw statement leading to stack unwinding, ). + EHCleanup = 0x1, + + /// Denotes a cleanup that should run when a scope is exited using normal + /// control flow (falling off the end of the scope, return, goto, ...). + NormalCleanup = 0x2, + + NormalAndEHCleanup = EHCleanup | NormalCleanup, + + LifetimeMarker = 0x8, + NormalEHLifetimeMarker = LifetimeMarker | NormalAndEHCleanup, +}; + +/// A stack of scopes which respond to exceptions, including cleanups +/// and catch blocks. +class EHScopeStack { +public: + /// Information for lazily generating a cleanup. Subclasses must be + /// POD-like: cleanups will not be destructed, and they will be + /// allocated on the cleanup stack and freely copied and moved + /// around. + /// + /// Cleanup implementations should generally be declared in an + /// anonymous namespace. + class Cleanup { + // Anchor the construction vtable. + virtual void anchor(); + + public: + Cleanup(const Cleanup &) = default; + Cleanup(Cleanup &&) {} + Cleanup() = default; + + virtual ~Cleanup() = default; + + /// Emit the cleanup. For normal cleanups, this is run in the + /// same EH context as when the cleanup was pushed, i.e. the + /// immediately-enclosing context of the cleanup scope. For + /// EH cleanups, this is run in a terminate context. + /// + // \param flags cleanup kind. + virtual void emit(CIRGenFunction &cgf) = 0; + }; + + // Classic codegen has a finely tuned custom allocator and a complex stack + // management scheme. We'll probably eventually want to find a way to share + // that implementation. For now, we will use a very simplified implementation + // to get cleanups working. + llvm::SmallVector<std::unique_ptr<Cleanup>, 8> cleanupStack; + +private: + /// The CGF this Stack belong to + CIRGenFunction *cgf = nullptr; + +public: + EHScopeStack() = default; + ~EHScopeStack() = default; + + /// Push a lazily-created cleanup on the stack. + template <class T, class... As> void pushCleanup(CleanupKind kind, As... a) { + cleanupStack.push_back(std::make_unique<T>(a...)); + } + + void setCGF(CIRGenFunction *inCGF) { cgf = inCGF; } + + size_t getStackDepth() const { return cleanupStack.size(); } +}; + +} // namespace clang::CIRGen + +#endif // CLANG_LIB_CIR_CODEGEN_EHSCOPESTACK_H diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index f0416b6..cd77166 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -489,6 +489,104 @@ LogicalResult cir::CastOp::verify() { return emitOpError() << "requires two types differ in addrspace only"; return success(); } + case cir::CastKind::float_to_complex: { + if (!mlir::isa<cir::FPTypeInterface>(srcType)) + return emitOpError() << "requires !cir.float type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy) + return emitOpError() << "requires !cir.complex type for result"; + if (srcType != resComplexTy.getElementType()) + return emitOpError() << "requires source type match result element type"; + return success(); + } + case cir::CastKind::int_to_complex: { + if (!mlir::isa<cir::IntType>(srcType)) + return emitOpError() << "requires !cir.int type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy) + return emitOpError() << "requires !cir.complex type for result"; + if (srcType != resComplexTy.getElementType()) + return emitOpError() << "requires source type match result element type"; + return success(); + } + case cir::CastKind::float_complex_to_real: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy) + return emitOpError() << "requires !cir.complex type for source"; + if (!mlir::isa<cir::FPTypeInterface>(resType)) + return emitOpError() << "requires !cir.float type for result"; + if (srcComplexTy.getElementType() != resType) + return emitOpError() << "requires source element type match result type"; + return success(); + } + case cir::CastKind::int_complex_to_real: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy) + return emitOpError() << "requires !cir.complex type for source"; + if (!mlir::isa<cir::IntType>(resType)) + return emitOpError() << "requires !cir.int type for result"; + if (srcComplexTy.getElementType() != resType) + return emitOpError() << "requires source element type match result type"; + return success(); + } + case cir::CastKind::float_complex_to_bool: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex()) + return emitOpError() + << "requires floating point !cir.complex type for source"; + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + return success(); + } + case cir::CastKind::int_complex_to_bool: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isIntegerComplex()) + return emitOpError() + << "requires floating point !cir.complex type for source"; + if (!mlir::isa<cir::BoolType>(resType)) + return emitOpError() << "requires !cir.bool type for result"; + return success(); + } + case cir::CastKind::float_complex: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex()) + return emitOpError() + << "requires floating point !cir.complex type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy || !resComplexTy.isFloatingPointComplex()) + return emitOpError() + << "requires floating point !cir.complex type for result"; + return success(); + } + case cir::CastKind::float_complex_to_int_complex: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isFloatingPointComplex()) + return emitOpError() + << "requires floating point !cir.complex type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy || !resComplexTy.isIntegerComplex()) + return emitOpError() << "requires integer !cir.complex type for result"; + return success(); + } + case cir::CastKind::int_complex: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isIntegerComplex()) + return emitOpError() << "requires integer !cir.complex type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy || !resComplexTy.isIntegerComplex()) + return emitOpError() << "requires integer !cir.complex type for result"; + return success(); + } + case cir::CastKind::int_complex_to_float_complex: { + auto srcComplexTy = mlir::dyn_cast<cir::ComplexType>(srcType); + if (!srcComplexTy || !srcComplexTy.isIntegerComplex()) + return emitOpError() << "requires integer !cir.complex type for source"; + auto resComplexTy = mlir::dyn_cast<cir::ComplexType>(resType); + if (!resComplexTy || !resComplexTy.isFloatingPointComplex()) + return emitOpError() + << "requires floating point !cir.complex type for result"; + return success(); + } default: llvm_unreachable("Unknown CastOp kind?"); } diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 8f848c7..cef83ea 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -8,11 +8,14 @@ #include "PassDetail.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/CharUnits.h" #include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/Dialect/IR/CIROpsEnums.h" #include "clang/CIR/Dialect/Passes.h" +#include "clang/CIR/MissingFeatures.h" +#include <iostream> #include <memory> using namespace mlir; @@ -24,11 +27,106 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> { void runOnOperation() override; void runOnOp(mlir::Operation *op); + void lowerCastOp(cir::CastOp op); void lowerUnaryOp(cir::UnaryOp op); + void lowerArrayCtor(ArrayCtor op); + + /// + /// AST related + /// ----------- + + clang::ASTContext *astCtx; + + void setASTContext(clang::ASTContext *c) { astCtx = c; } }; } // namespace +static mlir::Value lowerScalarToComplexCast(mlir::MLIRContext &ctx, + cir::CastOp op) { + cir::CIRBaseBuilderTy builder(ctx); + builder.setInsertionPoint(op); + + mlir::Value src = op.getSrc(); + mlir::Value imag = builder.getNullValue(src.getType(), op.getLoc()); + return builder.createComplexCreate(op.getLoc(), src, imag); +} + +static mlir::Value lowerComplexToScalarCast(mlir::MLIRContext &ctx, + cir::CastOp op, + cir::CastKind elemToBoolKind) { + cir::CIRBaseBuilderTy builder(ctx); + builder.setInsertionPoint(op); + + mlir::Value src = op.getSrc(); + if (!mlir::isa<cir::BoolType>(op.getType())) + return builder.createComplexReal(op.getLoc(), src); + + // Complex cast to bool: (bool)(a+bi) => (bool)a || (bool)b + mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src); + mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src); + + cir::BoolType boolTy = builder.getBoolTy(); + mlir::Value srcRealToBool = + builder.createCast(op.getLoc(), elemToBoolKind, srcReal, boolTy); + mlir::Value srcImagToBool = + builder.createCast(op.getLoc(), elemToBoolKind, srcImag, boolTy); + return builder.createLogicalOr(op.getLoc(), srcRealToBool, srcImagToBool); +} + +static mlir::Value lowerComplexToComplexCast(mlir::MLIRContext &ctx, + cir::CastOp op, + cir::CastKind scalarCastKind) { + CIRBaseBuilderTy builder(ctx); + builder.setInsertionPoint(op); + + mlir::Value src = op.getSrc(); + auto dstComplexElemTy = + mlir::cast<cir::ComplexType>(op.getType()).getElementType(); + + mlir::Value srcReal = builder.createComplexReal(op.getLoc(), src); + mlir::Value srcImag = builder.createComplexImag(op.getLoc(), src); + + mlir::Value dstReal = builder.createCast(op.getLoc(), scalarCastKind, srcReal, + dstComplexElemTy); + mlir::Value dstImag = builder.createCast(op.getLoc(), scalarCastKind, srcImag, + dstComplexElemTy); + return builder.createComplexCreate(op.getLoc(), dstReal, dstImag); +} + +void LoweringPreparePass::lowerCastOp(cir::CastOp op) { + mlir::MLIRContext &ctx = getContext(); + mlir::Value loweredValue = [&]() -> mlir::Value { + switch (op.getKind()) { + case cir::CastKind::float_to_complex: + case cir::CastKind::int_to_complex: + return lowerScalarToComplexCast(ctx, op); + case cir::CastKind::float_complex_to_real: + case cir::CastKind::int_complex_to_real: + return lowerComplexToScalarCast(ctx, op, op.getKind()); + case cir::CastKind::float_complex_to_bool: + return lowerComplexToScalarCast(ctx, op, cir::CastKind::float_to_bool); + case cir::CastKind::int_complex_to_bool: + return lowerComplexToScalarCast(ctx, op, cir::CastKind::int_to_bool); + case cir::CastKind::float_complex: + return lowerComplexToComplexCast(ctx, op, cir::CastKind::floating); + case cir::CastKind::float_complex_to_int_complex: + return lowerComplexToComplexCast(ctx, op, cir::CastKind::float_to_int); + case cir::CastKind::int_complex: + return lowerComplexToComplexCast(ctx, op, cir::CastKind::integral); + case cir::CastKind::int_complex_to_float_complex: + return lowerComplexToComplexCast(ctx, op, cir::CastKind::int_to_float); + default: + return nullptr; + } + }(); + + if (loweredValue) { + op.replaceAllUsesWith(loweredValue); + op.erase(); + } +} + void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) { mlir::Type ty = op.getType(); if (!mlir::isa<cir::ComplexType>(ty)) @@ -71,8 +169,85 @@ void LoweringPreparePass::lowerUnaryOp(cir::UnaryOp op) { op.erase(); } +static void lowerArrayDtorCtorIntoLoop(cir::CIRBaseBuilderTy &builder, + clang::ASTContext *astCtx, + mlir::Operation *op, mlir::Type eltTy, + mlir::Value arrayAddr, + uint64_t arrayLen) { + // Generate loop to call into ctor/dtor for every element. + mlir::Location loc = op->getLoc(); + + // TODO: instead of fixed integer size, create alias for PtrDiffTy and unify + // with CIRGen stuff. + const unsigned sizeTypeSize = + astCtx->getTypeSize(astCtx->getSignedSizeType()); + auto ptrDiffTy = + cir::IntType::get(builder.getContext(), sizeTypeSize, /*isSigned=*/false); + mlir::Value numArrayElementsConst = builder.getUnsignedInt(loc, arrayLen, 64); + + auto begin = builder.create<cir::CastOp>( + loc, eltTy, cir::CastKind::array_to_ptrdecay, arrayAddr); + mlir::Value end = builder.create<cir::PtrStrideOp>(loc, eltTy, begin, + numArrayElementsConst); + + mlir::Value tmpAddr = builder.createAlloca( + loc, /*addr type*/ builder.getPointerTo(eltTy), + /*var type*/ eltTy, "__array_idx", builder.getAlignmentAttr(1)); + builder.createStore(loc, begin, tmpAddr); + + cir::DoWhileOp loop = builder.createDoWhile( + loc, + /*condBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr); + mlir::Type boolTy = cir::BoolType::get(b.getContext()); + auto cmp = builder.create<cir::CmpOp>(loc, boolTy, cir::CmpOpKind::ne, + currentElement, end); + builder.createCondition(cmp); + }, + /*bodyBuilder=*/ + [&](mlir::OpBuilder &b, mlir::Location loc) { + auto currentElement = b.create<cir::LoadOp>(loc, eltTy, tmpAddr); + + cir::CallOp ctorCall; + op->walk([&](cir::CallOp c) { ctorCall = c; }); + assert(ctorCall && "expected ctor call"); + + auto one = builder.create<cir::ConstantOp>( + loc, ptrDiffTy, cir::IntAttr::get(ptrDiffTy, 1)); + + ctorCall->moveAfter(one); + ctorCall->setOperand(0, currentElement); + + // Advance pointer and store them to temporary variable + auto nextElement = + builder.create<cir::PtrStrideOp>(loc, eltTy, currentElement, one); + builder.createStore(loc, nextElement, tmpAddr); + builder.createYield(loc); + }); + + op->replaceAllUsesWith(loop); + op->erase(); +} + +void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor op) { + cir::CIRBaseBuilderTy builder(getContext()); + builder.setInsertionPointAfter(op.getOperation()); + + mlir::Type eltTy = op->getRegion(0).getArgument(0).getType(); + assert(!cir::MissingFeatures::vlas()); + auto arrayLen = + mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize(); + lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(), + arrayLen); +} + void LoweringPreparePass::runOnOp(mlir::Operation *op) { - if (auto unary = dyn_cast<cir::UnaryOp>(op)) + if (auto arrayCtor = dyn_cast<ArrayCtor>(op)) + lowerArrayCtor(arrayCtor); + else if (auto cast = mlir::dyn_cast<cir::CastOp>(op)) + lowerCastOp(cast); + else if (auto unary = mlir::dyn_cast<cir::UnaryOp>(op)) lowerUnaryOp(unary); } @@ -82,7 +257,7 @@ void LoweringPreparePass::runOnOperation() { llvm::SmallVector<mlir::Operation *> opsToTransform; op->walk([&](mlir::Operation *op) { - if (mlir::isa<cir::UnaryOp>(op)) + if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op)) opsToTransform.push_back(op); }); @@ -93,3 +268,10 @@ void LoweringPreparePass::runOnOperation() { std::unique_ptr<Pass> mlir::createLoweringPreparePass() { return std::make_unique<LoweringPreparePass>(); } + +std::unique_ptr<Pass> +mlir::createLoweringPreparePass(clang::ASTContext *astCtx) { + auto pass = std::make_unique<LoweringPreparePass>(); + pass->setASTContext(astCtx); + return std::move(pass); +} diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp b/clang/lib/CIR/Lowering/CIRPasses.cpp index 5607abc..bb9781b 100644 --- a/clang/lib/CIR/Lowering/CIRPasses.cpp +++ b/clang/lib/CIR/Lowering/CIRPasses.cpp @@ -31,7 +31,7 @@ mlir::LogicalResult runCIRToCIRPasses(mlir::ModuleOp theModule, if (enableCIRSimplify) pm.addPass(mlir::createCIRSimplifyPass()); - pm.addPass(mlir::createLoweringPreparePass()); + pm.addPass(mlir::createLoweringPreparePass(&astContext)); pm.enableVerifier(enableVerifier); (void)mlir::applyPassManagerCLOptions(pm); diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 840e856..3cd7de0 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -460,6 +460,17 @@ mlir::LogicalResult CIRToLLVMAssumeOpLowering::matchAndRewrite( return mlir::success(); } +mlir::LogicalResult CIRToLLVMAssumeSepStorageOpLowering::matchAndRewrite( + cir::AssumeSepStorageOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const { + auto cond = rewriter.create<mlir::LLVM::ConstantOp>(op.getLoc(), + rewriter.getI1Type(), 1); + rewriter.replaceOpWithNewOp<mlir::LLVM::AssumeOp>( + op, cond, mlir::LLVM::AssumeSeparateStorageTag{}, adaptor.getPtr1(), + adaptor.getPtr2()); + return mlir::success(); +} + mlir::LogicalResult CIRToLLVMBitClrsbOpLowering::matchAndRewrite( cir::BitClrsbOp op, OpAdaptor adaptor, mlir::ConversionPatternRewriter &rewriter) const { @@ -2066,6 +2077,7 @@ void ConvertCIRToLLVMPass::runOnOperation() { patterns.add< // clang-format off CIRToLLVMAssumeOpLowering, + CIRToLLVMAssumeSepStorageOpLowering, CIRToLLVMBaseClassAddrOpLowering, CIRToLLVMBinOpLowering, CIRToLLVMBitClrsbOpLowering, @@ -2571,7 +2583,7 @@ mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite( assert(storageSize > size && "Invalid bitfield size."); mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>( - op.getLoc(), intType, adaptor.getAddr(), /* alignment */ 0, + op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(), op.getIsVolatile()); srcVal = @@ -2588,7 +2600,7 @@ mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite( } rewriter.create<mlir::LLVM::StoreOp>(op.getLoc(), srcVal, adaptor.getAddr(), - /* alignment */ 0, op.getIsVolatile()); + op.getAlignment(), op.getIsVolatile()); mlir::Type resultTy = getTypeConverter()->convertType(op.getType()); @@ -2662,7 +2674,8 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite( computeBitfieldIntType(storageType, context, storageSize); mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>( - op.getLoc(), intType, adaptor.getAddr(), 0, op.getIsVolatile()); + op.getLoc(), intType, adaptor.getAddr(), op.getAlignment(), + op.getIsVolatile()); val = rewriter.create<mlir::LLVM::BitcastOp>(op.getLoc(), intType, val); if (info.getIsSigned()) { diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h index 3faf1e9..2911ced 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h @@ -44,6 +44,16 @@ public: mlir::ConversionPatternRewriter &) const override; }; +class CIRToLLVMAssumeSepStorageOpLowering + : public mlir::OpConversionPattern<cir::AssumeSepStorageOp> { +public: + using mlir::OpConversionPattern<cir::AssumeSepStorageOp>::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(cir::AssumeSepStorageOp op, OpAdaptor, + mlir::ConversionPatternRewriter &) const override; +}; + class CIRToLLVMBitClrsbOpLowering : public mlir::OpConversionPattern<cir::BitClrsbOp> { public: |