Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to instruct VC++ compiler to not inline a constant?

I have the following global constant in my C++ program:

const int K = 123456 ;

When I compile the program, the resulting executable contains the literal value 123456 in all the places where the value is used (dozens of times).

But, if I remove the const qualifier, the value 123456 appears only once in the entire executable (in the .data section).
This is the result I'm looking for. I want the value 123456 to appear only once so that it can be changed simply by editing the .exe file with a HEX editor.

However, I don't want to remove the const qualifier because I want the compiler to prevent me from accidentally modifying the constant in the source code.

Is it possible to instruct the compiler somehow to not inline the value of said constant?


The reason I need to do this is so that the executable is easily modifiable by students who will be tasked with "cracking" an example program to alter its behavior. The exercise must be simple enough for inexperienced people.

like image 440
Richt Avatar asked Apr 20 '19 07:04

Richt


2 Answers

If you don't want K to be inlined then put this in a header file:

extern const int K;

This means "K is defined somewhere else". Then put this in a cpp file:

const int K = 123456;

In all the places where K is used, the compiler only knows that K is a const int declared externally. The compiler doesn't know the value of K so it cannot be inlined. The linker will find the definition of K in the cpp file put it in the .data section of the executable.

Alternatively, you could define K like this:

const volatile int K = 123456;

This means "K might magically change so you better not assume its value". It has a similar effect to the previous approach as the compiler won't inline K because it can't assume that K will always be 123456. The previous approach would fail if LTO was enabled but using volatile should work in that case.

I must say, this is a really weird thing to do. If you want to make your program configurable, you should put the value of K into a text file and then read the file at startup.

like image 120
Indiana Kernick Avatar answered Nov 16 '22 10:11

Indiana Kernick


The simplest option is probably to declare it as global without const, so the compiler can't assume that it still has the value of the static initializer.

int K = 123456;

Even link-time optimization can't know that a library function doesn't access this global, assuming you call any in your program.

If your used static int K = 123456;, the compiler could notice that no functions in the compilation unit write the value, and none of them pass or return its address, so escape analysis for the whole compilation unit could discover that it was effectively a constant and could be optimized away.

(If you really wanted it to be static int K;, include a global function like void setK(int x){K=x;} that you never actually call. Without Link-Time Optimization, the compiler will have to assume that something outside this compilation unit could have called this function and changed K, and that any call to a function whose definition isn't visible might result in such a call.)


Beware that volatile const int K = 123456; can hurt optimization significantly more than making it non-const, especially if you have expressions that use K multiple times.

(But either of these can hurt a lot, depending on what optimizations were possible. Constant-propagation can be a huge win.)

The compiler is required to emit asm that loads exactly K once for each time the C abstract machine reads it. (e.g. reading K is considered a visible side-effect, like a read from an MMIO port or a location you have a hardware watchpoint on.)

If you want to let a compiler load it once per loop, and assume K is a loop invariant, then code that uses it should do int local_k = K;. It's up to you how often you want to re-read K, i.e. what scope you do / redo local_k = K at.

On x86, using a memory source operand that stays hot in L1d cache is probably not much of a performance problem, but it will prevent auto-vectorization.

The reason I need to do this is so that the executable is easily modifiable by students who will be tasked with "cracking" an example program to alter its behavior. The exercise must be simple enough for inexperienced people.

For this use-case, yes volatile is exactly what you want. Having all uses re-read from memory on the spot makes it slightly simpler than following the value cached in a register.

And performance is essentially irrelevant, and you won't want auto-vectorization. Probably just light optimization so the students don't have to wade through store/reload of everything after every C++ statement. Like gcc's -Og would be ideal.

With MSVC, maybe try -O1 or -O2 and see if it does anything confusing. I don't think it has options for some but not too aggressive optimization, it might be either debug build (nice for single-stepping the C++ source, bad for reading asm), or fully optimized for size or speed.

like image 34
Peter Cordes Avatar answered Nov 16 '22 10:11

Peter Cordes