forked from chakra-core/ChakraCore
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathInterpreterThunkEmitter.h
More file actions
152 lines (135 loc) · 5.44 KB
/
InterpreterThunkEmitter.h
File metadata and controls
152 lines (135 loc) · 5.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
#if ENABLE_NATIVE_CODEGEN
class ThunkBlock
{
private:
#if PDATA_ENABLED
void* registeredPdataTable;
#endif
BYTE* start;
BVFixed* freeList;
DWORD thunkCount;
public:
ThunkBlock(BYTE* start, DWORD thunkCount) :
start(start),
thunkCount(thunkCount),
freeList(NULL)
#if PDATA_ENABLED
, registeredPdataTable(NULL)
#endif
{}
bool Contains(BYTE* address) const;
void Release(BYTE* address);
bool EnsureFreeList(ArenaAllocator* allocator);
BYTE* AllocateFromFreeList();
bool IsFreeListEmpty() const;
BYTE* GetStart() const { return start; }
#if PDATA_ENABLED
void* GetPdata() const { return registeredPdataTable; }
void SetPdata(void* pdata) { Assert(!this->registeredPdataTable); this->registeredPdataTable = pdata; }
#endif
private:
BVIndex FromThunkAddress(BYTE* address);
BYTE* ToThunkAddress(BVIndex index);
};
//
// Emits interpreter thunks such that each javascript function in the interpreter gets a unique return address on call.
// This unique address can be used to identify the function when a stackwalk is
// performed using ETW.
//
// On every call, we generate 1 thunk (size ~ 1 page) that is implemented as a jump table.
// The shape of the thunk is as follows:
// 1. Function prolog
// 2. Determine the thunk number from the JavascriptFunction object passed as first argument.
// 3. Calculate the address of the correct calling point and jump to it.
// 4. Make the call and jump to the epilog.
// 5. Function epilog.
//
class InterpreterThunkEmitter
{
public:
// InterpreterThunkSize must be public to be accessible to all code in InterpreterThunkEmitter.cpp.
#ifdef _M_X64
#ifdef _WIN32
static constexpr size_t InterpreterThunkSize = 96;
#else // Sys V AMD64
static constexpr size_t InterpreterThunkSize = 72;
#endif
#elif defined(_M_ARM)
static constexpr size_t InterpreterThunkSize = 72;
#elif defined(_M_ARM64)
static constexpr size_t InterpreterThunkSize = 64;
#else
static constexpr size_t InterpreterThunkSize = 56;
#endif
private:
/* ------- instance methods --------*/
InProcEmitBufferManager emitBufferManager;
SListBase<ThunkBlock> thunkBlocks;
SListBase<ThunkBlock> freeListedThunkBlocks;
bool isAsmInterpreterThunk; // To emit address of InterpreterAsmThunk or InterpreterThunk
BYTE* thunkBuffer;
ArenaAllocator* allocator;
Js::ScriptContext * scriptContext;
DWORD thunkCount; // Count of thunks available in the current thunk block
static constexpr BYTE PageCount = 1;
/* ------private helpers -----------*/
bool NewThunkBlock();
#ifdef ENABLE_OOP_NATIVE_CODEGEN
bool NewOOPJITThunkBlock();
#endif
static void EncodeInterpreterThunk(
__in_bcount(InterpreterThunkSize) BYTE* thunkBuffer,
__in const intptr_t thunkBufferStartAddress,
__in const intptr_t epilogStart,
__in const DWORD epilogSize,
__in const intptr_t interpreterThunk);
#if defined(_M_ARM32_OR_ARM64)
static DWORD EncodeMove(DWORD opCode, int reg, DWORD imm16);
static void GeneratePdata(_In_ const BYTE* entryPoint, _In_ const DWORD functionSize, _Out_ RUNTIME_FUNCTION* function);
#endif
/*-------static helpers ---------*/
inline static DWORD FillDebugBreak(_Out_writes_bytes_all_(count) BYTE* dest, _In_ DWORD count);
inline static DWORD CopyWithAlignment(_Out_writes_bytes_all_(sizeInBytes) BYTE* dest, _In_ const DWORD sizeInBytes, _In_reads_bytes_(srcSize) const BYTE* src, _In_ const DWORD srcSize, _In_ const DWORD alignment);
template<class T>
inline static void Emit(__in_bcount(sizeof(T) + offset) BYTE* dest, __in const DWORD offset, __in const T value)
{
AssertMsg(*(T*) (dest + offset) == 0, "Overwriting an already existing opcode?");
*(T*)(dest + offset) = value;
};
BYTE* AllocateFromFreeList(PVOID* ppDynamicInterpreterThunk);
public:
static const BYTE ThunkSize;
static constexpr uint BlockSize = AutoSystemInfo::PageSize * PageCount;
static void* ConvertToEntryPoint(PVOID dynamicInterpreterThunk);
InterpreterThunkEmitter(Js::ScriptContext * context, ArenaAllocator* allocator, CustomHeap::InProcCodePageAllocators * codePageAllocators, bool isAsmInterpreterThunk = false);
BYTE* GetNextThunk(PVOID* ppDynamicInterpreterThunk);
SListBase<ThunkBlock>* GetThunkBlocksList();
void Close();
void Release(BYTE* thunkAddress, bool addtoFreeList);
// Returns true if the argument falls within the range managed by this buffer.
#if DBG
bool IsInHeap(void* address);
#endif
const InProcEmitBufferManager* GetEmitBufferManager() const
{
return &emitBufferManager;
}
static void FillBuffer(
_In_ ThreadContextInfo * context,
_In_ bool asmJsThunk,
_In_ intptr_t finalAddr,
_In_ size_t bufferSize,
_Out_writes_bytes_all_(BlockSize) BYTE* buffer,
#if PDATA_ENABLED
_Out_ PRUNTIME_FUNCTION * pdataTableStart,
_Out_ intptr_t * epilogEndAddr,
#endif
_Out_ DWORD * thunkCount
);
};
#endif