Support assigning structs.
authorDavid Anderson <dvander@alliedmods.net>
Sat Aug 04 23:26:49 2012 -0700 (2012-08-04)
changeset 152153d0cafc1fd
parent 151 bb419abf0df4
child 153 fc91c4a3dfe2
Support assigning structs.
src/BytecodeCompiler.cpp
src/BytecodeEmitter.h
src/Interpreter.cpp
src/Opcodes.cpp
src/Opcodes.tbl
src/Structures.h
src/Types.h
     1.1 --- a/src/BytecodeCompiler.cpp	Sat Aug 04 21:47:30 2012 -0700
     1.2 +++ b/src/BytecodeCompiler.cpp	Sat Aug 04 23:26:49 2012 -0700
     1.3 @@ -201,6 +201,8 @@
     1.4  void
     1.5  BytecodeCompiler::visit(Assignment *node)
     1.6  {
     1.7 +    Local<Type> type(zone_, node->expression()->type());
     1.8 +
     1.9      // The first step is three sub-steps:
    1.10      //
    1.11      //   (1) Bind the left-hand side.
    1.12 @@ -213,8 +215,12 @@
    1.13          Variable *var = proxy->binding()->toVariable();
    1.14  
    1.15          // Bind.
    1.16 -        if (var->type()->isReference() || var->type()->isArray())
    1.17 +        if (var->type()->isReference() ||
    1.18 +            var->type()->isArray() ||
    1.19 +            var->type()->isStruct())
    1.20 +        {
    1.21              __ get(var);
    1.22 +        }
    1.23  
    1.24          // Get the value if needed.
    1.25          if (node->token() != TOK_ASSIGN) {
    1.26 @@ -234,15 +240,16 @@
    1.27  
    1.28          expr->left()->accept(this);
    1.29  
    1.30 -        if (node->token() != TOK_ASSIGN) {
    1.31 +        if (node->token() != TOK_ASSIGN)
    1.32              __ dup();
    1.33 +
    1.34 +        if (node->token() != TOK_ASSIGN || (type->isStruct() || type->isArray()))
    1.35              __ getfield(expr->fieldIndex(), expr->type());
    1.36 -        }
    1.37      } else {
    1.38          IndexExpression *index = node->lvalue()->toIndexExpression();
    1.39          __ note_position(index->pos());
    1.40  
    1.41 -        // Note: Eventually, watch out for a whole here. An expression like:
    1.42 +        // Note: Eventually, watch out for a hole here. An expression like:
    1.43          //
    1.44          //   (a[4] = (5, a = []))
    1.45          //
    1.46 @@ -252,13 +259,12 @@
    1.47          index->left()->accept(this);
    1.48          index->right()->accept(this);
    1.49  
    1.50 -        if (node->token() != TOK_ASSIGN) {
    1.51 +        if (node->token() != TOK_ASSIGN)
    1.52              __ dup2();
    1.53 +
    1.54 +        if (node->token() != TOK_ASSIGN || (type->isStruct() || type->isArray())) {
    1.55              __ bounds();
    1.56 -            if (node->type()->isArray())
    1.57 -                __ getarray_o();
    1.58 -            else
    1.59 -                __ getarray(node->type());
    1.60 +            __ getarray(index->type());
    1.61          }
    1.62      }
    1.63  
    1.64 @@ -273,11 +279,16 @@
    1.65      }
    1.66  
    1.67      // Emit the store.
    1.68 -    if (node->expression()->type()->isArray()) {
    1.69 +    if (type->isArray()) {
    1.70          // Unlike references, which can only be computed from a NamePRoxy,
    1.71          // arrays can be computed from multiple lvalue types.
    1.72          __ note_position(node->pos());
    1.73 -        __ arraycopy();
    1.74 +        __ copyarray();
    1.75 +    } else if (type->isStruct()) {
    1.76 +        // Structs have value semantics, so emit a full copy. We don't break
    1.77 +        // this down into u-ops, to avoid unnecessary bytecode explosion.
    1.78 +        __ note_position(node->pos());
    1.79 +        __ copystruct();
    1.80      } else if (node->lvalue()->isNameProxy()) {
    1.81          NameProxy *proxy = node->lvalue()->toNameProxy();
    1.82          Variable *var = proxy->binding()->toVariable();
    1.83 @@ -793,7 +804,7 @@
    1.84          if (node->initialization()) {
    1.85              node->initialization()->accept(this);
    1.86              __ note_position(node->pos());
    1.87 -            __ arraycopy();
    1.88 +            __ copyarray();
    1.89          }
    1.90      } else if (type->isStruct()) {
    1.91          Local<Map> map(zone_, type->newMap());
     2.1 --- a/src/BytecodeEmitter.h	Sat Aug 04 21:47:30 2012 -0700
     2.2 +++ b/src/BytecodeEmitter.h	Sat Aug 04 23:26:49 2012 -0700
     2.3 @@ -535,8 +535,12 @@
     2.4          writeObject(obj);
     2.5          markOop(1);
     2.6      }
     2.7 -    void arraycopy() {
     2.8 -        singleByteOp(OP_ARRAYCOPY);
     2.9 +    void copyarray() {
    2.10 +        singleByteOp(OP_COPYARRAY);
    2.11 +        markOop(1);
    2.12 +    }
    2.13 +    void copystruct() {
    2.14 +        singleByteOp(OP_COPYSTRUCT);
    2.15          markOop(1);
    2.16      }
    2.17  
     3.1 --- a/src/Interpreter.cpp	Sat Aug 04 21:47:30 2012 -0700
     3.2 +++ b/src/Interpreter.cpp	Sat Aug 04 23:26:49 2012 -0700
     3.3 @@ -106,6 +106,8 @@
     3.4      return array;
     3.5  }
     3.6  
     3.7 +// Even though this function can fail, we immediately bail out, so it doesn't
     3.8 +// matter if we've leaving things unrooted.
     3.9  static bool
    3.10  CopyArray(Zone *zone, Array *source, Array *dest)
    3.11  {
    3.12 @@ -120,6 +122,95 @@
    3.13      return true;
    3.14  }
    3.15  
    3.16 +static void
    3.17 +GetField(Struct *obj, Descriptor *desc, LocalSlot *vp)
    3.18 +{
    3.19 +    // :TODO: assert no GC.
    3.20 +    if (desc->type()->isTraceable()) {
    3.21 +        vp->obj = obj->get<Object *>(desc);
    3.22 +    } else {
    3.23 +        switch (desc->type()->pod()) {
    3.24 +          case PrimitiveType_Float:
    3.25 +            vp->f = obj->get<float>(desc);
    3.26 +            break;
    3.27 +
    3.28 +          default:
    3.29 +            assert(desc->type()->pod() == PrimitiveType_Int32);
    3.30 +            vp->i32 = obj->get<int>(desc);
    3.31 +            break;
    3.32 +        }
    3.33 +    }
    3.34 +}
    3.35 +
    3.36 +static void
    3.37 +GetField(Struct *obj, unsigned index, LocalSlot *vp)
    3.38 +{
    3.39 +    Descriptor *desc = Descriptor::cast(obj->map()->type()->fields()->at(index));
    3.40 +    GetField(obj, desc, vp);
    3.41 +}
    3.42 +
    3.43 +static void
    3.44 +SetField(Zone *zone, Struct *obj, Descriptor *desc, const LocalSlot &v)
    3.45 +{
    3.46 +    // :TODO: assert no GC.
    3.47 +    if (desc->type()->isTraceable()) {
    3.48 +        obj->set(zone, desc, v.obj);
    3.49 +    } else {
    3.50 +        switch (desc->type()->pod()) {
    3.51 +          case PrimitiveType_Float:
    3.52 +            obj->set<float>(desc, v.f);
    3.53 +            break;
    3.54 +
    3.55 +          default:
    3.56 +            assert(desc->type()->pod() == PrimitiveType_Int32);
    3.57 +            obj->set<int>(desc, v.i32);
    3.58 +            break;
    3.59 +        }
    3.60 +    }
    3.61 +}
    3.62 +
    3.63 +static void
    3.64 +SetField(Zone *zone, Struct *obj, unsigned index, const LocalSlot &v)
    3.65 +{
    3.66 +    Descriptor *desc = Descriptor::cast(obj->map()->type()->fields()->at(index));
    3.67 +    SetField(zone, obj, desc, v);
    3.68 +}
    3.69 +
    3.70 +// Even though this function can fail, we immediately bail out, so it doesn't
    3.71 +// matter if we've leaving things unrooted.
    3.72 +static bool
    3.73 +CopyStruct(Zone *zone, Struct *left, Struct *right)
    3.74 +{
    3.75 +    assert(left->map()->type() == right->map()->type());
    3.76 +
    3.77 +    Type *type = left->map()->type();
    3.78 +    for (unsigned i = 0; i < type->fields()->length(); i++) {
    3.79 +        Descriptor *desc = Descriptor::cast(type->fields()->at(i));
    3.80 +
    3.81 +        LocalSlot rval;
    3.82 +        GetField(right, desc, &rval);
    3.83 +
    3.84 +        // If the field is a struct, we need to deep-copy.
    3.85 +        if (desc->type()->isStruct()) {
    3.86 +            LocalSlot lval;
    3.87 +
    3.88 +            GetField(left, desc, &lval);
    3.89 +            if (!CopyStruct(zone, Struct::cast(lval.obj), Struct::cast(rval.obj)))
    3.90 +                return false;
    3.91 +        } else if (desc->type()->isArray()) {
    3.92 +            LocalSlot lval;
    3.93 +
    3.94 +            GetField(left, desc, &lval);
    3.95 +            if (!CopyArray(zone, Array::cast(lval.obj), Array::cast(rval.obj)))
    3.96 +                return false;
    3.97 +        } else {
    3.98 +            SetField(zone, left, desc, rval);
    3.99 +        }
   3.100 +    }
   3.101 +
   3.102 +    return true;
   3.103 +}
   3.104 +
   3.105  static bool
   3.106  InvokeNative(Zone *zone, Handle<Native> native, void *identity, LocalSlot *args, unsigned argc,
   3.107               int *result)
   3.108 @@ -795,7 +886,7 @@
   3.109      break;
   3.110  }
   3.111  
   3.112 -case OP_ARRAYCOPY:
   3.113 +case OP_COPYARRAY:
   3.114  {
   3.115      // Note for this assignment opcode, we return the left-hand side, not the
   3.116      // right, since in this case the result of the expression could be different.
   3.117 @@ -952,6 +1043,31 @@
   3.118      break;
   3.119  }
   3.120  
   3.121 +case OP_GETFIELD:
   3.122 +{
   3.123 +    Struct *obj = Struct::cast(top.sp[-1].obj);
   3.124 +    GetField(obj, READ_UINT32(top.pc), &top.sp[-1]);
   3.125 +    break;
   3.126 +}
   3.127 +
   3.128 +case OP_SETFIELD:
   3.129 +{
   3.130 +    Struct *obj = Struct::cast(top.sp[-2].obj);
   3.131 +    SetField(zone, obj, READ_UINT32(top.pc), top.sp[-1]);
   3.132 +    top.sp[-2] = top.sp[-1];
   3.133 +    top.sp--;
   3.134 +    break;
   3.135 +}
   3.136 +
   3.137 +case OP_COPYSTRUCT:
   3.138 +{
   3.139 +    if (!CopyStruct(zone, Struct::cast(top.sp[-2].obj), Struct::cast(top.sp[-1].obj)))
   3.140 +        goto error;
   3.141 +    top.sp[-2].obj = top.sp[-1].obj;
   3.142 +    top.sp--;
   3.143 +    break;
   3.144 +}
   3.145 +
   3.146  default:
   3.147      assert(false);
   3.148      return false;
     4.1 --- a/src/Opcodes.cpp	Sat Aug 04 21:47:30 2012 -0700
     4.2 +++ b/src/Opcodes.cpp	Sat Aug 04 23:26:49 2012 -0700
     4.3 @@ -80,7 +80,7 @@
     4.4      Opcode op = Opcode(*(start() + pcOffset));
     4.5      assert(op == OP_CALL || op == OP_NEWARRAY || op == OP_VARREF_LIFO ||
     4.6             op == OP_ARGREF_LIFO || op == OP_SLOTREF_LIFO || op == OP_ARRAYREF_LIFO ||
     4.7 -           op == OP_ARRAYCOPY);
     4.8 +           op == OP_COPYARRAY);
     4.9  #endif
    4.10  
    4.11      if (!safepoints_)
    4.12 @@ -180,7 +180,8 @@
    4.13            case OP_SETREF_F:
    4.14            case OP_SLOTREF_LIFO:
    4.15            case OP_ARRAYREF_LIFO:
    4.16 -          case OP_ARRAYCOPY:
    4.17 +          case OP_COPYARRAY:
    4.18 +          case OP_COPYSTRUCT:
    4.19            case OP_DUP:
    4.20            case OP_DUP2:
    4.21            case OP_SWAP:
     5.1 --- a/src/Opcodes.tbl	Sat Aug 04 21:47:30 2012 -0700
     5.2 +++ b/src/Opcodes.tbl	Sat Aug 04 23:26:49 2012 -0700
     5.3 @@ -188,7 +188,7 @@
     5.4  //   Right = pop()
     5.5  //   copy(Left, Right)
     5.6  //   push(Left)
     5.7 -OPDEF(OP_ARRAYCOPY,     82,         "arraycopy",    1,      2,  1)
     5.8 +OPDEF(OP_COPYARRAY,     82,         "arraycopy",    1,      2,  1)
     5.9  
    5.10  // Duplicates the top item on the stack.
    5.11  OPDEF(OP_DUP,           83,         "dup",          1,      1,  2)
    5.12 @@ -266,3 +266,10 @@
    5.13  OPDEF(OP_GETFIELD,      104,        "getfield",     5,      1, 1)
    5.14  OPDEF(OP_SETFIELD,      105,        "setfield",     5,      2, 1)
    5.15  
    5.16 +// Performs a struct-to-struct assignment.
    5.17 +//   Left = pop()
    5.18 +//   Right = pop()
    5.19 +//   copy(Left, Right)
    5.20 +//   push(Left)
    5.21 +OPDEF(OP_COPYSTRUCT,    106,        "structcopy",   1,      2, 1)
    5.22 +
     6.1 --- a/src/Structures.h	Sat Aug 04 21:47:30 2012 -0700
     6.2 +++ b/src/Structures.h	Sat Aug 04 23:26:49 2012 -0700
     6.3 @@ -150,6 +150,26 @@
     6.4          return StructMap::cast(Object::map());
     6.5      }
     6.6  
     6.7 +    // This must NOT be used with any Object * type!
     6.8 +    template <typename T> T get(Descriptor *desc) {
     6.9 +        return *reinterpret_cast<T *>(
    6.10 +                    reinterpret_cast<Address>(this) +
    6.11 +                    offsetOfSlotOffset(desc->offset()));
    6.12 +    }
    6.13 +
    6.14 +    template <typename T> void set(Descriptor *desc, const T &t) {
    6.15 +        *reinterpret_cast<T *>(
    6.16 +            reinterpret_cast<Address>(this) +
    6.17 +            offsetOfSlotOffset(desc->offset())) = t;
    6.18 +    }
    6.19 +
    6.20 +    void set(Zone *zone, Descriptor *desc, Object *obj) {
    6.21 +        // :TODO: write barrier
    6.22 +        *reinterpret_cast<Object **>(
    6.23 +            reinterpret_cast<Address>(this) +
    6.24 +            offsetOfSlotOffset(desc->offset())) = obj;
    6.25 +    }
    6.26 +
    6.27      static inline size_t offsetOfSlotOffset(unsigned offset) {
    6.28          return sizeof(Struct) + offset;
    6.29      }
     7.1 --- a/src/Types.h	Sat Aug 04 21:47:30 2012 -0700
     7.2 +++ b/src/Types.h	Sat Aug 04 23:26:49 2012 -0700
     7.3 @@ -125,7 +125,9 @@
     7.4      PrimitiveType pod() {
     7.5          assert(isPrimitive() || isEnum());
     7.6          return isPrimitive()
     7.7 -               ? primitive()
     7.8 +               ? (primitive() == PrimitiveType_Bool)
     7.9 +                 ? PrimitiveType_Int32
    7.10 +                 : primitive()
    7.11                 : PrimitiveType_Int32;
    7.12      }
    7.13      Type *contained() {