@@ -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