0

We are basically working in a sparc architecture.and using gcc to compile our code.

We are using some constants in our code, but compiler instead of allocating memory for some of these constants is instead optimizing and making it part of the code

for example

cHello : CONSTANT INTEGER_16 := 16#FFFE#;
a = cHello [ cHello is a constant ];

The assembly is as follows.

set 16#FFFE#, %g1
sthw %g1, [%g2]

compiler is putting value of cHello inline to code(.text) instead of loading from memory.

How to make compiler load constant from memory instead of putting them inline

Edit: The language is Ada

Why do i care:

Here is the problem, we have an embedded system, where our code is actually running from RAM, and we would like to have the constant modifiable. We don’t want to make it .data, as then we will have two copies of it; at power on they are copied to Variable RAM area. So constant are better in this case. Plus the RAM from where we are executing is LOCKED from writes. So we unlock it and then write to Code RAM.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
zephyr0110
  • 223
  • 1
  • 11
  • inline as an immediate, you mean? I doubt the compiler has options to make it generate worse code, and getting a small constant into a register with one ALU instruction is probably always better than a load. [SPARC immediate constants can be up to 11 bits](https://stackoverflow.com/questions/5184258/sparc-assembly-question), or the high 21 bits for `sethi`. – Peter Cordes May 18 '18 at 08:48
  • well yes, but some inlines are like, is generating set CONSTANT, %g1 type of instruction. It just saves one extra load instruction. I guess i have to either make it volatile or declare another section ! – zephyr0110 May 18 '18 at 12:25
  • You didn't specify a programming language when asking your question. I am not sure if my answer is applicable to Ada at all. For the next time, always without failure specify the programming language you are programming in. I have deleted my answer because it does not apply to Ada. – fuz May 18 '18 at 12:26
  • That is either an XY problem or you missed relevant information. **Why** do you care at all how the code is optimised? In general one should not, except for hardware peripheral registers in which case you have to use language-specific (or even compiler-specific, IDK Ada) features. Currently your question is unclear. – too honest for this site May 18 '18 at 12:28
  • @fuz: Your answer doesn't apply to C either, depending on the circunstances the compiler is very well free to change the behaviour. Also C does not have constants (other than what's commonly called "literals") at all. And fyi, it's "abstract machine", not "virtual machine". – too honest for this site May 18 '18 at 12:30
  • @Olaf Can you show me a concrete example where the compiler does not behave the way my answer indicated? Doing it anyhow else is a clear violation of the spirit of `volatile`, the whole point of which is to force the compiler to do the exact sequence of operations the programmer specified. – fuz May 18 '18 at 12:38
  • Edited my question about why do i care. – zephyr0110 May 18 '18 at 12:44
  • @fWrong place, but: 1) there is s defect report for the standard (which persists since C99 at least IIRC). 2) The compiler could ignore `volatile` e.g. for a local without the address ever taken. That's well within the abstract machine. The fact current compilers don't do it (although I had effects long time ago which make me consider the compiler did optimise them) does not mean they are not allowed to. Just be creative to develop scenarios where external effects just can't occur (`volatile` also does not mean "store in memory"). – too honest for this site May 18 '18 at 13:02
  • If you don't have a copy of the initialiser in the text, how will the RAM "constant" be initialised? You have to have two copies. In C++ a constant will have a memory address if you explicitly take its address. Not sure about Ada. But the data may still then be in ROM, an if it is not, you will still have an initial value in ROM. – Clifford May 19 '18 at 07:23
  • `pragma volatile(cHello);` is an option, and the way a read-only hardware register (e.g. an input port on an embedded microcontroller) would typically be handled. So @fuz now deleted answer wasn't so far off the mark despite the wrong syntax. But for your use case there are better answers such as Simon's and Egil's below –  May 19 '18 at 10:04

4 Answers4

2

It is working as expected. Constant variables are being allocated in the .text (or any const section), however you want to use RAM as flash, being the RAM a valued possesion. Anyway, if you do want to use the RAM to allocate constant values, you can create a new section (.myownconst) in RAM through the linker and declare your const variable as __attribute__((section(".myownconst")))

Jose
  • 3,306
  • 1
  • 17
  • 22
  • There is no guarantee where and how objects or even literals are accessed. Using sections does not change that, they are just used by the linker. But that does not mean accesses to `const` qualified objects are not e.g. converted to immediate operands in the generated code. – too honest for this site May 18 '18 at 12:34
  • @Olaf: Right, so you declare it as non-const and simply don't write any code that modifies it. Then use linker tricks to put it where you want it. (And then check that link-time optimization didn't notice that and treat it as const...) The OP is doing embedded development with a specific known toolchain, so a solution that happens to work there is probably sufficient, as long as it's not likely to break from just source changes, only from major compiler behaviour changes. I don't think there is any language support for reading once from mem, but assumed not to change across func calls. – Peter Cordes May 18 '18 at 20:34
  • @PeterCordes And what would that change? The compiler is still free to do what it wants within the rules of the language. Something like "The OP is doing embedded development with a specific known toolchain" is just calling for trouble on maintenance/extending. Embedded software is often maintain for decades which makes your advice even more dangerous. As I wrote, the question is almost certainly an XY problem. – too honest for this site May 19 '18 at 00:42
  • @Olaf: I added an answer that avoids letting the Ada compiler see the value at all, while hopefully minimizing the efficiency impact: put the value in a separately-compiled asm source, or C that you compile without LTO. – Peter Cordes May 20 '18 at 00:57
2

The syntax of this answer is going to assume a recent (GNAT, Ada2012) compiler, but I’ve no doubt you could do the same with pragmas.

From observation, GNAT will make the constant into an immediate literal if it can see it.

GNAT won’t let you make the variable both constant and volatile.

The only way I’ve found to force the constant to be fetched from store at all is to fool the compiler by making it import the variable:

with Interfaces;
package Prakhar_Constants is
private
   Chello : constant Interfaces.Integer_16 := 16#7FFE#
   with
     Export,
     External_Name => "constant_chello";
end Prakhar_Constants;

and then

with Interfaces;
with Prakhar_Constants;  -- so the binder will know to include it
procedure Prakhar is
   Chello : constant Interfaces.Integer_16
   with
     Import,
     Convention => Ada,
     External_Name => "constant_chello";
   A : Interfaces.Integer_16;
begin
   A := CHello;
end Prakhar;

I don’t think you need bother with volatile (unless you’re going to change the "constant" mid-execution).

Simon Wright
  • 25,108
  • 2
  • 35
  • 62
  • wow! exactly what I wanted. " GNAT will make the constant into an immediate literal if it can see it", this statement is what I was searching in the reference manual all over !. Is it mentioned in some document ? @simonwright – zephyr0110 May 18 '18 at 15:54
  • https://stackoverflow.com/questions/50407178/two-copies-of-constant-appearing-in-the-compiled-elf @simonwright Can you help me with this observation too ! I cant make head or tails of why would compiler do that for some constants ! and this behavior for some constants. – zephyr0110 May 18 '18 at 15:58
  • I don’t know why GNAT makes the constant into an immediate literal if it can see it; you’d have to ask the compiler people. But it probably has to do with caching (the immediate literal may be in the instruction cache??) It was just an observation, anyway, not knowledge!) – Simon Wright May 18 '18 at 16:11
1

You could try an aliased constant:

cHello : aliased constant Interfaces.Integer_16 := 16#FFFE#;
egilhh
  • 6,464
  • 1
  • 18
  • 19
0

I think you should define the value of the constant in a stand-alone asm or C file. This is a guaranteed way to stop the compiler inlining the value anywhere even with link-time optimization, without using anything as inefficient as volatile. i.e. the Ada compiler never sees the value of the constant at all, in any source file.

I don't know Ada, but the C equivalent would be extern const int my_const; and then in a separate constant.S file use .section .rodata / my_const: .long 0x12345. Or use a custom section and a linker script to get your modifiable constants placed somewhere specific in your binary.

For more about interfacing Ada with with C or asm, see https://gcc.gnu.org/onlinedocs/gcc-7.3.0/gnat_ugn/Interfacing-to-C.html. It has examples of importing and exporting symbol definitions to/from C. (And the mapping between C and asm is very simple, so you can just tell Ada that it's C even if you create the object files using asm.)

My_Num : Integer;
pragma Import (C, My_Num, "Ada_my_num");

Ideally you can declare My_Num in a way that the Ada compiler knows it's a constant. This declares it as a plain global (with an external definition using the Ada_my_num C symbol name.)

This is pretty similar to what @Jose's answer is suggesting, except using stand-alone asm to hide the value from the compiler.


You want the compiler to be able to optimize as much as possible. i.e. to assume that the value of this "variable" doesn't change during the lifetime of the program, so it can load it into a register at the start of a function and assume that calls to non-inline functions can't change it. Or to avoid redoing calculations involving it (CSE), so if your source code has
a * my_const both before and after a function call, it can save the result in a register instead of reloading the constant from memory and redoing the multiply after the function call.

This can't happen if the compiler thinks it's an ordinary global variable with unknown value; it would have to assume that the function call might have changed the value of any global variable.

But if you used an ordinary global variable and assign a value to it anywhere the Ada compiler can see, then whole-program link-time optimization could propagate that value to other places, or even bake it into other constants. (e.g. if you ever do a += 2 * my_constant, you could have 2*my_constant hard-coded somewhere in your asm output).

(e.g. if you compile+link with -flto to let the compiler optimize between compilation units. IDK if GNAT can do this the same way the C front-end can, but hopefully it can.)


Why the compiler does this: because it's more efficient, of course!

Loading a value from static data in memory typically takes multiple instructions to generate a 32-bit address (on a fixed instruction-width ISA like SPARC); with the same number of instructions you could have created an arbitrary 32-bit constant in a register directly. ALU instructions are typically cheaper than loads, and can't miss in cache.

Small constants are even more efficient, and can be used as a single immediate operand for add, or, and, or whatever.

Constant folding and constant propagation after inlining is a major way that the asm version of a program can do less work than the source. e.g. 5 * my_const can be done at compile-time if the compiler knows the value of my_const. (So it could generate that in a register directly, if needed, instead of loading my_const and using a shift/add.)

Some generic function might check if(x>0), but the compiler might be able to prove that's always true in one place where the function inlines, if it knows something about the values of constants. (That's a value-range optimization).

Denying your compiler the value of a constant can definitely make your code less efficient, depending on how you use the constant.


Example compiler output. (I assume you can write equivalent Ada which GNAT's / gcc's SPARC back-end will optimize similarly to what clang/LLVM -target sparc does). The point of this is to illustrate the difference between a constant of unknown value vs. a known constant:

(From clang6.0 -O3 -fomit-frame-pointer -target sparc on the Godbolt compiler explorer)

const int my_const1, my_const2;
static const int my_static = 123;  // a small constant that works as an immediate

int foo(int x) {
    return x + my_static;

}

    retl
    add %o0, 123, %o0         # branch-delay slot


int one_constant(int x) {
    return x + my_const1;
}

    sethi %hi(my_const1), %o1
    ld [%o1+%lo(my_const1)], %o1
    retl
    add %o1, %o0, %o0

The latter is pretty obviously less efficient. It seems that clang doesn't know if / when %hi(my_const1) and %hi(my_const2) are the same, so it uses another sethi for each static location. Ideally the compiler could use the same reference point for multiple accesses inside a large function, but that doesn't seem to be the case for clang/LLVM. Godbolt doesn't have SPARC gcc, so I couldn't try that easily.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847