[BPF] do compile-once run-everywhere relocation for bitfields
authorYonghong Song <yhs@fb.com>
Tue, 8 Oct 2019 18:23:17 +0000 (18:23 +0000)
committerYonghong Song <yhs@fb.com>
Tue, 8 Oct 2019 18:23:17 +0000 (18:23 +0000)
A bpf specific clang intrinsic is introduced:
   u32 __builtin_preserve_field_info(member_access, info_kind)
Depending on info_kind, different information will
be returned to the program. A relocation is also
recorded for this builtin so that bpf loader can
patch the instruction on the target host.
This clang intrinsic is used to get certain information
to facilitate struct/union member relocations.

The offset relocation is extended by 4 bytes to
include relocation kind.
Currently supported relocation kinds are
 enum {
    FIELD_BYTE_OFFSET = 0,
    FIELD_BYTE_SIZE,
    FIELD_EXISTENCE,
    FIELD_SIGNEDNESS,
    FIELD_LSHIFT_U64,
    FIELD_RSHIFT_U64,
 };
for __builtin_preserve_field_info. The old
access offset relocation is covered by
    FIELD_BYTE_OFFSET = 0.

An example:
struct s {
    int a;
    int b1:9;
    int b2:4;
};
enum {
    FIELD_BYTE_OFFSET = 0,
    FIELD_BYTE_SIZE,
    FIELD_EXISTENCE,
    FIELD_SIGNEDNESS,
    FIELD_LSHIFT_U64,
    FIELD_RSHIFT_U64,
};

void bpf_probe_read(void *, unsigned, const void *);
int field_read(struct s *arg) {
  unsigned long long ull = 0;
  unsigned offset = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_OFFSET);
  unsigned size = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_SIZE);
 #ifdef USE_PROBE_READ
  bpf_probe_read(&ull, size, (const void *)arg + offset);
  unsigned lshift = __builtin_preserve_field_info(arg->b2, FIELD_LSHIFT_U64);
 #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
  lshift = lshift + (size << 3) - 64;
 #endif
 #else
  switch(size) {
  case 1:
    ull = *(unsigned char *)((void *)arg + offset); break;
  case 2:
    ull = *(unsigned short *)((void *)arg + offset); break;
  case 4:
    ull = *(unsigned int *)((void *)arg + offset); break;
  case 8:
    ull = *(unsigned long long *)((void *)arg + offset); break;
  }
  unsigned lshift = __builtin_preserve_field_info(arg->b2, FIELD_LSHIFT_U64);
 #endif
  ull <<= lshift;
  if (__builtin_preserve_field_info(arg->b2, FIELD_SIGNEDNESS))
    return (long long)ull >> __builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
  return ull >> __builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
}

There is a minor overhead for bpf_probe_read() on big endian.

The code and relocation generated for field_read where bpf_probe_read() is
used to access argument data on little endian mode:
        r3 = r1
        r1 = 0
        r1 = 4  <=== relocation (FIELD_BYTE_OFFSET)
        r3 += r1
        r1 = r10
        r1 += -8
        r2 = 4  <=== relocation (FIELD_BYTE_SIZE)
        call bpf_probe_read
        r2 = 51 <=== relocation (FIELD_LSHIFT_U64)
        r1 = *(u64 *)(r10 - 8)
        r1 <<= r2
        r2 = 60 <=== relocation (FIELD_RSHIFT_U64)
        r0 = r1
        r0 >>= r2
        r3 = 1  <=== relocation (FIELD_SIGNEDNESS)
        if r3 == 0 goto LBB0_2
        r1 s>>= r2
        r0 = r1
LBB0_2:
        exit

Compare to the above code between relocations FIELD_LSHIFT_U64 and
FIELD_LSHIFT_U64, the code with big endian mode has four more
instructions.
        r1 = 41   <=== relocation (FIELD_LSHIFT_U64)
        r6 += r1
        r6 += -64
        r6 <<= 32
        r6 >>= 32
        r1 = *(u64 *)(r10 - 8)
        r1 <<= r6
        r2 = 60   <=== relocation (FIELD_RSHIFT_U64)

The code and relocation generated when using direct load.
        r2 = 0
        r3 = 4
        r4 = 4
        if r4 s> 3 goto LBB0_3
        if r4 == 1 goto LBB0_5
        if r4 == 2 goto LBB0_6
        goto LBB0_9
LBB0_6:                                 # %sw.bb1
        r1 += r3
        r2 = *(u16 *)(r1 + 0)
        goto LBB0_9
LBB0_3:                                 # %entry
        if r4 == 4 goto LBB0_7
        if r4 == 8 goto LBB0_8
        goto LBB0_9
LBB0_8:                                 # %sw.bb9
        r1 += r3
        r2 = *(u64 *)(r1 + 0)
        goto LBB0_9
LBB0_5:                                 # %sw.bb
        r1 += r3
        r2 = *(u8 *)(r1 + 0)
        goto LBB0_9
LBB0_7:                                 # %sw.bb5
        r1 += r3
        r2 = *(u32 *)(r1 + 0)
LBB0_9:                                 # %sw.epilog
        r1 = 51
        r2 <<= r1
        r1 = 60
        r0 = r2
        r0 >>= r1
        r3 = 1
        if r3 == 0 goto LBB0_11
        r2 s>>= r1
        r0 = r2
LBB0_11:                                # %sw.epilog
        exit

Considering verifier is able to do limited constant
propogation following branches. The following is the
code actually traversed.
        r2 = 0
        r3 = 4   <=== relocation
        r4 = 4   <=== relocation
        if r4 s> 3 goto LBB0_3
LBB0_3:                                 # %entry
        if r4 == 4 goto LBB0_7
LBB0_7:                                 # %sw.bb5
        r1 += r3
        r2 = *(u32 *)(r1 + 0)
LBB0_9:                                 # %sw.epilog
        r1 = 51   <=== relocation
        r2 <<= r1
        r1 = 60   <=== relocation
        r0 = r2
        r0 >>= r1
        r3 = 1
        if r3 == 0 goto LBB0_11
        r2 s>>= r1
        r0 = r2
LBB0_11:                                # %sw.epilog
        exit

For native load case, the load size is calculated to be the
same as the size of load width LLVM otherwise used to load
the value which is then used to extract the bitfield value.

Differential Revision: https://reviews.llvm.org/D67980

llvm-svn: 374099

69 files changed:
clang/include/clang/Basic/BuiltinsBPF.def [new file with mode: 0644]
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/TargetBuiltins.h
clang/include/clang/Sema/Sema.h
clang/include/clang/module.modulemap
clang/lib/Basic/Targets/BPF.cpp
clang/lib/Basic/Targets/BPF.h
clang/lib/CodeGen/CGBuiltin.cpp
clang/lib/CodeGen/CGExpr.cpp
clang/lib/CodeGen/CodeGenFunction.h
clang/lib/Sema/SemaChecking.cpp
clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c [new file with mode: 0644]
clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c [new file with mode: 0644]
clang/test/Sema/builtins-bpf.c [new file with mode: 0644]
llvm/include/llvm/IR/IntrinsicsBPF.td
llvm/lib/Target/BPF/BPF.h
llvm/lib/Target/BPF/BPFAbstractMemberAccess.cpp
llvm/lib/Target/BPF/BPFCORE.h
llvm/lib/Target/BPF/BPFTargetMachine.cpp
llvm/lib/Target/BPF/BTF.h
llvm/lib/Target/BPF/BTFDebug.cpp
llvm/lib/Target/BPF/BTFDebug.h
llvm/test/CodeGen/BPF/CORE/intrinsic-array.ll
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-3.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-3.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-3.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-3.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/intrinsic-struct.ll
llvm/test/CodeGen/BPF/CORE/intrinsic-union.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-access-str.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-basic.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-array-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-array-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-struct-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-struct-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-struct-3.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-union-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-cast-union-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-end-load.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-end-ret.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-1.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-2.ll [new file with mode: 0644]
llvm/test/CodeGen/BPF/CORE/offset-reloc-global-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-global-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-global-3.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-ignore.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-middle-chain.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-multi-array-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-multi-array-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-multilevel.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-pointer-1.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-pointer-2.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-struct-anonymous.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-struct-array.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-typedef-array.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-typedef-struct.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-typedef-union.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-typedef.ll
llvm/test/CodeGen/BPF/CORE/offset-reloc-union.ll

diff --git a/clang/include/clang/Basic/BuiltinsBPF.def b/clang/include/clang/Basic/BuiltinsBPF.def
new file mode 100644 (file)
index 0000000..bd96b9e
--- /dev/null
@@ -0,0 +1,24 @@
+//===--- BuiltinsBPF.def - BPF Builtin function database --------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the BPF-specific builtin function database.  Users of
+// this file must define the BUILTIN macro to make use of this information.
+//
+//===----------------------------------------------------------------------===//
+
+// The format of this database matches clang/Basic/Builtins.def.
+
+#if defined(BUILTIN) && !defined(TARGET_BUILTIN)
+#   define TARGET_BUILTIN(ID, TYPE, ATTRS, FEATURE) BUILTIN(ID, TYPE, ATTRS)
+#endif
+
+// Get record field information.
+TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "")
+
+#undef BUILTIN
+#undef TARGET_BUILTIN
index 23cedca..d0d1a7d 100644 (file)
@@ -9954,6 +9954,11 @@ def err_builtin_launder_invalid_arg : Error<
   "%select{non-pointer|function pointer|void pointer}0 argument to "
   "'__builtin_launder' is not allowed">;
 
+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_bit_cast_non_trivially_copyable : Error<
   "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">;
 def err_bit_cast_type_size_mismatch : Error<
index 50262fa..0e2f075 100644 (file)
@@ -52,6 +52,16 @@ namespace clang {
   };
   }
 
+  /// BPF builtins
+  namespace BPF {
+  enum {
+    LastTIBuiltin = clang::Builtin::FirstTSBuiltin - 1,
+  #define BUILTIN(ID, TYPE, ATTRS) BI##ID,
+  #include "clang/Basic/BuiltinsBPF.def"
+    LastTSBuiltin
+  };
+  }
+
   /// PPC builtins
   namespace PPC {
     enum {
index 28180ed..bb05a08 100644 (file)
@@ -11056,6 +11056,7 @@ private:
   bool CheckARMBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
 
   bool CheckAArch64BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
+  bool CheckBPFBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
   bool CheckHexagonBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
   bool CheckHexagonBuiltinCpu(unsigned BuiltinID, CallExpr *TheCall);
   bool CheckHexagonBuiltinArgument(unsigned BuiltinID, CallExpr *TheCall);
index f7bf482..0f9b7bb 100644 (file)
@@ -34,6 +34,7 @@ module Clang_Basic {
   textual header "Basic/BuiltinsAArch64.def"
   textual header "Basic/BuiltinsAMDGPU.def"
   textual header "Basic/BuiltinsARM.def"
+  textual header "Basic/BuiltinsBPF.def"
   textual header "Basic/Builtins.def"
   textual header "Basic/BuiltinsHexagon.def"
   textual header "Basic/BuiltinsLe64.def"
index 0cf55a5..2fe2450 100644 (file)
 #include "BPF.h"
 #include "Targets.h"
 #include "clang/Basic/MacroBuilder.h"
+#include "clang/Basic/TargetBuiltins.h"
 #include "llvm/ADT/StringRef.h"
 
 using namespace clang;
 using namespace clang::targets;
 
+const Builtin::Info BPFTargetInfo::BuiltinInfo[] = {
+#define BUILTIN(ID, TYPE, ATTRS)                                               \
+  {#ID, TYPE, ATTRS, nullptr, ALL_LANGUAGES, nullptr},
+#include "clang/Basic/BuiltinsBPF.def"
+};
+
 void BPFTargetInfo::getTargetDefines(const LangOptions &Opts,
                                      MacroBuilder &Builder) const {
   Builder.defineMacro("__bpf__");
@@ -34,3 +41,8 @@ bool BPFTargetInfo::isValidCPUName(StringRef Name) const {
 void BPFTargetInfo::fillValidCPUList(SmallVectorImpl<StringRef> &Values) const {
   Values.append(std::begin(ValidCPUNames), std::end(ValidCPUNames));
 }
+
+ArrayRef<Builtin::Info> BPFTargetInfo::getTargetBuiltins() const {
+  return llvm::makeArrayRef(BuiltinInfo, clang::BPF::LastTSBuiltin -
+                                             Builtin::FirstTSBuiltin);
+}
index 79abd88..117f814 100644 (file)
@@ -22,6 +22,8 @@ namespace clang {
 namespace targets {
 
 class LLVM_LIBRARY_VISIBILITY BPFTargetInfo : public TargetInfo {
+  static const Builtin::Info BuiltinInfo[];
+
 public:
   BPFTargetInfo(const llvm::Triple &Triple, const TargetOptions &)
       : TargetInfo(Triple) {
@@ -54,7 +56,7 @@ public:
     Features[Name] = Enabled;
   }
 
-  ArrayRef<Builtin::Info> getTargetBuiltins() const override { return None; }
+  ArrayRef<Builtin::Info> getTargetBuiltins() const override;
 
   const char *getClobbers() const override { return ""; }
 
index 905db49..d5389d7 100644 (file)
@@ -4242,6 +4242,9 @@ static Value *EmitTargetArchBuiltinExpr(CodeGenFunction *CGF,
   case llvm::Triple::aarch64:
   case llvm::Triple::aarch64_be:
     return CGF->EmitAArch64BuiltinExpr(BuiltinID, E, Arch);
+  case llvm::Triple::bpfeb:
+  case llvm::Triple::bpfel:
+    return CGF->EmitBPFBuiltinExpr(BuiltinID, E);
   case llvm::Triple::x86:
   case llvm::Triple::x86_64:
     return CGF->EmitX86BuiltinExpr(BuiltinID, E);
@@ -9300,6 +9303,37 @@ Value *CodeGenFunction::EmitAArch64BuiltinExpr(unsigned BuiltinID,
   }
 }
 
+Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID,
+                                           const CallExpr *E) {
+  assert(BuiltinID == BPF::BI__builtin_preserve_field_info &&
+         "unexpected ARM builtin");
+
+  const Expr *Arg = E->getArg(0);
+  bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField;
+
+  if (!getDebugInfo()) {
+    CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g");
+    return IsBitField ? EmitLValue(Arg).getBitFieldPointer()
+                      : EmitLValue(Arg).getPointer();
+  }
+
+  // Enable underlying preserve_*_access_index() generation.
+  bool OldIsInPreservedAIRegion = IsInPreservedAIRegion;
+  IsInPreservedAIRegion = true;
+  Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer()
+                                : EmitLValue(Arg).getPointer();
+  IsInPreservedAIRegion = OldIsInPreservedAIRegion;
+
+  ConstantInt *C = cast<ConstantInt>(EmitScalarExpr(E->getArg(1)));
+  Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue());
+
+  // Built the IR for the preserve_field_info intrinsic.
+  llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration(
+      &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info,
+      {FieldAddr->getType()});
+  return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind});
+}
+
 llvm::Value *CodeGenFunction::
 BuildVector(ArrayRef<llvm::Value*> Ops) {
   assert((Ops.size() & (Ops.size() - 1)) == 0 &&
index 1ea0267..2bd1b0b 100644 (file)
@@ -3990,9 +3990,19 @@ LValue CodeGenFunction::EmitLValueForField(LValue base,
     const CGBitFieldInfo &Info = RL.getBitFieldInfo(field);
     Address Addr = base.getAddress();
     unsigned Idx = RL.getLLVMFieldNo(field);
-    if (Idx != 0)
-      // For structs, we GEP to the field that the record layout suggests.
-      Addr = Builder.CreateStructGEP(Addr, Idx, field->getName());
+    if (!IsInPreservedAIRegion) {
+      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,
+          getDebugInfoFIndex(rec, field->getFieldIndex()),
+          DbgInfo);
+    }
+
     // Get the access type.
     llvm::Type *FieldIntTy =
       llvm::Type::getIntNTy(getLLVMContext(), Info.StorageSize);
index 02007ab..ef16cd3 100644 (file)
@@ -3760,6 +3760,7 @@ public:
   llvm::Value *vectorWrapScalar16(llvm::Value *Op);
   llvm::Value *EmitAArch64BuiltinExpr(unsigned BuiltinID, const CallExpr *E,
                                       llvm::Triple::ArchType Arch);
+  llvm::Value *EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
 
   llvm::Value *BuildVector(ArrayRef<llvm::Value*> Ops);
   llvm::Value *EmitX86BuiltinExpr(unsigned BuiltinID, const CallExpr *E);
index de8e1ef..07d3648 100644 (file)
@@ -1540,6 +1540,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
         if (CheckAArch64BuiltinFunctionCall(BuiltinID, TheCall))
           return ExprError();
         break;
+      case llvm::Triple::bpfeb:
+      case llvm::Triple::bpfel:
+        if (CheckBPFBuiltinFunctionCall(BuiltinID, TheCall))
+          return ExprError();
+        break;
       case llvm::Triple::hexagon:
         if (CheckHexagonBuiltinFunctionCall(BuiltinID, TheCall))
           return ExprError();
@@ -1940,6 +1945,40 @@ bool Sema::CheckAArch64BuiltinFunctionCall(unsigned BuiltinID,
   return SemaBuiltinConstantArgRange(TheCall, i, l, u + l);
 }
 
+bool Sema::CheckBPFBuiltinFunctionCall(unsigned BuiltinID,
+                                       CallExpr *TheCall) {
+  assert(BuiltinID == BPF::BI__builtin_preserve_field_info &&
+         "unexpected ARM builtin");
+
+  if (checkArgCount(*this, TheCall, 2))
+    return true;
+
+  // The first argument needs to be a record field access.
+  // If it is an array element access, we delay decision
+  // to BPF backend to check whether the access is a
+  // field access or not.
+  Expr *Arg = TheCall->getArg(0);
+  if (Arg->getType()->getAsPlaceholderType() ||
+      (Arg->IgnoreParens()->getObjectKind() != OK_BitField &&
+       !dyn_cast<MemberExpr>(Arg->IgnoreParens()) &&
+       !dyn_cast<ArraySubscriptExpr>(Arg->IgnoreParens()))) {
+    Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_field)
+        << 1 << Arg->getSourceRange();
+    return true;
+  }
+
+  // The second argument needs to be a constant int
+  llvm::APSInt Value;
+  if (!TheCall->getArg(1)->isIntegerConstantExpr(Value, Context)) {
+    Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const)
+        << 2 << Arg->getSourceRange();
+    return true;
+  }
+
+  TheCall->setType(Context.UnsignedIntTy);
+  return false;
+}
+
 bool Sema::CheckHexagonBuiltinCpu(unsigned BuiltinID, CallExpr *TheCall) {
   struct BuiltinAndString {
     unsigned BuiltinID;
diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-1.c
new file mode 100644 (file)
index 0000000..a244bd1
--- /dev/null
@@ -0,0 +1,35 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define _(x, y) (__builtin_preserve_field_info((x), (y)))
+
+struct s1 {
+  char a;
+  char b:2;
+};
+
+union u1 {
+  char a;
+  char b:2;
+};
+
+unsigned unit1(struct s1 *arg) {
+  return _(arg->a, 10) + _(arg->b, 10);
+}
+// CHECK: define dso_local i32 @unit1
+// CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+// CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+
+unsigned unit2(union u1 *arg) {
+  return _(arg->a, 10) + _(arg->b, 10);
+}
+// CHECK: define dso_local i32 @unit2
+// CHECK: call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %{{[0-9a-z]+}}, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[UNION_U1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+// CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_union.u1s(%union.u1* %{{[0-9a-z]+}}, i32 0, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[UNION_U1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+
+// CHECK: ![[STRUCT_S1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1"
+// CHECK: ![[UNION_U1]] = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1"
diff --git a/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c b/clang/test/CodeGen/builtins-bpf-preserve-field-info-2.c
new file mode 100644 (file)
index 0000000..f50c997
--- /dev/null
@@ -0,0 +1,26 @@
+// REQUIRES: bpf-registered-target
+// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s
+
+#define _(x, y) (__builtin_preserve_field_info((x), (y)))
+
+struct s1 {
+  char a;
+  char b:2;
+};
+struct s2 {
+  struct s1 s;
+};
+
+unsigned unit1(struct s2 *arg) {
+  return _(arg->s.a, 10) + _(arg->s.b, 10);
+}
+// CHECK: define dso_local i32 @unit1
+// CHECK: call %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S2:[0-9]+]]
+// CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+// CHECK: call %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.s2s(%struct.s2* %{{[0-9a-z]+}}, i32 0, i32 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S2:[0-9]+]]
+// CHECK: call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %{{[0-9a-z]+}}, i32 1, i32 1), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[STRUCT_S1:[0-9]+]]
+// CHECK: call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %{{[0-9a-z]+}}, i64 10), !dbg !{{[0-9]+}}
+
+// CHECK: ![[STRUCT_S2]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s2"
+// CHECK: ![[STRUCT_S1]] = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1"
diff --git a/clang/test/Sema/builtins-bpf.c b/clang/test/Sema/builtins-bpf.c
new file mode 100644 (file)
index 0000000..8df9697
--- /dev/null
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -x c -triple bpf-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s
+
+struct s { int a; int b[4]; int c:1; };
+union u { int a; int b[4]; int c:1; };
+
+unsigned invalid1(const int *arg) {
+  return __builtin_preserve_field_info(arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 not a field access}}
+}
+
+unsigned invalid2(const int *arg) {
+  return __builtin_preserve_field_info(*arg, 1); // expected-error {{__builtin_preserve_field_info argument 1 not a field access}}
+}
+
+void *invalid3(struct s *arg) {
+  return __builtin_preserve_field_info(arg->a, 1); // expected-warning {{incompatible integer to pointer conversion returning 'unsigned int' from a function with result type 'void *'}}
+}
+
+unsigned valid4(struct s *arg) {
+  return __builtin_preserve_field_info(arg->b[1], 1);
+}
+
+unsigned valid5(union u *arg) {
+  return __builtin_preserve_field_info(arg->b[2], 1);
+}
+
+unsigned valid6(struct s *arg) {
+  return __builtin_preserve_field_info(arg->a, 1);
+}
+
+unsigned valid7(struct s *arg) {
+  return __builtin_preserve_field_info(arg->c, 1ULL);
+}
+
+unsigned valid8(union u *arg) {
+  return __builtin_preserve_field_info(arg->a, 1);
+}
+
+unsigned valid9(union u *arg) {
+  return __builtin_preserve_field_info(arg->c, 'a');
+}
+
+unsigned invalid10(struct s *arg) {
+  return __builtin_preserve_field_info(arg->a, arg); // expected-error {{__builtin_preserve_field_info argument 2 not a constant}}
+}
+
+unsigned invalid11(struct s *arg, int info_kind) {
+  return __builtin_preserve_field_info(arg->a, info_kind); // expected-error {{__builtin_preserve_field_info argument 2 not a constant}}
+}
index d7595a2..3618cc6 100644 (file)
@@ -20,4 +20,7 @@ let TargetPrefix = "bpf" in {  // All intrinsics start with "llvm.bpf."
               Intrinsic<[llvm_i64_ty], [llvm_ptr_ty, llvm_i64_ty], [IntrReadMem]>;
   def int_bpf_pseudo : GCCBuiltin<"__builtin_bpf_pseudo">,
               Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty]>;
+  def int_bpf_preserve_field_info : GCCBuiltin<"__builtin_bpf_preserve_field_info">,
+              Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i64_ty],
+              [IntrNoMem, ImmArg<1>]>;
 }
index d311fc1..ba21503 100644 (file)
@@ -15,7 +15,7 @@
 namespace llvm {
 class BPFTargetMachine;
 
-ModulePass *createBPFAbstractMemberAccess();
+ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM);
 
 FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
 FunctionPass *createBPFMISimplifyPatchablePass();
index 5a9a34e..1b44281 100644 (file)
 //   addr = preserve_struct_access_index(base, gep_index, di_index)
 //          !llvm.preserve.access.index <struct_ditype>
 //
+// Bitfield member access needs special attention. User cannot take the
+// address of a bitfield acceess. To facilitate kernel verifier
+// for easy bitfield code optimization, a new clang intrinsic is introduced:
+//   uint32_t __builtin_preserve_field_info(member_access, info_kind)
+// In IR, a chain with two (or more) intrinsic calls will be generated:
+//   ...
+//   addr = preserve_struct_access_index(base, 1, 1) !struct s
+//   uint32_t result = bpf_preserve_field_info(addr, info_kind)
+//
+// Suppose the info_kind is FIELD_SIGNEDNESS,
+// The above two IR intrinsics will be replaced with
+// a relocatable insn:
+//   signness = /* signness of member_access */
+// and signness can be changed by bpf loader based on the
+// types on the host.
+//
+// User can also test whether a field exists or not with
+//   uint32_t result = bpf_preserve_field_info(member_access, FIELD_EXISTENCE)
+// The field will be always available (result = 1) during initial
+// compilation, but bpf loader can patch with the correct value
+// on the target host where the member_access may or may not be available
+//
 //===----------------------------------------------------------------------===//
 
 #include "BPF.h"
@@ -88,7 +110,11 @@ class BPFAbstractMemberAccess final : public ModulePass {
 
 public:
   static char ID;
-  BPFAbstractMemberAccess() : ModulePass(ID) {}
+  TargetMachine *TM;
+  // Add optional BPFTargetMachine parameter so that BPF backend can add the phase
+  // with target machine to find out the endianness. The default constructor (without
+  // parameters) is used by the pass manager for managing purposes.
+  BPFAbstractMemberAccess(BPFTargetMachine *TM = nullptr) : ModulePass(ID), TM(TM) {}
 
   struct CallInfo {
     uint32_t Kind;
@@ -96,19 +122,21 @@ public:
     MDNode *Metadata;
     Value *Base;
   };
+  typedef std::stack<std::pair<CallInst *, CallInfo>> CallInfoStack;
 
 private:
   enum : uint32_t {
     BPFPreserveArrayAI = 1,
     BPFPreserveUnionAI = 2,
     BPFPreserveStructAI = 3,
+    BPFPreserveFieldInfoAI = 4,
   };
 
   std::map<std::string, GlobalVariable *> GEPGlobals;
   // A map to link preserve_*_access_index instrinsic calls.
   std::map<CallInst *, std::pair<CallInst *, CallInfo>> AIChain;
   // A map to hold all the base preserve_*_access_index instrinsic calls.
-  // The base call is not an input of any other preserve_*_access_index
+  // The base call is not an input of any other preserve_*
   // intrinsics.
   std::map<CallInst *, CallInfo> BaseAICalls;
 
@@ -127,6 +155,12 @@ private:
   bool removePreserveAccessIndexIntrinsic(Module &M);
   void replaceWithGEP(std::vector<CallInst *> &CallList,
                       uint32_t NumOfZerosIndex, uint32_t DIIndex);
+  bool HasPreserveFieldInfoCall(CallInfoStack &CallStack);
+  void GetStorageBitRange(DICompositeType *CTy, DIDerivedType *MemberTy,
+                          uint32_t AccessIndex, uint32_t &StartBitOffset,
+                          uint32_t &EndBitOffset);
+  uint32_t GetFieldInfo(uint32_t InfoKind, DICompositeType *CTy,
+                        uint32_t AccessIndex, uint32_t PatchImm);
 
   Value *computeBaseAndAccessKey(CallInst *Call, CallInfo &CInfo,
                                  std::string &AccessKey, MDNode *&BaseMeta);
@@ -139,8 +173,8 @@ char BPFAbstractMemberAccess::ID = 0;
 INITIALIZE_PASS(BPFAbstractMemberAccess, DEBUG_TYPE,
                 "abstracting struct/union member accessees", false, false)
 
-ModulePass *llvm::createBPFAbstractMemberAccess() {
-  return new BPFAbstractMemberAccess();
+ModulePass *llvm::createBPFAbstractMemberAccess(BPFTargetMachine *TM) {
+  return new BPFAbstractMemberAccess(TM);
 }
 
 bool BPFAbstractMemberAccess::runOnModule(Module &M) {
@@ -231,6 +265,16 @@ bool BPFAbstractMemberAccess::IsPreserveDIAccessIndexCall(const CallInst *Call,
     CInfo.Base = Call->getArgOperand(0);
     return true;
   }
+  if (GV->getName().startswith("llvm.bpf.preserve.field.info")) {
+    CInfo.Kind = BPFPreserveFieldInfoAI;
+    CInfo.Metadata = nullptr;
+    // Check validity of info_kind as clang did not check this.
+    uint64_t InfoKind = getConstant(Call->getArgOperand(1));
+    if (InfoKind >= BPFCoreSharedInfo::MAX_FIELD_RELOC_KIND)
+      report_fatal_error("Incorrect info_kind for llvm.bpf.preserve.field.info intrinsic");
+    CInfo.AccessIndex = InfoKind;
+    return true;
+  }
 
   return false;
 }
@@ -306,6 +350,9 @@ bool BPFAbstractMemberAccess::removePreserveAccessIndexIntrinsic(Module &M) {
 bool BPFAbstractMemberAccess::IsValidAIChain(const MDNode *ParentType,
                                              uint32_t ParentAI,
                                              const MDNode *ChildType) {
+  if (!ChildType)
+    return true; // preserve_field_info, no type comparison needed.
+
   const DIType *PType = stripQualifiers(cast<DIType>(ParentType));
   const DIType *CType = stripQualifiers(cast<DIType>(ChildType));
 
@@ -463,7 +510,187 @@ uint64_t BPFAbstractMemberAccess::getConstant(const Value *IndexValue) {
   return CV->getValue().getZExtValue();
 }
 
-/// Compute the base of the whole preserve_*_access_index chains, i.e., the base
+/// Get the start and the end of storage offset for \p MemberTy.
+/// The storage bits are corresponding to the LLVM internal types,
+/// and the storage bits for the member determines what load width
+/// to use in order to extract the bitfield value.
+void BPFAbstractMemberAccess::GetStorageBitRange(DICompositeType *CTy,
+                                                 DIDerivedType *MemberTy,
+                                                 uint32_t AccessIndex,
+                                                 uint32_t &StartBitOffset,
+                                                 uint32_t &EndBitOffset) {
+  auto SOff = dyn_cast<ConstantInt>(MemberTy->getStorageOffsetInBits());
+  assert(SOff);
+  StartBitOffset = SOff->getZExtValue();
+
+  EndBitOffset = CTy->getSizeInBits();
+  uint32_t Index = AccessIndex + 1;
+  for (; Index < CTy->getElements().size(); ++Index) {
+    auto Member = cast<DIDerivedType>(CTy->getElements()[Index]);
+    if (!Member->getStorageOffsetInBits()) {
+      EndBitOffset = Member->getOffsetInBits();
+      break;
+    }
+    SOff = dyn_cast<ConstantInt>(Member->getStorageOffsetInBits());
+    assert(SOff);
+    unsigned BitOffset = SOff->getZExtValue();
+    if (BitOffset != StartBitOffset) {
+      EndBitOffset = BitOffset;
+      break;
+    }
+  }
+}
+
+uint32_t BPFAbstractMemberAccess::GetFieldInfo(uint32_t InfoKind,
+                                               DICompositeType *CTy,
+                                               uint32_t AccessIndex,
+                                               uint32_t PatchImm) {
+  if (InfoKind == BPFCoreSharedInfo::FIELD_EXISTENCE)
+      return 1;
+
+  uint32_t Tag = CTy->getTag();
+  if (InfoKind == BPFCoreSharedInfo::FIELD_BYTE_OFFSET) {
+    if (Tag == dwarf::DW_TAG_array_type) {
+      auto *EltTy = stripQualifiers(CTy->getBaseType());
+      PatchImm += AccessIndex * calcArraySize(CTy, 1) *
+                  (EltTy->getSizeInBits() >> 3);
+    } else if (Tag == dwarf::DW_TAG_structure_type) {
+      auto *MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
+      if (!MemberTy->isBitField()) {
+        PatchImm += MemberTy->getOffsetInBits() >> 3;
+      } else {
+        auto SOffset = dyn_cast<ConstantInt>(MemberTy->getStorageOffsetInBits());
+        assert(SOffset);
+        PatchImm += SOffset->getZExtValue() >> 3;
+      }
+    }
+    return PatchImm;
+  }
+
+  if (InfoKind == BPFCoreSharedInfo::FIELD_BYTE_SIZE) {
+    if (Tag == dwarf::DW_TAG_array_type) {
+      auto *EltTy = stripQualifiers(CTy->getBaseType());
+      return calcArraySize(CTy, 1) * (EltTy->getSizeInBits() >> 3);
+    } else {
+      auto *MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
+      uint32_t SizeInBits = MemberTy->getSizeInBits();
+      if (!MemberTy->isBitField())
+        return SizeInBits >> 3;
+
+      unsigned SBitOffset, NextSBitOffset;
+      GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
+      SizeInBits = NextSBitOffset - SBitOffset;
+      if (SizeInBits & (SizeInBits - 1))
+        report_fatal_error("Unsupported field expression for llvm.bpf.preserve.field.info");
+      return SizeInBits >> 3;
+    }
+  }
+
+  if (InfoKind == BPFCoreSharedInfo::FIELD_SIGNEDNESS) {
+    const DIType *BaseTy;
+    if (Tag == dwarf::DW_TAG_array_type) {
+      // Signedness only checked when final array elements are accessed.
+      if (CTy->getElements().size() != 1)
+        report_fatal_error("Invalid array expression for llvm.bpf.preserve.field.info");
+      BaseTy = stripQualifiers(CTy->getBaseType());
+    } else {
+      auto *MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
+      BaseTy = stripQualifiers(MemberTy->getBaseType());
+    }
+
+    // Only basic types and enum types have signedness.
+    const auto *BTy = dyn_cast<DIBasicType>(BaseTy);
+    while (!BTy) {
+      const auto *CompTy = dyn_cast<DICompositeType>(BaseTy);
+      // Report an error if the field expression does not have signedness.
+      if (!CompTy || CompTy->getTag() != dwarf::DW_TAG_enumeration_type)
+        report_fatal_error("Invalid field expression for llvm.bpf.preserve.field.info");
+      BaseTy = stripQualifiers(CompTy->getBaseType());
+      BTy = dyn_cast<DIBasicType>(BaseTy);
+    }
+    uint32_t Encoding = BTy->getEncoding();
+    return (Encoding == dwarf::DW_ATE_signed || Encoding == dwarf::DW_ATE_signed_char);
+  }
+
+  if (InfoKind == BPFCoreSharedInfo::FIELD_LSHIFT_U64) {
+    // The value is loaded into a value with FIELD_BYTE_SIZE size,
+    // and then zero or sign extended to U64.
+    // FIELD_LSHIFT_U64 and FIELD_RSHIFT_U64 are operations
+    // to extract the original value.
+    const Triple &Triple = TM->getTargetTriple();
+    DIDerivedType *MemberTy = nullptr;
+    bool IsBitField = false;
+    uint32_t SizeInBits;
+
+    if (Tag == dwarf::DW_TAG_array_type) {
+      auto *EltTy = stripQualifiers(CTy->getBaseType());
+      SizeInBits = calcArraySize(CTy, 1) * EltTy->getSizeInBits();
+    } else {
+      MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
+      SizeInBits = MemberTy->getSizeInBits();
+      IsBitField = MemberTy->isBitField();
+    }
+
+    if (!IsBitField) {
+      if (SizeInBits > 64)
+        report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
+      return 64 - SizeInBits;
+    }
+
+    unsigned SBitOffset, NextSBitOffset;
+    GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
+    if (NextSBitOffset - SBitOffset > 64)
+      report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
+
+    unsigned OffsetInBits = MemberTy->getOffsetInBits();
+    if (Triple.getArch() == Triple::bpfel)
+      return SBitOffset + 64 - OffsetInBits - SizeInBits;
+    else
+      return OffsetInBits + 64 - NextSBitOffset;
+  }
+
+  if (InfoKind == BPFCoreSharedInfo::FIELD_RSHIFT_U64) {
+    DIDerivedType *MemberTy = nullptr;
+    bool IsBitField = false;
+    uint32_t SizeInBits;
+    if (Tag == dwarf::DW_TAG_array_type) {
+      auto *EltTy = stripQualifiers(CTy->getBaseType());
+      SizeInBits = calcArraySize(CTy, 1) * EltTy->getSizeInBits();
+    } else {
+      MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
+      SizeInBits = MemberTy->getSizeInBits();
+      IsBitField = MemberTy->isBitField();
+    }
+
+    if (!IsBitField) {
+      if (SizeInBits > 64)
+        report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
+      return 64 - SizeInBits;
+    }
+
+    unsigned SBitOffset, NextSBitOffset;
+    GetStorageBitRange(CTy, MemberTy, AccessIndex, SBitOffset, NextSBitOffset);
+    if (NextSBitOffset - SBitOffset > 64)
+      report_fatal_error("too big field size for llvm.bpf.preserve.field.info");
+
+    return 64 - SizeInBits;
+  }
+
+  llvm_unreachable("Unknown llvm.bpf.preserve.field.info info kind");
+}
+
+bool BPFAbstractMemberAccess::HasPreserveFieldInfoCall(CallInfoStack &CallStack) {
+  // This is called in error return path, no need to maintain CallStack.
+  while (CallStack.size()) {
+    auto StackElem = CallStack.top();
+    if (StackElem.second.Kind == BPFPreserveFieldInfoAI)
+      return true;
+    CallStack.pop();
+  }
+  return false;
+}
+
+/// Compute the base of the whole preserve_* intrinsics chains, i.e., the base
 /// pointer of the first preserve_*_access_index call, and construct the access
 /// string, which will be the name of a global variable.
 Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
@@ -472,7 +699,7 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
                                                         MDNode *&TypeMeta) {
   Value *Base = nullptr;
   std::string TypeName;
-  std::stack<std::pair<CallInst *, CallInfo>> CallStack;
+  CallInfoStack CallStack;
 
   // Put the access chain into a stack with the top as the head of the chain.
   while (Call) {
@@ -492,7 +719,8 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
   //    int a[10][20]; ... __builtin_preserve_access_index(&a[2][3]) ...
   // we will skip them.
   uint32_t FirstIndex = 0;
-  uint32_t AccessOffset = 0;
+  uint32_t PatchImm = 0; // AccessOffset or the requested field info
+  uint32_t InfoKind = BPFCoreSharedInfo::FIELD_BYTE_OFFSET;
   while (CallStack.size()) {
     auto StackElem = CallStack.top();
     Call = StackElem.first;
@@ -507,10 +735,12 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
       // struct or union type
       TypeName = Ty->getName();
       TypeMeta = Ty;
-      AccessOffset += FirstIndex * Ty->getSizeInBits() >> 3;
+      PatchImm += FirstIndex * (Ty->getSizeInBits() >> 3);
       break;
     }
 
+    assert(CInfo.Kind == BPFPreserveArrayAI);
+
     // Array entries will always be consumed for accumulative initial index.
     CallStack.pop();
 
@@ -546,16 +776,22 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
 
     if (CheckElemType) {
       auto *CTy = dyn_cast<DICompositeType>(BaseTy);
-      if (!CTy)
+      if (!CTy) {
+        if (HasPreserveFieldInfoCall(CallStack))
+          report_fatal_error("Invalid field access for llvm.preserve.field.info intrinsic");
         return nullptr;
+      }
 
       unsigned CTag = CTy->getTag();
-      if (CTag != dwarf::DW_TAG_structure_type && CTag != dwarf::DW_TAG_union_type)
-        return nullptr;
-      else
+      if (CTag == dwarf::DW_TAG_structure_type || CTag == dwarf::DW_TAG_union_type) {
         TypeName = CTy->getName();
+      } else {
+        if (HasPreserveFieldInfoCall(CallStack))
+          report_fatal_error("Invalid field access for llvm.preserve.field.info intrinsic");
+        return nullptr;
+      }
       TypeMeta = CTy;
-      AccessOffset += FirstIndex * CTy->getSizeInBits() >> 3;
+      PatchImm += FirstIndex * (CTy->getSizeInBits() >> 3);
       break;
     }
   }
@@ -569,6 +805,20 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
     CInfo = StackElem.second;
     CallStack.pop();
 
+    if (CInfo.Kind == BPFPreserveFieldInfoAI)
+      break;
+
+    // If the next Call (the top of the stack) is a BPFPreserveFieldInfoAI,
+    // the action will be extracting field info.
+    if (CallStack.size()) {
+      auto StackElem2 = CallStack.top();
+      CallInfo CInfo2 = StackElem2.second;
+      if (CInfo2.Kind == BPFPreserveFieldInfoAI) {
+        InfoKind = CInfo2.AccessIndex;
+        assert(CallStack.size() == 1);
+      }
+    }
+
     // Access Index
     uint64_t AccessIndex = CInfo.AccessIndex;
     AccessKey += ":" + std::to_string(AccessIndex);
@@ -576,20 +826,13 @@ Value *BPFAbstractMemberAccess::computeBaseAndAccessKey(CallInst *Call,
     MDNode *MDN = CInfo.Metadata;
     // At this stage, it cannot be pointer type.
     auto *CTy = cast<DICompositeType>(stripQualifiers(cast<DIType>(MDN)));
-    uint32_t Tag = CTy->getTag();
-    if (Tag == dwarf::DW_TAG_structure_type) {
-      auto *MemberTy = cast<DIDerivedType>(CTy->getElements()[AccessIndex]);
-      AccessOffset += MemberTy->getOffsetInBits() >> 3;
-    } else if (Tag == dwarf::DW_TAG_array_type) {
-      auto *EltTy = stripQualifiers(CTy->getBaseType());
-      AccessOffset += AccessIndex * calcArraySize(CTy, 1) *
-                      EltTy->getSizeInBits() >> 3;
-    }
+    PatchImm = GetFieldInfo(InfoKind, CTy, AccessIndex, PatchImm);
   }
 
-  // Access key is the type name + access string, uniquely identifying
-  // one kernel memory access.
-  AccessKey = TypeName + ":" + std::to_string(AccessOffset) + "$" + AccessKey;
+  // Access key is the type name + reloc type + patched imm + access string,
+  // uniquely identifying one relocation.
+  AccessKey = TypeName + ":" + std::to_string(InfoKind) + ":" +
+              std::to_string(PatchImm) + "$" + AccessKey;
 
   return Base;
 }
@@ -605,22 +848,18 @@ bool BPFAbstractMemberAccess::transformGEPChain(Module &M, CallInst *Call,
   if (!Base)
     return false;
 
-  // Do the transformation
-  // For any original GEP Call and Base %2 like
-  //   %4 = bitcast %struct.net_device** %dev1 to i64*
-  // it is transformed to:
-  //   %6 = load sk_buff:50:$0:0:0:2:0
-  //   %7 = bitcast %struct.sk_buff* %2 to i8*
-  //   %8 = getelementptr i8, i8* %7, %6
-  //   %9 = bitcast i8* %8 to i64*
-  //   using %9 instead of %4
-  // The original Call inst is removed.
   BasicBlock *BB = Call->getParent();
   GlobalVariable *GV;
 
   if (GEPGlobals.find(AccessKey) == GEPGlobals.end()) {
-    GV = new GlobalVariable(M, Type::getInt64Ty(BB->getContext()), false,
-                            GlobalVariable::ExternalLinkage, NULL, AccessKey);
+    IntegerType *VarType;
+    if (CInfo.Kind == BPFPreserveFieldInfoAI)
+      VarType = Type::getInt32Ty(BB->getContext()); // 32bit return value
+    else
+      VarType = Type::getInt64Ty(BB->getContext()); // 64bit ptr arith
+
+    GV = new GlobalVariable(M, VarType, false, GlobalVariable::ExternalLinkage,
+                            NULL, AccessKey);
     GV->addAttribute(BPFCoreSharedInfo::AmaAttr);
     GV->setMetadata(LLVMContext::MD_preserve_access_index, TypeMeta);
     GEPGlobals[AccessKey] = GV;
@@ -628,6 +867,25 @@ bool BPFAbstractMemberAccess::transformGEPChain(Module &M, CallInst *Call,
     GV = GEPGlobals[AccessKey];
   }
 
+  if (CInfo.Kind == BPFPreserveFieldInfoAI) {
+    // Load the global variable which represents the returned field info.
+    auto *LDInst = new LoadInst(Type::getInt32Ty(BB->getContext()), GV);
+    BB->getInstList().insert(Call->getIterator(), LDInst);
+    Call->replaceAllUsesWith(LDInst);
+    Call->eraseFromParent();
+    return true;
+  }
+
+  // For any original GEP Call and Base %2 like
+  //   %4 = bitcast %struct.net_device** %dev1 to i64*
+  // it is transformed to:
+  //   %6 = load sk_buff:50:$0:0:0:2:0
+  //   %7 = bitcast %struct.sk_buff* %2 to i8*
+  //   %8 = getelementptr i8, i8* %7, %6
+  //   %9 = bitcast i8* %8 to i64*
+  //   using %9 instead of %4
+  // The original Call inst is removed.
+
   // Load the global variable.
   auto *LDInst = new LoadInst(Type::getInt64Ty(BB->getContext()), GV);
   BB->getInstList().insert(Call->getIterator(), LDInst);
index e0950d9..a6cb3cf 100644 (file)
@@ -13,6 +13,16 @@ namespace llvm {
 
 class BPFCoreSharedInfo {
 public:
+  enum OffsetRelocKind : uint32_t {
+    FIELD_BYTE_OFFSET = 0,
+    FIELD_BYTE_SIZE,
+    FIELD_EXISTENCE,
+    FIELD_SIGNEDNESS,
+    FIELD_LSHIFT_U64,
+    FIELD_RSHIFT_U64,
+
+    MAX_FIELD_RELOC_KIND,
+  };
   /// The attribute attached to globals representing a member offset
   static const std::string AmaAttr;
   /// The section name to identify a patchable external global
index a69a806..d940ac9 100644 (file)
@@ -94,7 +94,7 @@ TargetPassConfig *BPFTargetMachine::createPassConfig(PassManagerBase &PM) {
 
 void BPFPassConfig::addIRPasses() {
 
-  addPass(createBPFAbstractMemberAccess());
+  addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine()));
 
   TargetPassConfig::addIRPasses();
 }
index ad56716..ef408da 100644 (file)
@@ -17,7 +17,7 @@
 ///
 /// The binary layout for .BTF.ext section:
 ///   struct ExtHeader
-///   FuncInfo, LineInfo, OffsetReloc and ExternReloc subsections
+///   FuncInfo, LineInfo, FieldReloc and ExternReloc subsections
 /// The FuncInfo subsection is defined as below:
 ///   BTFFuncInfo Size
 ///   struct SecFuncInfo for ELF section #1
 ///   struct SecLineInfo for ELF section #2
 ///   A number of struct BPFLineInfo for ELF section #2
 ///   ...
-/// The OffsetReloc subsection is defined as below:
-///   BPFOffsetReloc Size
-///   struct SecOffsetReloc for ELF section #1
-///   A number of struct BPFOffsetReloc for ELF section #1
-///   struct SecOffsetReloc for ELF section #2
-///   A number of struct BPFOffsetReloc for ELF section #2
+/// The FieldReloc subsection is defined as below:
+///   BPFFieldReloc Size
+///   struct SecFieldReloc for ELF section #1
+///   A number of struct BPFFieldReloc for ELF section #1
+///   struct SecFieldReloc for ELF section #2
+///   A number of struct BPFFieldReloc for ELF section #2
 ///   ...
 /// The ExternReloc subsection is defined as below:
 ///   BPFExternReloc Size
@@ -72,11 +72,11 @@ enum {
   BTFDataSecVarSize = 12,
   SecFuncInfoSize = 8,
   SecLineInfoSize = 8,
-  SecOffsetRelocSize = 8,
+  SecFieldRelocSize = 8,
   SecExternRelocSize = 8,
   BPFFuncInfoSize = 8,
   BPFLineInfoSize = 16,
-  BPFOffsetRelocSize = 12,
+  BPFFieldRelocSize = 16,
   BPFExternRelocSize = 8,
 };
 
@@ -213,8 +213,8 @@ struct ExtHeader {
   uint32_t FuncInfoLen;    ///< Length of func info section
   uint32_t LineInfoOff;    ///< Offset of line info section
   uint32_t LineInfoLen;    ///< Length of line info section
-  uint32_t OffsetRelocOff; ///< Offset of offset reloc section
-  uint32_t OffsetRelocLen; ///< Length of offset reloc section
+  uint32_t FieldRelocOff; ///< Offset of offset reloc section
+  uint32_t FieldRelocLen; ///< Length of offset reloc section
   uint32_t ExternRelocOff; ///< Offset of extern reloc section
   uint32_t ExternRelocLen; ///< Length of extern reloc section
 };
@@ -247,16 +247,17 @@ struct SecLineInfo {
 };
 
 /// Specifying one offset relocation.
-struct BPFOffsetReloc {
+struct BPFFieldReloc {
   uint32_t InsnOffset;    ///< Byte offset in this section
   uint32_t TypeID;        ///< TypeID for the relocation
   uint32_t OffsetNameOff; ///< The string to traverse types
+  uint32_t RelocKind;     ///< What to patch the instruction
 };
 
 /// Specifying offset relocation's in one section.
-struct SecOffsetReloc {
+struct SecFieldReloc {
   uint32_t SecNameOff;     ///< Section name index in the .BTF string table
-  uint32_t NumOffsetReloc; ///< Number of offset reloc's in this section
+  uint32_t NumFieldReloc; ///< Number of offset reloc's in this section
 };
 
 /// Specifying one offset relocation.
index 3ad3234..a8f8573 100644 (file)
@@ -754,7 +754,7 @@ void BTFDebug::emitBTFSection() {
 void BTFDebug::emitBTFExtSection() {
   // Do not emit section if empty FuncInfoTable and LineInfoTable.
   if (!FuncInfoTable.size() && !LineInfoTable.size() &&
-      !OffsetRelocTable.size() && !ExternRelocTable.size())
+      !FieldRelocTable.size() && !ExternRelocTable.size())
     return;
 
   MCContext &Ctx = OS.getContext();
@@ -766,8 +766,8 @@ void BTFDebug::emitBTFExtSection() {
 
   // Account for FuncInfo/LineInfo record size as well.
   uint32_t FuncLen = 4, LineLen = 4;
-  // Do not account for optional OffsetReloc/ExternReloc.
-  uint32_t OffsetRelocLen = 0, ExternRelocLen = 0;
+  // Do not account for optional FieldReloc/ExternReloc.
+  uint32_t FieldRelocLen = 0, ExternRelocLen = 0;
   for (const auto &FuncSec : FuncInfoTable) {
     FuncLen += BTF::SecFuncInfoSize;
     FuncLen += FuncSec.second.size() * BTF::BPFFuncInfoSize;
@@ -776,17 +776,17 @@ void BTFDebug::emitBTFExtSection() {
     LineLen += BTF::SecLineInfoSize;
     LineLen += LineSec.second.size() * BTF::BPFLineInfoSize;
   }
-  for (const auto &OffsetRelocSec : OffsetRelocTable) {
-    OffsetRelocLen += BTF::SecOffsetRelocSize;
-    OffsetRelocLen += OffsetRelocSec.second.size() * BTF::BPFOffsetRelocSize;
+  for (const auto &FieldRelocSec : FieldRelocTable) {
+    FieldRelocLen += BTF::SecFieldRelocSize;
+    FieldRelocLen += FieldRelocSec.second.size() * BTF::BPFFieldRelocSize;
   }
   for (const auto &ExternRelocSec : ExternRelocTable) {
     ExternRelocLen += BTF::SecExternRelocSize;
     ExternRelocLen += ExternRelocSec.second.size() * BTF::BPFExternRelocSize;
   }
 
-  if (OffsetRelocLen)
-    OffsetRelocLen += 4;
+  if (FieldRelocLen)
+    FieldRelocLen += 4;
   if (ExternRelocLen)
     ExternRelocLen += 4;
 
@@ -795,8 +795,8 @@ void BTFDebug::emitBTFExtSection() {
   OS.EmitIntValue(FuncLen, 4);
   OS.EmitIntValue(LineLen, 4);
   OS.EmitIntValue(FuncLen + LineLen, 4);
-  OS.EmitIntValue(OffsetRelocLen, 4);
-  OS.EmitIntValue(FuncLen + LineLen + OffsetRelocLen, 4);
+  OS.EmitIntValue(FieldRelocLen, 4);
+  OS.EmitIntValue(FuncLen + LineLen + FieldRelocLen, 4);
   OS.EmitIntValue(ExternRelocLen, 4);
 
   // Emit func_info table.
@@ -831,19 +831,20 @@ void BTFDebug::emitBTFExtSection() {
     }
   }
 
-  // Emit offset reloc table.
-  if (OffsetRelocLen) {
-    OS.AddComment("OffsetReloc");
-    OS.EmitIntValue(BTF::BPFOffsetRelocSize, 4);
-    for (const auto &OffsetRelocSec : OffsetRelocTable) {
-      OS.AddComment("Offset reloc section string offset=" +
-                    std::to_string(OffsetRelocSec.first));
-      OS.EmitIntValue(OffsetRelocSec.first, 4);
-      OS.EmitIntValue(OffsetRelocSec.second.size(), 4);
-      for (const auto &OffsetRelocInfo : OffsetRelocSec.second) {
-        Asm->EmitLabelReference(OffsetRelocInfo.Label, 4);
-        OS.EmitIntValue(OffsetRelocInfo.TypeID, 4);
-        OS.EmitIntValue(OffsetRelocInfo.OffsetNameOff, 4);
+  // Emit field reloc table.
+  if (FieldRelocLen) {
+    OS.AddComment("FieldReloc");
+    OS.EmitIntValue(BTF::BPFFieldRelocSize, 4);
+    for (const auto &FieldRelocSec : FieldRelocTable) {
+      OS.AddComment("Field reloc section string offset=" +
+                    std::to_string(FieldRelocSec.first));
+      OS.EmitIntValue(FieldRelocSec.first, 4);
+      OS.EmitIntValue(FieldRelocSec.second.size(), 4);
+      for (const auto &FieldRelocInfo : FieldRelocSec.second) {
+        Asm->EmitLabelReference(FieldRelocInfo.Label, 4);
+        OS.EmitIntValue(FieldRelocInfo.TypeID, 4);
+        OS.EmitIntValue(FieldRelocInfo.OffsetNameOff, 4);
+        OS.EmitIntValue(FieldRelocInfo.RelocKind, 4);
       }
     }
   }
@@ -958,23 +959,27 @@ unsigned BTFDebug::populateStructType(const DIType *Ty) {
   return Id;
 }
 
-/// Generate a struct member offset relocation.
-void BTFDebug::generateOffsetReloc(const MachineInstr *MI,
+/// Generate a struct member field relocation.
+void BTFDebug::generateFieldReloc(const MachineInstr *MI,
                                    const MCSymbol *ORSym, DIType *RootTy,
                                    StringRef AccessPattern) {
   unsigned RootId = populateStructType(RootTy);
   size_t FirstDollar = AccessPattern.find_first_of('$');
   size_t FirstColon = AccessPattern.find_first_of(':');
+  size_t SecondColon = AccessPattern.find_first_of(':', FirstColon + 1);
   StringRef IndexPattern = AccessPattern.substr(FirstDollar + 1);
-  StringRef OffsetStr = AccessPattern.substr(FirstColon + 1,
-      FirstDollar - FirstColon);
-
-  BTFOffsetReloc OffsetReloc;
-  OffsetReloc.Label = ORSym;
-  OffsetReloc.OffsetNameOff = addString(IndexPattern);
-  OffsetReloc.TypeID = RootId;
-  AccessOffsets[AccessPattern.str()] = std::stoi(OffsetStr);
-  OffsetRelocTable[SecNameOff].push_back(OffsetReloc);
+  StringRef RelocKindStr = AccessPattern.substr(FirstColon + 1,
+      SecondColon - FirstColon);
+  StringRef PatchImmStr = AccessPattern.substr(SecondColon + 1,
+      FirstDollar - SecondColon);
+
+  BTFFieldReloc FieldReloc;
+  FieldReloc.Label = ORSym;
+  FieldReloc.OffsetNameOff = addString(IndexPattern);
+  FieldReloc.TypeID = RootId;
+  FieldReloc.RelocKind = std::stoull(RelocKindStr);
+  PatchImms[AccessPattern.str()] = std::stoul(PatchImmStr);
+  FieldRelocTable[SecNameOff].push_back(FieldReloc);
 }
 
 void BTFDebug::processLDimm64(const MachineInstr *MI) {
@@ -982,7 +987,7 @@ void BTFDebug::processLDimm64(const MachineInstr *MI) {
   // will generate an .BTF.ext record.
   //
   // If the insn is "r2 = LD_imm64 @__BTF_...",
-  // add this insn into the .BTF.ext OffsetReloc subsection.
+  // add this insn into the .BTF.ext FieldReloc subsection.
   // Relocation looks like:
   //  . SecName:
   //    . InstOffset
@@ -1013,7 +1018,7 @@ void BTFDebug::processLDimm64(const MachineInstr *MI) {
 
       MDNode *MDN = GVar->getMetadata(LLVMContext::MD_preserve_access_index);
       DIType *Ty = dyn_cast<DIType>(MDN);
-      generateOffsetReloc(MI, ORSym, Ty, GVar->getName());
+      generateFieldReloc(MI, ORSym, Ty, GVar->getName());
     } else if (GVar && !GVar->hasInitializer() && GVar->hasExternalLinkage() &&
                GVar->getSection() == BPFCoreSharedInfo::PatchableExtSecName) {
       MCSymbol *ORSym = OS.getContext().createTempSymbol();
@@ -1154,8 +1159,8 @@ bool BTFDebug::InstLower(const MachineInstr *MI, MCInst &OutMI) {
       const GlobalValue *GVal = MO.getGlobal();
       auto *GVar = dyn_cast<GlobalVariable>(GVal);
       if (GVar && GVar->hasAttribute(BPFCoreSharedInfo::AmaAttr)) {
-        // Emit "mov ri, <imm>" for abstract member accesses.
-        int64_t Imm = AccessOffsets[GVar->getName().str()];
+        // Emit "mov ri, <imm>" for patched immediate.
+        uint32_t Imm = PatchImms[GVar->getName().str()];
         OutMI.setOpcode(BPF::MOV_ri);
         OutMI.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
         OutMI.addOperand(MCOperand::createImm(Imm));
index a79527d..eec8614 100644 (file)
@@ -195,7 +195,7 @@ class BTFStringTable {
   /// A mapping from string table offset to the index
   /// of the Table. It is used to avoid putting
   /// duplicated strings in the table.
-  std::unordered_map<uint32_t, uint32_t> OffsetToIdMap;
+  std::map<uint32_t, uint32_t> OffsetToIdMap;
   /// A vector of strings to represent the string table.
   std::vector<std::string> Table;
 
@@ -224,10 +224,11 @@ struct BTFLineInfo {
 };
 
 /// Represent one offset relocation.
-struct BTFOffsetReloc {
+struct BTFFieldReloc {
   const MCSymbol *Label;  ///< MCSymbol identifying insn for the reloc
   uint32_t TypeID;        ///< Type ID
   uint32_t OffsetNameOff; ///< The string to traverse types
+  uint32_t RelocKind;     ///< What to patch the instruction
 };
 
 /// Represent one extern relocation.
@@ -249,12 +250,12 @@ class BTFDebug : public DebugHandlerBase {
   std::unordered_map<const DIType *, uint32_t> DIToIdMap;
   std::map<uint32_t, std::vector<BTFFuncInfo>> FuncInfoTable;
   std::map<uint32_t, std::vector<BTFLineInfo>> LineInfoTable;
-  std::map<uint32_t, std::vector<BTFOffsetReloc>> OffsetRelocTable;
+  std::map<uint32_t, std::vector<BTFFieldReloc>> FieldRelocTable;
   std::map<uint32_t, std::vector<BTFExternReloc>> ExternRelocTable;
   StringMap<std::vector<std::string>> FileContent;
   std::map<std::string, std::unique_ptr<BTFKindDataSec>> DataSecEntries;
   std::vector<BTFTypeStruct *> StructTypes;
-  std::map<std::string, int64_t> AccessOffsets;
+  std::map<std::string, uint32_t> PatchImms;
   std::map<StringRef, std::pair<bool, std::vector<BTFTypeDerived *>>>
       FixupDerivedTypes;
 
@@ -300,7 +301,7 @@ class BTFDebug : public DebugHandlerBase {
   void processGlobals(bool ProcessingMapDef);
 
   /// Generate one offset relocation record.
-  void generateOffsetReloc(const MachineInstr *MI, const MCSymbol *ORSym,
+  void generateFieldReloc(const MachineInstr *MI, const MCSymbol *ORSym,
                            DIType *RootTy, StringRef AccessPattern);
 
   /// Populating unprocessed struct type.
index fe2c196..cf5511e 100644 (file)
@@ -28,12 +28,13 @@ entry:
 ; CHECK:       exit
 ;
 ; CHECK:      .section        .BTF.ext,"",@progbits
-; CHECK:      .long   12                      # OffsetReloc
-; CHECK-NEXT: .long   20                      # Offset reloc section string offset=20
+; CHECK:      .long   16                      # FieldReloc
+; CHECK-NEXT: .long   20                      # Field reloc section string offset=20
 ; CHECK-NEXT: .long   1
 ; CHECK-NEXT: .long   [[RELOC]]
 ; CHECK-NEXT: .long   2
 ; CHECK-NEXT: .long   26
+; CHECK-NEXT: .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-1.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-1.ll
new file mode 100644 (file)
index 0000000..d8be7b8
--- /dev/null
@@ -0,0 +1,148 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1:7; int a2:4; int a3:5; int a4:16;} __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_BYTE_SIZE = 1, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_BYTE_SIZE);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_BYTE_SIZE);
+;     unsigned r3 = __builtin_preserve_field_info(arg->b2.a3, FIELD_BYTE_SIZE);
+;     unsigned r4 = __builtin_preserve_field_info(arg->b2.a4, FIELD_BYTE_SIZE);
+;     /* r1: 4, r2: 4, r3: 4, r4: 4 */
+;     return r1 + r2 + r3 + r4;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { i32 }
+%struct.s1 = type { i32 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !28, metadata !DIExpression()), !dbg !33
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !34, !llvm.preserve.access.index !16
+  %b2 = bitcast %union.u1* %0 to %struct.s1*, !dbg !34
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !35, !llvm.preserve.access.index !21
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 1), !dbg !36
+  call void @llvm.dbg.value(metadata i32 %2, metadata !29, metadata !DIExpression()), !dbg !33
+  %3 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 1), !dbg !37, !llvm.preserve.access.index !21
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %3, i64 1), !dbg !38
+  call void @llvm.dbg.value(metadata i32 %4, metadata !30, metadata !DIExpression()), !dbg !33
+  %5 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 2), !dbg !39, !llvm.preserve.access.index !21
+  %6 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %5, i64 1), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %6, metadata !31, metadata !DIExpression()), !dbg !33
+  %7 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 3), !dbg !41, !llvm.preserve.access.index !21
+  %8 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %7, i64 1), !dbg !42
+  call void @llvm.dbg.value(metadata i32 %8, metadata !32, metadata !DIExpression()), !dbg !33
+  %add = add i32 %4, %2, !dbg !43
+  %add4 = add i32 %add, %6, !dbg !44
+  %add5 = add i32 %add4, %8, !dbg !45
+  ret i32 %add5, !dbg !46
+}
+
+; CHECK:             r1 = 4
+; CHECK:             r0 = 4
+; CHECK:             r0 += r1
+; CHECK:             r1 = 4
+; CHECK:             r0 += r1
+; CHECK:             r1 = 4
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=43
+; CHECK:             .ascii  "0:1:0"                 # string offset=49
+; CHECK:             .ascii  "0:1:1"                 # string offset=92
+; CHECK:             .ascii  "0:1:2"                 # string offset=98
+; CHECK:             .ascii  "0:1:3"                 # string offset=104
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   43                      # Field reloc section string offset=43
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   49
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   92
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   98
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   104
+; CHECK-NEXT:        .long   1
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_BYTE_SIZE", value: 1, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !27)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 32, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 32)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 32, elements: !22)
+!22 = !{!23, !24, !25, !26}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 7, flags: DIFlagBitField, extraData: i64 0)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !14, size: 4, offset: 7, flags: DIFlagBitField, extraData: i64 0)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !21, file: !1, line: 1, baseType: !14, size: 5, offset: 11, flags: DIFlagBitField, extraData: i64 0)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "a4", scope: !21, file: !1, line: 1, baseType: !14, size: 16, offset: 16, flags: DIFlagBitField, extraData: i64 0)
+!27 = !{!28, !29, !30, !31, !32}
+!28 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!29 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!30 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!31 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 7, type: !4)
+!32 = !DILocalVariable(name: "r4", scope: !11, file: !1, line: 8, type: !4)
+!33 = !DILocation(line: 0, scope: !11)
+!34 = !DILocation(line: 5, column: 52, scope: !11)
+!35 = !DILocation(line: 5, column: 55, scope: !11)
+!36 = !DILocation(line: 5, column: 17, scope: !11)
+!37 = !DILocation(line: 6, column: 55, scope: !11)
+!38 = !DILocation(line: 6, column: 17, scope: !11)
+!39 = !DILocation(line: 7, column: 55, scope: !11)
+!40 = !DILocation(line: 7, column: 17, scope: !11)
+!41 = !DILocation(line: 8, column: 55, scope: !11)
+!42 = !DILocation(line: 8, column: 17, scope: !11)
+!43 = !DILocation(line: 10, column: 13, scope: !11)
+!44 = !DILocation(line: 10, column: 18, scope: !11)
+!45 = !DILocation(line: 10, column: 23, scope: !11)
+!46 = !DILocation(line: 10, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-2.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-2.ll
new file mode 100644 (file)
index 0000000..291d79f
--- /dev/null
@@ -0,0 +1,138 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1; char a2; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_BYTE_SIZE = 1, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_SIZE);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a1, FIELD_BYTE_SIZE);
+;     unsigned r3 = __builtin_preserve_field_info(arg->b2.a2, FIELD_BYTE_SIZE);
+;     /* r1: 8, r2: 4, r3: 1 */
+;     return r1 + r2 + r3;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { i32, i8 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !27, metadata !DIExpression()), !dbg !31
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !32, !llvm.preserve.access.index !16
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !32
+  %1 = tail call i32 @llvm.bpf.preserve.field.info.p0s_struct.s1s(%struct.s1* %b2, i64 1), !dbg !33
+  call void @llvm.dbg.value(metadata i32 %1, metadata !28, metadata !DIExpression()), !dbg !31
+  %2 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !34, !llvm.preserve.access.index !21
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %2, i64 1), !dbg !35
+  call void @llvm.dbg.value(metadata i32 %3, metadata !29, metadata !DIExpression()), !dbg !31
+  %4 = tail call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 1), !dbg !36, !llvm.preserve.access.index !21
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %4, i64 1), !dbg !37
+  call void @llvm.dbg.value(metadata i32 %5, metadata !30, metadata !DIExpression()), !dbg !31
+  %add = add i32 %3, %1, !dbg !38
+  %add3 = add i32 %add, %5, !dbg !39
+  ret i32 %add3, !dbg !40
+}
+
+; CHECK:             r1 = 8
+; CHECK:             r0 = 4
+; CHECK:             r0 += r1
+; CHECK:             r1 = 1
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=42
+; CHECK:             .ascii  "0:1"                   # string offset=48
+; CHECK:             .ascii  "0:1:0"                 # string offset=89
+; CHECK:             .ascii  "0:1:1"                 # string offset=95
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   42                      # Field reloc section string offset=42
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   48
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   89
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   95
+; CHECK-NEXT:        .long   1
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0s_struct.s1s(%struct.s1*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_BYTE_SIZE", value: 1, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !26)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 64, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 64)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 64, elements: !22)
+!22 = !{!23, !24}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 32)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !25, size: 8, offset: 32)
+!25 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!26 = !{!27, !28, !29, !30}
+!27 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!28 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!29 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!30 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 7, type: !4)
+!31 = !DILocation(line: 0, scope: !11)
+!32 = !DILocation(line: 5, column: 52, scope: !11)
+!33 = !DILocation(line: 5, column: 17, scope: !11)
+!34 = !DILocation(line: 6, column: 55, scope: !11)
+!35 = !DILocation(line: 6, column: 17, scope: !11)
+!36 = !DILocation(line: 7, column: 55, scope: !11)
+!37 = !DILocation(line: 7, column: 17, scope: !11)
+!38 = !DILocation(line: 9, column: 13, scope: !11)
+!39 = !DILocation(line: 9, column: 18, scope: !11)
+!40 = !DILocation(line: 9, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-3.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-byte-size-3.ll
new file mode 100644 (file)
index 0000000..fd517bf
--- /dev/null
@@ -0,0 +1,130 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1[10][10]; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_BYTE_SIZE = 1, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1[5], FIELD_BYTE_SIZE);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a1[5][5], FIELD_BYTE_SIZE);
+;     /* r1: 40, r2: 4 */
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { [10 x [10 x i32]] }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !18 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !31, metadata !DIExpression()), !dbg !34
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !35, !llvm.preserve.access.index !22
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !35
+  %1 = tail call [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !36, !llvm.preserve.access.index !27
+  %2 = tail call [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]* %1, i32 1, i32 5), !dbg !37, !llvm.preserve.access.index !8
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0a10i32([10 x i32]* %2, i64 1), !dbg !38
+  call void @llvm.dbg.value(metadata i32 %3, metadata !32, metadata !DIExpression()), !dbg !34
+  %4 = tail call i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]* %2, i32 1, i32 5), !dbg !39, !llvm.preserve.access.index !12
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %4, i64 1), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %5, metadata !33, metadata !DIExpression()), !dbg !34
+  %add = add i32 %5, %3, !dbg !41
+  ret i32 %add, !dbg !42
+}
+
+; CHECK:             r1 = 40
+; CHECK:             r0 = 4
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=54
+; CHECK:             .ascii  "0:1:0:5"               # string offset=60
+; CHECK:             .ascii  "0:1:0:5:5"             # string offset=105
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   54                      # Field reloc section string offset=54
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   60
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   105
+; CHECK-NEXT:        .long   1
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0a10i32([10 x i32]*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !7, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_BYTE_SIZE", value: 1, isUnsigned: true)
+!7 = !{!8, !12}
+!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 3200, elements: !10)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !{!11, !11}
+!11 = !DISubrange(count: 10)
+!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 320, elements: !13)
+!13 = !{!11}
+!14 = !{i32 2, !"Dwarf Version", i32 4}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)"}
+!18 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !19, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !30)
+!19 = !DISubroutineType(types: !20)
+!20 = !{!9, !21}
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64)
+!22 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 3200, elements: !23)
+!23 = !{!24, !25}
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !22, file: !1, line: 2, baseType: !9, size: 32)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !22, file: !1, line: 2, baseType: !26, size: 3200)
+!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !27)
+!27 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 3200, elements: !28)
+!28 = !{!29}
+!29 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !27, file: !1, line: 1, baseType: !8, size: 3200)
+!30 = !{!31, !32, !33}
+!31 = !DILocalVariable(name: "arg", arg: 1, scope: !18, file: !1, line: 4, type: !21)
+!32 = !DILocalVariable(name: "r1", scope: !18, file: !1, line: 5, type: !4)
+!33 = !DILocalVariable(name: "r2", scope: !18, file: !1, line: 6, type: !4)
+!34 = !DILocation(line: 0, scope: !18)
+!35 = !DILocation(line: 5, column: 52, scope: !18)
+!36 = !DILocation(line: 5, column: 55, scope: !18)
+!37 = !DILocation(line: 5, column: 47, scope: !18)
+!38 = !DILocation(line: 5, column: 17, scope: !18)
+!39 = !DILocation(line: 6, column: 47, scope: !18)
+!40 = !DILocation(line: 6, column: 17, scope: !18)
+!41 = !DILocation(line: 8, column: 13, scope: !18)
+!42 = !DILocation(line: 8, column: 3, scope: !18)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-1.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-1.ll
new file mode 100644 (file)
index 0000000..f140dcb
--- /dev/null
@@ -0,0 +1,162 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef unsigned __uint;
+;   struct s1 { int a1; __uint a2:9; __uint a3:4; };
+;   union u1 { int b1; __uint b2:9; __uint b3:4; };
+;   enum { FIELD_EXISTENCE = 2, };
+;   int test(struct s1 *arg1, union u1 *arg2) {
+;     unsigned r1 = __builtin_preserve_field_info(arg1->a1, FIELD_EXISTENCE);
+;     unsigned r2 = __builtin_preserve_field_info(arg1->a3, FIELD_EXISTENCE);
+;     unsigned r3 = __builtin_preserve_field_info(arg2->b1, FIELD_EXISTENCE);
+;     unsigned r4 = __builtin_preserve_field_info(arg2->b3, FIELD_EXISTENCE);
+;     return r1 + r2 + r3 + r4;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.s1 = type { i32, i16 }
+%union.u1 = type { i32 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%struct.s1* %arg1, %union.u1* %arg2) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %struct.s1* %arg1, metadata !29, metadata !DIExpression()), !dbg !35
+  call void @llvm.dbg.value(metadata %union.u1* %arg2, metadata !30, metadata !DIExpression()), !dbg !35
+  %0 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %arg1, i32 0, i32 0), !dbg !36, !llvm.preserve.access.index !16
+  %1 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %0, i64 2), !dbg !37
+  call void @llvm.dbg.value(metadata i32 %1, metadata !31, metadata !DIExpression()), !dbg !35
+  %2 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %arg1, i32 1, i32 2), !dbg !38, !llvm.preserve.access.index !16
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %2, i64 2), !dbg !39
+  call void @llvm.dbg.value(metadata i32 %3, metadata !32, metadata !DIExpression()), !dbg !35
+  %4 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg2, i32 0), !dbg !40, !llvm.preserve.access.index !23
+  %b1 = getelementptr inbounds %union.u1, %union.u1* %4, i64 0, i32 0, !dbg !40
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %b1, i64 2), !dbg !41
+  call void @llvm.dbg.value(metadata i32 %5, metadata !33, metadata !DIExpression()), !dbg !35
+  %6 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_union.u1s(%union.u1* %arg2, i32 0, i32 2), !dbg !42, !llvm.preserve.access.index !23
+  %7 = bitcast i32* %6 to i8*, !dbg !42
+  %8 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %7, i64 2), !dbg !43
+  call void @llvm.dbg.value(metadata i32 %8, metadata !34, metadata !DIExpression()), !dbg !35
+  %add = add i32 %3, %1, !dbg !44
+  %add1 = add i32 %add, %5, !dbg !45
+  %add2 = add i32 %add1, %8, !dbg !46
+  ret i32 %add2, !dbg !47
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 1
+; CHECK:             r0 += r1
+; CHECK:             r1 = 1
+; CHECK:             r0 += r1
+; CHECK:             r1 = 1
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_STRUCT(id = 2)
+; CHECK:             .long   37                      # BTF_KIND_UNION(id = 7)
+; CHECK:             .ascii  "s1"                    # string offset=1
+; CHECK:             .ascii  "u1"                    # string offset=37
+; CHECK:             .ascii  ".text"                 # string offset=64
+; CHECK:             .ascii  "0:0"                   # string offset=70
+; CHECK:             .ascii  "0:2"                   # string offset=111
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   64                      # Field reloc section string offset=64
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   70
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   111
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   7
+; CHECK-NEXT:        .long   70
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   7
+; CHECK-NEXT:        .long   111
+; CHECK-NEXT:        .long   2
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_union.u1s(%union.u1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 4, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_EXISTENCE", value: 2, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !28)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15, !22}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 2, size: 64, elements: !17)
+!17 = !{!18, !19, !21}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !16, file: !1, line: 2, baseType: !20, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint", file: !1, line: 1, baseType: !4)
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !16, file: !1, line: 2, baseType: !20, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64)
+!23 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 3, size: 32, elements: !24)
+!24 = !{!25, !26, !27}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !23, file: !1, line: 3, baseType: !14, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !23, file: !1, line: 3, baseType: !20, size: 9, flags: DIFlagBitField, extraData: i64 0)
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "b3", scope: !23, file: !1, line: 3, baseType: !20, size: 4, flags: DIFlagBitField, extraData: i64 0)
+!28 = !{!29, !30, !31, !32, !33, !34}
+!29 = !DILocalVariable(name: "arg1", arg: 1, scope: !11, file: !1, line: 5, type: !15)
+!30 = !DILocalVariable(name: "arg2", arg: 2, scope: !11, file: !1, line: 5, type: !22)
+!31 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 6, type: !4)
+!32 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 7, type: !4)
+!33 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 8, type: !4)
+!34 = !DILocalVariable(name: "r4", scope: !11, file: !1, line: 9, type: !4)
+!35 = !DILocation(line: 0, scope: !11)
+!36 = !DILocation(line: 6, column: 53, scope: !11)
+!37 = !DILocation(line: 6, column: 17, scope: !11)
+!38 = !DILocation(line: 7, column: 53, scope: !11)
+!39 = !DILocation(line: 7, column: 17, scope: !11)
+!40 = !DILocation(line: 8, column: 53, scope: !11)
+!41 = !DILocation(line: 8, column: 17, scope: !11)
+!42 = !DILocation(line: 9, column: 53, scope: !11)
+!43 = !DILocation(line: 9, column: 17, scope: !11)
+!44 = !DILocation(line: 10, column: 13, scope: !11)
+!45 = !DILocation(line: 10, column: 18, scope: !11)
+!46 = !DILocation(line: 10, column: 23, scope: !11)
+!47 = !DILocation(line: 10, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-2.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-2.ll
new file mode 100644 (file)
index 0000000..186af86
--- /dev/null
@@ -0,0 +1,121 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef unsigned __uint;
+;   struct s1 { int a1; __uint a2:9; __uint a3:4; };
+;   union u1 { int b1; struct s1 b2; };
+;   enum { FIELD_EXISTENCE = 2, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_EXISTENCE);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a3, FIELD_EXISTENCE);
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { i32, i16 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !27, metadata !DIExpression()), !dbg !30
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !31, !llvm.preserve.access.index !16
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !31
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !32, !llvm.preserve.access.index !20
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 2), !dbg !33
+  call void @llvm.dbg.value(metadata i32 %2, metadata !28, metadata !DIExpression()), !dbg !30
+  %3 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 2), !dbg !34, !llvm.preserve.access.index !20
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %3, i64 2), !dbg !35
+  call void @llvm.dbg.value(metadata i32 %4, metadata !29, metadata !DIExpression()), !dbg !30
+  %add = add i32 %4, %2, !dbg !36
+  ret i32 %add, !dbg !37
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 1
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=55
+; CHECK:             .ascii  "0:1:0"                 # string offset=61
+; CHECK:             .ascii  "0:1:2"                 # string offset=104
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   55                      # Field reloc section string offset=55
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   61
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   104
+; CHECK-NEXT:        .long   2
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 4, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_EXISTENCE", value: 2, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !26)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 3, size: 64, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 3, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 3, baseType: !20, size: 64)
+!20 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 2, size: 64, elements: !21)
+!21 = !{!22, !23, !25}
+!22 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !20, file: !1, line: 2, baseType: !14, size: 32)
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !20, file: !1, line: 2, baseType: !24, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint", file: !1, line: 1, baseType: !4)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !20, file: !1, line: 2, baseType: !24, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!26 = !{!27, !28, !29}
+!27 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 5, type: !15)
+!28 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 6, type: !4)
+!29 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 7, type: !4)
+!30 = !DILocation(line: 0, scope: !11)
+!31 = !DILocation(line: 6, column: 52, scope: !11)
+!32 = !DILocation(line: 6, column: 55, scope: !11)
+!33 = !DILocation(line: 6, column: 17, scope: !11)
+!34 = !DILocation(line: 7, column: 55, scope: !11)
+!35 = !DILocation(line: 7, column: 17, scope: !11)
+!36 = !DILocation(line: 8, column: 13, scope: !11)
+!37 = !DILocation(line: 8, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-3.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-existence-3.ll
new file mode 100644 (file)
index 0000000..4c0a93a
--- /dev/null
@@ -0,0 +1,129 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1[10][10]; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_EXISTENCE = 2, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1[5], FIELD_EXISTENCE);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a1[5][5], FIELD_EXISTENCE);
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { [10 x [10 x i32]] }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !18 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !31, metadata !DIExpression()), !dbg !34
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !35, !llvm.preserve.access.index !22
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !35
+  %1 = tail call [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !36, !llvm.preserve.access.index !27
+  %2 = tail call [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]* %1, i32 1, i32 5), !dbg !37, !llvm.preserve.access.index !8
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0a10i32([10 x i32]* %2, i64 2), !dbg !38
+  call void @llvm.dbg.value(metadata i32 %3, metadata !32, metadata !DIExpression()), !dbg !34
+  %4 = tail call i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]* %2, i32 1, i32 5), !dbg !39, !llvm.preserve.access.index !12
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %4, i64 2), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %5, metadata !33, metadata !DIExpression()), !dbg !34
+  %add = add i32 %5, %3, !dbg !41
+  ret i32 %add, !dbg !42
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 1
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=54
+; CHECK:             .ascii  "0:1:0:5"               # string offset=60
+; CHECK:             .ascii  "0:1:0:5:5"             # string offset=105
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   54                      # Field reloc section string offset=54
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   60
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   105
+; CHECK-NEXT:        .long   2
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0a10i32([10 x i32]*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !7, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_EXISTENCE", value: 2, isUnsigned: true)
+!7 = !{!8, !12}
+!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 3200, elements: !10)
+!9 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!10 = !{!11, !11}
+!11 = !DISubrange(count: 10)
+!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 320, elements: !13)
+!13 = !{!11}
+!14 = !{i32 2, !"Dwarf Version", i32 4}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)"}
+!18 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !19, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !30)
+!19 = !DISubroutineType(types: !20)
+!20 = !{!9, !21}
+!21 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !22, size: 64)
+!22 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 3200, elements: !23)
+!23 = !{!24, !25}
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !22, file: !1, line: 2, baseType: !9, size: 32)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !22, file: !1, line: 2, baseType: !26, size: 3200)
+!26 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !27)
+!27 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 3200, elements: !28)
+!28 = !{!29}
+!29 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !27, file: !1, line: 1, baseType: !8, size: 3200)
+!30 = !{!31, !32, !33}
+!31 = !DILocalVariable(name: "arg", arg: 1, scope: !18, file: !1, line: 4, type: !21)
+!32 = !DILocalVariable(name: "r1", scope: !18, file: !1, line: 5, type: !4)
+!33 = !DILocalVariable(name: "r2", scope: !18, file: !1, line: 6, type: !4)
+!34 = !DILocation(line: 0, scope: !18)
+!35 = !DILocation(line: 5, column: 52, scope: !18)
+!36 = !DILocation(line: 5, column: 55, scope: !18)
+!37 = !DILocation(line: 5, column: 47, scope: !18)
+!38 = !DILocation(line: 5, column: 17, scope: !18)
+!39 = !DILocation(line: 6, column: 47, scope: !18)
+!40 = !DILocation(line: 6, column: 17, scope: !18)
+!41 = !DILocation(line: 7, column: 13, scope: !18)
+!42 = !DILocation(line: 7, column: 3, scope: !18)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-1.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-1.ll
new file mode 100644 (file)
index 0000000..ce4074c
--- /dev/null
@@ -0,0 +1,153 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB %s
+; Source code:
+;   typedef struct s1 { int a1:7; int a2:4; int a3:5; int a4:16;} __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_LSHIFT_U64 = 4, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_LSHIFT_U64);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_LSHIFT_U64);
+;     unsigned r3 = __builtin_preserve_field_info(arg->b2.a3, FIELD_LSHIFT_U64);
+;     unsigned r4 = __builtin_preserve_field_info(arg->b2.a4, FIELD_LSHIFT_U64);
+;     /* big endian:    r1: 32, r2: 39, r3: 43, r4: 48 */
+;     /* little endian: r1: 57, r2: 53, r3: 48, r4: 32 */
+;     return r1 + r2 + r3 + r4;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { i32 }
+%struct.s1 = type { i32 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !28, metadata !DIExpression()), !dbg !33
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !34, !llvm.preserve.access.index !16
+  %b2 = bitcast %union.u1* %0 to %struct.s1*, !dbg !34
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !35, !llvm.preserve.access.index !21
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 4), !dbg !36
+  call void @llvm.dbg.value(metadata i32 %2, metadata !29, metadata !DIExpression()), !dbg !33
+  %3 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 1), !dbg !37, !llvm.preserve.access.index !21
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %3, i64 4), !dbg !38
+  call void @llvm.dbg.value(metadata i32 %4, metadata !30, metadata !DIExpression()), !dbg !33
+  %5 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 2), !dbg !39, !llvm.preserve.access.index !21
+  %6 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %5, i64 4), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %6, metadata !31, metadata !DIExpression()), !dbg !33
+  %7 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 3), !dbg !41, !llvm.preserve.access.index !21
+  %8 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %7, i64 4), !dbg !42
+  call void @llvm.dbg.value(metadata i32 %8, metadata !32, metadata !DIExpression()), !dbg !33
+  %add = add i32 %4, %2, !dbg !43
+  %add4 = add i32 %add, %6, !dbg !44
+  %add5 = add i32 %add4, %8, !dbg !45
+  ret i32 %add5, !dbg !46
+}
+
+; CHECK-EL:          r1 = 57
+; CHECK-EL:          r0 = 53
+; CHECK-EB:          r1 = 32
+; CHECK-EB:          r0 = 39
+; CHECK:             r0 += r1
+; CHECK-EL:          r1 = 48
+; CHECK-EB:          r1 = 43
+; CHECK:             r0 += r1
+; CHECK-EL:          r1 = 32
+; CHECK-EB:          r1 = 48
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=43
+; CHECK:             .ascii  "0:1:0"                 # string offset=49
+; CHECK:             .ascii  "0:1:1"                 # string offset=92
+; CHECK:             .ascii  "0:1:2"                 # string offset=98
+; CHECK:             .ascii  "0:1:3"                 # string offset=104
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   43                      # Field reloc section string offset=43
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   49
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   92
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   98
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   104
+; CHECK-NEXT:        .long   4
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 5635073377f153f7f2ff9b34c77af3c79885ff4a)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_LSHIFT_U64", value: 4, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 5635073377f153f7f2ff9b34c77af3c79885ff4a)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !27)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 32, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 32)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 32, elements: !22)
+!22 = !{!23, !24, !25, !26}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 7, flags: DIFlagBitField, extraData: i64 0)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !14, size: 4, offset: 7, flags: DIFlagBitField, extraData: i64 0)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !21, file: !1, line: 1, baseType: !14, size: 5, offset: 11, flags: DIFlagBitField, extraData: i64 0)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "a4", scope: !21, file: !1, line: 1, baseType: !14, size: 16, offset: 16, flags: DIFlagBitField, extraData: i64 0)
+!27 = !{!28, !29, !30, !31, !32}
+!28 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!29 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!30 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!31 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 7, type: !4)
+!32 = !DILocalVariable(name: "r4", scope: !11, file: !1, line: 8, type: !4)
+!33 = !DILocation(line: 0, scope: !11)
+!34 = !DILocation(line: 5, column: 52, scope: !11)
+!35 = !DILocation(line: 5, column: 55, scope: !11)
+!36 = !DILocation(line: 5, column: 17, scope: !11)
+!37 = !DILocation(line: 6, column: 55, scope: !11)
+!38 = !DILocation(line: 6, column: 17, scope: !11)
+!39 = !DILocation(line: 7, column: 55, scope: !11)
+!40 = !DILocation(line: 7, column: 17, scope: !11)
+!41 = !DILocation(line: 8, column: 55, scope: !11)
+!42 = !DILocation(line: 8, column: 17, scope: !11)
+!43 = !DILocation(line: 11, column: 13, scope: !11)
+!44 = !DILocation(line: 11, column: 18, scope: !11)
+!45 = !DILocation(line: 11, column: 23, scope: !11)
+!46 = !DILocation(line: 11, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-2.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-lshift-2.ll
new file mode 100644 (file)
index 0000000..83d1b3e
--- /dev/null
@@ -0,0 +1,122 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1; short a2; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_LSHIFT_U64 = 4, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_LSHIFT_U64);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_LSHIFT_U64);
+;     /* big endian:    r1: 32, r2: 48 */
+;     /* little endian: r1: 32, r2: 48 */
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { i32, i16 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !27, metadata !DIExpression()), !dbg !30
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !31, !llvm.preserve.access.index !16
+  %b2 = getelementptr %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !31
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !32, !llvm.preserve.access.index !21
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 4), !dbg !33
+  call void @llvm.dbg.value(metadata i32 %2, metadata !28, metadata !DIExpression()), !dbg !30
+  %3 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 1), !dbg !34, !llvm.preserve.access.index !21
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %3, i64 4), !dbg !35
+  call void @llvm.dbg.value(metadata i32 %4, metadata !29, metadata !DIExpression()), !dbg !30
+  %add = add i32 %4, %2, !dbg !36
+  ret i32 %add, !dbg !37
+}
+
+; CHECK:             r1 = 32
+; CHECK:             r0 = 48
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=43
+; CHECK:             .ascii  "0:1:0"                 # string offset=49
+; CHECK:             .ascii  "0:1:1"                 # string offset=92
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   43                      # Field reloc section string offset=43
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   49
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   92
+; CHECK-NEXT:        .long   4
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 5635073377f153f7f2ff9b34c77af3c79885ff4a)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_LSHIFT_U64", value: 4, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 5635073377f153f7f2ff9b34c77af3c79885ff4a)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !26)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 64, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 64)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 64, elements: !22)
+!22 = !{!23, !24}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 32)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !25, size: 16, offset: 32)
+!25 = !DIBasicType(name: "short", size: 16, encoding: DW_ATE_signed)
+!26 = !{!27, !28, !29}
+!27 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!28 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!29 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!30 = !DILocation(line: 0, scope: !11)
+!31 = !DILocation(line: 5, column: 52, scope: !11)
+!32 = !DILocation(line: 5, column: 55, scope: !11)
+!33 = !DILocation(line: 5, column: 17, scope: !11)
+!34 = !DILocation(line: 6, column: 55, scope: !11)
+!35 = !DILocation(line: 6, column: 17, scope: !11)
+!36 = !DILocation(line: 9, column: 13, scope: !11)
+!37 = !DILocation(line: 9, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-1.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-1.ll
new file mode 100644 (file)
index 0000000..70791d3
--- /dev/null
@@ -0,0 +1,148 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1:7; int a2:4; int a3:5; int a4:16;} __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_RSHIFT_U64 = 5, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_RSHIFT_U64);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_RSHIFT_U64);
+;     unsigned r3 = __builtin_preserve_field_info(arg->b2.a3, FIELD_RSHIFT_U64);
+;     unsigned r4 = __builtin_preserve_field_info(arg->b2.a4, FIELD_RSHIFT_U64);
+;     /* r1: 57, r2: 60, r3: 59, r4: 48 */
+;     return r1 + r2 + r3 + r4;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { i32 }
+%struct.s1 = type { i32 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !28, metadata !DIExpression()), !dbg !33
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !34, !llvm.preserve.access.index !16
+  %b2 = bitcast %union.u1* %0 to %struct.s1*, !dbg !34
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !35, !llvm.preserve.access.index !21
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 5), !dbg !36
+  call void @llvm.dbg.value(metadata i32 %2, metadata !29, metadata !DIExpression()), !dbg !33
+  %3 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 1), !dbg !37, !llvm.preserve.access.index !21
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %3, i64 5), !dbg !38
+  call void @llvm.dbg.value(metadata i32 %4, metadata !30, metadata !DIExpression()), !dbg !33
+  %5 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 2), !dbg !39, !llvm.preserve.access.index !21
+  %6 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %5, i64 5), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %6, metadata !31, metadata !DIExpression()), !dbg !33
+  %7 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 3), !dbg !41, !llvm.preserve.access.index !21
+  %8 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %7, i64 5), !dbg !42
+  call void @llvm.dbg.value(metadata i32 %8, metadata !32, metadata !DIExpression()), !dbg !33
+  %add = add i32 %4, %2, !dbg !43
+  %add4 = add i32 %add, %6, !dbg !44
+  %add5 = add i32 %add4, %8, !dbg !45
+  ret i32 %add5, !dbg !46
+}
+
+; CHECK:             r1 = 57
+; CHECK:             r0 = 60
+; CHECK:             r0 += r1
+; CHECK:             r1 = 59
+; CHECK:             r0 += r1
+; CHECK:             r1 = 48
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=43
+; CHECK:             .ascii  "0:1:0"                 # string offset=49
+; CHECK:             .ascii  "0:1:1"                 # string offset=92
+; CHECK:             .ascii  "0:1:2"                 # string offset=98
+; CHECK:             .ascii  "0:1:3"                 # string offset=104
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   43                      # Field reloc section string offset=43
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   49
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   92
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   98
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   104
+; CHECK-NEXT:        .long   5
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_RSHIFT_U64", value: 5, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !27)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 32, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 32)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 32, elements: !22)
+!22 = !{!23, !24, !25, !26}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 7, flags: DIFlagBitField, extraData: i64 0)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !14, size: 4, offset: 7, flags: DIFlagBitField, extraData: i64 0)
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !21, file: !1, line: 1, baseType: !14, size: 5, offset: 11, flags: DIFlagBitField, extraData: i64 0)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "a4", scope: !21, file: !1, line: 1, baseType: !14, size: 16, offset: 16, flags: DIFlagBitField, extraData: i64 0)
+!27 = !{!28, !29, !30, !31, !32}
+!28 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!29 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!30 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!31 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 7, type: !4)
+!32 = !DILocalVariable(name: "r4", scope: !11, file: !1, line: 8, type: !4)
+!33 = !DILocation(line: 0, scope: !11)
+!34 = !DILocation(line: 5, column: 52, scope: !11)
+!35 = !DILocation(line: 5, column: 55, scope: !11)
+!36 = !DILocation(line: 5, column: 17, scope: !11)
+!37 = !DILocation(line: 6, column: 55, scope: !11)
+!38 = !DILocation(line: 6, column: 17, scope: !11)
+!39 = !DILocation(line: 7, column: 55, scope: !11)
+!40 = !DILocation(line: 7, column: 17, scope: !11)
+!41 = !DILocation(line: 8, column: 55, scope: !11)
+!42 = !DILocation(line: 8, column: 17, scope: !11)
+!43 = !DILocation(line: 10, column: 13, scope: !11)
+!44 = !DILocation(line: 10, column: 18, scope: !11)
+!45 = !DILocation(line: 10, column: 23, scope: !11)
+!46 = !DILocation(line: 10, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-2.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-2.ll
new file mode 100644 (file)
index 0000000..657b19c
--- /dev/null
@@ -0,0 +1,121 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { int a1; char a2; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_RSHIFT_U64 = 5, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_RSHIFT_U64);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_RSHIFT_U64);
+;     /* r1: 32, r2: 56 */
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { i32, i8 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !27, metadata !DIExpression()), !dbg !30
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !31, !llvm.preserve.access.index !16
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !31
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !32, !llvm.preserve.access.index !21
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 5), !dbg !33
+  call void @llvm.dbg.value(metadata i32 %2, metadata !28, metadata !DIExpression()), !dbg !30
+  %3 = tail call i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 1), !dbg !34, !llvm.preserve.access.index !21
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %3, i64 5), !dbg !35
+  call void @llvm.dbg.value(metadata i32 %4, metadata !29, metadata !DIExpression()), !dbg !30
+  %add = add i32 %4, %2, !dbg !36
+  ret i32 %add, !dbg !37
+}
+
+; CHECK:             r1 = 32
+; CHECK:             r0 = 56
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=42
+; CHECK:             .ascii  "0:1:0"                 # string offset=48
+; CHECK:             .ascii  "0:1:1"                 # string offset=91
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   42                      # Field reloc section string offset=42
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   48
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   91
+; CHECK-NEXT:        .long   5
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.preserve.struct.access.index.p0i8.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_RSHIFT_U64", value: 5, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !12, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !26)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 64, elements: !17)
+!17 = !{!18, !19}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !16, file: !1, line: 2, baseType: !20, size: 64)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !21)
+!21 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 64, elements: !22)
+!22 = !{!23, !24}
+!23 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !21, file: !1, line: 1, baseType: !14, size: 32)
+!24 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !21, file: !1, line: 1, baseType: !25, size: 8, offset: 32)
+!25 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!26 = !{!27, !28, !29}
+!27 = !DILocalVariable(name: "arg", arg: 1, scope: !11, file: !1, line: 4, type: !15)
+!28 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 5, type: !4)
+!29 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 6, type: !4)
+!30 = !DILocation(line: 0, scope: !11)
+!31 = !DILocation(line: 5, column: 52, scope: !11)
+!32 = !DILocation(line: 5, column: 55, scope: !11)
+!33 = !DILocation(line: 5, column: 17, scope: !11)
+!34 = !DILocation(line: 6, column: 55, scope: !11)
+!35 = !DILocation(line: 6, column: 17, scope: !11)
+!36 = !DILocation(line: 8, column: 13, scope: !11)
+!37 = !DILocation(line: 8, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-3.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-rshift-3.ll
new file mode 100644 (file)
index 0000000..2f9cf2f
--- /dev/null
@@ -0,0 +1,131 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef struct s1 { char a1 [5][5]; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_RSHIFT_U64 = 5, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1[3], FIELD_RSHIFT_U64);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a1[3][3], FIELD_RSHIFT_U64);
+;     /* r1 : 24, r2 : 56 */
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { i32, [24 x i8] }
+%struct.s1 = type { [5 x [5 x i8]] }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !18 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !32, metadata !DIExpression()), !dbg !35
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !36, !llvm.preserve.access.index !23
+  %b2 = bitcast %union.u1* %0 to %struct.s1*, !dbg !36
+  %1 = tail call [5 x [5 x i8]]* @llvm.preserve.struct.access.index.p0a5a5i8.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !37, !llvm.preserve.access.index !28
+  %2 = tail call [5 x i8]* @llvm.preserve.array.access.index.p0a5i8.p0a5a5i8([5 x [5 x i8]]* %1, i32 1, i32 3), !dbg !38, !llvm.preserve.access.index !8
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0a5i8([5 x i8]* %2, i64 5), !dbg !39
+  call void @llvm.dbg.value(metadata i32 %3, metadata !33, metadata !DIExpression()), !dbg !35
+  %4 = tail call i8* @llvm.preserve.array.access.index.p0i8.p0a5i8([5 x i8]* %2, i32 1, i32 3), !dbg !40, !llvm.preserve.access.index !12
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %4, i64 5), !dbg !41
+  call void @llvm.dbg.value(metadata i32 %5, metadata !34, metadata !DIExpression()), !dbg !35
+  %add = add i32 %5, %3, !dbg !42
+  ret i32 %add, !dbg !43
+}
+
+; CHECK:             r1 = 24
+; CHECK:             r0 = 56
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=59
+; CHECK:             .ascii  "0:1:0:3"               # string offset=65
+; CHECK:             .ascii  "0:1:0:3:3"             # string offset=110
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   59                      # Field reloc section string offset=59
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   65
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   110
+; CHECK-NEXT:        .long   5
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [5 x [5 x i8]]* @llvm.preserve.struct.access.index.p0a5a5i8.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [5 x i8]* @llvm.preserve.array.access.index.p0a5i8.p0a5a5i8([5 x [5 x i8]]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0a5i8([5 x i8]*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i8* @llvm.preserve.array.access.index.p0i8.p0a5i8([5 x i8]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!14, !15, !16}
+!llvm.ident = !{!17}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !7, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 3, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_RSHIFT_U64", value: 5, isUnsigned: true)
+!7 = !{!8, !12}
+!8 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 200, elements: !10)
+!9 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char)
+!10 = !{!11, !11}
+!11 = !DISubrange(count: 5)
+!12 = !DICompositeType(tag: DW_TAG_array_type, baseType: !9, size: 40, elements: !13)
+!13 = !{!11}
+!14 = !{i32 2, !"Dwarf Version", i32 4}
+!15 = !{i32 2, !"Debug Info Version", i32 3}
+!16 = !{i32 1, !"wchar_size", i32 4}
+!17 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)"}
+!18 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 4, type: !19, scopeLine: 4, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !31)
+!19 = !DISubroutineType(types: !20)
+!20 = !{!21, !22}
+!21 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64)
+!23 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 2, size: 224, elements: !24)
+!24 = !{!25, !26}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !23, file: !1, line: 2, baseType: !21, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !23, file: !1, line: 2, baseType: !27, size: 200)
+!27 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 1, baseType: !28)
+!28 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 1, size: 200, elements: !29)
+!29 = !{!30}
+!30 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !28, file: !1, line: 1, baseType: !8, size: 200)
+!31 = !{!32, !33, !34}
+!32 = !DILocalVariable(name: "arg", arg: 1, scope: !18, file: !1, line: 4, type: !22)
+!33 = !DILocalVariable(name: "r1", scope: !18, file: !1, line: 5, type: !4)
+!34 = !DILocalVariable(name: "r2", scope: !18, file: !1, line: 6, type: !4)
+!35 = !DILocation(line: 0, scope: !18)
+!36 = !DILocation(line: 5, column: 52, scope: !18)
+!37 = !DILocation(line: 5, column: 55, scope: !18)
+!38 = !DILocation(line: 5, column: 47, scope: !18)
+!39 = !DILocation(line: 5, column: 17, scope: !18)
+!40 = !DILocation(line: 6, column: 47, scope: !18)
+!41 = !DILocation(line: 6, column: 17, scope: !18)
+!42 = !DILocation(line: 8, column: 13, scope: !18)
+!43 = !DILocation(line: 8, column: 3, scope: !18)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-1.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-1.ll
new file mode 100644 (file)
index 0000000..f5605cb
--- /dev/null
@@ -0,0 +1,162 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   typedef unsigned __uint;
+;   struct s1 { int a1; __uint a2:9; __uint a3:4; };
+;   union u1 { int b1; __uint b2:9; __uint b3:4; };
+;   enum { FIELD_SIGNEDNESS = 3, };
+;   int test(struct s1 *arg1, union u1 *arg2) {
+;     unsigned r1 = __builtin_preserve_field_info(arg1->a1, FIELD_SIGNEDNESS);
+;     unsigned r2 = __builtin_preserve_field_info(arg1->a3, FIELD_SIGNEDNESS);
+;     unsigned r3 = __builtin_preserve_field_info(arg2->b1, FIELD_SIGNEDNESS);
+;     unsigned r4 = __builtin_preserve_field_info(arg2->b3, FIELD_SIGNEDNESS);
+;     return r1 + r2 + r3 + r4;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.s1 = type { i32, i16 }
+%union.u1 = type { i32 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%struct.s1* %arg1, %union.u1* %arg2) local_unnamed_addr #0 !dbg !11 {
+entry:
+  call void @llvm.dbg.value(metadata %struct.s1* %arg1, metadata !29, metadata !DIExpression()), !dbg !35
+  call void @llvm.dbg.value(metadata %union.u1* %arg2, metadata !30, metadata !DIExpression()), !dbg !35
+  %0 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %arg1, i32 0, i32 0), !dbg !36, !llvm.preserve.access.index !16
+  %1 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %0, i64 3), !dbg !37
+  call void @llvm.dbg.value(metadata i32 %1, metadata !31, metadata !DIExpression()), !dbg !35
+  %2 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %arg1, i32 1, i32 2), !dbg !38, !llvm.preserve.access.index !16
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %2, i64 3), !dbg !39
+  call void @llvm.dbg.value(metadata i32 %3, metadata !32, metadata !DIExpression()), !dbg !35
+  %4 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg2, i32 0), !dbg !40, !llvm.preserve.access.index !23
+  %b1 = getelementptr inbounds %union.u1, %union.u1* %4, i64 0, i32 0, !dbg !40
+  %5 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %b1, i64 3), !dbg !41
+  call void @llvm.dbg.value(metadata i32 %5, metadata !33, metadata !DIExpression()), !dbg !35
+  %6 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_union.u1s(%union.u1* %arg2, i32 0, i32 2), !dbg !42, !llvm.preserve.access.index !23
+  %7 = bitcast i32* %6 to i8*, !dbg !42
+  %8 = tail call i32 @llvm.bpf.preserve.field.info.p0i8(i8* %7, i64 3), !dbg !43
+  call void @llvm.dbg.value(metadata i32 %8, metadata !34, metadata !DIExpression()), !dbg !35
+  %add = add i32 %3, %1, !dbg !44
+  %add1 = add i32 %add, %5, !dbg !45
+  %add2 = add i32 %add1, %8, !dbg !46
+  ret i32 %add2, !dbg !47
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 0
+; CHECK:             r0 += r1
+; CHECK:             r1 = 1
+; CHECK:             r0 += r1
+; CHECK:             r1 = 0
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_STRUCT(id = 2)
+; CHECK:             .long   37                      # BTF_KIND_UNION(id = 7)
+; CHECK:             .ascii  "s1"                    # string offset=1
+; CHECK:             .ascii  "u1"                    # string offset=37
+; CHECK:             .ascii  ".text"                 # string offset=64
+; CHECK:             .ascii  "0:0"                   # string offset=70
+; CHECK:             .ascii  "0:2"                   # string offset=111
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   64                      # Field reloc section string offset=64
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   70
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   111
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   7
+; CHECK-NEXT:        .long   70
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   7
+; CHECK-NEXT:        .long   111
+; CHECK-NEXT:        .long   3
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_union.u1s(%union.u1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i8(i8*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!7, !8, !9}
+!llvm.ident = !{!10}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 4, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6}
+!6 = !DIEnumerator(name: "FIELD_SIGNEDNESS", value: 3, isUnsigned: true)
+!7 = !{i32 2, !"Dwarf Version", i32 4}
+!8 = !{i32 2, !"Debug Info Version", i32 3}
+!9 = !{i32 1, !"wchar_size", i32 4}
+!10 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!11 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 5, type: !12, scopeLine: 5, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !28)
+!12 = !DISubroutineType(types: !13)
+!13 = !{!14, !15, !22}
+!14 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !16, size: 64)
+!16 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 2, size: 64, elements: !17)
+!17 = !{!18, !19, !21}
+!18 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !16, file: !1, line: 2, baseType: !14, size: 32)
+!19 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !16, file: !1, line: 2, baseType: !20, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!20 = !DIDerivedType(tag: DW_TAG_typedef, name: "__uint", file: !1, line: 1, baseType: !4)
+!21 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !16, file: !1, line: 2, baseType: !20, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!22 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !23, size: 64)
+!23 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 3, size: 32, elements: !24)
+!24 = !{!25, !26, !27}
+!25 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !23, file: !1, line: 3, baseType: !14, size: 32)
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !23, file: !1, line: 3, baseType: !20, size: 9, flags: DIFlagBitField, extraData: i64 0)
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "b3", scope: !23, file: !1, line: 3, baseType: !20, size: 4, flags: DIFlagBitField, extraData: i64 0)
+!28 = !{!29, !30, !31, !32, !33, !34}
+!29 = !DILocalVariable(name: "arg1", arg: 1, scope: !11, file: !1, line: 5, type: !15)
+!30 = !DILocalVariable(name: "arg2", arg: 2, scope: !11, file: !1, line: 5, type: !22)
+!31 = !DILocalVariable(name: "r1", scope: !11, file: !1, line: 6, type: !4)
+!32 = !DILocalVariable(name: "r2", scope: !11, file: !1, line: 7, type: !4)
+!33 = !DILocalVariable(name: "r3", scope: !11, file: !1, line: 8, type: !4)
+!34 = !DILocalVariable(name: "r4", scope: !11, file: !1, line: 9, type: !4)
+!35 = !DILocation(line: 0, scope: !11)
+!36 = !DILocation(line: 6, column: 53, scope: !11)
+!37 = !DILocation(line: 6, column: 17, scope: !11)
+!38 = !DILocation(line: 7, column: 53, scope: !11)
+!39 = !DILocation(line: 7, column: 17, scope: !11)
+!40 = !DILocation(line: 8, column: 53, scope: !11)
+!41 = !DILocation(line: 8, column: 17, scope: !11)
+!42 = !DILocation(line: 9, column: 53, scope: !11)
+!43 = !DILocation(line: 9, column: 17, scope: !11)
+!44 = !DILocation(line: 10, column: 13, scope: !11)
+!45 = !DILocation(line: 10, column: 18, scope: !11)
+!46 = !DILocation(line: 10, column: 23, scope: !11)
+!47 = !DILocation(line: 10, column: 3, scope: !11)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-2.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-2.ll
new file mode 100644 (file)
index 0000000..cec9790
--- /dev/null
@@ -0,0 +1,151 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   enum A { AA = -1, AB = 0, }; /* signed */
+;   enum B { BA = 0, BB = 1, };  /* unsigned */
+;   typedef enum A __A;
+;   typedef enum B __B;
+;   typedef int __int;           /* signed */
+;   struct s1 { __A a1; __B a2:9; __int a3:4; };
+;   union u1 { int b1; struct s1 b2; };
+;   enum { FIELD_SIGNEDNESS = 3, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1, FIELD_SIGNEDNESS);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2, FIELD_SIGNEDNESS);
+;     unsigned r3 = __builtin_preserve_field_info(arg->b2.a3, FIELD_SIGNEDNESS);
+;     return r1 + r2 + r3;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { i32, i16 }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !20 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !37, metadata !DIExpression()), !dbg !41
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !42, !llvm.preserve.access.index !24
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !42
+  %1 = tail call i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !43, !llvm.preserve.access.index !28
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %1, i64 3), !dbg !44
+  call void @llvm.dbg.value(metadata i32 %2, metadata !38, metadata !DIExpression()), !dbg !41
+  %3 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 1), !dbg !45, !llvm.preserve.access.index !28
+  %4 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %3, i64 3), !dbg !46
+  call void @llvm.dbg.value(metadata i32 %4, metadata !39, metadata !DIExpression()), !dbg !41
+  %5 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 2), !dbg !47, !llvm.preserve.access.index !28
+  %6 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %5, i64 3), !dbg !48
+  call void @llvm.dbg.value(metadata i32 %6, metadata !40, metadata !DIExpression()), !dbg !41
+  %add = add i32 %4, %2, !dbg !49
+  %add3 = add i32 %add, %6, !dbg !50
+  ret i32 %add3, !dbg !51
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 0
+; CHECK:             r0 += r1
+; CHECK:             r1 = 1
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=65
+; CHECK:             .ascii  "0:1:0"                 # string offset=71
+; CHECK:             .ascii  "0:1:1"                 # string offset=114
+; CHECK:             .ascii  "0:1:2"                 # string offset=120
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   65                      # Field reloc section string offset=65
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   71
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   114
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   3
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!16, !17, !18}
+!llvm.ident = !{!19}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3, !8, !13}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "A", file: !1, line: 1, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!5 = !{!6, !7}
+!6 = !DIEnumerator(name: "AA", value: -1)
+!7 = !DIEnumerator(name: "AB", value: 0)
+!8 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "B", file: !1, line: 2, baseType: !9, size: 32, elements: !10)
+!9 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!10 = !{!11, !12}
+!11 = !DIEnumerator(name: "BA", value: 0, isUnsigned: true)
+!12 = !DIEnumerator(name: "BB", value: 1, isUnsigned: true)
+!13 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 8, baseType: !9, size: 32, elements: !14)
+!14 = !{!15}
+!15 = !DIEnumerator(name: "FIELD_SIGNEDNESS", value: 3, isUnsigned: true)
+!16 = !{i32 2, !"Dwarf Version", i32 4}
+!17 = !{i32 2, !"Debug Info Version", i32 3}
+!18 = !{i32 1, !"wchar_size", i32 4}
+!19 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 4a60741b74384f14b21fdc0131ede326438840ab)"}
+!20 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 9, type: !21, scopeLine: 9, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !36)
+!21 = !DISubroutineType(types: !22)
+!22 = !{!4, !23}
+!23 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !24, size: 64)
+!24 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 7, size: 64, elements: !25)
+!25 = !{!26, !27}
+!26 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !24, file: !1, line: 7, baseType: !4, size: 32)
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !24, file: !1, line: 7, baseType: !28, size: 64)
+!28 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 6, size: 64, elements: !29)
+!29 = !{!30, !32, !34}
+!30 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !28, file: !1, line: 6, baseType: !31, size: 32)
+!31 = !DIDerivedType(tag: DW_TAG_typedef, name: "__A", file: !1, line: 3, baseType: !3)
+!32 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !28, file: !1, line: 6, baseType: !33, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!33 = !DIDerivedType(tag: DW_TAG_typedef, name: "__B", file: !1, line: 4, baseType: !8)
+!34 = !DIDerivedType(tag: DW_TAG_member, name: "a3", scope: !28, file: !1, line: 6, baseType: !35, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!35 = !DIDerivedType(tag: DW_TAG_typedef, name: "__int", file: !1, line: 5, baseType: !4)
+!36 = !{!37, !38, !39, !40}
+!37 = !DILocalVariable(name: "arg", arg: 1, scope: !20, file: !1, line: 9, type: !23)
+!38 = !DILocalVariable(name: "r1", scope: !20, file: !1, line: 10, type: !9)
+!39 = !DILocalVariable(name: "r2", scope: !20, file: !1, line: 11, type: !9)
+!40 = !DILocalVariable(name: "r3", scope: !20, file: !1, line: 12, type: !9)
+!41 = !DILocation(line: 0, scope: !20)
+!42 = !DILocation(line: 10, column: 52, scope: !20)
+!43 = !DILocation(line: 10, column: 55, scope: !20)
+!44 = !DILocation(line: 10, column: 17, scope: !20)
+!45 = !DILocation(line: 11, column: 55, scope: !20)
+!46 = !DILocation(line: 11, column: 17, scope: !20)
+!47 = !DILocation(line: 12, column: 55, scope: !20)
+!48 = !DILocation(line: 12, column: 17, scope: !20)
+!49 = !DILocation(line: 13, column: 13, scope: !20)
+!50 = !DILocation(line: 13, column: 18, scope: !20)
+!51 = !DILocation(line: 13, column: 3, scope: !20)
diff --git a/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-3.ll b/llvm/test/CodeGen/BPF/CORE/intrinsic-fieldinfo-signedness-3.ll
new file mode 100644 (file)
index 0000000..619ca8a
--- /dev/null
@@ -0,0 +1,149 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   enum A { AA = -1, AB = 0, };
+;   enum B { BA = 0, BB = 1, };
+;   typedef enum A __A;
+;   typedef enum B __B;
+;   typedef struct s1 { __A a1[10]; __B a2[10][10]; } __s1;
+;   union u1 { int b1; __s1 b2; };
+;   enum { FIELD_SIGNEDNESS = 3, };
+;   int test(union u1 *arg) {
+;     unsigned r1 = __builtin_preserve_field_info(arg->b2.a1[5], FIELD_SIGNEDNESS);
+;     unsigned r2 = __builtin_preserve_field_info(arg->b2.a2[5][5], FIELD_SIGNEDNESS);
+;     /* r1 : 1, r2 : 0 */
+;     return r1 + r2;
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%union.u1 = type { %struct.s1 }
+%struct.s1 = type { [10 x i32], [10 x [10 x i32]] }
+
+; Function Attrs: nounwind readnone
+define dso_local i32 @test(%union.u1* %arg) local_unnamed_addr #0 !dbg !29 {
+entry:
+  call void @llvm.dbg.value(metadata %union.u1* %arg, metadata !43, metadata !DIExpression()), !dbg !46
+  %0 = tail call %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1* %arg, i32 1), !dbg !47, !llvm.preserve.access.index !33
+  %b2 = getelementptr inbounds %union.u1, %union.u1* %0, i64 0, i32 0, !dbg !47
+  %1 = tail call [10 x i32]* @llvm.preserve.struct.access.index.p0a10i32.p0s_struct.s1s(%struct.s1* %b2, i32 0, i32 0), !dbg !48, !llvm.preserve.access.index !38
+  %2 = tail call i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]* %1, i32 1, i32 5), !dbg !49, !llvm.preserve.access.index !17
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %2, i64 3), !dbg !50
+  call void @llvm.dbg.value(metadata i32 %3, metadata !44, metadata !DIExpression()), !dbg !46
+  %4 = tail call [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1* %b2, i32 1, i32 1), !dbg !51, !llvm.preserve.access.index !38
+  %5 = tail call [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]* %4, i32 1, i32 5), !dbg !52, !llvm.preserve.access.index !21
+  %6 = tail call i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]* %5, i32 1, i32 5), !dbg !52, !llvm.preserve.access.index !24
+  %7 = tail call i32 @llvm.bpf.preserve.field.info.p0i32(i32* %6, i64 3), !dbg !53
+  call void @llvm.dbg.value(metadata i32 %7, metadata !45, metadata !DIExpression()), !dbg !46
+  %add = add i32 %7, %3, !dbg !54
+  ret i32 %add, !dbg !55
+}
+
+; CHECK:             r1 = 1
+; CHECK:             r0 = 0
+; CHECK:             r0 += r1
+; CHECK:             exit
+
+; CHECK:             .long   1                       # BTF_KIND_UNION(id = 2)
+; CHECK:             .ascii  "u1"                    # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=81
+; CHECK:             .ascii  "0:1:0:5"               # string offset=87
+; CHECK:             .ascii  "0:1:1:5:5"             # string offset=132
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   81                      # Field reloc section string offset=81
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   87
+; CHECK-NEXT:        .long   3
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   132
+; CHECK-NEXT:        .long   3
+
+; Function Attrs: nounwind readnone
+declare %union.u1* @llvm.preserve.union.access.index.p0s_union.u1s.p0s_union.u1s(%union.u1*, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x i32]* @llvm.preserve.struct.access.index.p0a10i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32* @llvm.preserve.array.access.index.p0i32.p0a10i32([10 x i32]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i32(i32*, i64) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x [10 x i32]]* @llvm.preserve.struct.access.index.p0a10a10i32.p0s_struct.s1s(%struct.s1*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare [10 x i32]* @llvm.preserve.array.access.index.p0a10i32.p0a10a10i32([10 x [10 x i32]]*, i32, i32) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!25, !26, !27}
+!llvm.ident = !{!28}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !16, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3, !8, !13}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "A", file: !1, line: 1, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!5 = !{!6, !7}
+!6 = !DIEnumerator(name: "AA", value: -1)
+!7 = !DIEnumerator(name: "AB", value: 0)
+!8 = !DICompositeType(tag: DW_TAG_enumeration_type, name: "B", file: !1, line: 2, baseType: !9, size: 32, elements: !10)
+!9 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!10 = !{!11, !12}
+!11 = !DIEnumerator(name: "BA", value: 0, isUnsigned: true)
+!12 = !DIEnumerator(name: "BB", value: 1, isUnsigned: true)
+!13 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 7, baseType: !9, size: 32, elements: !14)
+!14 = !{!15}
+!15 = !DIEnumerator(name: "FIELD_SIGNEDNESS", value: 3, isUnsigned: true)
+!16 = !{!17, !21, !24}
+!17 = !DICompositeType(tag: DW_TAG_array_type, baseType: !18, size: 320, elements: !19)
+!18 = !DIDerivedType(tag: DW_TAG_typedef, name: "__A", file: !1, line: 3, baseType: !3)
+!19 = !{!20}
+!20 = !DISubrange(count: 10)
+!21 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 3200, elements: !23)
+!22 = !DIDerivedType(tag: DW_TAG_typedef, name: "__B", file: !1, line: 4, baseType: !8)
+!23 = !{!20, !20}
+!24 = !DICompositeType(tag: DW_TAG_array_type, baseType: !22, size: 320, elements: !19)
+!25 = !{i32 2, !"Dwarf Version", i32 4}
+!26 = !{i32 2, !"Debug Info Version", i32 3}
+!27 = !{i32 1, !"wchar_size", i32 4}
+!28 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git c1e02f16f1105ffaf1c35ee8bc38b7d6db5c6ea9)"}
+!29 = distinct !DISubprogram(name: "test", scope: !1, file: !1, line: 8, type: !30, scopeLine: 8, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !42)
+!30 = !DISubroutineType(types: !31)
+!31 = !{!4, !32}
+!32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !33, size: 64)
+!33 = distinct !DICompositeType(tag: DW_TAG_union_type, name: "u1", file: !1, line: 6, size: 3520, elements: !34)
+!34 = !{!35, !36}
+!35 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !33, file: !1, line: 6, baseType: !4, size: 32)
+!36 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !33, file: !1, line: 6, baseType: !37, size: 3520)
+!37 = !DIDerivedType(tag: DW_TAG_typedef, name: "__s1", file: !1, line: 5, baseType: !38)
+!38 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s1", file: !1, line: 5, size: 3520, elements: !39)
+!39 = !{!40, !41}
+!40 = !DIDerivedType(tag: DW_TAG_member, name: "a1", scope: !38, file: !1, line: 5, baseType: !17, size: 320)
+!41 = !DIDerivedType(tag: DW_TAG_member, name: "a2", scope: !38, file: !1, line: 5, baseType: !21, size: 3200, offset: 320)
+!42 = !{!43, !44, !45}
+!43 = !DILocalVariable(name: "arg", arg: 1, scope: !29, file: !1, line: 8, type: !32)
+!44 = !DILocalVariable(name: "r1", scope: !29, file: !1, line: 9, type: !9)
+!45 = !DILocalVariable(name: "r2", scope: !29, file: !1, line: 10, type: !9)
+!46 = !DILocation(line: 0, scope: !29)
+!47 = !DILocation(line: 9, column: 52, scope: !29)
+!48 = !DILocation(line: 9, column: 55, scope: !29)
+!49 = !DILocation(line: 9, column: 47, scope: !29)
+!50 = !DILocation(line: 9, column: 17, scope: !29)
+!51 = !DILocation(line: 10, column: 55, scope: !29)
+!52 = !DILocation(line: 10, column: 47, scope: !29)
+!53 = !DILocation(line: 10, column: 17, scope: !29)
+!54 = !DILocation(line: 12, column: 13, scope: !29)
+!55 = !DILocation(line: 12, column: 3, scope: !29)
index c07c16f..1c0189b 100644 (file)
@@ -28,12 +28,13 @@ entry:
 ; CHECK:       exit
 ;
 ; CHECK:      .section        .BTF.ext,"",@progbits
-; CHECK:      .long   12                      # OffsetReloc
-; CHECK-NEXT: .long   20                      # Offset reloc section string offset=20
+; CHECK:      .long   16                      # FieldReloc
+; CHECK-NEXT: .long   20                      # Field reloc section string offset=20
 ; CHECK-NEXT: .long   1
 ; CHECK-NEXT: .long   [[RELOC]]
 ; CHECK-NEXT: .long   2
 ; CHECK-NEXT: .long   26
+; CHECK-NEXT: .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index 19b4595..48cfa45 100644 (file)
@@ -27,12 +27,13 @@ entry:
 ; CHECK:       exit
 
 ; CHECK:      .section        .BTF.ext,"",@progbits
-; CHECK:      .long   12                      # OffsetReloc
-; CHECK-NEXT: .long   20                      # Offset reloc section string offset=20
+; CHECK:      .long   16                      # FieldReloc
+; CHECK-NEXT: .long   20                      # Field reloc section string offset=20
 ; CHECK-NEXT: .long   1
 ; CHECK-NEXT: .long   [[RELOC]]
 ; CHECK-NEXT: .long   2
 ; CHECK-NEXT: .long   26
+; CHECK-NEXT: .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index 5625bf9..c2e3296 100644 (file)
@@ -33,15 +33,17 @@ entry:
 ; CHECK:             .ascii  "0:1"                   # string offset=[[ACCESS_STR:[0-9]+]]
 ; CHECK-NEXT:        .byte   0
 ; CHECK:             .section        .BTF.ext,"",@progbits
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   [[SEC_INDEX]]           # Offset reloc section string offset=[[SEC_INDEX]]
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   [[SEC_INDEX]]           # Field reloc section string offset=[[SEC_INDEX]]
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   {{[0-9]+}}
 ; CHECK-NEXT:        .long   [[ACCESS_STR]]
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   {{[0-9]+}}
 ; CHECK-NEXT:        .long   [[ACCESS_STR]]
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i8*, i8*) local_unnamed_addr #1
 
index a6fb3a3..6c4bbf1 100644 (file)
@@ -109,17 +109,18 @@ define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15
 ; CHECK-NEXT:        .long   20
 ; CHECK-NEXT:        .long   124
 ; CHECK-NEXT:        .long   144
-; CHECK-NEXT:        .long   24
-; CHECK-NEXT:        .long   168
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   172
 ; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   8                       # FuncInfo
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   43                      # Offset reloc section string offset=43
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   43                      # Field reloc section string offset=43
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp2
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   86
+; CHECK-NEXT:        .long   0
 
 ; Function Attrs: argmemonly nounwind
 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
index 9e291cd..ceda85b 100644 (file)
@@ -45,15 +45,17 @@ entry:
 ; CHECK:              .ascii  "0:1:0"                 # string offset=52
 ; CHECK:              .ascii  "2:1"                   # string offset=107
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   46                      # Offset reloc section string offset=46
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   46                      # Field reloc section string offset=46
 ; CHECK-NEXT:         .long   2
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   52
+; CHECK-NEXT:         .long   0
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID2]]
 ; CHECK-NEXT:         .long   107
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 7903179..19a8aa3 100644 (file)
@@ -47,15 +47,17 @@ entry:
 ; CHECK:              .ascii  "v1"                    # string offset=100
 ; CHECK:              .ascii  "11:1"                  # string offset=107
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   46                      # Offset reloc section string offset=46
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   46                      # Field reloc section string offset=46
 ; CHECK-NEXT:         .long   2
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   52
+; CHECK-NEXT:         .long   0
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID2]]
 ; CHECK-NEXT:         .long   107
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index a97c6a0..9ec78dd 100644 (file)
@@ -46,15 +46,17 @@ entry:
 ; CHECK:             .ascii  "v1"                    # string offset=81
 ; CHECK-NEXT:        .byte   0
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   [[SEC_STR]]             # Offset reloc section string offset=[[SEC_STR]]
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   [[SEC_STR]]             # Field reloc section string offset=[[SEC_STR]]
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[V3_TID]]
 ; CHECK-NEXT:        .long   [[ACCESS_STR]]
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[V1_TID]]
 ; CHECK-NEXT:        .long   [[ACCESS_STR]]
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index f65b3f3..ee2b617 100644 (file)
@@ -46,15 +46,17 @@ entry:
 ; CHECK:             .ascii  "v1"                    # string offset=91
 
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   39                      # Offset reloc section string offset=39
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   39                      # Field reloc section string offset=39
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID1]]
 ; CHECK-NEXT:        .long   45
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID2]]
 ; CHECK-NEXT:        .long   45
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index ed78b84..7b51029 100644 (file)
@@ -45,15 +45,17 @@ entry:
 ; CHECK:             .ascii  "v1"                    # string offset=111
 ; CHECK:             .ascii  "0:1"                   # string offset=118
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   57                      # Offset reloc section string offset=57
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   57                      # Field reloc section string offset=57
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID1]]
 ; CHECK-NEXT:        .long   63
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID2]]
 ; CHECK-NEXT:        .long   118
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 1e8f99c..8a6f908 100644 (file)
@@ -46,15 +46,17 @@ entry:
 ; CHECK:             .ascii  "0:1"                   # string offset=45
 ; CHECK:             .ascii  "v1"                    # string offset=91
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   39                      # Offset reloc section string offset=39
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   39                      # Field reloc section string offset=39
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID1]]
 ; CHECK-NEXT:        .long   45
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID2]]
 ; CHECK-NEXT:        .long   45
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 320b0a9..fd4734b 100644 (file)
@@ -47,15 +47,17 @@ entry:
 ; CHECK:             .ascii  "v1"                    # string offset=111
 ; CHECK:             .ascii  "0:1"                   # string offset=118
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   57                      # Offset reloc section string offset=57
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   57                      # Field reloc section string offset=57
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID1]]
 ; CHECK-NEXT:        .long   63
+; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID2]]
 ; CHECK-NEXT:        .long   118
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 35415f6..f175425 100644 (file)
@@ -30,12 +30,13 @@ entry:
 ; CHECK:       .ascii  ".text"                 # string offset=20
 ; CHECK:       .ascii  "0:1"                   # string offset=26
 ;
-; CHECK:       .long   12                      # OffsetReloc
-; CHECK-NEXT:  .long   20                      # Offset reloc section string offset=20
+; CHECK:       .long   16                      # FieldReloc
+; CHECK-NEXT:  .long   20                      # Field reloc section string offset=20
 ; CHECK-NEXT:  .long   1
 ; CHECK-NEXT:  .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:  .long   2
 ; CHECK-NEXT:  .long   26
+; CHECK-NEXT:  .long   0
 
 ; Function Attrs: nounwind readnone
 declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.ss(%struct.s*, i32, i32) #1
index 52a6300..f15c9a3 100644 (file)
@@ -30,12 +30,13 @@ entry:
 ; CHECK:       .ascii  ".text"                 # string offset=20
 ; CHECK:       .ascii  "0:1"                   # string offset=63
 ;
-; CHECK:       .long   12                      # OffsetReloc
-; CHECK-NEXT:  .long   20                      # Offset reloc section string offset=20
+; CHECK:       .long   16                      # FieldReloc
+; CHECK-NEXT:  .long   20                      # Field reloc section string offset=20
 ; CHECK-NEXT:  .long   1
 ; CHECK-NEXT:  .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:  .long   2
 ; CHECK-NEXT:  .long   63
+; CHECK-NEXT:  .long   0
 
 ; Function Attrs: nounwind readnone
 declare i32* @llvm.preserve.struct.access.index.p0i32.p0s_struct.ss(%struct.s*, i32, i32) #1
diff --git a/llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-1.ll b/llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-1.ll
new file mode 100644 (file)
index 0000000..49982a5
--- /dev/null
@@ -0,0 +1,189 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK %s
+; Source code:
+;   struct s {
+;     int a;
+;     int b1:9;
+;     int b2:4;
+;   };
+;   enum {
+;       FIELD_BYTE_OFFSET = 0,
+;       FIELD_BYTE_SIZE,
+;       FIELD_EXISTENCE,
+;       FIELD_SIGNEDNESS,
+;       FIELD_LSHIFT_U64,
+;       FIELD_RSHIFT_U64,
+;   };
+;   void bpf_probe_read(void *, unsigned, const void *);
+;   int field_read(struct s *arg) {
+;     unsigned long long ull;
+;     unsigned offset = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_OFFSET);
+;     unsigned size = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_SIZE);
+;     unsigned lshift;
+;
+;     bpf_probe_read(&ull, size, (const void *)arg + offset);
+;     lshift = __builtin_preserve_field_info(arg->b2, FIELD_LSHIFT_U64);
+;   #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+;     lshift = lshift + (size << 3) - 64;
+;   #endif
+;     ull <<= lshift;
+;     if (__builtin_preserve_field_info(arg->b2, FIELD_SIGNEDNESS))
+;       return (long long)ull >> __builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
+;     return ull >> __builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
+;   }
+; Compilation flag:
+;   clang -target bpfel -O2 -g -S -emit-llvm test.c
+
+%struct.s = type { i32, i16 }
+
+; Function Attrs: nounwind
+define dso_local i32 @field_read(%struct.s* %arg) local_unnamed_addr #0 !dbg !20 {
+entry:
+  %ull = alloca i64, align 8
+  call void @llvm.dbg.value(metadata %struct.s* %arg, metadata !31, metadata !DIExpression()), !dbg !37
+  %0 = bitcast i64* %ull to i8*, !dbg !38
+  call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %0) #5, !dbg !38
+  %1 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s* %arg, i32 1, i32 2), !dbg !39, !llvm.preserve.access.index !25
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %1, i64 0), !dbg !40
+  call void @llvm.dbg.value(metadata i32 %2, metadata !34, metadata !DIExpression()), !dbg !37
+  %3 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %1, i64 1), !dbg !41
+  call void @llvm.dbg.value(metadata i32 %3, metadata !35, metadata !DIExpression()), !dbg !37
+  %4 = bitcast %struct.s* %arg to i8*, !dbg !42
+  %idx.ext = zext i32 %2 to i64, !dbg !43
+  %add.ptr = getelementptr i8, i8* %4, i64 %idx.ext, !dbg !43
+  call void @bpf_probe_read(i8* nonnull %0, i32 %3, i8* %add.ptr) #5, !dbg !44
+  %5 = call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %1, i64 4), !dbg !45
+  call void @llvm.dbg.value(metadata i32 %5, metadata !36, metadata !DIExpression()), !dbg !37
+  %6 = load i64, i64* %ull, align 8, !dbg !46, !tbaa !47
+  call void @llvm.dbg.value(metadata i64 %6, metadata !32, metadata !DIExpression()), !dbg !37
+  %sh_prom = zext i32 %5 to i64, !dbg !46
+  %shl = shl i64 %6, %sh_prom, !dbg !46
+  call void @llvm.dbg.value(metadata i64 %shl, metadata !32, metadata !DIExpression()), !dbg !37
+  %7 = call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %1, i64 3), !dbg !51
+  %tobool = icmp eq i32 %7, 0, !dbg !51
+  %8 = call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %1, i64 5), !dbg !37
+  %sh_prom1 = zext i32 %8 to i64, !dbg !37
+  %shr = ashr i64 %shl, %sh_prom1, !dbg !53
+  %shr3 = lshr i64 %shl, %sh_prom1, !dbg !53
+  %retval.0.in = select i1 %tobool, i64 %shr3, i64 %shr, !dbg !53
+  %retval.0 = trunc i64 %retval.0.in to i32, !dbg !37
+  call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %0) #5, !dbg !54
+  ret i32 %retval.0, !dbg !54
+}
+
+; CHECK:             r{{[0-9]+}} = 4
+; CHECK:             r{{[0-9]+}} = 4
+; CHECK:             r{{[0-9]+}} = 51
+; CHECK:             r{{[0-9]+}} = 60
+; CHECK:             r{{[0-9]+}} = 1
+
+; CHECK:             .byte   115                     # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=30
+; CHECK:             .ascii  "0:2"                   # string offset=73
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   30                      # Field reloc section string offset=30
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   73
+; CHECK-NEXT:        .long   0
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   73
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   73
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   73
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   73
+; CHECK-NEXT:        .long   3
+
+; Function Attrs: argmemonly nounwind willreturn
+declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s*, i32, i32) #2
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #2
+
+declare dso_local void @bpf_probe_read(i8*, i32, i8*) local_unnamed_addr #3
+
+; Function Attrs: argmemonly nounwind willreturn
+declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #4
+
+attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { argmemonly nounwind willreturn }
+attributes #2 = { nounwind readnone }
+attributes #3 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #4 = { nounwind readnone speculatable willreturn }
+attributes #5 = { nounwind }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!16, !17, !18}
+!llvm.ident = !{!19}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 923aa0ce806f7739b754167239fee2c9a15e2f31)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !12, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 6, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6, !7, !8, !9, !10, !11}
+!6 = !DIEnumerator(name: "FIELD_BYTE_OFFSET", value: 0, isUnsigned: true)
+!7 = !DIEnumerator(name: "FIELD_BYTE_SIZE", value: 1, isUnsigned: true)
+!8 = !DIEnumerator(name: "FIELD_EXISTENCE", value: 2, isUnsigned: true)
+!9 = !DIEnumerator(name: "FIELD_SIGNEDNESS", value: 3, isUnsigned: true)
+!10 = !DIEnumerator(name: "FIELD_LSHIFT_U64", value: 4, isUnsigned: true)
+!11 = !DIEnumerator(name: "FIELD_RSHIFT_U64", value: 5, isUnsigned: true)
+!12 = !{!13, !15}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null)
+!15 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed)
+!16 = !{i32 2, !"Dwarf Version", i32 4}
+!17 = !{i32 2, !"Debug Info Version", i32 3}
+!18 = !{i32 1, !"wchar_size", i32 4}
+!19 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 923aa0ce806f7739b754167239fee2c9a15e2f31)"}
+!20 = distinct !DISubprogram(name: "field_read", scope: !1, file: !1, line: 15, type: !21, scopeLine: 15, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !30)
+!21 = !DISubroutineType(types: !22)
+!22 = !{!23, !24}
+!23 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!24 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !25, size: 64)
+!25 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s", file: !1, line: 1, size: 64, elements: !26)
+!26 = !{!27, !28, !29}
+!27 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !25, file: !1, line: 2, baseType: !23, size: 32)
+!28 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !25, file: !1, line: 3, baseType: !23, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!29 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !25, file: !1, line: 4, baseType: !23, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!30 = !{!31, !32, !34, !35, !36}
+!31 = !DILocalVariable(name: "arg", arg: 1, scope: !20, file: !1, line: 15, type: !24)
+!32 = !DILocalVariable(name: "ull", scope: !20, file: !1, line: 16, type: !33)
+!33 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned)
+!34 = !DILocalVariable(name: "offset", scope: !20, file: !1, line: 17, type: !4)
+!35 = !DILocalVariable(name: "size", scope: !20, file: !1, line: 18, type: !4)
+!36 = !DILocalVariable(name: "lshift", scope: !20, file: !1, line: 19, type: !4)
+!37 = !DILocation(line: 0, scope: !20)
+!38 = !DILocation(line: 16, column: 3, scope: !20)
+!39 = !DILocation(line: 17, column: 56, scope: !20)
+!40 = !DILocation(line: 17, column: 21, scope: !20)
+!41 = !DILocation(line: 18, column: 19, scope: !20)
+!42 = !DILocation(line: 21, column: 30, scope: !20)
+!43 = !DILocation(line: 21, column: 48, scope: !20)
+!44 = !DILocation(line: 21, column: 3, scope: !20)
+!45 = !DILocation(line: 22, column: 12, scope: !20)
+!46 = !DILocation(line: 26, column: 7, scope: !20)
+!47 = !{!48, !48, i64 0}
+!48 = !{!"long long", !49, i64 0}
+!49 = !{!"omnipotent char", !50, i64 0}
+!50 = !{!"Simple C/C++ TBAA"}
+!51 = !DILocation(line: 27, column: 7, scope: !52)
+!52 = distinct !DILexicalBlock(scope: !20, file: !1, line: 27, column: 7)
+!53 = !DILocation(line: 27, column: 7, scope: !20)
+!54 = !DILocation(line: 30, column: 1, scope: !20)
diff --git a/llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-2.ll b/llvm/test/CodeGen/BPF/CORE/offset-reloc-fieldinfo-2.ll
new file mode 100644 (file)
index 0000000..96680c9
--- /dev/null
@@ -0,0 +1,246 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EL %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s | FileCheck -check-prefixes=CHECK,CHECK-EB %s
+; Source code:
+;   struct s {
+;     int a;
+;     int b1:9;
+;     int b2:4;
+;   };
+;   enum {
+;     FIELD_BYTE_OFFSET = 0,
+;     FIELD_BYTE_SIZE,
+;     FIELD_EXISTENCE,
+;     FIELD_SIGNEDNESS,
+;     FIELD_LSHIFT_U64,
+;     FIELD_RSHIFT_U64,
+;   };
+;   int field_read(struct s *arg) {
+;     unsigned long long ull;
+;     unsigned offset = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_OFFSET);
+;     unsigned size = __builtin_preserve_field_info(arg->b2, FIELD_BYTE_SIZE);
+;     switch(size) {
+;     case 1:
+;       ull = *(unsigned char *)((void *)arg + offset); break;
+;     case 2:
+;       ull = *(unsigned short *)((void *)arg + offset); break;
+;     case 4:
+;       ull = *(unsigned int *)((void *)arg + offset); break;
+;     case 8:
+;       ull = *(unsigned long long *)((void *)arg + offset); break;
+;     }
+;     ull <<= __builtin_preserve_field_info(arg->b2, FIELD_LSHIFT_U64);
+;     if (__builtin_preserve_field_info(arg->b2, FIELD_SIGNEDNESS))
+;       return ((long long)ull) >>__builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
+;     return ull >> __builtin_preserve_field_info(arg->b2, FIELD_RSHIFT_U64);
+;   }
+; Compilation flag:
+;   clang -target bpf -O2 -g -S -emit-llvm test.c
+
+%struct.s = type { i32, i16 }
+
+; Function Attrs: nounwind readonly
+define dso_local i32 @field_read(%struct.s* %arg) local_unnamed_addr #0 !dbg !26 {
+entry:
+  call void @llvm.dbg.value(metadata %struct.s* %arg, metadata !37, metadata !DIExpression()), !dbg !41
+  %0 = tail call i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s* %arg, i32 1, i32 2), !dbg !42, !llvm.preserve.access.index !31
+  %1 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 0), !dbg !43
+  call void @llvm.dbg.value(metadata i32 %1, metadata !39, metadata !DIExpression()), !dbg !41
+  %2 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 1), !dbg !44
+  call void @llvm.dbg.value(metadata i32 %2, metadata !40, metadata !DIExpression()), !dbg !41
+  switch i32 %2, label %sw.epilog [
+    i32 1, label %sw.bb
+    i32 2, label %sw.bb1
+    i32 4, label %sw.bb5
+    i32 8, label %sw.bb9
+  ], !dbg !45
+
+sw.bb:                                            ; preds = %entry
+  %3 = bitcast %struct.s* %arg to i8*, !dbg !46
+  %idx.ext = zext i32 %1 to i64, !dbg !48
+  %add.ptr = getelementptr i8, i8* %3, i64 %idx.ext, !dbg !48
+  %4 = load i8, i8* %add.ptr, align 1, !dbg !49, !tbaa !50
+  %conv = zext i8 %4 to i64, !dbg !49
+  call void @llvm.dbg.value(metadata i64 %conv, metadata !38, metadata !DIExpression()), !dbg !41
+  br label %sw.epilog, !dbg !53
+
+sw.bb1:                                           ; preds = %entry
+  %5 = bitcast %struct.s* %arg to i8*, !dbg !54
+  %idx.ext2 = zext i32 %1 to i64, !dbg !55
+  %add.ptr3 = getelementptr i8, i8* %5, i64 %idx.ext2, !dbg !55
+  %6 = bitcast i8* %add.ptr3 to i16*, !dbg !56
+  %7 = load i16, i16* %6, align 2, !dbg !57, !tbaa !58
+  %conv4 = zext i16 %7 to i64, !dbg !57
+  call void @llvm.dbg.value(metadata i64 %conv4, metadata !38, metadata !DIExpression()), !dbg !41
+  br label %sw.epilog, !dbg !60
+
+sw.bb5:                                           ; preds = %entry
+  %8 = bitcast %struct.s* %arg to i8*, !dbg !61
+  %idx.ext6 = zext i32 %1 to i64, !dbg !62
+  %add.ptr7 = getelementptr i8, i8* %8, i64 %idx.ext6, !dbg !62
+  %9 = bitcast i8* %add.ptr7 to i32*, !dbg !63
+  %10 = load i32, i32* %9, align 4, !dbg !64, !tbaa !65
+  %conv8 = zext i32 %10 to i64, !dbg !64
+  call void @llvm.dbg.value(metadata i64 %conv8, metadata !38, metadata !DIExpression()), !dbg !41
+  br label %sw.epilog, !dbg !67
+
+sw.bb9:                                           ; preds = %entry
+  %11 = bitcast %struct.s* %arg to i8*, !dbg !68
+  %idx.ext10 = zext i32 %1 to i64, !dbg !69
+  %add.ptr11 = getelementptr i8, i8* %11, i64 %idx.ext10, !dbg !69
+  %12 = bitcast i8* %add.ptr11 to i64*, !dbg !70
+  %13 = load i64, i64* %12, align 8, !dbg !71, !tbaa !72
+  call void @llvm.dbg.value(metadata i64 %13, metadata !38, metadata !DIExpression()), !dbg !41
+  br label %sw.epilog, !dbg !74
+
+sw.epilog:                                        ; preds = %entry, %sw.bb9, %sw.bb5, %sw.bb1, %sw.bb
+  %ull.0 = phi i64 [ undef, %entry ], [ %13, %sw.bb9 ], [ %conv8, %sw.bb5 ], [ %conv4, %sw.bb1 ], [ %conv, %sw.bb ]
+  call void @llvm.dbg.value(metadata i64 %ull.0, metadata !38, metadata !DIExpression()), !dbg !41
+  %14 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 4), !dbg !75
+  %sh_prom = zext i32 %14 to i64, !dbg !76
+  %shl = shl i64 %ull.0, %sh_prom, !dbg !76
+  call void @llvm.dbg.value(metadata i64 %shl, metadata !38, metadata !DIExpression()), !dbg !41
+  %15 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 3), !dbg !77
+  %tobool = icmp eq i32 %15, 0, !dbg !77
+  %16 = tail call i32 @llvm.bpf.preserve.field.info.p0i16(i16* %0, i64 5), !dbg !41
+  %sh_prom12 = zext i32 %16 to i64, !dbg !41
+  %shr = ashr i64 %shl, %sh_prom12, !dbg !79
+  %shr15 = lshr i64 %shl, %sh_prom12, !dbg !79
+  %retval.0.in = select i1 %tobool, i64 %shr15, i64 %shr, !dbg !79
+  %retval.0 = trunc i64 %retval.0.in to i32, !dbg !41
+  ret i32 %retval.0, !dbg !80
+}
+
+; CHECK:             r{{[0-9]+}} = 4
+; CHECK:             r{{[0-9]+}} = 4
+; CHECK-EL:          r{{[0-9]+}} = 51
+; CHECK-EB:          r{{[0-9]+}} = 41
+; CHECK:             r{{[0-9]+}} = 60
+; CHECK:             r{{[0-9]+}} = 1
+
+; CHECK:             .long   1                       # BTF_KIND_STRUCT(id = 2)
+; CHECK:             .byte   115                     # string offset=1
+; CHECK:             .ascii  ".text"                 # string offset=30
+; CHECK:             .ascii  "0:2"                   # string offset=36
+
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   30                      # Field reloc section string offset=30
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   0
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   1
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   4
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   5
+; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
+; CHECK-NEXT:        .long   2
+; CHECK-NEXT:        .long   36
+; CHECK-NEXT:        .long   3
+
+; Function Attrs: nounwind readnone
+declare i16* @llvm.preserve.struct.access.index.p0i16.p0s_struct.ss(%struct.s*, i32, i32) #1
+
+; Function Attrs: nounwind readnone
+declare i32 @llvm.bpf.preserve.field.info.p0i16(i16*, i64) #1
+
+; Function Attrs: nounwind readnone speculatable willreturn
+declare void @llvm.dbg.value(metadata, metadata, metadata) #2
+
+attributes #0 = { nounwind readonly "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
+attributes #1 = { nounwind readnone }
+attributes #2 = { nounwind readnone speculatable willreturn }
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!22, !23, !24}
+!llvm.ident = !{!25}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 10.0.0 (https://github.com/llvm/llvm-project.git 923aa0ce806f7739b754167239fee2c9a15e2f31)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, retainedTypes: !12, nameTableKind: None)
+!1 = !DIFile(filename: "test.c", directory: "/tmp/home/yhs/work/tests/core")
+!2 = !{!3}
+!3 = !DICompositeType(tag: DW_TAG_enumeration_type, file: !1, line: 6, baseType: !4, size: 32, elements: !5)
+!4 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned)
+!5 = !{!6, !7, !8, !9, !10, !11}
+!6 = !DIEnumerator(name: "FIELD_BYTE_OFFSET", value: 0, isUnsigned: true)
+!7 = !DIEnumerator(name: "FIELD_BYTE_SIZE", value: 1, isUnsigned: true)
+!8 = !DIEnumerator(name: "FIELD_EXISTENCE", value: 2, isUnsigned: true)
+!9 = !DIEnumerator(name: "FIELD_SIGNEDNESS", value: 3, isUnsigned: true)
+!10 = !DIEnumerator(name: "FIELD_LSHIFT_U64", value: 4, isUnsigned: true)
+!11 = !DIEnumerator(name: "FIELD_RSHIFT_U64", value: 5, isUnsigned: true)
+!12 = !{!13, !15, !16, !18, !19, !21}
+!13 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !14, size: 64)
+!14 = !DIBasicType(name: "unsigned char", size: 8, encoding: DW_ATE_unsigned_char)
+!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64)
+!16 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !17, size: 64)
+!17 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned)
+!18 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !4, size: 64)
+!19 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !20, size: 64)
+!20 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned)
+!21 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed)
+!22 = !{i32 2, !"Dwarf Version", i32 4}
+!23 = !{i32 2, !"Debug Info Version", i32 3}
+!24 = !{i32 1, !"wchar_size", i32 4}
+!25 = !{!"clang version 10.0.0 (https://github.com/llvm/llvm-project.git 923aa0ce806f7739b754167239fee2c9a15e2f31)"}
+!26 = distinct !DISubprogram(name: "field_read", scope: !1, file: !1, line: 14, type: !27, scopeLine: 14, flags: DIFlagPrototyped, isDefinition: true, isOptimized: true, unit: !0, retainedNodes: !36)
+!27 = !DISubroutineType(types: !28)
+!28 = !{!29, !30}
+!29 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+!30 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !31, size: 64)
+!31 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "s", file: !1, line: 1, size: 64, elements: !32)
+!32 = !{!33, !34, !35}
+!33 = !DIDerivedType(tag: DW_TAG_member, name: "a", scope: !31, file: !1, line: 2, baseType: !29, size: 32)
+!34 = !DIDerivedType(tag: DW_TAG_member, name: "b1", scope: !31, file: !1, line: 3, baseType: !29, size: 9, offset: 32, flags: DIFlagBitField, extraData: i64 32)
+!35 = !DIDerivedType(tag: DW_TAG_member, name: "b2", scope: !31, file: !1, line: 4, baseType: !29, size: 4, offset: 41, flags: DIFlagBitField, extraData: i64 32)
+!36 = !{!37, !38, !39, !40}
+!37 = !DILocalVariable(name: "arg", arg: 1, scope: !26, file: !1, line: 14, type: !30)
+!38 = !DILocalVariable(name: "ull", scope: !26, file: !1, line: 15, type: !20)
+!39 = !DILocalVariable(name: "offset", scope: !26, file: !1, line: 16, type: !4)
+!40 = !DILocalVariable(name: "size", scope: !26, file: !1, line: 17, type: !4)
+!41 = !DILocation(line: 0, scope: !26)
+!42 = !DILocation(line: 16, column: 56, scope: !26)
+!43 = !DILocation(line: 16, column: 21, scope: !26)
+!44 = !DILocation(line: 17, column: 19, scope: !26)
+!45 = !DILocation(line: 18, column: 3, scope: !26)
+!46 = !DILocation(line: 20, column: 30, scope: !47)
+!47 = distinct !DILexicalBlock(scope: !26, file: !1, line: 18, column: 16)
+!48 = !DILocation(line: 20, column: 42, scope: !47)
+!49 = !DILocation(line: 20, column: 11, scope: !47)
+!50 = !{!51, !51, i64 0}
+!51 = !{!"omnipotent char", !52, i64 0}
+!52 = !{!"Simple C/C++ TBAA"}
+!53 = !DILocation(line: 20, column: 53, scope: !47)
+!54 = !DILocation(line: 22, column: 31, scope: !47)
+!55 = !DILocation(line: 22, column: 43, scope: !47)
+!56 = !DILocation(line: 22, column: 12, scope: !47)
+!57 = !DILocation(line: 22, column: 11, scope: !47)
+!58 = !{!59, !59, i64 0}
+!59 = !{!"short", !51, i64 0}
+!60 = !DILocation(line: 22, column: 54, scope: !47)
+!61 = !DILocation(line: 24, column: 29, scope: !47)
+!62 = !DILocation(line: 24, column: 41, scope: !47)
+!63 = !DILocation(line: 24, column: 12, scope: !47)
+!64 = !DILocation(line: 24, column: 11, scope: !47)
+!65 = !{!66, !66, i64 0}
+!66 = !{!"int", !51, i64 0}
+!67 = !DILocation(line: 24, column: 52, scope: !47)
+!68 = !DILocation(line: 26, column: 35, scope: !47)
+!69 = !DILocation(line: 26, column: 47, scope: !47)
+!70 = !DILocation(line: 26, column: 12, scope: !47)
+!71 = !DILocation(line: 26, column: 11, scope: !47)
+!72 = !{!73, !73, i64 0}
+!73 = !{!"long long", !51, i64 0}
+!74 = !DILocation(line: 26, column: 58, scope: !47)
+!75 = !DILocation(line: 28, column: 11, scope: !26)
+!76 = !DILocation(line: 28, column: 7, scope: !26)
+!77 = !DILocation(line: 29, column: 7, scope: !78)
+!78 = distinct !DILexicalBlock(scope: !26, file: !1, line: 29, column: 7)
+!79 = !DILocation(line: 29, column: 7, scope: !26)
+!80 = !DILocation(line: 32, column: 1, scope: !26)
index 296e2d4..e83b329 100644 (file)
@@ -34,12 +34,13 @@ entry:
 ; CHECK:              .ascii  "v3"                    # string offset=16
 ; CHECK:              .ascii  "0:1"                   # string offset=23
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   10                      # Offset reloc section string offset=10
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   10                      # Field reloc section string offset=10
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   23
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 721081e..66e8e66 100644 (file)
@@ -36,12 +36,13 @@ entry:
 ; CHECK:              .ascii  "v3"                    # string offset=16
 ; CHECK:              .ascii  "7:1"                   # string offset=23
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   10                      # Offset reloc section string offset=10
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   10                      # Field reloc section string offset=10
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   23
+; CHECK-NEXT:         .long   0
 
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
index 394d04f..13c70fd 100644 (file)
@@ -34,12 +34,13 @@ entry:
 ; CHECK:              .ascii  "v3"                    # string offset=16
 ; CHECK:              .ascii  "0:1"                   # string offset=23
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   10                      # Offset reloc section string offset=10
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   10                      # Field reloc section string offset=10
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   23
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 2d14e71..4646377 100644 (file)
@@ -21,7 +21,7 @@ entry:
 ; CHECK:             r1 += 16
 ; CHECK:             call get_value
 ; CHECK:             .section        .BTF.ext,"",@progbits
-; CHECK-NOT:         .long   12                      # OffsetReloc
+; CHECK-NOT:         .long   16                      # FieldReloc
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 0f75cd8..14e58d7 100644 (file)
@@ -50,18 +50,21 @@ entry:
 ; CHECK:             .ascii  "0:0:0"                 # string offset=76
 ; CHECK:             .ascii  "0:0:0:0"               # string offset=82
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   29                      # Offset reloc section string offset=29
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   29                      # Field reloc section string offset=29
 ; CHECK-NEXT:        .long   3
 ; CHECK_NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK_NEXT:        .long   2
 ; CHECK_NEXT:        .long   72
+; CHECK_NEXT:        .long   0
 ; CHECK_NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK_NEXT:        .long   2
 ; CHECK_NEXT:        .long   76
+; CHECK_NEXT:        .long   0
 ; CHECK_NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK_NEXT:        .long   2
 ; CHECK_NEXT:        .long   82
+; CHECK_NEXT:        .long   0
 
 ; Function Attrs: nounwind readnone
 declare %struct.s1* @llvm.preserve.struct.access.index.p0s_struct.s1s.p0s_struct.r1s(%struct.r1*, i32, i32) #1
index 7f79196..61ff0f6 100644 (file)
@@ -35,12 +35,13 @@ entry:
 ; CHECK:              .ascii  ".text"                 # string offset=52
 ; CHECK:              .ascii  "1:1:2:3"               # string offset=58
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   52                      # Offset reloc section string offset=52
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   52                      # Field reloc section string offset=52
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   58
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index a9c29aa..612c3d2 100644 (file)
@@ -36,12 +36,13 @@ entry:
 ; CHECK:             .ascii  ".text"                 # string offset=52
 ; CHECK:             .ascii  "1:1:2:3:2"             # string offset=58
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   52                      # Offset reloc section string offset=52
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   52                      # Field reloc section string offset=52
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:        .long   [[TID1]]
 ; CHECK-NEXT:        .long   58
+; CHECK-NEXT:        .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 779b395..120b85d 100644 (file)
@@ -117,17 +117,18 @@ define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15
 ; CHECK-NEXT:        .long   20
 ; CHECK-NEXT:        .long   76
 ; CHECK-NEXT:        .long   96
-; CHECK-NEXT:        .long   24
-; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   124
 ; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   8                       # FuncInfo
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   57                      # Offset reloc section string offset=57
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   57                      # Field reloc section string offset=57
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp2
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   100
+; CHECK-NEXT:        .long   0
 
 ; Function Attrs: argmemonly nounwind
 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
index e85b393..4d8875c 100644 (file)
@@ -32,12 +32,13 @@ entry:
 ; CHECK:              .ascii  ".text"                 # string offset=26
 ; CHECK:              .byte   49                      # string offset=32
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   26                      # Offset reloc section string offset=26
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   26                      # Field reloc section string offset=26
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   32
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 1c54935..3527902 100644 (file)
@@ -31,12 +31,13 @@ entry:
 ; CHECK:              .ascii  ".text"                 # string offset=26
 ; CHECK:              .ascii  "1:1"                   # string offset=32
 
-; CHECK:              .long   12                      # OffsetReloc
-; CHECK-NEXT:         .long   26                      # Offset reloc section string offset=26
+; CHECK:              .long   16                      # FieldReloc
+; CHECK-NEXT:         .long   26                      # Field reloc section string offset=26
 ; CHECK-NEXT:         .long   1
 ; CHECK-NEXT:         .long   .Ltmp{{[0-9]+}}
 ; CHECK-NEXT:         .long   [[TID1]]
 ; CHECK-NEXT:         .long   32
+; CHECK-NEXT:         .long   0
 
 declare dso_local i32 @get_value(i32*) local_unnamed_addr #1
 
index 08a204f..f77152b 100644 (file)
@@ -127,17 +127,18 @@ define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15
 ; CHECK-NEXT:        .long   20
 ; CHECK-NEXT:        .long   76
 ; CHECK-NEXT:        .long   96
-; CHECK-NEXT:        .long   24
-; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   124
 ; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   8                       # FuncInfo
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   66                      # Offset reloc section string offset=66
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   66                      # Field reloc section string offset=66
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp2
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   109
+; CHECK-NEXT:        .long   0
 
 ; Function Attrs: argmemonly nounwind
 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
index b18a4ab..a56b8fd 100644 (file)
@@ -130,17 +130,18 @@ define dso_local i32 @bpf_prog(%struct.sk_buff*) local_unnamed_addr #0 !dbg !15
 ; CHECK-NEXT:        .long   20
 ; CHECK-NEXT:        .long   76
 ; CHECK-NEXT:        .long   96
-; CHECK-NEXT:        .long   24
-; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   124
 ; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   8                       # FuncInfo
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   77                      # Offset reloc section string offset=77
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   77                      # Field reloc section string offset=77
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp2
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   0
 
 ; Function Attrs: argmemonly nounwind
 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1
index d63bc07..4659d1b 100644 (file)
@@ -38,12 +38,13 @@ entry:
 ; CHECK-NEXT:    .byte   0
 ; CHECK:         .ascii  "0:0:1"                 # string offset=[[ACCESS_STR:[0-9]+]]
 ; CHECK-NEXT:    .byte   0
-; CHECK:         .long   12                      # OffsetReloc
-; CHECK-NEXT:    .long   [[SEC_INDEX]]           # Offset reloc section string offset=[[SEC_INDEX]]
+; CHECK:         .long   16                      # FieldReloc
+; CHECK-NEXT:    .long   [[SEC_INDEX]]           # Field reloc section string offset=[[SEC_INDEX]]
 ; CHECK-NEXT:    .long   1
 ; CHECK-NEXT:    .long   [[RELOC]]
 ; CHECK-NEXT:    .long   [[TYPE_ID]]
 ; CHECK-NEXT:    .long   [[ACCESS_STR]]
+; CHECK-NEXT:    .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index a0d7532..4bf6cff 100644 (file)
@@ -37,12 +37,13 @@ entry:
 ; CHECK-NEXT:   .byte   0
 ; CHECK:        .ascii  "0:1"                   # string offset=[[ACCESS_STR:[0-9]+]]
 ; CHECK-NEXT:   .byte   0
-; CHECK:        .long   12                      # OffsetReloc
-; CHECK-NEXT:   .long   [[SEC_STR]]             # Offset reloc section string offset={{[0-9]+}}
+; CHECK:        .long   16                      # FieldReloc
+; CHECK-NEXT:   .long   [[SEC_STR]]             # Field reloc section string offset={{[0-9]+}}
 ; CHECK-NEXT:   .long   1
 ; CHECK-NEXT:   .long   [[RELOC]]
 ; CHECK-NEXT:   .long   [[TYPE_ID]]
 ; CHECK-NEXT:   .long   [[ACCESS_STR]]
+; CHECK-NEXT:   .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index caefc2b..707a458 100644 (file)
@@ -37,12 +37,13 @@ entry:
 ; CHECK-NEXT:    .byte   0
 ; CHECK:         .ascii  "0:1"                   # string offset=[[ACCESS_STR:[0-9]+]]
 ; CHECK-NEXT:    .byte   0
-; CHECK:         .long   12                      # OffsetReloc
-; CHECK-NEXT:    .long   [[SEC_INDEX]]           # Offset reloc section string offset=[[SEC_INDEX]]
+; CHECK:         .long   16                      # FieldReloc
+; CHECK-NEXT:    .long   [[SEC_INDEX]]           # Field reloc section string offset=[[SEC_INDEX]]
 ; CHECK-NEXT:    .long   1
 ; CHECK-NEXT:    .long   [[RELOC]]
 ; CHECK-NEXT:    .long   [[TYPE_ID]]
 ; CHECK-NEXT:    .long   [[ACCESS_STR]]
+; CHECK-NEXT:    .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index 8281694..b6a0854 100644 (file)
@@ -45,12 +45,13 @@ entry:
 ; CHECK-NEXT:    .byte   0
 ; CHECK:         .ascii  "1:1:1"                 # string offset=[[ACCESS_STR:[0-9]+]]
 ; CHECK-NEXT:    .byte   0
-; CHECK:         .long   12                      # OffsetReloc
-; CHECK-NEXT:    .long   [[SEC_STR:[0-9]+]]      # Offset reloc section string offset=[[SEC_STR:[0-9]+]]
+; CHECK:         .long   16                      # FieldReloc
+; CHECK-NEXT:    .long   [[SEC_STR:[0-9]+]]      # Field reloc section string offset=[[SEC_STR:[0-9]+]]
 ; CHECK-NEXT:    .long   1
 ; CHECK-NEXT:    .long   [[RELOC:.Ltmp[0-9]+]]
 ; CHECK-NEXT:    .long   [[TYPE_ID:[0-9]+]]
 ; CHECK-NEXT:    .long   [[ACCESS_STR:[0-9]+]]
+; CHECK-NEXT:    .long   0
 
 declare dso_local i32 @get_value(i8*) local_unnamed_addr #1
 
index eefa1eb..cb60c81 100644 (file)
@@ -133,17 +133,18 @@ define dso_local i32 @bpf_prog(%union.sk_buff*) local_unnamed_addr #0 !dbg !15 {
 ; CHECK-NEXT:        .long   20
 ; CHECK-NEXT:        .long   76
 ; CHECK-NEXT:        .long   96
-; CHECK-NEXT:        .long   24
-; CHECK-NEXT:        .long   120
+; CHECK-NEXT:        .long   28
+; CHECK-NEXT:        .long   124
 ; CHECK-NEXT:        .long   0
 ; CHECK-NEXT:        .long   8                       # FuncInfo
 
-; CHECK:             .long   12                      # OffsetReloc
-; CHECK-NEXT:        .long   54                      # Offset reloc section string offset=54
+; CHECK:             .long   16                      # FieldReloc
+; CHECK-NEXT:        .long   54                      # Field reloc section string offset=54
 ; CHECK-NEXT:        .long   1
 ; CHECK-NEXT:        .long   .Ltmp2
 ; CHECK-NEXT:        .long   2
 ; CHECK-NEXT:        .long   97
+; CHECK-NEXT:        .long   0
 
 ; Function Attrs: argmemonly nounwind
 declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1