1

How to tell the GCC compiler that code should be generated serial, i.e., without jumps.

I'm working on a project that embeds inline assembly into a C source code (or LLVM IR).

My implementation depends on code between the inline assembly to be written into the executable as-is.

More formally, suppose I have the soure code (C or LLVM IR):

.label_start: (inserted as inline assembly)
inline_assembly0
source_code0
source_code1
inline_assembly1
...
.label_end: (inserted as inline assembly)
...

Now, this should not be compiled as:

.jmp_target:
source_code1
inline_assembly1
...
.label_end: (inserted as inline assembly)
...

.label_start: (inserted as inline assembly)
inline_assembly0
source_code0
jmp jmp_target

I.e. code should stay between labels without jumps reordering .label_start and .label_end.

Is there any way of telling GCC that everything between two inline assembly labels should stay "intact" without being reordered? My implementation depends on this.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Shuzheng
  • 11,288
  • 20
  • 88
  • 186

2 Answers2

4

If I understand your question, the GCC manual has a few words on this (emphasis added).

Note that the compiler can move even volatile asm instructions relative to other code, including across jump instructions. For example, on many targets there is a system register that controls the rounding mode of floating-point operations. Setting it with a volatile asm, as in the following PowerPC example, does not work reliably.

asm volatile("mtfsf 255, %0" : : "f" (fpenv));
sum = x + y;

The compiler may move the addition back before the volatile asm. To make it work as expected, add an artificial dependency to the asm by referencing a variable in the subsequent code, for example:

asm volatile("mtfsf 255,%1" : "=X" (sum) : "f" (fpenv));
sum = x + y;

Basically, you need a “dummy use” to prevent reordering.

We also use this sort of thing in Mono to extend the liveness of a reference in low-level GC code, ensuring it won’t be prematurely freed if the GC interrupts a routine:

static inline void dummy_use (void *v)
    __asm__ volatile ("" : "=r"(v) : "r"(v));
}
Community
  • 1
  • 1
Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • 1
    This deals with order of execution, but the question is discussing memory layout – Ben Voigt May 11 '17 at 06:21
  • Thanks, this was very informative! But does it ensure memory layout? – Shuzheng May 11 '17 at 06:36
  • 2
    @Shuzheng: Not really, as Ben Voigt says, it only lets you ensure that the code executes in a certain order. For example, a cold branch might still be moved out of line. In general I guess you need to do something like `#pragma GCC push_options`, `#pragma GCC optimize ("O0")`, …, `#pragma GCC pop_options` to locally disable all optimisations. (`-fno-reorder-blocks` might be enough.) You could also use inline assembly for everything in these blocks, if that’s feasible for you. – Jon Purdy May 11 '17 at 06:53
  • @JonPurdy - it isn't, because some code between labels may be LLVM IR or C. So, I would need to convert source to inline assembly. Also, inline assembly for everything may still be reordered, right? (Using jumps) – Shuzheng May 11 '17 at 06:59
1

try to disable optimization, gcc -O0 source.c

sailfish009
  • 2,561
  • 1
  • 24
  • 31