Not only is the Internet dead, it's starting to smell really bad.:最新 5 日分

2022/01/12(Wed)

[どうでもいい話] オープンソースにしない自由もGitHubを使わない自由もあるよ

オープンソース開発者、広く使われてるライブラリを破壊し、多くのプロジェクトに影響だと。

まぁぶっちゃけ

で、争いは同じレベルのもの同士でしか発生しないのである(例のカンガルーの画像略)。

で金の話、今風のナウい解決策としてはGitHubもソースコードのあらゆる箇所にコメントで広告入れまくるかいっそ難読化加工して、有料会員だけオリジナルのコードがチェックアウトできるようにして、その収益を作者に還元すればいいんじゃないですかね(適当)。

まぁこんなのマジでやったとすると今度はYoutubeのように他人の権利物をパッチワークして金を稼ごうとする泥棒が雪崩れ込んできて破綻する未来しかないので、あくまでこれは冗談絶対に成功しないと断言し…いやもうこれわかんねえな。ここ20年くらい泥棒が成功する姿ばかりみてきたからな、ゲームチェンジ(笑)。

そうそう、昔はプログラム国際化(ドキュメントの翻訳ふくむ)なんてやっても一銭にもならんしハッカー(死語)としても底辺と蔑まれ時には叩かれまでされたのだが、いまやYoutubeで海外の他人の権利物(スポーツものとかね)にDeepLで翻訳した字幕をつけて適当にテロップつけてアップロードするだけで100万チャンネル登録で札束風呂も夢じゃないのです。はー!そらもうオープンソースなんて誰もやらんわ!

オープンソースと金の問題で思い出すのは、TheoがSun MicrosystemがOpenSSHを金銭的に支援するのではなくSunSSHをフォークしたことを嘆いてた件かな(Oracle買収後にOpenSSHに回帰)。 なおOpenSSH自体もオリジナルSSHが有償化する前のバージョンからのフォーク由来なので、オリジナルSSHの作者もOpenSSHによるフォークを嘆いてたというオチがつくのだが(後に商標でもめてたな)。

2022/01/11(Tue)

[オレオレN6] 続cat(1)

前回の続き、作業量の少ないgetc(3)禁止だけオレオレcat(1)に適用したけど( 差分)、これだけでもそこそこ速くなるわな、まだGNU coreutilsには追いつかないけど。

残りはputchar(3)の方もwrite(2)に置き換える作業だけど-uオプションの問題が出てくるのでめんどくさいのでまた次回、つーか-uってどういう事情でできたんだっけ…GNU coreutilsは未実装だしよくわからん。

なおオプション無しのcatの場合のプロファイル

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls   s/call   s/call  name
 99.53     27.60    27.60   131073     0.00     0.00  read
  0.29     27.68     0.08   131072     0.00     0.00  write
  0.18     27.73     0.05        1     0.05    27.73  catfn_std
...

そしてcat -sのプロファイル

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total
 time   seconds   seconds    calls   s/call   s/call  name
 40.94     59.03    59.03 8589934102     0.00     0.00  __sputc
 30.03    102.33    43.30   131073     0.00     0.00  read
 27.65    142.21    39.87        1    39.87   142.37  catfn_s
  1.27    144.04     1.83                             print_number
  0.09    144.17     0.13   131071     0.00     0.00  write
  0.01    144.18     0.01   131072     0.00     0.00  __swbuf
  0.01    144.19     0.01   131071     0.00     0.00  __flockfile_internal
  0.01    144.20     0.01   131071     0.00     0.00  fflush
  0.00    144.20     0.00   131071     0.00     0.00  __funlockfile_internal
  0.00    144.20     0.00   131071     0.00     0.00  __sflush
  0.00    144.20     0.00   131071     0.00     0.00  __swrite
...

やはり圧倒的にputchar(3)の中の人である__sputcが重いのよね、まぁ-pgつきでコンパイルされてるからinline化がキャンセルされてるせいもあるけど。

[オレオレN6] 続comm(1)

ざっくり 書いてみたけど、 前回書いたcheck-order/nocheck-order周りの挙動の違いを決めかねてるので暫定的な実装になってる。

まぁ誰も使ってないだろうからこんなんGNU coreutilsの挙動に寄せてしまっていい気もするのだが、前後行をstrcmp(3)する分がっつり性能落ちるのがな。

2022/01/08(Sat)

[オレオレN6] cat(1)

続き、GNU coreutilsのcat(1)のベンチとってみたら素cat以外*BSDの実装よりかなり高速なんやな。 オレオレN6だとgccが古いせいでGNU coreutilsはwc(1)のAVX2使ったコードが通らんのだが、cat(1)だけビルドし前回と同じテストデータで計測。

+ '[' -f /etc/shrc ']'
+ '.' /etc/shrc
+ 'CAT=/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat'
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' 'unko.dat'
       41.21 real         0.07 user        21.81 sys
+ time cat 'unko.dat'
       41.80 real         0.06 user        24.50 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -e 'unko.dat'
      108.16 real        65.45 user        32.18 sys
+ time cat -e 'unko.dat'
      151.92 real        95.74 user        47.41 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -t 'unko.dat'
      113.16 real        66.24 user        33.90 sys
+ time cat -t 'unko.dat'
      147.20 real        91.61 user        46.19 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -v 'unko.dat'
      110.72 real        65.61 user        32.95 sys
+ time cat -v 'unko.dat'
      152.99 real        97.84 user        46.11 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -et 'unko.dat'
      107.98 real        65.85 user        32.22 sys
+ time cat -et 'unko.dat'
      149.51 real        92.71 user        48.42 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -s 'unko.dat'
       45.65 real         9.60 user        23.44 sys
+ time cat -s 'unko.dat'
       60.12 real        28.41 user        21.52 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -se 'unko.dat'
      110.89 real        67.57 user        31.69 sys
+ time cat -se 'unko.dat'
      156.44 real        98.55 user        48.38 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -st 'unko.dat'
      114.80 real        66.20 user        36.18 sys
+ time cat -st 'unko.dat'
      149.40 real        94.95 user        44.72 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sv 'unko.dat'
      116.62 real        66.35 user        36.50 sys
+ time cat -sv 'unko.dat'
      158.82 real        99.58 user        48.81 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -set 'unko.dat'
      116.37 real        66.81 user        34.30 sys
+ time cat -set 'unko.dat'
      154.78 real        95.49 user        49.65 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -n 'unko.dat'
       52.89 real        10.97 user        27.40 sys
+ time cat -n 'unko.dat'
       68.15 real        32.33 user        26.47 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -ne 'unko.dat'
      117.20 real        67.06 user        35.81 sys
+ time cat -ne 'unko.dat'
      165.24 real       106.97 user        47.94 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -nt 'unko.dat'
      117.70 real        66.74 user        36.78 sys
+ time cat -nt 'unko.dat'
      160.01 real       104.61 user        46.99 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -nv 'unko.dat'
      116.72 real        66.18 user        37.73 sys
+ time cat -nv 'unko.dat'
      164.62 real       105.56 user        49.82 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -net 'unko.dat'
      118.85 real        66.90 user        37.19 sys
+ time cat -net 'unko.dat'
      164.03 real       102.97 user        51.58 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -b 'unko.dat'
       49.34 real        10.84 user        24.97 sys
+ time cat -b 'unko.dat'
       68.13 real        32.48 user        24.63 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -be 'unko.dat'
      118.19 real        67.35 user        35.15 sys
+ time cat -be 'unko.dat'
      167.21 real       104.96 user        51.11 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -bt 'unko.dat'
      117.35 real        66.59 user        36.01 sys
+ time cat -bt 'unko.dat'
      160.49 real       101.02 user        50.37 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -bv 'unko.dat'
      118.55 real        66.35 user        38.22 sys
+ time cat -bv 'unko.dat'
      166.44 real       104.89 user        51.97 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -bet 'unko.dat'
      115.50 real        66.51 user        36.82 sys
+ time cat -bet 'unko.dat'
      166.10 real       102.25 user        52.09 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sn 'unko.dat'
       50.26 real        11.14 user        24.25 sys
+ time cat -sn 'unko.dat'
       70.62 real        32.85 user        26.35 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sne 'unko.dat'
      119.40 real        67.05 user        39.21 sys
+ time cat -sne 'unko.dat'
      170.98 real       106.63 user        49.41 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -snt 'unko.dat'
      121.00 real        66.04 user        30.92 sys
+ time cat -snt 'unko.dat'
      152.32 real       104.73 user        41.49 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -snv 'unko.dat'
      116.84 real        66.84 user        37.08 sys
+ time cat -snv 'unko.dat'
      163.88 real       105.05 user        49.45 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -snet 'unko.dat'
      115.43 real        66.58 user        36.32 sys
+ time cat -snet 'unko.dat'
      163.72 real        99.05 user        52.54 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sb 'unko.dat'
       49.56 real        10.53 user        26.36 sys
+ time cat -sb 'unko.dat'
       66.04 real        32.31 user        23.41 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sbe 'unko.dat'
      111.09 real        67.09 user        31.62 sys
+ time cat -sbe 'unko.dat'
      154.04 real       105.49 user        40.40 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sbt 'unko.dat'
      111.01 real        67.21 user        30.67 sys
+ time cat -sbt 'unko.dat'
      155.74 real        98.00 user        49.85 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sbv 'unko.dat'
      112.82 real        66.85 user        32.98 sys
+ time cat -sbv 'unko.dat'
      162.87 real       102.00 user        51.37 sys
+ time '/usr/pkgsrc/sysutils/coreutils/work.x86_64/coreutils-9.0/src/cat' -sbet 'unko.dat'
      114.34 real        66.41 user        34.65 sys
+ time cat -sbet 'unko.dat'
      159.16 real       102.44 user        46.96 sys

ワイの実装のように数パーセントの微妙な性能向上だとやる気も失せるけど、こんな感じで数十パーセント向上するなら今の実装置き換えてもいいかなって気分になるよね…

この性能差ってばマクロ版のgetc/putcharですらボトルネックちゅーことかな、オレオレcat(1)も実装見直さないとアカンなこりゃ、来年あるいは来世はがんばる。

あと*BSDとGNU coreutilsでcat -be*の実装で空白行のパディング有無に差があるがこれは既存実装を踏襲やな…

[オレオレN6] comm(1)

いまどき誰もcomm(1)とか使う人もいないと思うのだがこいつのGNU拡張も一応実装するかとコード読んだら、それ以前の問題でLINE_MAX以上の長さの行が扱えない制限が未だに残ってるじゃねえの。 こうして誰も使ってないことをコードが自ら証明していくのいいよね…

あとGNU coreutilsでのcomm(1)の実装って

  1. デフォルトでは行がソートされてなければ警告を出す
  2. --check-orderが指定されてる場合、行がソートされてなければエラー終了する
  3. --nocheck-orderが指定されてる場合は、行がソートされてるかはチェックしない

という挙動なんだけど*BSDの場合は3がデフォルトなんだよな。なのでGNU拡張実装すると挙動変わっちゃうのが悩ましい。

2021/12/28(Tue)

[どうでもいい話] 元現場猫のひとりごと

もう終わりだよこの2021年、2週間後の日本は NY(New Year)というタイミングでこの現場猫案件、京大データ喪失でなく京大生DT喪失ならよかったのにね…

そもそも/LARGE0から/LARGE1へのミラーリングはバックアップとよべるものじゃないのでデータ喪失なんぞ起きて当然の約束された事故なのだけど、後者の文章に

弊社 100%の責任により

とあるのでサービスレベルアグリーメントと設計に乖離があったってことっすかね、現場猫も現場猫なら営業猫も営業猫だよヨシ!

そんで現場猫の方のやらかしは

3 ファイル消失が発生した原因
バックアップスクリプトには、find コマンドにより 10 日以上古いログファイルを削除する処
理が含まれています。スクリプトの機能改善と合わせて、find コマンドの削除処理に渡す変数名
を視認性・可読性を高めるため変更いたしましたが、この修正したスクリプトのリリース手順に
考慮不足がありました。
bash は、シェルスクリプトの実行中に適時シェルスクリプトを読み込みます。この挙動によ
る副作用を認識できておらず、実行中のスクリプトが存在している状態でスクリプトの上書きに
よりリリースしてしまったことで、途中から修正したシェルスクリプトの再読み込みが発生し、
結果的に未定義の変数を含む find コマンドが実行されてしまいました。この結果、本来のログ
ディレクトリに保存されたファイルの削除をする処理ではなく、/LARGE0 のファイルを削除し
てしまいました。

うーん、古より伝わるUNIX(TM)園児にゃぁの最も簡単な自殺法「rm -rf $TMP/*実行時に$TMPをセットし忘れる」とほぼ同じというね、うーんクラッシック。

これ読む限りでは

find ${logpath}/ -mtime -10 -delete

的なコードだったのが問題で、bashは再読み込みするから実行中にファイル書換えたことで云々は軽率ではあるけど根本的な問題ではなく

if [ -n "$logpath" ]; then
	find ${logpath}/ -mtime -10 -delete
fi

というフェールセーフを思いつくほどの頭がコーダーにしろコードレビュアー(まぁ存在したかは怪しい)に無かったのが問題だったという気がする、というかlogrotate(8)使わんの?log4jといいオレオレ実装はわりとろくでもない結果にしかならんぞ。

(追記) よく考えると if [ -n "$logfile" ]; then のある行より先にファイルポジションが進んでたら結局アウトか、申し訳ないこれはワイの責任HP(Hundled Percent)でございます。

同様の理由で set -u もアウト、特にこっちはスクリプトの先頭で宣言するだろうから、元スクリプトに無かったら無理だわ。

まぁワイはシェルスクリプト怖いのでshebang含めて3行以上のシェルスクリプトは書かない主義なのでこういう頓珍漢なことを書いても許してチョンマゲ。

@隙在自語

後世への戒めとしてワイがかつて遭遇したfind(1)使った事故についての怪談をしよう。

昔々とある組織の基幹サーバーで夜間バッチが動いてたそうじゃ、このバッチの目的は

  • ある特定のディレクトリ以下に蓄積される大量の日次ファイル
  • ↑のタイムスタンプを確認し一定期間より古いものはすべて削除する

というものじゃった。

この日次ファイル、ファイル名には空白文字を含まないという暗黙の了解があったのだが

  • 日次ファイルはクライアント端末からアップロードされる
  • ファイル名はその端末およびユーザー固有情報より組み立てられる
  • 端末およびユーザー固有情報はおま環で空白文字を含むものがわずかに存在した
  • クライアント/サーバー側どちらも空白文字の存在チェックは行っていない

といういい加減な仕様と運用によるイレギュラーにより、全体の0.1%くらいはファイル名に空白文字を含むものがあったんですわ。

そして削除スクリプト書いた現場猫はそもそもfind(1)に-print0オプションがあることすら知らないレベルだったと思われる。

よってこの0.1%のイレギュラーは削除されることなくどんどん蓄積されていくことに。 ファイルサイズ自体はとても小さいのでディスクスペースを圧迫し容量不足でアラートを上げるようなことも無く、誰にも気づかれることは無かった、そうこれを読んでるオッサンどもの血中脂肪のようにね。

そして数年が過ぎ開発担当者はとっくに別の現場に異動になりこのスクリプトの存在を知るものは誰もいなくなった頃、満を持したのかのように爆弾は破裂した。

もうお判りですね?そう大量の削除漏れファイルによってinodeが枯渇して、ディスクに空きがあってもファイルが作れない状態になってしまったのだ。

その結果として夜間バッチのほぼ全てが異常終了し、ジョブマネージャーの管理画面が真っ赤に染まりオペレーターは絶叫しサイレンは鳴り響き重大障害として取締役が呼ばれるまでエスカレート、そんでワイが後始末をぜんぶ押し付けられて深夜に呼び出され、殺気立った面々に罵倒され熱く焼かれた鉄板の上でアチアチ踊りさせられるというね、あーほんとクソ。

ちなみにこのスクリプトにはもうひとつ面白い話がある、なぜかよりにもよってC Shellつまりcsh(1)で書かれたスクリプトなのだ。 これはジョブ管理にとある会社のスターウォーズの登場人物っぽい名前のミドルウェアが採用されてたのだが、なぜかスクリプトはよりにもよって(2回目)C Shellスクリプトで書かなくてはならないという謎ルールが上から降りてきたためとのこと。

あまりに理不尽な話なので、なぜよりにもよって(3回目)C ShellなのかBorne Shellで開発すべきなのではと何度と無く苦情を言ったのだが、このミドルウェアの開発元がC Shellを推奨してるんだからそうしろという回答が帰ってくるのみ。 もちろんマニュアルを読んでもそんなトンデモな制限は無い *1

そしてある日はたと気づいた、これは営業猫同士の伝言ゲームによって「Cで書かれたプログラムあるいはシェルスクリプトを推奨」が「Cシェルスクリプトを推奨」に化けたんだこれ! 現実世界でパタリロの「さきほどの伝言の意味を知らせよ、あるいは暗号か」→ 「さっきはどの田楽も実は白子よ、蟻は貧乏か」をやるんじゃねえよクソァ!

というかこの現場、他にもろくでもない思い出が一杯あるのだがそれはまた別の機会ということで。

*1:ちなみにそのミドルウェア、正式なビルドイン言語はなんと時代遅れにもほどがあるTclだった、えぇ…

2021/12/16(Thu)

[プログラミング] sljit覚書(その1)

ちょこっと弄っただけなのでさっぱりわからんのだが、来世で使う可能性もあるので生まれ変わった僕が検索でヒットするようメモでも残しておこう。 といいつつ今の時代は検索エンジンからこのチラシの裏に辿り着くことなんてまず不可能だろうけどな!

@sljit_create_compiler

SLJIT_API_FUNC_ATTRIBUTE struct sljit_compiler* sljit_create_compiler(void *allocator_data, void *exec_allocator_data);

その名の通りJust-In-Timeコンパイラのインスタンスを生成する、失敗したらNULLが返る。

struct sljit_compiler *c;
c = sljit_create_compiler(NULL, NULL);
if (c == NULL)
	abort();

引数2つあるけど通常利用であればどちらもNULLでいい、この引数はメモリ管理をオーバーライドするような特殊な使い方をする時にのみ必要なものだから。

まず第一引数allocator_dataについて、sljitは通常メモリアロケーションに標準のmalloc/freeを使うが、freestanding環境などでこれらの関数が使えない場合は

  • -DSLJIT_STD_MACROS_DEFINED=0を指定してC標準のmalloc/free/memcpy/NULLを無効にする
  • SLJIT_MALLOC/SLJIT_FREE/SLJIT_MEMCPY/NULLをオレオレ実装で再定義する
  • 渡したallocator_dataはsljit_compiler構造体が保持する
    struct sljit_compiler {
    ...
            void *allocator_data;
    ...
    };
    
  • SLJIT_MALLOC/SLJIT_FREEの第二引数にこのallocator_dataが渡される
    #ifndef SLJIT_MALLOC
    #define SLJIT_MALLOC(size, allocator_data) malloc(size)
    #endif
    
    #ifndef SLJIT_FREE
    #define SLJIT_FREE(ptr, allocator_data) free(ptr)
    #endif
    
  • よってメモリチャンクなり関数ポインタなりフラグ情報なりなんなり、オレオレmalloc/free実装にあわせてお好きにどうぞ

という手順でオーバーライドする、まぁbpf(4)のようにkernelにsljitを組み込むなどの用途でもない限り必要は無い。

お次にexec_allocator_data、こちらはJITコンパイルした実行可能コード用のアロケーター、同様の手順でオーバーライド可能。

  • -DSLJIT_EXECUTABLE_ALLOCATOR=0を指定する
  • SLJIT_MALLOC_EXEC/SLJIT_FREE_EXEC/SLJIT_EXEC_OFFSETをオレオレ実装で再定義する
  • exec_allocator_dataについてはallocator_dataと同じように使う
  • 以下同文

なお何故SLJIT_MALLOC/SLJIT_FREEのペアとSLJIT_MALLOC_EXEC/SLJIT_FREE_EXECのペアのふたつも必要なのかというと、これはいまどきのCPUやOSはセキュリティ上の理由で データ実行防止というというセキュリティ機構でスタックやヒープ上でのコード実行を禁止しているから。 よってPROT_EXECフラグつきでmmap(2)呼んでページを実行可能にマークするとか、W^XやPaXそしてExcec SheildなどでNXビットを有効にするなどの処理が必要になる。 まぁ知らんでいいこと知りたい暇なオッサンは隅っこでsljitExecAllocator.c/sljitProtExecAllocator.c/sljitWXExecAllocator.cのコードでも読んでろ。

よしここまで危険性を書いとけば意味も無くオーバーライドしてハードラックとダンスっちまうやつも現れないだろう。 そもそも本当にJITを使う必要性があるのかからよーく考えような、Microsoft Edgeブラウザなんかは性能向上より安全性をとってJIT無効にするモードを用意するくらいやぞ。

@sljit_compiler_verbose

SLJIT_API_FUNC_ATTRIBUTE void sljit_compiler_verbose(struct sljit_compiler *compiler, FILE* verbose);

冗長モードをセットする、開発中は標準エラー出力にでもデバッグ情報を出すようにしておく。

sljit_compiler_verbose(c, stderr);

これ-DSLJIT_VERBOSE=0するとコンパイルエラーになるのがちょっと不便、空文におきかえてくれればいいのにね-DNDEBUG=1の時のassert(3)みたいに。

余談だけどデフォルトでSLJIT_DEBUGが有効なので、アサーション無効にしたければ-DSLJIT_DEBUG=0でコンパイルする必要があることにも注意。

@sljit_emit_enter

SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_enter(struct sljit_compiler *compiler,
        sljit_s32 options, sljit_s32 arg_types, sljit_s32 scratches, sljit_s32 saveds,
        sljit_s32 fscratches, sljit_s32 fsaveds, sljit_s32 local_size);

これはx86のenter命令的なもの?といえばいいのかな、関数(=JITコンパイルされた実行コード)の入口。

まず第1引数のoption、これは今のところ

  • SLJIT_F64_ALIGNMENT … アライメントをsjjit_sw型(=long)でなくsljit_f64型(=double)のサイズに合わせる

しか無いので、32bit環境でも無い限り0でいい。

第2引数のarg_typesはJITコンパイルされた実行コードを関数ポインタとして呼び出すときの

  • 引数の個数 … 最大3個まで、SLJIT_ARG1~3マクロのORで表現する
  • 引数の型 … sljit_sw(符号ありワード型)かsljit_uw(符合なしワード型)のいずれか、SLJIT_ARG1~3マクロの引数にSWあるいはUWをセットすることで表現する

を指定するもの、通常引数ってスタックに置かれるもんだけどStack-Less JITの名の通りスタックが無いのでこの制限となる、詳しい話は回を改めて説明する。

以下は一例、実行コードを

typedef sljit_sw (*func_t)(sljit_sw, sljit_sw, sljit_sw);

と3つの引数を渡して呼び出すのであれば、arg_typesの指定は

sljit_emit_enter(c, SLJIT_ARG1(SW)|SLJIT_ARG2(SW)||SLJIT_ARG3(SW), ...);

となる、これらの引数はJITコード内だとSLJIT_S0~2という汎用レジスタに置かれる、

そんでsljitのアセンブラ(LIR)レジスタには

  • 汎用レジスタ(ワードレジスタ) … 最大は SLJIT_NUMBER_OF_REGISTERS 個
  • 浮動小数点用レジスタ … 最大は SLJIT_NUMBER_OF_FLOAT_REGISTERS 個

の2種類があり、更にそれぞれに

  • 「Scratched」 … 一時的な利用のためのレジスタ、関数の呼出前後で値が保持されない可能性がある。
  • 「Saved」 … 〃値は常に保持される

の2種類があるので合計で4種類となる。

これらにアクセスするためには

  • 汎用Scratchedレジスタ
    • SLJIT_R0~9、それ以上はSLJIT_R(N)
    • Nの最大はSLJIT_NUMBER_OF_SCRATCH_REGISTERS
  • 汎用Savedレジスタ
    • SLJIT_S0~9、それ以上はSLJIT_S(N)
    • Nの最大はSLJIT_NUMBER_OF_SAVED_REGISTERS
  • 浮動小数点数用Scratchedレジスタ
    • SLJIT_FR0~5、それ以上はSLJIT_FR(N)
    • Nの最大はSLJIT_NUMBER_OF_SCRATCH_FLOAT_REGISTERS
  • 浮動小数点数用Savedレジスタ
    • SLJIT_FS0~5、それ以上はSLJIT_FS(数値)
    • Nの最大はSLJIT_NUMBER_OF_SAVED_FLOAT_REGISTERS

というマクロを使う。

んで第3~6引数にはこの4種類のレジスタを最大何個使用するかを指定するわけ。

  • 実行コードが戻り値を返す場合、scratchesには返却値を格納するレジスタ(SLJIT_RETURN_REG)としてSLJIT_R0が必要なので、1以上の値を指定する。
  • savedsにはさっきのarg_typesで指定した引数の個数Nだけは必要になるので、N以上の値を指定する。
  • fscratchesとfsavedsは浮動小数点数を使わないなら0でいい。

ちゅーかんじ。

なおSLJIT_NUMBER_OF_*はCPUによって値は異なるので移植性に注意すること、現状でサポートされるCPUの最大公約数をとると

  • SLJIT_NUMBER_OF_REGISTERS は12個(ただしx86_32ではうち6は仮想)
  • SLJIT_NUMBER_OF_SCRATCH_REGISTERS は6個

となる、またScratchとSavedはオーバーラップする場合があることに注意。

     R0   |        |   R0 is always a scratch register
     R1   |        |   R1 is always a scratch register
    [R2]  |   S2   |   R2 and S2 represent the same physical register
    [R3]  |   S1   |   R3 and S1 represent the same physical register
    [R4]  |   S0   |   R4 and S0 represent the same physical register

オーバーラップしてるかどうかは

SLJIT_NUMBER_OF_REGISTERS - SLJIT_NUMBER_OF_SCRATCH_REGISTERS < SLJIT_NUMBER_OF_SAVED_REGISTERS

の真偽で判るはず。

第7引数local_sizeはローカルスタックサイズの指定、0~SLJIT_MAX_LOCAL_SIZE(65536)の範囲で指定するけど現状使われてないので0でいいや。 これ将来的にStack-Less JITといいつつスタックエミュレーションも実装する予定なのかな、よく判らん。

@sljit_emit_return

SLJIT_API_FUNC_ATTRIBUTE sljit_s32 sljit_emit_return(struct sljit_compiler *compiler, sljit_s32 op,
        sljit_s32 src, sljit_sw srcw);

これはsljit_emit_enterと対になるx86でいうとこのleave命令みたいな?もの、つまり関数(=JITコンパイルされた実行コード)の出口。

引数については

  • 第2引数のopにSLJIT_MOV*命令がセットされてる場合
    • 第3引数に指定したレジスタの値
    • あるいは第3~4引数で指定したメモリ範囲
    をSLJIT_RETURN_REGにセットして返す
  • SLJIT_UNUSEDがセットされてる場合には戻り値は無し(その場合は第3~4引数は0にする)

ちゅーかんじ、なおSLJIT_MOV*命令とSLJIT_MEMマクロによるメモリ範囲指定はまた回を改めて説明する。

さっきの例、実行コードが

typedef sljit_sw (*func_t)(sljit_sw, sljit_sw, sljit_sw);

とsljit_sw型を返すのであれば

sljit_emit_return(c, SLJIT_MOV, SLJIT_R0, 0);

みたいにするとSLJIT_R0の値が戻り値として返される。

@sljit_generate_code

SLJIT_API_FUNC_ATTRIBUTE void* sljit_generate_code(struct sljit_compiler *compiler);

これはJITコンパイラから実行コードを生成する、戻り値のポインタは関数ポインタにキャストすれば実行可能ちゅーこと。

@sljit_free_compiler

SLJIT_API_FUNC_ATTRIBUTE void sljit_free_compiler(struct sljit_compiler *compiler);

これはJITコンパイラを破棄する、実行コードは破棄されない。

@sljit_free_code

SLJIT_API_FUNC_ATTRIBUTE void sljit_free_code(void* code, void *exec_allocator_data);

これは実行コードを破棄する、第2引数はさっきのオーバーライドをしてないのでNULLでいい。

ここまでの例をざっとまとめて、引数1~3を加算して返すJITコードの例。

#include <stddef.h>
#include <stdio.h>

#include <sljitLir.h>

typedef sljit_sw (*func_t)(sljit_sw, sljit_sw, sljit_sw);

int
main(void)
{
	struct sljit_compiler *c;
	func_t func;
	sljit_sw ret;

	c = sljit_create_compiler(NULL, NULL);
	if (c == NULL)
		abort();
	sljit_compiler_verbose(c, stderr);

	if (sljit_emit_enter(c, 0, SLJIT_ARG1(SW)|SLJIT_ARG2(SW)|SLJIT_ARG3(SW), 1, 3, 0, 0, 0) != SLJIT_SUCCESS)
		abort();
	/* R0 = S0 */
	if (sljit_emit_op1(c, SLJIT_MOV, SLJIT_R0, 0, SLJIT_S0, 0) != SLJIT_SUCCESS)
		abort();
	/* R0 += S1 */
	if (sljit_emit_op2(c, SLJIT_ADD, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_S1, 0) != SLJIT_SUCCESS)
		abort();
	/* R0 += S2 */
	if (sljit_emit_op2(c, SLJIT_ADD, SLJIT_R0, 0, SLJIT_R0, 0, SLJIT_S2, 0) != SLJIT_SUCCESS)
		abort();
	/* return R0 */
	if (sljit_emit_return(c, SLJIT_MOV, SLJIT_R0, 0) != SLJIT_SUCCESS)
		abort();

	func = (func_t)sljit_generate_code(c);
	if (func == NULL)
		abort();
	sljit_free_compiler(c);

	ret = (*func)(1, 2, 3);

	printf("%ld\n", ret);

	sljit_free_code((void *)func, NULL);

	exit(0);
}

これをコンパイルし実行すると

$ gcc -I./sljit/sljit_src unko.c ./sljit/sljit_src/sljitLir.c -o unko
$ ./unko
  enter options: args[sw,sw,sw] scratches:1 saveds:3 fscratches:0 fsaveds:0 local_size:0
  mov r0, s0
  add r0, r0, s1
  add r0, r0, s2
  return r0
6

はい、1 + 2 + 3 = 6が表示されました。

@次回

いつになるかは未定だけどLIRの命令についてざっと説明する予定。