[BOLT] Handle generation of compare and jump sequences (#131949)

This patch fixes the following two issues with the createCmpJE for
AArch64:
1. Avoids overwriting the value of the input register RegNo by use XZR
as the destination register.
   subs xzr, RegNo, #Imm
   which is equivalent to a simple
   cmp RegNo, #Imm
2. The immediate operand to the Bcc instruction must be EQ instead of
#Imm.

This patch also adds a new function for createCmpJNE and unit tests for
the both createCmpJE and createCmpJNE for X86 and AArch64.
This commit is contained in:
Rodrigo Rocha 2025-04-04 02:34:24 +01:00 committed by GitHub
parent ae5306f30e
commit b9891715af
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 144 additions and 1 deletions

View File

@ -1751,6 +1751,15 @@ public:
return {};
}
/// Create a sequence of instructions to compare contents of a register
/// \p RegNo to immediate \Imm and jump to \p Target if they are different.
virtual InstructionListType createCmpJNE(MCPhysReg RegNo, int64_t Imm,
const MCSymbol *Target,
MCContext *Ctx) const {
llvm_unreachable("not implemented");
return {};
}
/// Creates inline memcpy instruction. If \p ReturnEnd is true, then return
/// (dest + n) instead of dest.
virtual InstructionListType createInlineMemcpy(bool ReturnEnd) const {

View File

@ -1361,17 +1361,47 @@ public:
int getUncondBranchEncodingSize() const override { return 28; }
// This helper function creates the snippet of code that compares a register
// RegNo with an immedaite Imm, and jumps to Target if they are equal.
// cmp RegNo, #Imm
// b.eq Target
// where cmp is an alias for subs, which results in the code below:
// subs xzr, RegNo, #Imm
// b.eq Target.
InstructionListType createCmpJE(MCPhysReg RegNo, int64_t Imm,
const MCSymbol *Target,
MCContext *Ctx) const override {
InstructionListType Code;
Code.emplace_back(MCInstBuilder(AArch64::SUBSXri)
.addReg(RegNo)
.addReg(AArch64::XZR)
.addReg(RegNo)
.addImm(Imm)
.addImm(0));
Code.emplace_back(MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::EQ)
.addExpr(MCSymbolRefExpr::create(
Target, MCSymbolRefExpr::VK_None, *Ctx)));
return Code;
}
// This helper function creates the snippet of code that compares a register
// RegNo with an immedaite Imm, and jumps to Target if they are not equal.
// cmp RegNo, #Imm
// b.ne Target
// where cmp is an alias for subs, which results in the code below:
// subs xzr, RegNo, #Imm
// b.ne Target.
InstructionListType createCmpJNE(MCPhysReg RegNo, int64_t Imm,
const MCSymbol *Target,
MCContext *Ctx) const override {
InstructionListType Code;
Code.emplace_back(MCInstBuilder(AArch64::SUBSXri)
.addReg(AArch64::XZR)
.addReg(RegNo)
.addImm(Imm)
.addImm(0));
Code.emplace_back(MCInstBuilder(AArch64::Bcc)
.addImm(AArch64CC::NE)
.addExpr(MCSymbolRefExpr::create(
Target, MCSymbolRefExpr::VK_None, *Ctx)));
return Code;

View File

@ -2426,6 +2426,18 @@ public:
return Code;
}
InstructionListType createCmpJNE(MCPhysReg RegNo, int64_t Imm,
const MCSymbol *Target,
MCContext *Ctx) const override {
InstructionListType Code;
Code.emplace_back(MCInstBuilder(X86::CMP64ri8).addReg(RegNo).addImm(Imm));
Code.emplace_back(MCInstBuilder(X86::JCC_1)
.addExpr(MCSymbolRefExpr::create(
Target, MCSymbolRefExpr::VK_None, *Ctx))
.addImm(X86::COND_NE));
return Code;
}
std::optional<Relocation>
createRelocation(const MCFixup &Fixup,
const MCAsmBackend &MAB) const override {

View File

@ -107,6 +107,54 @@ TEST_P(MCPlusBuilderTester, AliasSmallerX0) {
testRegAliases(Triple::aarch64, AArch64::X0, AliasesX0, AliasesX0Count, true);
}
TEST_P(MCPlusBuilderTester, AArch64_CmpJE) {
if (GetParam() != Triple::aarch64)
GTEST_SKIP();
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
InstructionListType Instrs =
BC->MIB->createCmpJE(AArch64::X0, 2, BB->getLabel(), BC->Ctx.get());
BB->addInstructions(Instrs.begin(), Instrs.end());
BB->addSuccessor(BB.get());
auto II = BB->begin();
ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri);
ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR);
ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0);
ASSERT_EQ(II->getOperand(2).getImm(), 2);
ASSERT_EQ(II->getOperand(3).getImm(), 0);
II++;
ASSERT_EQ(II->getOpcode(), AArch64::Bcc);
ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::EQ);
const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 1);
ASSERT_EQ(Label, BB->getLabel());
}
TEST_P(MCPlusBuilderTester, AArch64_CmpJNE) {
if (GetParam() != Triple::aarch64)
GTEST_SKIP();
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
InstructionListType Instrs =
BC->MIB->createCmpJNE(AArch64::X0, 2, BB->getLabel(), BC->Ctx.get());
BB->addInstructions(Instrs.begin(), Instrs.end());
BB->addSuccessor(BB.get());
auto II = BB->begin();
ASSERT_EQ(II->getOpcode(), AArch64::SUBSXri);
ASSERT_EQ(II->getOperand(0).getReg(), AArch64::XZR);
ASSERT_EQ(II->getOperand(1).getReg(), AArch64::X0);
ASSERT_EQ(II->getOperand(2).getImm(), 2);
ASSERT_EQ(II->getOperand(3).getImm(), 0);
II++;
ASSERT_EQ(II->getOpcode(), AArch64::Bcc);
ASSERT_EQ(II->getOperand(0).getImm(), AArch64CC::NE);
const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 1);
ASSERT_EQ(Label, BB->getLabel());
}
#endif // AARCH64_AVAILABLE
#ifdef X86_AVAILABLE
@ -143,6 +191,50 @@ TEST_P(MCPlusBuilderTester, ReplaceRegWithImm) {
ASSERT_EQ(II->getOperand(1).getImm(), 1);
}
TEST_P(MCPlusBuilderTester, X86_CmpJE) {
if (GetParam() != Triple::x86_64)
GTEST_SKIP();
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
InstructionListType Instrs =
BC->MIB->createCmpJE(X86::EAX, 2, BB->getLabel(), BC->Ctx.get());
BB->addInstructions(Instrs.begin(), Instrs.end());
BB->addSuccessor(BB.get());
auto II = BB->begin();
ASSERT_EQ(II->getOpcode(), X86::CMP64ri8);
ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX);
ASSERT_EQ(II->getOperand(1).getImm(), 2);
II++;
ASSERT_EQ(II->getOpcode(), X86::JCC_1);
const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 0);
ASSERT_EQ(Label, BB->getLabel());
ASSERT_EQ(II->getOperand(1).getImm(), X86::COND_E);
}
TEST_P(MCPlusBuilderTester, X86_CmpJNE) {
if (GetParam() != Triple::x86_64)
GTEST_SKIP();
BinaryFunction *BF = BC->createInjectedBinaryFunction("BF", true);
std::unique_ptr<BinaryBasicBlock> BB = BF->createBasicBlock();
InstructionListType Instrs =
BC->MIB->createCmpJNE(X86::EAX, 2, BB->getLabel(), BC->Ctx.get());
BB->addInstructions(Instrs.begin(), Instrs.end());
BB->addSuccessor(BB.get());
auto II = BB->begin();
ASSERT_EQ(II->getOpcode(), X86::CMP64ri8);
ASSERT_EQ(II->getOperand(0).getReg(), X86::EAX);
ASSERT_EQ(II->getOperand(1).getImm(), 2);
II++;
ASSERT_EQ(II->getOpcode(), X86::JCC_1);
const MCSymbol *Label = BC->MIB->getTargetSymbol(*II, 0);
ASSERT_EQ(Label, BB->getLabel());
ASSERT_EQ(II->getOperand(1).getImm(), X86::COND_NE);
}
#endif // X86_AVAILABLE
TEST_P(MCPlusBuilderTester, Annotation) {