Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion llvm/include/llvm/Target/GlobalISel/Combine.td
Original file line number Diff line number Diff line change
Expand Up @@ -1040,6 +1040,34 @@ def funnel_shift_overshift: GICombineRule<
(apply [{ Helper.applyFunnelShiftConstantModulo(*${root}); }])
>;

// Transform: fshl x, z, y | shl x, y -> fshl x, z, y
// Transform: shl x, y | fshl x, z, y -> fshl x, z, y
// FIXME: TableGen didn't handle G_OR commutativity on its own,
// necessitating the use of !foreach to handle it manually.
def funnel_shift_or_shift_to_funnel_shift_left_frags : GICombinePatFrag<
(outs root: $dst, $out1, $out2), (ins),
!foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)],
(pattern (G_FSHL $out1, $x, $z, $y), (G_SHL $out2, $x, $y), inst))>;
def funnel_shift_or_shift_to_funnel_shift_left: GICombineRule<
(defs root:$root),
(match (funnel_shift_or_shift_to_funnel_shift_left_frags $root, $out1, $out2)),
(apply (GIReplaceReg $root, $out1))
>;

// Transform: fshr z, x, y | srl x, y -> fshr z, x, y
// Transform: srl x, y | fshr z, x, y -> fshr z, x, y
// FIXME: TableGen didn't handle G_OR commutativity on its own,
// necessitating the use of !foreach to handle it manually.
def funnel_shift_or_shift_to_funnel_shift_right_frags : GICombinePatFrag<
(outs root: $dst, $out1, $out2), (ins),
!foreach(inst, [(G_OR $dst, $out1, $out2), (G_OR $dst, $out2, $out1)],
(pattern (G_FSHR $out1, $z, $x, $y), (G_LSHR $out2, $x, $y), inst))>;
def funnel_shift_or_shift_to_funnel_shift_right: GICombineRule<
(defs root:$root),
(match (funnel_shift_or_shift_to_funnel_shift_right_frags $root, $out1, $out2)),
(apply (GIReplaceReg $root, $out1))
>;

def rotate_out_of_range : GICombineRule<
(defs root:$root),
(match (wip_match_opcode G_ROTR, G_ROTL):$root,
Expand Down Expand Up @@ -1112,7 +1140,9 @@ def funnel_shift_combines : GICombineGroup<[funnel_shift_from_or_shift,
funnel_shift_to_rotate,
funnel_shift_right_zero,
funnel_shift_left_zero,
funnel_shift_overshift]>;
funnel_shift_overshift,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In addition to adding to funnel_shift_combines as you did here, each target also selects a set of combine rules they want to use. For instance, RISCV does this in lib/Target/RISCV/RISCVCombine.td and funnel_shift_combines is not one of them. So before writing a test, you need to import funnel_shift_combines.

Copy link
Owner Author

@axelcool1234 axelcool1234 Mar 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was writing my tests and realized "wait, they're passing and it was mentioned that RISCV doesn't use these combines, what gives?"

Upon further investigation, I found that RISCV does use the funnel shifts:
https://github.com/axelcool1234/llvm-project/blob/f1f8463c457e4b2e08a53aea62b9a47490815882/llvm/lib/Target/RISCV/RISCVCombine.td#L13C1-L16C2

This includes all_combines, which includes funnel_shift_combines:
https://github.com/axelcool1234/llvm-project/blob/f1f8463c457e4b2e08a53aea62b9a47490815882/llvm/include/llvm/Target/GlobalISel/Combine.td#L2042C1-L2042C79

It seems all of these combines are BEFORE the legalize phase (RISCVPreLegalizerCombiner) - I assume you meant that I should include the funnel shift combines during the legalization phase? Should I include the funnel shift combines in RISCVPostLegalizerCombiner (it does say // TODO: Add more combines.)? And if I do, I'm not sure how I'd test it since my tests are already passing.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume you meant that I should include the funnel shift combines during the legalization phase? Should I include the funnel shift combines in RISCVPostLegalizerCombiner

Oh, I forgot that RISC-V runs all the combiner rules pre-legalizations. And I think your combiner rule is only useful pre-legalized since RISC-V doesn't really have native support for funnel shifts, so they'll be expanded by the legalizer: https://github.com/axelcool1234/llvm-project/blob/f1f8463c457e4b2e08a53aea62b9a47490815882/llvm/lib/Target/RISCV/GISel/RISCVLegalizerInfo.cpp#L193

Bottom line: I think you're good, no need to explicitly add funnel_shift_combines into RISCVCombine.td

funnel_shift_or_shift_to_funnel_shift_left,
funnel_shift_or_shift_to_funnel_shift_right]>;

def bitfield_extract_from_sext_inreg : GICombineRule<
(defs root:$root, build_fn_matchinfo:$info),
Expand Down
24 changes: 10 additions & 14 deletions llvm/test/CodeGen/AArch64/funnel-shift.ll
Original file line number Diff line number Diff line change
Expand Up @@ -674,14 +674,12 @@ define i32 @or_shl_fshl_simplify(i32 %x, i32 %y, i32 %s) {
; CHECK-GI-LABEL: or_shl_fshl_simplify:
; CHECK-GI: // %bb.0:
; CHECK-GI-NEXT: mov w8, #31 // =0x1f
; CHECK-GI-NEXT: and w9, w2, #0x1f
; CHECK-GI-NEXT: lsr w10, w0, #1
; CHECK-GI-NEXT: lsl w11, w1, w2
; CHECK-GI-NEXT: lsr w9, w0, #1
; CHECK-GI-NEXT: and w10, w2, #0x1f
; CHECK-GI-NEXT: bic w8, w8, w2
; CHECK-GI-NEXT: lsl w9, w1, w9
; CHECK-GI-NEXT: lsr w8, w10, w8
; CHECK-GI-NEXT: orr w9, w9, w11
; CHECK-GI-NEXT: orr w0, w9, w8
; CHECK-GI-NEXT: lsl w10, w1, w10
; CHECK-GI-NEXT: lsr w8, w9, w8
; CHECK-GI-NEXT: orr w0, w10, w8
; CHECK-GI-NEXT: ret
%shy = shl i32 %y, %s
%fun = call i32 @llvm.fshl.i32(i32 %y, i32 %x, i32 %s)
Expand All @@ -702,14 +700,12 @@ define i32 @or_lshr_fshr_simplify(i32 %x, i32 %y, i32 %s) {
; CHECK-GI-LABEL: or_lshr_fshr_simplify:
; CHECK-GI: // %bb.0:
; CHECK-GI-NEXT: mov w8, #31 // =0x1f
; CHECK-GI-NEXT: and w9, w2, #0x1f
; CHECK-GI-NEXT: lsl w10, w0, #1
; CHECK-GI-NEXT: lsr w11, w1, w2
; CHECK-GI-NEXT: lsl w9, w0, #1
; CHECK-GI-NEXT: and w10, w2, #0x1f
; CHECK-GI-NEXT: bic w8, w8, w2
; CHECK-GI-NEXT: lsr w9, w1, w9
; CHECK-GI-NEXT: lsl w8, w10, w8
; CHECK-GI-NEXT: orr w9, w11, w9
; CHECK-GI-NEXT: orr w0, w9, w8
; CHECK-GI-NEXT: lsl w8, w9, w8
; CHECK-GI-NEXT: lsr w9, w1, w10
; CHECK-GI-NEXT: orr w0, w8, w9
; CHECK-GI-NEXT: ret
%shy = lshr i32 %y, %s
%fun = call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %s)
Expand Down
48 changes: 48 additions & 0 deletions llvm/test/CodeGen/RISCV/GlobalISel/shift.ll
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,51 @@ define i16 @test_shl_i48_2(i48 %x, i48 %y) {
%trunc = trunc i48 %shl to i16
ret i16 %trunc
}

define i32 @test_fshl_i32(i32 %x, i32 %_, i32 %y) {
; RV32-LABEL: test_fshl_i32:
; RV32: # %bb.0:
; RV32-NEXT: not a3, a2
; RV32-NEXT: sll a0, a0, a2
; RV32-NEXT: srli a1, a1, 1
; RV32-NEXT: srl a1, a1, a3
; RV32-NEXT: or a0, a0, a1
; RV32-NEXT: ret
;
; RV64-LABEL: test_fshl_i32:
; RV64: # %bb.0:
; RV64-NEXT: not a3, a2
; RV64-NEXT: sllw a0, a0, a2
; RV64-NEXT: srliw a1, a1, 1
; RV64-NEXT: srlw a1, a1, a3
; RV64-NEXT: or a0, a0, a1
; RV64-NEXT: ret
%fshl = call i32 @llvm.fshl.i32(i32 %x, i32 %_, i32 %y)
%shl = shl i32 %x, %y
%or = or i32 %fshl, %shl
ret i32 %or
}

define i32 @test_fshr_i32(i32 %_, i32 %x, i32 %y) {
; RV32-LABEL: test_fshr_i32:
; RV32: # %bb.0:
; RV32-NEXT: not a3, a2
; RV32-NEXT: slli a0, a0, 1
; RV32-NEXT: sll a0, a0, a3
; RV32-NEXT: srl a1, a1, a2
; RV32-NEXT: or a0, a0, a1
; RV32-NEXT: ret
;
; RV64-LABEL: test_fshr_i32:
; RV64: # %bb.0:
; RV64-NEXT: not a3, a2
; RV64-NEXT: slli a0, a0, 1
; RV64-NEXT: sllw a0, a0, a3
; RV64-NEXT: srlw a1, a1, a2
; RV64-NEXT: or a0, a0, a1
; RV64-NEXT: ret
%fshr = call i32 @llvm.fshr.i32(i32 %_, i32 %x, i32 %y)
%lshr = lshr i32 %x, %y
%or = or i32 %lshr, %fshr
ret i32 %or
}