Reputation: 880
I read this source (https://github.com/lattera/glibc/blob/master/stdio-common/vfprintf.c) and find some interesting lines, which I do not fully understand:
#ifdef SHARED
/* 'int' is enough and it saves some space on 64 bit systems. */
# define JUMP_TABLE_TYPE const int
# define JUMP_TABLE_BASE_LABEL do_form_unknown
# define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL
# define JUMP(ChExpr, table) \
do \
{ \
int offset; \
void *ptr; \
spec = (ChExpr); \
offset = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown) \
: table[CHAR_CLASS (spec)]; \
ptr = &&JUMP_TABLE_BASE_LABEL + offset; \
goto *ptr; \
} \
while (0)
...
#define STEP0_3_TABLE \
/* Step 0: at the beginning. */ \
static JUMP_TABLE_TYPE step0_jumps[30] = \
{ \
REF (form_unknown), \
REF (flag_space), /* for ' ' */ \
REF (flag_plus), /* for '+' */ \
REF (flag_minus), /* for '-' */ \
REF (flag_hash), /* for '<hash>' */ \
REF (flag_zero), /* for '0' */ \
REF (flag_quote), /* for '\'' */ \
REF (width_asterics), /* for '*' */ \
REF (width), /* for '1'...'9' */ \
REF (precision), /* for '.' */ \
REF (mod_half), /* for 'h' */ \
...
I wrote simple example and understand that this line &&do_##Name
cast do_##Name
to pointer to void. But I don't understand how works pointer arithmetic in this case: #define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL
Can someone write simple explanation? Or write some link to Internet resource where I can read about this technique.
Upvotes: 1
Views: 186
Reputation: 60117
Presumably to be guaranteed to have liner complexity, the code is using a jump table made of labels used as values.
Labels-as-values is a GNU C extension which allows you to use &&
to take the address of a label. The address is typed void *
and you can then use goto *address;
to jump to it.
The little twist with the base label is that instead of storing absolute labels in the table, to code is storing offsets from a do_uknown_form
label.
That saves space (the offset can be an 4 byte int instead of an 8 byte pointer) in the table and helps generated better code for shared libs (hence the #ifdef SHARED
) as even a static const
jump table of absolute labels would need to be patched when the code is loaded in a relocatable shared library, but the offsets remain the same so then the need for patching vanishes and the table can be stored in read-only memory.
The technique is described in the How to Write Shared Libraries essay by Ulrich Drepper.
Upvotes: 2