Gerhardh
Gerhardh

Reputation: 12404

Prevent compiler optimization on static struct variable

In our project, we are using ticlang compiler, i.e. a flavor of clang from TI. Optimization is set to level -Os.

In the code we have variables that have a struct type and are only used within a C file and hence are defined as static struct_type_xy variable;

The compiler performs some optimization where the members of such a struct are not kept in sequence in one block of memory but are re-ordered and even split.

This means that while debugging such variables cannot be displayed properly. Of course, I could define them as volatile but that would also prevent optimizing multiple accesses to same members which I don't want to happen.

Therefore I want to prevent this kind of optimization.

What is the name of such an optimization and how can I disable it in clang?

I don't have a MCVE yet but I can provide a few details:

typedef struct
{
  Command_t      Command; // this is an enum type
  int            Par_1;   // System uses 32 bit integers.
  int            Par_2;
  int            Par_3;
  int            Par_4;
  size_t         Num_Tok;
} Cmd_t;

static Cmd_t     Cmd;

The map file then contains:

                  20000540    00000004     Cmd.o (.bss.Cmd.1)
                  20000544    00000004     Cmd.o (.bss.Cmd.2)
                  20000548    00000004     Cmd.o (.bss.Cmd.5)
                  2000054c    00000004     HAL_*
                  ...
                  2000057b    00000001     XY_*
                  2000057c    00000001     Cmd.o (.bss.Cmd.0)

The parts of Cmd are split accross the memory and some are even removed. (I used a bulid configuration where the missing 2 members are not used but the struct definition is identical for all configurations)

If I remove static this changes to

                  200004c4    00000018     (.common:Cmd)

Upvotes: 3

Views: 987

Answers (1)

Peter Cordes
Peter Cordes

Reputation: 364287

Clang is apparently scalarizing the static struct, breaking it up into separate members, since the address is never taken or used, and doesn't escape the compilation unit. This lets it optimize away unused members.

LLVM has a "Scalar Replacement of Aggregates" (sroa) optimization pass. https://llvm.org/docs/Passes.html#sroa-scalar-replacement-of-aggregates (The alloca mentioned in that doc is an LLVM IR instruction, not the C alloca() function. Also, google found a random copy of the LLVM source that implements this while I was trying to find the right search terms.)

clang -O3 -Rpass=sroa might print a "remark" for each struct it optimizes, if that pass supports optimization reports.

According to Clang optimization levels, -sroa is enabled at -O1 and higher. But -sroa isn't a clang option, nor it an LLVM option for clang -mllvm -sroa. In 2011, someone asked about adding a command-line option to disable an arbitrary optimization pass; IDK if any feature ever got added.


clang -cc1 -mllvm -help-list-hidden does show some interesting option names, like --stop-before=<pass-name> and --start-after=<pass-name>, and there's a --sroa-strict-inbounds.

clang -mllvm --sroa-strict-inbounds -O1 does actually compile, but I don't know what it does.

clang -mllvm --stop-before=sroa -O3 hello.c doesn't work on my system with clang 13. Or with --stop-before=-sroa. I get error in backend: "sroa" pass is not registered.

So I don't know how to actually disable this optimization pass, but that's almost certainly the one responsible. This is as far as I've gotten.

It's enabled at -O1, so it's not viable to use a lower optimization level and enabling the other optimization flags that normally implies. -O0 is special, and marks everything as optnone, to make sure code-gen is suitably literal, storing/reloading everything between C statements.

Upvotes: 2

Related Questions