-
Notifications
You must be signed in to change notification settings - Fork 5.4k
JIT: Emit mulx for GT_MULHI and GT_MUL_LONG if BMI2 is available #116198
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
Changes from 8 commits
b6fbd02
b8d054f
4ae76cb
9f84b3f
1266195
8929847
f5d77fc
feeb24b
59ecc67
0ff6c20
e56ee0a
ce9e87b
691e442
280022a
46ee7af
7854142
5f848c6
82ece23
04450b6
45625b7
7c8dcfb
8ab4c9d
cbd5824
ab4ec11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -795,6 +795,14 @@ bool LinearScan::isRMWRegOper(GenTree* tree) | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| return (!tree->gtGetOp2()->isContainedIntOrIImmed() && !tree->gtGetOp1()->isContainedIntOrIImmed()); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| #ifdef TARGET_X86 | ||||||||||||||||||||||||||||||||||
| case GT_MUL_LONG: | ||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||
| case GT_MULHI: | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // MUL, IMUL are RMW but mulx is not (which is used for unsigned operands when BMI2 is availible) | ||||||||||||||||||||||||||||||||||
| return !(tree->IsUnsigned() && compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2)); | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about extracting a helper method for determining if a multiply node should emit mulx since
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make sense to look at using mulx when numbers are signed, but they are proven to be non-negative ? If so where would it make sense to have a helper and do you have a suggestion for name |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| #ifdef FEATURE_HW_INTRINSICS | ||||||||||||||||||||||||||||||||||
| case GT_HWINTRINSIC: | ||||||||||||||||||||||||||||||||||
|
|
@@ -3213,18 +3221,47 @@ int LinearScan::BuildMul(GenTree* tree) | |||||||||||||||||||||||||||||||||
| return BuildSimple(tree); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // ToDo-APX : imul currently doesn't have rex2 support. So, cannot use R16-R31. | ||||||||||||||||||||||||||||||||||
| int srcCount = BuildBinaryUses(tree->AsOp()); | ||||||||||||||||||||||||||||||||||
| bool isUnsignedMultiply = tree->IsUnsigned(); | ||||||||||||||||||||||||||||||||||
| bool requiresOverflowCheck = tree->gtOverflowEx(); | ||||||||||||||||||||||||||||||||||
| bool useMulx = tree->OperGet() != GT_MUL && isUnsignedMultiply && | ||||||||||||||||||||||||||||||||||
| compiler->compOpportunisticallyDependsOn(InstructionSet_BMI2); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // ToDo-APX : imul currently doesn't have rex2 support. So, cannot use R16-R31. | ||||||||||||||||||||||||||||||||||
| int srcCount = 0; | ||||||||||||||||||||||||||||||||||
| int dstCount = 1; | ||||||||||||||||||||||||||||||||||
| SingleTypeRegSet dstCandidates = RBM_NONE; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| bool isUnsignedMultiply = ((tree->gtFlags & GTF_UNSIGNED) != 0); | ||||||||||||||||||||||||||||||||||
| bool requiresOverflowCheck = tree->gtOverflowEx(); | ||||||||||||||||||||||||||||||||||
| // Lowering has ensured that op1 is never the memory operand | ||||||||||||||||||||||||||||||||||
| assert(!op1->isUsedFromMemory()); | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // There are three forms of x86 multiply: | ||||||||||||||||||||||||||||||||||
| // Start with building the uses, ensuring that one of the operands is in the implicit register (RAX or RDX) | ||||||||||||||||||||||||||||||||||
| // Place first operand in implicit register, unless: | ||||||||||||||||||||||||||||||||||
| // * it is a memory address | ||||||||||||||||||||||||||||||||||
| // * or the second operand is already in the register | ||||||||||||||||||||||||||||||||||
| if (useMulx) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // In lowering, we place any memory operand in op2 so we default to placing op1 in RDX | ||||||||||||||||||||||||||||||||||
|
Daniel-Svensson marked this conversation as resolved.
Outdated
|
||||||||||||||||||||||||||||||||||
| // By selecting RDX here we don't have to kill it | ||||||||||||||||||||||||||||||||||
| srcCount = BuildOperandUses(op1, SRBM_RDX); | ||||||||||||||||||||||||||||||||||
| srcCount += BuildOperandUses(op2, RBM_NONE); | ||||||||||||||||||||||||||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is heavily inspired by how MultiplyNoFlags is implemented. Is it safe to not have RDX killed if I hope this is able to produce slightly better code than to always kill RDX and just specify any register instead. (since rdx can be reused)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just found #10196 runtime/src/coreclr/jit/lsrabuild.cpp Lines 956 to 971 in 9703a4c
Is that still an issue? (NI_AVX2_MultiplyNoFlags does not do anything similar and still seems to work) |
||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| SingleTypeRegSet srcCandidates1 = RBM_NONE; | ||||||||||||||||||||||||||||||||||
| // If op2 is memory then tell allocator to pass op1 in RAX | ||||||||||||||||||||||||||||||||||
| if (op2->isUsedFromMemory()) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| srcCandidates1 = SRBM_RAX; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| srcCount = BuildRMWUses(tree, op1, op2, srcCandidates1, RBM_NONE); | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // There are three forms of x86 multiply in base instruction set | ||||||||||||||||||||||||||||||||||
| // one-op form: RDX:RAX = RAX * r/m | ||||||||||||||||||||||||||||||||||
| // two-op form: reg *= r/m | ||||||||||||||||||||||||||||||||||
| // three-op form: reg = r/m * imm | ||||||||||||||||||||||||||||||||||
| // If the BMI2 instruction set is supported there is an additional unsigned multiply | ||||||||||||||||||||||||||||||||||
| // mulx reg1:reg2 = RDX * reg3/m | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // This special widening 32x32->64 MUL is not used on x64 | ||||||||||||||||||||||||||||||||||
| #if defined(TARGET_X86) | ||||||||||||||||||||||||||||||||||
|
|
@@ -3248,16 +3285,22 @@ int LinearScan::BuildMul(GenTree* tree) | |||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| else if (tree->OperGet() == GT_MULHI) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // Have to use the encoding:RDX:RAX = RAX * rm. Since we only care about the | ||||||||||||||||||||||||||||||||||
| // upper 32 bits of the result set the destination candidate to REG_RDX. | ||||||||||||||||||||||||||||||||||
| dstCandidates = SRBM_RDX; | ||||||||||||||||||||||||||||||||||
| if (!useMulx) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // Have to use the encoding:RDX:RAX = RAX * rm. Since we only care about the | ||||||||||||||||||||||||||||||||||
| // upper 32 bits of the result set the destination candidate to REG_RDX. | ||||||||||||||||||||||||||||||||||
| dstCandidates = SRBM_RDX; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| #if defined(TARGET_X86) | ||||||||||||||||||||||||||||||||||
| else if (tree->OperGet() == GT_MUL_LONG) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // have to use the encoding:RDX:RAX = RAX * rm | ||||||||||||||||||||||||||||||||||
| dstCandidates = SRBM_RAX | SRBM_RDX; | ||||||||||||||||||||||||||||||||||
| dstCount = 2; | ||||||||||||||||||||||||||||||||||
| dstCount = 2; | ||||||||||||||||||||||||||||||||||
| if (!useMulx) | ||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||
| // We have to use the encoding:RDX:RAX = RAX * rm | ||||||||||||||||||||||||||||||||||
| dstCandidates = SRBM_RAX | SRBM_RDX; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| #endif | ||||||||||||||||||||||||||||||||||
| GenTree* containedMemOp = nullptr; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.