Fixture 102

duffs device

C · 1 functions · 4 lanes · 0 of 4 function-lanes behave identically

4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/1), clang-O2 (0/1), gcc-O0 (0/1), gcc-O2 (0/1).

Duff's device: a switch whose cases fall into the middle of a do/while. The loop and the switch share a body, so no structuring that treats them as separate regions can reproduce it.

tests/decompiler_fixtures/src/102_duffs_device.c source
#include <stdint.h>

/* Duff's device: a switch whose cases fall into the middle of a do/while. The
 * loop and the switch share a body, so no structuring that treats them as
 * separate regions can reproduce it. */

#define DUFF_MAX 16

__attribute__((noinline)) int32_t
duff_copy(const int32_t *source, int32_t *destination, int32_t count) {
    int32_t index = 0;
    int32_t blocks;
    if (source == 0 || destination == 0 || count < 1 || count > DUFF_MAX) {
        return -1;
    }
    blocks = (count + 7) / 8;
    switch (count % 8) {
    case 0:
        do {
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 7:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 6:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 5:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 4:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 3:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 2:
            destination[index] = source[index];
            index += 1;
            __attribute__((fallthrough));
    case 1:
            destination[index] = source[index];
            index += 1;
        } while (--blocks > 0);
    }
    return index;
}

Recovered C

Generated by glaurung decompile --style decbench at b47f6b43. baseline.json records the result after recompiling the C and calling it beside the original with seeded inputs.

clang -O0

0/1
duff_copy fail 35 lines
// glaurung: duff_copy @ 0x1100
int32_t duff_copy(const int32_t * arg0, int32_t * arg1, int32_t arg2) {
    int index;
    int blocks;
    long local_30;
    int local_4;
    long var19;
    int var2;
    index = 0;
    if ((arg0 != 0)) {
        if ((arg1 != 0)) {
            if ((1 <= (long)(arg2))) {
                if ((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16))) {
                    goto L_114c;
                }
            }
        }
    }
    local_4 = -1;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
    L_114c: ;
    var2 = ((unsigned int)(arg2) + 7);
    blocks = ((int)((((long)((int)(var2)) < 0) ? (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) + 14))) : (unsigned long)((unsigned int)(var2)))) >> 3);
    var19 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) - (unsigned long)((unsigned int)(((((long)(arg2) < 0) ? (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) + 7))) : (unsigned long)((unsigned int)(arg2))) & -8))))));
    local_30 = (unsigned int)(var19);
    if (((((unsigned long)((unsigned long)((unsigned int)(var19))) < (unsigned long)(7)) | ((unsigned long)((unsigned int)((var19 - 7))) == 0)) == 0)) {
        goto L_12ad;
    }
    /* unrecovered indirect jump through ((long)((int)(*(int *)((0x2000 + (local_30 * 4))))) + 0x2000) */
    L_12ad: ;
    local_4 = index;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}

clang -O2

0/1
duff_copy fail 17 lines
// glaurung: duff_copy @ 0x1100
int32_t duff_copy(const int32_t * arg0, int32_t * arg1, int32_t arg2) {
    int blocks;
    int index;
    long ret;
    ret = 0xffffffff;
    if ((arg0 == 0)) {
        return ret;
    }
    if ((arg1 == 0)) {
        return ret;
    }
    if (((unsigned long)((unsigned long)((unsigned int)((arg2 - 17)))) < (unsigned long)(0xfffffff0))) {
        return ret;
    }
    /* unrecovered indirect jump through ((long)((int)((((unsigned long)((unsigned int)((arg2 & 7))) == 0) ? 0xfffff140 : (((unsigned long)((unsigned int)((arg2 & 7))) == 1) ? 0xfffff18e : (((unsigned long)((unsigned int)((arg2 & 7))) == 2) ? 0xfffff183 : (((unsigned long)((unsigned int)((arg2 & 7))) == 3) ? 0xfffff178 : (((unsigned long)((unsigned int)((arg2 & 7))) == 4) ? 0xfffff16d : (((unsigned long)((unsigned int)((arg2 & 7))) == 5) ? 0xfffff162 : (((unsigned long)((unsigned int)((arg2 & 7))) == 6) ? 0xfffff157 : (((unsigned long)((unsigned int)((arg2 & 7))) == 7) ? 0xfffff14c : *(int *)((0x2000 + ((unsigned long)((unsigned int)((arg2 & 7))) * 4))))))))))))) + 0x2000) */
}

gcc -O0

0/1
duff_copy fail 30 lines
// glaurung: duff_copy @ 0x10f9
int32_t duff_copy(const int32_t * arg0, int32_t * arg1, int32_t arg2) {
    int index;
    int blocks;
    int var1;
    long var12;
    long var18;
    index = 0;
    if ((arg0 != 0)) {
        if ((arg1 != 0)) {
            if (((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0)) == 0)) {
                if ((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16))) {
                    goto L_1137;
                }
            }
        }
    }
    // x86-64 epilogue: restore rbp
    return 0xffffffff;
    L_1137: ;
    var1 = ((unsigned int)(arg2) + 7);
    blocks = ((int)((((long)((int)(var1)) < 0) ? (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var1)) + 7))) : (unsigned long)((unsigned int)(var1)))) >> 3);
    var12 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((unsigned long)((long)(arg2)) >> 32) & 0xffffffff))) >> 29)));
    var18 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) + var12))) & 7))) - var12)));
    if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned int)(var18))))) {
        // x86-64 epilogue: restore rbp
        return (unsigned int)(index);
    }
    /* unrecovered indirect jump through ((long)((int)((((unsigned long)((unsigned int)(var18)) == 0) ? 0xfffff186 : (((unsigned long)((unsigned int)(var18)) == 1) ? 0xfffff2dd : (((unsigned long)((unsigned int)(var18)) == 2) ? 0xfffff2ac : (((unsigned long)((unsigned int)(var18)) == 3) ? 0xfffff27b : (((unsigned long)((unsigned int)(var18)) == 4) ? 0xfffff24a : (((unsigned long)((unsigned int)(var18)) == 5) ? 0xfffff219 : (((unsigned long)((unsigned int)(var18)) == 6) ? 0xfffff1e8 : (((unsigned long)((unsigned int)(var18)) == 7) ? 0xfffff1b7 : *(int *)((((unsigned long)((unsigned int)(var18)) * 4) + 0x2000)))))))))))) + 0x2000) */
}

gcc -O2

0/1
duff_copy fail 54 lines
// glaurung: duff_copy @ 0x1100
int32_t duff_copy(const int32_t * arg0, int32_t * arg1, int32_t arg2) {
    int blocks;
    int index;
    long var1;
    long var14;
    long var15;
    long var16;
    long var17;
    long var18;
    long var19;
    long var21;
    long var24;
    long var27;
    long var4;
    if ((arg0 == 0)) {
        goto L_12e0;
    }
    if ((arg1 == 0)) {
        goto L_12e0;
    }
    if (((unsigned long)(15) < (unsigned long)((unsigned long)((unsigned int)((arg2 - 1)))))) {
        goto L_12e0;
    }
    var1 = (long)arg0;
    var4 = (unsigned long)((unsigned int)((arg2 & 7)));
    /* unrecovered indirect jump through ((long)((int)((((unsigned long)((unsigned int)(var4)) == 0) ? 0xfffff2e8 : (((unsigned long)((unsigned int)(var4)) == 1) ? 0xfffff230 : (((unsigned long)((unsigned int)(var4)) == 2) ? 0xfffff240 : (((unsigned long)((unsigned int)(var4)) == 3) ? 0xfffff250 : (((unsigned long)((unsigned int)(var4)) == 4) ? 0xfffff268 : (((unsigned long)((unsigned int)(var4)) == 5) ? 0xfffff288 : (((unsigned long)((unsigned int)(var4)) == 6) ? 0xfffff2b0 : (((unsigned long)((unsigned int)(var4)) == 7) ? 0xfffff150 : *(int *)((0x2000 + ((unsigned long)((unsigned int)(var4)) * 4))))))))))))) + 0x2000) */
    L_1180: ;
    var21 = (long)((int)(var4));
    var4 = (unsigned long)((unsigned int)((var4 + 8)));
    var24 = (var21 << 2);
    var27 = (var24 + 4);
    var19 = (var24 + 8);
    var15 = (var24 + 12);
    var18 = (var24 + 16);
    var17 = (var24 + 20);
    var16 = (var24 + 24);
    var14 = (var24 + 28);
    *(int *)(((long)arg1 + var24)) = *(int *)((var1 + var24));
    *(int *)((var27 + (long)arg1)) = *(int *)((var1 + var27));
    *(int *)((var19 + (long)arg1)) = *(int *)((var1 + var19));
    *(int *)((var15 + (long)arg1)) = *(int *)((var1 + var15));
    *(int *)((var18 + (long)arg1)) = *(int *)((var1 + var18));
    *(int *)((var17 + (long)arg1)) = *(int *)((var1 + var17));
    *(int *)((var16 + (long)arg1)) = *(int *)((var1 + var16));
    *(int *)((var14 + (long)arg1)) = *(int *)((var1 + var14));
    if (0) {
        goto L_1180;
    }
    // x86-64 epilogue: tear down frame
    return (unsigned int)(var4);
    L_12e0: ;
    return 0xffffffff;
}

← 213 fixtures