1

(This is my first post ever on stack overflow, please lmk if I've done something wrong)
I'm currently brushing up on assembly code for C and doing some sample problems to test my understanding. One multiple-choice practice problem gives a description of the code in C and asks you to pick the corresponding assembly code. Answers A and C are clearly wrong. Answers B and D are identical (I ran them through a text comparison thing to make sure I wasn't missing anything, that's how sure I am that they're identical) except for the following two (greatly simplified) lines:

# Answer B
...
6   testq %rax, %rax
7   jle .L3
...
# Answer D
...
6   cmpq $0, %rax
7   jle .L3
...

According to the practice problem, D is correct and B is wrong.

But from what I understand, shouldn't they calculate the same thing? Here's my reasoning, can anyone tell me where I went wrong?

1. Both testq and cmpq alter condition codes but not the registers themselves, so nothing but the condition codes should make a difference.
2. testq S1 S2 will set condition codes according to the calculation S1&S2. If S1==S2 (both %rax here), testq should set condition codes according to whatever is currently in %rax.
3. cmpq S1 S2 will set condition codes according to the calculation S2-S1. If S1==0, then cmpq should set condition codes according to %rax-0 = %rax.

So they're both setting condition codes according to the same thing, the contents of %rax. That was enough to really confuse me. I thought maybe looking at the problem at a lower level, specifically in terms of condition codes, might clear things up, but that didn't really help either.

4. This specific jump (jle/jng if you prefer) will jump to .L3 if (SF^OF)|ZF is true.
5. %rax is not going to overflow on something&something or something-0, so OF=0. Therefore it will jump if SF=1 or ZF=1.
6a. If %rax is negative, %rax & %rax will still yield a negative, so SF is set and jump occurs. If %rax is negative, %rax-$0 will still yield a negative, so SF is set and jump occurs.
6b. If %rax is 0, %rax&%rax is 0, so ZF is set and jump occurs. If %raxis0, %rax-0 = 0-0 = 0, so ZF is set and jump occurs.
6c. If %rax is positive (and non-zero), %rax&%rax will yield a positive (non-zero), so SF=0 and ZF=0 and the jump doesn't happen. If %rax is positive (non-zero), %rax-0 will yield a positive (non-zero), so SF=0 and ZF=0 and the jump doesn't happen.

Where did I go wrong? From what I understand after thinking about the problem, shouldn't it always be true that

testq R,R

has the same logical outcome as

cmpq $0,R

(Note: I know almost nothing about efficiency. It's possible one instruction is shorter or more efficient than the other, but I'm primarily concerned with the logical/condition code outcomes here. For real code and not just textbook problems, the computer/gcc will decide which one it thinks is faster later, when it optimizes it to the point of being unreadable.)

Thanks!

Talha
  • 524
  • 1
  • 6
  • 22
k0zm0tis
  • 69
  • 2
  • Are you sure you correctly simplified the original lines of code? Did one of them have a memory operand? Obviously `test (mem),(mem)` can't work, so you have to use `cmpq $0, (mem)`, or load into a register. Or maybe there was some other difference, because yes, the linked duplicate is correct that they set FLAGS identically except for AF, and `jle` doesn't look at AF. (In fact there are no JCC instructions that look at AF, and with `AAA`, `DAA` and other BCD instructions not available in 64-bit mode, I think the only way it's observable is with `pushf` to store RFLAGS.) – Peter Cordes Apr 03 '19 at 17:58

0 Answers0