Adding option to LLVM opt to disable a specific pass from command line

Hi all,

I would like to add an option for LLVM ‘opt’ to disable a specific optimization pass from command line.

The idea is to have something like:

opt -O2 -disable-pass=[passname,…]

Do you think it could be useful ? How should I proceed to develop it and commit changes to LLVM trunk ?
Thanks for your advices and recommandations.
Best Regards
Seb

Hello,

Hi all,

I would like to add an option for LLVM 'opt' to disable a specific optimization pass from command line.

The idea is to have something like:

opt -O2 -disable-pass=[passname,...]

Do you think it could be useful ?

I have few questions :

- Why (and when) would you us this ?
- Some of the passes are executed multiple times, how would you select which invocation to disable ? Or would you disable all invocation of such passes ?
- Some passes are required by another passes. In such cases PassManager will insist on running them, which may conflict with the user request from command line. Who wins?
- Why not update the list passes run as part of -O2 (use --debug-pass=Arguments to get it) to remove selected passes and run opt <my list of passes> ... ?

Hello Devang,

answers are interleaved

2011/12/7 Devang Patel <dpatel@apple.com>

Hello,

Hi all,

I would like to add an option for LLVM ‘opt’ to disable a specific optimization pass from command line.

The idea is to have something like:

opt -O2 -disable-pass=[passname,…]

Do you think it could be useful ?

I have few questions :

  • Why (and when) would you us this ?

I woudl use this to selectively disable passes in opt that are either redundant with passes performed by the fron-end I’m working on our exhibit a BUG in opt. For instance, I figured out that loop-idiom pass has a BUG in LLVM 2.9, a llvm.memcpy is generated for an overlapping memory region and then x86 backend reorder loads/store thus generating a BUG.

So my use would be to disable all loop-idiom invocations.

  • Some of the passes are executed multiple times, how would you select which invocation to disable ? Or would you disable all invocation of such passes ?

By default I guess I would like to disable all invocation of such passes. We could also imagine to have an option -disable-pass=[pass_name:inv_num, …] to deal with multiple invocation.

  • Some passes are required by another passes. In such cases PassManager will insist on running them, which may conflict with the user request from command line. Who wins?

How to I know that a pass is required by another ?
Let’s say give priority to pass manager and issue a warning like like
‘option -disable-pass=<pass_name> ignored because required by pass <other_pass>.’

  • Why not update the list passes run as part of -O2 (use --debug-pass=Arguments to get it) to remove selected passes and run opt … ?

I also thought about this but it can lead to long command lines and source of errors.

Hello,

    - Some passes are required by another passes. In such cases
    PassManager will insist on running them, which may conflict with the
    user request from command line. Who wins?

How to I know that a pass is required by another ?
Let's say give priority to pass manager and issue a warning like like
'option -disable-pass=<pass_name> ignored because required by pass
<other_pass>.'

I would opt for disabling dependent passes as well, and providing a warning that they have been disabled implicitly (possibly giving the disabled pass that caused this effect).

I think such behavior would make sense if you were disabling passes because they contain bugs.

Another option would be to differentiate between a strong (disabling pass + dependents) and weak (disabling pass if no dependents) disable parameter.

- Roel

For instance, I figured out that loop-idiom pass has a BUG in
LLVM 2.9, a llvm.memcpy is generated for an overlapping memory region and
then x86 backend reorder loads/store thus generating a BUG.

Just for the record it seems this is a bug in your frontend, not in
the LLVM backend. The memcpy intrinsic, like the standard memcpy
function, requires that the regions be non-overlapping:
http://llvm.org/docs/LangRef.html#int_memcpy By violating this
contract it's possible you'll encounter all sorts of issues in other
passes too.

- David

David,

I think my explanation is not clear, my front-end did NOTt generate ‘llvm.memcpy’ it generate LL code that after use of LLVM ‘opt’ get transformed by ‘loop-idom’ pass into an ‘llvm.memcpy’ for an overlapping loop:

static void
t0(int n)
{
int i;
for (i=0; i<n; i++)
result[i+1] = result[i];
}

Then ‘llc’ expanded llvm.memcpy into a sequence of load/store that where performed out-of-order and thus the final code was incorrect.
So to sumarize, BUG was not in my front-end, it was in LLVM ‘opt’ loop-idom pass, it seems now fixed into 3.0. However I think I would be a good idea to add more control from command line on pass we want to disable.

Best Regards
Seb

2011/12/8 David Blaikie <dblaikie@gmail.com>

Do you really want to assign result[0] to everything?

I wonder how much work it is to each the loop-idiom pass to handle this
and the case of reverse indices correctly, if result is char *. E.g.
create either memset or memmove...

Joerg

2011/12/9 Joerg Sonnenberger <joerg@britannica.bec.de>

I think my explanation is not clear, my front-end did NOTt generate
‘llvm.memcpy’ it generate LL code that after use of LLVM ‘opt’ get
transformed by ‘loop-idom’ pass into an ‘llvm.memcpy’ for an overlapping
loop:

static void
t0(int n)
{
int i;
for (i=0; i<n; i++)
result[i+1] = result[i];
}

Do you really want to assign result[0] to everything?

I wonder how much work it is to each the loop-idiom pass to handle this
and the case of reverse indices correctly, if result is char *. E.g.
create either memset or memmove…

Joerg

This thread is not to discuss how relevant this example is. I just would like to know:
a) If people think that adding an option to disable a specific pass is useful.
b) To discuss implementation details (disable all pass invocations, what to do when there are dependencies between passes invocations).
c) If I implement it, what’s the process to get my change merge with trunk.

Now for my own purpose, I commented out loop-idiom invocation in LLVM 2.9 sources and it worked well. I just wanted to develop something generic that could benefit LLVM community.

Best Regards
Seb

By the way, CLANG 2.9 produce same behavior as my front-end:

Try clang

clang -O2 -S -emit-llvm rec.c -o rec.ll

on following example:

/*— Recurrence recognition. */

static int expect = {
1, 1, 1, 1 /* t0: 0 - 3 */
};

extern void check() ;

#define N sizeof(expect) / sizeof(int)

static int result[N];

static void
t0(int n)
{
int i;
for (i=0; i<n; i++)
result[i+1] = result[i];
}

void
test() {
result[0] = 1;
t0(3);

check(result, expect, N);
}

LL file generated is:
; ModuleID = ‘ka50.c’
target datalayout = “e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64”
target triple = “x86_64-unknown-linux-gnu”

@result = internal global [4 x i32] zeroinitializer, align 16
@expect = internal global [4 x i32] [i32 1, i32 1, i32 1, i32 1], align 16

define void @test() nounwind {
store i32 1, i32* getelementptr inbounds ([4 x i32]* @result, i64 0, i64 0), align 16, !tbaa !0
tail call void @llvm.memcpy.p0i8.p0i8.i64(i8* bitcast (i32* getelementptr inbounds ([4 x i32]* @result, i64 0, i64 1) to i8*), i8* bitcast ([4 x i32]* @result to i8*), i64 12, i32 4, i1 false) nounwind
tail call void (…)* @check(i32* getelementptr inbounds ([4 x i32]* @result, i64 0, i64 0), i32* getelementptr inbounds ([4 x i32]* @expect, i64 0, i64 0), i64 4) nounwind
ret void
}

declare void @check(…)

Then assembly produced is:

test: # @test
.Leh_func_begin0:

BB#0:

pushq %rbp
.Ltmp0:
movq %rsp, %rbp
.Ltmp1:
movl $1, result(%rip)
movl result+8(%rip), %eax
movl %eax, result+12(%rip)
movq result(%rip), %rax
movq %rax, result+4(%rip)
movl $result, %edi
movl $expect, %esi
movl $4, %edx
xorb %al, %al
callq check
popq %rbp
ret

As you can see resul+8 is read before being written and thus the problem.
Hope that clarifies the problem.

2011/12/9 Seb <babslachem@gmail.com>

Yes, a peer of mine recently wanted such an option to disable individual passes in order to 1) narrow in on a bug in one of them that affected stack back tracing, and 2) provide a workaround for the bug. He didn't pursue modifying code to remove the passes one at a time and thus the bug remains hidden.

Tom.

Hi Tom,

This thread is not to discuss how relevant this example is. I just would
like to know:
a) If people think that adding an option to disable a specific pass is
useful.

Yes, a peer of mine recently wanted such an option to disable individual
passes in order to 1) narrow in on a bug in one of them that affected
stack back tracing, and 2) provide a workaround for the bug. He didn't
pursue modifying code to remove the passes one at a time and thus the
bug remains hidden.

you don't have to modify code to reduce the list of passes. Get clang (or
whatever) to produce unoptimized bitcode. Check that running
   opt -std-compile-opts
(or opt -O3) or whatever reproduces the problem. Adding -debug-pass=Arguments
will show you the list of passes used. You can then run opt with this explicit
list of passes (rather than -std-compile-opts), and remove passes until you find
the problematic one. Bugpoint can do this for you automatically.

Ciao, Duncan.