Skip to content

Commit 4c6ab48

Browse files
committed
GlobalISel: Try to combine G_[SU]DIV and G_[SU]REM
It is good to have a combined `divrem` instruction when the `div` and `rem` are computed from identical input operands. Some targets can lower them through a single expansion that computes both division and remainder. It effectively reduces the number of instructions than individually expanding them. Reviewed By: arsenm, paquette Differential Revision: https://reviews.llvm.org/D96013
1 parent 99b01cf commit 4c6ab48

File tree

12 files changed

+971
-2
lines changed

12 files changed

+971
-2
lines changed

llvm/docs/GlobalISel/GenericOpcode.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ These each perform their respective integer arithmetic on a scalar.
245245
246246
%2:_(s32) = G_ADD %0:_(s32), %1:_(s32)
247247
248+
G_SDIVREM, G_UDIVREM
249+
^^^^^^^^^^^^^^^^^^^^
250+
251+
Perform integer division and remainder thereby producing two results.
252+
253+
.. code-block:: none
254+
255+
%div:_(s32), %rem:_(s32) = G_SDIVREM %0:_(s32), %1:_(s32)
256+
248257
G_SADDSAT, G_UADDSAT, G_SSUBSAT, G_USUBSAT, G_SSHLSAT, G_USHLSAT
249258
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
250259

llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ class CombinerHelper {
156156
bool matchSextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
157157
bool applySextInRegOfLoad(MachineInstr &MI, std::tuple<Register, unsigned> &MatchInfo);
158158

159+
/// Try to combine G_[SU]DIV and G_[SU]REM into a single G_[SU]DIVREM
160+
/// when their source operands are identical.
161+
bool matchCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
162+
void applyCombineDivRem(MachineInstr &MI, MachineInstr *&OtherMI);
163+
159164
/// If a brcond's true block is not the fallthrough, make it so by inverting
160165
/// the condition and swapping operands.
161166
bool matchOptBrCondByInvertingCond(MachineInstr &MI);

llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,7 @@ class LegalizerHelper {
373373
LegalizeResult lowerReadWriteRegister(MachineInstr &MI);
374374
LegalizeResult lowerSMULH_UMULH(MachineInstr &MI);
375375
LegalizeResult lowerSelect(MachineInstr &MI);
376-
376+
LegalizeResult lowerDIVREM(MachineInstr &MI);
377377
};
378378

379379
/// Helper function that creates a libcall to the given \p Name using the given

llvm/include/llvm/Support/TargetOpcodes.def

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,12 @@ HANDLE_TARGET_OPCODE(G_SREM)
248248
// Generic unsigned remainder instruction.
249249
HANDLE_TARGET_OPCODE(G_UREM)
250250

251+
// Generic signed divrem instruction.
252+
HANDLE_TARGET_OPCODE(G_SDIVREM)
253+
254+
// Generic unsigned divrem instruction.
255+
HANDLE_TARGET_OPCODE(G_UDIVREM)
256+
251257
/// Generic bitwise and instruction.
252258
HANDLE_TARGET_OPCODE(G_AND)
253259

llvm/include/llvm/Target/GenericOpcodes.td

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,22 @@ def G_UREM : GenericInstruction {
285285
let isCommutable = false;
286286
}
287287

288+
// Generic signed division and remainder.
289+
def G_SDIVREM : GenericInstruction {
290+
let OutOperandList = (outs type0:$div, type0:$rem);
291+
let InOperandList = (ins type0:$src1, type0:$src2);
292+
let hasSideEffects = false;
293+
let isCommutable = false;
294+
}
295+
296+
// Generic unsigned division and remainder.
297+
def G_UDIVREM : GenericInstruction {
298+
let OutOperandList = (outs type0:$div, type0:$rem);
299+
let InOperandList = (ins type0:$src1, type0:$src2);
300+
let hasSideEffects = false;
301+
let isCommutable = false;
302+
}
303+
288304
// Generic bitwise and.
289305
def G_AND : GenericInstruction {
290306
let OutOperandList = (outs type0:$dst);

llvm/include/llvm/Target/GlobalISel/Combine.td

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,15 @@ def urem_pow2_to_mask : GICombineRule<
306306
(apply [{ return Helper.applySimplifyURemByPow2(*${root}); }])
307307
>;
308308

309+
// Transform d = [su]div(x, y) and r = [su]rem(x, y) - > d, r = [su]divrem(x, y)
310+
def div_rem_to_divrem_matchdata : GIDefMatchData<"MachineInstr *">;
311+
def div_rem_to_divrem : GICombineRule<
312+
(defs root:$root, div_rem_to_divrem_matchdata:$matchinfo),
313+
(match (wip_match_opcode G_SDIV, G_UDIV, G_SREM, G_UREM):$root,
314+
[{ return Helper.matchCombineDivRem(*${root}, ${matchinfo}); }]),
315+
(apply [{ Helper.applyCombineDivRem(*${root}, ${matchinfo}); }])
316+
>;
317+
309318
// Fold (x op 0) - > 0
310319
def binop_right_to_zero: GICombineRule<
311320
(defs root:$root),
@@ -630,4 +639,5 @@ def all_combines : GICombineGroup<[trivial_combines, insert_vec_elt_combines,
630639
unmerge_merge, fabs_fabs_fold, unmerge_cst, unmerge_dead_to_trunc,
631640
unmerge_zext_to_zext, trunc_ext_fold, trunc_shl,
632641
const_combines, xor_of_and_with_same_reg, ptr_add_with_zero,
633-
shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine]>;
642+
shift_immed_chain, shift_of_shifted_logic_chain, load_or_combine,
643+
div_rem_to_divrem]>;

llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,93 @@ void CombinerHelper::applyCombineIndexedLoadStore(
946946
LLVM_DEBUG(dbgs() << " Combinined to indexed operation");
947947
}
948948

949+
bool CombinerHelper::matchCombineDivRem(MachineInstr &MI,
950+
MachineInstr *&OtherMI) {
951+
unsigned Opcode = MI.getOpcode();
952+
bool IsDiv, IsSigned;
953+
954+
switch (Opcode) {
955+
default:
956+
llvm_unreachable("Unexpected opcode!");
957+
case TargetOpcode::G_SDIV:
958+
case TargetOpcode::G_UDIV: {
959+
IsDiv = true;
960+
IsSigned = Opcode == TargetOpcode::G_SDIV;
961+
break;
962+
}
963+
case TargetOpcode::G_SREM:
964+
case TargetOpcode::G_UREM: {
965+
IsDiv = false;
966+
IsSigned = Opcode == TargetOpcode::G_SREM;
967+
break;
968+
}
969+
}
970+
971+
Register Src1 = MI.getOperand(1).getReg();
972+
unsigned DivOpcode, RemOpcode, DivremOpcode;
973+
if (IsSigned) {
974+
DivOpcode = TargetOpcode::G_SDIV;
975+
RemOpcode = TargetOpcode::G_SREM;
976+
DivremOpcode = TargetOpcode::G_SDIVREM;
977+
} else {
978+
DivOpcode = TargetOpcode::G_UDIV;
979+
RemOpcode = TargetOpcode::G_UREM;
980+
DivremOpcode = TargetOpcode::G_UDIVREM;
981+
}
982+
983+
if (!isLegalOrBeforeLegalizer({DivremOpcode, {MRI.getType(Src1)}}))
984+
return false;
985+
986+
// Combine:
987+
// %div:_ = G_[SU]DIV %src1:_, %src2:_
988+
// %rem:_ = G_[SU]REM %src1:_, %src2:_
989+
// into:
990+
// %div:_, %rem:_ = G_[SU]DIVREM %src1:_, %src2:_
991+
992+
// Combine:
993+
// %rem:_ = G_[SU]REM %src1:_, %src2:_
994+
// %div:_ = G_[SU]DIV %src1:_, %src2:_
995+
// into:
996+
// %div:_, %rem:_ = G_[SU]DIVREM %src1:_, %src2:_
997+
998+
for (auto &UseMI : MRI.use_nodbg_instructions(Src1)) {
999+
if (MI.getParent() == UseMI.getParent() &&
1000+
((IsDiv && UseMI.getOpcode() == RemOpcode) ||
1001+
(!IsDiv && UseMI.getOpcode() == DivOpcode)) &&
1002+
matchEqualDefs(MI.getOperand(2), UseMI.getOperand(2))) {
1003+
OtherMI = &UseMI;
1004+
return true;
1005+
}
1006+
}
1007+
1008+
return false;
1009+
}
1010+
1011+
void CombinerHelper::applyCombineDivRem(MachineInstr &MI,
1012+
MachineInstr *&OtherMI) {
1013+
unsigned Opcode = MI.getOpcode();
1014+
assert(OtherMI && "OtherMI shouldn't be empty.");
1015+
1016+
Register DestDivReg, DestRemReg;
1017+
if (Opcode == TargetOpcode::G_SDIV || Opcode == TargetOpcode::G_UDIV) {
1018+
DestDivReg = MI.getOperand(0).getReg();
1019+
DestRemReg = OtherMI->getOperand(0).getReg();
1020+
} else {
1021+
DestDivReg = OtherMI->getOperand(0).getReg();
1022+
DestRemReg = MI.getOperand(0).getReg();
1023+
}
1024+
1025+
bool IsSigned =
1026+
Opcode == TargetOpcode::G_SDIV || Opcode == TargetOpcode::G_SREM;
1027+
Builder.setInstrAndDebugLoc(MI);
1028+
Builder.buildInstr(IsSigned ? TargetOpcode::G_SDIVREM
1029+
: TargetOpcode::G_UDIVREM,
1030+
{DestDivReg, DestRemReg},
1031+
{MI.getOperand(1).getReg(), MI.getOperand(2).getReg()});
1032+
MI.eraseFromParent();
1033+
OtherMI->eraseFromParent();
1034+
}
1035+
9491036
bool CombinerHelper::matchOptBrCondByInvertingCond(MachineInstr &MI) {
9501037
if (MI.getOpcode() != TargetOpcode::G_BR)
9511038
return false;

llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3148,6 +3148,9 @@ LegalizerHelper::lower(MachineInstr &MI, unsigned TypeIdx, LLT LowerHintTy) {
31483148
}
31493149
case G_SELECT:
31503150
return lowerSelect(MI);
3151+
case G_SDIVREM:
3152+
case G_UDIVREM:
3153+
return lowerDIVREM(MI);
31513154
}
31523155
}
31533156

@@ -6341,3 +6344,19 @@ LegalizerHelper::LegalizeResult LegalizerHelper::lowerSelect(MachineInstr &MI) {
63416344
MI.eraseFromParent();
63426345
return Legalized;
63436346
}
6347+
6348+
LegalizerHelper::LegalizeResult LegalizerHelper::lowerDIVREM(MachineInstr &MI) {
6349+
// Split DIVREM into individual instructions.
6350+
unsigned Opcode = MI.getOpcode();
6351+
6352+
MIRBuilder.buildInstr(
6353+
Opcode == TargetOpcode::G_SDIVREM ? TargetOpcode::G_SDIV
6354+
: TargetOpcode::G_UDIV,
6355+
{MI.getOperand(0).getReg()}, {MI.getOperand(2), MI.getOperand(3)});
6356+
MIRBuilder.buildInstr(
6357+
Opcode == TargetOpcode::G_SDIVREM ? TargetOpcode::G_SREM
6358+
: TargetOpcode::G_UREM,
6359+
{MI.getOperand(1).getReg()}, {MI.getOperand(2), MI.getOperand(3)});
6360+
MI.eraseFromParent();
6361+
return Legalized;
6362+
}

llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@
4545
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
4646
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
4747
#
48+
# DEBUG-NEXT: G_SDIVREM (opcode {{[0-9]+}}): 1 type index, 0 imm indices
49+
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
50+
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
51+
#
52+
# DEBUG-NEXT: G_UDIVREM (opcode {{[0-9]+}}): 1 type index, 0 imm indices
53+
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
54+
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
55+
#
4856
# DEBUG-NEXT: G_AND (opcode {{[0-9]+}}): 1 type index, 0 imm indices
4957
# DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}}
5058
# DEBUG-NEXT: .. the first uncovered type index: 1, OK

0 commit comments

Comments
 (0)