/* * $Id$ * * Copyright (C) 2003-2015 JNode.org * * This library is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; If not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.jnode.vm.x86.compiler.l1a; import org.jnode.assembler.x86.X86Register; import org.jnode.vm.JvmType; import org.jnode.vm.bytecode.TypeStack; import org.jnode.vm.facade.VmUtils; import org.jnode.vm.x86.compiler.AbstractX86StackManager; /** * @author Patrik Reali */ // TODO: work with Items to keep track of each item's stack level until popped // and ensure consistency and correctness final class VirtualStack { // explicitely check that elements on the operant stack // are popped in the appropriate order static final boolean checkOperandStack = true; // explicitely check that elements on the FPU stack // are popped in the appropriate order static final boolean checkFpuStack = true; // the virtual stack private Item[] stack; // top of stack; stack[tos] is not part of the stack! private int tos; final ItemStack operandStack; final FPUStack fpuStack = new FPUStack(); /** * Constructor; create and initialize stack with default size */ VirtualStack() { this.operandStack = checkOperandStack ? new ItemStack(Item.Kind.STACK, Integer.MAX_VALUE) : null; stack = new Item[8]; tos = 0; } void reset(EmitterContext ec) { while (!isEmpty()) pop().release(ec); tos = 0; if (checkOperandStack) { operandStack.reset(ec); } } int TOS() { return tos; } boolean isEmpty() { return (tos == 0); } /** * Increase stack size */ private void growStack() { final Item[] tmp = new Item[stack.length * 2]; System.arraycopy(stack, 0, tmp, 0, stack.length); stack = tmp; } /** * Pop top item from stack. If no item on the stack, return UNKNOWN item * (avoiding this requires knowing the stack contents across basic blocks) * <p/> * Use pop as far as possible, but the brain-dead implementation of all dup * opcodes (from the 2nd edition of the spec) allows popping elements * without knowing their type. * * @return top item */ Item pop() { // do not autocreate item: if no type available, just fail; avoid this // case in the bytecode visitor // if (tos == 0) // //TODO: support items across basic blocks // pushStack(Item.UNKNOWN); tos--; final Item i = stack[tos]; stack[tos] = null; return i; } /** * Equals to pop, but also pops of the operand stack if the popped item is * on the stack. * * @return */ Item pop1() { final Item i = pop(); if (checkOperandStack && i.isStack()) { operandStack.pop(i); } return i; } /** * Pop top item from stack, check its type also. If none is present, create * a new stack item with the given type * * @param type * @return pop the top of stack item * @throws VerifyError if the type does not correspond */ Item pop(int type) { // if (tos == 0) { // // the item requested in not on the virtual stack // // but already on the operand stack (it was pushed // // outside the current basic block) // // thus create a new stack item // Item it = createStack(type); // if (checkOperandStack) { // // insert at the begin of stack // // even if the vstack is empty, there // // may still be items popped from vstack // // that are not popped from operand stack // prependToOperandStack(it); // } // return it; // // pushStack(type); // } tos--; final Item i = stack[tos]; stack[tos] = null; if (i.getType() != type) throw new VerifyError("Expected:" + Integer.toString(type) + " Actual:" + Integer.toString(i.getType())); return i; } /** * Pop an item of the stack. If the type is different from INT, an exception * is thrown. * * @return */ final IntItem popInt() { // testing in pop and casting here: test is just redundant return (IntItem) pop(JvmType.INT); } /** * Pop an item of the stack. If the type is different from LONG, an * exception is thrown. * * @return */ final LongItem popLong() { // testing in pop and casting here: test is just redundant return (LongItem) pop(JvmType.LONG); } /** * Pop an item of the stack. If the type is different from REFERENCE, an * exception is thrown. * * @return */ final RefItem popRef() { // testing in pop and casting here: test is just redundant return (RefItem) pop(JvmType.REFERENCE); } /** * Pop an item of the stack. If the type is different from FLOAT, an * exception is thrown. * * @return */ final FloatItem popFloat() { // testing in pop and casting here: test is just redundant return (FloatItem) pop(JvmType.FLOAT); } /** * Pop an item of the stack. If the type is different from REFERENCE, an * exception is thrown. * * @return */ final DoubleItem popDouble() { // testing in pop and casting here: test is just redundant return (DoubleItem) pop(JvmType.DOUBLE); } /** * Push item on stack. If the item is on the FPU stack, it is also pushed on * fpuStack. */ void push(Item item) { if (VmUtils.verifyAssertions()) VmUtils._assert(item.getKind() > 0, "Kind > 0"); if ((item.isStack()) && (tos > 0)) { if (VmUtils.verifyAssertions()) VmUtils._assert(stack[tos - 1].isStack(), "stack[ tos - 1].isStack()"); } if (tos == stack.length) { growStack(); } stack[tos++] = item; } /** * Push on vstack and operand stack (special case for old-style code, to be * eventually removed) */ void push1(Item item) { push(item); if (checkOperandStack && (item.getKind() == Item.Kind.STACK)) { operandStack.push(item); } } /** * Does this stack contain the given item. * * @param item * @return */ final boolean contains(Item item) { for (int i = 0; i < tos; i++) { if (stack[i] == item) return true; } return false; } /** * load every instance of local with given index into a register (used to * avoid aliasing problems) * * @param offsetToFP */ void loadLocal(EmitterContext ec, int offsetToFP) { for (int i = 0; i < tos; i++) { final Item item = stack[i]; if (item.isLocal() && item.isAtOffset(offsetToFP)) { item.load(ec); } } } /** * Push all items on the virtual stack to the actual stack. * * @param ec */ final int push(EmitterContext ec) { int i = 0; while ((i < tos) && (stack[i].getKind() == Item.Kind.STACK)) { i++; } int cnt = 0; while (i < tos) { final Item item = stack[i]; if (VmUtils.verifyAssertions()) VmUtils._assert(item.getKind() != Item.Kind.STACK, "item.getKind() != Item.Kind.STACK"); item.push(ec); i++; cnt++; } return cnt; } // /** // * Push items on the virtual stack to the actual stack until there are no // * more volative registers in use on the stack. // * // * param ec // */ // final int pushAllVolatile(EmitterContext ec) { // int i = 0; // while ((i < tos) && stack[i].isStack()) { // i++; // } // int cnt = 0; // final int max = findTopVolatileRegisterIndex(ec.getPool()); // while (i <= max) { // final Item item = stack[i]; // Item.assertCondition(!item.isStack(), "!item.isStack()"); // item.push(ec); // i++; // cnt++; // } // return cnt; // } // /** // * Find the largest index that contains a volatile register. // * // * @return // */ // private final int findTopVolatileRegisterIndex(X86RegisterPool pool) { // for (int i = tos - 1; i >= 0; i--) { // if (stack[i].usesVolatileRegister(pool)) { // return i; // } // } // return -1; // } // private void prependToOperandStack(Item item) { // os.log("prepend"); // Item.myAssert(item.getKind() == Item.Kind.STACK); // // if (operandTos == operandStack.length) growOperandStack(); // // for (int i = operandTos; i > 0; i--) // operandStack[ i] = operandStack[ i - 1]; // // operandTos++; // operandStack[ 0] = item; // } // /** * @param reg * @return */ boolean uses(X86Register reg) { for (int i = 0; i < tos; i++) { if (stack[i].uses(reg)) { return true; } } return false; } /** * Create a typestack reflecting the state of this stack. * * @return */ TypeStack asTypeStack() { final TypeStack tstack = new TypeStack(); for (int i = 0; i < tos; i++) { tstack.push(stack[i].getType()); } return tstack; } /** * Push items (kind = STACK) on stack for each type in the given typestack. * * @param ifac * @param tstack May be null or empty */ final void pushAll(ItemFactory ifac, TypeStack tstack) { if ((tstack != null) && !tstack.isEmpty()) { final int size = tstack.size(); for (int i = 0; i < size; i++) { final int type = tstack.getType(i); final Item item = ifac.createStack(type); push(item); if (VirtualStack.checkOperandStack) { operandStack.push(item); } } } } final AbstractX86StackManager createStackMgr(X86RegisterPool pool, ItemFactory ifac) { return new StackManagerImpl(pool, ifac); } final void initializeStackMgr(AbstractX86StackManager stackMgr, EmitterContext ec) { ((StackManagerImpl) stackMgr).setEmitterContext(ec); } public String toString() { if (tos == 0) { return "EMPTY"; } final StringBuilder buf = new StringBuilder(); for (int i = 0; i < tos; i++) { if (i != 0) { buf.append(','); } buf.append('('); buf.append(JvmType.toString(stack[i].getType())); buf.append(','); buf.append(Item.Kind.toString(stack[i].getKind())); buf.append(')'); } buf.append("TOS"); return buf.toString(); } final void visitItems(ItemVisitor visitor) { for (int i = 0; i < tos; i++) { visitor.visit(stack[i]); } } final class StackManagerImpl implements AbstractX86StackManager { private EmitterContext ec; private final X86RegisterPool pool; private final ItemFactory ifac; public StackManagerImpl(X86RegisterPool pool, ItemFactory ifac) { this.pool = pool; this.ifac = ifac; } /** * @see org.jnode.vm.x86.compiler.AbstractX86StackManager#writePUSH(int, * org.jnode.assembler.x86.X86Register.GPR) */ public void writePUSH(int jvmType, X86Register.GPR reg) { final Item item = ifac.createReg(ec, jvmType, reg); final boolean ok = pool.request(reg, item); if (VmUtils.verifyAssertions()) VmUtils._assert(ok, "request"); push(item); } /** * @see org.jnode.vm.x86.compiler.AbstractX86StackManager#writePUSH64(int, * org.jnode.assembler.x86.X86Register.GPR, org.jnode.assembler.x86.X86Register.GPR) */ public void writePUSH64(int jvmType, X86Register.GPR lsbReg, X86Register.GPR msbReg) { final Item item = ifac.createReg(ec, jvmType, lsbReg, msbReg); final boolean lsbOk = pool.request(lsbReg, item); final boolean msbOk = pool.request(msbReg, item); if (VmUtils.verifyAssertions()) { VmUtils._assert(lsbOk, "request-lsb"); VmUtils._assert(msbOk, "request-msb"); } push(item); } /** * @see org.jnode.vm.x86.compiler.AbstractX86StackManager#writePUSH64(int, * org.jnode.assembler.x86.X86Register.GPR64) */ public void writePUSH64(int jvmType, X86Register.GPR64 reg) { final Item item = ifac.createReg(ec, jvmType, reg); final boolean ok = pool.request(reg, item); if (VmUtils.verifyAssertions()) VmUtils._assert(ok, "request"); push(item); } public final void setEmitterContext(EmitterContext ec) { this.ec = ec; } } }