Skip to content

Commit bebe6a6

Browse files
author
Jessica Paquette
committed
[GlobalISel] Combine (logic_op (op x...), (op y...)) -> (op (logic_op x, y))
This implements ``` (logic_op (op x...), (op y...)) -> (op (logic_op x, y)) ``` when `op` is an extend, a shift, or an and. This is similar to `DAGCombiner::hoistLogicOpWithSameOpcodeHands` (with a bunch of missing cases, e.g. G_TRUNC, G_BITCAST, etc.) This is implemented so it works both pre and post-legalization. This also adds a general way to add a series of instructions in a combine. (`applyBuildInstructionSteps`). Differential Revision: https://reviews.llvm.org/D85050
1 parent d2c18b5 commit bebe6a6

File tree

6 files changed

+912
-4
lines changed

6 files changed

+912
-4
lines changed

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@ namespace llvm {
2525

2626
class GISelChangeObserver;
2727
class MachineIRBuilder;
28+
class MachineInstrBuilder;
2829
class MachineRegisterInfo;
2930
class MachineInstr;
3031
class MachineOperand;
3132
class GISelKnownBits;
3233
class MachineDominatorTree;
3334
class LegalizerInfo;
35+
struct LegalityQuery;
3436

3537
struct PreferredTuple {
3638
LLT Ty; // The result type of the extend.
@@ -50,6 +52,25 @@ struct PtrAddChain {
5052
Register Base;
5153
};
5254

55+
using OperandBuildSteps =
56+
SmallVector<std::function<void(MachineInstrBuilder &)>, 4>;
57+
struct InstructionBuildSteps {
58+
unsigned Opcode = 0; /// The opcode for the produced instruction.
59+
OperandBuildSteps OperandFns; /// Operands to be added to the instruction.
60+
InstructionBuildSteps() = default;
61+
InstructionBuildSteps(unsigned Opcode, const OperandBuildSteps &OperandFns)
62+
: Opcode(Opcode), OperandFns(OperandFns) {}
63+
};
64+
65+
struct InstructionStepsMatchInfo {
66+
/// Describes instructions to be built during a combine.
67+
SmallVector<InstructionBuildSteps, 2> InstrsToBuild;
68+
InstructionStepsMatchInfo() = default;
69+
InstructionStepsMatchInfo(
70+
std::initializer_list<InstructionBuildSteps> InstrsToBuild)
71+
: InstrsToBuild(InstrsToBuild) {}
72+
};
73+
5374
class CombinerHelper {
5475
protected:
5576
MachineIRBuilder &Builder;
@@ -69,6 +90,10 @@ class CombinerHelper {
6990
return KB;
7091
}
7192

93+
/// \return true if the combine is running prior to legalization, or if \p
94+
/// Query is legal on the target.
95+
bool isLegalOrBeforeLegalizer(const LegalityQuery &Query) const;
96+
7297
/// MachineRegisterInfo::replaceRegWith() and inform the observer of the changes
7398
void replaceRegWith(MachineRegisterInfo &MRI, Register FromReg, Register ToReg) const;
7499

@@ -260,6 +285,15 @@ class CombinerHelper {
260285
bool applySimplifyAddToSub(MachineInstr &MI,
261286
std::tuple<Register, Register> &MatchInfo);
262287

288+
/// Match (logic_op (op x...), (op y...)) -> (op (logic_op x, y))
289+
bool
290+
matchHoistLogicOpWithSameOpcodeHands(MachineInstr &MI,
291+
InstructionStepsMatchInfo &MatchInfo);
292+
293+
/// Replace \p MI with a series of instructions described in \p MatchInfo.
294+
bool applyBuildInstructionSteps(MachineInstr &MI,
295+
InstructionStepsMatchInfo &MatchInfo);
296+
263297
/// Try to transform \p MI by using all of the above
264298
/// combine functions. Returns true if changed.
265299
bool tryCombine(MachineInstr &MI);

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class GIDefMatchData<string type> : GIDefKind {
8585

8686
def extending_load_matchdata : GIDefMatchData<"PreferredTuple">;
8787
def indexed_load_store_matchdata : GIDefMatchData<"IndexedLoadStoreMatchInfo">;
88+
def instruction_steps_matchdata: GIDefMatchData<"InstructionStepsMatchInfo">;
8889

8990
/// The operator at the root of a GICombineRule.Match dag.
9091
def match;
@@ -275,6 +276,14 @@ def i2p_to_p2i: GICombineRule<
275276
(apply [{ return Helper.applyCombineP2IToI2P(*${root}, ${info}); }])
276277
>;
277278

279+
// Simplify: (logic_op (op x...), (op y...)) -> (op (logic_op x, y))
280+
def hoist_logic_op_with_same_opcode_hands: GICombineRule <
281+
(defs root:$root, instruction_steps_matchdata:$info),
282+
(match (wip_match_opcode G_AND, G_OR, G_XOR):$root,
283+
[{ return Helper.matchHoistLogicOpWithSameOpcodeHands(*${root}, ${info}); }]),
284+
(apply [{ return Helper.applyBuildInstructionSteps(*${root}, ${info});}])
285+
>;
286+
278287
// FIXME: These should use the custom predicate feature once it lands.
279288
def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero,
280289
undef_to_negative_one,
@@ -285,10 +294,11 @@ def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero,
285294

286295
def identity_combines : GICombineGroup<[select_same_val, right_identity_zero,
287296
binop_same_val, binop_left_to_zero,
288-
binop_right_to_zero, p2i_to_i2p,
297+
binop_right_to_zero, p2i_to_i2p,
289298
i2p_to_p2i]>;
290299

291300
def trivial_combines : GICombineGroup<[copy_prop, mul_to_shl]>;
292301
def all_combines : GICombineGroup<[trivial_combines, ptr_add_immed_chain,
293302
combines_for_extload, combine_indexed_load_store, undef_combines,
294-
identity_combines, simplify_add_to_sub]>;
303+
identity_combines, simplify_add_to_sub,
304+
hoist_logic_op_with_same_opcode_hands]>;

llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp

Lines changed: 112 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ static cl::opt<bool>
3434
cl::desc("Force all indexed operations to be "
3535
"legal for the GlobalISel combiner"));
3636

37-
3837
CombinerHelper::CombinerHelper(GISelChangeObserver &Observer,
3938
MachineIRBuilder &B, GISelKnownBits *KB,
4039
MachineDominatorTree *MDT,
@@ -44,6 +43,11 @@ CombinerHelper::CombinerHelper(GISelChangeObserver &Observer,
4443
(void)this->KB;
4544
}
4645

46+
bool CombinerHelper::isLegalOrBeforeLegalizer(
47+
const LegalityQuery &Query) const {
48+
return !LI || LI->getAction(Query).Action == LegalizeActions::Legal;
49+
}
50+
4751
void CombinerHelper::replaceRegWith(MachineRegisterInfo &MRI, Register FromReg,
4852
Register ToReg) const {
4953
Observer.changingAllUsesOfReg(MRI, FromReg);
@@ -1776,6 +1780,113 @@ bool CombinerHelper::applySimplifyAddToSub(
17761780
return true;
17771781
}
17781782

1783+
bool CombinerHelper::matchHoistLogicOpWithSameOpcodeHands(
1784+
MachineInstr &MI, InstructionStepsMatchInfo &MatchInfo) {
1785+
// Matches: logic (hand x, ...), (hand y, ...) -> hand (logic x, y), ...
1786+
//
1787+
// Creates the new hand + logic instruction (but does not insert them.)
1788+
//
1789+
// On success, MatchInfo is populated with the new instructions. These are
1790+
// inserted in applyHoistLogicOpWithSameOpcodeHands.
1791+
unsigned LogicOpcode = MI.getOpcode();
1792+
assert(LogicOpcode == TargetOpcode::G_AND ||
1793+
LogicOpcode == TargetOpcode::G_OR ||
1794+
LogicOpcode == TargetOpcode::G_XOR);
1795+
MachineIRBuilder MIB(MI);
1796+
Register Dst = MI.getOperand(0).getReg();
1797+
Register LHSReg = MI.getOperand(1).getReg();
1798+
Register RHSReg = MI.getOperand(2).getReg();
1799+
1800+
// Don't recompute anything.
1801+
if (!MRI.hasOneNonDBGUse(LHSReg) || !MRI.hasOneNonDBGUse(RHSReg))
1802+
return false;
1803+
1804+
// Make sure we have (hand x, ...), (hand y, ...)
1805+
MachineInstr *LeftHandInst = getDefIgnoringCopies(LHSReg, MRI);
1806+
MachineInstr *RightHandInst = getDefIgnoringCopies(RHSReg, MRI);
1807+
if (!LeftHandInst || !RightHandInst)
1808+
return false;
1809+
unsigned HandOpcode = LeftHandInst->getOpcode();
1810+
if (HandOpcode != RightHandInst->getOpcode())
1811+
return false;
1812+
if (!LeftHandInst->getOperand(1).isReg() ||
1813+
!RightHandInst->getOperand(1).isReg())
1814+
return false;
1815+
1816+
// Make sure the types match up, and if we're doing this post-legalization,
1817+
// we end up with legal types.
1818+
Register X = LeftHandInst->getOperand(1).getReg();
1819+
Register Y = RightHandInst->getOperand(1).getReg();
1820+
LLT XTy = MRI.getType(X);
1821+
LLT YTy = MRI.getType(Y);
1822+
if (XTy != YTy)
1823+
return false;
1824+
if (!isLegalOrBeforeLegalizer({LogicOpcode, {XTy, YTy}}))
1825+
return false;
1826+
1827+
// Optional extra source register.
1828+
Register ExtraHandOpSrcReg;
1829+
switch (HandOpcode) {
1830+
default:
1831+
return false;
1832+
case TargetOpcode::G_ANYEXT:
1833+
case TargetOpcode::G_SEXT:
1834+
case TargetOpcode::G_ZEXT: {
1835+
// Match: logic (ext X), (ext Y) --> ext (logic X, Y)
1836+
break;
1837+
}
1838+
case TargetOpcode::G_AND:
1839+
case TargetOpcode::G_ASHR:
1840+
case TargetOpcode::G_LSHR:
1841+
case TargetOpcode::G_SHL: {
1842+
// Match: logic (binop x, z), (binop y, z) -> binop (logic x, y), z
1843+
MachineOperand &ZOp = LeftHandInst->getOperand(2);
1844+
if (!matchEqualDefs(ZOp, RightHandInst->getOperand(2)))
1845+
return false;
1846+
ExtraHandOpSrcReg = ZOp.getReg();
1847+
break;
1848+
}
1849+
}
1850+
1851+
// Record the steps to build the new instructions.
1852+
//
1853+
// Steps to build (logic x, y)
1854+
auto NewLogicDst = MRI.createGenericVirtualRegister(XTy);
1855+
OperandBuildSteps LogicBuildSteps = {
1856+
[=](MachineInstrBuilder &MIB) { MIB.addDef(NewLogicDst); },
1857+
[=](MachineInstrBuilder &MIB) { MIB.addReg(X); },
1858+
[=](MachineInstrBuilder &MIB) { MIB.addReg(Y); }};
1859+
InstructionBuildSteps LogicSteps(LogicOpcode, LogicBuildSteps);
1860+
1861+
// Steps to build hand (logic x, y), ...z
1862+
OperandBuildSteps HandBuildSteps = {
1863+
[=](MachineInstrBuilder &MIB) { MIB.addDef(Dst); },
1864+
[=](MachineInstrBuilder &MIB) { MIB.addReg(NewLogicDst); }};
1865+
if (ExtraHandOpSrcReg.isValid())
1866+
HandBuildSteps.push_back(
1867+
[=](MachineInstrBuilder &MIB) { MIB.addReg(ExtraHandOpSrcReg); });
1868+
InstructionBuildSteps HandSteps(HandOpcode, HandBuildSteps);
1869+
1870+
MatchInfo = InstructionStepsMatchInfo({LogicSteps, HandSteps});
1871+
return true;
1872+
}
1873+
1874+
bool CombinerHelper::applyBuildInstructionSteps(
1875+
MachineInstr &MI, InstructionStepsMatchInfo &MatchInfo) {
1876+
assert(MatchInfo.InstrsToBuild.size() &&
1877+
"Expected at least one instr to build?");
1878+
Builder.setInstr(MI);
1879+
for (auto &InstrToBuild : MatchInfo.InstrsToBuild) {
1880+
assert(InstrToBuild.Opcode && "Expected a valid opcode?");
1881+
assert(InstrToBuild.OperandFns.size() && "Expected at least one operand?");
1882+
MachineInstrBuilder Instr = Builder.buildInstr(InstrToBuild.Opcode);
1883+
for (auto &OperandFn : InstrToBuild.OperandFns)
1884+
OperandFn(Instr);
1885+
}
1886+
MI.eraseFromParent();
1887+
return true;
1888+
}
1889+
17791890
bool CombinerHelper::tryCombine(MachineInstr &MI) {
17801891
if (tryCombineCopy(MI))
17811892
return true;

llvm/lib/Target/AArch64/AArch64Combine.td

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def shuffle_vector_pseudos : GICombineGroup<[dup, rev, ext, zip, uzp, trn]>;
7979
def AArch64PostLegalizerCombinerHelper
8080
: GICombinerHelper<"AArch64GenPostLegalizerCombinerHelper",
8181
[erase_undef_store, combines_for_extload,
82-
sext_trunc_sextload, shuffle_vector_pseudos]> {
82+
sext_trunc_sextload, shuffle_vector_pseudos,
83+
hoist_logic_op_with_same_opcode_hands]> {
8384
let DisableRuleOption = "aarch64postlegalizercombiner-disable-rule";
8485
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
# RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
3+
4+
...
5+
---
6+
name: or_combine_sext
7+
tracksRegLiveness: true
8+
legalized: true
9+
body: |
10+
bb.0:
11+
liveins: $w0, $w1
12+
; or (sext X), (sext Y) --> sext (or X, Y)
13+
;
14+
; CHECK-LABEL: name: or_combine_sext
15+
; CHECK: liveins: $w0, $w1
16+
; CHECK: %x:_(s32) = COPY $w0
17+
; CHECK: %y:_(s32) = COPY $w1
18+
; CHECK: [[OR:%[0-9]+]]:_(s32) = G_OR %x, %y
19+
; CHECK: %logic_op:_(s64) = G_SEXT [[OR]](s32)
20+
; CHECK: $x0 = COPY %logic_op(s64)
21+
; CHECK: RET_ReallyLR implicit $x0
22+
%x:_(s32) = COPY $w0
23+
%y:_(s32) = COPY $w1
24+
%hand1:_(s64) = G_SEXT %x(s32)
25+
%hand2:_(s64) = G_SEXT %y(s32)
26+
%logic_op:_(s64) = G_OR %hand1, %hand2
27+
$x0 = COPY %logic_op(s64)
28+
RET_ReallyLR implicit $x0
29+
30+
...
31+
---
32+
name: illegal_ty
33+
tracksRegLiveness: true
34+
legalized: true
35+
body: |
36+
bb.0:
37+
liveins: $w0, $w1
38+
; Post-legalization, we should not perform the optimization here, since
39+
; it would create an illegal G_OR.
40+
;
41+
; CHECK-LABEL: name: illegal_ty
42+
; CHECK: liveins: $w0, $w1
43+
; CHECK: %x_wide:_(s32) = COPY $w0
44+
; CHECK: %y_wide:_(s32) = COPY $w1
45+
; CHECK: %x:_(s1) = G_TRUNC %x_wide(s32)
46+
; CHECK: %y:_(s1) = G_TRUNC %y_wide(s32)
47+
; CHECK: %hand1:_(s64) = G_SEXT %x(s1)
48+
; CHECK: %hand2:_(s64) = G_SEXT %y(s1)
49+
; CHECK: %logic_op:_(s64) = G_OR %hand1, %hand2
50+
; CHECK: $x0 = COPY %logic_op(s64)
51+
; CHECK: RET_ReallyLR implicit $x0
52+
%x_wide:_(s32) = COPY $w0
53+
%y_wide:_(s32) = COPY $w1
54+
%x:_(s1) = G_TRUNC %x_wide
55+
%y:_(s1) = G_TRUNC %y_wide
56+
%hand1:_(s64) = G_SEXT %x(s1)
57+
%hand2:_(s64) = G_SEXT %y(s1)
58+
%logic_op:_(s64) = G_OR %hand1, %hand2
59+
$x0 = COPY %logic_op(s64)
60+
RET_ReallyLR implicit $x0

0 commit comments

Comments
 (0)