[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