Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 72 additions & 1 deletion src/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -789,7 +789,78 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genLongToIntCast(GenTree* treeNode);
#endif

void genIntToIntCast(GenTree* treeNode);
struct GenIntCastDesc
{
enum CheckKind
{
CHECK_NONE,
CHECK_SMALL_INT_RANGE,
CHECK_POSITIVE,
#ifdef _TARGET_64BIT_
CHECK_UINT_RANGE,
CHECK_POSITIVE_INT_RANGE,
CHECK_INT_RANGE,
#endif
};

enum ExtendKind
{
COPY,
ZERO_EXTEND_SMALL_INT,
SIGN_EXTEND_SMALL_INT,
#ifdef _TARGET_64BIT_
ZERO_EXTEND_INT,
SIGN_EXTEND_INT,
#endif
};

private:
CheckKind m_checkKind;
unsigned m_checkSrcSize;
int m_checkSmallIntMin;
int m_checkSmallIntMax;
ExtendKind m_extendKind;
unsigned m_extendSrcSize;

public:
GenIntCastDesc(GenTreeCast* cast);

CheckKind CheckKind() const
{
return m_checkKind;
}

unsigned CheckSrcSize() const
{
assert(m_checkKind != CHECK_NONE);
return m_checkSrcSize;
}

int CheckSmallIntMin() const
{
assert(m_checkKind == CHECK_SMALL_INT_RANGE);
return m_checkSmallIntMin;
}

int CheckSmallIntMax() const
{
assert(m_checkKind == CHECK_SMALL_INT_RANGE);
return m_checkSmallIntMax;
}

ExtendKind ExtendKind() const
{
return m_extendKind;
}

unsigned ExtendSrcSize() const
{
return m_extendSrcSize;
}
};

void genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& desc, regNumber reg);
void genIntToIntCast(GenTreeCast* cast);
void genFloatToFloatCast(GenTree* treeNode);
void genFloatToIntCast(GenTree* treeNode);
void genIntToFloatCast(GenTree* treeNode);
Expand Down
272 changes: 110 additions & 162 deletions src/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2850,202 +2850,150 @@ void CodeGen::genJmpMethod(GenTree* jmp)
}

//------------------------------------------------------------------------
// genIntToIntCast: Generate code for an integer cast
// genIntCastOverflowCheck: Generate overflow checking code for an integer cast.
//
// Arguments:
// treeNode - The GT_CAST node
//
// Return Value:
// None.
// cast - The GT_CAST node
// desc - The cast description
// reg - The register containing the value to check
//
// Assumptions:
// The treeNode must have an assigned register.
// For a signed convert from byte, the source must be in a byte-addressable register.
// Neither the source nor target type can be a floating point type.
//
// TODO-ARM64-CQ: Allow castOp to be a contained node without an assigned register.
//
void CodeGen::genIntToIntCast(GenTree* treeNode)
void CodeGen::genIntCastOverflowCheck(GenTreeCast* cast, const GenIntCastDesc& desc, regNumber reg)
{
assert(treeNode->OperGet() == GT_CAST);

GenTree* castOp = treeNode->gtCast.CastOp();
emitter* emit = getEmitter();

var_types dstType = treeNode->CastToType();
var_types srcType = genActualType(castOp->TypeGet());

assert(genTypeSize(srcType) <= genTypeSize(TYP_I_IMPL));

regNumber targetReg = treeNode->gtRegNum;
regNumber sourceReg = castOp->gtRegNum;

// For Long to Int conversion we will have a reserved integer register to hold the immediate mask
regNumber tmpReg = (treeNode->AvailableTempRegCount() == 0) ? REG_NA : treeNode->GetSingleTempReg();

assert(genIsValidIntReg(targetReg));
assert(genIsValidIntReg(sourceReg));

genConsumeReg(castOp);
Lowering::CastInfo castInfo;
switch (desc.CheckKind())
{
case GenIntCastDesc::CHECK_POSITIVE:
getEmitter()->emitIns_R_I(INS_cmp, EA_ATTR(desc.CheckSrcSize()), reg, 0);
genJumpToThrowHlpBlk(EJ_lt, SCK_OVERFLOW);
break;

// Get information about the cast.
Lowering::getCastDescription(treeNode, &castInfo);
#ifdef _TARGET_64BIT_
case GenIntCastDesc::CHECK_UINT_RANGE:
// We need to check if the value is not greater than 0xFFFFFFFF but this value
// cannot be encoded in the immediate operand of CMP. Use TST instead to check
// if the upper 32 bits are zero.
getEmitter()->emitIns_R_I(INS_tst, EA_8BYTE, reg, 0xFFFFFFFF00000000LL);
genJumpToThrowHlpBlk(EJ_ne, SCK_OVERFLOW);
break;

if (castInfo.requiresOverflowCheck)
{
bool movRequired = (sourceReg != targetReg);
emitAttr movSize = emitActualTypeSize(dstType);
emitAttr cmpSize = EA_ATTR(genTypeSize(srcType));
case GenIntCastDesc::CHECK_POSITIVE_INT_RANGE:
// We need to check if the value is not greater than 0x7FFFFFFF but this value
// cannot be encoded in the immediate operand of CMP. Use TST instead to check
// if the upper 33 bits are zero.
getEmitter()->emitIns_R_I(INS_tst, EA_8BYTE, reg, 0xFFFFFFFF80000000LL);
genJumpToThrowHlpBlk(EJ_ne, SCK_OVERFLOW);
break;

if (castInfo.signCheckOnly)
case GenIntCastDesc::CHECK_INT_RANGE:
{
// We only need to check for a negative value in sourceReg
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, 0);
emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
noway_assert(genTypeSize(srcType) == 4 || genTypeSize(srcType) == 8);
// This is only interesting case to ensure zero-upper bits.
if ((srcType == TYP_INT) && (dstType == TYP_ULONG))
{
// cast to TYP_ULONG:
// We use a mov with size=EA_4BYTE
// which will zero out the upper bits
movSize = EA_4BYTE;
movRequired = true;
}
const regNumber tempReg = cast->GetSingleTempReg();
assert(tempReg != reg);
instGen_Set_Reg_To_Imm(EA_8BYTE, tempReg, INT32_MAX);
getEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, tempReg);
genJumpToThrowHlpBlk(EJ_gt, SCK_OVERFLOW);
instGen_Set_Reg_To_Imm(EA_8BYTE, tempReg, INT32_MIN);
getEmitter()->emitIns_R_R(INS_cmp, EA_8BYTE, reg, tempReg);
genJumpToThrowHlpBlk(EJ_lt, SCK_OVERFLOW);
}
else if (castInfo.unsignedSource || castInfo.unsignedDest)
break;
#endif

default:
{
// When we are converting from/to unsigned,
// we only have to check for any bits set in 'typeMask'
assert(desc.CheckKind() == GenIntCastDesc::CHECK_SMALL_INT_RANGE);
const int castMaxValue = desc.CheckSmallIntMax();
const int castMinValue = desc.CheckSmallIntMin();

noway_assert(castInfo.typeMask != 0);
#if defined(_TARGET_ARM_)
if (arm_Valid_Imm_For_Instr(INS_tst, castInfo.typeMask, INS_FLAGS_DONT_CARE))
// Values greater than 255 cannot be encoded in the immediate operand of CMP.
// Replace (x > max) with (x >= max + 1) where max + 1 (a power of 2) can be
// encoded. We could do this for all max values but on ARM32 "cmp r0, 255"
// is better than "cmp r0, 256" because it has a shorter encoding.
if (castMaxValue > 255)
{
emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
assert((castMaxValue == 32767) || (castMaxValue == 65535));
getEmitter()->emitIns_R_I(INS_cmp, EA_SIZE(desc.CheckSrcSize()), reg, castMaxValue + 1);
genJumpToThrowHlpBlk((castMinValue == 0) ? EJ_hs : EJ_ge, SCK_OVERFLOW);
}
else
{
noway_assert(tmpReg != REG_NA);
instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMask);
emit->emitIns_R_R(INS_tst, cmpSize, sourceReg, tmpReg);
getEmitter()->emitIns_R_I(INS_cmp, EA_SIZE(desc.CheckSrcSize()), reg, castMaxValue);
genJumpToThrowHlpBlk((castMinValue == 0) ? EJ_hi : EJ_gt, SCK_OVERFLOW);
}
#elif defined(_TARGET_ARM64_)
emit->emitIns_R_I(INS_tst, cmpSize, sourceReg, castInfo.typeMask);
#endif // _TARGET_ARM*
emitJumpKind jmpNotEqual = genJumpKindForOper(GT_NE, CK_SIGNED);
genJumpToThrowHlpBlk(jmpNotEqual, SCK_OVERFLOW);
}
else
{
// For a narrowing signed cast
//
// We must check the value is in a signed range.

// Compare with the MAX

noway_assert((castInfo.typeMin != 0) && (castInfo.typeMax != 0));

#if defined(_TARGET_ARM_)
if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, INS_FLAGS_DONT_CARE))
#elif defined(_TARGET_ARM64_)
if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMax, cmpSize))
#endif // _TARGET_*
{
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMax);
}
else
if (castMinValue != 0)
{
noway_assert(tmpReg != REG_NA);
instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMax);
emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
getEmitter()->emitIns_R_I(INS_cmp, EA_SIZE(desc.CheckSrcSize()), reg, castMinValue);
genJumpToThrowHlpBlk(EJ_lt, SCK_OVERFLOW);
}
}
break;
}
}

emitJumpKind jmpGT = genJumpKindForOper(GT_GT, CK_SIGNED);
genJumpToThrowHlpBlk(jmpGT, SCK_OVERFLOW);
//------------------------------------------------------------------------
// genIntToIntCast: Generate code for an integer cast, with or without overflow check.
//
// Arguments:
// cast - The GT_CAST node
//
// Assumptions:
// The cast node is not a contained node and must have an assigned register.
// Neither the source nor target type can be a floating point type.
//
// TODO-ARM64-CQ: Allow castOp to be a contained node without an assigned register.
//
void CodeGen::genIntToIntCast(GenTreeCast* cast)
{
genConsumeRegs(cast->gtGetOp1());

// Compare with the MIN
const regNumber srcReg = cast->gtGetOp1()->gtRegNum;
const regNumber dstReg = cast->gtRegNum;

#if defined(_TARGET_ARM_)
if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, INS_FLAGS_DONT_CARE))
#elif defined(_TARGET_ARM64_)
if (emitter::emitIns_valid_imm_for_cmp(castInfo.typeMin, cmpSize))
#endif // _TARGET_*
{
emit->emitIns_R_I(INS_cmp, cmpSize, sourceReg, castInfo.typeMin);
}
else
{
noway_assert(tmpReg != REG_NA);
instGen_Set_Reg_To_Imm(cmpSize, tmpReg, castInfo.typeMin);
emit->emitIns_R_R(INS_cmp, cmpSize, sourceReg, tmpReg);
}
assert(genIsValidIntReg(srcReg));
assert(genIsValidIntReg(dstReg));

emitJumpKind jmpLT = genJumpKindForOper(GT_LT, CK_SIGNED);
genJumpToThrowHlpBlk(jmpLT, SCK_OVERFLOW);
}
GenIntCastDesc desc(cast);

if (movRequired)
{
emit->emitIns_R_R(INS_mov, movSize, targetReg, sourceReg);
}
if (desc.CheckKind() != GenIntCastDesc::CHECK_NONE)
{
genIntCastOverflowCheck(cast, desc, srcReg);
}
else // Non-overflow checking cast.

if ((desc.ExtendKind() != GenIntCastDesc::COPY) || (srcReg != dstReg))
{
const unsigned srcSize = genTypeSize(srcType);
const unsigned dstSize = genTypeSize(dstType);
instruction ins;
emitAttr insSize;
instruction ins;
unsigned insSize;

if (dstSize < 4)
switch (desc.ExtendKind())
{
// Casting to a small type really means widening from that small type to INT/LONG.
ins = ins_Move_Extend(dstType, true);
insSize = emitActualTypeSize(treeNode->TypeGet());
}
case GenIntCastDesc::ZERO_EXTEND_SMALL_INT:
ins = (desc.ExtendSrcSize() == 1) ? INS_uxtb : INS_uxth;
insSize = 4;
break;
case GenIntCastDesc::SIGN_EXTEND_SMALL_INT:
ins = (desc.ExtendSrcSize() == 1) ? INS_sxtb : INS_sxth;
insSize = 4;
break;
#ifdef _TARGET_64BIT_
// dstType cannot be a long type on 32 bit targets, such casts should have been decomposed.
// srcType cannot be a small type since it's the "actual type" of the cast operand.
// This means that widening casts do not occur on 32 bit targets.
else if (dstSize > srcSize)
{
// (U)INT to (U)LONG widening cast
assert((srcSize == 4) && (dstSize == 8));
// Make sure the node type has the same size as the destination type.
assert(genTypeSize(treeNode->TypeGet()) == dstSize);

ins = treeNode->IsUnsigned() ? INS_mov : INS_sxtw;
// SXTW requires EA_8BYTE but MOV requires EA_4BYTE in order to zero out the upper 32 bits.
insSize = (ins == INS_sxtw) ? EA_8BYTE : EA_4BYTE;
}
case GenIntCastDesc::ZERO_EXTEND_INT:
ins = INS_mov;
insSize = 4;
break;
case GenIntCastDesc::SIGN_EXTEND_INT:
ins = INS_sxtw;
insSize = 8;
break;
#endif
else
{
// Sign changing cast or narrowing cast
assert(dstSize <= srcSize);
// Note that narrowing casts are possible only on 64 bit targets.
assert(srcSize <= genTypeSize(TYP_I_IMPL));
// Make sure the node type has the same size as the destination type.
assert(genTypeSize(treeNode->TypeGet()) == dstSize);

// This cast basically does nothing, even when narrowing it is the job of the
// consumer of this node to use the appropiate register size (32 or 64 bit)
// and not rely on the cast to set the upper 32 bits in a certain manner.
// Still, we will need to generate a MOV instruction if the source and target
// registers are different.
ins = (sourceReg != targetReg) ? INS_mov : INS_none;
insSize = EA_SIZE(dstSize);
default:
assert(desc.ExtendKind() == GenIntCastDesc::COPY);
ins = INS_mov;
insSize = desc.ExtendSrcSize();
break;
}

if (ins != INS_none)
{
emit->emitIns_R_R(ins, insSize, targetReg, sourceReg);
}
getEmitter()->emitIns_R_R(ins, EA_ATTR(insSize), dstReg, srcReg);
}

genProduceReg(treeNode);
genProduceReg(cast);
}

//------------------------------------------------------------------------
Expand Down
Loading