From f4b33493b80e34147f85b6ba155b092a7915175e Mon Sep 17 00:00:00 2001 From: "rogerl%netscape.com" Date: Sat, 1 Apr 2000 01:30:32 +0000 Subject: [PATCH] Added do & switch statements, more hacking at the code state thing. --- js/js2/icodegenerator.cpp | 231 +++++++++++++++++++++++++++++++----- js/js2/icodegenerator.h | 91 +++++++++----- js/js2/js2.cpp | 33 +++++- js2/src/icodegenerator.cpp | 231 +++++++++++++++++++++++++++++++----- js2/src/icodegenerator.h | 91 +++++++++----- js2/tests/cpp/js2_shell.cpp | 33 +++++- 6 files changed, 590 insertions(+), 120 deletions(-) diff --git a/js/js2/icodegenerator.cpp b/js/js2/icodegenerator.cpp index 5b719dd88e5e..100b736a019b 100644 --- a/js/js2/icodegenerator.cpp +++ b/js/js2/icodegenerator.cpp @@ -144,24 +144,37 @@ void ICodeGenerator::setLabel(int32 label) l->itsOffset = iCode->size(); } +void ICodeGenerator::setLabel(InstructionStream *stream, int32 label) +{ + Label* l; +#ifdef __GNUC__ + // libg++'s vector class doesn't have at(): + if (label >= labels.size()) + throw std::out_of_range("label out of range"); + l = labels[label]; +#else + l = labels.at(label); +#endif + l->itsBase = stream; + l->itsOffset = stream->size(); +} + /***********************************************************************************************/ void MultiPathICodeState::mergeStream(InstructionStream *mainStream, LabelList &labels) { - if (itsTopLabel < labels.size()) { - // labels (might) have been allocated in this stream - // we need to adjust their position relative to the - // size of the stream we're joining - for (LabelList::iterator i = labels.begin() + itsTopLabel; i != labels.end(); i++) { - if ((*i)->itsBase == its_iCode) { - (*i)->itsBase = mainStream; - (*i)->itsOffset += mainStream->size(); - } + // change InstructionStream to be a class that also remembers + // if it contains any labels (maybe even remembers the labels + // themselves?) in order to avoid running this loop unnecessarily. + for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { + if ((*i)->itsBase == its_iCode) { + (*i)->itsBase = mainStream; + (*i)->itsOffset += mainStream->size(); } } - for (InstructionIterator i = its_iCode->begin(); i != its_iCode->end(); i++) - mainStream->push_back(*i); + for (InstructionIterator ii = its_iCode->begin(); ii != its_iCode->end(); ii++) + mainStream->push_back(*ii); } @@ -178,43 +191,176 @@ void ICodeGenerator::beginWhileStatement(const SourcePosition &pos) branch(whileConditionTop); // save off the current stream while we gen code for the condition - stitcher.push(new WhileCodeState(iCode, labels.size(), whileConditionTop, whileBlockStart)); + stitcher.push_back(new WhileCodeState(whileConditionTop, whileBlockStart, this)); iCode = new InstructionStream(); } void ICodeGenerator::endWhileExpression(Register condition) { - WhileCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == While_State); + WhileCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == While_state); - branchConditional(ics->whileBodyLabel, condition); + branchConditional(ics->whileBody, condition); resetTopRegister(); // stash away the condition expression and switch // back to the main stream iCode = ics->swapStream(iCode); - setLabel(ics->whileBodyLabel); // mark the start of the while block + setLabel(ics->whileBody); // mark the start of the while block } void ICodeGenerator::endWhileStatement() { // recover the while stream - WhileCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == While_State); - stitcher.pop(); + WhileCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == While_state); + stitcher.pop_back(); // mark the start of the condition code // and re-attach it to the main stream - setLabel(ics->whileConditionLabel); + setLabel(ics->whileCondition); ics->mergeStream(iCode, labels); + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); - delete ics->its_iCode; delete ics; resetTopRegister(); } +/***********************************************************************************************/ + +void ICodeGenerator::beginDoStatement(const SourcePosition &pos) +{ + resetTopRegister(); + + // mark the top of the loop body + // and reserve a label for the condition + int32 doBlock = getLabel(); + int32 doCondition = getLabel(); + setLabel(doBlock); + + stitcher.push_back(new DoCodeState(doBlock, doCondition, this)); + + iCode = new InstructionStream(); +} + +void ICodeGenerator::endDoStatement() +{ + DoCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Do_state); + + // mark the start of the do conditional + setLabel(ics->doCondition); + + resetTopRegister(); +} + +void ICodeGenerator::endDoExpression(Register condition) +{ + DoCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Do_state); + stitcher.pop_back(); + + // add branch to top of do block + branchConditional(ics->doBody, condition); + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); + + delete ics; + + resetTopRegister(); +} + +/***********************************************************************************************/ + +void ICodeGenerator::beginSwitchStatement(const SourcePosition &pos, Register expression) +{ + // stash the control expression value + resetTopRegister(); + op(MOVE_TO, getRegister(), expression); + // build an instruction stream for the case statements, the case + // expressions are generated into the main stream directly, the + // case statements are then added back in afterwards. + InstructionStream *x = new InstructionStream(); + SwitchCodeState *ics = new SwitchCodeState(expression, this); + ics->swapStream(x); + stitcher.push_back(ics); +} + +void ICodeGenerator::endCaseCondition(Register expression) +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + + int32 caseLabel = getLabel(); + Register r = op(COMPARE, expression, ics->controlExpression); + branchConditional(caseLabel, r); + + setLabel(ics->its_iCode, caseLabel); // mark the case in the Case Statement stream + resetTopRegister(); +} + +void ICodeGenerator::beginCaseStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + iCode = ics->swapStream(iCode); // switch to Case Statement stream +} + +void ICodeGenerator::endCaseStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); // do more to guarantee correct blocking? + iCode = ics->swapStream(iCode); // switch back to Case Conditional stream + resetTopRegister(); +} + +void ICodeGenerator::beginDefaultStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + ASSERT(ics->defaultLabel == -1); + ics->defaultLabel = getLabel(); + setLabel(ics->its_iCode, ics->defaultLabel); + iCode = ics->swapStream(iCode); // switch to Case Statement stream +} + +void ICodeGenerator::endDefaultStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + ASSERT(ics->defaultLabel != -1); // do more to guarantee correct blocking? + iCode = ics->swapStream(iCode); // switch to Case Statement stream + resetTopRegister(); +} + +void ICodeGenerator::endSwitchStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + stitcher.pop_back(); + + // ground out the case chain at the default block or fall thru + // to the break label + if (ics->defaultLabel != -1) + branch(ics->defaultLabel); + else { + if (ics->breakLabel == -1) + ics->breakLabel = getLabel(); + branch(ics->breakLabel); + } + + // dump all the case statements into the main stream + ics->mergeStream(iCode, labels); + + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); + + delete ics; +} + /***********************************************************************************************/ @@ -222,7 +368,7 @@ void ICodeGenerator::beginIfStatement(const SourcePosition &pos, Register condit { int32 elseLabel = getLabel(); - stitcher.push(new IfCodeState(elseLabel, -1)); + stitcher.push_back(new IfCodeState(elseLabel, -1, this)); Register notCond = op(NOT, condition); branchConditional(elseLabel, notCond); @@ -232,8 +378,8 @@ void ICodeGenerator::beginIfStatement(const SourcePosition &pos, Register condit void ICodeGenerator::beginElseStatement(bool hasElse) { - IfCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == If_State); + IfCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == If_state); if (hasElse) { int32 beyondElse = getLabel(); @@ -246,35 +392,54 @@ void ICodeGenerator::beginElseStatement(bool hasElse) void ICodeGenerator::endIfStatement() { - IfCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == If_State); + IfCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == If_state); + stitcher.pop_back(); if (ics->beyondElse != -1) { // had an else setLabel(ics->beyondElse); // the beyond else label } - stitcher.pop(); + + delete ics; resetTopRegister(); } +/***********************************************************************************************/ + +void ICodeGenerator::breakStatement() +{ + for (std::vector::reverse_iterator p = stitcher.rbegin(); p != stitcher.rend(); p++) { + +// this is NOT going to stay this way + + if (((*p)->stateKind == While_state) + || ((*p)->stateKind == Do_state) + || ((*p)->stateKind == For_state) + || ((*p)->stateKind == Switch_state)) { + if ((*p)->breakLabel == -1) + (*p)->breakLabel = getLabel(); + branch((*p)->breakLabel); + return; + } + } + NOT_REACHED("no break target available"); +} /***********************************************************************************************/ char *opcodeName[] = { + "move_to", "load_var", "save_var", - "load_imm", - "load_name", "save_name", - "get_prop", "set_prop", - "add", + "compare", "not", - "branch", "branch_cond", }; @@ -330,11 +495,13 @@ ostream &ICodeGenerator::print(ostream &s) } break; case ADD : + case COMPARE : { Instruction_3 *t = static_cast * >(instr); s << "R" << t->itsOperand1 << ", R" << t->itsOperand2 << ", R" << t->itsOperand3; } break; + case MOVE_TO : case NOT : { Instruction_3 *t = static_cast * >(instr); diff --git a/js/js2/icodegenerator.h b/js/js2/icodegenerator.h index 896e8990799c..ba173b6611ec 100644 --- a/js/js2/icodegenerator.h +++ b/js/js2/icodegenerator.h @@ -34,6 +34,8 @@ namespace JavaScript { enum ICodeOp { // Operand1 Operand2 Operand3 + MOVE_TO, // Source Register Destination Register + LOAD_VAR, // index of frame slot Destination Register SAVE_VAR, // index of frame slot Source Register @@ -46,6 +48,7 @@ namespace JavaScript { SET_PROP, // StringAtom & Base Register Source Register ADD, // Source Register 1 Source Register 2 Destination Register + COMPARE, // Source Register 1 Source Register 2 Destination Register NOT, // Source Register Destination Register BRANCH, // Target label @@ -104,46 +107,66 @@ namespace JavaScript { /****************************************************************/ /****************************************************************/ - enum StateKind { While_State, If_State }; + class ICodeGenerator; // forward declaration - class ICodeState { + enum StateKind { While_state, If_state, Do_state, Switch_state, For_state }; + + class ICodeState { public : - ICodeState(StateKind kind) : stateKind(kind) { } + ICodeState(StateKind kind, ICodeGenerator *icg); // inline below StateKind stateKind; + int32 breakLabel; + int32 continueLabel; + int32 registerBase; }; // an ICodeState that handles switching to a new InstructionStream // and then re-combining the streams later class MultiPathICodeState : public ICodeState { public: - MultiPathICodeState(StateKind kind,InstructionStream *iCode, int32 topLabel) - : ICodeState(kind), its_iCode(iCode), itsTopLabel(topLabel) {} + MultiPathICodeState(StateKind kind, ICodeGenerator *icg); // inline below + virtual ~MultiPathICodeState() { delete its_iCode; } InstructionStream *swapStream(InstructionStream *iCode) { InstructionStream *t = its_iCode; its_iCode = iCode; return t; } InstructionStream *its_iCode; - int32 itsTopLabel; // set to the highest label allocated when this stream - // was created. If that value changes, this stream may - // contain labels that will need to be adjusted when - // the streams are merged. + void mergeStream(InstructionStream *mainStream, LabelList &labels); }; class WhileCodeState : public MultiPathICodeState { public: - WhileCodeState(InstructionStream *iCode, int32 topLabel, int32 a, int32 b) - : MultiPathICodeState(While_State, iCode, topLabel), whileConditionLabel(a), whileBodyLabel(b) { } - int32 whileConditionLabel; - int32 whileBodyLabel; + WhileCodeState(int32 conditionLabel, int32 bodyLabel, ICodeGenerator *icg) + : MultiPathICodeState(While_state, icg), whileCondition(conditionLabel), whileBody(bodyLabel) { } + int32 whileCondition; + int32 whileBody; }; class IfCodeState : public ICodeState { public: - IfCodeState(int32 a, int32 b) : ICodeState(If_State), elseLabel(a), beyondElse(b) { } + IfCodeState(int32 a, int32 b, ICodeGenerator *icg) + : ICodeState(If_state, icg), elseLabel(a), beyondElse(b) { } int32 elseLabel; int32 beyondElse; }; + class DoCodeState : public ICodeState { + public: + DoCodeState(int32 bodyLabel, int32 conditionLabel, ICodeGenerator *icg) + : ICodeState(Do_state, icg), doBody(bodyLabel), doCondition(conditionLabel) { } + int32 doBody; + int32 doCondition; + }; + + class SwitchCodeState : public MultiPathICodeState { + public: + SwitchCodeState(Register control, ICodeGenerator *icg) + : MultiPathICodeState(Switch_state, icg), controlExpression(control), defaultLabel(-1) { } + + Register controlExpression; + int32 defaultLabel; + }; + /****************************************************************/ // An ICodeGenerator provides the interface between the parser and the interpreter. @@ -157,20 +180,21 @@ namespace JavaScript { LabelList labels; - std::stack stitcher; + std::vector stitcher; Register topRegister; Register getRegister() { return topRegister++; } - void resetTopRegister() { topRegister = 0; } + void resetTopRegister() { topRegister = stitcher.empty() ? 0 : stitcher.back()->registerBase; } int32 getLabel(); void setLabel(int32 label); + void setLabel(InstructionStream *stream, int32 label); void branch(int32 label); void branchConditional(int32 label, Register condition); public: - ICodeGenerator() { iCode = new InstructionStream(); } + ICodeGenerator() : topRegister(0) { iCode = new InstructionStream(); } InstructionStream *complete(); @@ -187,8 +211,8 @@ namespace JavaScript { Register loadName(StringAtom &name); Register getProperty(StringAtom &name, Register base); - - void beginStatement(const SourcePosition &pos) { resetTopRegister(); } + Register getRegisterBase() { return topRegister; } + InstructionStream *get_iCode() { return iCode; } // Rather than have the ICG client maniplate labels and branches, it @@ -197,13 +221,16 @@ namespace JavaScript { // in the order listed for each construct, (internal error otherwise). // The ICG will enforce correct nesting and closing. + // expression statements + void beginStatement(const SourcePosition &pos) { resetTopRegister(); } + void beginWhileStatement(const SourcePosition &pos); void endWhileExpression(Register condition); void endWhileStatement(); - void beginDoStatement(); + void beginDoStatement(const SourcePosition &pos); void endDoStatement(); void endDoExpression(Register condition); @@ -220,23 +247,27 @@ namespace JavaScript { void endForStatement(); - void beginSwitchStatement(Register expression); + void beginSwitchStatement(const SourcePosition &pos, Register expression); - // sequences of the next three follow for each case clause - void beginCaseStatement(); void endCaseCondition(Register expression); - void endCaseStatement(); // corresponds to a break and may be omitted + void beginCaseStatement(); + void endCaseStatement(); + // optionally void beginDefaultStatement(); - void endDefaultStatement(); // the break for the default clause, may be omitted + void endDefaultStatement(); void endSwitchStatement(); - void labelStatement(const StringAtom &label); // adds to label set for next statement, - // removed when that statement is finished + void labelStatement(const StringAtom &label); // adds to label set for next statement, + // removed when that statement is finished + void continueStatement(); + void breakStatement(); + void continueStatement(const StringAtom &label); + void breakStatement(const StringAtom &label); void throwStatement(Register expression); @@ -253,5 +284,11 @@ namespace JavaScript { ostream &operator<<(ostream &s, ICodeGenerator &i); ostream &operator<<(ostream &s, StringAtom &str); + + inline ICodeState::ICodeState(StateKind kind, ICodeGenerator *icg) + : stateKind(kind), breakLabel(-1), continueLabel(-1), registerBase(icg->getRegisterBase()) { } + + inline MultiPathICodeState::MultiPathICodeState(StateKind kind, ICodeGenerator *icg) + : ICodeState(kind, icg), its_iCode(icg->get_iCode()) {} } #endif \ No newline at end of file diff --git a/js/js2/js2.cpp b/js/js2/js2.cpp index 6a76a31cb6b5..baa40c2ae1f9 100644 --- a/js/js2/js2.cpp +++ b/js/js2/js2.cpp @@ -371,6 +371,37 @@ void testICG(World &world) icg.beginElseStatement(false); icg.endIfStatement(); + + // switch (i) { case 3: case 4: j = 4; break; case 5: j = 5; break; default : j = 6; } + r1 = icg.loadVariable(0); + icg.beginSwitchStatement(pos, r1); + // case 3, note empty case statement (?necessary???) + icg.endCaseCondition(icg.loadImmediate(3)); + icg.beginCaseStatement(); + icg.endCaseStatement(); + // case 4 + icg.endCaseCondition(icg.loadImmediate(4)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(4)); + icg.breakStatement(); + icg.endCaseStatement(); + // case 5 + icg.endCaseCondition(icg.loadImmediate(5)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(5)); + icg.breakStatement(); + icg.endCaseStatement(); + // default + icg.beginDefaultStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(6)); + icg.endDefaultStatement(); + icg.endSwitchStatement(); + + InstructionStream *iCode = icg.complete(); + std::cout << icg; } @@ -380,7 +411,7 @@ int main(int argc, char **argv) initConsole("\pJavaScript Shell", "Welcome to the js2 shell.\n", argc, argv); #endif World world; -#if 0 +#if 1 testICG(world); #else readEvalPrint(std::cin, world); diff --git a/js2/src/icodegenerator.cpp b/js2/src/icodegenerator.cpp index 5b719dd88e5e..100b736a019b 100644 --- a/js2/src/icodegenerator.cpp +++ b/js2/src/icodegenerator.cpp @@ -144,24 +144,37 @@ void ICodeGenerator::setLabel(int32 label) l->itsOffset = iCode->size(); } +void ICodeGenerator::setLabel(InstructionStream *stream, int32 label) +{ + Label* l; +#ifdef __GNUC__ + // libg++'s vector class doesn't have at(): + if (label >= labels.size()) + throw std::out_of_range("label out of range"); + l = labels[label]; +#else + l = labels.at(label); +#endif + l->itsBase = stream; + l->itsOffset = stream->size(); +} + /***********************************************************************************************/ void MultiPathICodeState::mergeStream(InstructionStream *mainStream, LabelList &labels) { - if (itsTopLabel < labels.size()) { - // labels (might) have been allocated in this stream - // we need to adjust their position relative to the - // size of the stream we're joining - for (LabelList::iterator i = labels.begin() + itsTopLabel; i != labels.end(); i++) { - if ((*i)->itsBase == its_iCode) { - (*i)->itsBase = mainStream; - (*i)->itsOffset += mainStream->size(); - } + // change InstructionStream to be a class that also remembers + // if it contains any labels (maybe even remembers the labels + // themselves?) in order to avoid running this loop unnecessarily. + for (LabelList::iterator i = labels.begin(); i != labels.end(); i++) { + if ((*i)->itsBase == its_iCode) { + (*i)->itsBase = mainStream; + (*i)->itsOffset += mainStream->size(); } } - for (InstructionIterator i = its_iCode->begin(); i != its_iCode->end(); i++) - mainStream->push_back(*i); + for (InstructionIterator ii = its_iCode->begin(); ii != its_iCode->end(); ii++) + mainStream->push_back(*ii); } @@ -178,43 +191,176 @@ void ICodeGenerator::beginWhileStatement(const SourcePosition &pos) branch(whileConditionTop); // save off the current stream while we gen code for the condition - stitcher.push(new WhileCodeState(iCode, labels.size(), whileConditionTop, whileBlockStart)); + stitcher.push_back(new WhileCodeState(whileConditionTop, whileBlockStart, this)); iCode = new InstructionStream(); } void ICodeGenerator::endWhileExpression(Register condition) { - WhileCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == While_State); + WhileCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == While_state); - branchConditional(ics->whileBodyLabel, condition); + branchConditional(ics->whileBody, condition); resetTopRegister(); // stash away the condition expression and switch // back to the main stream iCode = ics->swapStream(iCode); - setLabel(ics->whileBodyLabel); // mark the start of the while block + setLabel(ics->whileBody); // mark the start of the while block } void ICodeGenerator::endWhileStatement() { // recover the while stream - WhileCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == While_State); - stitcher.pop(); + WhileCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == While_state); + stitcher.pop_back(); // mark the start of the condition code // and re-attach it to the main stream - setLabel(ics->whileConditionLabel); + setLabel(ics->whileCondition); ics->mergeStream(iCode, labels); + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); - delete ics->its_iCode; delete ics; resetTopRegister(); } +/***********************************************************************************************/ + +void ICodeGenerator::beginDoStatement(const SourcePosition &pos) +{ + resetTopRegister(); + + // mark the top of the loop body + // and reserve a label for the condition + int32 doBlock = getLabel(); + int32 doCondition = getLabel(); + setLabel(doBlock); + + stitcher.push_back(new DoCodeState(doBlock, doCondition, this)); + + iCode = new InstructionStream(); +} + +void ICodeGenerator::endDoStatement() +{ + DoCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Do_state); + + // mark the start of the do conditional + setLabel(ics->doCondition); + + resetTopRegister(); +} + +void ICodeGenerator::endDoExpression(Register condition) +{ + DoCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Do_state); + stitcher.pop_back(); + + // add branch to top of do block + branchConditional(ics->doBody, condition); + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); + + delete ics; + + resetTopRegister(); +} + +/***********************************************************************************************/ + +void ICodeGenerator::beginSwitchStatement(const SourcePosition &pos, Register expression) +{ + // stash the control expression value + resetTopRegister(); + op(MOVE_TO, getRegister(), expression); + // build an instruction stream for the case statements, the case + // expressions are generated into the main stream directly, the + // case statements are then added back in afterwards. + InstructionStream *x = new InstructionStream(); + SwitchCodeState *ics = new SwitchCodeState(expression, this); + ics->swapStream(x); + stitcher.push_back(ics); +} + +void ICodeGenerator::endCaseCondition(Register expression) +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + + int32 caseLabel = getLabel(); + Register r = op(COMPARE, expression, ics->controlExpression); + branchConditional(caseLabel, r); + + setLabel(ics->its_iCode, caseLabel); // mark the case in the Case Statement stream + resetTopRegister(); +} + +void ICodeGenerator::beginCaseStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + iCode = ics->swapStream(iCode); // switch to Case Statement stream +} + +void ICodeGenerator::endCaseStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); // do more to guarantee correct blocking? + iCode = ics->swapStream(iCode); // switch back to Case Conditional stream + resetTopRegister(); +} + +void ICodeGenerator::beginDefaultStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + ASSERT(ics->defaultLabel == -1); + ics->defaultLabel = getLabel(); + setLabel(ics->its_iCode, ics->defaultLabel); + iCode = ics->swapStream(iCode); // switch to Case Statement stream +} + +void ICodeGenerator::endDefaultStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + ASSERT(ics->defaultLabel != -1); // do more to guarantee correct blocking? + iCode = ics->swapStream(iCode); // switch to Case Statement stream + resetTopRegister(); +} + +void ICodeGenerator::endSwitchStatement() +{ + SwitchCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == Switch_state); + stitcher.pop_back(); + + // ground out the case chain at the default block or fall thru + // to the break label + if (ics->defaultLabel != -1) + branch(ics->defaultLabel); + else { + if (ics->breakLabel == -1) + ics->breakLabel = getLabel(); + branch(ics->breakLabel); + } + + // dump all the case statements into the main stream + ics->mergeStream(iCode, labels); + + if (ics->breakLabel != -1) + setLabel(ics->breakLabel); + + delete ics; +} + /***********************************************************************************************/ @@ -222,7 +368,7 @@ void ICodeGenerator::beginIfStatement(const SourcePosition &pos, Register condit { int32 elseLabel = getLabel(); - stitcher.push(new IfCodeState(elseLabel, -1)); + stitcher.push_back(new IfCodeState(elseLabel, -1, this)); Register notCond = op(NOT, condition); branchConditional(elseLabel, notCond); @@ -232,8 +378,8 @@ void ICodeGenerator::beginIfStatement(const SourcePosition &pos, Register condit void ICodeGenerator::beginElseStatement(bool hasElse) { - IfCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == If_State); + IfCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == If_state); if (hasElse) { int32 beyondElse = getLabel(); @@ -246,35 +392,54 @@ void ICodeGenerator::beginElseStatement(bool hasElse) void ICodeGenerator::endIfStatement() { - IfCodeState *ics = static_cast(stitcher.top()); - ASSERT(ics->stateKind == If_State); + IfCodeState *ics = static_cast(stitcher.back()); + ASSERT(ics->stateKind == If_state); + stitcher.pop_back(); if (ics->beyondElse != -1) { // had an else setLabel(ics->beyondElse); // the beyond else label } - stitcher.pop(); + + delete ics; resetTopRegister(); } +/***********************************************************************************************/ + +void ICodeGenerator::breakStatement() +{ + for (std::vector::reverse_iterator p = stitcher.rbegin(); p != stitcher.rend(); p++) { + +// this is NOT going to stay this way + + if (((*p)->stateKind == While_state) + || ((*p)->stateKind == Do_state) + || ((*p)->stateKind == For_state) + || ((*p)->stateKind == Switch_state)) { + if ((*p)->breakLabel == -1) + (*p)->breakLabel = getLabel(); + branch((*p)->breakLabel); + return; + } + } + NOT_REACHED("no break target available"); +} /***********************************************************************************************/ char *opcodeName[] = { + "move_to", "load_var", "save_var", - "load_imm", - "load_name", "save_name", - "get_prop", "set_prop", - "add", + "compare", "not", - "branch", "branch_cond", }; @@ -330,11 +495,13 @@ ostream &ICodeGenerator::print(ostream &s) } break; case ADD : + case COMPARE : { Instruction_3 *t = static_cast * >(instr); s << "R" << t->itsOperand1 << ", R" << t->itsOperand2 << ", R" << t->itsOperand3; } break; + case MOVE_TO : case NOT : { Instruction_3 *t = static_cast * >(instr); diff --git a/js2/src/icodegenerator.h b/js2/src/icodegenerator.h index 896e8990799c..ba173b6611ec 100644 --- a/js2/src/icodegenerator.h +++ b/js2/src/icodegenerator.h @@ -34,6 +34,8 @@ namespace JavaScript { enum ICodeOp { // Operand1 Operand2 Operand3 + MOVE_TO, // Source Register Destination Register + LOAD_VAR, // index of frame slot Destination Register SAVE_VAR, // index of frame slot Source Register @@ -46,6 +48,7 @@ namespace JavaScript { SET_PROP, // StringAtom & Base Register Source Register ADD, // Source Register 1 Source Register 2 Destination Register + COMPARE, // Source Register 1 Source Register 2 Destination Register NOT, // Source Register Destination Register BRANCH, // Target label @@ -104,46 +107,66 @@ namespace JavaScript { /****************************************************************/ /****************************************************************/ - enum StateKind { While_State, If_State }; + class ICodeGenerator; // forward declaration - class ICodeState { + enum StateKind { While_state, If_state, Do_state, Switch_state, For_state }; + + class ICodeState { public : - ICodeState(StateKind kind) : stateKind(kind) { } + ICodeState(StateKind kind, ICodeGenerator *icg); // inline below StateKind stateKind; + int32 breakLabel; + int32 continueLabel; + int32 registerBase; }; // an ICodeState that handles switching to a new InstructionStream // and then re-combining the streams later class MultiPathICodeState : public ICodeState { public: - MultiPathICodeState(StateKind kind,InstructionStream *iCode, int32 topLabel) - : ICodeState(kind), its_iCode(iCode), itsTopLabel(topLabel) {} + MultiPathICodeState(StateKind kind, ICodeGenerator *icg); // inline below + virtual ~MultiPathICodeState() { delete its_iCode; } InstructionStream *swapStream(InstructionStream *iCode) { InstructionStream *t = its_iCode; its_iCode = iCode; return t; } InstructionStream *its_iCode; - int32 itsTopLabel; // set to the highest label allocated when this stream - // was created. If that value changes, this stream may - // contain labels that will need to be adjusted when - // the streams are merged. + void mergeStream(InstructionStream *mainStream, LabelList &labels); }; class WhileCodeState : public MultiPathICodeState { public: - WhileCodeState(InstructionStream *iCode, int32 topLabel, int32 a, int32 b) - : MultiPathICodeState(While_State, iCode, topLabel), whileConditionLabel(a), whileBodyLabel(b) { } - int32 whileConditionLabel; - int32 whileBodyLabel; + WhileCodeState(int32 conditionLabel, int32 bodyLabel, ICodeGenerator *icg) + : MultiPathICodeState(While_state, icg), whileCondition(conditionLabel), whileBody(bodyLabel) { } + int32 whileCondition; + int32 whileBody; }; class IfCodeState : public ICodeState { public: - IfCodeState(int32 a, int32 b) : ICodeState(If_State), elseLabel(a), beyondElse(b) { } + IfCodeState(int32 a, int32 b, ICodeGenerator *icg) + : ICodeState(If_state, icg), elseLabel(a), beyondElse(b) { } int32 elseLabel; int32 beyondElse; }; + class DoCodeState : public ICodeState { + public: + DoCodeState(int32 bodyLabel, int32 conditionLabel, ICodeGenerator *icg) + : ICodeState(Do_state, icg), doBody(bodyLabel), doCondition(conditionLabel) { } + int32 doBody; + int32 doCondition; + }; + + class SwitchCodeState : public MultiPathICodeState { + public: + SwitchCodeState(Register control, ICodeGenerator *icg) + : MultiPathICodeState(Switch_state, icg), controlExpression(control), defaultLabel(-1) { } + + Register controlExpression; + int32 defaultLabel; + }; + /****************************************************************/ // An ICodeGenerator provides the interface between the parser and the interpreter. @@ -157,20 +180,21 @@ namespace JavaScript { LabelList labels; - std::stack stitcher; + std::vector stitcher; Register topRegister; Register getRegister() { return topRegister++; } - void resetTopRegister() { topRegister = 0; } + void resetTopRegister() { topRegister = stitcher.empty() ? 0 : stitcher.back()->registerBase; } int32 getLabel(); void setLabel(int32 label); + void setLabel(InstructionStream *stream, int32 label); void branch(int32 label); void branchConditional(int32 label, Register condition); public: - ICodeGenerator() { iCode = new InstructionStream(); } + ICodeGenerator() : topRegister(0) { iCode = new InstructionStream(); } InstructionStream *complete(); @@ -187,8 +211,8 @@ namespace JavaScript { Register loadName(StringAtom &name); Register getProperty(StringAtom &name, Register base); - - void beginStatement(const SourcePosition &pos) { resetTopRegister(); } + Register getRegisterBase() { return topRegister; } + InstructionStream *get_iCode() { return iCode; } // Rather than have the ICG client maniplate labels and branches, it @@ -197,13 +221,16 @@ namespace JavaScript { // in the order listed for each construct, (internal error otherwise). // The ICG will enforce correct nesting and closing. + // expression statements + void beginStatement(const SourcePosition &pos) { resetTopRegister(); } + void beginWhileStatement(const SourcePosition &pos); void endWhileExpression(Register condition); void endWhileStatement(); - void beginDoStatement(); + void beginDoStatement(const SourcePosition &pos); void endDoStatement(); void endDoExpression(Register condition); @@ -220,23 +247,27 @@ namespace JavaScript { void endForStatement(); - void beginSwitchStatement(Register expression); + void beginSwitchStatement(const SourcePosition &pos, Register expression); - // sequences of the next three follow for each case clause - void beginCaseStatement(); void endCaseCondition(Register expression); - void endCaseStatement(); // corresponds to a break and may be omitted + void beginCaseStatement(); + void endCaseStatement(); + // optionally void beginDefaultStatement(); - void endDefaultStatement(); // the break for the default clause, may be omitted + void endDefaultStatement(); void endSwitchStatement(); - void labelStatement(const StringAtom &label); // adds to label set for next statement, - // removed when that statement is finished + void labelStatement(const StringAtom &label); // adds to label set for next statement, + // removed when that statement is finished + void continueStatement(); + void breakStatement(); + void continueStatement(const StringAtom &label); + void breakStatement(const StringAtom &label); void throwStatement(Register expression); @@ -253,5 +284,11 @@ namespace JavaScript { ostream &operator<<(ostream &s, ICodeGenerator &i); ostream &operator<<(ostream &s, StringAtom &str); + + inline ICodeState::ICodeState(StateKind kind, ICodeGenerator *icg) + : stateKind(kind), breakLabel(-1), continueLabel(-1), registerBase(icg->getRegisterBase()) { } + + inline MultiPathICodeState::MultiPathICodeState(StateKind kind, ICodeGenerator *icg) + : ICodeState(kind, icg), its_iCode(icg->get_iCode()) {} } #endif \ No newline at end of file diff --git a/js2/tests/cpp/js2_shell.cpp b/js2/tests/cpp/js2_shell.cpp index 6a76a31cb6b5..baa40c2ae1f9 100644 --- a/js2/tests/cpp/js2_shell.cpp +++ b/js2/tests/cpp/js2_shell.cpp @@ -371,6 +371,37 @@ void testICG(World &world) icg.beginElseStatement(false); icg.endIfStatement(); + + // switch (i) { case 3: case 4: j = 4; break; case 5: j = 5; break; default : j = 6; } + r1 = icg.loadVariable(0); + icg.beginSwitchStatement(pos, r1); + // case 3, note empty case statement (?necessary???) + icg.endCaseCondition(icg.loadImmediate(3)); + icg.beginCaseStatement(); + icg.endCaseStatement(); + // case 4 + icg.endCaseCondition(icg.loadImmediate(4)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(4)); + icg.breakStatement(); + icg.endCaseStatement(); + // case 5 + icg.endCaseCondition(icg.loadImmediate(5)); + icg.beginCaseStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(5)); + icg.breakStatement(); + icg.endCaseStatement(); + // default + icg.beginDefaultStatement(); + icg.beginStatement(pos); + icg.saveVariable(1, icg.loadImmediate(6)); + icg.endDefaultStatement(); + icg.endSwitchStatement(); + + InstructionStream *iCode = icg.complete(); + std::cout << icg; } @@ -380,7 +411,7 @@ int main(int argc, char **argv) initConsole("\pJavaScript Shell", "Welcome to the js2 shell.\n", argc, argv); #endif World world; -#if 0 +#if 1 testICG(world); #else readEvalPrint(std::cin, world);