blob: 08606ddd609e71220c0971bcd688e3f78d5ae0e3 [file] [log] [blame]
// Copyright (c) 2020, 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:kernel/ast.dart';
import 'package:vm/transformations/type_flow/types.dart' show RecordShape;
enum UnboxingKind {
boxed,
int,
double,
record,
unknown, // Not calculated yet.
}
class UnboxingType {
final UnboxingKind kind;
final RecordShape? recordShape;
const UnboxingType._(this.kind, this.recordShape);
UnboxingType.record(RecordShape shape) : this._(UnboxingKind.record, shape);
static const kUnknown = UnboxingType._(UnboxingKind.unknown, null);
static const kInt = UnboxingType._(UnboxingKind.int, null);
static const kDouble = UnboxingType._(UnboxingKind.double, null);
static const kBoxed = UnboxingType._(UnboxingKind.boxed, null);
UnboxingType intersect(UnboxingType other) {
if (kind == UnboxingKind.unknown) return other;
if (other.kind == UnboxingKind.unknown) return this;
if (this == other) return this;
return kBoxed;
}
@override
bool operator ==(other) =>
identical(this, other) ||
(other is UnboxingType &&
this.kind == other.kind &&
this.recordShape == other.recordShape);
@override
int get hashCode => (kind.index * 31) + recordShape.hashCode;
void writeToBinary(BinarySink sink) {
sink.writeUInt30(kind.index);
if (kind == UnboxingKind.record) {
recordShape!.writeToBinary(sink);
}
}
factory UnboxingType.readFromBinary(BinarySource source) {
final kind = UnboxingKind.values[source.readUInt30()];
final recordShape =
(kind == UnboxingKind.record)
? RecordShape.readFromBinary(source)
: null;
return UnboxingType._(kind, recordShape);
}
@override
String toString() {
switch (kind) {
case UnboxingKind.boxed:
return 'b';
case UnboxingKind.int:
return 'i';
case UnboxingKind.double:
return 'd';
case UnboxingKind.record:
{
final sb = StringBuffer();
sb.write('r<');
sb.write(recordShape!.numPositionalFields.toString());
for (final named in recordShape!.namedFields) {
sb.write(',');
sb.write(named);
}
sb.write('>');
return sb.toString();
}
case UnboxingKind.unknown:
return '_|_';
}
}
}
class UnboxingInfoMetadata {
/// For GDT selectors the length of this array reflects minimum number of
/// direct parameters (excluding this) across all implementations reachable
/// through a selector. If there is an override with less direct parameters
/// than interface target has declared then
/// [hasOverridesWithLessDirectParameters] must be set to `true`.
final List<UnboxingType> argsInfo;
UnboxingType returnInfo;
bool mustUseStackCallingConvention;
bool hasOverridesWithLessDirectParameters = false;
UnboxingInfoMetadata(
int argsLen, {
UnboxingType initialValue = UnboxingType.kUnknown,
}) : argsInfo = List<UnboxingType>.filled(
argsLen,
initialValue,
growable: true,
),
returnInfo = initialValue,
mustUseStackCallingConvention = false;
factory UnboxingInfoMetadata.readFromBinary(BinarySource source) {
final result = UnboxingInfoMetadata(
source.readUInt30(),
initialValue: UnboxingType.kBoxed,
);
final flags = source.readByte();
result.mustUseStackCallingConvention =
(flags & _mustUseStackCallingConventionFlag) != 0;
result.hasOverridesWithLessDirectParameters =
(flags & _hasOverridesWithLessDirectParametersFlag) != 0;
if ((flags & _hasUnboxedParameterOrReturnValueFlag) != 0) {
for (var i = 0; i < result.argsInfo.length; i++) {
result.argsInfo[i] = UnboxingType.readFromBinary(source);
}
result.returnInfo = UnboxingType.readFromBinary(source);
}
return result;
}
void adjustParameterCount(int argsLen) {
if (argsLen != argsInfo.length) {
hasOverridesWithLessDirectParameters = true;
}
if (argsLen < argsInfo.length) {
argsInfo.length = argsLen;
}
}
bool get hasUnboxedParameterOrReturnValue {
if (returnInfo != UnboxingType.kBoxed) return true;
for (final argInfo in argsInfo) {
if (argInfo != UnboxingType.kBoxed) return true;
}
return false;
}
void setFullyBoxed() {
argsInfo.length = 0;
returnInfo = UnboxingType.kBoxed;
mustUseStackCallingConvention = true;
}
// Returns `true` if this [UnboxingInfoMetadata] matches default one:
// all arguments and the return value are boxed, the method is not
// forced to use stack based calling convention and there is no override
// which uses less direct parameters.
//
// Trivial metadata can be omitted and not written into the Kernel binary.
bool get isTrivial {
return !mustUseStackCallingConvention &&
!hasOverridesWithLessDirectParameters &&
!hasUnboxedParameterOrReturnValue;
}
static const _mustUseStackCallingConventionFlag = 1 << 0;
static const _hasUnboxedParameterOrReturnValueFlag = 1 << 1;
static const _hasOverridesWithLessDirectParametersFlag = 1 << 2;
void writeToBinary(BinarySink sink) {
sink.writeUInt30(argsInfo.length);
final flags =
(mustUseStackCallingConvention
? _mustUseStackCallingConventionFlag
: 0) |
(hasUnboxedParameterOrReturnValue
? _hasUnboxedParameterOrReturnValueFlag
: 0) |
(hasOverridesWithLessDirectParameters
? _hasOverridesWithLessDirectParametersFlag
: 0);
sink.writeByte(flags);
if ((flags & _hasUnboxedParameterOrReturnValueFlag) != 0) {
for (final argInfo in argsInfo) {
argInfo.writeToBinary(sink);
}
returnInfo.writeToBinary(sink);
}
}
/// Remove placeholder parameter info slot for setters that the getter is
/// grouped with.
UnboxingInfoMetadata toGetterInfo() =>
UnboxingInfoMetadata(0)
..returnInfo = returnInfo
..mustUseStackCallingConvention = mustUseStackCallingConvention;
UnboxingInfoMetadata toFieldInfo() {
if (argsInfo.length == 1 && argsInfo[0] == UnboxingType.kUnknown) {
// Drop information about the setter if we did not compute anything
// useful.
return toGetterInfo();
}
return this;
}
@override
String toString() {
final sb = StringBuffer();
if (mustUseStackCallingConvention) {
return '[!regcc]';
}
sb.write('(');
for (int i = 0; i < argsInfo.length; ++i) {
final argInfo = argsInfo[i];
sb.write(argInfo.toString());
if (i != (argsInfo.length - 1)) {
sb.write(',');
}
}
sb.write(')');
sb.write('->');
sb.write(returnInfo.toString());
return sb.toString();
}
}
class UnboxingInfoMetadataRepository
extends MetadataRepository<UnboxingInfoMetadata> {
static const repositoryTag = 'vm.unboxing-info.metadata';
@override
final String tag = repositoryTag;
@override
final Map<TreeNode, UnboxingInfoMetadata> mapping =
<TreeNode, UnboxingInfoMetadata>{};
@override
void writeToBinary(
UnboxingInfoMetadata metadata,
Node node,
BinarySink sink,
) {
metadata.writeToBinary(sink);
}
@override
UnboxingInfoMetadata readFromBinary(Node node, BinarySource source) {
return UnboxingInfoMetadata.readFromBinary(source);
}
}