// 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.assembler; import 'package:kernel/ast.dart' show TreeNode; import 'dbc.dart'; import 'exceptions.dart' show ExceptionsTable; import 'local_variable_table.dart' show LocalVariableTable; import 'source_positions.dart' show SourcePositions; class Label { final bool allowsBackwardJumps; List _jumps = []; int offset = -1; Label({this.allowsBackwardJumps: false}); bool get isBound => offset >= 0; int jumpOperand(int jumpOffset) { if (isBound) { if (offset <= jumpOffset && !allowsBackwardJumps) { throw 'Backward jump to this label is not allowed'; } // Jump instruction takes a relative offset. return offset - jumpOffset; } _jumps.add(jumpOffset); return 0; } List bind(int offset) { assert(!isBound); this.offset = offset; final jumps = _jumps; _jumps = null; return jumps; } } class BytecodeAssembler { static const int kByteMask = 0xFF; static const int kUint32Mask = 0xFFFFFFFF; static const int kMinInt8 = -0x80; static const int kMaxInt8 = 0x7F; static const int kMinInt24 = -0x800000; static const int kMaxInt24 = 0x7FFFFF; static const int kMinInt32 = -0x80000000; static const int kMaxInt32 = 0x7FFFFFFF; // TODO(alexmarkov): figure out more efficient storage for generated bytecode. final List bytecode = new List(); final ExceptionsTable exceptionsTable = new ExceptionsTable(); final LocalVariableTable localVariableTable = new LocalVariableTable(); final SourcePositions sourcePositions = new SourcePositions(); bool isUnreachable = false; int currentSourcePosition = TreeNode.noOffset; BytecodeAssembler(); int get offset => bytecode.length; void bind(Label label) { final List jumps = label.bind(offset); for (int jumpOffset in jumps) { _patchJump(jumpOffset, label.jumpOperand(jumpOffset)); } if (jumps.isNotEmpty || label.allowsBackwardJumps) { isUnreachable = false; } } void emitSourcePosition() { if (currentSourcePosition != TreeNode.noOffset && !isUnreachable) { sourcePositions.add(offset, currentSourcePosition); } } void emitYieldPointSourcePosition() { if (!isUnreachable) { sourcePositions.addYieldPoint(offset, currentSourcePosition); } } void _emitByte(int abyte) { assert(_isUint8(abyte)); bytecode.add(abyte); } void _emitBytes2(int b0, int b1) { assert(_isUint8(b0) && _isUint8(b1)); bytecode.add(b0); bytecode.add(b1); } void _emitBytes3(int b0, int b1, int b2) { assert(_isUint8(b0) && _isUint8(b1) && _isUint8(b2)); bytecode.add(b0); bytecode.add(b1); bytecode.add(b2); } void _emitBytes4(int b0, int b1, int b2, int b3) { assert(_isUint8(b0) && _isUint8(b1) && _isUint8(b2) && _isUint8(b3)); bytecode.add(b0); bytecode.add(b1); bytecode.add(b2); bytecode.add(b3); } void _emitBytes5(int b0, int b1, int b2, int b3, int b4) { assert(_isUint8(b0) && _isUint8(b1) && _isUint8(b2) && _isUint8(b3) && _isUint8(b4)); bytecode.add(b0); bytecode.add(b1); bytecode.add(b2); bytecode.add(b3); bytecode.add(b4); } void _emitBytes6(int b0, int b1, int b2, int b3, int b4, int b5) { assert(_isUint8(b0) && _isUint8(b1) && _isUint8(b2) && _isUint8(b3) && _isUint8(b4) && _isUint8(b5)); bytecode.add(b0); bytecode.add(b1); bytecode.add(b2); bytecode.add(b3); bytecode.add(b4); bytecode.add(b5); } int _byteAt(int pos) { return bytecode[pos]; } void _setByteAt(int pos, int value) { assert(_isUint8(value)); bytecode[pos] = value; } int _byte0(int v) => v & kByteMask; int _byte1(int v) => (v >> 8) & kByteMask; int _byte2(int v) => (v >> 16) & kByteMask; int _byte3(int v) => (v >> 24) & kByteMask; bool _isInt8(int v) => (kMinInt8 <= v) && (v <= kMaxInt8); bool _isInt24(int v) => (kMinInt24 <= v) && (v <= kMaxInt24); bool _isInt32(int v) => (kMinInt32 <= v) && (v <= kMaxInt32); bool _isUint8(int v) => (v & kByteMask) == v; bool _isUint32(int v) => (v & kUint32Mask) == v; void _emitInstruction0(Opcode opcode) { if (isUnreachable) { return; } _emitByte(opcode.index); } void _emitInstructionA(Opcode opcode, int ra) { if (isUnreachable) { return; } _emitBytes2(opcode.index, ra); } void _emitInstructionD(Opcode opcode, int rd) { if (isUnreachable) { return; } if (_isUint8(rd)) { _emitBytes2(opcode.index, rd); } else { assert(_isUint32(rd)); _emitBytes5(opcode.index + kWideModifier, _byte0(rd), _byte1(rd), _byte2(rd), _byte3(rd)); } } void _emitInstructionX(Opcode opcode, int rx) { if (isUnreachable) { return; } if (_isInt8(rx)) { _emitBytes2(opcode.index, rx & kByteMask); } else { assert(_isInt32(rx)); _emitBytes5(opcode.index + kWideModifier, _byte0(rx), _byte1(rx), _byte2(rx), _byte3(rx)); } } void _emitInstructionAE(Opcode opcode, int ra, int re) { if (isUnreachable) { return; } if (_isUint8(re)) { _emitBytes3(opcode.index, ra, re); } else { assert(_isUint32(re)); _emitBytes6(opcode.index + kWideModifier, ra, _byte0(re), _byte1(re), _byte2(re), _byte3(re)); } } void _emitInstructionAY(Opcode opcode, int ra, int ry) { if (isUnreachable) { return; } if (_isInt8(ry)) { _emitBytes3(opcode.index, ra, ry & kByteMask); } else { assert(_isInt32(ry)); _emitBytes6(opcode.index + kWideModifier, ra, _byte0(ry), _byte1(ry), _byte2(ry), _byte3(ry)); } } void _emitInstructionDF(Opcode opcode, int rd, int rf) { if (isUnreachable) { return; } if (_isUint8(rd)) { _emitBytes3(opcode.index, rd, rf); } else { assert(_isUint32(rd)); _emitBytes6(opcode.index + kWideModifier, _byte0(rd), _byte1(rd), _byte2(rd), _byte3(rd), rf); } } void _emitInstructionABC(Opcode opcode, int ra, int rb, int rc) { if (isUnreachable) { return; } _emitBytes4(opcode.index, ra, rb, rc); } void emitSpecializedBytecode(Opcode opcode) { assert(BytecodeFormats[opcode].encoding == Encoding.k0); emitSourcePosition(); _emitInstruction0(opcode); } void _emitJumpInstruction(Opcode opcode, Label label) { assert(isJump(opcode)); if (isUnreachable) { return; } final int target = label.jumpOperand(offset); // Use compact representation only for backwards jumps. // TODO(alexmarkov): generate compact forward jumps as well. if (label.isBound && _isInt8(target)) { _emitBytes2(opcode.index, target & kByteMask); } else { assert(_isInt24(target)); _emitBytes4(opcode.index + kWideModifier, _byte0(target), _byte1(target), _byte2(target)); } } void _patchJump(int pos, int rt) { final Opcode opcode = Opcode.values[_byteAt(pos) - kWideModifier]; assert(hasWideVariant(opcode)); assert(isJump(opcode)); assert(_isInt24(rt)); _setByteAt(pos + 1, _byte0(rt)); _setByteAt(pos + 2, _byte1(rt)); _setByteAt(pos + 3, _byte2(rt)); } void emitTrap() { _emitInstruction0(Opcode.kTrap); isUnreachable = true; } void emitDrop1() { _emitInstruction0(Opcode.kDrop1); } void emitJump(Label label) { emitSourcePosition(); _emitJumpInstruction(Opcode.kJump, label); isUnreachable = true; } void emitJumpIfNoAsserts(Label label) { _emitJumpInstruction(Opcode.kJumpIfNoAsserts, label); } void emitJumpIfNotZeroTypeArgs(Label label) { _emitJumpInstruction(Opcode.kJumpIfNotZeroTypeArgs, label); } void emitJumpIfEqStrict(Label label) { _emitJumpInstruction(Opcode.kJumpIfEqStrict, label); } void emitJumpIfNeStrict(Label label) { _emitJumpInstruction(Opcode.kJumpIfNeStrict, label); } void emitJumpIfTrue(Label label) { _emitJumpInstruction(Opcode.kJumpIfTrue, label); } void emitJumpIfFalse(Label label) { _emitJumpInstruction(Opcode.kJumpIfFalse, label); } void emitJumpIfNull(Label label) { _emitJumpInstruction(Opcode.kJumpIfNull, label); } void emitJumpIfNotNull(Label label) { _emitJumpInstruction(Opcode.kJumpIfNotNull, label); } void emitReturnTOS() { emitSourcePosition(); _emitInstruction0(Opcode.kReturnTOS); isUnreachable = true; } void emitPush(int rx) { _emitInstructionX(Opcode.kPush, rx); } void emitLoadConstant(int ra, int re) { _emitInstructionAE(Opcode.kLoadConstant, ra, re); } void emitPushConstant(int rd) { _emitInstructionD(Opcode.kPushConstant, rd); } void emitPushNull() { _emitInstruction0(Opcode.kPushNull); } void emitPushTrue() { _emitInstruction0(Opcode.kPushTrue); } void emitPushFalse() { _emitInstruction0(Opcode.kPushFalse); } void emitPushInt(int rx) { _emitInstructionX(Opcode.kPushInt, rx); } void emitStoreLocal(int rx) { emitSourcePosition(); _emitInstructionX(Opcode.kStoreLocal, rx); } void emitPopLocal(int rx) { emitSourcePosition(); _emitInstructionX(Opcode.kPopLocal, rx); } void emitDirectCall(int rd, int rf) { emitSourcePosition(); _emitInstructionDF(Opcode.kDirectCall, rd, rf); } void emitInterfaceCall(int rd, int rf) { emitSourcePosition(); _emitInstructionDF(Opcode.kInterfaceCall, rd, rf); } void emitUncheckedInterfaceCall(int rd, int rf) { emitSourcePosition(); _emitInstructionDF(Opcode.kUncheckedInterfaceCall, rd, rf); } void emitDynamicCall(int rd, int rf) { emitSourcePosition(); _emitInstructionDF(Opcode.kDynamicCall, rd, rf); } void emitNativeCall(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kNativeCall, rd); } void emitStoreStaticTOS(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kStoreStaticTOS, rd); } void emitPushStatic(int rd) { _emitInstructionD(Opcode.kPushStatic, rd); } void emitCreateArrayTOS() { _emitInstruction0(Opcode.kCreateArrayTOS); } void emitAllocate(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kAllocate, rd); } void emitAllocateT() { emitSourcePosition(); _emitInstruction0(Opcode.kAllocateT); } void emitStoreIndexedTOS() { _emitInstruction0(Opcode.kStoreIndexedTOS); } void emitStoreFieldTOS(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kStoreFieldTOS, rd); } void emitStoreContextParent() { _emitInstruction0(Opcode.kStoreContextParent); } void emitStoreContextVar(int ra, int re) { _emitInstructionAE(Opcode.kStoreContextVar, ra, re); } void emitLoadFieldTOS(int rd) { _emitInstructionD(Opcode.kLoadFieldTOS, rd); } void emitLoadTypeArgumentsField(int rd) { _emitInstructionD(Opcode.kLoadTypeArgumentsField, rd); } void emitLoadContextParent() { _emitInstruction0(Opcode.kLoadContextParent); } void emitLoadContextVar(int ra, int re) { _emitInstructionAE(Opcode.kLoadContextVar, ra, re); } void emitBooleanNegateTOS() { _emitInstruction0(Opcode.kBooleanNegateTOS); } void emitThrow(int ra) { emitSourcePosition(); _emitInstructionA(Opcode.kThrow, ra); isUnreachable = true; } void emitEntry(int rd) { _emitInstructionD(Opcode.kEntry, rd); } void emitFrame(int rd) { _emitInstructionD(Opcode.kFrame, rd); } void emitSetFrame(int ra) { _emitInstructionA(Opcode.kSetFrame, ra); } void emitAllocateContext(int ra, int re) { _emitInstructionAE(Opcode.kAllocateContext, ra, re); } void emitCloneContext(int ra, int re) { _emitInstructionAE(Opcode.kCloneContext, ra, re); } void emitMoveSpecial(SpecialIndex ra, int ry) { _emitInstructionAY(Opcode.kMoveSpecial, ra.index, ry); } void emitInstantiateType(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kInstantiateType, rd); } void emitInstantiateTypeArgumentsTOS(int ra, int re) { emitSourcePosition(); _emitInstructionAE(Opcode.kInstantiateTypeArgumentsTOS, ra, re); } void emitAssertAssignable(int ra, int re) { emitSourcePosition(); _emitInstructionAE(Opcode.kAssertAssignable, ra, re); } void emitAssertSubtype() { emitSourcePosition(); _emitInstruction0(Opcode.kAssertSubtype); } void emitAssertBoolean(int ra) { emitSourcePosition(); _emitInstructionA(Opcode.kAssertBoolean, ra); } void emitCheckStack(int ra) { emitSourcePosition(); _emitInstructionA(Opcode.kCheckStack, ra); } void emitCheckFunctionTypeArgs(int ra, int re) { emitSourcePosition(); _emitInstructionAE(Opcode.kCheckFunctionTypeArgs, ra, re); } void emitEntryFixed(int ra, int re) { _emitInstructionAE(Opcode.kEntryFixed, ra, re); } void emitEntryOptional(int ra, int rb, int rc) { _emitInstructionABC(Opcode.kEntryOptional, ra, rb, rc); } void emitAllocateClosure(int rd) { emitSourcePosition(); _emitInstructionD(Opcode.kAllocateClosure, rd); } }