#Ternary and if-statement compile to different things

25 messages · Page 1 of 1 (latest)

mint flame
#
for (uint8_t y_cord = 0; y_cord < height; y_cord++) {
    for (uint8_t x_cord = 0; x_cord < width; x_cord++) {
        /* Write pixel if not transparent */
            // 3503 bytes
            *dst_buf = (*src_buf != global_Transparent_Color) ? *src_buf : *dst_buf;
            // 3518 bytes
            if (*src_buf != global_Transparent_Color) { *dst_buf = *src_buf; }

        src_buf++;
        dst_buf += x_jump;
    }
    dst_buf -= y_jump;
}
```I am compiling for the eZ80 with Clang C17 -Oz. I have this code to write some (8bit indexed color) image data to the screen (Copying src to dst). The code checks that the pixel isn't equal to `global_Transparent_Color` before writing it.

Why do the ternary and if-statement versions of the code compile to different things?
zenith stirrupBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

mint flame
#

Are there any edge cases that are causing them to be different?

velvet osprey
#

;asm -Oz

void foo(int* dest, int* source) {
    *dest = (*source != -1) ? *source : *dest;
}
void foo2(int* dest, int* source) {
    if (*source != -1) {
        *dest = *source;
    }
}
errant prismBOT
#
Assembly Output
foo:
  mov eax, DWORD PTR [rsi]
  cmp eax, -1
  jne .L2
  mov eax, DWORD PTR [rdi]
.L2:
  mov DWORD PTR [rdi], eax
  ret
foo2:
  mov eax, DWORD PTR [rsi]
  cmp eax, -1
  je .L4
  mov DWORD PTR [rdi], eax
.L4:
  ret

velvet osprey
#

IDK, maybe the optimizer just doesn't know how to reason that the false condition of the ternary is a no-op

mint flame
#

To clarify, the ternary version compiles to smaller and faster code (saves 15 bytes)

#

thats werid that its the opposite for x86-64

velvet osprey
#

one moment

#

Well unless you can show the part of the assembly that is different, it is hard to reason about

mint flame
#

I tried a bunch of random compilers and archtectures on godbolt, every one seems to have that one additional instruction for the ternary version. Only one produced the exact same for both versions

#

The eZ80 is 24bit, so pointers are also 24bit```c
typedef struct gfy_sprite_t {
uint8_t width;
uint8_t height;
uint8_t data[];
} gfy_sprite_t;

void gfy_TransparentSprite_NoClip(const gfy_sprite_t* sprite, uint24_t x, uint8_t y) {
const uint8_t* src_buf = sprite->data;
uint8_t* dst_buf = (uint8_t*)((volatile uint24_t)0xE30014) + y + (x * 240);
const uint24_t dst_jump = (240 * sprite->width) - 1;

for (uint8_t y_cord = 0; y_cord < sprite->height; y_cord++) {
    for (uint8_t x_cord = 0; x_cord < sprite->width; x_cord++) {
        /* Write pixel if not transparent */
            // 3503 bytes
            *dst_buf = (*src_buf != global_Transparent_Color) ? *src_buf : *dst_buf;
            // 3518 bytes
            if (*src_buf != global_Transparent_Color) { *dst_buf = *src_buf; }

        src_buf++;
        dst_buf += 240;
    }
    dst_buf -= dst_jump;
}

}

#

the differences between lines 5-36 seem to do the exact same thing, so they can be ignored

tropic obsidian
#

Even adding const and restrict, and compiling them separately, these indeed produce always different outputs. Using -Ofast -march=native gives identical output.

#

;asm -Oz

void foo(int* restrict const dest, const int* restrict const source) {
    *dest = (*source != -1) ? *source : *dest;
}
errant prismBOT
#
Assembly Output
foo:
  mov eax, DWORD PTR [rsi]
  cmp eax, -1
  jne .L2
  mov eax, DWORD PTR [rdi]
.L2:
  mov DWORD PTR [rdi], eax
  ret

tropic obsidian
#

;asm -Oz

void foo2(int* restrict const dest, const int* restrict const source) {
    if (*source != -1) {
        *dest = *source;
    }
}
errant prismBOT
#
Assembly Output
foo2:
  mov eax, DWORD PTR [rsi]
  cmp eax, -1
  je .L1
  mov DWORD PTR [rdi], eax
.L1:
  ret

tropic obsidian
#

This seems pretty strange, but I guess the difference comes down to the ternary version always assigning to dest? But I'd think that shouldn't matter

tropic obsidian
#

The answers to my question on SO say that it's simply a GCC and Clang skill issue, huh

mint flame
#

interesting

tropic obsidian
#

@mint flame So since I think your question has been answered, with Stack Overflow experts even weighing in, I think you can mark your question with !solved now :)

mint flame
#

!solved