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
{ {
@ -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:
_unwindInfo->UnwindCodes[codeIndex++] = uwop;
_unwindInfo->UnwindCodes[codeIndex++] = (ushort)spOffset;
spOffset += 16;
}
}
_unwindInfo->UnwindCodes[0] = PackUwop(UnwindOperation.AllocLarge, unwindInfo.PrologueSize, 1);
_unwindInfo->UnwindCodes[1] = (ushort)(unwindInfo.FixedAllocSize >> 0);
_unwindInfo->UnwindCodes[2] = (ushort)(unwindInfo.FixedAllocSize >> 16);
codeIndex += 3;
for (int index = unwindInfo.PushEntries.Length - 1; index >= 0; index--)
{ {
var entry = unwindInfo.PushEntries[index]; int stackOffset = entry.StackOffsetOrAllocSize;
if (entry.Type == RegisterType.Integer) Debug.Assert(stackOffset % 16 == 0);
if (stackOffset <= 0xFFFF0)
{ {
ushort uwop = PackUwop(UnwindOperation.PushNonvol, entry.StreamEndOffset, entry.Index); _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);
}
_unwindInfo->UnwindCodes[codeIndex++] = uwop; 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->VersionAndFlags = 1; Debug.Assert(codeIndex <= MaxUnwindCodesArraySize);
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologueSize;
_unwindInfo->VersionAndFlags = 1; // Flags: The function has no handler.
_unwindInfo->SizeOfProlog = (byte)unwindInfo.PrologSize;
_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));
} }
} }
} }