summaryrefslogtreecommitdiff
path: root/graphics/asymptote/coder.h
blob: e07aa11e639eaa9e56459b9eb856de8188f61ddf (plain)
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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
/*****
 * coder.h
 * Andy Hammerlindl 2004/11/06
 *
 * Handles encoding of syntax into programs.  It's methods are called by
 * abstract syntax objects during translation to construct the virtual machine
 * code.
 *****/

#ifndef CODER_H
#define CODER_H

#include "errormsg.h"
#include "entry.h"
#include "types.h"
#include "record.h"
#include "frame.h"
#include "program.h"
#include "util.h"
#include "modifier.h"
#include "inst.h"

namespace trans {

using sym::symbol;
using types::ty;
using types::function;
using types::record;

using vm::bltin;
using vm::inst;
using vm::item;

#ifdef DEBUG_BLTIN
void assertBltinLookup(inst::opcode op, item it);
#endif

// Labels used by the coder class to denote where in the code a jump
// instruction should go to.  Label can be used before their exact location is
// known.
// Declared outside of the coder class, so that it can be declared in exp.h.
struct label_t : public gc {
  vm::program::label location;
  vm::program::label firstUse;

  // Most labels are used only once, and so we optimize for that case.  We do,
  // however, have to handle labels which are used multiple times (such as
  // break locations in a loop), and so a useVector is allocated to store
  // these if necessary.
  typedef mem::vector<vm::program::label> useVector;
  useVector *moreUses;

  // Only the constructor is defined.  Everything else is handles by methods
  // of the coder class.
  label_t() : location(), firstUse(), moreUses(0) {}
};
typedef label_t *label;

class coder {
  // The frame of the function we are currently encoding.  This keeps
  // track of local variables, and parameters with respect to the stack.
  frame *level;

  // The frame of the enclosing record that the "this" expression yields.  ie.
  // the highest frame that is a record, not a function.
  frame *recordLevel;

  // The type of the enclosing record.  Also needed for the "this" expression.
  record *recordType;

  // Are we translating a codelet?
  bool isCodelet;

  // The lambda being constructed. In some cases, this lambda is needed
  // before full translation of the function, so it is stored,
  // incomplete, here.
  vm::lambda *l;

  // The type of the function being translated.
  const function *funtype;

  // The enclosing environment.  Null if this is a file-level module.
  coder *parent;

  // The mode of encoding, either static or dynamic. sord is used as an
  // acronym for Static OR Dynamic.
  // Once something is static, no amount of dynamic modifiers can change
  // that, so once a stack is EXPLICIT_STATIC, additional modifiers will
  // be pushed on as EXPLICIT_STATIC.
  modifier sord;
  std::stack<modifier> sord_stack;

  // What permissions will be given to a new access.
  // TODO: Ensure private fields don't show up calling lookup for a
  // record.
  permission perm;

  // The function code as its being written.  Code points to next place in
  // array to write.
  vm::program *program;

  // Some loops allocate nested frames, in case variables in an
  // iteration escape in a closure.  This stack keeps track of where the
  // pushframe instructions are, so the size of the frame can be encoded.
  std::stack<vm::program::label> pushframeLabels;

  // Loops need to store labels to where break and continue statements
  // should pass control.  Since loops can be nested, this needs to
  // be stored as a stack.  We also store which of the loops are being encoded
  // with an additional frame for variables.  This is needed to know if the
  // break and continue statements need to pop the frame.
  struct loopdata_t : gc {
    label continueLabel;
    label breakLabel;
    bool pushedFrame;

    loopdata_t(label c, label b)
      : continueLabel(c), breakLabel(b), pushedFrame(false) {}
  };
  mem::stack<loopdata_t> loopdata;

  // Current File Position
  position curPos;

public:
  // Define a new function coder.  If reframe is true, this gives the function
  // its own frame, which is the usual (sensible) thing to do.  It is set to
  // false for a line-at-a-time codelet, where variables should be allocated in
  // the lower frame.
  coder(position pos,
        string name, function *t, coder *parent,
        modifier sord = DEFAULT_DYNAMIC,
        bool reframe=true);

  // Start encoding the body of the record.  The function being encoded
  // is the record's initializer.
  coder(position pos,
        record *t, coder *parent, modifier sord = DEFAULT_DYNAMIC);

  coder(position pos,
        string name, modifier sord = DEFAULT_DYNAMIC);

  coder(const coder&);

  /* Add a static or dynamic modifier. */
  void pushModifier(modifier s)
  {
    /* Default setting should only be used in the constructor. */
    assert(s != DEFAULT_STATIC && s != DEFAULT_DYNAMIC);

    /* Non-default static overrules. */
    if (sord != EXPLICIT_STATIC)
      sord = s;

    sord_stack.push(sord);
  }

  /* Tests if encoding mode is currently static. */
  bool isStatic()
  {
    switch(sord) {
      case DEFAULT_STATIC:
      case EXPLICIT_STATIC:
        return true;
      case DEFAULT_DYNAMIC:
      case EXPLICIT_DYNAMIC:
        return false;
      default:
        assert(False);
        return false;
    }
  }


  /* Remove a modifier. */
  void popModifier()
  {
    assert(!sord_stack.empty());
    sord_stack.pop();

    assert(!sord_stack.empty());
    sord = sord_stack.top();
  }

  /* Set/get/clear permissions. */
  void setPermission(permission p)
  {
    perm = p;
  }
  permission getPermission()
  {
    return perm;
  }
  void clearPermission()
  {
    perm = DEFAULT_PERM;
  }


  // Says what the return type of the function is.
  ty *getReturnType() {
    return funtype->result;
  }

  bool isRecord();

  // Creates a new coder to handle the translation of a new function.
  coder newFunction(position pos,
                    string name, function *t, modifier sord=DEFAULT_DYNAMIC);

  // Creates a new record type.
  record *newRecord(symbol id);

  // Create a coder for the initializer of the record.
  coder newRecordInit(position pos, record *r, modifier sord=DEFAULT_DYNAMIC);

  // Create a coder for translating a small piece of code.  Used for
  // line-at-a-time mode.
  coder newCodelet(position pos);

  frame *getFrame()
  {
    if (isStatic() && !isTopLevel()) {
      assert(parent->getFrame());
      return parent->getFrame();
    }
    else
      return level;
  }

  // Tests if the function or record with the given frame is currently under
  // translation (either by this coder or an ancestor).
  bool inTranslation(frame *f) {
    frame *level=this->level;
    while (level) {
      if (f==level)
        return true;
      level=level->getParent();
    }
    return parent && parent->inTranslation(f);
  }

  // Allocates space in the function or record frame for a new local variable.
  access *allocLocal()
  {
    return getFrame()->allocLocal();
  }

  // Get the access in the frame for a specified formal parameter.
  access *accessFormal(Int index)
  {
    // NOTE: This hasn't been extended to handle frames for loops, but is
    // currently only called when starting to translate a function, where there
    // can be no loops.
    return level->accessFormal(index);
  }

  // Checks if we are at the top level, which is true for a file-level module or
  // a codelet.
  bool isTopLevel() {
    return parent==0 || isCodelet;
  }

  // The encode functions add instructions and operands on to the code array.
private:
  void encode(inst i)
  {
    i.pos = curPos;
    // Static code is put into the enclosing coder, unless we are translating a
    // codelet.
    if (isStatic() && !isTopLevel()) {
      assert(parent);
      parent->encode(i);
    }
    else {
      program->encode(i);
    }
  }

  // Encode a jump to a not yet known location.
  vm::program::label encodeEmptyJump(inst::opcode op);

public:
  void encode(inst::opcode op)
  {
    inst i; i.op = op; i.pos = nullPos;
    encode(i);
  }
  void encode(inst::opcode op, item it)
  {
#ifdef DEBUG_BLTIN
    assertBltinLookup(op, it);
#endif
    inst i; i.op = op; i.pos = nullPos; i.ref = it;
    encode(i);
  }

  // Encodes a pop instruction, or merges the pop into the previous
  // instruction (ex. varsave+pop becomes varpop).
  void encodePop();

  // Puts the requested frame on the stack.  If the frame is not that of
  // this coder or its ancestors, false is returned.
  bool encode(frame *f);

  // Puts the frame corresponding to the expression "this" on the stack.
  bool encodeThis()
  {
    assert(recordLevel);
    return encode(recordLevel);
  }

  // An access that encodes the frame corresponding to "this".
  access *thisLocation()
  {
    assert(recordLevel);
    return new frameAccess(recordLevel);
  }

  // Returns the type of the enclosing record.
  record *thisType()
  {
    return recordType;
  }

  // Puts the 'dest' frame on the stack, assuming the frame 'top' is on
  // top of the stack.  If 'dest' is not an ancestor frame of 'top',
  // false is returned.
  bool encode(frame *dest, frame *top);


  // Assigns a handle to the current point in the list of bytecode
  // instructions and returns that handle.
  label defNewLabel();

  // Sets the handle given by label to the current point in the list of
  // instructions.
  label defLabel(label label);

  // Encodes the address pointed to by the handle label into the
  // sequence of instructions.  This is useful for a jump instruction to
  // jump to where a label was defined.
  void useLabel(inst::opcode op, label label);

  // If an address has to be used for a jump instruction before it is
  // actually encoded, a handle can be given to it by this function.
  // When that handle's label is later defined, the proper address will
  // be inserted into the code where the handle was used.
  label fwdLabel();

  void pushLoop(label c, label b) {
    loopdata.push(loopdata_t(c,b));
  }
  void popLoop() {
    loopdata.pop();
  }
  void loopPushesFrame()
  {
    assert(!loopdata.empty());
    loopdata_t& d = loopdata.top();
    d.pushedFrame = true;
  }
  bool encodeBreak() {
    if (loopdata.empty())
      return false;
    else {
      loopdata_t& d = loopdata.top();
      if (d.pushedFrame)
        encode(inst::popframe);
      useLabel(inst::jmp,d.breakLabel);
      return true;
    }
  }
  bool encodeContinue() {
    if (loopdata.empty())
      return false;
    else {
      loopdata_t& d = loopdata.top();
      if (d.pushedFrame)
        encode(inst::popframe);
      useLabel(inst::jmp,d.continueLabel);
      return true;
    }
  }

  // Returns true if a pushclosure has been encoded since the definition of
  // the label.
  bool usesClosureSinceLabel(label l);

  // Turn a no-op into a jump to bypass incorrect code.
  void encodePatch(label from, label to);

public:
  void encodePushFrame() {
    pushframeLabels.push(program->end());
    encode(inst::pushframe, (Int)0);

    level = new frame("encodePushFrame", level, 0);
  }

  void encodePopFrame() {
    pushframeLabels.top()->ref = level->size();
    pushframeLabels.pop();

    encode(inst::popframe);

    level = level->getParent();
  }

  // Adds an entry into the position list, linking the given point in the
  // source code to the current position in the virtual machine code.  This is
  // used to print positions at runtime.
  void markPos(position pos);

  // When translation of the function is finished, this ties up loose ends
  // and returns the lambda.
  vm::lambda *close();

  // Finishes translating the initializer of a record.
  void closeRecord();

private: // Non-copyable
  void operator=(const coder&);
};

} // namespace trans

#endif