Skip to content

Commit bc09ec6

Browse files
authored
[CodeGen] Revamp counted_by calculations (#70606)
Break down the counted_by calculations so that they correctly handle anonymous structs, which are specified internally as IndirectFieldDecls. Improves the calculation of __bdos on a different field member in the struct. And also improves support for __bdos in an index into the FAM. If the index is further out than the length of the FAM, then we return __bdos's "can't determine the size" value (zero or negative one, depending on type). Also simplify the code to use helper methods to get the field referenced by counted_by and the flexible array member itself, which also had some issues with FAMs in sub-structs.
1 parent 11f52f7 commit bc09ec6

File tree

4 files changed

+797
-127
lines changed

4 files changed

+797
-127
lines changed

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 140 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/AST/Attr.h"
2626
#include "clang/AST/Decl.h"
2727
#include "clang/AST/OSLog.h"
28+
#include "clang/AST/OperationKinds.h"
2829
#include "clang/Basic/TargetBuiltins.h"
2930
#include "clang/Basic/TargetInfo.h"
3031
#include "clang/Basic/TargetOptions.h"
@@ -858,63 +859,155 @@ CodeGenFunction::emitBuiltinObjectSize(const Expr *E, unsigned Type,
858859
}
859860
}
860861

862+
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
863+
// evaluate E for side-effects. In either case, we shouldn't lower to
864+
// @llvm.objectsize.
865+
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
866+
return getDefaultBuiltinObjectSizeResult(Type, ResType);
867+
861868
if (IsDynamic) {
862-
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
863-
getLangOpts().getStrictFlexArraysLevel();
869+
// The code generated here calculates the size of a struct with a flexible
870+
// array member that uses the counted_by attribute. There are two instances
871+
// we handle:
872+
//
873+
// struct s {
874+
// unsigned long flags;
875+
// int count;
876+
// int array[] __attribute__((counted_by(count)));
877+
// }
878+
//
879+
// 1) bdos of the flexible array itself:
880+
//
881+
// __builtin_dynamic_object_size(p->array, 1) ==
882+
// p->count * sizeof(*p->array)
883+
//
884+
// 2) bdos of a pointer into the flexible array:
885+
//
886+
// __builtin_dynamic_object_size(&p->array[42], 1) ==
887+
// (p->count - 42) * sizeof(*p->array)
888+
//
889+
// 2) bdos of the whole struct, including the flexible array:
890+
//
891+
// __builtin_dynamic_object_size(p, 1) ==
892+
// max(sizeof(struct s),
893+
// offsetof(struct s, array) + p->count * sizeof(*p->array))
894+
//
864895
const Expr *Base = E->IgnoreParenImpCasts();
865-
866-
if (FieldDecl *FD = FindCountedByField(Base, StrictFlexArraysLevel)) {
867-
const auto *ME = dyn_cast<MemberExpr>(Base);
868-
llvm::Value *ObjectSize = nullptr;
869-
870-
if (!ME) {
871-
const auto *DRE = dyn_cast<DeclRefExpr>(Base);
872-
ValueDecl *VD = nullptr;
873-
874-
ObjectSize = ConstantInt::get(
875-
ResType,
876-
getContext().getTypeSize(DRE->getType()->getPointeeType()) / 8,
877-
true);
878-
879-
if (auto *RD = DRE->getType()->getPointeeType()->getAsRecordDecl())
880-
VD = RD->getLastField();
881-
882-
Expr *ICE = ImplicitCastExpr::Create(
883-
getContext(), DRE->getType(), CK_LValueToRValue,
884-
const_cast<Expr *>(cast<Expr>(DRE)), nullptr, VK_PRValue,
885-
FPOptionsOverride());
886-
ME = MemberExpr::CreateImplicit(getContext(), ICE, true, VD,
887-
VD->getType(), VK_LValue, OK_Ordinary);
896+
const Expr *Idx = nullptr;
897+
if (const auto *UO = dyn_cast<UnaryOperator>(Base);
898+
UO && UO->getOpcode() == UO_AddrOf) {
899+
if (const auto *ASE =
900+
dyn_cast<ArraySubscriptExpr>(UO->getSubExpr()->IgnoreParens())) {
901+
Base = ASE->getBase();
902+
Idx = ASE->getIdx()->IgnoreParenImpCasts();
903+
904+
if (const auto *IL = dyn_cast<IntegerLiteral>(Idx);
905+
IL && !IL->getValue().getSExtValue())
906+
Idx = nullptr;
888907
}
908+
}
889909

890-
// At this point, we know that \p ME is a flexible array member.
891-
const auto *ArrayTy = getContext().getAsArrayType(ME->getType());
892-
unsigned Size = getContext().getTypeSize(ArrayTy->getElementType());
893-
894-
llvm::Value *CountField =
895-
EmitAnyExprToTemp(MemberExpr::CreateImplicit(
896-
getContext(), const_cast<Expr *>(ME->getBase()),
897-
ME->isArrow(), FD, FD->getType(), VK_LValue,
898-
OK_Ordinary))
899-
.getScalarVal();
910+
if (const ValueDecl *CountedByFD = FindCountedByField(Base)) {
911+
bool IsSigned = CountedByFD->getType()->isSignedIntegerType();
912+
const RecordDecl *OuterRD =
913+
CountedByFD->getDeclContext()->getOuterLexicalRecordContext();
914+
ASTContext &Ctx = getContext();
915+
916+
// Load the counted_by field.
917+
const Expr *CountedByExpr = BuildCountedByFieldExpr(Base, CountedByFD);
918+
Value *CountedByInst = EmitAnyExprToTemp(CountedByExpr).getScalarVal();
919+
llvm::Type *CountedByTy = CountedByInst->getType();
920+
921+
if (Idx) {
922+
// There's an index into the array. Remove it from the count.
923+
bool IdxSigned = Idx->getType()->isSignedIntegerType();
924+
Value *IdxInst = EmitAnyExprToTemp(Idx).getScalarVal();
925+
IdxInst = IdxSigned ? Builder.CreateSExtOrTrunc(IdxInst, CountedByTy)
926+
: Builder.CreateZExtOrTrunc(IdxInst, CountedByTy);
927+
928+
// If the index is negative, don't subtract it from the counted_by
929+
// value. The pointer is pointing to something before the FAM.
930+
IdxInst = Builder.CreateNeg(IdxInst, "", !IdxSigned, IdxSigned);
931+
CountedByInst =
932+
Builder.CreateAdd(CountedByInst, IdxInst, "", !IsSigned, IsSigned);
933+
}
900934

901-
llvm::Value *Mul = Builder.CreateMul(
902-
CountField, llvm::ConstantInt::get(CountField->getType(), Size / 8));
903-
Mul = Builder.CreateZExtOrTrunc(Mul, ResType);
935+
// Get the size of the flexible array member's base type.
936+
const ValueDecl *FAMDecl = nullptr;
937+
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
938+
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
939+
getLangOpts().getStrictFlexArraysLevel();
940+
if (const ValueDecl *MD = ME->getMemberDecl();
941+
MD && Decl::isFlexibleArrayMemberLike(
942+
Ctx, MD, MD->getType(), StrictFlexArraysLevel,
943+
/*IgnoreTemplateOrMacroSubstitution=*/true))
944+
// Base is referencing the FAM itself.
945+
FAMDecl = MD;
946+
}
904947

905-
if (ObjectSize)
906-
return Builder.CreateAdd(ObjectSize, Mul);
948+
if (!FAMDecl)
949+
FAMDecl = FindFlexibleArrayMemberField(Ctx, OuterRD);
950+
951+
assert(FAMDecl && "Can't find the flexible array member field");
952+
953+
const ArrayType *ArrayTy = Ctx.getAsArrayType(FAMDecl->getType());
954+
CharUnits Size = Ctx.getTypeSizeInChars(ArrayTy->getElementType());
955+
llvm::Constant *ElemSize =
956+
llvm::ConstantInt::get(CountedByTy, Size.getQuantity(), IsSigned);
957+
958+
// Calculate how large the flexible array member is in bytes.
959+
Value *FAMSize =
960+
Builder.CreateMul(CountedByInst, ElemSize, "", !IsSigned, IsSigned);
961+
FAMSize = IsSigned ? Builder.CreateSExtOrTrunc(FAMSize, ResType)
962+
: Builder.CreateZExtOrTrunc(FAMSize, ResType);
963+
Value *Res = FAMSize;
964+
965+
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
966+
// The whole struct is specificed in the __bdos.
967+
const ASTRecordLayout &Layout = Ctx.getASTRecordLayout(OuterRD);
968+
969+
// Get the offset of the FAM.
970+
CharUnits Offset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(FAMDecl));
971+
llvm::Constant *FAMOffset =
972+
ConstantInt::get(ResType, Offset.getQuantity(), IsSigned);
973+
974+
// max(sizeof(struct s),
975+
// offsetof(struct s, array) + p->count * sizeof(*p->array))
976+
Value *OffsetAndFAMSize =
977+
Builder.CreateAdd(FAMOffset, Res, "", !IsSigned, IsSigned);
978+
979+
// Get the full size of the struct.
980+
llvm::Constant *SizeofStruct =
981+
ConstantInt::get(ResType, Layout.getSize().getQuantity(), IsSigned);
982+
983+
Res = IsSigned
984+
? Builder.CreateBinaryIntrinsic(
985+
llvm::Intrinsic::smax, OffsetAndFAMSize, SizeofStruct)
986+
: Builder.CreateBinaryIntrinsic(
987+
llvm::Intrinsic::umax, OffsetAndFAMSize, SizeofStruct);
988+
} else if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
989+
// Pointing to a place before the FAM. Add the difference to the FAM's
990+
// size.
991+
if (const ValueDecl *MD = ME->getMemberDecl(); MD != FAMDecl) {
992+
CharUnits Offset = Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(MD));
993+
CharUnits FAMOffset =
994+
Ctx.toCharUnitsFromBits(Ctx.getFieldOffset(FAMDecl));
995+
996+
Res = Builder.CreateAdd(
997+
Res, ConstantInt::get(ResType, FAMOffset.getQuantity() -
998+
Offset.getQuantity()));
999+
}
1000+
}
9071001

908-
return Mul;
1002+
// A negative 'FAMSize' means that the index was greater than the count,
1003+
// or an improperly set count field. Return -1 (for types 0 and 1) or 0
1004+
// (for types 2 and 3).
1005+
return Builder.CreateSelect(
1006+
Builder.CreateIsNeg(FAMSize),
1007+
getDefaultBuiltinObjectSizeResult(Type, ResType), Res);
9091008
}
9101009
}
9111010

912-
// LLVM can't handle Type=3 appropriately, and __builtin_object_size shouldn't
913-
// evaluate E for side-effects. In either case, we shouldn't lower to
914-
// @llvm.objectsize.
915-
if (Type == 3 || (!EmittedE && E->HasSideEffects(getContext())))
916-
return getDefaultBuiltinObjectSizeResult(Type, ResType);
917-
9181011
Value *Ptr = EmittedE ? EmittedE : EmitScalarExpr(E);
9191012
assert(Ptr->getType()->isPointerTy() &&
9201013
"Non-pointer passed to __builtin_object_size?");

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 97 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -938,14 +938,10 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
938938
// Ignore pass_object_size here. It's not applicable on decayed pointers.
939939
}
940940

941-
if (FieldDecl *FD = CGF.FindCountedByField(Base, StrictFlexArraysLevel)) {
942-
const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr());
941+
if (const ValueDecl *VD = CGF.FindCountedByField(Base)) {
943942
IndexedType = Base->getType();
944-
return CGF
945-
.EmitAnyExprToTemp(MemberExpr::CreateImplicit(
946-
CGF.getContext(), const_cast<Expr *>(ME->getBase()),
947-
ME->isArrow(), FD, FD->getType(), VK_LValue, OK_Ordinary))
948-
.getScalarVal();
943+
const Expr *E = CGF.BuildCountedByFieldExpr(Base, VD);
944+
return CGF.EmitAnyExprToTemp(E).getScalarVal();
949945
}
950946
}
951947

@@ -960,46 +956,115 @@ static llvm::Value *getArrayIndexingBound(CodeGenFunction &CGF,
960956
return nullptr;
961957
}
962958

963-
FieldDecl *CodeGenFunction::FindCountedByField(
964-
const Expr *Base,
965-
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel) {
966-
const ValueDecl *VD = nullptr;
959+
const Expr *
960+
CodeGenFunction::BuildCountedByFieldExpr(const Expr *Base,
961+
const ValueDecl *CountedByVD) {
962+
// Find the outer struct expr (i.e. p in p->a.b.c.d).
963+
Expr *CountedByExpr = const_cast<Expr *>(Base)->IgnoreParenImpCasts();
964+
965+
// Work our way up the expression until we reach the DeclRefExpr.
966+
while (!isa<DeclRefExpr>(CountedByExpr))
967+
if (const auto *ME = dyn_cast<MemberExpr>(CountedByExpr))
968+
CountedByExpr = ME->getBase()->IgnoreParenImpCasts();
969+
970+
// Add back an implicit cast to create the required pr-value.
971+
CountedByExpr = ImplicitCastExpr::Create(
972+
getContext(), CountedByExpr->getType(), CK_LValueToRValue, CountedByExpr,
973+
nullptr, VK_PRValue, FPOptionsOverride());
974+
975+
if (const auto *IFD = dyn_cast<IndirectFieldDecl>(CountedByVD)) {
976+
// The counted_by field is inside an anonymous struct / union. The
977+
// IndirectFieldDecl has the correct order of FieldDecls to build this
978+
// easily. (Yay!)
979+
for (NamedDecl *ND : IFD->chain()) {
980+
auto *VD = cast<ValueDecl>(ND);
981+
CountedByExpr =
982+
MemberExpr::CreateImplicit(getContext(), CountedByExpr,
983+
CountedByExpr->getType()->isPointerType(),
984+
VD, VD->getType(), VK_LValue, OK_Ordinary);
985+
}
986+
} else {
987+
CountedByExpr = MemberExpr::CreateImplicit(
988+
getContext(), const_cast<Expr *>(CountedByExpr),
989+
CountedByExpr->getType()->isPointerType(),
990+
const_cast<ValueDecl *>(CountedByVD), CountedByVD->getType(), VK_LValue,
991+
OK_Ordinary);
992+
}
967993

968-
Base = Base->IgnoreParenImpCasts();
994+
return CountedByExpr;
995+
}
996+
997+
const ValueDecl *
998+
CodeGenFunction::FindFlexibleArrayMemberField(ASTContext &Ctx,
999+
const RecordDecl *RD) {
1000+
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
1001+
getLangOpts().getStrictFlexArraysLevel();
1002+
1003+
for (const Decl *D : RD->decls()) {
1004+
if (const auto *VD = dyn_cast<ValueDecl>(D);
1005+
VD && Decl::isFlexibleArrayMemberLike(
1006+
Ctx, VD, VD->getType(), StrictFlexArraysLevel,
1007+
/*IgnoreTemplateOrMacroSubstitution=*/true))
1008+
return VD;
1009+
1010+
if (const auto *Record = dyn_cast<RecordDecl>(D))
1011+
if (const ValueDecl *VD = FindFlexibleArrayMemberField(Ctx, Record))
1012+
return VD;
1013+
}
1014+
1015+
return nullptr;
1016+
}
1017+
1018+
const ValueDecl *CodeGenFunction::FindCountedByField(const Expr *Base) {
1019+
ASTContext &Ctx = getContext();
1020+
const RecordDecl *OuterRD = nullptr;
1021+
const FieldDecl *FD = nullptr;
9691022

970-
if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
971-
VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
972-
} else if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
973-
// Pointing to the full structure.
974-
VD = dyn_cast<ValueDecl>(DRE->getDecl());
1023+
Base = Base->IgnoreParenImpCasts();
9751024

976-
QualType Ty = VD->getType();
1025+
// Get the outer-most lexical RecordDecl.
1026+
if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) {
1027+
QualType Ty = DRE->getDecl()->getType();
9771028
if (Ty->isPointerType())
9781029
Ty = Ty->getPointeeType();
9791030

9801031
if (const auto *RD = Ty->getAsRecordDecl())
981-
VD = RD->getLastField();
982-
} else if (const auto *CE = dyn_cast<CastExpr>(Base)) {
983-
if (const auto *ME = dyn_cast<MemberExpr>(CE->getSubExpr()))
984-
VD = dyn_cast<ValueDecl>(ME->getMemberDecl());
1032+
OuterRD = RD->getOuterLexicalRecordContext();
1033+
} else if (const auto *ME = dyn_cast<MemberExpr>(Base)) {
1034+
if (const ValueDecl *MD = ME->getMemberDecl()) {
1035+
OuterRD = MD->getDeclContext()->getOuterLexicalRecordContext();
1036+
1037+
const LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel =
1038+
getLangOpts().getStrictFlexArraysLevel();
1039+
if (Decl::isFlexibleArrayMemberLike(
1040+
Ctx, MD, MD->getType(), StrictFlexArraysLevel,
1041+
/*IgnoreTemplateOrMacroSubstitution=*/true))
1042+
// Base is referencing the FAM itself.
1043+
FD = dyn_cast<FieldDecl>(MD);
1044+
}
9851045
}
9861046

987-
const auto *FD = dyn_cast_if_present<FieldDecl>(VD);
988-
if (!FD || !FD->getParent() ||
989-
!Decl::isFlexibleArrayMemberLike(getContext(), FD, FD->getType(),
990-
StrictFlexArraysLevel, true))
1047+
if (!OuterRD)
9911048
return nullptr;
9921049

1050+
if (!FD) {
1051+
const ValueDecl *VD = FindFlexibleArrayMemberField(Ctx, OuterRD);
1052+
FD = dyn_cast_if_present<FieldDecl>(VD);
1053+
if (!FD)
1054+
return nullptr;
1055+
}
1056+
9931057
const auto *CBA = FD->getAttr<CountedByAttr>();
9941058
if (!CBA)
9951059
return nullptr;
9961060

997-
StringRef FieldName = CBA->getCountedByField()->getName();
998-
auto It =
999-
llvm::find_if(FD->getParent()->fields(), [&](const FieldDecl *Field) {
1000-
return FieldName == Field->getName();
1001-
});
1002-
return It != FD->getParent()->field_end() ? *It : nullptr;
1061+
DeclarationName DName(CBA->getCountedByField());
1062+
DeclContext::lookup_result Lookup = OuterRD->lookup(DName);
1063+
1064+
if (Lookup.empty())
1065+
return nullptr;
1066+
1067+
return dyn_cast<ValueDecl>(Lookup.front());
10031068
}
10041069

10051070
void CodeGenFunction::EmitBoundsCheck(const Expr *E, const Expr *Base,

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,11 +3022,18 @@ class CodeGenFunction : public CodeGenTypeCache {
30223022
void EmitBoundsCheck(const Expr *E, const Expr *Base, llvm::Value *Index,
30233023
QualType IndexType, bool Accessed);
30243024

3025+
// Find a struct's flexible array member. It may be embedded inside multiple
3026+
// sub-structs, but must still be the last field.
3027+
const ValueDecl *FindFlexibleArrayMemberField(ASTContext &Ctx,
3028+
const RecordDecl *RD);
3029+
30253030
/// Find the FieldDecl specified in a FAM's "counted_by" attribute. Returns
30263031
/// \p nullptr if either the attribute or the field doesn't exist.
3027-
FieldDecl *FindCountedByField(
3028-
const Expr *Base,
3029-
LangOptions::StrictFlexArraysLevelKind StrictFlexArraysLevel);
3032+
const ValueDecl *FindCountedByField(const Expr *Base);
3033+
3034+
/// Build an expression accessing the "counted_by" field.
3035+
const Expr *BuildCountedByFieldExpr(const Expr *Base,
3036+
const ValueDecl *CountedByVD);
30303037

30313038
llvm::Value *EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,
30323039
bool isInc, bool isPre);

0 commit comments

Comments
 (0)