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
9 changes: 9 additions & 0 deletions llvm/lib/Transforms/Utils/BasicBlockUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,15 @@ bool llvm::MergeBlockIntoPredecessor(BasicBlock *BB, DomTreeUpdater *DTU,
// Don't break self-loops.
if (PredBB == BB) return false;

// Don't break if both the basic block and the predecessor contain convergent
// intrinsics.
for (Instruction &I : *BB)
if (isa<ConvergenceControlInst>(I)) {
for (Instruction &I : *PredBB)
if (isa<ConvergenceControlInst>(I))
return false;
}
Comment on lines +235 to +240
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a move expensive check because it has to traverse potentially two basic blocks. We should move it later. I would make it the last check before deciding to merge.

Also the way this is written is a little unclear. First if we know that there can be at most one convergence token per basic block, we could make this two separate checks and take advantage of short-circuit evaluation:

bool HasConvergenceToken(const BasicBlock *BB) {                                                                                                                                                                                                                                               
  for (const Instruction &I : *BB)                                                                                                                                                                                                                                                             
    if (isa<ConvergenceControlInst>(I))                                                                                                                                                                                                                                                        
      return true;                                                                                                                                                                                                                                                                             
  return false;                                                                                                                                                                                                                                                                                
} 

Then the check becomes:

if (HasConvergenceToken(BB) && HasConvervenceToken(PredBB))
  return false; 


// Don't break unwinding instructions or terminators with other side-effects.
Instruction *PTI = PredBB->getTerminator();
if (PTI->isSpecialTerminator() || PTI->mayHaveSideEffects())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt < %s -S -passes=simplifycfg | FileCheck %s

declare token @llvm.experimental.convergence.entry() #0

define void @nested(i32 %tidx, i32 %tidy, ptr %array) #0 {
; CHECK-LABEL: @nested(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[TMP0:%.*]] = tail call token @llvm.experimental.convergence.entry()
; CHECK-NEXT: [[TMP1:%.*]] = or i32 [[TIDY:%.*]], [[TIDX:%.*]]
; CHECK-NEXT: [[OR_COND_I:%.*]] = icmp eq i32 [[TMP1]], 0
; CHECK-NEXT: br label [[FOR_COND_I:%.*]]
; CHECK: for.cond.i:
; CHECK-NEXT: [[TMP2:%.*]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[TMP0]]) ]
; CHECK-NEXT: br label [[FOR_COND1_I:%.*]]
; CHECK: for.cond1.i:
; CHECK-NEXT: [[CMP2_I:%.*]] = phi i1 [ false, [[FOR_BODY4_I:%.*]] ], [ true, [[FOR_COND_I]] ]
; CHECK-NEXT: [[TMP3:%.*]] = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token [[TMP2]]) ]
; CHECK-NEXT: br i1 [[CMP2_I]], label [[FOR_BODY4_I]], label [[EXIT:%.*]]
; CHECK: for.body4.i:
; CHECK-NEXT: br i1 [[OR_COND_I]], label [[IF_THEN_I:%.*]], label [[FOR_COND1_I]]
; CHECK: if.then.i:
; CHECK-NEXT: [[TEST_VAL:%.*]] = call spir_func i32 @func_test(i32 0) [ "convergencectrl"(token [[TMP3]]) ]
; CHECK-NEXT: [[TMP4:%.*]] = getelementptr inbounds i32, ptr [[ARRAY:%.*]], i32 0
; CHECK-NEXT: store i32 [[TEST_VAL]], ptr [[TMP4]], align 4
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
;
entry:
%0 = tail call token @llvm.experimental.convergence.entry()
%2 = or i32 %tidy, %tidx
%or.cond.i = icmp eq i32 %2, 0
br label %for.cond.i

for.cond.i:
%3 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %0) ]
br label %for.cond1.i

for.cond1.i:
%cmp2.i = phi i1 [ false, %for.body4.i ], [ true, %for.cond.i ]
%4 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %3) ]
br i1 %cmp2.i, label %for.body4.i, label %cleanup.i.loopexit

for.body4.i:
br i1 %or.cond.i, label %if.then.i, label %for.cond1.i

if.then.i:
%test.val = call spir_func i32 @func_test(i32 0) [ "convergencectrl"(token %4) ]
%5 = getelementptr inbounds i32, ptr %array, i32 0
store i32 %test.val, ptr %5, align 4
br label %cleanup.i

cleanup.i.loopexit:
br label %cleanup.i

cleanup.i:
br label %exit

exit:
ret void
}

declare token @llvm.experimental.convergence.loop() #0

declare i32 @func_test(i32) #0

attributes #0 = { convergent }
Loading