Skip to content

Commit bb28442

Browse files
authored
[CodeGen][X86] Fix lowering of tailcalls when -ms-hotpatch is used (#77245)
Previously, tail jump pseudo-opcodes were skipped by the `encodeInstruction()` call inside `X86AsmPrinter::LowerPATCHABLE_OP`. This caused emission of a 2-byte NOP and dropping of the tail jump. With this PR, we change `PATCHABLE_OP` to not wrap the first `MachineInstr` anymore, but inserting itself before, leaving the instruction unaltered. At lowering time in `X86AsmPrinter`, we now "look ahead" for the next non-pseudo `MachineInstr` and lower+encode it, to inspect its size. If the size is below what `PATCHABLE_OP` expects, it inserts NOPs; otherwise it does nothing. That way, now the first `MachineInstr` is always lowered as usual even if `"patchable-function"="prologue-short-redirect"` is used. Fixes #76879, #76958 and #59039
1 parent 73ff017 commit bb28442

File tree

5 files changed

+62
-72
lines changed

5 files changed

+62
-72
lines changed

llvm/include/llvm/Support/TargetOpcodes.def

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,10 @@ HANDLE_TARGET_OPCODE(LOCAL_ESCAPE)
170170
/// comparisons into existing memory operations.
171171
HANDLE_TARGET_OPCODE(FAULTING_OP)
172172

173-
/// Wraps a machine instruction to add patchability constraints. An
174-
/// instruction wrapped in PATCHABLE_OP has to either have a minimum
173+
/// Precedes a machine instruction to add patchability constraints. An
174+
/// instruction after PATCHABLE_OP has to either have a minimum
175175
/// size or be preceded with a nop of that size. The first operand is
176-
/// an immediate denoting the minimum size of the instruction, the
177-
/// second operand is an immediate denoting the opcode of the original
178-
/// instruction. The rest of the operands are the operands of the
179-
/// original instruction.
180-
/// PATCHABLE_OP can be used as second operand to only insert a nop of
181-
/// required size.
176+
/// an immediate denoting the minimum size of the following instruction.
182177
HANDLE_TARGET_OPCODE(PATCHABLE_OP)
183178

184179
/// This is a marker instruction which gets translated into a nop sled, useful

llvm/lib/CodeGen/PatchableFunction.cpp

Lines changed: 11 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -38,58 +38,28 @@ struct PatchableFunction : public MachineFunctionPass {
3838
}
3939

4040
bool PatchableFunction::runOnMachineFunction(MachineFunction &MF) {
41+
MachineBasicBlock &FirstMBB = *MF.begin();
42+
4143
if (MF.getFunction().hasFnAttribute("patchable-function-entry")) {
42-
MachineBasicBlock &FirstMBB = *MF.begin();
4344
const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo();
4445
// The initial .loc covers PATCHABLE_FUNCTION_ENTER.
4546
BuildMI(FirstMBB, FirstMBB.begin(), DebugLoc(),
4647
TII->get(TargetOpcode::PATCHABLE_FUNCTION_ENTER));
4748
return true;
48-
}
49-
50-
if (!MF.getFunction().hasFnAttribute("patchable-function"))
51-
return false;
52-
49+
} else if (MF.getFunction().hasFnAttribute("patchable-function")) {
5350
#ifndef NDEBUG
54-
Attribute PatchAttr = MF.getFunction().getFnAttribute("patchable-function");
55-
StringRef PatchType = PatchAttr.getValueAsString();
56-
assert(PatchType == "prologue-short-redirect" && "Only possibility today!");
51+
Attribute PatchAttr = MF.getFunction().getFnAttribute("patchable-function");
52+
StringRef PatchType = PatchAttr.getValueAsString();
53+
assert(PatchType == "prologue-short-redirect" && "Only possibility today!");
5754
#endif
58-
59-
auto &FirstMBB = *MF.begin();
60-
auto *TII = MF.getSubtarget().getInstrInfo();
61-
62-
MachineBasicBlock::iterator FirstActualI = llvm::find_if(
63-
FirstMBB, [](const MachineInstr &MI) { return !MI.isMetaInstruction(); });
64-
65-
if (FirstActualI == FirstMBB.end()) {
66-
// As of Microsoft documentation on /hotpatch feature, we must ensure that
67-
// "the first instruction of each function is at least two bytes, and no
68-
// jump within the function goes to the first instruction"
69-
70-
// When the first MBB is empty, insert a patchable no-op. This ensures the
71-
// first instruction is patchable in two special cases:
72-
// - the function is empty (e.g. unreachable)
73-
// - the function jumps back to the first instruction, which is in a
74-
// successor MBB.
75-
BuildMI(&FirstMBB, DebugLoc(), TII->get(TargetOpcode::PATCHABLE_OP))
76-
.addImm(2)
77-
.addImm(TargetOpcode::PATCHABLE_OP);
55+
auto *TII = MF.getSubtarget().getInstrInfo();
56+
BuildMI(FirstMBB, FirstMBB.begin(), DebugLoc(),
57+
TII->get(TargetOpcode::PATCHABLE_OP))
58+
.addImm(2);
7859
MF.ensureAlignment(Align(16));
7960
return true;
8061
}
81-
82-
auto MIB = BuildMI(FirstMBB, FirstActualI, FirstActualI->getDebugLoc(),
83-
TII->get(TargetOpcode::PATCHABLE_OP))
84-
.addImm(2)
85-
.addImm(FirstActualI->getOpcode());
86-
87-
for (auto &MO : FirstActualI->operands())
88-
MIB.add(MO);
89-
90-
FirstActualI->eraseFromParent();
91-
MF.ensureAlignment(Align(16));
92-
return true;
62+
return false;
9363
}
9464

9565
char PatchableFunction::ID = 0;

llvm/lib/Target/X86/X86MCInstLower.cpp

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -948,24 +948,22 @@ void X86AsmPrinter::LowerASAN_CHECK_MEMACCESS(const MachineInstr &MI) {
948948

949949
void X86AsmPrinter::LowerPATCHABLE_OP(const MachineInstr &MI,
950950
X86MCInstLower &MCIL) {
951-
// PATCHABLE_OP minsize, opcode, operands
951+
// PATCHABLE_OP minsize
952952

953953
NoAutoPaddingScope NoPadScope(*OutStreamer);
954954

955-
unsigned MinSize = MI.getOperand(0).getImm();
956-
unsigned Opcode = MI.getOperand(1).getImm();
957-
// Opcode PATCHABLE_OP is a special case: there is no instruction to wrap,
958-
// simply emit a nop of size MinSize.
959-
bool EmptyInst = (Opcode == TargetOpcode::PATCHABLE_OP);
960-
961-
MCInst MCI;
962-
MCI.setOpcode(Opcode);
963-
for (auto &MO : drop_begin(MI.operands(), 2))
964-
if (auto MaybeOperand = MCIL.LowerMachineOperand(&MI, MO))
965-
MCI.addOperand(*MaybeOperand);
955+
auto NextMI = std::find_if(std::next(MI.getIterator()),
956+
MI.getParent()->end().getInstrIterator(),
957+
[](auto &II) { return !II.isMetaInstruction(); });
966958

967959
SmallString<256> Code;
968-
if (!EmptyInst) {
960+
unsigned MinSize = MI.getOperand(0).getImm();
961+
962+
if (NextMI != MI.getParent()->end()) {
963+
// Lower the next MachineInstr to find its byte size.
964+
MCInst MCI;
965+
MCIL.Lower(&*NextMI, MCI);
966+
969967
SmallVector<MCFixup, 4> Fixups;
970968
CodeEmitter->encodeInstruction(MCI, Code, Fixups, getSubtargetInfo());
971969
}
@@ -981,21 +979,12 @@ void X86AsmPrinter::LowerPATCHABLE_OP(const MachineInstr &MI,
981979
OutStreamer->emitInstruction(
982980
MCInstBuilder(X86::MOV32rr_REV).addReg(X86::EDI).addReg(X86::EDI),
983981
*Subtarget);
984-
} else if (MinSize == 2 && Opcode == X86::PUSH64r) {
985-
// This is an optimization that lets us get away without emitting a nop in
986-
// many cases.
987-
//
988-
// NB! In some cases the encoding for PUSH64r (e.g. PUSH64r %r9) takes two
989-
// bytes too, so the check on MinSize is important.
990-
MCI.setOpcode(X86::PUSH64rmr);
991982
} else {
992983
unsigned NopSize = emitNop(*OutStreamer, MinSize, Subtarget);
993984
assert(NopSize == MinSize && "Could not implement MinSize!");
994985
(void)NopSize;
995986
}
996987
}
997-
if (!EmptyInst)
998-
OutStreamer->emitInstruction(MCI, getSubtargetInfo());
999988
}
1000989

1001990
// Lower a stackmap of the form:
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
; RUN: llc -verify-machineinstrs -mtriple=x86_64-windows-msvc < %s | FileCheck %s --check-prefix=CHECK
2+
3+
; CHECK: f1:
4+
; CHECK-NEXT: # %bb.0:
5+
; CHECK-NEXT: jmp f0 # TAILCALL
6+
7+
; CHECK: f2:
8+
; CHECK-NEXT: # %bb.0:
9+
; CHECK-NEXT: jmp malloc # TAILCALL
10+
11+
define ptr @f1(i64 %count) "patchable-function"="prologue-short-redirect" {
12+
entry:
13+
%call = tail call ptr @f0(i64 %count)
14+
ret ptr %call
15+
}
16+
17+
declare ptr @f0(i64)
18+
19+
define noalias ptr @f2(i64 %count) "patchable-function"="prologue-short-redirect" {
20+
entry:
21+
%call = tail call ptr @malloc(i64 %count)
22+
ret ptr %call
23+
}
24+
25+
declare noalias ptr @malloc(i64) #0
26+
27+
attributes #0 = { allockind("alloc,uninitialized") allocsize(0) memory(inaccessiblemem: readwrite) "alloc-family"="malloc" }
28+
29+
!llvm.module.flags = !{!0, !1, !2, !3}
30+
31+
!0 = !{i32 1, !"wchar_size", i32 2}
32+
!1 = !{i32 8, !"PIC Level", i32 2}
33+
!2 = !{i32 7, !"uwtable", i32 2}
34+
!3 = !{i32 1, !"MaxTLSAlign", i32 65536}

llvm/test/CodeGen/X86/patchable-prologue.ll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ define void @f0() "patchable-function"="prologue-short-redirect" {
3232

3333
define void @f1() "patchable-function"="prologue-short-redirect" "frame-pointer"="all" {
3434
; CHECK-LABEL: _f1
35-
; CHECK-NEXT: ff f5 pushq %rbp
35+
; CHECK-NEXT: 66 90 nop
36+
; CHECK-NEXT: 55 pushq %rbp
3637

3738
; CHECK-ALIGN: .p2align 4, 0x90
3839
; CHECK-ALIGN: _f1:
@@ -47,6 +48,7 @@ define void @f1() "patchable-function"="prologue-short-redirect" "frame-pointer"
4748
; 64: f1:
4849
; 64-NEXT: .seh_proc f1
4950
; 64-NEXT: # %bb.0:
51+
; 64-NEXT: xchgw %ax, %ax
5052
; 64-NEXT: pushq %rbp
5153

5254
ret void

0 commit comments

Comments
 (0)