Re-add LIFO stack optimizations.
authorDavid Anderson <dvander@alliedmods.net>
Sat Dec 22 18:22:14 2012 -0800 (2012-12-22)
changeset 230fb3cd08bc7fb
parent 229 ce072f38628c
child 231 6030e0b46fe2
Re-add LIFO stack optimizations.
src/Code.h
src/Interpreter.cpp
src/Opcodes.cpp
src/Opcodes.tbl
src/Stack.cpp
src/Stack.h
src/compiler/BytecodeEmitter.cpp
src/compiler/BytecodeEmitter.h
src/compiler/SemanticAnalysis.cpp
src/compiler/SemanticAnalysis.h
tests/manual/lifo.sp
tests/prototype/lifo.sp
     1.1 --- a/src/Code.h	Sat Dec 22 16:42:20 2012 -0800
     1.2 +++ b/src/Code.h	Sat Dec 22 18:22:14 2012 -0800
     1.3 @@ -44,6 +44,7 @@
     1.4      Barriered<ByteArray> sourcemap_;
     1.5      unsigned maxStackDepth_;
     1.6      unsigned lineNumber_;
     1.7 +    unsigned maxLoopDepth_;
     1.8  
     1.9    public:
    1.10      static inline size_t BytesPerSafepoint(size_t stackDepth) {
    1.11 @@ -53,7 +54,7 @@
    1.12    public:
    1.13      static Code *New(Zone *zone, Handle<ByteArray> bytecode, Handle<FixedArray> locals,
    1.14                       Handle<FixedArray> objects, Handle<FixedArray> strings, unsigned maxStackDepth,
    1.15 -                     Handle<ByteArray> safepoints, Handle<ByteArray> sourcemap);
    1.16 +                     unsigned maxLoopDepth, Handle<ByteArray> safepoints, Handle<ByteArray> sourcemap);
    1.17  
    1.18      Module *module() {
    1.19          return module_;
    1.20 @@ -73,6 +74,9 @@
    1.21      unsigned maxStackDepth() {
    1.22          return maxStackDepth_;
    1.23      }
    1.24 +    unsigned maxLoopDepth() {
    1.25 +        return maxLoopDepth_;
    1.26 +    }
    1.27      Map *map(size_t index) {
    1.28          return Map::cast(objects_->at(index));
    1.29      }
     2.1 --- a/src/Interpreter.cpp	Sat Dec 22 16:42:20 2012 -0800
     2.2 +++ b/src/Interpreter.cpp	Sat Dec 22 18:22:14 2012 -0800
     2.3 @@ -446,8 +446,18 @@
     2.4  }
     2.5  
     2.6  case OP_JUMP:
     2.7 +{
     2.8      top.pc += READ_INT32(top.pc);
     2.9      continue;
    2.10 +}
    2.11 +
    2.12 +case OP_BREAK:
    2.13 +case OP_CONTINUE:
    2.14 +{
    2.15 +    top.fp->leaveLoop(zone);
    2.16 +    top.pc += READ_INT32(top.pc);
    2.17 +    continue;
    2.18 +}
    2.19  
    2.20  case OP_GETLOCAL:
    2.21  {
    2.22 @@ -1065,6 +1075,18 @@
    2.23      break;
    2.24  }
    2.25  
    2.26 +case OP_ENTERLOOP:
    2.27 +{
    2.28 +    top.fp->enterLoop(zone);
    2.29 +    break;
    2.30 +}
    2.31 +
    2.32 +case OP_LEAVELOOP:
    2.33 +{
    2.34 +    top.fp->leaveLoop(zone);
    2.35 +    break;
    2.36 +}
    2.37 +
    2.38  default:
    2.39      assert(false);
    2.40      return false;
     3.1 --- a/src/Opcodes.cpp	Sat Dec 22 16:42:20 2012 -0800
     3.2 +++ b/src/Opcodes.cpp	Sat Dec 22 18:22:14 2012 -0800
     3.3 @@ -40,7 +40,7 @@
     3.4  Code *
     3.5  Code::New(Zone *zone, Handle<ByteArray> bytecode, Handle<FixedArray> locals,
     3.6            Handle<FixedArray> objects, Handle<FixedArray> strings, unsigned maxStackDepth,
     3.7 -          Handle<ByteArray> safepoints, Handle<ByteArray> sourcemap)
     3.8 +          unsigned maxLoopDepth, Handle<ByteArray> safepoints, Handle<ByteArray> sourcemap)
     3.9  {
    3.10      Local<Code> code(zone, Code::cast(zone->allocate(MapKind_Code, sizeof(Code), Heap::Tenure_Old)));
    3.11      if (!code)
    3.12 @@ -55,6 +55,7 @@
    3.13      code->sourcemap_ = sourcemap;
    3.14      code->lineNumber_ = 0;
    3.15      code->strings_ = strings;
    3.16 +    code->maxLoopDepth_ = maxLoopDepth;
    3.17      return code;
    3.18  }
    3.19  
    3.20 @@ -160,6 +161,7 @@
    3.21            case OP_MUL_F:
    3.22            case OP_DIV_F:
    3.23            case OP_CVT_I2F:
    3.24 +          case OP_CVT_I2B:
    3.25            case OP_POP:
    3.26            case OP_GETELEM:
    3.27            case OP_SETELEM:
    3.28 @@ -207,6 +209,12 @@
    3.29            case OP_FALSE:
    3.30            case OP_SIZEOF:
    3.31            case OP_PUBLIC:
    3.32 +          case OP_LEAVELOOP:
    3.33 +            break;
    3.34 +
    3.35 +          case OP_ENTERLOOP:
    3.36 +            if (pc[1])
    3.37 +              fprintf(fp, "; lifo region");
    3.38              break;
    3.39  
    3.40            case OP_STOP:
    3.41 @@ -224,6 +232,8 @@
    3.42            case OP_JT:
    3.43            case OP_JF:
    3.44            case OP_JUMP:
    3.45 +          case OP_CONTINUE:
    3.46 +          case OP_BREAK:
    3.47              fprintf(fp, "%u (%+d)", unsigned((pc + READ_INT32(pc)) - code->start()), READ_INT32(pc));
    3.48              break;
    3.49  
     4.1 --- a/src/Opcodes.tbl	Sat Dec 22 16:42:20 2012 -0800
     4.2 +++ b/src/Opcodes.tbl	Sat Dec 22 18:22:14 2012 -0800
     4.3 @@ -43,6 +43,10 @@
     4.4  // Unconditional jump.
     4.5  OPDEF(OP_JUMP,          "jump",         5,      0,  0)
     4.6  
     4.7 +// Aliases for JUMP, that automatically leave LR regions.
     4.8 +OPDEF(OP_BREAK,         "break",        5,      0,  0)
     4.9 +OPDEF(OP_CONTINUE,      "continue",     5,      0,  0)
    4.10 +
    4.11  // Gets or sets the value of a local variable onto the stack. The local
    4.12  // variable is given by an immediate index.
    4.13  OPDEF(OP_GETLOCAL,      "getlocal",     5,      0,  1)
    4.14 @@ -243,4 +247,12 @@
    4.15  OPDEF(OP_SIZEOF,        "sizeof",       1,      1, 1)
    4.16  
    4.17  // Registers a function as a publicly exported name.
    4.18 -OPDEF(OP_PUBLIC,        "public",       1,      1, 0)
    4.19 \ No newline at end of file
    4.20 +OPDEF(OP_PUBLIC,        "public",       1,      1, 0)
    4.21 +
    4.22 +// Enter and leave loop bodies. This is to track positions in the LIFO stack.
    4.23 +// This is only used for the outermost scope inside loop bodies. BREAK and
    4.24 +// CONTINUE implicitly perform a LEAVELOOP.
    4.25 +//
    4.26 +// If the second byte to ENTERLOOP is 0, then no LIFO region is needed.
    4.27 +OPDEF(OP_ENTERLOOP,     "enterloop",    2,      0, 0)
    4.28 +OPDEF(OP_LEAVELOOP,     "leaveloop",    1,      0, 0)
    4.29 \ No newline at end of file
     5.1 --- a/src/Stack.cpp	Sat Dec 22 16:42:20 2012 -0800
     5.2 +++ b/src/Stack.cpp	Sat Dec 22 18:22:14 2012 -0800
     5.3 @@ -67,7 +67,9 @@
     5.4  static inline size_t
     5.5  SizeForCodeFrame(Code *code)
     5.6  {
     5.7 -    return sizeof(Frame) + sizeof(LocalSlot) * (code->nlocals() + code->maxStackDepth());
     5.8 +    return sizeof(Frame) + 
     5.9 +           sizeof(void *) * (code->maxLoopDepth()) +
    5.10 +           sizeof(LocalSlot) * (code->nlocals() + code->maxStackDepth());
    5.11  }
    5.12  
    5.13  bool
    5.14 @@ -199,6 +201,22 @@
    5.15      top_.fp = top_.fp->prev();
    5.16  }
    5.17  
    5.18 +void
    5.19 +Frame::enterLoop(Zone *zone)
    5.20 +{
    5.21 +    assert(loopDepth_ < code_->maxLoopDepth());
    5.22 +    loopStack()[loopDepth_] = zone->heap().lifoPosition();
    5.23 +    loopDepth_++;
    5.24 +}
    5.25 +
    5.26 +void
    5.27 +Frame::leaveLoop(Zone *zone)
    5.28 +{
    5.29 +    assert(loopDepth_ > 0);
    5.30 +    loopDepth_--;
    5.31 +    zone->heap().unwindLifo(loopStack()[loopDepth_]);
    5.32 +}
    5.33 +
    5.34  void
    5.35  Frame::accept(VirtualObjectVisitor *visitor)
    5.36  {
     6.1 --- a/src/Stack.h	Sat Dec 22 16:42:20 2012 -0800
     6.2 +++ b/src/Stack.h	Sat Dec 22 18:22:14 2012 -0800
     6.3 @@ -66,7 +66,14 @@
     6.4          Object *callee_;
     6.5      };
     6.6      Code *code_;
     6.7 +
     6.8 +    // Position of the LIFO stack at function entry.
     6.9      Address lifoPosition_;
    6.10 +    
    6.11 +    // Loop nesting level, for LIFO stack balancing. LIFO stack balancing
    6.12 +    // is not strictly required, but it helps to avoid unnecessary waste,
    6.13 +    // since once the LIFO stack is depleted, we must use the heap instead.
    6.14 +    unsigned loopDepth_;
    6.15  
    6.16    public:
    6.17      Frame(Frame *prev, bytecode *prevpc, Function *fun, Code *code, Address lifoPosition)
    6.18 @@ -74,7 +81,8 @@
    6.19          prevpc_(prevpc),
    6.20          fun_(fun),
    6.21          code_(code),
    6.22 -        lifoPosition_(lifoPosition)
    6.23 +        lifoPosition_(lifoPosition),
    6.24 +        loopDepth_(0)
    6.25      {
    6.26      }
    6.27  
    6.28 @@ -87,8 +95,11 @@
    6.29      {
    6.30      }
    6.31  
    6.32 +    Address *loopStack() {
    6.33 +        return reinterpret_cast<Address *>(this + 1);
    6.34 +    }
    6.35      LocalSlot *locals() {
    6.36 -        return reinterpret_cast<LocalSlot *>(this + 1);
    6.37 +        return reinterpret_cast<LocalSlot *>(loopStack() + code_->maxLoopDepth());
    6.38      }
    6.39      LocalSlot *stackBase() {
    6.40          return locals() + code_->nlocals();
    6.41 @@ -124,6 +135,8 @@
    6.42          assert(slot < fun()->nargs());
    6.43          return *(reinterpret_cast<LocalSlot *>(this) - slot - 1);
    6.44      }
    6.45 +    void enterLoop(Zone *zone);
    6.46 +    void leaveLoop(Zone *zone);
    6.47      void accept(VirtualObjectVisitor *visitor);
    6.48  };
    6.49  
     7.1 --- a/src/compiler/BytecodeEmitter.cpp	Sat Dec 22 16:42:20 2012 -0800
     7.2 +++ b/src/compiler/BytecodeEmitter.cpp	Sat Dec 22 18:22:14 2012 -0800
     7.3 @@ -90,7 +90,9 @@
     7.4      buffer_pos_(buffer_),
     7.5      buffer_end_(buffer_pos_ + ARRAY_LENGTH(inline_buffer_)),
     7.6      oom_(false),
     7.7 -    maxStackDepth_(0)
     7.8 +    maxStackDepth_(0),
     7.9 +    loopDepth_(0),
    7.10 +    maxLoopDepth_(0)
    7.11  {
    7.12      lastCodePosition_ = 0;
    7.13      firstLine_ = 1;
    7.14 @@ -377,6 +379,7 @@
    7.15                       objects,
    7.16                       strings,
    7.17                       maxStackDepth(),
    7.18 +                     maxLoopDepth_,
    7.19                       safepoints,
    7.20                       sourcemap);
    7.21  
     8.1 --- a/src/compiler/BytecodeEmitter.h	Sat Dec 22 16:42:20 2012 -0800
     8.2 +++ b/src/compiler/BytecodeEmitter.h	Sat Dec 22 18:22:14 2012 -0800
     8.3 @@ -137,6 +137,8 @@
     8.4      StringTable strings_;
     8.5      Vector<Symbol *> locals_;
     8.6      Vector<Object *> objects_;
     8.7 +    unsigned loopDepth_;
     8.8 +    unsigned maxLoopDepth_;
     8.9  
    8.10    private:
    8.11      // The maximum size of a code object must be such that the distance between
    8.12 @@ -342,7 +344,8 @@
    8.13          if (oom())
    8.14              return;
    8.15  
    8.16 -        assert(op == OP_JT || op == OP_JF || op == OP_JUMP);
    8.17 +        assert(op == OP_JT || op == OP_JF || op == OP_JUMP ||
    8.18 +               op == OP_CONTINUE || op == OP_BREAK);
    8.19          ensure(OP_JUMP_LENGTH);
    8.20  
    8.21          if (label->bound()) {
    8.22 @@ -433,43 +436,30 @@
    8.23              markOop(1);
    8.24      }
    8.25  
    8.26 -#if 0
    8.27 -    void getglobal(Variable *var) {
    8.28 -        ensure(OP_GETGLOBAL_F_LENGTH);
    8.29 -        if (var->type()->isPrimitive()) {
    8.30 -            switch (var->type()->primitive()) {
    8.31 -              case PrimitiveType_Float:
    8.32 -                writeOp(OP_GETGLOBAL_F);
    8.33 -                break;
    8.34 -              default:
    8.35 -                assert(var->type()->primitive() == PrimitiveType_Int32);
    8.36 -                writeOp(OP_GETGLOBAL_I);
    8.37 -                break;
    8.38 -            }
    8.39 -        } else {
    8.40 -            writeOp(OP_GETGLOBAL_O);
    8.41 -            markOop(1);
    8.42 -        }
    8.43 -        writeUint32(var->slot());
    8.44 +    int enterloop() {
    8.45 +        int offset = position();
    8.46 +
    8.47 +        ensure(OP_ENTERLOOP);
    8.48 +        writeOp(OP_ENTERLOOP);
    8.49 +        writeUint8(0);
    8.50 +
    8.51 +        loopDepth_++;
    8.52 +        if (loopDepth_ > maxLoopDepth_)
    8.53 +            maxLoopDepth_ = loopDepth_;
    8.54 +
    8.55 +        return offset;
    8.56      }
    8.57 -    void setglobal(Variable *var) {
    8.58 -        ensure(OP_SETGLOBAL_F_LENGTH);
    8.59 -        if (var->type()->isPrimitive()) {
    8.60 -            switch (var->type()->primitive()) {
    8.61 -              case PrimitiveType_Float:
    8.62 -                writeOp(OP_SETGLOBAL_F);
    8.63 -                break;
    8.64 -              default:
    8.65 -                assert(var->type()->primitive() == PrimitiveType_Int32);
    8.66 -                writeOp(OP_SETGLOBAL_I);
    8.67 -                break;
    8.68 -            }
    8.69 -        } else {
    8.70 -            writeOp(OP_SETGLOBAL_O);
    8.71 -        }
    8.72 -        writeUint32(var->slot());
    8.73 +    void leaveloop() {
    8.74 +        singleByteOp(OP_LEAVELOOP);
    8.75 +
    8.76 +        assert(loopDepth_);
    8.77 +        loopDepth_--;
    8.78      }
    8.79 -#endif
    8.80 +    void setLoopUsesLifoStack(int pos) {
    8.81 +        assert(pos >= 0 && pos < position());
    8.82 +        assert(*(buffer() + pos) == OP_ENTERLOOP);
    8.83 +        *(buffer() + pos + 1) = 1;
    8.84 +    }
    8.85  
    8.86      void float_(float value) {
    8.87          ensure(OP_FLOAT_LENGTH);
     9.1 --- a/src/compiler/SemanticAnalysis.cpp	Sat Dec 22 16:42:20 2012 -0800
     9.2 +++ b/src/compiler/SemanticAnalysis.cpp	Sat Dec 22 18:22:14 2012 -0800
     9.3 @@ -242,6 +242,36 @@
     9.4  }
     9.5  
     9.6  void
     9.7 +SemanticAnalysis::requireLifoStack()
     9.8 +{
     9.9 +    if (!loop_)
    9.10 +        return;
    9.11 +
    9.12 +    loop_->setUsesLifoStack();
    9.13 +}
    9.14 +
    9.15 +void
    9.16 +SemanticAnalysis::visitLoopBody(Statement *stmt)
    9.17 +{
    9.18 +    // We always mark off a loop body, so on each edge, we can balance the
    9.19 +    // LIFO stack. Right now, we don't have the ability to budget the LIFO
    9.20 +    // stack ahead of time. If we turn out to never need the LIFO stack, we
    9.21 +    // mark it as unnecessary. (The interpreter can't really make use of this,
    9.22 +    // but the JIT can).
    9.23 +    //
    9.24 +    // Note that balancing the LIFO stack isn't even necessary, so this
    9.25 +    // feature may be removed. All this mechanism does is mitigate the
    9.26 +    // possibility that a long-running loop will overflow the LIFO region
    9.27 +    // and therefore start requiring nursery allocations.
    9.28 +    int pos = __ enterloop();
    9.29 +    stmt->accept(this);
    9.30 +    __ leaveloop();
    9.31 +
    9.32 +    if (loop_->usesLifoStack())
    9.33 +        __ setLoopUsesLifoStack(pos);
    9.34 +}
    9.35 +
    9.36 +void
    9.37  SemanticAnalysis::visitForStatement(ForStatement *node)
    9.38  {
    9.39      AutoEnterScope scope(&scope_, node->scope());
    9.40 @@ -259,7 +289,7 @@
    9.41          __ jump(&test);
    9.42  
    9.43      __ bind(&header);
    9.44 -    node->body()->accept(this);
    9.45 +    visitLoopBody(node->body());
    9.46  
    9.47      __ bind(&update);
    9.48      if (node->update())
    9.49 @@ -291,10 +321,10 @@
    9.50      if (node->token() == TOK_WHILE) {
    9.51          Label body;
    9.52          visitForTest(node->condition(), &body, &join, &body);
    9.53 -        node->body()->accept(this);
    9.54 +        visitLoopBody(node->body());
    9.55          __ jump(&header);
    9.56      } else {
    9.57 -        node->body()->accept(this);
    9.58 +        visitLoopBody(node->body());
    9.59          visitForTest(node->condition(), &header, &join, &join);
    9.60      }
    9.61  
    9.62 @@ -308,7 +338,12 @@
    9.63  {
    9.64      if (sym->scope()->kind() == Scope::GLOBAL)
    9.65          return Heap::Tenure_Old;
    9.66 -    // :TODO: need to use LIFO!
    9.67 +
    9.68 +    // SP2's array semantics allow any array kind to escape, so we must use
    9.69 +    // the normal heap. (Hopefully escape analysis can ease this pain later).
    9.70 +    if (sym->type()->isArray())
    9.71 +        return Heap::Tenure_Default;
    9.72 +
    9.73      return Heap::Tenure_Default;
    9.74  }
    9.75  
    9.76 @@ -531,8 +566,11 @@
    9.77              mustComputeReference = actual->isReference() && !given->isReference();
    9.78          }
    9.79  
    9.80 -        if (mustComputeReference)
    9.81 +        if (mustComputeReference) {
    9.82 +            // Refs always use the LIFO stack if they can.
    9.83              arg = new (pool_) HToRef(arg->node(), arg->type(), arg);
    9.84 +            requireLifoStack();
    9.85 +        }
    9.86  
    9.87          if (!args->append(arg))
    9.88              return;
    9.89 @@ -563,8 +601,7 @@
    9.90          return;
    9.91      }
    9.92  
    9.93 -    // Note, if we re-introduce LIFO allocs, we need to unwind here.
    9.94 -    __ jump(loop_->break_());
    9.95 +    __ j(OP_BREAK, loop_->break_());
    9.96  }
    9.97  
    9.98  void
    9.99 @@ -575,8 +612,7 @@
   9.100          return;
   9.101      }
   9.102  
   9.103 -    // Note, if we re-introduce LIFO allocs, we need to unwind here.
   9.104 -    __ jump(loop_->continue_());
   9.105 +    __ j(OP_CONTINUE, loop_->continue_());
   9.106  }
   9.107  
   9.108  void
    10.1 --- a/src/compiler/SemanticAnalysis.h	Sat Dec 22 16:42:20 2012 -0800
    10.2 +++ b/src/compiler/SemanticAnalysis.h	Sat Dec 22 18:22:14 2012 -0800
    10.3 @@ -88,6 +88,8 @@
    10.4          Coerce_Assign
    10.5      };
    10.6  
    10.7 +    void requireLifoStack();
    10.8 +    void visitLoopBody(Statement *stmt);
    10.9      void visitForTest(Expression *expr,
   10.10                        BytecodeEmitter::Label *trueBranch,
   10.11                        BytecodeEmitter::Label *falseBranch,
   10.12 @@ -114,6 +116,7 @@
   10.13          BytecodeEmitter::Label *ct_;
   10.14          LoopScope **prevp_;
   10.15          LoopScope *prev_;
   10.16 +        bool usesLifoStack_;
   10.17  
   10.18        public:
   10.19          LoopScope(Scope *scope, BytecodeEmitter::Label *bk, BytecodeEmitter::Label *ct,
   10.20 @@ -122,7 +125,8 @@
   10.21              bk_(bk),
   10.22              ct_(ct),
   10.23              prevp_(prevp),
   10.24 -            prev_(*prevp)
   10.25 +            prev_(*prevp),
   10.26 +            usesLifoStack_(false)
   10.27          {
   10.28              *prevp_ = this;
   10.29          }
   10.30 @@ -141,6 +145,12 @@
   10.31          Scope *scope() {
   10.32              return scope_;
   10.33          }
   10.34 +        void setUsesLifoStack() {
   10.35 +            usesLifoStack_ = true;
   10.36 +        }
   10.37 +        bool usesLifoStack() const {
   10.38 +            return usesLifoStack_;
   10.39 +        }
   10.40      };
   10.41      
   10.42    private:
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/tests/manual/lifo.sp	Sat Dec 22 18:22:14 2012 -0800
    11.3 @@ -0,0 +1,16 @@
    11.4 +native PrintNum(num);
    11.5 +
    11.6 +q(&a)
    11.7 +{
    11.8 +    return 5;
    11.9 +}
   11.10 +
   11.11 +public main()
   11.12 +{
   11.13 +    // This isn't a real test, yet. The diassembly should have
   11.14 +    // LIFO save/restore functions.
   11.15 +    for (new i = 0; i < 100; i++) {
   11.16 +        q(i);
   11.17 +    }
   11.18 +}
   11.19 +
    12.1 --- a/tests/prototype/lifo.sp	Sat Dec 22 16:42:20 2012 -0800
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,13 +0,0 @@
    12.4 -q(&a)
    12.5 -{
    12.6 -    return 5;
    12.7 -}
    12.8 -
    12.9 -main()
   12.10 -{
   12.11 -    for (new i = 0; i < 100; i++) {
   12.12 -        q(i);
   12.13 -    }
   12.14 -    return 5;
   12.15 -}
   12.16 -