From d049eef4b5ab8e7f8dcfb40406d81666d9f5dfdc Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Thu, 19 Mar 2026 15:37:43 +0000 Subject: [PATCH] [DAG] Use value tracking to detect or_disjoint patterns and add a add_like pattern matcher (#187478) Extend the generic or_disjoint pattern to call haveNoCommonBitsSet, this allows us to remove the similar x86 or_is_add pattern, use or_disjoint directly and merge some add/or_is_add matching patterns to use a add_like wrapper pattern instead --- .../include/llvm/Target/TargetSelectionDAG.td | 7 ++++- llvm/lib/Target/X86/X86InstrCompiler.td | 31 ++++++++----------- llvm/lib/Target/X86/X86InstrFragments.td | 10 ------ llvm/test/CodeGen/X86/fold-masked-merge.ll | 29 +++++++++-------- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td index d689b3c1beda..3cf110615f27 100644 --- a/llvm/include/llvm/Target/TargetSelectionDAG.td +++ b/llvm/include/llvm/Target/TargetSelectionDAG.td @@ -1310,13 +1310,18 @@ def ineg : PatFrag<(ops node:$in), (sub 0, node:$in)>; def or_disjoint : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs), [{ - return N->getFlags().hasDisjoint(); + return N->getFlags().hasDisjoint() || + CurDAG->haveNoCommonBitsSet(N->getOperand(0), N->getOperand(1)); }]> { let GISelPredicateCode = [{ return MI.getFlag(MachineInstr::Disjoint); }]; } +def add_like : PatFrags<(ops node:$lhs, node:$rhs), + [(add node:$lhs, node:$rhs), + (or_disjoint node:$lhs, node:$rhs)]>; + def xor_like : PatFrags<(ops node:$lhs, node:$rhs), [(xor node:$lhs, node:$rhs), (or_disjoint node:$lhs, node:$rhs)]>; diff --git a/llvm/lib/Target/X86/X86InstrCompiler.td b/llvm/lib/Target/X86/X86InstrCompiler.td index bc05dae7351b..ebbfa48d2660 100644 --- a/llvm/lib/Target/X86/X86InstrCompiler.td +++ b/llvm/lib/Target/X86/X86InstrCompiler.td @@ -1539,33 +1539,33 @@ let isConvertibleToThreeAddress = 1, isPseudo = 1, let isCommutable = 1 in { def ADD8rr_DB : I<0, Pseudo, (outs GR8:$dst), (ins GR8:$src1, GR8:$src2), "", // orb/addb REG, REG - [(set GR8:$dst, (or_is_add GR8:$src1, GR8:$src2))]>; + [(set GR8:$dst, (or_disjoint GR8:$src1, GR8:$src2))]>; def ADD16rr_DB : I<0, Pseudo, (outs GR16:$dst), (ins GR16:$src1, GR16:$src2), "", // orw/addw REG, REG - [(set GR16:$dst, (or_is_add GR16:$src1, GR16:$src2))]>; + [(set GR16:$dst, (or_disjoint GR16:$src1, GR16:$src2))]>; def ADD32rr_DB : I<0, Pseudo, (outs GR32:$dst), (ins GR32:$src1, GR32:$src2), "", // orl/addl REG, REG - [(set GR32:$dst, (or_is_add GR32:$src1, GR32:$src2))]>; + [(set GR32:$dst, (or_disjoint GR32:$src1, GR32:$src2))]>; def ADD64rr_DB : I<0, Pseudo, (outs GR64:$dst), (ins GR64:$src1, GR64:$src2), "", // orq/addq REG, REG - [(set GR64:$dst, (or_is_add GR64:$src1, GR64:$src2))]>; + [(set GR64:$dst, (or_disjoint GR64:$src1, GR64:$src2))]>; } // isCommutable def ADD8ri_DB : I<0, Pseudo, (outs GR8:$dst), (ins GR8:$src1, i8imm:$src2), "", // orb/addb REG, imm8 - [(set GR8:$dst, (or_is_add GR8:$src1, imm:$src2))]>; + [(set GR8:$dst, (or_disjoint GR8:$src1, imm:$src2))]>; def ADD16ri_DB : I<0, Pseudo, (outs GR16:$dst), (ins GR16:$src1, i16imm:$src2), "", // orw/addw REG, imm - [(set GR16:$dst, (or_is_add GR16:$src1, imm:$src2))]>; + [(set GR16:$dst, (or_disjoint GR16:$src1, imm:$src2))]>; def ADD32ri_DB : I<0, Pseudo, (outs GR32:$dst), (ins GR32:$src1, i32imm:$src2), "", // orl/addl REG, imm - [(set GR32:$dst, (or_is_add GR32:$src1, imm:$src2))]>; + [(set GR32:$dst, (or_disjoint GR32:$src1, imm:$src2))]>; def ADD64ri32_DB : I<0, Pseudo, (outs GR64:$dst), (ins GR64:$src1, i64i32imm:$src2), "", // orq/addq REG, imm - [(set GR64:$dst, (or_is_add GR64:$src1, - i64immSExt32:$src2))]>; + [(set GR64:$dst, (or_disjoint GR64:$src1, + i64immSExt32:$src2))]>; } } // AddedComplexity, SchedRW @@ -2230,9 +2230,9 @@ multiclass EFLAGSDefiningPats(INC8r#suffix) GR8:$src)>; - def : Pat<(add GR16:$src, 1), (!cast(INC16r#suffix) GR16:$src)>; - def : Pat<(add GR32:$src, 1), (!cast(INC32r#suffix) GR32:$src)>; + def : Pat<(add_like GR8:$src, 1), (!cast(INC8r#suffix) GR8:$src)>; + def : Pat<(add_like GR16:$src, 1), (!cast(INC16r#suffix) GR16:$src)>; + def : Pat<(add_like GR32:$src, 1), (!cast(INC32r#suffix) GR32:$src)>; def : Pat<(add GR8:$src, -1), (!cast(DEC8r#suffix) GR8:$src)>; def : Pat<(add GR16:$src, -1), (!cast(DEC16r#suffix) GR16:$src)>; def : Pat<(add GR32:$src, -1), (!cast(DEC32r#suffix) GR32:$src)>; @@ -2243,18 +2243,13 @@ multiclass EFLAGSDefiningPats(INC8r#suffix) GR8:$src)>; def : Pat<(X86sub_flag_nocf GR16:$src, -1), (!cast(INC16r#suffix) GR16:$src)>; def : Pat<(X86sub_flag_nocf GR32:$src, -1), (!cast(INC32r#suffix) GR32:$src)>; - - def : Pat<(or_is_add GR8:$src, 1), (!cast(INC8r#suffix) GR8:$src)>; - def : Pat<(or_is_add GR16:$src, 1), (!cast(INC16r#suffix) GR16:$src)>; - def : Pat<(or_is_add GR32:$src, 1), (!cast(INC32r#suffix) GR32:$src)>; } let Predicates = [UseIncDec, PredNDDI] in { - def : Pat<(add GR64:$src, 1), (!cast(INC64r#suffix) GR64:$src)>; + def : Pat<(add_like GR64:$src, 1), (!cast(INC64r#suffix) GR64:$src)>; def : Pat<(add GR64:$src, -1), (!cast(DEC64r#suffix) GR64:$src)>; def : Pat<(X86add_flag_nocf GR64:$src, -1), (!cast(DEC64r#suffix) GR64:$src)>; def : Pat<(X86sub_flag_nocf GR64:$src, -1), (!cast(INC64r#suffix) GR64:$src)>; - def : Pat<(or_is_add GR64:$src, 1), (!cast(INC64r#suffix) GR64:$src)>; } } diff --git a/llvm/lib/Target/X86/X86InstrFragments.td b/llvm/lib/Target/X86/X86InstrFragments.td index 3cd05ab0351b..bae1211e5133 100644 --- a/llvm/lib/Target/X86/X86InstrFragments.td +++ b/llvm/lib/Target/X86/X86InstrFragments.td @@ -850,16 +850,6 @@ def def32 : PatLeaf<(i32 GR32:$src), [{ (!N->getOperand(0).isUndef() && !isa(N->getOperand(0)))); }]>; -// Treat an 'or' node is as an 'add' if the or'ed bits are known to be zero. -def or_is_add : PatFrag<(ops node:$lhs, node:$rhs), (or node:$lhs, node:$rhs),[{ - if (ConstantSDNode *CN = dyn_cast(N->getOperand(1))) - return CurDAG->MaskedValueIsZero(N->getOperand(0), CN->getAPIntValue()); - - KnownBits Known0 = CurDAG->computeKnownBits(N->getOperand(0), 0); - KnownBits Known1 = CurDAG->computeKnownBits(N->getOperand(1), 0); - return (~Known0.Zero & ~Known1.Zero) == 0; -}]>; - def shiftMask8 : PatFrag<(ops node:$lhs), (and node:$lhs, imm), [{ return isUnneededShiftMask(N, 3); }]>; diff --git a/llvm/test/CodeGen/X86/fold-masked-merge.ll b/llvm/test/CodeGen/X86/fold-masked-merge.ll index 05e7b2a2de37..43db5b10a1ed 100644 --- a/llvm/test/CodeGen/X86/fold-masked-merge.ll +++ b/llvm/test/CodeGen/X86/fold-masked-merge.ll @@ -191,11 +191,12 @@ define i32 @not_a_masked_merge4(i32 %a0, i32 %a1, i32 %a2) { define i32 @masked_merge_no_transform0(i32 %a0, i32 %a1, i32 %a2, ptr %p1) { ; NOBMI-LABEL: masked_merge_no_transform0: ; NOBMI: # %bb.0: -; NOBMI-NEXT: movl %edi, %eax +; NOBMI-NEXT: # kill: def $esi killed $esi def $rsi +; NOBMI-NEXT: # kill: def $edi killed $edi def $rdi ; NOBMI-NEXT: andl %edi, %esi -; NOBMI-NEXT: notl %eax -; NOBMI-NEXT: andl %edx, %eax -; NOBMI-NEXT: orl %esi, %eax +; NOBMI-NEXT: notl %edi +; NOBMI-NEXT: andl %edx, %edi +; NOBMI-NEXT: leal (%rdi,%rsi), %eax ; NOBMI-NEXT: movl %esi, (%rcx) ; NOBMI-NEXT: retq ; @@ -218,11 +219,12 @@ define i32 @masked_merge_no_transform0(i32 %a0, i32 %a1, i32 %a2, ptr %p1) { define i32 @masked_merge_no_transform1(i32 %a0, i32 %a1, i32 %a2, ptr %p1) { ; NOBMI-LABEL: masked_merge_no_transform1: ; NOBMI: # %bb.0: -; NOBMI-NEXT: movl %edx, %eax +; NOBMI-NEXT: # kill: def $edx killed $edx def $rdx +; NOBMI-NEXT: # kill: def $esi killed $esi def $rsi ; NOBMI-NEXT: andl %edi, %esi ; NOBMI-NEXT: notl %edi -; NOBMI-NEXT: andl %edi, %eax -; NOBMI-NEXT: orl %esi, %eax +; NOBMI-NEXT: andl %edi, %edx +; NOBMI-NEXT: leal (%rdx,%rsi), %eax ; NOBMI-NEXT: movl %edi, (%rcx) ; NOBMI-NEXT: retq ; @@ -246,20 +248,21 @@ define i32 @masked_merge_no_transform1(i32 %a0, i32 %a1, i32 %a2, ptr %p1) { define i32 @masked_merge_no_transform2(i32 %a0, i32 %a1, i32 %a2, ptr %p1) { ; NOBMI-LABEL: masked_merge_no_transform2: ; NOBMI: # %bb.0: -; NOBMI-NEXT: movl %esi, %eax -; NOBMI-NEXT: andl %edi, %eax +; NOBMI-NEXT: # kill: def $esi killed $esi def $rsi +; NOBMI-NEXT: # kill: def $edi killed $edi def $rdi +; NOBMI-NEXT: andl %edi, %esi ; NOBMI-NEXT: notl %edi ; NOBMI-NEXT: andl %edx, %edi -; NOBMI-NEXT: orl %edi, %eax +; NOBMI-NEXT: leal (%rsi,%rdi), %eax ; NOBMI-NEXT: movl %edi, (%rcx) ; NOBMI-NEXT: retq ; ; BMI-LABEL: masked_merge_no_transform2: ; BMI: # %bb.0: -; BMI-NEXT: movl %esi, %eax -; BMI-NEXT: andl %edi, %eax +; BMI-NEXT: # kill: def $esi killed $esi def $rsi +; BMI-NEXT: andl %edi, %esi ; BMI-NEXT: andnl %edx, %edi, %edx -; BMI-NEXT: orl %edx, %eax +; BMI-NEXT: leal (%rsi,%rdx), %eax ; BMI-NEXT: movl %edx, (%rcx) ; BMI-NEXT: retq %and0 = and i32 %a0, %a1