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.

Reply via email to