卒研発表で LT をしました。そのスライドです。
今なって考えると、これ「コード生成器を用いた」だなぁ...
前回の続きなので前回のリンクはこちら。
今回の研究?で使った Compiler::CodeGenerator::LLVM は improvement/wasm
ブランチ。
デモで用いたコードはこんな感じ
#!/usr/bin/env perl use feature 'say'; say "Hello, World"; say 1 + 1; say "---------------"; sub loop { my $a = 10; for (my $i = 1; $i < $a; $i++) { if ($i == 9) { say "Last!!"; } else { say $i; } } } loop();
これは関数コール、ループ、条件分岐を交えたコードである。
これを次のスクリプトを使って WebAssembly へコンパイル、サーブを行った。
#!/usr/bin/env perl use strict; use warnings; use feature 'say'; package Compiler { use Moo; use Carp 'croak'; use Fcntl qw(:flock); use File::Basename; use Cwd 'realpath'; use Plack::Loader; use Plack::App::Directory; use Plack::Middleware::Rewrite; use Compiler::Lexer; use Compiler::Parser; use Compiler::CodeGenerator::LLVM; has lexer => ( is => 'ro', lazy => 1, default => sub { Compiler::Lexer->new } ); has parser => ( is => 'ro', lazy => 1, default => sub { Compiler::Parser->new } ); has generator => ( is => 'ro', lazy => 1, default => sub { Compiler::CodeGenerator::LLVM->new({emcc => 1}) } ); sub tokenize { my ($self, $script) = @_; return $self->lexer->tokenize($script); } sub parse { my ($self, $tokens) = @_; return $self->parser->parse($tokens); } sub compile { my ($self, $filename) = @_; my $script = $self->_read($filename); my $tokens = $self->tokenize($script); my $ast = $self->parse($tokens); my $llvm_ir = $self->generator->generate($ast); my $trimmed_filename = $self->_trim_filename($filename); $self->_write("${trimmed_filename}.ll", $llvm_ir); $self->_execute_emcc($trimmed_filename); } sub _execute_emcc { my ($self, $trimmed_filename) = @_; system "emcc ${trimmed_filename}.ll -s ALLOW_MEMORY_GROWTH=1 -o index.html"; if ($? == 0) { my $port = 8000; my $app = Plack::App::Directory->new({root => realpath('.')})->to_app; say "You can access to http://127.0.0.1:$port"; $app = Plack::Middleware::Rewrite->wrap($app, rules => sub { return 301 if s{^(.*)/$}{$1/index.html}; }); Plack::Loader->auto(port => $port)->run($app); } } sub _trim_filename { my ($self, $filename) = @_; my $basename = basename($filename); return $basename =~ s/\.[^.]+$//r; } sub _write { my ($self, $filename, $content) = @_; open my $fh, ">", $filename or croak "Failed to open a $filename"; flock $fh, LOCK_EX or croak "Failed to lock a $filename"; print $fh $content; flock $fh, LOCK_UN or croak "Failed to unlock a $filename"; close $fh; } sub _read { my ($self, $filename) = @_; open my $fh, "<", $filename or croak "Failed to open a $filename"; flock $fh, LOCK_EX or croak "Failed to lock a $filename"; my $script = do { local $/, <$fh> }; flock $fh, LOCK_UN or croak "Failed to unlock a $filename"; return $script; } __PACKAGE__->meta->make_immutable; }; Compiler->new->compile($ARGV[0]);
このスクリプトを compile.pl
とかで保存して perl compile.pl target.pl
とか実行すれば http://127.0.0.1:8000/
にアクセスしてくれやって表示される。表示すると実行結果を確認することができる。
perl
で実行した結果(ネイティブ)
perl compile.pl target.pl
で実行した結果(WebAssembly)
色々まだ問題はあるが、結果はある程度現実味がでてきた気がするぞ!!
卒研終わった皆さんお疲れ様でした!!