[Clang][counted_by] Add support for 'counted_by' on struct pointers (#137250)
The 'counted_by' attribute is now available for pointers in structs.
It generates code for sanity checks as well as
__builtin_dynamic_object_size()
calculations. For example:
struct annotated_ptr {
int count;
char *buf __attribute__((counted_by(count)));
};
If the pointer's type is 'void *', use the 'sized_by' attribute, which
works similarly to 'counted_by', but can handle the 'void' base type:
struct annotated_ptr {
int count;
void *buf __attribute__((sized_by(count)));
};
If the 'count' field member occurs after the pointer, use the
'-fexperimental-late-parse-attributes' flag during compilation.
Note that 'counted_by' cannot be applied to a pointer to an incomplete
type, because the size isn't known.
struct foo;
struct annotated_ptr {
int count;
struct foo *buf __attribute__((counted_by(count))); /* invalid */
};
Signed-off-by: Bill Wendling <morbo@google.com>
This commit is contained in:
parent
4e604d4668
commit
9ae3bce175
@ -909,7 +909,8 @@ class StructFieldAccess
|
||||
bool AddrOfSeen = false;
|
||||
|
||||
public:
|
||||
const ArraySubscriptExpr *ASE = nullptr;
|
||||
const Expr *ArrayIndex = nullptr;
|
||||
QualType ArrayElementTy;
|
||||
|
||||
const Expr *VisitMemberExpr(const MemberExpr *E) {
|
||||
if (AddrOfSeen && E->getType()->isArrayType())
|
||||
@ -919,12 +920,13 @@ public:
|
||||
}
|
||||
|
||||
const Expr *VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
|
||||
if (ASE)
|
||||
if (ArrayIndex)
|
||||
// We don't support multiple subscripts.
|
||||
return nullptr;
|
||||
|
||||
AddrOfSeen = false; // '&ptr->array[idx]' is okay.
|
||||
ASE = E;
|
||||
ArrayIndex = E->getIdx();
|
||||
ArrayElementTy = E->getBase()->getType();
|
||||
return Visit(E->getBase());
|
||||
}
|
||||
const Expr *VisitCastExpr(const CastExpr *E) {
|
||||
@ -1016,12 +1018,10 @@ GetFieldOffset(ASTContext &Ctx, const RecordDecl *RD, const FieldDecl *FD) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
llvm::Value *
|
||||
CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
unsigned Type,
|
||||
llvm::IntegerType *ResType) {
|
||||
ASTContext &Ctx = getContext();
|
||||
|
||||
llvm::Value *CodeGenFunction::emitCountedBySize(const Expr *E,
|
||||
llvm::Value *EmittedE,
|
||||
unsigned Type,
|
||||
llvm::IntegerType *ResType) {
|
||||
// Note: If the whole struct is specificed in the __bdos (i.e. Visitor
|
||||
// returns a DeclRefExpr). The calculation of the whole size of the structure
|
||||
// with a flexible array member can be done in two ways:
|
||||
@ -1040,38 +1040,13 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
// GCC does for consistency's sake.
|
||||
|
||||
StructFieldAccess Visitor;
|
||||
const MemberExpr *ME = dyn_cast_if_present<MemberExpr>(Visitor.Visit(E));
|
||||
if (!ME)
|
||||
E = Visitor.Visit(E);
|
||||
if (!E)
|
||||
return nullptr;
|
||||
|
||||
const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
|
||||
if (!FD)
|
||||
return nullptr;
|
||||
|
||||
const RecordDecl *RD = FD->getDeclContext()->getOuterLexicalRecordContext();
|
||||
const FieldDecl *FlexibleArrayMemberFD = nullptr;
|
||||
|
||||
if (Decl::isFlexibleArrayMemberLike(
|
||||
Ctx, FD, FD->getType(), getLangOpts().getStrictFlexArraysLevel(),
|
||||
/*IgnoreTemplateOrMacroSubstitution=*/true))
|
||||
FlexibleArrayMemberFD = FD;
|
||||
else
|
||||
FlexibleArrayMemberFD = FindFlexibleArrayMemberField(*this, Ctx, RD);
|
||||
|
||||
if (!FlexibleArrayMemberFD ||
|
||||
!FlexibleArrayMemberFD->getType()->isCountAttributedType())
|
||||
return nullptr;
|
||||
|
||||
const FieldDecl *CountFD = FlexibleArrayMemberFD->findCountedByField();
|
||||
if (!CountFD)
|
||||
// Can't find the field referenced by the "counted_by" attribute.
|
||||
return nullptr;
|
||||
|
||||
const Expr *Idx = nullptr;
|
||||
if (Visitor.ASE) {
|
||||
Idx = Visitor.ASE->getIdx();
|
||||
|
||||
if (Idx->HasSideEffects(Ctx))
|
||||
const Expr *Idx = Visitor.ArrayIndex;
|
||||
if (Idx) {
|
||||
if (Idx->HasSideEffects(getContext()))
|
||||
// We can't have side-effects.
|
||||
return getDefaultBuiltinObjectSizeResult(Type, ResType);
|
||||
|
||||
@ -1086,8 +1061,225 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the flexible array member's object size using these formulae
|
||||
// (note: if the calculation is negative, we return 0.):
|
||||
// __counted_by on either a flexible array member or a pointer into a struct
|
||||
// with a flexible array member.
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(E))
|
||||
return emitCountedByMemberSize(ME, Idx, EmittedE, Visitor.ArrayElementTy,
|
||||
Type, ResType);
|
||||
|
||||
// __counted_by on a pointer in a struct.
|
||||
if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E);
|
||||
ICE && ICE->getCastKind() == CK_LValueToRValue)
|
||||
return emitCountedByPointerSize(ICE, Idx, EmittedE, Visitor.ArrayElementTy,
|
||||
Type, ResType);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static llvm::Value *EmitPositiveResultOrZero(CodeGenFunction &CGF,
|
||||
llvm::Value *Res,
|
||||
llvm::Value *Index,
|
||||
llvm::IntegerType *ResType,
|
||||
bool IsSigned) {
|
||||
// cmp = (array_size >= 0)
|
||||
Value *Cmp = CGF.Builder.CreateIsNotNeg(Res);
|
||||
if (Index)
|
||||
// cmp = (cmp && index >= 0)
|
||||
Cmp = CGF.Builder.CreateAnd(CGF.Builder.CreateIsNotNeg(Index), Cmp);
|
||||
|
||||
// return cmp ? result : 0
|
||||
return CGF.Builder.CreateSelect(Cmp, Res,
|
||||
ConstantInt::get(ResType, 0, IsSigned));
|
||||
}
|
||||
|
||||
static std::pair<llvm::Value *, llvm::Value *>
|
||||
GetCountFieldAndIndex(CodeGenFunction &CGF, const MemberExpr *ME,
|
||||
const FieldDecl *ArrayFD, const FieldDecl *CountFD,
|
||||
const Expr *Idx, llvm::IntegerType *ResType,
|
||||
bool IsSigned) {
|
||||
// count = ptr->count;
|
||||
Value *Count = CGF.EmitLoadOfCountedByField(ME, ArrayFD, CountFD);
|
||||
if (!Count)
|
||||
return std::make_pair<Value *>(nullptr, nullptr);
|
||||
Count = CGF.Builder.CreateIntCast(Count, ResType, IsSigned, "count");
|
||||
|
||||
// index = ptr->index;
|
||||
Value *Index = nullptr;
|
||||
if (Idx) {
|
||||
bool IdxSigned = Idx->getType()->isSignedIntegerType();
|
||||
Index = CGF.EmitScalarExpr(Idx);
|
||||
Index = CGF.Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
|
||||
}
|
||||
|
||||
return std::make_pair(Count, Index);
|
||||
}
|
||||
|
||||
llvm::Value *CodeGenFunction::emitCountedByPointerSize(
|
||||
const ImplicitCastExpr *E, const Expr *Idx, llvm::Value *EmittedE,
|
||||
QualType CastedArrayElementTy, unsigned Type, llvm::IntegerType *ResType) {
|
||||
assert(E->getCastKind() == CK_LValueToRValue &&
|
||||
"must be an LValue to RValue cast");
|
||||
|
||||
const MemberExpr *ME = dyn_cast<MemberExpr>(E->getSubExpr());
|
||||
if (!ME)
|
||||
return nullptr;
|
||||
|
||||
const auto *ArrayBaseFD = dyn_cast<FieldDecl>(ME->getMemberDecl());
|
||||
if (!ArrayBaseFD || !ArrayBaseFD->getType()->isPointerType() ||
|
||||
!ArrayBaseFD->getType()->isCountAttributedType())
|
||||
return nullptr;
|
||||
|
||||
// Get the 'count' FieldDecl.
|
||||
const FieldDecl *CountFD = ArrayBaseFD->findCountedByField();
|
||||
if (!CountFD)
|
||||
// Can't find the field referenced by the "counted_by" attribute.
|
||||
return nullptr;
|
||||
|
||||
// Calculate the array's object size using these formulae. (Note: if the
|
||||
// calculation is negative, we return 0.):
|
||||
//
|
||||
// struct p;
|
||||
// struct s {
|
||||
// /* ... */
|
||||
// struct p **array __attribute__((counted_by(count)));
|
||||
// int count;
|
||||
// };
|
||||
//
|
||||
// 1) 'ptr->array':
|
||||
//
|
||||
// count = ptr->count;
|
||||
//
|
||||
// array_element_size = sizeof (*ptr->array);
|
||||
// array_size = count * array_element_size;
|
||||
//
|
||||
// result = array_size;
|
||||
//
|
||||
// cmp = (result >= 0)
|
||||
// return cmp ? result : 0;
|
||||
//
|
||||
// 2) '&((cast) ptr->array)[idx]':
|
||||
//
|
||||
// count = ptr->count;
|
||||
// index = idx;
|
||||
//
|
||||
// array_element_size = sizeof (*ptr->array);
|
||||
// array_size = count * array_element_size;
|
||||
//
|
||||
// casted_array_element_size = sizeof (*((cast) ptr->array));
|
||||
//
|
||||
// index_size = index * casted_array_element_size;
|
||||
// result = array_size - index_size;
|
||||
//
|
||||
// cmp = (result >= 0)
|
||||
// if (index)
|
||||
// cmp = (cmp && index > 0)
|
||||
// return cmp ? result : 0;
|
||||
|
||||
auto GetElementBaseSize = [&](QualType ElementTy) {
|
||||
CharUnits ElementSize =
|
||||
getContext().getTypeSizeInChars(ElementTy->getPointeeType());
|
||||
|
||||
if (ElementSize.isZero()) {
|
||||
// This might be a __sized_by on a 'void *', which counts bytes, not
|
||||
// elements.
|
||||
auto *CAT = ElementTy->getAs<CountAttributedType>();
|
||||
if (!CAT || (CAT->getKind() != CountAttributedType::SizedBy &&
|
||||
CAT->getKind() != CountAttributedType::SizedByOrNull))
|
||||
// Okay, not sure what it is now.
|
||||
// FIXME: Should this be an assert?
|
||||
return std::optional<CharUnits>();
|
||||
|
||||
ElementSize = CharUnits::One();
|
||||
}
|
||||
|
||||
return std::optional<CharUnits>(ElementSize);
|
||||
};
|
||||
|
||||
// Get the sizes of the original array element and the casted array element,
|
||||
// if different.
|
||||
std::optional<CharUnits> ArrayElementBaseSize =
|
||||
GetElementBaseSize(ArrayBaseFD->getType());
|
||||
if (!ArrayElementBaseSize)
|
||||
return nullptr;
|
||||
|
||||
std::optional<CharUnits> CastedArrayElementBaseSize = ArrayElementBaseSize;
|
||||
if (!CastedArrayElementTy.isNull() && CastedArrayElementTy->isPointerType()) {
|
||||
CastedArrayElementBaseSize = GetElementBaseSize(CastedArrayElementTy);
|
||||
if (!CastedArrayElementBaseSize)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsSigned = CountFD->getType()->isSignedIntegerType();
|
||||
|
||||
// count = ptr->count;
|
||||
// index = ptr->index;
|
||||
Value *Count, *Index;
|
||||
std::tie(Count, Index) = GetCountFieldAndIndex(
|
||||
*this, ME, ArrayBaseFD, CountFD, Idx, ResType, IsSigned);
|
||||
if (!Count)
|
||||
return nullptr;
|
||||
|
||||
// array_element_size = sizeof (*ptr->array)
|
||||
auto *ArrayElementSize = llvm::ConstantInt::get(
|
||||
ResType, ArrayElementBaseSize->getQuantity(), IsSigned);
|
||||
|
||||
// casted_array_element_size = sizeof (*((cast) ptr->array));
|
||||
auto *CastedArrayElementSize = llvm::ConstantInt::get(
|
||||
ResType, CastedArrayElementBaseSize->getQuantity(), IsSigned);
|
||||
|
||||
// array_size = count * array_element_size;
|
||||
Value *ArraySize = Builder.CreateMul(Count, ArrayElementSize, "array_size",
|
||||
!IsSigned, IsSigned);
|
||||
|
||||
// Option (1) 'ptr->array'
|
||||
// result = array_size
|
||||
Value *Result = ArraySize;
|
||||
|
||||
if (Idx) { // Option (2) '&((cast) ptr->array)[idx]'
|
||||
// index_size = index * casted_array_element_size;
|
||||
Value *IndexSize = Builder.CreateMul(Index, CastedArrayElementSize,
|
||||
"index_size", !IsSigned, IsSigned);
|
||||
|
||||
// result = result - index_size;
|
||||
Result =
|
||||
Builder.CreateSub(Result, IndexSize, "result", !IsSigned, IsSigned);
|
||||
}
|
||||
|
||||
return EmitPositiveResultOrZero(*this, Result, Index, ResType, IsSigned);
|
||||
}
|
||||
|
||||
llvm::Value *CodeGenFunction::emitCountedByMemberSize(
|
||||
const MemberExpr *ME, const Expr *Idx, llvm::Value *EmittedE,
|
||||
QualType CastedArrayElementTy, unsigned Type, llvm::IntegerType *ResType) {
|
||||
const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl());
|
||||
if (!FD)
|
||||
return nullptr;
|
||||
|
||||
// Find the flexible array member and check that it has the __counted_by
|
||||
// attribute.
|
||||
ASTContext &Ctx = getContext();
|
||||
const RecordDecl *RD = FD->getDeclContext()->getOuterLexicalRecordContext();
|
||||
const FieldDecl *FlexibleArrayMemberFD = nullptr;
|
||||
|
||||
if (Decl::isFlexibleArrayMemberLike(
|
||||
Ctx, FD, FD->getType(), getLangOpts().getStrictFlexArraysLevel(),
|
||||
/*IgnoreTemplateOrMacroSubstitution=*/true))
|
||||
FlexibleArrayMemberFD = FD;
|
||||
else
|
||||
FlexibleArrayMemberFD = FindFlexibleArrayMemberField(*this, Ctx, RD);
|
||||
|
||||
if (!FlexibleArrayMemberFD ||
|
||||
!FlexibleArrayMemberFD->getType()->isCountAttributedType())
|
||||
return nullptr;
|
||||
|
||||
// Get the 'count' FieldDecl.
|
||||
const FieldDecl *CountFD = FlexibleArrayMemberFD->findCountedByField();
|
||||
if (!CountFD)
|
||||
// Can't find the field referenced by the "counted_by" attribute.
|
||||
return nullptr;
|
||||
|
||||
// Calculate the flexible array member's object size using these formulae.
|
||||
// (Note: if the calculation is negative, we return 0.):
|
||||
//
|
||||
// struct p;
|
||||
// struct s {
|
||||
@ -1100,76 +1292,84 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
//
|
||||
// count = ptr->count;
|
||||
//
|
||||
// flexible_array_member_base_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_element_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_size =
|
||||
// count * flexible_array_member_base_size;
|
||||
// count * flexible_array_member_element_size;
|
||||
//
|
||||
// if (flexible_array_member_size < 0)
|
||||
// return 0;
|
||||
// return flexible_array_member_size;
|
||||
// result = flexible_array_member_size;
|
||||
//
|
||||
// 2) '&ptr->array[idx]':
|
||||
// cmp = (result >= 0)
|
||||
// return cmp ? result : 0;
|
||||
//
|
||||
// 2) '&((cast) ptr->array)[idx]':
|
||||
//
|
||||
// count = ptr->count;
|
||||
// index = idx;
|
||||
//
|
||||
// flexible_array_member_base_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_element_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_size =
|
||||
// count * flexible_array_member_base_size;
|
||||
// count * flexible_array_member_element_size;
|
||||
//
|
||||
// index_size = index * flexible_array_member_base_size;
|
||||
// casted_flexible_array_member_element_size =
|
||||
// sizeof (*((cast) ptr->array));
|
||||
// index_size = index * casted_flexible_array_member_element_size;
|
||||
//
|
||||
// if (flexible_array_member_size < 0 || index < 0)
|
||||
// return 0;
|
||||
// return flexible_array_member_size - index_size;
|
||||
// result = flexible_array_member_size - index_size;
|
||||
//
|
||||
// cmp = (result >= 0)
|
||||
// if (index != 0)
|
||||
// cmp = (cmp && index >= 0)
|
||||
// return cmp ? result : 0;
|
||||
//
|
||||
// 3) '&ptr->field':
|
||||
//
|
||||
// count = ptr->count;
|
||||
// sizeof_struct = sizeof (struct s);
|
||||
//
|
||||
// flexible_array_member_base_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_element_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_size =
|
||||
// count * flexible_array_member_base_size;
|
||||
// count * flexible_array_member_element_size;
|
||||
//
|
||||
// field_offset = offsetof (struct s, field);
|
||||
// offset_diff = sizeof_struct - field_offset;
|
||||
//
|
||||
// if (flexible_array_member_size < 0)
|
||||
// return 0;
|
||||
// return offset_diff + flexible_array_member_size;
|
||||
// result = offset_diff + flexible_array_member_size;
|
||||
//
|
||||
// 4) '&ptr->field_array[idx]':
|
||||
// cmp = (result >= 0)
|
||||
// return cmp ? result : 0;
|
||||
//
|
||||
// 4) '&((cast) ptr->field_array)[idx]':
|
||||
//
|
||||
// count = ptr->count;
|
||||
// index = idx;
|
||||
// sizeof_struct = sizeof (struct s);
|
||||
//
|
||||
// flexible_array_member_base_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_element_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_size =
|
||||
// count * flexible_array_member_base_size;
|
||||
// count * flexible_array_member_element_size;
|
||||
//
|
||||
// field_base_size = sizeof (*ptr->field_array);
|
||||
// casted_field_element_size = sizeof (*((cast) ptr->field_array));
|
||||
// field_offset = offsetof (struct s, field)
|
||||
// field_offset += index * field_base_size;
|
||||
// field_offset += index * casted_field_element_size;
|
||||
//
|
||||
// offset_diff = sizeof_struct - field_offset;
|
||||
//
|
||||
// if (flexible_array_member_size < 0 || index < 0)
|
||||
// return 0;
|
||||
// return offset_diff + flexible_array_member_size;
|
||||
// result = offset_diff + flexible_array_member_size;
|
||||
//
|
||||
// cmp = (result >= 0)
|
||||
// if (index != 0)
|
||||
// cmp = (cmp && index >= 0)
|
||||
// return cmp ? result : 0;
|
||||
|
||||
QualType CountTy = CountFD->getType();
|
||||
bool IsSigned = CountTy->isSignedIntegerType();
|
||||
bool IsSigned = CountFD->getType()->isSignedIntegerType();
|
||||
|
||||
QualType FlexibleArrayMemberTy = FlexibleArrayMemberFD->getType();
|
||||
QualType FieldTy = FD->getType();
|
||||
|
||||
// Explicit cast because otherwise the CharWidth will promote an i32's into
|
||||
// u64's leading to overflows..
|
||||
// u64's leading to overflows.
|
||||
int64_t CharWidth = static_cast<int64_t>(CGM.getContext().getCharWidth());
|
||||
|
||||
// size_t field_offset = offsetof (struct s, field);
|
||||
// field_offset = offsetof (struct s, field);
|
||||
Value *FieldOffset = nullptr;
|
||||
if (FlexibleArrayMemberFD != FD) {
|
||||
std::optional<int64_t> Offset = GetFieldOffset(Ctx, RD, FD);
|
||||
@ -1179,81 +1379,90 @@ CodeGenFunction::emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
llvm::ConstantInt::get(ResType, *Offset / CharWidth, IsSigned);
|
||||
}
|
||||
|
||||
// size_t count = (size_t) ptr->count;
|
||||
Value *Count = EmitLoadOfCountedByField(ME, FlexibleArrayMemberFD, CountFD);
|
||||
// count = ptr->count;
|
||||
// index = ptr->index;
|
||||
Value *Count, *Index;
|
||||
std::tie(Count, Index) = GetCountFieldAndIndex(
|
||||
*this, ME, FlexibleArrayMemberFD, CountFD, Idx, ResType, IsSigned);
|
||||
if (!Count)
|
||||
return nullptr;
|
||||
Count = Builder.CreateIntCast(Count, ResType, IsSigned, "count");
|
||||
|
||||
// size_t index = (size_t) ptr->index;
|
||||
Value *Index = nullptr;
|
||||
if (Idx) {
|
||||
bool IdxSigned = Idx->getType()->isSignedIntegerType();
|
||||
Index = EmitScalarExpr(Idx);
|
||||
Index = Builder.CreateIntCast(Index, ResType, IdxSigned, "index");
|
||||
}
|
||||
|
||||
// size_t flexible_array_member_base_size = sizeof (*ptr->array);
|
||||
// flexible_array_member_element_size = sizeof (*ptr->array);
|
||||
const ArrayType *ArrayTy = Ctx.getAsArrayType(FlexibleArrayMemberTy);
|
||||
CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
|
||||
auto *FlexibleArrayMemberBaseSize =
|
||||
auto *FlexibleArrayMemberElementSize =
|
||||
llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
|
||||
|
||||
// size_t flexible_array_member_size =
|
||||
// count * flexible_array_member_base_size;
|
||||
// flexible_array_member_size = count * flexible_array_member_element_size;
|
||||
Value *FlexibleArrayMemberSize =
|
||||
Builder.CreateMul(Count, FlexibleArrayMemberBaseSize,
|
||||
Builder.CreateMul(Count, FlexibleArrayMemberElementSize,
|
||||
"flexible_array_member_size", !IsSigned, IsSigned);
|
||||
|
||||
Value *Res = nullptr;
|
||||
Value *Result = nullptr;
|
||||
if (FlexibleArrayMemberFD == FD) {
|
||||
if (Idx) { // Option (2) '&ptr->array[idx]'
|
||||
// size_t index_size = index * flexible_array_member_base_size;
|
||||
Value *IndexSize = Builder.CreateMul(FlexibleArrayMemberBaseSize, Index,
|
||||
"index_size", !IsSigned, IsSigned);
|
||||
if (Idx) { // Option (2) '&((cast) ptr->array)[idx]'
|
||||
// casted_flexible_array_member_element_size =
|
||||
// sizeof (*((cast) ptr->array));
|
||||
llvm::ConstantInt *CastedFlexibleArrayMemberElementSize =
|
||||
FlexibleArrayMemberElementSize;
|
||||
if (!CastedArrayElementTy.isNull() &&
|
||||
CastedArrayElementTy->isPointerType()) {
|
||||
CharUnits BaseSize =
|
||||
Ctx.getTypeSizeInChars(CastedArrayElementTy->getPointeeType());
|
||||
CastedFlexibleArrayMemberElementSize =
|
||||
llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
|
||||
}
|
||||
|
||||
// return flexible_array_member_size - index_size;
|
||||
Res = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
|
||||
!IsSigned, IsSigned);
|
||||
// index_size = index * casted_flexible_array_member_element_size;
|
||||
Value *IndexSize =
|
||||
Builder.CreateMul(Index, CastedFlexibleArrayMemberElementSize,
|
||||
"index_size", !IsSigned, IsSigned);
|
||||
|
||||
// result = flexible_array_member_size - index_size;
|
||||
Result = Builder.CreateSub(FlexibleArrayMemberSize, IndexSize, "result",
|
||||
!IsSigned, IsSigned);
|
||||
} else { // Option (1) 'ptr->array'
|
||||
// return flexible_array_member_size;
|
||||
Res = FlexibleArrayMemberSize;
|
||||
// result = flexible_array_member_size;
|
||||
Result = FlexibleArrayMemberSize;
|
||||
}
|
||||
} else {
|
||||
// size_t sizeof_struct = sizeof (struct s);
|
||||
// sizeof_struct = sizeof (struct s);
|
||||
llvm::StructType *StructTy = getTypes().getCGRecordLayout(RD).getLLVMType();
|
||||
const llvm::DataLayout &Layout = CGM.getDataLayout();
|
||||
TypeSize Size = Layout.getTypeSizeInBits(StructTy);
|
||||
Value *SizeofStruct =
|
||||
llvm::ConstantInt::get(ResType, Size.getKnownMinValue() / CharWidth);
|
||||
|
||||
if (Idx) { // Option (4) '&ptr->field_array[idx]'
|
||||
// size_t field_base_size = sizeof (*ptr->field_array);
|
||||
const ArrayType *ArrayTy = Ctx.getAsArrayType(FieldTy);
|
||||
CharUnits BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
|
||||
auto *FieldBaseSize =
|
||||
if (Idx) { // Option (4) '&((cast) ptr->field_array)[idx]'
|
||||
// casted_field_element_size = sizeof (*((cast) ptr->field_array));
|
||||
CharUnits BaseSize;
|
||||
if (!CastedArrayElementTy.isNull() &&
|
||||
CastedArrayElementTy->isPointerType()) {
|
||||
BaseSize =
|
||||
Ctx.getTypeSizeInChars(CastedArrayElementTy->getPointeeType());
|
||||
} else {
|
||||
const ArrayType *ArrayTy = Ctx.getAsArrayType(FD->getType());
|
||||
BaseSize = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
|
||||
}
|
||||
|
||||
llvm::ConstantInt *CastedFieldElementSize =
|
||||
llvm::ConstantInt::get(ResType, BaseSize.getQuantity(), IsSigned);
|
||||
|
||||
// field_offset += index * field_base_size;
|
||||
Value *Mul = Builder.CreateMul(Index, FieldBaseSize, "field_offset",
|
||||
!IsSigned, IsSigned);
|
||||
// field_offset += index * casted_field_element_size;
|
||||
Value *Mul = Builder.CreateMul(Index, CastedFieldElementSize,
|
||||
"field_offset", !IsSigned, IsSigned);
|
||||
FieldOffset = Builder.CreateAdd(FieldOffset, Mul);
|
||||
}
|
||||
// Option (3) '&ptr->field', and Option (4) continuation.
|
||||
|
||||
// size_t offset_diff = flexible_array_member_offset - field_offset;
|
||||
// offset_diff = flexible_array_member_offset - field_offset;
|
||||
Value *OffsetDiff = Builder.CreateSub(SizeofStruct, FieldOffset,
|
||||
"offset_diff", !IsSigned, IsSigned);
|
||||
|
||||
// return offset_diff + flexible_array_member_size;
|
||||
Res = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
|
||||
// result = offset_diff + flexible_array_member_size;
|
||||
Result = Builder.CreateAdd(FlexibleArrayMemberSize, OffsetDiff, "result");
|
||||
}
|
||||
|
||||
Value *Cmp = Builder.CreateIsNotNeg(Res);
|
||||
if (Idx)
|
||||
Cmp = Builder.CreateAnd(Builder.CreateIsNotNeg(Index), Cmp);
|
||||
|
||||
return Builder.CreateSelect(Cmp, Res, ConstantInt::get(ResType, 0, IsSigned));
|
||||
return EmitPositiveResultOrZero(*this, Result, Index, ResType, IsSigned);
|
||||
}
|
||||
|
||||
/// Returns a Value corresponding to the size of the given expression.
|
||||
@ -1301,7 +1510,7 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
|
||||
if (IsDynamic)
|
||||
// Emit special code for a flexible array member with the "counted_by"
|
||||
// attribute.
|
||||
if (Value *V = emitCountedByMemberSize(E, Ptr, Type, ResType))
|
||||
if (Value *V = emitCountedBySize(E, Ptr, Type, ResType))
|
||||
return V;
|
||||
|
||||
Function *F =
|
||||
|
||||
@ -4274,6 +4274,24 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
|
||||
return Address(eltPtr, CGF.ConvertTypeForMem(eltType), eltAlign);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
/// StructFieldAccess is a simple visitor class to grab the first l-value to
|
||||
/// r-value cast Expr.
|
||||
struct StructFieldAccess
|
||||
: public ConstStmtVisitor<StructFieldAccess, const Expr *> {
|
||||
const Expr *VisitCastExpr(const CastExpr *E) {
|
||||
if (E->getCastKind() == CK_LValueToRValue)
|
||||
return E;
|
||||
return Visit(E->getSubExpr());
|
||||
}
|
||||
const Expr *VisitParenExpr(const ParenExpr *E) {
|
||||
return Visit(E->getSubExpr());
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
/// The offset of a field from the beginning of the record.
|
||||
static bool getFieldOffsetInBits(CodeGenFunction &CGF, const RecordDecl *RD,
|
||||
const FieldDecl *Field, int64_t &Offset) {
|
||||
@ -4329,6 +4347,60 @@ static std::optional<int64_t> getOffsetDifferenceInBits(CodeGenFunction &CGF,
|
||||
return std::make_optional<int64_t>(FD1Offset - FD2Offset);
|
||||
}
|
||||
|
||||
/// EmitCountedByBoundsChecking - If the array being accessed has a "counted_by"
|
||||
/// attribute, generate bounds checking code. The "count" field is at the top
|
||||
/// level of the struct or in an anonymous struct, that's also at the top level.
|
||||
/// Future expansions may allow the "count" to reside at any place in the
|
||||
/// struct, but the value of "counted_by" will be a "simple" path to the count,
|
||||
/// i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
|
||||
/// similar to emit the correct GEP.
|
||||
void CodeGenFunction::EmitCountedByBoundsChecking(
|
||||
const Expr *E, llvm::Value *Idx, Address Addr, QualType IdxTy,
|
||||
QualType ArrayTy, bool Accessed, bool FlexibleArray) {
|
||||
const auto *ME = dyn_cast<MemberExpr>(E->IgnoreImpCasts());
|
||||
if (!ME || !ME->getMemberDecl()->getType()->isCountAttributedType())
|
||||
return;
|
||||
|
||||
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
|
||||
getLangOpts().getStrictFlexArraysLevel();
|
||||
if (FlexibleArray &&
|
||||
!ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel))
|
||||
return;
|
||||
|
||||
const FieldDecl *FD = cast<FieldDecl>(ME->getMemberDecl());
|
||||
const FieldDecl *CountFD = FD->findCountedByField();
|
||||
if (!CountFD)
|
||||
return;
|
||||
|
||||
if (std::optional<int64_t> Diff =
|
||||
getOffsetDifferenceInBits(*this, CountFD, FD)) {
|
||||
if (!Addr.isValid()) {
|
||||
// An invalid Address indicates we're checking a pointer array access.
|
||||
// Emit the checked L-Value here.
|
||||
LValue LV = EmitCheckedLValue(E, TCK_MemberAccess);
|
||||
Addr = LV.getAddress();
|
||||
}
|
||||
|
||||
// FIXME: The 'static_cast' is necessary, otherwise the result turns into a
|
||||
// uint64_t, which messes things up if we have a negative offset difference.
|
||||
Diff = *Diff / static_cast<int64_t>(CGM.getContext().getCharWidth());
|
||||
|
||||
// Create a GEP with the byte offset between the counted object and the
|
||||
// count and use that to load the count value.
|
||||
Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(Addr, Int8PtrTy, Int8Ty);
|
||||
|
||||
llvm::Type *CountTy = ConvertType(CountFD->getType());
|
||||
llvm::Value *Res =
|
||||
Builder.CreateInBoundsGEP(Int8Ty, Addr.emitRawPointer(*this),
|
||||
Builder.getInt32(*Diff), ".counted_by.gep");
|
||||
Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
|
||||
".counted_by.load");
|
||||
|
||||
// Now emit the bounds checking.
|
||||
EmitBoundsCheckImpl(E, Res, Idx, IdxTy, ArrayTy, Accessed);
|
||||
}
|
||||
}
|
||||
|
||||
LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
|
||||
bool Accessed) {
|
||||
// The index must always be an integer, which is not an aggregate. Emit it
|
||||
@ -4455,46 +4527,10 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
|
||||
ArrayLV = EmitLValue(Array);
|
||||
auto *Idx = EmitIdxAfterBase(/*Promote*/true);
|
||||
|
||||
if (SanOpts.has(SanitizerKind::ArrayBounds)) {
|
||||
// If the array being accessed has a "counted_by" attribute, generate
|
||||
// bounds checking code. The "count" field is at the top level of the
|
||||
// struct or in an anonymous struct, that's also at the top level. Future
|
||||
// expansions may allow the "count" to reside at any place in the struct,
|
||||
// but the value of "counted_by" will be a "simple" path to the count,
|
||||
// i.e. "a.b.count", so we shouldn't need the full force of EmitLValue or
|
||||
// similar to emit the correct GEP.
|
||||
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
|
||||
getLangOpts().getStrictFlexArraysLevel();
|
||||
|
||||
if (const auto *ME = dyn_cast<MemberExpr>(Array);
|
||||
ME &&
|
||||
ME->isFlexibleArrayMemberLike(getContext(), StrictFlexArraysLevel) &&
|
||||
ME->getMemberDecl()->getType()->isCountAttributedType()) {
|
||||
const FieldDecl *FAMDecl = cast<FieldDecl>(ME->getMemberDecl());
|
||||
if (const FieldDecl *CountFD = FAMDecl->findCountedByField()) {
|
||||
if (std::optional<int64_t> Diff =
|
||||
getOffsetDifferenceInBits(*this, CountFD, FAMDecl)) {
|
||||
CharUnits OffsetDiff = CGM.getContext().toCharUnitsFromBits(*Diff);
|
||||
|
||||
// Create a GEP with a byte offset between the FAM and count and
|
||||
// use that to load the count value.
|
||||
Addr = Builder.CreatePointerBitCastOrAddrSpaceCast(
|
||||
ArrayLV.getAddress(), Int8PtrTy, Int8Ty);
|
||||
|
||||
llvm::Type *CountTy = ConvertType(CountFD->getType());
|
||||
llvm::Value *Res = Builder.CreateInBoundsGEP(
|
||||
Int8Ty, Addr.emitRawPointer(*this),
|
||||
Builder.getInt32(OffsetDiff.getQuantity()), ".counted_by.gep");
|
||||
Res = Builder.CreateAlignedLoad(CountTy, Res, getIntAlign(),
|
||||
".counted_by.load");
|
||||
|
||||
// Now emit the bounds checking.
|
||||
EmitBoundsCheckImpl(E, Res, Idx, E->getIdx()->getType(),
|
||||
Array->getType(), Accessed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SanOpts.has(SanitizerKind::ArrayBounds))
|
||||
EmitCountedByBoundsChecking(Array, Idx, ArrayLV.getAddress(),
|
||||
E->getIdx()->getType(), Array->getType(),
|
||||
Accessed, /*FlexibleArray=*/true);
|
||||
|
||||
// Propagate the alignment from the array itself to the result.
|
||||
QualType arrayType = Array->getType();
|
||||
@ -4506,12 +4542,25 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
|
||||
EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType());
|
||||
} else {
|
||||
// The base must be a pointer; emit it with an estimate of its alignment.
|
||||
Addr = EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
|
||||
Address BaseAddr =
|
||||
EmitPointerWithAlignment(E->getBase(), &EltBaseInfo, &EltTBAAInfo);
|
||||
auto *Idx = EmitIdxAfterBase(/*Promote*/true);
|
||||
QualType ptrType = E->getBase()->getType();
|
||||
Addr = emitArraySubscriptGEP(
|
||||
*this, Addr, Idx, E->getType(), !getLangOpts().PointerOverflowDefined,
|
||||
SignedIndices, E->getExprLoc(), &ptrType, E->getBase());
|
||||
Addr = emitArraySubscriptGEP(*this, BaseAddr, Idx, E->getType(),
|
||||
!getLangOpts().PointerOverflowDefined,
|
||||
SignedIndices, E->getExprLoc(), &ptrType,
|
||||
E->getBase());
|
||||
|
||||
if (SanOpts.has(SanitizerKind::ArrayBounds)) {
|
||||
StructFieldAccess Visitor;
|
||||
const Expr *Base = Visitor.Visit(E->getBase());
|
||||
|
||||
if (const auto *CE = dyn_cast_if_present<CastExpr>(Base);
|
||||
CE && CE->getCastKind() == CK_LValueToRValue)
|
||||
EmitCountedByBoundsChecking(CE, Idx, Address::invalid(),
|
||||
E->getIdx()->getType(), ptrType, Accessed,
|
||||
/*FlexibleArray=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
LValue LV = MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo);
|
||||
|
||||
@ -3369,6 +3369,13 @@ public:
|
||||
llvm::Value *EmitLoadOfCountedByField(const Expr *Base, const FieldDecl *FD,
|
||||
const FieldDecl *CountDecl);
|
||||
|
||||
// Emit bounds checking for flexible array and pointer members with the
|
||||
// counted_by attribute.
|
||||
void EmitCountedByBoundsChecking(const Expr *E, llvm::Value *Idx,
|
||||
Address Addr, QualType IdxTy,
|
||||
QualType ArrayTy, bool Accessed,
|
||||
bool FlexibleArray);
|
||||
|
||||
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
|
||||
bool isInc, bool isPre);
|
||||
ComplexPairTy EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV,
|
||||
@ -5422,10 +5429,21 @@ private:
|
||||
llvm::IntegerType *ResType,
|
||||
llvm::Value *EmittedE, bool IsDynamic);
|
||||
|
||||
llvm::Value *emitCountedByMemberSize(const Expr *E, llvm::Value *EmittedE,
|
||||
llvm::Value *emitCountedBySize(const Expr *E, llvm::Value *EmittedE,
|
||||
unsigned Type, llvm::IntegerType *ResType);
|
||||
|
||||
llvm::Value *emitCountedByMemberSize(const MemberExpr *E, const Expr *Idx,
|
||||
llvm::Value *EmittedE,
|
||||
QualType CastedArrayElementTy,
|
||||
unsigned Type,
|
||||
llvm::IntegerType *ResType);
|
||||
|
||||
llvm::Value *emitCountedByPointerSize(const ImplicitCastExpr *E,
|
||||
const Expr *Idx, llvm::Value *EmittedE,
|
||||
QualType CastedArrayElementTy,
|
||||
unsigned Type,
|
||||
llvm::IntegerType *ResType);
|
||||
|
||||
void emitZeroOrPatternForAutoVarInit(QualType type, const VarDecl &D,
|
||||
Address Loc);
|
||||
|
||||
|
||||
473
clang/test/CodeGen/attr-counted-by-for-pointers.c
Normal file
473
clang/test/CodeGen/attr-counted-by-for-pointers.c
Normal file
@ -0,0 +1,473 @@
|
||||
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 3
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITH-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -DWITH_ATTRS -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITH-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fsanitize=array-bounds,object-size,local-bounds -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=SANITIZE-WITHOUT-ATTR %s
|
||||
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -O2 -Wall -fstrict-flex-arrays=3 -fexperimental-late-parse-attributes -emit-llvm -o - %s | FileCheck --check-prefix=NO-SANITIZE-WITHOUT-ATTR %s
|
||||
|
||||
#if !__has_attribute(counted_by)
|
||||
#error "has attribute broken"
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ATTRS
|
||||
#define __counted_by(member) __attribute__((__counted_by__(member)))
|
||||
#define __sized_by(member) __attribute__((__sized_by__(member)))
|
||||
#else
|
||||
#define __counted_by(member)
|
||||
#define __sized_by(member)
|
||||
#endif
|
||||
|
||||
#define __bdos(P) __builtin_dynamic_object_size(P, 0)
|
||||
|
||||
typedef long unsigned int size_t;
|
||||
|
||||
struct foo { size_t field; };
|
||||
struct annotated_ptr {
|
||||
unsigned long flags;
|
||||
struct foo **buf __counted_by(ptr_count);
|
||||
int ptr_count;
|
||||
};
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2:![0-9]+]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT10:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3:![0-9]+]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB2:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3:[0-9]+]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont10:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4:![0-9]+]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13:![0-9]+]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test1(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test1(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2:![0-9]+]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11:![0-9]+]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test1(struct annotated_ptr *p, int index, struct foo *value) {
|
||||
p->buf[index] = value;
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT10:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB4:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont10:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP2]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test2(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test2(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test2(struct annotated_ptr *p, int index, struct foo *value) {
|
||||
((struct foo **)((char *)p->buf))[index] = value;
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT10:%.*]], !prof [[PROF15:![0-9]+]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB5:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont10:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA4]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP1]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA13]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret void
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local void @test3(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]], ptr noundef [[VALUE:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[BUF:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[TMP0:%.*]] = load ptr, ptr [[BUF]], align 8, !tbaa [[TBAA2]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds ptr, ptr [[TMP0]], i64 [[IDXPROM]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: store ptr [[VALUE]], ptr [[ARRAYIDX]], align 8, !tbaa [[TBAA11]]
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret void
|
||||
//
|
||||
void test3(struct annotated_ptr *p, int index, struct foo *value) {
|
||||
*&*&*&p->buf[index] = value;
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test4(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test4(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR1:[0-9]+]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test4(struct annotated_ptr *p) {
|
||||
return __bdos(p->buf);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test5(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -17179869184, 17179869177) i64 @test5(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[ARRAY_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[ARRAY_SIZE]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test5(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test5(struct annotated_ptr *p, int index) {
|
||||
return __bdos((struct foo **)((char *)p->buf));
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 17179869177) i64 @test6(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB6:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont8:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = tail call i64 @llvm.smax.i64(i64 [[TMP1]], i64 0)
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = shl nuw nsw i64 [[TMP2]], 3
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -34359738360, 34359738361) i64 @test6(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = shl nsw i64 [[TMP0]], 3
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i64 [[TMP0]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = and i1 [[TMP2]], [[TMP1]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP4:%.*]] = select i1 [[TMP3]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP4]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test6(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test6(struct annotated_ptr *p, int index) {
|
||||
return __bdos(&p->buf[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test7(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp ult i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[TMP1]], label [[CONT10:%.*]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], !prof [[PROF3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB7:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont10:
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test7(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test7(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test7(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test7(struct annotated_ptr *p, int index) {
|
||||
return __bdos(((struct foo **)(char *)p->buf)[index]);
|
||||
}
|
||||
|
||||
struct annotated_sized_ptr {
|
||||
unsigned long flags;
|
||||
void *buf __sized_by(ptr_count);
|
||||
int ptr_count;
|
||||
};
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test8(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP0]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test8(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[NARROW:%.*]] = tail call i32 @llvm.smax.i32(i32 [[COUNTED_BY_LOAD]], i32 0)
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext nneg i32 [[NARROW]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP0]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test8(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test8(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test8(struct annotated_sized_ptr *p, int index) {
|
||||
return __bdos(p->buf);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test9(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB9:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont8:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -4294967295, 4294967296) i64 @test9(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test9(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test9(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test9(struct annotated_sized_ptr *p, int index) {
|
||||
return __bdos(&p->buf[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 0, 2147483648) i64 @test10(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTCOUNTED_BY_LOAD:%.*]] = load i32, ptr [[DOTCOUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = zext i32 [[DOTCOUNTED_BY_LOAD]] to i64, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[DOTNOT:%.*]] = icmp ugt i64 [[IDXPROM]], [[TMP0]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: br i1 [[DOTNOT]], label [[HANDLER_OUT_OF_BOUNDS:%.*]], label [[CONT8:%.*]], !prof [[PROF15]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: handler.out_of_bounds:
|
||||
// SANITIZE-WITH-ATTR-NEXT: tail call void @__ubsan_handle_out_of_bounds_abort(ptr nonnull @[[GLOB11:[0-9]+]], i64 [[IDXPROM]]) #[[ATTR3]], !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: unreachable, !nosanitize [[META2]]
|
||||
// SANITIZE-WITH-ATTR: cont8:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[DOTCOUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[INDEX_SIZE:%.*]] = shl nuw nsw i64 [[IDXPROM]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[INDEX_SIZE]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = tail call i64 @llvm.smax.i64(i64 [[RESULT]], i64 0)
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -10737418236, 10737418240) i64 @test10(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[COUNT]], [[INDEX_SIZE]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test10(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test10(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test10(struct annotated_sized_ptr *p, int index) {
|
||||
return __bdos(&((unsigned int *) p->buf)[index]);
|
||||
}
|
||||
@ -194,6 +194,42 @@ size_t test2_bdos(struct annotated *p) {
|
||||
return __bdos(p->array);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -8589934592, 8589934589) i64 @test2_bdos_cast(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -8589934592, 8589934589) i64 @test2_bdos_cast(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i32 [[COUNTED_BY_LOAD]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = select i1 [[TMP0]], i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP1]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test2_bdos_cast(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test2_bdos_cast(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test2_bdos_cast(struct annotated *p) {
|
||||
return __bdos((char *)p->array);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test3(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i64 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
@ -265,6 +301,30 @@ size_t test3_bdos(struct annotated *p) {
|
||||
return __bdos(p);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test3_bdos_cast(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test3_bdos_cast(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR3]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3_bdos_cast(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test3_bdos_cast(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test3_bdos_cast(struct annotated *p) {
|
||||
return __bdos((char *)p);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test4(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]], i32 noundef [[FAM_IDX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
@ -469,6 +529,96 @@ size_t test4_bdos(struct annotated *p, int index) {
|
||||
return __bdos(&p->array[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -12884901886, 12884901885) i64 @test4_bdos_cast1(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[INDEX_SIZE]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -12884901886, 12884901885) i64 @test4_bdos_cast1(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[INDEX_SIZE:%.*]] = shl nsw i64 [[IDXPROM]], 1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[INDEX_SIZE]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos_cast1(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos_cast1(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test4_bdos_cast1(struct annotated *p, int index) {
|
||||
return __bdos(&((unsigned short *) ((char *)p->array))[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -10737418239, 10737418237) i64 @test4_bdos_cast2(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[IDXPROM]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -10737418239, 10737418237) i64 @test4_bdos_cast2(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 2
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[IDXPROM]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[RESULT]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos_cast2(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test4_bdos_cast2(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test4_bdos_cast2(struct annotated *p, int index) {
|
||||
return __bdos(&((char *) ((unsigned short *)p->array))[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local void @test5(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[P:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
@ -2059,6 +2209,54 @@ size_t test32_bdos(struct annotated_with_array *ptr, int index) {
|
||||
return __bdos(&ptr->flags[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -21474836134, 21474836817) i64 @test32_bdos_cast(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 336
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[FIELD_OFFSET:%.*]] = shl nsw i64 [[IDXPROM]], 1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[REASS_SUB:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[FIELD_OFFSET]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = add nsw i64 [[REASS_SUB]], 344
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[REASS_SUB]], -345
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// NO-SANITIZE-WITH-ATTR-LABEL: define dso_local range(i64 -21474836134, 21474836817) i64 @test32_bdos_cast(
|
||||
// NO-SANITIZE-WITH-ATTR-SAME: ptr noundef readonly captures(none) [[PTR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR2]] {
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[IDXPROM:%.*]] = sext i32 [[INDEX]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_GEP:%.*]] = getelementptr inbounds nuw i8, ptr [[PTR]], i64 336
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNTED_BY_LOAD:%.*]] = load i32, ptr [[COUNTED_BY_GEP]], align 4
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[COUNT:%.*]] = sext i32 [[COUNTED_BY_LOAD]] to i64
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[FLEXIBLE_ARRAY_MEMBER_SIZE:%.*]] = shl nsw i64 [[COUNT]], 3
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[FIELD_OFFSET:%.*]] = shl nsw i64 [[IDXPROM]], 1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[REASS_SUB:%.*]] = sub nsw i64 [[FLEXIBLE_ARRAY_MEMBER_SIZE]], [[FIELD_OFFSET]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[RESULT:%.*]] = add nsw i64 [[REASS_SUB]], 344
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP0:%.*]] = icmp sgt i64 [[REASS_SUB]], -345
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDEX]], -1
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP2:%.*]] = and i1 [[TMP1]], [[TMP0]]
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: [[TMP3:%.*]] = select i1 [[TMP2]], i64 [[RESULT]], i64 0
|
||||
// NO-SANITIZE-WITH-ATTR-NEXT: ret i64 [[TMP3]]
|
||||
//
|
||||
// SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test32_bdos_cast(
|
||||
// SANITIZE-WITHOUT-ATTR-SAME: ptr noundef [[PTR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
// NO-SANITIZE-WITHOUT-ATTR-LABEL: define dso_local i64 @test32_bdos_cast(
|
||||
// NO-SANITIZE-WITHOUT-ATTR-SAME: ptr noundef readnone [[PTR:%.*]], i32 noundef [[INDEX:%.*]]) local_unnamed_addr #[[ATTR1]] {
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: entry:
|
||||
// NO-SANITIZE-WITHOUT-ATTR-NEXT: ret i64 -1
|
||||
//
|
||||
size_t test32_bdos_cast(struct annotated_with_array *ptr, int index) {
|
||||
return __bdos(&((unsigned short *) ((char *) ptr->flags))[index]);
|
||||
}
|
||||
|
||||
// SANITIZE-WITH-ATTR-LABEL: define dso_local i64 @test33(
|
||||
// SANITIZE-WITH-ATTR-SAME: ptr noundef [[PTR:%.*]]) local_unnamed_addr #[[ATTR0]] {
|
||||
// SANITIZE-WITH-ATTR-NEXT: entry:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user