-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpropane_runtime.hpp
More file actions
379 lines (337 loc) · 11.6 KB
/
propane_runtime.hpp
File metadata and controls
379 lines (337 loc) · 11.6 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
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#ifndef _HEADER_PROPANE_RUNTIME
#define _HEADER_PROPANE_RUNTIME
#include "propane_common.hpp"
#include "propane_block.hpp"
namespace propane
{
// Field
// Contains info regarding struct fields
struct field
{
field() = default;
field(name_idx name, type_idx type, size_t offset = 0) :
name(name),
type(type),
offset(offset) {}
// Field name
name_idx name;
// Field type
type_idx type;
// Byte offset in the struct (relative to front of struct)
aligned_size_t offset;
};
// Stackvar
// Contains info regarding stack variables or parameters
struct stackvar
{
stackvar() = default;
stackvar(type_idx type, size_t offset = 0) :
type(type),
offset(offset) {}
// Variable type
type_idx type;
// Byte offset on the stack (relative to front of stack)
aligned_size_t offset;
};
// Generate type information
// Contains information regarding generated types
struct generated_type
{
struct pointer_data
{
pointer_data() = default;
pointer_data(type_idx underlying_type, size_t underlying_size) :
underlying_type(underlying_type),
underlying_size(underlying_size) {}
// Underlying type index
type_idx underlying_type;
// Underlying type size (for pointer arithmetics)
aligned_size_t underlying_size;
};
struct array_data
{
array_data() = default;
array_data(type_idx underlying_type, size_t array_size) :
underlying_type(underlying_type),
array_size(array_size) {}
// Underlying type index
type_idx underlying_type;
// Array element count (number of items, not byte size)
aligned_size_t array_size;
};
struct signature_data
{
signature_data() = default;
signature_data(signature_idx index) :
index(index),
zero(0) {}
// Index to the signature of this method pointer
signature_idx index;
private:
// For initialization
aligned_size_t zero;
};
generated_type() = default;
generated_type(int32_t) :
pointer(type_idx(0), 0) {}
generated_type(const pointer_data& pointer) :
pointer(pointer) {}
generated_type(const array_data& array) :
array(array) {}
generated_type(const signature_data& signature) :
signature(signature) {}
union
{
// This data is valid if type is a pointer
pointer_data pointer;
// This data is valid if type is an array
array_data array;
// This data is valid if type is a signature
signature_data signature;
};
};
// Optional meta data that gets included per type/method,
// if set during generation, this data will contain the filename
// and line number where the type/method was originally defined.
// If not set during generation, index will equal to meta_idx::invalid
struct metadata
{
meta_idx index;
uint32_t line_number;
};
// Type definition
struct type
{
// Name (invalid for generated types)
name_idx name;
// Unique index
type_idx index;
// Flags (see helper functions below)
type_flags flags;
// Generated type information
// (only valid if this type is a generated type)
generated_type generated;
// List of fields
static_block<field> fields;
// Total type size (in bytes)
aligned_size_t total_size;
// Index to the pointer type that uses this type as underlying
// This is optional, some types might not have need for a pointer type
type_idx pointer_type;
// Metadata
metadata meta;
inline bool is_external() const noexcept
{
return flags & type_flags::is_external;
}
inline bool is_integral() const noexcept
{
return propane::is_integral(index);
}
inline bool is_floating_point() const noexcept
{
return propane::is_floating_point(index);
}
inline bool is_arithmetic() const noexcept
{
return propane::is_arithmetic(index);
}
inline bool is_pointer() const noexcept
{
return flags & type_flags::is_pointer_type;
}
inline bool is_array() const noexcept
{
return flags & type_flags::is_array_type;
}
inline bool is_signature() const noexcept
{
return flags & type_flags::is_signature_type;
}
inline bool is_generated() const noexcept
{
return flags & type_flags::is_generated_type;
}
inline bool is_struct() const noexcept
{
return !is_arithmetic() && !is_generated();
}
inline bool is_union() const noexcept
{
return flags & type_flags::is_union;
}
};
// Method signature
// Contains information required for invoking methods
struct signature
{
// Unique index
signature_idx index;
// Return type (voidtype if none)
type_idx return_type;
// List of parameters (and their byte offsets)
static_block<stackvar> parameters;
// Total size of parameter list in bytes
aligned_size_t parameters_size;
inline bool has_return_value() const noexcept
{
return return_type != type_idx::voidtype;
}
};
// Method definition
struct method
{
// Name
name_idx name;
// Unique index
method_idx index;
// Flags (see helper functions below)
type_flags flags;
// Signature index
signature_idx signature;
// Actual instruction bytecode
static_block<uint8_t> bytecode;
// Label locations (byte offset relative to start of bytecode)
static_block<uint32_t> labels;
// Stack variables
static_block<stackvar> stackvars;
// Total size in bytes of the method stack (parameters + variables)
aligned_size_t method_stack_size;
// Total size in bytes of the method stack (method_stack_size + extra space for return values)
aligned_size_t total_stack_size;
// Metadata
metadata meta;
inline bool is_external() const noexcept
{
return flags & type_flags::is_external;
}
};
// Field address
// Contains the set of field names required for accessing
// nested fields. This can be important for generators when
// unwinding nested structs.
struct field_address
{
// Type from which any field is initially accessed
type_idx object_type;
// Field name chain leading down to target field
static_block<name_idx> field_names;
};
// Field offset
// Contains information regarding field offsets. Interpeter runtime
// utilizes this for fast lookup of field offsets, but this part of
// information is probably useless to a generator.
struct field_offset
{
// Field address (see above)
field_address name;
// Field type
type_idx type;
// Field offset (relative to field address root type)
aligned_size_t offset;
};
// Data table
// Contains global data
struct data_table
{
// List of names and offsets per global
// Offset is relative to front of data array
indexed_static_block<global_idx, field> info;
// Actual global data
static_block<uint8_t> data;
};
// String table
// Tightly packed container for strings
template<typename key_t> struct string_table
{
// String info (offset and length)
static_block<string_offset> entries;
// String character data
static_block<char> strings;
inline std::string_view operator[](key_t key) const noexcept
{
if (is_valid_index(key))
{
const auto& entry = entries[static_cast<size_t>(key)];
return std::string_view(strings.data() + entry.offset, entry.length);
}
return std::string_view();
}
inline bool is_valid_index(key_t key) const noexcept
{
return static_cast<size_t>(key) < entries.size();
}
};
// Actual assembly data
// Contains all types, methods, signatures and offsets required to
// A) Generate a program in any programming language or assembler
// If this is the case, information regarding names and strings is
// more relevant than data offsets.
// B) Execute directly in an interpreter
// If this is the case, information regarding data offsets
// is more relevant than names and fields.
struct assembly_data
{
// List of types
indexed_static_block<type_idx, type> types;
// List of methods
indexed_static_block<method_idx, method> methods;
// List of signatures
indexed_static_block<signature_idx, signature> signatures;
// List of offsets
indexed_static_block<offset_idx, field_offset> offsets;
// Global
data_table globals;
// Constant data
data_table constants;
// Database of type/method/field names
string_table<name_idx> database;
// Database of type/method meta info
string_table<meta_idx> metatable;
// Index of main entry point method
// (method_idx::invalid if none was provided)
method_idx main;
// Runtime hash for validation checking
aligned_size_t runtime_hash;
// Utility function for generating a full typename.
// Generated type names don't get exported into the database,
// so this function can help generating a typename for debugging purposes.
void generate_name(type_idx type, std::string& out_name) const;
};
// Allow custom logging for the interpreter.
// Strings are null-terminated.
// If left unassigned, output is redirected to stdout.
typedef void(*print_method_handle)(const char*, size_t);
struct runtime_parameters
{
size_t max_stack_size = 1 << 20;
size_t min_stack_size = 1 << 15;
uint32_t max_callstack_depth = 1024;
print_method_handle print_method = nullptr;
};
// Environment object.
// Contains a list of libraries with external function calls which can be invoked at runtime.
class environment : public handle<class environment_data, sizeof(size_t) * 8>
{
public:
environment(span<const class library> libs = span<const class library>());
environment(const class library& lib);
~environment();
environment& operator+=(const class library& lib);
private:
friend class runtime;
};
// Runtime object.
// When executing an assembly, make sure the assembly was linked with the same environment.
class runtime : public handle<class runtime_data, sizeof(size_t) * 32>
{
public:
runtime();
explicit runtime(const environment& env);
~runtime();
int32_t execute(const class assembly& linked_assembly, runtime_parameters parameters = runtime_parameters()) const;
private:
friend class assembly_linker;
};
}
#endif