Optimization Levels Result Differently

Hello all,

LLVM version 20.0.0git

I try to compile a C code using clang with optimization levels, O1, O2 and O3. However, when I directly convert the code to executable results in different file than when I convert the code to LLVM IR and then the executable. That is; when I have the following commands

clang -O1 exampleProgram.c -o executable1.out

and

clang -O1 -S -emit-llvm exampleProgram.c -o exampleProgram.ll
clang exampleProgram.ll -o executable2.out

executable1.out and executable2.out are different. What is the reason for that?

Also, when I use a single optimization such as loop unroll, I know that it may not be applied for some reason. But how can I know for sure if it is applied or not? Is there an option for me to see which optimizations are applied when a code is compiled?

Thanks in advance.

Zuhal

Usually this means the input program has some undefined behavior. Sometimes (rarely) it indicates a compiler bug.

I recommend trying out optimization remarks: Clang Compiler User’s Manual — Clang 20.0.0git documentation

e.g. -Rpass=* -Rpass-missed=* -Rpass-analysis=*

Dear Mr. Roelofs,

Thank you so much for your answer.

I have a follow up question regarding the second question above.

What I try to do is actually to see the affects of individual optimizations on some benchmark applications. I used clang to convert the C code to LLVM IR:

clang -O0 -S -emit-llvm example_program.c -o unoptimized_example_program.ll

Later, I try to add individual optimizations with opt:

opt -S -passes=‘loop-unroll’ unoptimized_example_program.ll -o optimized_example_program.ll

However, unoptimized_example_program.ll and optimized_example_program.ll files are identical.

I have tried to add -disable-O0-optnone option to the clang command above as recommended here, but it changed nothing.

Then, I added pragmas as #pragma unroll as recommended here. It changed the resulting ll file. However, it does not print loop unroll when I compile it using the -Rpass=.* option. Hence, I assume it is not actually applied.

Later, I added additional optimizations (mem2reg,loop-simplify,loop-rotate) as written here but again, it changed nothing.

I have the same problem with other optimizations such as loop vectorization, enabling unsafe fp math and function inlining. What can I do to enforce an optimization? I see unrolling is applied to the code when I compile it with -O3 -Rpass=.* options.

Thanks in advance.

My best guess is that you’re missing passes that canonicalize the IR into a form that the loop passes expect, but it’s hard to say what specifically you’re running into without concrete examples.

Hey Zuhal, to check if the issue is that no passes are being applied (rather than a problem with loop forms), you can try running the mem2reg pass alone on an IR that contains alloca instructions.

The IR should include simple stack allocations (alloca) with corresponding store and load operations. After applying the mem2reg pass, the resulting IR should no longer have any alloca instructions.

If the alloca instructions are still present in the output IR, it indicates that the mem2reg pass (and probably other passes) is not being correctly applied, and something in the pipeline might be misconfigured.

Also did you build llvm locally on your machine or you used a package manager?

Dear Mr. Amodu,

Thank you so much for your answer.

I have tried mem2reg pass using opt on an IR containing alloca instructions and it changed nothing in the file. Does this mean there is something wrong with my LLVM configuration? I have installed LLVM using the instructions here.