-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathbytecode.h
359 lines (293 loc) · 12 KB
/
bytecode.h
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
#ifndef BYTECODE_H
#define BYTECODE_H
// ****************************************************************************
// bytecode.h ELFE project
// ****************************************************************************
//
// File Description:
//
// Implementation of a bytecode to evaluate ELFE programs faster
// without the need to generate machine code directly
//
//
//
//
//
//
//
// ****************************************************************************
// (C) 2015 Christophe de Dinechin <[email protected]>
// (C) 2015 Taodyne SAS
// ****************************************************************************
//
// All bytecode operations are represented by an instance of the 'Op'
// class, which has a virtual member 'Run' taking a 'Data' argument.
//
// The 'Data' argument is an array of Tree_p represented by a pointer.
// Local values are stored at positive offsets, input arguments and
// closure data at negative offsets.
// [0] : Self / result
// [1] : Evaluation context (more precisely, the Scope for it)
// [2..N] : Local variables, temporaries, output slots
// [-1..-M] : Input arguments, captured values (closure)
#include "tree.h"
#include "context.h"
#include <vector>
#include <map>
#include <iostream>
ELFE_BEGIN
struct Op; // An individual operation
struct Code; // A sequence of operations
struct Function; // Internal representation of functions
struct CallOp; // A call operation
struct CodeBuilder; // Code generator
typedef std::vector<Op *> Ops; // Sequence of operations
typedef std::map<Tree *, int> TreeIDs;
typedef std::map<Tree *, Op *> TreeOps;
typedef std::vector<int> ParmOrder;
typedef Tree_p * Data;
// ============================================================================
//
// Main entry point
//
// ============================================================================
Tree * EvaluateWithBytecode(Context *context, Tree *input);
Function * CompileToBytecode(Context *context, Tree *input, Tree *type,
TreeIDs &parms, TreeList &captured);
// ============================================================================
//
// Code representation
//
// ============================================================================
struct Op
// ----------------------------------------------------------------------------
// An individual operation
// ----------------------------------------------------------------------------
{
public:
// The action to perform
virtual Op * Run(Data data) { return success; }
public:
Op() : success(NULL) {}
virtual ~Op() {}
virtual Op * Fail() { return NULL; }
virtual void Dump(std::ostream &out) { out << OpID(); }
virtual kstring OpID() { return "op"; }
public:
Op * success;
};
struct Code : Op, Info
// ----------------------------------------------------------------------------
// A sequence of operations (may be local evaluation code in a function)
// ----------------------------------------------------------------------------
{
Code(Context *, Tree *self);
Code(Context *, Tree *self, Op *instr);
~Code();
virtual Op * Run(Data data);
void SetOps(Op **ops, Ops *instr, uint outId);
virtual void Dump(std::ostream &out);
static void Dump(std::ostream &out, Op *ops, Ops &instrs);
static text Ref(Op *op, text sep, text set, text null);
virtual uint Inputs() { return 0; }
virtual uint Locals() { return 0; }
virtual kstring OpID() { return "code"; }
public:
Context_p context;
Tree_p self;
Op * ops;
Ops instrs;
};
struct Function : Code
// ----------------------------------------------------------------------------
// A sequence of operations with its own scope
// ----------------------------------------------------------------------------
{
Function(Context *context, Tree *self, uint nInputs, uint nLocals);
Function(Function *original, Data data, ParmOrder &capture);
~Function();
virtual Op * Run(Data data);
virtual void Dump(std::ostream &out);
virtual uint Inputs() { return nInputs; }
virtual uint Locals() { return nLocals; }
virtual kstring OpID() { return "function"; }
uint Closures() { return captured.size(); }
Tree_p * ClosureData() { return &captured[0]; }
uint OffsetSize() { return Inputs() + Closures(); }
uint FrameSize() { return 2 + OffsetSize() + Locals(); }
public:
uint nInputs, nLocals;
TreeList captured;
};
// ============================================================================
//
// Build code from the input
//
// ============================================================================
struct CodeBuilder
// ----------------------------------------------------------------------------
// Build the bytecode to rapidly evaluate a tree
// ----------------------------------------------------------------------------
{
CodeBuilder(TreeList &captured);
~CodeBuilder();
public:
Function * Compile(Context *context,Tree *tree,TreeIDs &parms,Tree *type);
Op * CompileInternal(Context *context, Tree *what, bool defer);
bool Instructions(Context *context, Tree *tree);
public:
// Tree::Do interface
enum strength { NEVER, SOMETIMES, ALWAYS };
typedef strength value_type;
strength DoInteger(Integer *what);
strength DoReal(Real *what);
strength DoText(Text *what);
strength DoName(Name *what);
strength DoPrefix(Prefix *what);
strength DoPostfix(Postfix *what);
strength DoInfix(Infix *what);
strength DoBlock(Block *what);
strength DoLeftRight(Tree *wl, Tree *wr, Tree *l, Tree *r);
// Evaluation and binding of values
int ValueID(Tree *);
int CaptureID(Tree *);
int Evaluate(Context *, Tree *, bool deferEval = false);
int EvaluationTemporary(Tree *);
void Enclose(Context *context, Scope *old, Tree *what);
int Bind(Name *name, Tree *value, Tree *type=NULL);
CallOp * Call(Context *context, Tree *value, Tree *type,
TreeIDs &inputs, ParmOrder &parms);
// Contexts management
enum depth { LOCAL, PARAMETER, ENCLOSING, GLOBAL };
depth ScopeDepth(Scope *scope);
// Adding an opcode
void Add(Op *op);
void AddEval(int id, Op *op);
void AddTypeCheck(Context *, Tree *value, Tree *type);
// Success at end of declaration
void Success();
void InstructionsSuccess(uint oldNumEvals);
public:
Op * ops; // List of operations to evaluate that tree
Op ** lastOp; // Last operation being written to
TreeIDs inputs; // Input arguments
TreeIDs outputs; // Output arguments
TreeIDs values; // Local variables and evaluation temporaries
TreeList &captured; // Captured values from enclosing contexts
uint nEvals; // Max number of evals on all candidates
uint nParms; // Max number of parms on all candidates
uint candidates; // Number of candidates found
Tree_p test; // Current form to test
Tree_p resultType; // Result type declared in rewrite
Context_p context; // Evaluation context
Context_p parmsCtx; // Parameter declarations for current function
Context_p argsCtx; // Argument declarations for called functions
Op * failOp; // Exit instruction if evaluation fails
Op * successOp; // Exit instruction in case of success
Ops instrs; // All instructions
TreeOps subexprs; // Code generated for sub-expressions
ParmOrder parms; // Indices for parameters
bool defer; // Deferred evaluation
};
// ============================================================================
//
// Functions returning specific items in a data scope
//
// ============================================================================
inline Tree *DataSelf(Data data)
// ----------------------------------------------------------------------------
// Return the 'self' input argument in the input data
// ----------------------------------------------------------------------------
{
return data[0];
}
inline Tree *DataResult(Data data)
// ----------------------------------------------------------------------------
// Update the result
// ----------------------------------------------------------------------------
{
return data[0];
}
inline void DataResult(Data data, Tree *result)
// ----------------------------------------------------------------------------
// Update the result
// ----------------------------------------------------------------------------
{
data[0] = result;
}
inline Scope *DataScope(Data data)
// ----------------------------------------------------------------------------
// Return the scope in the input data
// ----------------------------------------------------------------------------
{
return data[1]->As<Scope>();
}
inline void DataScope(Data data, Scope *scope)
// ----------------------------------------------------------------------------
// Set the scope in the input data
// ----------------------------------------------------------------------------
{
data[1] = scope;
}
inline Tree *DataArg(Data data, uint index)
// ----------------------------------------------------------------------------
// Return the Nth input argument
// ----------------------------------------------------------------------------
{
return data[~index];
}
inline Tree *DataValue(Data data, uint index)
// ----------------------------------------------------------------------------
// Return the Nth input argument
// ----------------------------------------------------------------------------
{
return data[2+index];
}
inline void DataValue(Data data, uint index, Tree *value)
// ----------------------------------------------------------------------------
// Return the Nth input argument
// ----------------------------------------------------------------------------
{
data[2+index] = value;
}
// ============================================================================
//
// Streaming operators
//
// ============================================================================
inline std::ostream &operator<<(std::ostream &out, Op *op)
// ----------------------------------------------------------------------------
// Dump one specific opcode
// ----------------------------------------------------------------------------
{
if (op)
op->Dump(out);
else
out << "NULL";
return out;
}
inline std::ostream &operator<<(std::ostream &out, Op &ops)
// ----------------------------------------------------------------------------
// Dump all the opcodes in a sequence
// ----------------------------------------------------------------------------
{
Op *op = &ops;
while (op)
{
op->Dump(out);
op = op->success;
if (op)
out << "\n";
}
return out;
}
inline std::ostream &operator<<(std::ostream &out, Ops &instrs)
// ----------------------------------------------------------------------------
// Dump all the opcodes in a sequence
// ----------------------------------------------------------------------------
{
Code::Dump(out, NULL, instrs);
return out;
}
ELFE_END
#endif // BYTECODE_H