forked from OSchip/llvm-project
Clang changes for alloc_align attribute
GCC has the alloc_align attribute, which is similar to assume_aligned, except the attribute's parameter is the index of the integer parameter that needs aligning to. Differential Revision: https://reviews.llvm.org/D29599 llvm-svn: 299117
This commit is contained in:
parent
265a7c71d0
commit
623efd8a75
|
@ -1225,6 +1225,14 @@ def AssumeAligned : InheritableAttr {
|
|||
let Documentation = [AssumeAlignedDocs];
|
||||
}
|
||||
|
||||
def AllocAlign : InheritableAttr {
|
||||
let Spellings = [GCC<"alloc_align">];
|
||||
let Subjects = SubjectList<[HasFunctionProto], WarnDiag,
|
||||
"ExpectedFunctionWithProtoType">;
|
||||
let Args = [IntArgument<"ParamIndex">];
|
||||
let Documentation = [AllocAlignDocs];
|
||||
}
|
||||
|
||||
def NoReturn : InheritableAttr {
|
||||
let Spellings = [GCC<"noreturn">, Declspec<"noreturn">];
|
||||
// FIXME: Does GCC allow this on the function instead?
|
||||
|
|
|
@ -244,6 +244,36 @@ An example of how to use ``alloc_size``
|
|||
}];
|
||||
}
|
||||
|
||||
def AllocAlignDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
Use ``__attribute__((alloc_align(<alignment>))`` on a function
|
||||
declaration to specify that the return value of the function (which must be a
|
||||
pointer type) is at least as aligned as the value of the indicated parameter. The
|
||||
parameter is given by its index in the list of formal parameters; the first
|
||||
parameter has index 1 unless the function is a C++ non-static member function,
|
||||
in which case the first parameter has index 2 to account for the implicit ``this``
|
||||
parameter.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
// The returned pointer has the alignment specified by the first parameter.
|
||||
void *a(size_t align) __attribute__((alloc_align(1)));
|
||||
|
||||
// The returned pointer has the alignment specified by the second parameter.
|
||||
void *b(void *v, size_t align) __attribute__((alloc_align(2)));
|
||||
|
||||
// The returned pointer has the alignment specified by the second visible
|
||||
// parameter, however it must be adjusted for the implicit 'this' parameter.
|
||||
void *Foo::b(void *v, size_t align) __attribute__((alloc_align(3)));
|
||||
|
||||
Note that this attribute merely informs the compiler that a function always
|
||||
returns a sufficiently aligned pointer. It does not cause the compiler to
|
||||
emit code to enforce that alignment. The behavior is undefined if the returned
|
||||
poitner is not sufficiently aligned.
|
||||
}];
|
||||
}
|
||||
|
||||
def EnableIfDocs : Documentation {
|
||||
let Category = DocCatFunction;
|
||||
let Content = [{
|
||||
|
|
|
@ -8176,6 +8176,11 @@ public:
|
|||
void AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, Expr *OE,
|
||||
unsigned SpellingListIndex);
|
||||
|
||||
/// AddAllocAlignAttr - Adds an alloc_align attribute to a particular
|
||||
/// declaration.
|
||||
void AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
|
||||
unsigned SpellingListIndex);
|
||||
|
||||
/// AddAlignValueAttr - Adds an align_value attribute to a particular
|
||||
/// declaration.
|
||||
void AddAlignValueAttr(SourceRange AttrRange, Decl *D, Expr *E,
|
||||
|
|
|
@ -4348,6 +4348,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
|
|||
llvm::ConstantInt *AlignmentCI = cast<llvm::ConstantInt>(Alignment);
|
||||
EmitAlignmentAssumption(Ret.getScalarVal(), AlignmentCI->getZExtValue(),
|
||||
OffsetValue);
|
||||
} else if (const auto *AA = TargetDecl->getAttr<AllocAlignAttr>()) {
|
||||
llvm::Value *ParamVal =
|
||||
CallArgs[AA->getParamIndex() - 1].RV.getScalarVal();
|
||||
EmitAlignmentAssumption(Ret.getScalarVal(), ParamVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2465,6 +2465,12 @@ public:
|
|||
PeepholeProtection protectFromPeepholes(RValue rvalue);
|
||||
void unprotectFromPeepholes(PeepholeProtection protection);
|
||||
|
||||
void EmitAlignmentAssumption(llvm::Value *PtrValue, llvm::Value *Alignment,
|
||||
llvm::Value *OffsetValue = nullptr) {
|
||||
Builder.CreateAlignmentAssumption(CGM.getDataLayout(), PtrValue, Alignment,
|
||||
OffsetValue);
|
||||
}
|
||||
|
||||
//===--------------------------------------------------------------------===//
|
||||
// Statement Emission
|
||||
//===--------------------------------------------------------------------===//
|
||||
|
|
|
@ -218,21 +218,45 @@ static bool checkAttributeAtMostNumArgs(Sema &S, const AttributeList &Attr,
|
|||
std::greater<unsigned>());
|
||||
}
|
||||
|
||||
/// \brief A helper function to provide Attribute Location for the Attr types
|
||||
/// AND the AttributeList.
|
||||
template <typename AttrInfo>
|
||||
static typename std::enable_if<std::is_base_of<clang::Attr, AttrInfo>::value,
|
||||
SourceLocation>::type
|
||||
getAttrLoc(const AttrInfo &Attr) {
|
||||
return Attr.getLocation();
|
||||
}
|
||||
static SourceLocation getAttrLoc(const clang::AttributeList &Attr) {
|
||||
return Attr.getLoc();
|
||||
}
|
||||
|
||||
/// \brief A helper function to provide Attribute Name for the Attr types
|
||||
/// AND the AttributeList.
|
||||
template <typename AttrInfo>
|
||||
static typename std::enable_if<std::is_base_of<clang::Attr, AttrInfo>::value,
|
||||
const AttrInfo *>::type
|
||||
getAttrName(const AttrInfo &Attr) {
|
||||
return &Attr;
|
||||
}
|
||||
const IdentifierInfo *getAttrName(const clang::AttributeList &Attr) {
|
||||
return Attr.getName();
|
||||
}
|
||||
|
||||
/// \brief If Expr is a valid integer constant, get the value of the integer
|
||||
/// expression and return success or failure. May output an error.
|
||||
static bool checkUInt32Argument(Sema &S, const AttributeList &Attr,
|
||||
const Expr *Expr, uint32_t &Val,
|
||||
unsigned Idx = UINT_MAX) {
|
||||
template<typename AttrInfo>
|
||||
static bool checkUInt32Argument(Sema &S, const AttrInfo& Attr, const Expr *Expr,
|
||||
uint32_t &Val, unsigned Idx = UINT_MAX) {
|
||||
llvm::APSInt I(32);
|
||||
if (Expr->isTypeDependent() || Expr->isValueDependent() ||
|
||||
!Expr->isIntegerConstantExpr(I, S.Context)) {
|
||||
if (Idx != UINT_MAX)
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type)
|
||||
<< Attr.getName() << Idx << AANT_ArgumentIntegerConstant
|
||||
S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type)
|
||||
<< getAttrName(Attr) << Idx << AANT_ArgumentIntegerConstant
|
||||
<< Expr->getSourceRange();
|
||||
else
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_argument_type)
|
||||
<< Attr.getName() << AANT_ArgumentIntegerConstant
|
||||
S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_type)
|
||||
<< getAttrName(Attr) << AANT_ArgumentIntegerConstant
|
||||
<< Expr->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
|
@ -250,9 +274,9 @@ static bool checkUInt32Argument(Sema &S, const AttributeList &Attr,
|
|||
/// \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) {
|
||||
template<typename AttrInfo>
|
||||
static bool checkPositiveIntArgument(Sema &S, const AttrInfo& Attr, const Expr *Expr,
|
||||
int &Val, unsigned Idx = UINT_MAX) {
|
||||
uint32_t UVal;
|
||||
if (!checkUInt32Argument(S, Attr, Expr, UVal, Idx))
|
||||
return false;
|
||||
|
@ -287,11 +311,10 @@ static bool checkAttrMutualExclusion(Sema &S, Decl *D, SourceRange Range,
|
|||
/// instance method D. May output an error.
|
||||
///
|
||||
/// \returns true if IdxExpr is a valid index.
|
||||
static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D,
|
||||
const AttributeList &Attr,
|
||||
unsigned AttrArgNum,
|
||||
const Expr *IdxExpr,
|
||||
uint64_t &Idx) {
|
||||
template <typename AttrInfo>
|
||||
static bool checkFunctionOrMethodParameterIndex(
|
||||
Sema &S, const Decl *D, const AttrInfo& Attr,
|
||||
unsigned AttrArgNum, const Expr *IdxExpr, uint64_t &Idx) {
|
||||
assert(isFunctionOrMethodOrBlock(D));
|
||||
|
||||
// In C++ the implicit 'this' function parameter also counts.
|
||||
|
@ -305,24 +328,24 @@ static bool checkFunctionOrMethodParameterIndex(Sema &S, const Decl *D,
|
|||
llvm::APSInt IdxInt;
|
||||
if (IdxExpr->isTypeDependent() || IdxExpr->isValueDependent() ||
|
||||
!IdxExpr->isIntegerConstantExpr(IdxInt, S.Context)) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_type)
|
||||
<< Attr.getName() << AttrArgNum << AANT_ArgumentIntegerConstant
|
||||
S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_n_type)
|
||||
<< getAttrName(Attr) << AttrArgNum << AANT_ArgumentIntegerConstant
|
||||
<< IdxExpr->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
|
||||
Idx = IdxInt.getLimitedValue();
|
||||
if (Idx < 1 || (!IV && Idx > NumParams)) {
|
||||
S.Diag(Attr.getLoc(), diag::err_attribute_argument_out_of_bounds)
|
||||
<< Attr.getName() << AttrArgNum << IdxExpr->getSourceRange();
|
||||
S.Diag(getAttrLoc(Attr), diag::err_attribute_argument_out_of_bounds)
|
||||
<< getAttrName(Attr) << AttrArgNum << IdxExpr->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
Idx--; // Convert to zero-based.
|
||||
if (HasImplicitThisParam) {
|
||||
if (Idx == 0) {
|
||||
S.Diag(Attr.getLoc(),
|
||||
S.Diag(getAttrLoc(Attr),
|
||||
diag::err_attribute_invalid_implicit_this_argument)
|
||||
<< Attr.getName() << IdxExpr->getSourceRange();
|
||||
<< getAttrName(Attr) << IdxExpr->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
--Idx;
|
||||
|
@ -753,29 +776,46 @@ static void handleAssertExclusiveLockAttr(Sema &S, Decl *D,
|
|||
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
|
||||
/// \brief Checks to be sure that the given parameter number is in bounds, and is
|
||||
/// an 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.
|
||||
template <typename AttrInfo>
|
||||
static bool checkParamIsIntegerType(Sema &S, const FunctionDecl *FD,
|
||||
const AttrInfo &Attr, Expr *AttrArg,
|
||||
unsigned FuncParamNo, unsigned AttrArgNo,
|
||||
bool AllowDependentType = false) {
|
||||
uint64_t Idx;
|
||||
if (!checkFunctionOrMethodParameterIndex(S, FD, Attr, FuncParamNo, AttrArg,
|
||||
Idx))
|
||||
return false;
|
||||
|
||||
const ParmVarDecl *Param = FD->getParamDecl(Idx);
|
||||
if (AllowDependentType && Param->getType()->isDependentType())
|
||||
return true;
|
||||
if (!Param->getType()->isIntegerType() && !Param->getType()->isCharType()) {
|
||||
SourceLocation SrcLoc = AttrArg->getLocStart();
|
||||
S.Diag(SrcLoc, diag::err_attribute_integers_only)
|
||||
<< getAttrName(Attr) << Param->getSourceRange();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \brief Checks to be sure that the given parameter number is in bounds, and is
|
||||
/// an 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) {
|
||||
unsigned FuncParamNo, unsigned AttrArgNo,
|
||||
bool AllowDependentType = false) {
|
||||
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;
|
||||
return checkParamIsIntegerType(S, FD, Attr, Attr.getArgAsExpr(AttrArgNo),
|
||||
FuncParamNo, AttrArgNo, AllowDependentType);
|
||||
}
|
||||
|
||||
static void handleAllocSizeAttr(Sema &S, Decl *D, const AttributeList &Attr) {
|
||||
|
@ -1484,6 +1524,12 @@ static void handleAssumeAlignedAttr(Sema &S, Decl *D,
|
|||
Attr.getAttributeSpellingListIndex());
|
||||
}
|
||||
|
||||
static void handleAllocAlignAttr(Sema &S, Decl *D,
|
||||
const AttributeList &Attr) {
|
||||
S.AddAllocAlignAttr(Attr.getRange(), D, Attr.getArgAsExpr(0),
|
||||
Attr.getAttributeSpellingListIndex());
|
||||
}
|
||||
|
||||
void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E,
|
||||
Expr *OE, unsigned SpellingListIndex) {
|
||||
QualType ResultType = getFunctionOrMethodResultType(D);
|
||||
|
@ -1535,6 +1581,44 @@ void Sema::AddAssumeAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E,
|
|||
AssumeAlignedAttr(AttrRange, Context, E, OE, SpellingListIndex));
|
||||
}
|
||||
|
||||
void Sema::AddAllocAlignAttr(SourceRange AttrRange, Decl *D, Expr *ParamExpr,
|
||||
unsigned SpellingListIndex) {
|
||||
QualType ResultType = getFunctionOrMethodResultType(D);
|
||||
|
||||
AllocAlignAttr TmpAttr(AttrRange, Context, 0, SpellingListIndex);
|
||||
SourceLocation AttrLoc = AttrRange.getBegin();
|
||||
|
||||
if (!ResultType->isDependentType() &&
|
||||
!isValidPointerAttrType(ResultType, /* RefOkay */ true)) {
|
||||
Diag(AttrLoc, diag::warn_attribute_return_pointers_refs_only)
|
||||
<< &TmpAttr << AttrRange << getFunctionOrMethodResultSourceRange(D);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t IndexVal;
|
||||
const auto *FuncDecl = cast<FunctionDecl>(D);
|
||||
if (!checkFunctionOrMethodParameterIndex(*this, FuncDecl, TmpAttr,
|
||||
/*AttrArgNo=*/1, ParamExpr,
|
||||
IndexVal))
|
||||
return;
|
||||
|
||||
QualType Ty = getFunctionOrMethodParamType(D, IndexVal);
|
||||
if (!Ty->isDependentType() && !Ty->isIntegralType(Context)) {
|
||||
Diag(ParamExpr->getLocStart(), diag::err_attribute_integers_only)
|
||||
<< &TmpAttr << FuncDecl->getParamDecl(IndexVal)->getSourceRange();
|
||||
return;
|
||||
}
|
||||
|
||||
// We cannot use the Idx returned from checkFunctionOrMethodParameterIndex
|
||||
// because that has corrected for the implicit this parameter, and is zero-
|
||||
// based. The attribute expects what the user wrote explicitly.
|
||||
llvm::APSInt Val;
|
||||
ParamExpr->EvaluateAsInt(Val, Context);
|
||||
|
||||
D->addAttr(::new (Context) AllocAlignAttr(
|
||||
AttrRange, Context, Val.getZExtValue(), SpellingListIndex));
|
||||
}
|
||||
|
||||
/// Normalize the attribute, __foo__ becomes foo.
|
||||
/// Returns true if normalization was applied.
|
||||
static bool normalizeName(StringRef &AttrName) {
|
||||
|
@ -5957,6 +6041,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
|
|||
case AttributeList::AT_AssumeAligned:
|
||||
handleAssumeAlignedAttr(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_AllocAlign:
|
||||
handleAllocAlignAttr(S, D, Attr);
|
||||
break;
|
||||
case AttributeList::AT_Overloadable:
|
||||
handleSimpleAttribute<OverloadableAttr>(S, D, Attr);
|
||||
break;
|
||||
|
|
|
@ -168,6 +168,16 @@ static void instantiateDependentAlignValueAttr(
|
|||
Aligned->getSpellingListIndex());
|
||||
}
|
||||
|
||||
static void instantiateDependentAllocAlignAttr(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const AllocAlignAttr *Align, Decl *New) {
|
||||
Expr *Param = IntegerLiteral::Create(
|
||||
S.getASTContext(), llvm::APInt(64, Align->getParamIndex()),
|
||||
S.getASTContext().UnsignedLongLongTy, Align->getLocation());
|
||||
S.AddAllocAlignAttr(Align->getLocation(), New, Param,
|
||||
Align->getSpellingListIndex());
|
||||
}
|
||||
|
||||
static Expr *instantiateDependentFunctionAttrCondition(
|
||||
Sema &S, const MultiLevelTemplateArgumentList &TemplateArgs,
|
||||
const Attr *A, Expr *OldCond, const Decl *Tmpl, FunctionDecl *New) {
|
||||
|
@ -380,6 +390,12 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
|
|||
continue;
|
||||
}
|
||||
|
||||
if (const auto *AllocAlign = dyn_cast<AllocAlignAttr>(TmplAttr)) {
|
||||
instantiateDependentAllocAlignAttr(*this, TemplateArgs, AllocAlign, New);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (const auto *EnableIf = dyn_cast<EnableIfAttr>(TmplAttr)) {
|
||||
instantiateDependentEnableIfAttr(*this, TemplateArgs, EnableIf, Tmpl,
|
||||
cast<FunctionDecl>(New));
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-pc-linux -emit-llvm -o - %s | FileCheck %s
|
||||
|
||||
__INT32_TYPE__*m1(__INT32_TYPE__ i) __attribute__((alloc_align(1)));
|
||||
|
||||
// Condition where parameter to m1 is not size_t.
|
||||
__INT32_TYPE__ test1(__INT32_TYPE__ a) {
|
||||
// CHECK: define i32 @test1
|
||||
return *m1(a);
|
||||
// CHECK: call i32* @m1(i32 [[PARAM1:%[^\)]+]])
|
||||
// CHECK: [[ALIGNCAST1:%.+]] = sext i32 [[PARAM1]] to i64
|
||||
// CHECK: [[ISPOS1:%.+]] = icmp sgt i64 [[ALIGNCAST1]], 0
|
||||
// CHECK: [[POSMASK1:%.+]] = sub i64 [[ALIGNCAST1]], 1
|
||||
// CHECK: [[MASK1:%.+]] = select i1 [[ISPOS1]], i64 [[POSMASK1]], i64 0
|
||||
// CHECK: [[PTRINT1:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR1:%.+]] = and i64 [[PTRINT1]], [[MASK1]]
|
||||
// CHECK: [[MASKCOND1:%.+]] = icmp eq i64 [[MASKEDPTR1]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND1]])
|
||||
}
|
||||
// Condition where test2 param needs casting.
|
||||
__INT32_TYPE__ test2(__SIZE_TYPE__ a) {
|
||||
// CHECK: define i32 @test2
|
||||
return *m1(a);
|
||||
// CHECK: [[CONV2:%.+]] = trunc i64 %{{.+}} to i32
|
||||
// CHECK: call i32* @m1(i32 [[CONV2]])
|
||||
// CHECK: [[ALIGNCAST2:%.+]] = sext i32 [[CONV2]] to i64
|
||||
// CHECK: [[ISPOS2:%.+]] = icmp sgt i64 [[ALIGNCAST2]], 0
|
||||
// CHECK: [[POSMASK2:%.+]] = sub i64 [[ALIGNCAST2]], 1
|
||||
// CHECK: [[MASK2:%.+]] = select i1 [[ISPOS2]], i64 [[POSMASK2]], i64 0
|
||||
// CHECK: [[PTRINT2:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR2:%.+]] = and i64 [[PTRINT2]], [[MASK2]]
|
||||
// CHECK: [[MASKCOND2:%.+]] = icmp eq i64 [[MASKEDPTR2]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND2]])
|
||||
}
|
||||
__INT32_TYPE__ *m2(__SIZE_TYPE__ i) __attribute__((alloc_align(1)));
|
||||
|
||||
// test3 param needs casting, but 'm2' is correct.
|
||||
__INT32_TYPE__ test3(__INT32_TYPE__ a) {
|
||||
// CHECK: define i32 @test3
|
||||
return *m2(a);
|
||||
// CHECK: [[CONV3:%.+]] = sext i32 %{{.+}} to i64
|
||||
// CHECK: call i32* @m2(i64 [[CONV3]])
|
||||
// CHECK: [[ISPOS3:%.+]] = icmp sgt i64 [[CONV3]], 0
|
||||
// CHECK: [[POSMASK3:%.+]] = sub i64 [[CONV3]], 1
|
||||
// CHECK: [[MASK3:%.+]] = select i1 [[ISPOS3]], i64 [[POSMASK3]], i64 0
|
||||
// CHECK: [[PTRINT3:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR3:%.+]] = and i64 [[PTRINT3]], [[MASK3]]
|
||||
// CHECK: [[MASKCOND3:%.+]] = icmp eq i64 [[MASKEDPTR3]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND3]])
|
||||
}
|
||||
|
||||
// Every type matches, canonical example.
|
||||
__INT32_TYPE__ test4(__SIZE_TYPE__ a) {
|
||||
// CHECK: define i32 @test4
|
||||
return *m2(a);
|
||||
// CHECK: call i32* @m2(i64 [[PARAM4:%[^\)]+]])
|
||||
// CHECK: [[ISPOS4:%.+]] = icmp sgt i64 [[PARAM4]], 0
|
||||
// CHECK: [[POSMASK4:%.+]] = sub i64 [[PARAM4]], 1
|
||||
// CHECK: [[MASK4:%.+]] = select i1 [[ISPOS4]], i64 [[POSMASK4]], i64 0
|
||||
// CHECK: [[PTRINT4:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR4:%.+]] = and i64 [[PTRINT4]], [[MASK4]]
|
||||
// CHECK: [[MASKCOND4:%.+]] = icmp eq i64 [[MASKEDPTR4]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND4]])
|
||||
}
|
||||
|
||||
|
||||
struct Empty {};
|
||||
struct MultiArgs { __INT64_TYPE__ a, b;};
|
||||
// Struct parameter doesn't take up an IR parameter, 'i' takes up 2.
|
||||
// Truncation to i64 is permissible, since alignments of greater than 2^64 are insane.
|
||||
__INT32_TYPE__ *m3(struct Empty s, __int128_t i) __attribute__((alloc_align(2)));
|
||||
__INT32_TYPE__ test5(__int128_t a) {
|
||||
// CHECK: define i32 @test5
|
||||
struct Empty e;
|
||||
return *m3(e, a);
|
||||
// CHECK: call i32* @m3(i64 %{{.*}}, i64 %{{.*}})
|
||||
// CHECK: [[ALIGNCAST5:%.+]] = trunc i128 %{{.*}} to i64
|
||||
// CHECK: [[ISPOS5:%.+]] = icmp sgt i64 [[ALIGNCAST5]], 0
|
||||
// CHECK: [[POSMASK5:%.+]] = sub i64 [[ALIGNCAST5]], 1
|
||||
// CHECK: [[MASK5:%.+]] = select i1 [[ISPOS5]], i64 [[POSMASK5]], i64 0
|
||||
// CHECK: [[PTRINT5:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR5:%.+]] = and i64 [[PTRINT5]], [[MASK5]]
|
||||
// CHECK: [[MASKCOND5:%.+]] = icmp eq i64 [[MASKEDPTR5]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND5]])
|
||||
}
|
||||
// Struct parameter takes up 2 parameters, 'i' takes up 2.
|
||||
__INT32_TYPE__ *m4(struct MultiArgs s, __int128_t i) __attribute__((alloc_align(2)));
|
||||
__INT32_TYPE__ test6(__int128_t a) {
|
||||
// CHECK: define i32 @test6
|
||||
struct MultiArgs e;
|
||||
return *m4(e, a);
|
||||
// CHECK: call i32* @m4(i64 %{{.*}}, i64 %{{.*}}, i64 %{{.*}})
|
||||
// CHECK: [[ALIGNCAST6:%.+]] = trunc i128 %{{.*}} to i64
|
||||
// CHECK: [[ISPOS6:%.+]] = icmp sgt i64 [[ALIGNCAST6]], 0
|
||||
// CHECK: [[POSMASK6:%.+]] = sub i64 [[ALIGNCAST6]], 1
|
||||
// CHECK: [[MASK6:%.+]] = select i1 [[ISPOS6]], i64 [[POSMASK6]], i64 0
|
||||
// CHECK: [[PTRINT6:%.+]] = ptrtoint
|
||||
// CHECK: [[MASKEDPTR6:%.+]] = and i64 [[PTRINT6]], [[MASK6]]
|
||||
// CHECK: [[MASKCOND6:%.+]] = icmp eq i64 [[MASKEDPTR6]], 0
|
||||
// CHECK: call void @llvm.assume(i1 [[MASKCOND6]])
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
// return values
|
||||
void test_void_alloc_align(void) __attribute__((alloc_align(1))); // expected-warning {{'alloc_align' attribute only applies to return values that are pointers}}
|
||||
void *test_ptr_alloc_align(int a) __attribute__((alloc_align(1))); // no-warning
|
||||
|
||||
int j __attribute__((alloc_align(1))); // expected-warning {{'alloc_align' attribute only applies to non-K&R-style functions}}
|
||||
void *test_no_params_zero(void) __attribute__((alloc_align(0))); // expected-error {{'alloc_align' attribute parameter 1 is out of bounds}}
|
||||
void *test_no_params(void) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute parameter 1 is out of bounds}}
|
||||
void *test_incorrect_param_type(float a) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}}
|
||||
|
||||
// argument type
|
||||
void *test_bad_param_type(void) __attribute((alloc_align(1.1))); // expected-error {{'alloc_align' attribute requires parameter 1 to be an integer constant}}
|
||||
|
||||
// argument count
|
||||
void *test_no_fn_proto() __attribute__((alloc_align)); // expected-error {{'alloc_align' attribute takes one argument}}
|
||||
void *test_no_fn_proto() __attribute__((alloc_align())); // expected-error {{'alloc_align' attribute takes one argument}}
|
||||
void *test_no_fn_proto() __attribute__((alloc_align(32, 45, 37))); // expected-error {{'alloc_align' attribute takes one argument}}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// RUN: %clang_cc1 -fsyntax-only -verify %s
|
||||
|
||||
struct param_num {
|
||||
void* Foo(int a) __attribute__((alloc_align(1))); // expected-error {{'alloc_align' attribute is invalid for the implicit this argument}}
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
struct dependent_ret {
|
||||
T* Foo(int a) __attribute__((alloc_align(2)));// no-warning, ends up being int**.
|
||||
T Foo2(int a) __attribute__((alloc_align(2)));// expected-warning {{'alloc_align' attribute only applies to return values that are pointers or references}}
|
||||
};
|
||||
|
||||
// Following 2 errors associated only with the 'float' versions below.
|
||||
template <typename T>
|
||||
struct dependent_param_struct {
|
||||
void* Foo(T param) __attribute__((alloc_align(2))); // expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
void* dependent_param_func(T param) __attribute__((alloc_align(1)));// expected-error {{'alloc_align' attribute argument may only refer to a function parameter of integer type}}
|
||||
|
||||
template <int T>
|
||||
void* illegal_align_param(int p) __attribute__((alloc_align(T))); // expected-error {{'alloc_align' attribute requires parameter 1 to be an integer constant}}
|
||||
|
||||
void dependent_impl() {
|
||||
dependent_ret<int> a; // expected-note {{in instantiation of template class 'dependent_ret<int>' requested here}}
|
||||
a.Foo(1);
|
||||
a.Foo2(1);
|
||||
dependent_ret<int*> b;
|
||||
a.Foo(1);
|
||||
a.Foo2(1);
|
||||
|
||||
dependent_param_struct<int> c;
|
||||
c.Foo(1);
|
||||
dependent_param_struct<float> d; // expected-note {{in instantiation of template class 'dependent_param_struct<float>' requested here}}
|
||||
d.Foo(1.0);
|
||||
dependent_param_func<int>(1);
|
||||
dependent_param_func<float>(1); // expected-note {{in instantiation of function template specialization 'dependent_param_func<float>' requested here}}
|
||||
}
|
Loading…
Reference in New Issue