GCC PowerPC avoiding .rodata section for floats
I'm writing C
code and compile it for the PowerPC
architecture. That said C
code contains floating point variable constants which I want to be placed in the .text
section instead of .rodata
so the function code is self-contained.
The problem with this is that in PowerPC
, the only way to move a floating point value into a floating point register is by loading it from memory. It is an instruction set restriction.
To convince GCC
to help me, I tried declaring the floats as static const
. No difference. Using pointers, same results. Using __attribute__((section(".text")))
for the function, same results and for each floating point variable individually:
error: myFloatConstant causes a section type conflict with myFunction
I also tried disabling optimizations via #pragma GCC push_options
#pragma GCC optimize("O0")
and #pragma GCC pop_options
. Plus pretending I have an unsigned int
worked:
unsigned int *myFloatConstant = (unsigned int *) (0x11000018);
*myFloatConstant = 0x4C000000;
Using the float:
float theActualFloat = *(float *) myFloatConstant;
I still would like to keep -O3
but it again uses .rodata
so a potential answer would include which optimization flag causes the floats to be placed in .rodata
since starting from -O1
this is happening?
Best case scenario would be that I can use floats "normally" in the code plus maximum optimizations and they never get placed in .rodata
at all.
What I imagine GCC
to possibly do is placing the float constant in-between the code by mixing data and code, loading from that place into a floating point register and continue. This is possible to write manually I believe but how to make GCC
do that? Forcing the attribute per variable causes the error from above but technically this should be feasible.
Using GCC 7.1.0 powerpc-eabi (cross compiler under Linux) the following code worked for me:
float test(void)
{
int x;
volatile float y;
float theActualFloat;
*(float *)&x = 1.2345f;
*(int *)&y = x;
theActualFloat = y;
return theActualFloat;
}
Resulting assembly code:
test:
stwu 1,-24(1)
lis 9,0x3f9e
ori 9,9,0x419
stw 9,8(1)
lfs 1,8(1)
addi 1,1,24
blr
Explaination:
In the line *(float *)&x = value
you write to an integer which will be optimized by the compiler. The compiler will perform an integer operation which does not access floating point values in .rodata
.
The line *(int *)&y = x
is a pure integer operation anyway.
The line theActualFloat = y
cannot be optimized due to the volatile
so the compiler has to write the integer to the variable on the stack and it has to read the result from the variable.
I found another solution which avoids stack frame
creation and .rodata
usage but requires an absolute memory address to store the float in:
static inline volatile float *getFloatPointer(int address, float value) {
float *pointer = (float *) address;
*pointer = value;
return pointer;
}
It is used like this:
volatile float *myFloat = getFloatPointer(0x12345678, 30.f);
printf("%f", *myFloat);
It is important to not make a local float
variable, only volatile
pointers so it won't use .rodata
again.