# HG changeset patch # User David Anderson # Date 1344148009 25200 # Node ID 153d0cafc1fd949aef1018252dc074e0c0eca345 # Parent bb419abf0df4718b9030d0ab1601c8fc8d169232 Support assigning structs. diff -r bb419abf0df4 -r 153d0cafc1fd src/BytecodeCompiler.cpp --- a/src/BytecodeCompiler.cpp Sat Aug 04 21:47:30 2012 -0700 +++ b/src/BytecodeCompiler.cpp Sat Aug 04 23:26:49 2012 -0700 @@ -201,6 +201,8 @@ void BytecodeCompiler::visit(Assignment *node) { + Local type(zone_, node->expression()->type()); + // The first step is three sub-steps: // // (1) Bind the left-hand side. @@ -213,8 +215,12 @@ Variable *var = proxy->binding()->toVariable(); // Bind. - if (var->type()->isReference() || var->type()->isArray()) + if (var->type()->isReference() || + var->type()->isArray() || + var->type()->isStruct()) + { __ get(var); + } // Get the value if needed. if (node->token() != TOK_ASSIGN) { @@ -234,15 +240,16 @@ expr->left()->accept(this); - if (node->token() != TOK_ASSIGN) { + if (node->token() != TOK_ASSIGN) __ dup(); + + if (node->token() != TOK_ASSIGN || (type->isStruct() || type->isArray())) __ getfield(expr->fieldIndex(), expr->type()); - } } else { IndexExpression *index = node->lvalue()->toIndexExpression(); __ note_position(index->pos()); - // Note: Eventually, watch out for a whole here. An expression like: + // Note: Eventually, watch out for a hole here. An expression like: // // (a[4] = (5, a = [])) // @@ -252,13 +259,12 @@ index->left()->accept(this); index->right()->accept(this); - if (node->token() != TOK_ASSIGN) { + if (node->token() != TOK_ASSIGN) __ dup2(); + + if (node->token() != TOK_ASSIGN || (type->isStruct() || type->isArray())) { __ bounds(); - if (node->type()->isArray()) - __ getarray_o(); - else - __ getarray(node->type()); + __ getarray(index->type()); } } @@ -273,11 +279,16 @@ } // Emit the store. - if (node->expression()->type()->isArray()) { + if (type->isArray()) { // Unlike references, which can only be computed from a NamePRoxy, // arrays can be computed from multiple lvalue types. __ note_position(node->pos()); - __ arraycopy(); + __ copyarray(); + } else if (type->isStruct()) { + // Structs have value semantics, so emit a full copy. We don't break + // this down into u-ops, to avoid unnecessary bytecode explosion. + __ note_position(node->pos()); + __ copystruct(); } else if (node->lvalue()->isNameProxy()) { NameProxy *proxy = node->lvalue()->toNameProxy(); Variable *var = proxy->binding()->toVariable(); @@ -793,7 +804,7 @@ if (node->initialization()) { node->initialization()->accept(this); __ note_position(node->pos()); - __ arraycopy(); + __ copyarray(); } } else if (type->isStruct()) { Local map(zone_, type->newMap()); diff -r bb419abf0df4 -r 153d0cafc1fd src/BytecodeEmitter.h --- a/src/BytecodeEmitter.h Sat Aug 04 21:47:30 2012 -0700 +++ b/src/BytecodeEmitter.h Sat Aug 04 23:26:49 2012 -0700 @@ -535,8 +535,12 @@ writeObject(obj); markOop(1); } - void arraycopy() { - singleByteOp(OP_ARRAYCOPY); + void copyarray() { + singleByteOp(OP_COPYARRAY); + markOop(1); + } + void copystruct() { + singleByteOp(OP_COPYSTRUCT); markOop(1); } diff -r bb419abf0df4 -r 153d0cafc1fd src/Interpreter.cpp --- a/src/Interpreter.cpp Sat Aug 04 21:47:30 2012 -0700 +++ b/src/Interpreter.cpp Sat Aug 04 23:26:49 2012 -0700 @@ -106,6 +106,8 @@ return array; } +// Even though this function can fail, we immediately bail out, so it doesn't +// matter if we've leaving things unrooted. static bool CopyArray(Zone *zone, Array *source, Array *dest) { @@ -120,6 +122,95 @@ return true; } +static void +GetField(Struct *obj, Descriptor *desc, LocalSlot *vp) +{ + // :TODO: assert no GC. + if (desc->type()->isTraceable()) { + vp->obj = obj->get(desc); + } else { + switch (desc->type()->pod()) { + case PrimitiveType_Float: + vp->f = obj->get(desc); + break; + + default: + assert(desc->type()->pod() == PrimitiveType_Int32); + vp->i32 = obj->get(desc); + break; + } + } +} + +static void +GetField(Struct *obj, unsigned index, LocalSlot *vp) +{ + Descriptor *desc = Descriptor::cast(obj->map()->type()->fields()->at(index)); + GetField(obj, desc, vp); +} + +static void +SetField(Zone *zone, Struct *obj, Descriptor *desc, const LocalSlot &v) +{ + // :TODO: assert no GC. + if (desc->type()->isTraceable()) { + obj->set(zone, desc, v.obj); + } else { + switch (desc->type()->pod()) { + case PrimitiveType_Float: + obj->set(desc, v.f); + break; + + default: + assert(desc->type()->pod() == PrimitiveType_Int32); + obj->set(desc, v.i32); + break; + } + } +} + +static void +SetField(Zone *zone, Struct *obj, unsigned index, const LocalSlot &v) +{ + Descriptor *desc = Descriptor::cast(obj->map()->type()->fields()->at(index)); + SetField(zone, obj, desc, v); +} + +// Even though this function can fail, we immediately bail out, so it doesn't +// matter if we've leaving things unrooted. +static bool +CopyStruct(Zone *zone, Struct *left, Struct *right) +{ + assert(left->map()->type() == right->map()->type()); + + Type *type = left->map()->type(); + for (unsigned i = 0; i < type->fields()->length(); i++) { + Descriptor *desc = Descriptor::cast(type->fields()->at(i)); + + LocalSlot rval; + GetField(right, desc, &rval); + + // If the field is a struct, we need to deep-copy. + if (desc->type()->isStruct()) { + LocalSlot lval; + + GetField(left, desc, &lval); + if (!CopyStruct(zone, Struct::cast(lval.obj), Struct::cast(rval.obj))) + return false; + } else if (desc->type()->isArray()) { + LocalSlot lval; + + GetField(left, desc, &lval); + if (!CopyArray(zone, Array::cast(lval.obj), Array::cast(rval.obj))) + return false; + } else { + SetField(zone, left, desc, rval); + } + } + + return true; +} + static bool InvokeNative(Zone *zone, Handle native, void *identity, LocalSlot *args, unsigned argc, int *result) @@ -795,7 +886,7 @@ break; } -case OP_ARRAYCOPY: +case OP_COPYARRAY: { // Note for this assignment opcode, we return the left-hand side, not the // right, since in this case the result of the expression could be different. @@ -952,6 +1043,31 @@ break; } +case OP_GETFIELD: +{ + Struct *obj = Struct::cast(top.sp[-1].obj); + GetField(obj, READ_UINT32(top.pc), &top.sp[-1]); + break; +} + +case OP_SETFIELD: +{ + Struct *obj = Struct::cast(top.sp[-2].obj); + SetField(zone, obj, READ_UINT32(top.pc), top.sp[-1]); + top.sp[-2] = top.sp[-1]; + top.sp--; + break; +} + +case OP_COPYSTRUCT: +{ + if (!CopyStruct(zone, Struct::cast(top.sp[-2].obj), Struct::cast(top.sp[-1].obj))) + goto error; + top.sp[-2].obj = top.sp[-1].obj; + top.sp--; + break; +} + default: assert(false); return false; diff -r bb419abf0df4 -r 153d0cafc1fd src/Opcodes.cpp --- a/src/Opcodes.cpp Sat Aug 04 21:47:30 2012 -0700 +++ b/src/Opcodes.cpp Sat Aug 04 23:26:49 2012 -0700 @@ -80,7 +80,7 @@ Opcode op = Opcode(*(start() + pcOffset)); assert(op == OP_CALL || op == OP_NEWARRAY || op == OP_VARREF_LIFO || op == OP_ARGREF_LIFO || op == OP_SLOTREF_LIFO || op == OP_ARRAYREF_LIFO || - op == OP_ARRAYCOPY); + op == OP_COPYARRAY); #endif if (!safepoints_) @@ -180,7 +180,8 @@ case OP_SETREF_F: case OP_SLOTREF_LIFO: case OP_ARRAYREF_LIFO: - case OP_ARRAYCOPY: + case OP_COPYARRAY: + case OP_COPYSTRUCT: case OP_DUP: case OP_DUP2: case OP_SWAP: diff -r bb419abf0df4 -r 153d0cafc1fd src/Opcodes.tbl --- a/src/Opcodes.tbl Sat Aug 04 21:47:30 2012 -0700 +++ b/src/Opcodes.tbl Sat Aug 04 23:26:49 2012 -0700 @@ -188,7 +188,7 @@ // Right = pop() // copy(Left, Right) // push(Left) -OPDEF(OP_ARRAYCOPY, 82, "arraycopy", 1, 2, 1) +OPDEF(OP_COPYARRAY, 82, "arraycopy", 1, 2, 1) // Duplicates the top item on the stack. OPDEF(OP_DUP, 83, "dup", 1, 1, 2) @@ -266,3 +266,10 @@ OPDEF(OP_GETFIELD, 104, "getfield", 5, 1, 1) OPDEF(OP_SETFIELD, 105, "setfield", 5, 2, 1) +// Performs a struct-to-struct assignment. +// Left = pop() +// Right = pop() +// copy(Left, Right) +// push(Left) +OPDEF(OP_COPYSTRUCT, 106, "structcopy", 1, 2, 1) + diff -r bb419abf0df4 -r 153d0cafc1fd src/Structures.h --- a/src/Structures.h Sat Aug 04 21:47:30 2012 -0700 +++ b/src/Structures.h Sat Aug 04 23:26:49 2012 -0700 @@ -150,6 +150,26 @@ return StructMap::cast(Object::map()); } + // This must NOT be used with any Object * type! + template T get(Descriptor *desc) { + return *reinterpret_cast( + reinterpret_cast
(this) + + offsetOfSlotOffset(desc->offset())); + } + + template void set(Descriptor *desc, const T &t) { + *reinterpret_cast( + reinterpret_cast
(this) + + offsetOfSlotOffset(desc->offset())) = t; + } + + void set(Zone *zone, Descriptor *desc, Object *obj) { + // :TODO: write barrier + *reinterpret_cast( + reinterpret_cast
(this) + + offsetOfSlotOffset(desc->offset())) = obj; + } + static inline size_t offsetOfSlotOffset(unsigned offset) { return sizeof(Struct) + offset; } diff -r bb419abf0df4 -r 153d0cafc1fd src/Types.h --- a/src/Types.h Sat Aug 04 21:47:30 2012 -0700 +++ b/src/Types.h Sat Aug 04 23:26:49 2012 -0700 @@ -125,7 +125,9 @@ PrimitiveType pod() { assert(isPrimitive() || isEnum()); return isPrimitive() - ? primitive() + ? (primitive() == PrimitiveType_Bool) + ? PrimitiveType_Int32 + : primitive() : PrimitiveType_Int32; } Type *contained() {