[llvm-dev] Early CSE clobbering llvm.assume
    Sanjoy Das via llvm-dev 
    llvm-dev at lists.llvm.org
       
    Tue Jun 14 12:49:32 PDT 2016
    
    
  
> I don’t follow your logic, you seem to be implying we don’t optimize
> property-propagation through “if-then” and “while-do” well ?
As far as property propagation is concerned, control flow is just as
good as assumes in theory (and I doubt you'll find realistic cases
where we do better with assumes than we do with control flow); but
replacing all assumes with control flow is not a good idea for other
reasons.
E.g we can LICM the load in f_0 (not today, we've regressed after some
recent changes that I'll fix right now) but not f_1 since it isn't
obvious that the load from %ptr can be speculated above the control
flow.  In this case hoisting the load is easy (since all %leave does
is "unreachable" and thus "can never happen"), but e.g. you could have
sunk a call instruction (which could internally calls exit(0)) down
that path and then it would not be so obvious to see "%leave" as a
"can never happen" branch.
declare void @llvm.assume(i1)
define void @f_0(i1 %cond, i32* %ptr) {
entry:
  br label %loop
loop:
  %x = phi i32 [ 0, %entry ], [ %x.inc, %loop ]
  call void @llvm.assume(i1 %cond)
  %val = load i32, i32* %ptr
  %x.inc = add i32 %x, %val
  br label %loop
}
define void @f_1(i1 %cond, i32* %ptr) {
entry:
  br label %loop
loop:
  %x = phi i32 [ 0, %entry ], [ %x.inc, %stay ]
  br i1 %cond, label %stay, label %leave
stay:
  call void @llvm.assume(i1 %cond)
  %val = load i32, i32* %ptr
  %x.inc = add i32 %x, %val
  br label %loop
leave:
  unreachable
}
You're right that in both the cases all (edge/call) dominated uses of
%cond can be folded to true, but changing all assumes to be control
flow can still be a net regression by breaking optimizations like
above.
Note: using pseudo abort does not save you either, since you could
start with:
for (;;) {
  // lowered assume
  some_call();
  if (!cond) {
    pseudo_abort();
    unreachable;
  }
  int x = obj->field;
}
==> sink call down both paths
for (;;) {
  // lowered assume
  if (!cond) {
    some_call();
    pseudo_abort();
    unreachable;
  }
  some_call();
  int x = obj->field;
}
and now the "failing" branch of the assume no longer just a
pseudo-abort (so the branch is necessary now).  And consider what
happens if the body of "some_call()" is basically:
void some_call() {
  while (receive_request())
    service_request();
  unreachable;
}
after inlining some_call(), the pseudo_abort() will just disappear
too!
What an intrinsic assume gives us today is by "internalizing" the
control flow it ensures that
for (;;) {
  // lowered assume
  some_call();
  if (!cond) {
    pseudo_abort();
    unreachable;
  }
  int x = obj->field;
}
can only be optimized to
for (;;) {
  // lowered assume
  if (!cond) {
    pseudo_abort();
    unreachable;
  }
  some_call();
  int x = obj->field;
}
avoiding the problem above.
-- Sanjoy
    
    
More information about the llvm-dev
mailing list