[llvm-branch-commits] [llvm] [llvm-readobj][ELF] Print call graph callees in section order (PR #230649)

Paul Kirth via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Mon Oct 12 10:49:32 PDT 2026


================
@@ -8410,34 +8419,39 @@ template <class ELFT> void LLVMELFDumper<ELFT>::printCallGraphInfo() {
       W.printString("Name", RelSymOrErr->Name);
     };
 
-    auto PrintFunc = [&](uint64_t FuncPC) {
-      // In a relocatable object file, FuncPC is the offset of the address
-      // field, which is used to look up the relocation that applies to it.
-      if (this->Obj.getHeader().e_type == ELF::ET_REL) {
-        PrintRelocatableFuncSymbol(FuncPC);
+    auto PrintFunc = [&](const CallGraphFunc &Func) {
+      // In a relocatable object file, the address field holds a placeholder.
+      // Its offset is used to look up the relocation that applies to it.
+      if (IsRelocatable) {
+        PrintRelocatableFuncSymbol(Func.FieldOffset);
         return;
       }
-      uint64_t FuncEntryPC = FuncPC;
+      uint64_t FuncEntryPC = Func.FieldValue;
       // In ARM thumb mode the LSB of the function pointer is set to 1. Since
       // this detail is unnecessary in call graph reconstruction, we are
       // clearing this bit to facilitate tooling.
       if (this->Obj.getHeader().e_machine == ELF::EM_ARM)
-        FuncEntryPC = FuncPC & ~1;
+        FuncEntryPC &= ~1;
       PrintNonRelocatableFuncSymbol(FuncEntryPC);
     };
     if (!CGI)
       CGI = std::make_unique<ListScope>(W, "CallGraph");
     for (const FunctionCallGraphInfo &CGInfo : FuncCGInfos) {
       DictScope D(W, "Function");
-      PrintFunc(CGInfo.FunctionAddress);
+      PrintFunc(CGInfo.Entry);
       W.printNumber("Version", CGInfo.FormatVersionNumber);
       W.printBoolean("IsIndirectTarget", CGInfo.IsIndirectTarget);
       W.printHex("TypeID", CGInfo.FunctionTypeID);
       {
         ListScope DCs(W, "DirectCallees");
-        for (uint64_t CalleePC : CGInfo.DirectCallees) {
+        // In a linked file, different callees can end up at the same address,
+        // e.g. after identical code folding. List each address once.
+        SmallSet<uint64_t, 4> SeenCallees;
+        for (const CallGraphFunc &Callee : CGInfo.DirectCallees) {
+          if (!IsRelocatable && !SeenCallees.insert(Callee.FieldValue).second)
+            continue;
----------------
ilovepi wrote:

`sort` and then `unique + erase` is also an option. Not sure if one is particularly better than the other. I guess it depends on if you want to minimize allocations or iteration. typically I try to minimize allocation, since it also requires copying, but YMMV.

https://github.com/llvm/llvm-project/pull/230649


More information about the llvm-branch-commits mailing list