Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the right way to create a constant pool for inline assembly?

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));
like image 444
Nikolai Avatar asked Mar 03 '15 13:03

Nikolai


People also ask

What is inline assembly explain with an example?

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.

What is __ asm __ in C?

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.

What is asm function in C?

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.


1 Answers

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));
like image 104
artless noise Avatar answered Oct 25 '22 13:10

artless noise