From 3b70a28087a52f18c376a5cdf35fd6c910e064e8 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Fri, 15 May 2020 13:46:35 +0200 Subject: [PATCH] Unwinding Follow-up. Fix a bug in JitUnwindWindows where ... (#1238) ... in case of "Vector" unwind codes the remaining unwind codes could be corrupted. Nits. --- ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs | 12 +- .../Unwinding/UnwindPseudoOperation.cs | 11 ++ .../CodeGen/Unwinding/UnwindPushEntry.cs | 20 ++- ARMeilleure/CodeGen/X86/CodeGenerator.cs | 17 ++- ARMeilleure/Translation/JitUnwindWindows.cs | 114 +++++++++++------- 5 files changed, 101 insertions(+), 73 deletions(-) create mode 100644 ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs b/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs index 4955f1b4..8072acd9 100644 --- a/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs +++ b/ARMeilleure/CodeGen/Unwinding/UnwindInfo.cs @@ -3,16 +3,12 @@ namespace ARMeilleure.CodeGen.Unwinding struct UnwindInfo { public UnwindPushEntry[] PushEntries { get; } + public int PrologSize { get; } - public int PrologueSize { get; } - - public int FixedAllocSize { get; } - - public UnwindInfo(UnwindPushEntry[] pushEntries, int prologueSize, int fixedAllocSize) + public UnwindInfo(UnwindPushEntry[] pushEntries, int prologSize) { - PushEntries = pushEntries; - PrologueSize = prologueSize; - FixedAllocSize = fixedAllocSize; + PushEntries = pushEntries; + PrologSize = prologSize; } } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs b/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs new file mode 100644 index 00000000..44ed23f5 --- /dev/null +++ b/ARMeilleure/CodeGen/Unwinding/UnwindPseudoOperation.cs @@ -0,0 +1,11 @@ +namespace ARMeilleure.CodeGen.Unwinding +{ + enum UnwindPseudoOp + { + PushReg, + SetFrame, + AllocStack, + SaveReg, + SaveXmm128 + } +} \ No newline at end of file diff --git a/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs b/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs index 6597e2b4..021479a4 100644 --- a/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs +++ b/ARMeilleure/CodeGen/Unwinding/UnwindPushEntry.cs @@ -1,20 +1,18 @@ -using ARMeilleure.IntermediateRepresentation; - namespace ARMeilleure.CodeGen.Unwinding { struct UnwindPushEntry { - public int Index { get; } + public UnwindPseudoOp PseudoOp { get; } + public int PrologOffset { get; } + public int RegIndex { get; } + public int StackOffsetOrAllocSize { get; } - public RegisterType Type { get; } - - public int StreamEndOffset { get; } - - public UnwindPushEntry(int index, RegisterType type, int streamEndOffset) + public UnwindPushEntry(UnwindPseudoOp pseudoOp, int prologOffset, int regIndex = -1, int stackOffsetOrAllocSize = -1) { - Index = index; - Type = type; - StreamEndOffset = streamEndOffset; + PseudoOp = pseudoOp; + PrologOffset = prologOffset; + RegIndex = regIndex; + StackOffsetOrAllocSize = stackOffsetOrAllocSize; } } } \ No newline at end of file diff --git a/ARMeilleure/CodeGen/X86/CodeGenerator.cs b/ARMeilleure/CodeGen/X86/CodeGenerator.cs index a6347b27..0faba6dd 100644 --- a/ARMeilleure/CodeGen/X86/CodeGenerator.cs +++ b/ARMeilleure/CodeGen/X86/CodeGenerator.cs @@ -1525,30 +1525,27 @@ namespace ARMeilleure.CodeGen.X86 context.Assembler.Pshufd(dest, dest, 0xfc); } + [Conditional("DEBUG")] private static void ValidateUnOp(Operand dest, Operand source) { -#if DEBUG EnsureSameReg (dest, source); EnsureSameType(dest, source); -#endif } + [Conditional("DEBUG")] private static void ValidateBinOp(Operand dest, Operand src1, Operand src2) { -#if DEBUG EnsureSameReg (dest, src1); EnsureSameType(dest, src1, src2); -#endif } + [Conditional("DEBUG")] private static void ValidateShift(Operand dest, Operand src1, Operand src2) { -#if DEBUG EnsureSameReg (dest, src1); EnsureSameType(dest, src1); Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); -#endif } private static void EnsureSameReg(Operand op1, Operand op2) @@ -1595,7 +1592,7 @@ namespace ARMeilleure.CodeGen.X86 context.Assembler.Push(Register((X86Register)bit)); - pushEntries.Add(new UnwindPushEntry(bit, RegisterType.Integer, context.StreamOffset)); + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.PushReg, context.StreamOffset, regIndex: bit)); mask &= ~(1 << bit); } @@ -1612,6 +1609,8 @@ namespace ARMeilleure.CodeGen.X86 if (reservedStackSize != 0) { context.Assembler.Sub(rsp, Const(reservedStackSize), OperandType.I64); + + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.AllocStack, context.StreamOffset, stackOffsetOrAllocSize: reservedStackSize)); } int offset = reservedStackSize; @@ -1628,12 +1627,12 @@ namespace ARMeilleure.CodeGen.X86 context.Assembler.Movdqu(memOp, Xmm((X86Register)bit)); - pushEntries.Add(new UnwindPushEntry(bit, RegisterType.Vector, context.StreamOffset)); + pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.SaveXmm128, context.StreamOffset, bit, offset)); mask &= ~(1 << bit); } - return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset, reservedStackSize); + return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset); } private static void WriteEpilogue(CodeGenContext context) diff --git a/ARMeilleure/Translation/JitUnwindWindows.cs b/ARMeilleure/Translation/JitUnwindWindows.cs index 108dc2c5..3f5b3282 100644 --- a/ARMeilleure/Translation/JitUnwindWindows.cs +++ b/ARMeilleure/Translation/JitUnwindWindows.cs @@ -1,12 +1,15 @@ -using ARMeilleure.IntermediateRepresentation; +// https://github.com/MicrosoftDocs/cpp-docs/blob/master/docs/build/exception-handling-x64.md + +using ARMeilleure.CodeGen.Unwinding; using System; +using System.Diagnostics; using System.Runtime.InteropServices; namespace ARMeilleure.Translation { static class JitUnwindWindows { - private const int MaxUnwindCodesArraySize = 9 + 10 * 2 + 3; + private const int MaxUnwindCodesArraySize = 32; // Must be an even value. private struct RuntimeFunction { @@ -41,12 +44,12 @@ namespace ARMeilleure.Translation [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] private static unsafe extern bool RtlInstallFunctionTableCallback( - ulong tableIdentifier, - ulong baseAddress, - uint length, + ulong tableIdentifier, + ulong baseAddress, + uint length, GetRuntimeFunctionCallback callback, - IntPtr context, - string outOfProcessCallbackDll); + IntPtr context, + string outOfProcessCallbackDll); private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; @@ -93,59 +96,80 @@ namespace ARMeilleure.Translation if (!JitCache.TryFind(offset, out JitCacheEntry funcEntry)) { - // Not found. - return null; + return null; // Not found. } var unwindInfo = funcEntry.UnwindInfo; int codeIndex = 0; - int spOffset = unwindInfo.FixedAllocSize; - - foreach (var entry in unwindInfo.PushEntries) - { - if (entry.Type == RegisterType.Vector) - { - spOffset -= 16; - } - } - for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--) { var entry = unwindInfo.PushEntries[index]; - if (entry.Type == RegisterType.Vector) + switch (entry.PseudoOp) { - ushort uwop = PackUwop(UnwindOperation.SaveXmm128, entry.StreamEndOffset, entry.Index); + case UnwindPseudoOp.SaveXmm128: + { + int stackOffset = entry.StackOffsetOrAllocSize; - _unwindInfo->UnwindCodes[codeIndex++] = uwop; - _unwindInfo->UnwindCodes[codeIndex++] = (ushort)spOffset; + Debug.Assert(stackOffset % 16 == 0); - spOffset += 16; + if (stackOffset <= 0xFFFF0) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.SaveXmm128, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset / 16); + } + else + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.SaveXmm128Far, entry.PrologOffset, entry.RegIndex); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(stackOffset >> 16); + } + + break; + } + + case UnwindPseudoOp.AllocStack: + { + int allocSize = entry.StackOffsetOrAllocSize; + + Debug.Assert(allocSize % 8 == 0); + + if (allocSize <= 128) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocSmall, entry.PrologOffset, (allocSize / 8) - 1); + } + else if (allocSize <= 0x7FFF8) + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocLarge, entry.PrologOffset, 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize / 8); + } + else + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.AllocLarge, entry.PrologOffset, 1); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 0); + _unwindInfo->UnwindCodes[codeIndex++] = (ushort)(allocSize >> 16); + } + + break; + } + + case UnwindPseudoOp.PushReg: + { + _unwindInfo->UnwindCodes[codeIndex++] = PackUnwindOp(UnwindOperation.PushNonvol, entry.PrologOffset, entry.RegIndex); + + break; + } + + default: throw new NotImplementedException($"({nameof(entry.PseudoOp)} = {entry.PseudoOp})"); } } - _unwindInfo->UnwindCodes[0] = PackUwop(UnwindOperation.AllocLarge, unwindInfo.PrologueSize, 1); - _unwindInfo->UnwindCodes[1] = (ushort)(unwindInfo.FixedAllocSize >> 0); - _unwindInfo->UnwindCodes[2] = (ushort)(unwindInfo.FixedAllocSize >> 16); + Debug.Assert(codeIndex <= MaxUnwindCodesArraySize); - codeIndex += 3; - - for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--) - { - var entry = unwindInfo.PushEntries[index]; - - if (entry.Type == RegisterType.Integer) - { - ushort uwop = PackUwop(UnwindOperation.PushNonvol, entry.StreamEndOffset, entry.Index); - - _unwindInfo->UnwindCodes[codeIndex++] = uwop; - } - } - - _unwindInfo->VersionAndFlags = 1; - _unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologueSize; + _unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler. + _unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize; _unwindInfo->CountOfUnwindCodes = (byte)codeIndex; _unwindInfo->FrameRegister = 0; @@ -156,9 +180,9 @@ namespace ARMeilleure.Translation return _runtimeFunction; } - private static ushort PackUwop(UnwindOperation uwop, int prologOffset, int opInfo) + private static ushort PackUnwindOp(UnwindOperation op, int prologOffset, int opInfo) { - return (ushort)(prologOffset | ((int)uwop << 8) | (opInfo << 12)); + return (ushort)(prologOffset | ((int)op << 8) | (opInfo << 12)); } } } \ No newline at end of file