/// Erase \p MI
bool eraseInst(MachineInstr &MI);
+ /// Return true if MI is a G_ADD which can be simplified to a G_SUB.
+ bool matchSimplifyAddToSub(MachineInstr &MI,
+ std::tuple<Register, Register> &MatchInfo);
+ bool applySimplifyAddToSub(MachineInstr &MI,
+ std::tuple<Register, Register> &MatchInfo);
+
/// Try to transform \p MI by using all of the above
/// combine functions. Returns true if changed.
bool tryCombine(MachineInstr &MI);
(apply [{ return Helper.eraseInst(*${root}); }])
>;
+def simplify_add_to_sub_matchinfo: GIDefMatchData<"std::tuple<Register, Register>">;
+def simplify_add_to_sub: GICombineRule <
+ (defs root:$root, simplify_add_to_sub_matchinfo:$info),
+ (match (wip_match_opcode G_ADD):$root,
+ [{ return Helper.matchSimplifyAddToSub(*${root}, ${info}); }]),
+ (apply [{ return Helper.applySimplifyAddToSub(*${root}, ${info});}])
+>;
+
// FIXME: These should use the custom predicate feature once it lands.
def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero,
undef_to_negative_one,
def trivial_combines : GICombineGroup<[copy_prop, mul_to_shl]>;
def all_combines : GICombineGroup<[trivial_combines, ptr_add_immed_chain,
combines_for_extload, combine_indexed_load_store, undef_combines,
- identity_combines]>;
+ identity_combines, simplify_add_to_sub]>;
return true;
}
+bool CombinerHelper::matchSimplifyAddToSub(
+ MachineInstr &MI, std::tuple<Register, Register> &MatchInfo) {
+ Register LHS = MI.getOperand(1).getReg();
+ Register RHS = MI.getOperand(2).getReg();
+ Register &NewLHS = std::get<0>(MatchInfo);
+ Register &NewRHS = std::get<1>(MatchInfo);
+
+ // Helper lambda to check for opportunities for
+ // ((0-A) + B) -> B - A
+ // (A + (0-B)) -> A - B
+ auto CheckFold = [&](Register &MaybeSub, Register &MaybeNewLHS) {
+ int64_t Cst;
+ if (!mi_match(MaybeSub, MRI, m_GSub(m_ICst(Cst), m_Reg(NewRHS))) ||
+ Cst != 0)
+ return false;
+ NewLHS = MaybeNewLHS;
+ return true;
+ };
+
+ return CheckFold(LHS, RHS) || CheckFold(RHS, LHS);
+}
+
+bool CombinerHelper::applySimplifyAddToSub(
+ MachineInstr &MI, std::tuple<Register, Register> &MatchInfo) {
+ Builder.setInstr(MI);
+ Register SubLHS, SubRHS;
+ std::tie(SubLHS, SubRHS) = MatchInfo;
+ Builder.buildSub(MI.getOperand(0).getReg(), SubLHS, SubRHS);
+ MI.eraseFromParent();
+ return true;
+}
+
bool CombinerHelper::tryCombine(MachineInstr &MI) {
if (tryCombineCopy(MI))
return true;
--- /dev/null
+# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
+# RUN: llc -mtriple aarch64 -run-pass=aarch64-prelegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s
+
+name: pat1
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $x0, $x1
+ ; Fold ((0-A) + B) -> B - A
+ ; CHECK-LABEL: name: pat1
+ ; CHECK: liveins: $x0, $x1
+ ; CHECK: %copy1:_(s64) = COPY $x0
+ ; CHECK: %copy2:_(s64) = COPY $x1
+ ; CHECK: %add:_(s64) = G_SUB %copy2, %copy1
+ ; CHECK: $x0 = COPY %add(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %copy1:_(s64) = COPY $x0
+ %copy2:_(s64) = COPY $x1
+ %zero:_(s64) = G_CONSTANT i64 0
+ %sub:_(s64) = G_SUB %zero, %copy1
+ %add:_(s64) = G_ADD %sub, %copy2
+ $x0 = COPY %add(s64)
+ RET_ReallyLR implicit $x0
+
+...
+---
+name: pat2
+tracksRegLiveness: true
+body: |
+ bb.0:
+ liveins: $x0, $x1
+ ; Fold (A + (0-B)) -> A - B
+ ; CHECK-LABEL: name: pat2
+ ; CHECK: liveins: $x0, $x1
+ ; CHECK: %copy1:_(s64) = COPY $x0
+ ; CHECK: %copy2:_(s64) = COPY $x1
+ ; CHECK: %add:_(s64) = G_SUB %copy1, %copy2
+ ; CHECK: $x0 = COPY %add(s64)
+ ; CHECK: RET_ReallyLR implicit $x0
+ %copy1:_(s64) = COPY $x0
+ %copy2:_(s64) = COPY $x1
+ %zero:_(s64) = G_CONSTANT i64 0
+ %sub:_(s64) = G_SUB %zero, %copy2
+ %add:_(s64) = G_ADD %copy1, %sub
+ $x0 = COPY %add(s64)
+ RET_ReallyLR implicit $x0
+
+...