[llvm] [BOLT] Fix code section ordering comparator (PR #216725)

Alexander Wilson via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 17 06:14:45 PDT 2026


https://github.com/rdtscp created https://github.com/llvm/llvm-project/pull/216725

RewriteInstance::getCodeSections() used a comparator that did not form a strict weak ordering. Distinct sections with identical special names could compare less than each other, while cold and other sections had a non-transitive equivalence relation.

Assign explicit ranks to BOLT code section kinds. For --hot-functions-at-end, also place injected functions before ordinary cold functions in the output function list so emission order remains synchronized with physical section order.

Add direct getCodeSections() and ordering-property unit tests, plus an end-to-end hugify and split-functions layout regression.

Assisted-by: Codex

>From b5bb18dbf64ae31e3efae50aa0b3570aaf311361 Mon Sep 17 00:00:00 2001
From: Alexander Wilson <rdtscp at meta.com>
Date: Fri, 14 Aug 2026 10:48:36 -0700
Subject: [PATCH] [BOLT] Fix code section ordering comparator

RewriteInstance::getCodeSections() used a comparator that did not form a
strict weak ordering. Distinct sections with identical special names
could compare less than each other, while cold and other sections had a
non-transitive equivalence relation.

Assign explicit ranks to BOLT code section kinds. For
--hot-functions-at-end, also place injected functions before ordinary
cold functions in the output function list so emission order remains
synchronized with physical section order.

Add direct getCodeSections() and ordering-property unit tests, plus an
end-to-end hugify and split-functions layout regression.

Assisted-by: Codex
---
 bolt/include/bolt/Rewrite/RewriteInstance.h |  33 +++
 bolt/lib/Passes/BinaryPasses.cpp            |  15 +-
 bolt/lib/Rewrite/RewriteInstance.cpp        | 117 +++++----
 bolt/test/X86/code-section-order-injected.s |  43 ++++
 bolt/unittests/Core/CMakeLists.txt          |   1 +
 bolt/unittests/Core/RewriteInstance.cpp     | 252 ++++++++++++++++++++
 6 files changed, 412 insertions(+), 49 deletions(-)
 create mode 100644 bolt/test/X86/code-section-order-injected.s
 create mode 100644 bolt/unittests/Core/RewriteInstance.cpp

diff --git a/bolt/include/bolt/Rewrite/RewriteInstance.h b/bolt/include/bolt/Rewrite/RewriteInstance.h
index a624c056ada14..5250b5c2ee4da 100644
--- a/bolt/include/bolt/Rewrite/RewriteInstance.h
+++ b/bolt/include/bolt/Rewrite/RewriteInstance.h
@@ -36,6 +36,38 @@ class BoltAddressTranslation;
 class CFIReaderWriter;
 class DWARFRewriter;
 class ProfileReaderBase;
+class RewriteInstanceTest;
+
+namespace detail {
+
+/// Defines the strict weak ordering for BOLT-produced code sections.
+class CodeSectionOrder {
+public:
+  CodeSectionOrder(StringRef ColdSectionName, StringRef HotTextMoverSectionName,
+                   StringRef MainSectionName, StringRef WarmSectionName,
+                   bool HotText, bool HotFunctionsAtEnd)
+      : ColdSectionName(ColdSectionName),
+        HotTextMoverSectionName(HotTextMoverSectionName),
+        MainSectionName(MainSectionName), WarmSectionName(WarmSectionName),
+        HotText(HotText), HotFunctionsAtEnd(HotFunctionsAtEnd) {}
+
+  bool operator()(StringRef AName, StringRef BName) const;
+
+private:
+  enum class SectionKind { Mover, Main, Warm, Cold, Other };
+
+  SectionKind getKind(StringRef Name) const;
+  unsigned getRank(SectionKind Kind) const;
+
+  StringRef ColdSectionName;
+  StringRef HotTextMoverSectionName;
+  StringRef MainSectionName;
+  StringRef WarmSectionName;
+  bool HotText;
+  bool HotFunctionsAtEnd;
+};
+
+} // namespace detail
 
 /// This class encapsulates all data necessary to carry on binary reading,
 /// disassembly, CFG building, BB reordering (among other binary-level
@@ -618,6 +650,7 @@ class RewriteInstance {
   const Regex FunctionFragmentTemplate{"(.*)\\.(cold|warm)(\\.[0-9]+)?"};
 
   friend class RewriteInstanceDiff;
+  friend class RewriteInstanceTest;
 };
 
 #undef ELF_FUNCTION
diff --git a/bolt/lib/Passes/BinaryPasses.cpp b/bolt/lib/Passes/BinaryPasses.cpp
index d24295e5c2c32..d2c042571e8b3 100644
--- a/bolt/lib/Passes/BinaryPasses.cpp
+++ b/bolt/lib/Passes/BinaryPasses.cpp
@@ -603,17 +603,22 @@ Error PopulateOutputFunctions::runOnFunctions(BinaryContext &BC) {
   llvm::copy(BC.getInjectedBinaryFunctions(),
              std::back_inserter(OutputFunctions));
 
-  // Place hot text movers in front.
-  if (opts::HotText) {
+  if (opts::HotFunctionsAtEnd) {
+    // Injected functions have no profile and precede other cold functions in
+    // the reverse layout.
     std::stable_partition(
         OutputFunctions.begin(), OutputFunctions.end(),
-        [](const BinaryFunction *A) { return opts::isHotTextMover(*A); });
+        [](const BinaryFunction *A) { return A->isInjected(); });
+    std::stable_partition(
+        OutputFunctions.begin(), OutputFunctions.end(),
+        [](const BinaryFunction *A) { return !A->hasValidIndex(); });
   }
 
-  if (opts::HotFunctionsAtEnd) {
+  // Place hot text movers in front.
+  if (opts::HotText) {
     std::stable_partition(
         OutputFunctions.begin(), OutputFunctions.end(),
-        [](const BinaryFunction *A) { return !A->hasValidIndex(); });
+        [](const BinaryFunction *A) { return opts::isHotTextMover(*A); });
   }
 
   return Error::success();
diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp
index a5de2b5733355..0918e0ca1720f 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -4361,58 +4361,87 @@ void RewriteInstance::mapFileSections(BOLTLinker::SectionMapper MapSection) {
   }
 }
 
+llvm::bolt::detail::CodeSectionOrder::SectionKind
+llvm::bolt::detail::CodeSectionOrder::getKind(StringRef Name) const {
+  if (HotText && Name == HotTextMoverSectionName)
+    return SectionKind::Mover;
+  if (Name == MainSectionName)
+    return SectionKind::Main;
+  if (Name == WarmSectionName)
+    return SectionKind::Warm;
+  if (Name.starts_with(ColdSectionName))
+    return SectionKind::Cold;
+  return SectionKind::Other;
+}
+
+unsigned llvm::bolt::detail::CodeSectionOrder::getRank(SectionKind Kind) const {
+  if (Kind == SectionKind::Mover)
+    return 0;
+  if (HotFunctionsAtEnd) {
+    switch (Kind) {
+    case SectionKind::Other:
+      return 1;
+    case SectionKind::Cold:
+      return 2;
+    case SectionKind::Warm:
+      return 3;
+    case SectionKind::Main:
+      return 4;
+    case SectionKind::Mover:
+      llvm_unreachable("handled above");
+    }
+  }
+  switch (Kind) {
+  case SectionKind::Main:
+    return 1;
+  case SectionKind::Warm:
+    return 2;
+  case SectionKind::Cold:
+    return 3;
+  case SectionKind::Other:
+    return 4;
+  case SectionKind::Mover:
+    llvm_unreachable("handled above");
+  }
+  llvm_unreachable("unknown section kind");
+}
+
+bool llvm::bolt::detail::CodeSectionOrder::operator()(StringRef AName,
+                                                      StringRef BName) const {
+  const SectionKind AKind = getKind(AName);
+  const SectionKind BKind = getKind(BName);
+  const unsigned ARank = getRank(AKind);
+  const unsigned BRank = getRank(BKind);
+  if (ARank != BRank)
+    return ARank < BRank;
+
+  if (AKind == SectionKind::Cold) {
+    if (AName.size() != BName.size())
+      return HotFunctionsAtEnd ? AName.size() > BName.size()
+                               : AName.size() < BName.size();
+    if (AName != BName)
+      return HotFunctionsAtEnd ? AName > BName : AName < BName;
+  }
+
+  return false;
+}
+
 std::vector<BinarySection *> RewriteInstance::getCodeSections() {
   std::vector<BinarySection *> CodeSections;
   for (BinarySection &Section : BC->textSections())
     if (Section.hasValidSectionID())
       CodeSections.emplace_back(&Section);
 
-  auto compareSections = [&](const BinarySection *A, const BinarySection *B) {
-    if (A == B)
-      return false;
-
-    // If both A and B have names starting with ".text.cold", then
-    // - if opts::HotFunctionsAtEnd is true, we want order
-    //   ".text.cold.T", ".text.cold.T-1", ... ".text.cold.1", ".text.cold"
-    // - if opts::HotFunctionsAtEnd is false, we want order
-    //   ".text.cold", ".text.cold.1", ... ".text.cold.T-1", ".text.cold.T"
-    if (A->getName().starts_with(BC->getColdCodeSectionName()) &&
-        B->getName().starts_with(BC->getColdCodeSectionName())) {
-      if (A->getName().size() != B->getName().size())
-        return (opts::HotFunctionsAtEnd)
-                   ? (A->getName().size() > B->getName().size())
-                   : (A->getName().size() < B->getName().size());
-      return (opts::HotFunctionsAtEnd) ? (A->getName() > B->getName())
-                                       : (A->getName() < B->getName());
-    }
-
-    // Place hot text movers before anything else.
-    if (opts::HotText) {
-      if (A->getName() == BC->getHotTextMoverSectionName())
-        return true;
-      if (B->getName() == BC->getHotTextMoverSectionName())
-        return false;
-    }
-
-    // Depending on opts::HotFunctionsAtEnd, place main and warm sections in
-    // order.
-    if (opts::HotFunctionsAtEnd) {
-      if (B->getName() == BC->getMainCodeSectionName())
-        return true;
-      if (A->getName() == BC->getMainCodeSectionName())
-        return false;
-      return (B->getName() == BC->getWarmCodeSectionName());
-    } else {
-      if (A->getName() == BC->getMainCodeSectionName())
-        return true;
-      if (B->getName() == BC->getMainCodeSectionName())
-        return false;
-      return (A->getName() == BC->getWarmCodeSectionName());
-    }
-  };
+  const llvm::bolt::detail::CodeSectionOrder CompareSections(
+      BC->getColdCodeSectionName(), BC->getHotTextMoverSectionName(),
+      BC->getMainCodeSectionName(), BC->getWarmCodeSectionName(), opts::HotText,
+      opts::HotFunctionsAtEnd);
 
   // Determine the order of sections.
-  llvm::stable_sort(CodeSections, compareSections);
+  llvm::stable_sort(CodeSections,
+                    [&](const BinarySection *A, const BinarySection *B) {
+                      return CompareSections(A->getName(), B->getName());
+                    });
 
 #ifndef NDEBUG
   // Verify that the order of sections and functions is consistent.
diff --git a/bolt/test/X86/code-section-order-injected.s b/bolt/test/X86/code-section-order-injected.s
new file mode 100644
index 0000000000000..ab8c8e7051cec
--- /dev/null
+++ b/bolt/test/X86/code-section-order-injected.s
@@ -0,0 +1,43 @@
+# Check the physical order of BOLT-created injected and cold sections when hot
+# functions are placed at the end.
+
+# REQUIRES: system-linux, bolt-runtime
+
+# RUN: llvm-mc -filetype=obj -triple x86_64 %s -o %t.o
+# RUN: ld.lld --emit-relocs -e _start -o %t %t.o
+# RUN: llvm-bolt %t -o %t.bolt --lite=0 --hugify --split-functions \
+# RUN:   --split-strategy=all --split-all-cold --hot-functions-at-end
+# RUN: llvm-nm --defined-only --numeric-sort %t.bolt \
+# RUN:   | FileCheck --check-prefix=ORDER %s
+
+# ORDER: __bolt_hugify_start_program
+# ORDER: _start.cold.2
+# ORDER: _start.cold.1
+# ORDER: _start.cold.0
+# ORDER: _start
+
+  .text
+  .globl _start
+  .type _start, at function
+_start:
+  callq target
+  testl %edi, %edi
+  je .Lone
+  # Keep each outlined fragment large enough to pass x86 split profitability.
+  .rept 32
+  addl $1, %edi
+  .endr
+  jmp .Ldone
+.Lone:
+  .rept 32
+  subl $1, %edi
+  .endr
+.Ldone:
+  retq
+  .size _start, .-_start
+
+  .globl target
+  .type target, at function
+target:
+  retq
+  .size target, .-target
diff --git a/bolt/unittests/Core/CMakeLists.txt b/bolt/unittests/Core/CMakeLists.txt
index 297dec7449202..b622071efaf4a 100644
--- a/bolt/unittests/Core/CMakeLists.txt
+++ b/bolt/unittests/Core/CMakeLists.txt
@@ -11,6 +11,7 @@ add_bolt_unittest(CoreTests
   MCPlusBuilder.cpp
   MemoryMaps.cpp
   DynoStats.cpp
+  RewriteInstance.cpp
 
   # FIXME CoreTests uses `llvm::detail::TakeError(llvm::Error)`, but linking
   #       to LLVMTestingSupport introduces a transitive dependency on the
diff --git a/bolt/unittests/Core/RewriteInstance.cpp b/bolt/unittests/Core/RewriteInstance.cpp
new file mode 100644
index 0000000000000..287e917dbd8b8
--- /dev/null
+++ b/bolt/unittests/Core/RewriteInstance.cpp
@@ -0,0 +1,252 @@
+//===- bolt/unittest/Core/RewriteInstance.cpp -----------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "bolt/Rewrite/RewriteInstance.h"
+#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetSelect.h"
+#include "gtest/gtest.h"
+#include <cstring>
+#include <memory>
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::ELF;
+using namespace llvm::object;
+
+namespace opts {
+extern cl::opt<bool> HotFunctionsAtEnd;
+extern cl::opt<bool> HotText;
+} // namespace opts
+
+namespace llvm::bolt {
+
+class RewriteInstanceTest : public testing::Test {
+protected:
+  void SetUp() override {
+#define BOLT_TARGET(target)                                                    \
+  LLVMInitialize##target##TargetInfo();                                        \
+  LLVMInitialize##target##TargetMC();                                          \
+  LLVMInitialize##target##AsmParser();                                         \
+  LLVMInitialize##target##Disassembler();                                      \
+  LLVMInitialize##target##Target();                                            \
+  LLVMInitialize##target##AsmPrinter();
+
+#include "bolt/Core/TargetConfig.def"
+
+    memcpy(ELFBuffer, "\177ELF", 4);
+    auto *Header = reinterpret_cast<ELF64LE::Ehdr *>(ELFBuffer);
+    Header->e_ident[EI_CLASS] = ELFCLASS64;
+    Header->e_ident[EI_DATA] = ELFDATA2LSB;
+    Header->e_machine = EM_X86_64;
+    Header->e_type = ET_EXEC;
+    MemoryBufferRef Source(StringRef(ELFBuffer, sizeof(ELFBuffer)), "ELF");
+    Object = cantFail(ObjectFile::createObjectFile(Source));
+    Instance =
+        cantFail(RewriteInstance::create(cast<ELFObjectFileBase>(Object.get()),
+                                         0, nullptr, "", NullOut, NullErr));
+    OriginalHotFunctionsAtEnd = opts::HotFunctionsAtEnd;
+    OriginalHotText = opts::HotText;
+  }
+
+  void TearDown() override {
+    opts::HotFunctionsAtEnd = OriginalHotFunctionsAtEnd;
+    opts::HotText = OriginalHotText;
+  }
+
+  BinarySection &addTextSection(StringRef Name, StringRef ID) {
+    BinarySection &Section = Instance->BC->registerOrUpdateSection(
+        Name, ELF::SHT_PROGBITS,
+        BinarySection::getFlags(/*IsReadOnly=*/true, /*IsText=*/true,
+                                /*IsAllocatable=*/true));
+    Section.setSectionID(ID);
+    return Section;
+  }
+
+  BinarySection &addTextSectionWithoutID(StringRef Name) {
+    return Instance->BC->registerOrUpdateSection(
+        Name, ELF::SHT_PROGBITS,
+        BinarySection::getFlags(/*IsReadOnly=*/true, /*IsText=*/true,
+                                /*IsAllocatable=*/true));
+  }
+
+  BinarySection &duplicateTextSection(StringRef Name,
+                                      const BinarySection &Original,
+                                      StringRef ID) {
+    BinarySection &Section = Instance->BC->registerSection(Name, Original);
+    Section.setSectionID(ID);
+    return Section;
+  }
+
+  void addNonTextSection(StringRef Name, bool IsAllocatable, StringRef ID) {
+    BinarySection &Section = Instance->BC->registerOrUpdateSection(
+        Name, ELF::SHT_PROGBITS,
+        BinarySection::getFlags(/*IsReadOnly=*/true, /*IsText=*/false,
+                                IsAllocatable));
+    Section.setSectionID(ID);
+  }
+
+  std::vector<StringRef> getCodeSectionIDs(bool HotFunctionsAtEnd,
+                                           bool HotText) {
+    opts::HotFunctionsAtEnd = HotFunctionsAtEnd;
+    opts::HotText = HotText;
+    std::vector<StringRef> IDs;
+    for (const BinarySection *Section : Instance->getCodeSections())
+      IDs.push_back(Section->getSectionID());
+    return IDs;
+  }
+
+  char ELFBuffer[sizeof(ELF64LE::Ehdr)] = {};
+  raw_null_ostream NullOut;
+  raw_null_ostream NullErr;
+  std::unique_ptr<ObjectFile> Object;
+  std::unique_ptr<RewriteInstance> Instance;
+  bool OriginalHotFunctionsAtEnd{false};
+  bool OriginalHotText{false};
+};
+
+TEST_F(RewriteInstanceTest, GetCodeSections_FiltersInvalidSections) {
+  addTextSection(".text.other.valid", "valid");
+  addTextSectionWithoutID(".text.invalid");
+  addNonTextSection(".data", /*IsAllocatable=*/true, "data");
+  addNonTextSection(".debug_info", /*IsAllocatable=*/false, "debug");
+
+  EXPECT_EQ((std::vector<StringRef>{"valid"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/false,
+                              /*HotText=*/false));
+}
+
+TEST_F(RewriteInstanceTest, GetCodeSections_ForwardLayoutOrdersAllKinds) {
+  addTextSection(".text.cold.2", "cold2");
+  addTextSection(".text.cold.10", "cold10");
+  addTextSection(".text.cold", "cold");
+  addTextSection(".text.cold.1", "cold1");
+  addTextSection(".text.other.low", "other-low");
+  addTextSection(".text.other.high", "other-high");
+  addTextSection(".text.warm", "warm");
+  addTextSection(".text", "main");
+  addTextSection(".text.mover", "mover");
+
+  EXPECT_EQ(
+      (std::vector<StringRef>{"mover", "main", "warm", "cold", "cold1", "cold2",
+                              "cold10", "other-low", "other-high"}),
+      getCodeSectionIDs(/*HotFunctionsAtEnd=*/false, /*HotText=*/true));
+}
+
+TEST_F(RewriteInstanceTest, GetCodeSections_HotAtEndLayoutOrdersAllKinds) {
+  addTextSection(".text.other.high", "other-high");
+  addTextSection(".text.other.low", "other-low");
+  addTextSection(".text.cold.2", "cold2");
+  addTextSection(".text.cold.10", "cold10");
+  addTextSection(".text.cold", "cold");
+  addTextSection(".text.cold.1", "cold1");
+  addTextSection(".text.warm", "warm");
+  addTextSection(".text", "main");
+  addTextSection(".text.mover", "mover");
+
+  EXPECT_EQ(
+      (std::vector<StringRef>{"mover", "other-high", "other-low", "cold10",
+                              "cold2", "cold1", "cold", "warm", "main"}),
+      getCodeSectionIDs(/*HotFunctionsAtEnd=*/true, /*HotText=*/true));
+}
+
+TEST_F(RewriteInstanceTest,
+       GetCodeSections_ColdNamesAndEquivalentSectionsHaveExpectedOrder) {
+  addTextSection(".text.cold.2", "cold2");
+  addTextSection(".text.cold.10", "cold10");
+  addTextSection(".text.cold", "cold");
+  BinarySection &Cold1 = addTextSection(".text.cold.1", "cold1a");
+  duplicateTextSection(".text.cold.1", Cold1, "cold1b");
+
+  EXPECT_EQ(
+      (std::vector<StringRef>{"cold", "cold1a", "cold1b", "cold2", "cold10"}),
+      getCodeSectionIDs(/*HotFunctionsAtEnd=*/false, /*HotText=*/false));
+  EXPECT_EQ(
+      (std::vector<StringRef>{"cold10", "cold2", "cold1a", "cold1b", "cold"}),
+      getCodeSectionIDs(/*HotFunctionsAtEnd=*/true, /*HotText=*/false));
+}
+
+TEST_F(RewriteInstanceTest, GetCodeSections_EquivalentOtherSectionsStayStable) {
+  BinarySection &Other = addTextSection(".text.other", "other1");
+  duplicateTextSection(".text.other", Other, "other2");
+
+  EXPECT_EQ((std::vector<StringRef>{"other1", "other2"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/false,
+                              /*HotText=*/false));
+  EXPECT_EQ((std::vector<StringRef>{"other1", "other2"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/true, /*HotText=*/false));
+}
+
+TEST_F(RewriteInstanceTest,
+       GetCodeSections_EquivalentSpecialSectionsStayStable) {
+  BinarySection &Mover = addTextSection(".text.mover", "mover1");
+  duplicateTextSection(".text.mover", Mover, "mover2");
+  BinarySection &Main = addTextSection(".text", "main1");
+  duplicateTextSection(".text", Main, "main2");
+  BinarySection &Warm = addTextSection(".text.warm", "warm1");
+  duplicateTextSection(".text.warm", Warm, "warm2");
+
+  EXPECT_EQ((std::vector<StringRef>{"mover1", "mover2", "main1", "main2",
+                                    "warm1", "warm2"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/false, /*HotText=*/true));
+  EXPECT_EQ((std::vector<StringRef>{"mover1", "mover2", "warm1", "warm2",
+                                    "main1", "main2"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/true, /*HotText=*/true));
+}
+
+TEST_F(RewriteInstanceTest, GetCodeSections_MoverIsOrdinaryWithoutHotText) {
+  addTextSection(".text.mover", "mover");
+  addTextSection(".text.other", "other");
+  addTextSection(".text", "main");
+
+  EXPECT_EQ((std::vector<StringRef>{"main", "mover", "other"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/false,
+                              /*HotText=*/false));
+  EXPECT_EQ((std::vector<StringRef>{"mover", "other", "main"}),
+            getCodeSectionIDs(/*HotFunctionsAtEnd=*/true, /*HotText=*/false));
+}
+
+TEST(CodeSectionOrderTest, RepresentativeNamesFormStrictWeakOrdering) {
+  const std::vector<StringRef> Names = {
+      ".text.mover",  ".text.mover",   ".text",         ".text",
+      ".text.warm",   ".text.warm",    ".text.cold",    ".text.cold.1",
+      ".text.cold.2", ".text.cold.10", ".text.other.a", ".text.other.b",
+  };
+
+  for (const bool HotText : {false, true}) {
+    for (const bool HotFunctionsAtEnd : {false, true}) {
+      const detail::CodeSectionOrder Order(".text.cold", ".text.mover", ".text",
+                                           ".text.warm", HotText,
+                                           HotFunctionsAtEnd);
+      const auto Equivalent = [&](StringRef A, StringRef B) {
+        return !Order(A, B) && !Order(B, A);
+      };
+
+      for (StringRef A : Names) {
+        EXPECT_FALSE(Order(A, A));
+        for (StringRef B : Names) {
+          if (Order(A, B)) {
+            EXPECT_FALSE(Order(B, A));
+          }
+          for (StringRef C : Names) {
+            if (Order(A, B) && Order(B, C)) {
+              EXPECT_TRUE(Order(A, C));
+            }
+            if (Equivalent(A, B) && Equivalent(B, C)) {
+              EXPECT_TRUE(Equivalent(A, C));
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+} // namespace llvm::bolt



More information about the llvm-commits mailing list