2
int get_cont(int *p1, int *p2) 
{       
    if (p1 > p2)  
        return *p2;
    else
        return *p1;
}

The assembly code is:

    movl    8(%ebp), %eax
    cmpl    12(%ebp), %eax
    jbe    .L2
    movl    12(%ebp), %eax
    movl    (%eax), %eax
    jmp    .L3

Why use jbe? The condition is p1 > p2. Why not use ja?

ja and jbe are both unsigned jump opcode. Why did the compiler reverse the condition?

polo sop
  • 21
  • 4
  • It was convenient because of the value already held in a register. The compiler may implement what it likes, as long as the result is indistinguishable. – Weather Vane Apr 18 '23 at 14:09
  • 2
    It's totally arbitrary which block the compiler uses as the fall-through vs. the taken side of the condition. But if it wants to lay out the function with the `true` side of the if/else first (matching the C source, as in a debug build), then it needs to jump *over* it if the condition is false, i.e. `jna` which is a synonym for `jbe`. As in [why logical"NOT" the condition in if statement in assembly?](https://stackoverflow.com/q/52368539) – Peter Cordes Apr 18 '23 at 14:10
  • @WeatherVane: This is a 32-bit debug build. None of the values are in registers to start with; notice it loads the pointers from the stack to compare them, then in the `if` body it reloads the `p2` pointer from `12(%ebp)` and dereferences it. – Peter Cordes Apr 18 '23 at 14:11
  • @PeterCordes, I can see that the pointers are 32-bit. One of them was loaded into a register, then the appropriate branch test so that it could be the 'default' result. They were not both loaded before the compare – Weather Vane Apr 18 '23 at 14:13
  • @WeatherVane: What "default result"? This is just the `if() { return *p2; }` part, without showing the `else` body. Since this is a debug build, it will be self-contained (not keeping any variables in regs across C statements), also loading `p1` from the stack, because it's not `register int *p1` – Peter Cordes Apr 18 '23 at 14:16
  • @PeterCordes: when I said "one value is already held in a register" I meant "One value has been loaded into a register". By "default" I mean "the value already loaded into a register". Is that clear enough now? You said both values were loaded before the comparison, but that is not so. One of them was, and the other being compared is still on the stack, and if it isn't the one wanted to dereference, it can stay there. – Weather Vane Apr 18 '23 at 14:18
  • Ok, I see what you mean. But "*if it isn't the one wanted to dereference, it can stay there.*" seems to imply that the `else` block will just do `mov (%eax), %eax`. But that's not the case in a debug build: it's a separate C statement so it will do its own `mov 8(%ebp), %eax` to reload `p1` first. Also, I didn't say values were loaded *before* the compare, I said both `p1` and `p2` were in memory before the first instruction of the block of asm implementing the `if(p1 < p2){` statement. (The `mov` + `cmp` + `jbe`). Both get loaded *as part of* the compare, one by the `cmp` insn itself. – Peter Cordes Apr 18 '23 at 14:46
  • @WeatherVane: Check https://godbolt.org/z/Yn184sPvx and look at the color highlighting of blocks, and/or mouseover to match parts of the asm to parts of the C. – Peter Cordes Apr 18 '23 at 14:48
  • @WeatherVane: anyway, to the overall point, loading `12(%ebp)` first and using the opposite `jcc` predicate would work equally well. Or using `cmp %eax, 12(%ebp)`. So the choice of which value was already in a register before the `cmp` executed was also something the compiler chose; nothing would be more convenient about doing it the other way. But maybe you were missing the fact that it was unoptimized output, so were thinking about leaving one pointer in a register for one of the deref return statements. – Peter Cordes Apr 18 '23 at 14:53
  • 1
    You might find that, as an experiment, simplifying to using `return 1; ` and `return 2;` instead makes it more clear which comes first, the then-part or the else-part; that should make it easier to follow the flow of control. As others have said in assembly's if-goto-label form for if-then we tell the processor what to skip, whereas in C we tell the processor what to run — since those are exactly opposite, the condition is negated when keeping ordering of then- and else-parts.., (or otherwise we can swap the order of fhen-part and else-part, compiler's / assembly programmer's choice). – Erik Eidt Apr 18 '23 at 15:11

1 Answers1

5

In your example, the compiler generated the code to execute if the test succeeds immediately after the jump, hence the jump must be taken if the test fails, ie: jump if below or equal (jbe) for the test p1 > p2.

Note however that the compiler may generate any appropriate code, and the operands may appear in a different order in the assembly instructions, causing incorrect interpretations.

chqrlie
  • 131,814
  • 10
  • 121
  • 189