Files
sdk/pkg/vm/lib/bytecode/constant_pool.dart
T
Alexander Markov 13a69b5631 [vm/bytecode] Replace ICData constant pool entries with DynamicCall
VM no longer relies on distinct ICData entries in constant pool, and
it no longer creates actual ICData objects.

This change introduces new DynamicCall constant pool entry which is
 * de-duplicated;
 * stores arguments descriptor inline instead of allocating a separate
   constant pool entry and referencing it by index;
 * reserves extra slot in constant pool (like other call constants), so
   we can eventually remove creation of extra UnlinkedCall objects.

Change-Id: I8d21954803ca5be5b6fc8335b700e8a9086245cf
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/116120
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Régis Crelier <regis@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
2019-09-07 02:17:37 +00:00

854 lines
23 KiB
Dart

// Copyright (c) 2018, 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.
library vm.bytecode.constant_pool;
import 'package:kernel/ast.dart' hide MapEntry;
import 'dbc.dart' show constantPoolIndexLimit, BytecodeLimitExceededException;
import 'bytecode_serialization.dart'
show
BufferedWriter,
BufferedReader,
BytecodeSizeStatistics,
NamedEntryStatistics,
StringTable;
import 'object_table.dart' show ObjectHandle, ObjectTable;
/*
In kernel binary, constant pool is encoded in the following way
(using notation from pkg/kernel/binary.md):
type ConstantPool {
List<ConstantPoolEntry>
}
type ConstantIndex = UInt;
abstract type ConstantPoolEntry {
Byte tag;
}
type ConstantStaticField extends ConstantPoolEntry {
Byte tag = 9;
PackedObject field;
}
// Occupies 2 entries in the constant pool.
type ConstantInstanceField extends ConstantPoolEntry {
Byte tag = 10;
PackedObject field;
}
type ConstantClass extends ConstantPoolEntry {
Byte tag = 11;
PackedObject class;
}
type ConstantTypeArgumentsField extends ConstantPoolEntry {
Byte tag = 12;
PackedObject class;
}
type ConstantType extends ConstantPoolEntry {
Byte tag = 14;
PackedObject type;
}
type ConstantClosureFunction extends ConstantPoolEntry {
Byte tag = 19;
UInt closureIndex;
}
type ConstantEndClosureFunctionScope extends ConstantPoolEntry {
Byte tag = 20;
}
type ConstantNativeEntry extends ConstantPoolEntry {
Byte tag = 21;
PackedString nativeName;
}
type ConstantSubtypeTestCache extends ConstantPoolEntry {
Byte tag = 22;
}
type ConstantEmptyTypeArguments extends ConstantPoolEntry {
Byte tag = 24;
}
type ConstantObjectRef extends ConstantPoolEntry {
Byte tag = 27;
PackedObject object;
}
// Occupies 2 entries in the constant pool.
type ConstantDirectCall extends ConstantPoolEntry {
Byte tag = 28;
PackedObject target;
PackedObject argDesc;
}
// Occupies 2 entries in the constant pool.
type ConstantInterfaceCall extends ConstantPoolEntry {
Byte tag = 29;
PackedObject target;
PackedObject argDesc;
}
// Occupies 3 entries in the constant pool.
type ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
Byte tag = 30;
PackedObject target;
PackedObject argDesc;
PackedObject staticReceiverType;
}
// Occupies 2 entries in the constant pool
type ConstantDynamicCall extends ConstantPoolEntry {
Byte tag = 31;
PackedObject selectorName;
PackedObject argDesc;
}
*/
enum ConstantTag {
kInvalid,
kUnused1,
kUnused2,
kUnused3,
kUnused4,
kUnused5,
kUnused6,
kUnused6a,
kUnused7,
kStaticField,
kInstanceField,
kClass,
kTypeArgumentsField,
kUnused8,
kType,
kUnused9,
kUnused10,
kUnused11,
kUnused12,
kClosureFunction,
kEndClosureFunctionScope,
kNativeEntry,
kSubtypeTestCache,
kUnused13,
kEmptyTypeArguments,
kUnused14,
kUnused15,
kObjectRef,
kDirectCall,
kInterfaceCall,
kInstantiatedInterfaceCall,
kDynamicCall,
}
String constantTagToString(ConstantTag tag) =>
tag.toString().substring('ConstantTag.k'.length);
abstract class ConstantPoolEntry {
const ConstantPoolEntry();
ConstantTag get tag;
// Returns number of extra reserved constant pool entries
// following this entry.
int get numReservedEntries => 0;
void write(BufferedWriter writer) {
writer.writeByte(tag.index);
writeValue(writer);
}
void writeValue(BufferedWriter writer);
factory ConstantPoolEntry.read(BufferedReader reader) {
ConstantTag tag = ConstantTag.values[reader.readByte()];
switch (tag) {
case ConstantTag.kInvalid:
break;
case ConstantTag.kStaticField:
return new ConstantStaticField.read(reader);
case ConstantTag.kInstanceField:
return new ConstantInstanceField.read(reader);
case ConstantTag.kClass:
return new ConstantClass.read(reader);
case ConstantTag.kTypeArgumentsField:
return new ConstantTypeArgumentsField.read(reader);
case ConstantTag.kType:
return new ConstantType.read(reader);
case ConstantTag.kClosureFunction:
return new ConstantClosureFunction.read(reader);
case ConstantTag.kEndClosureFunctionScope:
return new ConstantEndClosureFunctionScope.read(reader);
case ConstantTag.kNativeEntry:
return new ConstantNativeEntry.read(reader);
case ConstantTag.kSubtypeTestCache:
return new ConstantSubtypeTestCache.read(reader);
case ConstantTag.kEmptyTypeArguments:
return new ConstantEmptyTypeArguments.read(reader);
case ConstantTag.kObjectRef:
return new ConstantObjectRef.read(reader);
case ConstantTag.kDirectCall:
return new ConstantDirectCall.read(reader);
case ConstantTag.kInterfaceCall:
return new ConstantInterfaceCall.read(reader);
case ConstantTag.kInstantiatedInterfaceCall:
return new ConstantInstantiatedInterfaceCall.read(reader);
case ConstantTag.kDynamicCall:
return new ConstantDynamicCall.read(reader);
// Make analyzer happy.
case ConstantTag.kUnused1:
case ConstantTag.kUnused2:
case ConstantTag.kUnused3:
case ConstantTag.kUnused4:
case ConstantTag.kUnused5:
case ConstantTag.kUnused6:
case ConstantTag.kUnused6a:
case ConstantTag.kUnused7:
case ConstantTag.kUnused8:
case ConstantTag.kUnused9:
case ConstantTag.kUnused10:
case ConstantTag.kUnused11:
case ConstantTag.kUnused12:
case ConstantTag.kUnused13:
case ConstantTag.kUnused14:
case ConstantTag.kUnused15:
break;
}
throw 'Unexpected constant tag $tag';
}
}
enum InvocationKind {
method, // x.foo(...) or foo(...)
getter, // x.foo
setter // x.foo = ...
}
class ConstantStaticField extends ConstantPoolEntry {
final ObjectHandle field;
ConstantStaticField(this.field);
@override
ConstantTag get tag => ConstantTag.kStaticField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(field);
}
ConstantStaticField.read(BufferedReader reader)
: field = reader.readPackedObject();
@override
String toString() => 'StaticField $field';
@override
int get hashCode => field.hashCode;
@override
bool operator ==(other) =>
other is ConstantStaticField && this.field == other.field;
}
class ConstantInstanceField extends ConstantPoolEntry {
final ObjectHandle field;
int get numReservedEntries => 1;
ConstantInstanceField(this.field);
@override
ConstantTag get tag => ConstantTag.kInstanceField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(field);
}
ConstantInstanceField.read(BufferedReader reader)
: field = reader.readPackedObject();
@override
String toString() => 'InstanceField $field';
@override
int get hashCode => field.hashCode;
@override
bool operator ==(other) =>
other is ConstantInstanceField && this.field == other.field;
}
class ConstantClass extends ConstantPoolEntry {
final ObjectHandle classHandle;
ConstantClass(this.classHandle);
@override
ConstantTag get tag => ConstantTag.kClass;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(classHandle);
}
ConstantClass.read(BufferedReader reader)
: classHandle = reader.readPackedObject();
@override
String toString() => 'Class $classHandle';
@override
int get hashCode => classHandle.hashCode;
@override
bool operator ==(other) =>
other is ConstantClass && this.classHandle == other.classHandle;
}
class ConstantTypeArgumentsField extends ConstantPoolEntry {
final ObjectHandle classHandle;
ConstantTypeArgumentsField(this.classHandle);
@override
ConstantTag get tag => ConstantTag.kTypeArgumentsField;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(classHandle);
}
ConstantTypeArgumentsField.read(BufferedReader reader)
: classHandle = reader.readPackedObject();
@override
String toString() => 'TypeArgumentsField $classHandle';
@override
int get hashCode => classHandle.hashCode;
@override
bool operator ==(other) =>
other is ConstantTypeArgumentsField &&
this.classHandle == other.classHandle;
}
class ConstantType extends ConstantPoolEntry {
final ObjectHandle type;
ConstantType(this.type);
@override
ConstantTag get tag => ConstantTag.kType;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(type);
}
ConstantType.read(BufferedReader reader) : type = reader.readPackedObject();
@override
String toString() => 'Type $type';
@override
int get hashCode => type.hashCode;
@override
bool operator ==(other) => other is ConstantType && this.type == other.type;
}
class ConstantClosureFunction extends ConstantPoolEntry {
final int closureIndex;
ConstantClosureFunction(this.closureIndex);
@override
ConstantTag get tag => ConstantTag.kClosureFunction;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedUInt30(closureIndex);
}
ConstantClosureFunction.read(BufferedReader reader)
: closureIndex = reader.readPackedUInt30();
@override
String toString() {
return 'ClosureFunction $closureIndex';
}
@override
int get hashCode => closureIndex;
@override
bool operator ==(other) =>
other is ConstantClosureFunction &&
this.closureIndex == other.closureIndex;
}
class ConstantEndClosureFunctionScope extends ConstantPoolEntry {
ConstantEndClosureFunctionScope();
@override
ConstantTag get tag => ConstantTag.kEndClosureFunctionScope;
@override
void writeValue(BufferedWriter writer) {}
ConstantEndClosureFunctionScope.read(BufferedReader reader) {}
@override
String toString() => 'EndClosureFunctionScope';
// ConstantEndClosureFunctionScope entries are created per closure and should
// not be merged, so ConstantEndClosureFunctionScope class uses identity
// [hashCode] and [operator ==].
}
class ConstantNativeEntry extends ConstantPoolEntry {
final String nativeName;
ConstantNativeEntry(this.nativeName);
@override
ConstantTag get tag => ConstantTag.kNativeEntry;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedStringReference(nativeName);
}
ConstantNativeEntry.read(BufferedReader reader)
: nativeName = reader.readPackedStringReference();
@override
String toString() => 'NativeEntry $nativeName';
@override
int get hashCode => nativeName.hashCode;
@override
bool operator ==(other) =>
other is ConstantNativeEntry && this.nativeName == other.nativeName;
}
class ConstantSubtypeTestCache extends ConstantPoolEntry {
ConstantSubtypeTestCache();
@override
ConstantTag get tag => ConstantTag.kSubtypeTestCache;
@override
void writeValue(BufferedWriter writer) {}
ConstantSubtypeTestCache.read(BufferedReader reader);
@override
String toString() => 'SubtypeTestCache';
// ConstantSubtypeTestCache entries are created per subtype test site and
// should not be merged, so ConstantSubtypeTestCache class uses identity
// [hashCode] and [operator ==].
@override
int get hashCode => identityHashCode(this);
@override
bool operator ==(other) => identical(this, other);
}
class ConstantEmptyTypeArguments extends ConstantPoolEntry {
const ConstantEmptyTypeArguments();
@override
ConstantTag get tag => ConstantTag.kEmptyTypeArguments;
@override
void writeValue(BufferedWriter writer) {}
ConstantEmptyTypeArguments.read(BufferedReader reader);
@override
String toString() => 'EmptyTypeArguments';
@override
int get hashCode => 997;
@override
bool operator ==(other) => other is ConstantEmptyTypeArguments;
}
class ConstantObjectRef extends ConstantPoolEntry {
final ObjectHandle object;
ConstantObjectRef(this.object);
@override
ConstantTag get tag => ConstantTag.kObjectRef;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(object);
}
ConstantObjectRef.read(BufferedReader reader)
: object = reader.readPackedObject();
@override
String toString() => 'ObjectRef $object';
@override
int get hashCode => object.hashCode;
@override
bool operator ==(other) =>
other is ConstantObjectRef && this.object == other.object;
}
class ConstantDirectCall extends ConstantPoolEntry {
final ObjectHandle target;
final ObjectHandle argDesc;
ConstantDirectCall(this.target, this.argDesc);
// Reserve 1 extra slot for arguments descriptor, following target slot.
int get numReservedEntries => 1;
@override
ConstantTag get tag => ConstantTag.kDirectCall;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(target);
writer.writePackedObject(argDesc);
}
ConstantDirectCall.read(BufferedReader reader)
: target = reader.readPackedObject(),
argDesc = reader.readPackedObject();
@override
String toString() => "DirectCall '$target', $argDesc";
@override
int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode);
@override
bool operator ==(other) =>
other is ConstantDirectCall &&
this.target == other.target &&
this.argDesc == other.argDesc;
}
class ConstantInterfaceCall extends ConstantPoolEntry {
final ObjectHandle target;
final ObjectHandle argDesc;
ConstantInterfaceCall(this.target, this.argDesc);
// Reserve 1 extra slot for arguments descriptor, following target slot.
int get numReservedEntries => 1;
@override
ConstantTag get tag => ConstantTag.kInterfaceCall;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(target);
writer.writePackedObject(argDesc);
}
ConstantInterfaceCall.read(BufferedReader reader)
: target = reader.readPackedObject(),
argDesc = reader.readPackedObject();
@override
String toString() => "InterfaceCall '$target', $argDesc";
@override
int get hashCode => _combineHashes(target.hashCode, argDesc.hashCode);
@override
bool operator ==(other) =>
other is ConstantInterfaceCall &&
this.target == other.target &&
this.argDesc == other.argDesc;
}
class ConstantInstantiatedInterfaceCall extends ConstantPoolEntry {
final ObjectHandle target;
final ObjectHandle argDesc;
final ObjectHandle staticReceiverType;
ConstantInstantiatedInterfaceCall(
this.target, this.argDesc, this.staticReceiverType);
// Reserve 2 extra slots (3 slots total).
int get numReservedEntries => 2;
@override
ConstantTag get tag => ConstantTag.kInstantiatedInterfaceCall;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(target);
writer.writePackedObject(argDesc);
writer.writePackedObject(staticReceiverType);
}
ConstantInstantiatedInterfaceCall.read(BufferedReader reader)
: target = reader.readPackedObject(),
argDesc = reader.readPackedObject(),
staticReceiverType = reader.readPackedObject();
@override
String toString() =>
"InstantiatedInterfaceCall '$target', $argDesc, receiver $staticReceiverType";
@override
int get hashCode => _combineHashes(
_combineHashes(target.hashCode, argDesc.hashCode),
staticReceiverType.hashCode);
@override
bool operator ==(other) =>
other is ConstantInstantiatedInterfaceCall &&
this.target == other.target &&
this.argDesc == other.argDesc &&
this.staticReceiverType == other.staticReceiverType;
}
class ConstantDynamicCall extends ConstantPoolEntry {
final ObjectHandle selectorName;
final ObjectHandle argDesc;
ConstantDynamicCall(this.selectorName, this.argDesc);
// Reserve 1 extra slot for arguments descriptor, following selector slot.
int get numReservedEntries => 1;
@override
ConstantTag get tag => ConstantTag.kDynamicCall;
@override
void writeValue(BufferedWriter writer) {
writer.writePackedObject(selectorName);
writer.writePackedObject(argDesc);
}
ConstantDynamicCall.read(BufferedReader reader)
: selectorName = reader.readPackedObject(),
argDesc = reader.readPackedObject();
@override
String toString() => 'DynamicCall $selectorName, $argDesc';
@override
int get hashCode => _combineHashes(selectorName.hashCode, argDesc.hashCode);
@override
bool operator ==(other) =>
other is ConstantDynamicCall &&
this.selectorName == other.selectorName &&
this.argDesc == other.argDesc;
}
/// Reserved constant pool entry.
class _ReservedConstantPoolEntry extends ConstantPoolEntry {
const _ReservedConstantPoolEntry();
ConstantTag get tag => throw 'This constant pool entry is reserved';
void writeValue(BufferedWriter writer) =>
throw 'This constant pool entry is reserved';
@override
String toString() => 'Reserved';
}
class ConstantPool {
final StringTable stringTable;
final ObjectTable objectTable;
final List<ConstantPoolEntry> entries = <ConstantPoolEntry>[];
final Map<ConstantPoolEntry, int> _canonicalizationCache =
<ConstantPoolEntry, int>{};
ConstantPool(this.stringTable, this.objectTable);
int addString(String value) => addObjectRef(new StringConstant(value));
int addArgDesc(int numArguments,
{int numTypeArgs = 0, List<String> argNames = const <String>[]}) =>
_add(new ConstantObjectRef(
objectTable.getArgDescHandle(numArguments, numTypeArgs, argNames)));
int addArgDescByArguments(Arguments args,
{bool hasReceiver: false, bool isFactory: false}) =>
_add(new ConstantObjectRef(objectTable.getArgDescHandleByArguments(args,
hasReceiver: hasReceiver, isFactory: isFactory)));
int addDirectCall(
InvocationKind invocationKind, Member target, ObjectHandle argDesc) =>
_add(new ConstantDirectCall(
objectTable.getMemberHandle(target,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDesc));
int addInterfaceCall(
InvocationKind invocationKind, Member target, ObjectHandle argDesc) =>
_add(new ConstantInterfaceCall(
objectTable.getMemberHandle(target,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDesc));
int addInstantiatedInterfaceCall(InvocationKind invocationKind, Member target,
ObjectHandle argDesc, DartType staticReceiverType) =>
_add(new ConstantInstantiatedInterfaceCall(
objectTable.getMemberHandle(target,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDesc,
objectTable.getHandle(staticReceiverType)));
int addDynamicCall(
InvocationKind invocationKind, Name selector, ObjectHandle argDesc) =>
_add(new ConstantDynamicCall(
objectTable.getSelectorNameHandle(selector,
isGetter: invocationKind == InvocationKind.getter,
isSetter: invocationKind == InvocationKind.setter),
argDesc));
int addInstanceCall(InvocationKind invocationKind, Member target,
Name targetName, ObjectHandle argDesc) =>
(target == null)
? addDynamicCall(invocationKind, targetName, argDesc)
: addInterfaceCall(invocationKind, target, argDesc);
int addStaticField(Field field) =>
_add(new ConstantStaticField(objectTable.getHandle(field)));
int addInstanceField(Field field) =>
_add(new ConstantInstanceField(objectTable.getHandle(field)));
int addClass(Class node) =>
_add(new ConstantClass(objectTable.getHandle(node)));
int addTypeArgumentsField(Class node) =>
_add(new ConstantTypeArgumentsField(objectTable.getHandle(node)));
int addType(DartType type) =>
_add(new ConstantType(objectTable.getHandle(type)));
int addTypeArguments(List<DartType> typeArgs) =>
_add(new ConstantObjectRef(objectTable.getTypeArgumentsHandle(typeArgs)));
int addClosureFunction(int closureIndex) =>
_add(new ConstantClosureFunction(closureIndex));
int addEndClosureFunctionScope() =>
_add(new ConstantEndClosureFunctionScope());
int addNativeEntry(String nativeName) =>
_add(new ConstantNativeEntry(_indexString(nativeName)));
int addSubtypeTestCache() => _add(new ConstantSubtypeTestCache());
int addEmptyTypeArguments() => _add(const ConstantEmptyTypeArguments());
int addObjectRef(Node node) =>
_add(new ConstantObjectRef(objectTable.getHandle(node)));
int _add(ConstantPoolEntry entry) {
return _canonicalizationCache.putIfAbsent(entry, () {
int index = entries.length;
if (index >= constantPoolIndexLimit) {
throw new ConstantPoolIndexOverflowException();
}
_addEntry(entry);
return index;
});
}
void _addEntry(ConstantPoolEntry entry) {
entries.add(entry);
for (int i = 0; i < entry.numReservedEntries; ++i) {
entries.add(const _ReservedConstantPoolEntry());
}
}
// Currently, string table is written as a part of Component's metadata
// *before* constant pools are written.
// So we need to index all strings when filling up constant pools.
String _indexString(String str) {
stringTable.put(str);
return str;
}
void write(BufferedWriter writer) {
final start = writer.offset;
if (BytecodeSizeStatistics.constantPoolStats.isEmpty) {
for (var tag in ConstantTag.values) {
BytecodeSizeStatistics.constantPoolStats
.add(new NamedEntryStatistics(constantTagToString(tag)));
}
}
writer.writePackedUInt30(entries.length);
entries.forEach((e) {
if (e is _ReservedConstantPoolEntry) {
return;
}
final entryStart = writer.offset;
e.write(writer);
final entryStat = BytecodeSizeStatistics.constantPoolStats[e.tag.index];
entryStat.size += (writer.offset - entryStart);
++entryStat.count;
});
BytecodeSizeStatistics.constantPoolSize += (writer.offset - start);
}
ConstantPool.read(BufferedReader reader)
: stringTable = reader.stringReader,
objectTable = reader.objectReader {
int len = reader.readPackedUInt30();
for (int i = 0; i < len; i++) {
final e = new ConstantPoolEntry.read(reader);
_addEntry(e);
i += e.numReservedEntries;
}
}
@override
String toString() {
StringBuffer sb = new StringBuffer();
sb.writeln('ConstantPool {');
for (int i = 0; i < entries.length; i++) {
sb.writeln(' [$i] = ${entries[i]}');
}
sb.writeln('}');
return sb.toString();
}
}
int _combineHashes(int hash1, int hash2) =>
(((hash1 * 31) & 0x3fffffff) + hash2) & 0x3fffffff;
class ConstantPoolIndexOverflowException
extends BytecodeLimitExceededException {}