[Hexagon][llvm-objdump] Improve disassembly of Hexagon bundles (#145807)

Hexagon instructions are VLIW "bundles" of up to four instruction words
encoded as a single MCInst with operands for each sub-instruction.
Previously, the disassembler's getInstruction() returned the full
bundle, which made it difficult to work with llvm-objdump.

For example, since all instructions are bundles, and bundles do not
branch, branch targets could not be printed.

This patch modifies the Hexagon disassembler to return individual
sub-instructions instead of entire bundles, enabling correct printing of
branch targets and relocations. It also introduces
`MCDisassembler::getInstructionBundle` for cases where the full bundle
is still needed.

By default, llvm-objdump separates instructions with newlines. However,
this does not work well for Hexagon syntax:

  { inst1
    inst2
    inst3
    inst4 <branch> } :endloop0

Instructions may be followed by a closing brace, a closing brace with
`:endloop`, or a newline. Branches must appear within the braces.

To address this, `PrettyPrinter::getInstructionSeparator()` is added and
overridden for Hexagon.
This commit is contained in:
quic-areg 2025-07-18 10:27:59 -05:00 committed by GitHub
parent 32f0fc597f
commit ac7ceb3dab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 300 additions and 141 deletions

View File

@ -30,31 +30,31 @@
# DIS: <_start>:
## Direct call
## Call foo directly
# DIS-NEXT: { call 0x2003c }
# DIS-NEXT: { call 0x2003c <foo> }
## Call bar via plt
# DIS-NEXT: { call 0x20060 }
# DIS-NEXT: { call 0x20060 <bar@plt> }
## Call weak via plt
# DIS-NEXT: { call 0x20070 }
# DIS-NEXT: { call 0x20070 <weak@plt> }
# DIS-NEXT: { immext(#0)
## Call foo directly
# DIS-NEXT: if (p0) jump:nt 0x2003c }
# DIS-NEXT: if (p0) jump:nt 0x2003c <foo> }
# DIS-NEXT: { immext(#64)
## Call bar via plt
# DIS-NEXT: if (p0) jump:nt 0x20060 }
# DIS-NEXT: if (p0) jump:nt 0x20060 <bar@plt> }
# DIS-NEXT: { immext(#64)
## Call weak via plt
# DIS-NEXT: if (p0) jump:nt 0x20070 }
# DIS-NEXT: if (p0) jump:nt 0x20070 <weak@plt> }
# DIS-NEXT: { immext(#0)
## Call foo directly
# DIS-NEXT: r0 = #0 ; jump 0x2003c }
# DIS-NEXT: r0 = #0 ; jump 0x2003c <foo> }
# DIS-NEXT: { immext(#0)
## Call bar via plt
# DIS-NEXT: r0 = #0 ; jump 0x20060 }
# DIS-NEXT: r0 = #0 ; jump 0x20060 <bar@plt> }
# DIS-NEXT: { immext(#0)
## Call weak via plt
# DIS-NEXT: r0 = #0 ; jump 0x20070 }
# DIS-NEXT: r0 = #0 ; jump 0x20070 <weak@plt> }
# DIS: <foo>:
# DIS-NEXT: 2003c:

View File

@ -88,7 +88,7 @@ pvar:
# PLT-NEXT: jumpr r28 }
# TEXT: bc 00 01 00 000100bc
# TEXT: { call 0x10300 }
# TEXT: { call 0x10300 <bar@plt> }
# TEXT: if (p0) jump:nt 0x10300
# TEXT: r0 = #0 ; jump 0x10300
# TEXT: r0 = add(r1,##-65548)

View File

@ -18,10 +18,10 @@
_start:
.ifdef GDPLT
call x@gdplt
# CHECK_GDPLT: 101ec: { call 0x10220 }
# CHECK_GDPLT: 101ec: { call 0x10220 <__tls_get_addr@plt> }
.else
call x
# CHECK: 101b8: { call 0x101e0 }
# CHECK: 101b8: { call 0x101e0 <x@plt> }
.endif
# CHECK_GDPLT: 10220: { immext(#0x20040)

View File

@ -136,6 +136,18 @@ public:
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &CStream) const = 0;
/// Returns the disassembly of an instruction bundle for VLIW architectures
/// like Hexagon.
///
/// \param Instr - An MCInst to populate with the contents of
/// the Bundle with sub-instructions encoded as Inst operands.
virtual DecodeStatus getInstructionBundle(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &CStream) const {
return Fail;
}
/// Used to perform separate target specific disassembly for a particular
/// symbol. May parse any prelude that precedes instructions after the
/// start of a symbol, or the entire symbol.

View File

@ -43,12 +43,12 @@ namespace {
class HexagonDisassembler : public MCDisassembler {
public:
std::unique_ptr<MCInstrInfo const> const MCII;
std::unique_ptr<MCInst *> CurrentBundle;
mutable std::unique_ptr<MCInst> CurrentBundle;
mutable MCInst const *CurrentExtender;
HexagonDisassembler(const MCSubtargetInfo &STI, MCContext &Ctx,
MCInstrInfo const *MCII)
: MCDisassembler(STI, Ctx), MCII(MCII), CurrentBundle(new MCInst *),
: MCDisassembler(STI, Ctx), MCII(MCII), CurrentBundle(nullptr),
CurrentExtender(nullptr) {}
DecodeStatus getSingleInstruction(MCInst &Instr, MCInst &MCB,
@ -57,7 +57,23 @@ public:
DecodeStatus getInstruction(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &CStream) const override;
DecodeStatus getInstructionBundle(MCInst &Instr, uint64_t &Size,
ArrayRef<uint8_t> Bytes, uint64_t Address,
raw_ostream &CStream) const override;
void remapInstruction(MCInst &Instr) const;
private:
bool makeBundle(ArrayRef<uint8_t> Bytes, uint64_t Address,
uint64_t &BytesToSkip, raw_ostream &CS) const;
void resetBundle() const {
CurrentBundle.reset();
CurrentInstruction = nullptr;
}
mutable MCOperand *CurrentInstruction = nullptr;
};
static uint64_t fullValue(HexagonDisassembler const &Disassembler, MCInst &MI,
@ -171,43 +187,88 @@ LLVMInitializeHexagonDisassembler() {
createHexagonDisassembler);
}
bool HexagonDisassembler::makeBundle(ArrayRef<uint8_t> Bytes, uint64_t Address,
uint64_t &BytesToSkip,
raw_ostream &CS) const {
bool Complete = false;
DecodeStatus Result = DecodeStatus::Success;
CurrentBundle.reset(new MCInst);
CurrentBundle->setOpcode(Hexagon::BUNDLE);
CurrentBundle->addOperand(MCOperand::createImm(0));
while (Result == Success && !Complete) {
if (Bytes.size() < HEXAGON_INSTR_SIZE)
return false;
MCInst *Inst = getContext().createMCInst();
Result = getSingleInstruction(*Inst, *CurrentBundle, Bytes, Address, CS,
Complete);
CurrentBundle->addOperand(MCOperand::createInst(Inst));
BytesToSkip += HEXAGON_INSTR_SIZE;
Bytes = Bytes.slice(HEXAGON_INSTR_SIZE);
}
if (Result == MCDisassembler::Fail)
return false;
if (BytesToSkip > HEXAGON_MAX_PACKET_SIZE)
return false;
const auto ArchSTI = Hexagon_MC::getArchSubtarget(&STI);
const auto STI_ = (ArchSTI != nullptr) ? *ArchSTI : STI;
HexagonMCChecker Checker(getContext(), *MCII, STI_, *CurrentBundle,
*getContext().getRegisterInfo(), false);
if (!Checker.check())
return false;
remapInstruction(*CurrentBundle);
return true;
}
DecodeStatus HexagonDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &CS) const {
CommentStream = &CS;
DecodeStatus Result = DecodeStatus::Success;
bool Complete = false;
Size = 0;
uint64_t BytesToSkip = 0;
*CurrentBundle = &MI;
MI.setOpcode(Hexagon::BUNDLE);
MI.addOperand(MCOperand::createImm(0));
while (Result == Success && !Complete) {
if (Bytes.size() < HEXAGON_INSTR_SIZE)
if (!CurrentBundle) {
if (!makeBundle(Bytes, Address, BytesToSkip, CS)) {
Size = BytesToSkip;
resetBundle();
return MCDisassembler::Fail;
MCInst *Inst = getContext().createMCInst();
Result = getSingleInstruction(*Inst, MI, Bytes, Address, CS, Complete);
MI.addOperand(MCOperand::createInst(Inst));
Size += HEXAGON_INSTR_SIZE;
Bytes = Bytes.slice(HEXAGON_INSTR_SIZE);
}
CurrentInstruction = (CurrentBundle->begin() + 1);
}
if (Result == MCDisassembler::Fail)
return Result;
if (Size > HEXAGON_MAX_PACKET_SIZE)
return MCDisassembler::Fail;
const auto ArchSTI = Hexagon_MC::getArchSubtarget(&STI);
const auto STI_ = (ArchSTI != nullptr) ? *ArchSTI : STI;
HexagonMCChecker Checker(getContext(), *MCII, STI_, MI,
*getContext().getRegisterInfo(), false);
if (!Checker.check())
return MCDisassembler::Fail;
remapInstruction(MI);
MI = *(CurrentInstruction->getInst());
Size = HEXAGON_INSTR_SIZE;
if (++CurrentInstruction == CurrentBundle->end())
resetBundle();
return MCDisassembler::Success;
}
DecodeStatus HexagonDisassembler::getInstructionBundle(MCInst &MI,
uint64_t &Size,
ArrayRef<uint8_t> Bytes,
uint64_t Address,
raw_ostream &CS) const {
CommentStream = &CS;
Size = 0;
uint64_t BytesToSkip = 0;
assert(!CurrentBundle);
if (!makeBundle(Bytes, Address, BytesToSkip, CS)) {
Size = BytesToSkip;
resetBundle();
return MCDisassembler::Fail;
}
MI = *CurrentBundle;
Size = HEXAGON_INSTR_SIZE * HexagonMCInstrInfo::bundleSize(MI);
resetBundle();
return Success;
}
void HexagonDisassembler::remapInstruction(MCInst &Instr) const {
for (auto I: HexagonMCInstrInfo::bundleInstructions(Instr)) {
auto &MI = const_cast<MCInst &>(*I.getInst());
@ -482,7 +543,7 @@ DecodeStatus HexagonDisassembler::getSingleInstruction(MCInst &MI, MCInst &MCB,
unsigned Offset = 1;
bool Vector = HexagonMCInstrInfo::isVector(*MCII, MI);
bool PrevVector = false;
auto Instructions = HexagonMCInstrInfo::bundleInstructions(**CurrentBundle);
auto Instructions = HexagonMCInstrInfo::bundleInstructions(*CurrentBundle);
auto i = Instructions.end() - 1;
for (auto n = Instructions.begin() - 1;; --i, ++Offset) {
if (i == n)

View File

@ -33,30 +33,18 @@ void HexagonInstPrinter::printRegName(raw_ostream &O, MCRegister Reg) {
void HexagonInstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot, const MCSubtargetInfo &STI,
raw_ostream &OS) {
assert(HexagonMCInstrInfo::isBundle(*MI));
assert(HexagonMCInstrInfo::bundleSize(*MI) <= HEXAGON_PACKET_SIZE);
assert(HexagonMCInstrInfo::bundleSize(*MI) > 0);
HasExtender = false;
for (auto const &I : HexagonMCInstrInfo::bundleInstructions(*MI)) {
MCInst const &MCI = *I.getInst();
if (HexagonMCInstrInfo::isDuplex(MII, MCI)) {
printInstruction(MCI.getOperand(1).getInst(), Address, OS);
OS << '\v';
HasExtender = false;
printInstruction(MCI.getOperand(0).getInst(), Address, OS);
} else
printInstruction(&MCI, Address, OS);
HasExtender = HexagonMCInstrInfo::isImmext(MCI);
OS << "\n";
}
bool IsLoop0 = HexagonMCInstrInfo::isInnerLoop(*MI);
bool IsLoop1 = HexagonMCInstrInfo::isOuterLoop(*MI);
if (IsLoop0) {
OS << (IsLoop1 ? " :endloop01" : " :endloop0");
} else if (IsLoop1) {
OS << " :endloop1";
if (HexagonMCInstrInfo::isDuplex(MII, *MI)) {
printInstruction(MI->getOperand(1).getInst(), Address, OS);
OS << '\v';
HasExtender = false;
printInstruction(MI->getOperand(0).getInst(), Address, OS);
} else {
printInstruction(MI, Address, OS);
}
HasExtender = HexagonMCInstrInfo::isImmext(*MI);
if ((MI->getOpcode() & HexagonII::INST_PARSE_MASK) ==
HexagonII::INST_PARSE_PACKET_END)
HasExtender = false;
}
void HexagonInstPrinter::printOperand(MCInst const *MI, unsigned OpNo,

View File

@ -252,8 +252,21 @@ public:
std::string Buffer;
{
raw_string_ostream TempStream(Buffer);
InstPrinter.printInst(&Inst, Address, "", STI, TempStream);
for (auto &I : HexagonMCInstrInfo::bundleInstructions(Inst)) {
InstPrinter.printInst(I.getInst(), Address, "", STI, TempStream);
TempStream << "\n";
}
}
std::string LoopString = "";
bool IsLoop0 = HexagonMCInstrInfo::isInnerLoop(Inst);
bool IsLoop1 = HexagonMCInstrInfo::isOuterLoop(Inst);
if (IsLoop0) {
LoopString += (IsLoop1 ? " :endloop01" : " :endloop0");
} else if (IsLoop1) {
LoopString += " :endloop1";
}
StringRef Contents(Buffer);
auto PacketBundle = Contents.rsplit('\n');
auto HeadTail = PacketBundle.first.split('\n');
@ -275,9 +288,9 @@ public:
}
if (HexagonMCInstrInfo::isMemReorderDisabled(Inst))
OS << "\n\t} :mem_noshuf" << PacketBundle.second;
OS << "\n\t} :mem_noshuf" << LoopString;
else
OS << "\t}" << PacketBundle.second;
OS << "\t}" << LoopString;
}
void finish() override { finishAttributeSection(); }

View File

@ -6,7 +6,7 @@
if (!p1) call foo_b
}
# CHECK: 00004000 { immext(#0)
# CHECK: 5d004100 if (p1) call 0x0
# CHECK: 5d004100 if (p1) call 0x0 <.text>
# CHECK: 00004000 immext(#0)
# CHECK: 5d20c100 if (!p1) call 0x0 }
# CHECK: 5d20c100 if (!p1) call 0x0 <.text> }

View File

@ -0,0 +1,47 @@
/// Checks that various hexagon scenarios are handled correctly:
/// - branch targets
/// - endloops
/// - inline-relocs
/// - multi-insn bundles
{
r6 = sub(r1, r0)
r7 = and(r4, #0x0)
if (p1) jump:t target1
if (p2) jump:nt target2
}
{
r8 = r7
r9 = add(r8, #0)
r10 = memw(r9)
} :endloop0
{ jump ##sym }
target1:
nop
target2:
nop
// RUN: llvm-mc %s --triple=hexagon -filetype=obj | llvm-objdump -d -r - | FileCheck %s
// CHECK: 00000000 <.text>:
// CHECK-NEXT: 0: 12 51 00 5c 5c005112 { if (p1) jump:t 0x24 <target1>
// CHECK-NEXT: 4: 14 42 00 5c 5c004214 if (p2) jump:nt 0x28 <target2>
// CHECK-NEXT: 8: 06 41 20 f3 f3204106 r6 = sub(r1,r0)
// CHECK-NEXT: c: 07 c0 04 76 7604c007 r7 = and(r4,#0x0) }
// CHECK-NEXT: 10: 08 80 67 70 70678008 { r8 = r7
// CHECK-NEXT: 14: 09 40 08 b0 b0084009 r9 = add(r8,#0x0)
// CHECK-NEXT: 18: 0a c0 89 91 9189c00a r10 = memw(r9+#0x0) } :endloop0
// CHECK-NEXT: 1c: 00 40 00 00 00004000 { immext(#0x0)
// CHECK-NEXT: 0000001c: R_HEX_B32_PCREL_X sym
// CHECK-NEXT: 20: 00 c0 00 58 5800c000 jump 0x1c <.text+0x1c> }
// CHECK-NEXT: 00000020: R_HEX_B22_PCREL_X sym+0x4
// CHECK-EMPTY:
// CHECK-NEXT: 00000024 <target1>:
// CHECK-NEXT: 24: 00 c0 00 7f 7f00c000 { nop }
// CHECK-EMPTY:
// CHECK-NEXT: 00000028 <target2>:
// CHECK-NEXT: 28: 00 c0 00 7f 7f00c000 { nop }

View File

@ -45,7 +45,11 @@ static bool PrintInsts(const MCDisassembler &DisAsm, const ByteArrayTy &Bytes,
MCInst Inst;
MCDisassembler::DecodeStatus S;
S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
if (STI.getTargetTriple().getArch() == Triple::hexagon)
S = DisAsm.getInstructionBundle(Inst, Size, Data.slice(Index), Index,
nulls());
else
S = DisAsm.getInstruction(Inst, Size, Data.slice(Index), Index, nulls());
switch (S) {
case MCDisassembler::Fail:
SM.PrintMessage(SMLoc::getFromPointer(Bytes.second[Index]),

View File

@ -693,6 +693,30 @@ public:
} else
OS << "\t<unknown>";
}
virtual void emitPostInstructionInfo(formatted_raw_ostream &FOS,
const MCAsmInfo &MAI,
const MCSubtargetInfo &STI,
StringRef Comments,
LiveVariablePrinter &LVP) {
do {
if (!Comments.empty()) {
// Emit a line of comments.
StringRef Comment;
std::tie(Comment, Comments) = Comments.split('\n');
// MAI.getCommentColumn() assumes that instructions are printed at the
// position of 8, while getInstStartColumn() returns the actual
// position.
unsigned CommentColumn =
MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
FOS.PadToColumn(CommentColumn);
FOS << MAI.getCommentString() << ' ' << Comment;
}
LVP.printAfterInst(FOS);
FOS << "\n";
} while (!Comments.empty());
FOS.flush();
}
};
PrettyPrinter PrettyPrinterInst;
@ -714,6 +738,35 @@ public:
}
}
}
std::string getInstructionSeparator() const {
SmallString<40> Separator;
raw_svector_ostream OS(Separator);
if (ShouldClosePacket) {
OS << " }";
if (IsLoop0 || IsLoop1)
OS << " ";
if (IsLoop0)
OS << (IsLoop1 ? ":endloop01" : ":endloop0");
else if (IsLoop1)
OS << ":endloop1";
}
OS << '\n';
return OS.str().str();
}
void emitPostInstructionInfo(formatted_raw_ostream &FOS, const MCAsmInfo &MAI,
const MCSubtargetInfo &STI, StringRef Comments,
LiveVariablePrinter &LVP) override {
// Hexagon does not write anything to the comment stream, so we can just
// print the separator.
LVP.printAfterInst(FOS);
FOS << getInstructionSeparator();
FOS.flush();
if (ShouldClosePacket)
reset();
}
void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
object::SectionedAddress Address, formatted_raw_ostream &OS,
StringRef Annot, MCSubtargetInfo const &STI, SourcePrinter *SP,
@ -724,60 +777,64 @@ public:
if (!MI) {
printLead(Bytes, Address.Address, OS);
OS << " <unknown>";
reset();
return;
}
std::string Buffer;
StringRef Preamble = IsStartOfBundle ? " { " : " ";
if (SP && (PrintSource || PrintLines))
SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
printLead(Bytes, Address.Address, OS);
OS << Preamble;
std::string Buf;
{
raw_string_ostream TempStream(Buffer);
raw_string_ostream TempStream(Buf);
IP.printInst(MI, Address.Address, "", STI, TempStream);
}
StringRef Contents(Buffer);
// Split off bundle attributes
auto PacketBundle = Contents.rsplit('\n');
// Split off first instruction from the rest
auto HeadTail = PacketBundle.first.split('\n');
auto Preamble = " { ";
auto Separator = "";
StringRef Contents(Buf);
// Hexagon's packets require relocations to be inline rather than
// clustered at the end of the packet.
std::vector<RelocationRef>::const_iterator RelCur = Rels->begin();
std::vector<RelocationRef>::const_iterator RelEnd = Rels->end();
auto PrintReloc = [&]() -> void {
while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address.Address)) {
if (RelCur->getOffset() == Address.Address) {
printRelocation(OS, ObjectFilename, *RelCur, Address.Address, false);
return;
}
++RelCur;
}
};
while (!HeadTail.first.empty()) {
OS << Separator;
Separator = "\n";
if (SP && (PrintSource || PrintLines))
SP->printSourceLine(OS, Address, ObjectFilename, LVP, "");
printLead(Bytes, Address.Address, OS);
OS << Preamble;
Preamble = " ";
StringRef Inst;
auto Duplex = HeadTail.first.split('\v');
if (!Duplex.second.empty()) {
OS << Duplex.first;
OS << "; ";
Inst = Duplex.second;
}
else
Inst = HeadTail.first;
OS << Inst;
HeadTail = HeadTail.second.split('\n');
if (HeadTail.first.empty())
OS << " } " << PacketBundle.second;
PrintReloc();
Bytes = Bytes.slice(4);
Address.Address += 4;
auto Duplex = Contents.split('\v');
bool HasDuplex = !Duplex.second.empty();
if (HasDuplex) {
OS << Duplex.first;
OS << "; ";
OS << Duplex.second;
} else {
OS << Duplex.first;
}
uint32_t Instruction = support::endian::read32le(Bytes.data());
uint32_t ParseMask = 0x0000c000;
uint32_t PacketEndMask = 0x0000c000;
uint32_t LoopEndMask = 0x00008000;
uint32_t ParseBits = Instruction & ParseMask;
if (ParseBits == LoopEndMask) {
if (IsStartOfBundle)
IsLoop0 = true;
else
IsLoop1 = true;
}
IsStartOfBundle = false;
if (ParseBits == PacketEndMask || HasDuplex)
ShouldClosePacket = true;
}
private:
bool IsStartOfBundle = true;
bool IsLoop0 = false;
bool IsLoop1 = false;
bool ShouldClosePacket = false;
void reset() {
IsStartOfBundle = true;
IsLoop0 = false;
IsLoop1 = false;
ShouldClosePacket = false;
}
};
HexagonPrettyPrinter HexagonPrettyPrinterInst;
@ -1610,29 +1667,6 @@ static StringRef getSegmentName(const MachOObjectFile *MachO,
return "";
}
static void emitPostInstructionInfo(formatted_raw_ostream &FOS,
const MCAsmInfo &MAI,
const MCSubtargetInfo &STI,
StringRef Comments,
LiveVariablePrinter &LVP) {
do {
if (!Comments.empty()) {
// Emit a line of comments.
StringRef Comment;
std::tie(Comment, Comments) = Comments.split('\n');
// MAI.getCommentColumn() assumes that instructions are printed at the
// position of 8, while getInstStartColumn() returns the actual position.
unsigned CommentColumn =
MAI.getCommentColumn() - 8 + getInstStartColumn(STI);
FOS.PadToColumn(CommentColumn);
FOS << MAI.getCommentString() << ' ' << Comment;
}
LVP.printAfterInst(FOS);
FOS << '\n';
} while (!Comments.empty());
FOS.flush();
}
static void createFakeELFSections(ObjectFile &Obj) {
assert(Obj.isELF());
if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(&Obj))
@ -2526,15 +2560,15 @@ disassembleObject(ObjectFile &Obj, const ObjectFile &DbgObj,
}
assert(DT->Context->getAsmInfo());
emitPostInstructionInfo(FOS, *DT->Context->getAsmInfo(),
*DT->SubtargetInfo, CommentStream.str(), LVP);
DT->Printer->emitPostInstructionInfo(FOS, *DT->Context->getAsmInfo(),
*DT->SubtargetInfo,
CommentStream.str(), LVP);
Comments.clear();
if (BTF)
printBTFRelocation(FOS, *BTF, {Index, Section.getIndex()}, LVP);
// Hexagon handles relocs in pretty printer
if (InlineRelocs && Obj.getArch() != Triple::hexagon) {
if (InlineRelocs) {
while (findRel()) {
// When --adjust-vma is used, update the address printed.
printRelocation(FOS, Obj.getFileName(), *RelCur,