0

Here's a simple C++ program:

#include <iostream>
#include <vector>

using namespace std;

int main()
{
  vector<int> a = {7};
  int b = 7;
  if (a[0] > 6) {
    cout << "a bigger than 6" << endl;
  }
  if (b > 6) {
    cout << "b bigger than 6" << endl;
  }
  return 0;
}

Upon compiling with gdb -g ... and generating assembly with objdump --source-comment=";SOURCE: " -wlCS ..., here's the conditionals' x86 assembly code:

;SOURCE:   if (a[0] > 6) {
    1321:   48 8d 45 b0             lea    -0x50(%rbp),%rax
    1325:   be 00 00 00 00          mov    $0x0,%esi
    132a:   48 89 c7                mov    %rax,%rdi
    132d:   e8 82 02 00 00          call   15b4 <std::vector<int, std::allocator<int> >::operator[](unsigned long)>
    1332:   8b 00                   mov    (%rax),%eax
    1334:   83 f8 06                cmp    $0x6,%eax
    1337:   0f 9f c0                setg   %al
    133a:   84 c0                   test   %al,%al
    133c:   74 2b                   je     1369 <main+0xc0>

and

;SOURCE:   if (b > 6) {
    1369:   83 7d ac 06             cmpl   $0x6,-0x54(%rbp)
    136d:   7e 2b                   jle    139a <main+0xf1>

In the second example, the value of b is simply compared with 6 and the jump statement skips ahead if b <= 6.

In the first example, after the value 7 is read from the vector a, it is stored in %eax and compared with 6. I see that setg will set %al to match the "greater" condition (ZF=0 and SF=OF), test will set the zero flag to the inverse of %al (in this case), and je will jump if the zero flag is set (indicating that a[0] was not greater than 6).

My question is why setg, test, and je are needed here. Couldn't the latter portion of the first example be implemented in the same way as the second one, using only cmp(l) and jle? Would this potentially be faster?

Note: this doesn't have anything to do with objdump; the output of gdb -S contains the same instructions.

g++ (Ubuntu 11.3.0-1ubuntu1\~22.04.1) 11.3.0

GNU objdump (GNU Binutils for Ubuntu) 2.38

  • 1
    You're not writing the best possible test case. A better test case would not use initialized and trivial local variables, but rather parameters, so the compiler cannot see the values of the vector. As you've written it, using -O3, the compiler can see that `a[0]` is indeed > `6` at compile time and so eliminates the if-conditional and just does the then part. If you're not using some optimization, it is generally considered of very limited value to ask about code sequences especially regarding performance. – Erik Eidt Jun 23 '23 at 19:47
  • An absolutely wild guess: possible optimisation for pipeline? Without those extra instructions the pipeline would have to wait for result of heap access to make the jump. But, as Erik noted, it's harder to argue about non-optimised builds (and even `-O1` changes your code to `std::cout << "a bigger than 6\nb bigger than 6" << std::endl;` + `new`/`delete` for vector). – Yksisarvinen Jun 23 '23 at 19:56
  • 7
    If you don't enable optimization, don't be surprised when the asm isn't efficient. [Why does clang produce inefficient asm with -O0 (for this simple floating point sum)?](https://stackoverflow.com/q/53366394). GCC -O3 doesn't waste any instructions materializing a boolean: https://godbolt.org/z/3aMMro4h3 – Peter Cordes Jun 23 '23 at 20:01
  • Welp, that's do it. Forgot all about optimization in this little example! Of course, after optimizing (and using randomly generated values so the compiler still has to leave the conditionals), both conditionals are implemented using comparable `cmp` and `jmp` statements. – charlescochran Jun 23 '23 at 20:10

0 Answers0