Skip to content

Commit

Permalink
LICM: hoist BO assoc for and, or, xor (#111146)
Browse files Browse the repository at this point in the history
Trivially lift the Opcode limitation on hoistBOAssociation to also hoist
and, or, and xor.

Alive2 proofs: https://alive2.llvm.org/ce/z/rVNP2X
  • Loading branch information
artagnon authored Oct 4, 2024
1 parent c0f8889 commit 45817aa
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 16 deletions.
11 changes: 0 additions & 11 deletions llvm/lib/Transforms/Scalar/LICM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2820,18 +2820,7 @@ static bool hoistBOAssociation(Instruction &I, Loop &L,
if (!BO || !BO->isAssociative())
return false;

// TODO: Only hoist ADDs, MULs, FADDs, and FMULs for now.
Instruction::BinaryOps Opcode = BO->getOpcode();
switch (Opcode) {
case Instruction::Add:
case Instruction::Mul:
case Instruction::FAdd:
case Instruction::FMul:
break;
default:
return false;
}

bool LVInRHS = L.isLoopInvariant(BO->getOperand(0));
auto *BO0 = dyn_cast<BinaryOperator>(BO->getOperand(LVInRHS));
if (!BO0 || BO0->getOpcode() != Opcode || !BO0->isAssociative() ||
Expand Down
16 changes: 11 additions & 5 deletions llvm/test/CodeGen/PowerPC/p10-spill-crlt.ll
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ define dso_local void @P10_Spill_CR_LT() local_unnamed_addr {
; CHECK-NEXT: mflr r0
; CHECK-NEXT: std r0, 16(r1)
; CHECK-NEXT: stw r12, 8(r1)
; CHECK-NEXT: stdu r1, -48(r1)
; CHECK-NEXT: .cfi_def_cfa_offset 48
; CHECK-NEXT: stdu r1, -64(r1)
; CHECK-NEXT: .cfi_def_cfa_offset 64
; CHECK-NEXT: .cfi_offset lr, 16
; CHECK-NEXT: .cfi_offset r29, -24
; CHECK-NEXT: .cfi_offset r30, -16
; CHECK-NEXT: .cfi_offset cr2, 8
; CHECK-NEXT: .cfi_offset cr3, 8
; CHECK-NEXT: .cfi_offset cr4, 8
; CHECK-NEXT: std r30, 32(r1) # 8-byte Folded Spill
; CHECK-NEXT: std r29, 40(r1) # 8-byte Folded Spill
; CHECK-NEXT: std r30, 48(r1) # 8-byte Folded Spill
; CHECK-NEXT: bl call_2@notoc
; CHECK-NEXT: bc 12, 4*cr5+lt, .LBB0_13
; CHECK-NEXT: # %bb.1: # %bb
Expand Down Expand Up @@ -65,10 +67,11 @@ define dso_local void @P10_Spill_CR_LT() local_unnamed_addr {
; CHECK-NEXT: bc 12, 4*cr3+eq, .LBB0_11
; CHECK-NEXT: # %bb.6: # %bb32
; CHECK-NEXT: #
; CHECK-NEXT: rlwinm r30, r30, 0, 24, 22
; CHECK-NEXT: andi. r3, r30, 2
; CHECK-NEXT: rlwinm r29, r30, 0, 24, 22
; CHECK-NEXT: mcrf cr2, cr0
; CHECK-NEXT: bl call_4@notoc
; CHECK-NEXT: mr r30, r29
; CHECK-NEXT: beq+ cr2, .LBB0_3
; CHECK-NEXT: # %bb.7: # %bb37
; CHECK-NEXT: .LBB0_8: # %bb22
Expand All @@ -89,11 +92,13 @@ define dso_local void @P10_Spill_CR_LT() local_unnamed_addr {
; CHECK-BE-NEXT: stdu r1, -144(r1)
; CHECK-BE-NEXT: .cfi_def_cfa_offset 144
; CHECK-BE-NEXT: .cfi_offset lr, 16
; CHECK-BE-NEXT: .cfi_offset r28, -32
; CHECK-BE-NEXT: .cfi_offset r29, -24
; CHECK-BE-NEXT: .cfi_offset r30, -16
; CHECK-BE-NEXT: .cfi_offset cr2, 8
; CHECK-BE-NEXT: .cfi_offset cr2, 8
; CHECK-BE-NEXT: .cfi_offset cr2, 8
; CHECK-BE-NEXT: std r28, 112(r1) # 8-byte Folded Spill
; CHECK-BE-NEXT: std r29, 120(r1) # 8-byte Folded Spill
; CHECK-BE-NEXT: std r30, 128(r1) # 8-byte Folded Spill
; CHECK-BE-NEXT: bl call_2
Expand Down Expand Up @@ -126,11 +131,12 @@ define dso_local void @P10_Spill_CR_LT() local_unnamed_addr {
; CHECK-BE-NEXT: bc 12, 4*cr3+eq, .LBB0_11
; CHECK-BE-NEXT: # %bb.6: # %bb32
; CHECK-BE-NEXT: #
; CHECK-BE-NEXT: rlwinm r29, r29, 0, 24, 22
; CHECK-BE-NEXT: andi. r3, r29, 2
; CHECK-BE-NEXT: rlwinm r28, r29, 0, 24, 22
; CHECK-BE-NEXT: mcrf cr2, cr0
; CHECK-BE-NEXT: bl call_4
; CHECK-BE-NEXT: nop
; CHECK-BE-NEXT: mr r29, r28
; CHECK-BE-NEXT: beq+ cr2, .LBB0_3
; CHECK-BE-NEXT: # %bb.7: # %bb37
; CHECK-BE-NEXT: .LBB0_8: # %bb22
Expand Down
63 changes: 63 additions & 0 deletions llvm/test/Transforms/LICM/hoist-binop.ll
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,69 @@ loop:
br label %loop
}

; Trivially hoist and.
define void @and(i64 %c1, i64 %c2) {
; CHECK-LABEL: @and(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[INVARIANT_OP:%.*]] = and i64 [[C1:%.*]], [[C2:%.*]]
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[INDEX_NEXT_REASS]] = and i64 [[INDEX]], [[INVARIANT_OP]]
; CHECK-NEXT: br label [[LOOP]]
;
entry:
br label %loop

loop:
%index = phi i64 [ 0, %entry ], [ %index.next, %loop ]
%step.add = and i64 %index, %c1
%index.next = and i64 %step.add, %c2
br label %loop
}

; Trivially hoist or.
define void @or(i64 %c1, i64 %c2) {
; CHECK-LABEL: @or(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[INVARIANT_OP:%.*]] = or i64 [[C1:%.*]], [[C2:%.*]]
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[INDEX_NEXT_REASS]] = or i64 [[INDEX]], [[INVARIANT_OP]]
; CHECK-NEXT: br label [[LOOP]]
;
entry:
br label %loop

loop:
%index = phi i64 [ 0, %entry ], [ %index.next, %loop ]
%step.add = or i64 %index, %c1
%index.next = or i64 %c2, %step.add
br label %loop
}

; Trivially hoist xor.
define void @xor(i64 %c1, i64 %c2) {
; CHECK-LABEL: @xor(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[INVARIANT_OP:%.*]] = xor i64 [[C1:%.*]], [[C2:%.*]]
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDEX_NEXT_REASS:%.*]], [[LOOP]] ]
; CHECK-NEXT: [[INDEX_NEXT_REASS]] = xor i64 [[INDEX]], [[INVARIANT_OP]]
; CHECK-NEXT: br label [[LOOP]]
;
entry:
br label %loop

loop:
%index = phi i64 [ 0, %entry ], [ %index.next, %loop ]
%step.add = xor i64 %c1, %index
%index.next = xor i64 %step.add, %c2
br label %loop
}

; Don't hoist if the intermediate op has more than two uses. This is an
; heuristic that can be adjusted if warranted. Currently we are being
; conservative to minimise potential impact in code size.
Expand Down

0 comments on commit 45817aa

Please sign in to comment.