【更新あり】PC-9801のプログラム(ソースコード無し)をリバースエンジニアリングしてくれ!→変態技術の塊なことが判明しました

あの(一見)ネタ(と思ってしまうぐらいヤバい)依頼が無事解決したとのことで。解析された方のツイートのみのまとめです。 (たぶん追記される予定です)
376
roentgen/技術書典3 か-46 @roentgen

ネイティブコードにコンパイルするといっても中間コードを call に置き換えただけだとは前述したけど、ループカウンタなど、整数として使われないことが確実なときだけは結構普通のコンパイラっぽいネイティブコードを吐いたりする

2018-01-01 01:13:32
roentgen/技術書典3 か-46 @roentgen

実数は IEEE754 が使われていてこれは本当に助かった。オペコードは、 double か float, 文字列しかとらない。これらのデータ型を、マシンスタックと仮想マシンスタックとの間で交換する命令は非常に充実しており、これは BASIC で特徴的だった

2018-01-01 01:13:33
roentgen/技術書典3 か-46 @roentgen

大体最近の VM の命令体系はプリミティブデータ型を軽視する傾向にあるけど、 BASIC はかなり実践的で地に足がついている

2018-01-01 01:13:33
roentgen/技術書典3 か-46 @roentgen

もう一つ、空文字列 "" は演算によって得られた空と明示された空文字列を明確に区別している。最近の言語でも、ここを削りすぎて破綻している言語は沢山あるので、20 年以上前にこの落としどころにたどり着いていた BASIC はかなり先見性があると言えそうだ

2018-01-01 01:13:33
roentgen/技術書典3 か-46 @roentgen

通常、空文字列を区別しようとするとインターン文字列のような高価な方法になりがちだが、 割り当てアドレス空間を使って非常に安価に判定できるようになっている

2018-01-01 01:13:33
roentgen/技術書典3 か-46 @roentgen

この辺に気付いて、クソ言語だと思っていた BASIC って実は結構ポテンシャルがあったのでは?ビル・ゲイツって実はすごかったんでは??と完全に考えを改めることになりました

2018-01-01 01:13:34
roentgen/技術書典3 か-46 @roentgen

一方でスタックの使い方は猿のようであった。ブロックがないこと(構造化 BASIC でもない限り)は当時のプログラムとしては相当に割を食っている。すべてのデータは、サブルーチンを抜けるときまで保持される

2018-01-01 01:47:39
roentgen/技術書典3 か-46 @roentgen

構造体を持たない言語では変数が全部スタックに積まれたままになる、程度の話に聞こえるけど、実はファイル IO に使うあの不思議な名前付きのレコードバッファ、あれもスタックに確保されていた

2018-01-01 01:47:39
roentgen/技術書典3 か-46 @roentgen

このためマシンスタックを C では考えられないほど使う。サブルーチンのプロローグでマシンスタックを拡張する関数が呼び出される。 x86 か PPC でないとつらそうで、ポテンシャルがあっても今日日たとえば ARM に対応できないというのは処理系としては生きにくい

2018-01-01 01:47:40
roentgen/技術書典3 か-46 @roentgen

尤も、ブロックスコープなどでマメに削除できるというのに依存しきったコードてのも最近では (C++ では)ほとんど見ない。大体参照やスマートポインタによって遅延されるので。それこそ(BASIC 同様) string くらいだ。先見性の賜物なのか偶然かは分からないがいずれにせよ時代には合わなかった

2018-01-01 01:47:40
roentgen/技術書典3 か-46 @roentgen

ちなみに BASIC から生成されたコードは、文字列をいちいち割り当ててコピーする。途方もなく非効率だが、これで GC なしでプログラマはメモリ管理をしないで済む。 caller スタック上のレコードバッファはコピーせず参照のみを渡す。 callee 側で無制限アクセス可能、という話でもある

2018-01-01 01:47:40
roentgen/技術書典3 か-46 @roentgen

実際、本当に callee 側で caller の巨大スタックフレームの中を弄り回すコードがあって、技術的な面白ポイントを抜けて淡々と解析していた二週間目、ここだけは少し泣きたくなった. BP 相対でフラットにアクセスしやがるのでね……

2018-01-01 01:47:41
roentgen/技術書典3 か-46 @roentgen

BASIC 仮想マシンは他と同様フラグレジスタを持たない。だが cmp 命令は持っており、これはマシンのフラグを変更する。だからジャンプはネイティブ命令で行うのだけど、 and, or が混じると解析の難易度(というか混乱度)が高まる。これはスクリプトで ASM に notation を付けてカバーした

2018-01-01 02:14:11
roentgen/技術書典3 か-46 @roentgen

残念ながら使用された BASIC コンパイラでは最適化は殆どない。整数演算だけはそれっぽく最適化されるが、それ以外は全然されない。短絡評価もない。愚直に、書いた通りに実行される。 BASIC のポテンシャルに、コンパイラの技術が追い付いていなかったようだ

2018-01-01 02:14:11
roentgen/技術書典3 か-46 @roentgen

20 年前といえばコンパイラでも最適化できるものは最適化コンパイラと呼ばれ、高価だった(少なくとも高校にあった Quick-C にはなかった)。 BASIC コンパイラにそれを求めるのは無理ゲーなのだけど、言語のポテンシャルを生かせなかったのは残念なことだなぁ

2018-01-01 02:14:11
roentgen/技術書典3 か-46 @roentgen

というのも、BASIC の中間コードから愚直にネイティブ化されたコードは、自然なネイティブコードに比べて 5~10 倍以上のコードフットプリントになるから。 call の多さ, 文字列のコピー、最適化なし、短絡評価なし、スタックの利用はお猿ときている。

2018-01-01 02:14:11
roentgen/技術書典3 か-46 @roentgen

解析の上では、ネイティブコードライブラリコール, 仮想マシンのファンクションコール, BASIC サブルーチンコールと三種類の ABI (割り込みコールを数えれば 4 種類)常時あることになり、場合によっては入り乱れて非常に面倒くさかった

2018-01-01 02:14:12
roentgen/技術書典3 か-46 @roentgen

以上、 BASIC は実践的でありながら意外と先見性とポテンシャルがあったがコンパイラの技術がついてきておらず、ひたすらデカくて遅いバイナリができてしまいましたというお話でした。

2018-01-01 02:17:46