/* * $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.l1b; import org.jnode.assembler.Label; import org.jnode.assembler.x86.X86Assembler; import org.jnode.assembler.x86.X86Constants; import org.jnode.assembler.x86.X86Register; import org.jnode.assembler.x86.X86Register.FPU; import org.jnode.assembler.x86.X86Register.GPR; import org.jnode.assembler.x86.X86Register.GPR32; import org.jnode.assembler.x86.X86Register.GPR64; import org.jnode.bootlog.BootLogInstance; import org.jnode.vm.JvmType; import org.jnode.vm.facade.VmUtils; /** * @author Ewout Prangsma (epr@users.sourceforge.net) */ final class FPCompilerFPU extends FPCompiler { /** * @param os * @param ec * @param vstack */ public FPCompilerFPU(X86BytecodeVisitor bcv, X86Assembler os, EmitterContext ec, VirtualStack vstack, int arrayDataOffset) { super(bcv, os, ec, vstack, arrayDataOffset); } /** * fadd / dadd * * @param ec * @param vstack * @param type */ final void add(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); if (v1.isConstant() && v2.isConstant()) { final double fpv1 = getFPValue(v1); final double fpv2 = getFPValue(v2); vstack.push(createConst(ifac, type, fpv1 + fpv2)); v1.release(ec); v2.release(ec); } else { // Prepare stack final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, true); final Item result = fpuStack.getItem(reg); fpuStack.pop(); // Calculate os.writeFADDP(reg); // Push result vstack.push(result); } } /** * fcmpg, fcmpl, dcmpg, dcmpl * * @param os * @param ec * @param vstack * @param gt * @param type * @param curInstrLabel */ final void compare(boolean gt, int type, Label curInstrLabel) { final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); // Prepare operands final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, false); // We need reg to be ST1. fxchST1(os, fpuStack, reg); final X86RegisterPool pool = ec.getGPRPool(); pool.request(X86Register.EAX); final IntItem result = (IntItem) L1AHelper.requestWordRegister(ec, JvmType.INT, false); final GPR resr = result.getRegister(); // Clear resultr os.writeXOR(resr, resr); if (!gt) { // Reverse order FPUHelper.fxch(os, fpuStack, X86Register.ST1); } os.writeFUCOMPP(); // Compare, Pop twice os.writeFNSTSW_AX(); // Store fp status word in AX if (os.isCode32()) { os.writeSAHF(); // Store AH to Flags } // Pop fpu stack twice (FUCOMPP) fpuStack.pop(); fpuStack.pop(); final Label gtLabel = new Label(curInstrLabel + "gt"); final Label ltLabel = new Label(curInstrLabel + "lt"); final Label endLabel = new Label(curInstrLabel + "end"); if (os.isCode32()) { os.writeJCC(gtLabel, X86Constants.JA); os.writeJCC(ltLabel, X86Constants.JB); } else { // Emulate JA os.writeTEST(X86Register.EAX, (X86Constants.F_CF | X86Constants.F_ZF) << 8); os.writeJCC(gtLabel, X86Constants.JZ); // Emulate JB os.writeTEST(X86Register.EAX, (X86Constants.F_CF) << 8); os.writeJCC(ltLabel, X86Constants.JNZ); } os.writeJMP(endLabel); // equal // Greater os.setObjectRef(gtLabel); if (gt) { os.writeDEC(resr); } else { os.writeINC(resr); } os.writeJMP(endLabel); // Less os.setObjectRef(ltLabel); if (gt) { os.writeINC(resr); } else { os.writeDEC(resr); } // End os.setObjectRef(endLabel); // Push result vstack.push(result); // Release pool.release(X86Register.EAX); } /** * f2x / d2x * * @param ec * @param vstack */ final void convert(int fromType, int toType) { final ItemFactory ifac = ec.getItemFactory(); final Item v = vstack.pop(fromType); if (v.isConstant()) { vstack.push(createConst(ifac, toType, getFPValue(v))); v.release(ec); } else { v.pushToFPU(ec); vstack.fpuStack.pop(v); v.release(ec); final Item result = ifac.createFPUStack(toType); vstack.push(result); vstack.fpuStack.push(result); // Now load to GPR (to force conversion) result.loadToGPR(ec); } } /** * fdiv / ddiv * * @param ec * @param vstack * @param type */ final void div(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); if (v1.isConstant() && v2.isConstant()) { final double fpv1 = getFPValue(v1); final double fpv2 = getFPValue(v2); vstack.push(createConst(ifac, type, fpv1 / fpv2)); v1.release(ec); v2.release(ec); } else { // Prepare stack final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, false); final Item result = fpuStack.getItem(reg); fpuStack.pop(); // Calculate os.writeFDIVP(reg); // Push result vstack.push(result); } } /** * Ensure that there are at least items registers left on the FPU stack. If * the number of items is not free, the stack is flushed onto the CPU stack. * * @param os * @param ec * @param vstack * @param items */ static final void ensureStackCapacity(X86Assembler os, EmitterContext ec, VirtualStack vstack, int items) { final FPUStack fpuStack = vstack.fpuStack; if (!fpuStack.hasCapacity(items)) { BootLogInstance.get().debug("Flush FPU stack;\n fpuStack=" + fpuStack + ",\n vstack =" + vstack); vstack.push(ec); if (VmUtils.verifyAssertions()) VmUtils._assert(fpuStack.hasCapacity(items), "Out of FPU stack"); } } /** * Swap the given register and ST1 unless the given register is already ST1. * * @param os * @param fpuStack * @param fpuReg */ private static final void fxchST1(X86Assembler os, FPUStack fpuStack, FPU fpuReg) { // We need reg to be ST1, if not swap if (fpuReg != X86Register.ST1) { // Swap reg with ST0 FPUHelper.fxch(os, fpuStack, fpuReg); FPUHelper.fxch(os, fpuStack, X86Register.ST1); FPUHelper.fxch(os, fpuStack, fpuReg); } } /** * fmul / dmul * * @param ec * @param vstack * @param type */ final void mul(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); if (v1.isConstant() && v2.isConstant()) { final double fpv1 = getFPValue(v1); final double fpv2 = getFPValue(v2); vstack.push(createConst(ifac, type, fpv1 * fpv2)); v1.release(ec); v2.release(ec); } else { // Prepare stack final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, true); final Item result = fpuStack.getItem(reg); fpuStack.pop(); // Calculate os.writeFMULP(reg); // Push result vstack.push(result); } } /** * fneg / dneg * * @param ec * @param vstack * @param type */ final void neg(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v = vstack.pop(type); if (v.isConstant()) { final double fpv = getFPValue(v); vstack.push(createConst(ifac, type, -fpv)); v.release(ec); } else { // Prepare final FPUStack fpuStack = vstack.fpuStack; prepareForOperation(os, ec, vstack, fpuStack, v); // Calculate os.writeFCHS(); // Push result vstack.push(v); } } /** * Make sure that the given operand is on the top on the FPU stack. */ private static void prepareForOperation(X86Assembler os, EmitterContext ec, VirtualStack vstack, FPUStack fpuStack, Item left) { final boolean onFpu = left.isFPUStack(); // If the FPU stack will be full in this operation, we flush the vstack // first. int extraItems = onFpu ? 0 : 1; ensureStackCapacity(os, ec, vstack, extraItems); if (onFpu) { // Operand is on the FPU stack if (!fpuStack.isTos(left)) { // operand not on top, exchange it. final FPU reg = fpuStack.getRegister(left); os.writeFXCH(reg); fpuStack.fxch(reg); } } else { // operand is not on FPU stack left.pushToFPU(ec); // Now left is on top } } /** * Make sure both operand are on the FPU stack, on of which is in ST0. The * FPU register of the other operand is returned. If commutative is true, * either left of right will be located in ST0, if commutative is false, the * left operand will be in ST0. * <p/> * The item at ST0 is popped of the given fpuStack stack. * * @param left * @param right */ private static FPU prepareForOperation(X86Assembler os, EmitterContext ec, VirtualStack vstack, FPUStack fpuStack, Item left, Item right, boolean commutative) { final boolean lOnFpu = left.isFPUStack(); final boolean rOnFpu = right.isFPUStack(); final FPU reg; // If the FPU stack will be full in this operation, we flush the vstack // first. int extraItems = 0; extraItems += lOnFpu ? 0 : 1; extraItems += rOnFpu ? 0 : 1; ensureStackCapacity(os, ec, vstack, extraItems); if (lOnFpu && rOnFpu) { // Both operand are on the FPU stack if (fpuStack.isTos(left)) { // Left already on top reg = fpuStack.getRegister(right); } else if (fpuStack.isTos(right)) { // Right on top reg = fpuStack.getRegister(left); if (commutative) { // Commutative so return left register } else { // Non-commutative, so swap left-right and return left // register // System.out.println("stack: " + fpuStack + "\nleft: " + // left + "\nright: " + right + "\nreg: " + reg); FPUHelper.fxch(os, fpuStack, reg); } } else { // Neither left not right on top FPUHelper.fxch(os, fpuStack, fpuStack.getRegister(left)); // Swap left & // ST0, now left // is on top reg = fpuStack.getRegister(right); } } else if (!(lOnFpu || rOnFpu)) { // Neither operands are on the FPU stack left.pushToFPU(ec); right.pushToFPU(ec); // Now right is on top reg = X86Register.ST1; // Left is just below top if (!commutative) { FPUHelper.fxch(os, fpuStack, reg); } } else if (lOnFpu) { // Left operand is on FPU stack, right is not right.pushToFPU(ec); // Now right is on top // BootLogInstance.get().debug("left.kind=" + left.getKind()); reg = fpuStack.getRegister(left); if (!commutative) { FPUHelper.fxch(os, fpuStack, reg); } } else { // Right operand is on FPU stack, left is not left.pushToFPU(ec); // Now left is on top reg = fpuStack.getRegister(right); } return reg; } /** * frem / drem * * @param ec * @param vstack * @param type */ final void rem(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); if (v1.isConstant() && v2.isConstant()) { final double fpv1 = getFPValue(v1); final double fpv2 = getFPValue(v2); vstack.push(createConst(ifac, type, fpv1 % fpv2)); v1.release(ec); v2.release(ec); } else { // Prepare stack final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, false); // We need reg to be ST1, if not swap fxchST1(os, fpuStack, reg); // Pop the fpuStack.tos fpuStack.pop(); // Calculate os.writeFXCH(X86Register.ST1); os.writeFPREM(); os.writeFSTP(X86Register.ST1); // Push result vstack.push(fpuStack.tos()); } } /** * fsub / dsub * * @param ec * @param vstack * @param type */ final void sub(int type) { final ItemFactory ifac = ec.getItemFactory(); final Item v2 = vstack.pop(type); final Item v1 = vstack.pop(type); if (v1.isConstant() && v2.isConstant()) { final double fpv1 = getFPValue(v1); final double fpv2 = getFPValue(v2); vstack.push(createConst(ifac, type, fpv1 - fpv2)); v1.release(ec); v2.release(ec); } else { // Prepare stack final FPUStack fpuStack = vstack.fpuStack; final FPU reg = prepareForOperation(os, ec, vstack, fpuStack, v2, v1, false); final Item result = fpuStack.getItem(reg); fpuStack.pop(); // Calculate os.writeFSUBP(reg); // Push result vstack.push(result); } } /** * faload / daload * * @param type */ final void fpaload(int type) { final IntItem idx = vstack.popInt(); final RefItem ref = vstack.popRef(); idx.loadIf(ec, ~Item.Kind.CONSTANT); ref.load(ec); final GPR refr = ref.getRegister(); bcv.checkBounds(ref, idx); ensureStackCapacity(os, ec, vstack, 1); if (type == JvmType.FLOAT) { if (idx.isConstant()) { final int offset = idx.getValue() * 4; os.writeFLD32(refr, offset + arrayDataOffset); } else { GPR idxr = idx.getRegister(); if (os.isCode64()) { final GPR64 idxr64 = (GPR64) ec.getGPRPool().getRegisterInSameGroup(idxr, JvmType.LONG); os.writeMOVSXD(idxr64, (GPR32) idxr); idxr = idxr64; } os.writeFLD32(refr, idxr, 4, arrayDataOffset); } } else { if (idx.isConstant()) { final int offset = idx.getValue() * 8; os.writeFLD64(refr, offset + arrayDataOffset); } else { GPR idxr = idx.getRegister(); if (os.isCode64()) { final GPR64 idxr64 = (GPR64) ec.getGPRPool().getRegisterInSameGroup(idxr, JvmType.LONG); os.writeMOVSXD(idxr64, (GPR32) idxr); idxr = idxr64; } os.writeFLD64(refr, idxr, 8, arrayDataOffset); } } // Release ref.release(ec); idx.release(ec); // Push result final ItemFactory ifac = ec.getItemFactory(); final Item result = ifac.createFPUStack(type); vstack.fpuStack.push(result); vstack.push(result); } }