Skip to content

Commit

Permalink
[MC] [Win64EH] Write packed ARM64 epilogues if possible
Browse files Browse the repository at this point in the history
This gives a pretty substantial size reduction; for a 6.5 MB
DLL with 300 KB .xdata, the .xdata shrinks by 66 KB.

Differential Revision: https://reviews.llvm.org/D87369
  • Loading branch information
mstorsjo committed Sep 11, 2020
1 parent 700fbe5 commit 1308bb9
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 63 deletions.
8 changes: 8 additions & 0 deletions llvm/include/llvm/MC/MCWinEH.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ struct Instruction {

Instruction(unsigned Op, MCSymbol *L, unsigned Reg, unsigned Off)
: Label(L), Offset(Off), Register(Reg), Operation(Op) {}

bool operator==(const Instruction &I) const {
// Check whether two instructions refer to the same operation
// applied at a different spot (i.e. pointing at a different label).
return Offset == I.Offset && Register == I.Register &&
Operation == I.Operation;
}
bool operator!=(const Instruction &I) const { return !(*this == I); }
};

struct FrameInfo {
Expand Down
57 changes: 53 additions & 4 deletions llvm/lib/MC/MCWin64EH.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,8 +264,7 @@ static int64_t GetAbsDifference(MCStreamer &Streamer, const MCSymbol *LHS,
return value;
}

static uint32_t
ARM64CountOfUnwindCodes(const std::vector<WinEH::Instruction> &Insns) {
static uint32_t ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) {
uint32_t Count = 0;
for (const auto &I : Insns) {
switch (static_cast<Win64EH::UnwindOpcodes>(I.Operation)) {
Expand Down Expand Up @@ -553,18 +552,23 @@ static void simplifyOpcodes(std::vector<WinEH::Instruction> &Instructions,
// Convert 2-byte opcodes into equivalent 1-byte ones.
if (Inst.Operation == Win64EH::UOP_SaveRegP && Inst.Register == 29) {
Inst.Operation = Win64EH::UOP_SaveFPLR;
Inst.Register = -1;
} else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
Inst.Register == 29) {
Inst.Operation = Win64EH::UOP_SaveFPLRX;
Inst.Register = -1;
} else if (Inst.Operation == Win64EH::UOP_SaveRegPX &&
Inst.Register == 19 && Inst.Offset <= 248) {
Inst.Operation = Win64EH::UOP_SaveR19R20X;
Inst.Register = -1;
} else if (Inst.Operation == Win64EH::UOP_AddFP && Inst.Offset == 0) {
Inst.Operation = Win64EH::UOP_SetFP;
} else if (Inst.Operation == Win64EH::UOP_SaveRegP &&
Inst.Register == PrevRegister + 2 &&
Inst.Offset == PrevOffset + 16) {
Inst.Operation = Win64EH::UOP_SaveNext;
Inst.Register = -1;
Inst.Offset = 0;
// Intentionally not creating UOP_SaveNext for float register pairs,
// as current versions of Windows (up to at least 20.04) is buggy
// regarding SaveNext for float pairs.
Expand Down Expand Up @@ -601,6 +605,47 @@ static void simplifyOpcodes(std::vector<WinEH::Instruction> &Instructions,
}
}

static int checkPackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info,
int PrologCodeBytes) {
// Can only pack if there's one single epilog
if (info->EpilogMap.size() != 1)
return -1;

const std::vector<WinEH::Instruction> &Epilog =
info->EpilogMap.begin()->second;

// Can pack if the epilog is a subset of the prolog but not vice versa
if (Epilog.size() > info->Instructions.size())
return -1;

// Check that the epilog actually is a perfect match for the end (backwrds)
// of the prolog.
for (int I = Epilog.size() - 1; I >= 0; I--) {
if (info->Instructions[I] != Epilog[Epilog.size() - 1 - I])
return -1;
}

// Check that the epilog actually is at the very end of the function,
// otherwise it can't be packed.
uint32_t DistanceFromEnd = (uint32_t)GetAbsDifference(
streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first);
if (DistanceFromEnd / 4 != Epilog.size())
return -1;

int Offset = ARM64CountOfUnwindCodes(
ArrayRef<WinEH::Instruction>(&info->Instructions[Epilog.size()],
info->Instructions.size() - Epilog.size()));

// Check that the offset and prolog size fits in the first word; it's
// unclear whether the epilog count in the extension word can be taken
// as packed epilog offset.
if (Offset > 31 || PrologCodeBytes > 124)
return -1;

info->EpilogMap.clear();
return Offset;
}

// Populate the .xdata section. The format of .xdata on ARM64 is documented at
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling
static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
Expand Down Expand Up @@ -679,6 +724,8 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions);
uint32_t TotalCodeBytes = PrologCodeBytes;

int PackedEpilogOffset = checkPackedEpilog(streamer, info, PrologCodeBytes);

// Process epilogs.
MapVector<MCSymbol *, uint32_t> EpilogInfo;
// Epilogs processed so far.
Expand Down Expand Up @@ -711,15 +758,17 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) {
uint32_t CodeWordsMod = TotalCodeBytes % 4;
if (CodeWordsMod)
CodeWords++;
uint32_t EpilogCount = info->EpilogMap.size();
uint32_t EpilogCount =
PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size();
bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124;
if (!ExtensionWord) {
row1 |= (EpilogCount & 0x1F) << 22;
row1 |= (CodeWords & 0x1F) << 27;
}
// E is always 0 right now, TODO: packed epilog setup
if (info->HandlesExceptions) // X
row1 |= 1 << 20;
if (PackedEpilogOffset >= 0) // E
row1 |= 1 << 21;
row1 |= FuncLength & 0x3FFFF;
streamer.emitInt32(row1);

Expand Down
22 changes: 3 additions & 19 deletions llvm/test/CodeGen/AArch64/wineh3.mir
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
# CHECK-NEXT: FunctionLength: 124
# CHECK-NEXT: Version: 0
# CHECK-NEXT: ExceptionData: No
# CHECK-NEXT: EpiloguePacked: No
# CHECK-NEXT: EpilogueScopes: 1
# CHECK-NEXT: ByteCodeLength: 32
# CHECK-NEXT: EpiloguePacked: Yes
# CHECK-NEXT: EpilogueOffset: 0
# CHECK-NEXT: ByteCodeLength: 16
# CHECK-NEXT: Prologue [
# CHECK-NEXT: 0xc80c ; stp x19, x20, [sp, #96]
# CHECK-NEXT: 0xc88a ; stp x21, x22, [sp, #80]
Expand All @@ -21,22 +21,6 @@
# CHECK-NEXT: 0xda8d ; stp d10, d11, [sp, #-112]!
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: EpilogueScopes [
# CHECK-NEXT: EpilogueScope {
# CHECK-NEXT: StartOffset: 23
# CHECK-NEXT: EpilogueStartIndex: 15
# CHECK-NEXT: Opcodes [
# CHECK-NEXT: 0xc80c ; ldp x19, x20, [sp, #96]
# CHECK-NEXT: 0xc88a ; ldp x21, x22, [sp, #80]
# CHECK-NEXT: 0xc908 ; ldp x23, x24, [sp, #64]
# CHECK-NEXT: 0xc986 ; ldp x25, x26, [sp, #48]
# CHECK-NEXT: 0xca04 ; ldp x27, x28, [sp, #32]
# CHECK-NEXT: 0xd802 ; ldp d8, d9, [sp, #16]
# CHECK-NEXT: 0xda8d ; ldp d10, d11, [sp], #112
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: }
...
---
Expand Down
20 changes: 7 additions & 13 deletions llvm/test/CodeGen/AArch64/wineh6.mir
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,19 @@
# CHECK-NEXT: FunctionLength: 92
# CHECK-NEXT: Version: 0
# CHECK-NEXT: ExceptionData: No
# CHECK-NEXT: EpiloguePacked: No
# CHECK-NEXT: EpilogueScopes: 1
# CHECK-NEXT: ByteCodeLength: 8
# CHECK-NEXT: EpiloguePacked: Yes
# CHECK-NEXT: EpilogueOffset: 1
# CHECK-NEXT: ByteCodeLength: 4
# CHECK-NEXT: Prologue [
# CHECK-NEXT: 0x02 ; sub sp, #32
# CHECK-NEXT: 0xe1 ; mov fp, sp
# CHECK-NEXT: 0x81 ; stp x29, x30, [sp, #-16]!
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: EpilogueScopes [
# CHECK-NEXT: EpilogueScope {
# CHECK-NEXT: StartOffset: 20
# CHECK-NEXT: EpilogueStartIndex: 4
# CHECK-NEXT: Opcodes [
# CHECK-NEXT: 0xe1 ; mov sp, fp
# CHECK-NEXT: 0x81 ; ldp x29, x30, [sp], #16
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: Epilogue [
# CHECK-NEXT: 0xe1 ; mov sp, fp
# CHECK-NEXT: 0x81 ; ldp x29, x30, [sp], #16
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: }
...
Expand Down
19 changes: 3 additions & 16 deletions llvm/test/CodeGen/AArch64/wineh7.mir
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,16 @@
# CHECK-NEXT: FunctionLength: 72
# CHECK-NEXT: Version: 0
# CHECK-NEXT: ExceptionData: No
# CHECK-NEXT: EpiloguePacked: No
# CHECK-NEXT: EpilogueScopes: 1
# CHECK-NEXT: ByteCodeLength: 16
# CHECK-NEXT: EpiloguePacked: Yes
# CHECK-NEXT: EpilogueOffset: 0
# CHECK-NEXT: ByteCodeLength: 8
# CHECK-NEXT: Prologue [
# CHECK-NEXT: 0xe204 ; add fp, sp, #32
# CHECK-NEXT: 0x44 ; stp x29, x30, [sp, #32]
# CHECK-NEXT: 0xc802 ; stp x19, x20, [sp, #16]
# CHECK-NEXT: 0xcc85 ; stp x21, x22, [sp, #-48]!
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: EpilogueScopes [
# CHECK-NEXT: EpilogueScope {
# CHECK-NEXT: StartOffset: 13
# CHECK-NEXT: EpilogueStartIndex: 8
# CHECK-NEXT: Opcodes [
# CHECK-NEXT: 0xe204 ; sub sp, fp, #32
# CHECK-NEXT: 0x44 ; ldp x29, x30, [sp, #32]
# CHECK-NEXT: 0xc802 ; ldp x19, x20, [sp, #16]
# CHECK-NEXT: 0xcc85 ; ldp x21, x22, [sp], #48
# CHECK-NEXT: 0xe4 ; end
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: ]
# CHECK-NEXT: }
# CHECK-NEXT: }

Expand Down
Loading

0 comments on commit 1308bb9

Please sign in to comment.