#:g1: SPARCプロセッサのLisp向けタグ命令の謎

Posted 2019-05-27 18:01:15 GMT

Sun(今やOracle)のSPARCプロセッサにはLisp向けにタグ命令が実装されている(いた)という話を耳にされたことはないでしょうか。

具体的には、TADDCCTSUBCC系の命令で、タグ付きポインタの演算を支援する機能です(ちなみに残念ながら現在では非推奨の機能らしい)。

それはともかく、ウェブを検索したら、当時のLucidだったEric Benson氏がこの機能についての質問に回答していたメールをみつけました。

Yes, the tagged arithmetic instructions were put in the SPARC architecture
for Lucid Common Lisp. If the low-order two bits of a Lisp object
reference are zero, it is a 30-bit immediate fixnum. If some of those
bits are non-zero, it may be a pointer to a floating point number or a
bignum (arbitrary-precision integer). Generic arithmetic is generally
optimized for the fixnum case, since the overwhelming majority of
arithmetic is performed on small integers. On many machines + is compiled
inline as

Test low order two bits of first operand. If nonzero, use general case. (Operand could be a float or bignum.) Test low order two bits of second operand. If nonzero, use general case. (Operand could be a float or bignum.) Add two operands. If overflow, use general case. (Result is a bignum).

On the SPARC this is done as one instruction (TADDCC) followed by a conditional branch rarely taken.

メールによると、SPARCのこの命令は、Lucid CLのために入ったらしいのですが、Lucidは、SunにCommon Lisp処理系をOEM提供しており、Sun Common Lispとして販売されていたりで、LucidとSunはかなり密接な関係でした。

時代的にも当時は第二次AIブーム末期で、エキスパートシステムやCAD等、高価なSymbolics等の専用マシン上で稼動していたLispベースのアプリケーションを比較的廉価なワークステーション上でも動したいというニーズも高かった頃です。

Lucid Common Lispではどう活用しているのか

Lucid CLのために命令が導入されたのはよしとして、実際にどんな感じで活用されていたのか確認してみましょう。

とりあえず、2引数のfixnumの足し算をコンパイルしてdisassembleしてみます。

> (proclaim '(optimize (compilation-speed 0) (speed 3) (safety 3)))
t
> (disassemble (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))))
;;; You are using the compiler in PRODUCTION mode (compilation-speed = 0)
;;; If you want shorter compile time at the expense of reduced optimization,
;;; you should use the development mode of the compiler, which can be obtained
;;; by evaluating (proclaim '(optimize (compilation-speed 3)))
;;; Generation of full safety checking code is enabled (safety = 3)
;;; Optimization of tail calls is enabled (speed = 3)

        cmp         %u0, 8
        tne         16
        taddcctv    %in0, %in1, %loc0
        move        %loc0, %in0
        jmpl        %0, %ra + 8
        restore     %0, 4, %u0
nil

taddcctv(Tagged Add, modify icc and Trap on Overflow)というのがお目当ての命令ですが、探してみてもどうも専らtaddccではなく、tv付きが使われるようです。

safety 0にしてみると、

> (proclaim '(optimize (compilation-speed 0) (speed 3) (safety 0))) 
t
> (disassemble (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))))
;;; You are using the compiler in PRODUCTION mode (compilation-speed = 0)
;;; If you want shorter compile time at the expense of reduced optimization,
;;; you should use the development mode of the compiler, which can be obtained
;;; by evaluating (proclaim '(optimize (compilation-speed 3)))
;;; Generation of runtime error checking code is disabled (safety = 0)
;;; Optimization of tail calls is enabled (speed = 3)

        taddcctv    %in0, %in1, %loc0
        move        %loc0, %in0
        jmpl        %0, %ra + 8
        restore     %0, 4, %u0
nil

となり、アリティのチェックが省略されるようです。

しかし、CMUCLでも活用されていた

さすがLucid CL用に用意されただけあるなと思いましたが、CMUCL 17fのdisassemble結果を眺めたりしていた所、CMUCLでも活用されているのをみつけてしまいました。

CMU Common Lisp 17f, running on sun4
Send bug reports and questions to cmucl-bugs@cs.cmu.edu.
Loaded subsystems:
    Python 1.0, target SPARCstation/Sun 4
    CLOS based on PCL version:  September 16 92 PCL (f)
* 
* (proclaim '(optimize (compilation-speed 0) (speed 3) (safety 3)))
EXTENSIONS::%UNDEFINED%
* (disassemble (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))))
Compiling LAMBDA (X Y): 
Compiling Top-Level Form: 

070BFCA8: .ENTRY "LAMBDA (X Y)"(x y) ; (FUNCTION (FIXNUM FIXNUM) FIXNUM) C0: ADD -18, %CODE C4: ADD %CFP, 32, %CSP

C8: CMP %NARGS, 8 ; %NARGS = #:G1 CC: BNE L0 D0: NOP D4: TADDCCTV %ZERO, %A0 ; %A0 = #:G2 D8: TADDCCTV %ZERO, %A1 ; %A1 = #:G3 DC: TADDCCTV %A1, %A0 ; No-arg-parsing entry point E0: MOVE %CFP, %CSP E4: MOVE %OCFP, %CFP E8: J %LRA+5 EC: MOVE %LRA, %CODE F0: L0: UNIMP 10 ; Error trap F4: BYTE #x04 F5: BYTE #x19 ; INVALID-ARGUMENT-COUNT-ERROR F6: BYTE #xFE, #xEB, #x01 ; NARGS F9: .ALIGN 4 * (funcall (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))) most-positive-fixnum most-positive-fixnum) Compiling LAMBDA (X Y): Compiling Top-Level Form:

1073741822

しかし、Lucid CLと違うのは、safety 0にすると普通のaddになってしまう所。

(+ most-positive-fixnum most-positive-fixnum)-2になってしまっています。

* (proclaim '(optimize (compilation-speed 0) (speed 3) (safety 0)))
EXTENSIONS::%UNDEFINED%
* (disassemble (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))))
Compiling LAMBDA (X Y): 
Compiling Top-Level Form: 

071A11F8: .ENTRY "LAMBDA (X Y)"(x y) ; (FUNCTION (FIXNUM FIXNUM) FIXNUM) 210: ADD -18, %CODE 214: ADD %CFP, 32, %CSP 218: ADD %A1, %A0 ; No-arg-parsing entry point 21C: MOVE %CFP, %CSP 220: MOVE %OCFP, %CFP 224: J %LRA+5 228: MOVE %LRA, %CODE 22C: UNIMP 0 * (funcall (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))) most-positive-fixnum most-positive-fixnum) Compiling LAMBDA (X Y): Compiling Top-Level Form:

-2

Lucid CLでは、safety 0でもtaddcctvはそのままでbignumに切り替えます。

> (proclaim '(optimize (compilation-speed 0) (speed 3) (safety 0)))
t
> (disassemble (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))))
;;; You are using the compiler in PRODUCTION mode (compilation-speed = 0)
;;; If you want shorter compile time at the expense of reduced optimization,
;;; you should use the development mode of the compiler, which can be obtained
;;; by evaluating (proclaim '(optimize (compilation-speed 3)))
;;; Generation of runtime error checking code is disabled (safety = 0)
;;; Optimization of tail calls is enabled (speed = 3)

        taddcctv    %in0, %in1, %loc0
        move        %loc0, %in0
        jmpl        %0, %ra + 8
        restore     %0, 4, %u0
nil
> (funcall (compile nil '(lambda (x y) (declare (fixnum x y)) (the fixnum (+ x y)))) most-positive-fixnum most-positive-fixnum)
1073741822

この辺りの違いは、Lucid CLの方がtaddccを活用しているといえるのか、それとも処理系のポリシーの違いなのか。

まとめ

まとめらしいまとめはないですが、Common Lispの処理系のためにCPUに命令が追加されたと思うとSPARCが素晴らしいCPUに見えてきました。
(SPARCプロセッサも大分勢いが無くなってきましたが……)


HTML generated by 3bmd in LispWorks 7.0.0

comments powered by Disqus