Scheme Compiler の勉強(22) - letrec ,引数あり、のコンパイル

procedure (letrec)

(letrec *1 が通るようになった。おお、これは嬉しい。

以下のような scheme のプログラム断片が、

(build-program '(letrec ((f (lambda (n)
			      (if ($fxzero? n)
				  1
				  (fx* n (f ($fxsub1 n)))))))
		  (f 3)))

自作の”コンパイラ”によって以下の LLVM IR に変換される。

define i32 @Label_62(i32 %arg1) nounwind {
entry:
    %ret = alloca i32
; here
    %sta69 = alloca i32
    store i32 %arg1, i32* %sta69
; emit-lambda
    %var309 = load i32* %sta69
    store i32 %var309, i32* %ret
    %var306 = load i32* %ret
    %var307 = icmp eq i32 %var306, 0
    %var308 = select i1 %var307, i32 111, i32 47
    store i32 %var308, i32* %ret
    %var304 = load i32* %ret
    %var305 = icmp eq i32 %var304, 47
    br i1 %var305, label %Label_64, label %Label_63
Label_63:
    store i32 4, i32* %ret
    br label %Label_65
Label_64:
    %var320 = load i32* %sta69
    store i32 %var320, i32* %ret
    %sta70 = alloca i32
    %var310 = load i32* %ret
    store i32 %var310, i32* %sta70
    %var326 = load i32* %sta69
    store i32 %var326, i32* %ret
    %var323 = load i32* %ret
    %var324 = sub i32 4, %var323
    %var325 = sub i32 0, %var324
    store i32 %var325, i32* %ret
    %var322 = load i32* %ret
    %var321 = call i32 @Label_62(i32 %var322)
    store i32 %var321, i32* %ret
    %sta71 = alloca i32
    %var311 = load i32* %ret
    store i32 %var311, i32* %sta71
    %var312 = load i32* %sta70
    %var314 = load i32* %sta71
    %var313 = sdiv i32 %var312, 4
    %var315 = sdiv i32 %var314, 4
    %var316 = mul i32 %var313, %var315
    %var317 = mul i32 %var316, 4
    store i32 %var317, i32* %ret
    br label %Label_65
Label_65:
    %retval = load i32* %ret
    ret i32 %retval
}
define i32 @scheme_entry() nounwind {
entry:
    %ret = alloca i32
    store i32 12, i32* %ret
    %var328 = load i32* %ret
    %var327 = call i32 @Label_62(i32 %var328)
    store i32 %var327, i32* %ret
    %retval = load i32* %ret
    ret i32 %retval
}

LLVM の opt コマンドで最適化してやる。なんだか良くわからないけど、なにかの最適化がされているらしい。
※「なんだか良くわからないけど、なにかの最適化」というのはあんまりなのであとで追うこと。

define i32 @Label_62(i32 %arg1) nounwind  {
entry:
	%var307 = icmp eq i32 %arg1, 0		; <i1> [#uses=1]
	br i1 %var307, label %UnifiedReturnBlock, label %Label_64

Label_64:		; preds = %entry
	%var324 = add i32 %arg1, -4		; <i32> [#uses=1]
	%var321 = call i32 @Label_62( i32 %var324 )		; <i32> [#uses=1]
	%var313 = sdiv i32 %arg1, 4		; <i32> [#uses=1]
	%var315 = sdiv i32 %var321, 4		; <i32> [#uses=1]
	%var316 = shl i32 %var313, 2		; <i32> [#uses=1]
	%var317 = mul i32 %var316, %var315		; <i32> [#uses=1]
	ret i32 %var317

UnifiedReturnBlock:		; preds = %entry
	ret i32 4
}

define i32 @scheme_entry() nounwind  {
entry:
	%var321.i = tail call i32 @Label_62( i32 8 ) nounwind 		; <i32> [#uses=1]
	%var315.i = sdiv i32 %var321.i, 4		; <i32> [#uses=1]
	%var317.i = mul i32 %var315.i, 12		; <i32> [#uses=1]
	ret i32 %var317.i
}

で、さらにアセンブラ(PowerPC)に変換するとこんな感じ。ほほう。

	.machine ppc7400
	.section __TEXT,__textcoal_nt,coalesced,pure_instructions
	.section __TEXT,__symbol_stub1,symbol_stubs,pure_instructions,16
	.text


	.globl	_Label_62
	.align	4
_Label_62:
	mflr r0
	stw r0, 8(r1)
	stwu r1, -64(r1)
	stw r30, 60(r1)
	cmplwi cr0, r3, 0
	mr r30, r3
	beq cr0, LBB1_2	; UnifiedReturnBlock
LBB1_1:	; Label_64
	addi r3, r30, -4
	bl _Label_62
	srawi r2, r3, 2
	addze r2, r2
	srawi r3, r30, 2
	addze r3, r3
	mullw r2, r3, r2
	slwi r3, r2, 2
	lwz r30, 60(r1)
	addi r1, r1, 64
	lwz r0, 8(r1)
	mtlr r0
	blr 
LBB1_2:	; UnifiedReturnBlock
	li r3, 4
	lwz r30, 60(r1)
	addi r1, r1, 64
	lwz r0, 8(r1)
	mtlr r0
	blr 


	.globl	_scheme_entry
	.align	4
_scheme_entry:
	mflr r0
	stw r0, 8(r1)
	stwu r1, -64(r1)
	li r3, 8
	bl _Label_62
	srawi r2, r3, 2
	addze r2, r2
	mulli r3, r2, 12
	addi r1, r1, 64
	lwz r0, 8(r1)
	mtlr r0
	blr 

	.subsections_via_symbols

letrec 完了

引数の無いラムダ式 (lambda () 5)、引数1個(lambda (x) (fxadd1 x))、と順番に実装し、引数複数も対応した。これで以下のようなテストが通るようになった。

(letrec ([f (lambda (x) 
                 (if ($fxzero? x)
                     1
                     (fx* x (f ($fxsub1 x)))))])
      (f 5))
=> 120

(letrec ([f (lambda (x) (g x x))]
            [g (lambda (x y) (fx+ x y))])
     (f 12))
=> 24

*1:f lambda (x) 5))) (f