Files
clang-p2996/llvm/test/Transforms/JumpThreading/domtree-updates.ll
Bjorn Pettersson 297fb66484 Use a deterministic order when updating the DominatorTree
This solves a problem with non-deterministic output from opt due
to not performing dominator tree updates in a deterministic order.

The problem that was analysed indicated that JumpThreading was using
the DomTreeUpdater via llvm::MergeBasicBlockIntoOnlyPred. When
preparing the list of updates to send to DomTreeUpdater::applyUpdates
we iterated over a SmallPtrSet, which didn't give a well-defined
order of updates to perform.

The added domtree-updates.ll test case is an example that would
result in non-deterministic printouts of the domtree. Semantically
those domtree:s are equivalent, but it show the fact that when we
use the domtree iterator the order in which nodes are visited depend
on the order in which dominator tree updates are performed.

Since some passes (at least EarlyCSE) are iterating over nodes in the
dominator tree in a similar fashion as the domtree printer, then the
order in which transforms are applied by such passes, transitively,
also depend on the order in which dominator tree updates are
performed. And taking EarlyCSE as an example the end result could be
different depending on in which order the transforms are applied.

Reviewed By: nikic, kuhar

Differential Revision: https://reviews.llvm.org/D110292
2021-11-29 13:14:50 +01:00

112 lines
5.2 KiB
LLVM

; RUN: opt < %s -disable-output -passes='jump-threading,print<domtree>' 2>&1 | FileCheck %s
; REQUIRES: asserts
; The idea behind this test case is to verify that the dominator tree is
; updated in a deterministic way. Optimizations, at least EarlyCSE, are
; iterating the vectors that hold child nodes in the DominatorTree. Thus, the
; end result might differ depending on the order in which nodes are inserted
; in the dominator tree. Unfortunately this test case is quite large, but it
; happened to trigger a non-determinism quite often when being executed
; multipe times (it was possible to see varying results when running the test
; less that 10 times in a row).
; The actual problem was tracked down to llvm::MergeBasicBlockIntoOnlyPred, so
; the important property of the test is probably that it triggers a call to
; that function, and that the PredsOfPredBB set that is used to populate
; Updates for the DomTreeUpdater is populated with more than one entry.
; CHECK: Inorder Dominator Tree: DFSNumbers invalid: 0 slow queries.
; CHECK-NEXT: [1] %entry {4294967295,4294967295} [0]
; CHECK-NEXT: [2] %for.cond1 {4294967295,4294967295} [1]
; CHECK-NEXT: [3] %for.inc19 {4294967295,4294967295} [2]
; CHECK-NEXT: [3] %if.then {4294967295,4294967295} [2]
; CHECK-NEXT: [4] %for.cond5.preheader {4294967295,4294967295} [3]
; CHECK-NEXT: [5] %cleanup {4294967295,4294967295} [4]
; CHECK-NEXT: [6] %cleanup16 {4294967295,4294967295} [5]
; CHECK-NEXT: [7] %unreachable {4294967295,4294967295} [6]
; CHECK-NEXT: [7] %for.end21 {4294967295,4294967295} [6]
; CHECK-NEXT: [5] %for.body7 {4294967295,4294967295} [4]
; CHECK-NEXT: [6] %for.inc {4294967295,4294967295} [5]
; CHECK-NEXT: [5] %return {4294967295,4294967295} [4]
; CHECK-NEXT: [3] %cleanup16.thread {4294967295,4294967295} [2]
; CHECK-NEXT: [2] %infinite.loop {4294967295,4294967295} [1]
; CHECK-NEXT: Roots: %entry
@a = dso_local local_unnamed_addr global i16 0, align 1
; Function Attrs: nounwind
define dso_local i16 @g(i16 %a0, i16 %a1, i16 %a2, i16 %a3) local_unnamed_addr {
entry:
%tobool.not = icmp eq i16 %a0, 0
br i1 %tobool.not, label %for.cond1, label %infinite.loop
infinite.loop: ; preds = %infinite.loop, %entry
br label %infinite.loop
for.cond1: ; preds = %for.inc19, %entry
%retval.0 = phi i16 [ %retval.3, %for.inc19 ], [ undef, %entry ]
%i.0 = phi i16 [ %i.3, %for.inc19 ], [ undef, %entry ]
%tobool2.not = icmp eq i16 %a1, 0
br i1 %tobool2.not, label %if.end15, label %if.then
if.then: ; preds = %for.cond1
%tobool3.not = icmp eq i16 %a2, 0
br i1 %tobool3.not, label %if.end15, label %for.cond5.preheader
for.cond5.preheader: ; preds = %if.then
%tobool8.not = icmp eq i16 %a3, 0
%tobool6.not31 = icmp eq i16 %i.0, 0
br i1 %tobool6.not31, label %for.end10, label %for.body7
for.body7: ; preds = %for.inc, %for.cond5.preheader
%i.132 = phi i16 [ %inc, %for.inc ], [ %i.0, %for.cond5.preheader ]
br i1 %tobool8.not, label %for.inc, label %cleanup
for.inc: ; preds = %for.body7
%inc = add i16 %i.132, 1
%tobool6.not = icmp eq i16 %inc, 0
br i1 %tobool6.not, label %for.end10, label %for.body7
for.end10: ; preds = %for.inc, %for.cond5.preheader
%i.1.lcssa = phi i16 [ %i.0, %for.cond5.preheader ], [ 0, %for.inc ]
%.26 = select i1 %tobool8.not, i32 0, i32 4
br label %cleanup
cleanup: ; preds = %for.end10, %for.body7
%i.128 = phi i16 [ %i.1.lcssa, %for.end10 ], [ %i.0, %for.body7 ]
%retval.1 = phi i16 [ %retval.0, %for.end10 ], [ 1, %for.body7 ]
%cond = phi i1 [ %tobool8.not, %for.end10 ], [ false, %for.body7 ]
%cleanup.dest.slot.0 = phi i32 [ %.26, %for.end10 ], [ 1, %for.body7 ]
br i1 %cond, label %if.end15, label %cleanup16
if.end15: ; preds = %cleanup, %if.then, %for.cond1
%retval.2 = phi i16 [ %retval.1, %cleanup ], [ %retval.0, %if.then ], [ %retval.0, %for.cond1 ]
%i.2 = phi i16 [ %i.128, %cleanup ], [ %i.0, %if.then ], [ %i.0, %for.cond1 ]
store i16 0, i16* @a, align 1
br label %cleanup16
cleanup16: ; preds = %if.end15, %cleanup
%retval.3 = phi i16 [ %retval.2, %if.end15 ], [ %retval.1, %cleanup ]
%i.3 = phi i16 [ %i.2, %if.end15 ], [ %i.128, %cleanup ]
%cleanup.dest.slot.1 = phi i32 [ 0, %if.end15 ], [ %cleanup.dest.slot.0, %cleanup ]
switch i32 %cleanup.dest.slot.1, label %unreachable [
i32 0, label %for.inc19
i32 1, label %return
i32 4, label %for.end21
]
for.inc19: ; preds = %cleanup16
br label %for.cond1
for.end21: ; preds = %cleanup16
br label %return
return: ; preds = %for.end21, %cleanup16
%retval.4 = phi i16 [ 17, %for.end21 ], [ %retval.3, %cleanup16 ]
ret i16 %retval.4
unreachable: ; preds = %cleanup16
unreachable
}