The problem is that inside a C function I have an inline assembly. Something like
ldr r7, =0xdeadbeef
svc 0
If a literal pool wasn't created explicitly (this is the case), assembler creates one at the end of the translation unit. Usually this is fine, but if the translation unit turns out to be really huge, this doesn't work, because the literal pool is too far from the ldr instruction.
So, I wonder what is the best way to handle the problem. The most obvious way is to create a literal pool manually inside the inline assembly:
ldr r7, =0xdeadbeef
svc 0
b 1f
.ltorg
1:
Or
ldr r7, 1f
svc 0
b 2f
1:
.word 0xdeadbeef
2:
Unfortunately, this leads to a suboptimal code because of the redundant branch instruction. I don't expect assembler to be clever enough to find an appropriate place for the constant pool inside the function. What I would like to do is to create a constant pool at the end of the function. Is there any way to tell the compiler (gcc) to create a literal pool at the end of the function?
PS I ended up using movw/movt
pair instead of constant pools. Though,
firstly, the movw/movt solution is slightly less portable than literal pools and,
secondly, I simply wonder if it is possible to use constant pools in inline assembly
both reliably and efficiently.
Update: So, what is the best way to handle the problem?
To force the toolchain to create a constant pool after the function one can put the function in a separate code section. It works because at the end of a translation unit assembler generates separate constant pools for each section.
Though, in fact, the best way is to avoid loading constants into registers in inline assembly at all. It's better to let the compiler do that. In my case I eventually wrote a code similar to
register int var asm("r7") = 0xdeadbeef;
asm volatile("svc 0\n" :: "r" (var));
In computer programming, an inline assembler is a feature of some compilers that allows low-level code written in assembly language to be embedded within a program, among code that otherwise has been compiled from a higher-level language such as C or Ada.
The __asm keyword invokes the inline assembler and can appear wherever a C or C++ statement is legal. It cannot appear by itself. It must be followed by an assembly instruction, a group of instructions enclosed in braces, or, at the very least, an empty pair of braces.
The asm statement allows you to include assembly instructions directly within C code. This may help you to maximize performance in time-sensitive code or to access assembly instructions that are not readily available to C programs. Note that extended asm statements must be inside a function.
You can use -ffunction-sections
and as per query on -ffunction-section
, use a ld --gc-sections
to remove unused code.
There is the obvious of splitting up the file.
A solution that should work is to use a naked
function with an unused
annotation as it is never called. Place a single .ltorg
here and also put both functions in a special section; .text.ltorg_kludge
for instance. The linker script should use .text*
and functions in identical sub-sections are placed together. In some ways this is like splitting up the file as the compiler will try to inline static
functions.
You may rely on the compiler emitting functions as encountered in the source without a special section. However, I am not sure if this is a standard or happen-stance. Compilers may optimize better by emitting function in some DAG ordering of the call hierarchy.
Aside: movw
/movt
is more efficient due to cache effects. It is also works with ARMv6 and above Thumb2 code. I don't see portability as a big deal (as inline assembler is non-portable and you probably prefer performance over portability), but the question is relevant to ARMv4/5 users.
I investigated the use of the R constraint from gcc machine constraints,
R
An item in the constant pool
However, a sample with gcc-4.8 gives an error impossible constraint. Using alternative letters like C also give the same error message. Inspection of the source contraints.md seems to indicate that the R constraint is a documentation only feature. Unfortunate, as it sounds purpose built to solve this issue.
It is possible to have the compiler load the value, but this maybe sub-optimal depending on the inline
assembler. For example,
asm(" add %0, %0, %1\n" : "+r" (0xdeadbeef) : "r" (0xbaddeed0));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With