Fixture 91
union type punning
C · 3 functions · 4 lanes · 12 of 12 function-lanes behave identically
All 4 lanes recompile and return the same results as the original.
Reading a union member other than the one last stored is defined in C (it reinterprets the object representation). The byte order and the trap-free integer layout make this a precise test of storage-identity recovery.
#include <stdint.h>
/* Reading a union member other than the one last stored is defined in C (it
* reinterprets the object representation). The byte order and the trap-free
* integer layout make this a precise test of storage-identity recovery. */
union Punner {
uint32_t word;
uint8_t bytes[4];
struct {
uint16_t low;
uint16_t high;
} halves;
};
__attribute__((noinline)) int32_t
pun_byte_of_word(uint32_t word, int32_t index) {
union Punner punner;
punner.word = word;
if (index < 0 || index > 3) {
return -1;
}
return (int32_t)punner.bytes[index];
}
__attribute__((noinline)) uint32_t
pun_halves_swapped(uint32_t word) {
union Punner punner;
uint16_t swap;
punner.word = word;
swap = punner.halves.low;
punner.halves.low = punner.halves.high;
punner.halves.high = swap;
return punner.word;
}
__attribute__((noinline)) int32_t
pun_is_little_endian(void) {
union Punner punner;
punner.word = 1u;
return punner.bytes[0] == 1u;
} 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
3/3pun_byte_of_word pass 20 lines
// glaurung: pun_byte_of_word @ 0x1100
__attribute__((no_stack_protector)) int32_t pun_byte_of_word(uint32_t arg0, int32_t arg1) {
unsigned char local_10[4];
int local_4;
// x86-64 prologue: save rbp
*(int *)(&local_10[0]) = arg0;
if (((long)(arg1) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg1)) == 3) | ((long)(arg1) < 3)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
local_4 = (unsigned char)(*(char *)((&local_10[0] + (long)(arg1))));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} pun_halves_swapped pass 12 lines
// glaurung: pun_halves_swapped @ 0x1150
__attribute__((no_stack_protector)) uint32_t pun_halves_swapped(uint32_t arg0) {
unsigned short swap;
unsigned char local_8[4];
// x86-64 prologue: save rbp
*(int *)(&local_8[0]) = arg0;
swap = *(short *)(&local_8[0]);
*(short *)(&local_8[0]) = *(short *)((&local_8[0] + 2));
*(short *)((&local_8[0] + 2)) = swap;
// x86-64 epilogue: restore rbp
return (unsigned int)(*(int *)(&local_8[0]));
} pun_is_little_endian pass 8 lines
// glaurung: pun_is_little_endian @ 0x1180
__attribute__((no_stack_protector)) int32_t pun_is_little_endian(void) {
unsigned char local_8[4];
// x86-64 prologue: save rbp
*(int *)(&local_8[0]) = 1;
// x86-64 epilogue: restore rbp
return ((unsigned long)((unsigned int)((unsigned char)(*(char *)(&local_8[0])))) == 1);
} clang -O2
3/3pun_byte_of_word pass 11 lines
// glaurung: pun_byte_of_word @ 0x1100
__attribute__((no_stack_protector)) int32_t pun_byte_of_word(uint32_t arg0, int32_t arg1) {
unsigned char local_8[8];
long ret;
*(int *)(&local_8[0]) = arg0;
ret = 0xffffffff;
if (((unsigned long)((unsigned long)((unsigned int)(arg1))) <= (unsigned long)(3))) {
ret = (unsigned int)((unsigned char)(*(char *)((&local_8[0] + (unsigned long)((unsigned int)(arg1))))));
}
return ret;
} pun_halves_swapped pass 4 lines
// glaurung: pun_halves_swapped @ 0x1120
uint32_t pun_halves_swapped(uint32_t arg0) {
return (((unsigned long)(arg0) << 16) | ((unsigned long)(arg0) >> 16));
} pun_is_little_endian pass 4 lines
// glaurung: pun_is_little_endian @ 0x1130
int32_t pun_is_little_endian(void) {
return 1;
} gcc -O0
3/3pun_byte_of_word pass 16 lines
// glaurung: pun_byte_of_word @ 0x1119
int32_t pun_byte_of_word(uint32_t arg0, int32_t arg1) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
long local_8;
unsigned char local_c[4];
long ret;
// x86-64 prologue: save rbp, frame 32 bytes
local_8 = (long)(0x28);
*(int *)(&local_c[0]) = arg0;
ret = ((((long)(arg1) < 0) || (((unsigned long)((unsigned int)(arg1)) != 3) && (3 <= (long)(arg1)))) ? 0xffffffff : (unsigned int)((unsigned char)(((unsigned int)((unsigned char)(*(char *)((&local_c[0] + (long)(arg1))))) & 255))));
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} pun_halves_swapped pass 20 lines
// glaurung: pun_halves_swapped @ 0x1176
uint32_t pun_halves_swapped(uint32_t arg0) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
unsigned short swap;
long local_8;
unsigned char local_c[4];
long ret;
// x86-64 prologue: save rbp, frame 32 bytes
local_8 = (long)(0x28);
*(int *)(&local_c[0]) = arg0;
swap = *(short *)(&local_c[0]);
*(short *)(&local_c[0]) = *(short *)((&local_c[0] + 2));
*(short *)((&local_c[0] + 2)) = swap;
ret = (unsigned long)((unsigned int)(*(int *)(&local_c[0])));
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} pun_is_little_endian pass 16 lines
// glaurung: pun_is_little_endian @ 0x11cb
int32_t pun_is_little_endian(void) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
long local_8;
unsigned char local_c[4];
long ret;
// x86-64 prologue: save rbp, frame 16 bytes
local_8 = (long)(0x28);
*(int *)(&local_c[0]) = 1;
ret = ((unsigned long)((unsigned char)(((unsigned int)((unsigned char)(*(char *)(&local_c[0]))) & 255))) == 1);
if ((local_8 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} gcc -O2
3/3pun_byte_of_word pass 14 lines
// glaurung: pun_byte_of_word @ 0x1120
int32_t pun_byte_of_word(uint32_t arg0, int32_t arg1) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
long local_10;
unsigned char local_14[4];
long ret;
local_10 = (long)(0x28);
*(int *)(&local_14[0]) = arg0;
ret = (((unsigned long)(3) < (unsigned long)((unsigned long)((unsigned int)(arg1)))) ? 0xffffffff : (unsigned int)((unsigned char)(*(char *)((&local_14[0] + (long)(arg1))))));
if ((local_10 != 0x28)) {
__stack_chk_fail();
}
return ret;
} pun_halves_swapped pass 4 lines
// glaurung: pun_halves_swapped @ 0x1170
uint32_t pun_halves_swapped(uint32_t arg0) {
return (((unsigned long)(arg0) << 16) | ((unsigned long)(arg0) >> 16));
} pun_is_little_endian pass 4 lines
// glaurung: pun_is_little_endian @ 0x1180
int32_t pun_is_little_endian(void) {
return 1;
}