a21_memo01
の編集
https://essen.osask.jp/?a21_memo01
[
トップ
] [
編集
|
差分
|
バックアップ
|
添付
|
リロード
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
-- 雛形とするページ --
BracketName
EssenRev4
FormattingRules
FrontPage
Help
InterWiki
InterWikiName
InterWikiSandBox
K
MenuBar
PHP
PukiWiki
PukiWiki/1.4
PukiWiki/1.4/Manual
PukiWiki/1.4/Manual/Plugin
PukiWiki/1.4/Manual/Plugin/A-D
PukiWiki/1.4/Manual/Plugin/E-G
PukiWiki/1.4/Manual/Plugin/H-K
PukiWiki/1.4/Manual/Plugin/L-N
PukiWiki/1.4/Manual/Plugin/O-R
PukiWiki/1.4/Manual/Plugin/S-U
PukiWiki/1.4/Manual/Plugin/V-Z
RecentDeleted
SDL2_01
SandBox
WikiEngines
WikiName
WikiWikiWeb
YukiWiki
a21
a21_acl01
a21_bbs01
a21_challengers
a21_count
a21_edu01
a21_edu02
a21_edu03
a21_edu04
a21_edu05
a21_edu06
a21_edu07
a21_edu08
a21_edu09
a21_edu10
a21_edu11
a21_hlx000
a21_hlx001
a21_hlx001_1
a21_hlx001_2
a21_hlx001_3
a21_hlx002
a21_hlx002_1
a21_hlx003
a21_hlx003_1
a21_hlx004_1
a21_memo01
a21_opt
a21_opt02
a21_opt03
a21_p01
a21_special
a21_tl9a
a21_todo
a21_txt01
a21_txt01_10
a21_txt01_1a
a21_txt01_2
a21_txt01_2a
a21_txt01_2b
a21_txt01_3
a21_txt01_4
a21_txt01_5
a21_txt01_6
a21_txt01_6a
a21_txt01_7
a21_txt01_8
a21_txt01_8a
a21_txt01_9
a21_txt01_9a
a21_txt02
a21_txt02_10
a21_txt02_10a
a21_txt02_10b
a21_txt02_11
a21_txt02_11a
a21_txt02_12
a21_txt02_12a
a21_txt02_12b
a21_txt02_1a
a21_txt02_1b
a21_txt02_2
a21_txt02_2a
a21_txt02_3
a21_txt02_3a
a21_txt02_4
a21_txt02_4a
a21_txt02_5
a21_txt02_5a
a21_txt02_6
a21_txt02_6a
a21_txt02_6b
a21_txt02_6b_rev0
a21_txt02_6x
a21_txt02_7
a21_txt02_7a
a21_txt02_8
a21_txt02_8a
a21_txt02_9
a21_txt02_9a
a22_acl2_01
a22_acl2_02
a22_edu12
a22_intro01
a22_intro02
a22_intro03
a22_memman01
a22_memman02
a22_memman03
a22_memman04
a22_memman05
a22_memman06
a22_memman07
a22_memo01
a22_mingw_debug
a22_txt03
a22_txt03_1a
a22_txt03_1b
a22_txt03_2
a22_txt03_2a
a22_ufcs01
a23_bbs
a23_ec001
a23_ec002
a23_intro00
a23_intro000
a23_intro01
a23_intro02
a23_intro03
a23_intro04
a23_intro05
a23_intro06
a23_intro07
a23_intro08
a23_intro09
a23_intro10
a23_intro10wk1
a23_intro10wk2
a23_intro10wk3
a23_intro11
a23_intro12
a23_intro13
a23_intro13wk1
a23_intro14
a23_intro15
a23_intro16
a23_intro17
a23_intro17wk1
a23_intro18
a23_intro19
a23_intro90
a23_intro91
a23_neopixel1
a23_os01
a23_useSelfMade
a23_usm001
a23_usm002
a23_usm003
a23_usm004
a23_usm005
a23_usm006
a23_usm007
a23_usm008
a23_usm009
a24_AMap11
a24_AMapSim11
a24_AMemFile
a24_AMemMan
a24_aErrExit
a24_aFnv
a24_aOsFunc
a24_aQSort
a24_aXorShift32
a24_acl1T_doc01
a24_acl1Tiny
a24_acpp0
a24_buntan01
a24_cMin
a24_getTyp
a24_goodvalues
a24_idea001
a24_longdef
a24_memo01
a24_memo02
a24_osc20240310
a24_osc20241026
a24_picoLcd13
a24_picoTrain1
a24_programs
a24_raspberrypi01
a24_raspberrypi02
a24_schedule
a24_spc2tab
a24_tab2spc
a24_useSelfMade
a25_acl3
a25_buntan02
a25_buntan03
a25_buntan04
a25_buntan05
a25_kcas01
a25_kharc01
a25_kharc02
a25_kharc03
a25_kharc04
a25_kharc05
a25_kharc06
a25_kharcs1
a25_kharcs2
a25_kharcs3
a25_kharcs4
a25_kharcs5
a25_kharcs6
a25_kharcs7
a25_kharcs8
a25_kharcs9
aclib00
aclib01
aclib02
aclib03
aclib04
aclib05
aclib06
aclib07
aclib08
aclib09
aclib10
aclib11
aclib12
aclib13
aclib14
aclib15
aclib16
aclib17
aclib18
aclib19
aclib20
aclib21
aclib22
aclib23
aclib24
aclib25
aclib_bbs
arm64_01
avm0001
edu0001
edu0002
edu0003
esb02b_hrb
esb_dbg
esbasic0001
esbasic0002
esbasic0003
esbasic0004
esbasic0005
esbasic0006
esbasic0007
esbasic0008
esbasic0009
esbasic0010
esbasic0011
esbasic0012
esbasic0013
esbasic0014
esbasic0015
esbasic0016
esbasic0017
esbasic02a
esc0001
escm0001
essen_hist
esvm0001
esvm0002
esvm0003
esvm0004
esvm0005
esvm0006
esvm_i0
hh4a
idea0001
idea0002
idea0003
impressions
jck_0000
jck_0001
kawai
kbcl0_0000
kbcl0_0001
kbcl0_0002
kbcl0_0003
kbcl0_0004
kbcl0_0005
kbcl0_0006
kbcl0_0007
kclib1_0000
kclib1_0001
kclib1_0002
kclib1_0003
kclib1_0004
kclib1_0005
kclib1_0006
kclib1_0007
kclib1_0008
kclib1_0009
kclib1_0010
kpap0001
members
memo0001
osask4g
osask4g_r2
p20200311a
p20200610a
p20200610b
p20200624a
p20200711a
p20200716a
p20250813a
p20250813b
p20250813c
p20250815a
p20250903a
p20251006a
page0001
page0002
page0003
page0004
page0005
page0006
page0007
page0008
page0009
page0010
page0011
page0012
page0013
page0014
page0015
page0016
page0017
page0018
page0019
page0020
page0021
page0022
page0023
populars
seccamp
seccamp2019
sechack
sechack2019
seclang01
sh3_2020
sh3_2020_kw
sh3_2020_nk
sh3_2021_kw
sh3_2021_nk
sh3_2022_kw
sh3_2023_kw
sh3_2024_kw
sh3_2025_kw
sh3_kw_hist
termux001
termux002
text0001
text0001a
text0002
text0002a
text0003
text0004
text0005
text0006
text0006a
text0007
text0008
text0010
text0011
text0012
text0013
text0014
text0015
text0016
text0017
text0018
text0019
text0020
text0021
tl1c
tl2c
tl3c
tl3d
* [[a21]]に関する雑記#1 -(by [[K]], 2021.01.26) ** 2021.01.25(月) -今日は[[a21_txt01_3]]のTL-3を書きました。我ながらシンプルにわかりやすく書けた気がします。 -ラベル名をうまく使って、分岐先のpc値を高速に求められるようにしたので、結構高速に動いてくれそうな気がします。 -でもなあ、goto命令だからなあ。やっぱり嫌がられてしまうかなあ・・・。 ** 2021.01.26(火) -うーん、この先の拡張を考えると、トークンコードの処理は別の関数に切り出しておくほうがよさそうだなあ・・・。じゃあさかのぼってTL-2から修正するかな。 -TL-4で高速化して、TL-5で「式」を扱えるようにして、TL-6で型を導入したらいいかなあ・・・。 ** 2021.01.27(水) -TL-4での高速化はやっぱりやめます。それはTL-5でやることにします。 -TL-4でやるべきは「REPLの導入」だと思いなおしました。・・・TLシリーズはせっかくのスクリプト言語(インタプリタ)なので、そのメリットを生かさないともったいないです。だからREPLの導入はぜひやるべきだと確信しました! -その後の予定としては、TL-5で少し高速化して、TL-6でもっと高速化して、TL-7でwihleループやブロックifなどを導入して、TL-8かそれ以降でグラフィック命令を導入します。 -そのあたりまでできたら、次はTJシリーズに移行して、JITコンパイラかな・・・。 ~ -TL-4ができました。REPLできるのはすごく楽しいです。これがたった7.5KBのアプリで実現できるなんて! →[[a21_txt01]] ** 2021.01.28(木) -TLシリーズでどこまでやるかで少し悩んでいます。たぶんやりすぎないほうがいいんです。道半ばにしておいて、「自分だったらこうするのに」をいくつか残しておくほうが、その後の開発にチャレンジする人が増えると思うのです。 -ということで型も浮動小数点演算も配列も構造体も関数もローカル変数も全部見送ろうかなと思っているのですが、どうかなあ。 -具体的な改造コードを提示しないまでも、こうすればできそうだよね?くらいの話は書くかなあ。 ~ -[memo]あとでどこか別のところにまとめるつもりだけど、インタプリタ言語のメリット: --[1] 実行ファイルがいらない。ソースコードだけあればよい。あとで、実行ファイルだけになって、「ソースは失われてしまった」みたいな事故が起きない。 --[2] インタプリタ言語のソースコードは(たいてい)機種依存しないので、移植しなくていい。将来新しいCPUとかOSが出ても無修正で対応できる。 --[3] JITコンパイラとかであれば、それぞれの環境に応じた実行コードを生成して実行してくれる(可能性がある)。 --[4] インタプリタはREPLができる。 -対応するデメリット: --[1] ソースがあっても処理系がないと動かない(どのバージョンで動かすかわからなくなったとか)。 ~ -現在手元で開発中のTL-6ですが、どうせやるならJITコンパイルしない範囲での最速を目指してやろうということで、ちょっと頑張ってみました。結果はgcc比で13.7倍。我ながら、これはなかなかいいんじゃないかなと思いました。 --その後さらに頑張って、11.3倍まで速くできました。 ** 2021.02.02(火) -現在、TL-7で、演算子をどこまでサポートするかで悩んでいます。 -たくさんサポートすると、それだけでプログラムはかなり長くなってしまいます。 -でも少なすぎると、おんぼろすぎて、TL-7が魅力的には見えなくなってしまいます。 ** 2021.02.05(金) -プログラムで変数に対する説明が省略されすぎじゃないかと言われて、その通りだと思ったので少しずつ直していきます! ** 2021.02.06(土) -今になって、lexer()とgetTc()を統合すればもっとすっきりと書けるということに気づきました。 -それで、どういう話の流れにしたら最小の手間で自然にその結論に到達できるかを悩み中です。 ** 2021.02.15(月) -私はTL-7でどうにかしてスタックマシンを構成しないで、式の評価をやりたいです。しかもできるだけ自然な感じで。・・・アイデアはあるんです。きっとできると思っています。 ** 2021.02.16(火) -TL-7が手元ではできた!・・・454行で、9.50KB。これはかなりコンパクトだと自分では思います。それでいて、ちゃんと優先順位を反映して計算できています。やったね! ** 2021.02.17(水) -プログラムを差分で書いて説明しているけど、差分ではなく全部が欲しいという人もいるだろうなあ・・・。TL-1から全部をまとめてzipとかでダウンロードできるようにしたらいいのかなあ。 ** 2021.02.18(木) -現在、手元では開発中のTL-9で、マンデルブロー集合の描画プログラムが動いています。しかもかなり高速で、同じことをC言語で書いて gcc -O3 でビルドして動かしたものと比べて、4.0倍しか遅くありません。これはすごい! -今のTL-9は725行で、実行ファイルでは17.0KBです。グラフィック描画のためにaclライブラリを使っています(17.0KBの中にaclライブラリも含まれます)。 ** 2021.02.19(金) -TL-9でどこまでやるかを考えています。 --演算子:% --組み込み関数:xorShift() --組み込み関数:getPix() --組み込み関数:fillRect0() --この4つがあれば、[[esbasic02a]]の (4-3)「迷路作成(穴掘り法)」 が移植できるはずです。演算子も組み込み関数も簡単にできるのでこれは余裕そうです。 -さらに、 --配列変数のサポート。 --関数定義と関数呼び出し。 --演算子:& --演算子:/ --演算子:< >= <= --組み込み関数:ff16sin() --組み込み関数:ff16cos() --組み込み関数:inkey() --組み込み関数:wait() --ここまでやれば、[[esbasic02a]]の (4-4)「キューブ回転」 が移植できるはずです。 -その先も、文字列の簡易サポートをして、組み込み関数をさらに増やせば、 --(4-5)「インベーダゲーム」 --(4-6)「ブロック崩し」 -も移植できると思うのですが、そこまでやるかどうかは悩みどころです・・・。 -でも、やったほうがおもしろいかなあとは思います。 ---- -さらに考えを進めて、一つのソースコードで、gccでコンパイルして動かすことができて、かつTL-9でも動作できるような、そんな仕様ができないものかと検討中です。 --[1] lexer()を改造して、#を記号として認識させる。 --[2] ついでにコメントも書けるようにしてやる(おまけ)。 --[3] #ifndef TL9 ~ #endif で囲まれた範囲は、TL-9からはコメントとして無視してやる(gccには認識される)。 --[4] #ifdef TL9 ~ #endif で囲まれた範囲は、TL-9からは通常通り解釈してやる(gccには無視される)。 --[5] #includeは無視する。 --[6] 組み込み関数の名前や仕様をaclライブラリの本来のものに似せる。 ** 2021.02.26(金) -TL-9aまで作ってみて、自分で「これはとても良い出来栄えだ」と思いました。こんなにいいゴールに行けるのなら、今のようなレベルではなく、もっと気合を入れて説明を書きたいです。 -ということでTLシリーズを書き直して、HLシリーズとして出直します。 --TL-9aについては、ここで詳しく紹介しています。→[[a21_tl9a]] ** 2021.03.12(金) -[1] -今日はtwitterで、「10日くらいでできる!プログラミング自作入門」のことを紹介しました。たくさんの人からいいね!してもらえて、たくさんの人にテキストを見てもらえて、私はとっても嬉しいです!本当にどうもありがとうございます。 --https://twitter.com/hkawai3/status/1370198988096368643 -[2] -やっぱり、言語の自作といえば、セルフホストは目標の一つだよなあ。HL-9aにあと何を足したら、HL-1を実行できるようになるかなあ。 --関数の定義と呼び出し機能。 --char型の配列をどうするか問題。ついに型に手を出すべきか?(簡易なものならまあできそう) --printf()をどうするか。まあprintfを使って実装してしまえばいいだけかもしれないけど。 -うーん、これは100行じゃできなさそうだなあ。300行くらいかなあ。 -300行頑張って、HL-9aが動かせるようになるなら本気でやりたいけど、HL-1だもんなあ。・・・うーむー。 -まあでもそこからさらに数百行頑張れば、HL-9aくらいはできるかもしれないけれど・・・。 ** 2021.03.23(火) -自作言語を作る場合に、セルフホスト(自分自身のソースコードをコンパイルできる)を目指す目標設定があります。 -私はこれがいい目標になっていることを認めつつも、しかしこれだけが目標だとそもそも何のために言語を作るのかというところが怪しくなると思っていて、だから今まではこれを目標に設定しないでやってきました。 -私としては、かっこいいグラフィックが表示できてデモの見栄えが良いような言語を目指していたのです。 -しかしいざそこまでできて、この言語でさらに高みを目指すにはどうしたらいいのかを考えると、私は「生産性の高い言語」を作りたいと思うようになりました。 -そして生産性をどう測るかですが、やっぱり同じプログラムを書いて記述量が少なくて済むほうこそが、生産性が高いと言えると思うのです。 -さて私は何を作る際に生産性を高めたいのかというと、今は言語を作っているので、言語を作りやすい言語を目指したいです。・・・あれ?そうなると、セルフホストできるようにした上で、自分自身を簡潔に記述できればよさそうです。 -ということで、私は次はセルフホストを目指します! ** 2021.04.05(月) -HL-9aをJITコンパイラ化しても、全然速くならない!もとのHL-9aが速すぎた!!・・・まさかそんなことがあるのか・・・。 ** 2021.04.06(火) -やはりレジスタ変数をサポートしたほうがいいのだろうか・・・。もはやそれ以外に勝ち目がないからなあ(レジスタ変数を使えば目に見えて速くなるのは確認済み)。 -うーん、説明が面倒になるから、できればレジスタ変数は話題にしたくはなかったんだけどなあ・・・。 -しかしレジスタ変数を扱わないと、結局JITコンパイラのメリットは何もわからないということになってしまうよなあ。それは避けたいよなあ。 -よし、いろいろ試行錯誤した結果、難易度をあまり上げずに高速化するめどが立ちました! ** 2021.04.08(木) -JITコンパイラで性能を出すためにどうしたらいいのかを試行錯誤した結果、レジスタ変数を使うようにしたら十分な効果があることがわかりました(前述のとおり)。 -それでふと思いついたのですが、HL-9aだってレジスタ変数を使うように改造することが可能なのです。それでそのように書いてみました。・・・しかし速くなりませんでした。 -原因を調べてみたところ、HL-9aの場合は変数アクセスのほかに、ic[](=内部コードを格納した配列)へのアクセスも必要で、そこをどうにかしない限りメモリアクセスは少ししか減らないので、速さが大して変わらなかったのだとわかりました。 -なるほどなあ。 ** 2021.04.09(金) -mandel.cの実行速度比較メモ。 --gcc(x86の32bit): 4.0秒(さすがに速い!) --HL-9a(x86の32bit): 27.6秒 --HL-14(仮)(x86の32bit, JITコンパイラ版, レジスタ変数なし): 5.9秒 --HL-14(仮)(x86の32bit, JITコンパイラ版, レジスタ変数あり): 3.7秒 -おお、かなり速くなった!・・・あれ?gccよりも速くなってしまった?!64bitと比較したらどうかな? --gcc(x64): 3.2秒(おおー!さすがだー) ** 2021.04.23(金) -いつの間にか登録されていた。ありがたやー。 --https://b.hatena.ne.jp/entry/essen.osask.jp/?a21_txt01 -「作者の作風が活かされていて、気軽で実践的なテイスト。とても面白い。」っていうコメントがすごく的確。そして面白いと思ってもらえてよかった。 ** 2021.05.01(土) -gcc-O3と同じくらいの速さが906行のHL-14aで実現できてしまったので、なんだー最適化って結構簡単かもしれないぞ!と調子に乗りつつあった私ですが、-Osを選択してサイズ最適化で勝負したら、もうまったく勝ち目がないことがよくわかりました。gccは本当によくできていて賢いです。 -そしてサイズを小さくすることは、ひいてはキャッシュヒット率にも直結することなので、最終的には速度にも影響するはずです。だからもっと規模の大きいプログラムになったら、速度でも追いつけなくなりそうだなと思いました。 ** 2021.05.07(金) -どうにかがんばって、HL-17のプログラムはできた。もっと頑張ってHL-17aまでは今日中に書いてしまいたいのだけど、間に合うかなあ。 ** 2021.05.17(月) -HL-22aまでやって、アセンブラ出力もやって、それでそろそろインタプリタとJITコンパイラの統合をやってみたい。 -そして言語に「型」を導入して、構造体も使えるようになりたい。さらにセルフホストができたら、既存のCコンパイラから卒業したい。 ** 2021.05.28(金) -HL-13以降の説明が、どんどん雑になっている気がする。これはよくない。 -これは私が教材の開発のほうが面白くなってしまって、文章まで気が回らなくなっていたせいだと思う。 -少しずつ直さなければ! ** 2021.05.31(月) -ちょっと思いついたのでメモ。 -x86やx64は、機械語をそのまま出すよりも、一回以下の構造体に出して、それで最適化して、最後に機械語で出力するほうがシンプルに作れるんじゃないかという気がする。 ??? { int op; // オペコード signed char preg, mreg, bas, idx, scl; // pregはmod r/mの真ん中のレジスタ // mregはmod r/mで表すレジスタ(ここが-1だとレジスタではなくメモリになる) // basはmod r/mで表すベースレジスタ(使わないのなら-1) // idxはsibで表すインデックスレジスタ(使わないのなら-1) // sclはsibで表すスケールファクタ(使わないのなら0) int dsp; // mod r/mのディスプレースメント int imm32; AInt64 imm64; }; ** 2021.06.01(火) -HL-22bが終わったら真っ先にやりたいこと。 --[1] HL-9a, HL-16b, HL-22b を統合する。 --[2] 整数型以外も扱えるようにする。 ** 2021.06.03(木) -HL-22bを書いているうちに気づいたこと。ややや、HL-18以降には、ひどいバグがあるじゃないか。これは直さなければ・・・。 ** 2021.06.30(水) -ここしばらく、ずっとHLX-001の開発をやっているのですが、何をどう書いても「うーん、作りたかったのはこんなものだったのだろうか?」という気持ちになります。 -なんでなかなかしっくりこない気がするんだろうって考えてみたら、ようやくわかりました。 -それはきっと判断の基準が無意識のうちにHL-22bになっているからなんです(まあHLX-001はHL-22b(HL-16b)の続きなのだから、当然といえば当然)。 -HL-22bのできが良かったんです。自分でも驚くくらいに良かったのです。だからこれに匹敵するレベルにするのが難しいのです。 -まあそんなことがわかっても何の解決にもならないのですが、とにかく満足できるまで頑張ります! ** 2021.07.08(木) -HLX-001では、命令長も自動で最適なものを選べるようにしたいと思っています。そのためにどうしたらいいのか、試行錯誤中です。たとえばJMP先のアドレスが近いのか遠いのか、メモリアクセス時のディスプレースメントが大きいのか小さいのか、そういうことで命令長は変わります。 -できるだけ柔軟にこれを処理できるようになりたいです。 -(数時間後) -ついに「しっくりくる」内部構成にたどり着いたかも?! 期待している処理を、期待している複雑さで(簡単さで)記述できるようになった・・・気がします。ここまで長かったー! ** 2021.07.09(金) -[今日の勉強] --x64で「CALL RAX」の機械語は、REXプリフィクスなしの「ff d0」。そして「CALL EAX」はできない(まあそれが正しいとも思う)。 ** 2021.07.14(水) -HLX-001でprintくらいはできるようになりました。しかしまだ定数式しか動きません。でもここまでできれば、あとはトントン拍子に進んでいくはずです。 ** 2021.07.16(金) -HLX-001で加減乗除などの演算もできるようになりました! これでループができればいろいろ動かせるのはわかっているのですが、分岐処理は near-jmp, short-jmp の両方を自動で最適化させるために複雑にしたので、ここを書き上げるのはもう少し時間がかかりそうです。 ** 2021.07.20(火) -HLX-001での分岐処理、うまくできるようになりました。これを実現するための改造でもあったので、大きい山を越えた感じです! --遠くに飛ぶときはHL-22bなどと同様に 0f 8x xx xx xx xx を出力しますが、ジャンプ先が-128~+127のときは、7x xxという短い形式を出力するようになりました。前方参照でも届くときはちゃんと短い形式を使うようになっています。 ** 2021.07.26(月) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958| |HLX-001 (2021.07.21版)|RIGHT:266|RIGHT:1004|RIGHT:1931|RIGHT:4486|RIGHT:2901| -こうしてみると、HL-16aにはまだまだ及ばないことがよくわかります。 -これは現状のHLX-001がほとんど最適化をやっていないせいです。それでこれをなんとかしたいので、まずは「先を実行しても参照されることのない」演算を探し出して、その演算を自動で削除する仕組みを導入しようと思います(というかもうすぐできそう)。 -これがあれば、それより上位では、無駄な計算を書いてしまっても問題ないことになります。 ** 2021.07.30(金)~2021.08.02(月) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|| |HL-16a|RIGHT:230|RIGHT:760|RIGHT:1542|RIGHT:3503|RIGHT:2366|レジスタ変数を一切使わない場合(比較用)。| |HLX-001 (2021.07.21版)|RIGHT:266|RIGHT:1004|RIGHT:1931|RIGHT:4486|RIGHT:2901|最適化ほとんどなし。| |HLX-001 (2021.07.30a版)|RIGHT:242|RIGHT:944|RIGHT:1919|RIGHT:4390|RIGHT:2773|中間コードレベルで、不要な演算を削除。| |HLX-001 (2021.07.30b版)|RIGHT:242|RIGHT:908|RIGHT:1919|RIGHT:4198|RIGHT:2729|中間コードレベルで、不要な一時変数を削除。| |HLX-001 (2021.07.31版)|RIGHT:210|RIGHT:740|RIGHT:1525|RIGHT:3550|RIGHT:2293|機械語レベルで、不要な演算を削除。| |HLX-001 (2021.08.01版)|RIGHT:206|RIGHT:730|RIGHT:1497|RIGHT:3488|RIGHT:2221|比較演算の後の条件分岐を統合する最適化。| |HLX-001 (2021.08.02版)|RIGHT:192|RIGHT:679|RIGHT:1486|RIGHT:3456|RIGHT:2169|for文の最適化を採用。| -(2021.07.30a版)の説明: --わざわざ演算したのにその結果を使わない記述が見つかった場合に、その演算を削除するという最適化。 c=a+b; d=c*3+2; とやったあとに、dもcも使わないのなら、dの演算式は消えるし、さかのぼってcの演算式も消える。 -(2021.07.30b版)の説明: -- _t0=a+b; c=_t0;みたいなコードを生成してしまうことがあるが、この場合の_t0を削除してa+bの結果を直接cに代入するようにする最適化。 ** 2021.08.03(火) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|レジスタ変数を利用| |HLX-001 (2021.08.03版)|RIGHT:109|RIGHT:529|RIGHT:1194|RIGHT:3014|RIGHT:1669|レジスタ変数を利用| ** 2021.08.05(木) -[1] HLX-001のJITコンパイラでは、いきなりx86やx64の機械語を生成するのをやめて、まずはHL-9aによく似た中間コードを出力して、その中間コードから機械語を生成するという二段階方式にしています(実際はさらにx86とx64の仕様を整理した別のコードにも変換しているので、厳密には三段階方式です。中間コードから機械語を生成するにあたって、どうしたら楽になるかを考えたらこうなりました)。 --なぜこうしたのかというと、中間コードにすればインタプリタ実行をするときに便利だし、中間コードは機種依存がないので他のCPUをサポートするときに便利だと思っているからです。・・・そうですね、少し説明が分かりにくくなりそうなので、この中間コードは「共通中間コード」と呼ぶことにします。 -[2] LLVMでも共通中間言語みたいなものはあると思いますが(LLVM-IR)、HLX-001の共通中間コードとの一番の違いは、テキストで表すか、バイナリで表すかというところだと思っています。テキストにすれば人間からは読みやすくなりますが、テキストで出力することもテキストを入力することも、CPUにとってはそれなりにオーバヘッドがあることです。テキスト化することのメリットも多いのですが、私はバイナリでどこまでできるかを見極めたいと思っています。 --そうすると、次はどんなバイナリ形式がいいかということになります。 --基本的にはintの配列を採用しています。そしてその命令長は可変長にしていました。命令によって長さがまちまちなのです。命令長は関数で取得できればそれでいいと思っていました。・・・しかしこれは結構煩雑になってきて、もしかしてこれは失敗だったかもと思い始めました。HL-9aの中間コードのように、固定長にするのがよさそうです。多少の例外は作るかもしれませんが、固定長を前提に命令フォーマットを再設計しようと思います。 -[3](翌日に追記)命令フォーマットを再設計しました。効果絶大で、最適化ルーチンは19%も短くなりました。そして他のところが大きくなるという弊害もなく、言語処理系の実行ファイルは1KBほどコンパクトになりました! ** 2021.08.06(金) -そろそろHLX-001を公開へもっていきたいと思っています。そのためにどんな残作業があるか列挙してみました。 --[1]スタック変数をサポートする --[2]x64対応がうまくいっているかテストする --[3]HL-9aっぽいインタプリタを載せる -とりあえずこの3つができたら公開したいです。アセンブラ出力は、HLX-002などの将来のバージョンでやろうと思います。・・・そしてこの3つだけなら、きっともうそんなに大変じゃないはずです。 ** 2021.08.11(水) -HLX-001の進捗 --[1]スタック変数のサポートはできた! --[2]x64対応がうまくいっているかのテストはこれから --[3]HL-9aっぽいインタプリタを載せるのは作業中 ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|レジスタ変数を利用| |HLX-001 (2021.08.03版)|RIGHT:109|RIGHT:529|RIGHT:1194|RIGHT:3014|RIGHT:1669|レジスタ変数を利用| |HLX-001 (2021.08.10版)|RIGHT:120|RIGHT:413|RIGHT:957|RIGHT:1974|RIGHT:1324|レジスタ変数を利用| -grd.cが大きくなってしまったので、そこは改良の余地がありますが、それ以外はだいぶ良くなっています。 ** 2021.08.16(月) -HLX-001の進捗 --[1]スタック変数のサポートはできた! --[2]x64対応がうまくいっているかのテストもクリア --[3]HL-9aっぽいインタプリタを載せるのもできた -どのくらい性能が向上したのかをmandel.cで確認しています。 --[1] HL-9aとの比較 |HL-9a vs HLX-001|32bitモード(x86)|29.667秒 vs ''23.220秒''| |HL-9a vs HLX-001|64bitモード(x64)|26.904秒 vs ''18.653秒''| --[2] HL-16aとの比較(32bitモード(x86)) |HL-16a vs HLX-001|3.246秒(657バイト) vs ''3.272秒(609バイト)''|速度が速くなるようにレジスタ変数を割り当て| |HL-16a vs HLX-001|3.730秒(613バイト) vs ''3.660秒(411バイト)''|サイズが小さくなるようにレジスタ変数を割り当て| --[3] HL-22aとの比較(64bitモード(x64)) |HL-22a vs HLX-001|2.503秒(674バイト) vs ''2.512秒(655バイト)''|速度が速くなるようにレジスタ変数を割り当て| |HL-22a vs HLX-001|3.449秒(588バイト) vs ''3.459秒(489バイト)''|サイズが小さくなるようにレジスタ変数を割り当て| --[考察] ---[1]のHL-9aとの比較では、HLX-001になって有意に性能が向上しています。これはHLX-001が内部コードに対して最適化をしてから実行するようになったためです。 ---[2]のHL-16aとの比較では、有意な速度の向上はありませんでした。ただサイズにおいては有意な差がついています。これはどちらもアラインを考慮せずに比較しているので、その影響かもしれません。HLXにアライン機能を対応させたら、少しは差が出るかもしれません。 ---[3]のHL-22aとの比較では、有意な速度の向上はありませんでした。ただサイズにおいては有意な差がついています。これはどちらもアラインを考慮せずに比較しているので、その影響かもしれません。HLXにアライン機能を対応させたら、少しは差が出るかもしれません。 ** 2021.08.27(金) -C言語にはプリプロセッサがあって、#if~#endifがあります。私はその機能はよく使っていますが、でも実はあまり好きではありません。・・・普通のif文で同等のことができるべきだと思うのです。 -HLX-001では、ifの中の条件式が定数式であれば、最適化によって、まるで#ifのようにきれいに入れ替わります。それを保証しているので、#ifの代わりとしてもほぼ問題ない気がします。 -普通のif文にしておけば、もし定数ではなく変数で判断したくなっても、ほとんど改造しないで対応できるので、その点でもいいと思っています。 //-私は同じようなことをする機能が複数あるのは嫌いです。それらは1つに統合したほうがいいと思っています。 ** 2021.09.07(火) -さっきふと思いついて、printf()を追加してみました。これがあるといろいろ遊べることが分かりました。REPLとの相性がばっちりです。 for (i = 1; i <= 9; i++) { for (j = 1; j <= 9; j++) { printf("%2d ", i * j); } printf("\n"); } 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 ** 2021.09.24(金) -HLXで型をどう扱うか考えています。これは次のバージョンでどうこうする話ではなくて、最終的にどうするかという話です。 -まず、C++みたいなクラスは欲しいです。一方で、多重継承とか仮想関数とかはなくてもいいかもしれないとは思います。 --仮想関数は、自分で関数ポインタを管理すればいいかな、とりあえずは。 --いやまあ、サポートするかもしれないけど。 --仮に仮想関数をサポートするとして、使う予定のない仮想関数を排除する仕組み(それでコードサイズを小さく保つ仕組み)は欲しいかなとも思っています。 --そうなると、「絶対にこの文脈ではこの型が来る」という記述と、「この型もしくはこの型を継承した型が来る」という記述を書き分けられるといいかもしれないなあ。この情報があればコンパイラはvtableを引かなくてよくなります。 -テンプレートも使えないよりは使えたほうがいいかなあ。 --まあそれほど実現は難しくないかもしれない気がします。 -仮想関数をサポートしたとして、vtableがそのクラスのすべてのオブジェクトに入るのはなんかやだなあ。むしろvtable(というか型情報のすべて)を別に渡したいです。これをオブジェクトに含めるかどうかは、プログラマの裁量でいいんじゃないかな。 --malloc/freeで領域サイズをヒープブロックの先頭の前に入れるかどうか、のと類似の発想で。 -コンストラクタとデストラクタは欲しいです。あったらきっと便利です。 -これは型からは少しそれるけど、メモリ管理として、ガーベージコレクションに相当するものは欲しいです。 --でもマークアンドスイープやリンクカウント方式は好きじゃないです(リンクカウントが適切な状況は確かにあるけど、すべてのポインタに適用できるほど万能ではないと私は思っています)。 --大事なのは確実に管理できることじゃなくて、「維持する手間がが少ない&もしミスってもミスを発見しやすい」ことだと思います。だからメモリリークしちゃいけないわけじゃなくて、リークしたメモリ一覧とか、それはどこでどんな文脈でmallocしたものなのかが後から突き止められればいいはずです。しかもそのレポートはデバッグモードでだけ出ればいいのです。リリースモードではプログラムにミスがない前提で最高速度(or最小サイズ)で動けばいいと思うので。 --そうであれば、やっぱりAutoReleasePool方式がよさそうな気がします。すべてのオブジェクトはいずれかのAutoReleasePoolに属していて、ここに来たら消される(自動管理の場合)、もしくはここに来るまで残っていたらエラーにします(手動管理の場合)。 -オブジェクトの最適化ついて: --このメンバ関数が呼ばれたら「オブジェクトは利用された」と考える、それ以外は初期化や設定やあと始末なので利用ではない、一度も利用されないのならオブジェクトは生成しない、という最適化が欲しいです(もちろんオブジェクトの生成に副作用がない場合に限って)。 -関数の引数の最適化について: --「この引数は呼び出し先では結局一度も使われていない」という情報が欲しいです。そうであれば、その引数を積むのを省略できるし、引数の利用回数も減らせます。 --積むのを省略するだけじゃなくて、並び順から消してしまったりはできないかなあ。仮想関数みたいにインタフェースが決まっているときは変えられないけど。 -オブジェクトの利用回数について: --今は文脈を読んで、今代入した値が使われる前に上書きするもしくはvoid化されるというのを自動検出していますが、さらに関数全体で一度も使われていないか使われるかもしれないかを判定することが有効だと思います。これは判定が簡単なので実装したいです。 -クラス内のオブジェクトについて: --あるクラスのメンバが誰からも使われないのなら、消してしまったほうがいい気がします。しかし後で使うかもしれないです。REPLみたいなのを想定すると、将来まで使わないかどうかがわからないです。だからオブジェクトがプログラム終了までに消されるとわかっているものに限った話にしておけばいいかなあ。この考え方はAutoReleasePoolと相性がいいかもしれないです。 ** 2021.11.18(木) -しばらくここに書いていませんでしたが、いろいろバージョンアップ中です。 --'A'とか'b'を書けるようにしました。'\0'や'\n'も使えます。 --ifやforで { } を使わずに1文だけ書くやつができるようになりました。 --C言語風の記述で、以前のバージョンでは変数宣言があった場合その行ごと全部無視していましたが、開発中のバージョンではもし変数の初期化代入をしている部分があれば、それを認識するようになりました。 --ほぼすべて大文字で書ける、BASIC風の書き方も混在できるようになりました。 ** 2021.12.17(金) -先日リリースしたばかりの hlx003b ですが、BASIC風で書いたときはよく動くものの、C言語風に書くとなんか変な時があるみたいです。 hlx003a のほうは何の問題もないのですが・・・。直さなければ! // 条件ジャンプで1命令だけ飛ばす。 // そして次の命令が無条件分岐。 // そういう場合は、条件ジャンプを反転させて、ジャンプ先を変える。 // レジスタ変数をもっと増やしたい // できるだけたくさん増やすモードを付ける // 関数呼び出し前に専用領域へ退避させる // 引数処理は、読み替えればいい。 // JITコンパイラをJITコンパイラで拡張する。 // 配列や構造体への連続代入 // 自動enum。クラス名を書けば、enumしてくれる。 * こめんと欄 #comment
タイムスタンプを変更しない
* [[a21]]に関する雑記#1 -(by [[K]], 2021.01.26) ** 2021.01.25(月) -今日は[[a21_txt01_3]]のTL-3を書きました。我ながらシンプルにわかりやすく書けた気がします。 -ラベル名をうまく使って、分岐先のpc値を高速に求められるようにしたので、結構高速に動いてくれそうな気がします。 -でもなあ、goto命令だからなあ。やっぱり嫌がられてしまうかなあ・・・。 ** 2021.01.26(火) -うーん、この先の拡張を考えると、トークンコードの処理は別の関数に切り出しておくほうがよさそうだなあ・・・。じゃあさかのぼってTL-2から修正するかな。 -TL-4で高速化して、TL-5で「式」を扱えるようにして、TL-6で型を導入したらいいかなあ・・・。 ** 2021.01.27(水) -TL-4での高速化はやっぱりやめます。それはTL-5でやることにします。 -TL-4でやるべきは「REPLの導入」だと思いなおしました。・・・TLシリーズはせっかくのスクリプト言語(インタプリタ)なので、そのメリットを生かさないともったいないです。だからREPLの導入はぜひやるべきだと確信しました! -その後の予定としては、TL-5で少し高速化して、TL-6でもっと高速化して、TL-7でwihleループやブロックifなどを導入して、TL-8かそれ以降でグラフィック命令を導入します。 -そのあたりまでできたら、次はTJシリーズに移行して、JITコンパイラかな・・・。 ~ -TL-4ができました。REPLできるのはすごく楽しいです。これがたった7.5KBのアプリで実現できるなんて! →[[a21_txt01]] ** 2021.01.28(木) -TLシリーズでどこまでやるかで少し悩んでいます。たぶんやりすぎないほうがいいんです。道半ばにしておいて、「自分だったらこうするのに」をいくつか残しておくほうが、その後の開発にチャレンジする人が増えると思うのです。 -ということで型も浮動小数点演算も配列も構造体も関数もローカル変数も全部見送ろうかなと思っているのですが、どうかなあ。 -具体的な改造コードを提示しないまでも、こうすればできそうだよね?くらいの話は書くかなあ。 ~ -[memo]あとでどこか別のところにまとめるつもりだけど、インタプリタ言語のメリット: --[1] 実行ファイルがいらない。ソースコードだけあればよい。あとで、実行ファイルだけになって、「ソースは失われてしまった」みたいな事故が起きない。 --[2] インタプリタ言語のソースコードは(たいてい)機種依存しないので、移植しなくていい。将来新しいCPUとかOSが出ても無修正で対応できる。 --[3] JITコンパイラとかであれば、それぞれの環境に応じた実行コードを生成して実行してくれる(可能性がある)。 --[4] インタプリタはREPLができる。 -対応するデメリット: --[1] ソースがあっても処理系がないと動かない(どのバージョンで動かすかわからなくなったとか)。 ~ -現在手元で開発中のTL-6ですが、どうせやるならJITコンパイルしない範囲での最速を目指してやろうということで、ちょっと頑張ってみました。結果はgcc比で13.7倍。我ながら、これはなかなかいいんじゃないかなと思いました。 --その後さらに頑張って、11.3倍まで速くできました。 ** 2021.02.02(火) -現在、TL-7で、演算子をどこまでサポートするかで悩んでいます。 -たくさんサポートすると、それだけでプログラムはかなり長くなってしまいます。 -でも少なすぎると、おんぼろすぎて、TL-7が魅力的には見えなくなってしまいます。 ** 2021.02.05(金) -プログラムで変数に対する説明が省略されすぎじゃないかと言われて、その通りだと思ったので少しずつ直していきます! ** 2021.02.06(土) -今になって、lexer()とgetTc()を統合すればもっとすっきりと書けるということに気づきました。 -それで、どういう話の流れにしたら最小の手間で自然にその結論に到達できるかを悩み中です。 ** 2021.02.15(月) -私はTL-7でどうにかしてスタックマシンを構成しないで、式の評価をやりたいです。しかもできるだけ自然な感じで。・・・アイデアはあるんです。きっとできると思っています。 ** 2021.02.16(火) -TL-7が手元ではできた!・・・454行で、9.50KB。これはかなりコンパクトだと自分では思います。それでいて、ちゃんと優先順位を反映して計算できています。やったね! ** 2021.02.17(水) -プログラムを差分で書いて説明しているけど、差分ではなく全部が欲しいという人もいるだろうなあ・・・。TL-1から全部をまとめてzipとかでダウンロードできるようにしたらいいのかなあ。 ** 2021.02.18(木) -現在、手元では開発中のTL-9で、マンデルブロー集合の描画プログラムが動いています。しかもかなり高速で、同じことをC言語で書いて gcc -O3 でビルドして動かしたものと比べて、4.0倍しか遅くありません。これはすごい! -今のTL-9は725行で、実行ファイルでは17.0KBです。グラフィック描画のためにaclライブラリを使っています(17.0KBの中にaclライブラリも含まれます)。 ** 2021.02.19(金) -TL-9でどこまでやるかを考えています。 --演算子:% --組み込み関数:xorShift() --組み込み関数:getPix() --組み込み関数:fillRect0() --この4つがあれば、[[esbasic02a]]の (4-3)「迷路作成(穴掘り法)」 が移植できるはずです。演算子も組み込み関数も簡単にできるのでこれは余裕そうです。 -さらに、 --配列変数のサポート。 --関数定義と関数呼び出し。 --演算子:& --演算子:/ --演算子:< >= <= --組み込み関数:ff16sin() --組み込み関数:ff16cos() --組み込み関数:inkey() --組み込み関数:wait() --ここまでやれば、[[esbasic02a]]の (4-4)「キューブ回転」 が移植できるはずです。 -その先も、文字列の簡易サポートをして、組み込み関数をさらに増やせば、 --(4-5)「インベーダゲーム」 --(4-6)「ブロック崩し」 -も移植できると思うのですが、そこまでやるかどうかは悩みどころです・・・。 -でも、やったほうがおもしろいかなあとは思います。 ---- -さらに考えを進めて、一つのソースコードで、gccでコンパイルして動かすことができて、かつTL-9でも動作できるような、そんな仕様ができないものかと検討中です。 --[1] lexer()を改造して、#を記号として認識させる。 --[2] ついでにコメントも書けるようにしてやる(おまけ)。 --[3] #ifndef TL9 ~ #endif で囲まれた範囲は、TL-9からはコメントとして無視してやる(gccには認識される)。 --[4] #ifdef TL9 ~ #endif で囲まれた範囲は、TL-9からは通常通り解釈してやる(gccには無視される)。 --[5] #includeは無視する。 --[6] 組み込み関数の名前や仕様をaclライブラリの本来のものに似せる。 ** 2021.02.26(金) -TL-9aまで作ってみて、自分で「これはとても良い出来栄えだ」と思いました。こんなにいいゴールに行けるのなら、今のようなレベルではなく、もっと気合を入れて説明を書きたいです。 -ということでTLシリーズを書き直して、HLシリーズとして出直します。 --TL-9aについては、ここで詳しく紹介しています。→[[a21_tl9a]] ** 2021.03.12(金) -[1] -今日はtwitterで、「10日くらいでできる!プログラミング自作入門」のことを紹介しました。たくさんの人からいいね!してもらえて、たくさんの人にテキストを見てもらえて、私はとっても嬉しいです!本当にどうもありがとうございます。 --https://twitter.com/hkawai3/status/1370198988096368643 -[2] -やっぱり、言語の自作といえば、セルフホストは目標の一つだよなあ。HL-9aにあと何を足したら、HL-1を実行できるようになるかなあ。 --関数の定義と呼び出し機能。 --char型の配列をどうするか問題。ついに型に手を出すべきか?(簡易なものならまあできそう) --printf()をどうするか。まあprintfを使って実装してしまえばいいだけかもしれないけど。 -うーん、これは100行じゃできなさそうだなあ。300行くらいかなあ。 -300行頑張って、HL-9aが動かせるようになるなら本気でやりたいけど、HL-1だもんなあ。・・・うーむー。 -まあでもそこからさらに数百行頑張れば、HL-9aくらいはできるかもしれないけれど・・・。 ** 2021.03.23(火) -自作言語を作る場合に、セルフホスト(自分自身のソースコードをコンパイルできる)を目指す目標設定があります。 -私はこれがいい目標になっていることを認めつつも、しかしこれだけが目標だとそもそも何のために言語を作るのかというところが怪しくなると思っていて、だから今まではこれを目標に設定しないでやってきました。 -私としては、かっこいいグラフィックが表示できてデモの見栄えが良いような言語を目指していたのです。 -しかしいざそこまでできて、この言語でさらに高みを目指すにはどうしたらいいのかを考えると、私は「生産性の高い言語」を作りたいと思うようになりました。 -そして生産性をどう測るかですが、やっぱり同じプログラムを書いて記述量が少なくて済むほうこそが、生産性が高いと言えると思うのです。 -さて私は何を作る際に生産性を高めたいのかというと、今は言語を作っているので、言語を作りやすい言語を目指したいです。・・・あれ?そうなると、セルフホストできるようにした上で、自分自身を簡潔に記述できればよさそうです。 -ということで、私は次はセルフホストを目指します! ** 2021.04.05(月) -HL-9aをJITコンパイラ化しても、全然速くならない!もとのHL-9aが速すぎた!!・・・まさかそんなことがあるのか・・・。 ** 2021.04.06(火) -やはりレジスタ変数をサポートしたほうがいいのだろうか・・・。もはやそれ以外に勝ち目がないからなあ(レジスタ変数を使えば目に見えて速くなるのは確認済み)。 -うーん、説明が面倒になるから、できればレジスタ変数は話題にしたくはなかったんだけどなあ・・・。 -しかしレジスタ変数を扱わないと、結局JITコンパイラのメリットは何もわからないということになってしまうよなあ。それは避けたいよなあ。 -よし、いろいろ試行錯誤した結果、難易度をあまり上げずに高速化するめどが立ちました! ** 2021.04.08(木) -JITコンパイラで性能を出すためにどうしたらいいのかを試行錯誤した結果、レジスタ変数を使うようにしたら十分な効果があることがわかりました(前述のとおり)。 -それでふと思いついたのですが、HL-9aだってレジスタ変数を使うように改造することが可能なのです。それでそのように書いてみました。・・・しかし速くなりませんでした。 -原因を調べてみたところ、HL-9aの場合は変数アクセスのほかに、ic[](=内部コードを格納した配列)へのアクセスも必要で、そこをどうにかしない限りメモリアクセスは少ししか減らないので、速さが大して変わらなかったのだとわかりました。 -なるほどなあ。 ** 2021.04.09(金) -mandel.cの実行速度比較メモ。 --gcc(x86の32bit): 4.0秒(さすがに速い!) --HL-9a(x86の32bit): 27.6秒 --HL-14(仮)(x86の32bit, JITコンパイラ版, レジスタ変数なし): 5.9秒 --HL-14(仮)(x86の32bit, JITコンパイラ版, レジスタ変数あり): 3.7秒 -おお、かなり速くなった!・・・あれ?gccよりも速くなってしまった?!64bitと比較したらどうかな? --gcc(x64): 3.2秒(おおー!さすがだー) ** 2021.04.23(金) -いつの間にか登録されていた。ありがたやー。 --https://b.hatena.ne.jp/entry/essen.osask.jp/?a21_txt01 -「作者の作風が活かされていて、気軽で実践的なテイスト。とても面白い。」っていうコメントがすごく的確。そして面白いと思ってもらえてよかった。 ** 2021.05.01(土) -gcc-O3と同じくらいの速さが906行のHL-14aで実現できてしまったので、なんだー最適化って結構簡単かもしれないぞ!と調子に乗りつつあった私ですが、-Osを選択してサイズ最適化で勝負したら、もうまったく勝ち目がないことがよくわかりました。gccは本当によくできていて賢いです。 -そしてサイズを小さくすることは、ひいてはキャッシュヒット率にも直結することなので、最終的には速度にも影響するはずです。だからもっと規模の大きいプログラムになったら、速度でも追いつけなくなりそうだなと思いました。 ** 2021.05.07(金) -どうにかがんばって、HL-17のプログラムはできた。もっと頑張ってHL-17aまでは今日中に書いてしまいたいのだけど、間に合うかなあ。 ** 2021.05.17(月) -HL-22aまでやって、アセンブラ出力もやって、それでそろそろインタプリタとJITコンパイラの統合をやってみたい。 -そして言語に「型」を導入して、構造体も使えるようになりたい。さらにセルフホストができたら、既存のCコンパイラから卒業したい。 ** 2021.05.28(金) -HL-13以降の説明が、どんどん雑になっている気がする。これはよくない。 -これは私が教材の開発のほうが面白くなってしまって、文章まで気が回らなくなっていたせいだと思う。 -少しずつ直さなければ! ** 2021.05.31(月) -ちょっと思いついたのでメモ。 -x86やx64は、機械語をそのまま出すよりも、一回以下の構造体に出して、それで最適化して、最後に機械語で出力するほうがシンプルに作れるんじゃないかという気がする。 ??? { int op; // オペコード signed char preg, mreg, bas, idx, scl; // pregはmod r/mの真ん中のレジスタ // mregはmod r/mで表すレジスタ(ここが-1だとレジスタではなくメモリになる) // basはmod r/mで表すベースレジスタ(使わないのなら-1) // idxはsibで表すインデックスレジスタ(使わないのなら-1) // sclはsibで表すスケールファクタ(使わないのなら0) int dsp; // mod r/mのディスプレースメント int imm32; AInt64 imm64; }; ** 2021.06.01(火) -HL-22bが終わったら真っ先にやりたいこと。 --[1] HL-9a, HL-16b, HL-22b を統合する。 --[2] 整数型以外も扱えるようにする。 ** 2021.06.03(木) -HL-22bを書いているうちに気づいたこと。ややや、HL-18以降には、ひどいバグがあるじゃないか。これは直さなければ・・・。 ** 2021.06.30(水) -ここしばらく、ずっとHLX-001の開発をやっているのですが、何をどう書いても「うーん、作りたかったのはこんなものだったのだろうか?」という気持ちになります。 -なんでなかなかしっくりこない気がするんだろうって考えてみたら、ようやくわかりました。 -それはきっと判断の基準が無意識のうちにHL-22bになっているからなんです(まあHLX-001はHL-22b(HL-16b)の続きなのだから、当然といえば当然)。 -HL-22bのできが良かったんです。自分でも驚くくらいに良かったのです。だからこれに匹敵するレベルにするのが難しいのです。 -まあそんなことがわかっても何の解決にもならないのですが、とにかく満足できるまで頑張ります! ** 2021.07.08(木) -HLX-001では、命令長も自動で最適なものを選べるようにしたいと思っています。そのためにどうしたらいいのか、試行錯誤中です。たとえばJMP先のアドレスが近いのか遠いのか、メモリアクセス時のディスプレースメントが大きいのか小さいのか、そういうことで命令長は変わります。 -できるだけ柔軟にこれを処理できるようになりたいです。 -(数時間後) -ついに「しっくりくる」内部構成にたどり着いたかも?! 期待している処理を、期待している複雑さで(簡単さで)記述できるようになった・・・気がします。ここまで長かったー! ** 2021.07.09(金) -[今日の勉強] --x64で「CALL RAX」の機械語は、REXプリフィクスなしの「ff d0」。そして「CALL EAX」はできない(まあそれが正しいとも思う)。 ** 2021.07.14(水) -HLX-001でprintくらいはできるようになりました。しかしまだ定数式しか動きません。でもここまでできれば、あとはトントン拍子に進んでいくはずです。 ** 2021.07.16(金) -HLX-001で加減乗除などの演算もできるようになりました! これでループができればいろいろ動かせるのはわかっているのですが、分岐処理は near-jmp, short-jmp の両方を自動で最適化させるために複雑にしたので、ここを書き上げるのはもう少し時間がかかりそうです。 ** 2021.07.20(火) -HLX-001での分岐処理、うまくできるようになりました。これを実現するための改造でもあったので、大きい山を越えた感じです! --遠くに飛ぶときはHL-22bなどと同様に 0f 8x xx xx xx xx を出力しますが、ジャンプ先が-128~+127のときは、7x xxという短い形式を出力するようになりました。前方参照でも届くときはちゃんと短い形式を使うようになっています。 ** 2021.07.26(月) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958| |HLX-001 (2021.07.21版)|RIGHT:266|RIGHT:1004|RIGHT:1931|RIGHT:4486|RIGHT:2901| -こうしてみると、HL-16aにはまだまだ及ばないことがよくわかります。 -これは現状のHLX-001がほとんど最適化をやっていないせいです。それでこれをなんとかしたいので、まずは「先を実行しても参照されることのない」演算を探し出して、その演算を自動で削除する仕組みを導入しようと思います(というかもうすぐできそう)。 -これがあれば、それより上位では、無駄な計算を書いてしまっても問題ないことになります。 ** 2021.07.30(金)~2021.08.02(月) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|| |HL-16a|RIGHT:230|RIGHT:760|RIGHT:1542|RIGHT:3503|RIGHT:2366|レジスタ変数を一切使わない場合(比較用)。| |HLX-001 (2021.07.21版)|RIGHT:266|RIGHT:1004|RIGHT:1931|RIGHT:4486|RIGHT:2901|最適化ほとんどなし。| |HLX-001 (2021.07.30a版)|RIGHT:242|RIGHT:944|RIGHT:1919|RIGHT:4390|RIGHT:2773|中間コードレベルで、不要な演算を削除。| |HLX-001 (2021.07.30b版)|RIGHT:242|RIGHT:908|RIGHT:1919|RIGHT:4198|RIGHT:2729|中間コードレベルで、不要な一時変数を削除。| |HLX-001 (2021.07.31版)|RIGHT:210|RIGHT:740|RIGHT:1525|RIGHT:3550|RIGHT:2293|機械語レベルで、不要な演算を削除。| |HLX-001 (2021.08.01版)|RIGHT:206|RIGHT:730|RIGHT:1497|RIGHT:3488|RIGHT:2221|比較演算の後の条件分岐を統合する最適化。| |HLX-001 (2021.08.02版)|RIGHT:192|RIGHT:679|RIGHT:1486|RIGHT:3456|RIGHT:2169|for文の最適化を採用。| -(2021.07.30a版)の説明: --わざわざ演算したのにその結果を使わない記述が見つかった場合に、その演算を削除するという最適化。 c=a+b; d=c*3+2; とやったあとに、dもcも使わないのなら、dの演算式は消えるし、さかのぼってcの演算式も消える。 -(2021.07.30b版)の説明: -- _t0=a+b; c=_t0;みたいなコードを生成してしまうことがあるが、この場合の_t0を削除してa+bの結果を直接cに代入するようにする最適化。 ** 2021.08.03(火) -JITコンパイラのコード生成の質の比較: ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|レジスタ変数を利用| |HLX-001 (2021.08.03版)|RIGHT:109|RIGHT:529|RIGHT:1194|RIGHT:3014|RIGHT:1669|レジスタ変数を利用| ** 2021.08.05(木) -[1] HLX-001のJITコンパイラでは、いきなりx86やx64の機械語を生成するのをやめて、まずはHL-9aによく似た中間コードを出力して、その中間コードから機械語を生成するという二段階方式にしています(実際はさらにx86とx64の仕様を整理した別のコードにも変換しているので、厳密には三段階方式です。中間コードから機械語を生成するにあたって、どうしたら楽になるかを考えたらこうなりました)。 --なぜこうしたのかというと、中間コードにすればインタプリタ実行をするときに便利だし、中間コードは機種依存がないので他のCPUをサポートするときに便利だと思っているからです。・・・そうですね、少し説明が分かりにくくなりそうなので、この中間コードは「共通中間コード」と呼ぶことにします。 -[2] LLVMでも共通中間言語みたいなものはあると思いますが(LLVM-IR)、HLX-001の共通中間コードとの一番の違いは、テキストで表すか、バイナリで表すかというところだと思っています。テキストにすれば人間からは読みやすくなりますが、テキストで出力することもテキストを入力することも、CPUにとってはそれなりにオーバヘッドがあることです。テキスト化することのメリットも多いのですが、私はバイナリでどこまでできるかを見極めたいと思っています。 --そうすると、次はどんなバイナリ形式がいいかということになります。 --基本的にはintの配列を採用しています。そしてその命令長は可変長にしていました。命令によって長さがまちまちなのです。命令長は関数で取得できればそれでいいと思っていました。・・・しかしこれは結構煩雑になってきて、もしかしてこれは失敗だったかもと思い始めました。HL-9aの中間コードのように、固定長にするのがよさそうです。多少の例外は作るかもしれませんが、固定長を前提に命令フォーマットを再設計しようと思います。 -[3](翌日に追記)命令フォーマットを再設計しました。効果絶大で、最適化ルーチンは19%も短くなりました。そして他のところが大きくなるという弊害もなく、言語処理系の実行ファイルは1KBほどコンパクトになりました! ** 2021.08.06(金) -そろそろHLX-001を公開へもっていきたいと思っています。そのためにどんな残作業があるか列挙してみました。 --[1]スタック変数をサポートする --[2]x64対応がうまくいっているかテストする --[3]HL-9aっぽいインタプリタを載せる -とりあえずこの3つができたら公開したいです。アセンブラ出力は、HLX-002などの将来のバージョンでやろうと思います。・・・そしてこの3つだけなら、きっともうそんなに大変じゃないはずです。 ** 2021.08.11(水) -HLX-001の進捗 --[1]スタック変数のサポートはできた! --[2]x64対応がうまくいっているかのテストはこれから --[3]HL-9aっぽいインタプリタを載せるのは作業中 ||grd.c|mandel.c|maze.c|kcube.c|invader.c|| |HL-16a|RIGHT:158|RIGHT:622|RIGHT:1274|RIGHT:3125|RIGHT:1958|レジスタ変数を利用| |HLX-001 (2021.08.03版)|RIGHT:109|RIGHT:529|RIGHT:1194|RIGHT:3014|RIGHT:1669|レジスタ変数を利用| |HLX-001 (2021.08.10版)|RIGHT:120|RIGHT:413|RIGHT:957|RIGHT:1974|RIGHT:1324|レジスタ変数を利用| -grd.cが大きくなってしまったので、そこは改良の余地がありますが、それ以外はだいぶ良くなっています。 ** 2021.08.16(月) -HLX-001の進捗 --[1]スタック変数のサポートはできた! --[2]x64対応がうまくいっているかのテストもクリア --[3]HL-9aっぽいインタプリタを載せるのもできた -どのくらい性能が向上したのかをmandel.cで確認しています。 --[1] HL-9aとの比較 |HL-9a vs HLX-001|32bitモード(x86)|29.667秒 vs ''23.220秒''| |HL-9a vs HLX-001|64bitモード(x64)|26.904秒 vs ''18.653秒''| --[2] HL-16aとの比較(32bitモード(x86)) |HL-16a vs HLX-001|3.246秒(657バイト) vs ''3.272秒(609バイト)''|速度が速くなるようにレジスタ変数を割り当て| |HL-16a vs HLX-001|3.730秒(613バイト) vs ''3.660秒(411バイト)''|サイズが小さくなるようにレジスタ変数を割り当て| --[3] HL-22aとの比較(64bitモード(x64)) |HL-22a vs HLX-001|2.503秒(674バイト) vs ''2.512秒(655バイト)''|速度が速くなるようにレジスタ変数を割り当て| |HL-22a vs HLX-001|3.449秒(588バイト) vs ''3.459秒(489バイト)''|サイズが小さくなるようにレジスタ変数を割り当て| --[考察] ---[1]のHL-9aとの比較では、HLX-001になって有意に性能が向上しています。これはHLX-001が内部コードに対して最適化をしてから実行するようになったためです。 ---[2]のHL-16aとの比較では、有意な速度の向上はありませんでした。ただサイズにおいては有意な差がついています。これはどちらもアラインを考慮せずに比較しているので、その影響かもしれません。HLXにアライン機能を対応させたら、少しは差が出るかもしれません。 ---[3]のHL-22aとの比較では、有意な速度の向上はありませんでした。ただサイズにおいては有意な差がついています。これはどちらもアラインを考慮せずに比較しているので、その影響かもしれません。HLXにアライン機能を対応させたら、少しは差が出るかもしれません。 ** 2021.08.27(金) -C言語にはプリプロセッサがあって、#if~#endifがあります。私はその機能はよく使っていますが、でも実はあまり好きではありません。・・・普通のif文で同等のことができるべきだと思うのです。 -HLX-001では、ifの中の条件式が定数式であれば、最適化によって、まるで#ifのようにきれいに入れ替わります。それを保証しているので、#ifの代わりとしてもほぼ問題ない気がします。 -普通のif文にしておけば、もし定数ではなく変数で判断したくなっても、ほとんど改造しないで対応できるので、その点でもいいと思っています。 //-私は同じようなことをする機能が複数あるのは嫌いです。それらは1つに統合したほうがいいと思っています。 ** 2021.09.07(火) -さっきふと思いついて、printf()を追加してみました。これがあるといろいろ遊べることが分かりました。REPLとの相性がばっちりです。 for (i = 1; i <= 9; i++) { for (j = 1; j <= 9; j++) { printf("%2d ", i * j); } printf("\n"); } 1 2 3 4 5 6 7 8 9 2 4 6 8 10 12 14 16 18 3 6 9 12 15 18 21 24 27 4 8 12 16 20 24 28 32 36 5 10 15 20 25 30 35 40 45 6 12 18 24 30 36 42 48 54 7 14 21 28 35 42 49 56 63 8 16 24 32 40 48 56 64 72 9 18 27 36 45 54 63 72 81 ** 2021.09.24(金) -HLXで型をどう扱うか考えています。これは次のバージョンでどうこうする話ではなくて、最終的にどうするかという話です。 -まず、C++みたいなクラスは欲しいです。一方で、多重継承とか仮想関数とかはなくてもいいかもしれないとは思います。 --仮想関数は、自分で関数ポインタを管理すればいいかな、とりあえずは。 --いやまあ、サポートするかもしれないけど。 --仮に仮想関数をサポートするとして、使う予定のない仮想関数を排除する仕組み(それでコードサイズを小さく保つ仕組み)は欲しいかなとも思っています。 --そうなると、「絶対にこの文脈ではこの型が来る」という記述と、「この型もしくはこの型を継承した型が来る」という記述を書き分けられるといいかもしれないなあ。この情報があればコンパイラはvtableを引かなくてよくなります。 -テンプレートも使えないよりは使えたほうがいいかなあ。 --まあそれほど実現は難しくないかもしれない気がします。 -仮想関数をサポートしたとして、vtableがそのクラスのすべてのオブジェクトに入るのはなんかやだなあ。むしろvtable(というか型情報のすべて)を別に渡したいです。これをオブジェクトに含めるかどうかは、プログラマの裁量でいいんじゃないかな。 --malloc/freeで領域サイズをヒープブロックの先頭の前に入れるかどうか、のと類似の発想で。 -コンストラクタとデストラクタは欲しいです。あったらきっと便利です。 -これは型からは少しそれるけど、メモリ管理として、ガーベージコレクションに相当するものは欲しいです。 --でもマークアンドスイープやリンクカウント方式は好きじゃないです(リンクカウントが適切な状況は確かにあるけど、すべてのポインタに適用できるほど万能ではないと私は思っています)。 --大事なのは確実に管理できることじゃなくて、「維持する手間がが少ない&もしミスってもミスを発見しやすい」ことだと思います。だからメモリリークしちゃいけないわけじゃなくて、リークしたメモリ一覧とか、それはどこでどんな文脈でmallocしたものなのかが後から突き止められればいいはずです。しかもそのレポートはデバッグモードでだけ出ればいいのです。リリースモードではプログラムにミスがない前提で最高速度(or最小サイズ)で動けばいいと思うので。 --そうであれば、やっぱりAutoReleasePool方式がよさそうな気がします。すべてのオブジェクトはいずれかのAutoReleasePoolに属していて、ここに来たら消される(自動管理の場合)、もしくはここに来るまで残っていたらエラーにします(手動管理の場合)。 -オブジェクトの最適化ついて: --このメンバ関数が呼ばれたら「オブジェクトは利用された」と考える、それ以外は初期化や設定やあと始末なので利用ではない、一度も利用されないのならオブジェクトは生成しない、という最適化が欲しいです(もちろんオブジェクトの生成に副作用がない場合に限って)。 -関数の引数の最適化について: --「この引数は呼び出し先では結局一度も使われていない」という情報が欲しいです。そうであれば、その引数を積むのを省略できるし、引数の利用回数も減らせます。 --積むのを省略するだけじゃなくて、並び順から消してしまったりはできないかなあ。仮想関数みたいにインタフェースが決まっているときは変えられないけど。 -オブジェクトの利用回数について: --今は文脈を読んで、今代入した値が使われる前に上書きするもしくはvoid化されるというのを自動検出していますが、さらに関数全体で一度も使われていないか使われるかもしれないかを判定することが有効だと思います。これは判定が簡単なので実装したいです。 -クラス内のオブジェクトについて: --あるクラスのメンバが誰からも使われないのなら、消してしまったほうがいい気がします。しかし後で使うかもしれないです。REPLみたいなのを想定すると、将来まで使わないかどうかがわからないです。だからオブジェクトがプログラム終了までに消されるとわかっているものに限った話にしておけばいいかなあ。この考え方はAutoReleasePoolと相性がいいかもしれないです。 ** 2021.11.18(木) -しばらくここに書いていませんでしたが、いろいろバージョンアップ中です。 --'A'とか'b'を書けるようにしました。'\0'や'\n'も使えます。 --ifやforで { } を使わずに1文だけ書くやつができるようになりました。 --C言語風の記述で、以前のバージョンでは変数宣言があった場合その行ごと全部無視していましたが、開発中のバージョンではもし変数の初期化代入をしている部分があれば、それを認識するようになりました。 --ほぼすべて大文字で書ける、BASIC風の書き方も混在できるようになりました。 ** 2021.12.17(金) -先日リリースしたばかりの hlx003b ですが、BASIC風で書いたときはよく動くものの、C言語風に書くとなんか変な時があるみたいです。 hlx003a のほうは何の問題もないのですが・・・。直さなければ! // 条件ジャンプで1命令だけ飛ばす。 // そして次の命令が無条件分岐。 // そういう場合は、条件ジャンプを反転させて、ジャンプ先を変える。 // レジスタ変数をもっと増やしたい // できるだけたくさん増やすモードを付ける // 関数呼び出し前に専用領域へ退避させる // 引数処理は、読み替えればいい。 // JITコンパイラをJITコンパイラで拡張する。 // 配列や構造体への連続代入 // 自動enum。クラス名を書けば、enumしてくれる。 * こめんと欄 #comment
テキスト整形のルールを表示する