[Bug c/38354] Spurious error: initializer element is not computable at load time
adam at consulting dot net.nz
gcc-bugzilla@gcc.gnu.org
Wed Sep 10 03:10:00 GMT 2014
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=38354
--- Comment #11 from Adam Warner <adam at consulting dot net.nz> ---
Thank you Joseph for clarifying in Comment 10 why this should be considered an
enhancement request. It would be non-trivial to change the model of what GNU C
considers a constant expression and code relying on an enhanced model of
constant expression is likely to be incompatible with other compilers.
I now have a 100% efficient fairly portable workaround in a few lines of bash
scripting. Here is an example lookup_table.c:
#include <stdint.h>
#include <stdio.h>
#include "lookup_table.h"
void fn0(void) {printf("fn0\n");}
void fn1(void) {printf("fn1\n");}
void fn2(void) {printf("fn2\n");}
void fn3(void) {printf("fn3\n");}
int main(void) {
for (int i=0; i<4; ++i) ((void (*)(void)) (uint64_t) lookup_table[i])();
return 0;
}
To compile/execute the C code run this bash script named lookup_table.sh:
#!/bin/bash
if [[ ! -f lookup_table.h ]]; then echo "uint32_t lookup_table[4];" >>
lookup_table.h; fi
gcc -std=gnu11 -O3 lookup_table.c -o lookup_table
mv -f lookup_table.h lookup_table.h.old
echo "uint32_t lookup_table[] = {" > lookup_table.h
objdump -d -m i386:x86-64 lookup_table | grep '<fn' | sed 's/^0/0x0/' | sed 's/
.*/,/' >> lookup_table.h
echo "};" >> lookup_table.h
diff -su lookup_table.h.old lookup_table.h && ./lookup_table ||
./lookup_table.sh
Example output:
$ ./lookup_table.sh
--- lookup_table.h.old 2014-09-10 13:35:03.162644646 +1200
+++ lookup_table.h 2014-09-10 13:35:03.222648312 +1200
@@ -1 +1,6 @@
-uint32_t lookup_table[4];
+uint32_t lookup_table[] = {
+0x0000000000400530,
+0x0000000000400540,
+0x0000000000400550,
+0x0000000000400560,
+};
Files lookup_table.h.old and lookup_table.h are identical
fn0
fn1
fn2
fn3
The lookup table functions must share a unique prefix. If the new
lookup_table.h matches the old lookup_table.h then the binary is internally
consistent and ready for execution.
There is no additional overhead in the final binary:
0000000000400410 <main>:
400410: 53 push rbx
400411: bb e0 09 60 00 mov ebx,0x6009e0
400416: 8b 03 mov eax,DWORD PTR [rbx]
400418: 48 83 c3 04 add rbx,0x4
40041c: ff d0 call rax
40041e: 48 81 fb f0 09 60 00 cmp rbx,0x6009f0
400425: 75 ef jne 400416 <main+0x6>
400427: 31 c0 xor eax,eax
400429: 5b pop rbx
40042a: c3 ret
In this example the lookup table has been mapped into memory at address
0x6009e0. There is a 32-bit load for each function call. No code is required to
populate the lookup table at run time. No internal run time checks are required
to ensure the lookup table and function addresses match. This is a perfectly
efficient workaround that is superior to the C++ approach.
More information about the Gcc-bugs
mailing list