Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Do GCC's function __attribute__s work with virtual functions?

The GCC C++ compiler offers a family of extensions via function attributes, such as:

int square(int) __attribute__((const));

Two attributes in particular, const and pure, allow you to declare that a function's evaluation has no side effects and depends only on its arguments (const), or only on its arguments and global variables (pure). This allows for common subexpression elimination, which may have the effect that such a function gets called fewer times than it is written in the code.

My question is whether this can be used safely, correctly and sensibly for virtual member functions:

struct Foo
{
    virtual int square(int) __attribute__((pure));   // does that make sense?
};

Does this have any sensible semantics? Is it allowed at all? Or is it just ignored? I'm afraid I can't find the answer to this in the GCC documentation.

The reason for this question is that there is a family of compiler options -Wsuggest-attribute which make GCC produce suggestions of where those attributes might be placed to improve the code. However, it seems to end up making these suggestions even for virtual functions, and I wonder whether those suggestions should be taken seriously.

like image 823
Kerrek SB Avatar asked Jun 12 '12 09:06

Kerrek SB


People also ask

Do virtual functions work with references?

A virtual function is a member function that you expect to be redefined in derived classes. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class's version of the function.

What are the rules for virtual function?

Rules for Virtual FunctionsVirtual functions cannot be static. A virtual function can be a friend function of another class. Virtual functions should be accessed using pointer or reference of base class type to achieve runtime polymorphism.

What does __ attribute __ mean in C?

The __attribute__ directive is used to decorate a code declaration in C, C++ and Objective-C programming languages. This gives the declared code additional attributes that would help the compiler incorporate optimizations or elicit useful warnings to the consumer of that code.

Which functions Cannot be virtual?

In C++, a static member function of a class cannot be virtual. Virtual functions are invoked when you have a pointer or reference to an instance of a class. Static functions aren't tied to the instance of a class but they are tied to the class.


2 Answers

G++ 4.8.1 appears to respect the pure and const function attributes on virtual member functions if and only if the function is called via a static binding.

Given the following source code:

struct Base {
    void w();
    void x() __attribute__ ((const));
    virtual void y();
    virtual void z() __attribute__ ((const));
};

struct Derived : public Base {
    void w() __attribute__ ((const));
    void x();
    virtual void y() __attribute__ ((const));
    virtual void z();
};

void example() {
    Base b, *pb;
    Derived d, *pd;
    b.w(); // called
    b.x(); // not called
    b.y(); // called
    b.z(); // not called
    pb->w(); // called
    pb->x(); // not called
    pb->y(); // called
    pb->z(); // called
    d.w(); // not called
    d.x(); // called
    d.y(); // not called
    d.z(); // called
    pd->w(); // not called
    pd->x(); // called
    pd->y(); // called
    pd->z(); // called
}

…the compiler produces the following (excerpted) assembly code:

void example() {
    Base b, *pb;
    Derived d, *pd;
    b.w(); // called
  1c:   e8 00 00 00 00          callq  21 <_Z7examplev+0x21>
    b.x(); // not called
    b.y(); // called
  21:   48 89 e7                mov    %rsp,%rdi
  24:   e8 00 00 00 00          callq  29 <_Z7examplev+0x29>
    b.z(); // not called
    pb->w(); // called
  29:   48 89 df                mov    %rbx,%rdi
  2c:   e8 00 00 00 00          callq  31 <_Z7examplev+0x31>
    pb->x(); // not called
    pb->y(); // called
  31:   48 8b 2b                mov    (%rbx),%rbp
  34:   48 89 df                mov    %rbx,%rdi
  37:   ff 55 00                callq  *0x0(%rbp)
    pb->z(); // called
  3a:   48 89 df                mov    %rbx,%rdi
  3d:   ff 55 08                callq  *0x8(%rbp)
    d.w(); // not called
    d.x(); // called
  40:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  45:   e8 00 00 00 00          callq  4a <_Z7examplev+0x4a>
    d.y(); // not called
    d.z(); // called
  4a:   48 8d 7c 24 10          lea    0x10(%rsp),%rdi
  4f:   e8 00 00 00 00          callq  54 <_Z7examplev+0x54>
    pd->w(); // not called
    pd->x(); // called
  54:   48 89 df                mov    %rbx,%rdi
  57:   e8 00 00 00 00          callq  5c <_Z7examplev+0x5c>
    pd->y(); // called
  5c:   48 8b 2b                mov    (%rbx),%rbp
  5f:   48 89 df                mov    %rbx,%rdi
  62:   ff 55 00                callq  *0x0(%rbp)
    pd->z(); // called
  65:   48 89 df                mov    %rbx,%rdi
  68:   ff 55 08                callq  *0x8(%rbp)
}
like image 68
Matt Whitlock Avatar answered Sep 20 '22 12:09

Matt Whitlock


The first question is whether these attributes even have valid semantics for virtual methods. In my opinion they do. I would expect that if a virtual function was labelled pure you would be promising the compiler that all implementations only rely on their arguments and the data in global memory (and do not change that), where data in global memory would also include the contents of the object. If a virtual function were labelled const this would mean it could depend on only on its arguments, it would not even be allowed to inspect the contents of the object. The compiler would have to enforce that all overriding virtual methods declare attributes at least as strong as their parents.

The next question is whether GCC uses these attributes to make optimisations. In the following test program you can see that version 4.6.3 does not (try compiling to assembler with -O3 and you will see the loop is unrolled).

struct A {
    virtual int const_f(int x) __attribute__((const)) = 0;
};

int do_stuff(A *a) {
    int b = 0;
    for (int i=0; i<10; i++) {
        b += a->const_f(0);
    }
    return b;
}

Even in the following program where the type is known at compile time the compiler does not optimize the loop.

struct A {
    virtual int const_f(int x) __attribute__((const)) = 0;
};

struct B : public A {
    int const_f(int x) __attribute__((const));
};

int do_stuff(B *b) {
    int c = 0;
    for (int i=0; i<10; i++) {
        c += b->const_f(0);
    }
    return c;
}

Removing the inheritance from A (and thus making the method non-virtual) allows the compiler to make the expected optimisation.

There is no standard or documentation regarding these attributes, so the best reference we can have is the implementation. As they currently have no effect I would suggest avoiding their use on virtual methods in case the behaviour changes unexpectedly in the future.

like image 20
jleahy Avatar answered Sep 21 '22 12:09

jleahy