[llvm-branch-commits] [llvm] [LowerTypeTests] Reorder CFI jump table entries based on hotness (PR #221046)
Vitaly Buka via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Sep 4 16:06:04 PDT 2026
https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/221046
>From 886e68b4aa8377be01626b898e32fca180d39291 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Thu, 3 Sep 2026 13:30:51 -0700
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20to=20main=20this=20commit=20is=20based=20on?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
[skip ci]
---
.../llvm/Transforms/IPO/LowerTypeTests.h | 20 +-
llvm/lib/Transforms/IPO/CMakeLists.txt | 1 +
llvm/lib/Transforms/IPO/LowerTypeTests.cpp | 100 +++++--
.../LowerTypeTests/Inputs/export-icall.yaml | 29 --
.../LowerTypeTests/Inputs/exported-funcs.yaml | 39 ---
.../Inputs/use-typeid1-dead.yaml | 7 -
.../Inputs/use-typeid1-typeid2.yaml | 6 -
.../LowerTypeTests/cfi-icall-alias.ll | 101 +++----
.../cfi-jumptable-hotness-summary.ll | 179 +++++++++++
.../LowerTypeTests/cfi-jumptable-hotness.ll | 278 ++++++++++++++++++
.../Transforms/LowerTypeTests/export-alias.ll | 14 +-
.../LowerTypeTests/export-allones.ll | 17 +-
.../LowerTypeTests/export-bytearray.ll | 17 +-
.../LowerTypeTests/export-cross-dso-cfi.ll | 15 +-
.../Transforms/LowerTypeTests/export-dead.ll | 12 +-
.../Transforms/LowerTypeTests/export-icall.ll | 19 +-
.../LowerTypeTests/export-inline.ll | 22 +-
.../LowerTypeTests/export-rename-local.ll | 10 +-
.../LowerTypeTests/export-single.ll | 11 +-
.../LowerTypeTests/export-symver.ll | 12 +-
.../LowerTypeTests/function-arm-thumb.ll | 9 +-
.../test/Transforms/LowerTypeTests/pr37625.ll | 10 +-
.../Transforms/IPO/LowerTypeTests.cpp | 65 +++-
23 files changed, 798 insertions(+), 195 deletions(-)
delete mode 100644 llvm/test/Transforms/LowerTypeTests/Inputs/export-icall.yaml
delete mode 100644 llvm/test/Transforms/LowerTypeTests/Inputs/exported-funcs.yaml
delete mode 100644 llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-dead.yaml
delete mode 100644 llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-typeid2.yaml
create mode 100644 llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
create mode 100644 llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
diff --git a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
index 914a7db326a4f..c80aee6f15f40 100644
--- a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
+++ b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
@@ -14,6 +14,7 @@
#ifndef LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H
#define LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H
+#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/Compiler.h"
@@ -119,9 +120,9 @@ struct BitSetBuilder {
///
/// The bit set lowering pass assigns an object index to each object that needs
/// to be laid out, and calls addFragment for each bit set passing the object
-/// indices of its referenced globals. It then assembles a layout from the
-/// computed layout in the Fragments field.
-struct GlobalLayoutBuilder {
+/// indices of its referenced globals. It then assembles a layout by calling
+/// build().
+class GlobalLayoutBuilder {
/// The computed layout. Each element of this vector contains a fragment of
/// layout (which may be empty) consisting of object indices.
std::vector<std::vector<uint64_t>> Fragments;
@@ -129,13 +130,22 @@ struct GlobalLayoutBuilder {
/// Mapping from object index to fragment index.
std::vector<uint64_t> FragmentMap;
- GlobalLayoutBuilder(uint64_t NumObjects)
- : Fragments(1), FragmentMap(NumObjects) {}
+ /// Optional comparator for object hotness/ordering.
+ function_ref<bool(uint64_t, uint64_t)> Less;
+
+public:
+ GlobalLayoutBuilder(
+ uint64_t NumObjects,
+ function_ref<bool(uint64_t, uint64_t)> Less = nullptr)
+ : Fragments(1), FragmentMap(NumObjects), Less(Less) {}
/// Add F to the layout while trying to keep its indices contiguous.
/// If a previously seen fragment uses any of F's indices, that
/// fragment will be laid out inside F.
LLVM_ABI void addFragment(const std::set<uint64_t> &F);
+
+ /// Flatten fragments into a single layout and return it.
+ LLVM_ABI const std::vector<uint64_t> &build();
};
/// This class is used to build a byte array containing overlapping bit sets. By
diff --git a/llvm/lib/Transforms/IPO/CMakeLists.txt b/llvm/lib/Transforms/IPO/CMakeLists.txt
index ca0e140264829..fd791f9fe86f8 100644
--- a/llvm/lib/Transforms/IPO/CMakeLists.txt
+++ b/llvm/lib/Transforms/IPO/CMakeLists.txt
@@ -61,6 +61,7 @@ add_llvm_component_library(LLVMipo
LINK_COMPONENTS
AggressiveInstCombine
Analysis
+ AsmParser
BitReader
BitWriter
Core
diff --git a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
index 3ac15da30169b..cc176d7a3961e 100644
--- a/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/lib/Transforms/IPO/LowerTypeTests.cpp
@@ -30,6 +30,7 @@
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/TypeMetadataUtils.h"
#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/AsmParser/Parser.h"
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
@@ -72,6 +73,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TrailingObjects.h"
#include "llvm/Support/YAMLTraits.h"
#include "llvm/Support/raw_ostream.h"
@@ -114,10 +116,11 @@ static cl::opt<PassSummaryAction> ClSummaryAction(
"Export typeid resolutions to summary and globals")),
cl::Hidden);
-static cl::opt<std::string> ClReadSummary(
- "lowertypetests-read-summary",
- cl::desc("Read summary from given YAML file before running pass"),
- cl::Hidden);
+static cl::opt<std::string>
+ ClReadSummary("lowertypetests-read-summary",
+ cl::desc("Read summary from given textual assembly or YAML "
+ "file before running pass"),
+ cl::Hidden);
static cl::opt<std::string> ClWriteSummary(
"lowertypetests-write-summary",
@@ -196,34 +199,69 @@ BitSetInfo BitSetBuilder::build() {
}
void GlobalLayoutBuilder::addFragment(const std::set<uint64_t> &F) {
+ assert(Fragments.front().empty());
+
// Create a new fragment to hold the layout for F.
Fragments.emplace_back();
std::vector<uint64_t> &Fragment = Fragments.back();
uint64_t FragmentIndex = Fragments.size() - 1;
+ std::vector<std::vector<uint64_t>> SubFragments;
for (auto ObjIndex : F) {
uint64_t OldFragmentIndex = FragmentMap[ObjIndex];
if (OldFragmentIndex == 0) {
// We haven't seen this object index before, so just add it to the current
// fragment.
- Fragment.push_back(ObjIndex);
- } else {
+ SubFragments.push_back({ObjIndex});
+ } else if (!Fragments[OldFragmentIndex].empty()) {
// This index belongs to an existing fragment. Copy the elements of the
// old fragment into this one and clear the old fragment. We don't update
// the fragment map just yet, this ensures that any further references to
// indices from the old fragment in this fragment do not insert any more
// indices.
- std::vector<uint64_t> &OldFragment = Fragments[OldFragmentIndex];
- llvm::append_range(Fragment, OldFragment);
- OldFragment.clear();
+ SubFragments.push_back(std::move(Fragments[OldFragmentIndex]));
}
}
+ if (Less) {
+ llvm::stable_sort(SubFragments,
+ [&](const std::vector<uint64_t> &A,
+ const std::vector<uint64_t> &B) {
+ return Less(A.back(), B.back());
+ });
+ }
+
+ for (auto &SF : SubFragments)
+ llvm::append_range(Fragment, std::move(SF));
+
// Update the fragment map to point our object indices to this fragment.
for (uint64_t ObjIndex : Fragment)
FragmentMap[ObjIndex] = FragmentIndex;
}
+const std::vector<uint64_t> &GlobalLayoutBuilder::build() {
+ if (Less) {
+ // If multiple root fragments remain (e.g. disjoint signatures with no
+ // generalized type), order them so the one containing the hottest function
+ // is placed last.
+ llvm::erase_if(Fragments, [](const std::vector<uint64_t> &F) {
+ return F.empty();
+ });
+ llvm::stable_sort(Fragments,
+ [&](const std::vector<uint64_t> &FA,
+ const std::vector<uint64_t> &FB) {
+ return Less(FA.back(), FB.back());
+ });
+ }
+
+ std::vector<uint64_t> Layout;
+ Layout.reserve(FragmentMap.size());
+ for (auto &&F : Fragments)
+ llvm::append_range(Layout, std::move(F));
+ Fragments = {std::move(Layout)};
+ return Fragments.front();
+}
+
void ByteArrayBuilder::allocate(const std::set<uint64_t> &Bits,
uint64_t BitSize, uint64_t &AllocByteOffset,
uint8_t &AllocMask) {
@@ -1940,13 +1978,11 @@ void LowerTypeTestsModule::buildBitSetsFromDisjointSet(
Globals.empty() || isa<GlobalVariable>(Globals[0]->getGlobal());
std::vector<GlobalTypeMember *> OrderedGTMs(Globals.size());
auto OGTMI = OrderedGTMs.begin();
- for (auto &&F : GLB.Fragments) {
- for (auto &&Offset : F) {
- if (IsGlobalSet != isa<GlobalVariable>(Globals[Offset]->getGlobal()))
- report_fatal_error("Type identifier may not contain both global "
- "variables and functions");
- *OGTMI++ = Globals[Offset];
- }
+ for (uint64_t Offset : GLB.build()) {
+ if (IsGlobalSet != isa<GlobalVariable>(Globals[Offset]->getGlobal()))
+ report_fatal_error("Type identifier may not contain both global "
+ "variables and functions");
+ *OGTMI++ = Globals[Offset];
}
// Build the bitsets from this disjoint set.
@@ -1994,7 +2030,7 @@ LowerTypeTestsModule::LowerTypeTestsModule(
}
bool LowerTypeTestsModule::runForTesting(Module &M, ModuleAnalysisManager &AM) {
- ModuleSummaryIndex Summary(/*HaveGVs=*/false);
+ std::unique_ptr<ModuleSummaryIndex> Summary;
// Handle the command-line summary arguments. This code is for testing
// purposes only, so we handle errors directly.
@@ -2003,17 +2039,33 @@ bool LowerTypeTestsModule::runForTesting(Module &M, ModuleAnalysisManager &AM) {
": ");
auto ReadSummaryFile = ExitOnErr(errorOrToExpected(
MemoryBuffer::getFile(ClReadSummary, /*IsText=*/true)));
-
- yaml::Input In(ReadSummaryFile->getBuffer());
- In >> Summary;
- ExitOnErr(errorCodeToError(In.error()));
+ // TODO: Convert the rest of tests (some YAML features are missing from
+ // textual summary assembly) and remove YAML from this file.
+ if (ReadSummaryFile->getBuffer().starts_with("---")) {
+ Summary = std::make_unique<ModuleSummaryIndex>(/*HaveGVs=*/false);
+ yaml::Input In(ReadSummaryFile->getBuffer());
+ In >> *Summary;
+ ExitOnErr(errorCodeToError(In.error()));
+ } else {
+ SMDiagnostic Err;
+ Summary =
+ parseSummaryIndexAssembly(ReadSummaryFile->getMemBufferRef(), Err);
+ if (!Summary) {
+ Err.print(ClReadSummary.c_str(), errs());
+ report_fatal_error("Failed to parse summary index assembly");
+ }
+ }
+ } else {
+ Summary = std::make_unique<ModuleSummaryIndex>(/*HaveGVs=*/false);
}
bool Changed =
LowerTypeTestsModule(
M, AM,
- ClSummaryAction == PassSummaryAction::Export ? &Summary : nullptr,
- ClSummaryAction == PassSummaryAction::Import ? &Summary : nullptr)
+ ClSummaryAction == PassSummaryAction::Export ? Summary.get()
+ : nullptr,
+ ClSummaryAction == PassSummaryAction::Import ? Summary.get()
+ : nullptr)
.lower();
if (!ClWriteSummary.empty()) {
@@ -2024,7 +2076,7 @@ bool LowerTypeTestsModule::runForTesting(Module &M, ModuleAnalysisManager &AM) {
ExitOnErr(errorCodeToError(EC));
yaml::Output Out(OS);
- Out << Summary;
+ Out << *Summary;
}
return Changed;
diff --git a/llvm/test/Transforms/LowerTypeTests/Inputs/export-icall.yaml b/llvm/test/Transforms/LowerTypeTests/Inputs/export-icall.yaml
deleted file mode 100644
index b85cfd2eb0005..0000000000000
--- a/llvm/test/Transforms/LowerTypeTests/Inputs/export-icall.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
----
-GlobalValueMap:
- 42:
- - Live: true
- # guid("f"), guid("f2"), guid("f3"), guid("g"), guid("h"), guid("external"), guid("external_weak")
- Refs: [14740650423002898831, 8471399308421654326, 4197650231481825559, 13146401226427987378, 8124147457056772133, 5224464028922159466, 5227079976482001346]
- TypeTests: [14276520915468743435, 15427464259790519041] # guid("typeid1"), guid("typeid2")
- 14740650423002898831: # guid("f")
- - Linkage: 0 # external
- Live: true
- 8471399308421654326: # guid("f2")
- - Linkage: 0 # external
- Live: true
- 4197650231481825559: # guid("f3")
- - Linkage: 0 # external
- Live: true
- 13146401226427987378: # guid("g")
- - Linkage: 0 # external
- Live: true
- 8124147457056772133: # guid("h")
- - Linkage: 0 # external
- Live: true
- 5224464028922159466: # guid("external")
- - Linkage: 0 # external
- Live: true
- 5227079976482001346: # guid("external_weak")
- - Linkage: 9 # extern_weak
- Live: true
-...
diff --git a/llvm/test/Transforms/LowerTypeTests/Inputs/exported-funcs.yaml b/llvm/test/Transforms/LowerTypeTests/Inputs/exported-funcs.yaml
deleted file mode 100644
index 71ffc7d37619b..0000000000000
--- a/llvm/test/Transforms/LowerTypeTests/Inputs/exported-funcs.yaml
+++ /dev/null
@@ -1,39 +0,0 @@
----
-GlobalValueMap:
- 42:
- - Live: true
- Refs: [16594175687743574550, 2415377257478301385, 4497425064003378793] # guid("external_addrtaken"), guid("external_addrtaken2"), guid("external_addrtaken_dso_local")
- TypeTests: [14276520915468743435, 15427464259790519041] # guid("typeid1"), guid("typeid2")
- 5224464028922159466: # guid("external")
- - Linkage: 0 # external
- Live: true
- 16430208882958242304: # guid("external2")
- - Linkage: 0 # external
- Live: true
- 16594175687743574550: # guid("external_addrtaken")
- - Linkage: 0 # external
- Live: true
- 2415377257478301385: # guid("external_addrtaken2")
- - Linkage: 0 # external
- Live: true
- 15859245615183425489: # guid("internal")
- - Linkage: 7 # internal
- Live: true
- 1062103744896965210: # guid("alias1")
- - Linkage: 4 # weak
- Live: true
- Aliasee: 16594175687743574550 # guid("external_addrtaken")
- 2510616090736846890: # guid("alias2")
- - Linkage: 0 # weak
- Live: true
- Aliasee: 16594175687743574550 # guid("external_addrtaken")
- 4497425064003378793: # guid("external_addrtaken_dso_local")
- - Linkage: 0 # external
- Live: true
- Local: true
- 15765058181946593837: # guid("alias_dso_local")
- - Linkage: 4 # weak
- Live: true
- Local: true
- Aliasee: 4497425064003378793 # guid("external_addrtaken_dso_local")
-...
diff --git a/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-dead.yaml b/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-dead.yaml
deleted file mode 100644
index 7baa02ada86cc..0000000000000
--- a/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-dead.yaml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-GlobalValueMap:
- 42:
- - Live: false
- TypeTests: [14276520915468743435] # guid("typeid1")
-WithGlobalValueDeadStripping: true
-...
diff --git a/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-typeid2.yaml b/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-typeid2.yaml
deleted file mode 100644
index f30257cfc0d4e..0000000000000
--- a/llvm/test/Transforms/LowerTypeTests/Inputs/use-typeid1-typeid2.yaml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-GlobalValueMap:
- 42:
- - Live: true
- TypeTests: [14276520915468743435, 15427464259790519041] # guid("typeid1"), guid("typeid2")
-...
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-icall-alias.ll b/llvm/test/Transforms/LowerTypeTests/cfi-icall-alias.ll
index 50f5d6445e29b..a420a3724ff55 100644
--- a/llvm/test/Transforms/LowerTypeTests/cfi-icall-alias.ll
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-icall-alias.ll
@@ -1,54 +1,47 @@
-;; Check that if the address of a weak function is only taken through an alias,
-;; it is still added to a list of exported functions and @llvm.type.test() is
-;; lowered to an actual check against the generated CFI jumptable.
-
-RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir
-RUN: opt test.ll --thinlto-bc --thinlto-split-lto-unit -o test.bc
-RUN: llvm-modextract test.bc -n 0 -o test0.bc
-RUN: llvm-modextract test.bc -n 1 -o test1.bc
-
-;; Check that a CFI jumptable is generated.
-RUN: opt test1.bc -passes=lowertypetests -lowertypetests-read-summary=in.yaml \
-RUN: -lowertypetests-summary-action=export -lowertypetests-write-summary=exported.yaml \
-RUN: -S -o - | FileCheck %s --check-prefix=REGULAR
-REGULAR: @__typeid__ZTSFvvE_global_addr = hidden alias i8, ptr @.cfi.jumptable
-REGULAR: @f = alias [8 x i8], ptr @.cfi.jumptable
-REGULAR: define private void @.cfi.jumptable()
-
-;; CHECK that @llvm.type.test() is lowered to an actual check.
-RUN: opt test0.bc -passes=lowertypetests -lowertypetests-read-summary=exported.yaml \
-RUN: -lowertypetests-summary-action=import -S -o - | FileCheck %s --check-prefix=THIN
-THIN: define i1 @test() !guid !{{.*}} {
-THIN-NEXT: %1 = icmp eq i64 ptrtoint (ptr @alias to i64), ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64)
-THIN-NEXT: ret i1 %1
-THIN-NEXT: }
-
-;--- test.ll
-target triple = "x86_64-pc-linux-gnu"
-
- at alias = alias void(), ptr @f
-
-define weak void @f() !type !0 {
- ret void
-}
-
-define i1 @test() {
- %1 = call i1 @llvm.type.test(ptr nonnull @alias, metadata !"_ZTSFvvE")
- ret i1 %1
-}
-
-declare i1 @llvm.type.test(ptr, metadata)
-
-!0 = !{i64 0, !"_ZTSFvvE"}
-;--- in.yaml
----
-GlobalValueMap:
- 8346051122425466633: # guid("test")
- - Live: true
- Refs: [5833419078793185394] # guid("alias")
- TypeTests: [9080559750644022485] # guid("_ZTSFvvE")
- 5833419078793185394: # guid("alias")
- - Aliasee: 14740650423002898831 # guid("f")
- 14740650423002898831: # guid("f")
- -
-...
+;; Check that if the address of a weak function is only taken through an alias,
+;; it is still added to a list of exported functions and @llvm.type.test() is
+;; lowered to an actual check against the generated CFI jumptable.
+
+RUN: rm -rf %t.dir && split-file %s %t.dir && cd %t.dir
+RUN: opt test.ll --thinlto-bc --thinlto-split-lto-unit -o test.bc
+RUN: llvm-modextract test.bc -n 0 -o test0.bc
+RUN: llvm-modextract test.bc -n 1 -o test1.bc
+
+;; Check that a CFI jumptable is generated.
+RUN: opt test1.bc -passes=lowertypetests -lowertypetests-read-summary=in.ll \
+RUN: -lowertypetests-summary-action=export -lowertypetests-write-summary=exported.yaml \
+RUN: -S -o - | FileCheck %s --check-prefix=REGULAR
+REGULAR: @__typeid__ZTSFvvE_global_addr = hidden alias i8, ptr @.cfi.jumptable
+REGULAR: @f = alias [8 x i8], ptr @.cfi.jumptable
+REGULAR: define private void @.cfi.jumptable()
+
+;; CHECK that @llvm.type.test() is lowered to an actual check.
+RUN: opt test0.bc -passes=lowertypetests -lowertypetests-read-summary=exported.yaml \
+RUN: -lowertypetests-summary-action=import -S -o - | FileCheck %s --check-prefix=THIN
+THIN: define i1 @test() !guid !{{.*}} {
+THIN-NEXT: %1 = icmp eq i64 ptrtoint (ptr @alias to i64), ptrtoint (ptr @__typeid__ZTSFvvE_global_addr to i64)
+THIN-NEXT: ret i1 %1
+THIN-NEXT: }
+
+;--- test.ll
+target triple = "x86_64-pc-linux-gnu"
+
+ at alias = alias void(), ptr @f
+
+define weak void @f() !type !0 {
+ ret void
+}
+
+define i1 @test() {
+ %1 = call i1 @llvm.type.test(ptr nonnull @alias, metadata !"_ZTSFvvE")
+ ret i1 %1
+}
+
+declare i1 @llvm.type.test(ptr, metadata)
+
+!0 = !{i64 0, !"_ZTSFvvE"}
+;--- in.ll
+^0 = module: (path: "test.bc", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 8346051122425466633, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2), typeIdInfo: (typeTests: (9080559750644022485)))))
+^2 = gv: (guid: 5833419078793185394, summaries: (alias: (module: ^0, flags: (live: 1), aliasee: ^3)))
+^3 = gv: (guid: 14740650423002898831, summaries: (function: (module: ^0, flags: (linkage: weak, live: 1), insts: 1)))
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
new file mode 100644
index 0000000000000..c7fd7fb1d9280
--- /dev/null
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
@@ -0,0 +1,179 @@
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%t/summary.ll %t/main.ll | FileCheck %s
+
+; Tests that functions in a jump table are reordered according to call edge hotness
+; recorded in the ThinLTO summary index:
+; 1. (Highest priority) Within each strict type, hotter functions are placed later,
+; ordered strictly by: Cold (0) < Unknown (1) < None (2) < Hot (3) < Critical (4).
+; 2. (High priority) Across fragments, the fragment with the globally hottest function
+; (@f_critical) is placed at the very end of the jump table.
+; 3. (Best effort) Each strict type's members remain contiguous (lowered to range checks, no holes).
+
+;--- summary.ll
+^0 = module: (path: "cfi-jumptable-hotness-summary.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 100, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1)))
+
+; Functions in typeid1 covering all 5 call edge hotness types:
+; f_cold (GUID: 3658589069114391263) -> Cold (tier 0)
+^2 = gv: (guid: 3658589069114391263, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: cold)))))
+; f_unknown (GUID: 1205929326482009600) -> Unknown (tier 1)
+^3 = gv: (guid: 1205929326482009600, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: unknown)))))
+; f_none (GUID: 1928809046209326017) -> None (tier 2)
+^4 = gv: (guid: 1928809046209326017, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: none)))))
+; f_hot (GUID: 9377218764429055595) -> Hot (tier 3)
+^5 = gv: (guid: 9377218764429055595, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: hot)))))
+; f_critical (GUID: 14457025706112322155) -> Critical (tier 4)
+^6 = gv: (guid: 14457025706112322155, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: critical)))))
+
+; Functions in typeid2:
+; g_cold (GUID: 4485074774011110174) -> Cold (tier 0)
+^7 = gv: (guid: 4485074774011110174, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: cold)))))
+; g_unknown (GUID: 16691380702939550262) -> multiple calls (cold, unknown), max hotness Unknown (tier 1)
+^8 = gv: (guid: 16691380702939550262, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: cold), (callee: ^1, hotness: unknown)))))
+; g_hot (GUID: 18081025037889099144) -> multiple calls, max hotness Hot (tier 3)
+^9 = gv: (guid: 18081025037889099144, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1, calls: ((callee: ^1, hotness: none), (callee: ^1, hotness: hot)))))
+
+;--- main.ll
+target datalayout = "e-p:64:64"
+target triple = "x86_64-unknown-linux-gnu"
+
+ at 0 = private unnamed_addr constant [8 x ptr] [ptr @f_critical, ptr @g_unknown, ptr @f_cold, ptr @g_hot, ptr @f_none, ptr @g_cold, ptr @f_unknown, ptr @f_hot], align 16
+
+; Strict typeid1 and typeid2 functions defined in scrambled order:
+define void @f_critical() !type !0 !type !1 {
+ ret void
+}
+
+define void @g_unknown() !type !2 !type !1 {
+ ret void
+}
+
+define void @f_cold() !type !0 !type !1 {
+ ret void
+}
+
+define void @g_hot() !type !2 !type !1 {
+ ret void
+}
+
+define void @f_none() !type !0 !type !1 {
+ ret void
+}
+
+define void @g_cold() !type !2 !type !1 {
+ ret void
+}
+
+define void @f_unknown() !type !0 !type !1 {
+ ret void
+}
+
+define void @f_hot() !type !0 !type !1 {
+ ret void
+}
+
+declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) nounwind readnone
+
+define i1 @test_typeid1(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid1")
+ ret i1 %x
+}
+
+define i1 @test_typeid2(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid2")
+ ret i1 %x
+}
+
+define i1 @test_generalized(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid.generalized")
+ ret i1 %x
+}
+
+!0 = !{i32 0, !"typeid1"}
+!1 = !{i32 0, !"typeid2"}
+!2 = !{i32 0, !"typeid.generalized"}
+; CHECK-LABEL: define hidden void @f_critical.cfi(
+; CHECK-SAME: ) !type [[META0:![0-9]+]] !type [[META1:![0-9]+]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_unknown.cfi(
+; CHECK-SAME: ) !type [[META2:![0-9]+]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_cold.cfi(
+; CHECK-SAME: ) !type [[META0]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_hot.cfi(
+; CHECK-SAME: ) !type [[META2]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_none.cfi(
+; CHECK-SAME: ) !type [[META0]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_cold.cfi(
+; CHECK-SAME: ) !type [[META2]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_unknown.cfi(
+; CHECK-SAME: ) !type [[META0]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_hot.cfi(
+; CHECK-SAME: ) !type [[META0]] !type [[META1]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define i1 @test_typeid1(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 32) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 4
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK-LABEL: define i1 @test_typeid2(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 56) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 7
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK-LABEL: define i1 @test_generalized(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 56) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 2
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK-LABEL: define private void @.cfi.jumptable(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] prefalign(8) !elf_section_properties [[META3:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_critical.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_cold.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_none.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_unknown.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_hot.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_unknown.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_hot.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_cold.cfi)
+; CHECK-NEXT: unreachable
+;
+;
+; CHECK: [[META0]] = !{i32 0, !"typeid1"}
+; CHECK: [[META1]] = !{i32 0, !"typeid2"}
+; CHECK: [[META2]] = !{i32 0, !"typeid.generalized"}
+; CHECK: [[META3]] = !{i64 1879002126, i64 8}
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
new file mode 100644
index 0000000000000..e820f210b7735
--- /dev/null
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
@@ -0,0 +1,278 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --include-generated-funcs --version 6
+; RUN: opt -S -passes=lowertypetests -mtriple=x86_64-unknown-linux-gnu %s | FileCheck %s
+
+; Tests that functions in a jump table are reordered according to hotness priorities:
+; 1. (Highest priority) Within each strict type / fragment, hotter functions are placed later:
+; cold (no count) < cold (with count) < unannotated (count 0) < count 10 < count 100 < hot (count 0) < hot (with count).
+; - Tier 0: functions with 'cold' attribute (ties broken by entry count).
+; - Tier 1: unannotated / default functions (ties broken by entry count).
+; - Tier 3: functions with 'hot' attribute (ties broken by entry count).
+; - Tier comparison takes precedence over count: cold with count < unannotated count 0,
+; and unannotated count 100 < hot count 0.
+; 2. (High priority) Across fragments, the fragment with the globally hottest function
+; is placed at the very end of the jump table (so the globally hottest function is
+; the last entry in .cfi.jumptable).
+; 3. (Best effort) Each strict type's members remain contiguous (lowered to range checks, no holes).
+
+target datalayout = "e-p:64:64"
+
+; Pass functions in arbitrary / scrambled order to verify that hotness sorting
+; is independent of input declaration order.
+ at 0 = private unnamed_addr constant [14 x ptr] [
+ ptr @f_hot_count1000,
+ ptr @f_cold,
+ ptr @g_hot_count500,
+ ptr @f_count100,
+ ptr @g_cold,
+ ptr @f_cold_count42,
+ ptr @g_normal,
+ ptr @f_normal,
+ ptr @g_count10,
+ ptr @g_cold_count42,
+ ptr @f_count10,
+ ptr @g_count100,
+ ptr @f_hot,
+ ptr @g_hot
+], align 16
+
+define void @f_hot_count1000() hot !prof !5 !type !0 !type !2 {
+ ret void
+}
+
+define void @f_cold() cold !type !0 !type !2 {
+ ret void
+}
+
+define void @g_hot_count500() hot !prof !7 !type !1 !type !2 {
+ ret void
+}
+
+define void @f_count100() !prof !4 !type !0 !type !2 {
+ ret void
+}
+
+define void @g_cold() cold !type !1 !type !2 {
+ ret void
+}
+
+define void @f_cold_count42() cold !prof !6 !type !0 !type !2 {
+ ret void
+}
+
+define void @g_normal() !type !1 !type !2 {
+ ret void
+}
+
+define void @f_normal() !type !0 !type !2 {
+ ret void
+}
+
+define void @g_count10() !prof !3 !type !1 !type !2 {
+ ret void
+}
+
+define void @g_cold_count42() cold !prof !6 !type !1 !type !2 {
+ ret void
+}
+
+define void @f_count10() !prof !3 !type !0 !type !2 {
+ ret void
+}
+
+define void @g_count100() !prof !4 !type !1 !type !2 {
+ ret void
+}
+
+define void @f_hot() hot !type !0 !type !2 {
+ ret void
+}
+
+define void @g_hot() hot !type !1 !type !2 {
+ ret void
+}
+
+declare i1 @llvm.type.test(ptr %ptr, metadata %bitset) nounwind readnone
+
+define i1 @test_typeid1(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid1")
+ ret i1 %x
+}
+
+define i1 @test_typeid2(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid2")
+ ret i1 %x
+}
+
+define i1 @test_generalized(ptr %p) {
+ %x = call i1 @llvm.type.test(ptr %p, metadata !"typeid.generalized")
+ ret i1 %x
+}
+
+!0 = !{i32 0, !"typeid1"}
+!1 = !{i32 0, !"typeid2"}
+!2 = !{i32 0, !"typeid.generalized"}
+!3 = !{!"function_entry_count", i64 10}
+!4 = !{!"function_entry_count", i64 100}
+!5 = !{!"function_entry_count", i64 1000}
+!6 = !{!"function_entry_count", i64 42}
+!7 = !{!"function_entry_count", i64 500}
+;.
+; CHECK: @[[GLOB0:[0-9]+]] = private unnamed_addr constant [14 x ptr] [ptr @f_hot_count1000, ptr @f_cold, ptr @g_hot_count500, ptr @f_count100, ptr @g_cold, ptr @f_cold_count42, ptr @g_normal, ptr @f_normal, ptr @g_count10, ptr @g_cold_count42, ptr @f_count10, ptr @g_count100, ptr @f_hot, ptr @g_hot], align 16
+; CHECK: @[[GLOB1:[0-9]+]] = private constant [0 x i8] zeroinitializer
+; CHECK: @f_hot_count1000 = alias [8 x i8], ptr @.cfi.jumptable
+; CHECK: @f_cold = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 1)
+; CHECK: @f_count100 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 2)
+; CHECK: @f_cold_count42 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 3)
+; CHECK: @f_normal = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 4)
+; CHECK: @f_count10 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 5)
+; CHECK: @f_hot = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 6)
+; CHECK: @g_hot_count500 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 7)
+; CHECK: @g_cold = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 8)
+; CHECK: @g_normal = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 9)
+; CHECK: @g_count10 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 10)
+; CHECK: @g_cold_count42 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 11)
+; CHECK: @g_count100 = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 12)
+; CHECK: @g_hot = alias [8 x i8], getelementptr inbounds ([14 x [8 x i8]], ptr @.cfi.jumptable, i64 0, i64 13)
+;.
+; CHECK: Function Attrs: hot
+; CHECK-LABEL: define hidden void @f_hot_count1000.cfi(
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] !prof [[PROF0:![0-9]+]] !type [[META1:![0-9]+]] !type [[META2:![0-9]+]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: cold
+; CHECK-LABEL: define hidden void @f_cold.cfi(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: hot
+; CHECK-LABEL: define hidden void @g_hot_count500.cfi(
+; CHECK-SAME: ) #[[ATTR0]] !prof [[PROF3:![0-9]+]] !type [[META4:![0-9]+]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_count100.cfi(
+; CHECK-SAME: ) !prof [[PROF5:![0-9]+]] !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: cold
+; CHECK-LABEL: define hidden void @g_cold.cfi(
+; CHECK-SAME: ) #[[ATTR1]] !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: cold
+; CHECK-LABEL: define hidden void @f_cold_count42.cfi(
+; CHECK-SAME: ) #[[ATTR1]] !prof [[PROF6:![0-9]+]] !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_normal.cfi(
+; CHECK-SAME: ) !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_normal.cfi(
+; CHECK-SAME: ) !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_count10.cfi(
+; CHECK-SAME: ) !prof [[PROF7:![0-9]+]] !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: cold
+; CHECK-LABEL: define hidden void @g_cold_count42.cfi(
+; CHECK-SAME: ) #[[ATTR1]] !prof [[PROF6]] !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @f_count10.cfi(
+; CHECK-SAME: ) !prof [[PROF7]] !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define hidden void @g_count100.cfi(
+; CHECK-SAME: ) !prof [[PROF5]] !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: hot
+; CHECK-LABEL: define hidden void @f_hot.cfi(
+; CHECK-SAME: ) #[[ATTR0]] !type [[META1]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK: Function Attrs: hot
+; CHECK-LABEL: define hidden void @g_hot.cfi(
+; CHECK-SAME: ) #[[ATTR0]] !type [[META4]] !type [[META2]] {
+; CHECK-NEXT: ret void
+;
+;
+; CHECK-LABEL: define i1 @test_typeid1(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 48) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 6
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK-LABEL: define i1 @test_typeid2(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 104) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 6
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK-LABEL: define i1 @test_generalized(
+; CHECK-SAME: ptr [[P:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = ptrtoint ptr [[P]] to i64
+; CHECK-NEXT: [[TMP2:%.*]] = sub i64 ptrtoint (ptr getelementptr (i8, ptr @.cfi.jumptable, i64 104) to i64), [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = call i64 @llvm.fshr.i64(i64 [[TMP2]], i64 [[TMP2]], i64 3)
+; CHECK-NEXT: [[TMP4:%.*]] = icmp ule i64 [[TMP3]], 13
+; CHECK-NEXT: ret i1 [[TMP4]]
+;
+;
+; CHECK: Function Attrs: naked nocf_check noinline
+; CHECK-LABEL: define private void @.cfi.jumptable(
+; CHECK-SAME: ) #[[ATTR3:[0-9]+]] prefalign(8) !elf_section_properties [[META8:![0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_hot_count1000.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_cold.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_count100.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_cold_count42.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_normal.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_count10.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @f_hot.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_hot_count500.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_cold.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_normal.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_count10.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_cold_count42.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_count100.cfi)
+; CHECK-NEXT: call void asm sideeffect "jmp ${0:c}@plt\0Aint3\0Aint3\0Aint3\0A", "s"(ptr @g_hot.cfi)
+; CHECK-NEXT: unreachable
+;
+;.
+; CHECK: attributes #[[ATTR0]] = { hot }
+; CHECK: attributes #[[ATTR1]] = { cold }
+; CHECK: attributes #[[ATTR2:[0-9]+]] = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
+; CHECK: attributes #[[ATTR3]] = { naked nocf_check noinline }
+; CHECK: attributes #[[ATTR4:[0-9]+]] = { nocallback nocreateundeforpoison nofree nosync nounwind speculatable willreturn memory(none) }
+;.
+; CHECK: [[PROF0]] = !{!"function_entry_count", i64 1000}
+; CHECK: [[META1]] = !{i32 0, !"typeid1"}
+; CHECK: [[META2]] = !{i32 0, !"typeid.generalized"}
+; CHECK: [[PROF3]] = !{!"function_entry_count", i64 500}
+; CHECK: [[META4]] = !{i32 0, !"typeid2"}
+; CHECK: [[PROF5]] = !{!"function_entry_count", i64 100}
+; CHECK: [[PROF6]] = !{!"function_entry_count", i64 42}
+; CHECK: [[PROF7]] = !{!"function_entry_count", i64 10}
+; CHECK: [[META8]] = !{i64 1879002126, i64 8}
+;.
diff --git a/llvm/test/Transforms/LowerTypeTests/export-alias.ll b/llvm/test/Transforms/LowerTypeTests/export-alias.ll
index 9c3cceb6ed234..d20268b6e7171 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-alias.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-alias.ll
@@ -1,5 +1,8 @@
-; RUN: opt -S %s -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml | FileCheck %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S %t/main.ll -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll | FileCheck %s
;
+;--- main.ll
; CHECK: @external_addrtaken = alias [8 x i8], ptr @[[JT:.*]]
; CHECK: @external_addrtaken_dso_local = dso_local alias [8 x i8], {{.*}}ptr @[[JT]]
; CHECK: @alias1 = alias [8 x i8], ptr @external_addrtaken
@@ -23,3 +26,12 @@ target triple = "x86_64-unknown-linux"
!7 = !{!"external_addrtaken_dso_local", i8 0, i64 4497425064003378793, !1}
!8 = !{!"alias_dso_local", i8 0, i64 15765058181946593837, !1}
!9 = !{!"external_addrtaken_dso_local", !"alias_dso_local"}
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2, ^5), typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = gv: (guid: 16594175687743574550, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^3 = gv: (guid: 1062103744896965210, summaries: (alias: (module: ^0, flags: (linkage: weak, live: 1), aliasee: ^2)))
+^4 = gv: (guid: 2510616090736846890, summaries: (alias: (module: ^0, flags: (live: 1), aliasee: ^2)))
+^5 = gv: (guid: 4497425064003378793, summaries: (function: (module: ^0, flags: (live: 1, dsoLocal: 1), insts: 1)))
+^6 = gv: (guid: 15765058181946593837, summaries: (alias: (module: ^0, flags: (linkage: weak, live: 1, dsoLocal: 1), aliasee: ^5)))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-allones.ll b/llvm/test/Transforms/LowerTypeTests/export-allones.ll
index b5b4f8c5360c1..43f335f903e92 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-allones.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-allones.ll
@@ -1,8 +1,13 @@
-; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,X86 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86 %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,X86 %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86 %s < %t/out.summary
-; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,ARM %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-ARM %s < %t
+; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,ARM %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-ARM %s < %t/out.summary
+
+;--- main.ll
@foo = constant [2048 x i8] zeroinitializer, !type !0, !type !1, !type !2, !type !3, !type !4, !type !5, !type !6, !type !7, !type !8, !type !9, !type !10, !type !11, !type !12, !type !13, !type !14, !type !15, !type !16, !type !17, !type !18, !type !19, !type !20, !type !21, !type !22, !type !23, !type !24, !type !25, !type !26, !type !27, !type !28, !type !29, !type !30, !type !31, !type !32, !type !33, !type !34, !type !35, !type !36, !type !37, !type !38, !type !39, !type !40, !type !41, !type !42, !type !43, !type !44, !type !45, !type !46, !type !47, !type !48, !type !49, !type !50, !type !51, !type !52, !type !53, !type !54, !type !55, !type !56, !type !57, !type !58, !type !59, !type !60, !type !61, !type !62, !type !63, !type !64, !type !65, !type !66, !type !67, !type !68, !type !69, !type !70, !type !71, !type !72, !type !73, !type !74, !type !75, !type !76, !type !77, !type !78, !type !79, !type !80, !type !81, !type !82, !type !83, !type !84, !type !85, !type !86, !type !87, !type !88, !type !89, !type !90, !type !91, !type !92, !type !93, !type !94, !type !95, !type !96, !type !97, !type !98, !type !99, !type !100, !type !101, !type !102, !type !103, !type !104, !type !105, !type !106, !type !107, !type !108, !type !109, !type !110, !type !111, !type !112, !type !113, !type !114, !type !115, !type !116, !type !117, !type !118, !type !119, !type !120, !type !121, !type !122, !type !123, !type !124, !type !125, !type !126, !type !127, !type !128, !type !129, !type !130
@@ -180,3 +185,7 @@
; SUMMARY-ARM-NEXT: BitMask: 0
; SUMMARY-ARM-NEXT: InlineBits: 0
; SUMMARY-NEXT: WPDRes:
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435, 15427464259790519041)))))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-bytearray.ll b/llvm/test/Transforms/LowerTypeTests/export-bytearray.ll
index 89b3f0663be1d..5bcad5ebad608 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-bytearray.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-bytearray.ll
@@ -1,8 +1,13 @@
-; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,X86 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86 %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,X86 %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86 %s < %t/out.summary
-; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,ARM %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-ARM %s < %t
+; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,ARM %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-ARM %s < %t/out.summary
+
+;--- main.ll
@foo = constant [2048 x i8] zeroinitializer, !type !0, !type !1, !type !2, !type !3
@@ -59,3 +64,7 @@
; SUMMARY-ARM-NEXT: BitMask: 1
; SUMMARY-ARM-NEXT: InlineBits: 0
; SUMMARY-NEXT: WPDRes:
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435, 15427464259790519041)))))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll b/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
index dbc1e22fae029..5cbd2dd45ebd9 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-cross-dso-cfi.ll
@@ -1,9 +1,13 @@
; Test that external functions have jumptable entries emitted even if they are
; not address-taken when Cross-DSO CFI is used, but not otherwise.
-; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml %s | FileCheck --check-prefixes=CHECK,CROSSDSO %s
-; RUN: grep -v "llvm.module.flags" %s | opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml | FileCheck --check-prefixes=CHECK,NORMAL %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll %t/main.ll | FileCheck --check-prefixes=CHECK,CROSSDSO %s
+; RUN: grep -v "llvm.module.flags" %t/main.ll | opt -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll | FileCheck --check-prefixes=CHECK,NORMAL %s
+;--- main.ll
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@@ -37,3 +41,10 @@ define internal void @regularlto_internal() !type !1 !type !2 {
!3 = !{!"external2", i8 1, i64 16430208882958242304, !1, !2}
!4 = !{!"internal", i8 0, i64 15859245615183425489, !1, !2}
!5 = !{i32 4, !"Cross-DSO CFI", i32 1}
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = gv: (guid: 5224464028922159466, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^3 = gv: (guid: 16430208882958242304, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^4 = gv: (guid: 15859245615183425489, summaries: (function: (module: ^0, flags: (linkage: internal, live: 1), insts: 1)))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-dead.ll b/llvm/test/Transforms/LowerTypeTests/export-dead.ll
index 4163c23d2a146..f7cb15c6bc49d 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-dead.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-dead.ll
@@ -1,8 +1,11 @@
; The only use of "typeid1" is in a dead function. Export nothing.
-; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-dead.yaml -lowertypetests-write-summary=%t %s | FileCheck %s
-; RUN: FileCheck --check-prefix=SUMMARY %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck %s
+; RUN: FileCheck --check-prefix=SUMMARY %s < %t/out.summary
+;--- main.ll
@foo = constant i32 42, !type !0
!0 = !{i32 0, !"typeid1"}
@@ -12,3 +15,8 @@
; SUMMARY: TypeIdMap:
; SUMMARY-NEXT: WithGlobalValueDeadStripping: true
; SUMMARY-NEXT: ...
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 0), insts: 1, typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = flags: 1
diff --git a/llvm/test/Transforms/LowerTypeTests/export-icall.ll b/llvm/test/Transforms/LowerTypeTests/export-icall.ll
index 6f1516db79225..ca3209223eaf8 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-icall.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-icall.ll
@@ -1,5 +1,9 @@
-; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/export-icall.yaml -lowertypetests-write-summary=%t %s | FileCheck %s
-; RUN: FileCheck --check-prefix=SUMMARY %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck %s
+; RUN: FileCheck --check-prefix=SUMMARY %s < %t/out.summary
+
+;--- main.ll
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@@ -102,3 +106,14 @@ define void @f3(i32 %x) !type !8 !guid !{i64 4197650231481825559} {
; SUMMARY-NEXT: - Name: external_weak
; SUMMARY-NEXT: GUID: 5227079976482001346
; SUMMARY-NEXT: ...
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2, ^3, ^4, ^5, ^6, ^7, ^8), typeIdInfo: (typeTests: (14276520915468743435, 15427464259790519041)))))
+^2 = gv: (guid: 14740650423002898831, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^3 = gv: (guid: 8471399308421654326, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^4 = gv: (guid: 4197650231481825559, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^5 = gv: (guid: 13146401226427987378, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^6 = gv: (guid: 8124147457056772133, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^7 = gv: (guid: 5224464028922159466, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^8 = gv: (guid: 5227079976482001346, summaries: (function: (module: ^0, flags: (linkage: extern_weak, live: 1), insts: 1)))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-inline.ll b/llvm/test/Transforms/LowerTypeTests/export-inline.ll
index 72275b900208d..8875c790f3bd8 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-inline.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-inline.ll
@@ -1,13 +1,19 @@
; REQUIRES: x86-registered-target
-; RUN: opt -mtriple=i686-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,CHECK-X86-32 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86,SUMMARY-X86-32 %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -mtriple=i686-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,CHECK-X86-32 %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86,SUMMARY-X86-32 %s < %t/out.summary
-; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,CHECK-64 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86,SUMMARY-64 %s < %t
+; RUN: opt -mtriple=x86_64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,CHECK-64 %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-X86,SUMMARY-64 %s < %t/out.summary
-; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck --check-prefixes=CHECK,CHECK-64 %s
-; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-64,SUMMARY-ARM %s < %t
+; RUN: opt -mtriple=aarch64-unknown-linux -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck --check-prefixes=CHECK,CHECK-64 %s
+; RUN: FileCheck --check-prefixes=SUMMARY,SUMMARY-64,SUMMARY-ARM %s < %t/out.summary
+
+;--- main.ll
@foo = constant [2048 x i8] zeroinitializer, !type !0, !type !1, !type !2, !type !3
@@ -63,3 +69,7 @@
; SUMMARY-ARM-NEXT: BitMask: 0
; SUMMARY-ARM-NEXT: InlineBits: 8589934593
; SUMMARY-NEXT: WPDRes:
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435, 15427464259790519041)))))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll b/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
index 4be15b8a45c06..393ad13e6c6f8 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-rename-local.ll
@@ -1,5 +1,8 @@
-; RUN: opt -S %s -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml | FileCheck %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S %t/main.ll -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll | FileCheck %s
+;--- main.ll
; CHECK: define internal void @external_addrtaken.1()
; CHECK: declare {{.*}} void @external_addrtaken.cfi()
@@ -13,3 +16,8 @@ define internal void @external_addrtaken() !type !1 {
!0 = !{!"external_addrtaken", i8 0, i64 16594175687743574550, !1}
!1 = !{i64 0, !"typeid1"}
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2), typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = gv: (guid: 16594175687743574550, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-single.ll b/llvm/test/Transforms/LowerTypeTests/export-single.ll
index 7a62e3bb5f671..cfb2fb532e0ce 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-single.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-single.ll
@@ -1,6 +1,9 @@
-; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck %s
-; RUN: FileCheck --check-prefix=SUMMARY %s < %t
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck %s
+; RUN: FileCheck --check-prefix=SUMMARY %s < %t/out.summary
+;--- main.ll
@foo = constant i32 42, !type !0
!0 = !{i32 0, !"typeid1"}
@@ -15,3 +18,7 @@
; SUMMARY-NEXT: TTRes:
; SUMMARY-NEXT: Kind: Single
; SUMMARY-NEXT: SizeM1BitWidth: 0
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435)))))
diff --git a/llvm/test/Transforms/LowerTypeTests/export-symver.ll b/llvm/test/Transforms/LowerTypeTests/export-symver.ll
index 7c3ed3da3537e..7b6080b41fc75 100644
--- a/llvm/test/Transforms/LowerTypeTests/export-symver.ll
+++ b/llvm/test/Transforms/LowerTypeTests/export-symver.ll
@@ -1,5 +1,8 @@
-; RUN: opt -S %s -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml | FileCheck %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S %t/main.ll -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll | FileCheck %s
;
+;--- main.ll
; CHECK: module asm
; CHECK-NEXT: ".symver external_addrtaken, alias1"
; CHECK-NOT: .symver external_addrtaken2
@@ -15,3 +18,10 @@ target triple = "x86_64-unknown-linux"
!2 = !{i64 0, !"typeid1"}
!3 = !{!"external_addrtaken", !"alias1"}
!4 = !{!"not_exported", !"alias2"}
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2, ^3), typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = gv: (guid: 16594175687743574550, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^3 = gv: (guid: 2415377257478301385, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
+^4 = gv: (guid: 1062103744896965210, summaries: (alias: (module: ^0, flags: (linkage: weak, live: 1), aliasee: ^2)))
diff --git a/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll b/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
index 45b7905f0bf01..f948714596350 100644
--- a/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
+++ b/llvm/test/Transforms/LowerTypeTests/function-arm-thumb.ll
@@ -1,7 +1,10 @@
; REQUIRES: arm-registered-target
-; RUN: opt -S -mtriple=arm-unknown-linux-gnu -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/use-typeid1-typeid2.yaml -lowertypetests-write-summary=%t %s | FileCheck %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S -mtriple=arm-unknown-linux-gnu -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary %t/main.ll | FileCheck %s
+;--- main.ll
target datalayout = "e-p:64:64"
define void @f1() "target-features"="+thumb-mode,+v6t2" !type !0 {
@@ -50,3 +53,7 @@ define void @addrtaken() {
; CHECK-DAG: attributes #[[AA]] = { naked noinline "target-features"="-thumb-mode" }
; CHECK-DAG: attributes #[[AT]] = { naked noinline "target-cpu"="cortex-a8" "target-features"="+thumb-mode" }
+
+;--- summary.ll
+^0 = module: (path: "use.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, typeIdInfo: (typeTests: (14276520915468743435, 15427464259790519041)))))
diff --git a/llvm/test/Transforms/LowerTypeTests/pr37625.ll b/llvm/test/Transforms/LowerTypeTests/pr37625.ll
index 3f7fc8beaa964..2be9124e0ec64 100644
--- a/llvm/test/Transforms/LowerTypeTests/pr37625.ll
+++ b/llvm/test/Transforms/LowerTypeTests/pr37625.ll
@@ -1,5 +1,8 @@
-; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%S/Inputs/exported-funcs.yaml -lowertypetests-write-summary=%t %s | FileCheck %s
+; RUN: rm -rf %t && split-file %s %t
+; RUN: opt -S %t/main.ll -passes=lowertypetests -lowertypetests-summary-action=export \
+; RUN: -lowertypetests-read-summary=%t/summary.ll -lowertypetests-write-summary=%t/out.summary | FileCheck %s
+;--- main.ll
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
@@ -12,3 +15,8 @@ declare !type !2 extern_weak void @external_addrtaken(i8)
!2 = !{i64 0, !"typeid1"}
; CHECK-DAG: @external_addrtaken = alias [8 x i8], ptr @.cfi.jumptable
+
+;--- summary.ll
+^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
+^1 = gv: (guid: 42, summaries: (function: (module: ^0, flags: (live: 1), insts: 1, refs: (^2), typeIdInfo: (typeTests: (14276520915468743435)))))
+^2 = gv: (guid: 16594175687743574550, summaries: (function: (module: ^0, flags: (live: 1), insts: 1)))
diff --git a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
index 3d6b7efbccd91..704260f2f31be 100644
--- a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
@@ -95,11 +95,68 @@ TEST(LowerTypeTests, GlobalLayoutBuilder) {
for (auto &&F : T.Fragments)
GLB.addFragment(F);
- std::vector<uint64_t> ComputedLayout;
- for (auto &&F : GLB.Fragments)
- llvm::append_range(ComputedLayout, F);
+ EXPECT_EQ(T.WantLayout, GLB.build());
+ }
+}
+
+TEST(LowerTypeTests, GlobalLayoutBuilderHotness) {
+ struct {
+ uint64_t NumObjects;
+ std::vector<uint64_t> Ranks;
+ std::vector<std::set<uint64_t>> Fragments;
+ std::vector<uint64_t> WantLayout;
+ } GLBTests[] = {
+ // 0 objects
+ {0, {}, {}, {}},
+ // 1 object
+ {1, {10}, {{0}}, {0}},
+ // Single fragment sorted ascending by rank
+ {4, {40, 10, 30, 20}, {{0, 1, 2, 3}}, {1, 3, 2, 0}},
+ // Multiple root fragments ordered by max element in build()
+ {4, {40, 10, 30, 20}, {{0, 1}, {2, 3}}, {3, 2, 1, 0}},
+ // Three root fragments ordered by max element:
+ // [2, 3] (max 40) < [0, 1] (max 50) < [4, 5] (max 60)
+ {6,
+ {10, 50, 30, 40, 20, 60},
+ {{0, 1}, {2, 3}, {4, 5}},
+ {2, 3, 0, 1, 4, 5}},
+ // Sub-fragments merged into universal set:
+ // Within {0, 1}: 1 (20) < 0 (40) -> [1, 0], max 40
+ // Within {2, 3}: 3 (10) < 2 (30) -> [3, 2], max 30
+ // In {0, 1, 2, 3}: [3, 2] (max 30) < [1, 0] (max 40) -> [3, 2, 1, 0]
+ {4, {40, 20, 30, 10}, {{0, 1}, {2, 3}, {0, 1, 2, 3}}, {3, 2, 1, 0}},
+ // Unassigned element added to existing fragment: unassigned colder
+ {3, {10, 30, 20}, {{0, 1}, {0, 2}}, {2, 0, 1}},
+ // Unassigned element added to existing fragment: unassigned hotter
+ {3, {10, 30, 40}, {{0, 1}, {0, 2}}, {0, 1, 2}},
+ // Merging three disjoint fragments via overlapping set
+ {6,
+ {10, 20, 40, 50, 30, 35},
+ {{0, 1}, {2, 3}, {4, 5}, {1, 3, 5}},
+ {0, 1, 4, 5, 2, 3}},
+ // Chained overlapping fragments
+ {4, {30, 10, 40, 20}, {{0, 1}, {1, 2}, {2, 3}}, {3, 1, 0, 2}},
+ // Multi-level hierarchy (leaves -> branch -> root)
+ {6,
+ {10, 80, 20, 70, 30, 90},
+ {{0, 1}, {2, 3}, {4, 5}, {0, 1, 2, 3}, {0, 1, 2, 3, 4, 5}},
+ {2, 3, 0, 1, 4, 5}},
+ // Preserves order on ties (stable sort)
+ {3, {10, 10, 10}, {{0, 1, 2}}, {0, 1, 2}},
+ // Already ascending and descending ranks
+ {4, {10, 20, 30, 40}, {{0, 1, 2, 3}}, {0, 1, 2, 3}},
+ {4, {40, 30, 20, 10}, {{0, 1, 2, 3}}, {3, 2, 1, 0}},
+ };
+
+ for (auto &&T : GLBTests) {
+ auto Less = [&](uint64_t A, uint64_t B) {
+ return T.Ranks[A] < T.Ranks[B];
+ };
+ GlobalLayoutBuilder GLB(T.NumObjects, Less);
+ for (auto &&F : T.Fragments)
+ GLB.addFragment(F);
- EXPECT_EQ(T.WantLayout, ComputedLayout);
+ EXPECT_EQ(T.WantLayout, GLB.build());
}
}
>From 3136afe18efafda49ee69544f459dee82b635509 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Thu, 3 Sep 2026 14:48:26 -0700
Subject: [PATCH 2/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
[skip ci]
---
llvm/include/llvm/Transforms/IPO/LowerTypeTests.h | 10 +++++++---
.../LowerTypeTests/cfi-jumptable-hotness-summary.ll | 2 +-
.../Transforms/LowerTypeTests/cfi-jumptable-hotness.ll | 2 +-
llvm/unittests/Transforms/IPO/LowerTypeTests.cpp | 6 ++++--
4 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
index b35e5d6699227..b3c275001a9a7 100644
--- a/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
+++ b/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
@@ -14,6 +14,7 @@
#ifndef LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H
#define LLVM_TRANSFORMS_IPO_LOWERTYPETESTS_H
+#include "llvm/ADT/FunctionExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/PassManager.h"
@@ -131,12 +132,15 @@ class GlobalLayoutBuilder {
std::vector<uint64_t> FragmentMap;
/// Optional comparator for object hotness/ordering.
- function_ref<bool(uint64_t, uint64_t)> Less;
+ unique_function<bool(uint64_t, uint64_t)> Less;
public:
+ /// Construct a layout builder for \p NumObjects objects.
+ /// If \p Less is provided, it is used to sort sub-fragments and root
+ /// fragments by maximum element.
GlobalLayoutBuilder(uint64_t NumObjects,
- function_ref<bool(uint64_t, uint64_t)> Less = nullptr)
- : Fragments(1), FragmentMap(NumObjects), Less(Less) {}
+ unique_function<bool(uint64_t, uint64_t)> Less = nullptr)
+ : Fragments(1), FragmentMap(NumObjects), Less(std::move(Less)) {}
/// Add F to the layout while trying to keep its indices contiguous.
/// If a previously seen fragment uses any of F's indices, that
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
index c7fd7fb1d9280..fe2e9aad7c4e6 100644
--- a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness-summary.ll
@@ -1,7 +1,7 @@
; RUN: rm -rf %t && split-file %s %t
; RUN: opt -S -passes=lowertypetests -lowertypetests-summary-action=export -lowertypetests-read-summary=%t/summary.ll %t/main.ll | FileCheck %s
-; Tests that functions in a jump table are reordered according to call edge hotness
+; TODO: Tests that functions in a jump table are reordered according to call edge hotness
; recorded in the ThinLTO summary index:
; 1. (Highest priority) Within each strict type, hotter functions are placed later,
; ordered strictly by: Cold (0) < Unknown (1) < None (2) < Hot (3) < Critical (4).
diff --git a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
index e820f210b7735..103926d794075 100644
--- a/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
+++ b/llvm/test/Transforms/LowerTypeTests/cfi-jumptable-hotness.ll
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-attributes --check-globals all --include-generated-funcs --version 6
; RUN: opt -S -passes=lowertypetests -mtriple=x86_64-unknown-linux-gnu %s | FileCheck %s
-; Tests that functions in a jump table are reordered according to hotness priorities:
+; TODO: Tests that functions in a jump table are reordered according to hotness priorities:
; 1. (Highest priority) Within each strict type / fragment, hotter functions are placed later:
; cold (no count) < cold (with count) < unannotated (count 0) < count 10 < count 100 < hot (count 0) < hot (with count).
; - Tier 0: functions with 'cold' attribute (ties broken by entry count).
diff --git a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
index ad1d343fd3cb9..e3c058958a3ac 100644
--- a/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
+++ b/llvm/unittests/Transforms/IPO/LowerTypeTests.cpp
@@ -149,8 +149,10 @@ TEST(LowerTypeTests, GlobalLayoutBuilderHotness) {
};
for (auto &&T : GLBTests) {
- auto Less = [&](uint64_t A, uint64_t B) { return T.Ranks[A] < T.Ranks[B]; };
- GlobalLayoutBuilder GLB(T.NumObjects, Less);
+ // Pass a temporary lambda directly to verify it does not dangle.
+ GlobalLayoutBuilder GLB(T.NumObjects, [&](uint64_t A, uint64_t B) {
+ return T.Ranks[A] < T.Ranks[B];
+ });
for (auto &&F : T.Fragments)
GLB.addFragment(F);
>From e7fea38a1024b2d9a5119fa43f9e0273f0b1fc42 Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 4 Sep 2026 00:36:11 -0700
Subject: [PATCH 3/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20ch?=
=?UTF-8?q?anges=20introduced=20through=20rebase?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
[skip ci]
---
llvm/lib/Analysis/RegionPass.cpp | 2 +-
llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/RegionPass.cpp b/llvm/lib/Analysis/RegionPass.cpp
index ae1d84659de86..f6f79e5c9bb33 100644
--- a/llvm/lib/Analysis/RegionPass.cpp
+++ b/llvm/lib/Analysis/RegionPass.cpp
@@ -206,7 +206,7 @@ class PrintRegionPass : public RegionPass {
};
char PrintRegionPass::ID = 0;
-} //end anonymous namespace
+} // end anonymous namespace
//===----------------------------------------------------------------------===//
// RegionPass
diff --git a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
index 5111322023d04..9353a2e851b76 100644
--- a/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
+++ b/llvm/lib/CodeGen/MachineFunctionPrinterPass.cpp
@@ -54,7 +54,7 @@ struct MachineFunctionPrinterPass : public MachineFunctionPass {
};
char MachineFunctionPrinterPass::ID = 0;
-}
+} // namespace
char &llvm::MachineFunctionPrinterPassID = MachineFunctionPrinterPass::ID;
INITIALIZE_PASS(MachineFunctionPrinterPass, "machineinstr-printer",
More information about the llvm-branch-commits
mailing list