is this compiler optimization in context of recursion? - c++

I was writing factorial using tail recursion and I have a question here. My original function looks like this
Code snippet A
#include <stdio.h>
int main(void)
{
int n = 0;
printf("Enter number to find factorial of : ");
scanf("%d",&n);
printf("fact == %d\n",fun(n,1));
return 0;
}
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
return ret;
}
However, even if I do not use return, it still works. This does not make perfect sense since I am returning the value only in the base case. Say if n==5, then 120 would be returned at the base case. But what gets returned from 4th invocation back to the 3rd invocation cannot be predicted since we are not explicitly specifying any return unlike in Code snippet A.
Code snippet B
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
}
I am thinking the above works because of some kind of compiler optimization ? Because If I add a printf statement to Code snippet B, it does not work anymore.
Code snippet C
int fun(int n, int sofar)
{
int ret = 0;
if(n == 0)
return sofar;
ret = fun(n-1,sofar*n);
printf("now it should not work\n");
}
Probably the printf causes something to be removed from the stack? Please help me understand this.

Not returning a value from a fuction which should return a value is undefined behavior.
If it works by any luck then it's not an optimization but just a coincidence given by how and where these automatic allocated values are stored.

One could speculate about the reason why this works, but there is no way to give a definitive answer: reaching the end of a value-returning function without the return statement and using the return value is undefined behavior, with or without optimization.
The reason this "works" in your compiler is that the return mechanism used by your compiler happens to have the right value at the time the end of function is reached. For example, if your compiler returns integers in the same register that has been used for the last computation in your code (i.e. ret = fun(n-1,sofar*n)) then the right value would be loaded into the return register by accident, masking undefined behavior.

It works because the return value is almost always stored in a specific CPU register (eax for x86). This means that is you don't explicitly return a value, the return register will not be explicitly set. Because of that, its value can be anything, but it is often the return value of the last called function. Thus, ending a function with myfunc() is almost guaranteed to have the same behavior as return myfunc(); (but it's still undefined behavior).

Here is the reason 'something' is calculated.
the call to printf() has a returned value (very rarely used) of the number of characters printed (including tabs, newlines, etc)
in the 'C' code snippet, printf() is returning 23.
'int' returned values are always returned in the same register.
The printf() set that register to 23.
So, something is returned, nothing was removed from the stack.

The reason why it seems to work in B is because the return value is most likely passed in a register on your architecture. So returning through all the layers of recursion (or if the compiler optimized the whole thing into iteration) nothing touches that register and your code appears to work.
A different compiler might not allow this to happen or maybe the next version of your compiler will optimize this differently. In fact, the compiler can remove most of the function because it is allowed to assume that since undefined behavior can't happen it must mean that the part of the function where the undefined behavior seems to happen will never be reached and can be safely removed.

Related

Can (a==1 && a==2 && a==3) ever evaluate to true in C or C++?

We know it can in Java and JavaScript.
But the question is, can the condition below ever evaluate to true in C or C++?
if(a==1 && a==2 && a==3)
printf("SUCCESS");
EDIT
If a was an integer.
Depends on your definition of "a is an integer":
int a__(){ static int r; return ++r; }
#define a a__() //a is now an expression of type `int`
int main()
{
return a==1 && a==2 && a==3; //returns 1
}
Of course:
int f(int b) { return b==1&&b==2&&b==3; }
will always return 0; and optimizers will generally replace the check with exactly that.
If we put macro magic aside, I can see one way that could positively answer this question. But it will require a bit more than just standard C. Let's assume we have an extension allowing to use the __attribute__((at(ADDRESS))); attribute, which is placing a variable at some specific memory location (available in some ARM compilers for example, like ARM GCC). Lets assume we have a hardware counter register at the address ADDRESS, which is incrementing each read. Then we could do something like this:
volatile int a __attribute__((at(ADDRESS)));
The volatile is forcing the compiler to generate the register read each time the comparison is performed, so the counter will increment 3 times. If the initial value of the counter is 1, the statement will return true.
P.S. If you don't like the at attribute, same effect can be achieved using linker script by placing a into specific memory section.
If a is of a primitive type (i.e all == and && operators are built in) and you are in defined behavior, and there's no way for another thread to modify a in the middle of execution (this is technically a case of undefined behavior - see comments - but I left it here anyway because it's the example given in the Java question), and there is no preprocessor magic involved (see chosen answer), then I don't believe there is anything way for this to evaluate to true. However, as you can see by that list of conditions, there are many scenarios in which that expression could evaluate to true, depending on the types used and the context of the code.
In C, yes it can. If a is uninitialised then (even if there is no UB, as discussed here), its value is indeterminate, reading it gives indeterminate results, and comparing it to other numbers therefore also gives indeterminate results.
As a direct consequence, a could compare true with 1 in one moment, then compare true with 2 instead the next moment. It can't hold both those values simultaneously, but it doesn't matter, because its value is indeterminate.
In practice I'd be surprised to see the behaviour you describe, though, because there's no real reason for the actual storage to change in memory in the time between the two comparisons.
In C++, sort of. The above is still true there, but reading an indeterminate value is always an undefined operation in C++ so really all bets are off.
Optimisations are allowed to aggressively bastardise your code, and when you do undefined things this can quite easily result in all manner of chaos.
So I'd be less surprised to see this result in C++ than in C but, if I did, it would be an observation without purpose or meaning because a program with undefined behaviour should be entirely ignored anyway.
And, naturally, in both languages there are "tricks" you can do, like #define a (x++), though these do not seem to be in the spirit of your question.
The following program randomly prints seen: yes or seen: no, depending on whether at some point in the execution of the main thread (a == 0 && a == 1 && a == 2) evaluated to true.
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
_Atomic int a = 0;
_Atomic int relse = 0;
void *writer(void *arg)
{
++relse;
while (relse != 2);
for (int i = 100; i > 0; --i)
{
a = 0;
a = 1;
a = 2;
}
return NULL;
}
int main(void)
{
int seen = 0;
pthread_t pt;
if (pthread_create(&pt, NULL, writer, NULL)) exit(EXIT_FAILURE);
++relse;
while (relse != 2);
for (int i = 100; i > 0; --i)
seen |= (a == 0 && a == 1 && a == 2);
printf("seen: %s\n", seen ? "yes":"no");
pthread_join(pt, NULL);
return 0;
}
As far as I am aware, this does not contain undefined behavior at any point, and a is of an integer type, as required by the question.
Obviously this is a race condition, and so whether seen: yes or seen: no is printed depends on the platform the program is run on. On Linux, x86_64, gcc 8.2.1 both answers appear regularly. If it doesn't work, try increasing the loop counters.

How can this c++ function return anything?

The following function does not have a "return grade", only return 0, yet it works and returns grade perfectly. How can this be?
int Grade(double points)
{
int grade = floor(0.25*points - 1.5);
if (grade < 0)
return 0;
}
How can this be?
Either the function returns 0, when grade is less than zero (after modification), or the behaviour of the program is undefined when grade is not less than zero. If function returns non-void, then it must not end without a return expression, or there will be UB.
Having a code path which does not return a value is clearly undefined behavior here, so the question is how can undefined behavior possibly return the correct value here? Presumably, there ought to be a return grade; at the end of the function.
One possibility is how values are returned from a function. On x86 systems, an int return value would typically be placed in the EAX register before the function returned. Other architectures may use different register names, but putting a return value in a register is pretty common.
It's likely that the line which computes the value of grade in the first place left the value of grade in the EAX register. Or, perhaps, the line which compares grade to 0 loads it into the EAX register. In either case, when the function returns, it would happen to have the correct value in EAX. If you really want to know what it's doing, have the compiler generate an assembly listing or step through the code with a debugger at the assembly language level.
Undefined behavior means the code can technically do anything. Which means that sometimes it does the (apparently) right thing.

What is the difference between return the function and not return in a recursive function?

I wrote a simple binary search function in C++. The code is like below:
int binary_search(int arr[], int key, int imin, int imax)
{
if (imin > imax)
{
return -1;
}
else
{
int imid = imin + (imax - imin) / 2;
if (arr[imid] > key) binary_search(arr, key, imin, imid - 1);
else if (arr[imid] < key) binary_search(arr, key, imid + 1, imax);
else return imid;
}
}
But I found that if I add return in lines 10 and 11, the code seems work in the same way. The code is like below:
int binary_search(int arr[], int key, int imin, int imax)
{
if (imin > imax)
{
return -1;
}
else
{
int imid = imin + (imax - imin) / 2;
if (arr[imid] > key) return binary_search(arr, key, imin, imid - 1);
else if (arr[imid] < key) return binary_search(arr, key, imid + 1, imax);
else return imid;
}
}
So my question is what is the difference between these two situation?
Any function that does not return nothing (void) must encounter a return statement before running out of things to do. This is quite simply because there is no magic "return X if I do not return something" imperative, and the person using the function might rely on the return that you promised (but failed to deliver).
If you now follow the path that leads to those recursive calls in the original function, you will see that the call that initiated the recursive call in the first place now has to return something. Instead it simply ignores the result of the recursive call and runs out of things to do.
This causes something called undefined behavior, because C++ does not know what the heck you expect it to do. In fact, "it is legal for it to make demons fly out of your nose.", although it will usually - out of the pure benevolence of its soul - restrict itself to crashing horribly and unpredictably.
Two primary options exist why you do not see a difference:
The code is compiled in such a way that its undefined-ness will work as intended. You must not rely on this, ever. In practice, your code will be compiled in such a way that a single register holds your return value (RAX). Since the recursive call is the last thing your code does, that register may not be modified again, causing the code to act as if you had returned the result of the recursive call.
Your test cases never actually do a recursive call. This is technically legal, as the correctness of your program depends on its behavior at runtime. You should not rely on this either.
If you are interested, the relevant part of the standard is [stmt.return]/2, which says:
[...] Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
"Flowing" refers to control-flow, and a "value-returning function" is any function that has a return type other than void.
If a function that returns non-void "falls of the end" (i.e. with no return statement) and the caller uses the return value, then the result is undefined behaviour. That is happening in your first version (either on your recursive calls, or for the caller) unless - by chance - your test case only ever results in the return imid statement being executed.
Unfortunately, one possible result of undefined behaviour is code that appears to work correctly for all your test cases. There is nothing preventing that. Program crashes (or other abnormal program terminations) are what people are often more acquainted with when undefined behaviour occurs, but a more insidious result of undefined behaviour is code that seems to behave as if nothing is wrong.
In practice, the reason your first case seems to work correctly is blind luck. For historical reasons that I won't expand on, a fair few compilers place working data for calculations (i.e. statements in your function) and results into machine registers, doesn't bother to clear those registers, and also (when a function returns int or other non-struct types) uses those same machine registers to hold return values from functions. So you can get lucky that the code in the first example behaves like the second. The problem is, however, that such behaviour is not guaranteed by the standard, may change between compilers, may change with compiler settings (e.g. optimisation options), and may even change when your compiler is upgraded at some future time.
The recursion will end eventually by actually executing a return statement. At that point, the compiler will place the returned value in the location used for return values.
When you get back to the calling function, without executing any other return statements, it just happens to still be there. Just by chance.

Function with no return statement but compiled and returned right answer

In the section of the code below, there is no return statement in the function but it still compiled and gave the right answer. Is this a normal behavior?
#include <iostream>
int multiply (int x, int y) {
int product = x * y;
}
int main (int argc, char **argv) {
std::cout << multiply(4, 5) << std::endl;
return 0;
}
No, getting the correct answer from the function is not normal behaviour, it's a coincidence. You should not rely on this.
From C++03, 6.6.3 The return statement /2 (and the same section for C++11 as well):
Flowing off the end of a function is equivalent to a return with no value; this results in undefined behavior in a value-returning function.
You'll probably find that you're getting the correct answer simply because of the side effects of your calling convention.
For example, if a function value is returned in the (mtyhical) r0 register, it may be that the calculation uses that register within the function to hold the value, before storing it into the memory representing the product variable. So the fact it's in r0 when you return is just a hang-over from the way things are done.
Compiling with different levels of optimisation, or using another compiler, or even compiling on a Tuesday evening during a blue moon may affect the outcome, that's the main problem with undefined behaviour - it's untrustworthy. At a bare minimum, a good compiler would have warned you about this.

Conditional branches

Why this piece of code compiles?
#include <iostream>
int foo(int x)
{
if(x == 10)
return x*10;
}
int main()
{
int a;
std::cin>>a;
std::cout<<foo(a)<<'\n';
}
The compiler shouldn't give me an error like "not all code paths returns a value"? What happens/returns my function when x isn't equal to ten?
The result is undefined, so the compiler is free to choose -- you probably get what happens to sit at the appropriate stack address where the caller expects the result. Activate compiler warnings, and your compiler will inform you about your omission.
The compiler is not required to give you an error in this circumstance. Many will, some will only issue warnings. Some apparently won't notice.
This is because it's possible that your code ensures outside of this function that the condition will always be true. Therefore, it isn't necessarily bad (though it almost always is, which is why most compilers will issue at least a warning).
The specification will state that the result of exiting a function that should return a value but doesn't is undefined behavior. A value may be returned. Or the program might crash. Or anything might happen. It's undefined.