Skip to content

Commit 4e2ce22

Browse files
committed
[BPF] Add preserve_access_index attribute for record definition
This is a resubmission for the previous reverted commit 9434360 with the same subject. This commit fixed the segfault issue and addressed additional review comments. This patch introduced a new bpf specific attribute which can be added to struct or union definition. For example, struct s { ... } __attribute__((preserve_access_index)); union u { ... } __attribute__((preserve_access_index)); The goal is to simplify user codes for cases where preserve access index happens for certain struct/union, so user does not need to use clang __builtin_preserve_access_index for every members. The attribute has no effect if -g is not specified. When the attribute is specified and -g is specified, any member access defined by that structure or union, including array subscript access and inner records, will be preserved through __builtin_preserve_{array,struct,union}_access_index() IR intrinsics, which will enable relocation generation in bpf backend. The following is an example to illustrate the usage: -bash-4.4$ cat t.c #define __reloc__ __attribute__((preserve_access_index)) struct s1 { int c; } __reloc__; struct s2 { union { struct s1 b[3]; }; } __reloc__; struct s3 { struct s2 a; } __reloc__; int test(struct s3 *arg) { return arg->a.b[2].c; } -bash-4.4$ clang -target bpf -g -S -O2 t.c A relocation with access string "0:0:0:0:2:0" will be generated representing access offset of arg->a.b[2].c. forward declaration with attribute is also handled properly such that the attribute is copied and populated in real record definition. Differential Revision: https://reviews.llvm.org/D69759
1 parent e5f3760 commit 4e2ce22

13 files changed

+412
-8
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ class TargetArch<list<string> arches> : TargetSpec {
332332
}
333333
def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>;
334334
def TargetAVR : TargetArch<["avr"]>;
335+
def TargetBPF : TargetArch<["bpfel", "bpfeb"]>;
335336
def TargetMips32 : TargetArch<["mips", "mipsel"]>;
336337
def TargetAnyMips : TargetArch<["mips", "mipsel", "mips64", "mips64el"]>;
337338
def TargetMSP430 : TargetArch<["msp430"]>;
@@ -1578,6 +1579,13 @@ def AMDGPUNumVGPR : InheritableAttr {
15781579
let Subjects = SubjectList<[Function], ErrorDiag, "kernel functions">;
15791580
}
15801581

1582+
def BPFPreserveAccessIndex : InheritableAttr,
1583+
TargetSpecificAttr<TargetBPF> {
1584+
let Spellings = [Clang<"preserve_access_index">];
1585+
let Subjects = SubjectList<[Record], ErrorDiag>;
1586+
let Documentation = [BPFPreserveAccessIndexDocs];
1587+
}
1588+
15811589
def WebAssemblyImportModule : InheritableAttr,
15821590
TargetSpecificAttr<TargetWebAssembly> {
15831591
let Spellings = [Clang<"import_module">];

clang/include/clang/Basic/AttrDocs.td

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,6 +1634,17 @@ The semantics are as follows:
16341634
}];
16351635
}
16361636

1637+
def BPFPreserveAccessIndexDocs : Documentation {
1638+
let Category = DocCatFunction;
1639+
let Content = [{
1640+
Clang supports the ``__attribute__((preserve_access_index))``
1641+
attribute for the BPF target. This attribute may be attached to a
1642+
struct or union declaration, where if -g is specified, it enables
1643+
preserving struct or union member access debuginfo indicies of this
1644+
struct or union, similar to clang ``__builtin_preserve_acceess_index()``.
1645+
}];
1646+
}
1647+
16371648
def MipsInterruptDocs : Documentation {
16381649
let Category = DocCatFunction;
16391650
let Heading = "interrupt (MIPS)";

clang/lib/CodeGen/CGExpr.cpp

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3402,11 +3402,67 @@ static QualType getFixedSizeElementType(const ASTContext &ctx,
34023402
return eltType;
34033403
}
34043404

3405+
/// Given an array base, check whether its member access belongs to a record
3406+
/// with preserve_access_index attribute or not.
3407+
static bool IsPreserveAIArrayBase(CodeGenFunction &CGF, const Expr *ArrayBase) {
3408+
if (!ArrayBase || !CGF.getDebugInfo())
3409+
return false;
3410+
3411+
const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(ArrayBase);
3412+
if (!ImplicitCast)
3413+
return false;
3414+
3415+
// Only support base as either a MemberExpr or DeclRefExpr.
3416+
// DeclRefExpr to cover cases like:
3417+
// struct s { int a; int b[10]; };
3418+
// struct s *p;
3419+
// p[1].a
3420+
// p[1] will generate a DeclRefExpr and p[1].a is a MemberExpr.
3421+
// p->b[5] is a MemberExpr example.
3422+
const Expr *E = ImplicitCast->getSubExpr();
3423+
const auto *MemberCast = dyn_cast<MemberExpr>(E);
3424+
if (MemberCast)
3425+
return MemberCast->getMemberDecl()->hasAttr<BPFPreserveAccessIndexAttr>();
3426+
3427+
const auto *DeclRefCast = dyn_cast<DeclRefExpr>(E);
3428+
if (DeclRefCast) {
3429+
const VarDecl *VarDef = dyn_cast<VarDecl>(DeclRefCast->getDecl());
3430+
if (!VarDef)
3431+
return false;
3432+
3433+
const auto *PtrT = dyn_cast<PointerType>(VarDef->getType().getTypePtr());
3434+
if (!PtrT)
3435+
return false;
3436+
const auto *PointeeT = PtrT->getPointeeType().getTypePtr();
3437+
3438+
// Peel off typedef's
3439+
const auto *TypedefT = dyn_cast<TypedefType>(PointeeT);
3440+
while (TypedefT) {
3441+
PointeeT = TypedefT->desugar().getTypePtr();
3442+
TypedefT = dyn_cast<TypedefType>(PointeeT);
3443+
}
3444+
3445+
// Not a typedef any more, it should be an elaborated type.
3446+
const auto ElaborateT = dyn_cast<ElaboratedType>(PointeeT);
3447+
if (!ElaborateT)
3448+
return false;
3449+
3450+
const auto *RecT = dyn_cast<RecordType>(ElaborateT->desugar().getTypePtr());
3451+
if (!RecT)
3452+
return false;
3453+
3454+
return RecT->getDecl()->hasAttr<BPFPreserveAccessIndexAttr>();
3455+
}
3456+
3457+
return false;
3458+
}
3459+
34053460
static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
34063461
ArrayRef<llvm::Value *> indices,
34073462
QualType eltType, bool inbounds,
34083463
bool signedIndices, SourceLocation loc,
34093464
QualType *arrayType = nullptr,
3465+
const Expr *Base = nullptr,
34103466
const llvm::Twine &name = "arrayidx") {
34113467
// All the indices except that last must be zero.
34123468
#ifndef NDEBUG
@@ -3428,7 +3484,8 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
34283484

34293485
llvm::Value *eltPtr;
34303486
auto LastIndex = dyn_cast<llvm::ConstantInt>(indices.back());
3431-
if (!CGF.IsInPreservedAIRegion || !LastIndex) {
3487+
if (!LastIndex ||
3488+
(!CGF.IsInPreservedAIRegion && !IsPreserveAIArrayBase(CGF, Base))) {
34323489
eltPtr = emitArraySubscriptGEP(
34333490
CGF, addr.getPointer(), indices, inbounds, signedIndices,
34343491
loc, name);
@@ -3582,7 +3639,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
35823639
Addr = emitArraySubscriptGEP(
35833640
*this, ArrayLV.getAddress(), {CGM.getSize(CharUnits::Zero()), Idx},
35843641
E->getType(), !getLangOpts().isSignedOverflowDefined(), SignedIndices,
3585-
E->getExprLoc(), &arrayType);
3642+
E->getExprLoc(), &arrayType, E->getBase());
35863643
EltBaseInfo = ArrayLV.getBaseInfo();
35873644
EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType());
35883645
} else {
@@ -3592,7 +3649,8 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
35923649
QualType ptrType = E->getBase()->getType();
35933650
Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(),
35943651
!getLangOpts().isSignedOverflowDefined(),
3595-
SignedIndices, E->getExprLoc(), &ptrType);
3652+
SignedIndices, E->getExprLoc(), &ptrType,
3653+
E->getBase());
35963654
}
35973655

35983656
LValue LV = MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo);
@@ -3993,12 +4051,13 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
39934051
const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
39944052
Address Addr = base.getAddress();
39954053
unsigned Idx = RL.getLLVMFieldNo(field);
3996-
if (!IsInPreservedAIRegion) {
4054+
const RecordDecl *rec = field->getParent();
4055+
if (!IsInPreservedAIRegion &&
4056+
(!getDebugInfo() || !rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
39974057
if (Idx != 0)
39984058
// For structs, we GEP to the field that the record layout suggests.
39994059
Addr = Builder.CreateStructGEP(Addr, Idx, field->getName());
40004060
} else {
4001-
const RecordDecl *rec = field->getParent();
40024061
llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
40034062
getContext().getRecordType(rec), rec->getLocation());
40044063
Addr = Builder.CreatePreserveStructAccessIndex(Addr, Idx,
@@ -4081,7 +4140,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
40814140
addr = Address(Builder.CreateLaunderInvariantGroup(addr.getPointer()),
40824141
addr.getAlignment());
40834142

4084-
if (IsInPreservedAIRegion) {
4143+
if (IsInPreservedAIRegion ||
4144+
(getDebugInfo() && rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
40854145
// Remember the original union field index
40864146
llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
40874147
getContext().getRecordType(rec), rec->getLocation());
@@ -4095,7 +4155,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
40954155
addr = Builder.CreateElementBitCast(
40964156
addr, CGM.getTypes().ConvertTypeForMem(FieldType), field->getName());
40974157
} else {
4098-
if (!IsInPreservedAIRegion)
4158+
if (!IsInPreservedAIRegion &&
4159+
(!getDebugInfo() || !rec->hasAttr<BPFPreserveAccessIndexAttr>()))
40994160
// For structs, we GEP to the field that the record layout suggests.
41004161
addr = emitAddrOfFieldStorage(*this, addr, field);
41014162
else

clang/lib/Sema/SemaDeclAttr.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5700,6 +5700,25 @@ static void handleAVRSignalAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
57005700
handleSimpleAttribute<AVRSignalAttr>(S, D, AL);
57015701
}
57025702

5703+
static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD) {
5704+
// Add preserve_access_index attribute to all fields and inner records.
5705+
for (auto D : RD->decls()) {
5706+
if (D->hasAttr<BPFPreserveAccessIndexAttr>())
5707+
continue;
5708+
5709+
D->addAttr(BPFPreserveAccessIndexAttr::CreateImplicit(S.Context));
5710+
if (auto *Rec = dyn_cast<RecordDecl>(D))
5711+
handleBPFPreserveAIRecord(S, Rec);
5712+
}
5713+
}
5714+
5715+
static void handleBPFPreserveAccessIndexAttr(Sema &S, Decl *D,
5716+
const ParsedAttr &AL) {
5717+
auto *Rec = cast<RecordDecl>(D);
5718+
handleBPFPreserveAIRecord(S, Rec);
5719+
Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5720+
}
5721+
57035722
static void handleWebAssemblyImportModuleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
57045723
if (!isFunctionOrMethod(D)) {
57055724
S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -6576,6 +6595,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
65766595
case ParsedAttr::AT_AVRSignal:
65776596
handleAVRSignalAttr(S, D, AL);
65786597
break;
6598+
case ParsedAttr::AT_BPFPreserveAccessIndex:
6599+
handleBPFPreserveAccessIndexAttr(S, D, AL);
6600+
break;
65796601
case ParsedAttr::AT_WebAssemblyImportModule:
65806602
handleWebAssemblyImportModuleAttr(S, D, AL);
65816603
break;
@@ -7325,14 +7347,20 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
73257347
}
73267348
}
73277349

7328-
// Helper for delayed processing TransparentUnion attribute.
7350+
// Helper for delayed processing TransparentUnion or BPFPreserveAccessIndexAttr
7351+
// attribute.
73297352
void Sema::ProcessDeclAttributeDelayed(Decl *D,
73307353
const ParsedAttributesView &AttrList) {
73317354
for (const ParsedAttr &AL : AttrList)
73327355
if (AL.getKind() == ParsedAttr::AT_TransparentUnion) {
73337356
handleTransparentUnionAttr(*this, D, AL);
73347357
break;
73357358
}
7359+
7360+
// For BPFPreserveAccessIndexAttr, we want to populate the attributes
7361+
// to fields and inner records as well.
7362+
if (D && D->hasAttr<BPFPreserveAccessIndexAttr>())
7363+
handleBPFPreserveAIRecord(*this, cast<RecordDecl>(D));
73367364
}
73377365

73387366
// Annotation attributes are the only attributes allowed after an access
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// REQUIRES: bpf-registered-target
2+
// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
3+
4+
#define __reloc__ __attribute__((preserve_access_index))
5+
6+
// test simple member access and initial struct with non-zero stride access
7+
struct s1 {
8+
int a;
9+
union {
10+
int b;
11+
int c;
12+
};
13+
} __reloc__;
14+
typedef struct s1 __s1;
15+
16+
int test(__s1 *arg) {
17+
return arg->a + arg[1].b;
18+
}
19+
20+
// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)
21+
// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 1)
22+
// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1)
23+
// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// REQUIRES: bpf-registered-target
2+
// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
3+
4+
#define __reloc__ __attribute__((preserve_access_index))
5+
6+
// test array access
7+
struct s1 {
8+
int a[3];
9+
union {
10+
int b;
11+
int c[4];
12+
};
13+
} __reloc__;
14+
typedef struct s1 __s1;
15+
16+
int test(__s1 *arg) {
17+
return arg->a[2] + arg->c[2];
18+
}
19+
20+
// CHECK: call [3 x i32]* @llvm.preserve.struct.access.index.p0a3i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)
21+
// CHECK: call i32* @llvm.preserve.array.access.index.p0i32.p0a3i32([3 x i32]* %{{[0-9a-z]+}}, i32 1, i32 2)
22+
// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1)
23+
// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 1)
24+
// CHECK: call i32* @llvm.preserve.array.access.index.p0i32.p0a4i32([4 x i32]* %{{[0-9a-z]+}}, i32 1, i32 2)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// REQUIRES: bpf-registered-target
2+
// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
3+
4+
#define __reloc__ __attribute__((preserve_access_index))
5+
6+
// chain of records, all with attributes
7+
struct s1 {
8+
int c;
9+
} __reloc__;
10+
typedef struct s1 __s1;
11+
12+
struct s2 {
13+
union {
14+
__s1 b[3];
15+
};
16+
} __reloc__;
17+
typedef struct s2 __s2;
18+
19+
struct s3 {
20+
__s2 a;
21+
} __reloc__;
22+
typedef struct s3 __s3;
23+
24+
int test(__s3 *arg) {
25+
return arg->a.b[2].c;
26+
}
27+
28+
// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
29+
// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0)
30+
// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
31+
// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0a3s_struct.s1s([3 x %struct.s1]* %{{[0-9a-z]+}}, i32 1, i32 2)
32+
// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// REQUIRES: bpf-registered-target
2+
// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
3+
4+
#define __reloc__ __attribute__((preserve_access_index))
5+
6+
// chain of records, some do not have attributes.
7+
struct s1 {
8+
int c;
9+
};
10+
typedef struct s1 __s1;
11+
12+
struct s2 {
13+
union {
14+
__s1 b[3];
15+
};
16+
} __reloc__;
17+
typedef struct s2 __s2;
18+
19+
struct s3 {
20+
__s2 a;
21+
};
22+
typedef struct s3 __s3;
23+
24+
int test(__s3 *arg) {
25+
return arg->a.b[2].c;
26+
}
27+
28+
// CHECK: define dso_local i32 @test
29+
// CHECK-NOT: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s
30+
// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %a, i32 0, i32 0)
31+
// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %1, i32 0)
32+
// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0a3s_struct.s1s([3 x %struct.s1]* %b, i32 1, i32 2)
33+
// CHECK-NOT: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// REQUIRES: bpf-registered-target
2+
// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
3+
4+
#define __reloc__ __attribute__((preserve_access_index))
5+
6+
// chain of records, attribute may be in inner record.
7+
struct s1 {
8+
int c;
9+
} __reloc__;
10+
typedef struct s1 __s1;
11+
12+
struct s2 {
13+
union {
14+
__s1 b[3];
15+
} __reloc__;
16+
};
17+
typedef struct s2 __s2;
18+
19+
struct s3 {
20+
__s2 a;
21+
} __reloc__;
22+
typedef struct s3 __s3;
23+
24+
int test(__s3 *arg) {
25+
return arg->a.b[2].c;
26+
}
27+
28+
// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
29+
// CHECK-NOT: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s
30+
// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
31+
// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0a3s_struct.s1s([3 x %struct.s1]* %{{[0-9a-z]+}}, i32 1, i32 2)
32+
// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

0 commit comments

Comments
 (0)