Index: pkg/fletchc/lib/src/bytecode_assembler.dart |
diff --git a/pkg/fletchc/lib/src/bytecode_assembler.dart b/pkg/fletchc/lib/src/bytecode_assembler.dart |
deleted file mode 100644 |
index ab1bef5285047f7f0c6bcfe07841fa900a4d4713..0000000000000000000000000000000000000000 |
--- a/pkg/fletchc/lib/src/bytecode_assembler.dart |
+++ /dev/null |
@@ -1,670 +0,0 @@ |
-// Copyright (c) 2015, the Dartino 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.md file. |
- |
-library fletchc.bytecode_assembler; |
- |
-import '../bytecodes.dart'; |
- |
-const int IMPLICIT_STACK_OVERFLOW_LIMIT = 32; |
-const int frameDescriptorSize = 3; |
- |
-class BytecodeLabel { |
- int position = -1; |
- final List<int> usage = <int>[]; |
- |
- void addUsage(int bytecodePosition) { |
- usage.add(bytecodePosition); |
- } |
- |
- void forEach(f(int index)) { |
- usage.forEach(f); |
- } |
- |
- int get lastIndex { |
- if (usage.isEmpty) return -1; |
- return usage.last; |
- } |
- |
- void removeLastUsage() { |
- usage.removeLast(); |
- } |
- |
- void bind(int value) { |
- position = value; |
- usage.clear(); |
- } |
- |
- bool get isBound => position != -1; |
- |
- bool get isUsed => usage.isNotEmpty; |
-} |
- |
-class BytecodeAssembler { |
- final List<Bytecode> bytecodes = <Bytecode>[]; |
- final List<int> catchRanges = <int>[]; |
- |
- final int functionArity; |
- |
- int byteSize = 0; |
- int stackSize = 0; |
- int maxStackSize = 0; |
- |
- // A bind after a terminator will still look like the last bytecode |
- // is a terminator, however, due to the bind it's not. |
- bool hasBindAfterTerminator = false; |
- |
- // A bind after a pop will still look like the last bytecode is a |
- // pop, however, due to the bind we cannot collapse more pops |
- // together. |
- bool hasBindAfterPop = false; |
- |
- BytecodeAssembler(this.functionArity); |
- |
- int computeParameterSlot(int parameter) { |
- assert(parameter >= 0 && parameter < functionArity); |
- return parameter - frameDescriptorSize - functionArity; |
- } |
- |
- void reuse() { |
- bytecodes.clear(); |
- catchRanges.clear(); |
- byteSize = 0; |
- stackSize = 0; |
- maxStackSize = 0; |
- } |
- |
- /** |
- * Apply a fix to the currently known stack size. |
- */ |
- void applyStackSizeFix(int diff) { |
- stackSize += diff; |
- if (stackSize > maxStackSize) maxStackSize = stackSize; |
- } |
- |
- void addCatchFrameRange(int start, int end) { |
- catchRanges |
- ..add(start) |
- ..add(end) |
- ..add(stackSize); |
- } |
- |
- void loadConst(int id) { |
- internalAdd(new LoadConst(id)); |
- } |
- |
- void loadLocal(int offset) { |
- assert(offset < stackSize); |
- loadLocalHelper(offset); |
- } |
- |
- void loadLocalHelper(int offset) { |
- assert(offset >= 0); |
- Bytecode bytecode; |
- switch (offset) { |
- case 0: |
- bytecode = const LoadLocal0(); |
- break; |
- case 1: |
- bytecode = const LoadLocal1(); |
- break; |
- case 2: |
- bytecode = const LoadLocal2(); |
- break; |
- case 3: |
- bytecode = const LoadLocal3(); |
- break; |
- case 4: |
- bytecode = const LoadLocal4(); |
- break; |
- case 5: |
- bytecode = const LoadLocal5(); |
- break; |
- default: |
- if (offset >= 256) { |
- bytecode = new LoadLocalWide(offset); |
- } else { |
- bytecode = new LoadLocal(offset); |
- } |
- break; |
- } |
- internalAdd(bytecode); |
- } |
- |
- void loadBoxed(int offset) { |
- assert(offset < stackSize); |
- loadBoxedHelper(offset); |
- } |
- |
- void loadBoxedHelper(int offset) { |
- assert(offset >= 0 && offset <= 255); |
- internalAdd(new LoadBoxed(offset)); |
- } |
- |
- void dup() { |
- loadLocal(0); |
- } |
- |
- /** |
- * A 'slot' is an artificial indexing, that are frame relative. That means |
- * the current frame is indexed by where 0 .. frameSize-1, -1 is the return |
- * address and -1 - functionArity is the first argument, -2 is the last |
- * argument. |
- * |
- * This kind of indexing are sometimes easier to use than stack-relative, |
- * as locals and parameters have a fixed value. |
- */ |
- void loadSlot(int slot) { |
- int offset = stackSize - slot - 1; |
- loadLocal(offset); |
- } |
- |
- void loadBoxedSlot(int slot) { |
- int offset = stackSize - slot - 1; |
- loadBoxed(offset); |
- } |
- |
- int computeParameterOffset(int parameter) { |
- return frameDescriptorSize + stackSize + functionArity - parameter - 1; |
- } |
- |
- void loadParameter(int parameter) { |
- assert(parameter >= 0 && parameter < functionArity); |
- loadLocalHelper(computeParameterOffset(parameter)); |
- } |
- |
- void loadBoxedParameter(int parameter) { |
- assert(parameter >= 0 && parameter < functionArity); |
- loadBoxedHelper(computeParameterOffset(parameter)); |
- } |
- |
- void loadParameterSlot(int parameterSlot) { |
- int offset = stackSize - parameterSlot - 1; |
- loadLocalHelper(offset); |
- } |
- |
- void loadBoxedParameterSlot(int parameterSlot) { |
- int offset = stackSize - parameterSlot - 1; |
- loadBoxedHelper(offset); |
- } |
- |
- void loadStatic(int index) { |
- internalAdd(new LoadStatic(index)); |
- } |
- |
- void loadStaticInit(int index) { |
- internalAdd(new LoadStaticInit(index)); |
- } |
- |
- void loadField(int index) { |
- if (index >= 256) { |
- internalAdd(new LoadFieldWide(index)); |
- } else { |
- internalAdd(new LoadField(index)); |
- } |
- } |
- |
- void loadLiteralNull() { |
- internalAdd(new LoadLiteralNull()); |
- } |
- |
- void loadLiteralTrue() { |
- internalAdd(new LoadLiteralTrue()); |
- } |
- |
- void loadLiteralFalse() { |
- internalAdd(new LoadLiteralFalse()); |
- } |
- |
- void loadLiteral(int value) { |
- if (value == 0) { |
- internalAdd(const LoadLiteral0()); |
- } else if (value == 1) { |
- internalAdd(const LoadLiteral1()); |
- } else if (value < 256) { |
- internalAdd(new LoadLiteral(value)); |
- } else { |
- internalAdd(new LoadLiteralWide(value)); |
- } |
- } |
- |
- void storeLocal(int offset) { |
- assert(offset < stackSize); |
- storeLocalHelper(offset); |
- } |
- |
- void storeLocalHelper(int offset) { |
- assert(offset >= 0 && offset <= 255); |
- internalAdd(new StoreLocal(offset)); |
- } |
- |
- void storeBoxed(int offset) { |
- assert(offset < stackSize); |
- storeBoxedHelper(offset); |
- } |
- |
- void storeBoxedHelper(int offset) { |
- assert(offset >= 0 && offset <= 255); |
- internalAdd(new StoreBoxed(offset)); |
- } |
- |
- /** |
- * See loadSlot for information about 'slots'. |
- */ |
- void storeSlot(int slot) { |
- int offset = stackSize - slot - 1; |
- storeLocal(offset); |
- } |
- |
- void storeBoxedSlot(int slot) { |
- int offset = stackSize - slot - 1; |
- storeBoxed(offset); |
- } |
- |
- void storeParameter(int parameter) { |
- assert(parameter >= 0 && parameter < functionArity); |
- storeLocalHelper(computeParameterOffset(parameter)); |
- } |
- |
- void storeBoxedParameter(int parameter) { |
- assert(parameter >= 0 && parameter < functionArity); |
- storeBoxedHelper(computeParameterOffset(parameter)); |
- } |
- |
- void storeParameterSlot(int parameterSlot) { |
- int offset = stackSize - parameterSlot - 1; |
- storeLocalHelper(offset); |
- } |
- |
- void storeBoxedParameterSlot(int parameterSlot) { |
- int offset = stackSize - parameterSlot - 1; |
- storeBoxedHelper(offset); |
- } |
- |
- void storeStatic(int index) { |
- internalAdd(new StoreStatic(index)); |
- } |
- |
- void storeField(int index) { |
- if (index >= 256) { |
- internalAdd(new StoreFieldWide(index)); |
- } else { |
- internalAdd(new StoreField(index)); |
- } |
- } |
- |
- void invokeStatic(int id, int arity) { |
- internalAddStackPointerDifference( |
- new InvokeStatic(id), |
- 1 - arity); |
- } |
- |
- void invokeFactory(int id, int arity) { |
- internalAddStackPointerDifference( |
- new InvokeFactory(id), |
- 1 - arity); |
- } |
- |
- void invokeMethod(int selector, int arity, [String name]) { |
- var bytecode; |
- switch (name) { |
- case '==': |
- bytecode = new InvokeEqUnfold(selector); |
- break; |
- |
- case '<': |
- bytecode = new InvokeLtUnfold(selector); |
- break; |
- |
- case '<=': |
- bytecode = new InvokeLeUnfold(selector); |
- break; |
- |
- case '>': |
- bytecode = new InvokeGtUnfold(selector); |
- break; |
- |
- case '>=': |
- bytecode = new InvokeGeUnfold(selector); |
- break; |
- |
- case '+': |
- bytecode = new InvokeAddUnfold(selector); |
- break; |
- |
- case '-': |
- bytecode = new InvokeSubUnfold(selector); |
- break; |
- |
- case '*': |
- bytecode = new InvokeMulUnfold(selector); |
- break; |
- |
- case '~/': |
- bytecode = new InvokeTruncDivUnfold(selector); |
- break; |
- |
- case '%': |
- bytecode = new InvokeModUnfold(selector); |
- break; |
- |
- case '~': |
- bytecode = new InvokeBitNotUnfold(selector); |
- break; |
- |
- case '&': |
- bytecode = new InvokeBitAndUnfold(selector); |
- break; |
- |
- case '|': |
- bytecode = new InvokeBitOrUnfold(selector); |
- break; |
- |
- case '^': |
- bytecode = new InvokeBitXorUnfold(selector); |
- break; |
- |
- case '<<': |
- bytecode = new InvokeBitShlUnfold(selector); |
- break; |
- |
- case '>>': |
- bytecode = new InvokeBitShrUnfold(selector); |
- break; |
- |
- default: |
- bytecode = new InvokeMethodUnfold(selector); |
- break; |
- } |
- internalAddStackPointerDifference(bytecode, -arity); |
- } |
- |
- void invokeTest(int selector, int arity) { |
- internalAddStackPointerDifference(new InvokeTestUnfold(selector), -arity); |
- } |
- |
- void invokeSelector(int slot) { |
- internalAddStackPointerDifference(new InvokeSelector(slot), 0); |
- } |
- |
- void pop() { |
- if (hasBindAfterPop) { |
- internalAdd(new Pop()); |
- hasBindAfterPop = false; |
- return; |
- } |
- Bytecode last = bytecodes.last; |
- if (last.opcode == Opcode.Drop) { |
- Drop drop = last; |
- int amount = drop.uint8Argument0 + 1; |
- if (amount <= 255) { |
- bytecodes[bytecodes.length - 1] = new Drop(amount); |
- applyStackSizeFix(-1); |
- } else { |
- internalAdd(new Pop()); |
- } |
- } else if (last.opcode == Opcode.Pop) { |
- bytecodes[bytecodes.length - 1] = new Drop(2); |
- byteSize += 1; |
- applyStackSizeFix(-1); |
- } else { |
- internalAdd(new Pop()); |
- } |
- |
- } |
- |
- void popMany(int count) { |
- while (count > 255) { |
- internalAddStackPointerDifference(new Drop(255), -255); |
- count -= 255; |
- } |
- if (count > 1) { |
- internalAddStackPointerDifference(new Drop(count), -count); |
- } else if (count == 1) { |
- internalAdd(new Pop()); |
- } |
- hasBindAfterPop = false; |
- } |
- |
- void ret() { |
- hasBindAfterTerminator = false; |
- if (stackSize <= 0) throw "Bad stackSize for return bytecode: $stackSize"; |
- internalAdd(const Return()); |
- } |
- |
- void returnNull() { |
- hasBindAfterTerminator = false; |
- internalAdd(const ReturnNull()); |
- } |
- |
- void identical() { |
- internalAdd(const Identical()); |
- } |
- |
- void identicalNonNumeric() { |
- internalAdd(const IdenticalNonNumeric()); |
- } |
- |
- void negate() { |
- internalAdd(const Negate()); |
- } |
- |
- void bind(BytecodeLabel label) { |
- internalBind(label, false); |
- } |
- |
- void internalBind(BytecodeLabel label, bool isSubroutineReturn) { |
- if (label.isUsed) hasBindAfterTerminator = true; |
- hasBindAfterPop = true; |
- assert(label.position == -1); |
- // TODO(ajohnsen): If the previous bytecode is a branch to this label, |
- // consider popping it - if no other binds has happened at this bytecode |
- // index. |
- int position = byteSize; |
- label.forEach((int index) { |
- var bytecode = bytecodes[index]; |
- switch (bytecode.opcode) { |
- case Opcode.BranchIfTrueWide: |
- int offset = position - bytecode.uint32Argument0; |
- bytecodes[index] = new BranchIfTrueWide(offset); |
- break; |
- |
- case Opcode.BranchIfFalseWide: |
- int offset = position - bytecode.uint32Argument0; |
- bytecodes[index] = new BranchIfFalseWide(offset); |
- break; |
- |
- case Opcode.BranchWide: |
- int offset = position - bytecode.uint32Argument0; |
- bytecodes[index] = new BranchWide(offset); |
- break; |
- |
- case Opcode.PopAndBranchWide: |
- int offset = position - bytecode.uint32Argument1; |
- bytecodes[index] = new PopAndBranchWide( |
- bytecode.uint8Argument0, |
- offset); |
- break; |
- |
- case Opcode.EnterNoSuchMethod: |
- int offset = position - bytecode.uint8Argument0; |
- bytecodes[index] = new EnterNoSuchMethod(offset); |
- break; |
- |
- case Opcode.SubroutineCall: |
- if (isSubroutineReturn) { |
- int offset = position - bytecode.uint32Argument1; |
- offset -= bytecode.size; |
- bytecodes[index] = new SubroutineCall( |
- bytecode.uint32Argument0, |
- offset); |
- } else { |
- int offset = position - bytecode.uint32Argument0; |
- bytecodes[index] = new SubroutineCall( |
- offset, |
- bytecode.uint32Argument1); |
- } |
- break; |
- |
- default: |
- throw "Unhandled bind bytecode: $bytecode"; |
- } |
- }); |
- label.bind(position); |
- } |
- |
- void branchIfTrue(BytecodeLabel label) { |
- if (label.isBound) { |
- internalBranchBack( |
- label, |
- (v) => new BranchBackIfTrue(v), |
- (v) => new BranchBackIfTrueWide(v)); |
- } else { |
- label.addUsage(bytecodes.length); |
- internalAdd(new BranchIfTrueWide(byteSize)); |
- } |
- } |
- |
- void branchIfFalse(BytecodeLabel label) { |
- if (label.isBound) { |
- internalBranchBack( |
- label, |
- (v) => new BranchBackIfFalse(v), |
- (v) => new BranchBackIfFalseWide(v)); |
- } else { |
- label.addUsage(bytecodes.length); |
- internalAdd(new BranchIfFalseWide(byteSize)); |
- } |
- } |
- |
- void branch(BytecodeLabel label) { |
- if (label.isBound) { |
- internalBranchBack( |
- label, |
- (v) => new BranchBack(v), |
- (v) => new BranchBackWide(v)); |
- } else { |
- label.addUsage(bytecodes.length); |
- internalAdd(new BranchWide(byteSize)); |
- } |
- } |
- |
- void popAndBranch(int diff, BytecodeLabel label) { |
- assert(diff >= 0 && diff <= 255); |
- if (label.isBound) { |
- internalBranchBack( |
- label, |
- (v) => new PopAndBranchBackWide(diff, v), |
- (v) => new PopAndBranchBackWide(diff, v)); |
- } else { |
- label.addUsage(bytecodes.length); |
- internalAdd(new PopAndBranchWide(diff, byteSize)); |
- } |
- } |
- |
- void internalBranchBack( |
- BytecodeLabel label, |
- Bytecode short(int offset), |
- Bytecode long(int offset)) { |
- int offset = byteSize - label.position; |
- if (offset < 255) { |
- internalAdd(short(offset)); |
- } else { |
- internalAdd(long(offset)); |
- } |
- } |
- |
- void allocate(int classId, int fields, {bool immutable: false}) { |
- var instruction = immutable ? |
- new AllocateImmutable(classId) : new Allocate(classId); |
- internalAddStackPointerDifference(instruction, 1 - fields); |
- } |
- |
- void allocateBoxed() { |
- internalAdd(const AllocateBoxed()); |
- } |
- |
- void subroutineCall(BytecodeLabel label, BytecodeLabel returnLabel) { |
- assert(!label.isBound); |
- assert(!returnLabel.isBound); |
- label.addUsage(bytecodes.length); |
- returnLabel.addUsage(bytecodes.length); |
- internalAddStackPointerDifference( |
- new SubroutineCall(byteSize, byteSize), |
- 0); |
- } |
- |
- void subroutineReturn(BytecodeLabel returnLabel) { |
- internalBind(returnLabel, true); |
- internalAdd(const SubroutineReturn()); |
- } |
- |
- bool get endsWithTerminator { |
- if (bytecodes.isEmpty) return false; |
- if (hasBindAfterTerminator) return false; |
- Opcode opcode = bytecodes.last.opcode; |
- return opcode == Opcode.Return || opcode == Opcode.Throw; |
- } |
- |
- void enterNoSuchMethod(BytecodeLabel skipGetterLabel) { |
- assert(!skipGetterLabel.isBound); |
- skipGetterLabel.addUsage(bytecodes.length); |
- internalAddStackPointerDifference(new EnterNoSuchMethod(byteSize), 0); |
- } |
- |
- void exitNoSuchMethod() { |
- internalAdd(const ExitNoSuchMethod()); |
- } |
- |
- void methodEnd() { |
- if (maxStackSize > IMPLICIT_STACK_OVERFLOW_LIMIT) { |
- var bytecode = new StackOverflowCheck( |
- maxStackSize - IMPLICIT_STACK_OVERFLOW_LIMIT); |
- bytecodes.insert(0, bytecode); |
- byteSize += bytecode.size; |
- } |
- int value = (byteSize << 1) | (catchRanges.isNotEmpty ? 1 : 0); |
- internalAdd(new MethodEnd(value)); |
- } |
- |
- void processYield() { |
- internalAdd(const ProcessYield()); |
- } |
- |
- void coroutineChange() { |
- internalAdd(const CoroutineChange()); |
- } |
- |
- void internalAdd(Bytecode bytecode) { |
- internalAddStackPointerDifference( |
- bytecode, |
- bytecode.stackPointerDifference); |
- } |
- |
- void internalAddStackPointerDifference( |
- Bytecode bytecode, |
- int stackPointerDifference) { |
- assert(stackPointerDifference != VAR_DIFF); |
- assert(bytecodes.isEmpty || bytecodes.last.opcode != Opcode.MethodEnd); |
- bytecodes.add(bytecode); |
- byteSize += bytecode.size; |
- applyStackSizeFix(stackPointerDifference); |
- } |
- |
- void invokeNative(int arity, int index) { |
- internalAdd(new InvokeNative(arity, index)); |
- } |
- |
- void invokeDetachableNative(int arity, int index) { |
- internalAdd(new InvokeDetachableNative(arity, index)); |
- } |
- |
- void invokeNativeYield(int arity, int index) { |
- internalAdd(new InvokeNativeYield(arity, index)); |
- } |
- |
- void emitThrow() { |
- hasBindAfterTerminator = false; |
- internalAdd(const Throw()); |
- } |
-} |