16

If I do

typedef void Cb();

int foo(int const& a, Cb cb) {
  int x = a;
  cb();
  return x - a;
}

and compile with g++ -O3 -save-temps -c foo.cpp, I see that the subtraction is preserved, whereas if cb(); is commented out, the entire function optimizes to

xorl    %eax, %eax

Is there something I can do to the specification of the parameter a so that the subtraction will be optimized out regardless of the call to cb(), and without forcing a to be a unique reference (ie, that it may be referred to elsewhere, but that via none of those references will it be modified)?

Owen
  • 38,836
  • 14
  • 95
  • 125
  • 11
    You could take the parameter by value (`int a`) which will assure the compiler that nothing can change it. I'm not sure what you mean by a "unique reference" either. – cdhowie Dec 04 '14 at 16:50
  • what happens with const int x = a? – oblitum Dec 04 '14 at 16:52
  • 1
    @pepper_chico `a` can still be a reference to something that potentially is changed by call to `cb()`, so the subtraction still can't be optimized out. – Felix Glas Dec 04 '14 at 16:53
  • Think about what it means to pass an `int` by `const &`. A reference is a pointer underneath the hood so you are passing a pointer to an `int` into the function. Since a pointer type is often larger than an `int` you are using up more space for absolutely no advantage. – sjdowling Dec 04 '14 at 16:58
  • 3
    @sjdowling Nothing in the standard requires that references be implemented the same way pointers are; a reference is just another name for an object. – cdhowie Dec 04 '14 at 17:00
  • 1
    There is the concept of "pure" functions, but it is not standardized. gcc has an extension for that purpose: http://stackoverflow.com/q/9441262 The result is, as expected, the same as if there's no function call: http://coliru.stacked-crooked.com/a/0639d89c013ae02f – dyp Dec 04 '14 at 17:55

5 Answers5

15

Doing the suggested optimization would be incorrect because the rest of the code might be:

static int var;

void func()
{
    var++;
}

// ...
foo(var, func);

I'm not aware of any compiler-specific attribute you can set to say that cb() will not modify a.

M.M
  • 138,810
  • 21
  • 208
  • 365
15

There's the __restrict extension, you can try this on gcc.godbolt.org:

typedef void Cb();

int foo(const int & __restrict a, Cb cb) {
  int x = a;
  cb();
  return x - a;
}

Curiously, only clang does the optimization, gcc doesn't do it.


Notice that restrict-like aliasing is being considered to be part of the C++ standard:

Maybe in the future you can do it by the standard.

oblitum
  • 11,380
  • 6
  • 54
  • 120
  • I think `restrict` does not work here: it only says that two references in a given block scope don't alias. The problem here is that `a` might alias with a global variable, and `restrict` cannot be used for globals. See also: http://stackoverflow.com/a/30827880/895245 – Ciro Santilli OurBigBook.com Jul 02 '15 at 09:33
6

Why don't you just move the int x = a; line below the function call?

If cb() influences neither x nor a, you should be fine doing that, and I think the compiler will optimize the call again, because x cannot change between the two calls. If there is a reason why you cannot reorder these two calls, you can probably not optimize it in the first place.

This is not something you can hint the compiler to do though, since there is no way to guarantee that neither x nor a have changed after the call to cb().

Think about this as an order of read/write accesses. If no read or write access to a and x happens during cb(), you can do a manual reordering of the function-call.

If a or x is written, you cannot reorder, and the optimization would not be correct.

If x is read, you cannot reorder the function call, but if x truly is only read, you could read from a instead, and define x only after the call since it will have the same value as a, had you declared it before the call.

midor
  • 5,487
  • 2
  • 23
  • 52
5

You could use the C restrict on the reference, if your compiler supports that extension.
(Some compiler allow __restrict or __restrict__, which are part of the implementations namespace.)

That's a promise from you to the compiler that the object is not aliased anywhere, and it can thus optimize it.
If you lied to the compiler, well, you get the broken code you deserve.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118
  • Thank you, that is a useful thing to know. It seems the specification of `restrict`, if I understand correctly, requires not just that `a` won't be modified elsewhere, but that it also won't be referred to elsewhere. Ideally I would like that `a` could have many other references, none of which modify the underlying object. – Owen Dec 04 '14 at 17:06
  • 1
    Well, iff the object is not modified through any of them, it does not really matter that there are aliases. At least for the programs correctness. – Deduplicator Dec 04 '14 at 17:10
  • If I understand [this answer](http://stackoverflow.com/a/745877/371739) correctly, it could matter. – Owen Dec 04 '14 at 17:12
  • @Owen: That answer does not actually say so. See, one of the arrays he mentions which can (not) overlap is explicitly modified. – Deduplicator Dec 04 '14 at 17:14
  • gcc supports `__restrict`, but adding it in this case didn't change the compiled code at all. At least with `int foo(int const& __restrict a, Cb cb) { ... }` – Barry Dec 04 '14 at 17:15
  • @Deduplicator Yes you are right, see http://stackoverflow.com/questions/18059205/can-you-use-restrict-ed-pointers-to-access-the-same-object-in-some-cases. – Owen Dec 04 '14 at 19:21
-1

__attribute__((const)) is enough

As documented at: https://gcc.gnu.org/onlinedocs/gcc-5.1.0/gcc/Function-Attributes.html , it tells the compiler that the given function does not modify globals (although it can read them).

There is also the pure subset of const, which also forbids global reads.

Consider the following simplified example:

int __attribute__((const)) g();

int f(int a) {
  int x = a;
  g();
  return x - a;
}

On g++ 4.8 x86_64 -O3, it compiles to:

  • xor %eax,%eax with const
  • a large function that does not suppose a is not modified without const

There seems to be no finer grained attribute that says that a function does not modify a given variable as you require.

Can __attribute__((const)) be applied to function pointers?

Let's try:

int f(int& a, void __attribute__((const)) (*g)(void)) {
    int x = a;
    (*g)();
    return x - a;
}

Once again, it compiles to xor %eax,%eax, and to a large function without const, so the answer is yes.

This syntax was asked at: Function pointer to __attribute__((const)) function?

It also appeared in 2011 on the mailing list at: http://comments.gmane.org/gmane.comp.gcc.help/38052 At the time at least, it only worked for some attributes.

Can __attribute__((const)) be added to the typedef?

This works:

typedef void __attribute__((const)) (*g_t)(void);

int f(int& a, g_t g) {
    int x = a;
    (*g)();
    return x - a;
}

or:

typedef void (g_t)(void);

int f(int& a, g_t __attribute__((const)) g) {
    int x = a;
    g();
    return x - a;
}

But I couldn't find a way to both put the attribute on the typedef and pass a function, not a pointer, like:

typedef void __attribute__((const)) (g_t)(void);

GCC gives a warning saying the attribute was ignored in this case.

Community
  • 1
  • 1
Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985