diff options
Diffstat (limited to 'clang/lib/CIR/CodeGen')
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCall.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCall.h | 5 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenClass.cpp | 124 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenCleanup.cpp | 69 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 153 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 115 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 91 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp | 25 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 291 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenFunction.h | 117 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 2 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.cpp | 103 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenModule.h | 8 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 4 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 3 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/CMakeLists.txt | 1 | ||||
-rw-r--r-- | clang/lib/CIR/CodeGen/EHScopeStack.h | 99 |
18 files changed, 1134 insertions, 89 deletions
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index ef136f8..9049a01 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -190,6 +190,11 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID, assert(!cir::MissingFeatures::builtinCheckKind()); return emitBuiltinBitOp<cir::BitClzOp>(*this, e, /*poisonZero=*/true); + case Builtin::BI__builtin_ffs: + case Builtin::BI__builtin_ffsl: + case Builtin::BI__builtin_ffsll: + return emitBuiltinBitOp<cir::BitFfsOp>(*this, e); + case Builtin::BI__builtin_parity: case Builtin::BI__builtin_parityl: case Builtin::BI__builtin_parityll: diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index 938d143..fc208ff 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -582,6 +582,14 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo, cir::FuncOp directFuncOp; if (auto fnOp = dyn_cast<cir::FuncOp>(calleePtr)) { directFuncOp = fnOp; + } else if (auto getGlobalOp = mlir::dyn_cast<cir::GetGlobalOp>(calleePtr)) { + // FIXME(cir): This peephole optimization avoids indirect calls for + // builtins. This should be fixed in the builtin declaration instead by + // not emitting an unecessary get_global in the first place. + // However, this is also used for no-prototype functions. + mlir::Operation *globalOp = cgm.getGlobalValue(getGlobalOp.getName()); + assert(globalOp && "undefined global function"); + directFuncOp = mlir::cast<cir::FuncOp>(globalOp); } else { [[maybe_unused]] mlir::ValueTypeRange<mlir::ResultRange> resultTypes = calleePtr->getResultTypes(); diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h index bd11329..a78956b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.h +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -116,6 +116,11 @@ public: assert(isOrdinary()); return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr); } + + void setFunctionPointer(mlir::Operation *functionPtr) { + assert(isOrdinary()); + kindOrFunctionPtr = SpecialKind(reinterpret_cast<uintptr_t>(functionPtr)); + } }; /// Type for representing both the decl and type of parameters to a function. diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index fbf53db..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()); 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..6527fb5 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,152 @@ void CIRGenFunction::emitNullabilityCheck(LValue lhs, mlir::Value rhs, assert(!cir::MissingFeatures::sanitizers()); } + +/// Destroys all the elements of the given array, beginning from last to first. +/// The array cannot be zero-length. +/// +/// \param begin - a type* denoting the first element of the array +/// \param end - a type* denoting one past the end of the array +/// \param elementType - the element type of the array +/// \param destroyer - the function to call to destroy elements +void CIRGenFunction::emitArrayDestroy(mlir::Value begin, mlir::Value end, + QualType elementType, + CharUnits elementAlign, + Destroyer *destroyer) { + assert(!elementType->isArrayType()); + + // Differently from LLVM traditional codegen, use a higher level + // representation instead of lowering directly to a loop. + mlir::Type cirElementType = convertTypeForMem(elementType); + cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType); + + // Emit the dtor call that will execute for every array element. + cir::ArrayDtor::create( + builder, *currSrcLoc, begin, [&](mlir::OpBuilder &b, mlir::Location loc) { + auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc); + Address curAddr = Address(arg, cirElementType, elementAlign); + assert(!cir::MissingFeatures::dtorCleanups()); + + // Perform the actual destruction there. + destroyer(*this, curAddr, elementType); + + cir::YieldOp::create(builder, loc); + }); +} + +/// 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) { + const ArrayType *arrayType = getContext().getAsArrayType(type); + if (!arrayType) + return destroyer(*this, addr, type); + + mlir::Value length = emitArrayLength(arrayType, type, addr); + + CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement( + getContext().getTypeSizeInChars(type)); + + auto constantCount = length.getDefiningOp<cir::ConstantOp>(); + if (!constantCount) { + assert(!cir::MissingFeatures::vlas()); + cgm.errorNYI("emitDestroy: variable length array"); + return; + } + + auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue()); + // If it's constant zero, we can just skip the entire thing. + if (constIntAttr && constIntAttr.getUInt() == 0) + return; + + mlir::Value begin = addr.getPointer(); + mlir::Value end; // This will be used for future non-constant counts. + emitArrayDestroy(begin, end, type, elementAlign, destroyer); + + // If the array destroy didn't use the length op, we can erase it. + if (constantCount.use_empty()) + constantCount.erase(); +} + +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 1f64801..c18498f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -949,7 +949,6 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) { case CK_Dynamic: case CK_ToUnion: case CK_BaseToDerived: - case CK_LValueBitCast: case CK_AddressSpaceConversion: case CK_ObjCObjectLValueCast: case CK_VectorSplat: @@ -965,6 +964,18 @@ LValue CIRGenFunction::emitCastLValue(const CastExpr *e) { return {}; } + case CK_LValueBitCast: { + // This must be a reinterpret_cast (or c-style equivalent). + const auto *ce = cast<ExplicitCastExpr>(e); + + cgm.emitExplicitCastExprType(ce, this); + LValue LV = emitLValue(e->getSubExpr()); + Address V = LV.getAddress().withElementType( + builder, convertTypeForMem(ce->getTypeAsWritten()->getPointeeType())); + + return makeAddrLValue(V, e->getType(), LV.getBaseInfo()); + } + case CK_NoOp: { // CK_NoOp can model a qualification conversion, which can remove an array // bound and change the IR type. @@ -1269,7 +1280,7 @@ RValue CIRGenFunction::getUndefRValue(QualType ty) { } RValue CIRGenFunction::emitCall(clang::QualType calleeTy, - const CIRGenCallee &callee, + const CIRGenCallee &origCallee, const clang::CallExpr *e, ReturnValueSlot returnValue) { // Get the actual function type. The callee type will always be a pointer to @@ -1280,6 +1291,8 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy, calleeTy = getContext().getCanonicalType(calleeTy); auto pointeeTy = cast<PointerType>(calleeTy)->getPointeeType(); + CIRGenCallee callee = origCallee; + if (getLangOpts().CPlusPlus) assert(!cir::MissingFeatures::sanitizers()); @@ -1296,7 +1309,44 @@ RValue CIRGenFunction::emitCall(clang::QualType calleeTy, const CIRGenFunctionInfo &funcInfo = cgm.getTypes().arrangeFreeFunctionCall(args, fnType); - assert(!cir::MissingFeatures::opCallNoPrototypeFunc()); + // C99 6.5.2.2p6: + // If the expression that denotes the called function has a type that does + // not include a prototype, [the default argument promotions are performed]. + // If the number of arguments does not equal the number of parameters, the + // behavior is undefined. If the function is defined with a type that + // includes a prototype, and either the prototype ends with an ellipsis (, + // ...) or the types of the arguments after promotion are not compatible + // with the types of the parameters, the behavior is undefined. If the + // function is defined with a type that does not include a prototype, and + // the types of the arguments after promotion are not compatible with those + // of the parameters after promotion, the behavior is undefined [except in + // some trivial cases]. + // That is, in the general case, we should assume that a call through an + // unprototyped function type works like a *non-variadic* call. The way we + // make this work is to cast to the exxact type fo the promoted arguments. + if (isa<FunctionNoProtoType>(fnType)) { + assert(!cir::MissingFeatures::opCallChain()); + assert(!cir::MissingFeatures::addressSpace()); + cir::FuncType calleeTy = getTypes().getFunctionType(funcInfo); + // get non-variadic function type + calleeTy = cir::FuncType::get(calleeTy.getInputs(), + calleeTy.getReturnType(), false); + auto calleePtrTy = cir::PointerType::get(calleeTy); + + mlir::Operation *fn = callee.getFunctionPointer(); + mlir::Value addr; + if (auto funcOp = mlir::dyn_cast<cir::FuncOp>(fn)) { + addr = builder.create<cir::GetGlobalOp>( + getLoc(e->getSourceRange()), + cir::PointerType::get(funcOp.getFunctionType()), funcOp.getSymName()); + } else { + addr = fn->getResult(0); + } + + fn = builder.createBitcast(addr, calleePtrTy).getDefiningOp(); + callee.setFunctionPointer(fn); + } + assert(!cir::MissingFeatures::opCallFnInfoOpts()); assert(!cir::MissingFeatures::hip()); assert(!cir::MissingFeatures::opCallMustTail()); @@ -1657,37 +1707,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; - emitCXXConstructorCall(cd, type, forVirtualBase, delegating, dest, e); + 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); + } } RValue CIRGenFunction::emitReferenceBindingToExpr(const Expr *e) { diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 0d12c5c..51aab95 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -357,10 +357,97 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr( emitArrayInit(dest.getAddress(), arrayTy, e->getType(), e, args, arrayFiller); return; + } else if (e->getType()->isVariableArrayType()) { + cgf.cgm.errorNYI(e->getSourceRange(), + "visitCXXParenListOrInitListExpr variable array type"); + return; + } + + if (e->getType()->isArrayType()) { + cgf.cgm.errorNYI(e->getSourceRange(), + "visitCXXParenListOrInitListExpr array type"); + return; + } + + assert(e->getType()->isRecordType() && "Only support structs/unions here!"); + + // Do struct initialization; this code just sets each individual member + // to the approprate value. This makes bitfield support automatic; + // the disadvantage is that the generated code is more difficult for + // the optimizer, especially with bitfields. + unsigned numInitElements = args.size(); + RecordDecl *record = e->getType()->castAs<RecordType>()->getDecl(); + + // We'll need to enter cleanup scopes in case any of the element + // initializers throws an exception. + assert(!cir::MissingFeatures::requiresCleanups()); + + unsigned curInitIndex = 0; + + // Emit initialization of base classes. + if (auto *cxxrd = dyn_cast<CXXRecordDecl>(record)) { + assert(numInitElements >= cxxrd->getNumBases() && + "missing initializer for base class"); + if (cxxrd->getNumBases() > 0) { + cgf.cgm.errorNYI(e->getSourceRange(), + "visitCXXParenListOrInitListExpr base class init"); + return; + } + } + + LValue destLV = cgf.makeAddrLValue(dest.getAddress(), e->getType()); + + if (record->isUnion()) { + cgf.cgm.errorNYI(e->getSourceRange(), + "visitCXXParenListOrInitListExpr union type"); + return; } - cgf.cgm.errorNYI( - "visitCXXParenListOrInitListExpr Record or VariableSizeArray type"); + // Here we iterate over the fields; this makes it simpler to both + // default-initialize fields and skip over unnamed fields. + for (const FieldDecl *field : record->fields()) { + // We're done once we hit the flexible array member. + if (field->getType()->isIncompleteArrayType()) + break; + + // Always skip anonymous bitfields. + if (field->isUnnamedBitField()) + continue; + + // We're done if we reach the end of the explicit initializers, we + // have a zeroed object, and the rest of the fields are + // zero-initializable. + if (curInitIndex == numInitElements && dest.isZeroed() && + cgf.getTypes().isZeroInitializable(e->getType())) + break; + LValue lv = + cgf.emitLValueForFieldInitialization(destLV, field, field->getName()); + // We never generate write-barriers for initialized fields. + assert(!cir::MissingFeatures::setNonGC()); + + if (curInitIndex < numInitElements) { + // Store the initializer into the field. + CIRGenFunction::SourceLocRAIIObject loc{ + cgf, cgf.getLoc(record->getSourceRange())}; + emitInitializationToLValue(args[curInitIndex++], lv); + } else { + // We're out of initializers; default-initialize to null + emitNullInitializationToLValue(cgf.getLoc(e->getSourceRange()), lv); + } + + // Push a destructor if necessary. + // FIXME: if we have an array of structures, all explicitly + // initialized, we can end up pushing a linear number of cleanups. + if (field->getType().isDestructedType()) { + cgf.cgm.errorNYI(e->getSourceRange(), + "visitCXXParenListOrInitListExpr destructor"); + return; + } + + // From classic codegen, maybe not useful for CIR: + // If the GEP didn't get used because of a dead zero init or something + // else, clean it up for -O0 builds and general tidiness. + } } // TODO(cir): This could be shared with classic codegen. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp index d52da0c..a09d739 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprComplex.cpp @@ -60,7 +60,7 @@ public: mlir::Value VisitDeclRefExpr(DeclRefExpr *e); mlir::Value VisitGenericSelectionExpr(GenericSelectionExpr *e); mlir::Value VisitImplicitCastExpr(ImplicitCastExpr *e); - mlir::Value VisitInitListExpr(const InitListExpr *e); + mlir::Value VisitInitListExpr(InitListExpr *e); mlir::Value VisitCompoundLiteralExpr(CompoundLiteralExpr *e) { return emitLoadOfLValue(e); @@ -189,13 +189,20 @@ mlir::Value ComplexExprEmitter::emitCast(CastKind ck, Expr *op, } case CK_LValueBitCast: { - cgf.cgm.errorNYI("ComplexExprEmitter::emitCast CK_LValueBitCast"); - return {}; + LValue origLV = cgf.emitLValue(op); + Address addr = + origLV.getAddress().withElementType(builder, cgf.convertType(destTy)); + LValue destLV = cgf.makeAddrLValue(addr, destTy); + return emitLoadOfLValue(destLV, op->getExprLoc()); } case CK_LValueToRValueBitCast: { - cgf.cgm.errorNYI("ComplexExprEmitter::emitCast CK_LValueToRValueBitCast"); - return {}; + 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: @@ -448,7 +455,7 @@ mlir::Value ComplexExprEmitter::VisitImplicitCastExpr(ImplicitCastExpr *e) { return emitCast(e->getCastKind(), e->getSubExpr(), e->getType()); } -mlir::Value ComplexExprEmitter::VisitInitListExpr(const InitListExpr *e) { +mlir::Value ComplexExprEmitter::VisitInitListExpr(InitListExpr *e) { mlir::Location loc = cgf.getLoc(e->getExprLoc()); if (e->getNumInits() == 2) { mlir::Value real = cgf.emitScalarExpr(e->getInit(0)); @@ -456,10 +463,8 @@ mlir::Value ComplexExprEmitter::VisitInitListExpr(const InitListExpr *e) { return builder.createComplexCreate(loc, real, imag); } - if (e->getNumInits() == 1) { - cgf.cgm.errorNYI("Create Complex with InitList with size 1"); - return {}; - } + if (e->getNumInits() == 1) + return Visit(e->getInit(0)); assert(e->getNumInits() == 0 && "Unexpected number of inits"); mlir::Type complexTy = cgf.convertType(e->getType()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 3e69e56..c65d025 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(); @@ -808,4 +879,174 @@ 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); +} + +// TODO(cir): Most of this function can be shared between CIRGen +// and traditional LLVM codegen +void CIRGenFunction::emitVariablyModifiedType(QualType type) { + assert(type->isVariablyModifiedType() && + "Must pass variably modified type to EmitVLASizes!"); + + // We're going to walk down into the type and look for VLA + // expressions. + do { + assert(type->isVariablyModifiedType()); + + const Type *ty = type.getTypePtr(); + switch (ty->getTypeClass()) { + case Type::CountAttributed: + case Type::PackIndexing: + case Type::ArrayParameter: + case Type::HLSLAttributedResource: + case Type::HLSLInlineSpirv: + case Type::PredefinedSugar: + cgm.errorNYI("CIRGenFunction::emitVariablyModifiedType"); + +#define TYPE(Class, Base) +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) +#include "clang/AST/TypeNodes.inc" + llvm_unreachable( + "dependent type must be resolved before the CIR codegen"); + + // These types are never variably-modified. + case Type::Builtin: + case Type::Complex: + case Type::Vector: + case Type::ExtVector: + case Type::ConstantMatrix: + case Type::Record: + case Type::Enum: + case Type::Using: + case Type::TemplateSpecialization: + case Type::ObjCTypeParam: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + case Type::BitInt: + llvm_unreachable("type class is never variably-modified!"); + + case Type::Elaborated: + type = cast<clang::ElaboratedType>(ty)->getNamedType(); + break; + + case Type::Adjusted: + type = cast<clang::AdjustedType>(ty)->getAdjustedType(); + break; + + case Type::Decayed: + type = cast<clang::DecayedType>(ty)->getPointeeType(); + break; + + case Type::Pointer: + type = cast<clang::PointerType>(ty)->getPointeeType(); + break; + + case Type::BlockPointer: + type = cast<clang::BlockPointerType>(ty)->getPointeeType(); + break; + + case Type::LValueReference: + case Type::RValueReference: + type = cast<clang::ReferenceType>(ty)->getPointeeType(); + break; + + case Type::MemberPointer: + type = cast<clang::MemberPointerType>(ty)->getPointeeType(); + break; + + case Type::ConstantArray: + case Type::IncompleteArray: + // Losing element qualification here is fine. + type = cast<clang::ArrayType>(ty)->getElementType(); + break; + + case Type::VariableArray: { + cgm.errorNYI("CIRGenFunction::emitVariablyModifiedType VLA"); + break; + } + + case Type::FunctionProto: + case Type::FunctionNoProto: + type = cast<clang::FunctionType>(ty)->getReturnType(); + break; + + case Type::Paren: + case Type::TypeOf: + case Type::UnaryTransform: + case Type::Attributed: + case Type::BTFTagAttributed: + case Type::SubstTemplateTypeParm: + case Type::MacroQualified: + // Keep walking after single level desugaring. + type = type.getSingleStepDesugaredType(getContext()); + break; + + case Type::Typedef: + case Type::Decltype: + case Type::Auto: + case Type::DeducedTemplateSpecialization: + // Stop walking: nothing to do. + return; + + case Type::TypeOfExpr: + // Stop walking: emit typeof expression. + emitIgnoredExpr(cast<clang::TypeOfExprType>(ty)->getUnderlyingExpr()); + return; + + case Type::Atomic: + type = cast<clang::AtomicType>(ty)->getValueType(); + break; + + case Type::Pipe: + type = cast<clang::PipeType>(ty)->getElementType(); + break; + } + } while (type->isVariablyModifiedType()); +} + } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 2aceeef..603f750 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 /// ---------------------- @@ -766,6 +848,12 @@ public: /// even if no aggregate location is provided. RValue emitAnyExprToTemp(const clang::Expr *e); + void emitArrayDestroy(mlir::Value begin, mlir::Value end, + QualType elementType, CharUnits elementAlign, + Destroyer *destroyer); + + mlir::Value emitArrayLength(const clang::ArrayType *arrayType, + QualType &baseType, Address &addr); LValue emitArraySubscriptExpr(const clang::ArraySubscriptExpr *e); Address emitArrayToPointerDecay(const Expr *array); @@ -779,6 +867,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); @@ -836,6 +926,9 @@ public: 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); @@ -843,6 +936,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, @@ -1102,6 +1205,8 @@ public: /// inside a function, including static vars etc. void emitVarDecl(const clang::VarDecl &d); + void emitVariablyModifiedType(QualType ty); + mlir::LogicalResult emitWhileStmt(const clang::WhileStmt &s); /// Given an assignment `*lhs = rhs`, emit a test that checks if \p rhs is diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 6577f5f..e5e4c68 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -113,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); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 3502705..623b84f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -1103,6 +1103,60 @@ cir::GlobalLinkageKind CIRGenModule::getCIRLinkageForDeclarator( return cir::GlobalLinkageKind::ExternalLinkage; } +/// This function is called when we implement a function with no prototype, e.g. +/// "int foo() {}". If there are existing call uses of the old function in the +/// module, this adjusts them to call the new function directly. +/// +/// This is not just a cleanup: the always_inline pass requires direct calls to +/// functions to be able to inline them. If there is a bitcast in the way, it +/// won't inline them. Instcombine normally deletes these calls, but it isn't +/// run at -O0. +void CIRGenModule::replaceUsesOfNonProtoTypeWithRealFunction( + mlir::Operation *old, cir::FuncOp newFn) { + // If we're redefining a global as a function, don't transform it. + auto oldFn = mlir::dyn_cast<cir::FuncOp>(old); + if (!oldFn) + return; + + // TODO(cir): this RAUW ignores the features below. + assert(!cir::MissingFeatures::opFuncExceptions()); + assert(!cir::MissingFeatures::opFuncParameterAttributes()); + assert(!cir::MissingFeatures::opFuncOperandBundles()); + if (oldFn->getAttrs().size() <= 1) + errorNYI(old->getLoc(), + "replaceUsesOfNonProtoTypeWithRealFunction: Attribute forwarding"); + + // Mark new function as originated from a no-proto declaration. + newFn.setNoProto(oldFn.getNoProto()); + + // Iterate through all calls of the no-proto function. + std::optional<mlir::SymbolTable::UseRange> symUses = + oldFn.getSymbolUses(oldFn->getParentOp()); + for (const mlir::SymbolTable::SymbolUse &use : symUses.value()) { + mlir::OpBuilder::InsertionGuard guard(builder); + + if (auto noProtoCallOp = mlir::dyn_cast<cir::CallOp>(use.getUser())) { + builder.setInsertionPoint(noProtoCallOp); + + // Patch call type with the real function type. + cir::CallOp realCallOp = builder.createCallOp( + noProtoCallOp.getLoc(), newFn, noProtoCallOp.getOperands()); + + // Replace old no proto call with fixed call. + noProtoCallOp.replaceAllUsesWith(realCallOp); + noProtoCallOp.erase(); + } else if (auto getGlobalOp = + mlir::dyn_cast<cir::GetGlobalOp>(use.getUser())) { + // Replace type + getGlobalOp.getAddr().setType( + cir::PointerType::get(newFn.getFunctionType())); + } else { + errorNYI(use.getUser()->getLoc(), + "replaceUsesOfNonProtoTypeWithRealFunction: unexpected use"); + } + } +} + cir::GlobalLinkageKind CIRGenModule::getCIRLinkageVarDefinition(const VarDecl *vd, bool isConstant) { assert(!isConstant && "constant variables NYI"); @@ -1208,6 +1262,15 @@ cir::GlobalOp CIRGenModule::getGlobalForStringLiteral(const StringLiteral *s, return gv; } +void CIRGenModule::emitExplicitCastExprType(const ExplicitCastExpr *e, + CIRGenFunction *cgf) { + if (cgf && e->getType()->isVariablyModifiedType()) + cgf->emitVariablyModifiedType(e->getType()); + + assert(!cir::MissingFeatures::generateDebugInfo() && + "emitExplicitCastExprType"); +} + void CIRGenModule::emitDeclContext(const DeclContext *dc) { for (Decl *decl : dc->decls()) { // Unlike other DeclContexts, the contents of an ObjCImplDecl at TU scope @@ -1235,6 +1298,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { decl->getDeclKindName()); break; + case Decl::CXXConversion: case Decl::CXXMethod: case Decl::Function: { auto *fd = cast<FunctionDecl>(decl); @@ -1530,10 +1594,10 @@ static bool shouldAssumeDSOLocal(const CIRGenModule &cgm, const llvm::Triple &tt = cgm.getTriple(); const CodeGenOptions &cgOpts = cgm.getCodeGenOpts(); - if (tt.isWindowsGNUEnvironment()) { - // In MinGW, variables without DLLImport can still be automatically - // imported from a DLL by the linker; don't mark variables that - // potentially could come from another DLL as DSO local. + if (tt.isOSCygMing()) { + // In MinGW and Cygwin, variables without DLLImport can still be + // automatically imported from a DLL by the linker; don't mark variables + // that potentially could come from another DLL as DSO local. // With EmulatedTLS, TLS variables can be autoimported from other DLLs // (and this actually happens in the public interface of libstdc++), so @@ -1692,8 +1756,7 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( // Lookup the entry, lazily creating it if necessary. mlir::Operation *entry = getGlobalValue(mangledName); if (entry) { - if (!isa<cir::FuncOp>(entry)) - errorNYI(d->getSourceRange(), "getOrCreateCIRFunction: non-FuncOp"); + assert(mlir::isa<cir::FuncOp>(entry)); assert(!cir::MissingFeatures::weakRefReference()); @@ -1729,6 +1792,30 @@ cir::FuncOp CIRGenModule::getOrCreateCIRFunction( invalidLoc ? theModule->getLoc() : getLoc(funcDecl->getSourceRange()), mangledName, mlir::cast<cir::FuncType>(funcType), funcDecl); + // If we already created a function with the same mangled name (but different + // type) before, take its name and add it to the list of functions to be + // replaced with F at the end of CodeGen. + // + // This happens if there is a prototype for a function (e.g. "int f()") and + // then a definition of a different type (e.g. "int f(int x)"). + if (entry) { + + // Fetch a generic symbol-defining operation and its uses. + auto symbolOp = mlir::cast<mlir::SymbolOpInterface>(entry); + + // This might be an implementation of a function without a prototype, in + // which case, try to do special replacement of calls which match the new + // prototype. The really key thing here is that we also potentially drop + // arguments from the call site so as to make a direct call, which makes the + // inliner happier and suppresses a number of optimizer warnings (!) about + // dropping arguments. + if (symbolOp.getSymbolUses(symbolOp->getParentOp())) + replaceUsesOfNonProtoTypeWithRealFunction(entry, funcOp); + + // Obliterate no-proto declaration. + entry->erase(); + } + if (d) setFunctionAttributes(gd, funcOp, /*isIncompleteFunction=*/false, isThunk); @@ -1805,7 +1892,9 @@ CIRGenModule::createCIRFunction(mlir::Location loc, StringRef name, func = builder.create<cir::FuncOp>(loc, name, funcType); assert(!cir::MissingFeatures::opFuncAstDeclAttr()); - assert(!cir::MissingFeatures::opFuncNoProto()); + + if (funcDecl && !funcDecl->hasPrototype()) + func.setNoProto(true); assert(func.isDeclaration() && "expected empty body"); diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index 16922b1..5d07d38 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -252,6 +252,11 @@ public: getAddrOfGlobal(clang::GlobalDecl gd, ForDefinition_t isForDefinition = NotForDefinition); + /// Emit type info if type of an expression is a variably modified + /// type. Also emit proper debug info for cast types. + void emitExplicitCastExprType(const ExplicitCastExpr *e, + CIRGenFunction *cgf = nullptr); + /// Emit code for a single global function or variable declaration. Forward /// declarations are emitted lazily. void emitGlobal(clang::GlobalDecl gd); @@ -308,6 +313,9 @@ public: static void setInitializer(cir::GlobalOp &op, mlir::Attribute value); + void replaceUsesOfNonProtoTypeWithRealFunction(mlir::Operation *old, + cir::FuncOp newFn); + cir::FuncOp getOrCreateCIRFunction(llvm::StringRef mangledName, mlir::Type funcType, clang::GlobalDecl gd, bool forVTable, diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 05e8848..e4ec380 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -438,9 +438,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field, } else if (cirGenTypes.getCGModule() .getCodeGenOpts() .FineGrainedBitfieldAccesses) { - assert(!cir::MissingFeatures::nonFineGrainedBitfields()); - cirGenTypes.getCGModule().errorNYI(field->getSourceRange(), - "NYI FineGrainedBitfield"); + installBest = true; } else { // Otherwise, we're not installing. Update the bit size // of the current span to go all the way to limitOffset, which is 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 |