Skip to content

Commit 4a5aa1a

Browse files
committed
[BPF] Add preserve_access_index attribute for record definition
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 27c4eaa commit 4a5aa1a

14 files changed

+448
-8
lines changed

clang/include/clang/Basic/Attr.td

Lines changed: 7 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,12 @@ 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 Documentation = [BPFPreserveAccessIndexDocs];
1586+
}
1587+
15811588
def WebAssemblyImportModule : InheritableAttr,
15821589
TargetSpecificAttr<TargetWebAssembly> {
15831590
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/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10065,6 +10065,7 @@ def err_preserve_field_info_not_field : Error<
1006510065
"__builtin_preserve_field_info argument %0 not a field access">;
1006610066
def err_preserve_field_info_not_const: Error<
1006710067
"__builtin_preserve_field_info argument %0 not a constant">;
10068+
def err_preserve_access_index_wrong_type: Error<"%0 attribute only applies to %1">;
1006810069

1006910070
def err_bit_cast_non_trivially_copyable : Error<
1007010071
"__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;

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: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5700,6 +5700,59 @@ 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+
const BPFPreserveAccessIndexAttr &AL) {
5705+
// Add preserve_access_index attribute to all fields and inner records.
5706+
for (DeclContext::decl_iterator D = RD->decls_begin(), DEnd = RD->decls_end();
5707+
D != DEnd; ++D) {
5708+
// Any member or inner struct having attribute means done.
5709+
if (D->hasAttr<BPFPreserveAccessIndexAttr>())
5710+
return;
5711+
5712+
RecordDecl *Rec = dyn_cast<RecordDecl>(*D);
5713+
if (Rec) {
5714+
Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5715+
handleBPFPreserveAIRecord(S, Rec, AL);
5716+
} else {
5717+
D->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5718+
}
5719+
}
5720+
}
5721+
5722+
static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD,
5723+
const ParsedAttr &AL) {
5724+
// Add preserve_access_index attribute to all fields and inner records.
5725+
for (DeclContext::decl_iterator D = RD->decls_begin(), DEnd = RD->decls_end();
5726+
D != DEnd; ++D) {
5727+
RecordDecl *Rec = dyn_cast<RecordDecl>(*D);
5728+
if (Rec) {
5729+
// Inner record may have been processed.
5730+
if (!Rec->hasAttr<BPFPreserveAccessIndexAttr>()) {
5731+
Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5732+
handleBPFPreserveAIRecord(S, Rec, AL);
5733+
}
5734+
} else {
5735+
D->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5736+
}
5737+
}
5738+
}
5739+
5740+
static void handleBPFPreserveAccessIndexAttr(Sema &S, Decl *D,
5741+
const ParsedAttr &AL) {
5742+
RecordDecl *Rec = dyn_cast<RecordDecl>(D);
5743+
if (!Rec) {
5744+
S.Diag(D->getLocation(), diag::err_preserve_access_index_wrong_type)
5745+
<< "preserve_addess_index" << "struct or union type";
5746+
return;
5747+
}
5748+
5749+
if (!checkAttributeNumArgs(S, AL, 0))
5750+
return;
5751+
5752+
handleBPFPreserveAIRecord(S, Rec, AL);
5753+
Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
5754+
}
5755+
57035756
static void handleWebAssemblyImportModuleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
57045757
if (!isFunctionOrMethod(D)) {
57055758
S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -6576,6 +6629,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
65766629
case ParsedAttr::AT_AVRSignal:
65776630
handleAVRSignalAttr(S, D, AL);
65786631
break;
6632+
case ParsedAttr::AT_BPFPreserveAccessIndex:
6633+
handleBPFPreserveAccessIndexAttr(S, D, AL);
6634+
break;
65796635
case ParsedAttr::AT_WebAssemblyImportModule:
65806636
handleWebAssemblyImportModuleAttr(S, D, AL);
65816637
break;
@@ -7325,14 +7381,22 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
73257381
}
73267382
}
73277383

7328-
// Helper for delayed processing TransparentUnion attribute.
7384+
// Helper for delayed processing TransparentUnion or BPFPreserveAccessIndexAttr
7385+
// attribute.
73297386
void Sema::ProcessDeclAttributeDelayed(Decl *D,
73307387
const ParsedAttributesView &AttrList) {
73317388
for (const ParsedAttr &AL : AttrList)
73327389
if (AL.getKind() == ParsedAttr::AT_TransparentUnion) {
73337390
handleTransparentUnionAttr(*this, D, AL);
73347391
break;
73357392
}
7393+
7394+
// For BPFPreserveAccessIndexAttr, we want to populate the attributes
7395+
// to fields and inner records as well.
7396+
if (D->hasAttr<BPFPreserveAccessIndexAttr>()) {
7397+
handleBPFPreserveAIRecord(*this, cast<RecordDecl>(D),
7398+
*D->getAttr<BPFPreserveAccessIndexAttr>());
7399+
}
73367400
}
73377401

73387402
// 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

0 commit comments

Comments
 (0)