-
-
Save mtasic85/b6aed67b4a8c65bfab1d2b4dee8686e7 to your computer and use it in GitHub Desktop.
How to do a computed goto in an inline function in GCC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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