/*
* $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 java.util.HashMap;
import java.util.Map;
import org.jnode.assembler.Label;
import org.jnode.assembler.NativeStream;
import org.jnode.assembler.x86.X86Assembler;
import org.jnode.assembler.x86.X86Constants;
import org.jnode.assembler.x86.X86Operation;
import org.jnode.assembler.x86.X86Register;
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.bytecode.BasicBlock;
import org.jnode.vm.bytecode.BytecodeParser;
import org.jnode.vm.bytecode.TypeStack;
import org.jnode.vm.classmgr.ObjectLayout;
import org.jnode.vm.classmgr.Signature;
import org.jnode.vm.classmgr.TIBLayout;
import org.jnode.vm.classmgr.VmArray;
import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmClassType;
import org.jnode.vm.classmgr.VmConstClass;
import org.jnode.vm.classmgr.VmConstFieldRef;
import org.jnode.vm.classmgr.VmConstIMethodRef;
import org.jnode.vm.classmgr.VmConstMethodRef;
import org.jnode.vm.classmgr.VmConstString;
import org.jnode.vm.classmgr.VmField;
import org.jnode.vm.classmgr.VmInstanceField;
import org.jnode.vm.classmgr.VmInstanceMethod;
import org.jnode.vm.classmgr.VmIsolatedStaticsEntry;
import org.jnode.vm.classmgr.VmLocalVariable;
import org.jnode.vm.classmgr.VmMethod;
import org.jnode.vm.classmgr.VmSharedStaticsEntry;
import org.jnode.vm.classmgr.VmStaticField;
import org.jnode.vm.classmgr.VmStaticMethod;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.compiler.CompileError;
import org.jnode.vm.compiler.CompiledMethod;
import org.jnode.vm.compiler.EntryPoints;
import org.jnode.vm.compiler.InlineBytecodeVisitor;
import org.jnode.vm.facade.TypeSizeInfo;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.objects.CounterGroup;
import org.jnode.vm.x86.compiler.AbstractX86StackManager;
import org.jnode.vm.x86.compiler.X86CompilerConstants;
import org.jnode.vm.x86.compiler.X86CompilerHelper;
import org.jnode.vm.x86.compiler.X86IMTCompiler32;
import org.jnode.vm.x86.compiler.X86IMTCompiler64;
import org.jnode.vm.x86.compiler.X86JumpTable;
/**
* Actual converter from bytecodes to X86 native code. Uses a virtual stack to
* delay item emission, as described in the <a href="http://orp.sourceforge.net/">Open Runtime Platform (ORP)</a>
* project.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
* @author Patrik Reali
*/
final class X86BytecodeVisitor extends InlineBytecodeVisitor implements
X86CompilerConstants {
/**
* Debug this visitor, logs extra info
*/
private static final boolean debug = false;
/**
* If true, do additional verifications. Helps to develop this compiler
*/
private static final boolean paranoia = false;
/**
* Offset in bytes of the first data entry within an array-object
*/
private final int arrayDataOffset;
/**
* Offset in bytes of the array-length value within an array-object
*/
private final int arrayLengthOffset;
/**
* The destination compiled method
*/
private final CompiledMethod cm;
/**
* Current context
*/
private final EntryPoints context;
/**
* Bytecode Address of current instruction
*/
private int curAddress;
/**
* Label of current instruction
*/
private Label _curInstrLabel;
/**
* The method currently being compiled
*/
private VmMethod currentMethod;
/**
* The emitter context
*/
private final EmitterContext eContext;
/**
* Helper class
*/
private final X86CompilerHelper helper;
/**
* The method currently being inline (or null for none)
*/
private InlinedMethodInfo inlinedMethodInfo;
/**
* Class loader
*/
private VmClassLoader loader;
/**
* Emit logging info
*/
private final boolean log;
/**
* Maximum number of local variable slots
*/
private int maxLocals;
/**
* The output stream
*/
private final X86Assembler os;
/**
* Should we set the current instruction label on startInstruction?
*/
private boolean setCurInstrLabel;
/**
* Stackframe utility
*/
private X86StackFrame stackFrame;
/**
* Is this instruction the start of a basic block
*/
private boolean startOfBB;
/**
* Length of os at start of method
*/
private int startOffset;
/**
* Offset in bytes of the TIB reference within an object
*/
private final int tibOffset;
/**
* Item factory
*/
private final ItemFactory ifac;
/**
* Magic method compiler
*/
private final MagicHelper magicHelper;
/**
* FP instruction compiler
*/
private final FPCompiler fpCompiler;
/**
* Type size information
*/
private final TypeSizeInfo typeSizeInfo;
/**
* Current inline depth (starting at 0)
*/
private byte inlineDepth;
/**
* Register used by wstore (see xloadStored methods)
*/
private GPR wstoreReg;
/**
* Constant values that are stored in local variables
*/
private Map<Integer, Item> constLocals = new HashMap<Integer, Item>();
/**
* The current basic block
*/
private BasicBlock currentBasicBlock;
/**
* The parser that is parsing the bytecode we're compiling
*/
private BytecodeParser parser;
/**
* Virtual Stack: this stack contains values that have been computed but not
* emitted yet; emission is delayed to allow for optimizations, in
* particular using registers instead of stack operations.
* <p/>
* The vstack is valid only inside a basic block; items in the stack are
* flushed at the end of the basic block.
* <p/>
* Aliasing: modifying a value that is still on the stack is forbidden. Each
* time a local is assigned, the stack is checked for aliases. For the same
* reason, array and field operations are not delayed.
*/
private final VirtualStack vstack;
/**
* My counters
*/
private final CounterGroup counters = VmUtils.getVm().getCounterGroup(getClass().getName());
/**
* Do counting?
*/
private final boolean countBytecode = true;
private final boolean countConstOps = true;
/**
* Create a new instance
*
* @param outputStream
* @param cm
* @param isBootstrap
* @param context
*/
public X86BytecodeVisitor(NativeStream outputStream, CompiledMethod cm,
boolean isBootstrap, EntryPoints context,
MagicHelper magicHelper, TypeSizeInfo typeSizeInfo) {
this.os = (X86Assembler) outputStream;
this.context = context;
this.typeSizeInfo = typeSizeInfo;
this.magicHelper = magicHelper;
this.vstack = new VirtualStack(os);
final X86RegisterPool gprPool;
final X86RegisterPool xmmPool;
if (os.isCode32()) {
gprPool = new X86RegisterPool.GPRs32();
xmmPool = new X86RegisterPool.XMMs32();
} else {
gprPool = new X86RegisterPool.GPRs64();
xmmPool = new X86RegisterPool.XMMs64();
}
this.ifac = ItemFactory.getFactory();
final AbstractX86StackManager stackMgr = vstack.createStackMgr(gprPool,
ifac);
this.helper = new X86CompilerHelper(os, stackMgr, context, isBootstrap);
this.cm = cm;
final int slotSize = helper.SLOTSIZE;
this.arrayLengthOffset = VmArray.LENGTH_OFFSET * slotSize;
this.arrayDataOffset = VmArray.DATA_OFFSET * slotSize;
this.tibOffset = ObjectLayout.TIB_SLOT * slotSize;
this.log = os.isLogEnabled();
this.eContext = new EmitterContext(os, helper, vstack, gprPool,
xmmPool, ifac, context);
vstack.initializeStackMgr(stackMgr, eContext);
// TODO check for SSE support and switch to SSE compiler if available
//this.fpCompiler = new FPCompilerSSE(this, os, eContext, vstack, arrayDataOffset);
this.fpCompiler = new FPCompilerFPU(this, os, eContext, vstack, arrayDataOffset);
}
private final void assertCondition(boolean cond, String message) {
if (!cond)
throw new Error("assert failed at addresss " + curAddress + ": "
+ message);
}
private final void assertCondition(boolean cond, String message,
Object param) {
if (!cond)
throw new Error("assert failed at addresss " + curAddress + ": "
+ message + param);
}
/**
* Emit code to validate an index of a given array
*
* @param ref
* @param index
*/
final void checkBounds(RefItem ref, IntItem index) {
if (countBytecode) {
counters.getCounter("checkbounds").inc();
}
if (index.isConstant()) {
//System.err.println("Index constant = " + index.getValue() + ", ref.register = " + ref.getRegister());
// only one side has to be checked, > 0 can be checked at compile time
if (countConstOps) {
counters.getCounter("TODOcheckbounds-indexIsConst").inc();
}
}
final Label curInstrLabel = getCurInstrLabel();
final Label test = new Label(curInstrLabel + "$$cbtest");
final Label failed = new Label(curInstrLabel + "$$cbfailed");
assertCondition(ref.isGPR(), "ref must be in a register");
final GPR refr = ref.getRegister();
os.writeJMP(test);
os.setObjectRef(failed);
// Call SoftByteCodes.throwArrayOutOfBounds
os.writePUSH(refr);
if (index.isConstant()) {
os.writePUSH(index.getValue());
} else {
os.writePUSH(index.getRegister());
}
invokeJavaMethod(context.getThrowArrayOutOfBounds());
// CMP length, index
os.setObjectRef(test);
if (index.isConstant()) {
os
.writeCMP_Const(BITS32, refr, arrayLengthOffset, index
.getValue());
} else {
os.writeCMP(refr, arrayLengthOffset, index.getRegister());
}
os.writeJCC(failed, X86Constants.JNA);
}
/**
* Remove all method arguments of the vstack.
*
* @param method
* @param hasSelf
*/
private final void dropParameters(VmMethod method, boolean hasSelf) {
final int[] argTypes = JvmType.getArgumentTypes(method.getSignature());
final int count = argTypes.length;
for (int i = count - 1; i >= 0; i--) {
final int type = argTypes[i];
final Item v = vstack.pop(JvmType.TypeToContainingType(type));
v.release1(eContext);
}
if (hasSelf) {
RefItem v = vstack.popRef();
v.release1(eContext);
}
}
/**
* Store a double word item into an array.
*
* @see #visit_dastore()
* @see #visit_lastore()
*/
private final void dwastore(int jvmType) {
final DoubleWordItem val = (DoubleWordItem) vstack.pop(jvmType);
final IntItem idx = vstack.popInt();
final RefItem ref = vstack.popRef();
val.load(eContext);
idx.loadIf(eContext, ~Item.Kind.CONSTANT);
ref.load(eContext);
final GPR refr = ref.getRegister();
// Check bound
checkBounds(ref, idx);
// Store
loadArrayEntryOffset(refr, ref, idx, 8);
if (os.isCode32()) {
os.writeMOV(INTSIZE, refr, 0, val.getLsbRegister(eContext));
os.writeMOV(INTSIZE, refr, 4, val.getMsbRegister(eContext));
} else {
os.writeMOV(BITS64, refr, 0, val.getRegister(eContext));
}
// Release
ref.release(eContext);
idx.release(eContext);
val.release(eContext);
}
/**
* Pop a word item of the stack and return it to the caller
*
* @param JvmType
*/
private final void dwreturn(int jvmType, boolean callVisitReturn) {
final DoubleWordItem val = (DoubleWordItem) vstack.pop(jvmType);
if (os.isCode32()) {
// Return value must be in EAX:EDX
if (!(val.uses(X86Register.EAX) && val.uses(X86Register.EDX))) {
if (val.uses(X86Register.EAX) || val.uses(X86Register.EDX)) {
val.push(eContext);
}
L1AHelper.requestRegister(eContext, X86Register.EAX, val);
L1AHelper.requestRegister(eContext, X86Register.EDX, val);
val.loadTo32(eContext, X86Register.EAX, X86Register.EDX);
}
} else {
// Return value must be in RAX
if (!val.uses(X86Register.RAX)) {
L1AHelper.requestRegister(eContext, X86Register.RAX, val);
val.loadTo64(eContext, X86Register.RAX);
}
}
// Release
val.release(eContext);
// Do actual return
if (callVisitReturn) {
visit_return();
}
}
/**
* Store a double word item into a local variable
*
* @param jvmType
* @param index
*/
private final void dwstore(int jvmType, int index) {
final int disp = stackFrame.getWideEbpOffset(typeSizeInfo, index);
// Pin down (load) other references to this local
vstack.loadLocal(eContext, disp);
// Load
final DoubleWordItem val = (DoubleWordItem) vstack.pop(jvmType);
final boolean vconst = val.isConstant();
if (vconst && (jvmType == JvmType.LONG)) {
// Store constant long
final long lval = ((LongItem) val).getValue();
os.writeMOV_Const(BITS32, helper.BP, disp + LSB,
(int) (lval & 0xFFFFFFFFL));
os.writeMOV_Const(BITS32, helper.BP, disp + MSB,
(int) ((lval >>> 32) & 0xFFFFFFFFL));
} else if (vconst && (jvmType == JvmType.DOUBLE)) {
// Store constant double
final long lval = Double.doubleToRawLongBits(((DoubleItem) val)
.getValue());
os.writeMOV_Const(BITS32, helper.BP, disp + LSB,
(int) (lval & 0xFFFFFFFFL));
os.writeMOV_Const(BITS32, helper.BP, disp + MSB,
(int) ((lval >>> 32) & 0xFFFFFFFFL));
} else if (val.isFPUStack()) {
// Ensure item is on top of fpu stack
FPUHelper.fxch(os, vstack.fpuStack, val);
if (jvmType == JvmType.DOUBLE) {
os.writeFSTP64(helper.BP, disp);
} else {
os.writeFISTP64(helper.BP, disp);
}
vstack.fpuStack.pop(val);
} else if (val.isStack()) {
// Must be top of stack
if (VirtualStack.checkOperandStack) {
vstack.operandStack.pop(val);
}
if (os.isCode32()) {
os.writePOP(helper.BP, disp + LSB);
os.writePOP(helper.BP, disp + MSB);
} else {
os.writePOP(helper.BP, disp);
os.writeLEA(X86Register.RSP, X86Register.RSP, 8); // garbage
}
} else {
// Load into register
val.load(eContext);
if (os.isCode32()) {
final GPR lsb = val.getLsbRegister(eContext);
final GPR msb = val.getMsbRegister(eContext);
// Store
os.writeMOV(INTSIZE, helper.BP, disp + LSB, lsb);
os.writeMOV(INTSIZE, helper.BP, disp + MSB, msb);
} else {
final GPR64 reg = val.getRegister(eContext);
// Store
os.writeMOV(BITS64, helper.BP, disp, reg);
}
}
// Release
val.release(eContext);
}
/**
* The started basic block has finished.
*/
public void endBasicBlock() {
// flush vstack: at end/begin of basic block are all items on the stack
vstack.push(eContext);
}
/**
* @see org.jnode.vm.compiler.InlineBytecodeVisitor#endInlinedMethod(org.jnode.vm.classmgr.VmMethod)
*/
public void endInlinedMethod(VmMethod previousMethod) {
if (log) {
os.log("End of inlined method");
}
// Do some housekeeping
helper.setMethod(previousMethod);
os.setObjectRef(inlinedMethodInfo.getEndOfInlineLabel());
this.currentMethod = previousMethod;
this.inlineDepth--;
// Push the types on the vstack
inlinedMethodInfo.pushExitStack(ifac, vstack);
// Push the return value
inlinedMethodInfo.pushReturnValue(helper);
// Cleanup
helper.setLabelPrefix(inlinedMethodInfo.getPreviousLabelPrefix());
this.inlinedMethodInfo = inlinedMethodInfo.getPrevious();
if (debug) {
BootLogInstance.get().debug("endInlinedMethod");
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#endInstruction()
*/
public void endInstruction() {
// In debug mode, do a lot of verifications
if (paranoia) {
// Verify the register usage
// No registers can be in use, unless they are on the virtual stack.
final X86RegisterPool pool = eContext.getGPRPool();
pool.visitUsedRegisters(new RegisterVisitor() {
public void visit(X86Register reg) {
if (!vstack.uses(reg)) {
throw new InternalError("Register " + reg
+ " is in use outsite of the vstack in method "
+ currentMethod + " at bytecode address "
+ curAddress);
}
}
});
// All items on the FPU stack must also be on the vstack.
vstack.fpuStack.visitItems(new ItemVisitor() {
public void visit(Item item) {
if (!vstack.contains(item)) {
throw new InternalError(
"Item "
+ item
+ " is not on the vstack, but still of the fpu stack in method "
+ currentMethod + " at address "
+ curAddress);
}
}
});
// No item on the vstack may have been released (kind==0)
vstack.visitItems(new ItemVisitor() {
public void visit(Item item) {
if (item.getKind() == 0) {
throw new InternalError("Item " + item
+ " is kind 0 in method " + currentMethod
+ " at address " + curAddress);
}
if (item.isGPR()) {
if (item instanceof WordItem) {
if (pool.getOwner(((WordItem) item).getRegister()) != item) {
throw new InternalError(
"Item "
+ item
+ " uses a register which is not registered in the register pool in method "
+ currentMethod
+ " at address " + curAddress);
}
} else {
if (os.isCode32()) {
if (pool.getOwner(((DoubleWordItem) item)
.getLsbRegister(eContext)) != item) {
throw new InternalError(
"Item "
+ item
+ " uses an LSB register which is not registered in the " +
"register pool in method "
+ currentMethod
+ " at address "
+ curAddress);
}
if (pool.getOwner(((DoubleWordItem) item)
.getMsbRegister(eContext)) != item) {
throw new InternalError(
"Item "
+ item
+
" uses an MSB register which is not registered in the " +
"register pool in method "
+ currentMethod
+ " at address "
+ curAddress);
}
} else {
if (pool.getOwner(((DoubleWordItem) item)
.getRegister(eContext)) != item) {
throw new InternalError(
"Item "
+ item
+
" uses an register which is not registered in the register pool in method "
+ currentMethod
+ " at address "
+ curAddress);
}
}
}
}
}
});
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#endMethod()
*/
public void endMethod() {
stackFrame.emitTrailer(typeSizeInfo, maxLocals);
}
/**
* A try block has finished
*/
public void endTryBlock() {
setCurInstrLabel = true;
}
/**
* Helper for various pop bytecodes.
*
* @param size
*/
private final void generic_pop(int size) {
final Item v = vstack.pop();
assertCondition(v.getCategory() == (size / helper.SLOTSIZE), "category mismatch");
if (v.isStack()) {
// sanity check
if (VirtualStack.checkOperandStack) {
vstack.operandStack.pop(v);
}
os.writeLEA(helper.SP, helper.SP, size);
}
v.release(eContext);
}
/**
* Emit the core of the instanceof code.
*
* @param objectr Register containing the object reference
* @param trueLabel Where to jump for a true result. A false result will continue
* directly after this method Register ECX must be free and it
* destroyed.
*/
private final void instanceOfClass(GPR objectr, VmClassType<?> type, GPR tmpr,
GPR resultr, Label trueLabel, boolean skipNullTest) {
final int depth = type.getSuperClassDepth();
final int staticsOfs = helper.getSharedStaticsOffset(type);
final Label curInstrLabel = getCurInstrLabel();
final Label notInstanceOfLabel = new Label(curInstrLabel
+ "notInstanceOf");
if (!type.isAlwaysInitialized()) {
if (os.isCode32()) {
helper.writeGetStaticsEntry(curInstrLabel, tmpr, type);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) tmpr, (VmSharedStaticsEntry) type);
}
helper.writeClassInitialize(curInstrLabel, tmpr, tmpr, type);
}
// Clear result (means !instanceof)
if (resultr != null) {
os.writeXOR(resultr, resultr);
}
// Test objectr == null
if (!skipNullTest) {
// Is objectr null?
os.writeTEST(objectr, objectr);
os.writeJCC(notInstanceOfLabel, X86Constants.JZ);
}
final int slotSize = helper.SLOTSIZE;
final int asize = helper.ADDRSIZE;
// TIB -> tmp
os.writeMOV(asize, tmpr, objectr, tibOffset);
// SuperClassesArray -> tmp
os.writeMOV(asize, tmpr, tmpr, arrayDataOffset
+ (TIBLayout.SUPERCLASSES_INDEX * slotSize));
// Length of superclassarray must be >= depth
os.writeCMP_Const(BITS32, tmpr, arrayLengthOffset, depth);
os.writeJCC(notInstanceOfLabel, X86Constants.JNA);
// Get superClassesArray[depth] -> objectr
os.writeMOV(asize, tmpr, tmpr, arrayDataOffset + (depth * slotSize));
// Compare objectr with classtype
os.writeCMP(helper.STATICS, staticsOfs, tmpr);
if (resultr != null) {
os.writeSETCC(resultr, X86Constants.JE);
} else {
// Conditional forward jump is assumed not to be taken.
// Therefor will the JCC followed by a JMP be faster.
os.writeJCC(notInstanceOfLabel, X86Constants.JNE);
os.writeJMP(trueLabel);
}
os.setObjectRef(notInstanceOfLabel);
}
/**
* Emit the core of the instanceof code.
*
* @param objectr Register containing the object reference
* @param typer Register containing the type reference
* @param trueLabel Where to jump for a true result. A false result will continue
* directly after this method Register ECX must be free and it
* destroyed.
*/
private final void instanceOf(GPR objectr, GPR typer, GPR tmpr, GPR cntr,
Label trueLabel, boolean skipNullTest) {
final Label curInstrLabel = getCurInstrLabel();
final Label loopLabel = new Label(curInstrLabel + "loop");
final Label notInstanceOfLabel = new Label(curInstrLabel
+ "notInstanceOf");
if (VmUtils.verifyAssertions()) {
VmUtils._assert(objectr.getSize() == helper.ADDRSIZE, "objectr size");
VmUtils._assert(typer.getSize() == helper.ADDRSIZE, "typer size");
VmUtils._assert(tmpr.getSize() == helper.ADDRSIZE, "tmpr size");
VmUtils._assert(cntr.getSize() == BITS32, "cntr size");
}
if (!skipNullTest) {
/* Is objectref null? */
os.writeTEST(objectr, objectr);
os.writeJCC(notInstanceOfLabel, X86Constants.JZ);
}
final int slotSize = helper.SLOTSIZE;
final int asize = helper.ADDRSIZE;
// TIB -> tmp
os.writeMOV(asize, tmpr, objectr, tibOffset);
// SuperClassesArray -> tmp
os.writeMOV(asize, tmpr, tmpr, arrayDataOffset
+ (TIBLayout.SUPERCLASSES_INDEX * slotSize));
// SuperClassesArray.length -> cntr
os.writeMOV(BITS32, cntr, tmpr, arrayLengthOffset);
// &superClassesArray[cnt-1] -> tmpr
if (os.isCode64()) {
// the MOV to cntr already zero-extends it, so no extension needed.
cntr = L1AHelper.get64BitReg(eContext, cntr);
}
os.writeLEA(tmpr, tmpr, cntr, slotSize, arrayDataOffset - slotSize);
os.setObjectRef(loopLabel);
// cmp superClassesArray[index],type
os.writeCMP(tmpr, 0, typer);
// Is equal?
os.writeJCC(trueLabel, X86Constants.JE);
// index--
os.writeLEA(tmpr, tmpr, -slotSize);
// cnt--
os.writeDEC(cntr);
// if (cnt == 0)
os.writeJCC(notInstanceOfLabel, X86Constants.JZ);
// Goto loop
os.writeJMP(loopLabel);
// Not instanceof
os.setObjectRef(notInstanceOfLabel);
}
/**
* Generate code to invoke the given method.
*
* @param method
*/
private final void invokeJavaMethod(VmMethod method) {
if (log) {
os.log("VStack: " + vstack + ", method: " + method);
}
helper.invokeJavaMethod(method);
// Test the stack alignment
stackFrame.writeStackAlignmentTest(getCurInstrLabel());
}
/**
* Write an integer operation.
*
* @param operation
* @param commutative
* @see org.jnode.assembler.x86.X86Operation
*/
private final void ioperation(int operation, boolean commutative) {
IntItem v2 = vstack.popInt();
IntItem v1 = vstack.popInt();
if (countConstOps) {
counters.getCounter("ioperation").inc();
}
if (v2.isConstant() && v1.isConstant()) {
if (countConstOps) {
counters.getCounter("ioperation-const").inc();
}
final int v;
switch (operation) {
case X86Operation.ADD:
v = v1.getValue() + v2.getValue();
break;
case X86Operation.AND:
v = v1.getValue() & v2.getValue();
break;
case X86Operation.OR:
v = v1.getValue() | v2.getValue();
break;
case X86Operation.SUB:
v = v1.getValue() - v2.getValue();
break;
case X86Operation.XOR:
v = v1.getValue() ^ v2.getValue();
break;
default:
throw new RuntimeException("Invalid operation " + operation);
}
v1.release(eContext);
v2.release(eContext);
vstack.push(ifac.createIConst(eContext, v));
} else {
if (countConstOps) {
counters.getCounter("ioperation-nonconst").inc();
}
if (prepareForOperation(v1, v2, commutative)) {
// Swap
final IntItem tmp = v2;
v2 = v1;
v1 = tmp;
}
if (v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOioperation-SECONDconst").inc();
}
final int value = v2.getValue();
switch (operation) {
case X86Operation.ADD:
if (value == 0) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstADD0").inc();
}
v2.release(eContext);
vstack.push(v1);
return;
} else if (value == 1) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstADD1").inc();
}
v2.release(eContext);
os.writeINC(v1.getRegister());
vstack.push(v1);
return;
} else if (value == -1) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstADD-1").inc();
}
v2.release(eContext);
os.writeDEC(v1.getRegister());
vstack.push(v1);
return;
}
break;
case X86Operation.AND:
if (value == 0) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstAND0").inc();
}
v1.release(eContext);
v2.release(eContext);
vstack.push(ifac.createIConst(eContext, 0));
return;
} /*else if (value == 0xFF) {
// TODO DOES NOT WORK FOR SOME REASON :/
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstAND0xFF").inc();
}
v2.release(eContext)
v1.loadToBITS8GPR(eContext);
final GPR r = v1.getRegister();
os.writeMOVSX(r, r, BYTESIZE);
vstack.push(v1);
return;
} else if (value == 0xFFFF) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstAND0xFFFF").inc();
}
v2.release(eContext);
final GPR r1 = v1.getRegister();
os.writeMOVSX(r1, r1, WORDSIZE);
vstack.push(v1);
return;
}*/
break;
case X86Operation.OR:
if (value == 0) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstOR0").inc();
}
v2.release(eContext);
vstack.push(v1);
return;
}
break;
case X86Operation.SUB:
if (value == 1) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstSUB1").inc();
}
v2.release(eContext);
os.writeDEC(v1.getRegister());
vstack.push(v1);
return;
} else if (value == -1) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstSUB-1").inc();
}
v2.release(eContext);
os.writeINC(v1.getRegister());
vstack.push(v1);
return;
}
break;
case X86Operation.XOR:
if (value == -1) {
if (countConstOps) {
counters.getCounter("NEWioperation-SECONDconstXOR-1").inc();
}
v2.release(eContext);
os.writeNOT(v1.getRegister());
vstack.push(v1);
return;
}
break;
default:
throw new RuntimeException("Invalid operation " + operation);
}
//System.err.println("Commutativ operation, second = " + v2.getValue() + ", operation = " + operation);
}
//
// Normal processing of operation:
//
final X86Register.GPR r1 = (X86Register.GPR) v1.getRegister();
switch (v2.getKind()) {
case Item.Kind.GPR:
os.writeArithOp(operation, r1, (X86Register.GPR) v2.getRegister());
break;
case Item.Kind.LOCAL:
os.writeArithOp(operation, r1, helper.BP, v2
.getOffsetToFP(eContext));
break;
case Item.Kind.CONSTANT:
os.writeArithOp(operation, r1, v2.getValue());
break;
}
v2.release(eContext);
vstack.push(v1);
}
}
/**
* Write a shift operation.
*
* @param operation
*/
private final void ishift(int operation) {
final IntItem shift = vstack.popInt();
final boolean isconst = shift.isConstant();
if (!isconst && !shift.uses(X86Register.ECX)) {
L1AHelper.requestRegister(eContext, X86Register.ECX, shift);
shift.loadTo(eContext, X86Register.ECX);
}
// Pop & load
final IntItem val = vstack.popInt();
val.load(eContext);
if (val.isConstant() && isconst) {
// TODO, implement constant ishift
if (countConstOps) {
counters.getCounter("TODOishift-const").inc();
}
}
final GPR valr = val.getRegister();
if (isconst) {
final int imm8 = shift.getValue();
os.writeShift(operation, valr, imm8);
} else {
os.writeShift_CL(operation, valr);
}
// Release
shift.release(eContext);
// Push result
vstack.push(val);
}
/**
* Emit code to load the effective address of an array entry into the
* destination register.
*
* @param dst
* @param ref
* @param index
* @param scale
*/
private final void loadArrayEntryOffset(GPR dst, RefItem ref,
IntItem index, int scale) {
assertCondition(ref.isGPR(), "ref must be in a register");
final GPR refr = ref.getRegister();
if (index.isConstant()) {
final int offset = index.getValue() * scale;
os.writeLEA(dst, refr, arrayDataOffset + offset);
} else {
final GPR32 idxr = (GPR32) index.getRegister();
if (os.isCode32()) {
os.writeLEA(dst, refr, idxr, scale, arrayDataOffset);
} else {
final GPR64 idxr64 = (GPR64) eContext.getGPRPool().getRegisterInSameGroup(idxr, JvmType.LONG);
os.writeMOVSXD(idxr64, (GPR32) idxr);
os.writeLEA(dst, refr, idxr64, scale, arrayDataOffset);
}
}
}
/**
* Write an long operation.
*
* @param operation
* @param commutative
* @see org.jnode.assembler.x86.X86Operation
*/
private final void loperation(int operationLsb, int operationMsb,
boolean commutative) {
LongItem v2 = vstack.popLong();
LongItem v1 = vstack.popLong();
if (countConstOps) {
counters.getCounter("loperation").inc();
}
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOloperation-const").inc();
}
// TODO loperation for constants
} else if (v1.isConstant() || v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOloperation-ONEconst" + commutative).inc();
}
// TODO check for constants making it easier...
}
if (prepareForOperation(v1, v2, commutative)) {
// Swap
final LongItem tmp = v2;
v2 = v1;
v1 = tmp;
}
if (os.isCode32()) {
final GPR r1_lsb = v1.getLsbRegister(eContext);
final GPR r1_msb = v1.getMsbRegister(eContext);
switch (v2.getKind()) {
case Item.Kind.GPR:
os.writeArithOp(operationLsb, r1_lsb, v2
.getLsbRegister(eContext));
os.writeArithOp(operationMsb, r1_msb, v2
.getMsbRegister(eContext));
break;
case Item.Kind.LOCAL:
os.writeArithOp(operationLsb, r1_lsb, helper.BP, v2
.getLsbOffsetToFP(eContext));
os.writeArithOp(operationMsb, r1_msb, helper.BP, v2
.getMsbOffsetToFP(eContext));
break;
case Item.Kind.CONSTANT:
os.writeArithOp(operationLsb, r1_lsb, v2.getLsbValue());
os.writeArithOp(operationMsb, r1_msb, v2.getMsbValue());
break;
}
} else {
final GPR64 r1 = v1.getRegister(eContext);
switch (v2.getKind()) {
case Item.Kind.GPR:
os.writeArithOp(operationLsb, r1, v2.getRegister(eContext));
break;
case Item.Kind.LOCAL:
os.writeArithOp(operationLsb, r1, X86Register.RBP, v2
.getOffsetToFP(eContext));
break;
case Item.Kind.CONSTANT:
// 64-bit instructions still take 32-bit constants, so load it
// first.
v2.load(eContext);
os.writeArithOp(operationLsb, r1, v2.getRegister(eContext));
break;
}
}
// Release
v2.release(eContext);
// Push result
vstack.push(v1);
}
/**
* Prepare both operand for operand. At least one operand is loaded into a
* register. The other operand is constant, local or register.
*
* @param destAndSource
* @param source
* @param commutative
* @return True if the operand must be swapped. when not commutative, false
* is always returned.
*/
private final boolean prepareForOperation(Item destAndSource, Item source,
boolean commutative) {
// WARNING: source was on top of the virtual stack (thus higher than
// destAndSource)
// x86 can only deal with one complex argument
// destAndSource must be a register
if (commutative) {
if (destAndSource.getKindWeight() < source.getKindWeight()) {
// We should swap
source.load(eContext);
destAndSource.loadIf(eContext, (Item.Kind.STACK | Item.Kind.FPUSTACK));
return true;
}
}
source.loadIf(eContext, (Item.Kind.STACK | Item.Kind.FPUSTACK));
destAndSource.load(eContext);
return false;
}
/**
* @param parser
* @see org.jnode.vm.bytecode.BytecodeVisitor#setParser(org.jnode.vm.bytecode.BytecodeParser)
*/
public void setParser(BytecodeParser parser) {
this.parser = parser;
}
/**
* The given basic block is about to start.
*/
public void startBasicBlock(BasicBlock bb) {
this.currentBasicBlock = bb;
if (log) {
os.log("Start of basic block " + bb);
}
if (debug) {
BootLogInstance.get().debug("-- Start of BB " + bb);
}
startOfBB = true;
this.vstack.reset();
eContext.getGPRPool().reset(os);
// Push the result from the outer method stack on the vstack
if (inlinedMethodInfo != null) {
inlinedMethodInfo.pushOuterMethodStack(ifac, vstack);
}
// Push the items on the vstack the result from a previous basic block.
final TypeStack tstack = bb.getStartStack();
vstack.pushAll(ifac, tstack);
// Clear all constant locals
constLocals.clear();
if (debug) {
BootLogInstance.get().debug("-- VStack: " + vstack.toString());
}
}
/**
* @see org.jnode.vm.compiler.InlineBytecodeVisitor#startInlinedMethodCode(VmMethod,
* int)
*/
public void startInlinedMethodCode(VmMethod inlinedMethod, int newMaxLocals) {
if (log) {
os.log("Start of inlined method code");
}
if (debug) {
BootLogInstance.get().debug("startInlinedMethodCode(" + inlinedMethod + ')');
}
// TODO: check whether this is really needed
// For now yes, because a new basic block resets the registerpool
// and that fails if not all registers are freed.
vstack.push(eContext);
this.inlinedMethodInfo.setOuterMethodStack(vstack.asTypeStack());
this.inlineDepth++;
}
/**
* @see org.jnode.vm.compiler.InlineBytecodeVisitor#startInlinedMethodHeader(VmMethod,
* int)
*/
public void startInlinedMethodHeader(VmMethod inlinedMethod,
int newMaxLocals) {
if (log) {
os.log("Start of inlined method header " + inlinedMethod.getName());
}
if (debug) {
BootLogInstance.get().debug("startInlinedMethodHeader(" + inlinedMethod + ')');
}
maxLocals = newMaxLocals;
final Label curInstrLabel = getCurInstrLabel();
final String prefix = curInstrLabel + "_" + inlinedMethod.getName()
+ '_';
this.inlinedMethodInfo = new InlinedMethodInfo(inlinedMethodInfo,
inlinedMethod, new Label(curInstrLabel + "_end_of_inline"),
helper.getLabelPrefix());
helper.setLabelPrefix(prefix);
this.currentMethod = inlinedMethod;
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#startInstruction(int)
*/
public void startInstruction(int address) {
if (debug) {
BootLogInstance.get().debug("#" + address + '\t' + vstack);
}
if (log) {
if (debug) {
os.log("#" + address + " VStack: " + vstack.toString());
} else {
os.log("#" + address);
}
}
this.curAddress = address;
this._curInstrLabel = null;
if (startOfBB || setCurInstrLabel) {
os.setObjectRef(getCurInstrLabel());
startOfBB = false;
setCurInstrLabel = false;
}
final int offset = os.getLength() - startOffset;
cm.add(currentMethod, address, offset, inlineDepth);
}
/**
* @param method
* @see org.jnode.vm.bytecode.BytecodeVisitor#startMethod(org.jnode.vm.classmgr.VmMethod)
*/
public void startMethod(VmMethod method) {
if (debug) {
BootLogInstance.get().debug("setMethod(" + method + ')');
}
this.currentMethod = method;
this.maxLocals = method.getBytecode().getNoLocals();
this.loader = method.getDeclaringClass().getLoader();
helper.reset();
helper.setMethod(method);
// this.startOffset = os.getLength();
this.stackFrame = new X86StackFrame(os, helper, method, context, cm);
this.startOffset = stackFrame.emitHeader();
}
/**
* A try block is about to start
*/
public void startTryBlock() {
setCurInstrLabel = true;
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aaload()
*/
public final void visit_aaload() {
if (countBytecode) {
counters.getCounter("aaload").inc();
}
waload(JvmType.REFERENCE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aastore()
*/
public final void visit_aastore() {
if (countBytecode) {
counters.getCounter("aastore").inc();
}
wastore(JvmType.REFERENCE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aconst_null()
*/
public final void visit_aconst_null() {
if (countBytecode) {
counters.getCounter("aconst_null").inc();
}
vstack.push(ifac.createAConst(eContext, null));
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_aload(int)
*/
public final void visit_aload(int index) {
if (countBytecode) {
counters.getCounter("aload").inc();
}
wload(JvmType.REFERENCE, index, false);
}
/**
* @see org.jnode.vm.compiler.CompilerBytecodeVisitor#visit_aloadStored(int)
*/
public void visit_aloadStored(int index) {
if (countBytecode) {
counters.getCounter("aloadStored").inc();
}
wload(JvmType.REFERENCE, index, true);
}
/**
* @param classRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_anewarray(org.jnode.vm.classmgr.VmConstClass)
*/
public final void visit_anewarray(VmConstClass classRef) {
if (countBytecode) {
counters.getCounter("anewarray").inc();
}
// Push all, since we're going to call other methods
vstack.push(eContext);
// Claim EAX/RAX, we're going to use it later
L1AHelper.requestRegister(eContext, helper.AAX);
// Request tmp register
final GPR classr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
// Pop
final IntItem cnt = vstack.popInt();
// Load the count value
cnt.load(eContext);
final GPR cntr = cnt.getRegister();
// Resolve the class
writeResolveAndLoadClassToReg(classRef, classr);
// Release EAX so it can be used by invokeJavaMethod
L1AHelper.releaseRegister(eContext, helper.AAX);
os.writePUSH(classr); /* Class */
os.writePUSH(cntr); /* Count */
invokeJavaMethod(context.getAnewarrayMethod());
/* Result is already push on the stack */
// Release
cnt.release(eContext);
L1AHelper.releaseRegister(eContext, classr);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_areturn()
*/
public final void visit_areturn() {
if (countBytecode) {
counters.getCounter("areturn").inc();
}
wreturn(JvmType.REFERENCE, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_arraylength()
*/
public final void visit_arraylength() {
if (countBytecode) {
counters.getCounter("arraylength").inc();
}
final RefItem ref = vstack.popRef();
final IntItem result = (IntItem) L1AHelper.requestWordRegister(
eContext, JvmType.INT, false);
// Load
ref.load(eContext);
final GPR refr = ref.getRegister();
final GPR resultr = result.getRegister();
// Get length
os.writeMOV(INTSIZE, resultr, refr, arrayLengthOffset);
// Release
ref.release(eContext);
// Push result
vstack.push(result);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_astore(int)
*/
public final void visit_astore(int index) {
if (countBytecode) {
counters.getCounter("astore").inc();
}
wstore(JvmType.REFERENCE, index);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_athrow()
*/
public final void visit_athrow() {
if (countBytecode) {
counters.getCounter("athrow").inc();
}
final RefItem ref = vstack.popRef();
// Exception must be in EAX
if (!ref.uses(X86Register.EAX)) {
L1AHelper.requestRegister(eContext, X86Register.EAX, ref);
ref.loadTo(eContext, X86Register.EAX);
}
// Jump
helper.writeJumpTableCALL(X86JumpTable.VM_ATHROW_IDX);
// Release
ref.release(eContext);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_baload()
*/
public final void visit_baload() {
if (countBytecode) {
counters.getCounter("baload").inc();
}
waload(JvmType.BYTE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_bastore()
*/
public final void visit_bastore() {
if (countBytecode) {
counters.getCounter("bastore").inc();
}
wastore(JvmType.BYTE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_caload()
*/
public final void visit_caload() {
if (countBytecode) {
counters.getCounter("caload").inc();
}
waload(JvmType.CHAR);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_castore()
*/
public final void visit_castore() {
if (countBytecode) {
counters.getCounter("castore").inc();
}
wastore(JvmType.CHAR);
}
/**
* @param classRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_checkcast(org.jnode.vm.classmgr.VmConstClass)
*/
public final void visit_checkcast(VmConstClass classRef) {
if (countBytecode) {
counters.getCounter("checkcast").inc();
}
// Resolve classRef
classRef.resolve(loader);
final VmType<?> resolvedType = classRef.getResolvedVmClass();
final Label curInstrLabel = getCurInstrLabel();
if (resolvedType.isInterface() || resolvedType.isArray()) {
// ClassRef is an interface or array, do the slow test
// Pre-claim ECX
L1AHelper.requestRegister(eContext, helper.AAX);
// check that top item is a reference
final RefItem ref = vstack.popRef();
// Load the ref
ref.load(eContext);
final GPR refr = ref.getRegister();
final GPR classr = helper.AAX;
final GPR cntr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.INT, false);
final GPR tmpr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
// Resolve the class
writeResolveAndLoadClassToReg(classRef, classr);
helper.writeClassInitialize(curInstrLabel, classr, tmpr, resolvedType);
final Label okLabel = new Label(curInstrLabel + "cc_ok");
/* Is objectref null? */
os.writeTEST(refr, refr);
os.writeJCC(okLabel, X86Constants.JZ);
/* Is instanceof? */
instanceOf(refr, classr, tmpr, cntr, okLabel, true);
/* Not instanceof */
// Call classCastFailed
os.writePUSH(refr);
os.writePUSH(classr);
// Release temp registers here, so invokeJavaMethod can use it
L1AHelper.releaseRegister(eContext, cntr);
L1AHelper.releaseRegister(eContext, tmpr);
L1AHelper.releaseRegister(eContext, classr);
invokeJavaMethod(context.getClassCastFailedMethod());
/* Normal exit */
os.setObjectRef(okLabel);
// Leave ref on stack
vstack.push(ref);
} else {
// classRef is a class, do the fast test
// Pre-claim EAX/RAX
L1AHelper.requestRegister(eContext, helper.AAX);
// check that top item is a reference
final RefItem ref = vstack.popRef();
// Load the ref
ref.load(eContext);
final GPR refr = ref.getRegister();
final GPR tmpr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
final Label okLabel = new Label(curInstrLabel + "cc_ok");
// Is objectref null?
os.writeTEST(refr, refr);
os.writeJCC(okLabel, X86Constants.JZ);
// Is instanceof?
instanceOfClass(refr, (VmClassType<?>) resolvedType, tmpr, null,
okLabel, true);
// Not instanceof
// Load class into tmpr
if (os.isCode32()) {
helper.writeGetStaticsEntry(curInstrLabel, tmpr, resolvedType);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) tmpr, (VmSharedStaticsEntry) resolvedType);
}
// Call SoftByteCodes.classCastFailed(Object, VmType)
os.writePUSH(refr);
os.writePUSH(tmpr);
// Release temp registers here, so invokeJavaMethod can use it
L1AHelper.releaseRegister(eContext, helper.AAX);
L1AHelper.releaseRegister(eContext, tmpr);
invokeJavaMethod(context.getClassCastFailedMethod());
/* Normal exit */
os.setObjectRef(okLabel);
// Leave ref on stack
vstack.push(ref);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_d2f()
*/
public final void visit_d2f() {
if (countBytecode) {
counters.getCounter("d2f").inc();
}
fpCompiler.convert(JvmType.DOUBLE, JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_d2i()
*/
public final void visit_d2i() {
if (countBytecode) {
counters.getCounter("d2i").inc();
}
fpCompiler.convert(JvmType.DOUBLE, JvmType.INT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_d2l()
*/
public final void visit_d2l() {
if (countBytecode) {
counters.getCounter("d2l").inc();
}
fpCompiler.convert(JvmType.DOUBLE, JvmType.LONG);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dadd()
*/
public final void visit_dadd() {
if (countBytecode) {
counters.getCounter("dadd").inc();
}
fpCompiler.add(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_daload()
*/
public final void visit_daload() {
if (countBytecode) {
counters.getCounter("daload").inc();
}
fpCompiler.fpaload(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dastore()
*/
public final void visit_dastore() {
if (countBytecode) {
counters.getCounter("dastore").inc();
}
dwastore(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dcmpg()
*/
public final void visit_dcmpg() {
if (countBytecode) {
counters.getCounter("dcmpg").inc();
}
fpCompiler.compare(true, JvmType.DOUBLE, getCurInstrLabel());
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dcmpl()
*/
public final void visit_dcmpl() {
if (countBytecode) {
counters.getCounter("dcmpl").inc();
}
fpCompiler.compare(false, JvmType.DOUBLE, getCurInstrLabel());
}
/**
* @param value
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dconst(double)
*/
public final void visit_dconst(double value) {
if (countBytecode) {
counters.getCounter("dconst").inc();
}
vstack.push(ifac.createDConst(eContext, value));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ddiv()
*/
public final void visit_ddiv() {
if (countBytecode) {
counters.getCounter("ddiv").inc();
}
fpCompiler.div(JvmType.DOUBLE);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dload(int)
*/
public final void visit_dload(int index) {
if (countBytecode) {
counters.getCounter("dload").inc();
}
vstack.push(ifac.createLocal(JvmType.DOUBLE, stackFrame
.getWideEbpOffset(typeSizeInfo, index)));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dmul()
*/
public final void visit_dmul() {
if (countBytecode) {
counters.getCounter("dmul").inc();
}
fpCompiler.mul(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dneg()
*/
public final void visit_dneg() {
if (countBytecode) {
counters.getCounter("dneg").inc();
}
fpCompiler.neg(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_drem()
*/
public final void visit_drem() {
if (countBytecode) {
counters.getCounter("drem").inc();
}
fpCompiler.rem(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dreturn()
*/
public final void visit_dreturn() {
if (countBytecode) {
counters.getCounter("dreturn").inc();
}
dwreturn(JvmType.DOUBLE, true);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dstore(int)
*/
public final void visit_dstore(int index) {
if (countBytecode) {
counters.getCounter("dstore").inc();
}
dwstore(JvmType.DOUBLE, index);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dsub()
*/
public final void visit_dsub() {
if (countBytecode) {
counters.getCounter("dsub").inc();
}
fpCompiler.sub(JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup()
*/
public final void visit_dup() {
if (countBytecode) {
counters.getCounter("dup").inc();
}
final Item v1 = vstack.pop();
v1.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
vstack.push(v1);
vstack.push(v1.clone(eContext));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup_x1()
*/
public final void visit_dup_x1() {
if (countBytecode) {
counters.getCounter("dup_x1").inc();
}
final Item v1 = vstack.pop();
final Item v2 = vstack.pop();
v1.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
v2.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
vstack.push(v1.clone(eContext));
vstack.push(v2);
vstack.push(v1);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup_x2()
*/
public final void visit_dup_x2() {
if (countBytecode) {
counters.getCounter("dup_x2").inc();
}
final Item v1 = vstack.pop();
final Item v2 = vstack.pop();
v1.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
v2.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
if (v2.getCategory() == 2) {
// form2
vstack.push(v1.clone(eContext));
vstack.push(v2);
vstack.push(v1);
} else {
// form1
final Item v3 = vstack.pop();
v3.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
vstack.push(v1.clone(eContext));
vstack.push(v3);
vstack.push(v2);
vstack.push(v1);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup2()
*/
public final void visit_dup2() {
if (countBytecode) {
counters.getCounter("dup2").inc();
}
final Item v1 = vstack.pop();
v1.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
if (v1.getCategory() == 1) {
// form1
final Item v2 = vstack.pop();
v2.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
assertCondition(v2.getCategory() == 1, "category mismatch");
vstack.push(v2.clone(eContext));
vstack.push(v1.clone(eContext));
vstack.push(v2);
vstack.push(v1);
} else {
vstack.push(v1.clone(eContext));
vstack.push(v1);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup2_x1()
*/
public final void visit_dup2_x1() {
if (countBytecode) {
counters.getCounter("dup2_x1").inc();
}
final Item v1 = vstack.pop();
final Item v2 = vstack.pop();
v1.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
v2.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
assertCondition(v2.getCategory() == 1, "category mismatch");
if (v1.getCategory() == 2) { // form2
vstack.push(v1.clone(eContext));
vstack.push(v2);
vstack.push(v1);
} else {
final Item v3 = vstack.pop();
v3.loadIf(eContext, Item.Kind.STACK | Item.Kind.FPUSTACK);
vstack.push(v2.clone(eContext));
vstack.push(v1.clone(eContext));
vstack.push(v3);
vstack.push(v2);
vstack.push(v1);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup2_x2()
*
public final void visit_dup2_x2() {
if(countBytecode) { counters.getCounter("dup2_x2").inc(); }
// Push all on the stack, since this opcode is just too complicated
vstack.push(eContext);
System.out.println("####### dup2_x2");
final Item v1 = vstack.pop1();
final Item v2 = vstack.pop1();
final int c1 = v1.getCategory();
final int c2 = v2.getCategory();
// Perform a stack swap independent of the actual form
os.writePOP(helper.AAX); // Value1
os.writePOP(helper.ABX); // Value2
os.writePOP(helper.ACX); // Value3
os.writePOP(helper.ADX); // Value4
os.writePUSH(helper.ABX); // Value2
os.writePUSH(helper.AAX); // Value1
os.writePUSH(helper.ADX); // Value4
os.writePUSH(helper.ACX); // Value3
os.writePUSH(helper.ABX); // Value2
os.writePUSH(helper.AAX); // Value1
// Now update the operandstack
// cope with brain-dead definition from Sun (look-like somebody there
// was to eager to optimize this and it landed in the compiler...
if (c2 == 2) {
// form 4
assertCondition(c1 == 2, "category mismatch");
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v2);
vstack.push1(v1);
} else {
final Item v3 = vstack.pop1();
int c3 = v3.getCategory();
if (c1 == 2) {
// form 2
assertCondition(c3 == 1, "category mismatch");
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
} else if (c3 == 2) {
// form 3
vstack.push1(ifac.createStack(v2.getType()));
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
} else {
// form 1
final Item v4 = vstack.pop1();
vstack.push1(ifac.createStack(v2.getType()));
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v4);
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
}
}
}*/
/**
* New version, corrects issue 760
*
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_dup2_x2()
*/
public final void visit_dup2_x2() {
// Push all on the stack, since this opcode is just too complicated
vstack.push(eContext);
System.out.println("NEW dup2_x2");
final Item v1 = vstack.pop1();
final Item v2 = vstack.pop1();
final int c1 = v1.getCategory();
final int c2 = v2.getCategory();
// Perform a stack swap independent of the actual form
os.writePOP(helper.AAX); // Value1
os.writePOP(helper.ABX); // Value2
os.writePOP(helper.ACX); // Value3
os.writePOP(helper.ADX); // Value4
os.writePUSH(helper.ABX); // Value2
os.writePUSH(helper.AAX); // Value1
os.writePUSH(helper.ADX); // Value4
os.writePUSH(helper.ACX); // Value3
os.writePUSH(helper.ABX); // Value2
os.writePUSH(helper.AAX); // Value1
// Now update the operandstack
// cope with brain-dead definition from Sun (look-like somebody there
// was to eager to optimize this and it landed in the compiler...
if (c1 == 2) {
if (c2 == 2) {
// form 4
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v2);
vstack.push1(v1);
} else {
// form 2
final Item v3 = vstack.pop1();
int c3 = v3.getCategory();
assertCondition(c3 == 1, "category mismatch");
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
}
} else {
assertCondition(c2 == 1, "category mismatch");
final Item v3 = vstack.pop1();
int c3 = v3.getCategory();
if (c3 == 2) {
// form 3
vstack.push1(ifac.createStack(v2.getType()));
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
} else {
// form 1
final Item v4 = vstack.pop1();
vstack.push1(ifac.createStack(v2.getType()));
vstack.push1(ifac.createStack(v1.getType()));
vstack.push1(v4);
vstack.push1(v3);
vstack.push1(v2);
vstack.push1(v1);
}
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_f2d()
*/
public final void visit_f2d() {
if (countBytecode) {
counters.getCounter("f2d").inc();
}
fpCompiler.convert(JvmType.FLOAT, JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_f2i()
*/
public final void visit_f2i() {
if (countBytecode) {
counters.getCounter("f2i").inc();
}
fpCompiler.convert(JvmType.FLOAT, JvmType.INT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_f2l()
*/
public final void visit_f2l() {
if (countBytecode) {
counters.getCounter("f2l").inc();
}
fpCompiler.convert(JvmType.FLOAT, JvmType.LONG);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fadd()
*/
public final void visit_fadd() {
if (countBytecode) {
counters.getCounter("fadd").inc();
}
fpCompiler.add(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_faload()
*/
public final void visit_faload() {
if (countBytecode) {
counters.getCounter("faload").inc();
}
fpCompiler.fpaload(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fastore()
*/
public final void visit_fastore() {
if (countBytecode) {
counters.getCounter("fastore").inc();
}
wastore(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fcmpg()
*/
public final void visit_fcmpg() {
if (countBytecode) {
counters.getCounter("fcmpg").inc();
}
fpCompiler.compare(true, JvmType.FLOAT, getCurInstrLabel());
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fcmpl()
*/
public final void visit_fcmpl() {
if (countBytecode) {
counters.getCounter("fcmpl").inc();
}
fpCompiler.compare(false, JvmType.FLOAT, getCurInstrLabel());
}
/**
* @param value
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fconst(float)
*/
public final void visit_fconst(float value) {
if (countBytecode) {
counters.getCounter("fconst").inc();
}
vstack.push(ifac.createFConst(eContext, value));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fdiv()
*/
public final void visit_fdiv() {
if (countBytecode) {
counters.getCounter("fdiv").inc();
}
fpCompiler.div(JvmType.FLOAT);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fload(int)
*/
public final void visit_fload(int index) {
if (countBytecode) {
counters.getCounter("fload").inc();
}
wload(JvmType.FLOAT, index, false);
}
/**
* @see org.jnode.vm.compiler.CompilerBytecodeVisitor#visit_floadStored(int)
*/
public void visit_floadStored(int index) {
if (countBytecode) {
counters.getCounter("floadStored").inc();
}
wload(JvmType.FLOAT, index, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fmul()
*/
public final void visit_fmul() {
if (countBytecode) {
counters.getCounter("fmul").inc();
}
fpCompiler.mul(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fneg()
*/
public final void visit_fneg() {
if (countBytecode) {
counters.getCounter("fneg").inc();
}
fpCompiler.neg(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_frem()
*/
public final void visit_frem() {
if (countBytecode) {
counters.getCounter("frem").inc();
}
fpCompiler.rem(JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_freturn()
*/
public final void visit_freturn() {
if (countBytecode) {
counters.getCounter("freturn").inc();
}
wreturn(JvmType.FLOAT, true);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fstore(int)
*/
public final void visit_fstore(int index) {
if (countBytecode) {
counters.getCounter("fstore").inc();
}
wstore(JvmType.FLOAT, index);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_fsub()
*/
public final void visit_fsub() {
if (countBytecode) {
counters.getCounter("fsub").inc();
}
fpCompiler.sub(JvmType.FLOAT);
}
/**
* @param fieldRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_getfield(org.jnode.vm.classmgr.VmConstFieldRef)
*/
public final void visit_getfield(VmConstFieldRef fieldRef) {
if (countBytecode) {
counters.getCounter("getfield").inc();
}
fieldRef.resolve(loader);
final VmField field = fieldRef.getResolvedVmField();
if (field.isStatic()) {
throw new IncompatibleClassChangeError(
"getfield called on static field " + fieldRef.getName());
}
final VmInstanceField inf = (VmInstanceField) field;
final int fieldOffset = inf.getOffset();
final int type = JvmType.SignatureToType(fieldRef.getSignature());
final boolean isfloat = JvmType.isFloat(type);
// Pop & load
final RefItem ref = vstack.popRef();
ref.load(eContext);
final GPR refr = ref.getRegister();
// get field
final Item result;
if (!fieldRef.isWide()) {
if (isfloat) {
result = ifac.createFPUStack(JvmType.FLOAT);
os.writeFLD32(refr, fieldOffset);
vstack.fpuStack.push(result);
} else {
final char fieldType = field.getSignature().charAt(0);
final WordItem iw = L1AHelper.requestWordRegister(eContext,
type, (fieldType != 'I') && (type != JvmType.REFERENCE));
final GPR iwr = iw.getRegister();
switch (fieldType) {
case 'Z': // boolean
os.writeMOVZX(iwr, refr, fieldOffset, BITS8);
break;
case 'B': // byte
os.writeMOVSX(iwr, refr, fieldOffset, BITS8);
break;
case 'C': // char
os.writeMOVZX(iwr, refr, fieldOffset, BITS16);
break;
case 'S': // short
os.writeMOVSX(iwr, refr, fieldOffset, BITS16);
break;
case 'I': // int
case 'L': // Object
case '[': // array
os.writeMOV(iwr.getSize(), iwr, refr, fieldOffset);
break;
default:
throw new IllegalArgumentException("Unknown fieldType " + fieldType);
}
result = iw;
}
} else {
if (isfloat) {
result = ifac.createFPUStack(JvmType.DOUBLE);
os.writeFLD64(refr, fieldOffset);
vstack.fpuStack.push(result);
} else {
final DoubleWordItem idw = L1AHelper
.requestDoubleWordRegisters(eContext, type);
if (os.isCode32()) {
final GPR lsb = idw.getLsbRegister(eContext);
final GPR msb = idw.getMsbRegister(eContext);
os.writeMOV(BITS32, lsb, refr, fieldOffset + LSB);
os.writeMOV(BITS32, msb, refr, fieldOffset + MSB);
} else {
final GPR64 reg = idw.getRegister(eContext);
os.writeMOV(BITS64, reg, refr, fieldOffset);
}
result = idw;
}
}
// Release
ref.release(eContext);
// Push result
vstack.push(result);
}
/**
* @param fieldRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_getstatic(org.jnode.vm.classmgr.VmConstFieldRef)
*/
public final void visit_getstatic(VmConstFieldRef fieldRef) {
if (countBytecode) {
counters.getCounter("getstatic").inc();
}
final Label curInstrLabel = getCurInstrLabel();
fieldRef.resolve(loader);
final int type = JvmType.SignatureToType(fieldRef.getSignature());
final VmStaticField sf = (VmStaticField) fieldRef.getResolvedVmField();
// Initialize if needed
if (!sf.getDeclaringClass().isAlwaysInitialized()) {
writeInitializeClass(fieldRef);
}
// Get static field object
if (JvmType.isFloat(type)) {
final boolean is32bit = !fieldRef.isWide();
if (sf.isShared()) {
helper.writeGetStaticsEntryToFPU(curInstrLabel, (VmSharedStaticsEntry) sf, is32bit);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writeGetStaticsEntryToFPU(curInstrLabel,
(VmIsolatedStaticsEntry) sf, is32bit, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
final Item result = ifac.createFPUStack(type);
vstack.fpuStack.push(result);
vstack.push(result);
} else if (!fieldRef.isWide()) {
final WordItem result = L1AHelper.requestWordRegister(eContext,
type, false);
final GPR resultr = result.getRegister();
if (os.isCode32() || (type != JvmType.REFERENCE)) {
if (sf.isShared()) {
helper.writeGetStaticsEntry(curInstrLabel, resultr, (VmSharedStaticsEntry) sf);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writeGetStaticsEntry(curInstrLabel, resultr,
(VmIsolatedStaticsEntry) sf, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
} else {
if (sf.isShared()) {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) resultr, (VmSharedStaticsEntry) sf);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) resultr, (VmIsolatedStaticsEntry) sf);
}
}
vstack.push(result);
} else {
final DoubleWordItem result = L1AHelper.requestDoubleWordRegisters(
eContext, type);
if (os.isCode32()) {
final GPR lsb = result.getLsbRegister(eContext);
final GPR msb = result.getMsbRegister(eContext);
if (sf.isShared()) {
helper.writeGetStaticsEntry64(curInstrLabel, lsb, msb, (VmSharedStaticsEntry) sf);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, lsb, msb, (VmIsolatedStaticsEntry) sf);
}
} else {
final GPR64 reg = result.getRegister(eContext);
if (sf.isShared()) {
helper.writeGetStaticsEntry64(curInstrLabel, reg, (VmSharedStaticsEntry) sf);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, reg, (VmIsolatedStaticsEntry) sf);
}
}
vstack.push(result);
}
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_goto(int)
*/
public final void visit_goto(int address) {
if (countBytecode) {
counters.getCounter("goto").inc();
}
vstack.push(eContext);
os.writeJMP(helper.getInstrLabel(address));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2b()
*/
public final void visit_i2b() {
if (countBytecode) {
counters.getCounter("i2b").inc();
}
final IntItem v = vstack.popInt();
if (v.isConstant()) {
if (countConstOps) {
counters.getCounter("i2b-const").inc();
}
vstack.push(ifac.createIConst(eContext, (byte) v.getValue()));
} else {
if (countConstOps) {
counters.getCounter("i2b-nonconst").inc();
}
v.loadToBITS8GPR(eContext);
final GPR r = v.getRegister();
os.writeMOVSX(r, r, BYTESIZE);
vstack.push(v);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2c()
*/
public final void visit_i2c() {
if (countBytecode) {
counters.getCounter("i2c").inc();
}
final IntItem v = vstack.popInt();
if (v.isConstant()) {
if (countConstOps) {
counters.getCounter("i2c-const").inc();
}
vstack.push(ifac.createIConst(eContext, (char) v.getValue()));
} else {
if (countBytecode) {
counters.getCounter("i2c-nonconst").inc();
}
v.load(eContext);
final GPR r = v.getRegister();
os.writeMOVZX(r, r, WORDSIZE);
vstack.push(v);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2d()
*/
public final void visit_i2d() {
if (countBytecode) {
counters.getCounter("i2d").inc();
}
fpCompiler.convert(JvmType.INT, JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2f()
*/
public final void visit_i2f() {
if (countBytecode) {
counters.getCounter("i2f").inc();
}
fpCompiler.convert(JvmType.INT, JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2l()
*/
public final void visit_i2l() {
if (countBytecode) {
counters.getCounter("i2l").inc();
}
final IntItem v = vstack.popInt();
if (v.isConstant()) {
vstack.push(ifac.createLConst(eContext, v.getValue()));
} else {
final X86RegisterPool pool = eContext.getGPRPool();
final LongItem result;
if (!v.uses(X86Register.EAX)) {
L1AHelper.requestRegister(eContext, X86Register.EAX);
v.loadTo(eContext, X86Register.EAX);
}
if (os.isCode32()) {
L1AHelper.requestRegister(eContext, X86Register.EDX);
result = (LongItem) ifac.createReg(eContext, JvmType.LONG,
X86Register.EAX, X86Register.EDX);
os.writeCDQ(BITS32); /* Sign extend EAX -> EDX:EAX */
pool.transferOwnerTo(X86Register.EAX, result);
pool.transferOwnerTo(X86Register.EDX, result);
// We do not release v, because its register (EAX) is re-used in
// result
} else {
v.release(eContext);
L1AHelper.requestRegister(eContext, X86Register.RAX);
result = (LongItem) ifac.createReg(eContext, JvmType.LONG,
X86Register.RAX);
os.writeCDQE(); // Sign extend EAX -> RAX
pool.transferOwnerTo(X86Register.RAX, result);
}
// Push result
vstack.push(result);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_i2s()
*/
public final void visit_i2s() {
if (countBytecode) {
counters.getCounter("i2s").inc();
}
final IntItem v = vstack.popInt();
if (v.isConstant()) {
vstack.push(ifac.createIConst(eContext, (short) v.getValue()));
} else {
v.load(eContext);
final GPR r = v.getRegister();
os.writeMOVSX(r, r, WORDSIZE);
vstack.push(v);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iadd()
*/
public final void visit_iadd() {
if (countBytecode) {
counters.getCounter("iadd").inc();
}
ioperation(X86Operation.ADD, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iaload()
*/
public final void visit_iaload() {
if (countBytecode) {
counters.getCounter("iaload").inc();
}
waload(JvmType.INT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iand()
*/
public final void visit_iand() {
if (countBytecode) {
counters.getCounter("iand").inc();
}
ioperation(X86Operation.AND, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iastore()
*/
public final void visit_iastore() {
if (countBytecode) {
counters.getCounter("iastore").inc();
}
wastore(JvmType.INT);
}
/**
* @param value
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iconst(int)
*/
public final void visit_iconst(int value) {
if (countBytecode) {
counters.getCounter("iconst").inc();
}
vstack.push(ifac.createIConst(eContext, value));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_idiv()
*/
public final void visit_idiv() {
final X86RegisterPool pool = eContext.getGPRPool();
// Pop the arguments of the vstack
final IntItem v2 = vstack.popInt();
final IntItem v1 = vstack.popInt();
if (countBytecode) {
counters.getCounter("idiv").inc();
}
final int shift;
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("idiv-const").inc();
}
vstack.push(ifac.createIConst(eContext, v1.getValue() / v2.getValue()));
v1.release(eContext);
v2.release(eContext);
} else if (v2.isConstant() && (Math.abs(v2.getValue()) == 1)) {
// CHECK: check again if that is correct
if (countConstOps) {
counters.getCounter("NEWidiv-const+/-1").inc();
}
if (v2.getValue() < 0) {
// Load v1
v1.load(eContext);
os.writeNEG(v1.getRegister());
}
// Release
v2.release(eContext);
// And push the result on the vstack.
vstack.push(v1);
} else if (v2.isConstant() && ((shift = getShiftForMultiplier(Math.abs(v2.getValue()))) > 0)) {
// CHECK: check again if that is correct
if (v2.getValue() < 0) {
if (countConstOps) {
counters.getCounter("NEWidiv-const-shift<0").inc();
}
} else {
if (countConstOps) {
counters.getCounter("idiv-const-shift>0").inc();
}
}
// Load v1
v1.load(eContext);
// Divide by shifting
os.writeSAR(v1.getRegister(), shift);
if (v2.getValue() < 0) {
os.writeNEG(v1.getRegister());
}
// Release
v2.release(eContext);
// And push the result on the vstack.
vstack.push(v1);
} else {
if (countConstOps) {
counters.getCounter("idiv-nonconst").inc();
}
// We need v1 in EAX, so if that is not the case,
// spill those item using EAX
L1AHelper.requestRegister(eContext, X86Register.EAX, v1);
// We need to use EDX, so spill those items using it.
v1.spillIfUsing(eContext, X86Register.EDX);
v2.spillIfUsing(eContext, X86Register.EDX);
L1AHelper.requestRegister(eContext, X86Register.EDX);
// Load v2, v1 into a register
v2.load(eContext);
v1.loadTo(eContext, X86Register.EAX);
// EAX -> sign extend EDX:EAX
os.writeCDQ(BITS32);
// EAX = EDX:EAX / v2.reg
os.writeIDIV_EAX(v2.getRegister());
// Free unused registers
pool.release(X86Register.EDX);
v2.release(eContext);
// And push the result on the vstack.
vstack.push(v1);
}
}
/**
* Helper method for visit_if_acmpxx
*
* @param address
* @param jccOpcode
*/
private final void visit_if_acmp(int address, int jccOpcode) {
if (countConstOps) {
counters.getCounter("if_acmp").inc();
}
RefItem v2 = vstack.popRef();
RefItem v1 = vstack.popRef();
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOif_acmp-const").inc();
}
// TODO implement constant acmp
} else {
if (countConstOps) {
counters.getCounter("if_acmp-nonconst").inc();
}
}
// flush vstack before jumping
vstack.push(eContext);
// TODO: can be less restrictive: v1 must not be register
if (prepareForOperation(v1, v2, true)) {
// Swap
final RefItem tmp = v2;
v2 = v1;
v1 = tmp;
}
final GPR r1 = v1.getRegister();
switch (v2.getKind()) {
case Item.Kind.GPR:
os.writeCMP(r1, v2.getRegister());
break;
case Item.Kind.LOCAL:
os.writeCMP(r1, helper.BP, v2.getOffsetToFP(eContext));
break;
case Item.Kind.CONSTANT:
v2.load(eContext);
os.writeCMP(r1, v2.getRegister());
break;
}
v1.release(eContext);
v2.release(eContext);
os.writeJCC(helper.getInstrLabel(address), jccOpcode);
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_acmpeq(int)
*/
public final void visit_if_acmpeq(int address) {
if (countBytecode) {
counters.getCounter("if_acmpeq").inc();
}
visit_if_acmp(address, X86Constants.JE); // JE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_acmpne(int)
*/
public final void visit_if_acmpne(int address) {
if (countBytecode) {
counters.getCounter("if_acmpne").inc();
}
visit_if_acmp(address, X86Constants.JNE); // JNE
}
/**
* Helper method for visit_if_icmpxx
*
* @param address
* @param jccOpcode
*/
private final void visit_if_icmp(int address, int jccOpcode) {
if (countConstOps) {
counters.getCounter("if_icmp").inc();
}
IntItem v2 = vstack.popInt();
IntItem v1 = vstack.popInt();
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOif_icmp-const").inc();
}
// TODO implement constant if_icmp
} else if (v1.isConstant() || v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOif_icmp-ONEonst-CheckSpecialCases").inc();
}
}
// flush vstack before jumping
vstack.push(eContext);
// TODO: can be less restrictive: v1 must not be register
if (prepareForOperation(v1, v2, (jccOpcode == X86Constants.JE)
|| (jccOpcode == X86Constants.JNE))) {
// Swap
final IntItem tmp = v2;
v2 = v1;
v1 = tmp;
}
final GPR r1 = v1.getRegister();
switch (v2.getKind()) {
case Item.Kind.GPR:
os.writeCMP(r1, v2.getRegister());
break;
case Item.Kind.LOCAL:
os.writeCMP(r1, helper.BP, v2.getOffsetToFP(eContext));
break;
case Item.Kind.CONSTANT:
final int c2 = v2.getValue();
os.writeCMP_Const(r1, c2);
break;
}
v1.release(eContext);
v2.release(eContext);
os.writeJCC(helper.getInstrLabel(address), jccOpcode);
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpeq(int)
*/
public final void visit_if_icmpeq(int address) {
if (countBytecode) {
counters.getCounter("if_icmpeq").inc();
}
visit_if_icmp(address, X86Constants.JE); // JE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpge(int)
*/
public final void visit_if_icmpge(int address) {
if (countBytecode) {
counters.getCounter("if_icmpge").inc();
}
visit_if_icmp(address, X86Constants.JGE); // JGE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpgt(int)
*/
public final void visit_if_icmpgt(int address) {
if (countBytecode) {
counters.getCounter("if_icmpgt").inc();
}
visit_if_icmp(address, X86Constants.JG); // JG
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmple(int)
*/
public final void visit_if_icmple(int address) {
if (countBytecode) {
counters.getCounter("if_icmple").inc();
}
visit_if_icmp(address, X86Constants.JLE); // JLE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmplt(int)
*/
public final void visit_if_icmplt(int address) {
if (countBytecode) {
counters.getCounter("if_icmplt").inc();
}
visit_if_icmp(address, X86Constants.JL); // JL
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_if_icmpne(int)
*/
public final void visit_if_icmpne(int address) {
if (countBytecode) {
counters.getCounter("if_icmpne").inc();
}
visit_if_icmp(address, X86Constants.JNE); // JNE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifeq(int)
*/
public final void visit_ifeq(int address) {
if (countBytecode) {
counters.getCounter("ifeq").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JE); // JE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifge(int)
*/
public final void visit_ifge(int address) {
if (countBytecode) {
counters.getCounter("ifge").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JGE); // JGE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifgt(int)
*/
public final void visit_ifgt(int address) {
if (countBytecode) {
counters.getCounter("ifgt").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JG); // JG
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifle(int)
*/
public final void visit_ifle(int address) {
if (countBytecode) {
counters.getCounter("ifle").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JLE); // JLE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iflt(int)
*/
public final void visit_iflt(int address) {
if (countBytecode) {
counters.getCounter("iflt").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JL); // JL
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifne(int)
*/
public final void visit_ifne(int address) {
if (countBytecode) {
counters.getCounter("ifne").inc();
}
visit_ifxx(JvmType.INT, address, X86Constants.JNE); // JNE
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifnonnull(int)
*/
public final void visit_ifnonnull(int address) {
if (countBytecode) {
counters.getCounter("ifnonnull").inc();
}
visit_ifxx(JvmType.REFERENCE, address, X86Constants.JNE);
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ifnull(int)
*/
public final void visit_ifnull(int address) {
if (countBytecode) {
counters.getCounter("ifnull").inc();
}
visit_ifxx(JvmType.REFERENCE, address, X86Constants.JE);
}
private void visit_ifxx(int type, int address, int jccOpcode) {
// IMPROVE: Local case
final WordItem val = (WordItem) vstack.pop(type);
if ((type == JvmType.INT) && val.isConstant()) {
final int ival = ((IntItem) val).getValue();
final boolean jump;
switch (jccOpcode) {
case X86Constants.JE:
jump = (ival == 0);
break;
case X86Constants.JNE:
jump = (ival != 0);
break;
case X86Constants.JL:
jump = (ival < 0);
break;
case X86Constants.JGE:
jump = (ival >= 0);
break;
case X86Constants.JG:
jump = (ival > 0);
break;
case X86Constants.JLE:
jump = (ival <= 0);
break;
default:
throw new IllegalArgumentException("Unknown jccOpcode "
+ jccOpcode);
}
if (jump) {
// flush vstack before jumping
vstack.push(eContext);
// Actual jump
os.writeJMP(helper.getInstrLabel(address));
}
} else {
val.load(eContext);
final GPR valr = val.getRegister();
// flush vstack before jumping
vstack.push(eContext);
os.writeTEST(valr, valr);
os.writeJCC(helper.getInstrLabel(address), jccOpcode);
}
// Release
val.release(eContext);
}
/**
* @param index
* @param incValue
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iinc(int, int)
*/
public final void visit_iinc(int index, int incValue) {
if (countBytecode) {
counters.getCounter("iinc").inc();
}
final int ebpOfs = stackFrame.getEbpOffset(typeSizeInfo, index);
// pin down other references to this local
vstack.loadLocal(eContext, ebpOfs);
if (incValue == 1) {
os.writeINC(BITS32, helper.BP, ebpOfs);
} else {
os.writeADD(BITS32, helper.BP, ebpOfs, incValue);
}
// Local no longer constant
constLocals.remove(index);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iload(int)
*/
public final void visit_iload(int index) {
if (countBytecode) {
counters.getCounter("iload").inc();
}
wload(JvmType.INT, index, false);
}
/**
* @see org.jnode.vm.compiler.CompilerBytecodeVisitor#visit_iloadStored(int)
*/
public void visit_iloadStored(int index) {
if (countBytecode) {
counters.getCounter("iloadStored").inc();
}
wload(JvmType.INT, index, true);
}
/**
* Convert the given multiplier to a shift number.
*
* @param val
* @return -1 if not shiftable.
*/
private final int getShiftForMultiplier(int val) {
int mul = 2;
for (int i = 1; i <= 31; i++) {
if (val == mul) {
return i;
}
mul <<= 1;
}
return -1;
}
/**
* Convert the given multiplier to a shift number.
*
* @param val
* @return -1 if not shiftable.
*/
private final int getShiftForMultiplier(long val) {
long mul = 1;
for (int i = 0; i <= 63; i++) {
if (val == mul) {
return i;
}
mul <<= 1;
}
return -1;
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_imul()
*/
public final void visit_imul() {
IntItem v2 = vstack.popInt();
IntItem v1 = vstack.popInt();
if (countBytecode) {
counters.getCounter("imul").inc();
}
if (v2.isConstant() && v1.isConstant()) {
if (countConstOps) {
counters.getCounter("imul-const").inc();
}
vstack.push(ifac.createIConst(eContext, v1.getValue() * v2.getValue()));
v1.release(eContext);
v2.release(eContext);
} else {
if (prepareForOperation(v1, v2, true)) {
// Swap
final IntItem tmp = v2;
v2 = v1;
v1 = tmp;
}
final GPR r1 = v1.getRegister();
switch (v2.getKind()) {
case Item.Kind.GPR:
if (countConstOps) {
counters.getCounter("imul-nonconst").inc();
}
os.writeIMUL(r1, v2.getRegister());
break;
case Item.Kind.CONSTANT:
final int val = v2.getValue();
if (val == 0) {
if (countConstOps) {
counters.getCounter("NEWimul-const0").inc();
}
// CHECK: check again if this is correct
//os.writeXOR(r1, r1); // * 0
// since now v1 is 0, it is a CONSTANT value
v1.release(eContext);
v2.release(eContext);
final IntItem item = ifac.createIConst(eContext, 0);
vstack.push(item);
return;
} else if (val == 1) {
if (countConstOps) {
counters.getCounter("imul-const1").inc();
}
// Do nothing
} else if (val == -1) {
if (countConstOps) {
counters.getCounter("imul-const~1").inc();
}
os.writeNEG(r1); // * -1
} else {
final int shift = getShiftForMultiplier(Math.abs(val));
if (shift > 0) {
if (countConstOps) {
counters.getCounter("imul-constShift").inc();
}
// abs(val) is multiple of 2 && val=2^shift where shift
// <=
// 31
os.writeSAL(r1, shift);
if (val < 0) {
os.writeNEG(r1);
}
} else {
if (countConstOps) {
counters.getCounter("imul-nonconst").inc();
}
os.writeIMUL_3(r1, r1, val);
}
}
break;
case Item.Kind.LOCAL:
if (countConstOps) {
counters.getCounter("imul-nonconst").inc();
}
os.writeIMUL(r1, helper.BP, v2.getOffsetToFP(eContext));
break;
}
v2.release(eContext);
vstack.push(v1);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ineg()
*/
public final void visit_ineg() {
if (countBytecode) {
counters.getCounter("ineg").inc();
}
final IntItem val = vstack.popInt();
val.loadIf(eContext, ~Item.Kind.CONSTANT);
if (val.isConstant()) {
if (countConstOps) {
counters.getCounter("ineg-const").inc();
}
vstack.push(ifac.createIConst(eContext, -val.getValue()));
} else {
if (countConstOps) {
counters.getCounter("ineg-nonconst").inc();
}
os.writeNEG(val.getRegister());
vstack.push(val);
}
}
/**
* @see org.jnode.vm.compiler.InlineBytecodeVisitor#visit_inlinedReturn()
*/
public void visit_inlinedReturn(int jvmType) {
if (debug) {
BootLogInstance.get().debug("inlinedReturn [type " + jvmType + ']');
}
// Pop the return value
switch (jvmType) {
case JvmType.INT:
case JvmType.FLOAT:
case JvmType.REFERENCE:
wreturn(jvmType, false);
break;
case JvmType.LONG:
case JvmType.DOUBLE:
dwreturn(jvmType, false);
break;
case JvmType.VOID:
break;
default:
throw new CompileError("Unknown return type " + jvmType);
}
// Push the remaining vstack items to the stack
vstack.push(eContext);
inlinedMethodInfo.setExitStack(vstack);
os.writeJMP(inlinedMethodInfo.getEndOfInlineLabel());
}
/**
* @param classRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_instanceof(org.jnode.vm.classmgr.VmConstClass)
*/
public final void visit_instanceof(VmConstClass classRef) {
if (countBytecode) {
counters.getCounter("instanceof").inc();
}
// Resolve the classRef
classRef.resolve(loader);
// Prepare
final X86RegisterPool pool = eContext.getGPRPool();
final VmType<?> resolvedType = classRef.getResolvedVmClass();
if (resolvedType.isInterface() || resolvedType.isArray()) {
// It is an interface, do it the hard way
// Load reference
final RefItem ref = vstack.popRef();
ref.load(eContext);
final GPR refr = ref.getRegister();
// Allocate tmp registers
final GPR classr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
final GPR cntr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.INT, false);
final GPR tmpr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
final Label curInstrLabel = getCurInstrLabel();
/* Objectref is already on the stack */
writeResolveAndLoadClassToReg(classRef, classr);
helper.writeClassInitialize(curInstrLabel, classr, tmpr, resolvedType);
final Label trueLabel = new Label(curInstrLabel + "io_true");
final Label endLabel = new Label(curInstrLabel + "io_end");
/* Is instanceof? */
instanceOf(refr, classr, tmpr, cntr, trueLabel, false);
final IntItem result = (IntItem) L1AHelper.requestWordRegister(eContext, JvmType.INT, false);
final GPR resultr = result.getRegister();
/* Not instanceof */
// TODO: use setcc instead of jumps
os.writeXOR(resultr, resultr);
os.writeJMP(endLabel);
os.setObjectRef(trueLabel);
os.writeMOV_Const(resultr, 1);
// Push result
os.setObjectRef(endLabel);
ref.release(eContext);
vstack.push(result);
// Release
pool.release(classr);
pool.release(tmpr);
pool.release(cntr);
} else {
// It is a class, do the fast way
//vstack.push(eContext); // just a (slow) test
// Load reference
final RefItem ref = vstack.popRef();
ref.load(eContext);
final GPR refr = ref.getRegister();
// Allocate tmp registers
final GPR tmpr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
final IntItem result = (IntItem) L1AHelper.requestWordRegister(
eContext, JvmType.INT, true);
// Is instanceof
instanceOfClass(refr, (VmClassType<?>) classRef.getResolvedVmClass(),
tmpr, result.getRegister(), null, false);
// Push result
vstack.push(result);
// Release
ref.release(eContext);
pool.release(tmpr);
}
}
/**
* @param methodRef
* @param count
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokeinterface(VmConstIMethodRef,
* int)
*/
public final void visit_invokeinterface(VmConstIMethodRef methodRef,
int count) {
if (countBytecode) {
counters.getCounter("invokeinterface").inc();
}
vstack.push(eContext);
// Resolve the method
methodRef.resolve(loader);
final VmMethod method = methodRef.getResolvedVmMethod();
final int argSlotCount = count - 1;
// remove parameters from vstack
dropParameters(method, true);
// Get objectref -> EAX
os.writeMOV(helper.ADDRSIZE, helper.AAX, helper.SP, argSlotCount
* helper.SLOTSIZE);
// Write the actual invokeinterface
if (os.isCode32()) {
X86IMTCompiler32.emitInvokeInterface(os, method);
} else {
X86IMTCompiler64.emitInvokeInterface(os, method);
}
// Test the stack alignment
stackFrame.writeStackAlignmentTest(getCurInstrLabel());
// Write the push result
helper.pushReturnValue(method.getSignature());
}
/**
* @param methodRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokespecial(org.jnode.vm.classmgr.VmConstMethodRef)
*/
public final void visit_invokespecial(VmConstMethodRef methodRef) {
if (countBytecode) {
counters.getCounter("invokespecial").inc();
}
// Flush the stack before an invoke
vstack.push(eContext);
methodRef.resolve(loader);
try {
final VmMethod sm = methodRef.getResolvedVmMethod();
dropParameters(sm, true);
// Call the methods code from the statics table
helper.invokeJavaMethod(sm);
// Result is already on the stack.
} catch (ClassCastException ex) {
BootLogInstance.get().error(methodRef.getResolvedVmMethod().getClass().getName()
+ '#' + methodRef.getName());
throw ex;
}
}
/**
* @param methodRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokestatic(org.jnode.vm.classmgr.VmConstMethodRef)
*/
public final void visit_invokestatic(VmConstMethodRef methodRef) {
if (countBytecode) {
counters.getCounter("invokestatic").inc();
}
methodRef.resolve(loader);
final VmStaticMethod method = (VmStaticMethod) methodRef
.getResolvedVmMethod();
if (method.getDeclaringClass().isMagicType()) {
magicHelper.emitMagic(eContext, method, true, this, currentMethod);
} else {
// Flush the stack before an invoke
vstack.push(eContext);
dropParameters(method, false);
// Call the methods native code from the statics table
helper.invokeJavaMethod(method);
// Result is already on the stack.
}
}
/**
* @param methodRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_invokevirtual(org.jnode.vm.classmgr.VmConstMethodRef)
*/
public final void visit_invokevirtual(VmConstMethodRef methodRef) {
if (countBytecode) {
counters.getCounter("invokevirtual").inc();
}
methodRef.resolve(loader);
final VmMethod mts = methodRef.getResolvedVmMethod();
if (mts.isStatic()) {
throw new IncompatibleClassChangeError(
"Static method in invokevirtual");
}
final VmInstanceMethod method = (VmInstanceMethod) mts;
final VmType<?> declClass = method.getDeclaringClass();
if (declClass.isMagicType()) {
magicHelper.emitMagic(eContext, method, false, this, currentMethod);
} else {
// TODO: port to ORP style (http://orp.sourceforge.net/)
vstack.push(eContext);
dropParameters(mts, true);
if (method.isFinal() || method.isPrivate() || declClass.isFinal()) {
// Do a fast invocation
if (countBytecode) {
counters.getCounter("invokevirtual-final").inc();
}
// Call the methods native code from the statics table
helper.invokeJavaMethod(method);
// Result is already on the stack.
} else {
// Do a virtual method table invocation
if (countBytecode) {
counters.getCounter("invokevirtual-vmt").inc();
}
final int tibIndex = method.getTibOffset();
final int argSlotCount = Signature.getArgSlotCount(typeSizeInfo, methodRef
.getSignature());
final int slotSize = helper.SLOTSIZE;
final int asize = helper.ADDRSIZE;
/* Get objectref -> EAX */
os.writeMOV(asize, helper.AAX, helper.SP, argSlotCount
* slotSize);
/* Get VMT of objectef -> EAX */
os.writeMOV(asize, helper.AAX, helper.AAX, tibOffset);
/* Get entry in VMT -> EAX */
os.writeMOV(asize, helper.AAX, helper.AAX,
arrayDataOffset + (tibIndex * slotSize));
/* Now invoke the method */
os.writeCALL(helper.AAX, context.getVmMethodNativeCodeField().getOffset());
helper.pushReturnValue(methodRef.getSignature());
// Result is already on the stack.
}
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ior()
*/
public final void visit_ior() {
if (countBytecode) {
counters.getCounter("ior").inc();
}
ioperation(X86Operation.OR, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_irem()
*/
public final void visit_irem() {
if (countBytecode) {
counters.getCounter("irem").inc();
}
// Pre-claim result in EDX
L1AHelper.requestRegister(eContext, X86Register.EDX);
final IntItem result = (IntItem) ifac.createReg(eContext, JvmType.INT,
X86Register.EDX);
eContext.getGPRPool().transferOwnerTo(X86Register.EDX, result);
final IntItem v2 = vstack.popInt();
final IntItem v1 = vstack.popInt();
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOirem-const").inc();
}
} else if (v2.isConstant()) {
// TODO check for 2^n and use AND 0xFF instead of irem
if (countBytecode) {
counters.getCounter("TODOirem-SECONDconst" + v2.getValue()).inc();
}
}
// v1 must be in EAX
L1AHelper.requestRegister(eContext, X86Register.EAX, v1);
// Load
v2.loadIf(eContext, ~Item.Kind.LOCAL);
v1.loadTo(eContext, X86Register.EAX);
// Calculate
os.writeCDQ(BITS32); // EAX -> EDX:EAX
if (v2.isLocal()) {
os.writeIDIV_EAX(BITS32, helper.BP, v2.getOffsetToFP(eContext));
} else {
os.writeIDIV_EAX(v2.getRegister());
}
// Result
vstack.push(result);
// Release
v1.release(eContext);
v2.release(eContext);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ireturn()
*/
public final void visit_ireturn() {
if (countBytecode) {
counters.getCounter("ireturn").inc();
}
wreturn(JvmType.INT, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ishl()
*/
public final void visit_ishl() {
if (countBytecode) {
counters.getCounter("ishl").inc();
}
ishift(X86Operation.SAL);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ishr()
*/
public final void visit_ishr() {
if (countBytecode) {
counters.getCounter("ishr").inc();
}
ishift(X86Operation.SAR);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_istore(int)
*/
public final void visit_istore(int index) {
if (countBytecode) {
counters.getCounter("istore").inc();
}
wstore(JvmType.INT, index);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_isub()
*/
public final void visit_isub() {
if (countBytecode) {
counters.getCounter("isub").inc();
}
ioperation(X86Operation.SUB, false);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_iushr()
*/
public final void visit_iushr() {
if (countBytecode) {
counters.getCounter("iushr").inc();
}
ishift(X86Operation.SHR);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ixor()
*/
public final void visit_ixor() {
if (countBytecode) {
counters.getCounter("ixor").inc();
}
ioperation(X86Operation.XOR, true);
}
/**
* @param address
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_jsr(int)
*/
public final void visit_jsr(int address) {
if (countBytecode) {
counters.getCounter("jsr").inc();
}
os.writeCALL(helper.getInstrLabel(address));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_l2d()
*/
public final void visit_l2d() {
if (countBytecode) {
counters.getCounter("l2d").inc();
}
fpCompiler.convert(JvmType.LONG, JvmType.DOUBLE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_l2f()
*/
public final void visit_l2f() {
if (countBytecode) {
counters.getCounter("l2f").inc();
}
fpCompiler.convert(JvmType.LONG, JvmType.FLOAT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_l2i()
*/
public final void visit_l2i() {
if (countBytecode) {
counters.getCounter("l2i").inc();
}
final LongItem v = vstack.popLong();
if (v.isConstant()) {
if (countConstOps) {
counters.getCounter("l2i-const").inc();
}
vstack.push(ifac.createIConst(eContext, (int) v.getValue()));
} else {
if (countConstOps) {
counters.getCounter("l2i-nonconst").inc();
}
final X86RegisterPool pool = eContext.getGPRPool();
final IntItem result;
v.load(eContext);
if (os.isCode32()) {
final X86Register lsb = v.getLsbRegister(eContext);
v.release(eContext);
pool.request(lsb);
result = (IntItem) ifac.createReg(eContext, JvmType.INT, lsb);
pool.transferOwnerTo(lsb, result);
} else {
final X86Register reg = v.getRegister(eContext);
final X86Register intReg = pool.getRegisterInSameGroup(reg,
JvmType.INT);
v.release(eContext);
pool.request(intReg);
result = (IntItem) ifac.createReg(eContext, JvmType.INT, intReg);
pool.transferOwnerTo(intReg, result);
}
vstack.push(result);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ladd()
*/
public final void visit_ladd() {
if (countBytecode) {
counters.getCounter("ladd").inc();
}
loperation(X86Operation.ADD, X86Operation.ADC, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_laload()
*/
public final void visit_laload() {
if (countBytecode) {
counters.getCounter("laload").inc();
}
final IntItem idx = vstack.popInt();
final RefItem ref = vstack.popRef();
// Load
idx.load(eContext);
ref.load(eContext);
GPR idxr = idx.getRegister();
final GPR refr = ref.getRegister();
// Verify
checkBounds(ref, idx);
// Get data
final DoubleWordItem result = L1AHelper.requestDoubleWordRegisters(
eContext, JvmType.LONG);
if (os.isCode64()) {
final GPR64 idxr64 = (GPR64) eContext.getGPRPool().getRegisterInSameGroup(idxr, JvmType.LONG);
os.writeMOVSXD(idxr64, (GPR32) idxr);
idxr = idxr64;
}
os.writeLEA(refr, refr, idxr, 8, arrayDataOffset);
if (os.isCode32()) {
os.writeMOV(INTSIZE, result.getLsbRegister(eContext), refr, LSB);
os.writeMOV(INTSIZE, result.getMsbRegister(eContext), refr, MSB);
} else {
os.writeMOV(BITS64, result.getRegister(eContext), refr, 0);
}
// Result
vstack.push(result);
// Release
idx.release(eContext);
ref.release(eContext);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_land()
*/
public final void visit_land() {
if (countBytecode) {
counters.getCounter("land").inc();
}
loperation(X86Operation.AND, X86Operation.AND, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lastore()
*/
public final void visit_lastore() {
if (countBytecode) {
counters.getCounter("lastore").inc();
}
dwastore(JvmType.LONG);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lcmp()
*/
public final void visit_lcmp() {
if (countBytecode) {
counters.getCounter("lcmp").inc();
}
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
if (v1.isConstant() && v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOlcmp-const").inc();
}
} else if (v1.isConstant() || v2.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOlcmp-ONEconst-checkSpecialCases").inc();
}
}
// Load
v2.load(eContext);
v1.load(eContext);
// Claim result reg
final IntItem result = (IntItem) L1AHelper.requestWordRegister(
eContext, JvmType.INT, false);
final GPR resr = result.getRegister();
final Label curInstrLabel = getCurInstrLabel();
final Label ltLabel = new Label(curInstrLabel + "lt");
final Label endLabel = new Label(curInstrLabel + "end");
// Calculate
os.writeXOR(resr, resr);
if (os.isCode32()) {
final GPR v2_lsb = v2.getLsbRegister(eContext);
final GPR v2_msb = v2.getMsbRegister(eContext);
final GPR v1_lsb = v1.getLsbRegister(eContext);
final GPR v1_msb = v1.getMsbRegister(eContext);
os.writeSUB(v1_lsb, v2_lsb);
os.writeSBB(v1_msb, v2_msb);
os.writeJCC(ltLabel, X86Constants.JL); // JL
os.writeOR(v1_lsb, v1_msb);
} else {
final GPR64 v2r = v2.getRegister(eContext);
final GPR64 v1r = v1.getRegister(eContext);
os.writeCMP(v1r, v2r);
os.writeJCC(ltLabel, X86Constants.JL); // JL
}
os.writeJCC(endLabel, X86Constants.JZ); // value1 == value2
/** GT */
os.writeINC(resr);
os.writeJMP(endLabel);
/** LT */
os.setObjectRef(ltLabel);
os.writeDEC(resr);
os.setObjectRef(endLabel);
// Push
vstack.push(result);
// Release
v1.release(eContext);
v2.release(eContext);
}
/**
* @param v
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lconst(long)
*/
public final void visit_lconst(long v) {
if (countBytecode) {
counters.getCounter("lconst").inc();
}
vstack.push(ifac.createLConst(eContext, v));
}
/**
* @param value
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ldc(VmConstClass)
*/
public final void visit_ldc(VmConstClass classRef) {
if (countBytecode) {
counters.getCounter("ldc-class").inc();
}
// Push all, since we're going to call other methods
vstack.push(eContext);
L1AHelper.requestRegister(eContext, helper.AAX);
final GPR classr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
// Resolve the class
writeResolveAndLoadClassToReg(classRef, classr);
// Call SoftByteCodes#getClassForVmType
os.writePUSH(classr);
L1AHelper.releaseRegister(eContext, helper.AAX); // So it can be used by invoke
invokeJavaMethod(context.getGetClassForVmTypeMethod());
// The resulting class is now on the stack
// Release
L1AHelper.releaseRegister(eContext, classr);
}
/**
* Push the given VmType on the stack.
*/
public final void visit_ldc(VmType<?> value) {
if (countBytecode) {
counters.getCounter("ldc-vmtype").inc();
}
final WordItem item = L1AHelper.requestWordRegister(eContext, JvmType.REFERENCE, false);
final Label curInstrLabel = getCurInstrLabel();
final GPR reg = item.getRegister();
// Load the class from the statics table
if (os.isCode32()) {
helper.writeGetStaticsEntry(curInstrLabel, reg, value);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) reg, (VmSharedStaticsEntry) value);
}
vstack.push(item);
}
/**
* @param value
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ldc(VmConstString)
*/
public final void visit_ldc(VmConstString value) {
if (countBytecode) {
counters.getCounter("ldc-conststring").inc();
}
vstack.push(ifac.createAConst(eContext, value));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ldiv()
*/
public final void visit_ldiv() {
if (countBytecode) {
counters.getCounter("ldiv").inc();
}
if (os.isCode32()) {
// TODO: port to ORP style (http://orp.sourceforge.net/)
vstack.push(eContext);
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
if (countConstOps) {
if (v1.isConstant() && v2.isConstant()) {
counters.getCounter("TODOldiv-const").inc();
} else if (v2.isConstant() && (getShiftForMultiplier(Math.abs(v2.getValue())) > 0)) {
counters.getCounter("TODOldiv-const-shift").inc();
}
}
v2.release1(eContext);
v1.release1(eContext);
invokeJavaMethod(context.getLdivMethod());
} else {
final X86RegisterPool pool = eContext.getGPRPool();
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
// We need v1 in RAX, so if that is not the case,
// spill those item using RAX
L1AHelper.requestRegister(eContext, X86Register.RAX, v1);
// We need to use RDX, so spill those items using it.
v1.spillIfUsing(eContext, X86Register.RDX);
v2.spillIfUsing(eContext, X86Register.RDX);
L1AHelper.requestRegister(eContext, X86Register.RDX);
// Load v2, v1 into a register
v2.load(eContext);
v1.loadTo64(eContext, X86Register.RAX);
// RAX -> sign extend RDX:RAX
os.writeCDQ(BITS64);
// RAX = RDX:RAX / v2.reg
os.writeIDIV_EAX(v2.getRegister(eContext));
// Free unused registers
pool.release(X86Register.RDX);
v2.release(eContext);
// And push the result on the vstack.
vstack.push(v1);
}
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lload(int)
*/
public final void visit_lload(int index) {
if (countBytecode) {
counters.getCounter("lload").inc();
}
vstack.push(ifac.createLocal(JvmType.LONG, stackFrame
.getWideEbpOffset(typeSizeInfo, index)));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lmul()
*/
public final void visit_lmul() {
if (countBytecode) {
counters.getCounter("lmul").inc();
}
if (os.isCode32()) {
final Label curInstrLabel = getCurInstrLabel();
// TODO: port to ORP style (http://orp.sourceforge.net/)
vstack.push(eContext);
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
if (countConstOps) {
if (v1.isConstant() && v2.isConstant()) {
counters.getCounter("TODOlmul-const").inc();
} else if (v1.isConstant() && (getShiftForMultiplier(Math.abs(v1.getValue())) > 0)) {
counters.getCounter("TODOlmul-const1-shift").inc();
} else if (v2.isConstant() && (getShiftForMultiplier(Math.abs(v2.getValue())) > 0)) {
counters.getCounter("TODOlmul-const2-shift").inc();
}
}
v2.release1(eContext);
v1.release1(eContext);
assertCondition(eContext.getGPRPool().isFree(X86Register.EAX), "EAX not free");
assertCondition(eContext.getGPRPool().isFree(X86Register.EBX), "EBX not free");
assertCondition(eContext.getGPRPool().isFree(X86Register.ECX), "ECX not free");
assertCondition(eContext.getGPRPool().isFree(X86Register.EDX), "EDX not free");
assertCondition(eContext.getGPRPool().isFree(X86Register.ESI), "ESI not free");
writePOP64(X86Register.EBX, X86Register.ECX); // Value 2
final GPR v2_lsb = X86Register.EBX;
final GPR v2_msb = X86Register.ECX;
writePOP64(X86Register.ESI, X86Register.EDI); // Value 1
final GPR v1_lsb = X86Register.ESI;
final GPR v1_msb = X86Register.EDI;
final Label tmp1 = new Label(curInstrLabel + "$tmp1");
final Label tmp2 = new Label(curInstrLabel + "$tmp2");
final GPR EAX = X86Register.EAX;
final GPR EDX = X86Register.EDX;
os.writeMOV(INTSIZE, EAX, v1_msb); // hi2
os.writeOR(EAX, v2_msb); // hi1 | hi2
os.writeJCC(tmp1, X86Constants.JNZ);
os.writeMOV(INTSIZE, EAX, v1_lsb); // lo2
os.writeMUL_EAX(v2_lsb); // lo1*lo2
os.writeJMP(tmp2);
os.setObjectRef(tmp1);
os.writeMOV(INTSIZE, EAX, v1_lsb); // lo2
os.writeMUL_EAX(v2_msb); // hi1*lo2
os.writeMOV(INTSIZE, v2_msb, EAX);
os.writeMOV(INTSIZE, EAX, v1_msb); // hi2
os.writeMUL_EAX(v2_lsb); // hi2*lo1
os.writeADD(v2_msb, EAX); // hi2*lo1 + hi1*lo2
os.writeMOV(INTSIZE, EAX, v1_lsb); // lo2
os.writeMUL_EAX(v2_lsb); // lo1*lo2
os.writeADD(EDX, v2_msb); // hi2*lo1 + hi1*lo2 +
// hi(lo1*lo2)
os.setObjectRef(tmp2);
// Reload the statics table, since it was destroyed here
helper.writeLoadSTATICS(curInstrLabel, "lmul", false);
// Push
final LongItem result = (LongItem) L1AHelper
.requestDoubleWordRegisters(eContext, JvmType.LONG, EAX,
EDX);
vstack.push(result);
} else {
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
v2.load(eContext);
v1.load(eContext);
os.writeIMUL(v1.getRegister(eContext), v2.getRegister(eContext));
v2.release(eContext);
vstack.push(v1);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lneg()
*/
public final void visit_lneg() {
if (countBytecode) {
counters.getCounter("lneg").inc();
}
final LongItem v = vstack.popLong();
if (v.isConstant()) {
if (countConstOps) {
counters.getCounter("lneg-const").inc();
}
vstack.push(ifac.createLConst(eContext, -v.getValue()));
} else {
if (countConstOps) {
counters.getCounter("lneg-nonconst").inc();
}
// Load val
v.load(eContext);
if (os.isCode32()) {
final GPR lsb = v.getLsbRegister(eContext);
final GPR msb = v.getMsbRegister(eContext);
// Calculate
os.writeNEG(msb); // msb := -msb
os.writeNEG(lsb); // lsb := -lsb
os.writeSBB(msb, 0); // high += borrow
} else {
final GPR64 reg = v.getRegister(eContext);
// Calculate
os.writeNEG(reg); // reg := -reg
}
// Push
vstack.push(v);
}
}
/**
* @param defAddress
* @param matchValues
* @param addresses
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lookupswitch(int, int[],
* int[])
*/
public final void visit_lookupswitch(int defAddress, int[] matchValues,
int[] addresses) {
if (countBytecode) {
counters.getCounter("lookupswitch").inc();
}
final int n = matchValues.length;
// BootLogInstance.get().debug("lookupswitch length=" + n);
final IntItem key = vstack.popInt();
key.load(eContext);
final GPR keyr = key.getRegister();
// Conservative assumption, flush stack
vstack.push(eContext);
key.release(eContext);
for (int i = 0; i < n; i++) {
os.writeCMP_Const(keyr, matchValues[i]);
os.writeJCC(helper.getInstrLabel(addresses[i]), X86Constants.JE); // JE
}
os.writeJMP(helper.getInstrLabel(defAddress));
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lor()
*/
public final void visit_lor() {
if (countBytecode) {
counters.getCounter("lor").inc();
}
loperation(X86Operation.OR, X86Operation.OR, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lrem()
*/
public final void visit_lrem() {
if (countBytecode) {
counters.getCounter("lrem").inc();
}
if (os.isCode32()) {
// TODO: port to ORP style (http://orp.sourceforge.net/)
vstack.push(eContext);
Item v2 = vstack.pop(JvmType.LONG);
Item v1 = vstack.pop(JvmType.LONG);
v2.release1(eContext);
v1.release1(eContext);
invokeJavaMethod(context.getLremMethod());
} else {
// Pre-claim result in RDX
L1AHelper.requestRegister(eContext, X86Register.RDX);
final LongItem result = (LongItem) ifac.createReg(eContext, JvmType.LONG,
X86Register.RDX);
eContext.getGPRPool().transferOwnerTo(X86Register.RDX, result);
final LongItem v2 = vstack.popLong();
final LongItem v1 = vstack.popLong();
// v1 must be in RAX
L1AHelper.requestRegister(eContext, X86Register.RAX, v1);
// Load
v2.loadIf(eContext, ~Item.Kind.LOCAL);
v1.loadTo64(eContext, X86Register.RAX);
// Calculate
os.writeCDQ(BITS64); // RAX -> RDX:RAX
if (v2.isLocal()) {
os.writeIDIV_EAX(BITS64, helper.BP, v2.getOffsetToFP(eContext));
} else {
os.writeIDIV_EAX(v2.getRegister(eContext));
}
// Result
vstack.push(result);
// Release
v1.release(eContext);
v2.release(eContext);
}
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lreturn()
*/
public final void visit_lreturn() {
if (countBytecode) {
counters.getCounter("lreturn").inc();
}
dwreturn(JvmType.LONG, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lshl()
*/
public final void visit_lshl() {
if (countBytecode) {
counters.getCounter("lshl").inc();
}
final GPR ECX = X86Register.ECX;
final IntItem cnt = vstack.popInt();
final LongItem val = vstack.popLong();
if (cnt.isConstant() && val.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOlshl-const").inc();
}
}
if (!cnt.uses(ECX)) {
val.spillIfUsing(eContext, ECX);
L1AHelper.requestRegister(eContext, ECX, cnt);
cnt.loadTo(eContext, ECX);
}
val.load(eContext);
if (os.isCode32()) {
final GPR v1_lsb = val.getLsbRegister(eContext);
final GPR v1_msb = val.getMsbRegister(eContext);
final Label curInstrLabel = getCurInstrLabel();
os.writeAND(ECX, 63);
os.writeCMP_Const(ECX, 32);
final Label gt32Label = new Label(curInstrLabel + "gt32");
final Label endLabel = new Label(curInstrLabel + "end");
os.writeJCC(gt32Label, X86Constants.JAE); // JAE
/** ECX < 32 */
os.writeSHLD_CL(v1_msb, v1_lsb);
os.writeSHL_CL(v1_lsb);
os.writeJMP(endLabel);
/** ECX >= 32 */
os.setObjectRef(gt32Label);
os.writeMOV(INTSIZE, v1_msb, v1_lsb);
os.writeXOR(v1_lsb, v1_lsb);
os.writeSHL_CL(v1_msb);
os.setObjectRef(endLabel);
} else {
final GPR64 v1r = val.getRegister(eContext);
os.writeSHL_CL(v1r);
}
// Release
cnt.release(eContext);
vstack.push(val);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lshr()
*/
public final void visit_lshr() {
if (countBytecode) {
counters.getCounter("lshr").inc();
}
final GPR ECX = X86Register.ECX;
final IntItem cnt = vstack.popInt();
final LongItem val = vstack.popLong();
if (cnt.isConstant() && val.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOlshr-const").inc();
}
}
// Get cnt into ECX
if (!cnt.uses(ECX)) {
val.spillIfUsing(eContext, ECX);
L1AHelper.requestRegister(eContext, ECX, cnt);
cnt.loadTo(eContext, ECX);
}
// Load val
val.load(eContext);
if (os.isCode32()) {
final X86Register.GPR lsb = val.getLsbRegister(eContext);
final X86Register.GPR msb = val.getMsbRegister(eContext);
final Label curInstrLabel = getCurInstrLabel();
// Calculate
os.writeAND(ECX, 63);
os.writeCMP_Const(ECX, 32);
final Label gt32Label = new Label(curInstrLabel + "gt32");
final Label endLabel = new Label(curInstrLabel + "end");
os.writeJCC(gt32Label, X86Constants.JAE); // JAE
/** ECX < 32 */
os.writeSHRD_CL(lsb, msb);
os.writeSAR_CL(msb);
os.writeJMP(endLabel);
/** ECX >= 32 */
os.setObjectRef(gt32Label);
os.writeMOV(INTSIZE, lsb, msb);
os.writeSAR(msb, 31);
os.writeSAR_CL(lsb);
os.setObjectRef(endLabel);
} else {
final GPR64 valr = val.getRegister(eContext);
os.writeSAR_CL(valr);
}
vstack.push(val);
// Release
cnt.release(eContext);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lstore(int)
*/
public final void visit_lstore(int index) {
if (countBytecode) {
counters.getCounter("lstore").inc();
}
dwstore(JvmType.LONG, index);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lsub()
*/
public final void visit_lsub() {
if (countBytecode) {
counters.getCounter("lsub").inc();
}
loperation(X86Operation.SUB, X86Operation.SBB, false);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lushr()
*/
public final void visit_lushr() {
if (countBytecode) {
counters.getCounter("lushr").inc();
}
final GPR ECX = X86Register.ECX;
final IntItem cnt = vstack.popInt();
final LongItem val = vstack.popLong();
if (cnt.isConstant() && val.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOlushr-const").inc();
}
}
// Get cnt into ECX
if (!cnt.uses(ECX)) {
val.spillIfUsing(eContext, ECX);
L1AHelper.requestRegister(eContext, ECX, cnt);
cnt.loadTo(eContext, ECX);
}
// Load val
val.load(eContext);
if (os.isCode32()) {
final X86Register.GPR lsb = val.getLsbRegister(eContext);
final X86Register.GPR msb = val.getMsbRegister(eContext);
final Label curInstrLabel = getCurInstrLabel();
// Calculate
os.writeAND(ECX, 63);
os.writeCMP_Const(ECX, 32);
final Label gt32Label = new Label(curInstrLabel + "gt32");
final Label endLabel = new Label(curInstrLabel + "end");
os.writeJCC(gt32Label, X86Constants.JAE); // JAE
/** ECX < 32 */
os.writeSHRD_CL(lsb, msb);
os.writeSHR_CL(msb);
os.writeJMP(endLabel);
/** ECX >= 32 */
os.setObjectRef(gt32Label);
os.writeMOV(INTSIZE, lsb, msb);
os.writeXOR(msb, msb);
os.writeSHR_CL(lsb);
os.setObjectRef(endLabel);
} else {
final GPR64 valr = val.getRegister(eContext);
os.writeSHR_CL(valr);
}
// Push
vstack.push(val);
// Release
cnt.release(eContext);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_lxor()
*/
public final void visit_lxor() {
if (countBytecode) {
counters.getCounter("lxor").inc();
}
loperation(X86Operation.XOR, X86Operation.XOR, true);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_monitorenter()
*/
public final void visit_monitorenter() {
if (countBytecode) {
counters.getCounter("monitorenter").inc();
}
vstack.push(eContext);
final RefItem v = vstack.popRef();
v.release1(eContext);
// Objectref is already on the stack
invokeJavaMethod(context.getMonitorEnterMethod());
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_monitorexit()
*/
public final void visit_monitorexit() {
if (countBytecode) {
counters.getCounter("monitorexit").inc();
}
vstack.push(eContext);
final RefItem v = vstack.popRef();
v.release1(eContext);
// Objectref is already on the stack
invokeJavaMethod(context.getMonitorExitMethod());
}
/**
* @param clazz
* @param dimensions
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_multianewarray(VmConstClass,
* int)
*/
public final void visit_multianewarray(VmConstClass clazz, int dimensions) {
if (countBytecode) {
counters.getCounter("multianewarray").inc();
}
// flush all vstack items to the stack
// all registers are freed
vstack.push(eContext);
// Create the dimensions array
helper.writePushStaticsEntry(getCurInstrLabel(), helper.getMethod().getDeclaringClass()); /* currentClass */
os.writePUSH(10); /* type=int */
os.writePUSH(dimensions); /* elements */
invokeJavaMethod(context.getAllocPrimitiveArrayMethod());
final RefItem dims = vstack.popRef();
final GPR dimsr = dims.getRegister();
// Dimension array is now in dimsr
// Pop all dimensions (note the reverse order that allocMultiArray
// expects)
for (int i = 0; i < dimensions; i++) {
final int ofs = arrayDataOffset + (i * 4);
final IntItem v = vstack.popInt();
v.release1(eContext);
os.writePOP(dimsr, ofs);
}
// Allocate tmp register
final GPR classr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
// Resolve the array class
writeResolveAndLoadClassToReg(clazz, classr);
// Release dims, because invokeJavaMethod needs EAX
dims.release(eContext);
// Now call the multianewarrayhelper
os.writePUSH(classr); // array-class
os.writePUSH(dimsr); // dimensions[]
invokeJavaMethod(context.getAllocMultiArrayMethod());
// Result is now on the vstack
// Release
L1AHelper.releaseRegister(eContext, classr);
}
/**
* @param classRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_new(org.jnode.vm.classmgr.VmConstClass)
*/
public final void visit_new(VmConstClass classRef) {
if (countBytecode) {
counters.getCounter("new").inc();
}
// Push all
vstack.push(eContext);
// Allocate tmp register
final GPR classr = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
writeResolveAndLoadClassToReg(classRef, classr);
/* Setup a call to SoftByteCodes.allocObject */
os.writePUSH(classr); /* vmClass */
os.writePUSH(-1); /* Size */
invokeJavaMethod(context.getAllocObjectMethod());
// Result is already on the vstack
// Release
L1AHelper.releaseRegister(eContext, classr);
}
/**
* @param type
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_newarray(int)
*/
public final void visit_newarray(int type) {
if (countBytecode) {
counters.getCounter("newarray").inc();
}
// Load count
final IntItem count = vstack.popInt();
count.loadIf(eContext, Item.Kind.STACK);
// flush stack, result also on stack
vstack.push(eContext);
// Setup a call to SoftByteCodes.allocArray
helper.writePushStaticsEntry(getCurInstrLabel(), helper.getMethod().getDeclaringClass()); /* currentClass */
os.writePUSH(type); /* type */
count.push(eContext); /* count */
count.release1(eContext); // release and remove parameter from stack
invokeJavaMethod(context.getAllocPrimitiveArrayMethod());
// Result is already on the vstack
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_nop()
*/
public final void visit_nop() {
if (countBytecode) {
counters.getCounter("nop").inc();
}
// Nothing
os.writeNOP();
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_pop()
*/
public final void visit_pop() {
if (countBytecode) {
counters.getCounter("pop").inc();
}
generic_pop(helper.SLOTSIZE);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_pop2()
*/
public final void visit_pop2() {
if (countBytecode) {
counters.getCounter("pop2").inc();
}
generic_pop(helper.SLOTSIZE * 2);
}
/**
* @param fieldRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_putfield(org.jnode.vm.classmgr.VmConstFieldRef)
*/
public final void visit_putfield(VmConstFieldRef fieldRef) {
if (countBytecode) {
counters.getCounter("putfield").inc();
}
fieldRef.resolve(loader);
final VmField field = fieldRef.getResolvedVmField();
if (field.isStatic()) {
throw new IncompatibleClassChangeError(
"getfield called on static field " + fieldRef.getName());
}
final VmInstanceField inf = (VmInstanceField) field;
final int offset = inf.getOffset();
final boolean wide = fieldRef.isWide();
// Get operands
final Item val = vstack.pop();
assertCondition(val.getCategory() == ((wide) ? 2 : 1),
"category mismatch");
final RefItem ref = vstack.popRef();
// Load value & ref
val.load(eContext);
ref.load(eContext);
final GPR refr = ref.getRegister();
if (!wide) {
final WordItem wval = (WordItem) val;
final GPR valr = wval.getRegister();
final char fieldType = field.getSignature().charAt(0);
// Store field
switch (fieldType) {
case 'Z': // boolean
case 'B': // byte
wval.loadToBITS8GPR(eContext);
os.writeMOV(BITS8, refr, offset, wval.getRegister());
break;
case 'C': // char
case 'S': // short
os.writeMOV(BITS16, refr, offset, valr);
break;
case 'F': // float
case 'I': // int
case 'L': // Object
case '[': // array
os.writeMOV(valr.getSize(), refr, offset, valr);
break;
default:
throw new IllegalArgumentException("Unknown fieldType: " + fieldType);
}
// Writebarrier
if (!inf.isPrimitive() && helper.needsWriteBarrier()) {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writePutfieldWriteBarrier(inf, refr, valr, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
} else {
final DoubleWordItem dval = (DoubleWordItem) val;
if (os.isCode32()) {
os.writeMOV(BITS32, refr, offset + MSB, dval
.getMsbRegister(eContext));
os.writeMOV(BITS32, refr, offset + LSB, dval
.getLsbRegister(eContext));
} else {
os.writeMOV(BITS64, refr, offset, dval.getRegister(eContext));
}
}
// Release
val.release(eContext);
ref.release(eContext);
}
/**
* @param fieldRef
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_putstatic(org.jnode.vm.classmgr.VmConstFieldRef)
*/
public final void visit_putstatic(VmConstFieldRef fieldRef) {
if (countBytecode) {
counters.getCounter("putstatic").inc();
}
final Label curInstrLabel = getCurInstrLabel();
fieldRef.resolve(loader);
final VmStaticField sf = (VmStaticField) fieldRef.getResolvedVmField();
// Initialize class if needed
if (!sf.getDeclaringClass().isAlwaysInitialized()) {
writeInitializeClass(fieldRef);
}
// Get value
final Item val = vstack.pop();
val.load(eContext);
// Put static field
if (!fieldRef.isWide()) {
final WordItem wval = (WordItem) val;
final GPR valr = wval.getRegister();
if (os.isCode32() || (wval.getType() != JvmType.REFERENCE)) {
if (sf.isShared()) {
helper.writePutStaticsEntry(curInstrLabel, valr,
(VmSharedStaticsEntry) sf);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writePutStaticsEntry(curInstrLabel, valr,
(VmIsolatedStaticsEntry) sf, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
} else {
if (sf.isShared()) {
helper.writePutStaticsEntry64(curInstrLabel, (GPR64) valr,
(VmSharedStaticsEntry) sf);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writePutStaticsEntry64(curInstrLabel, (GPR64) valr,
(VmIsolatedStaticsEntry) sf, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
}
if (!sf.isPrimitive() && helper.needsWriteBarrier()) {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.INT, false);
helper.writePutstaticWriteBarrier(sf, valr, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
} else {
final DoubleWordItem dval = (DoubleWordItem) val;
if (os.isCode32()) {
if (sf.isShared()) {
helper.writePutStaticsEntry64(curInstrLabel, dval
.getLsbRegister(eContext), dval
.getMsbRegister(eContext),
(VmSharedStaticsEntry) sf);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writePutStaticsEntry64(curInstrLabel, dval
.getLsbRegister(eContext), dval
.getMsbRegister(eContext),
(VmIsolatedStaticsEntry) sf, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
} else {
if (sf.isShared()) {
helper.writePutStaticsEntry64(curInstrLabel, dval
.getRegister(eContext), (VmSharedStaticsEntry) sf);
} else {
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext,
JvmType.REFERENCE, false);
helper.writePutStaticsEntry64(curInstrLabel, dval
.getRegister(eContext),
(VmIsolatedStaticsEntry) sf, tmp);
L1AHelper.releaseRegister(eContext, tmp);
}
}
}
// Release
val.release(eContext);
}
/**
* @param index
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_ret(int)
*/
public final void visit_ret(int index) {
if (countBytecode) {
counters.getCounter("ret").inc();
}
// Calc EBP offset
final int ebpOfs = stackFrame.getEbpOffset(typeSizeInfo, index);
// Load ret & jmp
os.writeJMP(helper.BP, ebpOfs);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_return()
*/
public final void visit_return() {
if (countBytecode) {
counters.getCounter("return").inc();
}
// Discard vstack first
while (!vstack.isEmpty()) {
Item v = vstack.pop();
if (v.isStack()) {
// sanity check
if (VirtualStack.checkOperandStack) {
vstack.operandStack.pop(v);
}
}
v.release(eContext);
}
stackFrame.emitReturn();
assertCondition(vstack.isEmpty(), "vstack should be empty; it is ",
vstack);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_saload()
*/
public final void visit_saload() {
if (countBytecode) {
counters.getCounter("saload").inc();
}
waload(JvmType.SHORT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_sastore()
*/
public final void visit_sastore() {
if (countBytecode) {
counters.getCounter("sastore").inc();
}
wastore(JvmType.SHORT);
}
/**
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_swap()
*/
public final void visit_swap() {
if (countBytecode) {
counters.getCounter("swap").inc();
}
final Item v1 = vstack.pop();
final Item v2 = vstack.pop();
assertCondition((v1.getCategory() == 1) && (v2.getCategory() == 1),
"category mismatch");
final boolean v1_load = (v1.isStack() || v1.isFPUStack());
final boolean v2_load = (v2.isStack() || v2.isFPUStack());
if (v1_load || v2_load) {
// at least one element the stack: must be popped to be inverted
// (inverting only on vstack not enough)
v1.load(eContext);
v2.load(eContext);
}
vstack.push(v1);
vstack.push(v2);
}
/**
* @param defAddress
* @param lowValue
* @param highValue
* @param addresses
* @see org.jnode.vm.bytecode.BytecodeVisitor#visit_tableswitch(int, int,
* int, int[])
*/
public final void visit_tableswitch(int defAddress, int lowValue,
int highValue, int[] addresses) {
if (countBytecode) {
counters.getCounter("tableswitch").inc();
}
// IMPROVE: check Jaos implementation
final IntItem val = vstack.popInt();
if (val.isConstant()) {
if (countConstOps) {
counters.getCounter("TODOtableswitch-constVal!!").inc();
}
}
val.load(eContext);
GPR valr = val.getRegister();
vstack.push(eContext);
final int n = addresses.length;
if ((n > 4) && os.isCode32()) {
// Optimized version. Needs some overhead, so only useful for
// larger tables.
if (countBytecode) {
counters.getCounter("tableswitch-opt").inc();
}
final GPR tmp = (GPR) L1AHelper.requestRegister(eContext, JvmType.REFERENCE, false);
if (os.isCode64()) {
GPR64 valr64 = L1AHelper.get64BitReg(eContext, valr);
os.writeMOVSXD(valr64, (GPR32) valr);
valr = valr64;
}
if (lowValue != 0) {
os.writeSUB(valr, lowValue);
}
// If outsite low-high range, jump to default
os.writeCMP_Const(valr, n);
os.writeJCC(helper.getInstrLabel(defAddress), X86Constants.JAE);
final Label curInstrLabel = getCurInstrLabel();
final Label l1 = new Label(curInstrLabel + "$$l1");
final Label l2 = new Label(curInstrLabel + "$$l2");
final int l12distance = os.isCode32() ? 12 : 23;
// Get absolute address of l1 into S0. (do not use
// stackMgr.writePOP!)
os.writeCALL(l1);
os.setObjectRef(l1);
final int l1Ofs = os.getLength();
os.writePOP(tmp);
// Calculate absolute address of jumptable entry into S1
os.writeLEA(tmp, tmp, valr, helper.ADDRSIZE, l12distance);
// Calculate absolute address of jump target
if (os.isCode32()) {
os.writeADD(tmp, tmp, 0);
} else {
final GPR32 tmp2 = (GPR32) L1AHelper.requestRegister(eContext, JvmType.INT, false);
os.writeMOV(BITS32, tmp2, tmp, 0);
final GPR64 tmp2_64 = L1AHelper.get64BitReg(eContext, tmp2);
os.writeMOVSXD(tmp2_64, tmp2);
os.writeADD(tmp, tmp2_64);
L1AHelper.releaseRegister(eContext, tmp2);
}
os.writeLEA(tmp, tmp, 4); // Compensate for writeRelativeObject
// difference
// Jump to the calculated address
os.writeJMP(tmp);
// Emit offsets relative to where they are emitted
os.setObjectRef(l2);
final int l2Ofs = os.getLength();
for (int i = 0; i < n; i++) {
os.writeRelativeObjectRef(helper.getInstrLabel(addresses[i]));
}
if ((l2Ofs - l1Ofs) != l12distance) {
if (!os.isTextStream()) {
throw new CompileError("l12distance must be "
+ (l2Ofs - l1Ofs));
}
}
L1AHelper.releaseRegister(eContext, tmp);
} else {
// Space wasting, but simple implementation
if (countBytecode) {
counters.getCounter("tableswitch-nonopt").inc();
}
for (int i = 0; i < n; i++) {
os.writeCMP_Const(valr, lowValue + i);
os.writeJCC(helper.getInstrLabel(addresses[i]), X86Constants.JE); // JE
}
os.writeJMP(helper.getInstrLabel(defAddress));
}
val.release(eContext);
}
/**
* Load a WordItem out of an array.
*
* @param jvmType Type of the array elements
*/
final void waload(int jvmType) {
final IntItem idx = vstack.popInt();
final RefItem ref = vstack.popRef();
final int valSize;
final int scale;
final int resultType;
switch (jvmType) {
case JvmType.BYTE:
valSize = BITS8;
scale = 1;
resultType = JvmType.INT;
break;
case JvmType.CHAR:
case JvmType.SHORT:
valSize = BITS16;
scale = 2;
resultType = JvmType.INT;
break;
case JvmType.INT:
valSize = BITS32;
scale = 4;
resultType = JvmType.INT;
break;
case JvmType.FLOAT:
valSize = BITS32;
scale = 4;
resultType = JvmType.FLOAT;
break;
case JvmType.REFERENCE:
valSize = helper.ADDRSIZE;
scale = helper.SLOTSIZE;
resultType = JvmType.REFERENCE;
break;
default:
throw new IllegalArgumentException("Invalid type " + jvmType);
}
// Create result
final WordItem result;
final GPR resultr;
result = L1AHelper.requestWordRegister(eContext, resultType,
(valSize == BYTESIZE));
resultr = result.getRegister();
// Load
idx.loadIf(eContext, ~Item.Kind.CONSTANT);
ref.load(eContext);
final GPR refr = ref.getRegister();
// Verify
checkBounds(ref, idx);
if (jvmType == JvmType.CHAR) {
// Clear resultr, so we avoid a MOVZX afterwards.
os.writeXOR(resultr, resultr);
}
// Load data
if (idx.isConstant()) {
final int offset = idx.getValue() * scale;
os.writeMOV(valSize, resultr, refr, offset + arrayDataOffset);
} else {
GPR idxr = idx.getRegister();
if (os.isCode64()) {
final GPR64 idxr64 = (GPR64) eContext.getGPRPool().getRegisterInSameGroup(idxr, JvmType.LONG);
os.writeMOVSXD(idxr64, (GPR32) idxr);
idxr = idxr64;
}
os.writeMOV(valSize, resultr, refr, idxr, scale, arrayDataOffset);
}
// Post process
switch (jvmType) {
case JvmType.BYTE:
os.writeMOVSX(resultr, resultr, BYTESIZE);
break;
// case JvmType.CHAR:
// os.writeMOVZX(resultr, resultr, WORDSIZE);
// break;
case JvmType.SHORT:
os.writeMOVSX(resultr, resultr, WORDSIZE);
break;
}
// Release
ref.release(eContext);
idx.release(eContext);
// Push result
vstack.push(result);
}
/**
* Store a WordItem into an array.
*
* @param jvmType Type of the array elements
*/
final void wastore(int jvmType) {
final boolean useBarrier = (context.getWriteBarrier() != null);
final int valSize;
final int scale;
final int valType;
int extraLoadIdxMask = 0;
switch (jvmType) {
case JvmType.BYTE:
valSize = BITS8;
scale = 1;
valType = JvmType.INT;
break;
case JvmType.CHAR:
case JvmType.SHORT:
valSize = BITS16;
scale = 2;
valType = JvmType.INT;
break;
case JvmType.INT:
valSize = BITS32;
scale = 4;
valType = JvmType.INT;
break;
case JvmType.FLOAT:
valSize = BITS32;
scale = 4;
valType = JvmType.FLOAT;
break;
case JvmType.REFERENCE:
valSize = helper.ADDRSIZE;
scale = helper.SLOTSIZE;
valType = JvmType.REFERENCE;
extraLoadIdxMask = useBarrier ? ~Item.Kind.GPR : 0;
break;
default:
throw new IllegalArgumentException("Invalid type " + jvmType);
}
final WordItem val = (WordItem) vstack.pop(valType);
final IntItem idx = vstack.popInt();
final RefItem ref = vstack.popRef();
// IMPROVE: optimize case with const value
// Load
if (valSize == BYTESIZE) {
val.loadToBITS8GPR(eContext);
} else {
val.load(eContext);
}
idx.loadIf(eContext, ~Item.Kind.CONSTANT | extraLoadIdxMask);
ref.load(eContext);
final GPR refr = ref.getRegister();
final GPR valr = val.getRegister();
final X86RegisterPool pool = eContext.getGPRPool();
// Verify
checkBounds(ref, idx);
// Store
if (idx.isConstant()) {
final int offset = idx.getValue() * scale;
os.writeMOV(valSize, refr, offset + arrayDataOffset, valr);
} else {
GPR idxr = idx.getRegister();
if (os.isCode64()) {
final GPR64 idxr64 = (GPR64) pool.getRegisterInSameGroup(idxr, JvmType.LONG);
os.writeMOVSXD(idxr64, (GPR32) idxr);
idxr = idxr64;
}
os.writeMOV(valSize, refr, idxr, scale, arrayDataOffset, valr);
}
// Call write barrier (reference only)
if ((jvmType == JvmType.REFERENCE) && useBarrier) {
// the write barrier could easily be modified to avoid using a
// scratch register
final GPR idxr;
if (os.isCode32()) {
idxr = idx.getRegister();
} else {
idxr = (GPR) eContext.getGPRPool().getRegisterInSameGroup(idx.getRegister(), JvmType.LONG);
}
final GPR scratch = (GPR) pool.request(JvmType.INT);
helper.writeArrayStoreWriteBarrier(refr, idxr, valr, scratch);
pool.release(scratch);
}
// Release
val.release(eContext);
idx.release(eContext);
ref.release(eContext);
}
/**
* Pop a word item of the stack and return it to the caller
*
* @param JvmType
*/
private final void wreturn(int jvmType, boolean callVisitReturn) {
final WordItem val = (WordItem) vstack.pop(jvmType);
final GPR reg;
if (os.isCode32() || (jvmType != JvmType.REFERENCE)) {
reg = X86Register.EAX;
} else {
reg = X86Register.RAX;
}
// Return value must be in EAX
if (!val.uses(reg)) {
L1AHelper.requestRegister(eContext, reg, val);
val.loadTo(eContext, reg);
}
// Release
val.release(eContext);
// Do actual return
if (callVisitReturn) {
visit_return();
}
}
/**
* Write code to resolve the given constant field referred to by fieldRef
*
* @param fieldRef
* @param scratch
*/
private final void writeInitializeClass(VmConstFieldRef fieldRef) {
// Get fieldRef via constantpool to avoid direct object references in
// the native code
final VmType<?> declClass = fieldRef.getResolvedVmField()
.getDeclaringClass();
if (!declClass.isAlwaysInitialized()) {
final Label curInstrLabel = getCurInstrLabel();
// Allocate a register to hold the class
final GPR classReg = (GPR) L1AHelper.requestRegister(eContext, JvmType.REFERENCE, false);
// Load classRef into the register
// Load the class from the statics table
if (os.isCode32()) {
helper.writeGetStaticsEntry(new Label(curInstrLabel + "$$ic"),
classReg, declClass);
} else {
helper.writeGetStaticsEntry64(new Label(curInstrLabel + "$$ic"),
(GPR64) classReg, (VmSharedStaticsEntry) declClass);
}
// Write class initialization code
helper.writeClassInitialize(curInstrLabel, classReg, classReg, declClass);
// Free class
L1AHelper.releaseRegister(eContext, classReg);
}
}
/**
* Write code to pop a 64-bit word from the stack
*
* @param lsbReg
* @param msbReg
*/
private final void writePOP64(GPR lsbReg, GPR msbReg) {
os.writePOP(lsbReg);
os.writePOP(msbReg);
}
/**
* Write code to resolve the given constant class (if needed) and load the
* resolved class (VmType instance) into the given register.
*
* @param classRef
*/
private final void writeResolveAndLoadClassToReg(VmConstClass classRef,
GPR dst) {
// Resolve the class
classRef.resolve(loader);
final VmType type = classRef.getResolvedVmClass();
final Label curInstrLabel = getCurInstrLabel();
// Load the class from the statics table
if (os.isCode32()) {
helper.writeGetStaticsEntry(curInstrLabel, dst, type);
} else {
helper.writeGetStaticsEntry64(curInstrLabel, (GPR64) dst, (VmSharedStaticsEntry) type);
}
}
/**
* Generate code for a word load local instruction.
*
* @param jvmType
* @param index
*/
private final void wload(int jvmType, int index, boolean useStored) {
Item constValue = constLocals.get(index);
if (constValue != null) {
vstack.push(constValue.clone(eContext));
} else if (false && useStored && (wstoreReg != null)) {
vstack.push(L1AHelper.requestWordRegister(eContext, jvmType, wstoreReg));
} else {
vstack.push(ifac.createLocal(jvmType, stackFrame
.getEbpOffset(typeSizeInfo, index)));
}
}
/**
* Does the local variable with the given index and current
* program counter escape the current basic block?
*
* @param index
* @return
*/
private final boolean localEscapesBasicBlock(int index) {
if (true) {
return true;
}
VmLocalVariable var;
var = currentMethod.getBytecode().getVariable(curAddress, index);
if (var == null) {
var = currentMethod.getBytecode().getVariable(parser.getNextAddress(), index);
}
if (var != null) {
final int varBegin = var.getStartPC();
final int varEnd = var.getEndPC();
return ((varBegin < currentBasicBlock.getStartPC())
|| (varEnd > currentBasicBlock.getEndPC()));
} else {
return true;
}
}
/**
* Store a word item into a local variable
*
* @param jvmType
* @param index
*/
private final void wstore(int jvmType, int index) {
final int disp = stackFrame.getEbpOffset(typeSizeInfo, index);
wstoreReg = null;
// Pin down (load) other references to this local
vstack.loadLocal(eContext, disp);
// Load
final WordItem val = (WordItem) vstack.pop(jvmType);
final boolean vconst = val.isConstant();
if (vconst) {
// Store constant locals
constLocals.put(index, val.clone(eContext));
} else {
// Not constant anymore, remove it
constLocals.remove(index);
}
if (vconst && (jvmType == JvmType.INT)) {
if (localEscapesBasicBlock(index)) {
// Store constant int
final int ival = ((IntItem) val).getValue();
os.writeMOV_Const(BITS32, helper.BP, disp, ival);
}
} else if (vconst && (jvmType == JvmType.FLOAT)) {
if (localEscapesBasicBlock(index)) {
// Store constant float
final int ival = Float.floatToRawIntBits(((FloatItem) val)
.getValue());
os.writeMOV_Const(BITS32, helper.BP, disp, ival);
}
} else if (val.isFPUStack()) {
// Ensure item is on top of fpu stack
FPUHelper.fxch(os, vstack.fpuStack, val);
if (jvmType == JvmType.FLOAT) {
os.writeFSTP32(helper.BP, disp);
} else {
os.writeFISTP32(helper.BP, disp);
}
vstack.fpuStack.pop(val);
} else if (val.isStack()) {
// Must be top of stack
if (VirtualStack.checkOperandStack) {
vstack.operandStack.pop(val);
}
os.writePOP(helper.BP, disp);
} else {
// Load into register
val.load(eContext);
final GPR valr = val.getRegister();
// Store
os.writeMOV(valr.getSize(), helper.BP, disp, valr);
wstoreReg = valr;
}
// Release
val.release(eContext);
}
/**
* Insert a yieldpoint into the code
*/
public final void yieldPoint() {
helper.writeYieldPoint(getCurInstrLabel());
}
/**
* @return Returns the curInstrLabel.
*/
private final Label getCurInstrLabel() {
if (_curInstrLabel == null) {
_curInstrLabel = helper.getInstrLabel(this.curAddress);
}
return _curInstrLabel;
}
}