[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:
Alexander Markov
2024-08-28 13:12:48 +00:00
committed by Commit Queue
parent e65551b7ea
commit 2e63d3135a
8 changed files with 179 additions and 4 deletions
+10
View File
@@ -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);
}
}
+25 -3
View File
@@ -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
+10
View File
@@ -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.
+18
View File
@@ -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(
+53 -1
View File
@@ -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();
+4
View File
@@ -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.
+53
View File
@@ -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
+6
View File
@@ -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,