[BOLT] Don't terminate on trap instruction for Linux kernel (#87021)

Under normal circumstances, we terminate basic blocks on a trap
instruction. However, Linux kernel may resume execution after hitting a
trap (ud2 on x86). Thus, we introduce "--terminal-trap" option that will
specify if the trap instruction should terminate the control flow. The
option is on by default except for the Linux kernel mode when it's off.
This commit is contained in:
Maksim Panchenko 2024-03-29 16:41:15 -07:00 committed by GitHub
parent 7467dc188a
commit 7de82ca369
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 28 additions and 12 deletions

View File

@ -533,9 +533,7 @@ public:
return Analysis->isReturn(Inst);
}
virtual bool isTerminator(const MCInst &Inst) const {
return Analysis->isTerminator(Inst);
}
virtual bool isTerminator(const MCInst &Inst) const;
virtual bool isNoop(const MCInst &Inst) const {
llvm_unreachable("not implemented");

View File

@ -12,12 +12,14 @@
#include "bolt/Core/MCPlusBuilder.h"
#include "bolt/Core/MCPlus.h"
#include "bolt/Utils/CommandLineOpts.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstrAnalysis.h"
#include "llvm/MC/MCInstrDesc.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include <cstdint>
#include <queue>
@ -28,6 +30,13 @@ using namespace llvm;
using namespace bolt;
using namespace MCPlus;
namespace opts {
cl::opt<bool>
TerminalTrap("terminal-trap",
cl::desc("Assume that execution stops at trap instruction"),
cl::init(true), cl::Hidden, cl::cat(BoltCategory));
}
bool MCPlusBuilder::equals(const MCInst &A, const MCInst &B,
CompFuncTy Comp) const {
if (A.getOpcode() != B.getOpcode())
@ -121,6 +130,11 @@ bool MCPlusBuilder::equals(const MCTargetExpr &A, const MCTargetExpr &B,
llvm_unreachable("target-specific expressions are unsupported");
}
bool MCPlusBuilder::isTerminator(const MCInst &Inst) const {
return Analysis->isTerminator(Inst) ||
(opts::TerminalTrap && Info->get(Inst.getOpcode()).isTrap());
}
void MCPlusBuilder::setTailCall(MCInst &Inst) const {
assert(!hasAnnotation(Inst, MCAnnotation::kTailCall));
setAnnotationOpValue(Inst, MCAnnotation::kTailCall, true);

View File

@ -84,6 +84,7 @@ extern cl::opt<JumpTableSupportLevel> JumpTables;
extern cl::opt<bool> KeepNops;
extern cl::list<std::string> ReorderData;
extern cl::opt<bolt::ReorderFunctions::ReorderType> ReorderFunctions;
extern cl::opt<bool> TerminalTrap;
extern cl::opt<bool> TimeBuild;
cl::opt<bool> AllowStripped("allow-stripped",
@ -2033,8 +2034,14 @@ void RewriteInstance::adjustCommandLineOptions() {
if (opts::Lite)
BC->outs() << "BOLT-INFO: enabling lite mode\n";
if (BC->IsLinuxKernel && !opts::KeepNops.getNumOccurrences())
opts::KeepNops = true;
if (BC->IsLinuxKernel) {
if (!opts::KeepNops.getNumOccurrences())
opts::KeepNops = true;
// Linux kernel may resume execution after a trap instruction in some cases.
if (!opts::TerminalTrap.getNumOccurrences())
opts::TerminalTrap = false;
}
}
namespace {

View File

@ -211,13 +211,6 @@ public:
return false;
}
// FIXME: For compatibility with old LLVM only!
bool isTerminator(const MCInst &Inst) const override {
unsigned Opcode = Inst.getOpcode();
return Info->get(Opcode).isTerminator() || X86::isUD1(Opcode) ||
X86::isUD2(Opcode);
}
bool isIndirectCall(const MCInst &Inst) const override {
return isCall(Inst) &&
((getMemoryOperandNo(Inst) != -1) || Inst.getOperand(0).isReg());

View File

@ -40,6 +40,10 @@ _start:
# CHECK-REOPT-SAME: BugEntry: 2
ret
## The return instruction is reachable only via preceding ud2. Test that it is
## treated as a reachable instruction in the Linux kernel mode.
# CHECK-REOPT-NEXT: ret
.size _start, .-_start