diff options
author | Pat Gavlin <pgavlin@gmail.com> | 2017-06-05 14:06:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-05 14:06:45 -0700 |
commit | 8d33c81d18b010c36e071fa44a7bf9142efe286d (patch) | |
tree | 96ff72f552f7f32b3e2fa776fada3f83468cca57 | |
parent | 70deb0e97e0292b1ffaf0d216a9464ba0fe0db1d (diff) | |
parent | cc6286cc7f6d065ee823379a8afdfba25e603ed5 (diff) | |
download | coreclr-8d33c81d18b010c36e071fa44a7bf9142efe286d.tar.gz coreclr-8d33c81d18b010c36e071fa44a7bf9142efe286d.tar.bz2 coreclr-8d33c81d18b010c36e071fa44a7bf9142efe286d.zip |
Merge pull request #11884 from mikedn/arm-cmp-setcc
[WIP] Lower long compares that are materialized into a register for ARM32
-rw-r--r-- | src/jit/codegenarm.cpp | 218 | ||||
-rw-r--r-- | src/jit/codegenarmarch.cpp | 53 | ||||
-rw-r--r-- | src/jit/codegenlinear.h | 6 | ||||
-rw-r--r-- | src/jit/emitarm.cpp | 8 | ||||
-rw-r--r-- | src/jit/lower.cpp | 11 | ||||
-rw-r--r-- | src/jit/lowerarmarch.cpp | 1 | ||||
-rw-r--r-- | src/jit/lsraarm.cpp | 2 | ||||
-rw-r--r-- | src/jit/lsraarmarch.cpp | 23 |
8 files changed, 90 insertions, 232 deletions
diff --git a/src/jit/codegenarm.cpp b/src/jit/codegenarm.cpp index 124e99448b..438453b41a 100644 --- a/src/jit/codegenarm.cpp +++ b/src/jit/codegenarm.cpp @@ -1273,7 +1273,7 @@ void CodeGen::genCkfinite(GenTreePtr treeNode) } //------------------------------------------------------------------------ -// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT node. +// genCodeForCompare: Produce code for a GT_EQ/GT_NE/GT_LT/GT_LE/GT_GE/GT_GT/GT_CMP node. // // Arguments: // tree - the node @@ -1289,51 +1289,35 @@ void CodeGen::genCodeForCompare(GenTreeOp* tree) var_types op1Type = op1->TypeGet(); var_types op2Type = op2->TypeGet(); - if (varTypeIsLong(op1Type)) - { -#ifdef DEBUG - // The result of an unlowered long compare on a 32-bit target must either be - // a) materialized into a register, or - // b) unused. - // - // A long compare that has a result that is used but not materialized into a register should - // have been handled by Lowering::LowerCompare. + assert(!varTypeIsLong(op1Type)); + assert(!varTypeIsLong(op2Type)); - LIR::Use use; - assert((tree->gtRegNum != REG_NA) || !LIR::AsRange(compiler->compCurBB).TryGetUse(tree, &use)); -#endif - genCompareLong(tree); + regNumber targetReg = tree->gtRegNum; + emitter* emit = getEmitter(); + + genConsumeIfReg(op1); + genConsumeIfReg(op2); + + if (varTypeIsFloating(op1Type)) + { + assert(op1Type == op2Type); + assert(!tree->OperIs(GT_CMP)); + emit->emitInsBinary(INS_vcmp, emitTypeSize(op1Type), op1, op2); + // vmrs with register 0xf has special meaning of transferring flags + emit->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15); } else { - assert(!varTypeIsLong(op2Type)); - - regNumber targetReg = tree->gtRegNum; - emitter* emit = getEmitter(); - - genConsumeIfReg(op1); - genConsumeIfReg(op2); - - if (varTypeIsFloating(op1Type)) - { - assert(op1Type == op2Type); - emit->emitInsBinary(INS_vcmp, emitTypeSize(op1Type), op1, op2); - // vmrs with register 0xf has special meaning of transferring flags - emit->emitIns_R(INS_vmrs, EA_4BYTE, REG_R15); - } - else - { - assert(!varTypeIsFloating(op2Type)); - var_types cmpType = (op1Type == op2Type) ? op1Type : TYP_INT; - emit->emitInsBinary(INS_cmp, emitTypeSize(cmpType), op1, op2); - } + assert(!varTypeIsFloating(op2Type)); + var_types cmpType = (op1Type == op2Type) ? op1Type : TYP_INT; + emit->emitInsBinary(INS_cmp, emitTypeSize(cmpType), op1, op2); + } - // Are we evaluating this into a register? - if (targetReg != REG_NA) - { - genSetRegToCond(targetReg, tree); - genProduceReg(tree); - } + // Are we evaluating this into a register? + if (targetReg != REG_NA) + { + genSetRegToCond(targetReg, tree); + genProduceReg(tree); } } @@ -1430,158 +1414,6 @@ void CodeGen::genCodeForStoreInd(GenTreeStoreInd* tree) } //------------------------------------------------------------------------ -// genCompareLong: Generate code for comparing two longs when the result of the compare -// is manifested in a register. -// -// Arguments: -// treeNode - the compare tree -// -// Return Value: -// None. -// -// Comments: -// For long compares, we need to compare the high parts of operands first, then the low parts. -// If the high compare is false, we do not need to compare the low parts. For less than and -// greater than, if the high compare is true, we can assume the entire compare is true. -// -void CodeGen::genCompareLong(GenTreePtr treeNode) -{ - assert(treeNode->OperIsCompare()); - - GenTreeOp* tree = treeNode->AsOp(); - GenTreePtr op1 = tree->gtOp1; - GenTreePtr op2 = tree->gtOp2; - - assert(varTypeIsLong(op1->TypeGet())); - assert(varTypeIsLong(op2->TypeGet())); - - regNumber targetReg = treeNode->gtRegNum; - - genConsumeOperands(tree); - - GenTreePtr loOp1 = op1->gtGetOp1(); - GenTreePtr hiOp1 = op1->gtGetOp2(); - GenTreePtr loOp2 = op2->gtGetOp1(); - GenTreePtr hiOp2 = op2->gtGetOp2(); - - // Create compare for the high parts - instruction ins = INS_cmp; - var_types cmpType = TYP_INT; - emitAttr cmpAttr = emitTypeSize(cmpType); - - // Emit the compare instruction - getEmitter()->emitInsBinary(ins, cmpAttr, hiOp1, hiOp2); - - // If the result is not being materialized in a register, we're done. - if (targetReg == REG_NA) - { - return; - } - - BasicBlock* labelTrue = genCreateTempLabel(); - BasicBlock* labelFalse = genCreateTempLabel(); - BasicBlock* labelNext = genCreateTempLabel(); - - genJccLongHi(tree->gtOper, labelTrue, labelFalse, tree->IsUnsigned()); - getEmitter()->emitInsBinary(ins, cmpAttr, loOp1, loOp2); - genJccLongLo(tree->gtOper, labelTrue, labelFalse); - - genDefineTempLabel(labelFalse); - getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 0); - getEmitter()->emitIns_J(INS_b, labelNext); - - genDefineTempLabel(labelTrue); - getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(tree->gtType), tree->gtRegNum, 1); - - genDefineTempLabel(labelNext); - - genProduceReg(tree); -} - -void CodeGen::genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned) -{ - if (cmp != GT_NE) - { - jumpFalse->bbFlags |= BBF_JMP_TARGET | BBF_HAS_LABEL; - } - - switch (cmp) - { - case GT_EQ: - inst_JMP(EJ_ne, jumpFalse); - break; - - case GT_NE: - inst_JMP(EJ_ne, jumpTrue); - break; - - case GT_LT: - case GT_LE: - if (isUnsigned) - { - inst_JMP(EJ_hi, jumpFalse); - inst_JMP(EJ_lo, jumpTrue); - } - else - { - inst_JMP(EJ_gt, jumpFalse); - inst_JMP(EJ_lt, jumpTrue); - } - break; - - case GT_GE: - case GT_GT: - if (isUnsigned) - { - inst_JMP(EJ_lo, jumpFalse); - inst_JMP(EJ_hi, jumpTrue); - } - else - { - inst_JMP(EJ_lt, jumpFalse); - inst_JMP(EJ_gt, jumpTrue); - } - break; - - default: - noway_assert(!"expected a comparison operator"); - } -} - -void CodeGen::genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse) -{ - switch (cmp) - { - case GT_EQ: - inst_JMP(EJ_eq, jumpTrue); - break; - - case GT_NE: - inst_JMP(EJ_ne, jumpTrue); - break; - - case GT_LT: - inst_JMP(EJ_lo, jumpTrue); - break; - - case GT_LE: - inst_JMP(EJ_ls, jumpTrue); - break; - - case GT_GE: - inst_JMP(EJ_hs, jumpTrue); - break; - - case GT_GT: - inst_JMP(EJ_hi, jumpTrue); - break; - - default: - noway_assert(!"expected comparison"); - } -} - -//------------------------------------------------------------------------ // genSetRegToCond: Generate code to materialize a condition into a register. // // Arguments: diff --git a/src/jit/codegenarmarch.cpp b/src/jit/codegenarmarch.cpp index 37b9026db1..cb3a1d5cb7 100644 --- a/src/jit/codegenarmarch.cpp +++ b/src/jit/codegenarmarch.cpp @@ -230,6 +230,7 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode) case GT_LE: case GT_GE: case GT_GT: + case GT_CMP: genCodeForCompare(treeNode->AsOp()); break; @@ -243,6 +244,10 @@ void CodeGen::genCodeForTreeNode(GenTreePtr treeNode) genCodeForJcc(treeNode->AsCC()); break; + case GT_SETCC: + genCodeForSetcc(treeNode->AsCC()); + break; + #endif // _TARGET_ARM_ case GT_RETURNTRAP: @@ -2597,6 +2602,54 @@ void CodeGen::genCodeForJcc(GenTreeCC* tree) inst_JMP(jumpKind, compiler->compCurBB->bbJumpDest); } +//------------------------------------------------------------------------ +// genCodeForSetcc: Generates code for a GT_SETCC node. +// +// Arguments: +// setcc - the GT_SETCC node +// +// Assumptions: +// The condition represents an integer comparison. This code doesn't +// have the necessary logic to deal with floating point comparisons, +// in fact it doesn't even know if the comparison is integer or floating +// point because SETCC nodes do not have any operands. +// + +void CodeGen::genCodeForSetcc(GenTreeCC* setcc) +{ + regNumber dstReg = setcc->gtRegNum; + CompareKind compareKind = setcc->IsUnsigned() ? CK_UNSIGNED : CK_SIGNED; + emitJumpKind jumpKind = genJumpKindForOper(setcc->gtCondition, compareKind); + + assert(genIsValidIntReg(dstReg)); + // Make sure nobody is setting GTF_RELOP_NAN_UN on this node as it is ignored. + assert((setcc->gtFlags & GTF_RELOP_NAN_UN) == 0); + + // Emit code like that: + // ... + // bgt True + // movs rD, #0 + // b Next + // True: + // movs rD, #1 + // Next: + // ... + + BasicBlock* labelTrue = genCreateTempLabel(); + getEmitter()->emitIns_J(emitter::emitJumpKindToIns(jumpKind), labelTrue); + + getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(setcc->TypeGet()), dstReg, 0); + + BasicBlock* labelNext = genCreateTempLabel(); + getEmitter()->emitIns_J(INS_b, labelNext); + + genDefineTempLabel(labelTrue); + getEmitter()->emitIns_R_I(INS_mov, emitActualTypeSize(setcc->TypeGet()), dstReg, 1); + genDefineTempLabel(labelNext); + + genProduceReg(setcc); +} + #endif // defined(_TARGET_ARM_) //------------------------------------------------------------------------ diff --git a/src/jit/codegenlinear.h b/src/jit/codegenlinear.h index 59763bc5c8..54c6db1032 100644 --- a/src/jit/codegenlinear.h +++ b/src/jit/codegenlinear.h @@ -53,12 +53,6 @@ unsigned getFirstArgWithStackSlot(); void genCompareFloat(GenTreePtr treeNode); void genCompareInt(GenTreePtr treeNode); -#if defined(_TARGET_ARM_) -void genCompareLong(GenTreePtr treeNode); -void genJccLongHi(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse, bool isUnsigned = false); -void genJccLongLo(genTreeOps cmp, BasicBlock* jumpTrue, BasicBlock* jumpFalse); -#endif // defined(_TARGET_ARM_) - #ifdef FEATURE_SIMD enum SIMDScalarMoveType { diff --git a/src/jit/emitarm.cpp b/src/jit/emitarm.cpp index 4f3f5e629a..72ad88337d 100644 --- a/src/jit/emitarm.cpp +++ b/src/jit/emitarm.cpp @@ -7787,6 +7787,14 @@ regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, assert(!"Invalid ins for overflow check"); } } + + if (dst->gtSetFlags()) + { + assert((ins == INS_add) || (ins == INS_adc) || (ins == INS_sub) || (ins == INS_sbc) || (ins == INS_and) || + (ins == INS_orr) || (ins == INS_eor) || (ins == INS_orn)); + flags = INS_FLAGS_SET; + } + if (intConst != nullptr) { emitIns_R_R_I(ins, attr, dst->gtRegNum, nonIntReg->gtRegNum, intConst->IconValue(), flags); diff --git a/src/jit/lower.cpp b/src/jit/lower.cpp index 6fc6646547..5c46d3d235 100644 --- a/src/jit/lower.cpp +++ b/src/jit/lower.cpp @@ -2027,18 +2027,8 @@ GenTree* Lowering::LowerTailCallViaHelper(GenTreeCall* call, GenTree* callTarget void Lowering::LowerCompare(GenTree* cmp) { #ifndef _TARGET_64BIT_ - -#ifdef _TARGET_ARM_ - // TODO-ARM: Later ARM32 should make use of same condition of x86 once support for GT_CMP and GT_SETCC is added. - LIR::Use cmpUse; - if ((cmp->gtGetOp1()->TypeGet() == TYP_LONG) && BlockRange().TryGetUse(cmp, &cmpUse) && - cmpUse.User()->OperIs(GT_JTRUE)) -#elif defined(_TARGET_X86_) if (cmp->gtGetOp1()->TypeGet() == TYP_LONG) -#endif { -// TODO-ARM: This code should be enabled for ARM32 once support for GT_CMP and GT_SETCC is added. -#if _TARGET_X86_ // Currently this handles only relops that produce a value or aren't used. // The same approach can be used for relops that feed a GT_JTRUE, see the #if 0 // below and its associated comment. @@ -2167,7 +2157,6 @@ void Lowering::LowerCompare(GenTree* cmp) return; } -#endif // For 32-bit targets any comparison that feeds a `GT_JTRUE` node must be lowered such that // the liveness of the operands to the comparison is properly visible to the rest of the diff --git a/src/jit/lowerarmarch.cpp b/src/jit/lowerarmarch.cpp index daaedc4cf8..ae29cf21d5 100644 --- a/src/jit/lowerarmarch.cpp +++ b/src/jit/lowerarmarch.cpp @@ -132,6 +132,7 @@ bool Lowering::IsContainableImmed(GenTree* parentNode, GenTree* childNode) case GT_LE: case GT_GE: case GT_GT: + case GT_CMP: case GT_AND: case GT_OR: case GT_XOR: diff --git a/src/jit/lsraarm.cpp b/src/jit/lsraarm.cpp index 0e033cb661..4fade03ef4 100644 --- a/src/jit/lsraarm.cpp +++ b/src/jit/lsraarm.cpp @@ -639,6 +639,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree) case GT_LE: case GT_GE: case GT_GT: + case GT_CMP: TreeNodeInfoInitCmp(tree); break; @@ -739,6 +740,7 @@ void Lowering::TreeNodeInfoInit(GenTree* tree) case GT_LABEL: case GT_PINVOKE_PROLOG: case GT_JCC: + case GT_SETCC: case GT_MEMORYBARRIER: case GT_OBJ: info->dstCount = tree->IsValue() ? 1 : 0; diff --git a/src/jit/lsraarmarch.cpp b/src/jit/lsraarmarch.cpp index d33eb30f8c..e8620475fa 100644 --- a/src/jit/lsraarmarch.cpp +++ b/src/jit/lsraarmarch.cpp @@ -79,28 +79,7 @@ void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree) TreeNodeInfo* info = &(tree->gtLsraInfo); info->srcCount = 2; - info->dstCount = 1; - -#ifdef _TARGET_ARM_ - - GenTreePtr op1 = tree->gtOp.gtOp1; - GenTreePtr op2 = tree->gtOp.gtOp2; - var_types op1Type = op1->TypeGet(); - var_types op2Type = op2->TypeGet(); - - // Long compares will consume GT_LONG nodes, each of which produces two results. - // Thus for each long operand there will be an additional source. - // TODO-ARM-CQ: Mark hiOp2 and loOp2 as contained if it is a constant. - if (varTypeIsLong(op1Type)) - { - info->srcCount++; - } - if (varTypeIsLong(op2Type)) - { - info->srcCount++; - } - -#endif // _TARGET_ARM_ + info->dstCount = tree->OperIs(GT_CMP) ? 0 : 1; CheckImmedAndMakeContained(tree, tree->gtOp.gtOp2); } |