[llvm] [BOLT][Linux] Support ORC for alternative instructions (PR #96709)
Maksim Panchenko via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 27 17:41:58 PDT 2024
https://github.com/maksfb updated https://github.com/llvm/llvm-project/pull/96709
>From 501ad8b77802e319365f6cd619dd4d6a51e87e5d Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Tue, 25 Jun 2024 14:50:44 -0700
Subject: [PATCH 1/3] [BOLT][Linux] Support ORC for alternative instructions
Alternative instruction sequences in the Linux kernel can modify the
stack and thus they need their own ORC unwind entries. Since there's
only one ORC table, it has to be "shared" among multiple instruction
sequences. The kernel achieves this by putting a restriction on
instruction boundaries. If ORC state changes at a given IP, only one of
the alternative sequences can have an instruction starting/ending at
this IP. Then, developers can insert NOPs to guarantee the above
requirement is met.
The most common use of ORC with alternatives is "pushf; pop %rax"
sequence used for paravirtualization.
Before we implement a better support for alternatives, we can safely
skip ORC entries associated with them.
Fixes #87052.
---
bolt/include/bolt/Core/BinaryFunction.h | 4 ++++
bolt/lib/Core/BinaryFunction.cpp | 12 ++++++++++
bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 29 +++++++++++++++++++-----
3 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/bolt/include/bolt/Core/BinaryFunction.h b/bolt/include/bolt/Core/BinaryFunction.h
index 3c641581e247a..1048e50cd8058 100644
--- a/bolt/include/bolt/Core/BinaryFunction.h
+++ b/bolt/include/bolt/Core/BinaryFunction.h
@@ -930,6 +930,10 @@ class BinaryFunction {
return const_cast<BinaryFunction *>(this)->getInstructionAtOffset(Offset);
}
+ /// When the function is in disassembled state, return an instruction that
+ /// contains the \p Offset.
+ MCInst *getInstructionContainingOffset(uint64_t Offset);
+
std::optional<MCInst> disassembleInstructionAtOffset(uint64_t Offset) const;
/// Return offset for the first instruction. If there is data at the
diff --git a/bolt/lib/Core/BinaryFunction.cpp b/bolt/lib/Core/BinaryFunction.cpp
index c608ff40c6d9c..3b5a93b39ea17 100644
--- a/bolt/lib/Core/BinaryFunction.cpp
+++ b/bolt/lib/Core/BinaryFunction.cpp
@@ -4472,6 +4472,18 @@ MCInst *BinaryFunction::getInstructionAtOffset(uint64_t Offset) {
}
}
+MCInst *BinaryFunction::getInstructionContainingOffset(uint64_t Offset) {
+ assert(CurrentState == State::Disassembled && "Wrong function state");
+
+ if (Offset > Size)
+ return nullptr;
+
+ auto II = Instructions.upper_bound(Offset);
+ assert(II != Instructions.begin() && "First instruction not at offset 0");
+ --II;
+ return &II->second;
+}
+
void BinaryFunction::printLoopInfo(raw_ostream &OS) const {
if (!opts::shouldPrint(*this))
return;
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
index aa18e79f73b9d..fa5be036c7920 100644
--- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
+++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
@@ -295,9 +295,6 @@ class LinuxKernelRewriter final : public MetadataRewriter {
if (Error E = processSMPLocks())
return E;
- if (Error E = readORCTables())
- return E;
-
if (Error E = readStaticCalls())
return E;
@@ -313,6 +310,9 @@ class LinuxKernelRewriter final : public MetadataRewriter {
if (Error E = readAltInstructions())
return E;
+ if (Error E = readORCTables())
+ return E;
+
if (Error E = readPCIFixupTable())
return E;
@@ -563,11 +563,28 @@ Error LinuxKernelRewriter::readORCTables() {
if (!BF->hasInstructions())
continue;
- MCInst *Inst = BF->getInstructionAtOffset(IP - BF->getAddress());
- if (!Inst)
+ const uint64_t Offset = IP - BF->getAddress();
+ MCInst *Inst = BF->getInstructionAtOffset(Offset);
+ if (!Inst) {
+ // Check if there is an alternative instruction(s) at this IP. Multiple
+ // alternative instructions can take a place of a single original
+ // instruction and each alternative can have a separate ORC entry.
+ // Since ORC table is shared between all alternative sequences, there's
+ // a requirement that only one (out of many) sequences can have an
+ // instruction from the ORC table to avoid ambiguities/conflicts.
+ //
+ // For now, we have limited support for alternatives. I.e. we still print
+ // functions with them, but will not change the code in the output binary.
+ // As such, we can ignore alternative ORC entries. They will be preserved
+ // in the binary, but will not get printed in the instruction stream.
+ Inst = BF->getInstructionContainingOffset(Offset);
+ if (Inst || BC.MIB->hasAnnotation(*Inst, "AltInst"))
+ continue;
+
return createStringError(
errc::executable_format_error,
"no instruction at address 0x%" PRIx64 " in .orc_unwind_ip", IP);
+ }
// Some addresses will have two entries associated with them. The first
// one being a "weak" section terminator. Since we ignore the terminator,
@@ -1440,7 +1457,7 @@ Error LinuxKernelRewriter::tryReadAltInstructions(uint32_t AltInstFeatureSize,
AltBF->setIgnored();
}
- if (!BF || !BC.shouldEmit(*BF))
+ if (!BF || !BF->hasInstructions())
continue;
if (OrgInstAddress + OrgSize > BF->getAddress() + BF->getSize())
>From 404c35b2b535ec175a9982d6be2a72320014091d Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Tue, 25 Jun 2024 16:56:03 -0700
Subject: [PATCH 2/3] Test case for alternatives with ORC.
---
bolt/test/X86/linux-alt-instruction.s | 49 +++++++++++++++++++++++++--
1 file changed, 47 insertions(+), 2 deletions(-)
diff --git a/bolt/test/X86/linux-alt-instruction.s b/bolt/test/X86/linux-alt-instruction.s
index dc1b12a277785..fe3abbfc2b4c9 100644
--- a/bolt/test/X86/linux-alt-instruction.s
+++ b/bolt/test/X86/linux-alt-instruction.s
@@ -38,7 +38,7 @@
# RUN: llvm-bolt %t.exe --print-cfg -o %t.fs4.out | FileCheck %s
# CHECK: BOLT-INFO: Linux kernel binary detected
-# CHECK: BOLT-INFO: parsed 2 alternative instruction entries
+# CHECK: BOLT-INFO: parsed 3 alternative instruction entries
.text
.globl _start
@@ -50,10 +50,12 @@ _start:
# CHECK: rdtsc
# CHECK-SAME: AltInst: 1
# CHECK-SAME: AltInst2: 2
+# CHECK-SAME: AltInst3: 3
nop
# CHECK-NEXT: nop
# CHECK-SAME: AltInst: 1
# CHECK-SAME: AltInst2: 2
+# CHECK-SAME: AltInst3: 3
nop
nop
.L1:
@@ -66,6 +68,9 @@ _start:
rdtsc
.A1:
rdtscp
+.A2:
+ pushf
+ pop %rax
.Ae:
## Alternative instruction info.
@@ -92,11 +97,51 @@ _start:
.word 0x3b # feature flags
.endif
.byte .L1 - .L0 # org size
- .byte .Ae - .A1 # alt size
+ .byte .A2 - .A1 # alt size
.ifdef PADLEN
.byte 0
.endif
+ .long .L0 - . # org instruction
+ .long .A2 - . # alt instruction
+.ifdef FEATURE_SIZE_4
+ .long 0x110 # feature flags
+.else
+ .word 0x110 # feature flags
+.endif
+ .byte .L1 - .L0 # org size
+ .byte .Ae - .A2 # alt size
+.ifdef PADLEN
+ .byte 0
+.endif
+
+## ORC unwind for "pushf; pop %rax" alternative sequence.
+ .section .orc_unwind,"a", at progbits
+ .align 4
+ .section .orc_unwind_ip,"a", at progbits
+ .align 4
+
+ .section .orc_unwind
+ .2byte 8
+ .2byte 0
+ .2byte 0x205
+ .section .orc_unwind_ip
+ .long _start - .
+
+ .section .orc_unwind
+ .2byte 16
+ .2byte 0
+ .2byte 0x205
+ .section .orc_unwind_ip
+ .long .L0 + 1 - .
+
+ .section .orc_unwind
+ .2byte 8
+ .2byte 0
+ .2byte 0x205
+ .section .orc_unwind_ip
+ .long .L0 + 2 - .
+
## Fake Linux Kernel sections.
.section __ksymtab,"a", at progbits
.section __ksymtab_gpl,"a", at progbits
>From 2281e7147239c1463fcfe7320d086a9da4754c35 Mon Sep 17 00:00:00 2001
From: Maksim Panchenko <maks at fb.com>
Date: Thu, 27 Jun 2024 17:41:43 -0700
Subject: [PATCH 3/3] fixup! [BOLT][Linux] Support ORC for alternative
instructions
---
bolt/lib/Rewrite/LinuxKernelRewriter.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
index fa5be036c7920..03b414b71caca 100644
--- a/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
+++ b/bolt/lib/Rewrite/LinuxKernelRewriter.cpp
@@ -310,6 +310,8 @@ class LinuxKernelRewriter final : public MetadataRewriter {
if (Error E = readAltInstructions())
return E;
+ // Some ORC entries could be linked to alternative instruction
+ // sequences. Hence, we read ORC after .altinstructions.
if (Error E = readORCTables())
return E;
More information about the llvm-commits
mailing list