public Object visit(CompareExpression node) {

            // get expressions
            Expression left = node.getLeftExpression();
            Expression right = node.getRightExpression();

            // get associated types
            Type ltype = left.getType();
            Type rtype = right.getType();

            // create local variables
            generate(left);
            LocalVariable lvar =
                mBuilder.createLocalVariable(null, makeDesc(ltype));
            mBuilder.storeLocal(lvar);

            generate(right);
            LocalVariable rvar =
                mBuilder.createLocalVariable(null, makeDesc(rtype));
            mBuilder.storeLocal(rvar);

            // check if primitives and handle directly
            if (ltype.isPrimitive() && rtype.isPrimitive()) {

                if (!ltype.equals(rtype)) {
                    throw new RuntimeException(
                        "CompareExpression has invalid types: " +
                        ltype + ", " + rtype
                    );
                }

                generateComparison(lvar, rvar, ltype);
            }

            // otherwise, compare as nulls and then handle comparison
            else {
                Class<?> lclass = ltype.getNaturalClass();
                Class<?> rclass = rtype.getNaturalClass();

                Label location1 = mBuilder.createLabel();
                Label endLocation = mBuilder.createLabel();

                if (ltype.isNonNull()) {
                    if (rtype.isNullable()) {
                        // left is non-null, but right is nullable, so do a
                        // null check on right and if null we return [1], else
                        // we jump to the beginning of the comparison block
                        // as both are non-null at that point
                        mBuilder.loadLocal(rvar);
                        mBuilder.ifNullBranch(location1, false);
                        mBuilder.loadConstant(1);
                        mBuilder.branch(endLocation);
                    }
                }
                else {
                    Label location2 = mBuilder.createLabel();

                    // left is nullable, so do a null check on left.  If null
                    // we check right if necessary; otherwise if non-null, we
                    // check right for nullability or do the comparison
                    mBuilder.loadLocal(lvar);
                    mBuilder.ifNullBranch(location2, false);

                    if (rtype.isNonNull()) {
                        // right is non-null and at this point we have found
                        // left to be null, so just return [-1]. We also must
                        // set the location so that the left check will jump
                        // to here to begin the general comparison block
                        mBuilder.loadConstant(-1);
                        mBuilder.branch(endLocation);
                        location2.setLocation();
                    }
                    else {
                        Label location3 = mBuilder.createLabel();

                        // right is nullable so we must check if right is null
                        // or not.  If right is null, then left is already null,
                        // so we purely return [0]. Otherwise
                        mBuilder.loadLocal(rvar);
                        mBuilder.ifNullBranch(location3, false);
                        mBuilder.loadConstant(0);
                        mBuilder.branch(endLocation);

                        // left is already null and per the above we have now
                        // found right to be non-null, so return [-1].
                        location3.setLocation();
                        mBuilder.loadConstant(-1);
                        mBuilder.branch(endLocation);

                        // branch point from the initial nullable check on left
                        // so at this point left is not null so we must now
                        // check if right is null or not.  if not null, we jump
                        // to the comparison point...otherwise, left is not and
                        // right is, so we return [1]
                        location2.setLocation();
                        mBuilder.loadLocal(rvar);
                        mBuilder.ifNullBranch(location1, false);
                        mBuilder.loadConstant(1);
                        mBuilder.branch(endLocation);
                    }
                }

                // comparison branch point at which point both left and right
                // are non-null
                location1.setLocation();

                // if left is primitive and right has primitive peer
                if (ltype.isPrimitive() && rtype.hasPrimitivePeer()) {
                    Type type = ltype.getCompatibleType(rtype.toPrimitive());
                    TypeDesc desc = makeDesc(type);

                    LocalVariable lvar2 = lvar;
                    if (!type.equals(ltype)) {
                        lvar2 = mBuilder.createLocalVariable(null, desc);

                        mBuilder.loadLocal(lvar);
                        typeConvertEnd(ltype, type, false);
                        mBuilder.storeLocal(lvar2);
                    }

                    LocalVariable rvar2 =
                        mBuilder.createLocalVariable(null, desc);
                    mBuilder.loadLocal(rvar);
                    typeConvertEnd(rtype, type, false);
                    mBuilder.storeLocal(rvar2);

                    // primitive comparison
                    generateComparison(lvar2, rvar2, type);
                }

                // if right is primitive and left has primitive peer
                else if (ltype.hasPrimitivePeer() && rtype.isPrimitive()) {
                    Type type = rtype.getCompatibleType(ltype.toPrimitive());
                    TypeDesc desc = makeDesc(type);

                    LocalVariable lvar2 =
                        mBuilder.createLocalVariable(null, desc);
                    mBuilder.loadLocal(lvar);
                    typeConvertEnd(ltype, type, false);
                    mBuilder.storeLocal(lvar2);

                    LocalVariable rvar2 = rvar;
                    if (!type.equals(rtype)) {
                        rvar2 = mBuilder.createLocalVariable(null, desc);

                        mBuilder.loadLocal(rvar);
                        typeConvertEnd(rtype, type, false);
                        mBuilder.storeLocal(rvar2);
                    }

                    // primitive comparison
                    generateComparison(lvar2, rvar2, type);
                }

                // if left parent class of right and comparable
                else if (lclass.isAssignableFrom(rclass) &&
                         Comparable.class.isAssignableFrom(lclass)) {

                    Method method =
                        getMethod(Comparable.class, "compareTo", Object.class);

                    mBuilder.loadLocal(lvar);
                    mBuilder.loadLocal(rvar);
                    mBuilder.invoke(method);
                }

                // if right parent class of left and comparable
                else if (rclass.isAssignableFrom(lclass) &&
                         Comparable.class.isAssignableFrom(rclass)) {

                   Method method =
                       getMethod(Comparable.class, "compareTo", Object.class);

                   mBuilder.loadLocal(rvar);
                   mBuilder.loadLocal(lvar);
                   mBuilder.invoke(method);
                   mBuilder.math(Opcode.INEG);
               }

                // if Number parent class of either object types, then we must
                // perform runtime analysis of the numbers to compare to each
                // other
                else if ((ltype.isPrimitive() || Number.class.isAssignableFrom(lclass)) &&
                         (rtype.isPrimitive() || Number.class.isAssignableFrom(rclass))) {

                    if (!ltype.isPrimitive()) {
                        lclass = Number.class;
                    }

                    if (!rtype.isPrimitive()) {
                        rclass = Number.class;
                    }

                    mBuilder.loadLocal(lvar);
                    mBuilder.loadLocal(rvar);

                    Method method = getMethod(
                        WrapperTypeConversionUtil.class, "compare",
                        lclass, rclass
                    );

                    mBuilder.invoke(method);
                }

                // otherwise, compare at runtime as strings
                else {
                    warn("compare.not.convertible",
                          ltype.getClassName(), rtype.getClassName(), node);

                    mBuilder.loadLocal(lvar);
                    if (ltype.isPrimitive()) {
                        typeConvertEnd(ltype, ltype.toNonPrimitive(), false);
                    }

                    mBuilder.invoke(getMethod(Object.class, "toString"));

                    mBuilder.loadLocal(rvar);
                    if (rtype.isPrimitive()) {
                        typeConvertEnd(rtype, rtype.toNonPrimitive(), false);
                    }

                    mBuilder.invoke(getMethod(Object.class, "toString"));

                    Method method =
                        getMethod(Comparable.class, "compareTo", Object.class);
                    mBuilder.invoke(method);
                }

                endLocation.setLocation();
            }

            return null;
        }