Perlテックブログ

ITエンジニアの成長意欲を刺激する技術考察、モジュール開発の日記。Perlイベントや国内や海外のPerlの記事の紹介。

ExtUtils::CBuilderのコードを純粋なC言語の世界から呼び出したい

SPVM で、10000回サブルーチンが実行されたときに、そのサブルーチンだけを、JITして、機械語コンパイルするというようなことを行いたいと思っている。

これは思いのほかに難しい。

JITコンパイルのためのコードはExtUtils::CBuilderに依存

JITするためのコードは、ExtUtils::CBuilderのcompileメソッドに依存する。

  # SPVM::Build

  # Compile source files
  my $quiet = 1;
  my $cbuilder = ExtUtils::CBuilder->new(quiet => $quiet, config => $cbuilder_config);
  my $object_files = [];
  
  # Object file
  my $object_file = $source_file;
  $object_file =~ s/\.c$//;
  $object_file .= '.o';
  
  # Compile source file
  $cbuilder->compile(
    source => $source_file,
    object_file => $object_file,
    include_dirs => $include_dirs
  );
  push @$object_files, $object_file;

10000回を超えた時に、JITを行うというコードはC言語の世界

一方で、10000回を超えた時に、JITを行うというコードはC言語の世界から呼ばないといけない。

// spvm_runtime.c

SPVM_API_VALUE SPVM_RUNTIME_call_sub(SPVM_API* api, int32_t sub_id, SPVM_API_VALUE* args) {
  (void)api;
  
  // Runtime
  SPVM_RUNTIME* runtime = SPVM_RUNTIME_API_get_runtime(api);
  
  // 10000回呼び出されたサブルーチンがあればJITする
}

純粋なCの世界からPerlのサブルーチンを呼びだすための手法

できるかどうか試していたのだけれど、次の方法で、動きそうな予感がしている。

1. C言語側にインターフェースを実装する

C言語側にインターフェースを実装して、XSのコードに依存させないようにする。apiというのがちょうどインターフェースを実装しているので、これを利用。これで、C言語側から呼び出せる。

api->new_jitcode_sub(api, "Point::clear");
2. XSの上部で、純粋なC関数を実装。

XSの上のほうにC言語がそのまま書ける。ここで、Cの関数を定義して、ここから、ExtUtils::CBuilderを利用しているPerlの関数を呼んであげる。

int SPVM_STAB_new_jitcode_sub(SPVM_API* api, const char* sub_name) {
  dSP;
  int count;

  ENTER;
  SAVETMPS;

  PUSHMARK(SP);
  XPUSHs(sv_2mortal(newSVpv(sub_name, 0)));
  PUTBACK;

  count = call_pv("SPVM::new_jitcode_sub", G_SCALAR);

  SPAGAIN;

  if (count != 1)
      croak("Big trouble\n");

  int32_t success = POPi;

  PUTBACK;
  FREETMPS;
  LEAVE;
}
3. この関数のアドレスをインターフェースに設定

この関数のアドレスをインターフェスに設定してあげて終了。

api->new_jitcode_sub = SPVM_STAB_new_jitcode_sub;

インターフェスが実装されているファイル

C言語におけるインターフェースの実装に参考になるのは以下の二つのファイル