blob: 1812bbb361279246c4b5888cae5d08e0b962609b [file]
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:cfg/ir/field.dart';
import 'package:cfg/ir/functions.dart';
import 'package:cfg/ir/instructions.dart';
import 'package:cfg/utils/misc.dart';
import 'package:kernel/ast.dart' as ast;
import 'package:native_compiler/runtime/names.dart';
/// Helper class for building object pool accessible from generated code.
class ObjectPool {
final List<Object> entries = [];
final Map<Object, int> _objects = {};
int getObject(Object entry) => _objects[entry] ??= _addEntry(entry);
int _addEntry(Object entry) {
final index = entries.length;
entries.add(entry);
if (entry is SpecializedEntry) {
for (int i = 0, n = entry.numReservedEntries; i < n; ++i) {
entries.add(const ReservedEntry());
}
}
return index;
}
}
/// Base class for specialized object pool entries which are not just
/// object references.
sealed class const SpecializedEntry() {
/// Number of object pool entries reserved after this entry.
int get numReservedEntries => 0;
}
/// Base class for specialized object pool entries which
/// occupy 2 slots in the object pool.
sealed class const PairSpecializedEntry() extends SpecializedEntry {
/// Number of object pool entries reserved after this entry.
@override
int get numReservedEntries => 1;
}
/// Object pool entry representing tags for the new objects of the given class.
final class NewObjectTags(final ast.Class cls) extends SpecializedEntry {
@override
int get hashCode => cls.hashCode + 19;
@override
bool operator ==(Object other) =>
other is NewObjectTags && this.cls == other.cls;
}
/// ICData call object pool entries occupies 2 slots: ICData, dispatcher code.
sealed class ICDataCallEntry(
final CFunction owner,
final ArgumentsShape argumentsShape, {
required final Name selector,
}) extends PairSpecializedEntry {
@override
int get hashCode =>
finalizeHash(combineHash(selector.hashCode, argumentsShape.hashCode));
@override
bool operator ==(Object other) =>
other is ICDataCallEntry &&
this.owner == other.owner &&
this.argumentsShape == other.argumentsShape &&
this.selector == other.selector;
}
/// InterfaceCall object pool entry occupies 2 slots: dispatcher data, dispatcher code.
/// TODO: switch from ICData calls to dispatch table calls.
final class InterfaceCallEntry extends ICDataCallEntry {
InterfaceCallEntry(
super.owner,
super.argumentsShape,
CFunction interfaceTarget,
) : super(selector: Name.interfaceCallSelector(interfaceTarget));
}
/// DynamicCall object pool entry occupies 2 slots: ICData, dispatcher code.
final class DynamicCallEntry extends ICDataCallEntry {
DynamicCallEntry(
super.owner,
super.argumentsShape,
DynamicCallKind kind,
ast.Name selector,
) : super(selector: Name.dynamicCallSelector(kind, selector));
}
/// Reserved entry, filled from a preceeding [SpecializedEntry]
/// with a non-zero [numReservedEntries].
final class const ReservedEntry() extends SpecializedEntry {
@override
int get numReservedEntries => 0;
}
/// Object pool entry representing offset of the static field
/// relative to static field table.
final class StaticFieldOffset(final CField field) extends SpecializedEntry {
@override
int get hashCode => field.hashCode + 13;
@override
bool operator ==(Object other) =>
other is StaticFieldOffset && this.field == other.field;
}
/// Object pool entry representing a subtype test cache.
/// This is not a specialized entry, it is encoded as a regular object reference.
final class SubtypeTestCache(final int numInputs) {
// Use identity hashCode and == as separate subtype test caches are
// used for each type check.
}
/// A pair (subtype test cache, name). VM decodes type testing stub
/// calling sequence and reads name from object pool immediately
/// after subtype test cache when throwing type errors.
final class SubtypeTestCacheWithName extends PairSpecializedEntry {
final SubtypeTestCache stc;
final Name name;
SubtypeTestCacheWithName(this.stc, this.name);
@override
int get hashCode => finalizeHash(combineHash(stc.hashCode, name.hashCode));
@override
bool operator ==(Object other) =>
other is SubtypeTestCacheWithName &&
this.stc == other.stc &&
this.name == other.name;
}