Fixture 114

volatile access

C · 4 functions · 4 lanes · 16 of 16 function-lanes behave identically

All 4 lanes recompile and return the same results as the original.

Every volatile access is an observable side effect: the compiler may not merge, reorder, or elide them. Four reads of the same volatile object are four loads, and a redundant store is still emitted.

tests/decompiler_fixtures/src/114_volatile_access.c source
#include <stdint.h>

/* Every volatile access is an observable side effect: the compiler may not
 * merge, reorder, or elide them. Four reads of the same volatile object are
 * four loads, and a redundant store is still emitted. */

__attribute__((noinline)) int32_t
volatile_reads_are_not_merged(int32_t seed) {
    volatile int32_t cell = seed;
    int32_t total = 0;
    total += cell;
    total += cell;
    total += cell;
    total += cell;
    return total;
}

__attribute__((noinline)) int32_t
volatile_store_then_load(int32_t seed) {
    volatile int32_t cell;
    cell = seed;
    cell = seed + 1; /* not dead: volatile stores are observable */
    return cell;
}

__attribute__((noinline)) int32_t
volatile_loop_bound(int32_t limit) {
    volatile int32_t bound = limit;
    int32_t iterations = 0;
    if (limit < 0 || limit > 16) {
        return -1;
    }
    /* `bound` is reloaded on every test, so the trip count is not hoisted. */
    while (iterations < bound) {
        iterations += 1;
    }
    return iterations;
}

__attribute__((noinline)) int32_t
nonvolatile_control(int32_t seed) {
    int32_t cell = seed;
    int32_t total = 0;
    total += cell;
    total += cell;
    total += cell;
    total += cell;
    return total;
}

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

4/4
nonvolatile_control pass 14 lines
// glaurung: nonvolatile_control @ 0x11c0
int32_t nonvolatile_control(int32_t arg0) {
    int cell;
    int total;
    // x86-64 prologue: save rbp
    cell = arg0;
    total = 0;
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}
volatile_loop_bound pass 25 lines
// glaurung: volatile_loop_bound @ 0x1160
int32_t volatile_loop_bound(int32_t arg0) {
    int bound;
    int iterations;
    int local_4;
    // x86-64 prologue: save rbp
    bound = arg0;
    iterations = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    while ((iterations < bound)) {
        iterations = ((unsigned int)(iterations) + 1);
    }
    local_4 = iterations;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
volatile_reads_are_not_merged pass 14 lines
// glaurung: volatile_reads_are_not_merged @ 0x1100
int32_t volatile_reads_are_not_merged(int32_t arg0) {
    int cell;
    int total;
    // x86-64 prologue: save rbp
    cell = arg0;
    total = 0;
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    total = ((unsigned int)(cell) + total);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}
volatile_store_then_load pass 9 lines
// glaurung: volatile_store_then_load @ 0x1140
int32_t volatile_store_then_load(int32_t arg0) {
    int cell;
    // x86-64 prologue: save rbp
    cell = arg0;
    cell = ((unsigned int)(arg0) + 1);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(cell);
}

clang -O2

4/4
nonvolatile_control pass 5 lines
// glaurung: nonvolatile_control @ 0x1160
int32_t nonvolatile_control(int32_t arg0) {
    int total;
    return (unsigned int)((arg0 * 4));
}
volatile_loop_bound pass 17 lines
// glaurung: volatile_loop_bound @ 0x1130
int32_t volatile_loop_bound(int32_t arg0) {
    int iterations;
    long ret;
    long var0;
    int var1;
    ret = 0xffffffff;
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(16))) {
        var0 = 0xffffffff;
        do {
            var1 = (var0 + 1);
            var0 = (unsigned long)((unsigned int)(var1));
            ret = (unsigned long)((unsigned int)(var1));
        } while ((var1 < arg0));
    }
    return ret;
}
volatile_reads_are_not_merged pass 5 lines
// glaurung: volatile_reads_are_not_merged @ 0x1100
int32_t volatile_reads_are_not_merged(int32_t arg0) {
    int total;
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) + arg0))) + arg0))) + arg0));
}
volatile_store_then_load pass 7 lines
// glaurung: volatile_store_then_load @ 0x1120
int32_t volatile_store_then_load(int32_t arg0) {
    int local_4;
    local_4 = arg0;
    local_4 = (arg0 + 1);
    return (unsigned int)(local_4);
}

gcc -O0

4/4
nonvolatile_control pass 14 lines
// glaurung: nonvolatile_control @ 0x1189
int32_t nonvolatile_control(int32_t arg0) {
    int cell;
    int total;
    // x86-64 prologue: save rbp
    cell = arg0;
    total = 0;
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}
volatile_loop_bound pass 21 lines
// glaurung: volatile_loop_bound @ 0x114d
int32_t volatile_loop_bound(int32_t arg0) {
    int bound;
    int iterations;
    // x86-64 prologue: save rbp
    bound = arg0;
    iterations = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    while ((iterations < bound)) {
        iterations = (iterations + 1);
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(iterations);
}
volatile_reads_are_not_merged pass 14 lines
// glaurung: volatile_reads_are_not_merged @ 0x10f9
int32_t volatile_reads_are_not_merged(int32_t arg0) {
    int cell;
    int total;
    // x86-64 prologue: save rbp
    cell = arg0;
    total = 0;
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    total = (total + (unsigned int)(cell));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}
volatile_store_then_load pass 9 lines
// glaurung: volatile_store_then_load @ 0x112e
int32_t volatile_store_then_load(int32_t arg0) {
    int cell;
    // x86-64 prologue: save rbp
    cell = arg0;
    cell = ((unsigned int)(arg0) + 1);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(cell);
}

gcc -O2

4/4
nonvolatile_control pass 4 lines
// glaurung: nonvolatile_control @ 0x1180
int32_t nonvolatile_control(int32_t arg0) {
    return (unsigned int)((arg0 * 4));
}
volatile_loop_bound pass 18 lines
// glaurung: volatile_loop_bound @ 0x1140
int32_t volatile_loop_bound(int32_t arg0) {
    int bound;
    int iterations;
    int var2;
    bound = arg0;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    iterations = 0;
    if ((((unsigned long)((unsigned int)(bound)) != 0) && (0 <= (long)(bound)))) {
        do {
            var2 = (iterations + 1);
            iterations = (unsigned long)((unsigned int)(var2));
        } while (((((unsigned int)(bound) == (unsigned int)(var2)) | (bound < var2)) == 0));
    }
    return iterations;
}
volatile_reads_are_not_merged pass 7 lines
// glaurung: volatile_reads_are_not_merged @ 0x1100
int32_t volatile_reads_are_not_merged(int32_t arg0) {
    int cell;
    int total;
    cell = arg0;
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(cell)) + (unsigned long)((unsigned int)(cell))))) + (unsigned long)((unsigned int)(cell))))) + (unsigned long)((unsigned int)(cell))));
}
volatile_store_then_load pass 7 lines
// glaurung: volatile_store_then_load @ 0x1120
int32_t volatile_store_then_load(int32_t arg0) {
    int cell;
    cell = arg0;
    cell = (arg0 + 1);
    return (unsigned int)(cell);
}

← 213 fixtures