Unwinding Follow-up. Fix a bug in JitUnwindWindows where ... (#1238)

... in case of "Vector" unwind codes the remaining unwind codes could be corrupted.
Nits.
This commit is contained in:
LDj3SNuD 2020-05-15 13:46:35 +02:00 committed by GitHub
parent da3fd3f71b
commit 3b70a28087
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 73 deletions

View file

@ -3,16 +3,12 @@ namespace ARMeilleure.CodeGen.Unwinding
struct UnwindInfo struct UnwindInfo
{ {
public UnwindPushEntry[] PushEntries { get; } public UnwindPushEntry[] PushEntries { get; }
public int PrologSize { get; }
public int PrologueSize { get; } public UnwindInfo(UnwindPushEntry[] pushEntries, int prologSize)
public int FixedAllocSize { get; }
public UnwindInfo(UnwindPushEntry[] pushEntries, int prologueSize, int fixedAllocSize)
{ {
PushEntries = pushEntries; PushEntries = pushEntries;
PrologueSize = prologueSize; PrologSize = prologSize;
FixedAllocSize = fixedAllocSize;
} }
} }
} }

View file

@ -0,0 +1,11 @@
namespace ARMeilleure.CodeGen.Unwinding
{
enum UnwindPseudoOp
{
PushReg,
SetFrame,
AllocStack,
SaveReg,
SaveXmm128
}
}

View file

@ -1,20 +1,18 @@
using ARMeilleure.IntermediateRepresentation;
namespace ARMeilleure.CodeGen.Unwinding namespace ARMeilleure.CodeGen.Unwinding
{ {
struct UnwindPushEntry 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 UnwindPushEntry(UnwindPseudoOp pseudoOp, int prologOffset, int regIndex = -1, int stackOffsetOrAllocSize = -1)
public int StreamEndOffset { get; }
public UnwindPushEntry(int index, RegisterType type, int streamEndOffset)
{ {
Index = index; PseudoOp = pseudoOp;
Type = type; PrologOffset = prologOffset;
StreamEndOffset = streamEndOffset; RegIndex = regIndex;
StackOffsetOrAllocSize = stackOffsetOrAllocSize;
} }
} }
} }

View file

@ -1525,30 +1525,27 @@ namespace ARMeilleure.CodeGen.X86
context.Assembler.Pshufd(dest, dest, 0xfc); context.Assembler.Pshufd(dest, dest, 0xfc);
} }
[Conditional("DEBUG")]
private static void ValidateUnOp(Operand dest, Operand source) private static void ValidateUnOp(Operand dest, Operand source)
{ {
#if DEBUG
EnsureSameReg (dest, source); EnsureSameReg (dest, source);
EnsureSameType(dest, source); EnsureSameType(dest, source);
#endif
} }
[Conditional("DEBUG")]
private static void ValidateBinOp(Operand dest, Operand src1, Operand src2) private static void ValidateBinOp(Operand dest, Operand src1, Operand src2)
{ {
#if DEBUG
EnsureSameReg (dest, src1); EnsureSameReg (dest, src1);
EnsureSameType(dest, src1, src2); EnsureSameType(dest, src1, src2);
#endif
} }
[Conditional("DEBUG")]
private static void ValidateShift(Operand dest, Operand src1, Operand src2) private static void ValidateShift(Operand dest, Operand src1, Operand src2)
{ {
#if DEBUG
EnsureSameReg (dest, src1); EnsureSameReg (dest, src1);
EnsureSameType(dest, src1); EnsureSameType(dest, src1);
Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32); Debug.Assert(dest.Type.IsInteger() && src2.Type == OperandType.I32);
#endif
} }
private static void EnsureSameReg(Operand op1, Operand op2) private static void EnsureSameReg(Operand op1, Operand op2)
@ -1595,7 +1592,7 @@ namespace ARMeilleure.CodeGen.X86
context.Assembler.Push(Register((X86Register)bit)); 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); mask &= ~(1 << bit);
} }
@ -1612,6 +1609,8 @@ namespace ARMeilleure.CodeGen.X86
if (reservedStackSize != 0) if (reservedStackSize != 0)
{ {
context.Assembler.Sub(rsp, Const(reservedStackSize), OperandType.I64); context.Assembler.Sub(rsp, Const(reservedStackSize), OperandType.I64);
pushEntries.Add(new UnwindPushEntry(UnwindPseudoOp.AllocStack, context.StreamOffset, stackOffsetOrAllocSize: reservedStackSize));
} }
int offset = reservedStackSize; int offset = reservedStackSize;
@ -1628,12 +1627,12 @@ namespace ARMeilleure.CodeGen.X86
context.Assembler.Movdqu(memOp, Xmm((X86Register)bit)); 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); mask &= ~(1 << bit);
} }
return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset, reservedStackSize); return new UnwindInfo(pushEntries.ToArray(), context.StreamOffset);
} }
private static void WriteEpilogue(CodeGenContext context) private static void WriteEpilogue(CodeGenContext context)

View file

@ -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;
using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
static class JitUnwindWindows static class JitUnwindWindows
{ {
private const int MaxUnwindCodesArraySize = 9 + 10 * 2 + 3; private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
private struct RuntimeFunction private struct RuntimeFunction
{ {
@ -41,12 +44,12 @@ namespace ARMeilleure.Translation
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)] [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static unsafe extern bool RtlInstallFunctionTableCallback( private static unsafe extern bool RtlInstallFunctionTableCallback(
ulong tableIdentifier, ulong tableIdentifier,
ulong baseAddress, ulong baseAddress,
uint length, uint length,
GetRuntimeFunctionCallback callback, GetRuntimeFunctionCallback callback,
IntPtr context, IntPtr context,
string outOfProcessCallbackDll); string outOfProcessCallbackDll);
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback; private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;
@ -93,59 +96,80 @@ namespace ARMeilleure.Translation
if (!JitCache.TryFind(offset, out JitCacheEntry funcEntry)) if (!JitCache.TryFind(offset, out JitCacheEntry funcEntry))
{ {
// Not found. return null; // Not found.
return null;
} }
var unwindInfo = funcEntry.UnwindInfo; var unwindInfo = funcEntry.UnwindInfo;
int codeIndex = 0; 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--) for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
{ {
var entry = unwindInfo.PushEntries[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; Debug.Assert(stackOffset % 16 == 0);
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)spOffset;
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); Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
_unwindInfo->UnwindCodes[1] = (ushort)(unwindInfo.FixedAllocSize >> 0);
_unwindInfo->UnwindCodes[2] = (ushort)(unwindInfo.FixedAllocSize >> 16);
codeIndex += 3; _unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
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->CountOfUnwindCodes = (byte)codeIndex; _unwindInfo->CountOfUnwindCodes = (byte)codeIndex;
_unwindInfo->FrameRegister = 0; _unwindInfo->FrameRegister = 0;
@ -156,9 +180,9 @@ namespace ARMeilleure.Translation
return _runtimeFunction; 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));
} }
} }
} }