Skip to content

Instantly share code, notes, and snippets.

@mtasic85
Forked from gby/dragons.c
Created August 12, 2018 14:31
Show Gist options
  • Save mtasic85/b6aed67b4a8c65bfab1d2b4dee8686e7 to your computer and use it in GitHub Desktop.
Save mtasic85/b6aed67b4a8c65bfab1d2b4dee8686e7 to your computer and use it in GitHub Desktop.
How to do a computed goto in an inline function in GCC
#include <stdio.h>
#include <stdlib.h>
/*
* How to do a computed goto in an inline function.
* "Here be Dragons"
*/
/*
* This is used to keep the compiler from
* optimizing out the label code blocks because
* they looks unreachable.
*/
int unknown = 1;
/*
* Instantiate a label
*
* This uses linker trickery to put the address
* of the code block in a separate ELF section
* called my_jump_table
*/
#define label_me() \
asm volatile ( \
"1: \n" \
".section my_jump_table,\"a\"\n" \
".align 4\n" \
".long 1b\n" \
".text\n" \
: : : "memory")
/*
* Start a new block in the jump table
*
* Because each instance of the inline function
* needs its own set of jump table entries for
* all the labels, we keep a different block in
* the jump table for each instance of the inline
* function.
*
* This little nuggets open a new block in the
* jump table by writing a jump table block header
* which is a single long with the address of block
* instantiation (unused except for debugging) and
* returning the address of the first entry -
* which stores the address of the first label for
* this block.
*/
#define start_here(_table_addr) \
asm volatile ( \
".section my_jump_table,\"a\"\n" \
".align 4\n" \
"1: .long 1b\n" \
".text \n" \
"movl $1b, %0\n" \
: "=r"(_table_addr): : "memory"); \
_table_addr++
/*
* Change PC (jump) to the supplied address.
* This is simply a computed goto.
*/
#define jump(_addr) \
asm volatile ( \
"jmp *%0\n" : : "r"(_addr) : "memory")
#define always_inline inline __attribute__((always_inline))
#define LABELS_NUM 8
static always_inline int test(void)
{
int labels[LABELS_NUM] = {1, 0 ,1, 0, 1 ,1, 0, 2};
int i = 0;
void ** p;
start_here(p);
printf("Jump table address for this instance is %p\n", p);
for(i=0; i< LABELS_NUM; i++) {
// printf("p[%d] is %p\n", i, p[i]);
jump(p[labels[i]]);
printf("Not reached!\n");
label_me();
printf("Hello world! %d\n", i);
if(unknown) continue;
label_me();
printf("Middle ground %d\n", i);
if(unknown) continue;
label_me();
printf("Last stop! %d\n", i);
break;
}
return 0;
}
int main(void)
{
printf("First call...\n");
test();
printf("Done.\n\n");
printf("2nd call...\n");
test();
printf("Done.\n\n");
printf("3rd call...\n");
test();
printf("Done.\n\n");
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment