[clang] 4a5aa1a - [BPF] Add preserve_access_index attribute for record definition

Yonghong Song via cfe-commits cfe-commits at lists.llvm.org
Sat Nov 9 08:18:23 PST 2019


Author: Yonghong Song
Date: 2019-11-09T08:17:12-08:00
New Revision: 4a5aa1a7bf8b1714b817ede8e09cd28c0784228a

URL: https://github.com/llvm/llvm-project/commit/4a5aa1a7bf8b1714b817ede8e09cd28c0784228a
DIFF: https://github.com/llvm/llvm-project/commit/4a5aa1a7bf8b1714b817ede8e09cd28c0784228a.diff

LOG: [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

Added: 
    clang/test/CodeGen/bpf-attr-preserve-access-index-1.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-2.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-3.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-4.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-5.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-6.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-7.c
    clang/test/CodeGen/bpf-attr-preserve-access-index-8.c
    clang/test/Sema/bpf-attr-preserve-access-index.c

Modified: 
    clang/include/clang/Basic/Attr.td
    clang/include/clang/Basic/AttrDocs.td
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/CodeGen/CGExpr.cpp
    clang/lib/Sema/SemaDeclAttr.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 0d25e775e2af..aef02b5a8e96 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -332,6 +332,7 @@ class TargetArch<list<string> arches> : TargetSpec {
 }
 def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>;
 def TargetAVR : TargetArch<["avr"]>;
+def TargetBPF : TargetArch<["bpfel", "bpfeb"]>;
 def TargetMips32 : TargetArch<["mips", "mipsel"]>;
 def TargetAnyMips : TargetArch<["mips", "mipsel", "mips64", "mips64el"]>;
 def TargetMSP430 : TargetArch<["msp430"]>;
@@ -1578,6 +1579,12 @@ def AMDGPUNumVGPR : InheritableAttr {
   let Subjects = SubjectList<[Function], ErrorDiag, "kernel functions">;
 }
 
+def BPFPreserveAccessIndex : InheritableAttr,
+                             TargetSpecificAttr<TargetBPF>  {
+  let Spellings = [Clang<"preserve_access_index">];
+  let Documentation = [BPFPreserveAccessIndexDocs];
+}
+
 def WebAssemblyImportModule : InheritableAttr,
                               TargetSpecificAttr<TargetWebAssembly> {
   let Spellings = [Clang<"import_module">];

diff  --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 3a9093124637..50e76bf080b4 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -1634,6 +1634,17 @@ The semantics are as follows:
   }];
 }
 
+def BPFPreserveAccessIndexDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+Clang supports the ``__attribute__((preserve_access_index))``
+attribute for the BPF target. This attribute may be attached to a
+struct or union declaration, where if -g is specified, it enables
+preserving struct or union member access debuginfo indicies of this
+struct or union, similar to clang ``__builtin_preserve_acceess_index()``.
+  }];
+}
+
 def MipsInterruptDocs : Documentation {
   let Category = DocCatFunction;
   let Heading = "interrupt (MIPS)";

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 88d73dc89b55..33a0f9710856 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -10065,6 +10065,7 @@ def err_preserve_field_info_not_field : Error<
   "__builtin_preserve_field_info argument %0 not a field access">;
 def err_preserve_field_info_not_const: Error<
   "__builtin_preserve_field_info argument %0 not a constant">;
+def err_preserve_access_index_wrong_type: Error<"%0 attribute only applies to %1">;
 
 def err_bit_cast_non_trivially_copyable : Error<
   "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;

diff  --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 99406715cbf8..e32db8c56df9 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -3402,11 +3402,67 @@ static QualType getFixedSizeElementType(const ASTContext &ctx,
   return eltType;
 }
 
+/// Given an array base, check whether its member access belongs to a record
+/// with preserve_access_index attribute or not.
+static bool IsPreserveAIArrayBase(CodeGenFunction &CGF, const Expr *ArrayBase) {
+  if (!ArrayBase || !CGF.getDebugInfo())
+    return false;
+
+  const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(ArrayBase);
+  if (!ImplicitCast)
+    return false;
+
+  // Only support base as either a MemberExpr or DeclRefExpr.
+  // DeclRefExpr to cover cases like:
+  //    struct s { int a; int b[10]; };
+  //    struct s *p;
+  //    p[1].a
+  // p[1] will generate a DeclRefExpr and p[1].a is a MemberExpr.
+  // p->b[5] is a MemberExpr example.
+  const Expr *E = ImplicitCast->getSubExpr();
+  const auto *MemberCast = dyn_cast<MemberExpr>(E);
+  if (MemberCast)
+    return MemberCast->getMemberDecl()->hasAttr<BPFPreserveAccessIndexAttr>();
+
+  const auto *DeclRefCast = dyn_cast<DeclRefExpr>(E);
+  if (DeclRefCast) {
+    const VarDecl *VarDef = dyn_cast<VarDecl>(DeclRefCast->getDecl());
+    if (!VarDef)
+      return false;
+
+    const auto *PtrT = dyn_cast<PointerType>(VarDef->getType().getTypePtr());
+    if (!PtrT)
+      return false;
+    const auto *PointeeT = PtrT->getPointeeType().getTypePtr();
+
+    // Peel off typedef's
+    const auto *TypedefT = dyn_cast<TypedefType>(PointeeT);
+    while (TypedefT) {
+      PointeeT = TypedefT->desugar().getTypePtr();
+      TypedefT = dyn_cast<TypedefType>(PointeeT);
+    }
+
+    // Not a typedef any more, it should be an elaborated type.
+    const auto ElaborateT = dyn_cast<ElaboratedType>(PointeeT);
+    if (!ElaborateT)
+      return false;
+
+    const auto *RecT = dyn_cast<RecordType>(ElaborateT->desugar().getTypePtr());
+    if (!RecT)
+      return false;
+
+    return RecT->getDecl()->hasAttr<BPFPreserveAccessIndexAttr>();
+  }
+
+  return false;
+}
+
 static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
                                      ArrayRef<llvm::Value *> indices,
                                      QualType eltType, bool inbounds,
                                      bool signedIndices, SourceLocation loc,
                                      QualType *arrayType = nullptr,
+                                     const Expr *Base = nullptr,
                                      const llvm::Twine &name = "arrayidx") {
   // All the indices except that last must be zero.
 #ifndef NDEBUG
@@ -3428,7 +3484,8 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
 
   llvm::Value *eltPtr;
   auto LastIndex = dyn_cast<llvm::ConstantInt>(indices.back());
-  if (!CGF.IsInPreservedAIRegion || !LastIndex) {
+  if (!LastIndex ||
+      (!CGF.IsInPreservedAIRegion && !IsPreserveAIArrayBase(CGF, Base))) {
     eltPtr = emitArraySubscriptGEP(
         CGF, addr.getPointer(), indices, inbounds, signedIndices,
         loc, name);
@@ -3582,7 +3639,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
     Addr = emitArraySubscriptGEP(
         *this, ArrayLV.getAddress(), {CGM.getSize(CharUnits::Zero()), Idx},
         E->getType(), !getLangOpts().isSignedOverflowDefined(), SignedIndices,
-        E->getExprLoc(), &arrayType);
+        E->getExprLoc(), &arrayType, E->getBase());
     EltBaseInfo = ArrayLV.getBaseInfo();
     EltTBAAInfo = CGM.getTBAAInfoForSubobject(ArrayLV, E->getType());
   } else {
@@ -3592,7 +3649,8 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
     QualType ptrType = E->getBase()->getType();
     Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(),
                                  !getLangOpts().isSignedOverflowDefined(),
-                                 SignedIndices, E->getExprLoc(), &ptrType);
+                                 SignedIndices, E->getExprLoc(), &ptrType,
+                                 E->getBase());
   }
 
   LValue LV = MakeAddrLValue(Addr, E->getType(), EltBaseInfo, EltTBAAInfo);
@@ -3993,12 +4051,13 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
     const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
     Address Addr = base.getAddress();
     unsigned Idx = RL.getLLVMFieldNo(field);
-    if (!IsInPreservedAIRegion) {
+    const RecordDecl *rec = field->getParent();
+    if (!IsInPreservedAIRegion &&
+        (!getDebugInfo() || !rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
       if (Idx != 0)
         // For structs, we GEP to the field that the record layout suggests.
         Addr = Builder.CreateStructGEP(Addr, Idx, field->getName());
     } else {
-      const RecordDecl *rec = field->getParent();
       llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
           getContext().getRecordType(rec), rec->getLocation());
       Addr = Builder.CreatePreserveStructAccessIndex(Addr, Idx,
@@ -4081,7 +4140,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
       addr = Address(Builder.CreateLaunderInvariantGroup(addr.getPointer()),
                      addr.getAlignment());
 
-    if (IsInPreservedAIRegion) {
+    if (IsInPreservedAIRegion ||
+        (getDebugInfo() && rec->hasAttr<BPFPreserveAccessIndexAttr>())) {
       // Remember the original union field index
       llvm::DIType *DbgInfo = getDebugInfo()->getOrCreateRecordType(
           getContext().getRecordType(rec), rec->getLocation());
@@ -4095,7 +4155,8 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
       addr = Builder.CreateElementBitCast(
           addr, CGM.getTypes().ConvertTypeForMem(FieldType), field->getName());
   } else {
-    if (!IsInPreservedAIRegion)
+    if (!IsInPreservedAIRegion &&
+        (!getDebugInfo() || !rec->hasAttr<BPFPreserveAccessIndexAttr>()))
       // For structs, we GEP to the field that the record layout suggests.
       addr = emitAddrOfFieldStorage(*this, addr, field);
     else

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 7f68d2014916..526e61cdf9ee 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5700,6 +5700,59 @@ static void handleAVRSignalAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   handleSimpleAttribute<AVRSignalAttr>(S, D, AL);
 }
 
+static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD,
+    const BPFPreserveAccessIndexAttr &AL) {
+  // Add preserve_access_index attribute to all fields and inner records.
+  for (DeclContext::decl_iterator D = RD->decls_begin(), DEnd = RD->decls_end();
+       D != DEnd; ++D) {
+    // Any member or inner struct having attribute means done.
+    if (D->hasAttr<BPFPreserveAccessIndexAttr>())
+      return;
+
+    RecordDecl *Rec = dyn_cast<RecordDecl>(*D);
+    if (Rec) {
+      Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
+      handleBPFPreserveAIRecord(S, Rec, AL);
+    } else {
+      D->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
+    }
+  }
+}
+
+static void handleBPFPreserveAIRecord(Sema &S, RecordDecl *RD,
+    const ParsedAttr &AL) {
+  // Add preserve_access_index attribute to all fields and inner records.
+  for (DeclContext::decl_iterator D = RD->decls_begin(), DEnd = RD->decls_end();
+       D != DEnd; ++D) {
+    RecordDecl *Rec = dyn_cast<RecordDecl>(*D);
+    if (Rec) {
+      // Inner record may have been processed.
+      if (!Rec->hasAttr<BPFPreserveAccessIndexAttr>()) {
+        Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
+        handleBPFPreserveAIRecord(S, Rec, AL);
+      }
+    } else {
+      D->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
+    }
+  }
+}
+
+static void handleBPFPreserveAccessIndexAttr(Sema &S, Decl *D,
+    const ParsedAttr &AL) {
+  RecordDecl *Rec = dyn_cast<RecordDecl>(D);
+  if (!Rec) {
+    S.Diag(D->getLocation(), diag::err_preserve_access_index_wrong_type)
+        << "preserve_addess_index" << "struct or union type";
+    return;
+  }
+
+  if (!checkAttributeNumArgs(S, AL, 0))
+    return;
+
+  handleBPFPreserveAIRecord(S, Rec, AL);
+  Rec->addAttr(::new (S.Context) BPFPreserveAccessIndexAttr(S.Context, AL));
+}
+
 static void handleWebAssemblyImportModuleAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
   if (!isFunctionOrMethod(D)) {
     S.Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
@@ -6576,6 +6629,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
   case ParsedAttr::AT_AVRSignal:
     handleAVRSignalAttr(S, D, AL);
     break;
+  case ParsedAttr::AT_BPFPreserveAccessIndex:
+    handleBPFPreserveAccessIndexAttr(S, D, AL);
+    break;
   case ParsedAttr::AT_WebAssemblyImportModule:
     handleWebAssemblyImportModuleAttr(S, D, AL);
     break;
@@ -7325,7 +7381,8 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D,
   }
 }
 
-// Helper for delayed processing TransparentUnion attribute.
+// Helper for delayed processing TransparentUnion or BPFPreserveAccessIndexAttr
+// attribute.
 void Sema::ProcessDeclAttributeDelayed(Decl *D,
                                        const ParsedAttributesView &AttrList) {
   for (const ParsedAttr &AL : AttrList)
@@ -7333,6 +7390,13 @@ void Sema::ProcessDeclAttributeDelayed(Decl *D,
       handleTransparentUnionAttr(*this, D, AL);
       break;
     }
+
+  // For BPFPreserveAccessIndexAttr, we want to populate the attributes
+  // to fields and inner records as well.
+  if (D->hasAttr<BPFPreserveAccessIndexAttr>()) {
+    handleBPFPreserveAIRecord(*this, cast<RecordDecl>(D),
+        *D->getAttr<BPFPreserveAccessIndexAttr>());
+  }
 }
 
 // Annotation attributes are the only attributes allowed after an access

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-1.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-1.c
new file mode 100644
index 000000000000..accf6ab2cf99
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-1.c
@@ -0,0 +1,23 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// test simple member access and initial struct with non-zero stride access
+struct s1 {
+  int a;
+  union {
+   int b;
+   int c;
+  };
+} __reloc__;
+typedef struct s1 __s1;
+
+int test(__s1 *arg) {
+  return arg->a + arg[1].b;
+}
+
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 1)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-2.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-2.c
new file mode 100644
index 000000000000..a136eeea9f1e
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-2.c
@@ -0,0 +1,24 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// test array access
+struct s1 {
+  int a[3];
+  union {
+   int b;
+   int c[4];
+  };
+} __reloc__;
+typedef struct s1 __s1;
+
+int test(__s1 *arg) {
+  return arg->a[2] + arg->c[2];
+}
+
+// CHECK: call [3 x i32]* @llvm.preserve.struct.access.index.p0a3i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call i32* @llvm.preserve.array.access.index.p0i32.p0a3i32([3 x i32]* %{{[0-9a-z]+}}, i32 1, i32 2)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 1)
+// CHECK: call i32* @llvm.preserve.array.access.index.p0i32.p0a4i32([4 x i32]* %{{[0-9a-z]+}}, i32 1, i32 2)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-3.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-3.c
new file mode 100644
index 000000000000..917b508704ae
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-3.c
@@ -0,0 +1,32 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, all with attributes
+struct s1 {
+  int c;
+} __reloc__;
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  };
+} __reloc__;
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+} __reloc__;
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
+// 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)
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-4.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-4.c
new file mode 100644
index 000000000000..5ec56d96de18
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-4.c
@@ -0,0 +1,33 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, some do not have attributes.
+struct s1 {
+  int c;
+};
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  };
+} __reloc__;
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+};
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: define dso_local i32 @test
+// CHECK-NOT: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %a, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %1, i32 0)
+// CHECK: call %struct.s1* @llvm.preserve.array.access.index.p0s_struct.s1s.p0a3s_struct.s1s([3 x %struct.s1]* %b, i32 1, i32 2)
+// CHECK-NOT: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-5.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-5.c
new file mode 100644
index 000000000000..281bcdd782c8
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-5.c
@@ -0,0 +1,32 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, attribute may be in inner record.
+struct s1 {
+  int c;
+} __reloc__;
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  } __reloc__;
+};
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+} __reloc__;
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK-NOT: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
+// 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)
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-6.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-6.c
new file mode 100644
index 000000000000..a763f28bcd61
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-6.c
@@ -0,0 +1,32 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, both inner and outer record have attributes.
+struct s1 {
+  int c;
+} __reloc__;
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  } __reloc__;
+} __reloc__;
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+} __reloc__;
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
+// 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)
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-7.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-7.c
new file mode 100644
index 000000000000..49f4a4d2b261
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-7.c
@@ -0,0 +1,36 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, all with attributes
+struct __reloc__ s1;
+struct __reloc__ s2;
+struct __reloc__ s3;
+
+struct s1 {
+  int c;
+};
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  };
+};
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+};
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
+// 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)
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

diff  --git a/clang/test/CodeGen/bpf-attr-preserve-access-index-8.c b/clang/test/CodeGen/bpf-attr-preserve-access-index-8.c
new file mode 100644
index 000000000000..7febf7cbe2e7
--- /dev/null
+++ b/clang/test/CodeGen/bpf-attr-preserve-access-index-8.c
@@ -0,0 +1,36 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+
+// chain of records, all with attributes
+struct s1;
+struct s2;
+struct s3;
+
+struct s1 {
+  int c;
+} __reloc__;
+typedef struct s1 __s1;
+
+struct s2 {
+  union {
+    __s1 b[3];
+  };
+} __reloc__;
+typedef struct s2 __s2;
+
+struct s3 {
+  __s2 a;
+} __reloc__;
+typedef struct s3 __s3;
+
+int test(__s3 *arg) {
+  return arg->a.b[2].c;
+}
+
+// CHECK: call %struct.s2* @llvm.preserve.struct.access.index.p0s_struct.s2s.p0s_struct.s3s(%struct.s3* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.struct.access.index.p0s_union.anons.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0)
+// CHECK: call %union.anon* @llvm.preserve.union.access.index.p0s_union.anons.p0s_union.anons(%union.anon* %{{[0-9a-z]+}}, i32 0)
+// 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)
+// CHECK: call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0)

diff  --git a/clang/test/Sema/bpf-attr-preserve-access-index.c b/clang/test/Sema/bpf-attr-preserve-access-index.c
new file mode 100644
index 000000000000..472f778b6b57
--- /dev/null
+++ b/clang/test/Sema/bpf-attr-preserve-access-index.c
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -x c -triple bpf-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s
+
+#define __reloc__ __attribute__((preserve_access_index))
+#define __err_reloc__ __attribute__((preserve_access_index(0)))
+
+struct t1 {
+  int a;
+  int b[4];
+  int c:1;
+} __reloc__;
+
+union t2 {
+  int a;
+  int b[4];
+  int c:1;
+} __reloc__;
+
+struct t3 {
+  int a;
+} __err_reloc__; // expected-error {{'preserve_access_index' attribute takes no arguments}}
+
+struct t4 {
+  union {
+    int a;
+    char b[5];
+  };
+  struct {
+    int c:1;
+  } __reloc__;
+  int d;
+} __reloc__;
+
+struct __reloc__ p;
+struct __reloc__ q;
+struct p {
+  int a;
+};
+
+int a __reloc__; // expected-error {{preserve_addess_index attribute only applies to struct or union type}}
+struct s *p __reloc__; // expected-error {{preserve_addess_index attribute only applies to struct or union type}}
+
+void invalid1(const int __reloc__ *arg) {} // expected-error {{preserve_addess_index attribute only applies to struct or union type}}
+void invalid2() { const int __reloc__ *arg; } // expected-error {{preserve_addess_index attribute only applies to struct or union type}}
+int valid3(struct t4 *arg) { return arg->a + arg->b[3] + arg->c + arg->d; }
+int valid4(void *arg) {
+  struct local_t { int a; int b; } __reloc__;
+  return ((struct local_t *)arg)->b;
+}


        


More information about the cfe-commits mailing list