9f49b47823
This is a major revamp of bytecode metadata format. Now bytecode has its own serialization mechanisms. This CL adds 'bytecode component' metadata, which contains bytecode object table and string table. All references from bytecode (constant pools) to libraries, classes, members, types and strings now have a new format. References to frequently used objects are represented as indices in object table, while rarely used objects are written inline. This allows VM to cache frequently used objects while reading bytecode. Representation of strings is aligned with VM - string characters are stored in separate pools of one-byte and two-byte strings. This allows VM to avoid UTF-8 decoding and extra copying. Closure declarations are now explicit. Type parameters no longer require enslosing scopes when reading/writing them. Benchmarks: GenKernelKernelReadAllBytecode (Intel Core i5) +29.84% GenKernelKernelReadAllBytecode (Intel Xeon) +28.74% Change-Id: I4b80009733a8f8c038264af74f97c4e094b9e311 Reviewed-on: https://dart-review.googlesource.com/c/85469 Commit-Queue: Alexander Markov <alexmarkov@google.com> Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Zach Anderson <zra@google.com>
140 lines
4.0 KiB
Dart
140 lines
4.0 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.exceptions;
|
|
|
|
import 'bytecode_serialization.dart' show BufferedWriter, BufferedReader;
|
|
|
|
/*
|
|
|
|
In kernel binary, try blocks are encoded in the following way
|
|
(using notation from pkg/kernel/binary.md):
|
|
|
|
// Offset of a bytecode instruction, in DBC words.
|
|
type BytecodeOffset = UInt;
|
|
|
|
type TryBlock {
|
|
UInt outerTryIndexPlus1;
|
|
BytecodeOffset startPC; // Inclusive.
|
|
BytecodeOffset endPC; // Exclusive.
|
|
BytecodeOffset handlerPC;
|
|
Byte flags (needsStackTrace, isSynthetic);
|
|
List<ConstantIndex> types;
|
|
}
|
|
|
|
type ExceptionsTable {
|
|
// Ordered by startPC, then by nesting (outer precedes inner).
|
|
// Try blocks are properly nested. It means there are no partially
|
|
// overlapping try blocks - each pair of try block regions either
|
|
// has no intersection or one try block region encloses another.
|
|
List<TryBlock> tryBlocks;
|
|
}
|
|
|
|
*/
|
|
|
|
class TryBlock {
|
|
static const int flagNeedsStackTrace = 1 << 0;
|
|
static const int flagIsSynthetic = 1 << 1;
|
|
|
|
final int tryIndex;
|
|
final int outerTryIndex;
|
|
final int startPC;
|
|
int endPC;
|
|
int handlerPC;
|
|
int flags = 0;
|
|
List<int> types = <int>[];
|
|
|
|
TryBlock._(this.tryIndex, this.outerTryIndex, this.startPC);
|
|
|
|
bool get needsStackTrace => (flags & flagNeedsStackTrace) != 0;
|
|
|
|
void set needsStackTrace(bool value) {
|
|
flags = (flags & ~flagNeedsStackTrace) | (value ? flagNeedsStackTrace : 0);
|
|
}
|
|
|
|
bool get isSynthetic => (flags & flagIsSynthetic) != 0;
|
|
|
|
void set isSynthetic(bool value) {
|
|
flags = (flags & ~flagIsSynthetic) | (value ? flagIsSynthetic : 0);
|
|
}
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(outerTryIndex + 1);
|
|
writer.writePackedUInt30(startPC);
|
|
writer.writePackedUInt30(endPC);
|
|
writer.writePackedUInt30(handlerPC);
|
|
writer.writeByte(flags);
|
|
writer.writePackedUInt30(types.length);
|
|
types.forEach(writer.writePackedUInt30);
|
|
}
|
|
|
|
factory TryBlock.read(BufferedReader reader, int tryIndex) {
|
|
final outerTryIndex = reader.readPackedUInt30() - 1;
|
|
final startPC = reader.readPackedUInt30();
|
|
final tryBlock = new TryBlock._(tryIndex, outerTryIndex, startPC);
|
|
|
|
tryBlock.endPC = reader.readPackedUInt30();
|
|
tryBlock.handlerPC = reader.readPackedUInt30();
|
|
tryBlock.flags = reader.readByte();
|
|
tryBlock.types = new List<int>.generate(
|
|
reader.readPackedUInt30(), (_) => reader.readPackedUInt30());
|
|
|
|
return tryBlock;
|
|
}
|
|
|
|
@override
|
|
String toString() => 'try-index $tryIndex, outer $outerTryIndex, '
|
|
'start $startPC, end $endPC, handler $handlerPC, '
|
|
'${needsStackTrace ? 'needs-stack-trace, ' : ''}'
|
|
'${isSynthetic ? 'synthetic, ' : ''}'
|
|
'types ${types.map((t) => 'CP#$t').toList()}';
|
|
}
|
|
|
|
class ExceptionsTable {
|
|
List<TryBlock> blocks = <TryBlock>[];
|
|
|
|
ExceptionsTable();
|
|
|
|
TryBlock enterTryBlock(int startPC) {
|
|
assert(blocks.isEmpty || blocks.last.startPC <= startPC);
|
|
final tryBlock =
|
|
new TryBlock._(blocks.length, _outerTryBlockIndex(startPC), startPC);
|
|
blocks.add(tryBlock);
|
|
return tryBlock;
|
|
}
|
|
|
|
int _outerTryBlockIndex(int startPC) {
|
|
for (int i = blocks.length - 1; i >= 0; --i) {
|
|
final tryBlock = blocks[i];
|
|
if (tryBlock.endPC == null || tryBlock.endPC > startPC) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void write(BufferedWriter writer) {
|
|
writer.writePackedUInt30(blocks.length);
|
|
blocks.forEach((b) => b.write(writer));
|
|
}
|
|
|
|
ExceptionsTable.read(BufferedReader reader)
|
|
: blocks = new List<TryBlock>.generate(reader.readPackedUInt30(),
|
|
(int index) => new TryBlock.read(reader, index));
|
|
|
|
@override
|
|
String toString() {
|
|
if (blocks.isEmpty) {
|
|
return '';
|
|
}
|
|
StringBuffer sb = new StringBuffer();
|
|
sb.writeln('ExceptionsTable {');
|
|
for (var tryBlock in blocks) {
|
|
sb.writeln(' $tryBlock');
|
|
}
|
|
sb.writeln('}');
|
|
return sb.toString();
|
|
}
|
|
}
|