[GlobalISel] Port some basic undef combines from DAGCombiner.cpp

This ports some combines from DAGCombiner.cpp which perform some trivial
transformations on instructions with undef operands.

Not having these can make it extremely annoying to find out where we differ
from SelectionDAG by looking at existing lit tests. Without them, we tend to
produce pretty bad code generation when we run into instructions which use
undef operands.

Also remove the nonpow2_store_narrowing testcase from arm64-fallback.ll, since
we no longer fall back on the add.

Differential Revision: https://reviews.llvm.org/D76339
This commit is contained in:
Jessica Paquette 2020-03-17 18:59:51 -07:00
parent 1e4ee0bfc5
commit dc5f982639
5 changed files with 241 additions and 12 deletions

View File

@ -190,6 +190,19 @@ public:
bool applyCombineShiftToUnmerge(MachineInstr &MI, const unsigned &ShiftVal);
bool tryCombineShiftToUnmerge(MachineInstr &MI, unsigned TargetShiftAmount);
/// Return true if any explicit use operand on \p MI is defined by a
/// G_IMPLICIT_DEF.
bool matchAnyExplicitUseIsUndef(MachineInstr &MI);
/// Replace an instruction with a G_FCONSTANT with value \p C.
bool replaceInstWithFConstant(MachineInstr &MI, double C);
/// Replace an instruction with a G_CONSTANT with value \p C.
bool replaceInstWithConstant(MachineInstr &MI, int64_t C);
/// Replace an instruction with a G_IMPLICIT_DEF.
bool replaceInstWithUndef(MachineInstr &MI);
/// Try to transform \p MI by using all of the above
/// combine functions. Returns true if changed.
bool tryCombine(MachineInstr &MI);

View File

@ -142,7 +142,34 @@ def mul_to_shl : GICombineRule<
[{ return Helper.matchCombineMulToShl(*${mi}, ${matchinfo}); }]),
(apply [{ Helper.applyCombineMulToShl(*${mi}, ${matchinfo}); }])>;
// [us]itofp(undef) = 0, because the result value is bounded.
def undef_to_fp_zero : GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_UITOFP, G_SITOFP):$root,
[{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]),
(apply [{ Helper.replaceInstWithFConstant(*${root}, 0.0); }])>;
def undef_to_int_zero: GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_AND, G_MUL):$root,
[{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]),
(apply [{ Helper.replaceInstWithConstant(*${root}, 0); }])>;
def undef_to_negative_one: GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_OR):$root,
[{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]),
(apply [{ Helper.replaceInstWithConstant(*${root}, -1); }])>;
def propagate_undef: GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_ADD, G_FPTOSI, G_FPTOUI, G_SUB, G_XOR):$root,
[{ return Helper.matchAnyExplicitUseIsUndef(*${root}); }]),
(apply [{ Helper.replaceInstWithUndef(*${root}); }])>;
def undef_combines : GICombineGroup<[undef_to_fp_zero, undef_to_int_zero,
undef_to_negative_one, propagate_undef]>;
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]>;
combines_for_extload, combine_indexed_load_store, undef_combines]>;

View File

@ -1483,6 +1483,37 @@ bool CombinerHelper::tryCombineShiftToUnmerge(MachineInstr &MI,
return false;
}
bool CombinerHelper::matchAnyExplicitUseIsUndef(MachineInstr &MI) {
return any_of(MI.explicit_uses(), [this](const MachineOperand &MO) {
return MO.isReg() &&
getOpcodeDef(TargetOpcode::G_IMPLICIT_DEF, MO.getReg(), MRI);
});
}
bool CombinerHelper::replaceInstWithFConstant(MachineInstr &MI, double C) {
assert(MI.getNumDefs() == 1 && "Expected only one def?");
Builder.setInstr(MI);
Builder.buildFConstant(MI.getOperand(0), C);
MI.eraseFromParent();
return true;
}
bool CombinerHelper::replaceInstWithConstant(MachineInstr &MI, int64_t C) {
assert(MI.getNumDefs() == 1 && "Expected only one def?");
Builder.setInstr(MI);
Builder.buildConstant(MI.getOperand(0), C);
MI.eraseFromParent();
return true;
}
bool CombinerHelper::replaceInstWithUndef(MachineInstr &MI) {
assert(MI.getNumDefs() == 1 && "Expected only one def?");
Builder.setInstr(MI);
Builder.buildUndef(MI.getOperand(0));
MI.eraseFromParent();
return true;
}
bool CombinerHelper::tryCombine(MachineInstr &MI) {
if (tryCombineCopy(MI))
return true;

View File

@ -167,16 +167,6 @@ define void @nonpow2_load_narrowing() {
ret void
}
; FALLBACK-WITH-REPORT-ERR: remark: <unknown>:0:0: cannot select: %{{[0-9]+}}:gpr64(s64), %{{[0-9]+}}:gpr(s1) = G_UADDE %{{[0-9]+}}:gpr, %{{[0-9]+}}:gpr, %{{[0-9]+}}:gpr (in function: nonpow2_store_narrowing)
; FALLBACK-WITH-REPORT-ERR: warning: Instruction selection used fallback path for nonpow2_store_narrowing
; FALLBACK-WITH-REPORT-OUT-LABEL: nonpow2_store_narrowing:
define void @nonpow2_store_narrowing(i96* %c) {
%a = add i128 undef, undef
%b = trunc i128 %a to i96
store i96 %b, i96* %c
ret void
}
; Currently can't handle vector lengths that aren't an exact multiple of
; natively supported vector lengths. Test that the fall-back works for those.
; FALLBACK-WITH-REPORT-ERR-G_IMPLICIT_DEF-LEGALIZABLE: (FIXME: this is what is expected once we can legalize non-pow-of-2 G_IMPLICIT_DEF) remark: <unknown>:0:0: unable to legalize instruction: %1:_(<7 x s64>) = G_ADD %0, %0 (in function: nonpow2_vector_add_fewerelements

View File

@ -0,0 +1,168 @@
# 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: uitofp_to_zero
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: uitofp_to_zero
; CHECK: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 0.000000e+00
; CHECK: $s0 = COPY [[C]](s32)
; CHECK: RET_ReallyLR implicit $s0
%0:_(s32) = G_IMPLICIT_DEF
%1:_(s32) = G_UITOFP %0(s32)
$s0 = COPY %1(s32)
RET_ReallyLR implicit $s0
...
---
name: sitofp_to_zero
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: sitofp_to_zero
; CHECK: [[C:%[0-9]+]]:_(s32) = G_FCONSTANT float 0.000000e+00
; CHECK: $s0 = COPY [[C]](s32)
; CHECK: RET_ReallyLR implicit $s0
%0:_(s32) = G_IMPLICIT_DEF
%1:_(s32) = G_SITOFP %0(s32)
$s0 = COPY %1(s32)
RET_ReallyLR implicit $s0
...
---
name: and_to_zero
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: and_to_zero
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
; CHECK: $w0 = COPY [[C]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_AND %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: mul_to_zero
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: mul_to_zero
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 0
; CHECK: $w0 = COPY [[C]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_MUL %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: or_to_negative_one
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: or_to_negative_one
; CHECK: [[C:%[0-9]+]]:_(s32) = G_CONSTANT i32 -1
; CHECK: $w0 = COPY [[C]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_OR %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: xor_to_undef
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: xor_to_undef
; CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; CHECK: $w0 = COPY [[DEF]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_XOR %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: add_to_undef
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: add_to_undef
; CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; CHECK: $w0 = COPY [[DEF]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_ADD %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: sub_to_undef
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: sub_to_undef
; CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; CHECK: $w0 = COPY [[DEF]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_CONSTANT i32 10
%1:_(s32) = G_IMPLICIT_DEF
%2:_(s32) = G_SUB %0, %1
$w0 = COPY %2(s32)
RET_ReallyLR implicit $w0
...
---
name: fptoui_to_undef
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: fptoui_to_undef
; CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; CHECK: $w0 = COPY [[DEF]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_IMPLICIT_DEF
%1:_(s32) = G_FPTOUI %0(s32)
$w0 = COPY %1(s32)
RET_ReallyLR implicit $w0
...
---
name: fptosi_to_undef
alignment: 4
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: name: fptosi_to_undef
; CHECK: [[DEF:%[0-9]+]]:_(s32) = G_IMPLICIT_DEF
; CHECK: $w0 = COPY [[DEF]](s32)
; CHECK: RET_ReallyLR implicit $w0
%0:_(s32) = G_IMPLICIT_DEF
%1:_(s32) = G_FPTOSI %0(s32)
$w0 = COPY %1(s32)
RET_ReallyLR implicit $w0
...