Skip to content

Commit b6d5713

Browse files
remove register preference for mul, it does only make sense for extended 1 op mul
- some cleanup of BuildMul, reorder andremove dead code
1 parent 82ece23 commit b6d5713

File tree

1 file changed

+38
-67
lines changed

1 file changed

+38
-67
lines changed

src/coreclr/jit/lsraxarch.cpp

Lines changed: 38 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -3241,105 +3241,76 @@ int LinearScan::BuildMul(GenTree* tree)
32413241
int dstCount = 1;
32423242
SingleTypeRegSet dstCandidates = RBM_NONE;
32433243

3244-
// Start with building the uses, ensuring that one of the operands is in the implicit register (RAX or RDX)
3245-
// Place first operand in implicit register, unless:
3246-
// * it is a memory address
3247-
// * or the second operand is already in the register
3244+
// There are three forms of x86 multiply in base instruction set
3245+
// one-op form: RDX:RAX = RAX * r/m
3246+
// two-op form: reg *= r/m
3247+
// three-op form: reg = r/m * imm
3248+
// If the BMI2 instruction set is supported there is an additional unsigned multiply
3249+
// mulx reg1:reg2 = RDX * reg3/m
3250+
3251+
// This special widening 32x32->64 MUL is not used on x64
3252+
#if defined(TARGET_X86)
3253+
if (!tree->OperIs(GT_MUL_LONG))
3254+
#endif
3255+
{
3256+
assert((tree->gtFlags & GTF_MUL_64RSLT) == 0);
3257+
}
3258+
32483259
if (useMulx)
32493260
{
32503261
// Lowering has ensured that op1 is never the memory operand
32513262
assert(!op1->isUsedFromMemory());
32523263

3253-
SingleTypeRegSet srcCandidates1 = RBM_NONE;
3254-
32553264
// If one of the operands is a memory address, specify RDX for the other operand
3265+
SingleTypeRegSet srcCandidates1 = RBM_NONE;
32563266
if (op2->isUsedFromMemory())
32573267
{
3258-
// If op2 is a memory operand, we place it in RDX
32593268
srcCandidates1 = SRBM_RDX;
32603269
}
32613270

3262-
// In lowering, we place any memory operand in op2 so we default to placing op1 in RDX
3263-
// By selecting RDX here we don't have to kill it
32643271
srcCount = BuildOperandUses(op1, srcCandidates1);
32653272
srcCount += BuildOperandUses(op2, RBM_NONE);
3266-
}
3267-
else
3268-
{
3269-
assert(!op1->isUsedFromMemory() || !op2->isUsedFromMemory());
3270-
3271-
SingleTypeRegSet srcCandidates1 = RBM_NONE;
3272-
SingleTypeRegSet srcCandidates2 = RBM_NONE;
32733273

3274-
// If one of the operands is a memory address, specify RAX for the other operand
3275-
if (op1->isUsedFromMemory())
3276-
{
3277-
srcCandidates2 = SRBM_RAX;
3278-
}
3279-
else if (op2->isUsedFromMemory())
3274+
#if defined(TARGET_X86)
3275+
if (tree->OperIs(GT_MUL_LONG))
32803276
{
3281-
srcCandidates1 = SRBM_RAX;
3277+
dstCount = 2;
32823278
}
3283-
srcCount = BuildRMWUses(tree, op1, op2, srcCandidates1, srcCandidates2);
3284-
}
3285-
3286-
// There are three forms of x86 multiply in base instruction set
3287-
// one-op form: RDX:RAX = RAX * r/m
3288-
// two-op form: reg *= r/m
3289-
// three-op form: reg = r/m * imm
3290-
// If the BMI2 instruction set is supported there is an additional unsigned multiply
3291-
// mulx reg1:reg2 = RDX * reg3/m
3292-
3293-
// This special widening 32x32->64 MUL is not used on x64
3294-
#if defined(TARGET_X86)
3295-
if (!tree->OperIs(GT_MUL_LONG))
32963279
#endif
3297-
{
3298-
assert((tree->gtFlags & GTF_MUL_64RSLT) == 0);
32993280
}
3300-
3301-
// We do use the widening multiply to implement
3302-
// the overflow checking for unsigned multiply
3303-
//
3304-
if (isUnsignedMultiply && requiresOverflowCheck)
3281+
else
33053282
{
3306-
// The only encoding provided is RDX:RAX = RAX * rm
3307-
//
3308-
// Here we set RAX as the only destination candidate
3309-
// In LSRA we set the kill set for this operation to RBM_RAX|RBM_RDX
3283+
assert(!op1->isUsedFromMemory() || !op2->isUsedFromMemory());
3284+
srcCount = BuildRMWUses(tree, op1, op2, RBM_NONE, RBM_NONE);
3285+
3286+
// We do use the widening multiply to implement
3287+
// the overflow checking for unsigned multiply
33103288
//
3311-
dstCandidates = SRBM_RAX;
3312-
}
3313-
else if (tree->OperIs(GT_MULHI))
3314-
{
3315-
if (!useMulx)
3289+
if (isUnsignedMultiply && requiresOverflowCheck)
3290+
{
3291+
// The only encoding provided is RDX:RAX = RAX * rm
3292+
//
3293+
// Here we set RAX as the only destination candidate
3294+
// In LSRA we set the kill set for this operation to RBM_RAX|RBM_RDX
3295+
//
3296+
dstCandidates = SRBM_RAX;
3297+
}
3298+
else if (tree->OperIs(GT_MULHI))
33163299
{
33173300
// Have to use the encoding:RDX:RAX = RAX * rm. Since we only care about the
33183301
// upper 32 bits of the result set the destination candidate to REG_RDX.
33193302
dstCandidates = SRBM_RDX;
33203303
}
3321-
}
33223304
#if defined(TARGET_X86)
3323-
else if (tree->OperIs(GT_MUL_LONG))
3324-
{
3325-
dstCount = 2;
3326-
if (!useMulx)
3305+
else if (tree->OperIs(GT_MUL_LONG))
33273306
{
3307+
dstCount = 2;
33283308
// We have to use the encoding:RDX:RAX = RAX * rm
33293309
dstCandidates = SRBM_RAX | SRBM_RDX;
33303310
}
3331-
}
33323311
#endif
3333-
GenTree* containedMemOp = nullptr;
3334-
if (op1->isContained() && !op1->IsCnsIntOrI())
3335-
{
3336-
assert(!op2->isContained() || op2->IsCnsIntOrI());
3337-
containedMemOp = op1;
3338-
}
3339-
else if (op2->isContained() && !op2->IsCnsIntOrI())
3340-
{
3341-
containedMemOp = op2;
33423312
}
3313+
33433314
regMaskTP killMask = getKillSetForMul(tree->AsOp());
33443315
BuildDefWithKills(tree, dstCount, dstCandidates, killMask);
33453316
return srcCount;

0 commit comments

Comments
 (0)