トランポリンの代わりにgotoを使う

値としてのラベル

GCCはいろいろな独自拡張があるけど,その1つにラベルの指すアドレスを値として取得する機能がある.

void *address = &&label;
...
label:
...
goto *address;

のように&&演算子をラベル名の前に付けると,そのラベルのアドレスを取り出すことができ,通常の値と同様に変数に格納することもできるのだ.もちろんgotoを使って格納したアドレスへジャンプすることも可能である.

トランポリンの代わりにgotoを用いる

ここでいうトランポリンとは,以下に示すように次に実行すべき関数のアドレスを戻り値で返し,大元の実行ループでは返されたアドレス値を次々とコールしていくようなスタイルのことを指す.

void *f();
void *g();
int x;

void *f() {
  x++; return g;
}

void *g() {
  x--; return f;
}

int main() {
  void *(*r)() = f;
  for (;;) r = r();
  return 0;
}

残念ながらトランポリンスタイルは,無駄に関数のcall-returnを繰り返しているので効率が悪い.そこで,さきほどの値としてのラベルを使って無駄な関数callをなくす.

void *f;
void *g;
int x;

void f_module(int n) {
  f = &&l_f;
  if (n) return; // 無条件にreturnすると,以降の処理がデットコードとなり削除されてしまう
l_f:{ x++; goto *g; }
}

void g_module(int n) {
  g = &&l_g;
  if (n) return;
l_g:{ x--; goto *f; }
}

int main() {
  f_module(1);
  g_module(1);
  goto *f;
  return 0;
}