[llvm] [BOLT] Keep X86 HLT instruction as a terminator in user mode (PR #154402)
Maksim Panchenko via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 19 11:59:41 PDT 2025
https://github.com/maksfb updated https://github.com/llvm/llvm-project/pull/154402
>From 8e56f4bb9bd83b2cb047e8e1b49fd0ae5f917196 Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Tue, 19 Aug 2025 11:19:29 -0700
Subject: [PATCH] [BOLT] Keep X86 HLT instruction as a terminator in user mode
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 ++++-
bolt/test/X86/cfg_build_hlt.s | 17 -----------------
bolt/test/X86/hlt-terminator.s | 24 ++++++++++++++++++++++++
4 files changed, 40 insertions(+), 22 deletions(-)
delete mode 100644 bolt/test/X86/cfg_build_hlt.s
create mode 100644 bolt/test/X86/hlt-terminator.s
diff --git a/bolt/lib/Core/MCPlusBuilder.cpp b/bolt/lib/Core/MCPlusBuilder.cpp
index d8a2ac6f6837a..7f962e14ea115 100644
--- a/bolt/lib/Core/MCPlusBuilder.cpp
+++ b/bolt/lib/Core/MCPlusBuilder.cpp
@@ -30,6 +30,11 @@ using namespace bolt;
using namespace MCPlus;
namespace opts {
+cl::opt<bool>
+ TerminalHLT("terminal-x86-hlt",
+ cl::desc("Assume that execution stops at x86 HLT instruction"),
+ cl::init(true), cl::Hidden, cl::cat(BoltCategory));
+
cl::opt<bool>
TerminalTrap("terminal-trap",
cl::desc("Assume that execution stops at trap instruction"),
@@ -132,10 +137,13 @@ bool MCPlusBuilder::equals(const MCSpecifierExpr &A, const MCSpecifierExpr &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 fe4a23cc01382..b2056ba2380fb 100644
--- a/bolt/lib/Rewrite/RewriteInstance.cpp
+++ b/bolt/lib/Rewrite/RewriteInstance.cpp
@@ -84,6 +84,7 @@ extern cl::opt<bool> KeepNops;
extern cl::opt<bool> Lite;
extern cl::list<std::string> ReorderData;
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
+extern cl::opt<bool> TerminalHLT;
extern cl::opt<bool> TerminalTrap;
extern cl::opt<bool> TimeBuild;
extern cl::opt<bool> TimeRewrite;
@@ -2177,7 +2178,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/cfg_build_hlt.s
deleted file mode 100644
index a78134df34014..0000000000000
--- a/bolt/test/X86/cfg_build_hlt.s
+++ /dev/null
@@ -1,17 +0,0 @@
-## 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: <main>:
-# CHECK-BIN-NEXT: f4 hlt
-# CHECK-BIN-NEXT: c3 retq
-
-.global main
- .type main, %function
-main:
- hlt
- retq
-.size main, .-main
diff --git a/bolt/test/X86/hlt-terminator.s b/bolt/test/X86/hlt-terminator.s
new file mode 100644
index 0000000000000..3f67182fdf432
--- /dev/null
+++ b/bolt/test/X86/hlt-terminator.s
@@ -0,0 +1,24 @@
+## 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 --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-BIN: <main>:
+# CHECK-BIN-NEXT: f4 hlt
+# CHECK-BIN-NEXT: c3 retq
+
+.global main
+ .type main, %function
+main:
+ hlt
+ retq
+.size main, .-main
More information about the llvm-commits
mailing list