-
Notifications
You must be signed in to change notification settings - Fork 0
/
UIL.hpp
230 lines (198 loc) · 6.33 KB
/
UIL.hpp
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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include "Resolver.hpp"
#include <stack>
#pragma once
#ifndef _WIN64
#error "No UIL JIT support for non-windows x64 platforms currently"
#endif
namespace ULR::IL
{
using byte = unsigned char;
namespace Helpers
{
class StackValueExpression
{
enum ExpressionType
{
StringLiteral,
IntegerConstant,
FloatConstant,
FieldLookup,
FuncRetValue,
LocalLookup,
ArrayLookup
};
ExpressionType expr_type;
};
using EvalStack = std::stack<StackValueExpression>;
struct LocalInfo
{
int offset;
size_t size;
};
using LocalLookupTable = std::vector<LocalInfo>;
}
struct CompilationError
{
enum ErrorCode
{
None,
TypeExpected,
MemberExpected,
InvalidInstr,
LocalTypeExpected,
InvalidTypeIdentifer,
InvalidDirective,
SignalExpected
};
char* error;
ErrorCode code;
byte* byte_at;
operator bool()
{
return code != ErrorCode::None;
}
};
constexpr CompilationError NoError { nullptr, CompilationError::ErrorCode::None, nullptr };
enum OpCodes : byte
{
Add,
Sub,
Mul,
Div,
Mod,
DivMod,
And,
Or,
Not,
Xor,
CstNV, // Cast Numerical Value
LdStr, // to fix GC problem with ldstr, maybe allocate separately (on own) & delete at the end of JITContext
LdNC, // load numerical constant
LdFld, // load field
LdLoc, // load local
LdAPL, // load argument-passed local
LdElem, // load element
LdITO, // may remove, probably useless [load internal type object]
LdETO, // may remove, may not be feasible (also same GC problem?) [load external type object]
LdLst, // load last - pushes the most recently popped item back onto the evaluation stack (this is only guaranteed to work after a StXXX instruction and cannot be used successively)
VTSCp, // ValueType Stack Copy [may remove - find uses?]
Call,
Box,
UnBox,
VCall, // virtual call
StFld, // store field
StLoc, // store local
StAPL, // store argument-passed local
StElem, // store element
AllocS, // allocate (but don't initialize) new object [short lifetime - until next valtype allocation by Call/VCall or AllocS]
AllocL, // allocate (but don't initialize) new object [long lifetime]
NewArr, // create array
Ret,
/* Jumping Instructions */
Jmp, // jump
JNE, // jump if not eq
JEq, // jump if eq
JLT, // jump if less than
JGT, // jump if greater than
JLU, // jump if less than (unsigned)
JGU, // jump if greater than (unsigned)
JLE, // jump if less than or eq
JGE, // jump if greater than or eq
JLEU, // jump ig less than or eq (unsigned)
JGEU, // jump if greater than or eq (unsigned)
JNZ, // jump if truthy/nonzero (e.g != 0)
JEqZ, // jump if false (e.g. == 0)
/* Below are UIL signals, used to section code */
BeginType,
EndTypeMeta,
EndType,
BeginMethod,
BeginCtor,
BeginDtor,
EndMethod,
BeginSection,
EndAssembly,
NewArg,
FieldDecl,
LocalDecl,
};
enum Flags : byte
{
Static,
Instance,
Ctor
};
enum NumericalTypeIdentifier : byte
{
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64
};
class JITContext
{
std::vector<void*> virt_alloced;
std::vector<void*> malloc_alloced;
Resolver::ULRAPIImpl* api;
Type* SystemStringType;
void EnsureInitialized();
public:
JITContext(Resolver::ULRAPIImpl* api);
/*
JITContext::Compile() returns the error from the compilation (if any) and populates the Assembly pointed to by meta_asm with all of the methods, fields, and other content held within the il and stringref blocks. Compile() may use either stack-based assembly compilation or may make more heavy use of registers.
Both Compile() and StackBaseCompile() make three passes to compile IL: the first pass reads symbols and determines the size of fields, much like the ULR Loader reading phase. The second pass generates machine code bytes sectioned off by method and determines the size of memory needed to store each method. During the third pass, executable blocks of memory are allocated, the code is copied into the blocks, and any method call addresses within the code are resolved and added to the machine code bytes.
NOTE: meta_asm MUST be added to the current ULRAPI instance's records before jitting.
*/
CompilationError Compile(Assembly* meta_asm, byte il[], byte string_ref[]);
/*
This method is identical to Compile(), except in that it always compiles IL using a stack-based environment (limited register use).
NOTE: meta_asm MUST be added to the current ULRAPI instance's records before jitting.
*/
CompilationError StackBaseCompile(Assembly* meta_asm, byte il[], byte string_ref[]);
CompilationError ReadTypeMeta(Assembly* meta_asm, size_t& i, byte il[], byte string_ref[]);
CompilationError CompileType(
Assembly* meta_asm,
std::map<byte*, MemberInfo*>& replace_addrs,
std::map<MemberInfo*, std::vector<byte>>& dynamic_code,
size_t& i, byte il[], byte string_ref[]
);
CompilationError CompileGenericType(Assembly* meta_asm, size_t& i, byte il[], byte string_ref[], Type* (*ResolveGenericLookup)(byte));
// CompileSection -> CompileMethodBodySection
CompilationError CompileSection( // TODO: refactor so that this just maps to a CompileGenericSection call with no generic resolver passed
unsigned int& locals_size,
unsigned int& recyclable_stack_space,
unsigned int copy_to_rbp_offset_for_return,
Type* rettype,
std::map<byte*, MemberInfo*>& replace_addrs,
Helpers::LocalLookupTable& locals,
Helpers::LocalLookupTable& apls,
std::vector<byte>& code, size_t& i,
byte il[],
byte string_ref[]
);
// CompilationError CompileGenericSection(
// unsigned int& locals_size,
// unsigned int copy_to_rbp_offset_for_return,
// Type* rettype,
// std::map<byte*, MemberInfo*>& replace_addrs,
// Helpers::LocalLookupTable& locals,
// Helpers::LocalLookupTable& apls,
// std::vector<byte>& code, size_t& i,
// byte il[],
// byte string_ref[],
// Type* (*ResolveGenericLookup)(byte)
// );
CompilationError CompleteCompilation(std::map<byte*, MemberInfo*>& replace_addrs, std::map<MemberInfo*, std::vector<byte>>& dynamic_code, size_t offset_replace_addrs);
~JITContext();
private:
std::string_view LookupString(byte il_of_string_ref[], byte string_ref[]);
char* CreateULRString(const char* str, int len);
byte* LogMalloc(size_t);
};
}