mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-11 03:58:44 +08:00
CPU: avoid continuation emitter closure allocations (#306)
This commit is contained in:
@@ -5127,64 +5127,51 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
BindTlsBase(context);
|
BindTlsBase(context);
|
||||||
byte* ptr2 = (byte*)ptr;
|
byte* ptr2 = (byte*)ptr;
|
||||||
ulong hostRspSlot = (ulong)hostRspStorage;
|
ulong hostRspSlot = (ulong)hostRspStorage;
|
||||||
int offset = 0;
|
var emitter = new NativeCodeEmitter(ptr2);
|
||||||
|
|
||||||
void Emit(byte value) => ptr2[offset++] = value;
|
emitter.Emit(0x53); // push rbx
|
||||||
void EmitU64(ulong value)
|
emitter.Emit(0x55); // push rbp
|
||||||
{
|
emitter.Emit(0x57); // push rdi
|
||||||
*(ulong*)(ptr2 + offset) = value;
|
emitter.Emit(0x56); // push rsi
|
||||||
offset += sizeof(ulong);
|
emitter.Emit(0x41); emitter.Emit(0x54); // push r12
|
||||||
}
|
emitter.Emit(0x41); emitter.Emit(0x55); // push r13
|
||||||
void EmitMovR64Imm(byte rex, byte opcode, ulong value)
|
emitter.Emit(0x41); emitter.Emit(0x56); // push r14
|
||||||
{
|
emitter.Emit(0x41); emitter.Emit(0x57); // push r15
|
||||||
Emit(rex);
|
EmitHostNonvolatileXmmSave(ptr2, ref emitter.Offset);
|
||||||
Emit(opcode);
|
|
||||||
EmitU64(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
Emit(0x53); // push rbx
|
|
||||||
Emit(0x55); // push rbp
|
|
||||||
Emit(0x57); // push rdi
|
|
||||||
Emit(0x56); // push rsi
|
|
||||||
Emit(0x41); Emit(0x54); // push r12
|
|
||||||
Emit(0x41); Emit(0x55); // push r13
|
|
||||||
Emit(0x41); Emit(0x56); // push r14
|
|
||||||
Emit(0x41); Emit(0x57); // push r15
|
|
||||||
EmitHostNonvolatileXmmSave(ptr2, ref offset);
|
|
||||||
// Restore the fiber's floating-point control environment before
|
// Restore the fiber's floating-point control environment before
|
||||||
// abandoning the host stack. This path is used when a blocked guest
|
// abandoning the host stack. This path is used when a blocked guest
|
||||||
// continuation migrates to another managed worker.
|
// continuation migrates to another managed worker.
|
||||||
Emit(0x48); Emit(0x83); Emit(0xEC); Emit(0x08); // sub rsp,8
|
emitter.Emit(0x48); emitter.Emit(0x83); emitter.Emit(0xEC); emitter.Emit(0x08); // sub rsp,8
|
||||||
Emit(0xC7); Emit(0x04); Emit(0x24); // mov dword [rsp],imm32
|
emitter.Emit(0xC7); emitter.Emit(0x04); emitter.Emit(0x24); // mov dword [rsp],imm32
|
||||||
*(uint*)(ptr2 + offset) = context.Mxcsr; offset += sizeof(uint);
|
emitter.Emit(context.Mxcsr);
|
||||||
Emit(0x0F); Emit(0xAE); Emit(0x14); Emit(0x24); // ldmxcsr [rsp]
|
emitter.Emit(0x0F); emitter.Emit(0xAE); emitter.Emit(0x14); emitter.Emit(0x24); // ldmxcsr [rsp]
|
||||||
Emit(0x66); Emit(0xC7); Emit(0x04); Emit(0x24); // mov word [rsp],imm16
|
emitter.Emit(0x66); emitter.Emit(0xC7); emitter.Emit(0x04); emitter.Emit(0x24); // mov word [rsp],imm16
|
||||||
*(ushort*)(ptr2 + offset) = context.FpuControlWord; offset += sizeof(ushort);
|
emitter.Emit(context.FpuControlWord);
|
||||||
Emit(0xD9); Emit(0x2C); Emit(0x24); // fldcw [rsp]
|
emitter.Emit(0xD9); emitter.Emit(0x2C); emitter.Emit(0x24); // fldcw [rsp]
|
||||||
Emit(0x48); Emit(0x83); Emit(0xC4); Emit(0x08); // add rsp,8
|
emitter.Emit(0x48); emitter.Emit(0x83); emitter.Emit(0xC4); emitter.Emit(0x08); // add rsp,8
|
||||||
EmitMovR64Imm(0x49, 0xBA, hostRspSlot); // mov r10, hostRspSlot
|
emitter.EmitMovR64Immediate(0x49, 0xBA, hostRspSlot); // mov r10, hostRspSlot
|
||||||
Emit(0x49); Emit(0x89); Emit(0x22); // mov [r10], rsp
|
emitter.Emit(0x49); emitter.Emit(0x89); emitter.Emit(0x22); // mov [r10], rsp
|
||||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rsp]); // mov rax, guest rsp
|
emitter.EmitMovR64Immediate(0x48, 0xB8, context[CpuRegister.Rsp]); // mov rax, guest rsp
|
||||||
Emit(0x48); Emit(0x89); Emit(0xC4); // mov rsp, rax
|
emitter.Emit(0x48); emitter.Emit(0x89); emitter.Emit(0xC4); // mov rsp, rax
|
||||||
Emit(0x48); Emit(0x83); Emit(0xEC); Emit(0x08); // reserve transfer slot
|
emitter.Emit(0x48); emitter.Emit(0x83); emitter.Emit(0xEC); emitter.Emit(0x08); // reserve transfer slot
|
||||||
EmitMovR64Imm(0x48, 0xB8, entryPoint); // mov rax, entryPoint
|
emitter.EmitMovR64Immediate(0x48, 0xB8, entryPoint); // mov rax, entryPoint
|
||||||
Emit(0x48); Emit(0x89); Emit(0x04); Emit(0x24); // mov [rsp],rax
|
emitter.Emit(0x48); emitter.Emit(0x89); emitter.Emit(0x04); emitter.Emit(0x24); // mov [rsp],rax
|
||||||
EmitMovR64Imm(0x48, 0xBB, context[CpuRegister.Rbx]); // mov rbx, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xBB, context[CpuRegister.Rbx]); // mov rbx, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBD, context[CpuRegister.Rbp]); // mov rbp, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xBD, context[CpuRegister.Rbp]); // mov rbp, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBF, context[CpuRegister.Rdi]); // mov rdi, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xBF, context[CpuRegister.Rdi]); // mov rdi, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBE, context[CpuRegister.Rsi]); // mov rsi, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xBE, context[CpuRegister.Rsi]); // mov rsi, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBA, context[CpuRegister.Rdx]); // mov rdx, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xBA, context[CpuRegister.Rdx]); // mov rdx, imm64
|
||||||
EmitMovR64Imm(0x48, 0xB9, context[CpuRegister.Rcx]); // mov rcx, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xB9, context[CpuRegister.Rcx]); // mov rcx, imm64
|
||||||
EmitMovR64Imm(0x49, 0xB8, context[CpuRegister.R8]); // mov r8, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xB8, context[CpuRegister.R8]); // mov r8, imm64
|
||||||
EmitMovR64Imm(0x49, 0xB9, context[CpuRegister.R9]); // mov r9, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xB9, context[CpuRegister.R9]); // mov r9, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBA, context[CpuRegister.R10]); // mov r10, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBA, context[CpuRegister.R10]); // mov r10, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBC, context[CpuRegister.R12]); // mov r12, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBC, context[CpuRegister.R12]); // mov r12, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBD, context[CpuRegister.R13]); // mov r13, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBD, context[CpuRegister.R13]); // mov r13, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBE, context[CpuRegister.R14]); // mov r14, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBE, context[CpuRegister.R14]); // mov r14, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBF, context[CpuRegister.R15]); // mov r15, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBF, context[CpuRegister.R15]); // mov r15, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBB, context[CpuRegister.R11]); // mov r11, imm64
|
emitter.EmitMovR64Immediate(0x49, 0xBB, context[CpuRegister.R11]); // mov r11, imm64
|
||||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rax]); // mov rax, imm64
|
emitter.EmitMovR64Immediate(0x48, 0xB8, context[CpuRegister.Rax]); // mov rax, imm64
|
||||||
Emit(0xC3); // ret through the synthetic transfer slot
|
emitter.Emit(0xC3); // ret through the synthetic transfer slot
|
||||||
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||||
if (returnSlotAddress == 0 || !context.TryWriteUInt64(returnSlotAddress, (ulong)_guestReturnStub))
|
if (returnSlotAddress == 0 || !context.TryWriteUInt64(returnSlotAddress, (ulong)_guestReturnStub))
|
||||||
{
|
{
|
||||||
@@ -5248,6 +5235,45 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The continuation trampoline is rebuilt on every blocked-thread resume.
|
||||||
|
// Keep its tiny writer on the stack: capturing local emit functions create a
|
||||||
|
// managed display-class allocation on this extremely hot path.
|
||||||
|
private unsafe ref struct NativeCodeEmitter(byte* code)
|
||||||
|
{
|
||||||
|
private readonly byte* _code = code;
|
||||||
|
public int Offset;
|
||||||
|
|
||||||
|
public void Emit(byte value)
|
||||||
|
{
|
||||||
|
_code[Offset++] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(ushort value)
|
||||||
|
{
|
||||||
|
*(ushort*)(_code + Offset) = value;
|
||||||
|
Offset += sizeof(ushort);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Emit(uint value)
|
||||||
|
{
|
||||||
|
*(uint*)(_code + Offset) = value;
|
||||||
|
Offset += sizeof(uint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Emit(ulong value)
|
||||||
|
{
|
||||||
|
*(ulong*)(_code + Offset) = value;
|
||||||
|
Offset += sizeof(ulong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EmitMovR64Immediate(byte rex, byte opcode, ulong value)
|
||||||
|
{
|
||||||
|
Emit(rex);
|
||||||
|
Emit(opcode);
|
||||||
|
Emit(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static ulong AlignDown(ulong value, ulong alignment)
|
private static ulong AlignDown(ulong value, ulong alignment)
|
||||||
{
|
{
|
||||||
if (alignment == 0)
|
if (alignment == 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user