/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Common Public License (CPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/cpl1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.opt.ir; import org.jikesrvm.VM; import org.jikesrvm.classloader.VM_TypeReference; import org.jikesrvm.compilers.opt.OPT_ClassLoaderProxy; import org.jikesrvm.compilers.opt.OPT_Constants; import org.jikesrvm.compilers.opt.OPT_OptimizingCompilerException; import org.vmmagic.unboxed.Address; /** * An <code>OPT_Operand</code> identifies an operand for an * {@link OPT_Instruction}. A single OPT_Operand object should * not be shared between instructions (or even be used twice in * the same instruction). Operands should not be shared between * instructions because we use the * {@link #instruction reference to the operand's containing instruction} * to construct use/def chains. We also store program-point specific * information about an {@link OPT_Register symbolic register} * in the {@link OPT_RegisterOperand OPT_RegisterOperands} that * {@link OPT_RegisterOperand#register refer} to the * <code>OPT_Register</code>. * <p> * Operands are divided into several primary categories * <ul> * <li> {@link OPT_RegisterOperand} represent symbolic and * and physical registers. * <li> The subclasses of {@link OPT_ConstantOperand} * represent various kinds of constant operands. * <li> {@link OPT_MethodOperand} represents the targets of CALL instructions. * <li> {@link OPT_BranchOperand}, {@link OPT_BasicBlockOperand}, * and {@link OPT_BranchOperand} are used to encode CFG * information in LABEL, BBEND, and branch instructions. * <li> {@link OPT_ConditionOperand} and {@link OPT_TrapCodeOperand} * encode the conditions tested by conditional branches and * trap instructions. * <li> {@link OPT_LocationOperand} represents the memory location * accessed by a load or store operation. * <li> {@link OPT_TypeOperand} encodes a {@link org.jikesrvm.classloader.VM_Type} for use * in instructions such as NEW or INSTANCEOF that operate on the * type hierarchy. * </ul> * * @see OPT_Instruction * @see OPT_BasicBlockOperand * @see OPT_BranchOperand * @see OPT_ConditionOperand * @see OPT_ConstantOperand * @see OPT_DoubleConstantOperand * @see OPT_FloatConstantOperand * @see OPT_IntConstantOperand * @see OPT_LocationOperand * @see OPT_LongConstantOperand * @see OPT_MethodOperand * @see OPT_NullConstantOperand * @see OPT_RegisterOperand * @see OPT_StringConstantOperand * @see OPT_TrapCodeOperand * @see OPT_TrueGuardOperand * @see OPT_TypeOperand */ public abstract class OPT_Operand { /** * Handle back to containing instruction. */ public OPT_Instruction instruction; /** * Is the operand an {@link OPT_RegisterOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_RegisterOperand} * or <code>false</code> if it is not. */ public final boolean isRegister() { return this instanceof OPT_RegisterOperand; } /** * Is the operand an {@link OPT_ConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_ConstantOperand} * or <code>false</code> if it is not. */ public final boolean isConstant() { return this instanceof OPT_ConstantOperand; } /** * Is the operand an {@link OPT_IntConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_IntConstantOperand} * or <code>false</code> if it is not. */ public final boolean isIntConstant() { return this instanceof OPT_IntConstantOperand; } /** * Is the operand an {@link OPT_AddressConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_AddressConstantOperand} * or <code>false</code> if it is not. */ public final boolean isAddressConstant() { return this instanceof OPT_AddressConstantOperand; } /** * Is the operand an {@link OPT_FloatConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_FloatConstantOperand} * or <code>false</code> if it is not. */ public final boolean isFloatConstant() { return this instanceof OPT_FloatConstantOperand; } /** * Is the operand an {@link OPT_LongConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_LongConstantOperand} * or <code>false</code> if it is not. */ public final boolean isLongConstant() { return this instanceof OPT_LongConstantOperand; } /** * Is the operand an {@link OPT_DoubleConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_DoubleConstantOperand} * or <code>false</code> if it is not. */ public final boolean isDoubleConstant() { return this instanceof OPT_DoubleConstantOperand; } /** * Is the operand an {@link OPT_StringConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_StringConstantOperand} * or <code>false</code> if it is not. */ public final boolean isStringConstant() { return this instanceof OPT_StringConstantOperand; } /** * Is the operand an {@link OPT_ClassConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_ClassConstantOperand} * or <code>false</code> if it is not. */ public final boolean isClassConstant() { return this instanceof OPT_ClassConstantOperand; } /** * Is the operand an {@link OPT_ObjectConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_ObjectConstantOperand} * or <code>false</code> if it is not. */ public final boolean isObjectConstant() { return this instanceof OPT_ObjectConstantOperand; } /** * Is the operand a moveable {@link OPT_ObjectConstantOperand}? * * @return false */ public boolean isMoveableObjectConstant() { return false; } /** * Is the operand an {@link OPT_TIBConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_TIBConstantOperand} * or <code>false</code> if it is not. */ public final boolean isTIBConstant() { return this instanceof OPT_TIBConstantOperand; } /** * Is the operand an {@link OPT_NullConstantOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_NullConstantOperand} * or <code>false</code> if it is not. */ public final boolean isNullConstant() { return this instanceof OPT_NullConstantOperand; } /** * Is the operand an {@link OPT_TrueGuardOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_TrueGuardOperand} * or <code>false</code> if it is not. */ public final boolean isTrueGuard() { return this instanceof OPT_TrueGuardOperand; } /** * Is the operand an {@link OPT_BranchOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_BranchOperand} * or <code>false</code> if it is not. */ public final boolean isBranch() { return this instanceof OPT_BranchOperand; } /** * Is the operand an {@link OPT_BasicBlockOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_BasicBlockOperand} * or <code>false</code> if it is not. */ public final boolean isBlock() { return this instanceof OPT_BasicBlockOperand; } /** * Is the operand an {@link OPT_MemoryOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_MemoryOperand} * or <code>false</code> if it is not. */ public final boolean isMemory() { return this instanceof OPT_MemoryOperand; } /** * Is the operand an {@link OPT_StackLocationOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_StackLocationOperand} * or <code>false</code> if it is not. */ public final boolean isStackLocation() { return this instanceof OPT_StackLocationOperand; } /** * Is the operand an {@link OPT_MethodOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_MethodOperand} * or <code>false</code> if it is not. */ public final boolean isMethod() { return this instanceof OPT_MethodOperand; } /** * Is the operand an {@link OPT_LocationOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_LocationOperand} * or <code>false</code> if it is not. */ public final boolean isLocation() { return this instanceof OPT_LocationOperand; } /** * Is the operand an {@link OPT_TypeOperand}? * * @return <code>true</code> if <code>this</code> is an * <code>instanceof</code> an {@link OPT_TypeOperand} * or <code>false</code> if it is not. */ public final boolean isType() { return this instanceof OPT_TypeOperand; } /** * Cast to an {@link OPT_RegisterOperand}. * * @return <code>this</code> cast as an {@link OPT_RegisterOperand} */ public final OPT_RegisterOperand asRegister() { return (OPT_RegisterOperand) this; } /** * Cast to an {@link OPT_IntConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_IntConstantOperand} */ public final OPT_IntConstantOperand asIntConstant() { return (OPT_IntConstantOperand) this; } /** * Cast to an {@link OPT_AddressConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_AddressConstantOperand} */ public final OPT_AddressConstantOperand asAddressConstant() { return (OPT_AddressConstantOperand) this; } /** * Cast to an {@link OPT_FloatConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_FloatConstantOperand} */ public final OPT_FloatConstantOperand asFloatConstant() { return (OPT_FloatConstantOperand) this; } /** * Cast to an {@link OPT_LongConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_LongConstantOperand} */ public final OPT_LongConstantOperand asLongConstant() { return (OPT_LongConstantOperand) this; } /** * Cast to an {@link OPT_DoubleConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_DoubleConstantOperand} */ public final OPT_DoubleConstantOperand asDoubleConstant() { return (OPT_DoubleConstantOperand) this; } /** * Cast to an {@link OPT_StringConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_StringConstantOperand} */ public final OPT_StringConstantOperand asStringConstant() { return (OPT_StringConstantOperand) this; } /** * Cast to an {@link OPT_ClassConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_ClassConstantOperand} */ public final OPT_ClassConstantOperand asClassConstant() { return (OPT_ClassConstantOperand) this; } /** * Cast to an {@link OPT_ObjectConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_ObjectConstantOperand} */ public final OPT_ObjectConstantOperand asObjectConstant() { return (OPT_ObjectConstantOperand) this; } /** * Cast to an {@link OPT_TIBConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_TIBConstantOperand} */ public final OPT_TIBConstantOperand asTIBConstant() { return (OPT_TIBConstantOperand) this; } /** * Cast to an {@link OPT_NullConstantOperand}. * * @return <code>this</code> cast as an {@link OPT_NullConstantOperand} */ public final OPT_NullConstantOperand asNullConstant() { return (OPT_NullConstantOperand) this; } /** * Cast to an {@link OPT_BranchOperand}. * * @return <code>this</code> cast as an {@link OPT_BranchOperand} */ public final OPT_BranchOperand asBranch() { return (OPT_BranchOperand) this; } /** * Cast to an {@link OPT_BasicBlockOperand}. * * @return <code>this</code> cast as an {@link OPT_BasicBlockOperand} */ public final OPT_BasicBlockOperand asBlock() { return (OPT_BasicBlockOperand) this; } /** * Cast to an {@link OPT_MemoryOperand}. * * @return <code>this</code> cast as an {@link OPT_MemoryOperand} */ public final OPT_MemoryOperand asMemory() { return (OPT_MemoryOperand) this; } /** * Cast to an {@link OPT_StackLocationOperand}. * * @return <code>this</code> cast as an {@link OPT_StackLocationOperand} */ public final OPT_StackLocationOperand asStackLocation() { return (OPT_StackLocationOperand) this; } /** * Cast to an {@link OPT_MethodOperand}. * * @return <code>this</code> cast as an {@link OPT_MethodOperand} */ public final OPT_MethodOperand asMethod() { return (OPT_MethodOperand) this; } /** * Cast to an {@link OPT_TypeOperand}. * * @return <code>this</code> cast as an {@link OPT_TypeOperand} */ public final OPT_TypeOperand asType() { return (OPT_TypeOperand) this; } /** * Cast to an {@link OPT_ConditionOperand}. * * @return <code>this</code> cast as an {@link OPT_ConditionOperand} */ public final OPT_ConditionOperand asCondition() { return (OPT_ConditionOperand) this; } /** * Cast to an {@link OPT_LocationOperand}. * * @return <code>this</code> cast as an {@link OPT_LocationOperand} */ public final OPT_LocationOperand asLocation() { return (OPT_LocationOperand) this; } /** * Does the operand represent a value of an int-like data type? * * @return <code>true</code> if the data type of <code>this</code> * is int-like as defined by {@link VM_TypeReference#isIntLikeType} * or <code>false</code> if it is not. */ public boolean isIntLike() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the int data type? * * @return <code>true</code> if the data type of <code>this</code> * is an int as defined by {@link VM_TypeReference#isIntType} * or <code>false</code> if it is not. */ public boolean isInt() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the long data type? * * @return <code>true</code> if the data type of <code>this</code> * is a long as defined by {@link VM_TypeReference#isLongType} * or <code>false</code> if it is not. */ public boolean isLong() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the float data type? * * @return <code>true</code> if the data type of <code>this</code> * is a float as defined by {@link VM_TypeReference#isFloatType} * or <code>false</code> if it is not. */ public boolean isFloat() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the double data type? * * @return <code>true</code> if the data type of <code>this</code> * is a double as defined by {@link VM_TypeReference#isDoubleType} * or <code>false</code> if it is not. */ public boolean isDouble() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the reference data type? * * @return <code>true</code> if the data type of <code>this</code> * is a reference as defined by {@link VM_TypeReference#isReferenceType} * or <code>false</code> if it is not. */ public boolean isRef() { // default to false and then override in subclasses return false; } /** * Does the operand represent a value of the address data type? * * @return <code>true</code> if the data type of <code>this</code> * is an address as defined by {@link VM_TypeReference#isWordType} * or <code>false</code> if it is not. */ public boolean isAddress() { // default to false and then override in subclasses return false; } /** * Does the operand definitely represent <code>null</code>? * * @return <code>true</code> if the operand definitely represents * <code>null</code> or <code>false</code> if it does not. */ public boolean isDefinitelyNull() { // default to false and then override in subclasses return false; } /** * Return a new operand that is semantically equivalent to <code>this</code>. * * @return a copy of <code>this</code> */ public abstract OPT_Operand copy(); /** * Are two operands semantically equivalent? * * @param op other operand * @return <code>true</code> if <code>this</code> and <code>op</code> * are semantically equivalent or <code>false</code> * if they are not. */ public abstract boolean similar(OPT_Operand op); /** * Return the {@link VM_TypeReference} of the value represented by the operand. * * @return the type of the value represented by the operand */ public VM_TypeReference getType() { // by default throw OPT_OptimizingCompilerException as not all // operands have a type. throw new OPT_OptimizingCompilerException("Getting the type for this operand has no defined meaning: " + this); } /** * Return the index of the operand in its containing instruction (SLOW). * * @return the index of the operand in its containing instruction */ public int getIndexInInstruction() { for (int i = 0; i < instruction.getNumberOfOperands(); i++) { OPT_Operand op = instruction.getOperand(i); if (op == this) return i; } throw new OPT_OptimizingCompilerException("OPT_Operand.getIndexInInstruction"); } /** * Compare two operands based on their positions in the operand lattice. * For the purposes of doing dataflow analysis, OPT_Operands can be * thought of as forming a lattice. * This function compares two operands and returns whether or * not op1 is a conservative approximation of op2. * Or in other words, if conservativelyApproximates(op1, op2) * then meet(op1, op2) = op1. * Note that lattices are partial orders, so it is quite * possible for both conservativelyApproximates(op1, op2) * and conservativelyApproximates(op2, op1) to return false. * * @param op1 the first operand to compare * @param op2 the second operand to compare * @return <code>true</code> if op1 conservatively approximates op2 or * <code>false</code> if it does not. */ public static boolean conservativelyApproximates(OPT_Operand op1, OPT_Operand op2) { // Step 1: Handle pointer equality and bottom if (op1 == op2) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { if (op2 == null) { VM.sysWrite("operands are both bottom therefore trivially true\n"); } else { VM.sysWrite("operands are identical therefore trivially true\n"); } } return true; } if (op1 == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 is bottom, therefore trivially true\n"); } return true; } if (op2 == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op2 is bottom, therefore trivially false\n"); } return false; } // Now, handle the non-trivial cases: // Step 2: op1 is a constant but op2 is not the same constant if (op1.isConstant()) { if (op1.similar(op2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("operands are similar constants\n"); } return true; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 is a constant but op2 is not the same constant\n"); } return false; } } // Step 3: op1 is a RegisterOperand // This case is complicated by the need to ensure that the // various Flag bits are considered as well.... if (op1.isRegister()) { OPT_RegisterOperand rop1 = op1.asRegister(); VM_TypeReference type1 = rop1.getType(); if (op2.isRegister()) { OPT_RegisterOperand rop2 = op2.asRegister(); VM_TypeReference type2 = rop2.getType(); if (type1 == type2) { if (rop1.hasLessConservativeFlags(rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are registers of identical type, but incompatible flags\n"); } return false; } else if (OPT_BC2IR.hasLessConservativeGuard(rop1, rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are registers of identical type, but with incompatible non-null guards\n"); } return false; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are compatible register operands\n"); } return true; } } else if (compatiblePrimitives(type1, type2) || OPT_ClassLoaderProxy.includesType(type1, type2) == OPT_Constants.YES) { // types are ok, only have to worry about the flags if (rop1.isPreciseType() || rop1.hasLessConservativeFlags(rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Flag mismatch between type compatible register operands\n"); } return false; } else if (OPT_BC2IR.hasLessConservativeGuard(rop1, rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Non-null guard mismatch between type compatible register operands\n"); } return false; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are compatible register operands\n"); } return true; } } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are type incompatible register operands\n"); } return false; } } else { // op2 is not a register if (op2 instanceof OPT_BC2IR.ReturnAddressOperand || op2 == OPT_BC2IR.DUMMY) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are incompatibale values\n"); } return false; } VM_TypeReference type2 = op2.getType(); if (type1 == type2 || compatiblePrimitives(type1, type2) || (OPT_ClassLoaderProxy.includesType(type1, type2) == OPT_Constants.YES)) { // only have to consider state of op1's flags. Types are ok. if (rop1.isPreciseType() && (type1 != type2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 preciseType bit will be incorrect\n"); } return false; } if ((rop1.scratchObject instanceof OPT_Operand) && ((type2 == VM_TypeReference.NULL_TYPE) || (type2.isIntLikeType() && op2.asIntConstant().value == 0) || (type2.isWordType() && op2.asAddressConstant().value.EQ(Address.zero())) || (type2.isLongType() && op2.asLongConstant().value == 0L))) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 non null guard will be incorrect"); } return false; } if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("(Constant) op2 was compatible with register op1\n"); } return true; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Op2 not compatible with register op1\n"); } return false; } } } // Step 4: op1 is a OPT_IRGEN operand of some form if (op1.similar(op2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Compatible OPT_BC2IR.* operands\n"); } return true; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatible OPT_BC2IR.* operands\n"); } return false; } } /** * Meet two operands based on their positions in the operand lattice. * For the purposes of doing dataflow analysis, OPT_Operands can be * thought of as forming a lattice. * This function takes two operands and returns their meet (glb). * We use <code>null</code> to stand for bottom (the meet of * the two operands is an illegal value). For exmaple, * meet(5.0, "hi") would evalaute to bottom. * Meet returns op1 iff conservativelyApproximates(op1, op2): * this is exploited in BC2IR to avoid doing redundant * work. * <p> * Unfortunately there is a fair amount of code duplication * between {@link #conservativelyApproximates} and * {@link #meet}, but factoring out the common control logic * is a non-trivial task. * * @param op1 the first operand to meet * @param op2 the second operand to meet * @param reg the <code>OPT_Register</code> to use to * create a new <code>OPT_RegisterOperand</code> * if meeting op1 and op2 requires doing so. * @return the OPT_Operand that is the meet of op1 and op2. * This function will return <code>null</code> when * the meet evaluates to bottom. It will return * op1 when conservativelyApproximates(op1, op2) * evaluates to <code>true</code>. */ public static OPT_Operand meet(OPT_Operand op1, OPT_Operand op2, OPT_Register reg) { // Step 1: Handler pointer equality and bottom if (op1 == op2) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { if (op1 == null) { VM.sysWrite("Both operands are bottom\n"); } else { VM.sysWrite("Operands are identical\n"); } } return op1; } if (op1 == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Op1 was already bottom\n"); } return op1; } if (op2 == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Op2 is bottom (but op1 was not)\n"); } return op2; } // Now handle the nontrivial cases... // Step 2: op1 is <null> (the null constant) if (op1 instanceof OPT_NullConstantOperand) { if (op2 instanceof OPT_NullConstantOperand) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Both operands are <null>\n"); } return op1; } else { /* * XXX Opt compiler guru please check :) * * Protect this code from crashing if op2 is DUMMY. As I understand * the calling code this shouldn't happen, but the case for RegisterOperand * handles it so I guess it's not too bad for a NullConstantOperand * to do so. * * -- Robin Garner 1 Feb 7 */ if (op2 instanceof OPT_BC2IR.ReturnAddressOperand || op2 == OPT_BC2IR.DUMMY) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatabily typed operands"); } return null; // bottom } VM_TypeReference type2 = op2.getType(); if (type2.isReferenceType()) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 is <null>, but op2 is other ref type\n"); } return new OPT_RegisterOperand(reg, type2); } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 is <null>, but op2 is not a ref type\n"); } return null; // bottom } } } // Step 3: op1 is some other constant if (op1.isConstant()) { if (op1.similar(op2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 and op2 are similar constants\n"); } return op1; } else { VM_TypeReference superType = OPT_ClassLoaderProxy.findCommonSuperclass(op1.getType(), op2.getType()); if (superType == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("op1 and op2 have incompatible types\n"); } return null; // bottom } else { return new OPT_RegisterOperand(reg, superType); } } } // Step 4: op1 is a register operand // This case is complicated by the need to ensure that // the various Flag bits are considered as well.... if (op1.isRegister()) { OPT_RegisterOperand rop1 = op1.asRegister(); VM_TypeReference type1 = rop1.getType(); if (op2.isRegister()) { OPT_RegisterOperand rop2 = op2.asRegister(); VM_TypeReference type2 = rop2.getType(); if (type1 == type2) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Identically typed register operands, checking flags..."); } if (rop1.hasLessConservativeFlags(rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("mismatch\n"); } OPT_RegisterOperand res = new OPT_RegisterOperand(reg, type1, rop1.getFlags(), rop1.isPreciseType(), rop1.isDeclaredType()); if (rop1.scratchObject instanceof OPT_Operand && rop2.scratchObject instanceof OPT_Operand && (((OPT_Operand) rop1.scratchObject).similar(((OPT_Operand) rop2.scratchObject)))) { res.scratchObject = rop1.scratchObject; // compatible, so preserve onto res } res.meetInheritableFlags(rop2); return res; } else if (OPT_BC2IR.hasLessConservativeGuard(rop1, rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite( "Operands are registers of identical type with compatible flags but with incompatible non-null guards\n"); } // by not setting scratchObject we mark as possible null return new OPT_RegisterOperand(reg, type1, rop1.getFlags(), rop1.isPreciseType(), rop1.isDeclaredType()); } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("match\n"); } return op1; } } else if (compatiblePrimitives(type1, type2) || OPT_ClassLoaderProxy.includesType(type1, type2) == OPT_Constants.YES) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Compatabily typed register operands, checking flags..."); } if (rop1.isPreciseType() || rop1.hasLessConservativeFlags(rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("mismatch\n"); } OPT_RegisterOperand res = new OPT_RegisterOperand(reg, type1, rop1.getFlags(), rop1.isPreciseType(), rop1.isDeclaredType()); res.meetInheritableFlags(rop2); // even if both op1 & op2 are precise, // op1.type != op2.type, so clear it on res res.clearPreciseType(); if (rop1.scratchObject instanceof OPT_Operand && rop2.scratchObject instanceof OPT_Operand && (((OPT_Operand) rop1.scratchObject).similar(((OPT_Operand) rop2.scratchObject)))) { // it matched, so preserve onto res. res.scratchObject = rop1.scratchObject; } return res; } else if (OPT_BC2IR.hasLessConservativeGuard(rop1, rop2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Operands are registers of compatible type and flags but with incompatible non-null guards\n"); } return new OPT_RegisterOperand(reg, type1, rop1.getFlags(), rop1.isPreciseType(), rop1.isDeclaredType()); } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("match\n"); } return op1; } } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatibly typed register operands...(" + type1 + ", " + type2 + ")..."); } VM_TypeReference resType = OPT_ClassLoaderProxy.findCommonSuperclass(type1, type2); if (resType == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("no common supertype, returning bottom\n"); } return null; // bottom } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("found common supertype\n"); } OPT_RegisterOperand res = new OPT_RegisterOperand(reg, resType, rop1.getFlags(), false, false); res.meetInheritableFlags(rop2); res.clearPreciseType(); // invalid on res res.clearDeclaredType(); // invalid on res if (rop1.scratchObject instanceof OPT_Operand && rop2.scratchObject instanceof OPT_Operand && (((OPT_Operand) rop1.scratchObject).similar(((OPT_Operand) rop2.scratchObject)))) { // it matched, so preserve onto res. res.scratchObject = rop1.scratchObject; } return res; } } } else { if (op2 instanceof OPT_BC2IR.ReturnAddressOperand || op2 == OPT_BC2IR.DUMMY) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatabily typed operands"); } return null; // bottom } VM_TypeReference type2 = op2.getType(); if (type1 == type2 || compatiblePrimitives(type1, type2) || (OPT_ClassLoaderProxy.includesType(type1, type2) == OPT_Constants.YES)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Compatabily typed register & other operand, checking flags..."); } OPT_RegisterOperand res = rop1; if (res.isPreciseType() && type1 != type2) { res = res.copyU2U(); res.clearPreciseType(); } if ((rop1.scratchObject instanceof OPT_Operand) && ((type2 == VM_TypeReference.NULL_TYPE) || (type2.isIntLikeType() && op2.asIntConstant().value == 0) || (type2.isWordType() && op2.asAddressConstant().value.isZero()) || (type2.isLongType() && op2.asLongConstant().value == 0L))) { res = res.copyU2U(); res.scratchObject = null; } if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { if (res == rop1) { VM.sysWrite("match\n"); } else { VM.sysWrite("mismatch\n"); } } return res; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatabily typed register & other operand...(" + type1 + ", " + type2 + ")..."); } VM_TypeReference resType = OPT_ClassLoaderProxy.findCommonSuperclass(type1, type2); if (resType == null) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("no common supertype, returning bottom\n"); } return null; // bottom } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("found common supertype\n"); } return new OPT_RegisterOperand(reg, resType); } } } } // Step 5: op1 is some OPT_IRGEN operand if (op1.similar(op2)) { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Compatible OPT_BC2IR.* operands\n"); } return op1; } else { if (OPT_IRGenOptions.DBG_OPERAND_LATTICE) { VM.sysWrite("Incompatible OPT_BC2IR.* operands, returning bottom\n"); } return null; // bottom } } private static boolean compatiblePrimitives(VM_TypeReference type1, VM_TypeReference type2) { if (type1.isIntLikeType() && type2.isIntLikeType()) { if (type1.isIntType()) { return type2.isBooleanType() || type2.isByteType() || type2.isShortType() || type2.isIntType(); } if (type1.isShortType()) { return type2.isBooleanType() || type2.isByteType() || type2.isShortType(); } if (type1.isByteType()) { return type2.isBooleanType() || type2.isByteType(); } if (type1.isBooleanType()) { return type2.isBooleanType(); } } return false; } }