Can someone please clarify whether (and why) a function can be attributed pure or const if it has a pointer parameter.
According to the GCC documentation:
Some of common examples of pure functions are strlen or memcmp.
The whole point of a pure function is that it need only be called once for the same parameters, i.e. the result can be cached if the compiler thinks it fit to do so, however how does this work for memcmp?
for example:
char *x = calloc(1, 8);
char *y = calloc(1, 8);
if (memcmp(x, y, 8) > 0)
printf("x > y\n");
x[1] = 'a';
if (memcmp(x, y, 8) > 0)
printf("x > y\n");
The parameters to the second call to memcmp are identical to the first (the pointers point to the same address), how does the compiler know not to use the result from the first call, if memcmp is pure?
In my case I want to pass an array to a pure function, and calculate the result based on the array alone. Someone reassure me that this is okay, and that when values in the array change but the address does not, my function will be called correctly.
If I understood the documentation correctly, a pure function can depend on values of the memory, where the compiler knows whenever the memory changes. Moreover, a pure function can not change the state of the program, such as a global variable, it only produces a return value.
In your example code, memcmp can be a pure function. The compiler sees that the memory is changed between the calls to memcmp, and can not reuse the result of the first call for the second call.
On the other hand, memcmp can not be declared as a const function, since it depends on data in memory.
If it was const, the compiler could apply more aggressive optimizations.
For this reason, it seems safe to declare the function that you want to implement as pure (but not const).
With respect to pure we can see from the article Implications of pure and constant functions that pure means that the function does not have side effects and only depends on the parameters.
So if the compiler can determine that the arguments are the same, and memory has not changed between subsequent calls it can eliminate the subsequent calls to the pure function since it knows the pure function does not have side effects.
Which means the compiler has to do analysis to be able to determine if the arguments to the pure function could have been modified before it can decide to eliminate subsequent calls to a pure function for the same arguments.
An example from the article is as follows:
int someimpurefunction(int a);
int somepurefunction(int a)
__attribute__((pure));
int testfunction(int a, int b, int c, int d) {
int res1 = someimpurefunction(a) ? someimpurefunction(a) : b;
int res2 = somepurefunction(a) ? somepurefunction(a) : c;
int res3 = a+b ? a+b : d;
return res1+res2+res3;
}
and it shows the optimized assembly generated which shows that somepurefunction was only called once and then says:
As you can see, the pure function is called just once, because the two references inside the ternary operator are equivalent, while the other one is called twice. This is because there was no change to global memory known to the compiler between the two calls of the pure function (the function itself couldn't change it – note that the compiler will never take multi-threading into account, even when asking for it explicitly through the -pthread flag), while the non-pure function is allowed to change global memory or use I/O operations.
This logic also applies to a pointer, so if the compiler can prove the memory pointed to the pointer has not been modified then it can eliminate the call to the pure function so in your case when the compiler sees:
x[1] = 'a';
it can not eliminate the second call to memcmp because memory pointed to by x has changed.
Related
Analyzing the question on its low level, when a function returns a value, it is returned either in a cpu register, or in a space allocated on the stack previously by the caller.
At this point the calling function can take the value and copy it into its local variable.
int sum(int a,int b){return a + b;}
int main(){int risultato = sum(10,20);return 0;}
in this case the sum function returns the value in the EAX register. Then the main function copies the value from the eax register into a memory location on the stack.
This is what really happens.
Moving now to the abstraction of C ++, if I tried to do an operation like this:
sum (10.20) = 4;
it gives me an error.
This is because basically the function is not returning the memory location in which the value is contained, but the value itself.
Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value.
The issue becomes completely different when the dereferencing operator * is used.
In this case, it will not be returned a value, but the memory location itself (l-value), which will therefore be assignable.
Is what I wrote correct?
Let's take now this second exemple.
class class1 {public: int i; int b; class1(int i,int b) { this->i = i;this->b = b; }};
class1 fun() { class1 c(10,5); return c; }
int main() {fun().i = 4; return 0;}
in this case the function returns an object.
If I try to execute an instruction like this:
fun (). i = 4; I always get an error.
I know that when the function is called a temporary object is created on the stack.
Returning the function an object , but not as a variable (l-value), but as a set of values, it will not be possible to assign one of these with the value 4.
The same problem also seems to exist with this statement here:
class1(10,20).i = 4;
In this case I am creating a temporary object, I don't understand why it doesn't give me the possibility to assign the object's variable i, why in this case is it always interpreted as an r-value and not as an l-value?
I know that what I am doing has no use in practice, but it remains a purely theoretical question, which I need to understand the syntax of language correctly.
Could you comment everything I have said so far, expressing your point of view, and trying to answer the final question?
Moving now to the abstraction of C ++, if I tried to do an operation like this: sum (10.20) = 4; it gives me an error. This is because basically the function is not returning the memory location in which the value is contained, but the value itself. Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value. The issue becomes completely different when the dereferencing operator * is used. In this case, it will not be returned a value, but the memory location itself (l-value), which will therefore be assignable.
Is what I wrote correct?
Kind of. You say
This is because basically the function is not returning the memory location in which the value is contained
But that is not what happens. An object is returned, that object has a value. What makes it an rvalue is that the function "returns by value" (another name for makes a temporary object).
Being therefore an r-value, this will not be assignable, since it is not possible to assign a value to another value
This is only true for built in types. The assignment operator of built in types requires that the object being assigned to be an lvalue. If you have a user defined type (class, struct) then you can assign to an rvalue.
In this case I am creating a temporary object, I don't understand why it doesn't give me the possibility to assign the object's variable i, why in this case is it always interpreted as an r-value and not as an l-value?
The reason is that with operator . if the object you call it on is an rvalue, then the member you access is treated as an rvalue. Since i is a built in type, and an rvalue, you can't assign to it.
I know that what I am doing has no use in practice
This is the answer to your question:
Why in this case is it always interpreted as an r-value and not as an l-value?
It's harder to implement the compiler if it needs to make this an L-Value, and since it has no use, it's not worth the trouble.
There are some things that are just for the convenience of compiler writers.
#NathanOliver answered the C++ abstract machine part. I'll just add a note about how that maps to asm.
Then the main function copies the value from the eax register into a memory location on the stack.
Or not, if the optimizing compiler just keeps risultato in a register like EAX. Or optimizes it away completely because in this case it's unused.
In abstract C every object has a memory address (except for register int foo variables), but in practice unless you disable optimization variables only have addresses if the compiler runs out of registers.
The return-value object is in EAX.
Notice that mainstream C++ calling conventions only ever return trivially-copyable objects in registers. A non-trivial constructor or destructor will force even a struct of one member to be returned by hidden pointer, to make sure the constructor and destructor have a consistent this. (Calling convention rules can't depend on the content of the constructor and destructor functions, just whether either is defined at all.)
GCC can suggest functions for attribute pure and attribute const with the flags -Wsuggest-attribute=pure and -Wsuggest-attribute=const.
The GCC documentation says:
Many functions have no effects except the return value and their return value depends only on the parameters and/or global variables. Such a function can be subject to common subexpression elimination and loop optimization just as an arithmetic operator would be. These functions should be declared with the attribute pure.
But what can happen if you attach __attribute__((__pure__)) to a function that doesn't match the above description, and does have side effects? Is it simply the possibility that the function will be called fewer times than you would want it to be, or is it possible to create undefined behaviour or other kinds of serious problems?
Similarly for __attribute__((__const__)) which is stricter again - the documentation states:
Basically this is just slightly more strict class than the pure attribute below, since function is not allowed to read global memory.
But what can actually happen if you attach __attribute__((__const__)) to a function that does access global memory?
I would prefer technical answers with explanations of actual possible scenarios within the scope of GCC / G++, rather than the usual "nasal demons" handwaving that appears whenever undefined behaviour gets mentioned.
But what can happen if you attach __attribute__((__pure__))
to a function that doesn't match the above description,
and does have side effects?
Exactly. Here's a short example:
extern __attribute__((pure)) int mypure(const char *p);
int call_pure() {
int x = mypure("Hello");
int y = mypure("Hello");
return x + y;
}
My version of GCC (4.8.4) is clever enough to remove second call to mypure (result is 2*mypure()). Now imagine if mypure were printf - the side effect of printing string "Hello" would be lost.
Note that if I replace call_pure with
char s[];
int call_pure() {
int x = mypure("Hello");
s[0] = 1;
int y = mypure("Hello");
return x + y;
}
both calls will be emitted (because assignment to s[0] may change output value of mypure).
Is it simply the possibility that the function will be called fewer times
than you would want it to be, or is it possible to create
undefined behaviour or other kinds of serious problems?
Well, it can cause UB indirectly. E.g. here
extern __attribute__((pure)) int get_index();
char a[];
int i;
void foo() {
i = get_index(); // Returns -1
a[get_index()]; // Returns 0
}
Compiler will most likely drop second call to get_index() and use the first returned value -1 which will result in buffer overflow (well, technically underflow).
But what can actually happen if you attach __attribute__((__const__))
to a function that does access global memory?
Let's again take the above example with
int call_pure() {
int x = mypure("Hello");
s[0] = 1;
int y = mypure("Hello");
return x + y;
}
If mypure were annotated with __attribute__((const)), compiler would again drop the second call and optimize return to 2*mypure(...). If mypure actually reads s, this will result in wrong result being produced.
EDIT
I know you asked to avoid hand-waving but here's some generic explanation. By default function call blocks a lot of optimizations inside compiler as it has to be treated as a black box which may have arbitrary side effects (modify any global variable, etc.). Annotating function with const or pure instead allows compiler to treat it more like expression which allows for more aggressive optimization.
Examples are really too numerous to give. The one which I gave above is common subexpression elimination but we could as well easily demonstrate benefits for loop invariants, dead code elimination, alias analysis, etc.
What is the difference in generated code when I have the definition of a variable at the top of a function and when I have it declared later. Example:
int f(int parameter) {
int a = parameter * 2;
if (a == 4)
return 1;
int b = parameter * 4;
if (b == 4)
return 2;
return 0;
}
Does output code have b variable initialization and allocation after if (a == 4) or will a and b variables be initialized at the same moment?.
To see what actually happens, look at the generated assembler.
In terms of allocating space, most compilers will allocate enough space on the stack for all the variables used in a function at the start of the function. (This doesn't have to happen like this, but I don't know of any compiler which don't work like this.)
In terms of initializations, the abstract machine (as defined by the C and C++ standards) treats the initializations (that is, setting the initial values) as happening at different times. b is initialized after the comparison of a with 4.
Of course, by the as-if rule, if the initialization doesn't have side-effects, the compiler can move the initialization round as it sees fit. This happens more often with C than with C++, because C++ initializers often involve constructors in other translation units, and the compiler can't see if there are side effects there.
In this simple case if you optimize, it is quite likely that both a and b will only ever be stored in a register, and this may well be the same register. (This is because you are using plain int variables, and because you don't overlap the use of a and b.)
I know that where possible you should use the const keyword when passing parameters around by reference or by pointer for readability reasons. Is there any optimizations that the compiler can do if I specify that an argument is constant?
There could be a few cases:
Function parameters:
Constant reference:
void foo(const SomeClass& obj)
Constant SomeClass object:
void foo(const SomeClass* pObj)
And constant pointer to SomeClass:
void foo(SomeClass* const pObj)
Variable declarations:
const int i = 1234
Function declarations:
const char* foo()
What kind of compiler optimizations each one offers (if any)?
Source
Case 1:
When you declare a const in your program,
int const x = 2;
Compiler can optimize away this const by not providing storage for this variable; instead it can be added to the symbol table. So a subsequent read just needs indirection into the symbol table rather than instructions to fetch value from memory.
Note: If you do something like:
const int x = 1;
const int* y = &x;
Then this would force compiler to allocate space for x. So, that degree of optimization is not possible for this case.
In terms of function parameters const means that parameter is not modified in the function. As far as I know, there's no substantial performance gain for using const; rather it's a means to ensure correctness.
Case 2:
"Does declaring the parameter and/or the return value as const help the compiler to generate more optimal code?"
const Y& f( const X& x )
{
// ... do something with x and find a Y object ...
return someY;
}
What could the compiler do better? Could it avoid a copy of the parameter or the return value?
No, as argument is already passed by reference.
Could it put a copy of x or someY into read-only memory?
No, as both x and someY live outside its scope and come from and/or are given to the outside world. Even if someY is dynamically allocated on the fly within f() itself, it and its ownership are given up to the caller.
What about possible optimizations of code that appears inside the body of f()? Because of the const, could the compiler somehow improve the code it generates for the body of f()?
Even when you call a const member function, the compiler can't assume that the bits of object x or object someY won't be changed. Further, there are additional problems (unless the compiler performs global optimization): The compiler also may not know for sure that no other code might have a non-const reference that aliases the same object as x and/or someY, and whether any such non-const references to the same object might get used incidentally during the execution of f(); and the compiler may not even know whether the real objects, to which x and someY are merely references, were actually declared const in the first place.
Case 3:
void f( const Z z )
{
// ...
}
Will there be any optimization in this?
Yes because the compiler knows that z truly is a const object, it could perform some useful optimizations even without global analysis. For example, if the body of f() contains a call like g( &z ), the compiler can be sure that the non-mutable parts of z do not change during the call to g().
Before giving any answer, I want to emphasize that the reason to use or not use const really ought to be for program correctness and for clarity for other developers more so than for compiler optimizations; that is, making a parameter const documents that the method will not modify that parameter, and making a member function const documents that that member will not modify the object of which it is a member (at least not in a way that logically changes the output from any other const member function). Doing this, for example, allows developers to avoid making unnecessary copies of objects (because they don't have to worry that the original will be destroyed or modified) or to avoid unnecessary thread synchronization (e.g. by knowing that all threads merely read and do not mutate the object in question).
In terms of optimizations a compiler could make, at least in theory, albeit in an optimization mode that allows it to make certain non-standard assumptions that could break standard C++ code, consider:
for (int i = 0; i < obj.length(); ++i) {
f(obj);
}
Suppose the length function is marked as const but is actually an expensive operation (let's say it actually operates in O(n) time instead of O(1) time). If the function f takes its parameter by const reference, then the compiler could potentially optimize this loop to:
int cached_length = obj.length();
for (int i = 0; i < cached_length; ++i) {
f(obj);
}
... because the fact that the function f does not modify the parameter guarantees that the length function should return the same values each time given that the object has not changed. However, if f is declared to take the parameter by a mutable reference, then length would need to be recomputed on each iteration of the loop, as f could have modified the object in a way to produce a change in the value.
As pointed out in the comments, this is assuming a number of additional caveats and would only be possible when invoking the compiler in a non-standard mode that allows it to make additional assumptions (such as that const methods are strictly a function of their inputs and that optimizations can assume that code will never use const_cast to convert a const reference parameter to a mutable reference).
Function parameters:
const is not significant for referenced memory. It's like tying a hand behind the optimizer's back.
Suppose you call another function (e.g. void bar()) in foo which has no visible definition. The optimizer will have a restriction because it has no way of knowing whether or not bar has modified the function parameter passed to foo (e.g. via access to global memory). Potential to modify memory externally and aliasing introduce significant restrictions for optimizers in this area.
Although you did not ask, const values for function parameters does allow optimizations because the optimizer is guaranteed a const object. Of course, the cost to copy that parameter may be much higher than the optimizer's benefits.
See: http://www.gotw.ca/gotw/081.htm
Variable declarations: const int i = 1234
This depends on where it is declared, when it is created, and the type. This category is largely where const optimizations exist. It is undefined to modify a const object or known constant, so the compiler is allowed to make some optimizations; it assumes you do not invoke undefined behavior and that introduces some guarantees.
const int A(10);
foo(A);
// compiler can assume A's not been modified by foo
Obviously, an optimizer can also identify variables which do not change:
for (int i(0), n(10); i < n; ++i) { // << n is not const
std::cout << i << ' ';
}
Function declarations: const char* foo()
Not significant. The referenced memory may be modified externally. If the referenced variable returned by foo is visible, then an optimizer could make an optimization, but that has nothing to do with the presence/absence of const on the function's return type.
Again, a const value or object is different:
extern const char foo[];
The exact effects of const differ for each context where it is used. If const is used while declaring an variable, it is physically const and potently resides in read-only memory.
const int x = 123;
Trying to cast the const-ness away is undefined behavour:
Even though const_cast may remove constness or volatility from any pointer or reference, using the resulting pointer or reference to write to an object that was declared const or to access an object that was declared volatile invokes undefined behavior. cppreference/const_cast
So in this case, the compiler may assume that the value of x is always 123. This opens some optimization potential (constants propagation)
For functions it's a different matter. Suppose:
void doFancyStuff(const MyObject& o);
our function doFancyStuff may do any of the following things with o.
not modify the object.
cast the constness away, then modify the object
modify an mutable data member of MyObject
Note that if you call our function with an instance of MyObject that was declared as const, you'll invoke undefined behavior with #2.
Guru question: will the following invoke undefined behavior?
const int x = 1;
auto lam = [x]() mutable {const_cast<int&>(x) = 2;};
lam();
SomeClass* const pObj creates a constant object of pointer type. There exists no safe method of changing such an object, so the compiler can, for example, cache it into a register with only one memory read, even if its address is taken.
The others don't enable any optimizations specifically, although the const qualifier on the type will affect overload resolution and possibly result in different and faster functions being selected.
I have a code.
class A
{
public:
int foo(int i)
{
return i;
}
};
int foo(int i)
{
return i;
}
int (A::*ptrFoo)(int) = NULL;
int (*_foo)(int) = NULL;
int main()
{
ptrFoo = &A::foo;
_foo = foo;
(*_foo)++++++++++++++(10); //This dont compile...
A a;
(a.*ptrFoo)+++++++++++++++++(10); //This compiles ????
}
please tell me what it is ?? a undefined behavior or what ??? I compiled it in VS2008.Strangely the last line of code compiles successfully.
Neither expression should compile: in C++, you cannot perform arithmetic on a pointer to a function or member function, or on a function type or member function. The two expressions in your program attempt to perform arithmetic on a function and on a member function, respectively.
If your compiler accepts the second expression, it is due to a bug in the compiler.
First note that pointer to functions are different with pointer to member functions.
Your first example is a pointer to an ordinary function. It contains the real memory address of the function. When you dereference it ((*_foo)) you get the function itself, and arithmetic operations including ++ on a function (function pointer) are meaningless.
The second one is another story, pointers to member functions of classes do not carry the address of the function in memory. Actually how compiler manages member functions is implementation-specific. A pointer to a member function may contain some address or maybe some compiler-specific information. Arithmetic on this type is also meaningless.
Therefore we don't know what the value of (a.*ptrFoo) ever is, but in your case MSVC2008 managed to compiler it, either because of a bug or by design.
By the way, GCC does not compile any of the two statements and threw errors on both.
The above is true whether you put even number of +'s or odd numbers; we are doing arithmetic anyway. (If there are an odd number of +'s then there is no function call, as in your second example you are incrementing the function 8 times then the last remaining + adds 10 to the result. Again, this doesn't matter: we are trying to change a function/member function pointer.)