r290149 - Add the alloc_size attribute to clang.
Galina Kistanova via cfe-commits
cfe-commits at lists.llvm.org
Thu Dec 22 10:06:28 PST 2016
Hello George,
This commit added warning to some builders:
llvm.src\tools\clang\lib\Sema\SemaDeclAttr.cpp(259): warning C4018: '>':
signed/unsigned mismatch
http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-windows10pro-fast/builds/2892
Please have a look at this?
Thanks
Galina
On Mon, Dec 19, 2016 at 5:05 PM, George Burgess IV via cfe-commits <
cfe-commits at lists.llvm.org> wrote:
> Author: gbiv
> Date: Mon Dec 19 19:05:42 2016
> New Revision: 290149
>
> URL: http://llvm.org/viewvc/llvm-project?rev=290149&view=rev
> Log:
> Add the alloc_size attribute to clang.
>
> This patch does three things:
>
> - Gives us the alloc_size attribute in clang, which lets us infer the
> number of bytes handed back to us by malloc/realloc/calloc/any user
> functions that act in a similar manner.
> - Teaches our constexpr evaluator that evaluating some `const` variables
> is OK sometimes. This is why we have a change in
> test/SemaCXX/constant-expression-cxx11.cpp and other seemingly
> unrelated tests. Richard Smith okay'ed this idea some time ago in
> person.
> - Uniques some Blocks in CodeGen, which was reviewed separately at
> D26410. Lack of uniquing only really shows up as a problem when
> combined with our new eagerness in the face of const.
>
> Differential Revision: https://reviews.llvm.org/D14274
>
> Added:
> cfe/trunk/test/CodeGen/alloc-size.c
> cfe/trunk/test/CodeGenCXX/alloc-size.cpp
> cfe/trunk/test/Sema/alloc-size.c
> Modified:
> cfe/trunk/include/clang/Basic/Attr.td
> cfe/trunk/include/clang/Basic/AttrDocs.td
> cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> cfe/trunk/lib/AST/ExprConstant.cpp
> cfe/trunk/lib/CodeGen/CGBlocks.cpp
> cfe/trunk/lib/CodeGen/CGCall.cpp
> cfe/trunk/lib/CodeGen/CodeGenFunction.h
> cfe/trunk/lib/CodeGen/CodeGenModule.h
> cfe/trunk/lib/Sema/SemaDeclAttr.cpp
> cfe/trunk/test/CodeGenCXX/block-in-ctor-dtor.cpp
> cfe/trunk/test/CodeGenCXX/global-init.cpp
> cfe/trunk/test/CodeGenOpenCL/cl20-device-side-enqueue.cl
> cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
>
> Modified: cfe/trunk/include/clang/Basic/Attr.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
> clang/Basic/Attr.td?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/Basic/Attr.td (original)
> +++ cfe/trunk/include/clang/Basic/Attr.td Mon Dec 19 19:05:42 2016
> @@ -780,6 +780,15 @@ def EmptyBases : InheritableAttr, Target
> let Documentation = [EmptyBasesDocs];
> }
>
> +def AllocSize : InheritableAttr {
> + let Spellings = [GCC<"alloc_size">];
> + let Subjects = SubjectList<[HasFunctionProto], WarnDiag,
> + "ExpectedFunctionWithProtoType">;
> + let Args = [IntArgument<"ElemSizeParam">, IntArgument<"NumElemsParam",
> 1>];
> + let TemplateDependent = 1;
> + let Documentation = [AllocSizeDocs];
> +}
> +
> def EnableIf : InheritableAttr {
> let Spellings = [GNU<"enable_if">];
> let Subjects = SubjectList<[Function]>;
>
> Modified: cfe/trunk/include/clang/Basic/AttrDocs.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/
> clang/Basic/AttrDocs.td?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/Basic/AttrDocs.td (original)
> +++ cfe/trunk/include/clang/Basic/AttrDocs.td Mon Dec 19 19:05:42 2016
> @@ -206,6 +206,44 @@ to enforce the provided alignment assump
> }];
> }
>
> +def AllocSizeDocs : Documentation {
> + let Category = DocCatFunction;
> + let Content = [{
> +The ``alloc_size`` attribute can be placed on functions that return
> pointers in
> +order to hint to the compiler how many bytes of memory will be available
> at the
> +returned poiner. ``alloc_size`` takes one or two arguments.
> +
> +- ``alloc_size(N)`` implies that argument number N equals the number of
> + available bytes at the returned pointer.
> +- ``alloc_size(N, M)`` implies that the product of argument number N and
> + argument number M equals the number of available bytes at the returned
> + pointer.
> +
> +Argument numbers are 1-based.
> +
> +An example of how to use ``alloc_size``
> +
> +.. code-block:: c
> +
> + void *my_malloc(int a) __attribute__((alloc_size(1)));
> + void *my_calloc(int a, int b) __attribute__((alloc_size(1, 2)));
> +
> + int main() {
> + void *const p = my_malloc(100);
> + assert(__builtin_object_size(p, 0) == 100);
> + void *const a = my_calloc(20, 5);
> + assert(__builtin_object_size(a, 0) == 100);
> + }
> +
> +.. Note:: This attribute works differently in clang than it does in GCC.
> + Specifically, clang will only trace ``const`` pointers (as above); we
> give up
> + on pointers that are not marked as ``const``. In the vast majority of
> cases,
> + this is unimportant, because LLVM has support for the ``alloc_size``
> + attribute. However, this may cause mildly unintuitive behavior when
> used with
> + other attributes, such as ``enable_if``.
> + }];
> +}
> +
> def EnableIfDocs : Documentation {
> let Category = DocCatFunction;
> let Content = [{
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/
> DiagnosticSemaKinds.td?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon Dec 19
> 19:05:42 2016
> @@ -2297,6 +2297,9 @@ def warn_attribute_pointers_only : Warni
> "%0 attribute only applies to%select{| constant}1 pointer arguments">,
> InGroup<IgnoredAttributes>;
> def err_attribute_pointers_only : Error<warn_attribute_pointers_
> only.Text>;
> +def err_attribute_integers_only : Error<
> + "%0 attribute argument may only refer to a function parameter of
> integer "
> + "type">;
> def warn_attribute_return_pointers_only : Warning<
> "%0 attribute only applies to return values that are pointers">,
> InGroup<IgnoredAttributes>;
>
> Modified: cfe/trunk/lib/AST/ExprConstant.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/
> ExprConstant.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/AST/ExprConstant.cpp (original)
> +++ cfe/trunk/lib/AST/ExprConstant.cpp Mon Dec 19 19:05:42 2016
> @@ -109,19 +109,57 @@ namespace {
> return getAsBaseOrMember(E).getInt();
> }
>
> + /// Given a CallExpr, try to get the alloc_size attribute. May return
> null.
> + static const AllocSizeAttr *getAllocSizeAttr(const CallExpr *CE) {
> + const FunctionDecl *Callee = CE->getDirectCallee();
> + return Callee ? Callee->getAttr<AllocSizeAttr>() : nullptr;
> + }
> +
> + /// Attempts to unwrap a CallExpr (with an alloc_size attribute) from
> an Expr.
> + /// This will look through a single cast.
> + ///
> + /// Returns null if we couldn't unwrap a function with alloc_size.
> + static const CallExpr *tryUnwrapAllocSizeCall(const Expr *E) {
> + if (!E->getType()->isPointerType())
> + return nullptr;
> +
> + E = E->IgnoreParens();
> + // If we're doing a variable assignment from e.g. malloc(N), there
> will
> + // probably be a cast of some kind. Ignore it.
> + if (const auto *Cast = dyn_cast<CastExpr>(E))
> + E = Cast->getSubExpr()->IgnoreParens();
> +
> + if (const auto *CE = dyn_cast<CallExpr>(E))
> + return getAllocSizeAttr(CE) ? CE : nullptr;
> + return nullptr;
> + }
> +
> + /// Determines whether or not the given Base contains a call to a
> function
> + /// with the alloc_size attribute.
> + static bool isBaseAnAllocSizeCall(APValue::LValueBase Base) {
> + const auto *E = Base.dyn_cast<const Expr *>();
> + return E && E->getType()->isPointerType() &&
> tryUnwrapAllocSizeCall(E);
> + }
> +
> + /// Determines if an LValue with the given LValueBase will have an
> unsized
> + /// array in its designator.
> /// Find the path length and type of the most-derived subobject in the
> given
> /// path, and find the size of the containing array, if any.
> - static
> - unsigned findMostDerivedSubobject(ASTContext &Ctx, QualType Base,
> - ArrayRef<APValue::LValuePathEntry>
> Path,
> - uint64_t &ArraySize, QualType &Type,
> - bool &IsArray) {
> + static unsigned
> + findMostDerivedSubobject(ASTContext &Ctx, APValue::LValueBase Base,
> + ArrayRef<APValue::LValuePathEntry> Path,
> + uint64_t &ArraySize, QualType &Type, bool
> &IsArray) {
> + // This only accepts LValueBases from APValues, and APValues don't
> support
> + // arrays that lack size info.
> + assert(!isBaseAnAllocSizeCall(Base) &&
> + "Unsized arrays shouldn't appear here");
> unsigned MostDerivedLength = 0;
> - Type = Base;
> + Type = getType(Base);
> +
> for (unsigned I = 0, N = Path.size(); I != N; ++I) {
> if (Type->isArrayType()) {
> const ConstantArrayType *CAT =
> - cast<ConstantArrayType>(Ctx.getAsArrayType(Type));
> + cast<ConstantArrayType>(Ctx.getAsArrayType(Type));
> Type = CAT->getElementType();
> ArraySize = CAT->getSize().getZExtValue();
> MostDerivedLength = I + 1;
> @@ -162,17 +200,23 @@ namespace {
> /// Is this a pointer one past the end of an object?
> unsigned IsOnePastTheEnd : 1;
>
> + /// Indicator of whether the first entry is an unsized array.
> + bool FirstEntryIsAnUnsizedArray : 1;
> +
> /// Indicator of whether the most-derived object is an array element.
> unsigned MostDerivedIsArrayElement : 1;
>
> /// The length of the path to the most-derived object of which this
> is a
> /// subobject.
> - unsigned MostDerivedPathLength : 29;
> + unsigned MostDerivedPathLength : 28;
>
> /// The size of the array of which the most-derived object is an
> element.
> /// This will always be 0 if the most-derived object is not an array
> /// element. 0 is not an indicator of whether or not the most-derived
> object
> /// is an array, however, because 0-length arrays are allowed.
> + ///
> + /// If the current array is an unsized array, the value of this is
> + /// undefined.
> uint64_t MostDerivedArraySize;
>
> /// The type of the most derived object referred to by this address.
> @@ -187,23 +231,24 @@ namespace {
>
> explicit SubobjectDesignator(QualType T)
> : Invalid(false), IsOnePastTheEnd(false),
> - MostDerivedIsArrayElement(false), MostDerivedPathLength(0),
> - MostDerivedArraySize(0), MostDerivedType(T) {}
> + FirstEntryIsAnUnsizedArray(false), MostDerivedIsArrayElement(
> false),
> + MostDerivedPathLength(0), MostDerivedArraySize(0),
> + MostDerivedType(T) {}
>
> SubobjectDesignator(ASTContext &Ctx, const APValue &V)
> : Invalid(!V.isLValue() || !V.hasLValuePath()),
> IsOnePastTheEnd(false),
> - MostDerivedIsArrayElement(false), MostDerivedPathLength(0),
> - MostDerivedArraySize(0) {
> + FirstEntryIsAnUnsizedArray(false), MostDerivedIsArrayElement(
> false),
> + MostDerivedPathLength(0), MostDerivedArraySize(0) {
> + assert(V.isLValue() && "Non-LValue used to make an LValue
> designator?");
> if (!Invalid) {
> IsOnePastTheEnd = V.isLValueOnePastTheEnd();
> ArrayRef<PathEntry> VEntries = V.getLValuePath();
> Entries.insert(Entries.end(), VEntries.begin(), VEntries.end());
> if (V.getLValueBase()) {
> bool IsArray = false;
> - MostDerivedPathLength =
> - findMostDerivedSubobject(Ctx, getType(V.getLValueBase()),
> - V.getLValuePath(),
> MostDerivedArraySize,
> - MostDerivedType, IsArray);
> + MostDerivedPathLength = findMostDerivedSubobject(
> + Ctx, V.getLValueBase(), V.getLValuePath(),
> MostDerivedArraySize,
> + MostDerivedType, IsArray);
> MostDerivedIsArrayElement = IsArray;
> }
> }
> @@ -214,12 +259,25 @@ namespace {
> Entries.clear();
> }
>
> + /// Determine whether the most derived subobject is an array without a
> + /// known bound.
> + bool isMostDerivedAnUnsizedArray() const {
> + return FirstEntryIsAnUnsizedArray && Entries.size() == 1;
> + }
> +
> + /// Determine what the most derived array's size is. Results in an
> assertion
> + /// failure if the most derived array lacks a size.
> + uint64_t getMostDerivedArraySize() const {
> + assert(!isMostDerivedAnUnsizedArray() && "Unsized array has no
> size");
> + return MostDerivedArraySize;
> + }
> +
> /// Determine whether this is a one-past-the-end pointer.
> bool isOnePastTheEnd() const {
> assert(!Invalid);
> if (IsOnePastTheEnd)
> return true;
> - if (MostDerivedIsArrayElement &&
> + if (!isMostDerivedAnUnsizedArray() && MostDerivedIsArrayElement &&
> Entries[MostDerivedPathLength - 1].ArrayIndex ==
> MostDerivedArraySize)
> return true;
> return false;
> @@ -247,6 +305,21 @@ namespace {
> MostDerivedArraySize = CAT->getSize().getZExtValue();
> MostDerivedPathLength = Entries.size();
> }
> + /// Update this designator to refer to the first element within the
> array of
> + /// elements of type T. This is an array of unknown size.
> + void addUnsizedArrayUnchecked(QualType ElemTy) {
> + PathEntry Entry;
> + Entry.ArrayIndex = 0;
> + Entries.push_back(Entry);
> +
> + MostDerivedType = ElemTy;
> + MostDerivedIsArrayElement = true;
> + // The value in MostDerivedArraySize is undefined in this case. So,
> set it
> + // to an arbitrary value that's likely to loudly break things if
> it's
> + // used.
> + MostDerivedArraySize = std::numeric_limits<uint64_t>::max() / 2;
> + MostDerivedPathLength = Entries.size();
> + }
> /// Update this designator to refer to the given base or member of
> this
> /// object.
> void addDeclUnchecked(const Decl *D, bool Virtual = false) {
> @@ -280,10 +353,16 @@ namespace {
> /// Add N to the address of this subobject.
> void adjustIndex(EvalInfo &Info, const Expr *E, uint64_t N) {
> if (Invalid) return;
> + if (isMostDerivedAnUnsizedArray()) {
> + // Can't verify -- trust that the user is doing the right thing
> (or if
> + // not, trust that the caller will catch the bad behavior).
> + Entries.back().ArrayIndex += N;
> + return;
> + }
> if (MostDerivedPathLength == Entries.size() &&
> MostDerivedIsArrayElement) {
> Entries.back().ArrayIndex += N;
> - if (Entries.back().ArrayIndex > MostDerivedArraySize) {
> + if (Entries.back().ArrayIndex > getMostDerivedArraySize()) {
> diagnosePointerArithmetic(Info, E, Entries.back().ArrayIndex);
> setInvalid();
> }
> @@ -524,9 +603,15 @@ namespace {
> /// gets a chance to look at it.
> EM_PotentialConstantExpressionUnevaluated,
>
> - /// Evaluate as a constant expression. Continue evaluating if we
> find a
> - /// MemberExpr with a base that can't be evaluated.
> - EM_DesignatorFold,
> + /// Evaluate as a constant expression. Continue evaluating if
> either:
> + /// - We find a MemberExpr with a base that can't be evaluated.
> + /// - We find a variable initialized with a call to a function that
> has
> + /// the alloc_size attribute on it.
> + /// In either case, the LValue returned shall have an invalid base;
> in the
> + /// former, the base will be the invalid MemberExpr, in the latter,
> the
> + /// base will be either the alloc_size CallExpr or a CastExpr
> wrapping
> + /// said CallExpr.
> + EM_OffsetFold,
> } EvalMode;
>
> /// Are we checking whether the expression is a potential constant
> @@ -628,7 +713,7 @@ namespace {
> case EM_PotentialConstantExpression:
> case EM_ConstantExpressionUnevaluated:
> case EM_PotentialConstantExpressionUnevaluated:
> - case EM_DesignatorFold:
> + case EM_OffsetFold:
> HasActiveDiagnostic = false;
> return OptionalDiagnostic();
> }
> @@ -720,7 +805,7 @@ namespace {
> case EM_ConstantExpression:
> case EM_ConstantExpressionUnevaluated:
> case EM_ConstantFold:
> - case EM_DesignatorFold:
> + case EM_OffsetFold:
> return false;
> }
> llvm_unreachable("Missed EvalMode case");
> @@ -739,7 +824,7 @@ namespace {
> case EM_EvaluateForOverflow:
> case EM_IgnoreSideEffects:
> case EM_ConstantFold:
> - case EM_DesignatorFold:
> + case EM_OffsetFold:
> return true;
>
> case EM_PotentialConstantExpression:
> @@ -775,7 +860,7 @@ namespace {
> case EM_ConstantExpressionUnevaluated:
> case EM_ConstantFold:
> case EM_IgnoreSideEffects:
> - case EM_DesignatorFold:
> + case EM_OffsetFold:
> return false;
> }
> llvm_unreachable("Missed EvalMode case");
> @@ -805,7 +890,7 @@ namespace {
> }
>
> bool allowInvalidBaseExpr() const {
> - return EvalMode == EM_DesignatorFold;
> + return EvalMode == EM_OffsetFold;
> }
>
> class ArrayInitLoopIndex {
> @@ -856,11 +941,10 @@ namespace {
> struct FoldOffsetRAII {
> EvalInfo &Info;
> EvalInfo::EvaluationMode OldMode;
> - explicit FoldOffsetRAII(EvalInfo &Info, bool Subobject)
> + explicit FoldOffsetRAII(EvalInfo &Info)
> : Info(Info), OldMode(Info.EvalMode) {
> if (!Info.checkingPotentialConstantExpression())
> - Info.EvalMode = Subobject ? EvalInfo::EM_DesignatorFold
> - : EvalInfo::EM_ConstantFold;
> + Info.EvalMode = EvalInfo::EM_OffsetFold;
> }
>
> ~FoldOffsetRAII() { Info.EvalMode = OldMode; }
> @@ -966,10 +1050,12 @@ bool SubobjectDesignator::checkSubobject
>
> void SubobjectDesignator::diagnosePointerArithmetic(EvalInfo &Info,
> const Expr *E,
> uint64_t N) {
> + // If we're complaining, we must be able to statically determine the
> size of
> + // the most derived array.
> if (MostDerivedPathLength == Entries.size() &&
> MostDerivedIsArrayElement)
> Info.CCEDiag(E, diag::note_constexpr_array_index)
> << static_cast<int>(N) << /*array*/ 0
> - << static_cast<unsigned>(MostDerivedArraySize);
> + << static_cast<unsigned>(getMostDerivedArraySize());
> else
> Info.CCEDiag(E, diag::note_constexpr_array_index)
> << static_cast<int>(N) << /*non-array*/ 1;
> @@ -1102,12 +1188,16 @@ namespace {
> if (Designator.Invalid)
> V = APValue(Base, Offset, APValue::NoLValuePath(), CallIndex,
> IsNullPtr);
> - else
> + else {
> + assert(!InvalidBase && "APValues can't handle invalid LValue
> bases");
> + assert(!Designator.FirstEntryIsAnUnsizedArray &&
> + "Unsized array with a valid base?");
> V = APValue(Base, Offset, Designator.Entries,
> Designator.IsOnePastTheEnd, CallIndex, IsNullPtr);
> + }
> }
> void setFrom(ASTContext &Ctx, const APValue &V) {
> - assert(V.isLValue());
> + assert(V.isLValue() && "Setting LValue from a non-LValue?");
> Base = V.getLValueBase();
> Offset = V.getLValueOffset();
> InvalidBase = false;
> @@ -1118,6 +1208,15 @@ namespace {
>
> void set(APValue::LValueBase B, unsigned I = 0, bool BInvalid = false,
> bool IsNullPtr_ = false, uint64_t Offset_ = 0) {
> +#ifndef NDEBUG
> + // We only allow a few types of invalid bases. Enforce that here.
> + if (BInvalid) {
> + const auto *E = B.get<const Expr *>();
> + assert((isa<MemberExpr>(E) || tryUnwrapAllocSizeCall(E)) &&
> + "Unexpected type of invalid base");
> + }
> +#endif
> +
> Base = B;
> Offset = CharUnits::fromQuantity(Offset_);
> InvalidBase = BInvalid;
> @@ -1157,6 +1256,13 @@ namespace {
> if (checkSubobject(Info, E, isa<FieldDecl>(D) ? CSK_Field :
> CSK_Base))
> Designator.addDeclUnchecked(D, Virtual);
> }
> + void addUnsizedArray(EvalInfo &Info, QualType ElemTy) {
> + assert(Designator.Entries.empty() && getType(Base)->isPointerType()
> );
> + assert(isBaseAnAllocSizeCall(Base) &&
> + "Only alloc_size bases can have unsized arrays");
> + Designator.FirstEntryIsAnUnsizedArray = true;
> + Designator.addUnsizedArrayUnchecked(ElemTy);
> + }
> void addArray(EvalInfo &Info, const Expr *E, const ConstantArrayType
> *CAT) {
> if (checkSubobject(Info, E, CSK_ArrayToPointer))
> Designator.addArrayUnchecked(CAT);
> @@ -2796,7 +2902,7 @@ static CompleteObject findCompleteObject
> // All the remaining cases only permit reading.
> Info.FFDiag(E, diag::note_constexpr_modify_global);
> return CompleteObject();
> - } else if (VD->isConstexpr()) {
> + } else if (VD->isConstexpr() || BaseType.isConstQualified()) {
> // OK, we can read this variable.
> } else if (BaseType->isIntegralOrEnumerationType()) {
> // In OpenCL if a variable is in constant address space it is a
> const value.
> @@ -5079,6 +5185,105 @@ bool LValueExprEvaluator::VisitBinAssign
> // Pointer Evaluation
> //===-------------------------------------------------------
> ---------------===//
>
> +/// \brief Attempts to compute the number of bytes available at the
> pointer
> +/// returned by a function with the alloc_size attribute. Returns true if
> we
> +/// were successful. Places an unsigned number into `Result`.
> +///
> +/// This expects the given CallExpr to be a call to a function with an
> +/// alloc_size attribute.
> +static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
> + const CallExpr *Call,
> + llvm::APInt &Result) {
> + const AllocSizeAttr *AllocSize = getAllocSizeAttr(Call);
> +
> + // alloc_size args are 1-indexed, 0 means not present.
> + assert(AllocSize && AllocSize->getElemSizeParam() != 0);
> + unsigned SizeArgNo = AllocSize->getElemSizeParam() - 1;
> + unsigned BitsInSizeT = Ctx.getTypeSize(Ctx.getSizeType());
> + if (Call->getNumArgs() <= SizeArgNo)
> + return false;
> +
> + auto EvaluateAsSizeT = [&](const Expr *E, APSInt &Into) {
> + if (!E->EvaluateAsInt(Into, Ctx, Expr::SE_AllowSideEffects))
> + return false;
> + if (Into.isNegative() || !Into.isIntN(BitsInSizeT))
> + return false;
> + Into = Into.zextOrSelf(BitsInSizeT);
> + return true;
> + };
> +
> + APSInt SizeOfElem;
> + if (!EvaluateAsSizeT(Call->getArg(SizeArgNo), SizeOfElem))
> + return false;
> +
> + if (!AllocSize->getNumElemsParam()) {
> + Result = std::move(SizeOfElem);
> + return true;
> + }
> +
> + APSInt NumberOfElems;
> + // Argument numbers start at 1
> + unsigned NumArgNo = AllocSize->getNumElemsParam() - 1;
> + if (!EvaluateAsSizeT(Call->getArg(NumArgNo), NumberOfElems))
> + return false;
> +
> + bool Overflow;
> + llvm::APInt BytesAvailable = SizeOfElem.umul_ov(NumberOfElems,
> Overflow);
> + if (Overflow)
> + return false;
> +
> + Result = std::move(BytesAvailable);
> + return true;
> +}
> +
> +/// \brief Convenience function. LVal's base must be a call to an
> alloc_size
> +/// function.
> +static bool getBytesReturnedByAllocSizeCall(const ASTContext &Ctx,
> + const LValue &LVal,
> + llvm::APInt &Result) {
> + assert(isBaseAnAllocSizeCall(LVal.getLValueBase()) &&
> + "Can't get the size of a non alloc_size function");
> + const auto *Base = LVal.getLValueBase().get<const Expr *>();
> + const CallExpr *CE = tryUnwrapAllocSizeCall(Base);
> + return getBytesReturnedByAllocSizeCall(Ctx, CE, Result);
> +}
> +
> +/// \brief Attempts to evaluate the given LValueBase as the result of a
> call to
> +/// a function with the alloc_size attribute. If it was possible to do
> so, this
> +/// function will return true, make Result's Base point to said function
> call,
> +/// and mark Result's Base as invalid.
> +static bool evaluateLValueAsAllocSize(EvalInfo &Info,
> APValue::LValueBase Base,
> + LValue &Result) {
> + if (!Info.allowInvalidBaseExpr() || Base.isNull())
> + return false;
> +
> + // Because we do no form of static analysis, we only support const
> variables.
> + //
> + // Additionally, we can't support parameters, nor can we support static
> + // variables (in the latter case, use-before-assign isn't UB; in the
> former,
> + // we have no clue what they'll be assigned to).
> + const auto *VD =
> + dyn_cast_or_null<VarDecl>(Base.dyn_cast<const ValueDecl *>());
> + if (!VD || !VD->isLocalVarDecl() || !VD->getType().isConstQualified())
> + return false;
> +
> + const Expr *Init = VD->getAnyInitializer();
> + if (!Init)
> + return false;
> +
> + const Expr *E = Init->IgnoreParens();
> + if (!tryUnwrapAllocSizeCall(E))
> + return false;
> +
> + // Store E instead of E unwrapped so that the type of the LValue's base
> is
> + // what the user wanted.
> + Result.setInvalid(E);
> +
> + QualType Pointee = E->getType()->castAs<PointerType>()->
> getPointeeType();
> + Result.addUnsizedArray(Info, Pointee);
> + return true;
> +}
> +
> namespace {
> class PointerExprEvaluator
> : public ExprEvaluatorBase<PointerExprEvaluator> {
> @@ -5088,6 +5293,8 @@ class PointerExprEvaluator
> Result.set(E);
> return true;
> }
> +
> + bool visitNonBuiltinCallExpr(const CallExpr *E);
> public:
>
> PointerExprEvaluator(EvalInfo &info, LValue &Result)
> @@ -5270,6 +5477,19 @@ bool PointerExprEvaluator::VisitCastExpr
>
> case CK_FunctionToPointerDecay:
> return EvaluateLValue(SubExpr, Result, Info);
> +
> + case CK_LValueToRValue: {
> + LValue LVal;
> + if (!EvaluateLValue(E->getSubExpr(), LVal, Info))
> + return false;
> +
> + APValue RVal;
> + // Note, we use the subexpression's type in order to retain
> cv-qualifiers.
> + if (!handleLValueToRValueConversion(Info, E,
> E->getSubExpr()->getType(),
> + LVal, RVal))
> + return evaluateLValueAsAllocSize(Info, LVal.Base, Result);
> + return Success(RVal, E);
> + }
> }
>
> return ExprEvaluatorBaseTy::VisitCastExpr(E);
> @@ -5307,6 +5527,20 @@ static CharUnits GetAlignOfExpr(EvalInfo
> return GetAlignOfType(Info, E->getType());
> }
>
> +// To be clear: this happily visits unsupported builtins. Better name
> welcomed.
> +bool PointerExprEvaluator::visitNonBuiltinCallExpr(const CallExpr *E) {
> + if (ExprEvaluatorBaseTy::VisitCallExpr(E))
> + return true;
> +
> + if (!(Info.allowInvalidBaseExpr() && getAllocSizeAttr(E)))
> + return false;
> +
> + Result.setInvalid(E);
> + QualType PointeeTy = E->getType()->castAs<PointerType>()->
> getPointeeType();
> + Result.addUnsizedArray(Info, PointeeTy);
> + return true;
> +}
> +
> bool PointerExprEvaluator::VisitCallExpr(const CallExpr *E) {
> if (IsStringLiteralCall(E))
> return Success(E);
> @@ -5314,7 +5548,7 @@ bool PointerExprEvaluator::VisitCallExpr
> if (unsigned BuiltinOp = E->getBuiltinCallee())
> return VisitBuiltinCallExpr(E, BuiltinOp);
>
> - return ExprEvaluatorBaseTy::VisitCallExpr(E);
> + return visitNonBuiltinCallExpr(E);
> }
>
> bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
> @@ -5473,7 +5707,7 @@ bool PointerExprEvaluator::VisitBuiltinC
> }
>
> default:
> - return ExprEvaluatorBaseTy::VisitCallExpr(E);
> + return visitNonBuiltinCallExpr(E);
> }
> }
>
> @@ -6512,8 +6746,6 @@ public:
> bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
> bool VisitSizeOfPackExpr(const SizeOfPackExpr *E);
>
> -private:
> - bool TryEvaluateBuiltinObjectSize(const CallExpr *E, unsigned Type);
> // FIXME: Missing: array subscript of vector, member of vector
> };
> } // end anonymous namespace
> @@ -6785,7 +7017,7 @@ static QualType getObjectType(APValue::L
> }
>
> /// A more selective version of E->IgnoreParenCasts for
> -/// TryEvaluateBuiltinObjectSize. This ignores some casts/parens that
> serve only
> +/// tryEvaluateBuiltinObjectSize. This ignores some casts/parens that
> serve only
> /// to change the type of E.
> /// Ex. For E = `(short*)((char*)(&foo))`, returns `&foo`
> ///
> @@ -6852,39 +7084,51 @@ static bool isDesignatorAtObjectEnd(cons
> }
> }
>
> + unsigned I = 0;
> QualType BaseType = getType(Base);
> - for (int I = 0, E = LVal.Designator.Entries.size(); I != E; ++I) {
> + if (LVal.Designator.FirstEntryIsAnUnsizedArray) {
> + assert(isBaseAnAllocSizeCall(Base) &&
> + "Unsized array in non-alloc_size call?");
> + // If this is an alloc_size base, we should ignore the initial array
> index
> + ++I;
> + BaseType = BaseType->castAs<PointerType>()->getPointeeType();
> + }
> +
> + for (unsigned E = LVal.Designator.Entries.size(); I != E; ++I) {
> + const auto &Entry = LVal.Designator.Entries[I];
> if (BaseType->isArrayType()) {
> // Because __builtin_object_size treats arrays as objects, we can
> ignore
> // the index iff this is the last array in the Designator.
> if (I + 1 == E)
> return true;
> - auto *CAT = cast<ConstantArrayType>(Ctx.getAsArrayType(BaseType));
> - uint64_t Index = LVal.Designator.Entries[I].ArrayIndex;
> + const auto *CAT = cast<ConstantArrayType>(Ctx.
> getAsArrayType(BaseType));
> + uint64_t Index = Entry.ArrayIndex;
> if (Index + 1 != CAT->getSize())
> return false;
> BaseType = CAT->getElementType();
> } else if (BaseType->isAnyComplexType()) {
> - auto *CT = BaseType->castAs<ComplexType>();
> - uint64_t Index = LVal.Designator.Entries[I].ArrayIndex;
> + const auto *CT = BaseType->castAs<ComplexType>();
> + uint64_t Index = Entry.ArrayIndex;
> if (Index != 1)
> return false;
> BaseType = CT->getElementType();
> - } else if (auto *FD = getAsField(LVal.Designator.Entries[I])) {
> + } else if (auto *FD = getAsField(Entry)) {
> bool Invalid;
> if (!IsLastOrInvalidFieldDecl(FD, Invalid))
> return Invalid;
> BaseType = FD->getType();
> } else {
> - assert(getAsBaseClass(LVal.Designator.Entries[I]) != nullptr &&
> - "Expecting cast to a base class");
> + assert(getAsBaseClass(Entry) && "Expecting cast to a base class");
> return false;
> }
> }
> return true;
> }
>
> -/// Tests to see if the LValue has a designator (that isn't necessarily
> valid).
> +/// Tests to see if the LValue has a user-specified designator (that isn't
> +/// necessarily valid). Note that this always returns 'true' if the
> LValue has
> +/// an unsized array as its first designator entry, because there's
> currently no
> +/// way to tell if the user typed *foo or foo[0].
> static bool refersToCompleteObject(const LValue &LVal) {
> if (LVal.Designator.Invalid || !LVal.Designator.Entries.empty())
> return false;
> @@ -6892,42 +7136,142 @@ static bool refersToCompleteObject(const
> if (!LVal.InvalidBase)
> return true;
>
> - auto *E = LVal.Base.dyn_cast<const Expr *>();
> - (void)E;
> - assert(E != nullptr && isa<MemberExpr>(E));
> - return false;
> + // If `E` is a MemberExpr, then the first part of the designator is
> hiding in
> + // the LValueBase.
> + const auto *E = LVal.Base.dyn_cast<const Expr *>();
> + return !E || !isa<MemberExpr>(E);
> +}
> +
> +/// Attempts to detect a user writing into a piece of memory that's
> impossible
> +/// to figure out the size of by just using types.
> +static bool isUserWritingOffTheEnd(const ASTContext &Ctx, const LValue
> &LVal) {
> + const SubobjectDesignator &Designator = LVal.Designator;
> + // Notes:
> + // - Users can only write off of the end when we have an invalid base.
> Invalid
> + // bases imply we don't know where the memory came from.
> + // - We used to be a bit more aggressive here; we'd only be
> conservative if
> + // the array at the end was flexible, or if it had 0 or 1 elements.
> This
> + // broke some common standard library extensions (PR30346), but was
> + // otherwise seemingly fine. It may be useful to reintroduce this
> behavior
> + // with some sort of whitelist. OTOH, it seems that GCC is always
> + // conservative with the last element in structs (if it's an array),
> so our
> + // current behavior is more compatible than a whitelisting approach
> would
> + // be.
> + return LVal.InvalidBase &&
> + Designator.Entries.size() == Designator.MostDerivedPathLength &&
> + Designator.MostDerivedIsArrayElement &&
> + isDesignatorAtObjectEnd(Ctx, LVal);
> +}
> +
> +/// Converts the given APInt to CharUnits, assuming the APInt is unsigned.
> +/// Fails if the conversion would cause loss of precision.
> +static bool convertUnsignedAPIntToCharUnits(const llvm::APInt &Int,
> + CharUnits &Result) {
> + auto CharUnitsMax = std::numeric_limits<CharUnits:
> :QuantityType>::max();
> + if (Int.ugt(CharUnitsMax))
> + return false;
> + Result = CharUnits::fromQuantity(Int.getZExtValue());
> + return true;
> }
>
> -/// Tries to evaluate the __builtin_object_size for @p E. If successful,
> returns
> -/// true and stores the result in @p Size.
> +/// Helper for tryEvaluateBuiltinObjectSize -- Given an LValue, this will
> +/// determine how many bytes exist from the beginning of the object to
> either
> +/// the end of the current subobject, or the end of the object itself,
> depending
> +/// on what the LValue looks like + the value of Type.
> ///
> -/// If @p WasError is non-null, this will report whether the failure to
> evaluate
> -/// is to be treated as an Error in IntExprEvaluator.
> -static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type,
> - EvalInfo &Info, uint64_t &Size,
> - bool *WasError = nullptr) {
> - if (WasError != nullptr)
> - *WasError = false;
> -
> - auto Error = [&](const Expr *E) {
> - if (WasError != nullptr)
> - *WasError = true;
> +/// If this returns false, the value of Result is undefined.
> +static bool determineEndOffset(EvalInfo &Info, SourceLocation ExprLoc,
> + unsigned Type, const LValue &LVal,
> + CharUnits &EndOffset) {
> + // __builtin_object_size(&foo, N) == __builtin_object_size(&foo, (N &
> ~1U)).
> + // (Where foo is an expression that has no designator). Hence, if we've
> no
> + // designator, we can ignore the subobject bit.
> + bool EvaluateAsCompleteObject =
> + !(Type & 1) || LVal.Designator.isMostDerivedAnUnsizedArray() ||
> + refersToCompleteObject(LVal);
> +
> + // We want to evaluate the size of the entire object. This is a valid
> fallback
> + // for when Type=1 and the designator is invalid, because we're asked
> for an
> + // upper-bound.
> + if (LVal.Designator.Invalid || EvaluateAsCompleteObject) {
> + // We can't give a correct lower bound for Type=3 if the designator is
> + // invalid and we're meant to be evaluating it.
> + if (Type == 3 && LVal.Designator.Invalid && !EvaluateAsCompleteObject)
> + return false;
> +
> + llvm::APInt APEndOffset;
> + if (isBaseAnAllocSizeCall(LVal.getLValueBase()) &&
> + getBytesReturnedByAllocSizeCall(Info.Ctx, LVal, APEndOffset))
> + return convertUnsignedAPIntToCharUnits(APEndOffset, EndOffset);
> +
> + if (LVal.InvalidBase)
> + return false;
> +
> + QualType BaseTy = getObjectType(LVal.getLValueBase());
> + return !BaseTy.isNull() && HandleSizeof(Info, ExprLoc, BaseTy,
> EndOffset);
> + }
> +
> + // We want to evaluate the size of a subobject.
> + const SubobjectDesignator &Designator = LVal.Designator;
> +
> + // The following is a moderately common idiom in C:
> + //
> + // struct Foo { int a; char c[1]; };
> + // struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) +
> strlen(Bar));
> + // strcpy(&F->c[0], Bar);
> + //
> + // In order to not break too much legacy code, we need to support it.
> + if (isUserWritingOffTheEnd(Info.Ctx, LVal)) {
> + // If we can resolve this to an alloc_size call, we can hand that
> back,
> + // because we know for certain how many bytes there are to write to.
> + llvm::APInt APEndOffset;
> + if (isBaseAnAllocSizeCall(LVal.getLValueBase()) &&
> + getBytesReturnedByAllocSizeCall(Info.Ctx, LVal, APEndOffset))
> + return convertUnsignedAPIntToCharUnits(APEndOffset, EndOffset);
> +
> + // If we cannot determine the size of the initial allocation, then we
> can't
> + // given an accurate upper-bound. However, we are still able to give
> + // conservative lower-bounds for Type=3.
> + if (Type == 1)
> + return false;
> + }
> +
> + CharUnits BytesPerElem;
> + if (!HandleSizeof(Info, ExprLoc, Designator.MostDerivedType,
> BytesPerElem))
> return false;
> - };
>
> - auto Success = [&](uint64_t S, const Expr *E) {
> - Size = S;
> - return true;
> - };
> + // According to the GCC documentation, we want the size of the subobject
> + // denoted by the pointer. But that's not quite right -- what we
> actually
> + // want is the size of the immediately-enclosing array, if there is one.
> + int64_t ElemsRemaining;
> + if (Designator.MostDerivedIsArrayElement &&
> + Designator.Entries.size() == Designator.MostDerivedPathLength) {
> + uint64_t ArraySize = Designator.getMostDerivedArraySize();
> + uint64_t ArrayIndex = Designator.Entries.back().ArrayIndex;
> + ElemsRemaining = ArraySize <= ArrayIndex ? 0 : ArraySize - ArrayIndex;
> + } else {
> + ElemsRemaining = Designator.isOnePastTheEnd() ? 0 : 1;
> + }
> +
> + EndOffset = LVal.getLValueOffset() + BytesPerElem * ElemsRemaining;
> + return true;
> +}
>
> +/// \brief Tries to evaluate the __builtin_object_size for @p E. If
> successful,
> +/// returns true and stores the result in @p Size.
> +///
> +/// If @p WasError is non-null, this will report whether the failure to
> evaluate
> +/// is to be treated as an Error in IntExprEvaluator.
> +static bool tryEvaluateBuiltinObjectSize(const Expr *E, unsigned Type,
> + EvalInfo &Info, uint64_t &Size) {
> // Determine the denoted object.
> - LValue Base;
> + LValue LVal;
> {
> // The operand of __builtin_object_size is never evaluated for
> side-effects.
> // If there are any, but we can determine the pointed-to object
> anyway, then
> // ignore the side-effects.
> SpeculativeEvaluationRAII SpeculativeEval(Info);
> - FoldOffsetRAII Fold(Info, Type & 1);
> + FoldOffsetRAII Fold(Info);
>
> if (E->isGLValue()) {
> // It's possible for us to be given GLValues if we're called via
> @@ -6935,122 +7279,29 @@ static bool tryEvaluateBuiltinObjectSize
> APValue RVal;
> if (!EvaluateAsRValue(Info, E, RVal))
> return false;
> - Base.setFrom(Info.Ctx, RVal);
> - } else if (!EvaluatePointer(ignorePointerCastsAndParens(E), Base,
> Info))
> + LVal.setFrom(Info.Ctx, RVal);
> + } else if (!EvaluatePointer(ignorePointerCastsAndParens(E), LVal,
> Info))
> return false;
> }
>
> - CharUnits BaseOffset = Base.getLValueOffset();
> // If we point to before the start of the object, there are no
> accessible
> // bytes.
> - if (BaseOffset.isNegative())
> - return Success(0, E);
> -
> - // In the case where we're not dealing with a subobject, we discard the
> - // subobject bit.
> - bool SubobjectOnly = (Type & 1) != 0 && !refersToCompleteObject(Base);
> -
> - // If Type & 1 is 0, we need to be able to statically guarantee that
> the bytes
> - // exist. If we can't verify the base, then we can't do that.
> - //
> - // As a special case, we produce a valid object size for an unknown
> object
> - // with a known designator if Type & 1 is 1. For instance:
> - //
> - // extern struct X { char buff[32]; int a, b, c; } *p;
> - // int a = __builtin_object_size(p->buff + 4, 3); // returns 28
> - // int b = __builtin_object_size(p->buff + 4, 2); // returns 0, not 40
> - //
> - // This matches GCC's behavior.
> - if (Base.InvalidBase && !SubobjectOnly)
> - return Error(E);
> -
> - // If we're not examining only the subobject, then we reset to a
> complete
> - // object designator
> - //
> - // If Type is 1 and we've lost track of the subobject, just find the
> complete
> - // object instead. (If Type is 3, that's not correct behavior and we
> should
> - // return 0 instead.)
> - LValue End = Base;
> - if (!SubobjectOnly || (End.Designator.Invalid && Type == 1)) {
> - QualType T = getObjectType(End.getLValueBase());
> - if (T.isNull())
> - End.Designator.setInvalid();
> - else {
> - End.Designator = SubobjectDesignator(T);
> - End.Offset = CharUnits::Zero();
> - }
> + if (LVal.getLValueOffset().isNegative()) {
> + Size = 0;
> + return true;
> }
>
> - // If it is not possible to determine which objects ptr points to at
> compile
> - // time, __builtin_object_size should return (size_t) -1 for type 0 or 1
> - // and (size_t) 0 for type 2 or 3.
> - if (End.Designator.Invalid)
> + CharUnits EndOffset;
> + if (!determineEndOffset(Info, E->getExprLoc(), Type, LVal, EndOffset))
> return false;
>
> - // According to the GCC documentation, we want the size of the subobject
> - // denoted by the pointer. But that's not quite right -- what we
> actually
> - // want is the size of the immediately-enclosing array, if there is one.
> - int64_t AmountToAdd = 1;
> - if (End.Designator.MostDerivedIsArrayElement &&
> - End.Designator.Entries.size() == End.Designator.MostDerivedPathLength)
> {
> - // We got a pointer to an array. Step to its end.
> - AmountToAdd = End.Designator.MostDerivedArraySize -
> - End.Designator.Entries.back().ArrayIndex;
> - } else if (End.Designator.isOnePastTheEnd()) {
> - // We're already pointing at the end of the object.
> - AmountToAdd = 0;
> - }
> -
> - QualType PointeeType = End.Designator.MostDerivedType;
> - assert(!PointeeType.isNull());
> - if (PointeeType->isIncompleteType() || PointeeType->isFunctionType())
> - return Error(E);
> -
> - if (!HandleLValueArrayAdjustment(Info, E, End, End.Designator.
> MostDerivedType,
> - AmountToAdd))
> - return false;
> -
> - auto EndOffset = End.getLValueOffset();
> -
> - // The following is a moderately common idiom in C:
> - //
> - // struct Foo { int a; char c[1]; };
> - // struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) +
> strlen(Bar));
> - // strcpy(&F->c[0], Bar);
> - //
> - // So, if we see that we're examining an array at the end of a struct
> with an
> - // unknown base, we give up instead of breaking code that behaves this
> way.
> - // Note that we only do this when Type=1, because Type=3 is a lower
> bound, so
> - // answering conservatively is fine.
> - //
> - // We used to be a bit more aggressive here; we'd only be conservative
> if the
> - // array at the end was flexible, or if it had 0 or 1 elements. This
> broke
> - // some common standard library extensions (PR30346), but was otherwise
> - // seemingly fine. It may be useful to reintroduce this behavior with
> some
> - // sort of whitelist. OTOH, it seems that GCC is always conservative
> with the
> - // last element in structs (if it's an array), so our current behavior
> is more
> - // compatible than a whitelisting approach would be.
> - if (End.InvalidBase && SubobjectOnly && Type == 1 &&
> - End.Designator.Entries.size() == End.Designator.MostDerivedPathLength
> &&
> - End.Designator.MostDerivedIsArrayElement &&
> - isDesignatorAtObjectEnd(Info.Ctx, End))
> - return false;
> -
> - if (BaseOffset > EndOffset)
> - return Success(0, E);
> -
> - return Success((EndOffset - BaseOffset).getQuantity(), E);
> -}
> -
> -bool IntExprEvaluator::TryEvaluateBuiltinObjectSize(const CallExpr *E,
> - unsigned Type) {
> - uint64_t Size;
> - bool WasError;
> - if (::tryEvaluateBuiltinObjectSize(E->getArg(0), Type, Info, Size,
> &WasError))
> - return Success(Size, E);
> - if (WasError)
> - return Error(E);
> - return false;
> + // If we've fallen outside of the end offset, just pretend there's
> nothing to
> + // write to/read from.
> + if (EndOffset <= LVal.getLValueOffset())
> + Size = 0;
> + else
> + Size = (EndOffset - LVal.getLValueOffset()).getQuantity();
> + return true;
> }
>
> bool IntExprEvaluator::VisitCallExpr(const CallExpr *E) {
> @@ -7072,8 +7323,9 @@ bool IntExprEvaluator::VisitBuiltinCallE
> E->getArg(1)->EvaluateKnownConstInt(Info.Ctx).getZExtValue();
> assert(Type <= 3 && "unexpected type");
>
> - if (TryEvaluateBuiltinObjectSize(E, Type))
> - return true;
> + uint64_t Size;
> + if (tryEvaluateBuiltinObjectSize(E->getArg(0), Type, Info, Size))
> + return Success(Size, E);
>
> if (E->getArg(0)->HasSideEffects(Info.Ctx))
> return Success((Type & 2) ? 0 : -1, E);
> @@ -7086,7 +7338,7 @@ bool IntExprEvaluator::VisitBuiltinCallE
> case EvalInfo::EM_ConstantFold:
> case EvalInfo::EM_EvaluateForOverflow:
> case EvalInfo::EM_IgnoreSideEffects:
> - case EvalInfo::EM_DesignatorFold:
> + case EvalInfo::EM_OffsetFold:
> // Leave it to IR generation.
> return Error(E);
> case EvalInfo::EM_ConstantExpressionUnevaluated:
> @@ -10189,5 +10441,5 @@ bool Expr::tryEvaluateObjectSize(uint64_
>
> Expr::EvalStatus Status;
> EvalInfo Info(Ctx, Status, EvalInfo::EM_ConstantFold);
> - return ::tryEvaluateBuiltinObjectSize(this, Type, Info, Result);
> + return tryEvaluateBuiltinObjectSize(this, Type, Info, Result);
> }
>
> Modified: cfe/trunk/lib/CodeGen/CGBlocks.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/
> CGBlocks.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/CodeGen/CGBlocks.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGBlocks.cpp Mon Dec 19 19:05:42 2016
> @@ -686,6 +686,8 @@ llvm::Value *CodeGenFunction::EmitBlockL
> // If the block has no captures, we won't have a pre-computed
> // layout for it.
> if (!blockExpr->getBlockDecl()->hasCaptures()) {
> + if (llvm::Constant *Block = CGM.getAddrOfGlobalBlockIfEmitted(
> blockExpr))
> + return Block;
> CGBlockInfo blockInfo(blockExpr->getBlockDecl(), CurFn->getName());
> computeBlockInfo(CGM, this, blockInfo);
> blockInfo.BlockExpression = blockExpr;
> @@ -1047,9 +1049,19 @@ Address CodeGenFunction::GetAddrOfBlockD
> return addr;
> }
>
> +void CodeGenModule::setAddrOfGlobalBlock(const BlockExpr *BE,
> + llvm::Constant *Addr) {
> + bool Ok = EmittedGlobalBlocks.insert(std::make_pair(BE, Addr)).second;
> + (void)Ok;
> + assert(Ok && "Trying to replace an already-existing global block!");
> +}
> +
> llvm::Constant *
> CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *BE,
> StringRef Name) {
> + if (llvm::Constant *Block = getAddrOfGlobalBlockIfEmitted(BE))
> + return Block;
> +
> CGBlockInfo blockInfo(BE->getBlockDecl(), Name);
> blockInfo.BlockExpression = BE;
>
> @@ -1074,6 +1086,11 @@ static llvm::Constant *buildGlobalBlock(
> const CGBlockInfo &blockInfo,
> llvm::Constant *blockFn) {
> assert(blockInfo.CanBeGlobal);
> + // Callers should detect this case on their own: calling this function
> + // generally requires computing layout information, which is a waste of
> time
> + // if we've already emitted this block.
> + assert(!CGM.getAddrOfGlobalBlockIfEmitted(blockInfo.BlockExpression) &&
> + "Refusing to re-emit a global block.");
>
> // Generate the constants for the block literal initializer.
> ConstantInitBuilder builder(CGM);
> @@ -1103,9 +1120,12 @@ static llvm::Constant *buildGlobalBlock(
> /*constant*/ true);
>
> // Return a constant of the appropriately-casted type.
> - llvm::Type *requiredType =
> + llvm::Type *RequiredType =
> CGM.getTypes().ConvertType(blockInfo.getBlockExpr()->getType());
> - return llvm::ConstantExpr::getBitCast(literal, requiredType);
> + llvm::Constant *Result =
> + llvm::ConstantExpr::getBitCast(literal, RequiredType);
> + CGM.setAddrOfGlobalBlock(blockInfo.BlockExpression, Result);
> + return Result;
> }
>
> void CodeGenFunction::setBlockContextParameter(const ImplicitParamDecl
> *D,
>
> Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/
> CGCall.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGCall.cpp Mon Dec 19 19:05:42 2016
> @@ -1683,6 +1683,14 @@ void CodeGenModule::ConstructAttributeLi
>
> HasAnyX86InterruptAttr = TargetDecl->hasAttr<AnyX86InterruptAttr>();
> HasOptnone = TargetDecl->hasAttr<OptimizeNoneAttr>();
> + if (auto *AllocSize = TargetDecl->getAttr<AllocSizeAttr>()) {
> + Optional<unsigned> NumElemsParam;
> + // alloc_size args are base-1, 0 means not present.
> + if (unsigned N = AllocSize->getNumElemsParam())
> + NumElemsParam = N - 1;
> + FuncAttrs.addAllocSizeAttr(AllocSize->getElemSizeParam() - 1,
> + NumElemsParam);
> + }
> }
>
> // OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed.
>
> Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/
> CodeGenFunction.h?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/CodeGen/CodeGenFunction.h (original)
> +++ cfe/trunk/lib/CodeGen/CodeGenFunction.h Mon Dec 19 19:05:42 2016
> @@ -1499,7 +1499,6 @@ public:
> //===-------------------------------------------------------
> -------------===//
>
> llvm::Value *EmitBlockLiteral(const BlockExpr *);
> - llvm::Value *EmitBlockLiteral(const CGBlockInfo &Info);
> static void destroyBlockInfos(CGBlockInfo *info);
>
> llvm::Function *GenerateBlockFunction(GlobalDecl GD,
> @@ -2726,6 +2725,9 @@ public:
> OMPPrivateScope &LoopScope);
>
> private:
> + /// Helpers for blocks
> + llvm::Value *EmitBlockLiteral(const CGBlockInfo &Info);
> +
> /// Helpers for the OpenMP loop directives.
> void EmitOMPLoopBody(const OMPLoopDirective &D, JumpDest LoopExit);
> void EmitOMPSimdInit(const OMPLoopDirective &D, bool IsMonotonic =
> false);
>
> Modified: cfe/trunk/lib/CodeGen/CodeGenModule.h
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/
> CodeGenModule.h?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/CodeGen/CodeGenModule.h (original)
> +++ cfe/trunk/lib/CodeGen/CodeGenModule.h Mon Dec 19 19:05:42 2016
> @@ -455,6 +455,10 @@ private:
> bool isTriviallyRecursive(const FunctionDecl *F);
> bool shouldEmitFunction(GlobalDecl GD);
>
> + /// Map of the global blocks we've emitted, so that we don't have to
> re-emit
> + /// them if the constexpr evaluator gets aggressive.
> + llvm::DenseMap<const BlockExpr *, llvm::Constant *> EmittedGlobalBlocks;
> +
> /// @name Cache for Blocks Runtime Globals
> /// @{
>
> @@ -776,6 +780,16 @@ public:
>
> /// Gets the address of a block which requires no captures.
> llvm::Constant *GetAddrOfGlobalBlock(const BlockExpr *BE, StringRef
> Name);
> +
> + /// Returns the address of a block which requires no caputres, or null
> if
> + /// we've yet to emit the block for BE.
> + llvm::Constant *getAddrOfGlobalBlockIfEmitted(const BlockExpr *BE) {
> + return EmittedGlobalBlocks.lookup(BE);
> + }
> +
> + /// Notes that BE's global block is available via Addr. Asserts that BE
> + /// isn't already emitted.
> + void setAddrOfGlobalBlock(const BlockExpr *BE, llvm::Constant *Addr);
>
> /// Return a pointer to a constant CFString object for the given string.
> ConstantAddress GetAddrOfConstantCFString(const StringLiteral
> *Literal);
>
> Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/
> SemaDeclAttr.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Mon Dec 19 19:05:42 2016
> @@ -246,6 +246,28 @@ static bool checkUInt32Argument(Sema &S,
> return true;
> }
>
> +/// \brief Wrapper around checkUInt32Argument, with an extra check to be
> sure
> +/// that the result will fit into a regular (signed) int. All args have
> the same
> +/// purpose as they do in checkUInt32Argument.
> +static bool checkPositiveIntArgument(Sema &S, const AttributeList &Attr,
> + const Expr *Expr, int &Val,
> + unsigned Idx = UINT_MAX) {
> + uint32_t UVal;
> + if (!checkUInt32Argument(S, Attr, Expr, UVal, Idx))
> + return false;
> +
> + if (UVal > std::numeric_limits<int>::max()) {
> + llvm::APSInt I(32); // for toString
> + I = UVal;
> + S.Diag(Expr->getExprLoc(), diag::err_ice_too_large)
> + << I.toString(10, false) << 32 << /* Unsigned */ 0;
> + return false;
> + }
> +
> + Val = UVal;
> + return true;
> +}
> +
> /// \brief Diagnose mutually exclusive attributes when present on a given
> /// declaration. Returns true if diagnosed.
> template <typename AttrTy>
> @@ -730,6 +752,69 @@ static void handleAssertExclusiveLockAtt
> Attr.getAttributeSpellingListIndex(
> )));
> }
>
> +/// \brief Checks to be sure that the given parameter number is inbounds,
> and is
> +/// an some integral type. Will emit appropriate diagnostics if this
> returns
> +/// false.
> +///
> +/// FuncParamNo is expected to be from the user, so is base-1. AttrArgNo
> is used
> +/// to actually retrieve the argument, so it's base-0.
> +static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD,
> + const AttributeList &Attr,
> + unsigned FuncParamNo, unsigned
> AttrArgNo) {
> + assert(Attr.isArgExpr(AttrArgNo) && "Expected expression argument");
> + uint64_t Idx;
> + if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo,
> + Attr.getArgAsExpr(AttrArgNo),
> Idx))
> + return false;
> +
> + const ParmVarDecl *Param = FD->getParamDecl(Idx);
> + if (!Param->getType()->isIntegerType() &&
> !Param->getType()->isCharType()) {
> + SourceLocation SrcLoc = Attr.getArgAsExpr(AttrArgNo)->getLocStart();
> + S.Diag(SrcLoc, diag::err_attribute_integers_only)
> + << Attr.getName() << Param->getSourceRange();
> + return false;
> + }
> + return true;
> +}
> +
> +static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList
> &Attr) {
> + if (!checkAttributeAtLeastNumArgs(S, Attr, 1) ||
> + !checkAttributeAtMostNumArgs(S, Attr, 2))
> + return;
> +
> + const auto *FD = cast<FunctionDecl>(D);
> + if (!FD->getReturnType()->isPointerType()) {
> + S.Diag(Attr.getLoc(), diag::warn_attribute_return_pointers_only)
> + << Attr.getName();
> + return;
> + }
> +
> + const Expr *SizeExpr = Attr.getArgAsExpr(0);
> + int SizeArgNo;
> + // Paramater indices are 1-indexed, hence Index=1
> + if (!checkPositiveIntArgument(S, Attr, SizeExpr, SizeArgNo,
> /*Index=*/1))
> + return;
> +
> + if (!checkParamIsIntegerType(S, FD, Attr, SizeArgNo, /*AttrArgNo=*/0))
> + return;
> +
> + // Args are 1-indexed, so 0 implies that the arg was not present
> + int NumberArgNo = 0;
> + if (Attr.getNumArgs() == 2) {
> + const Expr *NumberExpr = Attr.getArgAsExpr(1);
> + // Paramater indices are 1-based, hence Index=2
> + if (!checkPositiveIntArgument(S, Attr, NumberExpr, NumberArgNo,
> + /*Index=*/2))
> + return;
> +
> + if (!checkParamIsIntegerType(S, FD, Attr, NumberArgNo,
> /*AttrArgNo=*/1))
> + return;
> + }
> +
> + D->addAttr(::new (S.Context) AllocSizeAttr(
> + Attr.getRange(), S.Context, SizeArgNo, NumberArgNo,
> + Attr.getAttributeSpellingListIndex()));
> +}
>
> static bool checkTryLockFunAttrCommon(Sema &S, Decl *D,
> const AttributeList &Attr,
> @@ -5552,6 +5637,9 @@ static void ProcessDeclAttribute(Sema &S
> case AttributeList::AT_AlignValue:
> handleAlignValueAttr(S, D, Attr);
> break;
> + case AttributeList::AT_AllocSize:
> + handleAllocSizeAttr(S, D, Attr);
> + break;
> case AttributeList::AT_AlwaysInline:
> handleAlwaysInlineAttr(S, D, Attr);
> break;
>
> Added: cfe/trunk/test/CodeGen/alloc-size.c
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> CodeGen/alloc-size.c?rev=290149&view=auto
> ============================================================
> ==================
> --- cfe/trunk/test/CodeGen/alloc-size.c (added)
> +++ cfe/trunk/test/CodeGen/alloc-size.c Mon Dec 19 19:05:42 2016
> @@ -0,0 +1,352 @@
> +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm %s -o - 2>&1 |
> FileCheck %s
> +
> +#define NULL ((void *)0)
> +
> +int gi;
> +
> +typedef unsigned long size_t;
> +
> +// CHECK-DAG-RE: define void @my_malloc({{.*}})
> #[[MALLOC_ATTR_NUMBER:[0-9]+]]
> +// N.B. LLVM's allocsize arguments are base-0, whereas ours are base-1
> (for
> +// compat with GCC)
> +// CHECK-DAG-RE: attributes #[[MALLOC_ATTR_NUMBER]] = {.*allocsize(0).*}
> +void *my_malloc(size_t) __attribute__((alloc_size(1)));
> +
> +// CHECK-DAG-RE: define void @my_calloc({{.*}})
> #[[CALLOC_ATTR_NUMBER:[0-9]+]]
> +// CHECK-DAG-RE: attributes #[[CALLOC_ATTR_NUMBER]] = {.*allocsize(0,
> 1).*}
> +void *my_calloc(size_t, size_t) __attribute__((alloc_size(1, 2)));
> +
> +// CHECK-LABEL: @test1
> +void test1() {
> + void *const vp = my_malloc(100);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 0);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 1);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 2);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 3);
> +
> + void *const arr = my_calloc(100, 5);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 0);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 1);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 2);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 3);
> +
> + // CHECK: store i32 100
> + gi = __builtin_object_size(my_malloc(100), 0);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(my_malloc(100), 1);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(my_malloc(100), 2);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(my_malloc(100), 3);
> +
> + // CHECK: store i32 500
> + gi = __builtin_object_size(my_calloc(100, 5), 0);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(my_calloc(100, 5), 1);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(my_calloc(100, 5), 2);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(my_calloc(100, 5), 3);
> +
> + void *const zeroPtr = my_malloc(0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(zeroPtr, 0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(my_malloc(0), 0);
> +
> + void *const zeroArr1 = my_calloc(0, 1);
> + void *const zeroArr2 = my_calloc(1, 0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(zeroArr1, 0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(zeroArr2, 0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(my_calloc(1, 0), 0);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(my_calloc(0, 1), 0);
> +}
> +
> +// CHECK-LABEL: @test2
> +void test2() {
> + void *const vp = my_malloc(gi);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(vp, 0);
> +
> + void *const arr1 = my_calloc(gi, 1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr1, 0);
> +
> + void *const arr2 = my_calloc(1, gi);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr2, 0);
> +}
> +
> +// CHECK-LABEL: @test3
> +void test3() {
> + char *const buf = (char *)my_calloc(100, 5);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(buf, 0);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(buf, 1);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(buf, 2);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(buf, 3);
> +}
> +
> +struct Data {
> + int a;
> + int t[10];
> + char pad[3];
> + char end[1];
> +};
> +
> +// CHECK-LABEL: @test5
> +void test5() {
> + struct Data *const data = my_malloc(sizeof(*data));
> + // CHECK: store i32 48
> + gi = __builtin_object_size(data, 0);
> + // CHECK: store i32 48
> + gi = __builtin_object_size(data, 1);
> + // CHECK: store i32 48
> + gi = __builtin_object_size(data, 2);
> + // CHECK: store i32 48
> + gi = __builtin_object_size(data, 3);
> +
> + // CHECK: store i32 40
> + gi = __builtin_object_size(&data->t[1], 0);
> + // CHECK: store i32 36
> + gi = __builtin_object_size(&data->t[1], 1);
> + // CHECK: store i32 40
> + gi = __builtin_object_size(&data->t[1], 2);
> + // CHECK: store i32 36
> + gi = __builtin_object_size(&data->t[1], 3);
> +
> + struct Data *const arr = my_calloc(sizeof(*data), 2);
> + // CHECK: store i32 96
> + gi = __builtin_object_size(arr, 0);
> + // CHECK: store i32 96
> + gi = __builtin_object_size(arr, 1);
> + // CHECK: store i32 96
> + gi = __builtin_object_size(arr, 2);
> + // CHECK: store i32 96
> + gi = __builtin_object_size(arr, 3);
> +
> + // CHECK: store i32 88
> + gi = __builtin_object_size(&arr->t[1], 0);
> + // CHECK: store i32 36
> + gi = __builtin_object_size(&arr->t[1], 1);
> + // CHECK: store i32 88
> + gi = __builtin_object_size(&arr->t[1], 2);
> + // CHECK: store i32 36
> + gi = __builtin_object_size(&arr->t[1], 3);
> +}
> +
> +// CHECK-LABEL: @test6
> +void test6() {
> + // Things that would normally trigger conservative estimates don't need
> to do
> + // so when we know the source of the allocation.
> + struct Data *const data = my_malloc(sizeof(*data) + 10);
> + // CHECK: store i32 11
> + gi = __builtin_object_size(data->end, 0);
> + // CHECK: store i32 11
> + gi = __builtin_object_size(data->end, 1);
> + // CHECK: store i32 11
> + gi = __builtin_object_size(data->end, 2);
> + // CHECK: store i32 11
> + gi = __builtin_object_size(data->end, 3);
> +
> + struct Data *const arr = my_calloc(sizeof(*arr) + 5, 3);
> + // AFAICT, GCC treats malloc and calloc identically. So, we should do
> the
> + // same.
> + //
> + // Additionally, GCC ignores the initial array index when determining
> whether
> + // we're writing off the end of an alloc_size base. e.g.
> + // arr[0].end
> + // arr[1].end
> + // arr[2].end
> + // ...Are all considered "writing off the end", because there's no way
> to tell
> + // with high accuracy if the user meant "allocate a single N-byte
> `Data`",
> + // or "allocate M smaller `Data`s with extra padding".
> +
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr->end, 0);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr->end, 1);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr->end, 2);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr->end, 3);
> +
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr[0].end, 0);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr[0].end, 1);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr[0].end, 2);
> + // CHECK: store i32 112
> + gi = __builtin_object_size(arr[0].end, 3);
> +
> + // CHECK: store i32 64
> + gi = __builtin_object_size(arr[1].end, 0);
> + // CHECK: store i32 64
> + gi = __builtin_object_size(arr[1].end, 1);
> + // CHECK: store i32 64
> + gi = __builtin_object_size(arr[1].end, 2);
> + // CHECK: store i32 64
> + gi = __builtin_object_size(arr[1].end, 3);
> +
> + // CHECK: store i32 16
> + gi = __builtin_object_size(arr[2].end, 0);
> + // CHECK: store i32 16
> + gi = __builtin_object_size(arr[2].end, 1);
> + // CHECK: store i32 16
> + gi = __builtin_object_size(arr[2].end, 2);
> + // CHECK: store i32 16
> + gi = __builtin_object_size(arr[2].end, 3);
> +}
> +
> +// CHECK-LABEL: @test7
> +void test7() {
> + struct Data *const data = my_malloc(sizeof(*data) + 5);
> + // CHECK: store i32 9
> + gi = __builtin_object_size(data->pad, 0);
> + // CHECK: store i32 3
> + gi = __builtin_object_size(data->pad, 1);
> + // CHECK: store i32 9
> + gi = __builtin_object_size(data->pad, 2);
> + // CHECK: store i32 3
> + gi = __builtin_object_size(data->pad, 3);
> +}
> +
> +// CHECK-LABEL: @test8
> +void test8() {
> + // Non-const pointers aren't currently supported.
> + void *buf = my_calloc(100, 5);
> + // CHECK: @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
> + gi = __builtin_object_size(buf, 0);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(buf, 1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(buf, 2);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(buf, 3);
> +}
> +
> +// CHECK-LABEL: @test9
> +void test9() {
> + // Check to be sure that we unwrap things correctly.
> + short *const buf0 = (my_malloc(100));
> + short *const buf1 = (short*)(my_malloc(100));
> + short *const buf2 = ((short*)(my_malloc(100)));
> +
> + // CHECK: store i32 100
> + gi = __builtin_object_size(buf0, 0);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(buf1, 0);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(buf2, 0);
> +}
> +
> +// CHECK-LABEL: @test10
> +void test10() {
> + // Yay overflow
> + short *const arr = my_calloc((size_t)-1 / 2 + 1, 2);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr, 0);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr, 1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr, 2);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(arr, 3);
> +
> + // As an implementation detail, CharUnits can't handle numbers greater
> than or
> + // equal to 2**63. Realistically, this shouldn't be a problem, but we
> should
> + // be sure we don't emit crazy results for this case.
> + short *const buf = my_malloc((size_t)-1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(buf, 0);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(buf, 1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(buf, 2);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(buf, 3);
> +
> + short *const arr_big = my_calloc((size_t)-1 / 2 - 1, 2);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr_big, 0);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr_big, 1);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr_big, 2);
> + // CHECK: store i32 0
> + gi = __builtin_object_size(arr_big, 3);
> +}
> +
> +void *my_tiny_malloc(char) __attribute__((alloc_size(1)));
> +void *my_tiny_calloc(char, char) __attribute__((alloc_size(1, 2)));
> +
> +// CHECK-LABEL: @test11
> +void test11() {
> + void *const vp = my_tiny_malloc(100);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 0);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 1);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 2);
> + // CHECK: store i32 100
> + gi = __builtin_object_size(vp, 3);
> +
> + // N.B. This causes char overflow, but not size_t overflow, so it
> should be
> + // supported.
> + void *const arr = my_tiny_calloc(100, 5);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 0);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 1);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 2);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(arr, 3);
> +}
> +
> +void *my_signed_malloc(long) __attribute__((alloc_size(1)));
> +void *my_signed_calloc(long, long) __attribute__((alloc_size(1, 2)));
> +
> +// CHECK-LABEL: @test12
> +void test12() {
> + // CHECK: store i32 100
> + gi = __builtin_object_size(my_signed_malloc(100), 0);
> + // CHECK: store i32 500
> + gi = __builtin_object_size(my_signed_calloc(100, 5), 0);
> +
> + void *const vp = my_signed_malloc(-2);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(vp, 0);
> + // N.B. These get lowered to -1 because the function calls may have
> + // side-effects, and we can't determine the objectsize.
> + // CHECK: store i32 -1
> + gi = __builtin_object_size(my_signed_malloc(-2), 0);
> +
> + void *const arr1 = my_signed_calloc(-2, 1);
> + void *const arr2 = my_signed_calloc(1, -2);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr1, 0);
> + // CHECK: @llvm.objectsize
> + gi = __builtin_object_size(arr2, 0);
> + // CHECK: store i32 -1
> + gi = __builtin_object_size(my_signed_calloc(1, -2), 0);
> + // CHECK: store i32 -1
> + gi = __builtin_object_size(my_signed_calloc(-2, 1), 0);
> +}
>
> Added: cfe/trunk/test/CodeGenCXX/alloc-size.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> CodeGenCXX/alloc-size.cpp?rev=290149&view=auto
> ============================================================
> ==================
> --- cfe/trunk/test/CodeGenCXX/alloc-size.cpp (added)
> +++ cfe/trunk/test/CodeGenCXX/alloc-size.cpp Mon Dec 19 19:05:42 2016
> @@ -0,0 +1,72 @@
> +// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -O0 %s -o -
> 2>&1 -std=c++11 | FileCheck %s
> +
> +namespace templates {
> +void *my_malloc(int N) __attribute__((alloc_size(1)));
> +void *my_calloc(int N, int M) __attribute__((alloc_size(1, 2)));
> +
> +struct MyType {
> + int arr[4];
> +};
> +
> +template <typename T> int callMalloc();
> +
> +template <typename T, int N> int callCalloc();
> +
> +// CHECK-LABEL: define i32 @_ZN9templates6testItEv()
> +int testIt() {
> + // CHECK: call i32 @_ZN9templates10callMallocINS_6MyTypeEEEiv
> + // CHECK: call i32 @_ZN9templates10callCallocINS_6MyTypeELi4EEEiv
> + return callMalloc<MyType>() + callCalloc<MyType, 4>();
> +}
> +
> +// CHECK-LABEL: define linkonce_odr i32
> +// @_ZN9templates10callMallocINS_6MyTypeEEEiv
> +template <typename T> int callMalloc() {
> + static_assert(sizeof(T) == 16, "");
> + // CHECK: ret i32 16
> + return __builtin_object_size(my_malloc(sizeof(T)), 0);
> +}
> +
> +// CHECK-LABEL: define linkonce_odr i32
> +// @_ZN9templates10callCallocINS_6MyTypeELi4EEEiv
> +template <typename T, int N> int callCalloc() {
> + static_assert(sizeof(T) * N == 64, "");
> + // CHECK: ret i32 64
> + return __builtin_object_size(my_malloc(sizeof(T) * N), 0);
> +}
> +}
> +
> +namespace templated_alloc_size {
> +using size_t = unsigned long;
> +
> +// We don't need bodies for any of these, because they're only used in
> +// __builtin_object_size, and that shouldn't need anything but a function
> +// decl with alloc_size on it.
> +template <typename T>
> +T *my_malloc(size_t N = sizeof(T)) __attribute__((alloc_size(1)));
> +
> +template <typename T>
> +T *my_calloc(size_t M, size_t N = sizeof(T)) __attribute__((alloc_size(2,
> 1)));
> +
> +template <size_t N>
> +void *dependent_malloc(size_t NT = N) __attribute__((alloc_size(1)));
> +
> +template <size_t N, size_t M>
> +void *dependent_calloc(size_t NT = N, size_t MT = M)
> + __attribute__((alloc_size(1, 2)));
> +
> +template <typename T, size_t M>
> +void *dependent_calloc2(size_t NT = sizeof(T), size_t MT = M)
> + __attribute__((alloc_size(1, 2)));
> +
> +// CHECK-LABEL: define i32 @_ZN20templated_alloc_size6testItEv
> +int testIt() {
> + // 122 = 4 + 5*4 + 6 + 7*8 + 4*9
> + // CHECK: ret i32 122
> + return __builtin_object_size(my_malloc<int>(), 0) +
> + __builtin_object_size(my_calloc<int>(5), 0) +
> + __builtin_object_size(dependent_malloc<6>(), 0) +
> + __builtin_object_size(dependent_calloc<7, 8>(), 0) +
> + __builtin_object_size(dependent_calloc2<int, 9>(), 0);
> +}
> +}
>
> Modified: cfe/trunk/test/CodeGenCXX/block-in-ctor-dtor.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> CodeGenCXX/block-in-ctor-dtor.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/test/CodeGenCXX/block-in-ctor-dtor.cpp (original)
> +++ cfe/trunk/test/CodeGenCXX/block-in-ctor-dtor.cpp Mon Dec 19 19:05:42
> 2016
> @@ -42,7 +42,5 @@ X::~X() {
> // CHECK-LABEL: define internal void @___ZN4ZoneD2Ev_block_invoke_
> // CHECK-LABEL: define internal void @___ZN1XC2Ev_block_invoke
> // CHECK-LABEL: define internal void @___ZN1XC2Ev_block_invoke_
> -// CHECK-LABEL: define internal void @___ZN1XC1Ev_block_invoke
> -// CHECK-LABEL: define internal void @___ZN1XC1Ev_block_invoke_
> // CHECK-LABEL: define internal void @___ZN1XD2Ev_block_invoke
> // CHECK-LABEL: define internal void @___ZN1XD2Ev_block_invoke_
>
> Modified: cfe/trunk/test/CodeGenCXX/global-init.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> CodeGenCXX/global-init.cpp?rev=290149&r1=290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/test/CodeGenCXX/global-init.cpp (original)
> +++ cfe/trunk/test/CodeGenCXX/global-init.cpp Mon Dec 19 19:05:42 2016
> @@ -18,9 +18,6 @@ struct D { ~D(); };
> // CHECK: @__dso_handle = external global i8
> // CHECK: @c = global %struct.C zeroinitializer, align 8
>
> -// It's okay if we ever implement the IR-generation optimization to
> remove this.
> -// CHECK: @_ZN5test3L3varE = internal constant i8* getelementptr inbounds
> ([7 x i8], [7 x i8]*
> -
> // PR6205: The casts should not require global initializers
> // CHECK: @_ZN6PR59741cE = external global %"struct.PR5974::C"
> // CHECK: @_ZN6PR59741aE = global %"struct.PR5974::A"* getelementptr
> inbounds (%"struct.PR5974::C", %"struct.PR5974::C"* @_ZN6PR59741cE, i32 0,
> i32 0)
>
> Modified: cfe/trunk/test/CodeGenOpenCL/cl20-device-side-enqueue.cl
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> CodeGenOpenCL/cl20-device-side-enqueue.cl?rev=290149&r1=
> 290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/test/CodeGenOpenCL/cl20-device-side-enqueue.cl (original)
> +++ cfe/trunk/test/CodeGenOpenCL/cl20-device-side-enqueue.cl Mon Dec 19
> 19:05:42 2016
> @@ -3,6 +3,8 @@
>
> typedef void (^bl_t)(local void *);
>
> +// N.B. The check here only exists to set BL_GLOBAL
> +// COMMON: @block_G = {{.*}}bitcast ([[BL_GLOBAL:[^@]+ at __block_
> literal_global(\.[0-9]+)?]]
> const bl_t block_G = (bl_t) ^ (local void *a) {};
>
> kernel void device_side_enqueue(global int *a, global int *b, int i) {
> @@ -122,28 +124,24 @@ kernel void device_side_enqueue(global i
> },
> 4294967296L);
>
> -
> + // The full type of these expressions are long (and repeated
> elsewhere), so we
> + // capture it as part of the regex for convenience and clarity.
> + // COMMON: store void ()* bitcast ([[BL_A:[^@]+ at __block_literal_global.[0-9]+]]
> to void ()*), void ()** %block_A
> void (^const block_A)(void) = ^{
> return;
> };
> +
> + // COMMON: store void (i8 addrspace(2)*)* bitcast
> ([[BL_B:[^@]+ at __block_literal_global.[0-9]+]] to void (i8
> addrspace(2)*)*), void (i8 addrspace(2)*)** %block_B
> void (^const block_B)(local void *) = ^(local void *a) {
> return;
> };
>
> - // COMMON: [[BL:%[0-9]+]] = load void ()*, void ()** %block_A
> - // COMMON: [[BL_I8:%[0-9]+]] = bitcast void ()* [[BL]] to i8*
> - // COMMON: call i32 @__get_kernel_work_group_size_impl(i8* [[BL_I8]])
> + // COMMON: call i32 @__get_kernel_work_group_size_impl(i8* bitcast
> ([[BL_A]] to i8*))
> unsigned size = get_kernel_work_group_size(block_A);
> - // COMMON: [[BL:%[0-9]+]] = load void (i8 addrspace(2)*)*, void (i8
> addrspace(2)*)** %block_B
> - // COMMON: [[BL_I8:%[0-9]+]] = bitcast void (i8 addrspace(2)*)* [[BL]]
> to i8*
> - // COMMON: call i32 @__get_kernel_work_group_size_impl(i8* [[BL_I8]])
> + // COMMON: call i32 @__get_kernel_work_group_size_impl(i8* bitcast
> ([[BL_B]] to i8*))
> size = get_kernel_work_group_size(block_B);
> - // COMMON: [[BL:%[0-9]+]] = load void ()*, void ()** %block_A
> - // COMMON: [[BL_I8:%[0-9]+]] = bitcast void ()* [[BL]] to i8*
> - // COMMON: call i32 @__get_kernel_preferred_work_group_multiple_impl(i8*
> [[BL_I8]])
> + // COMMON: call i32 @__get_kernel_preferred_work_group_multiple_impl(i8*
> bitcast ([[BL_A]] to i8*))
> size = get_kernel_preferred_work_group_size_multiple(block_A);
> - // COMMON: [[BL:%[0-9]+]] = load void (i8 addrspace(2)*)*, void (i8
> addrspace(2)*)* addrspace(1)* @block_G
> - // COMMON: [[BL_I8:%[0-9]+]] = bitcast void (i8 addrspace(2)*)* [[BL]]
> to i8*
> - // COMMON: call i32 @__get_kernel_preferred_work_group_multiple_impl(i8*
> [[BL_I8]])
> + // COMMON: call i32 @__get_kernel_preferred_work_group_multiple_impl(i8*
> bitcast ([[BL_GLOBAL]] to i8*))
> size = get_kernel_preferred_work_group_size_multiple(block_G);
> }
>
> Added: cfe/trunk/test/Sema/alloc-size.c
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Sema/
> alloc-size.c?rev=290149&view=auto
> ============================================================
> ==================
> --- cfe/trunk/test/Sema/alloc-size.c (added)
> +++ cfe/trunk/test/Sema/alloc-size.c Mon Dec 19 19:05:42 2016
> @@ -0,0 +1,23 @@
> +// RUN: %clang_cc1 %s -verify
> +
> +void *fail1(int a) __attribute__((alloc_size));
> //expected-error{{'alloc_size' attribute takes at least 1 argument}}
> +void *fail2(int a) __attribute__((alloc_size()));
> //expected-error{{'alloc_size' attribute takes at least 1 argument}}
> +
> +void *fail3(int a) __attribute__((alloc_size(0)));
> //expected-error{{'alloc_size' attribute parameter 0 is out of bounds}}
> +void *fail4(int a) __attribute__((alloc_size(2)));
> //expected-error{{'alloc_size' attribute parameter 2 is out of bounds}}
> +
> +void *fail5(int a, int b) __attribute__((alloc_size(0, 1)));
> //expected-error{{'alloc_size' attribute parameter 0 is out of bounds}}
> +void *fail6(int a, int b) __attribute__((alloc_size(3, 1)));
> //expected-error{{'alloc_size' attribute parameter 3 is out of bounds}}
> +
> +void *fail7(int a, int b) __attribute__((alloc_size(1, 0)));
> //expected-error{{'alloc_size' attribute parameter 0 is out of bounds}}
> +void *fail8(int a, int b) __attribute__((alloc_size(1, 3)));
> //expected-error{{'alloc_size' attribute parameter 3 is out of bounds}}
> +
> +int fail9(int a) __attribute__((alloc_size(1)));
> //expected-warning{{'alloc_size' attribute only applies to return values
> that are pointers}}
> +
> +int fail10 __attribute__((alloc_size(1))); //expected-warning{{'alloc_size'
> attribute only applies to non-K&R-style functions}}
> +
> +void *fail11(void *a) __attribute__((alloc_size(1)));
> //expected-error{{'alloc_size' attribute argument may only refer to a
> function parameter of integer type}}
> +
> +void *fail12(int a) __attribute__((alloc_size("abc")));
> //expected-error{{'alloc_size' attribute requires parameter 1 to be an
> integer constant}}
> +void *fail12(int a) __attribute__((alloc_size(1, "abc")));
> //expected-error{{'alloc_size' attribute requires parameter 2 to be an
> integer constant}}
> +void *fail13(int a) __attribute__((alloc_size(1U<<31)));
> //expected-error{{integer constant expression evaluates to value 2147483648
> that cannot be represented in a 32-bit signed integer type}}
>
> Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/
> SemaCXX/constant-expression-cxx11.cpp?rev=290149&r1=
> 290148&r2=290149&view=diff
> ============================================================
> ==================
> --- cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp (original)
> +++ cfe/trunk/test/SemaCXX/constant-expression-cxx11.cpp Mon Dec 19
> 19:05:42 2016
> @@ -1183,7 +1183,7 @@ constexpr int m1b = const_cast<const int
> constexpr int m2b = const_cast<const int&>(n2); // expected-error
> {{constant expression}} expected-note {{read of volatile object 'n2'}}
>
> struct T { int n; };
> -const T t = { 42 }; // expected-note {{declared here}}
> +const T t = { 42 };
>
> constexpr int f(volatile int &&r) {
> return r; // expected-note {{read of volatile-qualified type 'volatile
> int'}}
> @@ -1195,7 +1195,7 @@ struct S {
> int j : f(0); // expected-error {{constant expression}} expected-note
> {{in call to 'f(0)'}}
> int k : g(0); // expected-error {{constant expression}} expected-note
> {{temporary created here}} expected-note {{in call to 'g(0)'}}
> int l : n3; // expected-error {{constant expression}} expected-note
> {{read of non-const variable}}
> - int m : t.n; // expected-error {{constant expression}} expected-note
> {{read of non-constexpr variable}}
> + int m : t.n; // expected-warning{{width of bit-field 'm' (42 bits)}}
> };
>
> }
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20161222/1e8c9ae8/attachment-0001.html>
More information about the cfe-commits
mailing list