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 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
|
/*****
* coder.h
* Andy Hammerlindl 2004/11/06
*
* Handles encoding of syntax into programs. Its 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 (s == AUTOUNRAVEL || sord == AUTOUNRAVEL) {
sord = AUTOUNRAVEL;
} else if (s == EXPLICIT_STATIC || sord == EXPLICIT_STATIC) {
sord = EXPLICIT_STATIC;
} else {
sord = s;
}
sord_stack.push(sord);
}
/* Tests if encoding mode is currently static. */
bool isStatic()
{
switch(sord) {
case DEFAULT_STATIC:
case EXPLICIT_STATIC:
case AUTOUNRAVEL:
return true;
case DEFAULT_DYNAMIC:
case EXPLICIT_DYNAMIC:
return false;
default:
assert(False);
return false;
}
}
bool isAutoUnravel()
{
return sord == AUTOUNRAVEL;
}
/* 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()) {
frame *result = parent->getFrame();
assert(result);
return result;
}
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);
}
// Puts the frame corresponding to the parent of 'ent' on the stack. Triggers
// an error if 'ent' does not correspond to a record (a.k.a. asy struct) type.
bool encodeParent(position pos, trans::tyEntry *ent);
// 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);
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
|