diff options
Diffstat (limited to 'clang/lib')
51 files changed, 1512 insertions, 265 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 6b6275f..16cf114 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -940,7 +940,6 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM, FunctionProtoTypes(this_(), FunctionProtoTypesLog2InitSize), DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()), DependentPackIndexingTypes(this_()), TemplateSpecializationTypes(this_()), - DependentTemplateSpecializationTypes(this_()), DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()), DeducedTemplates(this_()), ArrayParameterTypes(this_()), CanonTemplateTemplateParms(this_()), SourceMgr(SM), LangOpts(LOpts), @@ -5979,10 +5978,9 @@ QualType ASTContext::getDependentTemplateSpecializationType( llvm::FoldingSetNodeID ID; DependentTemplateSpecializationType::Profile(ID, *this, Keyword, Name, Args); - void *InsertPos = nullptr; - if (auto *T = DependentTemplateSpecializationTypes.FindNodeOrInsertPos( - ID, InsertPos)) - return QualType(T, 0); + if (auto const T_iter = DependentTemplateSpecializationTypes.find(ID); + T_iter != DependentTemplateSpecializationTypes.end()) + return QualType(T_iter->getSecond(), 0); NestedNameSpecifier *NNS = Name.getQualifier(); @@ -6001,11 +5999,6 @@ QualType ASTContext::getDependentTemplateSpecializationType( CanonKeyword, {CanonNNS, Name.getName(), /*HasTemplateKeyword=*/true}, CanonArgs, /*IsCanonical=*/true); - // Find the insert position again. - [[maybe_unused]] auto *Nothing = - DependentTemplateSpecializationTypes.FindNodeOrInsertPos(ID, - InsertPos); - assert(!Nothing && "canonical type broken"); } } else { assert(Keyword == getCanonicalElaboratedTypeKeyword(Keyword)); @@ -6021,8 +6014,13 @@ QualType ASTContext::getDependentTemplateSpecializationType( alignof(DependentTemplateSpecializationType)); auto *T = new (Mem) DependentTemplateSpecializationType(Keyword, Name, Args, Canon); +#ifndef NDEBUG + llvm::FoldingSetNodeID InsertedID; + T->Profile(InsertedID, *this); + assert(InsertedID == ID && "ID does not match"); +#endif Types.push_back(T); - DependentTemplateSpecializationTypes.InsertNode(T, InsertPos); + DependentTemplateSpecializationTypes.try_emplace(ID, T); return QualType(T, 0); } diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 0d12161..9808298 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14636,7 +14636,9 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, if (!LHSDesignator.Invalid && !RHSDesignator.Invalid && IsRelational) { bool WasArrayIndex; unsigned Mismatch = FindDesignatorMismatch( - getType(LHSValue.Base), LHSDesignator, RHSDesignator, WasArrayIndex); + LHSValue.Base.isNull() ? QualType() + : getType(LHSValue.Base).getNonReferenceType(), + LHSDesignator, RHSDesignator, WasArrayIndex); // At the point where the designators diverge, the comparison has a // specified value if: // - we are comparing array indices @@ -14680,7 +14682,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E, // compare pointers within the object in question; otherwise, the result // depends on where the object is located in memory. if (!LHSValue.Base.isNull() && IsRelational) { - QualType BaseTy = getType(LHSValue.Base); + QualType BaseTy = getType(LHSValue.Base).getNonReferenceType(); if (BaseTy->isIncompleteType()) return Error(E); CharUnits Size = Info.Ctx.getTypeSizeInChars(BaseTy); diff --git a/clang/lib/AST/OSLog.cpp b/clang/lib/AST/OSLog.cpp index b777d4d..91f8410 100644 --- a/clang/lib/AST/OSLog.cpp +++ b/clang/lib/AST/OSLog.cpp @@ -1,4 +1,16 @@ -// TODO: header template +//===--- OSLog.cpp - OS log format string analysis ------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements analysis functions for OS log format strings and +/// buffer layout computation for __builtin_os_log_format and related builtins. +/// +//===----------------------------------------------------------------------===// #include "clang/AST/OSLog.h" #include "clang/AST/Attr.h" @@ -137,8 +149,8 @@ public: for (auto &Data : ArgsData) { if (!Data.MaskType.empty()) { CharUnits Size = CharUnits::fromQuantity(8); - Layout.Items.emplace_back(OSLogBufferItem::MaskKind, nullptr, - Size, 0, Data.MaskType); + Layout.Items.emplace_back(OSLogBufferItem::MaskKind, nullptr, Size, 0, + Data.MaskType); } if (Data.FieldWidth) { diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp index af25d25..e362350e 100644 --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -64,6 +64,7 @@ bool WebAssemblyTargetInfo::hasFeature(StringRef Feature) const { .Case("mutable-globals", HasMutableGlobals) .Case("nontrapping-fptoint", HasNontrappingFPToInt) .Case("reference-types", HasReferenceTypes) + .Case("gc", HasGC) .Case("relaxed-simd", SIMDLevel >= RelaxedSIMD) .Case("sign-ext", HasSignExt) .Case("simd128", SIMDLevel >= SIMD128) @@ -106,6 +107,8 @@ void WebAssemblyTargetInfo::getTargetDefines(const LangOptions &Opts, Builder.defineMacro("__wasm_nontrapping_fptoint__"); if (HasReferenceTypes) Builder.defineMacro("__wasm_reference_types__"); + if (HasGC) + Builder.defineMacro("__wasm_gc__"); if (SIMDLevel >= RelaxedSIMD) Builder.defineMacro("__wasm_relaxed_simd__"); if (HasSignExt) @@ -307,6 +310,14 @@ bool WebAssemblyTargetInfo::handleTargetFeatures( HasReferenceTypes = false; continue; } + if (Feature == "+gc") { + HasGC = true; + continue; + } + if (Feature == "-gc") { + HasGC = false; + continue; + } if (Feature == "+relaxed-simd") { SIMDLevel = std::max(SIMDLevel, RelaxedSIMD); continue; @@ -353,6 +364,11 @@ bool WebAssemblyTargetInfo::handleTargetFeatures( return false; } + // gc implies reference-types + if (HasGC) { + HasReferenceTypes = true; + } + // bulk-memory-opt is a subset of bulk-memory. if (HasBulkMemory) { HasBulkMemoryOpt = true; diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index 57b366c..c47c8cc 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -69,6 +69,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { bool HasMutableGlobals = false; bool HasNontrappingFPToInt = false; bool HasReferenceTypes = false; + bool HasGC = false; bool HasSignExt = false; bool HasTailCall = false; bool HasWideArithmetic = false; 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..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 1f64801..7ff5f26 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -1657,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; - 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..d8459b4 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -357,10 +357,98 @@ 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 (QualType::DestructionKind dtorKind = + 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..02685a3 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); @@ -194,8 +194,12 @@ mlir::Value ComplexExprEmitter::emitCast(CastKind ck, Expr *op, } 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 +452,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 +460,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..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(); @@ -808,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 2aceeef..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 /// ---------------------- @@ -766,6 +848,8 @@ public: /// 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); @@ -779,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); @@ -836,6 +922,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 +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, 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/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 cd77166..9c36a43 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -17,6 +17,7 @@ #include "mlir/Interfaces/ControlFlowInterfaces.h" #include "mlir/Interfaces/FunctionImplementation.h" +#include "mlir/Support/LLVM.h" #include "clang/CIR/Dialect/IR/CIROpsDialect.cpp.inc" #include "clang/CIR/Dialect/IR/CIROpsEnums.cpp.inc" @@ -2231,6 +2232,126 @@ LogicalResult cir::ComplexImagPtrOp::verify() { } //===----------------------------------------------------------------------===// +// Bit manipulation operations +//===----------------------------------------------------------------------===// + +static OpFoldResult +foldUnaryBitOp(mlir::Attribute inputAttr, + llvm::function_ref<llvm::APInt(const llvm::APInt &)> func, + bool poisonZero = false) { + auto input = mlir::dyn_cast_if_present<IntAttr>(inputAttr); + if (!input) + return nullptr; + + llvm::APInt inputValue = input.getValue(); + if (poisonZero && inputValue.isZero()) { + // TODO(cir): maybe we should return a poison value here? + assert(!MissingFeatures::poisonAttr()); + return nullptr; + } + + llvm::APInt resultValue = func(inputValue); + return IntAttr::get(input.getType(), resultValue); +} + +OpFoldResult BitClrsbOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + unsigned resultValue = + inputValue.getBitWidth() - inputValue.getSignificantBits(); + return llvm::APInt(inputValue.getBitWidth(), resultValue); + }); +} + +OpFoldResult BitClzOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp( + adaptor.getInput(), + [](const llvm::APInt &inputValue) { + unsigned resultValue = inputValue.countLeadingZeros(); + return llvm::APInt(inputValue.getBitWidth(), resultValue); + }, + getPoisonZero()); +} + +OpFoldResult BitCtzOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp( + adaptor.getInput(), + [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), + inputValue.countTrailingZeros()); + }, + getPoisonZero()); +} + +OpFoldResult BitParityOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount() % 2); + }); +} + +OpFoldResult BitPopcountOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return llvm::APInt(inputValue.getBitWidth(), inputValue.popcount()); + }); +} + +OpFoldResult BitReverseOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return inputValue.reverseBits(); + }); +} + +OpFoldResult ByteSwapOp::fold(FoldAdaptor adaptor) { + return foldUnaryBitOp(adaptor.getInput(), [](const llvm::APInt &inputValue) { + return inputValue.byteSwap(); + }); +} + +OpFoldResult RotateOp::fold(FoldAdaptor adaptor) { + auto input = mlir::dyn_cast_if_present<IntAttr>(adaptor.getInput()); + auto amount = mlir::dyn_cast_if_present<IntAttr>(adaptor.getAmount()); + if (!input && !amount) + return nullptr; + + // We could fold cir.rotate even if one of its two operands is not a constant: + // - `cir.rotate left/right %0, 0` could be folded into just %0 even if %0 + // is not a constant. + // - `cir.rotate left/right 0/0b111...111, %0` could be folded into 0 or + // 0b111...111 even if %0 is not a constant. + + llvm::APInt inputValue; + if (input) { + inputValue = input.getValue(); + if (inputValue.isZero() || inputValue.isAllOnes()) { + // An input value of all 0s or all 1s will not change after rotation + return input; + } + } + + uint64_t amountValue; + if (amount) { + amountValue = amount.getValue().urem(getInput().getType().getWidth()); + if (amountValue == 0) { + // A shift amount of 0 will not change the input value + return getInput(); + } + } + + if (!input || !amount) + return nullptr; + + assert(inputValue.getBitWidth() == getInput().getType().getWidth() && + "input value must have the same bit width as the input type"); + + llvm::APInt resultValue; + if (isRotateLeft()) + resultValue = inputValue.rotl(amountValue); + else + resultValue = inputValue.rotr(amountValue); + + return IntAttr::get(input.getContext(), input.getType(), resultValue); +} + +//===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp index e505db5..2143f16 100644 --- a/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp +++ b/clang/lib/CIR/Dialect/Transforms/CIRCanonicalize.cpp @@ -143,7 +143,8 @@ void CIRCanonicalizePass::runOnOperation() { if (isa<BrOp, BrCondOp, CastOp, ScopeOp, SwitchOp, SelectOp, UnaryOp, ComplexCreateOp, ComplexImagOp, ComplexRealOp, VecCmpOp, VecCreateOp, VecExtractOp, VecShuffleOp, VecShuffleDynamicOp, - VecTernaryOp>(op)) + VecTernaryOp, BitClrsbOp, BitClzOp, BitCtzOp, BitParityOp, + BitPopcountOp, BitReverseOp, ByteSwapOp, RotateOp>(op)) ops.push_back(op); }); diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp index 0f13417..cef83ea 100644 --- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp +++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp @@ -8,10 +8,12 @@ #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> @@ -27,6 +29,15 @@ struct LoweringPreparePass : public LoweringPrepareBase<LoweringPreparePass> { 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 @@ -158,8 +169,83 @@ 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 cast = mlir::dyn_cast<cir::CastOp>(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); @@ -171,7 +257,7 @@ void LoweringPreparePass::runOnOperation() { llvm::SmallVector<mlir::Operation *> opsToTransform; op->walk([&](mlir::Operation *op) { - if (mlir::isa<cir::CastOp, cir::UnaryOp>(op)) + if (mlir::isa<cir::ArrayCtor, cir::CastOp, cir::UnaryOp>(op)) opsToTransform.push_back(op); }); @@ -182,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/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 1b72578..0b8b824 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -1027,12 +1027,6 @@ void EmitAssemblyHelper::RunOptimizationPipeline( MPM.addPass( createModuleToFunctionPassAdaptor(ObjCARCExpandPass())); }); - PB.registerPipelineEarlySimplificationEPCallback( - [](ModulePassManager &MPM, OptimizationLevel Level, - ThinOrFullLTOPhase) { - if (Level != OptimizationLevel::O0) - MPM.addPass(ObjCARCAPElimPass()); - }); PB.registerScalarOptimizerLateEPCallback( [](FunctionPassManager &FPM, OptimizationLevel Level) { if (Level != OptimizationLevel::O0) diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 0bceece..d9bd443 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -2852,20 +2852,28 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, if (AI.getInReg()) Attrs.addAttribute(llvm::Attribute::InReg); - // Depending on the ABI, this may be either a byval or a dead_on_return - // argument. - if (AI.getIndirectByVal()) { - Attrs.addByValAttr(getTypes().ConvertTypeForMem(ParamType)); - } else { - // Add dead_on_return when the object's lifetime ends in the callee. - // This includes trivially-destructible objects, as well as objects - // whose destruction / clean-up is carried out within the callee (e.g., - // Obj-C ARC-managed structs, MSVC callee-destroyed objects). - if (!ParamType.isDestructedType() || !ParamType->isRecordType() || - ParamType->castAs<RecordType>() - ->getDecl() - ->isParamDestroyedInCallee()) - Attrs.addAttribute(llvm::Attribute::DeadOnReturn); + // HLSL out and inout parameters must not be marked with ByVal or + // DeadOnReturn attributes because stores to these parameters by the + // callee are visible to the caller. + if (auto ParamABI = FI.getExtParameterInfo(ArgNo).getABI(); + ParamABI != ParameterABI::HLSLOut && + ParamABI != ParameterABI::HLSLInOut) { + + // Depending on the ABI, this may be either a byval or a dead_on_return + // argument. + if (AI.getIndirectByVal()) { + Attrs.addByValAttr(getTypes().ConvertTypeForMem(ParamType)); + } else { + // Add dead_on_return when the object's lifetime ends in the callee. + // This includes trivially-destructible objects, as well as objects + // whose destruction / clean-up is carried out within the callee + // (e.g., Obj-C ARC-managed structs, MSVC callee-destroyed objects). + if (!ParamType.isDestructedType() || !ParamType->isRecordType() || + ParamType->castAs<RecordType>() + ->getDecl() + ->isParamDestroyedInCallee()) + Attrs.addAttribute(llvm::Attribute::DeadOnReturn); + } } auto *Decl = ParamType->getAsRecordDecl(); diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp index a371b67..77fc3a2 100644 --- a/clang/lib/CodeGen/CGDebugInfo.cpp +++ b/clang/lib/CodeGen/CGDebugInfo.cpp @@ -6435,7 +6435,7 @@ CodeGenFunction::LexicalScope::~LexicalScope() { static std::string SanitizerHandlerToCheckLabel(SanitizerHandler Handler) { std::string Label; switch (Handler) { -#define SANITIZER_CHECK(Enum, Name, Version) \ +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ case Enum: \ Label = "__ubsan_check_" #Name; \ break; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 85c7688..90aed79 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -85,6 +85,16 @@ enum VariableTypeDescriptorKind : uint16_t { // Miscellaneous Helper Methods //===--------------------------------------------------------------------===// +static llvm::StringRef GetUBSanTrapForHandler(SanitizerHandler ID) { + switch (ID) { +#define SANITIZER_CHECK(Enum, Name, Version, Msg) \ + case SanitizerHandler::Enum: \ + return Msg; + LIST_SANITIZER_CHECKS +#undef SANITIZER_CHECK + } +} + /// CreateTempAlloca - This creates a alloca and inserts it into the entry /// block. RawAddress @@ -3649,7 +3659,7 @@ struct SanitizerHandlerInfo { } const SanitizerHandlerInfo SanitizerHandlers[] = { -#define SANITIZER_CHECK(Enum, Name, Version) {#Name, Version}, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) {#Name, Version}, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; @@ -3954,6 +3964,8 @@ void CodeGenFunction::EmitCfiCheckFail() { StartFunction(GlobalDecl(), CGM.getContext().VoidTy, F, FI, Args, SourceLocation()); + ApplyDebugLocation ADL = ApplyDebugLocation::CreateArtificial(*this); + // This function is not affected by NoSanitizeList. This function does // not have a source location, but "src:*" would still apply. Revert any // changes to SanOpts made in StartFunction. @@ -4051,6 +4063,15 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, llvm::BasicBlock *&TrapBB = TrapBBs[CheckHandlerID]; + llvm::DILocation *TrapLocation = Builder.getCurrentDebugLocation(); + llvm::StringRef TrapMessage = GetUBSanTrapForHandler(CheckHandlerID); + + if (getDebugInfo() && !TrapMessage.empty() && + CGM.getCodeGenOpts().SanitizeDebugTrapReasons && TrapLocation) { + TrapLocation = getDebugInfo()->CreateTrapFailureMessageFor( + TrapLocation, "Undefined Behavior Sanitizer", TrapMessage); + } + NoMerge = NoMerge || !CGM.getCodeGenOpts().OptimizationLevel || (CurCodeDecl && CurCodeDecl->hasAttr<OptimizeNoneAttr>()); @@ -4059,8 +4080,8 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, auto Call = TrapBB->begin(); assert(isa<llvm::CallInst>(Call) && "Expected call in trap BB"); - Call->applyMergedLocation(Call->getDebugLoc(), - Builder.getCurrentDebugLocation()); + Call->applyMergedLocation(Call->getDebugLoc(), TrapLocation); + Builder.CreateCondBr(Checked, Cont, TrapBB, MDHelper.createLikelyBranchWeights()); } else { @@ -4069,6 +4090,8 @@ void CodeGenFunction::EmitTrapCheck(llvm::Value *Checked, MDHelper.createLikelyBranchWeights()); EmitBlock(TrapBB); + ApplyDebugLocation applyTrapDI(*this, TrapLocation); + llvm::CallInst *TrapCall = Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::ubsantrap), llvm::ConstantInt::get(CGM.Int8Ty, CheckHandlerID)); diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 359e30c..b8238a4 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -2146,30 +2146,9 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { return; } - // We might be deleting a pointer to array. If so, GEP down to the - // first non-array element. - // (this assumes that A(*)[3][7] is converted to [3 x [7 x %A]]*) - if (DeleteTy->isConstantArrayType()) { - llvm::Value *Zero = Builder.getInt32(0); - SmallVector<llvm::Value*,8> GEP; - - GEP.push_back(Zero); // point at the outermost array - - // For each layer of array type we're pointing at: - while (const ConstantArrayType *Arr - = getContext().getAsConstantArrayType(DeleteTy)) { - // 1. Unpeel the array type. - DeleteTy = Arr->getElementType(); - - // 2. GEP to the first element of the array. - GEP.push_back(Zero); - } - - Ptr = Builder.CreateInBoundsGEP(Ptr, GEP, ConvertTypeForMem(DeleteTy), - Ptr.getAlignment(), "del.first"); - } - - assert(ConvertTypeForMem(DeleteTy) == Ptr.getElementType()); + // We might be deleting a pointer to array. + DeleteTy = getContext().getBaseElementType(DeleteTy); + Ptr = Ptr.withElementType(ConvertTypeForMem(DeleteTy)); if (E->isArrayForm()) { EmitArrayDelete(*this, E, Ptr, DeleteTy); diff --git a/clang/lib/CodeGen/CGOpenMPRuntime.cpp b/clang/lib/CodeGen/CGOpenMPRuntime.cpp index ce2dd4d..91237cf 100644 --- a/clang/lib/CodeGen/CGOpenMPRuntime.cpp +++ b/clang/lib/CodeGen/CGOpenMPRuntime.cpp @@ -7080,6 +7080,110 @@ private: return ConstLength.getSExtValue() != 1; } + /// A helper class to copy structures with overlapped elements, i.e. those + /// which have mappings of both "s" and "s.mem". Consecutive elements that + /// are not explicitly copied have mapping nodes synthesized for them, + /// taking care to avoid generating zero-sized copies. + class CopyOverlappedEntryGaps { + CodeGenFunction &CGF; + MapCombinedInfoTy &CombinedInfo; + OpenMPOffloadMappingFlags Flags = OpenMPOffloadMappingFlags::OMP_MAP_NONE; + const ValueDecl *MapDecl = nullptr; + const Expr *MapExpr = nullptr; + Address BP = Address::invalid(); + bool IsNonContiguous = false; + uint64_t DimSize = 0; + // These elements track the position as the struct is iterated over + // (in order of increasing element address). + const RecordDecl *LastParent = nullptr; + uint64_t Cursor = 0; + unsigned LastIndex = -1u; + Address LB = Address::invalid(); + + public: + CopyOverlappedEntryGaps(CodeGenFunction &CGF, + MapCombinedInfoTy &CombinedInfo, + OpenMPOffloadMappingFlags Flags, + const ValueDecl *MapDecl, const Expr *MapExpr, + Address BP, Address LB, bool IsNonContiguous, + uint64_t DimSize) + : CGF(CGF), CombinedInfo(CombinedInfo), Flags(Flags), MapDecl(MapDecl), + MapExpr(MapExpr), BP(BP), IsNonContiguous(IsNonContiguous), + DimSize(DimSize), LB(LB) {} + + void processField( + const OMPClauseMappableExprCommon::MappableComponent &MC, + const FieldDecl *FD, + llvm::function_ref<LValue(CodeGenFunction &, const MemberExpr *)> + EmitMemberExprBase) { + const RecordDecl *RD = FD->getParent(); + const ASTRecordLayout &RL = CGF.getContext().getASTRecordLayout(RD); + uint64_t FieldOffset = RL.getFieldOffset(FD->getFieldIndex()); + uint64_t FieldSize = + CGF.getContext().getTypeSize(FD->getType().getCanonicalType()); + Address ComponentLB = Address::invalid(); + + if (FD->getType()->isLValueReferenceType()) { + const auto *ME = cast<MemberExpr>(MC.getAssociatedExpression()); + LValue BaseLVal = EmitMemberExprBase(CGF, ME); + ComponentLB = + CGF.EmitLValueForFieldInitialization(BaseLVal, FD).getAddress(); + } else { + ComponentLB = + CGF.EmitOMPSharedLValue(MC.getAssociatedExpression()).getAddress(); + } + + if (!LastParent) + LastParent = RD; + if (FD->getParent() == LastParent) { + if (FD->getFieldIndex() != LastIndex + 1) + copyUntilField(FD, ComponentLB); + } else { + LastParent = FD->getParent(); + if (((int64_t)FieldOffset - (int64_t)Cursor) > 0) + copyUntilField(FD, ComponentLB); + } + Cursor = FieldOffset + FieldSize; + LastIndex = FD->getFieldIndex(); + LB = CGF.Builder.CreateConstGEP(ComponentLB, 1); + } + + void copyUntilField(const FieldDecl *FD, Address ComponentLB) { + llvm::Value *ComponentLBPtr = ComponentLB.emitRawPointer(CGF); + llvm::Value *LBPtr = LB.emitRawPointer(CGF); + llvm::Value *Size = + CGF.Builder.CreatePtrDiff(CGF.Int8Ty, ComponentLBPtr, LBPtr); + copySizedChunk(LBPtr, Size); + } + + void copyUntilEnd(Address HB) { + if (LastParent) { + const ASTRecordLayout &RL = + CGF.getContext().getASTRecordLayout(LastParent); + if ((uint64_t)CGF.getContext().toBits(RL.getSize()) <= Cursor) + return; + } + llvm::Value *LBPtr = LB.emitRawPointer(CGF); + llvm::Value *Size = CGF.Builder.CreatePtrDiff( + CGF.Int8Ty, CGF.Builder.CreateConstGEP(HB, 1).emitRawPointer(CGF), + LBPtr); + copySizedChunk(LBPtr, Size); + } + + void copySizedChunk(llvm::Value *Base, llvm::Value *Size) { + CombinedInfo.Exprs.emplace_back(MapDecl, MapExpr); + CombinedInfo.BasePointers.push_back(BP.emitRawPointer(CGF)); + CombinedInfo.DevicePtrDecls.push_back(nullptr); + CombinedInfo.DevicePointers.push_back(DeviceInfoTy::None); + CombinedInfo.Pointers.push_back(Base); + CombinedInfo.Sizes.push_back( + CGF.Builder.CreateIntCast(Size, CGF.Int64Ty, /*isSigned=*/true)); + CombinedInfo.Types.push_back(Flags); + CombinedInfo.Mappers.push_back(nullptr); + CombinedInfo.NonContigInfo.Dims.push_back(IsNonContiguous ? DimSize : 1); + } + }; + /// Generate the base pointers, section pointers, sizes, map type bits, and /// user-defined mappers (all included in \a CombinedInfo) for the provided /// map type, map or motion modifiers, and expression components. @@ -7570,63 +7674,22 @@ private: getMapTypeBits(MapType, MapModifiers, MotionModifiers, IsImplicit, /*AddPtrFlag=*/false, /*AddIsTargetParamFlag=*/false, IsNonContiguous); - llvm::Value *Size = nullptr; + CopyOverlappedEntryGaps CopyGaps(CGF, CombinedInfo, Flags, MapDecl, + MapExpr, BP, LB, IsNonContiguous, + DimSize); // Do bitcopy of all non-overlapped structure elements. for (OMPClauseMappableExprCommon::MappableExprComponentListRef Component : OverlappedElements) { - Address ComponentLB = Address::invalid(); for (const OMPClauseMappableExprCommon::MappableComponent &MC : Component) { if (const ValueDecl *VD = MC.getAssociatedDeclaration()) { - const auto *FD = dyn_cast<FieldDecl>(VD); - if (FD && FD->getType()->isLValueReferenceType()) { - const auto *ME = - cast<MemberExpr>(MC.getAssociatedExpression()); - LValue BaseLVal = EmitMemberExprBase(CGF, ME); - ComponentLB = - CGF.EmitLValueForFieldInitialization(BaseLVal, FD) - .getAddress(); - } else { - ComponentLB = - CGF.EmitOMPSharedLValue(MC.getAssociatedExpression()) - .getAddress(); + if (const auto *FD = dyn_cast<FieldDecl>(VD)) { + CopyGaps.processField(MC, FD, EmitMemberExprBase); } - llvm::Value *ComponentLBPtr = ComponentLB.emitRawPointer(CGF); - llvm::Value *LBPtr = LB.emitRawPointer(CGF); - Size = CGF.Builder.CreatePtrDiff(CGF.Int8Ty, ComponentLBPtr, - LBPtr); - break; } } - assert(Size && "Failed to determine structure size"); - CombinedInfo.Exprs.emplace_back(MapDecl, MapExpr); - CombinedInfo.BasePointers.push_back(BP.emitRawPointer(CGF)); - CombinedInfo.DevicePtrDecls.push_back(nullptr); - CombinedInfo.DevicePointers.push_back(DeviceInfoTy::None); - CombinedInfo.Pointers.push_back(LB.emitRawPointer(CGF)); - CombinedInfo.Sizes.push_back(CGF.Builder.CreateIntCast( - Size, CGF.Int64Ty, /*isSigned=*/true)); - CombinedInfo.Types.push_back(Flags); - CombinedInfo.Mappers.push_back(nullptr); - CombinedInfo.NonContigInfo.Dims.push_back(IsNonContiguous ? DimSize - : 1); - LB = CGF.Builder.CreateConstGEP(ComponentLB, 1); } - CombinedInfo.Exprs.emplace_back(MapDecl, MapExpr); - CombinedInfo.BasePointers.push_back(BP.emitRawPointer(CGF)); - CombinedInfo.DevicePtrDecls.push_back(nullptr); - CombinedInfo.DevicePointers.push_back(DeviceInfoTy::None); - CombinedInfo.Pointers.push_back(LB.emitRawPointer(CGF)); - llvm::Value *LBPtr = LB.emitRawPointer(CGF); - Size = CGF.Builder.CreatePtrDiff( - CGF.Int8Ty, CGF.Builder.CreateConstGEP(HB, 1).emitRawPointer(CGF), - LBPtr); - CombinedInfo.Sizes.push_back( - CGF.Builder.CreateIntCast(Size, CGF.Int64Ty, /*isSigned=*/true)); - CombinedInfo.Types.push_back(Flags); - CombinedInfo.Mappers.push_back(nullptr); - CombinedInfo.NonContigInfo.Dims.push_back(IsNonContiguous ? DimSize - : 1); + CopyGaps.copyUntilEnd(HB); break; } llvm::Value *Size = getExprTypeSize(I->getAssociatedExpression()); diff --git a/clang/lib/CodeGen/SanitizerHandler.h b/clang/lib/CodeGen/SanitizerHandler.h index bb42e39..a66e7ab 100644 --- a/clang/lib/CodeGen/SanitizerHandler.h +++ b/clang/lib/CodeGen/SanitizerHandler.h @@ -14,35 +14,69 @@ #define LLVM_CLANG_LIB_CODEGEN_SANITIZER_HANDLER_H #define LIST_SANITIZER_CHECKS \ - SANITIZER_CHECK(AddOverflow, add_overflow, 0) \ - SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0) \ - SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0) \ - SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0) \ - SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0) \ - SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0) \ - SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0) \ - SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0) \ - SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0) \ - SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0) \ - SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0) \ - SANITIZER_CHECK(MissingReturn, missing_return, 0) \ - SANITIZER_CHECK(MulOverflow, mul_overflow, 0) \ - SANITIZER_CHECK(NegateOverflow, negate_overflow, 0) \ - SANITIZER_CHECK(NullabilityArg, nullability_arg, 0) \ - SANITIZER_CHECK(NullabilityReturn, nullability_return, 1) \ - SANITIZER_CHECK(NonnullArg, nonnull_arg, 0) \ - SANITIZER_CHECK(NonnullReturn, nonnull_return, 1) \ - SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0) \ - SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0) \ - SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0) \ - SANITIZER_CHECK(SubOverflow, sub_overflow, 0) \ - SANITIZER_CHECK(TypeMismatch, type_mismatch, 1) \ - SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0) \ - SANITIZER_CHECK(VLABoundNotPositive, vla_bound_not_positive, 0) \ - SANITIZER_CHECK(BoundsSafety, bounds_safety, 0) + SANITIZER_CHECK(AddOverflow, add_overflow, 0, "Integer addition overflowed") \ + SANITIZER_CHECK(BuiltinUnreachable, builtin_unreachable, 0, \ + "_builtin_unreachable(), execution reached an unreachable " \ + "program point") \ + SANITIZER_CHECK(CFICheckFail, cfi_check_fail, 0, \ + "Control flow integrity check failed") \ + SANITIZER_CHECK(DivremOverflow, divrem_overflow, 0, \ + "Integer divide or remainder overflowed") \ + SANITIZER_CHECK(DynamicTypeCacheMiss, dynamic_type_cache_miss, 0, \ + "Dynamic type cache miss, member call made on an object " \ + "whose dynamic type differs from the expected type") \ + SANITIZER_CHECK(FloatCastOverflow, float_cast_overflow, 0, \ + "Floating-point to integer conversion overflowed") \ + SANITIZER_CHECK(FunctionTypeMismatch, function_type_mismatch, 0, \ + "Function called with mismatched signature") \ + SANITIZER_CHECK(ImplicitConversion, implicit_conversion, 0, \ + "Implicit integer conversion overflowed or lost data") \ + SANITIZER_CHECK(InvalidBuiltin, invalid_builtin, 0, \ + "Invalid use of builtin function") \ + SANITIZER_CHECK(InvalidObjCCast, invalid_objc_cast, 0, \ + "Invalid Objective-C cast") \ + SANITIZER_CHECK(LoadInvalidValue, load_invalid_value, 0, \ + "Loaded an invalid or uninitialized value for the type") \ + SANITIZER_CHECK(MissingReturn, missing_return, 0, \ + "Execution reached the end of a value-returning function " \ + "without returning a value") \ + SANITIZER_CHECK(MulOverflow, mul_overflow, 0, \ + "Integer multiplication overflowed") \ + SANITIZER_CHECK(NegateOverflow, negate_overflow, 0, \ + "Integer negation overflowed") \ + SANITIZER_CHECK( \ + NullabilityArg, nullability_arg, 0, \ + "Passing null as an argument which is annotated with _Nonnull") \ + SANITIZER_CHECK(NullabilityReturn, nullability_return, 1, \ + "Returning null from a function with a return type " \ + "annotated with _Nonnull") \ + SANITIZER_CHECK(NonnullArg, nonnull_arg, 0, \ + "Passing null pointer as an argument which is declared to " \ + "never be null") \ + SANITIZER_CHECK(NonnullReturn, nonnull_return, 1, \ + "Returning null pointer from a function which is declared " \ + "to never return null") \ + SANITIZER_CHECK(OutOfBounds, out_of_bounds, 0, "Array index out of bounds") \ + SANITIZER_CHECK(PointerOverflow, pointer_overflow, 0, \ + "Pointer arithmetic overflowed bounds") \ + SANITIZER_CHECK(ShiftOutOfBounds, shift_out_of_bounds, 0, \ + "Shift exponent is too large for the type") \ + SANITIZER_CHECK(SubOverflow, sub_overflow, 0, \ + "Integer subtraction overflowed") \ + SANITIZER_CHECK(TypeMismatch, type_mismatch, 1, \ + "Type mismatch in operation") \ + SANITIZER_CHECK(AlignmentAssumption, alignment_assumption, 0, \ + "Alignment assumption violated") \ + SANITIZER_CHECK( \ + VLABoundNotPositive, vla_bound_not_positive, 0, \ + "Variable length array bound evaluates to non-positive value") \ + SANITIZER_CHECK(BoundsSafety, bounds_safety, 0, \ + "") // BoundsSafety Msg is empty because it is not considered + // part of UBSan; therefore, no trap reason is emitted for + // this case. enum SanitizerHandler { -#define SANITIZER_CHECK(Enum, Name, Version) Enum, +#define SANITIZER_CHECK(Enum, Name, Version, Msg) Enum, LIST_SANITIZER_CHECKS #undef SANITIZER_CHECK }; diff --git a/clang/lib/CodeGen/TargetBuiltins/AMDGPU.cpp b/clang/lib/CodeGen/TargetBuiltins/AMDGPU.cpp index 7dccf82..70f510a 100644 --- a/clang/lib/CodeGen/TargetBuiltins/AMDGPU.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/AMDGPU.cpp @@ -274,7 +274,7 @@ llvm::Value *CodeGenFunction::EmitScalarOrConstFoldImmArg(unsigned ICEArguments, void CodeGenFunction::AddAMDGPUFenceAddressSpaceMMRA(llvm::Instruction *Inst, const CallExpr *E) { - constexpr const char *Tag = "amdgpu-as"; + constexpr const char *Tag = "amdgpu-synchronize-as"; LLVMContext &Ctx = Inst->getContext(); SmallVector<MMRAMetadata::TagT, 3> MMRAs; @@ -633,6 +633,41 @@ Value *CodeGenFunction::EmitAMDGPUBuiltinExpr(unsigned BuiltinID, llvm::Function *F = CGM.getIntrinsic(IID, {LoadTy}); return Builder.CreateCall(F, {Addr}); } + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b32: + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b64: + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b128: + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b32: + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b64: + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b128: { + + Intrinsic::ID IID; + switch (BuiltinID) { + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b32: + IID = Intrinsic::amdgcn_global_load_monitor_b32; + break; + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b64: + IID = Intrinsic::amdgcn_global_load_monitor_b64; + break; + case AMDGPU::BI__builtin_amdgcn_global_load_monitor_b128: + IID = Intrinsic::amdgcn_global_load_monitor_b128; + break; + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b32: + IID = Intrinsic::amdgcn_flat_load_monitor_b32; + break; + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b64: + IID = Intrinsic::amdgcn_flat_load_monitor_b64; + break; + case AMDGPU::BI__builtin_amdgcn_flat_load_monitor_b128: + IID = Intrinsic::amdgcn_flat_load_monitor_b128; + break; + } + + llvm::Type *LoadTy = ConvertType(E->getType()); + llvm::Value *Addr = EmitScalarExpr(E->getArg(0)); + llvm::Value *Val = EmitScalarExpr(E->getArg(1)); + llvm::Function *F = CGM.getIntrinsic(IID, {LoadTy}); + return Builder.CreateCall(F, {Addr, Val}); + } case AMDGPU::BI__builtin_amdgcn_load_to_lds: { // Should this have asan instrumentation? return emitBuiltinWithOneOverloadedType<5>(*this, E, diff --git a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp index b7fd70e..33a8d8f 100644 --- a/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp +++ b/clang/lib/CodeGen/TargetBuiltins/WebAssembly.cpp @@ -12,7 +12,10 @@ #include "CGBuiltin.h" #include "clang/Basic/TargetBuiltins.h" +#include "llvm/ADT/APInt.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/IntrinsicsWebAssembly.h" +#include "llvm/Support/ErrorHandling.h" using namespace clang; using namespace CodeGen; @@ -218,6 +221,64 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func); return Builder.CreateCall(Callee); } + case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: { + Value *FuncRef = EmitScalarExpr(E->getArg(0)); + + // Get the function type from the argument's static type + QualType ArgType = E->getArg(0)->getType(); + const PointerType *PtrTy = ArgType->getAs<PointerType>(); + assert(PtrTy && "Sema should have ensured this is a function pointer"); + + const FunctionType *FuncTy = PtrTy->getPointeeType()->getAs<FunctionType>(); + assert(FuncTy && "Sema should have ensured this is a function pointer"); + + // In the llvm IR, we won't have access any more to the type of the function + // pointer so we need to insert this type information somehow. The + // @llvm.wasm.ref.test.func takes varargs arguments whose values are unused + // to indicate the type of the function to test for. See the test here: + // llvm/test/CodeGen/WebAssembly/ref-test-func.ll + // + // The format is: first we include the return types (since this is a C + // function pointer, there will be 0 or one of these) then a token type to + // indicate the boundary between return types and param types, then the + // param types. + + llvm::FunctionType *LLVMFuncTy = + cast<llvm::FunctionType>(ConvertType(QualType(FuncTy, 0))); + + unsigned NParams = LLVMFuncTy->getNumParams(); + std::vector<Value *> Args; + Args.reserve(NParams + 3); + // The only real argument is the FuncRef + Args.push_back(FuncRef); + + // Add the type information + auto addType = [this, &Args](llvm::Type *T) { + if (T->isVoidTy()) { + // Do nothing + } else if (T->isFloatingPointTy()) { + Args.push_back(ConstantFP::get(T, 0)); + } else if (T->isIntegerTy()) { + Args.push_back(ConstantInt::get(T, 0)); + } else if (T->isPointerTy()) { + Args.push_back(ConstantPointerNull::get(llvm::PointerType::get( + getLLVMContext(), T->getPointerAddressSpace()))); + } else { + // TODO: Handle reference types. For now, we reject them in Sema. + llvm_unreachable("Unhandled type"); + } + }; + + addType(LLVMFuncTy->getReturnType()); + // The token type indicates the boundary between return types and param + // types. + Args.push_back(PoisonValue::get(llvm::Type::getTokenTy(getLLVMContext()))); + for (unsigned i = 0; i < NParams; i++) { + addType(LLVMFuncTy->getParamType(i)); + } + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_test_func); + return Builder.CreateCall(Callee, Args); + } case WebAssembly::BI__builtin_wasm_swizzle_i8x16: { Value *Src = EmitScalarExpr(E->getArg(0)); Value *Indices = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp index 892049e..853f694 100644 --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -209,8 +209,8 @@ Driver::Driver(StringRef ClangExecutable, StringRef TargetTriple, CCLogDiagnostics(false), CCGenDiagnostics(false), CCPrintProcessStats(false), CCPrintInternalStats(false), TargetTriple(TargetTriple), Saver(Alloc), PrependArg(nullptr), - CheckInputsExist(true), ProbePrecompiled(true), - SuppressMissingInputWarning(false) { + PreferredLinker(CLANG_DEFAULT_LINKER), CheckInputsExist(true), + ProbePrecompiled(true), SuppressMissingInputWarning(false) { // Provide a sane fallback if no VFS is specified. if (!this->VFS) this->VFS = llvm::vfs::getRealFileSystem(); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 21e4cff..98793a5 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -1382,6 +1382,12 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-fsanitize-annotate-debug-info=" + toString(AnnotateDebugInfo))); + if (const Arg *A = + Args.getLastArg(options::OPT_fsanitize_debug_trap_reasons, + options::OPT_fno_sanitize_debug_trap_reasons)) { + CmdArgs.push_back(Args.MakeArgString(A->getAsString(Args))); + } + addSpecialCaseListOpt(Args, CmdArgs, "-fsanitize-ignorelist=", UserIgnorelistFiles); addSpecialCaseListOpt(Args, CmdArgs, diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 47f93fa1..1d7dad0 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -1087,7 +1087,7 @@ std::string ToolChain::GetLinkerPath(bool *LinkerIsLLD) const { // Get -fuse-ld= first to prevent -Wunused-command-line-argument. -fuse-ld= is // considered as the linker flavor, e.g. "bfd", "gold", or "lld". const Arg* A = Args.getLastArg(options::OPT_fuse_ld_EQ); - StringRef UseLinker = A ? A->getValue() : CLANG_DEFAULT_LINKER; + StringRef UseLinker = A ? A->getValue() : getDriver().getPreferredLinker(); // --ld-path= takes precedence over -fuse-ld= and specifies the executable // name. -B, COMPILER_PATH and PATH and consulted if the value does not diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 7d0c142..9d882db 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -3881,17 +3881,17 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D, const ArgList &Args, const InputInfo &Input, const InputInfo &Output, bool HaveStd20, ArgStringList &CmdArgs) { - bool IsCXX = types::isCXX(Input.getType()); - bool HaveStdCXXModules = IsCXX && HaveStd20; + const bool IsCXX = types::isCXX(Input.getType()); + const bool HaveStdCXXModules = IsCXX && HaveStd20; bool HaveModules = HaveStdCXXModules; // -fmodules enables the use of precompiled modules (off by default). // Users can pass -fno-cxx-modules to turn off modules support for // C++/Objective-C++ programs. + const bool AllowedInCXX = Args.hasFlag(options::OPT_fcxx_modules, + options::OPT_fno_cxx_modules, true); bool HaveClangModules = false; if (Args.hasFlag(options::OPT_fmodules, options::OPT_fno_modules, false)) { - bool AllowedInCXX = Args.hasFlag(options::OPT_fcxx_modules, - options::OPT_fno_cxx_modules, true); if (AllowedInCXX || !IsCXX) { CmdArgs.push_back("-fmodules"); HaveClangModules = true; @@ -3900,6 +3900,9 @@ static bool RenderModulesOptions(Compilation &C, const Driver &D, HaveModules |= HaveClangModules; + if (HaveModules && !AllowedInCXX) + CmdArgs.push_back("-fno-cxx-modules"); + // -fmodule-maps enables implicit reading of module map files. By default, // this is enabled if we are using Clang's flavor of precompiled modules. if (Args.hasFlag(options::OPT_fimplicit_module_maps, diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp index 7d31eea..bb469ff 100644 --- a/clang/lib/Driver/ToolChains/MSVC.cpp +++ b/clang/lib/Driver/ToolChains/MSVC.cpp @@ -279,8 +279,8 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA, AddRunTimeLibs(TC, TC.getDriver(), CmdArgs, Args); } - StringRef Linker = - Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER); + StringRef Linker = Args.getLastArgValue(options::OPT_fuse_ld_EQ, + TC.getDriver().getPreferredLinker()); if (Linker.empty()) Linker = "link"; // We need to translate 'lld' into 'lld-link'. diff --git a/clang/lib/Driver/ToolChains/MinGW.cpp b/clang/lib/Driver/ToolChains/MinGW.cpp index b2e36ae..6abd0c0 100644 --- a/clang/lib/Driver/ToolChains/MinGW.cpp +++ b/clang/lib/Driver/ToolChains/MinGW.cpp @@ -548,7 +548,7 @@ toolchains::MinGW::MinGW(const Driver &D, const llvm::Triple &Triple, getFilePaths().push_back(Base + "lib"); NativeLLVMSupport = - Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER) + Args.getLastArgValue(options::OPT_fuse_ld_EQ, D.getPreferredLinker()) .equals_insensitive("lld"); } diff --git a/clang/lib/Driver/ToolChains/Solaris.cpp b/clang/lib/Driver/ToolChains/Solaris.cpp index a3574e1..02aa598 100644 --- a/clang/lib/Driver/ToolChains/Solaris.cpp +++ b/clang/lib/Driver/ToolChains/Solaris.cpp @@ -39,7 +39,7 @@ void solaris::Assembler::ConstructJob(Compilation &C, const JobAction &JA, bool solaris::isLinkerGnuLd(const ToolChain &TC, const ArgList &Args) { // Only used if targetting Solaris. const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ); - StringRef UseLinker = A ? A->getValue() : CLANG_DEFAULT_LINKER; + StringRef UseLinker = A ? A->getValue() : TC.getDriver().getPreferredLinker(); return UseLinker == "bfd" || UseLinker == "gld"; } @@ -52,7 +52,7 @@ static bool getPIE(const ArgList &Args, const ToolChain &TC) { TC.isPIEDefault(Args)); } -// FIXME: Need to handle CLANG_DEFAULT_LINKER here? +// FIXME: Need to handle PreferredLinker here? std::string solaris::Linker::getLinkerPath(const ArgList &Args) const { const ToolChain &ToolChain = getToolChain(); if (const Arg *A = Args.getLastArg(options::OPT_fuse_ld_EQ)) { @@ -345,7 +345,7 @@ SanitizerMask Solaris::getSupportedSanitizers() const { const char *Solaris::getDefaultLinker() const { // FIXME: Only handle Solaris ld and GNU ld here. - return llvm::StringSwitch<const char *>(CLANG_DEFAULT_LINKER) + return llvm::StringSwitch<const char *>(getDriver().getPreferredLinker()) .Cases("bfd", "gld", "/usr/gnu/bin/ld") .Default("/usr/bin/ld"); } diff --git a/clang/lib/Driver/ToolChains/UEFI.cpp b/clang/lib/Driver/ToolChains/UEFI.cpp index ac6668e..2b41173 100644 --- a/clang/lib/Driver/ToolChains/UEFI.cpp +++ b/clang/lib/Driver/ToolChains/UEFI.cpp @@ -83,8 +83,8 @@ void tools::uefi::Linker::ConstructJob(Compilation &C, const JobAction &JA, // This should ideally be handled by ToolChain::GetLinkerPath but we need // to special case some linker paths. In the case of lld, we need to // translate 'lld' into 'lld-link'. - StringRef Linker = - Args.getLastArgValue(options::OPT_fuse_ld_EQ, CLANG_DEFAULT_LINKER); + StringRef Linker = Args.getLastArgValue(options::OPT_fuse_ld_EQ, + TC.getDriver().getPreferredLinker()); if (Linker.empty() || Linker == "lld") Linker = "lld-link"; diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp index bf67f9e..9a10403 100644 --- a/clang/lib/Format/ContinuationIndenter.cpp +++ b/clang/lib/Format/ContinuationIndenter.cpp @@ -1725,7 +1725,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State, } if (Previous && (Previous->isOneOf(TT_BinaryOperator, TT_ConditionalExpr) || (Previous->isOneOf(tok::l_paren, tok::comma, tok::colon) && - !Previous->isOneOf(TT_DictLiteral, TT_ObjCMethodExpr)))) { + !Previous->isOneOf(TT_DictLiteral, TT_ObjCMethodExpr, + TT_CtorInitializerColon)))) { CurrentState.NestedBlockInlined = !Newline && hasNestedBlockInlined(Previous, Current, Style); } diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 62feb3d..e6808f7 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -731,6 +731,7 @@ template <> struct MappingTraits<FormatStyle::SpaceBeforeParensCustom> { IO.mapOptional("AfterFunctionDeclarationName", Spacing.AfterFunctionDeclarationName); IO.mapOptional("AfterIfMacros", Spacing.AfterIfMacros); + IO.mapOptional("AfterNot", Spacing.AfterNot); IO.mapOptional("AfterOverloadedOperator", Spacing.AfterOverloadedOperator); IO.mapOptional("AfterPlacementOperator", Spacing.AfterPlacementOperator); IO.mapOptional("AfterRequiresInClause", Spacing.AfterRequiresInClause); @@ -1753,7 +1754,6 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { GoogleStyle.AttributeMacros.push_back("absl_nullable"); GoogleStyle.AttributeMacros.push_back("absl_nullability_unknown"); GoogleStyle.BreakTemplateDeclarations = FormatStyle::BTDS_Yes; - GoogleStyle.DerivePointerAlignment = true; GoogleStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Regroup; GoogleStyle.IncludeStyle.IncludeCategories = {{"^<ext/.*\\.h>", 2, 0, false}, {"^<.*\\.h>", 1, 0, false}, @@ -1862,6 +1862,7 @@ FormatStyle getGoogleStyle(FormatStyle::LanguageKind Language) { } else if (Language == FormatStyle::LK_ObjC) { GoogleStyle.AlwaysBreakBeforeMultilineStrings = false; GoogleStyle.ColumnLimit = 100; + GoogleStyle.DerivePointerAlignment = true; // "Regroup" doesn't work well for ObjC yet (main header heuristic, // relationship between ObjC standard library headers and other heades, // #imports, etc.) @@ -2643,13 +2644,14 @@ private: for (FormatToken *Tok = Line->First; Tok && Tok->Next; Tok = Tok->Next) { if (Tok->isNot(TT_PointerOrReference)) continue; - // Don't treat space in `void foo() &&` as evidence. + // Don't treat space in `void foo() &&` or `void() &&` as evidence. if (const auto *Prev = Tok->getPreviousNonComment()) { if (Prev->is(tok::r_paren) && Prev->MatchingParen) { if (const auto *Func = Prev->MatchingParen->getPreviousNonComment()) { if (Func->isOneOf(TT_FunctionDeclarationName, TT_StartOfName, - TT_OverloadedOperator)) { + TT_OverloadedOperator) || + Func->isTypeName(LangOpts)) { continue; } } diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 581bfba..d28d2fd 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5478,7 +5478,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, if (Left.TokenText == "!") return Style.SpaceAfterLogicalNot; assert(Left.TokenText == "not"); - return Right.isOneOf(tok::coloncolon, TT_UnaryOperator); + return Right.isOneOf(tok::coloncolon, TT_UnaryOperator) || + (Right.is(tok::l_paren) && Style.SpaceBeforeParensOptions.AfterNot); } // If the next token is a binary operator or a selector name, we have diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp index 31392d1d..bc8841c 100644 --- a/clang/lib/Parse/ParseDeclCXX.cpp +++ b/clang/lib/Parse/ParseDeclCXX.cpp @@ -4940,9 +4940,8 @@ void Parser::ParseHLSLRootSignatureAttributeArgs(ParsedAttributes &Attrs) { // signature string and construct the in-memory elements if (!Found) { // Invoke the root signature parser to construct the in-memory constructs - SmallVector<hlsl::RootSignatureElement> RootElements; - hlsl::RootSignatureParser Parser(getLangOpts().HLSLRootSigVer, RootElements, - Signature, PP); + hlsl::RootSignatureParser Parser(getLangOpts().HLSLRootSigVer, Signature, + PP); if (Parser.parse()) { T.consumeClose(); return; @@ -4950,7 +4949,7 @@ void Parser::ParseHLSLRootSignatureAttributeArgs(ParsedAttributes &Attrs) { // Construct the declaration. Actions.HLSL().ActOnFinishRootSignatureDecl(RootSignatureLoc, DeclIdent, - RootElements); + Parser.getElements()); } // Create the arg for the ParsedAttr diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp index db9ed83..98dc458 100644 --- a/clang/lib/Parse/ParseHLSLRootSignature.cpp +++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp @@ -27,11 +27,10 @@ static const TokenKind RootElementKeywords[] = { }; RootSignatureParser::RootSignatureParser( - llvm::dxbc::RootSignatureVersion Version, - SmallVector<RootSignatureElement> &Elements, StringLiteral *Signature, + llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature, Preprocessor &PP) - : Version(Version), Elements(Elements), Signature(Signature), - Lexer(Signature->getString()), PP(PP), CurToken(0) {} + : Version(Version), Signature(Signature), Lexer(Signature->getString()), + PP(PP), CurToken(0) {} bool RootSignatureParser::parse() { // Iterate as many RootSignatureElements as possible, until we hit the diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp index 8c6a173..68a698f 100644 --- a/clang/lib/Sema/SemaAvailability.cpp +++ b/clang/lib/Sema/SemaAvailability.cpp @@ -547,6 +547,12 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, return; } case AR_Deprecated: + // Suppress -Wdeprecated-declarations in implicit + // functions. + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(S.getCurFunctionDecl()); + FD && FD->isImplicit()) + return; + if (ObjCPropertyAccess) diag = diag::warn_property_method_deprecated; else if (S.currentEvaluationContext().IsCaseExpr) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 59f89b1..a4e8de4 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2041,7 +2041,8 @@ bool Sema::CheckAttrTarget(const ParsedAttr &AL) { // Check whether the attribute is valid on the current target. if (!AL.existsInTarget(Context.getTargetInfo())) { if (AL.isRegularKeywordAttribute()) - Diag(AL.getLoc(), diag::err_keyword_not_supported_on_target); + Diag(AL.getLoc(), diag::err_keyword_not_supported_on_target) + << AL << AL.getRange(); else DiagnoseUnknownAttribute(AL); AL.setInvalid(); diff --git a/clang/lib/Sema/SemaOpenACC.cpp b/clang/lib/Sema/SemaOpenACC.cpp index 128a5db..8bfea62 100644 --- a/clang/lib/Sema/SemaOpenACC.cpp +++ b/clang/lib/Sema/SemaOpenACC.cpp @@ -699,11 +699,19 @@ ExprResult SemaOpenACC::ActOnVar(OpenACCDirectiveKind DK, OpenACCClauseKind CK, // OpenACC3.3 2.13: // A 'var' in a 'declare' directive must be a variable or array name. if ((CK == OpenACCClauseKind::UseDevice || - DK == OpenACCDirectiveKind::Declare) && - isa<ArraySectionExpr, ArraySubscriptExpr>(CurVarExpr)) { - Diag(VarExpr->getExprLoc(), diag::err_acc_not_a_var_ref_use_device_declare) - << (DK == OpenACCDirectiveKind::Declare); - return ExprError(); + DK == OpenACCDirectiveKind::Declare)) { + if (isa<ArraySubscriptExpr>(CurVarExpr)) { + Diag(VarExpr->getExprLoc(), + diag::err_acc_not_a_var_ref_use_device_declare) + << (DK == OpenACCDirectiveKind::Declare); + return ExprError(); + } + // As an extension, we allow 'array sections'/'sub-arrays' here, as that is + // effectively defining an array, and are in common use. + if (isa<ArraySectionExpr>(CurVarExpr)) + Diag(VarExpr->getExprLoc(), + diag::ext_acc_array_section_use_device_declare) + << (DK == OpenACCDirectiveKind::Declare); } // Sub-arrays/subscript-exprs are fine as long as the base is a diff --git a/clang/lib/Sema/SemaOpenACCAtomic.cpp b/clang/lib/Sema/SemaOpenACCAtomic.cpp index 9c8c8d1..a9319dc 100644 --- a/clang/lib/Sema/SemaOpenACCAtomic.cpp +++ b/clang/lib/Sema/SemaOpenACCAtomic.cpp @@ -576,6 +576,11 @@ class AtomicOperandChecker { return AssocStmt; } + const Expr *IgnoreBeforeCompare(const Expr *E) { + return E->IgnoreParenImpCasts()->IgnoreParenNoopCasts( + SemaRef.getASTContext()); + } + bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX, IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) { llvm::FoldingSetNodeID First_ID, Second_ID; @@ -648,8 +653,10 @@ class AtomicOperandChecker { if (CheckOperandVariable(AssignRes->RHS, PD)) return getRecoveryExpr(); - if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var, - IDACInfo::SimpleAssign, AssignRes->RHS)) + if (CheckVarRefsSame(FirstExprResults.ExprKind, + IgnoreBeforeCompare(FirstExprResults.X_Var), + IDACInfo::SimpleAssign, + IgnoreBeforeCompare(AssignRes->RHS))) return getRecoveryExpr(); break; } @@ -660,9 +667,10 @@ class AtomicOperandChecker { if (SecondExprResults.Failed) return getRecoveryExpr(); - if (CheckVarRefsSame(FirstExprResults.ExprKind, FirstExprResults.X_Var, + if (CheckVarRefsSame(FirstExprResults.ExprKind, + IgnoreBeforeCompare(FirstExprResults.X_Var), SecondExprResults.ExprKind, - SecondExprResults.X_Var)) + IgnoreBeforeCompare(SecondExprResults.X_Var))) return getRecoveryExpr(); break; } diff --git a/clang/lib/Sema/SemaOpenACCClause.cpp b/clang/lib/Sema/SemaOpenACCClause.cpp index 3f90fe8..b54a012 100644 --- a/clang/lib/Sema/SemaOpenACCClause.cpp +++ b/clang/lib/Sema/SemaOpenACCClause.cpp @@ -1919,6 +1919,14 @@ ExprResult SemaOpenACC::CheckReductionVar(OpenACCDirectiveKind DirectiveKind, << EltTy << /*Sub array base type*/ 1; return ExprError(); } + } else if (VarExpr->getType()->isArrayType()) { + // Arrays are considered an 'aggregate variable' explicitly, so are OK, no + // additional checking required. + // + // Glossary: Aggregate variables – a variable of any non-scalar datatype, + // including array or composite variables. + // + // The next branch (record decl) checks for composite variables. } else if (auto *RD = VarExpr->getType()->getAsRecordDecl()) { if (!RD->isStruct() && !RD->isClass()) { Diag(VarExpr->getExprLoc(), diag::err_acc_reduction_composite_type) @@ -2246,7 +2254,13 @@ bool SemaOpenACC::CheckDeclareClause(SemaOpenACC::OpenACCParsedClause &Clause, continue; } } else { - const auto *DRE = cast<DeclRefExpr>(VarExpr); + + const Expr *VarExprTemp = VarExpr; + + while (const auto *ASE = dyn_cast<ArraySectionExpr>(VarExprTemp)) + VarExprTemp = ASE->getBase()->IgnoreParenImpCasts(); + + const auto *DRE = cast<DeclRefExpr>(VarExprTemp); if (const auto *Var = dyn_cast<VarDecl>(DRE->getDecl())) { CurDecl = Var->getCanonicalDecl(); diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e1a975b..9e56e697 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5523,6 +5523,15 @@ static TemplateDeductionResult CheckDeductionConsistency( // FIXME: A substitution can be incomplete on a non-structural part of the // type. Use the canonical type for now, until the TemplateInstantiator can // deal with that. + + // Workaround: Implicit deduction guides use InjectedClassNameTypes, whereas + // the explicit guides don't. The substitution doesn't transform these types, + // so let it transform their specializations instead. + bool IsDeductionGuide = isa<CXXDeductionGuideDecl>(FTD->getTemplatedDecl()); + if (IsDeductionGuide) { + if (auto *Injected = P->getAs<InjectedClassNameType>()) + P = Injected->getInjectedSpecializationType(); + } QualType InstP = S.SubstType(P.getCanonicalType(), MLTAL, FTD->getLocation(), FTD->getDeclName(), &IsIncompleteSubstitution); if (InstP.isNull() && !IsIncompleteSubstitution) @@ -5537,9 +5546,15 @@ static TemplateDeductionResult CheckDeductionConsistency( if (auto *PA = dyn_cast<PackExpansionType>(A); PA && !isa<PackExpansionType>(InstP)) A = PA->getPattern(); - if (!S.Context.hasSameType( - S.Context.getUnqualifiedArrayType(InstP.getNonReferenceType()), - S.Context.getUnqualifiedArrayType(A.getNonReferenceType()))) + auto T1 = S.Context.getUnqualifiedArrayType(InstP.getNonReferenceType()); + auto T2 = S.Context.getUnqualifiedArrayType(A.getNonReferenceType()); + if (IsDeductionGuide) { + if (auto *Injected = T1->getAs<InjectedClassNameType>()) + T1 = Injected->getInjectedSpecializationType(); + if (auto *Injected = T2->getAs<InjectedClassNameType>()) + T2 = Injected->getInjectedSpecializationType(); + } + if (!S.Context.hasSameType(T1, T2)) return TemplateDeductionResult::NonDeducedMismatch; return TemplateDeductionResult::Success; } diff --git a/clang/lib/Sema/SemaWasm.cpp b/clang/lib/Sema/SemaWasm.cpp index 6faea24..8998492 100644 --- a/clang/lib/Sema/SemaWasm.cpp +++ b/clang/lib/Sema/SemaWasm.cpp @@ -227,6 +227,53 @@ bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { return false; } +bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(CallExpr *TheCall) { + if (SemaRef.checkArgCount(TheCall, 1)) + return true; + + Expr *FuncPtrArg = TheCall->getArg(0); + QualType ArgType = FuncPtrArg->getType(); + + // Check that the argument is a function pointer + const PointerType *PtrTy = ArgType->getAs<PointerType>(); + if (!PtrTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + const FunctionProtoType *FuncTy = + PtrTy->getPointeeType()->getAs<FunctionProtoType>(); + if (!FuncTy) { + return Diag(FuncPtrArg->getBeginLoc(), + diag::err_typecheck_expect_function_pointer) + << ArgType << FuncPtrArg->getSourceRange(); + } + + // Check that the function pointer doesn't use reference types + if (FuncTy->getReturnType().isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag::err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange(); + } + auto NParams = FuncTy->getNumParams(); + for (unsigned I = 0; I < NParams; I++) { + if (FuncTy->getParamType(I).isWebAssemblyReferenceType()) { + return Diag( + FuncPtrArg->getBeginLoc(), + diag:: + err_wasm_builtin_test_fp_sig_cannot_include_reference_type) + << 1 << FuncPtrArg->getSourceRange(); + } + } + + // Set return type to int (the result of the test) + TheCall->setType(getASTContext().IntTy); + + return false; +} + bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall) { @@ -249,6 +296,8 @@ bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, return BuiltinWasmTableFill(TheCall); case WebAssembly::BI__builtin_wasm_table_copy: return BuiltinWasmTableCopy(TheCall); + case WebAssembly::BI__builtin_wasm_test_function_pointer_signature: + return BuiltinWasmTestFunctionPointerSignature(TheCall); } return false; diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 68efdba..a7704da 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -3730,13 +3730,15 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, return nullptr; } - // Save the first destructor/function as release point. - assert(!ReleaseFunctionLC && "There should be only one release point"); + // Record the stack frame that is _responsible_ for this memory release + // event. This will be used by the false positive suppression heuristics + // that recognize the release points of reference-counted objects. + // + // Usually (e.g. in C) we say that the _responsible_ stack frame is the + // current innermost stack frame: ReleaseFunctionLC = CurrentLC->getStackFrame(); - - // See if we're releasing memory while inlining a destructor that - // decrement reference counters (or one of its callees). - // This turns on various common false positive suppressions. + // ...but if the stack contains a destructor call, then we say that the + // outermost destructor stack frame is the _responsible_ one: for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) { if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) { if (isReferenceCountingPointerDestructor(DD)) { |