FizzBuzz
追いついた♪
I=1 'for I=1 to 100 *F A=1 if I mod 3 = 0 then ?"Fizz";:A=0 if I mod 5 = 0 then ?"Buzz";:A=0 if A then ?I; ? I=I+1:if I<=100 goto *F 'next
...>basicll -r fizzbuzz.bas 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz (以下略)
後はWHILEとFORのループ文を作って演算子を揃えればTiny BASIC並にはなるんじゃないかな……basicll.cが膨らむほどBASICに書き直してのブートストラップは無理なんじゃないかと思う今日この頃。どーでもいいですがLLVMの「言語」はもうちょっと自動生成向けの構文でもいいと思います……手書きを想定してるんじゃないかってぐらいに堅い堅い。LLVMについては文句しか言って無いな私。
basicll.cのことを書けば、BASICに切り替えたときにPRINT USINGを使うことを想定してPRINT USING用の内部関数using_xxxを呼びまくってますが、素直にsprintfを使っておいた方が幸せだった気が。STRING_CONSTもBDSで編集すること考えなければ複合リテラル使えばいいなあ。複合リテラルの存在を今更思い出したという。そういやC++BuilderってBoostばっか取り沙汰されてますけれどC99はどうなんでしょうか。2006ではstdint.hは付いてきてますが便利構文のあれやこれは壊滅状態です。
Cからの書き換えを考えると構造化用の機能がある程度必要になるわけですが、とりあえずポインタと構造体があれば充分なのか……な?Full BASICもMicrosoft系BASICも好きではないので勝手に考えると、構造体はDEF FNとFIELDの流れでDEF FIELDとでもするのが自然な気がします。ポインタはアクセスにはPEEK, POKEを使うとしても全部バイト単位ってわけにもいかないので型宣言が必要に。先は長い。
しかし追いかけるものがあると作業効率が違いますね。とりあえず追いついたので小休止か?こんな何の役にも立たんことよりVoVとThebe弄らないと。あと処理系のことを書くとアクセス数が減ると言うのは真実かも知れない。
上のコードはこんな風になります。定型部分は省く。
@vA$f = global float 0.0 @vI$f = global float 0.0 %string2_t = type {i32, i32, [5 x i8]} @string2 = internal constant %string2_t {i32 -1, i32 4, [5 x i8] c"Buzz\00"} %string1_t = type {i32, i32, [5 x i8]} @string1 = internal constant %string1_t {i32 -1, i32 4, [5 x i8] c"Fizz\00"} define void @program(i32 %start){ %tmp1 = add i32 1, 0 %tmp2 = sitofp i32 %tmp1 to float store float %tmp2, float* @vI$f br label %LF LF: %tmp3 = add i32 1, 0 %tmp4 = sitofp i32 %tmp3 to float store float %tmp4, float* @vA$f %tmp5 = load float* @vI$f %tmp6 = fptosi float %tmp5 to i32 %tmp7 = add i32 3, 0 %tmp8 = srem i32 %tmp6, %tmp7 %tmp9 = sitofp i32 %tmp8 to double %tmp10 = add i32 0, 0 %tmp11 = sitofp i32 %tmp10 to double %tmp12 = fcmp oeq double %tmp9, %tmp11 br i1 %tmp12, label %then13, label %else13 then13: %tmp14 = add i32 0, 0 %tmp15 = getelementptr %string1_t* @string1, i32 0, i32 2, i32 0 call void @print_string(i32 %tmp14, i8* %tmp15) call void @string_release(i8* %tmp15) %tmp16 = add i32 0, 0 %tmp17 = sitofp i32 %tmp16 to float store float %tmp17, float* @vA$f br label %if13 else13: br label %if13 if13: %tmp18 = load float* @vI$f %tmp19 = fptosi float %tmp18 to i32 %tmp20 = add i32 5, 0 %tmp21 = srem i32 %tmp19, %tmp20 %tmp22 = sitofp i32 %tmp21 to double %tmp23 = add i32 0, 0 %tmp24 = sitofp i32 %tmp23 to double %tmp25 = fcmp oeq double %tmp22, %tmp24 br i1 %tmp25, label %then26, label %else26 then26: %tmp27 = add i32 0, 0 %tmp28 = getelementptr %string2_t* @string2, i32 0, i32 2, i32 0 call void @print_string(i32 %tmp27, i8* %tmp28) call void @string_release(i8* %tmp28) %tmp29 = add i32 0, 0 %tmp30 = sitofp i32 %tmp29 to float store float %tmp30, float* @vA$f br label %if26 else26: br label %if26 if26: %tmp31 = load float* @vA$f %tmp32 = fcmp one float %tmp31, 0.0 br i1 %tmp32, label %then33, label %else33 then33: %tmp34 = add i32 0, 0 %tmp35 = load float* @vI$f %tmp36 = fpext float %tmp35 to double call void @print_double(i32 %tmp34, double %tmp36) br label %if33 else33: br label %if33 if33: %tmp37 = add i32 0, 0 call void @print_line(i32 %tmp37) %tmp38 = load float* @vI$f %tmp39 = fpext float %tmp38 to double %tmp40 = add i32 1, 0 %tmp41 = sitofp i32 %tmp40 to double %tmp42 = add double %tmp39, %tmp41 %tmp43 = fptrunc double %tmp42 to float store float %tmp43, float* @vI$f %tmp44 = load float* @vI$f %tmp45 = fpext float %tmp44 to double %tmp46 = add i32 100, 0 %tmp47 = sitofp i32 %tmp46 to double %tmp48 = fcmp ole double %tmp45, %tmp47 br i1 %tmp48, label %then49, label %else49 then49: br label %LF br label %if49 else49: br label %if49 if49: tail call void @END() noreturn unreachable }
それがllvm-ldの最適化でこうなります。program関数がmain関数の中にインライン展開されるためmain関数。
呼び出し規約を勝手にfastccに変えたりしているあたり要注目。
define i32 @main(i32 %argc, i8** %args) { ; <label>:0 %tmp3 = call i32 @_setjmp( i32* getelementptr ([16 x i32]* @basic_on_exit, i32 0, i32 0) ) ; <i32> [#uses=1] %tmp4 = icmp eq i32 %tmp3, 0 ; <i1> [#uses=1] br i1 %tmp4, label %LF.i, label %error LF.i: ; preds = %if33.i, %0 %"vI$f.0" = phi float [ 1.000000e+000, %0 ], [ %tmp43.i, %if33.i ] ; <float> [#uses=3] %tmp6.i = fptosi float %"vI$f.0" to i32 ; <i32> [#uses=2] %tmp8.i = srem i32 %tmp6.i, 3 ; <i32> [#uses=1] %tmp9.i = sitofp i32 %tmp8.i to double ; <double> [#uses=1] %tmp12.i = fcmp oeq double %tmp9.i, 0.000000e+000 ; <i1> [#uses=1] br i1 %tmp12.i, label %then13.i, label %if13.i then13.i: ; preds = %LF.i call fastcc void @print_string( i32 0, i8* getelementptr (%string1_t* @string1, i32 0, i32 2, i32 0) ) %tmp7.i.i = load i32* bitcast (i8* getelementptr (%string1_t* @string1, i32 0, i32 2, i32 -8) to i32*), align 4 ; <i32> [#uses=3] %tmp8.i.i = icmp eq i32 %tmp7.i.i, -1 ; <i1> [#uses=1] br i1 %tmp8.i.i, label %if13.i, label %cond_true11.i.i cond_true11.i.i: ; preds = %then13.i %tmp15.i.i = add i32 %tmp7.i.i, -1 ; <i32> [#uses=1] store i32 %tmp15.i.i, i32* bitcast (i8* getelementptr (%string1_t* @string1, i32 0, i32 2, i32 -8) to i32*), align 4 %tmp19.i.i = icmp eq i32 %tmp7.i.i, 1 ; <i1> [#uses=1] br i1 %tmp19.i.i, label %cond_true22.i.i, label %if13.i cond_true22.i.i: ; preds = %cond_true11.i.i free i8* getelementptr (%string1_t* @string1, i32 0, i32 2, i32 -8) br label %if13.i if13.i: ; preds = %cond_true22.i.i, %cond_true11.i.i, %then13.i, %LF.i %"vA$f.1" = phi float [ 0.000000e+000, %then13.i ], [ 0.000000e+000, %cond_true11.i.i ], [ 0.000000e+000, %cond_true22.i.i ], [ 1.000000e+000, %LF.i ] ; <float> [#uses=1] %tmp21.i = srem i32 %tmp6.i, 5 ; <i32> [#uses=1] %tmp22.i = sitofp i32 %tmp21.i to double ; <double> [#uses=1] %tmp25.i = fcmp oeq double %tmp22.i, 0.000000e+000 ; <i1> [#uses=1] br i1 %tmp25.i, label %then26.i, label %if26.i then26.i: ; preds = %if13.i call fastcc void @print_string( i32 0, i8* getelementptr (%string1_t* @string2, i32 0, i32 2, i32 0) ) %tmp7.i1.i = load i32* bitcast (i8* getelementptr (%string1_t* @string2, i32 0, i32 2, i32 -8) to i32*), align 4 ; <i32> [#uses=3] %tmp8.i2.i = icmp eq i32 %tmp7.i1.i, -1 ; <i1> [#uses=1] br i1 %tmp8.i2.i, label %if26.i, label %cond_true11.i5.i cond_true11.i5.i: ; preds = %then26.i %tmp15.i3.i = add i32 %tmp7.i1.i, -1 ; <i32> [#uses=1] store i32 %tmp15.i3.i, i32* bitcast (i8* getelementptr (%string1_t* @string2, i32 0, i32 2, i32 -8) to i32*), align 4 %tmp19.i4.i = icmp eq i32 %tmp7.i1.i, 1 ; <i1> [#uses=1] br i1 %tmp19.i4.i, label %cond_true22.i6.i, label %if26.i cond_true22.i6.i: ; preds = %cond_true11.i5.i free i8* getelementptr (%string1_t* @string2, i32 0, i32 2, i32 -8) br label %if26.i if26.i: ; preds = %cond_true22.i6.i, %cond_true11.i5.i, %then26.i, %if13.i %"vA$f.0" = phi float [ 0.000000e+000, %then26.i ], [ 0.000000e+000, %cond_true11.i5.i ], [ 0.000000e+000, %cond_true22.i6.i ], [ %"vA$f.1", %if13.i ] ; <float> [#uses=1] %tmp32.i = fcmp ueq float %"vA$f.0", 0.000000e+000 ; <i1> [#uses=1] br i1 %tmp32.i, label %if33.i, label %then33.i then33.i: ; preds = %if26.i %tmp36.i = fpext float %"vI$f.0" to double ; <double> [#uses=1] call fastcc void @print_double( i32 0, double %tmp36.i ) br label %if33.i if33.i: ; preds = %then33.i, %if26.i call fastcc void @print_line( i32 0 ) %tmp39.i = fpext float %"vI$f.0" to double ; <double> [#uses=1] %tmp42.i = add double %tmp39.i, 1.000000e+000 ; <double> [#uses=1] %tmp43.i = fptrunc double %tmp42.i to float ; <float> [#uses=2] %tmp45.i = fpext float %tmp43.i to double ; <double> [#uses=1] %tmp48.i = fcmp ugt double %tmp45.i, 1.000000e+002 ; <i1> [#uses=1] br i1 %tmp48.i, label %else49.i, label %LF.i else49.i: ; preds = %if33.i call void bitcast (void ()* @END to void () noreturn *)( ) noreturn unreachable exit: ; preds = %error ret i32 0 error: ; preds = %0 %tmp1.i = load i32* @error_number, align 4 ; <i32> [#uses=3] %tmp8 = icmp eq i32 %tmp1.i, 0 ; <i1> [#uses=1] br i1 %tmp8, label %exit, label %report report: ; preds = %error call fastcc void @print_string( i32 -2, i8* getelementptr (%error_message_t* @error_message, i32 0, i32 2, i32 0) ) %tmp1011 = sitofp i32 %tmp1.i to double ; <double> [#uses=1] call fastcc void @print_double( i32 -2, double %tmp1011 ) call fastcc void @print_line( i32 -2 ) %exerr = add i32 %tmp1.i, 100 ; <i32> [#uses=1] ret i32 %exerr }
え?なんで内部doubleなんだって?手抜きに決まってるじゃないですか、アハハ……。
どの道DEFINT A-Z書いたり%付けたりしない以上はBASICの仕様上「単精度」浮動小数点型になるわけですが。(それでもfloatではなくてdoubleなのは手抜き)