6202749429
Change-Id: I2882546741b1dd8f6e03a21d85b5487fb754b742 Reviewed-on: https://dart-review.googlesource.com/55324 Reviewed-by: Régis Crelier <regis@google.com> Reviewed-by: Zach Anderson <zra@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
192 lines
5.3 KiB
Dart
192 lines
5.3 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.disassembler;
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:vm/bytecode/dbc.dart';
|
|
import 'package:vm/bytecode/exceptions.dart';
|
|
|
|
class _Instruction {
|
|
final Opcode opcode;
|
|
final List<int> operands;
|
|
_Instruction(this.opcode, this.operands);
|
|
}
|
|
|
|
class BytecodeDisassembler {
|
|
static const int kOpcodeMask = 0xFF;
|
|
static const int kBitsPerInt = 64;
|
|
|
|
List<_Instruction> _instructions;
|
|
int _labelCount;
|
|
Map<int, String> _labels;
|
|
Map<int, List<String>> _markers;
|
|
|
|
String disassemble(List<int> bytecode, ExceptionsTable exceptionsTable) {
|
|
_init(bytecode);
|
|
_scanForJumpTargets();
|
|
_markTryBlocks(exceptionsTable);
|
|
return _disasm();
|
|
}
|
|
|
|
void _init(List<int> bytecode) {
|
|
final uint8list = new Uint8List.fromList(bytecode);
|
|
// TODO(alexmarkov): endianness?
|
|
Uint32List words = uint8list.buffer.asUint32List();
|
|
|
|
_instructions = new List<_Instruction>(words.length);
|
|
for (int i = 0; i < words.length; i++) {
|
|
_instructions[i] = _decodeInstruction(words[i]);
|
|
}
|
|
|
|
_labelCount = 0;
|
|
_labels = <int, String>{};
|
|
_markers = <int, List<String>>{};
|
|
}
|
|
|
|
_Instruction _decodeInstruction(int word) {
|
|
final opcode = Opcode.values[word & kOpcodeMask];
|
|
final format = BytecodeFormats[opcode];
|
|
return new _Instruction(opcode, _decodeOperands(format, word));
|
|
}
|
|
|
|
List<int> _decodeOperands(Format format, int word) {
|
|
switch (format.encoding) {
|
|
case Encoding.k0:
|
|
return const [];
|
|
case Encoding.kA:
|
|
return [_unsigned(word, 8, 8)];
|
|
case Encoding.kAD:
|
|
return [_unsigned(word, 8, 8), _unsigned(word, 16, 16)];
|
|
case Encoding.kAX:
|
|
return [_unsigned(word, 8, 8), _signed(word, 16, 16)];
|
|
case Encoding.kD:
|
|
return [_unsigned(word, 16, 16)];
|
|
case Encoding.kX:
|
|
return [_signed(word, 16, 16)];
|
|
case Encoding.kABC:
|
|
return [
|
|
_unsigned(word, 8, 8),
|
|
_unsigned(word, 16, 8),
|
|
_unsigned(word, 24, 8)
|
|
];
|
|
case Encoding.kABY:
|
|
return [
|
|
_unsigned(word, 8, 8),
|
|
_unsigned(word, 16, 8),
|
|
_signed(word, 24, 8)
|
|
];
|
|
case Encoding.kT:
|
|
return [_signed(word, 8, 24)];
|
|
}
|
|
throw 'Unexpected format $format';
|
|
}
|
|
|
|
int _unsigned(int word, int pos, int bits) =>
|
|
(word >> pos) & ((1 << bits) - 1);
|
|
|
|
int _signed(int word, int pos, int bits) =>
|
|
_unsigned(word, pos, bits) <<
|
|
(kBitsPerInt - bits) >>
|
|
(kBitsPerInt - bits);
|
|
|
|
void _scanForJumpTargets() {
|
|
for (int i = 0; i < _instructions.length; i++) {
|
|
final instr = _instructions[i];
|
|
if (instr.opcode == Opcode.kJump) {
|
|
final target = i + instr.operands[0];
|
|
assert(0 <= target && target < _instructions.length);
|
|
if (!_labels.containsKey(target)) {
|
|
final label = 'L${++_labelCount}';
|
|
_labels[target] = label;
|
|
_addMarker(target, '$label:');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void _markTryBlocks(ExceptionsTable exceptionsTable) {
|
|
for (var tryBlock in exceptionsTable.blocks) {
|
|
final int tryIndex = tryBlock.tryIndex;
|
|
_addMarker(tryBlock.startPC, 'Try #$tryIndex start:');
|
|
_addMarker(tryBlock.endPC, 'Try #$tryIndex end:');
|
|
_addMarker(tryBlock.handlerPC, 'Try #$tryIndex handler:');
|
|
}
|
|
}
|
|
|
|
void _addMarker(int pc, String marker) {
|
|
final markers = (_markers[pc] ??= <String>[]);
|
|
markers.add(marker);
|
|
}
|
|
|
|
String _disasm() {
|
|
StringBuffer out = new StringBuffer();
|
|
for (int i = 0; i < _instructions.length; i++) {
|
|
List<String> markers = _markers[i];
|
|
if (markers != null) {
|
|
markers.forEach(out.writeln);
|
|
}
|
|
_writeInstruction(out, i, _instructions[i]);
|
|
}
|
|
return out.toString();
|
|
}
|
|
|
|
void _writeInstruction(StringBuffer out, int bci, _Instruction instr) {
|
|
final format = BytecodeFormats[instr.opcode];
|
|
assert(format != null);
|
|
|
|
out.write(' ');
|
|
|
|
const int kOpcodeWidth = 20;
|
|
const String kOpcodePrefix = 'Opcode.k';
|
|
|
|
String opcode = instr.opcode.toString();
|
|
assert(opcode.startsWith(kOpcodePrefix));
|
|
opcode = opcode.substring(kOpcodePrefix.length);
|
|
|
|
if (instr.operands.isEmpty) {
|
|
out.writeln(opcode);
|
|
return;
|
|
}
|
|
|
|
out.write(opcode.padRight(kOpcodeWidth));
|
|
|
|
for (int i = 0; i < instr.operands.length; i++) {
|
|
if (i == 0) {
|
|
out.write(' ');
|
|
} else {
|
|
out.write(', ');
|
|
}
|
|
final operand =
|
|
_formatOperand(bci, format.operands[i], instr.operands[i]);
|
|
out.write(operand);
|
|
}
|
|
|
|
out.writeln();
|
|
}
|
|
|
|
String _formatOperand(int bci, Operand fmt, int value) {
|
|
switch (fmt) {
|
|
case Operand.none:
|
|
break;
|
|
case Operand.imm:
|
|
return '$value';
|
|
case Operand.lit:
|
|
return 'CP#$value';
|
|
case Operand.reg:
|
|
return 'r$value';
|
|
case Operand.xeg:
|
|
return (value < 0) ? 'FP[$value]' : 'r$value';
|
|
case Operand.tgt:
|
|
return _labels[bci + value] ?? (throw 'Label not found');
|
|
case Operand.spe:
|
|
return SpecialIndex.values[value]
|
|
.toString()
|
|
.substring('SpecialIndex.'.length);
|
|
}
|
|
throw 'Unexpected operand format $fmt';
|
|
}
|
|
}
|