From b42f926dc05ae366172abe902f1b27aeb2a4214c Mon Sep 17 00:00:00 2001 From: liyunfei Date: Mon, 1 Sep 2025 09:55:35 +0800 Subject: [PATCH] [BOLT] Keep X86 HLT instruction as a terminator in user mode --- ...LT-as-split-point-when-build-the-CFG.patch | 94 ++++++++++++++++ ...ruction-as-a-terminator-in-user-mode.patch | 104 ++++++++++++++++++ llvm-toolset-19.spec | 7 +- 3 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 0004-BOLT-Do-not-use-HLT-as-split-point-when-build-the-CFG.patch create mode 100644 0005-BOLT-Keep-X86-HLT-instruction-as-a-terminator-in-user-mode.patch diff --git a/0004-BOLT-Do-not-use-HLT-as-split-point-when-build-the-CFG.patch b/0004-BOLT-Do-not-use-HLT-as-split-point-when-build-the-CFG.patch new file mode 100644 index 0000000..78651fc --- /dev/null +++ b/0004-BOLT-Do-not-use-HLT-as-split-point-when-build-the-CFG.patch @@ -0,0 +1,94 @@ +From 51c62222f6cd8eca47f708592fc9e99f0a89471d Mon Sep 17 00:00:00 2001 +From: Haibo Jiang <33863061+Jianghibo@users.noreply.github.com> +Date: Sat, 16 Aug 2025 05:35:13 +0800 +Subject: [PATCH] [BOLT] Do not use HLT as split point when build the CFG + (#150963) + +For x86, the halt instruction is defined as a terminator instruction. +When building the CFG, the instruction sequence following the hlt +instruction is treated as an independent MBB. Since there is no jump +information, the predecessor of this MBB cannot be identified, and it is +considered an unreachable MBB that will be removed. + +Using this fix, the instruction sequences before and after hlt are +refused to be placed in different blocks. +--- + bolt/include/bolt/Core/MCPlusBuilder.h | 4 ++++ + bolt/lib/Core/MCPlusBuilder.cpp | 6 ++++-- + bolt/lib/Target/X86/X86MCPlusBuilder.cpp | 4 ++++ + bolt/test/X86/cfg_build_hlt.s | 17 +++++++++++++++++ + 4 files changed, 29 insertions(+), 2 deletions(-) + create mode 100644 bolt/test/X86/cfg_build_hlt.s + +diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h +index 5a8a4f6e391c..2e167ffb2909 100644 +--- a/bolt/include/bolt/Core/MCPlusBuilder.h ++++ b/bolt/include/bolt/Core/MCPlusBuilder.h +@@ -579,6 +579,10 @@ public: + return false; + } + ++ /// Return true if the hlt instruction under the x86, otherwise, default to ++ /// false. ++ virtual bool isX86HLT(const MCInst &Inst) const { return false; } ++ + /// Return the width, in bytes, of the memory access performed by \p Inst, if + /// this is a pop instruction. Return zero otherwise. + virtual int getPopSize(const MCInst &Inst) const { +diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp +index 5d25a514862a..cdfb9d7a94e5 100644 +--- a/bolt/lib/Core/MCPlusBuilder.cpp ++++ b/bolt/lib/Core/MCPlusBuilder.cpp +@@ -131,8 +131,10 @@ bool MCPlusBuilder::equals(const MCTargetExpr &A, const MCTargetExpr &B, + } + + bool MCPlusBuilder::isTerminator(const MCInst &Inst) const { +- return Analysis->isTerminator(Inst) || +- (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap()); ++ return (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap()) || ++ Analysis->isTerminator(Inst) ++ ? !isX86HLT(Inst) ++ : false; + } + + void MCPlusBuilder::setTailCall(MCInst &Inst) const { +diff --git a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +index 9b51ab0763e3..cd6172f2b0e9 100644 +--- a/bolt/lib/Target/X86/X86MCPlusBuilder.cpp ++++ b/bolt/lib/Target/X86/X86MCPlusBuilder.cpp +@@ -224,6 +224,10 @@ public: + return Inst.getOpcode() == X86::ENDBR32 || Inst.getOpcode() == X86::ENDBR64; + } + ++ bool isX86HLT(const MCInst &Inst) const override { ++ return Inst.getOpcode() == X86::HLT; ++ } ++ + int getPopSize(const MCInst &Inst) const override { + switch (Inst.getOpcode()) { + case X86::POP16r: +diff --git a/bolt/test/X86/cfg_build_hlt.s b/bolt/test/X86/cfg_build_hlt.s +new file mode 100644 +index 000000000000..a78134df3401 +--- /dev/null ++++ b/bolt/test/X86/cfg_build_hlt.s +@@ -0,0 +1,17 @@ ++## Check CFG for halt instruction ++ ++# RUN: %clang %cflags %s -static -o %t.exe -nostdlib ++# RUN: llvm-bolt %t.exe --print-cfg --print-only=main -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-CFG ++# RUN: llvm-objdump -d %t --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN ++ ++# CHECK-CFG: BB Count : 1 ++# CHECK-BIN:
: ++# CHECK-BIN-NEXT: f4 hlt ++# CHECK-BIN-NEXT: c3 retq ++ ++.global main ++ .type main, %function ++main: ++ hlt ++ retq ++.size main, .-main +-- +Gitee diff --git a/0005-BOLT-Keep-X86-HLT-instruction-as-a-terminator-in-user-mode.patch b/0005-BOLT-Keep-X86-HLT-instruction-as-a-terminator-in-user-mode.patch new file mode 100644 index 0000000..05413c6 --- /dev/null +++ b/0005-BOLT-Keep-X86-HLT-instruction-as-a-terminator-in-user-mode.patch @@ -0,0 +1,104 @@ +From bd29ac09768952f251b56ae143588fa3a5262f0a Mon Sep 17 00:00:00 2001 +From: Maksim Panchenko +Date: Tue, 19 Aug 2025 14:41:13 -0700 +Subject: [PATCH] [BOLT] Keep X86 HLT instruction as a terminator in user mode + (#154402) + +This is a follow-up to #150963. X86 HLT instruction may appear in the +user-level code, in which case we should treat it as a terminator. +Handle it as a non-terminator in the Linux kernel mode. +--- + bolt/lib/Core/MCPlusBuilder.cpp | 16 ++++++++++++---- + bolt/lib/Rewrite/RewriteInstance.cpp | 5 ++++- + .../X86/{cfg_build_hlt.s => hlt-terminator.s} | 15 +++++++++++---- + 3 files changed, 27 insertions(+), 9 deletions(-) + rename bolt/test/X86/{cfg_build_hlt.s => hlt-terminator.s} (31%) + +diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp +index cdfb9d7a94e5..778528d251b6 100644 +--- a/bolt/lib/Core/MCPlusBuilder.cpp ++++ b/bolt/lib/Core/MCPlusBuilder.cpp +@@ -31,6 +31,11 @@ using namespace bolt; + using namespace MCPlus; + + namespace opts { ++cl::opt ++ TerminalHLT("terminal-x86-hlt", ++ cl::desc("Assume that execution stops at x86 HLT instruction"), ++ cl::init(true), cl::Hidden, cl::cat(BoltCategory)); ++ + cl::opt + TerminalTrap("terminal-trap", + cl::desc("Assume that execution stops at trap instruction"), +@@ -131,10 +136,13 @@ bool MCPlusBuilder::equals(const MCTargetExpr &A, const MCTargetExpr &B, + } + + bool MCPlusBuilder::isTerminator(const MCInst &Inst) const { +- return (opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap()) || +- Analysis->isTerminator(Inst) +- ? !isX86HLT(Inst) +- : false; ++ if (isX86HLT(Inst)) ++ return opts::TerminalHLT; ++ ++ if (Info->get(Inst.getOpcode()).isTrap()) ++ return opts::TerminalTrap; ++ ++ return Analysis->isTerminator(Inst); + } + + void MCPlusBuilder::setTailCall(MCInst &Inst) const { +diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp +index 43d5c0cbcdf7..0c24ec652ecd 100644 +--- a/bolt/lib/Rewrite/RewriteInstance.cpp ++++ b/bolt/lib/Rewrite/RewriteInstance.cpp +@@ -83,6 +83,7 @@ extern cl::opt KeepNops; + extern cl::opt Lite; + extern cl::list ReorderData; + extern cl::opt ReorderFunctions; ++extern cl::opt TerminalHLT; + extern cl::opt TerminalTrap; + extern cl::opt TimeBuild; + extern cl::opt TimeRewrite; +@@ -2141,7 +2142,9 @@ void RewriteInstance::adjustCommandLineOptions() { + if (!opts::KeepNops.getNumOccurrences()) + opts::KeepNops = true; + +- // Linux kernel may resume execution after a trap instruction in some cases. ++ // Linux kernel may resume execution after a trap or x86 HLT instruction. ++ if (!opts::TerminalHLT.getNumOccurrences()) ++ opts::TerminalHLT = false; + if (!opts::TerminalTrap.getNumOccurrences()) + opts::TerminalTrap = false; + } +diff --git a/bolt/test/X86/cfg_build_hlt.s b/bolt/test/X86/hlt-terminator.s +similarity index 31% +rename from bolt/test/X86/cfg_build_hlt.s +rename to bolt/test/X86/hlt-terminator.s +index a78134df3401..3f67182fdf43 100644 +--- a/bolt/test/X86/cfg_build_hlt.s ++++ b/bolt/test/X86/hlt-terminator.s +@@ -1,10 +1,17 @@ +-## Check CFG for halt instruction ++## Check that HLT instruction is handled differently depending on the flags. ++## It's a terminator in the user-level code, but the execution can resume in ++## ring 0. + + # RUN: %clang %cflags %s -static -o %t.exe -nostdlib +-# RUN: llvm-bolt %t.exe --print-cfg --print-only=main -o %t 2>&1 | FileCheck %s --check-prefix=CHECK-CFG +-# RUN: llvm-objdump -d %t --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN ++# RUN: llvm-bolt %t.exe --print-cfg --print-only=main --terminal-x86-hlt=0 \ ++# RUN: -o %t.ring0 2>&1 | FileCheck %s --check-prefix=CHECK-RING0 ++# RUN: llvm-bolt %t.exe --print-cfg --print-only=main \ ++# RUN: -o %t.ring3 2>&1 | FileCheck %s --check-prefix=CHECK-RING3 ++# RUN: llvm-objdump -d %t.ring0 --print-imm-hex | FileCheck %s --check-prefix=CHECK-BIN ++ ++# CHECK-RING0: BB Count : 1 ++# CHECK-RING3: BB Count : 2 + +-# CHECK-CFG: BB Count : 1 + # CHECK-BIN:
: + # CHECK-BIN-NEXT: f4 hlt + # CHECK-BIN-NEXT: c3 retq +-- +Gitee diff --git a/llvm-toolset-19.spec b/llvm-toolset-19.spec index a02661e..1ec503a 100644 --- a/llvm-toolset-19.spec +++ b/llvm-toolset-19.spec @@ -164,7 +164,7 @@ #region main package Name: llvm-toolset-%{maj_ver} Version: %{maj_ver}.%{min_ver}.%{patch_ver} -Release: 11 +Release: 12 Summary: The Low Level Virtual Machine License: NCSA @@ -180,6 +180,8 @@ Source2: CMakeLists.txt Patch0001: 0001-PATCH-clang-Don-t-install-static-libraries.patch Patch0002: 0002-Always-build-shared-libs-for-LLD.patch Patch0003: 0003-PATCH-Bolt-CMake-Don-t-export-bolt-libraries-in-LLVM.patch +Patch0004: 0004-BOLT-Do-not-use-HLT-as-split-point-when-build-the-CFG.patch +Patch0005: 0005-BOLT-Keep-X86-HLT-instruction-as-a-terminator-in-user-mode.patch BuildRequires: gcc BuildRequires: gcc-c++ @@ -2930,6 +2932,9 @@ fi #endregion files %changelog +* Mon Sep 01 2025 liyunfei - 19.1.7-12 +- [BOLT] Keep X86 HLT instruction as a terminator in user mode + * Mon Aug 18 2025 liyunfei - 19.1.7-11 - apply master dist .oe0 -- Gitee