Question

Consider the following x86 code example:

#include <stdlib.h>

static int i;

static inline __attribute__((always_inline)) test(int x)
{
    asm volatile("mov %1, %0" : "=r"(i): "i"(x));
}

int main(void)
{
    test(5);

    return i;
}

If I build it with:

gcc -O test.c

It builds fine.

If I build it with (no optimization):

gcc test.c

It fails during assembly phase, because the value '5' is not propagated as an immediate value to the inline function test, so we fail the constraint.

I wish to be able to compile this code without turning on other non related optimization, in order to make debugging easier.

In theory, -O is simply a short cut to enable a bunch of GCC optimization options at once that are documented in the fine GCC manual. Unfortunately, I was not able to find the specific GCC flag that turns this behavior on.

Any ideas?

Clarification: To relieve any doubt, the code snippet is just an example. It does not make much sense by itself except to show what I am trying to do. The actual use case involves an instruction on a custom processor that can only take an immediate as an argument which I am trying to wrap in a C construct. A macro will indeed do the trick, but suffers from all the usual draw backs of a macro, hence I am trying to avoid it.

Update: For those who wondered, a macro wont work either. It seems the inline function doesn't play a part here at all. E.g., this doesn't work either:

void foo (void)
{
  int i = 6;

  asm volatile ("" : : "i" (i));
}

I also fixed the question title to reflect this.

Was it helpful?

Solution

Looks like -ftree-ter (Replace temporary expressions in the SSA->normal pass - whatever that is) does the trick:

gcc -ftree-ter test.c   # no errors

Here's how I determined that:

  1. gcc -Q --help=optimizers tells you what optimizations are enabled/disabled by default (some are enabled)
  2. gcc -O -Q --help=optimizers tells you what optimizations are enabled/disabled for -O
  3. redirect the output of those commands to files and diff them.
  4. try the optimizations that are enabled only when -O is specified until one works.

OTHER TIPS

Probably you are just abusing the "i" constraint. If you don't optimize there is no way for the compiler to "know" that this will be an immediate at the end.

I think you just should let gcc do the work to decide how much this can be optimised. I'd just use "g" as a constraint instead of "i". I am quite sure that when you compile with optimization on, everything will resolve fine to an immediate. But you'd better check the assembler that is produced to be sure.

always_inline is a strange attribute, very GCC specific, and possibly GCC version specific (so the detailed behavior might not be the same with GCC 4.5 and with GCC 4.7).

GCC is working by running a lot of optimization passes (even in -O0 some of these passes are running, otherwise no code would be emitted). Typically a GCC -O1 compilation is running two hundred optimization passes.

With gcc-4.7 your code don't even compile in -O0:

alw.c: In function ‘main’:
alw.c:7:5: warning: asm operand 1 probably doesn’t match constraints [enabled by default]
alw.c:7:5: error: impossible constraint in ‘asm’

To understand more what GCC is doing, you could run it with gcc -fdump-tree-alland you'll get a so called "dump file" (a textual representation of some of the internal representations transformed by a pass) for most GCC passes. Beware, you'll get hundred[s] of such dump files (and sadly, the number inside the name of dump files is not significant).

I can't understand why you want to do that. I suggest either making your test a macro, or always optimize (recent GCC deal quite well with both -g and -O1).

A possible alternative could be to extend GCC with a plugin, or better, a MELT extension (MELT is a high level domain specific language to extend GCC, implemented as a GPLv3 licensed GCC plugin). Then you could make your test function your own GCC builtin, since GCC can be extended to add builtins and pragmas. Your extension will then install your specific builtins and insert some specific passes to handle them appropriately. (This means several days of work, even if you know well GCC internals). Notice that builtins are commonly used to interface extra target processor specific instructions (just like your use case).

Recent GCC (notably 4.6 and 4.7) accept plugins (if they have been configured with --enable-plugins). Check with gcc -v if your particular GCC is accepting plugins. Some distributions dislike the GCC plugin idea (e.g. Suse & perhaps Redhat) so don't contain a GCC accepting plugins.

If your particular Linux distribution (a recent one) does not support yet GCC plugins, I suggest you to open a bug report to request plugins to be enabled inside GCC. If your GCC cross-compiler supplier don't support plugins, I also suggest you to query that feature, which exists in the FSF GNU Gcc since several years, e.g. since 4.5!

On Debian or Ubuntu, I suggest installing the gcc-4.6-plugin-dev or gcc-4.7-plugin-dev package. You'll then be able to build and use the MELT plugin (I am working to release MELT 0.9.6 for GCC 4.6 & 4.7 very soon, i.e. in july 2012).

Most recent distributions (Debian, Ubuntu, Mandriva, ArchLinux, ...) with a GCC 4.6 or 4.7 have a GCC extensible with plugins. MELT is such a plugin (it is a meta plugin, because melt.so is doing itself some dlopen). Once you have a GCC accepting plugins and have installed some plugin (e.g. MELT), using it is just running gcc -fplugin=melt with some other plugin specific options (e.g. -fplugin-arg-melt-mode=your-melt-mode for MELT).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top