summaryrefslogtreecommitdiff
path: root/Build/source/utils/asymptote/coder.h
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/asymptote/coder.h')
-rw-r--r--Build/source/utils/asymptote/coder.h428
1 files changed, 428 insertions, 0 deletions
diff --git a/Build/source/utils/asymptote/coder.h b/Build/source/utils/asymptote/coder.h
new file mode 100644
index 00000000000..e07aa11e639
--- /dev/null
+++ b/Build/source/utils/asymptote/coder.h
@@ -0,0 +1,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