https://github.com/xlauko updated https://github.com/llvm/llvm-project/pull/223418
>From 059e5cd5f78feec96cbd56e9fcc29600b4b9184d Mon Sep 17 00:00:00 2001 From: Henrich Lauko <[email protected]> Date: Mon, 14 Sep 2026 13:05:25 +0000 Subject: [PATCH] [CIR][CodeGen][NFCI] Move shared AST-only helpers into CodeGenUtils Follow-up to PR #221001. Ten more helpers that both CodeGens had equivalent copies of, several of them already marked "should be shared" in CIR. Instead of growing CodeGenUtils.h, each helper goes in a header named for the classic CodeGen file it came from, with the CGExpr*.cpp family sharing ExprUtils.h. CIR's files mirror the classic ones, so the grouping reads the same from both sides. CIR's getNoFPClassTestMask returned unsigned where classic returns llvm::FPClassTest; the shared version keeps the classic return type. No functional change intended. --- clang/include/clang/CodeGenUtils/CallUtils.h | 32 +++++ clang/include/clang/CodeGenUtils/ClassUtils.h | 29 ++++ clang/include/clang/CodeGenUtils/ExprUtils.h | 48 +++++++ .../clang/CodeGenUtils/FunctionUtils.h | 28 ++++ .../clang/CodeGenUtils/ItaniumCXXABIUtils.h | 28 ++++ clang/lib/CIR/CodeGen/CIRGenCall.cpp | 28 +--- clang/lib/CIR/CodeGen/CIRGenClass.cpp | 27 +--- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 24 +--- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 92 +------------ clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 24 +--- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 21 +-- clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 56 +------- clang/lib/CodeGen/CGCall.cpp | 34 ++--- clang/lib/CodeGen/CGClass.cpp | 28 +--- clang/lib/CodeGen/CGExpr.cpp | 24 +--- clang/lib/CodeGen/CGExprAgg.cpp | 93 +------------ clang/lib/CodeGen/CGExprScalar.cpp | 25 +--- clang/lib/CodeGen/CodeGenFunction.cpp | 23 +--- clang/lib/CodeGen/ItaniumCXXABI.cpp | 56 +------- clang/lib/CodeGenUtils/CMakeLists.txt | 5 + clang/lib/CodeGenUtils/CallUtils.cpp | 28 ++++ clang/lib/CodeGenUtils/ClassUtils.cpp | 39 ++++++ clang/lib/CodeGenUtils/ExprUtils.cpp | 128 ++++++++++++++++++ clang/lib/CodeGenUtils/FunctionUtils.cpp | 29 ++++ clang/lib/CodeGenUtils/ItaniumCXXABIUtils.cpp | 64 +++++++++ 25 files changed, 513 insertions(+), 500 deletions(-) create mode 100644 clang/include/clang/CodeGenUtils/CallUtils.h create mode 100644 clang/include/clang/CodeGenUtils/ClassUtils.h create mode 100644 clang/include/clang/CodeGenUtils/ExprUtils.h create mode 100644 clang/include/clang/CodeGenUtils/FunctionUtils.h create mode 100644 clang/include/clang/CodeGenUtils/ItaniumCXXABIUtils.h create mode 100644 clang/lib/CodeGenUtils/CallUtils.cpp create mode 100644 clang/lib/CodeGenUtils/ClassUtils.cpp create mode 100644 clang/lib/CodeGenUtils/ExprUtils.cpp create mode 100644 clang/lib/CodeGenUtils/FunctionUtils.cpp create mode 100644 clang/lib/CodeGenUtils/ItaniumCXXABIUtils.cpp diff --git a/clang/include/clang/CodeGenUtils/CallUtils.h b/clang/include/clang/CodeGenUtils/CallUtils.h new file mode 100644 index 0000000000000..450a62a448e49 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/CallUtils.h @@ -0,0 +1,32 @@ +//===--- CallUtils.h - Shared call emission queries -------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the AST and language option queries that both classic +// CodeGen and CIR CodeGen need while building a call and its argument and +// return value attributes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_CALLUTILS_H +#define LLVM_CLANG_CODEGENUTILS_CALLUTILS_H + +#include "clang/AST/ASTContext.h" +#include "llvm/ADT/FloatingPointMode.h" + +namespace clang::CodeGenUtils { + +/// Returns the canonical formal type of the given C++ method. +CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *MD); + +/// Returns the set of floating-point value kinds that the language options +/// promise never reach a function's arguments or return value. +llvm::FPClassTest getNoFPClassTestMask(const LangOptions &LangOpts); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_CALLUTILS_H diff --git a/clang/include/clang/CodeGenUtils/ClassUtils.h b/clang/include/clang/CodeGenUtils/ClassUtils.h new file mode 100644 index 0000000000000..63664ed737933 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/ClassUtils.h @@ -0,0 +1,29 @@ +//===--- ClassUtils.h - Shared C++ class emission queries -------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the AST queries about C++ class construction and destruction +// that both classic CodeGen and CIR CodeGen need, chiefly to decide when a +// vtable pointer has to be established before running member initializers or +// a destructor body. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_CLASSUTILS_H +#define LLVM_CLANG_CODEGENUTILS_CLASSUTILS_H + +#include "clang/AST/ASTContext.h" + +namespace clang::CodeGenUtils { + +/// Check whether \p Init uses 'this' in a way which requires the vtable to be +/// properly set. +bool baseInitializerUsesThis(ASTContext &Ctx, const Expr *Init); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_CLASSUTILS_H diff --git a/clang/include/clang/CodeGenUtils/ExprUtils.h b/clang/include/clang/CodeGenUtils/ExprUtils.h new file mode 100644 index 0000000000000..e6a5248440d33 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/ExprUtils.h @@ -0,0 +1,48 @@ +//===--- ExprUtils.h - Shared expression emission queries -------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the AST queries about expressions that both classic CodeGen +// and CIR CodeGen need while emitting scalar, aggregate and lvalue +// expressions. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_EXPRUTILS_H +#define LLVM_CLANG_CODEGENUTILS_EXPRUTILS_H + +#include "clang/AST/ASTContext.h" + +namespace clang::CodeGenUtils { + +/// Strip off the variably-modified array types wrapping \p VLA and return the +/// first element type that has a fixed size. +QualType getFixedSizeElementType(const ASTContext &Ctx, + const VariableArrayType *VLA); + +/// Check whether the value of \p E is possibly a reference to or into a +/// __block variable. +bool isBlockVarRef(const Expr *E); + +/// Check whether \p E is cheap enough and side-effect-free enough to evaluate +/// unconditionally instead of conditionally. This is used to convert control +/// flow into selects in some cases. +bool isCheapEnoughToEvaluateUnconditionally(const Expr *E, + const ASTContext &Ctx); + +/// Check whether \p E is a trivial array filler, that is, one that is +/// equivalent to zero-initialization. +bool isTrivialFiller(const Expr *E); + +/// Detect the unusual situation where an inline version of a builtin is +/// shadowed by a non-inline version. In that case we should pick the external +/// one everywhere. That's GCC behavior too. +bool onlyHasInlineBuiltinDeclaration(const FunctionDecl *FD); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_EXPRUTILS_H diff --git a/clang/include/clang/CodeGenUtils/FunctionUtils.h b/clang/include/clang/CodeGenUtils/FunctionUtils.h new file mode 100644 index 0000000000000..f924874d829e0 --- /dev/null +++ b/clang/include/clang/CodeGenUtils/FunctionUtils.h @@ -0,0 +1,28 @@ +//===--- FunctionUtils.h - Shared function emission queries -----*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the queries that both classic CodeGen and CIR CodeGen need +// while emitting a function body. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_FUNCTIONUTILS_H +#define LLVM_CLANG_CODEGENUTILS_FUNCTIONUTILS_H + +#include "clang/Basic/CodeGenOptions.h" +#include "clang/Basic/LangOptions.h" + +namespace clang::CodeGenUtils { + +/// Decide whether we need to emit the lifetime markers. +bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts, + const LangOptions &LangOpts); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_FUNCTIONUTILS_H diff --git a/clang/include/clang/CodeGenUtils/ItaniumCXXABIUtils.h b/clang/include/clang/CodeGenUtils/ItaniumCXXABIUtils.h new file mode 100644 index 0000000000000..45b358ad343bb --- /dev/null +++ b/clang/include/clang/CodeGenUtils/ItaniumCXXABIUtils.h @@ -0,0 +1,28 @@ +//===--- ItaniumCXXABIUtils.h - Shared Itanium C++ ABI queries --*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file holds the Itanium C++ ABI queries that both classic CodeGen and +// CIR CodeGen need while lowering ABI constructs whose encoding the ABI +// specifies in terms of the AST. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_CODEGENUTILS_ITANIUMCXXABIUTILS_H +#define LLVM_CLANG_CODEGENUTILS_ITANIUMCXXABIUTILS_H + +#include "clang/AST/ASTContext.h" + +namespace clang::CodeGenUtils { + +/// Compute the src2dst_offset hint as described in the Itanium C++ ABI [2.9.7]. +CharUnits computeOffsetHint(ASTContext &Ctx, const CXXRecordDecl *Src, + const CXXRecordDecl *Dst); + +} // namespace clang::CodeGenUtils + +#endif // LLVM_CLANG_CODEGENUTILS_ITANIUMCXXABIUTILS_H diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp index e7b451b82d853..d0ef751347b5a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp @@ -20,6 +20,7 @@ #include "mlir/IR/Attributes.h" #include "clang/CIR/ABIArgInfo.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/CallUtils.h" #include "llvm/ADT/FloatingPointMode.h" #include "llvm/ADT/StringSet.h" #include "llvm/Support/TypeSize.h" @@ -619,16 +620,6 @@ static bool determineNoUndef(QualType clangTy, CIRGenTypes &types, return false; } -/// Compute the nofpclass mask for FP types based on language options. -static unsigned getNoFPClassTestMask(const LangOptions &langOpts) { - unsigned mask = 0; - if (langOpts.NoHonorInfs) - mask |= llvm::fcInf; - if (langOpts.NoHonorNaNs) - mask |= llvm::fcNan; - return mask; -} - void CIRGenModule::constructFunctionReturnAttributes( const CIRGenFunctionInfo &info, const Decl *targetDecl, bool isThunk, mlir::NamedAttrList &retAttrs) { @@ -644,7 +635,8 @@ void CIRGenModule::constructFunctionReturnAttributes( mlir::UnitAttr::get(&getMLIRContext())); if (retTy->hasFloatingRepresentation()) - if (unsigned mask = getNoFPClassTestMask(getLangOpts())) + if (llvm::FPClassTest mask = + CodeGenUtils::getNoFPClassTestMask(getLangOpts())) retAttrs.set(mlir::LLVM::LLVMDialect::getNoFPClassAttrName(), builder.getI64IntegerAttr(mask)); @@ -769,7 +761,8 @@ void CIRGenModule::constructFunctionArgumentAttributes( } if (argType->hasFloatingRepresentation()) - if (unsigned mask = getNoFPClassTestMask(getLangOpts())) + if (llvm::FPClassTest mask = + CodeGenUtils::getNoFPClassTestMask(getLangOpts())) argAttrList.set(mlir::LLVM::LLVMDialect::getNoFPClassAttrName(), builder.getI64IntegerAttr(mask)); @@ -793,13 +786,6 @@ void CIRGenModule::constructFunctionArgumentAttributes( } } -/// Returns the canonical formal type of the given C++ method. -static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) { - return md->getType() - ->getCanonicalTypeUnqualified() - .getAs<FunctionProtoType>(); -} - /// Adds the formal parameters in FPT to the given prefix. If any parameter in /// FPT has pass_object_size attrs, then we'll add parameters for those, too. /// TODO(cir): this should be shared with LLVM codegen @@ -844,7 +830,7 @@ CIRGenTypes::arrangeCXXStructorDeclaration(GlobalDecl gd) { passParams = inheritingCtorHasParams(inherited, gd.getCtorType()); } - CanQual<FunctionProtoType> fpt = getFormalType(md); + CanQual<FunctionProtoType> fpt = CodeGenUtils::getFormalType(md); if (passParams) appendParameterTypes(*this, argTypes, fpt); @@ -994,7 +980,7 @@ const CIRGenFunctionInfo &CIRGenTypes::arrangeCXXConstructorCall( // +1 for implicit this, which should always be args[0] unsigned totalPrefixArgs = 1 + extraPrefixArgs; - CanQual<FunctionProtoType> fpt = getFormalType(d); + CanQual<FunctionProtoType> fpt = CodeGenUtils::getFormalType(d); RequiredArgs required = passProtoArgs ? RequiredArgs::getFromProtoWithExtraSlots( fpt, totalPrefixArgs + extraSuffixArgs) diff --git a/clang/lib/CIR/CodeGen/CIRGenClass.cpp b/clang/lib/CIR/CodeGen/CIRGenClass.cpp index 8cd014415e4ee..c8edc1ccd02c6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenClass.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenClass.cpp @@ -14,12 +14,12 @@ #include "CIRGenFunction.h" #include "CIRGenValue.h" -#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/ClassUtils.h" #include "clang/CodeGenUtils/CodeGenUtils.h" using namespace clang; @@ -199,31 +199,8 @@ struct CallDelegatingCtorDtor final : EHScopeStack::Cleanup { } }; -/// A visitor which checks whether an initializer uses 'this' in a -/// way which requires the vtable to be properly set. -struct DynamicThisUseChecker - : ConstEvaluatedExprVisitor<DynamicThisUseChecker> { - using super = ConstEvaluatedExprVisitor<DynamicThisUseChecker>; - - bool usesThis = false; - - DynamicThisUseChecker(const ASTContext &c) : super(c) {} - - // Black-list all explicit and implicit references to 'this'. - // - // Do we need to worry about external references to 'this' derived - // from arbitrary code? If so, then anything which runs arbitrary - // external code might potentially access the vtable. - void VisitCXXThisExpr(const CXXThisExpr *e) { usesThis = true; } -}; } // end anonymous namespace -static bool baseInitializerUsesThis(ASTContext &c, const Expr *init) { - DynamicThisUseChecker checker(c); - checker.Visit(init); - return checker.usesThis; -} - /// Gets the address of a direct base class within a complete object. /// This should only be used for (1) non-virtual bases or (2) virtual bases /// when the type is known to be complete (e.g. in complete destructors). @@ -266,7 +243,7 @@ void CIRGenFunction::emitBaseInitializer(mlir::Location loc, // If the initializer for the base (other than the constructor // itself) accesses 'this' in any way, we need to initialize the // vtables. - if (baseInitializerUsesThis(getContext(), baseInit->getInit())) + if (CodeGenUtils::baseInitializerUsesThis(getContext(), baseInit->getInit())) initializeVTablePointers(loc, classDecl); // We can pretend to be a complete class because it only matters for diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 7e9a4d9458c93..664425b20577f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -31,6 +31,7 @@ #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include <optional> using namespace clang; @@ -1300,15 +1301,6 @@ static CharUnits getArrayElementAlign(CharUnits arrayAlign, mlir::Value idx, return arrayAlign.alignmentOfArrayElement(eltSize); } -static QualType getFixedSizeElementType(const ASTContext &astContext, - const VariableArrayType *vla) { - QualType eltType; - do { - eltType = vla->getElementType(); - } while ((vla = astContext.getAsVariableArrayType(eltType))); - return eltType; -} - static mlir::Value emitArraySubscriptPtr(CIRGenFunction &cgf, mlir::Location beginLoc, mlir::Location endLoc, mlir::Value ptr, @@ -1332,7 +1324,7 @@ static Address emitArraySubscriptPtr(CIRGenFunction &cgf, // the thing that the indices are expressed in terms of. if (const VariableArrayType *vla = cgf.getContext().getAsVariableArrayType(eltType)) { - eltType = getFixedSizeElementType(cgf.getContext(), vla); + eltType = CodeGenUtils::getFixedSizeElementType(cgf.getContext(), vla); } // We can use that to compute the best alignment of the element. @@ -2218,16 +2210,6 @@ RValue CIRGenFunction::emitAnyExpr(const Expr *e, AggValueSlot aggSlot, llvm_unreachable("bad evaluation kind"); } -// Detect the unusual situation where an inline version is shadowed by a -// non-inline version. In that case we should pick the external one -// everywhere. That's GCC behavior too. -static bool onlyHasInlineBuiltinDeclaration(const FunctionDecl *fd) { - for (const FunctionDecl *pd = fd; pd; pd = pd->getPreviousDecl()) - if (!pd->isInlineBuiltinDeclaration()) - return false; - return true; -} - CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) { const auto *fd = cast<FunctionDecl>(gd.getDecl()); @@ -2249,7 +2231,7 @@ CIRGenCallee CIRGenFunction::emitDirectCallee(const GlobalDecl &gd) { // name to make it clear it's not the actual builtin. if (auto fn = dyn_cast<cir::FuncOp>(curFn); (!fn || fn.getName() != fdInlineName) && - onlyHasInlineBuiltinDeclaration(fd)) { + CodeGenUtils::onlyHasInlineBuiltinDeclaration(fd)) { cir::FuncOp clone = mlir::cast_or_null<cir::FuncOp>(cgm.getGlobalValue(fdInlineName)); diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 2fb66232d806c..f9a4eb2b2033a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -20,6 +20,7 @@ #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include "llvm/IR/Value.h" #include <cstdint> @@ -27,73 +28,6 @@ using namespace clang; using namespace clang::CIRGen; namespace { -// FIXME(cir): This should be a common helper between CIRGen -// and traditional CodeGen -/// Is the value of the given expression possibly a reference to or -/// into a __block variable? -static bool isBlockVarRef(const Expr *e) { - // Make sure we look through parens. - e = e->IgnoreParens(); - - // Check for a direct reference to a __block variable. - if (const DeclRefExpr *dre = dyn_cast<DeclRefExpr>(e)) { - const VarDecl *var = dyn_cast<VarDecl>(dre->getDecl()); - return (var && var->hasAttr<BlocksAttr>()); - } - - // More complicated stuff. - - // Binary operators. - if (const BinaryOperator *op = dyn_cast<BinaryOperator>(e)) { - // For an assignment or pointer-to-member operation, just care - // about the LHS. - if (op->isAssignmentOp() || op->isPtrMemOp()) - return isBlockVarRef(op->getLHS()); - - // For a comma, just care about the RHS. - if (op->getOpcode() == BO_Comma) - return isBlockVarRef(op->getRHS()); - - // FIXME: pointer arithmetic? - return false; - - // Check both sides of a conditional operator. - } else if (const AbstractConditionalOperator *op = - dyn_cast<AbstractConditionalOperator>(e)) { - return isBlockVarRef(op->getTrueExpr()) || - isBlockVarRef(op->getFalseExpr()); - - // OVEs are required to support BinaryConditionalOperators. - } else if (const OpaqueValueExpr *op = dyn_cast<OpaqueValueExpr>(e)) { - if (const Expr *src = op->getSourceExpr()) - return isBlockVarRef(src); - - // Casts are necessary to get things like (*(int*)&var) = foo(). - // We don't really care about the kind of cast here, except - // we don't want to look through l2r casts, because it's okay - // to get the *value* in a __block variable. - } else if (const CastExpr *cast = dyn_cast<CastExpr>(e)) { - if (cast->getCastKind() == CK_LValueToRValue) - return false; - return isBlockVarRef(cast->getSubExpr()); - - // Handle unary operators. Again, just aggressively look through - // it, ignoring the operation. - } else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(e)) { - return isBlockVarRef(uop->getSubExpr()); - - // Look into the base of a field access. - } else if (const MemberExpr *mem = dyn_cast<MemberExpr>(e)) { - return isBlockVarRef(mem->getBase()); - - // Look into the base of a subscript. - } else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(e)) { - return isBlockVarRef(sub->getBase()); - } - - return false; -} - class AggExprEmitter : public StmtVisitor<AggExprEmitter> { CIRGenFunction &cgf; @@ -172,7 +106,7 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { e->getRHS()->getType()) && "Invalid assignment"); - if (isBlockVarRef(e->getLHS()) && + if (CodeGenUtils::isBlockVarRef(e->getLHS()) && e->getRHS()->HasSideEffects(cgf.getContext())) { cgf.cgm.errorNYI(e->getSourceRange(), "block var reference with side effects"); @@ -729,26 +663,6 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { } // namespace -static bool isTrivialFiller(Expr *e) { - if (!e) - return true; - - if (isa<ImplicitValueInitExpr>(e)) - return true; - - if (auto *ile = dyn_cast<InitListExpr>(e)) { - if (ile->getNumInits()) - return false; - return isTrivialFiller(ile->getArrayFiller()); - } - - if (const auto *cons = dyn_cast_or_null<CXXConstructExpr>(e)) - return cons->getConstructor()->isDefaultConstructor() && - cons->getConstructor()->isTrivial(); - - return false; -} - /// Given an expression with aggregate type that represents a value lvalue, this /// method emits the address of the lvalue, then loads the result into DestPtr. void AggExprEmitter::emitAggLoadOfLValue(const Expr *e) { @@ -863,7 +777,7 @@ void AggExprEmitter::emitArrayInit(Address destPtr, cir::ArrayType arrayTy, const uint64_t numArrayElements = arrayTy.getSize(); // Check whether there's a non-trivial array-fill expression. - const bool hasTrivialFiller = isTrivialFiller(arrayFiller); + const bool hasTrivialFiller = CodeGenUtils::isTrivialFiller(arrayFiller); // Any remaining elements need to be zero-initialized, possibly // using the filler expression. We can skip this if the we're diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index eaca334e88d07..4b59698326f08 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -19,6 +19,7 @@ #include "clang/AST/StmtVisitor.h" #include "clang/CIR/Dialect/IR/CIRTypes.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h" #include "mlir/IR/Location.h" @@ -3138,23 +3139,6 @@ mlir::Value ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( e->EvaluateKnownConstInt(cgf.getContext()))); } -/// Return true if the specified expression is cheap enough and side-effect-free -/// enough to evaluate unconditionally instead of conditionally. This is used -/// to convert control flow into selects in some cases. -/// TODO(cir): can be shared with LLVM codegen. -static bool isCheapEnoughToEvaluateUnconditionally(const Expr *e, - CIRGenFunction &cgf) { - // Anything that is an integer or floating point constant is fine. - return e->IgnoreParens()->isEvaluatable(cgf.getContext()); - - // Even non-volatile automatic variables can't be evaluated unconditionally. - // Referencing a thread_local may cause non-trivial initialization work to - // occur. If we're inside a lambda and one of the variables is from the scope - // outside the lambda, that function may have returned already. Reading its - // locals is a bad idea. Also, these reads may introduce races there didn't - // exist in the source-level program. -} - mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( const AbstractConditionalOperator *e) { CIRGenBuilderTy &builder = cgf.getBuilder(); @@ -3250,8 +3234,10 @@ mlir::Value ScalarExprEmitter::VisitAbstractConditionalOperator( // If this is a really simple expression (like x ? 4 : 5), emit this as a // select instead of as control flow. We can only do this if it is cheap // and safe to evaluate the LHS and RHS unconditionally. - if (isCheapEnoughToEvaluateUnconditionally(lhsExpr, cgf) && - isCheapEnoughToEvaluateUnconditionally(rhsExpr, cgf)) { + if (CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(lhsExpr, + cgf.getContext()) && + CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(rhsExpr, + cgf.getContext())) { bool lhsIsVoid = false; mlir::Value condV = cgf.evaluateExprAsBool(condExpr); assert(!cir::MissingFeatures::incrementProfileCounter()); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index a1bd15d88b118..9ba717ce7135f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -22,6 +22,7 @@ #include "clang/CIR/Dialect/IR/CIRDialect.h" #include "clang/CIR/MissingFeatures.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/FunctionUtils.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/IR/FPEnv.h" @@ -29,24 +30,6 @@ namespace clang::CIRGen { -/// shouldEmitLifetimeMarkers - Decide whether we need emit the life-time -/// markers. Mirror of CodeGenFunction::shouldEmitLifetimeMarkers. -static bool shouldEmitLifetimeMarkers(const CodeGenOptions &cgOpts, - const LangOptions &langOpts) { - - if (cgOpts.DisableLifetimeMarkers) - return false; - - // Sanitizers may use markers. - if (cgOpts.SanitizeAddressUseAfterScope || - langOpts.Sanitize.has(SanitizerKind::HWAddress) || - langOpts.Sanitize.has(SanitizerKind::Memory) || - langOpts.Sanitize.has(SanitizerKind::MemtagStack)) - return true; - - return cgOpts.OptimizationLevel != 0; -} - /// Does the statement tree rooted at \p s contain a label, switch, or indirect /// goto that could bypass a local's initialization? A coarse stand-in for /// classic CodeGen's per-decl bypass analysis (PR28267). @@ -66,7 +49,7 @@ CIRGenFunction::CIRGenFunction(CIRGenModule &cgm, CIRGenBuilderTy &builder, : CIRGenTypeCache(cgm), cgm{cgm}, builder(builder), curFPFeatures(cgm.getLangOpts()) { ehStack.setCGF(this); - shouldEmitLifetimeMarkers = CIRGen::shouldEmitLifetimeMarkers( + shouldEmitLifetimeMarkers = CodeGenUtils::shouldEmitLifetimeMarkers( cgm.getCodeGenOpts(), getContext().getLangOpts()); } diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 4588576fd2ecf..59461a6c6de4a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -25,6 +25,7 @@ #include "clang/AST/TypeBase.h" #include "clang/AST/VTableBuilder.h" #include "clang/CIR/MissingFeatures.h" +#include "clang/CodeGenUtils/ItaniumCXXABIUtils.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; @@ -2105,58 +2106,6 @@ void CIRGenItaniumCXXABI::emitBadCastCall(CIRGenFunction &cgf, emitCallToBadCast(cgf, loc); } -// TODO(cir): This could be shared with classic codegen. -static CharUnits computeOffsetHint(ASTContext &astContext, - const CXXRecordDecl *src, - const CXXRecordDecl *dst) { - CXXBasePaths paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, - /*DetectVirtual=*/false); - - // If Dst is not derived from Src we can skip the whole computation below and - // return that Src is not a public base of Dst. Record all inheritance paths. - if (!dst->isDerivedFrom(src, paths)) - return CharUnits::fromQuantity(-2); - - unsigned numPublicPaths = 0; - CharUnits offset; - - // Now walk all possible inheritance paths. - for (const CXXBasePath &path : paths) { - if (path.Access != AS_public) // Ignore non-public inheritance. - continue; - - ++numPublicPaths; - - for (const CXXBasePathElement &pathElement : path) { - // If the path contains a virtual base class we can't give any hint. - // -1: no hint. - if (pathElement.Base->isVirtual()) - return CharUnits::fromQuantity(-1); - - if (numPublicPaths > 1) // Won't use offsets, skip computation. - continue; - - // Accumulate the base class offsets. - const ASTRecordLayout &L = - astContext.getASTRecordLayout(pathElement.Class); - offset += L.getBaseClassOffset( - pathElement.Base->getType()->getAsCXXRecordDecl()); - } - } - - // -2: Src is not a public base of Dst. - if (numPublicPaths == 0) - return CharUnits::fromQuantity(-2); - - // -3: Src is a multiple public base type but never a virtual base type. - if (numPublicPaths > 1) - return CharUnits::fromQuantity(-3); - - // Otherwise, the Src type is a unique public nonvirtual base type of Dst. - // Return the offset of Src from the origin of Dst. - return offset; -} - static cir::FuncOp getItaniumDynamicCastFn(CIRGenFunction &cgf) { // Prototype: // void *__dynamic_cast(const void *sub, @@ -2337,7 +2286,8 @@ static cir::DynamicCastInfoAttr emitDynamicCastInfo(CIRGenFunction &cgf, const CXXRecordDecl *srcDecl = srcRecordTy->getAsCXXRecordDecl(); const CXXRecordDecl *destDecl = destRecordTy->getAsCXXRecordDecl(); - CharUnits offsetHint = computeOffsetHint(cgf.getContext(), srcDecl, destDecl); + CharUnits offsetHint = + CodeGenUtils::computeOffsetHint(cgf.getContext(), srcDecl, destDecl); mlir::Type ptrdiffTy = cgf.convertType(cgf.getContext().getPointerDiffType()); auto offsetHintAttr = cir::IntAttr::get(ptrdiffTy, offsetHint.getQuantity()); diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index 4c3a4c75d3b52..c5896aa37dc5a 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -34,6 +34,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGen/SwiftCallingConv.h" +#include "clang/CodeGenUtils/CallUtils.h" #include "llvm/ABI/FunctionInfo.h" #include "llvm/ABI/IRTypeMapper.h" #include "llvm/ABI/TargetInfo.h" @@ -150,13 +151,6 @@ CanQualType CodeGenTypes::DeriveThisType(const CXXRecordDecl *RD, return Context.getPointerType(RecTy); } -/// Returns the canonical formal type of the given C++ method. -static CanQual<FunctionProtoType> GetFormalType(const CXXMethodDecl *MD) { - return MD->getType() - ->getCanonicalTypeUnqualified() - .getAs<FunctionProtoType>(); -} - /// Returns the "extra-canonicalized" return type, which discards /// qualifiers on the return type. Codegen doesn't care about them, /// and it makes ABI code a little easier to be able to assume that @@ -394,7 +388,7 @@ CodeGenTypes::arrangeCXXMethodDeclaration(const CXXMethodDecl *MD) { assert(!isa<CXXConstructorDecl>(MD) && "wrong method for constructors!"); assert(!isa<CXXDestructorDecl>(MD) && "wrong method for destructors!"); - CanQualType FT = GetFormalType(MD).getAs<Type>(); + CanQualType FT = CodeGenUtils::getFormalType(MD).getAs<Type>(); setCUDAKernelCallingConvention(FT, CGM, MD); auto prototype = FT.getAs<FunctionProtoType>(); @@ -442,7 +436,7 @@ CodeGenTypes::arrangeCXXStructorDeclaration(GlobalDecl GD) { PassParams = inheritingCtorHasParams(Inherited, GD.getCtorType()); } - CanQual<FunctionProtoType> FTP = GetFormalType(MD); + CanQual<FunctionProtoType> FTP = CodeGenUtils::getFormalType(MD); // Add the formal parameters. if (PassParams) @@ -518,7 +512,7 @@ const CGFunctionInfo &CodeGenTypes::arrangeCXXConstructorCall( // +1 for implicit this, which should always be args[0]. unsigned TotalPrefixArgs = 1 + ExtraPrefixArgs; - CanQual<FunctionProtoType> FPT = GetFormalType(D); + CanQual<FunctionProtoType> FPT = CodeGenUtils::getFormalType(D); RequiredArgs Required = PassProtoArgs ? RequiredArgs::forPrototypePlus( FPT, TotalPrefixArgs + ExtraSuffixArgs) @@ -661,7 +655,7 @@ const CGFunctionInfo &CodeGenTypes::arrangeGlobalDeclaration(GlobalDecl GD) { const CGFunctionInfo & CodeGenTypes::arrangeUnprototypedMustTailThunk(const CXXMethodDecl *MD) { assert(MD->isVirtual() && "only methods have thunks"); - CanQual<FunctionProtoType> FTP = GetFormalType(MD); + CanQual<FunctionProtoType> FTP = CodeGenUtils::getFormalType(MD); CanQualType ArgTys[] = {DeriveThisType(MD->getParent(), MD)}; return arrangeLLVMFunctionInfo(Context.VoidTy, FnInfoOpts::None, ArgTys, FTP->getExtInfo(), {}, RequiredArgs(1), MD); @@ -672,7 +666,7 @@ CodeGenTypes::arrangeMSCtorClosure(const CXXConstructorDecl *CD, CXXCtorType CT) { assert(CT == Ctor_CopyingClosure || CT == Ctor_DefaultClosure); - CanQual<FunctionProtoType> FTP = GetFormalType(CD); + CanQual<FunctionProtoType> FTP = CodeGenUtils::getFormalType(CD); SmallVector<CanQualType, 2> ArgTys; const CXXRecordDecl *RD = CD->getParent(); ArgTys.push_back(DeriveThisType(RD, CD)); @@ -2701,16 +2695,6 @@ static bool canApplyNoFPClass(const ABIArgInfo &AI, QualType ParamType, return false; } -/// Return the nofpclass mask that can be applied to floating-point parameters. -static llvm::FPClassTest getNoFPClassTestMask(const LangOptions &LangOpts) { - llvm::FPClassTest Mask = llvm::fcNone; - if (LangOpts.NoHonorInfs) - Mask |= llvm::fcInf; - if (LangOpts.NoHonorNaNs) - Mask |= llvm::fcNan; - return Mask; -} - void CodeGenModule::AdjustMemoryAttribute(StringRef Name, CGCalleeInfo CalleeInfo, llvm::AttributeList &Attrs) { @@ -3035,7 +3019,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, RetAttrs.addAttribute(llvm::Attribute::InReg); if (canApplyNoFPClass(RetAI, RetTy, true)) - RetAttrs.addNoFPClassAttr(getNoFPClassTestMask(getLangOpts())); + RetAttrs.addNoFPClassAttr( + CodeGenUtils::getNoFPClassTestMask(getLangOpts())); break; case ABIArgInfo::Ignore: @@ -3197,7 +3182,8 @@ void CodeGenModule::ConstructAttributeList(StringRef Name, Attrs.addStackAlignmentAttr(llvm::MaybeAlign(AI.getDirectAlign())); if (canApplyNoFPClass(AI, ParamType, false)) - Attrs.addNoFPClassAttr(getNoFPClassTestMask(getLangOpts())); + Attrs.addNoFPClassAttr( + CodeGenUtils::getNoFPClassTestMask(getLangOpts())); break; case ABIArgInfo::Indirect: { assert(!ParamType->isIncompleteType() && diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index ffa1304253519..63f5d9ae13dbf 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -21,11 +21,11 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" #include "clang/AST/DeclTemplate.h" -#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtCXX.h" #include "clang/Basic/CodeGenOptions.h" #include "clang/CodeGen/CGFunctionInfo.h" +#include "clang/CodeGenUtils/ClassUtils.h" #include "clang/CodeGenUtils/CodeGenUtils.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Metadata.h" @@ -513,31 +513,8 @@ struct CallBaseDtor final : EHScopeStack::Cleanup { } }; -/// A visitor which checks whether an initializer uses 'this' in a -/// way which requires the vtable to be properly set. -struct DynamicThisUseChecker - : ConstEvaluatedExprVisitor<DynamicThisUseChecker> { - typedef ConstEvaluatedExprVisitor<DynamicThisUseChecker> super; - - bool UsesThis; - - DynamicThisUseChecker(const ASTContext &C) : super(C), UsesThis(false) {} - - // Black-list all explicit and implicit references to 'this'. - // - // Do we need to worry about external references to 'this' derived - // from arbitrary code? If so, then anything which runs arbitrary - // external code might potentially access the vtable. - void VisitCXXThisExpr(const CXXThisExpr *E) { UsesThis = true; } -}; } // end anonymous namespace -static bool BaseInitializerUsesThis(ASTContext &C, const Expr *Init) { - DynamicThisUseChecker Checker(C); - Checker.Visit(Init); - return Checker.UsesThis; -} - static void EmitBaseInitializer(CodeGenFunction &CGF, const CXXRecordDecl *ClassDecl, CXXCtorInitializer *BaseInit) { @@ -552,7 +529,8 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, // If the initializer for the base (other than the constructor // itself) accesses 'this' in any way, we need to initialize the // vtables. - if (BaseInitializerUsesThis(CGF.getContext(), BaseInit->getInit())) + if (CodeGenUtils::baseInitializerUsesThis(CGF.getContext(), + BaseInit->getInit())) CGF.InitializeVTablePointers(ClassDecl); // We can pretend to be a complete class because it only matters for diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index eba802e187beb..35fad3a784da9 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -40,6 +40,7 @@ #include "clang/Basic/Module.h" #include "clang/Basic/SourceManager.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" @@ -4761,15 +4762,6 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, } } -static QualType getFixedSizeElementType(const ASTContext &ctx, - const VariableArrayType *vla) { - QualType eltType; - do { - eltType = vla->getElementType(); - } while ((vla = ctx.getAsVariableArrayType(eltType))); - return eltType; -} - static bool hasBPFPreserveStaticOffset(const RecordDecl *D) { return D && D->hasAttr<BPFPreserveStaticOffsetAttr>(); } @@ -4852,7 +4844,7 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr, // Determine the element size of the statically-sized base. This is // the thing that the indices are expressed in terms of. if (auto vla = CGF.getContext().getAsVariableArrayType(eltType)) { - eltType = getFixedSizeElementType(CGF.getContext(), vla); + eltType = CodeGenUtils::getFixedSizeElementType(CGF.getContext(), vla); } // We can use that to compute the best alignment of the element. @@ -6628,16 +6620,6 @@ RValue CodeGenFunction::EmitSimpleCallExpr(const CallExpr *E, /*Chain=*/nullptr, CallOrInvoke); } -// Detect the unusual situation where an inline version is shadowed by a -// non-inline version. In that case we should pick the external one -// everywhere. That's GCC behavior too. -static bool OnlyHasInlineBuiltinDeclaration(const FunctionDecl *FD) { - for (const FunctionDecl *PD = FD; PD; PD = PD->getPreviousDecl()) - if (!PD->isInlineBuiltinDeclaration()) - return false; - return true; -} - static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) { const FunctionDecl *FD = cast<FunctionDecl>(GD.getDecl()); @@ -6657,7 +6639,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) { // When directing calling an inline builtin, call it through it's mangled // name to make it clear it's not the actual builtin. if (CGF.CurFn->getName() != FDInlineName && - OnlyHasInlineBuiltinDeclaration(FD)) { + CodeGenUtils::onlyHasInlineBuiltinDeclaration(FD)) { llvm::Constant *CalleePtr = CGF.CGM.getRawFunctionPointer(GD); llvm::Function *Fn = llvm::cast<llvm::Function>(CalleePtr); llvm::Module *M = Fn->getParent(); diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index f18978c7a936e..ff0ed1473d7df 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -25,6 +25,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" @@ -490,29 +491,6 @@ void AggExprEmitter::VisitCXXStdInitializerListExpr( "Expected std::initializer_list to only have two fields"); } -/// Determine if E is a trivial array filler, that is, one that is -/// equivalent to zero-initialization. -static bool isTrivialFiller(Expr *E) { - if (!E) - return true; - - if (isa<ImplicitValueInitExpr>(E)) - return true; - - if (auto *ILE = dyn_cast<InitListExpr>(E)) { - if (ILE->getNumInits()) - return false; - return isTrivialFiller(ILE->getArrayFiller()); - } - - if (auto *Cons = dyn_cast_or_null<CXXConstructExpr>(E)) - return Cons->getConstructor()->isDefaultConstructor() && - Cons->getConstructor()->isTrivial(); - - // FIXME: Are there other cases where we can avoid emitting an initializer? - return false; -} - // emit an elementwise cast where the RHS is a scalar or vector // or emit an aggregate splat cast static void EmitHLSLScalarElementwiseAndSplatCasts(CodeGenFunction &CGF, @@ -698,7 +676,7 @@ void AggExprEmitter::EmitArrayInit(Address DestPtr, llvm::ArrayType *AType, } // Check whether there's a non-trivial array-fill expression. - bool hasTrivialFiller = isTrivialFiller(ArrayFiller); + bool hasTrivialFiller = CodeGenUtils::isTrivialFiller(ArrayFiller); // Any remaining elements need to be zero-initialized, possibly // using the filler expression. We can skip this if the we're @@ -1322,71 +1300,6 @@ void AggExprEmitter::VisitPointerToDataMemberBinaryOperator( EmitFinalDestCopy(E->getType(), LV); } -/// Is the value of the given expression possibly a reference to or -/// into a __block variable? -static bool isBlockVarRef(const Expr *E) { - // Make sure we look through parens. - E = E->IgnoreParens(); - - // Check for a direct reference to a __block variable. - if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { - const VarDecl *var = dyn_cast<VarDecl>(DRE->getDecl()); - return (var && var->hasAttr<BlocksAttr>()); - } - - // More complicated stuff. - - // Binary operators. - if (const BinaryOperator *op = dyn_cast<BinaryOperator>(E)) { - // For an assignment or pointer-to-member operation, just care - // about the LHS. - if (op->isAssignmentOp() || op->isPtrMemOp()) - return isBlockVarRef(op->getLHS()); - - // For a comma, just care about the RHS. - if (op->getOpcode() == BO_Comma) - return isBlockVarRef(op->getRHS()); - - // FIXME: pointer arithmetic? - return false; - - // Check both sides of a conditional operator. - } else if (const AbstractConditionalOperator *op = - dyn_cast<AbstractConditionalOperator>(E)) { - return isBlockVarRef(op->getTrueExpr()) || - isBlockVarRef(op->getFalseExpr()); - - // OVEs are required to support BinaryConditionalOperators. - } else if (const OpaqueValueExpr *op = dyn_cast<OpaqueValueExpr>(E)) { - if (const Expr *src = op->getSourceExpr()) - return isBlockVarRef(src); - - // Casts are necessary to get things like (*(int*)&var) = foo(). - // We don't really care about the kind of cast here, except - // we don't want to look through l2r casts, because it's okay - // to get the *value* in a __block variable. - } else if (const CastExpr *cast = dyn_cast<CastExpr>(E)) { - if (cast->getCastKind() == CK_LValueToRValue) - return false; - return isBlockVarRef(cast->getSubExpr()); - - // Handle unary operators. Again, just aggressively look through - // it, ignoring the operation. - } else if (const UnaryOperator *uop = dyn_cast<UnaryOperator>(E)) { - return isBlockVarRef(uop->getSubExpr()); - - // Look into the base of a field access. - } else if (const MemberExpr *mem = dyn_cast<MemberExpr>(E)) { - return isBlockVarRef(mem->getBase()); - - // Look into the base of a subscript. - } else if (const ArraySubscriptExpr *sub = dyn_cast<ArraySubscriptExpr>(E)) { - return isBlockVarRef(sub->getBase()); - } - - return false; -} - void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { ApplyAtomGroup Grp(CGF.getDebugInfo()); // For an assignment to work, the value on the right has @@ -1399,7 +1312,7 @@ void AggExprEmitter::VisitBinAssign(const BinaryOperator *E) { // potentially cause a block copy, we need to evaluate the RHS first // so that the assignment goes the right place. // This is pretty semantically fragile. - if (isBlockVarRef(E->getLHS()) && + if (CodeGenUtils::isBlockVarRef(E->getLHS()) && E->getRHS()->HasSideEffects(CGF.getContext())) { // Ensure that we have a destination, and evaluate the RHS into that. EnsureDest(E->getRHS()->getType()); diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index d264f4fb28bd6..7d7130dc1d389 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -33,6 +33,7 @@ #include "clang/Basic/CodeGenOptions.h" #include "clang/Basic/DiagnosticTrap.h" #include "clang/Basic/TargetInfo.h" +#include "clang/CodeGenUtils/ExprUtils.h" #include "llvm/ADT/APFixedPoint.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/IR/Argument.h" @@ -5913,24 +5914,6 @@ Value *ScalarExprEmitter::VisitBinComma(const BinaryOperator *E) { // Other Operators //===----------------------------------------------------------------------===// -/// isCheapEnoughToEvaluateUnconditionally - Return true if the specified -/// expression is cheap enough and side-effect-free enough to evaluate -/// unconditionally instead of conditionally. This is used to convert control -/// flow into selects in some cases. -static bool isCheapEnoughToEvaluateUnconditionally(const Expr *E, - CodeGenFunction &CGF) { - // Anything that is an integer or floating point constant is fine. - return E->IgnoreParens()->isEvaluatable(CGF.getContext()); - - // Even non-volatile automatic variables can't be evaluated unconditionally. - // Referencing a thread_local may cause non-trivial initialization work to - // occur. If we're inside a lambda and one of the variables is from the scope - // outside the lambda, that function may have returned already. Reading its - // locals is a bad idea. Also, these reads may introduce races there didn't - // exist in the source-level program. -} - - Value *ScalarExprEmitter:: VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { TestAndClearIgnoreResultAssign(); @@ -6036,8 +6019,10 @@ VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { // select instead of as control flow. We can only do this if it is cheap and // safe to evaluate the LHS and RHS unconditionally. if (!llvm::EnableSingleByteCoverage && - isCheapEnoughToEvaluateUnconditionally(lhsExpr, CGF) && - isCheapEnoughToEvaluateUnconditionally(rhsExpr, CGF)) { + CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(lhsExpr, + CGF.getContext()) && + CodeGenUtils::isCheapEnoughToEvaluateUnconditionally(rhsExpr, + CGF.getContext())) { llvm::Value *CondV = CGF.EvaluateExprAsBool(condExpr); llvm::Value *StepV = Builder.CreateZExtOrBitCast(CondV, CGF.Int64Ty); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index fe39235fcd4f6..745423945ca83 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -37,6 +37,7 @@ #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/CGFunctionInfo.h" #include "clang/CodeGenUtils/CodeGenUtils.h" +#include "clang/CodeGenUtils/FunctionUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Frontend/OpenMP/OMPIRBuilder.h" @@ -59,32 +60,14 @@ using namespace clang; using namespace CodeGen; -/// shouldEmitLifetimeMarkers - Decide whether we need emit the life-time -/// markers. -static bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts, - const LangOptions &LangOpts) { - if (CGOpts.DisableLifetimeMarkers) - return false; - - // Sanitizers may use markers. - if (CGOpts.SanitizeAddressUseAfterScope || - LangOpts.Sanitize.has(SanitizerKind::HWAddress) || - LangOpts.Sanitize.has(SanitizerKind::Memory) || - LangOpts.Sanitize.has(SanitizerKind::MemtagStack)) - return true; - - // For now, only in optimized builds. - return CGOpts.OptimizationLevel != 0; -} - CodeGenFunction::CodeGenFunction(CodeGenModule &cgm, bool suppressNewContext) : CodeGenTypeCache(cgm), CGM(cgm), Target(cgm.getTarget()), Builder(cgm, cgm.getModule().getContext(), CGBuilderInserterTy(this)), SanOpts(CGM.getLangOpts().Sanitize), CurFPFeatures(CGM.getLangOpts()), DebugInfo(CGM.getModuleDebugInfo()), PGO(std::make_unique<CodeGenPGO>(cgm)), - ShouldEmitLifetimeMarkers( - shouldEmitLifetimeMarkers(CGM.getCodeGenOpts(), CGM.getLangOpts())) { + ShouldEmitLifetimeMarkers(CodeGenUtils::shouldEmitLifetimeMarkers( + CGM.getCodeGenOpts(), CGM.getLangOpts())) { if (!suppressNewContext) CGM.getCXXABI().getMangleContext().startNewFunction(); EHStack.setCGF(this); diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index c17813140b10f..42d4a15adc46c 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -31,6 +31,7 @@ #include "clang/AST/Type.h" #include "clang/Basic/PointerAuthOptions.h" #include "clang/CodeGen/ConstantInitBuilder.h" +#include "clang/CodeGenUtils/ItaniumCXXABIUtils.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Instructions.h" @@ -1554,58 +1555,6 @@ static llvm::FunctionCallee getBadCastFn(CodeGenFunction &CGF) { return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_bad_cast"); } -/// Compute the src2dst_offset hint as described in the -/// Itanium C++ ABI [2.9.7] -static CharUnits computeOffsetHint(ASTContext &Context, - const CXXRecordDecl *Src, - const CXXRecordDecl *Dst) { - CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, - /*DetectVirtual=*/false); - - // If Dst is not derived from Src we can skip the whole computation below and - // return that Src is not a public base of Dst. Record all inheritance paths. - if (!Dst->isDerivedFrom(Src, Paths)) - return CharUnits::fromQuantity(-2ULL); - - unsigned NumPublicPaths = 0; - CharUnits Offset; - - // Now walk all possible inheritance paths. - for (const CXXBasePath &Path : Paths) { - if (Path.Access != AS_public) // Ignore non-public inheritance. - continue; - - ++NumPublicPaths; - - for (const CXXBasePathElement &PathElement : Path) { - // If the path contains a virtual base class we can't give any hint. - // -1: no hint. - if (PathElement.Base->isVirtual()) - return CharUnits::fromQuantity(-1ULL); - - if (NumPublicPaths > 1) // Won't use offsets, skip computation. - continue; - - // Accumulate the base class offsets. - const ASTRecordLayout &L = Context.getASTRecordLayout(PathElement.Class); - Offset += L.getBaseClassOffset( - PathElement.Base->getType()->getAsCXXRecordDecl()); - } - } - - // -2: Src is not a public base of Dst. - if (NumPublicPaths == 0) - return CharUnits::fromQuantity(-2ULL); - - // -3: Src is a multiple public base type but never a virtual base type. - if (NumPublicPaths > 1) - return CharUnits::fromQuantity(-3ULL); - - // Otherwise, the Src type is a unique public nonvirtual base type of Dst. - // Return the offset of Src from the origin of Dst. - return Offset; -} - static llvm::FunctionCallee getBadTypeidFn(CodeGenFunction &CGF) { // void __cxa_bad_typeid(); llvm::FunctionType *FTy = llvm::FunctionType::get(CGF.VoidTy, false); @@ -1667,7 +1616,8 @@ llvm::Value *ItaniumCXXABI::emitDynamicCastCall( const CXXRecordDecl *DestDecl = DestRecordTy->getAsCXXRecordDecl(); llvm::Value *OffsetHint = llvm::ConstantInt::getSigned( PtrDiffLTy, - computeOffsetHint(CGF.getContext(), SrcDecl, DestDecl).getQuantity()); + CodeGenUtils::computeOffsetHint(CGF.getContext(), SrcDecl, DestDecl) + .getQuantity()); // Emit the call to __dynamic_cast. llvm::Value *Value = ThisAddr.emitRawPointer(CGF); diff --git a/clang/lib/CodeGenUtils/CMakeLists.txt b/clang/lib/CodeGenUtils/CMakeLists.txt index b87c83b42dd9b..e3328aa13c343 100644 --- a/clang/lib/CodeGenUtils/CMakeLists.txt +++ b/clang/lib/CodeGenUtils/CMakeLists.txt @@ -3,7 +3,12 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangCodeGenUtils + CallUtils.cpp + ClassUtils.cpp CodeGenUtils.cpp + ExprUtils.cpp + FunctionUtils.cpp + ItaniumCXXABIUtils.cpp LINK_LIBS clangAST diff --git a/clang/lib/CodeGenUtils/CallUtils.cpp b/clang/lib/CodeGenUtils/CallUtils.cpp new file mode 100644 index 0000000000000..9eb3c7cf0bc04 --- /dev/null +++ b/clang/lib/CodeGenUtils/CallUtils.cpp @@ -0,0 +1,28 @@ +//===--- CallUtils.cpp - Shared call emission queries ---------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/CallUtils.h" + +namespace clang::CodeGenUtils { + +CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *MD) { + return MD->getType() + ->getCanonicalTypeUnqualified() + .getAs<FunctionProtoType>(); +} + +llvm::FPClassTest getNoFPClassTestMask(const LangOptions &LangOpts) { + llvm::FPClassTest Mask = llvm::fcNone; + if (LangOpts.NoHonorInfs) + Mask |= llvm::fcInf; + if (LangOpts.NoHonorNaNs) + Mask |= llvm::fcNan; + return Mask; +} + +} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/ClassUtils.cpp b/clang/lib/CodeGenUtils/ClassUtils.cpp new file mode 100644 index 0000000000000..90bab38280c30 --- /dev/null +++ b/clang/lib/CodeGenUtils/ClassUtils.cpp @@ -0,0 +1,39 @@ +//===--- ClassUtils.cpp - Shared C++ class emission queries ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/ClassUtils.h" +#include "clang/AST/EvaluatedExprVisitor.h" + +namespace clang::CodeGenUtils { +namespace { +/// A visitor which checks whether an initializer uses 'this' in a +/// way which requires the vtable to be properly set. +struct DynamicThisUseChecker + : ConstEvaluatedExprVisitor<DynamicThisUseChecker> { + using super = ConstEvaluatedExprVisitor<DynamicThisUseChecker>; + + bool UsesThis = false; + + DynamicThisUseChecker(const ASTContext &C) : super(C) {} + + // Black-list all explicit and implicit references to 'this'. + // + // Do we need to worry about external references to 'this' derived + // from arbitrary code? If so, then anything which runs arbitrary + // external code might potentially access the vtable. + void VisitCXXThisExpr(const CXXThisExpr *E) { UsesThis = true; } +}; +} // namespace + +bool baseInitializerUsesThis(ASTContext &Ctx, const Expr *Init) { + DynamicThisUseChecker Checker(Ctx); + Checker.Visit(Init); + return Checker.UsesThis; +} + +} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/ExprUtils.cpp b/clang/lib/CodeGenUtils/ExprUtils.cpp new file mode 100644 index 0000000000000..69924572c3d77 --- /dev/null +++ b/clang/lib/CodeGenUtils/ExprUtils.cpp @@ -0,0 +1,128 @@ +//===--- ExprUtils.cpp - Shared expression emission queries ---------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/ExprUtils.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ExprCXX.h" + +namespace clang::CodeGenUtils { + +QualType getFixedSizeElementType(const ASTContext &Ctx, + const VariableArrayType *VLA) { + QualType EltType; + do { + EltType = VLA->getElementType(); + } while ((VLA = Ctx.getAsVariableArrayType(EltType))); + return EltType; +} + +bool isBlockVarRef(const Expr *E) { + // Make sure we look through parens. + E = E->IgnoreParens(); + + // Check for a direct reference to a __block variable. + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + const VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl()); + return (Var && Var->hasAttr<BlocksAttr>()); + } + + // More complicated stuff. + + // Binary operators. + if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) { + // For an assignment or pointer-to-member operation, just care + // about the LHS. + if (Op->isAssignmentOp() || Op->isPtrMemOp()) + return isBlockVarRef(Op->getLHS()); + + // For a comma, just care about the RHS. + if (Op->getOpcode() == BO_Comma) + return isBlockVarRef(Op->getRHS()); + + // FIXME: pointer arithmetic? + return false; + + // Check both sides of a conditional operator. + } else if (const AbstractConditionalOperator *Op = + dyn_cast<AbstractConditionalOperator>(E)) { + return isBlockVarRef(Op->getTrueExpr()) || + isBlockVarRef(Op->getFalseExpr()); + + // OVEs are required to support BinaryConditionalOperators. + } else if (const OpaqueValueExpr *Op = dyn_cast<OpaqueValueExpr>(E)) { + if (const Expr *Src = Op->getSourceExpr()) + return isBlockVarRef(Src); + + // Casts are necessary to get things like (*(int*)&var) = foo(). + // We don't really care about the kind of cast here, except + // we don't want to look through l2r casts, because it's okay + // to get the *value* in a __block variable. + } else if (const CastExpr *Cast = dyn_cast<CastExpr>(E)) { + if (Cast->getCastKind() == CK_LValueToRValue) + return false; + return isBlockVarRef(Cast->getSubExpr()); + + // Handle unary operators. Again, just aggressively look through + // it, ignoring the operation. + } else if (const UnaryOperator *UOp = dyn_cast<UnaryOperator>(E)) { + return isBlockVarRef(UOp->getSubExpr()); + + // Look into the base of a field access. + } else if (const MemberExpr *Mem = dyn_cast<MemberExpr>(E)) { + return isBlockVarRef(Mem->getBase()); + + // Look into the base of a subscript. + } else if (const ArraySubscriptExpr *Sub = dyn_cast<ArraySubscriptExpr>(E)) { + return isBlockVarRef(Sub->getBase()); + } + + return false; +} + +bool isCheapEnoughToEvaluateUnconditionally(const Expr *E, + const ASTContext &Ctx) { + // Anything that is an integer or floating point constant is fine. + return E->IgnoreParens()->isEvaluatable(Ctx); + + // Even non-volatile automatic variables can't be evaluated unconditionally. + // Referencing a thread_local may cause non-trivial initialization work to + // occur. If we're inside a lambda and one of the variables is from the scope + // outside the lambda, that function may have returned already. Reading its + // locals is a bad idea. Also, these reads may introduce races there didn't + // exist in the source-level program. +} + +bool isTrivialFiller(const Expr *E) { + if (!E) + return true; + + if (isa<ImplicitValueInitExpr>(E)) + return true; + + if (const auto *ILE = dyn_cast<InitListExpr>(E)) { + if (ILE->getNumInits()) + return false; + return isTrivialFiller(ILE->getArrayFiller()); + } + + if (const auto *Cons = dyn_cast_or_null<CXXConstructExpr>(E)) + return Cons->getConstructor()->isDefaultConstructor() && + Cons->getConstructor()->isTrivial(); + + // FIXME: Are there other cases where we can avoid emitting an initializer? + return false; +} + +bool onlyHasInlineBuiltinDeclaration(const FunctionDecl *FD) { + for (const FunctionDecl *PD = FD; PD; PD = PD->getPreviousDecl()) + if (!PD->isInlineBuiltinDeclaration()) + return false; + return true; +} + +} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/FunctionUtils.cpp b/clang/lib/CodeGenUtils/FunctionUtils.cpp new file mode 100644 index 0000000000000..9f01745abbf82 --- /dev/null +++ b/clang/lib/CodeGenUtils/FunctionUtils.cpp @@ -0,0 +1,29 @@ +//===--- FunctionUtils.cpp - Shared function emission queries -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/FunctionUtils.h" + +namespace clang::CodeGenUtils { + +bool shouldEmitLifetimeMarkers(const CodeGenOptions &CGOpts, + const LangOptions &LangOpts) { + if (CGOpts.DisableLifetimeMarkers) + return false; + + // Sanitizers may use markers. + if (CGOpts.SanitizeAddressUseAfterScope || + LangOpts.Sanitize.has(SanitizerKind::HWAddress) || + LangOpts.Sanitize.has(SanitizerKind::Memory) || + LangOpts.Sanitize.has(SanitizerKind::MemtagStack)) + return true; + + // For now, only in optimized builds. + return CGOpts.OptimizationLevel != 0; +} + +} // namespace clang::CodeGenUtils diff --git a/clang/lib/CodeGenUtils/ItaniumCXXABIUtils.cpp b/clang/lib/CodeGenUtils/ItaniumCXXABIUtils.cpp new file mode 100644 index 0000000000000..9640bd23cca54 --- /dev/null +++ b/clang/lib/CodeGenUtils/ItaniumCXXABIUtils.cpp @@ -0,0 +1,64 @@ +//===--- ItaniumCXXABIUtils.cpp - Shared Itanium C++ ABI queries ----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/CodeGenUtils/ItaniumCXXABIUtils.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/RecordLayout.h" + +namespace clang::CodeGenUtils { + +CharUnits computeOffsetHint(ASTContext &Ctx, const CXXRecordDecl *Src, + const CXXRecordDecl *Dst) { + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + + // If Dst is not derived from Src we can skip the whole computation below and + // return that Src is not a public base of Dst. Record all inheritance paths. + if (!Dst->isDerivedFrom(Src, Paths)) + return CharUnits::fromQuantity(-2ULL); + + unsigned NumPublicPaths = 0; + CharUnits Offset; + + // Now walk all possible inheritance paths. + for (const CXXBasePath &Path : Paths) { + if (Path.Access != AS_public) // Ignore non-public inheritance. + continue; + + ++NumPublicPaths; + + for (const CXXBasePathElement &PathElement : Path) { + // If the path contains a virtual base class we can't give any hint. + // -1: no hint. + if (PathElement.Base->isVirtual()) + return CharUnits::fromQuantity(-1ULL); + + if (NumPublicPaths > 1) // Won't use offsets, skip computation. + continue; + + // Accumulate the base class offsets. + const ASTRecordLayout &L = Ctx.getASTRecordLayout(PathElement.Class); + Offset += L.getBaseClassOffset( + PathElement.Base->getType()->getAsCXXRecordDecl()); + } + } + + // -2: Src is not a public base of Dst. + if (NumPublicPaths == 0) + return CharUnits::fromQuantity(-2ULL); + + // -3: Src is a multiple public base type but never a virtual base type. + if (NumPublicPaths > 1) + return CharUnits::fromQuantity(-3ULL); + + // Otherwise, the Src type is a unique public nonvirtual base type of Dst. + // Return the offset of Src from the origin of Dst. + return Offset; +} + +} // namespace clang::CodeGenUtils _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
