bd698307ac
Issue: https://github.com/dart-lang/sdk/issues/61635 Change-Id: I4a6093afc01a2c68143e6e7d29d7661e17854a0b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497080 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
197 lines
5.8 KiB
Dart
197 lines
5.8 KiB
Dart
// Copyright (c) 2026, 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/global_context.dart';
|
|
import 'package:cfg/utils/misc.dart';
|
|
import 'package:kernel/ast.dart' as ast;
|
|
import 'package:native_compiler/runtime/vm_defs.dart';
|
|
|
|
/// Computes layout of Dart objects (field offsets and instance size).
|
|
class ObjectLayout {
|
|
final VMOffsets vmOffsets;
|
|
final int wordSize;
|
|
final int compressedWordSize;
|
|
|
|
/// Instance size before rounding.
|
|
final Map<ast.Class, int> _instanceSize = {};
|
|
|
|
/// Name of the implicit type arguments field.
|
|
late final ast.Name _typeArgumentsFieldName = ast.Name('#typeArguments');
|
|
|
|
/// Implicit type arguments field for a class.
|
|
final Map<ast.Class, CField> _typeArgumentsField = {};
|
|
|
|
/// Field offsets.
|
|
final Map<CField, int> _fieldOffset = {};
|
|
|
|
ObjectLayout(
|
|
this.vmOffsets, {
|
|
required this.wordSize,
|
|
required this.compressedWordSize,
|
|
});
|
|
|
|
/// Return true if [value] can be represented as a Smi (small integer).
|
|
bool isSmi(int value) {
|
|
final shiftedOut = value >> smiBits(compressedWordSize);
|
|
return shiftedOut == 0 || shiftedOut == -1;
|
|
}
|
|
|
|
int getUnalignedInstanceSize(ast.Class cls) {
|
|
_ensureComputed(cls);
|
|
return _instanceSize[cls]!;
|
|
}
|
|
|
|
int getInstanceSize(ast.Class cls) {
|
|
return roundUp(getUnalignedInstanceSize(cls), objectAlignment(wordSize));
|
|
}
|
|
|
|
int getFieldOffset(CField field) {
|
|
assert(!field.isStatic);
|
|
_ensureComputed(field.enclosingClass);
|
|
return _fieldOffset[field]!;
|
|
}
|
|
|
|
bool isUnboxedField(CField field) {
|
|
// TODO: support unboxed fields.
|
|
return false;
|
|
}
|
|
|
|
CField? getTypeArgumentsField(ast.Class cls) {
|
|
_ensureComputed(cls);
|
|
return _typeArgumentsField[cls];
|
|
}
|
|
|
|
bool _isComputed(ast.Class cls) => _instanceSize.containsKey(cls);
|
|
|
|
void _ensureComputed(ast.Class cls) {
|
|
if (!_isComputed(cls)) {
|
|
_computeLayout(cls);
|
|
}
|
|
}
|
|
|
|
void _computeLayout(ast.Class cls) {
|
|
if (_computeLayoutOfBuiltInClass(cls)) {
|
|
return;
|
|
}
|
|
|
|
final superclass = cls.superclass;
|
|
if (superclass != null) {
|
|
_ensureComputed(superclass);
|
|
}
|
|
|
|
int nextOffset = _instanceSize[superclass]!;
|
|
|
|
final superTypeArgs = superclass != null
|
|
? _typeArgumentsField[superclass]
|
|
: null;
|
|
if (superTypeArgs != null) {
|
|
// Inherit type arguments field from generic superclass.
|
|
_typeArgumentsField[cls] = superTypeArgs;
|
|
} else if (cls.typeParameters.isNotEmpty) {
|
|
// This class is generic but superclass is not, so
|
|
// introduce a new implicit type arguments field.
|
|
final typeArgs = _createTypeArgumentsField(cls);
|
|
_fieldOffset[typeArgs] = nextOffset;
|
|
nextOffset += compressedWordSize;
|
|
}
|
|
|
|
for (final field in cls.fields) {
|
|
if (!field.isStatic) {
|
|
_fieldOffset[CField(field)] = nextOffset;
|
|
nextOffset += compressedWordSize;
|
|
}
|
|
}
|
|
|
|
_instanceSize[cls] = nextOffset;
|
|
}
|
|
|
|
CField _createTypeArgumentsField(ast.Class cls) {
|
|
final field = CField(
|
|
ast.Field.immutable(
|
|
_typeArgumentsFieldName,
|
|
isFinal: true,
|
|
isStatic: false,
|
|
fileUri: ast.dummyUri,
|
|
)..parent = cls,
|
|
);
|
|
_typeArgumentsField[cls] = field;
|
|
return field;
|
|
}
|
|
|
|
// Layout of built-in instances is specified either as
|
|
// 'int size' or '(int size, int typeArgsOffset)' if class is generic.
|
|
|
|
late final Map<String, Object> _dartCoreInstanceLayout = {
|
|
'_Double': vmOffsets.Double_InstanceSize,
|
|
'_GrowableList': (
|
|
vmOffsets.GrowableObjectArray_InstanceSize,
|
|
vmOffsets.GrowableObjectArray_type_arguments_offset,
|
|
),
|
|
'_Mint': vmOffsets.Mint_InstanceSize,
|
|
'_WeakProperty': vmOffsets.WeakProperty_InstanceSize,
|
|
'_WeakReference': (
|
|
vmOffsets.WeakReference_InstanceSize,
|
|
vmOffsets.WeakReference_type_arguments_offset,
|
|
),
|
|
'Object': vmOffsets.Instance_InstanceSize,
|
|
};
|
|
|
|
late final Map<String, Object> _dartTypedDataInstanceLayout = {
|
|
'_Int32x4': vmOffsets.Int32x4_InstanceSize,
|
|
'_Float32x4': vmOffsets.Float32x4_InstanceSize,
|
|
'_Float64x2': vmOffsets.Float64x2_InstanceSize,
|
|
// TODO: add other built-in classes from dart:typed_data
|
|
};
|
|
|
|
late final Map<String, Object> _dartCompactHashInstanceLayout = {
|
|
'_LinkedHashBase': (
|
|
vmOffsets.LinkedHashBase_InstanceSize,
|
|
vmOffsets.LinkedHashBase_type_arguments_offset,
|
|
),
|
|
};
|
|
|
|
late final ast.Library _typedDataLibrary = GlobalContext
|
|
.instance
|
|
.coreTypes
|
|
.index
|
|
.getLibrary('dart:typed_data');
|
|
late final ast.Library _compactHashLibrary = GlobalContext
|
|
.instance
|
|
.coreTypes
|
|
.index
|
|
.getLibrary('dart:_compact_hash');
|
|
|
|
bool _computeLayoutOfBuiltInClass(ast.Class cls) {
|
|
final library = cls.enclosingLibrary;
|
|
if (!library.importUri.isScheme('dart')) {
|
|
return false;
|
|
}
|
|
Object? layout;
|
|
if (library == GlobalContext.instance.coreTypes.coreLibrary) {
|
|
layout = _dartCoreInstanceLayout[cls.name];
|
|
} else if (library == _typedDataLibrary) {
|
|
layout = _dartTypedDataInstanceLayout[cls.name];
|
|
} else if (library == _compactHashLibrary) {
|
|
layout = _dartCompactHashInstanceLayout[cls.name];
|
|
}
|
|
// TODO: add built-in classes from dart:ffi
|
|
if (layout != null) {
|
|
switch (layout) {
|
|
case int():
|
|
_instanceSize[cls] = layout;
|
|
break;
|
|
case (int size, int typeArgsOffset):
|
|
_instanceSize[cls] = size;
|
|
_fieldOffset[_createTypeArgumentsField(cls)] = typeArgsOffset;
|
|
break;
|
|
default:
|
|
throw 'Unexpected built-in class layout ${layout.runtimeType} $layout';
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|