}
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"]>;
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">];
}];
}
+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)";
"__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">;
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
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);
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 {
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);
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,
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());
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
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)
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;
}
}
-// 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)
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
--- /dev/null
+// 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)
--- /dev/null
+// 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)
--- /dev/null
+// 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)
--- /dev/null
+// 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
--- /dev/null
+// 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)
--- /dev/null
+// 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)
--- /dev/null
+// 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)
--- /dev/null
+// 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)
--- /dev/null
+// 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;
+}