-
Notifications
You must be signed in to change notification settings - Fork 14.7k
[Object] Parsing and dumping of SFrame Frame Row Entries #151301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,14 +32,25 @@ getDataSlice(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Size) { | |
} | ||
|
||
template <typename T> | ||
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data, | ||
uint64_t Offset) { | ||
static Expected<ArrayRef<T>> | ||
getDataSliceAsArrayOf(ArrayRef<uint8_t> Data, uint64_t Offset, uint64_t Count) { | ||
static_assert(std::is_trivial_v<T>); | ||
Expected<ArrayRef<uint8_t>> Slice = getDataSlice(Data, Offset, sizeof(T)); | ||
Expected<ArrayRef<uint8_t>> Slice = | ||
getDataSlice(Data, Offset, sizeof(T) * Count); | ||
if (!Slice) | ||
return Slice.takeError(); | ||
|
||
return *reinterpret_cast<const T *>(Slice->data()); | ||
return ArrayRef(reinterpret_cast<const T *>(Slice->data()), Count); | ||
} | ||
|
||
template <typename T> | ||
static Expected<const T &> getDataSliceAs(ArrayRef<uint8_t> Data, | ||
uint64_t Offset) { | ||
Expected<ArrayRef<T>> Array = getDataSliceAsArrayOf<T>(Data, Offset, 1); | ||
if (!Array) | ||
return Array.takeError(); | ||
|
||
return Array->front(); | ||
} | ||
|
||
template <endianness E> | ||
|
@@ -100,6 +111,119 @@ uint64_t SFrameParser<E>::getAbsoluteStartAddress( | |
return Result; | ||
} | ||
|
||
template <typename EndianT> | ||
static Error readArray(ArrayRef<uint8_t> Data, uint64_t Count, uint64_t &Offset, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this returning an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mainly because it simplifies the caller, which would otherwise need to unpack the Expected and copy/move the value into the larger object. I wouldn't do that for a public interface, but these functions are essentially an implementation detail of I can that if you feel strongly about it, but I think version looks better. |
||
SmallVectorImpl<int32_t> &Vec) { | ||
Expected<ArrayRef<EndianT>> RawArray = | ||
getDataSliceAsArrayOf<EndianT>(Data, Offset, Count); | ||
if (!RawArray) | ||
return RawArray.takeError(); | ||
Offset += Count * sizeof(EndianT); | ||
Vec.resize(Count); | ||
llvm::copy(*RawArray, Vec.begin()); | ||
return Error::success(); | ||
} | ||
|
||
template <typename T, endianness E> | ||
static Error readFRE(ArrayRef<uint8_t> Data, uint64_t &Offset, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same question. |
||
typename SFrameParser<E>::FrameRowEntry &FRE) { | ||
Expected<sframe::FrameRowEntry<T, E>> RawFRE = | ||
getDataSliceAs<sframe::FrameRowEntry<T, E>>(Data, Offset); | ||
if (!RawFRE) | ||
return RawFRE.takeError(); | ||
|
||
Offset += sizeof(*RawFRE); | ||
FRE.StartAddress = RawFRE->StartAddress; | ||
FRE.Info.Info = RawFRE->Info.Info; | ||
|
||
switch (FRE.Info.getOffsetSize()) { | ||
case sframe::FREOffset::B1: | ||
return readArray<sframe::detail::packed<int8_t, E>>( | ||
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); | ||
case sframe::FREOffset::B2: | ||
return readArray<sframe::detail::packed<int16_t, E>>( | ||
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); | ||
case sframe::FREOffset::B4: | ||
return readArray<sframe::detail::packed<int32_t, E>>( | ||
Data, FRE.Info.getOffsetCount(), Offset, FRE.Offsets); | ||
default: | ||
return createError("unsupported/unknown offset size"); | ||
} | ||
} | ||
|
||
template <endianness E> Error SFrameParser<E>::FallibleFREIterator::inc() { | ||
if (++Idx == Size) | ||
return Error::success(); | ||
|
||
switch (FREType) { | ||
case sframe::FREType::Addr1: | ||
return readFRE<uint8_t, E>(Data, Offset, FRE); | ||
case sframe::FREType::Addr2: | ||
return readFRE<uint16_t, E>(Data, Offset, FRE); | ||
case sframe::FREType::Addr4: | ||
return readFRE<uint32_t, E>(Data, Offset, FRE); | ||
default: | ||
return createError("invalid/unsupported FRE type"); | ||
} | ||
} | ||
|
||
template <endianness E> | ||
iterator_range<typename SFrameParser<E>::fre_iterator> | ||
SFrameParser<E>::fres(const sframe::FuncDescEntry<E> &FDE, Error &Err) const { | ||
uint64_t Offset = getFREBase() + FDE.StartFREOff; | ||
fre_iterator BeforeBegin = make_fallible_itr( | ||
FallibleFREIterator(Data, FDE.getFREType(), -1, FDE.NumFREs, Offset), | ||
Err); | ||
fre_iterator End = make_fallible_end( | ||
FallibleFREIterator(Data, FDE.getFREType(), FDE.NumFREs, FDE.NumFREs, | ||
/*Offset=*/0)); | ||
return {++BeforeBegin, End}; | ||
} | ||
|
||
static std::optional<int32_t> getOffset(ArrayRef<int32_t> Offsets, size_t Idx) { | ||
if (Offsets.size() > Idx) | ||
return Offsets[Idx]; | ||
return std::nullopt; | ||
} | ||
|
||
// The interpretation of offsets is ABI-specific. The implementation of this and | ||
// the following functions may need to be adjusted when adding support for a new | ||
// ABI. | ||
template <endianness E> | ||
std::optional<int32_t> | ||
SFrameParser<E>::getCFAOffset(const FrameRowEntry &FRE) const { | ||
return getOffset(FRE.Offsets, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm a little concerned about this and the similar code in the following blocks, mostly because future ABIs/architectures won't necessarily have the same meanings for the different offsets. It's probably sufficient for now to add a comment or two highlighting that all supported ABIs use the specified offset. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I expect that the implementation of these functions will need to change when adding a new ABI. In fact, it looks like the recently updated specification adds support for the s390x architecture/ABI. The offset numbers don't really change, but their interpretation is a lot more complicated (mainly to account for the fact that FP/RA registers can be saved in another register. That will most likely mean changing the interface (return value) of these functions as well. I added a comment as requested. I will also look into adding s390x support to make the parser complete. |
||
} | ||
|
||
template <endianness E> | ||
std::optional<int32_t> | ||
SFrameParser<E>::getRAOffset(const FrameRowEntry &FRE) const { | ||
if (usesFixedRAOffset()) | ||
return Header.CFAFixedRAOffset; | ||
return getOffset(FRE.Offsets, 1); | ||
} | ||
|
||
template <endianness E> | ||
std::optional<int32_t> | ||
SFrameParser<E>::getFPOffset(const FrameRowEntry &FRE) const { | ||
if (usesFixedFPOffset()) | ||
return Header.CFAFixedFPOffset; | ||
return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2); | ||
} | ||
|
||
template <endianness E> | ||
ArrayRef<int32_t> | ||
SFrameParser<E>::getExtraOffsets(const FrameRowEntry &FRE) const { | ||
size_t UsedOffsets = 1; // CFA | ||
if (!usesFixedRAOffset()) | ||
++UsedOffsets; | ||
if (!usesFixedFPOffset()) | ||
++UsedOffsets; | ||
if (FRE.Offsets.size() > UsedOffsets) | ||
return ArrayRef(FRE.Offsets).drop_front(UsedOffsets); | ||
return {}; | ||
} | ||
|
||
template class LLVM_EXPORT_TEMPLATE llvm::object::SFrameParser<endianness::big>; | ||
template class LLVM_EXPORT_TEMPLATE | ||
llvm::object::SFrameParser<endianness::little>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm probably missing something, but why not just have the static local exposed as a constant without the function wrapper?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you mean something like:
then I believe that should work (without creating new global constructors or anything). I did it this way because that's how this is implemented elsewhere (BinaryFormat/DXContainer.h, DebugInfo/CodeView/EnumTables.h)