[dart2bytecode, vm/interpreter] Records
TEST=language tests in vm-aot-dyn-linux-debug-x64 configuration Change-Id: I6dd7d5617f5c076c722304ab2a753159f22c0bf6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/382421 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Slava Egorov <vegorov@google.com>
This commit is contained in:
committed by
Commit Queue
parent
e65551b7ea
commit
2e63d3135a
@@ -755,4 +755,14 @@ class BytecodeAssembler {
|
||||
void emitJumpIfInitialized(Label label) {
|
||||
_emitJumpInstruction(Opcode.kJumpIfInitialized, label);
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
void emitAllocateRecord(int rd) {
|
||||
_emitInstructionD(Opcode.kAllocateRecord, rd);
|
||||
}
|
||||
|
||||
@pragma('vm:prefer-inline')
|
||||
void emitLoadRecordField(int rd) {
|
||||
_emitInstructionD(Opcode.kLoadRecordField, rd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4252,17 +4252,39 @@ class BytecodeGenerator extends RecursiveVisitor {
|
||||
|
||||
@override
|
||||
void visitRecordIndexGet(RecordIndexGet node) {
|
||||
_unimplemented(node, 'RecordIndexGet');
|
||||
_generateNode(node.receiver);
|
||||
asm.emitLoadRecordField(node.index);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordNameGet(RecordNameGet node) {
|
||||
_unimplemented(node, 'RecordNameGet');
|
||||
final type = node.receiverType;
|
||||
final namedFields = type.named;
|
||||
final name = node.name;
|
||||
int fieldIndex = -1;
|
||||
for (int i = 0; i < namedFields.length; ++i) {
|
||||
if (namedFields[i].name == name) {
|
||||
fieldIndex = type.positional.length + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fieldIndex < 0) {
|
||||
throw 'Unable to find record field "$name" in $type';
|
||||
}
|
||||
_generateNode(node.receiver);
|
||||
asm.emitLoadRecordField(fieldIndex);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitRecordLiteral(RecordLiteral node) {
|
||||
_unimplemented(node, 'RecordLiteral');
|
||||
assert(!node.isConst);
|
||||
for (final expr in node.positional) {
|
||||
_generateNode(expr);
|
||||
}
|
||||
for (final expr in node.named) {
|
||||
_generateNode(expr.value);
|
||||
}
|
||||
asm.emitAllocateRecord(cp.addType(node.recordType));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -209,6 +209,12 @@ enum Opcode {
|
||||
kCompareDoubleLt,
|
||||
kCompareDoubleGe,
|
||||
kCompareDoubleLe,
|
||||
|
||||
// Records
|
||||
kAllocateRecord,
|
||||
kAllocateRecord_Wide,
|
||||
kLoadRecordField,
|
||||
kLoadRecordField_Wide,
|
||||
}
|
||||
|
||||
/// Compact variants of opcodes are always even.
|
||||
@@ -456,6 +462,10 @@ const Map<Opcode, Format> BytecodeFormats = const {
|
||||
Encoding.k0, const [Operand.none, Operand.none, Operand.none]),
|
||||
Opcode.kCompareDoubleLe: const Format(
|
||||
Encoding.k0, const [Operand.none, Operand.none, Operand.none]),
|
||||
Opcode.kAllocateRecord: const Format(
|
||||
Encoding.kD, const [Operand.lit, Operand.none, Operand.none]),
|
||||
Opcode.kLoadRecordField: const Format(
|
||||
Encoding.kD, const [Operand.imm, Operand.none, Operand.none]),
|
||||
};
|
||||
|
||||
// Should match constant in runtime/vm/stack_frame_kbc.h.
|
||||
|
||||
@@ -308,6 +308,7 @@ enum ConstTag {
|
||||
kString,
|
||||
kMap,
|
||||
kSet,
|
||||
kRecord,
|
||||
}
|
||||
|
||||
enum TypeTag {
|
||||
@@ -1405,6 +1406,7 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
}
|
||||
break;
|
||||
case ConstTag.kList:
|
||||
case ConstTag.kRecord:
|
||||
case ConstTag.kMap:
|
||||
case ConstTag.kSet:
|
||||
{
|
||||
@@ -1461,6 +1463,7 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
reader.readPackedObject(), reader.readPackedObject())));
|
||||
break;
|
||||
case ConstTag.kList:
|
||||
case ConstTag.kRecord:
|
||||
case ConstTag.kMap:
|
||||
case ConstTag.kSet:
|
||||
type = reader.readPackedObject();
|
||||
@@ -1503,6 +1506,7 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
}
|
||||
break;
|
||||
case ConstTag.kList:
|
||||
case ConstTag.kRecord:
|
||||
case ConstTag.kMap:
|
||||
case ConstTag.kSet:
|
||||
{
|
||||
@@ -1579,6 +1583,7 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
_combineHashes(type.hashCode, mapHashCode(fieldValues));
|
||||
}
|
||||
case ConstTag.kList:
|
||||
case ConstTag.kRecord:
|
||||
case ConstTag.kMap:
|
||||
case ConstTag.kSet:
|
||||
{
|
||||
@@ -1610,6 +1615,7 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
case ConstTag.kInstance:
|
||||
return this.type == other.type && mapEquals(this.value, other.value);
|
||||
case ConstTag.kList:
|
||||
case ConstTag.kRecord:
|
||||
case ConstTag.kMap:
|
||||
case ConstTag.kSet:
|
||||
return this.type == other.type && listEquals(this.value, other.value);
|
||||
@@ -1634,6 +1640,8 @@ class _ConstObjectHandle extends ObjectHandle {
|
||||
return 'const $type $value';
|
||||
case ConstTag.kList:
|
||||
return 'const List<$type> $value';
|
||||
case ConstTag.kRecord:
|
||||
return 'const Record<$type> $value';
|
||||
case ConstTag.kMap:
|
||||
return 'const Map<$type> $value';
|
||||
case ConstTag.kSet:
|
||||
@@ -2382,6 +2390,16 @@ class _NodeVisitor extends VisitorDefault<ObjectHandle?>
|
||||
objectTable.getHandles(node.entries),
|
||||
objectTable.getHandle(node.typeArgument)));
|
||||
|
||||
@override
|
||||
ObjectHandle? visitRecordConstant(RecordConstant node) =>
|
||||
objectTable.getOrAddObject(new _ConstObjectHandle(
|
||||
ConstTag.kRecord,
|
||||
objectTable.getHandles([
|
||||
...node.positional,
|
||||
...node.named.values,
|
||||
]),
|
||||
objectTable.getHandle(node.recordType)));
|
||||
|
||||
@override
|
||||
ObjectHandle? visitMapConstant(MapConstant node) =>
|
||||
objectTable.getOrAddObject(new _ConstObjectHandle(
|
||||
|
||||
@@ -1064,6 +1064,7 @@ ObjectPtr BytecodeReaderHelper::ReadConstObject(intptr_t tag) {
|
||||
kString,
|
||||
kMap,
|
||||
kSet,
|
||||
kRecord,
|
||||
};
|
||||
|
||||
switch (tag) {
|
||||
@@ -1221,6 +1222,20 @@ ObjectPtr BytecodeReaderHelper::ReadConstObject(intptr_t tag) {
|
||||
}
|
||||
return Canonicalize(set);
|
||||
}
|
||||
case kRecord: {
|
||||
const RecordType& record_type =
|
||||
RecordType::CheckedHandle(Z, ReadObject());
|
||||
const intptr_t num_fields = reader_.ReadUInt();
|
||||
ASSERT(num_fields == record_type.NumFields());
|
||||
const RecordShape shape = record_type.shape();
|
||||
const auto& record = Record::Handle(Z, Record::New(shape));
|
||||
Object& value = Object::Handle(Z);
|
||||
for (intptr_t i = 0; i < num_fields; ++i) {
|
||||
value = ReadObject();
|
||||
record.SetFieldAt(i, value);
|
||||
}
|
||||
return Canonicalize(record);
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
@@ -1337,7 +1352,44 @@ ObjectPtr BytecodeReaderHelper::ReadType(intptr_t tag,
|
||||
/* has_positional_param_names = */ false,
|
||||
/* has_parameter_flags */ false);
|
||||
}
|
||||
case kRecordType:
|
||||
case kRecordType: {
|
||||
const intptr_t num_positional = reader_.ReadUInt();
|
||||
const intptr_t num_named = reader_.ReadUInt();
|
||||
|
||||
const intptr_t num_fields = num_positional + num_named;
|
||||
const Array& field_types =
|
||||
Array::Handle(Z, Array::New(num_fields, Heap::kOld));
|
||||
const Array& field_names =
|
||||
(num_named == 0)
|
||||
? Object::empty_array()
|
||||
: Array::Handle(Z, Array::New(num_named, Heap::kOld));
|
||||
AbstractType& type = AbstractType::Handle(Z);
|
||||
|
||||
intptr_t pos = 0;
|
||||
for (intptr_t i = 0; i < num_positional; ++i) {
|
||||
type ^= ReadObject();
|
||||
field_types.SetAt(pos++, type);
|
||||
}
|
||||
|
||||
if (num_named > 0) {
|
||||
String& name = String::Handle(Z);
|
||||
for (intptr_t i = 0; i < num_named; ++i) {
|
||||
name ^= ReadObject();
|
||||
field_names.SetAt(i, name);
|
||||
type ^= ReadObject();
|
||||
field_types.SetAt(pos++, type);
|
||||
}
|
||||
field_names.MakeImmutable();
|
||||
}
|
||||
|
||||
const RecordShape shape =
|
||||
RecordShape::Register(thread_, num_fields, field_names);
|
||||
|
||||
type = RecordType::New(shape, field_types, nullability);
|
||||
type.SetIsFinalized();
|
||||
return type.Canonicalize(thread_);
|
||||
}
|
||||
|
||||
UNIMPLEMENTED();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -638,6 +638,10 @@ namespace dart {
|
||||
V(CompareDoubleLt, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleGe, 0, ORDN, ___, ___, ___) \
|
||||
V(CompareDoubleLe, 0, ORDN, ___, ___, ___) \
|
||||
V(AllocateRecord, D, ORDN, lit, ___, ___) \
|
||||
V(AllocateRecord_Wide, D, WIDE, lit, ___, ___) \
|
||||
V(LoadRecordField, D, ORDN, num, ___, ___) \
|
||||
V(LoadRecordField_Wide, D, WIDE, num, ___, ___) \
|
||||
|
||||
// These bytecodes are only generated within the VM. Reassigning their
|
||||
// opcodes is not a breaking change.
|
||||
|
||||
@@ -1413,6 +1413,33 @@ bool Interpreter::AllocateArray(Thread* thread,
|
||||
return InvokeRuntime(thread, this, DRT_AllocateArray, args);
|
||||
}
|
||||
|
||||
// Allocate a Record with the given shape and put it into SP[0].
|
||||
// Returns false on exception.
|
||||
bool Interpreter::AllocateRecord(Thread* thread,
|
||||
RecordShape shape,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP) {
|
||||
const intptr_t num_fields = shape.num_fields();
|
||||
RecordPtr result;
|
||||
if (TryAllocate(thread, kRecordCid, Record::InstanceSize(num_fields),
|
||||
reinterpret_cast<ObjectPtr*>(&result))) {
|
||||
result->untag()->set_shape(shape.AsSmi());
|
||||
ObjectPtr null_value = Object::null();
|
||||
for (intptr_t i = 0; i < num_fields; i++) {
|
||||
result->untag()->set_field(i, null_value, thread);
|
||||
}
|
||||
SP[0] = result;
|
||||
return true;
|
||||
} else {
|
||||
SP[0] = 0; // Space for the result.
|
||||
SP[1] = shape.AsSmi();
|
||||
Exit(thread, FP, SP + 2, pc);
|
||||
NativeArguments args(thread, 1, SP + 1, SP);
|
||||
return InvokeRuntime(thread, this, DRT_AllocateRecord, args);
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a _Context with the given length and put it into SP[0].
|
||||
// Returns false on exception.
|
||||
bool Interpreter::AllocateContext(Thread* thread,
|
||||
@@ -2293,6 +2320,14 @@ SwitchDispatch:
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(LoadRecordField, D);
|
||||
const intptr_t field_index = rD;
|
||||
RecordPtr record = Record::RawCast(SP[0]);
|
||||
SP[0] = record->untag()->field(field_index);
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(AllocateContext, A_E);
|
||||
++SP;
|
||||
@@ -2411,6 +2446,24 @@ SwitchDispatch:
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(AllocateRecord, D);
|
||||
RecordTypePtr type = RecordType::RawCast(LOAD_CONSTANT(rD));
|
||||
RecordShape shape(Smi::RawCast(type->untag()->shape()));
|
||||
++SP;
|
||||
if (!AllocateRecord(thread, shape, pc, FP, SP)) {
|
||||
HANDLE_EXCEPTION;
|
||||
}
|
||||
RecordPtr record = Record::RawCast(SP[0]);
|
||||
const intptr_t num_fields = shape.num_fields();
|
||||
for (intptr_t i = 0; i < num_fields; ++i) {
|
||||
record->untag()->set_field(i, SP[-num_fields + i], thread);
|
||||
}
|
||||
SP -= num_fields;
|
||||
SP[0] = record;
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
{
|
||||
BYTECODE(AssertAssignable, A_E);
|
||||
// Stack: instance, type, instantiator type args, function type args, name
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "vm/compiler/method_recognizer.h"
|
||||
#include "vm/constants_kbc.h"
|
||||
#include "vm/object.h"
|
||||
#include "vm/tagged_pointer.h"
|
||||
|
||||
namespace dart {
|
||||
@@ -229,6 +230,11 @@ class Interpreter {
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateRecord(Thread* thread,
|
||||
RecordShape shape,
|
||||
const KBCInstr* pc,
|
||||
ObjectPtr* FP,
|
||||
ObjectPtr* SP);
|
||||
bool AllocateContext(Thread* thread,
|
||||
intptr_t num_variables,
|
||||
const KBCInstr* pc,
|
||||
|
||||
Reference in New Issue
Block a user