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

JIT: Model CPU flag dependencies explicitly in LIR #82355

Closed
Closed
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
7 changes: 3 additions & 4 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,6 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void genCompareInt(GenTree* treeNode);
#ifdef TARGET_XARCH
bool genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opType);
GenTreeCC* genTryFindFlagsConsumer(GenTree* flagsProducer);
#endif

#ifdef FEATURE_SIMD
Expand Down Expand Up @@ -1202,7 +1201,7 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
#if defined(TARGET_ARM64)
void genCodeForJumpCompare(GenTreeOp* tree);
void genCodeForBfiz(GenTreeOp* tree);
void genCodeForCond(GenTreeOp* tree);
void genCodeForCond(GenTreeOpFlagsCC* tree);
#endif // TARGET_ARM64

#if defined(FEATURE_EH_FUNCLETS)
Expand Down Expand Up @@ -1586,8 +1585,8 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
void inst_JCC(GenCondition condition, BasicBlock* target);
void inst_SETCC(GenCondition condition, var_types type, regNumber dstReg);

void genCodeForJcc(GenTreeCC* tree);
void genCodeForSetcc(GenTreeCC* setcc);
void genCodeForJcc(GenTreeFlagsCC* tree);
void genCodeForSetcc(GenTreeFlagsCC* setcc);
#endif // !TARGET_LOONGARCH64
};

Expand Down
25 changes: 13 additions & 12 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2529,7 +2529,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
assert(varTypeIsIntegral(tree));

// These operations cannot set flags
assert((tree->gtFlags & GTF_SET_FLAGS) == 0);
assert(!tree->ProducesFlags());

GenTree* a = op1;
GenTree* b = op2->gtGetOp1();
Expand Down Expand Up @@ -2576,7 +2576,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
instruction ins = genGetInsForOper(tree->OperGet(), targetType);
insOpts opt = INS_OPTS_NONE;

if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
if (tree->ProducesFlags())
{
// A subset of operations can still set flags

Expand Down Expand Up @@ -2649,7 +2649,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)
instruction ins = genGetInsForOper(tree->OperGet(), targetType);
insOpts opt = INS_OPTS_NONE;

if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
if (tree->ProducesFlags())
{
// A subset of operations can still set flags

Expand Down Expand Up @@ -2727,7 +2727,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* tree)

instruction ins = genGetInsForOper(tree->OperGet(), targetType);

if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
if (tree->ProducesFlags())
{
switch (oper)
{
Expand Down Expand Up @@ -3378,7 +3378,7 @@ void CodeGen::genCodeForNegNot(GenTree* tree)
regNumber targetReg = tree->GetRegNum();
instruction ins = genGetInsForOper(tree->OperGet(), targetType);

if ((tree->gtFlags & GTF_SET_FLAGS) != 0)
if (tree->ProducesFlags())
{
switch (tree->OperGet())
{
Expand Down Expand Up @@ -10355,18 +10355,19 @@ void CodeGen::genCodeForBfiz(GenTreeOp* tree)
// Arguments:
// tree - conditional op
//
void CodeGen::genCodeForCond(GenTreeOp* tree)
void CodeGen::genCodeForCond(GenTreeOpFlagsCC* tree)
{
assert(tree->OperIs(GT_CSNEG_MI, GT_CNEG_LT));
assert(!(tree->gtFlags & GTF_SET_FLAGS));
assert(tree->OperIs(GT_CSNEG, GT_CNEG));
assert(!tree->ProducesFlags());
genConsumeOperands(tree);

insCond cond = JumpKindToInsCond(GenConditionDesc::Get(tree->gtCondition).jumpKind1);

switch (tree->OperGet())
{
case GT_CSNEG_MI:
case GT_CSNEG:
{
instruction ins = INS_csneg;
insCond cond = INS_COND_MI;
instruction ins = INS_csneg;

regNumber dstReg = tree->GetRegNum();
regNumber op1Reg = tree->gtGetOp1()->GetRegNum();
Expand All @@ -10376,7 +10377,7 @@ void CodeGen::genCodeForCond(GenTreeOp* tree)
break;
}

case GT_CNEG_LT:
case GT_CNEG:
{
instruction ins = INS_cneg;
insCond cond = INS_COND_LT;
Expand Down
10 changes: 5 additions & 5 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
genCodeForBfiz(treeNode->AsOp());
break;

case GT_CSNEG_MI:
case GT_CNEG_LT:
genCodeForCond(treeNode->AsOp());
case GT_CSNEG:
case GT_CNEG:
genCodeForCond(treeNode->AsOpFlagsCC());
break;
#endif // TARGET_ARM64

Expand Down Expand Up @@ -370,11 +370,11 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
#endif // TARGET_ARM64

case GT_JCC:
genCodeForJcc(treeNode->AsCC());
genCodeForJcc(treeNode->AsFlagsCC());
break;

case GT_SETCC:
genCodeForSetcc(treeNode->AsCC());
genCodeForSetcc(treeNode->AsFlagsCC());
break;

case GT_RETURNTRAP:
Expand Down
4 changes: 2 additions & 2 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2595,7 +2595,7 @@ void CodeGen::genStoreLongLclVar(GenTree* treeNode)
// Arguments:
// jcc - The node
//
void CodeGen::genCodeForJcc(GenTreeCC* jcc)
void CodeGen::genCodeForJcc(GenTreeFlagsCC* jcc)
{
assert(compiler->compCurBB->bbJumpKind == BBJ_COND);
assert(jcc->OperIs(GT_JCC));
Expand Down Expand Up @@ -2638,7 +2638,7 @@ void CodeGen::inst_JCC(GenCondition condition, BasicBlock* target)
// Arguments:
// setcc - The node
//
void CodeGen::genCodeForSetcc(GenTreeCC* setcc)
void CodeGen::genCodeForSetcc(GenTreeFlagsCC* setcc)
{
assert(setcc->OperIs(GT_SETCC));

Expand Down
115 changes: 38 additions & 77 deletions src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
}
// now we know there are 3 different operands so attempt to use LEA
else if (oper == GT_ADD && !varTypeIsFloating(treeNode) && !treeNode->gtOverflowEx() // LEA does not set flags
&& (op2->isContainedIntOrIImmed() || op2->isUsedFromReg()) && !treeNode->gtSetFlags())
&& (op2->isContainedIntOrIImmed() || op2->isUsedFromReg()) && !treeNode->ProducesFlags())
{
if (op2->isContainedIntOrIImmed())
{
Expand Down Expand Up @@ -1001,7 +1001,7 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode)
// We can skip emitting 'and reg0, -1` if we know that the upper 32bits of 'reg0' are zero'ed.
if (compiler->opts.OptimizationEnabled())
{
if ((oper == GT_AND) && (targetType == TYP_INT) && !treeNode->gtSetFlags() && op2->IsIntegralConst(-1) &&
if ((oper == GT_AND) && (targetType == TYP_INT) && !treeNode->ProducesFlags() && op2->IsIntegralConst(-1) &&
emit->AreUpper32BitsZero(targetReg))
{
genProduceReg(treeNode);
Expand Down Expand Up @@ -1363,11 +1363,7 @@ instruction CodeGen::JumpKindToCmov(emitJumpKind condition)
//
void CodeGen::genCodeForSelect(GenTreeOp* select)
{
#ifdef TARGET_X86
assert(select->OperIs(GT_SELECT, GT_SELECT_HI));
#else
assert(select->OperIs(GT_SELECT));
#endif
assert(select->OperIs(GT_SELECT, GT_SELECTCC));

if (select->OperIs(GT_SELECT))
{
Expand All @@ -1385,25 +1381,13 @@ void CodeGen::genCodeForSelect(GenTreeOp* select)

if (select->OperIs(GT_SELECT))
{
GenTree* cond = select->AsConditional()->gtCond;
if (cond->isContained())
{
assert(cond->OperIsCompare());
genCodeForCompare(cond->AsOp());
cc = GenCondition::FromRelop(cond);

if (cc.PreferSwap())
{
// genCodeForCompare generated the compare with swapped
// operands because this swap requires fewer branches/cmovs.
cc = GenCondition::Swap(cc);
}
}
else
{
regNumber condReg = cond->GetRegNum();
GetEmitter()->emitIns_R_R(INS_test, EA_4BYTE, condReg, condReg);
}
GenTree* cond = select->AsConditional()->gtCond;
regNumber condReg = cond->GetRegNum();
GetEmitter()->emitIns_R_R(INS_test, emitActualTypeSize(cond), condReg, condReg);
}
else
{
cc = select->AsOpFlagsCC()->gtCondition;
}

// The usual codegen will be
Expand Down Expand Up @@ -1864,22 +1848,20 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
break;

case GT_JCC:
genCodeForJcc(treeNode->AsCC());
genCodeForJcc(treeNode->AsFlagsCC());
break;

case GT_SETCC:
genCodeForSetcc(treeNode->AsCC());
genCodeForSetcc(treeNode->AsFlagsCC());
break;

case GT_SELECT:
genCodeForSelect(treeNode->AsConditional());
break;

#ifdef TARGET_X86
case GT_SELECT_HI:
case GT_SELECTCC:
genCodeForSelect(treeNode->AsOp());
break;
#endif

case GT_RETURNTRAP:
genCodeForReturnTrap(treeNode->AsOp());
Expand Down Expand Up @@ -4552,7 +4534,7 @@ void CodeGen::genCodeForShift(GenTree* tree)
{
emitAttr size = emitTypeSize(tree);

bool mightOptimizeLsh = tree->OperIs(GT_LSH) && !tree->gtOverflowEx() && !tree->gtSetFlags();
bool mightOptimizeLsh = tree->OperIs(GT_LSH) && !tree->gtOverflowEx() && !tree->ProducesFlags();

// Optimize "X<<1" to "lea [reg+reg]" or "add reg, reg"
if (mightOptimizeLsh && shiftBy->IsIntegralConst(1))
Expand Down Expand Up @@ -6809,22 +6791,37 @@ bool CodeGen::genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opT
return false;
}

GenTreeCC* cc = nullptr;
GenCondition cond;
GenTree* consumer = nullptr;
GenCondition* mutableCond = nullptr;
GenCondition cond;

if (tree->OperIsCompare())
{
cond = GenCondition::FromIntegralRelop(tree);
}
else
{
cc = genTryFindFlagsConsumer(tree);
if (cc == nullptr)
LIR::Use flagsUse;
if (!LIR::AsRange(compiler->compCurBB).TryGetFlagsUse(tree, &flagsUse))
{
return false;
}

consumer = flagsUse.User();
if (consumer->OperIs(GT_SELECTCC))
{
mutableCond = &consumer->AsOpFlagsCC()->gtCondition;
}
else if (consumer->OperIs(GT_JCC, GT_SETCC))
{
mutableCond = &consumer->AsFlagsCC()->gtCondition;
}
else
{
return false;
}

cond = cc->gtCondition;
cond = *mutableCond;
}

if (GetEmitter()->AreFlagsSetToZeroCmp(op1->GetRegNum(), emitTypeSize(opType), cond))
Expand All @@ -6833,55 +6830,19 @@ bool CodeGen::genCanAvoidEmittingCompareAgainstZero(GenTree* tree, var_types opT
return true;
}

if ((cc != nullptr) && GetEmitter()->AreFlagsSetForSignJumpOpt(op1->GetRegNum(), emitTypeSize(opType), cond))
if ((mutableCond != nullptr) &&
GetEmitter()->AreFlagsSetForSignJumpOpt(op1->GetRegNum(), emitTypeSize(opType), cond))
{
JITDUMP("Not emitting compare due to sign being already set; modifying [%06u] to check sign flag\n",
Compiler::dspTreeID(cc));
cc->gtCondition =
Compiler::dspTreeID(consumer));
*mutableCond =
(cond.GetCode() == GenCondition::SLT) ? GenCondition(GenCondition::S) : GenCondition(GenCondition::NS);
return true;
}

return false;
}

//------------------------------------------------------------------------
// genTryFindFlagsConsumer: Given a node that produces flags, try to look ahead
// for the node that consumes those flags.
//
// Parameters:
// producer - the node that produces CPU flags
//
// Returns:
// A node that consumes the flags, or nullptr if no such node was found.
//
GenTreeCC* CodeGen::genTryFindFlagsConsumer(GenTree* producer)
{
assert((producer->gtFlags & GTF_SET_FLAGS) != 0);
// We allow skipping some nodes where we know for sure that the flags are
// not consumed. In particular we handle resolution nodes. If we see any
// other node after the compare (which is an uncommon case, happens
// sometimes with decomposition) then we assume it could consume the flags.
for (GenTree* candidate = producer->gtNext; candidate != nullptr; candidate = candidate->gtNext)
{
if (candidate->OperIs(GT_JCC, GT_SETCC))
{
return candidate->AsCC();
}

// The following nodes can be inserted between the compare and the user
// of the flags by resolution. Codegen for these will never modify CPU
// flags.
if (!candidate->OperIs(GT_LCL_VAR, GT_COPY, GT_SWAP))
{
// For other nodes we do the conservative thing.
return nullptr;
}
}

return nullptr;
}

#if !defined(TARGET_64BIT)
//------------------------------------------------------------------------
// genLongToIntCast: Generate code for long to int casts on x86.
Expand Down
9 changes: 0 additions & 9 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9842,15 +9842,6 @@ void cTreeFlags(Compiler* comp, GenTree* tree)
{
chars += printf("[SPILLED_OPER]");
}
#if FEATURE_SET_FLAGS
if (tree->gtFlags & GTF_SET_FLAGS)
{
if ((op != GT_IND) && (op != GT_STOREIND))
{
chars += printf("[ZSF_SET_FLAGS]");
}
}
#endif
if (tree->gtFlags & GTF_IND_NONFAULTING)
{
if (tree->OperIsIndirOrArrMetaData())
Expand Down
5 changes: 5 additions & 0 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -2414,6 +2414,11 @@ class Compiler
// For binary opers.
GenTree* gtNewOperNode(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2);

GenTreeOpFlags* gtNewOperFlags(genTreeOps oper, var_types type, GenTree* op1, GenTree* op2, GenTree* opFlags);
GenTreeFlagsCC* gtNewFlagsCC(genTreeOps oper, var_types type, GenTree* opFlags, GenCondition cond);
GenTreeOpFlagsCC* gtNewOperFlagsCC(
genTreeOps oper, var_types type, GenTree* op1, GenTree* op2, GenTree* opFlags, GenCondition cond);

GenTreeColon* gtNewColonNode(var_types type, GenTree* elseNode, GenTree* thenNode);
GenTreeQmark* gtNewQmarkNode(var_types type, GenTree* cond, GenTreeColon* colon);

Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4085,7 +4085,7 @@ void GenTree::VisitOperands(TVisitor visitor)

// Standard unary operators
#ifdef TARGET_ARM64
case GT_CNEG_LT:
case GT_CNEG:
#endif // TARGET_ARM64
case GT_STORE_LCL_VAR:
case GT_STORE_LCL_FLD:
Expand Down
Loading