closure conversion
テンプレートのなかにリストを展開するような簡単なS式操作は分かっているつもりだけど、Closure Conversion みたいな式の形がだいぶ変わるような変換は初めてだ。再帰的な処理の固まりで難しい。
しかしなんとかどんくさいコードながら実装。このエレガントな変形をいつかアニメーションにしたいくらい。明示的に funcall が必要なのがかっこわるいけどたいした問題ではない、はず。
gosh> (closure-conversion '(let ((f (lambda (g) (funcall g 2 13)))) (funcall f (lambda (n m) (* n m))))) (labels ((G703 (code (g) () (funcall g 2 13))) (G704 (code (n m) () (* n m)))) (let ((f (closure G703))) (funcall f (closure G704)))) gosh> (run-program (closure-conversion '(let ((f (lambda (g) (funcall g 2 13)))) (funcall f (lambda (n m) (* n m)))))) "26\n"
LLVM IR は以下のとおり(ほんとはこの中間にS式がある)。クロージャ。
gosh> (compile-program (closure-conversion '(let ((f (lambda (g) (funcall g 2 13)))) (funcall f (lambda (n m) (* n m)))))) declare i32 @display_obj(i32) declare i32 @display_vector(i32) declare i32 @display_pair(i32) declare i32 @newline() declare void @scheme_finalize() declare void @scheme_initialize() @ap = external global i32 define i32 @scheme_entry () { entry: %ret = alloca i32 %var121 = load i32* @ap %var125 = inttoptr i32 %var121 to i32* %var124 = ptrtoint i32 (i32)* @Label_5 to i32 store i32 %var124, i32* %var125 %var127 = or i32 6, %var121 store i32 %var127, i32* %ret %var122 = add i32 4, %var121 store i32 %var122, i32* @ap store i32 0, i32* %ret %var128 = load i32* %ret %var126 = inttoptr i32 %var122 to i32* store i32 %var128, i32* %var126 %var123 = add i32 4, %var122 store i32 %var123, i32* @ap store i32 %var127, i32* %ret %sta18 = alloca i32 %var131 = load i32* %ret store i32 %var131, i32* %sta18 %var133 = load i32* @ap %var137 = inttoptr i32 %var133 to i32* %var136 = ptrtoint i32 (i32, i32)* @Label_6 to i32 store i32 %var136, i32* %var137 %var139 = or i32 6, %var133 store i32 %var139, i32* %ret %var134 = add i32 4, %var133 store i32 %var134, i32* @ap store i32 0, i32* %ret %var140 = load i32* %ret %var138 = inttoptr i32 %var134 to i32* store i32 %var140, i32* %var138 %var135 = add i32 4, %var134 store i32 %var135, i32* @ap store i32 %var139, i32* %ret %var132 = load i32* %ret %var150 = load i32* %sta18 store i32 %var150, i32* %ret %var143 = load i32* %ret %var144 = sub i32 %var143, 6 %var145 = inttoptr i32 %var144 to i32* %var146 = load i32* %var145 %var147 = inttoptr i32 %var146 to i32 (i32, i32)* %var148 = call i32 %var147(i32 %var132, i32 %var143) store i32 %var148, i32* %ret %retval = load i32* %ret ret i32 %retval } define i32 @Label_6 (i32 %arg1, i32 %arg2) { entry: ; start-define %ret = alloca i32 %sta14 = alloca i32 store i32 %arg1, i32* %sta14 %sta15 = alloca i32 store i32 %arg2, i32* %sta15 %var119 = load i32* %sta14 store i32 %var119, i32* %ret %sta16 = alloca i32 %var111 = load i32* %ret store i32 %var111, i32* %sta16 %sta17 = alloca i32 %var120 = load i32* %sta15 store i32 %var120, i32* %ret %var112 = load i32* %ret store i32 %var112, i32* %sta17 %var113 = load i32* %sta16 %var114 = load i32* %sta17 %var115 = sdiv i32 %var113, 4 %var116 = sdiv i32 %var114, 4 %var117 = mul i32 %var115, %var116 %var118 = mul i32 %var117, 4 store i32 %var118, i32* %ret %retval = load i32* %ret ret i32 %retval } define i32 @Label_5 (i32 %arg1) { entry: ; start-define %ret = alloca i32 %sta13 = alloca i32 store i32 %arg1, i32* %sta13 store i32 8, i32* %ret %var101 = load i32* %ret store i32 52, i32* %ret %var102 = load i32* %ret %var110 = load i32* %sta13 store i32 %var110, i32* %ret %var103 = load i32* %ret %var104 = sub i32 %var103, 6 %var105 = inttoptr i32 %var104 to i32* %var106 = load i32* %var105 %var107 = inttoptr i32 %var106 to i32 (i32, i32, i32)* %var108 = call i32 %var107(i32 %var101, i32 %var102, i32 %var103) store i32 %var108, i32* %ret %retval = load i32* %ret ret i32 %retval } define i32 @main () { entry: call void @scheme_initialize() %tmp = call i32 @scheme_entry() call i32 @display_obj(i32 %tmp) call i32 @newline() call void @scheme_finalize() ret i32 0 } #<undef> gosh>