Skip to content

LoopUnroll: runtime check introduces branch on poison if fn call doesn't return #51012

Description

@nunoplopes
Bugzilla Link 51670
Version trunk
OS All
CC @atrick,@meheff,@nikic

Extended Description

Test: Transforms/LoopUnroll/loop-remarks.ll

In the test below, @​baz may not return. In that case, the source function will not branch on the possibly poison %exitcond (poison if %n poison).
The target function always branches on %n regardless of whether @​baz returns or not.
The fix is to freeze %n if the code doesn't provably reach the branch.

define i32 @runtime(i32 %n) {
%entry:
  br label %for.body

%for.body:
  %s.06 = phi i32 [ 0, %entry ], [ %add1, %for.body ]
  %i.05 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
  %add = add nsw i32 %i.05, 4
  %call = call i32 @baz(i32 %add)
  %add1 = add nsw i32 %call, %s.06
  %inc = add nsw i32 %i.05, 1
  %exitcond = icmp eq i32 %inc, %n
  br i1 %exitcond, label %for.end, label %for.body

%for.end:
  ret i32 %add1
}
=>
define i32 @​runtime(i32 %n) {
%entry:
  %0 = add i32 %n, 4294967295
  %xtraiter = and i32 %n, 3
  %1 = icmp ult i32 %0, 3
  br i1 %1, label %for.end.unr-lcssa, label %entry.new

%entry.new:
  %unroll_iter = sub i32 %n, %xtraiter
  br label %for.body

%for.body:
  %s.06 = phi i32 [ 0, %entry.new ], [ %add1.3, %for.body ]
  %i.05 = phi i32 [ 0, %entry.new ], [ %inc.3, %for.body ]
  %niter = phi i32 [ %unroll_iter, %entry.new ], [ %niter.nsub.3, %for.body ]
  %add = add nsw i32 %i.05, 4
  %call = call i32 @​baz(i32 %add)
  %add1 = add nsw i32 %call, %s.06
  %inc = add nsw nuw i32 %i.05, 1
  %niter.nsub = sub i32 %niter, 1
  %add.1 = add nsw i32 %inc, 4
  %call.1 = call i32 @​baz(i32 %add.1)
  %add1.1 = add nsw i32 %call.1, %add1
  %inc.1 = add nsw nuw i32 %inc, 1
  %niter.nsub.1 = sub i32 %niter.nsub, 1
  %add.2 = add nsw i32 %inc.1, 4
  %call.2 = call i32 @​baz(i32 %add.2)
  %add1.2 = add nsw i32 %call.2, %add1.1
  %inc.2 = add nsw nuw i32 %inc.1, 1
  %niter.nsub.2 = sub i32 %niter.nsub.1, 1
  %add.3 = add nsw i32 %inc.2, 4
  %call.3 = call i32 @​baz(i32 %add.3)
  %add1.3 = add nsw i32 %call.3, %add1.2
  %inc.3 = add nsw i32 %inc.2, 1
  %niter.nsub.3 = sub i32 %niter.nsub.2, 1
  %niter.ncmp.3 = icmp eq i32 %niter.nsub.3, 0
  br i1 %niter.ncmp.3, label %for.end.unr-lcssa.loopexit, label %for.body

%for.end.unr-lcssa.loopexit:
  %add1.lcssa.ph.ph = phi i32 [ %add1.3, %for.body ]
  %s.06.unr.ph = phi i32 [ %add1.3, %for.body ]
  %i.05.unr.ph = phi i32 [ %inc.3, %for.body ]
  br label %for.end.unr-lcssa

%for.end.unr-lcssa:
  %add1.lcssa.ph = phi i32 [ undef, %entry ], [ %add1.lcssa.ph.ph, %for.end.unr-lcssa.loopexit ]
  %s.06.unr = phi i32 [ 0, %entry ], [ %s.06.unr.ph, %for.end.unr-lcssa.loopexit ]
  %i.05.unr = phi i32 [ 0, %entry ], [ %i.05.unr.ph, %for.end.unr-lcssa.loopexit ]
  %lcmp.mod = icmp ne i32 %xtraiter, 0
  br i1 %lcmp.mod, label %for.body.epil.preheader, label %for.end

%for.body.epil.preheader:
  br label %for.body.epil

%for.body.epil:
  %add.epil = add nsw i32 %i.05.unr, 4
  %call.epil = call i32 @​baz(i32 %add.epil)
  %add1.epil = add nsw i32 %call.epil, %s.06.unr
  %inc.epil = add nsw i32 %i.05.unr, 1
  %epil.iter.sub = sub i32 %xtraiter, 1
  %epil.iter.cmp = icmp ne i32 %epil.iter.sub, 0
  br i1 %epil.iter.cmp, label %for.body.epil.1, label %for.end.epilog-lcssa

%for.body.epil.1:
  %add.epil.1 = add nsw i32 %inc.epil, 4
  %call.epil.1 = call i32 @​baz(i32 %add.epil.1)
  %add1.epil.1 = add nsw i32 %call.epil.1, %add1.epil
  %inc.epil.1 = add nsw i32 %inc.epil, 1
  %epil.iter.sub.1 = sub i32 %epil.iter.sub, 1
  %epil.iter.cmp.1 = icmp ne i32 %epil.iter.sub.1, 0
  br i1 %epil.iter.cmp.1, label %for.body.epil.2, label %for.end.epilog-lcssa

%for.body.epil.2:
  %add.epil.2 = add nsw i32 %inc.epil.1, 4
  %call.epil.2 = call i32 @​baz(i32 %add.epil.2)
  %add1.epil.2 = add nsw i32 %call.epil.2, %add1.epil.1
  br label %for.end.epilog-lcssa

%for.end.epilog-lcssa:
  %add1.lcssa.ph1 = phi i32 [ %add1.epil, %for.body.epil ], [ %add1.epil.1, %for.body.epil.1 ], [ %add1.epil.2, %for.body.epil.2 ]
  br label %for.end

%for.end:
  %add1.lcssa = phi i32 [ %add1.lcssa.ph, %for.end.unr-lcssa ], [ %add1.lcssa.ph1, %for.end.epilog-lcssa ]
  ret i32 %add1.lcssa
}
Transformation doesn't verify!
ERROR: Source is more defined than target

Example:
i32 %n = poison

Source:
i32 %s.06 = #x00000000 (0)
i32 %i.05 = #x00000000 (0)
i32 %add = #x00000004 (4)
i32 %call = poison
i32 %add1 = poison
i32 %inc = #x00000001 (1)
i1 %exitcond = poison

Target:
i32 %0 = poison
i32 %xtraiter = poison
i1 %1 = poison

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions