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なのは手抜き)