Fixture 214
arm literal pools
C · 6 functions · 4 lanes · 24 of 24 function-lanes behave identically
All 4 lanes recompile and return the same results as the original.
Constants too large to encode in an instruction, which ARM must therefore fetch from a LITERAL POOL — a block of data placed inside .text, addressed PC-relatively, and interleaved with the code that reads it.
WHY IT IS A DECOMPILER PROBLEM. A literal pool is data in an executable section, sitting between functions or even between basic blocks of one function. Three separate things have to get it right: the linear sweep must not decode pool words as instructions (they disassemble perfectly well as garbage); function discovery must not seed a function at a pool word that happens to look like a prologue; and the lifter must FOLD the load, because ldr r0, [pc, #24] reads a value that is fully known at lift time and leaving it as a memory read of an undefined register loses the constant.
We do fold it — lift_arm32 resolves [pc, #imm] through ctx.literal and emits Op::Assign with the word — and there is no fixture behind that path. The Thumb-2 encoding rounds pc DOWN to a 4-byte boundary while A32 does not, so the same source exercises two different address computations, and an off-by-four reads the neighbouring word and produces a confidently wrong constant.
120_const_and_literals covers constants as a C-level concept (const qualification, string literals, enum constants). It does not force a pool: its values are small enough to encode inline. 116_string_literals covers .rodata addressing, which is a relocation, not a PC-relative fetch.
The constants below are chosen to be unencodable as ARM immediates (which must be an 8-bit value rotated by an even amount), so every target must either build them with a multi-instruction sequence or fetch them from a pool. Several functions are placed so a pool must land between them.
#include <stdint.h>
/* Constants too large to encode in an instruction, which ARM must therefore
* fetch from a LITERAL POOL — a block of data placed inside `.text`, addressed
* PC-relatively, and interleaved with the code that reads it.
*
* WHY IT IS A DECOMPILER PROBLEM. A literal pool is data in an executable
* section, sitting between functions or even between basic blocks of one
* function. Three separate things have to get it right: the linear sweep must
* not decode pool words as instructions (they disassemble perfectly well as
* garbage); function discovery must not seed a function at a pool word that
* happens to look like a prologue; and the lifter must FOLD the load, because
* `ldr r0, [pc, #24]` reads a value that is fully known at lift time and
* leaving it as a memory read of an undefined register loses the constant.
*
* We do fold it — `lift_arm32` resolves `[pc, #imm]` through `ctx.literal` and
* emits `Op::Assign` with the word — and there is no fixture behind that path.
* The Thumb-2 encoding rounds `pc` DOWN to a 4-byte boundary while A32 does
* not, so the same source exercises two different address computations, and an
* off-by-four reads the neighbouring word and produces a confidently wrong
* constant.
*
* `120_const_and_literals` covers constants as a C-level concept (const
* qualification, string literals, enum constants). It does not force a pool:
* its values are small enough to encode inline. `116_string_literals` covers
* `.rodata` addressing, which is a relocation, not a PC-relative fetch.
*
* The constants below are chosen to be unencodable as ARM immediates (which
* must be an 8-bit value rotated by an even amount), so every target must
* either build them with a multi-instruction sequence or fetch them from a
* pool. Several functions are placed so a pool must land between them.
*/
/* A single unencodable constant. */
__attribute__((noinline)) uint32_t wide_constant_mix(uint32_t seed) {
return (seed ^ 0x5A3C7E19u) + 0x12345678u;
}
/* Enough distinct wide constants that the pool cannot be folded into the
* instruction stream, and must be emitted as a block. */
__attribute__((noinline)) uint32_t many_wide_constants(uint32_t seed) {
uint32_t acc = seed;
acc ^= 0xDEADBEEFu;
acc += 0xCAFEBABEu;
acc ^= 0x8BADF00Du;
acc += 0xFEEDFACEu;
acc ^= 0x0BADC0DEu;
acc += 0x1BADB002u;
return acc;
}
/* 64-bit constants: two pool words each on a 32-bit target. */
__attribute__((noinline)) uint64_t wide_64bit_constants(uint64_t seed) {
uint64_t acc = seed;
acc ^= 0x0123456789ABCDEFull;
acc += 0xFEDCBA9876543210ull;
return acc;
}
/* A long function whose constants are used near the END, so the assembler must
* place a pool in the middle of the function rather than after it — the case
* where pool words are interleaved with basic blocks. */
__attribute__((noinline)) uint32_t pool_inside_function(uint32_t seed,
int32_t count) {
uint32_t acc = seed;
if (count < 0 || count > 32) {
return 0xFFFFFFFFu;
}
for (int32_t i = 0; i < count; i++) {
acc = (acc << 1) ^ (acc >> 31);
acc += (uint32_t)i;
acc ^= (acc >> 7);
acc += (acc << 3);
acc ^= (acc >> 17);
}
acc ^= 0xA5A5F0F0u;
acc += 0x3C3C0F0Fu;
return acc;
}
/* Constants that ARE encodable as ARM immediates, so no pool is emitted. The
* control: if these fail, the defect is in constant handling generally. */
__attribute__((noinline)) uint32_t encodable_constants(uint32_t seed) {
uint32_t acc = seed;
acc ^= 0xFFu;
acc += 0x1000u;
acc ^= 0x00FF0000u;
return acc;
}
/* A wide constant used as a COMPARISON bound rather than as an operand, so the
* folded value has to reach a predicate rather than an arithmetic result. */
__attribute__((noinline)) int32_t compare_against_pool(uint32_t value) {
if (value > 0x7EDCBA98u) {
return 1;
}
if (value < 0x1234ABCDu) {
return 2;
}
return 3;
} 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
6/6compare_against_pool pass 13 lines
// glaurung: compare_against_pool @ 0x12b0
int32_t compare_against_pool(uint32_t arg0) {
// x86-64 prologue: save rbp
if (((unsigned long)(arg0) <= (unsigned long)(0x7edcba98))) {
if (((unsigned long)(0x1234abcd) <= (unsigned long)(arg0))) {
return 3;
} else {
return 2;
}
} else {
return 1;
}
} encodable_constants pass 11 lines
// glaurung: encodable_constants @ 0x1270
uint32_t encodable_constants(uint32_t arg0) {
unsigned int acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (acc ^ 255);
acc = (acc + 4096);
acc = (acc ^ 0xff0000);
// x86-64 epilogue: restore rbp
return acc;
} many_wide_constants pass 14 lines
// glaurung: many_wide_constants @ 0x1120
uint32_t many_wide_constants(uint32_t arg0) {
unsigned int acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (acc ^ -0x21524111LL);
acc = (acc - 0x35014542);
acc = (acc ^ -0x74520ff3LL);
acc = (acc - 0x1120532);
acc = (acc ^ 0xbadc0de);
acc = (acc + 0x1badb002);
// x86-64 epilogue: restore rbp
return acc;
} pool_inside_function pass 30 lines
// glaurung: pool_inside_function @ 0x11c0
uint32_t pool_inside_function(uint32_t arg0, int32_t arg1) {
unsigned int acc;
int i;
int local_4;
// x86-64 prologue: save rbp
acc = arg0;
if (((long)(arg1) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg1)) == 32) | ((long)(arg1) < 32)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
for (i = 0; (i < arg1); i++) {
acc = ((unsigned int)((acc << 1)) ^ (unsigned int)(((unsigned int)(acc) >> 31)));
acc = ((unsigned int)(i) + acc);
acc = ((unsigned int)(((unsigned int)(acc) >> 7)) ^ acc);
acc = ((unsigned int)((acc << 3)) + acc);
acc = ((unsigned int)(((unsigned int)(acc) >> 17)) ^ acc);
}
acc = (acc ^ -0x5a5a0f10LL);
acc = (acc + 0x3c3c0f0f);
local_4 = acc;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} wide_64bit_constants pass 10 lines
// glaurung: wide_64bit_constants @ 0x1180
uint64_t wide_64bit_constants(uint64_t arg0) {
unsigned long acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (0x123456789abcdef ^ acc);
acc = (-0x123456789abcdf0LL + acc);
// x86-64 epilogue: restore rbp
return acc;
} wide_constant_mix pass 6 lines
// glaurung: wide_constant_mix @ 0x1100
uint32_t wide_constant_mix(uint32_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) ^ 0x5a3c7e19))) + 0x12345678));
} clang -O2
6/6compare_against_pool pass 4 lines
// glaurung: compare_against_pool @ 0x1210
int32_t compare_against_pool(uint32_t arg0) {
return (((unsigned long)((unsigned long)(arg0)) < (unsigned long)(0x7edcba99)) ? (unsigned int)((3 - ((unsigned long)((unsigned long)(arg0)) < (unsigned long)(0x1234abcd)))) : 1);
} encodable_constants pass 5 lines
// glaurung: encodable_constants @ 0x11f0
uint32_t encodable_constants(uint32_t arg0) {
unsigned int acc;
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg0 ^ 255))) + 4096))) ^ 0xff0000));
} many_wide_constants pass 5 lines
// glaurung: many_wide_constants @ 0x1110
uint32_t many_wide_constants(uint32_t arg0) {
unsigned int acc;
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg0 ^ -0x21524111LL))) - 0x35014542))) ^ -0x74520ff3LL))) - 0x1120532))) ^ 0xbadc0de))) + 0x1badb002));
} pool_inside_function pass 59 lines
// glaurung: pool_inside_function @ 0x1160
uint32_t pool_inside_function(uint32_t arg0, int32_t arg1) {
int i;
unsigned int acc;
long ret;
long var0;
long var15;
long var16;
int var26;
long var3;
long var33;
long var34;
long var40;
int var41;
long var42;
long var43;
int var47;
long var54;
long var55;
long var6;
int var8;
ret = 0xffffffff;
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return ret;
}
var0 = (unsigned long)(arg0);
if (((unsigned long)((unsigned int)(arg1)) != 0)) {
if (((unsigned long)((unsigned int)(arg1)) != 1)) {
var3 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & -2)));
i = 0;
var6 = (unsigned long)(arg0);
do {
var8 = (((var6 << 1) | ((unsigned long)(var6) >> 31)) + i);
var15 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var8)) >> 7))) ^ (unsigned long)((unsigned int)(var8)))));
var16 = (unsigned long)((unsigned int)((var15 + (var15 * 8))));
var26 = ((unsigned int)((((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var16)) >> 17))) ^ var16))) << 1) | ((unsigned long)((unsigned int)(var16)) >> 31)) & 0xffffffff) + i)) + 1);
var33 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var26)) >> 7))) ^ (unsigned long)((unsigned int)(var26)))));
var34 = (unsigned long)((unsigned int)((var33 + (var33 * 8))));
var40 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var34)) >> 17))) ^ var34)));
var41 = (i + 2);
i = (unsigned long)((unsigned int)(var41));
var6 = var40;
var42 = (unsigned long)((unsigned int)(var41));
var43 = var40;
} while (((unsigned int)(var3) != (unsigned int)(var41)));
} else {
var42 = 0;
var43 = (unsigned long)(arg0);
}
var0 = var43;
if (((unsigned long)((unsigned char)((arg1 & 1))) != 0)) {
var47 = (((var43 << 1) | ((unsigned long)(var43) >> 31)) + var42);
var54 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var47)) >> 7))) ^ (unsigned long)((unsigned int)(var47)))));
var55 = (unsigned long)((unsigned int)((var54 + (var54 * 8))));
var0 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var55)) >> 17))) ^ var55)));
}
}
return (unsigned int)(((unsigned long)((unsigned int)((var0 ^ -0x5a5a0f10LL))) + 0x3c3c0f0f));
} wide_64bit_constants pass 5 lines
// glaurung: wide_64bit_constants @ 0x1140
uint64_t wide_64bit_constants(uint64_t arg0) {
unsigned long acc;
return (-0x123456789abcdf0LL + (0x123456789abcdef ^ arg0));
} wide_constant_mix pass 4 lines
// glaurung: wide_constant_mix @ 0x1100
uint32_t wide_constant_mix(uint32_t arg0) {
return (unsigned int)(((unsigned long)((unsigned int)((arg0 ^ 0x5a3c7e19))) + 0x12345678));
} gcc -O0
6/6compare_against_pool pass 13 lines
// glaurung: compare_against_pool @ 0x1227
int32_t compare_against_pool(uint32_t arg0) {
// x86-64 prologue: save rbp
if (((unsigned long)(arg0) <= (unsigned long)(0x7edcba98))) {
if (((unsigned long)(0x1234abcc) < (unsigned long)(arg0))) {
return 3;
} else {
return 2;
}
} else {
return 1;
}
} encodable_constants pass 11 lines
// glaurung: encodable_constants @ 0x11fc
uint32_t encodable_constants(uint32_t arg0) {
unsigned int acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (acc ^ 255);
acc = (acc + 4096);
acc = (acc ^ 0xff0000);
// x86-64 epilogue: restore rbp
return acc;
} many_wide_constants pass 14 lines
// glaurung: many_wide_constants @ 0x1113
uint32_t many_wide_constants(uint32_t arg0) {
unsigned int acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (acc ^ -0x21524111LL);
acc = (acc - 0x35014542);
acc = (acc ^ -0x74520ff3LL);
acc = (acc - 0x1120532);
acc = (acc ^ 0xbadc0de);
acc = (acc + 0x1badb002);
// x86-64 epilogue: restore rbp
return acc;
} pool_inside_function pass 26 lines
// glaurung: pool_inside_function @ 0x1189
uint32_t pool_inside_function(uint32_t arg0, int32_t arg1) {
unsigned int acc;
int i;
// x86-64 prologue: save rbp
acc = arg0;
if (((long)(arg1) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg1)) == 32) | ((long)(arg1) < 32)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
for (i = 0; (i < arg1); i++) {
acc = ((acc << 1) | ((unsigned int)(acc) >> 31));
acc = (acc + (unsigned int)(i));
acc = (acc ^ (unsigned int)(((unsigned int)(acc) >> 7)));
acc = (acc + (unsigned int)((acc << 3)));
acc = (acc ^ (unsigned int)(((unsigned int)(acc) >> 17)));
}
acc = (acc ^ -0x5a5a0f10LL);
acc = (acc + 0x3c3c0f0f);
// x86-64 epilogue: restore rbp
return acc;
} wide_64bit_constants pass 10 lines
// glaurung: wide_64bit_constants @ 0x1153
uint64_t wide_64bit_constants(uint64_t arg0) {
unsigned long acc;
// x86-64 prologue: save rbp
acc = arg0;
acc = (acc ^ 0x123456789abcdef);
acc = (acc - 0x123456789abcdf0);
// x86-64 epilogue: restore rbp
return acc;
} wide_constant_mix pass 6 lines
// glaurung: wide_constant_mix @ 0x10f9
uint32_t wide_constant_mix(uint32_t arg0) {
// x86-64 prologue: save rbp
// x86-64 epilogue: restore rbp
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) ^ 0x5a3c7e19))) + 0x12345678));
} gcc -O2
6/6compare_against_pool pass 9 lines
// glaurung: compare_against_pool @ 0x11e0
int32_t compare_against_pool(uint32_t arg0) {
long ret;
ret = 1;
if (((unsigned long)(arg0) <= (unsigned long)(0x7edcba98))) {
ret = (unsigned long)((unsigned int)((((unsigned long)(0x1234abcc) < (unsigned long)(arg0)) + 2)));
}
return ret;
} encodable_constants pass 5 lines
// glaurung: encodable_constants @ 0x11c0
uint32_t encodable_constants(uint32_t arg0) {
unsigned int acc;
return (unsigned int)(((unsigned long)((unsigned int)(((((unsigned long)(arg0) & -256) | ((((unsigned long)(arg0) & 255) ^ -1) & 255)) + 4096))) ^ 0xff0000));
} many_wide_constants pass 5 lines
// glaurung: many_wide_constants @ 0x1120
uint32_t many_wide_constants(uint32_t arg0) {
unsigned int acc;
return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) ^ -0x21524111LL))) - 0x35014542))) ^ -0x74520ff3LL))) - 0x1120532))) ^ 0xbadc0de))) + 0x1badb002));
} pool_inside_function pass 33 lines
// glaurung: pool_inside_function @ 0x1170
uint32_t pool_inside_function(uint32_t arg0, int32_t arg1) {
int i;
unsigned int acc;
long ret;
long var0;
long var14;
long var15;
long var21;
long var3;
int var6;
int var8;
ret = 0xffffffff;
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return ret;
}
var0 = (unsigned long)(arg0);
if (((unsigned long)((unsigned int)(arg1)) != 0)) {
var3 = (unsigned long)(arg0);
i = 0;
do {
var6 = (((var3 << 1) | ((unsigned long)(var3) >> 31)) + i);
var8 = (i + 1);
i = (unsigned long)((unsigned int)(var8));
var14 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) >> 7))) ^ (unsigned long)((unsigned int)(var6)))));
var15 = (unsigned long)((unsigned int)((var14 + (var14 * 8))));
var21 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var15)) >> 17))) ^ var15)));
var3 = var21;
var0 = var21;
} while (((unsigned int)(arg1) != (unsigned int)(var8)));
}
return (unsigned int)(((unsigned long)((unsigned int)((var0 ^ -0x5a5a0f10LL))) + 0x3c3c0f0f));
} wide_64bit_constants pass 5 lines
// glaurung: wide_64bit_constants @ 0x1150
uint64_t wide_64bit_constants(uint64_t arg0) {
unsigned long acc;
return (-0x123456789abcdf0LL + (arg0 ^ 0x123456789abcdef));
} wide_constant_mix pass 4 lines
// glaurung: wide_constant_mix @ 0x1100
uint32_t wide_constant_mix(uint32_t arg0) {
return (unsigned int)(((unsigned long)((unsigned int)((arg0 ^ 0x5a3c7e19))) + 0x12345678));
}