bool ConstantFolder::foldConv()

in vm/jitrino/src/optimizer/constantfolder.cpp [544:990]


bool ConstantFolder::foldConv(Type::Tag fromType, Type::Tag toType, Modifier mod,
                              ConstInst::ConstValue src,
                              ConstInst::ConstValue& res)
{
    bool ovfm = (mod.getOverflowModifier()!=Overflow_None);
    switch (toType) {
    case Type::Int32:
#ifndef PONTER64
    case Type::IntPtr:
#endif
        if (Type::isInteger(fromType)) {
            if (Type::isIntegerOf4Signed(fromType)) {
                res.i4 = src.i4; return true;
            } else if (Type::isIntegerOf4Unsigned(fromType)) {
                res.i4 = src.i4; 
                if (ovfm && (src.i4 < 0)) return false;
                return true;
            } else if (Type::isIntegerOf8Signed(fromType)) {
                res.i4 = CAST(I_32, src.i8);
                if (ovfm && (src.i8 != CAST(int64, res.i4))) return false;
                return true;
            } else if (Type::isIntegerOf8Unsigned(fromType)) {
                res.i4 = CAST(I_32, CAST(uint64, src.i8));
                if (ovfm && (CAST(uint64, src.i8) != CAST(uint64, res.i4))) return false;
                return true;
            }
            assert(0);
        } else if (fromType == Type::Single) {
            res.i4 = float2int<I_32, float>(src.s); 
            if (ovfm && (src.s != CAST(float, res.i4))) return false;
            return true;
        } else if (fromType == Type::Double) {
            res.i4 = float2int<I_32, double>(src.d);
            if (ovfm && (src.d != CAST(double, res.i4))) return false;
            return true;
        }
        break;
    case Type::UInt32:
#ifndef PONTER64
    case Type::UIntPtr:
#endif
        if (Type::isInteger(fromType)) {
            if (Type::isIntegerOf4Bytes(fromType)) {
                res.i4 = src.i4; return true;
            } else if (Type::isIntegerOf8Signed(fromType)) {
                res.i4 = CAST(I_32, CAST(U_32, src.i8));
                if (ovfm && (src.i8 != CAST(int64, CAST(U_32, res.i4)))) return false;
                return true;
            } else if (Type::isIntegerOf8Unsigned(fromType)) {
                res.i4 = CAST(I_32, CAST(U_32, CAST(uint64, src.i8)));
                if (ovfm && (CAST(uint64, src.i8) != CAST(uint64, CAST(U_32, res.i4)))) return false;
                return true;
            }
            assert(0);
        } else if (fromType == Type::Single) {
            res.i4 = float2uint<U_32, float>(src.s);
            if (ovfm && (src.s != CAST(float, CAST(U_32, res.i4)))) return false;
            return true;
        } else if (fromType == Type::Double) {
            res.i4 = float2uint<U_32, double>(src.d);
            if (ovfm && (src.d != CAST(double, CAST(U_32, res.i4)))) return false;
            return true;
        }
        break;
    case Type::Int64:
#ifdef PONTER64
    case Type::IntPtr:
#endif
        assert(!ovfm || mod.getOverflowModifier()==Overflow_Signed);
        if (Type::isInteger(fromType)) {
            if (Type::isIntegerOf4Signed(fromType)) {
                res.i8 = src.i4; return true;
            } else if (Type::isIntegerOf4Unsigned(fromType)) {
                res.i8 = CAST(U_32, src.i4); 
                if (ovfm && (src.i4 < 0)) return false;
                return true;
            } else if (Type::isIntegerOf8Signed(fromType)) {
                res.i8 = src.i8; return true;
            } else if (Type::isIntegerOf8Unsigned(fromType)) {
                res.i8 = src.i8; 
                if (ovfm && (src.i8 < 0)) return false;
                return true;
            }
            assert(0);
        } else if (fromType == Type::Single) {
            res.i8 = float2int<int64, float>(src.s); 
            if (ovfm && (src.s != CAST(float, res.i8))) return false;
            return true;
        } else if (fromType == Type::Double) {
            res.i8 = float2int<int64, double>(src.d);
            if (ovfm && (src.d != CAST(double, res.i8))) return false;
            return true;
        }
        break;
    case Type::UInt64:
#ifdef PONTER64
    case Type::UIntPtr:
#endif
        assert(!ovfm || mod.getOverflowModifier()==Overflow_Unsigned);
        if (Type::isInteger(fromType)) {
            if (Type::isIntegerOf4Signed(fromType)) {
                res.i8 = CAST(uint64, CAST(U_32, src.i4));
                if (ovfm && (src.i4 < 0)) return false;
                return true;
            } else if (Type::isIntegerOf4Unsigned(fromType)) {
                res.i8 = CAST(uint64, CAST(U_32, src.i4)); return true;
            } else if (Type::isIntegerOf8Bytes(fromType)) {
                res.i8 = src.i8; return true;
            }
        } else if (fromType == Type::Single) {
            res.i8 = float2uint<uint64, float>(src.s);
            if (ovfm) return false; 
            return true;
        } else if (fromType == Type::Double) {
            return false;
        }
        break;
    case Type::Single:
        switch (fromType) {
        case Type::Int32:
            res.s = CAST(float, src.i4); return true;
        case Type::UInt32:
            res.s = CAST(float, CAST(U_32, src.i4)); return true;
        case Type::Int64:
            res.s = CAST(float, src.i8);
            if (ovfm && (src.i8 != float2int<int64, float>(res.s))) return false;
            return true;
        case Type::UInt64:
            return false; 
        case Type::Single:
            res.s = src.s; return true;
        case Type::Double:
            res.s = CAST(float, src.d);
            if (ovfm && (src.d != CAST(double, res.s))) return false;
            return true;
        case Type::Float: 
        default:
            break;
        }
        break;
    case Type::Double:
        switch (fromType) {
        case Type::Int32:
            res.d = CAST(double, src.i4); return true;
        case Type::UInt32:
            res.d = CAST(double, CAST(U_32, src.i4)); return true;
        case Type::Int64:
            res.d = CAST(double, src.i8); return true;
        case Type::UInt64:
            return false; 
        case Type::Single:
            res.d = CAST(double, src.s); return true;
        case Type::Double:
            res.d = src.d; return true;
        case Type::Float: 
        default:
            break;
        }
        break;
    case Type::UnmanagedPtr:
        // Let's accept the conversion from properly sized integer
#ifdef PONTER64
        if (Type::isIntegerOf8Bytes(fromType)) {
#else
        if (Type::isIntegerOf4Bytes(fromType)) {
#endif
            return true;
        }
        break;
    default:
        break;
    }
    return false;
}

bool
ConstantFolder::foldCmp32(ComparisonModifier mod, I_32 c1, I_32 c2, I_32& result) {
    switch (mod) {
    case Cmp_EQ:    result = ((c1 == c2)?1:0);                  return true;
    case Cmp_NE_Un: result = ((c1 != c2)?1:0);                  return true;
    case Cmp_GT:    result = ((c1 > c2)?1:0);                   return true;
    case Cmp_GT_Un: result = ((CAST(U_32, c1) > CAST(U_32, c2))?1:0);   return true;
    case Cmp_GTE:   result = ((c1 >= c2)?1:0);                  return true;
    case Cmp_GTE_Un:result = ((CAST(U_32, c1) >= CAST(U_32, c2))?1:0);  return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmpRef(ComparisonModifier mod, void* c1, void* c2, I_32& result) {
    switch (mod) {
    case Cmp_EQ:    result = ((c1 == c2)?1:0);                  return true;
    case Cmp_NE_Un: result = ((c1 != c2)?1:0);                  return true;
    case Cmp_GT:    result = ((c1 > c2)?1:0);                   return true;
    case Cmp_GT_Un: result = ((c1 > c2)?1:0);                   return true;
    case Cmp_GTE:   result = ((c1 >= c2)?1:0);                  return true;
    case Cmp_GTE_Un:result = ((c1 >= c2)?1:0);                  return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmp64(ComparisonModifier mod, int64 c1, int64 c2, I_32& result) {
    switch (mod) {
    case Cmp_EQ:    result = ((c1 == c2)?1:0);                  return true;
    case Cmp_NE_Un: result = ((c1 != c2)?1:0);                  return true;
    case Cmp_GT:    result = ((c1 > c2)?1:0);                   return true;
    case Cmp_GT_Un: result = ((CAST(uint64, c1) > CAST(uint64, c2))?1:0);   return true;
    case Cmp_GTE:   result = ((c1 >= c2)?1:0);                  return true;
    case Cmp_GTE_Un:result = ((CAST(uint64, c1) >= CAST(uint64, c2))?1:0);  return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmpSingle(ComparisonModifier mod, float c1, float c2, I_32& result) {
    switch (mod) {
    case Cmp_EQ:
        if (isnan(c1) || isnan(c2)) { result = false; }
        else { result = ((c1 == c2)?1:0); }
        return true;
    case Cmp_NE_Un: 
        if (isnan(c1) || isnan(c2)) { result = true; }
        else { result = ((c1 != c2)?1:0); }
        return true;
    case Cmp_GT:    
    case Cmp_GT_Un: 
        if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GT_Un); }
        else { result = ((c1 > c2)?1:0); }
        return true;
    case Cmp_GTE:
    case Cmp_GTE_Un:
        if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GTE_Un); }
        else { result = ((c1 >= c2)?1:0); }
        return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmpDouble(ComparisonModifier mod, double c1, double c2, I_32& result){
    switch (mod) {
    case Cmp_EQ:
        if (isnan(c1) || isnan(c2)) { result = false; }
        else { result = ((c1 == c2)?1:0); }
        return true;
    case Cmp_NE_Un: 
        if (isnan(c1) || isnan(c2)) { result = true; }
        else { result = ((c1 != c2)?1:0); }
        return true;
    case Cmp_GT:    
    case Cmp_GT_Un: 
        if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GT_Un); }
        else { result = ((c1 > c2)?1:0); }
        return true;
    case Cmp_GTE:
    case Cmp_GTE_Un:
        if (isnan(c1) || isnan(c2)) { result = (mod == Cmp_GTE_Un); }
        else { result = ((c1 >= c2)?1:0); }
        return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmp32(ComparisonModifier mod, I_32 c, I_32& result) {
    switch (mod) {
    case Cmp_Zero:      result = ((c == 0)?1:0);    return true;
    case Cmp_NonZero:   result = ((c != 0)?1:0);    return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmp64(ComparisonModifier mod, int64 c, I_32& result) {
    switch (mod) {
    case Cmp_Zero:      result = ((c == 0)?1:0);    return true;
    case Cmp_NonZero:   result = ((c != 0)?1:0);    return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmpRef(ComparisonModifier mod, void* c, I_32& result) {
    switch (mod) {
    case Cmp_Zero:      result = ((c == NULL)?1:0);    return true;
    case Cmp_NonZero:   result = ((c != NULL)?1:0);    return true;
    default: break;
    }
    return false;
}

bool
ConstantFolder::foldCmp(Type::Tag cmpTypeTag,
                        ComparisonModifier mod,
                        ConstInst::ConstValue val,
                        ConstInst::ConstValue& result) {
    switch (cmpTypeTag) {
    case Type::Int32:   return foldCmp32(mod, val.i4, result.i4);
    case Type::Int64:   return foldCmp64(mod, val.i8, result.i4);
    case Type::UInt32:  return foldCmp32(mod, val.i4, result.i4);
    case Type::UInt64:  return foldCmp64(mod, val.i8, result.i4);
    default: break;
    }
    // fold comparisons of references against null
    if (Type::isObject(cmpTypeTag))
        return foldCmpRef(mod, val.i, result.i4);
    return false;
}
//
// binary comparison
//
bool
ConstantFolder::foldCmp(Type::Tag cmpTypeTag,
                        ComparisonModifier mod,
                        ConstInst::ConstValue val1,
                        ConstInst::ConstValue val2,
                        ConstInst::ConstValue& result) {
    switch (cmpTypeTag) {
    case Type::Int32: case Type::UInt32: return foldCmp32(mod, val1.i4, val2.i4, result.i4);
    case Type::Int64: case Type::UInt64: return foldCmp64(mod, val1.i8, val2.i8, result.i4);
    case Type::Single:  return foldCmpSingle(mod, val1.s, val2.s, result.i4);
    case Type::Double:  return foldCmpDouble(mod, val1.d, val2.d, result.i4);
    default: break;
    }
    return false;
}



bool
ConstantFolder::foldConstant(Type::Tag type,
                             Opcode opc,
                             ConstInst::ConstValue val1,
                             ConstInst::ConstValue val2,
                             ConstInst::ConstValue& result,
                             bool is_signed) {
    
    switch (type) {
    case Type::Int8:
    case Type::UInt8:  return fold8(opc, (I_8)val1.i4, (I_8)val2.i4, result.i4, is_signed);
    case Type::Int16:
    case Type::UInt16: return fold16(opc, (int16)val1.i4, (int16)val2.i4, result.i4, is_signed);
    case Type::Int32:   
    case Type::UInt32: return fold32(opc, val1.i4, val2.i4, result.i4, is_signed);
    case Type::Int64:
    case Type::UInt64: return fold64(opc, val1.i8, val2.i8, result.i8, is_signed);
    case Type::IntPtr:
    case Type::UIntPtr: {
        int psi = sizeof(POINTER_SIZE_INT);
        switch (psi) {
        case 1: return fold8(opc, (I_8)val1.i4, (I_8)val2.i4, result.i4, is_signed);
        case 2: return fold16(opc, (int16)val1.i4, (int16)val2.i4, result.i4, is_signed);
        case 4: return fold32(opc, val1.i4, val2.i4, result.i4, is_signed);
        case 8: return fold64(opc, val1.i8, val2.i8, result.i8, is_signed);
        default: return false;
        }
    }
    case Type::Single: return foldSingle(opc, val1.s, val2.s, result.s);
    case Type::Double: return foldDouble(opc, val1.d, val2.d, result.d);
    default: return false;
    }
}

bool
ConstantFolder::foldConstant(Type::Tag type,
                             Opcode opc,
                             ConstInst::ConstValue val,
                             ConstInst::ConstValue& result) {
    switch (type) {
    case Type::Int32: 
    case Type::UInt32: 
        return fold32(opc, val.i4, result.i4);
    case Type::Int64: 
    case Type::UInt64: 
        return fold64(opc, val.i8, result.i8);
    case Type::Single: 
        return foldSingle(opc, val.s, result.s);
    case Type::Double: 
        return foldDouble(opc, val.d, result.d);
    default: break;
    }
    return false;
}
//
// unary comparison
//

//
// Tries to constant fold the instruction, setting the resulting constant
// value to result.  Returns true if instruction was folded.
//
bool
ConstantFolder::fold(Inst* inst, ConstInst::ConstValue& result) {
    U_32 numSrcs = inst->getNumSrcOperands();
    if (numSrcs == 0)
        return false;
    ConstInst* constSrc0 = inst->getSrc(0)->getInst()->asConstInst();
    if (constSrc0 == NULL)
        return false;
    Opcode opc = inst->getOpcode();
    if (numSrcs == 1) {
        if (opc == Op_Cmp) { 
            return foldCmp(constSrc0->getType(),
                           inst->getComparisonModifier(),
                           constSrc0->getValue(),
                           result);
        }
        Modifier mod = inst->getModifier();
        return foldConstant(inst->getType(),
                            inst->getOpcode(),
                            constSrc0->getValue(),
                            result);
    }
    ConstInst* constSrc1 = inst->getSrc(1)->getInst()->asConstInst();
    if (constSrc1 == NULL)
        return false;
    if (numSrcs == 2) {
        if (opc == Op_Cmp) { 
            assert(constSrc0->getType() == constSrc1->getType());
            return foldCmp(inst->getType(),
                           inst->getComparisonModifier(),
                           constSrc0->getValue(),
                           constSrc1->getValue(),
                           result);
        }
        Modifier mod = inst->getModifier();
        bool is_signed = mod.isSigned();
        return foldConstant(inst->getType(),
                            inst->getOpcode(),
                            constSrc0->getValue(),
                            constSrc1->getValue(),
                            result,
                            is_signed);
    }
    return false;
}


} //namespace Jitrino