diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index ef668b59cd780f..2a2c7a6b8656fd 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -742,6 +742,7 @@ class CodeGen final : public CodeGenInterface #endif void genCodeForTreeNode(GenTree* treeNode); void genCodeForBinary(GenTreeOp* treeNode); + bool genIsSameLocalVar(GenTree* tree1, GenTree* tree2); #if defined(TARGET_X86) void genCodeForLongUMod(GenTreeOp* node); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 3e2ba356790a42..d5d04802c56125 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -636,6 +636,25 @@ regMaskTP CodeGenInterface::genGetRegMask(GenTree* tree) return regMask; } +//------------------------------------------------------------------------ +// genIsSameLocalVar: +// Check if two trees represent the same scalar local value. +// +// Arguments: +// op1 - first tree +// op2 - second tree +// +// Returns: +// True if so. +// +bool CodeGen::genIsSameLocalVar(GenTree* op1, GenTree* op2) +{ + GenTree* op1Skip = op1->gtSkipReloadOrCopy(); + GenTree* op2Skip = op2->gtSkipReloadOrCopy(); + return op1Skip->OperIs(GT_LCL_VAR) && op2Skip->OperIs(GT_LCL_VAR) && + (op1Skip->AsLclVar()->GetLclNum() == op2Skip->AsLclVar()->GetLclNum()); +} + // The given lclVar is either going live (being born) or dying. // It might be both going live and dying (that is, it is a dead store) under MinOpts. // Update regSet.GetMaskVars() accordingly. diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 16d950bd7642ea..af4031d3520511 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -1143,28 +1143,11 @@ void CodeGen::genCodeForBinary(GenTreeOp* treeNode) // In order for this operation to be correct // we need that op is a commutative operation so // we can convert it into reg1 = reg1 op reg2 and emit - // the same code as above + // the same code as above. Or we need both operands to + // be the same local. else if (op2reg == targetReg) { - -#ifdef DEBUG - unsigned lclNum1 = (unsigned)-1; - unsigned lclNum2 = (unsigned)-2; - - GenTree* op1Skip = op1->gtSkipReloadOrCopy(); - GenTree* op2Skip = op2->gtSkipReloadOrCopy(); - - if (op1Skip->OperIsLocalRead()) - { - lclNum1 = op1Skip->AsLclVarCommon()->GetLclNum(); - } - if (op2Skip->OperIsLocalRead()) - { - lclNum2 = op2Skip->AsLclVarCommon()->GetLclNum(); - } - - assert(GenTree::OperIsCommutative(oper) || (lclNum1 == lclNum2)); -#endif + assert(GenTree::OperIsCommutative(oper) || genIsSameLocalVar(op1, op2)); dst = op2; src = op1; diff --git a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp index a39224dcb338c1..f04be0c243870d 100644 --- a/src/coreclr/jit/hwintrinsiccodegenarm64.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenarm64.cpp @@ -368,8 +368,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { if (isRMW) { - assert((targetReg == op1Reg) || (targetReg != op2Reg)); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); HWIntrinsicImmOpHelper helper(this, intrin.op4, node); @@ -412,8 +412,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { if (isRMW) { - assert((targetReg == op1Reg) || (targetReg != op2Reg)); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); GetEmitter()->emitIns_R_R_R_I(ins, emitSize, targetReg, op2Reg, op3Reg, 0, opt); } @@ -511,7 +511,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) // If `falseReg` is zero, then move the first operand of `intrinEmbMask` in the // destination using /Z. - assert((targetReg != embMaskOp2Reg) || (embMaskOp1Reg == embMaskOp2Reg)); + assert((targetReg != embMaskOp2Reg) || (embMaskOp1Reg == embMaskOp2Reg) || + genIsSameLocalVar(intrinEmbMask.op1, intrinEmbMask.op2)); assert(intrin.op3->isContained() || !intrin.op1->IsTrueMask(node->GetSimdBaseType())); GetEmitter()->emitInsSve_R_R_R(INS_sve_movprfx, emitSize, targetReg, maskReg, embMaskOp1Reg, opt); } @@ -765,14 +766,16 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) switch (intrinEmbMask.id) { case NI_Sve_CreateBreakPropagateMask: - assert((targetReg == embMaskOp2Reg) || (targetReg != embMaskOp1Reg)); + assert((targetReg == embMaskOp2Reg) || (targetReg != embMaskOp1Reg) || + genIsSameLocalVar(intrinEmbMask.op1, intrinEmbMask.op2)); GetEmitter()->emitIns_Mov(INS_sve_mov, emitSize, targetReg, embMaskOp2Reg, /* canSkip */ true); emitInsHelper(targetReg, maskReg, embMaskOp1Reg); break; case NI_Sve_AddSequentialAcross: - assert((targetReg == op1Reg) || (targetReg != embMaskOp2Reg)); + assert((targetReg == op1Reg) || (targetReg != embMaskOp2Reg) || + genIsSameLocalVar(intrinEmbMask.op1, intrinEmbMask.op2)); GetEmitter()->emitIns_Mov(INS_fmov, GetEmitter()->optGetSveElemsize(embOpt), targetReg, embMaskOp1Reg, /* canSkip */ true); emitInsHelper(targetReg, maskReg, embMaskOp2Reg); @@ -1061,7 +1064,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { if (isRMW) { - assert((targetReg == op2Reg) || (targetReg != op1Reg)); + assert((targetReg == op2Reg) || (targetReg != op1Reg) || + genIsSameLocalVar(intrin.op1, intrin.op2)); GetEmitter()->emitIns_Mov(ins_Move_Extend(intrin.op2->TypeGet(), false), emitTypeSize(node), targetReg, op2Reg, /* canSkip */ true); @@ -1081,7 +1085,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } else if (isRMW) { - assert((targetReg == op1Reg) || (targetReg != op2Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || + genIsSameLocalVar(intrin.op1, intrin.op2)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); GetEmitter()->emitIns_R_R(ins, emitSize, targetReg, op2Reg, opt); @@ -1099,7 +1104,10 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { if (HWIntrinsicInfo::IsExplicitMaskedOperation(intrin.id)) { - assert((targetReg == op2Reg) || ((targetReg != op1Reg) && (targetReg != op3Reg))); + assert((targetReg == op2Reg) || (targetReg != op1Reg) || + genIsSameLocalVar(intrin.op2, intrin.op1)); + assert((targetReg == op2Reg) || (targetReg != op3Reg) || + genIsSameLocalVar(intrin.op2, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op2Reg, /* canSkip */ true); @@ -1107,7 +1115,10 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) } else { - assert((targetReg == op1Reg) || ((targetReg != op2Reg) && (targetReg != op3Reg))); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || + genIsSameLocalVar(intrin.op1, intrin.op2)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || + genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); @@ -1358,7 +1369,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_AdvSimd_InsertScalar: { assert(isRMW); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); HWIntrinsicImmOpHelper helper(this, intrin.op2, node); @@ -1375,7 +1386,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_AdvSimd_Arm64_InsertSelectedScalar: { assert(isRMW); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); const int resultIndex = (int)intrin.op2->AsIntCon()->gtIconVal; @@ -1387,7 +1398,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_AdvSimd_LoadAndInsertScalar: { assert(isRMW); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); HWIntrinsicImmOpHelper helper(this, intrin.op2, node); @@ -1983,7 +1994,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) break; } - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); GetEmitter()->emitIns_R_R_R(ins, emitSize, targetReg, op2Reg, op3Reg, opt); break; @@ -2348,8 +2359,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_SaturatingIncrementBy8BitElementCount: { assert(isRMW); - assert((targetReg == op1Reg) || (targetReg != op2Reg)); - assert((targetReg == op1Reg) || (targetReg != op3Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); + assert((targetReg == op1Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op1, intrin.op3)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); if (intrin.op2->IsCnsIntOrI() && intrin.op3->IsCnsIntOrI()) @@ -2402,7 +2413,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_SaturatingIncrementByActiveElementCount: { // RMW semantics - assert((targetReg == op1Reg) || (targetReg != op2Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); // Switch instruction if arg1 is unsigned. @@ -2442,7 +2453,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) case NI_Sve_ExtractVector: { assert(isRMW); - assert((targetReg == op1Reg) || (targetReg != op2Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); HWIntrinsicImmOpHelper helper(this, intrin.op3, node); @@ -2461,7 +2472,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { assert(isRMW); assert(emitter::isFloatReg(op2Reg) == varTypeIsFloating(intrin.baseType)); - assert((targetReg == op1Reg) || (targetReg != op2Reg)); + assert((targetReg == op1Reg) || (targetReg != op2Reg) || genIsSameLocalVar(intrin.op1, intrin.op2)); GetEmitter()->emitIns_Mov(INS_mov, emitTypeSize(node), targetReg, op1Reg, /* canSkip */ true); GetEmitter()->emitInsSve_R_R(ins, emitSize, targetReg, op2Reg, opt); @@ -2487,7 +2498,7 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { assert(isRMW); assert(HWIntrinsicInfo::IsExplicitMaskedOperation(intrin.id)); - assert((targetReg == op2Reg) || (targetReg != op1Reg)); + assert((targetReg == op2Reg) || (targetReg != op1Reg) || genIsSameLocalVar(intrin.op2, intrin.op1)); GetEmitter()->emitIns_Mov(INS_sve_mov, emitTypeSize(node), targetReg, op2Reg, /* canSkip */ true); GetEmitter()->emitIns_R_R(ins, emitSize, targetReg, op1Reg, INS_OPTS_SCALABLE_B); break; @@ -2559,8 +2570,8 @@ void CodeGen::genHWIntrinsic(GenTreeHWIntrinsic* node) { assert(emitter::isFloatReg(targetReg)); assert(varTypeIsFloating(node->gtType) || varTypeIsSIMD(node->gtType)); - assert((targetReg == op2Reg) || (targetReg != op1Reg)); - assert((targetReg == op2Reg) || (targetReg != op3Reg)); + assert((targetReg == op2Reg) || (targetReg != op1Reg) || genIsSameLocalVar(intrin.op2, intrin.op1)); + assert((targetReg == op2Reg) || (targetReg != op3Reg) || genIsSameLocalVar(intrin.op2, intrin.op3)); GetEmitter()->emitIns_Mov(INS_sve_mov, EA_SCALABLE, targetReg, op2Reg, /* canSkip */ true, opt); GetEmitter()->emitInsSve_R_R_R(ins, EA_SCALABLE, targetReg, op1Reg, op3Reg, opt, diff --git a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp index 9cb8819aabd6b9..9dc8a978174c9d 100644 --- a/src/coreclr/jit/hwintrinsiccodegenxarch.cpp +++ b/src/coreclr/jit/hwintrinsiccodegenxarch.cpp @@ -2623,7 +2623,8 @@ void CodeGen::genSse42Intrinsic(GenTreeHWIntrinsic* node, insOpts instOptions) regNumber op1Reg = op1->GetRegNum(); GenTree* op2 = node->Op(2); - assert(!op2->isUsedFromReg() || (op2->GetRegNum() != targetReg) || (op1Reg == targetReg)); + assert(!op2->isUsedFromReg() || (op2->GetRegNum() != targetReg) || (op1Reg == targetReg) || + genIsSameLocalVar(op1, op2)); emit->emitIns_Mov(INS_mov, emitTypeSize(targetType), targetReg, op1Reg, /* canSkip */ true); #ifdef TARGET_AMD64