Check if a number is odd in c++. Strange behaviour of code - c++

I am a teaching assistant for computer science and one of my students submitted the following code to check whether an integer is odd or even:
int is_odd (int i) {
if((i % 2 == 1) && (i % 2 == -1));
else;
}
Surprisingly (at least for me) this code gives correct results. I tested numbers up to 100000000, and I honestly cannot explain why this code is behaving as it does.
We are using gcc v6.2.1 and c++
I know that this is not a typical question for so, but I hope to find some help.

Flowing off the end of a function without returning anything is undefined behaviour, regardless of what actually happens with your compiler. Note that if you pass -O3 to GCC, or use Clang, then you get different results.
As for why you actually see the "correct" answer, this is the x86 assembly which GCC 6.2 produces at -O0:
push rbp
mov rbp, rsp
mov DWORD PTR [rbp-4], edi
mov eax, DWORD PTR [rbp-4]
cdq
shr edx, 31
add eax, edx
and eax, 1
sub eax, edx
cmp eax, 1
nop
pop rbp
ret
Don't worry if you can't read x86. The important thing to note is that eax is used for the return value, and all the intermediate calculations for the if statement use eax as their destination. So when the function exits, eax just happens to have the result of the branch check in it.
Of course, this is all a purely academic discussion; the student's code is wrong and I'd certainly give it zero marks, regardless of whether it passes whatever tests you run it through.

Related

Is there a performance difference while assigning value to the array

int i=0;
a[i] =3;
i++;
Vs
int i=0;
a[i++]=3;
Is it just a fancy way of writing code and saving lines or you really do improve the performance here?
Using https://godbolt.org/, GCC with optimisations enabled generates the same code for both cases.
main:
sub rsp, 8
mov esi, 3
mov edi, OFFSET FLAT:std::cout
call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
xor eax, eax
add rsp, 8
ret
_GLOBAL__sub_I_main:
sub rsp, 8
mov edi, OFFSET FLAT:std::__ioinit
call std::ios_base::Init::Init()
mov edx, OFFSET FLAT:__dso_handle
mov esi, OFFSET FLAT:std::__ioinit
mov edi, OFFSET FLAT:std::ios_base::Init::~Init()
add rsp, 8
jmp __cxa_atexit
Note: gcc has optimised std::cout << a[0] to std::cout<<3
Both segments of code, on their own, would have negligible differences in performance. In practice, both formats would probably be optimized out to the same performance level by the compiler. Placing both inside a loop where i goes from 0 to a million, the difference in runtime was a few ms that averaged out to be negligible.
There's no difference other than a[i++]=3; being more dangerous, since mixing the ++ operators with other operators in the same expression is not a good idea.
See Why are these constructs (using ++) undefined behavior in C? for examples of when this could be dangerous.
Sources: MISRA-C:2012 rule 13.3 et al, see this. The wording in the latest MISRA is that ++ should not be used in an expression with other side effects.
In theory (taking the code at face value with a very naive compiler) the second snippet is faster because of multiple reads to i in the first one..
BUT having said that, in practice it's always 100% the same as the compiler will always optimise both out.
Side note:
Stop thinking about these micro optimisations and let the compiler figure it out.

Why do compilers duplicate some instructions?

Sometimes compilers generate code with weird instruction duplications that can safely be removed. Consider the following piece of code:
int gcd(unsigned x, unsigned y) {
return x == 0 ? y : gcd(y % x, x);
}
Here is the assembly code (generated by clang 5.0 with optimizations enabled):
gcd(unsigned int, unsigned int): # #gcd(unsigned int, unsigned int)
mov eax, esi
mov edx, edi
test edx, edx
je .LBB0_1
.LBB0_2: # =>This Inner Loop Header: Depth=1
mov ecx, edx
xor edx, edx
div ecx
test edx, edx
mov eax, ecx
jne .LBB0_2
mov eax, ecx
ret
.LBB0_1:
ret
In the following snippet:
mov eax, ecx
jne .LBB0_2
mov eax, ecx
If the jump doesn't happen, eax is reassigned for no obvious reason.
The other example is two ret's at the end of the function: one would perfectly work as well.
Is the compiler simply not intelligent enough or there's a reason to not remove the duplications?
Compilers can perform optimisations that are not obvious to people and removing instructions does not always make things faster.
A small amount of searching shows that various AMD processors have branch prediction problems when a RET is immediately after a conditional branch. By filling that slot with what is essentially a no-op, the performance problem is avoided.
Update:
Example reference, section 6.2 of the "Software Optimization Guide for AMD64 Processors" (see http://support.amd.com/TechDocs/25112.PDF) says:
Specifically, avoid the following two situations:
Any kind of branch (either conditional or unconditional) that has the single-byte near-return RET instruction as its target. See “Examples.”
A conditional branch that occurs in the code directly before the single-byte near-return RET instruction.
It also goes into detail on why jump targets should have alignment which is also likely to explain the duplicate RETs at the end of the function.
Any compiler will have a bunch of transformations for register renaming, unrolling, hoisting, and so on. Combining their outputs can lead to suboptimal cases such as what you have shown. Marc Glisse offers good advice: it's worth a bug report. You are describing an opportunity for a peephole optimizer to discard instructions that either
don't affect the state of registers & memory at all, or
don't affect state that matters for a function's post-conditions, won't matter for its public API.
Sounds like an opportunity for symbolic execution techniques. If the constraint solver finds no branch points for a given MOV, perhaps it is really a NOP.

Difference between += 1 and ++ as executed by computer?

Are the commands the computer performs the same for these two expressions in C++:
myInt += 1;
myInt ++;
Am I supposed to use a GCC? Or if you know the answer can you tell me?
For many compilers, these will be identical. (Note that I said many compilers - see my disclaimer below). For example, for the following C++ code, both Test 1 and Test 2 will result in the same assembly language:
int main()
{
int test = 0;
// Test 1
test++;
// Test 2
test += 1;
return 0;
}
Many compilers (including Visual Studio) can be configured to show the resulting assembly language, which is the best way to settle questions like this. For example, in Visual Studio, I right-clicked on the project file, went to "Properties", and did the following:
In this case, as shown below, Visual Studio does, in fact, compile them to the same assembly language:
; Line 8
push ebp
mov ebp, esp
sub esp, 204 ; 000000ccH
push ebx
push esi
push edi
lea edi, DWORD PTR [ebp-204]
mov ecx, 51 ; 00000033H
mov eax, -858993460 ; ccccccccH
rep stosd
; Line 9
mov DWORD PTR _test$[ebp], 0
; Line 12 - this is Test 1
mov eax, DWORD PTR _test$[ebp]
add eax, 1
mov DWORD PTR _test$[ebp], eax
; Line 15 - This is Test 2 - note that the assembly is identical
mov eax, DWORD PTR _test$[ebp]
add eax, 1
mov DWORD PTR _test$[ebp], eax
; Line 17
xor eax, eax
; Line 18
pop edi
pop esi
pop ebx
mov esp, ebp
pop ebp
ret 0
_main ENDP
_TEXT ENDS
END
Interestingly enough, its C# compiler also produces the same MSIL (which is C#'s "equivalent" of assembly language) for similar C# code, so this apparently holds across multiple languages as well.
By the way, if you're using another compiler like gcc, you can follow the directions here to get assembly language output. According to the accepted answer, you should use the -S option, like the following:
gcc -S helloworld.c
If you're writing in Java and would like to do something similar, you can follow these directions to use javap to get the bytecode, which is Java's "equivalent" of assembly language, so to speak.
Also of interest, since this question originally asked about Java as well as C++, this question discusses the relationship between Java code, bytecode, and the eventual machine code (if any).
Caution: Different compilers may produce different assembly language, especially if you're compiling for different processors and platforms. Whether or not you have optimization turned on can also affect things. So, strictly speaking, the fact that Visual Studio is "smart enough" to know that those two statements "mean" the same thing doesn't necessarily mean that all compilers for all possible platforms will be that smart.

Is Visual Studio emitting erroneous assembly code?

Disassembling the following function using VS2010
int __stdcall modulo(int a, int b)
{
return a % b;
}
gives you:
push ebp
mov ebp,esp
mov eax,dword ptr [ebp+8]
cdq
idiv eax,dword ptr [ebp+0Ch]
mov eax,edx
pop ebp
ret 8
which is pretty straightforward.
Now, trying the same assembly code as inline assembly fails with error C2414: illegal number of operands, pointing to idiv.
I read Intel's manual and it says that idiv accept only 1 operand:
Divides the (signed) value in the AX, DX:AX, or EDX:EAX (dividend) by
the source operand (divisor) and stores the result in the AX (AH:AL),
DX:AX, or EDX:EAX registers
and sure enough, removing the extra eax compiles and the function returns the correct result.
So, what is going on here? why is VS2010 emitting erroneous code ? (btw, VS2012 emits exactly the same assembly)
The difference, most likely, is that the author of the disassembler intended for its output to be read by a human rather than by an assembler. Since it's a disassembly, one assumes the underlying code has already been assembled/compiled, so why worry about whether it can be re-assembled?
There are two major possibilities here:
The author didn't realize or didn't remember that the dividend is implicit for idiv (in which case this could be considered a bug).
They felt that making it explicit in the output would be helpful for a reader trying to understand the flow of the disassembly (in which case it's a feature). Without the explicit operand, it would be easy to overlook that idiv both depends on and modifies eax when scanning the disassembly.

ASSEMBLY Offset to C++ Code question

I've been trying to convert this code to C++ without any inlining and I cannot figure it out..
Say you got this line
sub edx, (offset loc_42C1F5+5)
My hex-rays gives me
edx -= (uint)((char*)loc_42C1F5 + 5))
But how would it really look like without the loc_42C1F5.
I would think it would be
edx -= 0x42C1FA;
But is that correct? (can't really step this code in any assembler-level debugger.. as it's damaged well protected)
loc_42C1F5 is a label actually..
seg000:0042C1F5 loc_42C1F5: ; DATA XREF: sub_4464A0+2B5o
seg000:0042C1F5 mov edx, [esi+4D98h]
seg000:0042C1FB lea ebx, [esi+4D78h]
seg000:0042C201 xor eax, eax
seg000:0042C203 xor ecx, ecx
seg000:0042C205 mov [ebx], eax
loc_42C1F5 is a symbol. Given the information you've provided, I cannot say what its offset is. It may be 0x42C1F5 or it may be something else entirely.
If it is 0x42C1F5, then your translation should be correct.
IDA has incorrectly identified 0x42C1FA as an offset, and Hex-Rays used that interpretation. Just convert it to plain number (press O) and all will be well. That's why it's called Interactive Disassembler :)