Fixture 123
sizeof semantics
C · 5 functions · 4 lanes · 18 of 20 function-lanes behave identically
2 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (4/5), gcc-O0 (4/5).
sizeof does not evaluate its operand (the sole exception being a VLA), so a call written inside sizeof is never emitted. The result is a compile-time constant of type size_t.
#include <stdint.h>
/* sizeof does not evaluate its operand (the sole exception being a VLA), so a
* call written inside sizeof is never emitted. The result is a compile-time
* constant of type size_t. */
/* Exported rather than static: `sizeof` never evaluates the call below, so a
* static definition would be unreferenced and clang rejects that under -Werror.
* External linkage forces it to be emitted while leaving the point intact. */
__attribute__((noinline)) int32_t sizeof_probe_sets_flag(int32_t *flag) {
if (flag == 0) {
return -1;
}
*flag = 1;
return 0;
}
__attribute__((noinline)) int32_t
sizeof_does_not_evaluate(int32_t *flag) {
if (flag == 0) {
return -1;
}
*flag = 0;
/* The call is not invoked; only its return type is inspected, so the flag
* stays 0. */
return (int32_t)sizeof(sizeof_probe_sets_flag(flag)) * 10 + *flag;
}
__attribute__((noinline)) int32_t sizeof_array_versus_pointer(void) {
int32_t array[8];
int32_t *pointer = array;
/* sizeof(array) is the whole object; sizeof(pointer) is one address. The
* element-count division is written only for the array, because both
* compilers reject `sizeof(pointer) / sizeof(element)` outright -- the
* gotcha this documents is now a diagnostic, so the sizes are compared
* directly instead. */
return (int32_t)(sizeof(array) / sizeof(array[0])) * 100 +
(int32_t)sizeof(pointer);
}
__attribute__((noinline)) int32_t sizeof_after_decay(const int32_t values[8]) {
/* A parameter declared as an array is really a pointer. `sizeof(values)`
* would prove it but is a hard error under -Werror on both compilers, so
* the decay is demonstrated by arithmetic instead: advancing the parameter
* by one steps one element, exactly as a pointer does, and the declared
* bound 8 has no effect on it at all. */
const int32_t *stepped;
if (values == 0) {
return -1;
}
stepped = values + 1;
return (int32_t)(stepped - values) * 100 + (int32_t)sizeof(values[0]);
}
__attribute__((noinline)) int32_t sizeof_vla_is_evaluated(int32_t count) {
if (count < 1 || count > 16) {
return -1;
}
{
int32_t scratch[count];
scratch[0] = count;
/* This sizeof IS a runtime computation. */
return (int32_t)(sizeof(scratch) / sizeof(scratch[0])) + scratch[0];
}
} 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/5sizeof_after_decay pass 11 lines
// glaurung: sizeof_after_decay @ 0x11a0
int32_t sizeof_after_decay(const int32_t * arg0) {
int * stepped;
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
stepped = (int *)((arg0 + 1));
return (unsigned int)(((((long)(((long)stepped - (long)arg0)) >> 2) * 100) + 4));
} else {
return (unsigned int)(-1);
}
} sizeof_array_versus_pointer pass 9 lines
// glaurung: sizeof_array_versus_pointer @ 0x1180
__attribute__((no_stack_protector)) int32_t sizeof_array_versus_pointer(void) {
int * pointer;
unsigned char local_20[32];
// x86-64 prologue: save rbp
pointer = (int *)&local_20[0];
// x86-64 epilogue: restore rbp
return 808;
} sizeof_does_not_evaluate pass 10 lines
// glaurung: sizeof_does_not_evaluate @ 0x1140
int32_t sizeof_does_not_evaluate(int32_t * arg0) {
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
*(int *)((long)arg0) = 0;
return (unsigned int)(((unsigned long)((unsigned int)(*(int *)((long)arg0))) + 40));
} else {
return (unsigned int)(-1);
}
} sizeof_probe_sets_flag pass 10 lines
// glaurung: sizeof_probe_sets_flag @ 0x1100
int32_t sizeof_probe_sets_flag(int32_t * arg0) {
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
*(int *)((long)arg0) = 1;
return 0;
} else {
return (unsigned int)(-1);
}
} sizeof_vla_is_evaluated fail 26 lines
// glaurung: sizeof_vla_is_evaluated @ 0x11f0
__attribute__((no_stack_protector)) int32_t sizeof_vla_is_evaluated(int32_t arg0) {
unsigned long __vla_expr0;
void * local_10;
int local_4;
unsigned char stack_1[32];
long var5;
// x86-64 prologue: save rbp, frame 32 bytes
if (((long)(arg0) < 1)) {
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);
}
local_10 = &stack_1[0];
var5 = (long)((&stack_1[0] - ((((unsigned long)((unsigned int)(arg0)) * 4) + 15) & -16)));
__vla_expr0 = (unsigned int)(arg0);
*(int *)((var5)) = arg0;
local_4 = (((unsigned long)(((unsigned long)((unsigned int)(arg0)) << 2)) >> 2) + *(int *)((var5)));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} gcc -O0
4/5sizeof_after_decay pass 11 lines
// glaurung: sizeof_after_decay @ 0x11b1
int32_t sizeof_after_decay(const int32_t * arg0) {
int * stepped;
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
stepped = (int *)((arg0 + 1));
return (unsigned int)(((((long)(((long)stepped - (long)arg0)) >> 2) * 100) + 4));
} else {
return 0xffffffff;
}
} sizeof_array_versus_pointer pass 17 lines
// glaurung: sizeof_array_versus_pointer @ 0x1173
int32_t sizeof_array_versus_pointer(void) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
int * pointer;
unsigned char local_30[32];
long local_8;
long ret;
// x86-64 prologue: save rbp, frame 64 bytes
local_8 = (long)(0x28);
pointer = (int *)&local_30[0];
ret = 808;
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} sizeof_does_not_evaluate pass 10 lines
// glaurung: sizeof_does_not_evaluate @ 0x1144
int32_t sizeof_does_not_evaluate(int32_t * arg0) {
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
*(int *)((long)arg0) = 0;
return (unsigned int)(((unsigned long)((unsigned int)(*(int *)((long)arg0))) + 40));
} else {
return 0xffffffff;
}
} sizeof_probe_sets_flag pass 10 lines
// glaurung: sizeof_probe_sets_flag @ 0x1119
int32_t sizeof_probe_sets_flag(int32_t * arg0) {
// x86-64 prologue: save rbp
if ((arg0 != 0)) {
*(int *)((long)arg0) = 1;
return 0;
} else {
return 0xffffffff;
}
} sizeof_vla_is_evaluated fail 42 lines
// glaurung: sizeof_vla_is_evaluated @ 0x11eb
int32_t sizeof_vla_is_evaluated(int32_t arg0) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
long local_10;
long local_18;
long local_8;
long ret;
long rsp;
long var23;
long var27;
long var37;
long var4;
long var5;
// x86-64 prologue: save rbp, frame 48 bytes
local_8 = (long)(0x28);
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) || (((unsigned long)((unsigned int)(arg0)) != 16) && (16 <= (long)(arg0))))) {
ret = 0xffffffff;
} else {
var4 = rsp;
var5 = (unsigned long)((unsigned int)(arg0));
local_18 = ((long)(arg0) - 1);
var23 = (((unsigned long)(((((unsigned __int128)(unsigned long)(0) << 64) | (unsigned long)(((16 - 1) + (0 + ((long)((int)((unsigned long)((unsigned int)(arg0)))) * 4))))) / (unsigned long)(16)))) * 16);
var27 = (rsp - (var23 & -4096));
while ((rsp != var27)) {
rsp = (rsp - 4096);
}
rsp = (rsp - (unsigned long)((unsigned int)((var23 & 4095))));
if (((unsigned long)((unsigned int)((var23 & 4095))) != 0)) {
var37 = (((unsigned long)((unsigned int)((var23 & 4095))) - 8) + rsp);
*(long *)((var37)) = *(long *)((var37));
}
local_10 = (((unsigned long)((rsp + 3)) >> 2) << 2);
*(int *)((local_10)) = arg0;
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(local_10))) + (unsigned long)((unsigned int)(((unsigned long)(((long)((int)(var5)) << 2)) >> 2))))));
rsp = var4;
}
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} clang -O2
5/5sizeof_after_decay pass 4 lines
// glaurung: sizeof_after_decay @ 0x1150
int32_t sizeof_after_decay(const int32_t * arg0) {
return (unsigned int)(((unsigned int)((0 - ((unsigned long)((long)arg0) < (unsigned long)(1)))) | 104));
} sizeof_array_versus_pointer pass 4 lines
// glaurung: sizeof_array_versus_pointer @ 0x1140
int32_t sizeof_array_versus_pointer(void) {
return 808;
} sizeof_does_not_evaluate pass 8 lines
// glaurung: sizeof_does_not_evaluate @ 0x1120
int32_t sizeof_does_not_evaluate(int32_t * arg0) {
if ((arg0 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg0)) = 0;
return 40;
} sizeof_probe_sets_flag pass 8 lines
// glaurung: sizeof_probe_sets_flag @ 0x1100
int32_t sizeof_probe_sets_flag(int32_t * arg0) {
if ((arg0 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg0)) = 1;
return 0;
} sizeof_vla_is_evaluated pass 4 lines
// glaurung: sizeof_vla_is_evaluated @ 0x1160
int32_t sizeof_vla_is_evaluated(int32_t arg0) {
return (((unsigned long)(0xfffffff0) <= (unsigned long)((unsigned long)((unsigned int)((arg0 - 17))))) ? (unsigned long)((unsigned int)((arg0 + arg0))) : 0xffffffff);
} gcc -O2
5/5sizeof_after_decay pass 4 lines
// glaurung: sizeof_after_decay @ 0x1150
int32_t sizeof_after_decay(const int32_t * arg0) {
return (unsigned int)(((unsigned int)((0 - ((unsigned long)((long)arg0) < (unsigned long)(1)))) | 104));
} sizeof_array_versus_pointer pass 4 lines
// glaurung: sizeof_array_versus_pointer @ 0x1140
int32_t sizeof_array_versus_pointer(void) {
return 808;
} sizeof_does_not_evaluate pass 8 lines
// glaurung: sizeof_does_not_evaluate @ 0x1120
int32_t sizeof_does_not_evaluate(int32_t * arg0) {
if ((arg0 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg0)) = 0;
return 40;
} sizeof_probe_sets_flag pass 8 lines
// glaurung: sizeof_probe_sets_flag @ 0x1100
int32_t sizeof_probe_sets_flag(int32_t * arg0) {
if ((arg0 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg0)) = 1;
return 0;
} sizeof_vla_is_evaluated pass 7 lines
// glaurung: sizeof_vla_is_evaluated @ 0x1160
int32_t sizeof_vla_is_evaluated(int32_t arg0) {
if (((unsigned long)(15) < (unsigned long)((unsigned long)((unsigned int)((arg0 - 1)))))) {
return 0xffffffff;
}
return (unsigned int)((arg0 + arg0));
}