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>]>;
}
namespace llvm {
class BPFTargetMachine;
-ModulePass *createBPFAbstractMemberAccess();
+ModulePass *createBPFAbstractMemberAccess(BPFTargetMachine *TM);
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
FunctionPass *createBPFMISimplifyPatchablePass();
// 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"
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;
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;
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);
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) {
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;
}
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));
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,
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) {
// 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;
// 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();
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;
}
}
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);
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;
}
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;
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);
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
void BPFPassConfig::addIRPasses() {
- addPass(createBPFAbstractMemberAccess());
+ addPass(createBPFAbstractMemberAccess(&getBPFTargetMachine()));
TargetPassConfig::addIRPasses();
}
///
/// 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
BTFDataSecVarSize = 12,
SecFuncInfoSize = 8,
SecLineInfoSize = 8,
- SecOffsetRelocSize = 8,
+ SecFieldRelocSize = 8,
SecExternRelocSize = 8,
BPFFuncInfoSize = 8,
BPFLineInfoSize = 16,
- BPFOffsetRelocSize = 12,
+ BPFFieldRelocSize = 16,
BPFExternRelocSize = 8,
};
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
};
};
/// 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.
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();
// 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;
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;
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.
}
}
- // 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);
}
}
}
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) {
// 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
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();
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));
/// 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;
};
/// 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.
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;
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.
; 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
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
--- /dev/null
+; 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)
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
--- /dev/null
+; 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)
--- /dev/null
+; 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)
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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
; 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