[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