Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Conditional nodes and Arm64 code generation #71616

Merged
merged 1 commit into from
Jul 5, 2022
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
9 changes: 9 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1042,6 +1042,9 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genIntToFloatCast(GenTree* treeNode);
void genCkfinite(GenTree* treeNode);
void genCodeForCompare(GenTreeOp* tree);
#ifdef TARGET_ARM64
void genCodeForConditional(GenTreeConditional* tree);
#endif
void genIntrinsic(GenTree* treeNode);
void genPutArgStk(GenTreePutArgStk* treeNode);
void genPutArgReg(GenTreeOp* tree);
Expand Down Expand Up @@ -1704,6 +1707,12 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
instruction genMapShiftInsToShiftByConstantIns(instruction ins, int shiftByValue);
#endif // TARGET_XARCH

#ifdef TARGET_ARM64
static insCond InsCondForCompareOp(GenTree* tree);
static insCond InvertInsCond(insCond cond);
static insCflags InsCflagsForCcmp(insCond cond);
#endif

#ifndef TARGET_LOONGARCH64
// Maps a GenCondition code to a sequence of conditional jumps or other conditional instructions
// such as X86's SETcc. A sequence of instructions rather than just a single one is required for
Expand Down
173 changes: 173 additions & 0 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4411,6 +4411,60 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree)
}
}

//------------------------------------------------------------------------
// genCodeForCompare: Produce code for a GT_CEQ/GT_CNE node.
//
// Arguments:
// tree - the node
//
void CodeGen::genCodeForConditional(GenTreeConditional* tree)
{
emitter* emit = GetEmitter();

GenTree* opcond = tree->gtCond;
GenTree* op1 = tree->gtOp1;
GenTree* op2 = tree->gtOp2;
var_types op1Type = genActualType(op1->TypeGet());
var_types op2Type = genActualType(op2->TypeGet());
emitAttr cmpSize = EA_ATTR(genTypeSize(op1Type));
insCond cond = InsCondForCompareOp(opcond);

assert(!op1->isUsedFromMemory());
assert(genTypeSize(op1Type) == genTypeSize(op2Type));

regNumber targetReg = tree->GetRegNum();
regNumber srcReg1 = genConsumeReg(op1);

if (tree->OperIs(GT_SELECT))
{
regNumber srcReg2 = genConsumeReg(op2);
emit->emitIns_R_R_R_COND(INS_csel, cmpSize, targetReg, srcReg1, srcReg2, cond);
regSet.verifyRegUsed(targetReg);
}
else
{
assert(!varTypeIsFloating(op2Type));
// We don't support swapping op1 and op2 to generate cmp reg, imm.
assert(!op1->isContainedIntOrIImmed());
// This should not be generating into a register.
assert(targetReg == REG_NA);

// For the ccmp flags, get the condition of the compare.
insCflags cflags = InsCflagsForCcmp(InsCondForCompareOp(tree));

if (op2->isContainedIntOrIImmed())
{
GenTreeIntConCommon* intConst = op2->AsIntConCommon();
emit->emitIns_R_I_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, (int)intConst->IconValue(), cflags, cond);
}
else
{
regNumber srcReg2 = genConsumeReg(op2);
emit->emitIns_R_R_FLAGS_COND(INS_ccmp, cmpSize, srcReg1, srcReg2, cflags, cond);
}
}
}

//------------------------------------------------------------------------
// genCodeForJumpCompare: Generates code for jmpCompare statement.
//
Expand Down Expand Up @@ -10381,4 +10435,123 @@ void CodeGen::genCodeForCond(GenTreeOp* tree)
genProduceReg(tree);
}

//------------------------------------------------------------------------
// InsCondForCompareOp: Map the condition in a Compare/Conditional op to a insCond.
//
// Arguments:
// tree - the node
//
insCond CodeGen::InsCondForCompareOp(GenTree* tree)
{
assert(tree->OperIsCompare() || tree->OperIsConditionalCompare());

if (tree->OperIsCompare())
{
switch (tree->AsOp()->OperGet())
{
case GT_EQ:
case GT_TEST_EQ:
return INS_COND_EQ;
case GT_NE:
case GT_TEST_NE:
return INS_COND_NE;
case GT_GE:
return INS_COND_GE;
case GT_GT:
return INS_COND_GT;
case GT_LT:
return INS_COND_LT;
case GT_LE:
return INS_COND_LE;
default:
assert(false && "Invalid condition");
return INS_COND_EQ;
}
}
else
{
switch (tree->AsConditional()->OperGet())
{
case GT_CEQ:
return INS_COND_EQ;
case GT_CNE:
return INS_COND_NE;
case GT_CGE:
return INS_COND_GE;
case GT_CGT:
return INS_COND_GT;
case GT_CLT:
return INS_COND_LT;
case GT_CLE:
return INS_COND_LE;
default:
assert(false && "Invalid condition");
return INS_COND_EQ;
}
}
}

//------------------------------------------------------------------------
// InvertInsCond: Invert an insCond
//
// Arguments:
// cond - the insCond.
//
insCond CodeGen::InvertInsCond(insCond cond)
{
switch (cond)
{
case INS_COND_EQ:
return INS_COND_NE;
case INS_COND_NE:
return INS_COND_EQ;
case INS_COND_GE:
return INS_COND_LT;
case INS_COND_GT:
return INS_COND_LE;
case INS_COND_LT:
return INS_COND_GE;
case INS_COND_LE:
return INS_COND_GT;
default:
assert(false && "Invalid condition");
return INS_COND_EQ;
}
}

//------------------------------------------------------------------------
// InsCflagsForCcmp: Get the Cflags for a required for a CCMP instruction.
//
// Consider:
// cmp w, x
// ccmp y, z, A, COND
// This is: compare w and x, if this matches condition COND, then compare y and z.
// Otherwise set flags to A - this should match the case where cmp failed.
// Given COND, this function returns A.
//
// Arguments:
// cond - the insCond.
//
insCflags CodeGen::InsCflagsForCcmp(insCond cond)
{
switch (InvertInsCond(cond))
{
case INS_COND_EQ:
return INS_FLAGS_Z;
case INS_COND_NE:
return INS_FLAGS_NONE;
case INS_COND_GE:
return INS_FLAGS_Z;
case INS_COND_GT:
return INS_FLAGS_NONE;
case INS_COND_LT:
return INS_FLAGS_NC;
case INS_COND_LE:
return INS_FLAGS_NZC;
default:
assert(false && "Invalid condition");
return INS_FLAGS_NONE;
}
}

#endif // TARGET_ARM64
12 changes: 12 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,18 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
genCodeForCompare(treeNode->AsOp());
break;

#ifdef TARGET_ARM64
case GT_SELECT:
case GT_CEQ:
case GT_CNE:
case GT_CLT:
case GT_CLE:
case GT_CGE:
case GT_CGT:
genCodeForConditional(treeNode->AsConditional());
break;
#endif

case GT_JTRUE:
genCodeForJumpTrue(treeNode->AsOp());
break;
Expand Down
11 changes: 10 additions & 1 deletion src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2628,7 +2628,16 @@ void CodeGen::genCodeForJumpTrue(GenTreeOp* jtrue)
assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
assert(jtrue->OperIs(GT_JTRUE));

GenTreeOp* relop = jtrue->gtGetOp1()->AsOp();
GenTreeOp* relop;
if (jtrue->gtGetOp1()->OperIsCompare())
{
relop = jtrue->gtGetOp1()->AsOp();
}
else
{
assert(jtrue->gtGetOp1()->OperIsConditionalCompare());
relop = jtrue->gtGetOp1()->AsConditional();
}
GenCondition condition = GenCondition::FromRelop(relop);

if (condition.PreferSwap())
Expand Down
12 changes: 9 additions & 3 deletions src/coreclr/jit/emitarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2365,6 +2365,12 @@ emitter::code_t emitter::emitInsCode(instruction ins, insFormat fmt)
return false; // not encodable
}

// true if this 'imm' can be encoded as a input operand to a ccmp instruction
/*static*/ bool emitter::emitIns_valid_imm_for_ccmp(INT64 imm)
{
return ((imm & 0x01f) == imm);
}

// true if 'imm' can be encoded as an offset in a ldp/stp instruction
/*static*/ bool emitter::canEncodeLoadOrStorePairOffset(INT64 imm, emitAttr attr)
{
Expand Down Expand Up @@ -7437,7 +7443,7 @@ void emitter::emitIns_R_I_FLAGS_COND(
ins = insReverse(ins);
imm = -imm;
}
if ((imm >= 0) && (imm <= 31))
if (isValidUimm5(imm))
{
cfi.imm5 = imm;
cfi.flags = flags;
Expand Down Expand Up @@ -12980,7 +12986,7 @@ void emitter::emitDispInsHelp(
cfi.immCFVal = (unsigned)emitGetInsSC(id);
emitDispImm(cfi.imm5, true);
emitDispFlags(cfi.flags);
printf(",");
printf(", ");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It gives us: ccmp w1, #10, z, lt
instead of ccmp w1, #10, z,lt

emitDispCond(cfi.cond);
break;

Expand Down Expand Up @@ -13050,7 +13056,7 @@ void emitter::emitDispInsHelp(
emitDispReg(id->idReg2(), size, true);
cfi.immCFVal = (unsigned)emitGetInsSC(id);
emitDispFlags(cfi.flags);
printf(",");
printf(", ");
emitDispCond(cfi.cond);
break;

Expand Down
9 changes: 9 additions & 0 deletions src/coreclr/jit/emitarm64.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,12 @@ static bool isStackRegister(regNumber reg)
return (reg == REG_ZR) || (reg == REG_FP);
} // ZR (R31) encodes the SP register

// Returns true if 'value' is a legal unsigned immediate 5 bit encoding (such as for CCMP).
static bool isValidUimm5(ssize_t value)
{
return (0 <= value) && (value <= 0x1FLL);
};

// Returns true if 'value' is a legal unsigned immediate 8 bit encoding (such as for fMOV).
static bool isValidUimm8(ssize_t value)
{
Expand Down Expand Up @@ -489,6 +495,9 @@ static bool emitIns_valid_imm_for_alu(INT64 imm, emitAttr size);
// true if this 'imm' can be encoded as the offset in a ldr/str instruction
static bool emitIns_valid_imm_for_ldst_offset(INT64 imm, emitAttr size);

// true if this 'imm' can be encoded as a input operand to a ccmp instruction
static bool emitIns_valid_imm_for_ccmp(INT64 imm);

// true if 'imm' can be encoded as an offset in a ldp/stp instruction
static bool canEncodeLoadOrStorePairOffset(INT64 imm, emitAttr size);

Expand Down
Loading