guile (3)

Table of Contents

Makefile in guile

ArchLinuxでmakeパッケージを導入すると、guileも一緒に入ってくるんで、そ の辺どうなってるか調べてみた。

GNU Make 4.0にGNU Guileが組み込まれた

[sakae@arch z]$ cat Makefile
$(guile "all:")
        echo $(guile (string-append "Hello " "guile"))
[sakae@arch z]$ make
echo Hello guile
Hello guile

ひょっとして、これってrubyのアレ(rake/Rakefile)を羨しがって、guile好きな人が要求した んではなかろうか。後援者にあのストールマンがついているからね。入れざる を得なかった、ってのが真実だろう。

12.1 GNU Guile Integration

gdb with guile

pythonを利用してgdbを操作できる事は知ってたけど、guileでも可能みたい。 これもストールマンの圧力のおかげか。マニュアルは下記にある。

23.4 Extending GDB using Guile

どのverのgdbから利用できるかは未確認だけど、少くともOpenBSDのportsから の9.2では駄目だった。FreeBSDのportsも駄目だった。Debian/WSL2でもサポー トしていなかった。pythonのバインディン グは有効になってるんだけど、guileは頑無視されてますよ。笛吹けど誰も踊 らずって状況。これじゃ、ストールマンの夢は潰えるわな。せめてオイラーだ けでも賛同しましょ!

しょうがないので、ArchLinuxのそれで試してみる。

read libguile-3.0.so.1.6.0-gdb.scm

6.26.5 GDB Support

随分色々と定義されてるけど、使えそうなのは、これぐらいかなあ? 引数の 無い手続、いわゆるチャンクで試してみるって寸法。

(define* (display-vm-frames #:optional (port (current-output-port)))
  "Display the VM frames on PORT."
  (stream-for-each (lambda (frame)
                     (dump-vm-frame frame port))
                   (vm-frames)))

fibを途中で止めて、フレームを表示させてみる。

(gdb) bt
 :
#26 0x00005d1e64651311 in main (argc=2, argv=0x7ffcb3493848)
    at /home/sakae/guile-3.0.9/libguile/guile.c:94
(gdb) guile (display-vm-frames)
/usr/local/lib/libguile-3.0.so.1.6.0-gdb.scm:293:21:
;;; note: source file /usr/local/share/guile/3.0/system/vm/frame.scm
;;;       newer than compiled /usr/lib/guile/3.0/ccache/system/vm/frame.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /usr/local/share/guile/3.0/system/vm/frame.scm
;;; note: source file /usr/local/share/guile/3.0/language/bytecode/spec.scm
;;;       newer than compiled /usr/lib/guile/3.0/ccache/language/bytecode/spec.go
;;; compiling /usr/local/share/guile/3.0/language/bytecode/spec.scm
;;; WARNING: compilation of /usr/local/share/guile/3.0/language/bytecode/spec.scm failed:
:

確認はしてないけど、これってインストール時のBUGではなかろうか? 回避す る為に export GUILE_AUTO_COMPILE=0 をgdbの端末で実行しておいて、同じ事を実行

(gdb) guile (display-vm-frames)
/usr/local/lib/libguile-3.0.so.1.6.0-gdb.scm:293:21: In procedure vm-frame-function-name:
Unbound variable: program-debug-info-name
Error while executing Scheme code.
(gdb) guile (find-vp)
#<gdb:value {ip = 0x7f2e72abc1cc, sp = 0x7f2e738b8a00, fp = 0x7f2e738b8a38, stack_limit = 0x7f2e738b8000, compare_result = 2 '\002',
apply_hook_enabled = 0 '\000', return_hook_enabled = 0 '\000', next_hook_enabled = 0 '\000', abort_hook_enabled = 0 '\000',
disable_mcode = 0 '\000', engine = 0 '\000', unused = 0 '\000', stack_size = 512, stack_bottom = 0x7f2e738b8000, apply_hook = 0x4,
return_hook = 0x4, next_hook = 0x4, abort_hook = 0x4, stack_top = 0x7f2e738b9000, overflow_handler_stack = 0x304,
registers = 0x7ffddb16f5a0, mra_after_abort = 0x0, trace_level = 0}>
(gdb) guile (newest-vm-frame)
#<<vm-frame> ip: #<gdb:value 0x7f2e72abc1cc> sp: #<gdb:value 0x7f2e738b8a00> fp: #<gdb:value 0x7f2e738b8a38>
saved-ip: #<gdb:value 0x0> saved-fp: #<gdb:value 0x478a208e99738>>

大体、program-debug-info-name なんて変数は未登録と言ってきた。

しょうがないので、find-vp "Find the scm_vm pointer for the current thread." と、newest-vm-frame "Return the newest VM frame or #f." を、 確認してみた。

program-debug-info-name

Unboundの正体を探ってみる。エラーオンデマンドね。bound不良の文字列を頼 りにlibguleの中を探すもヒットせず。だって、名前からしてアトミックな関 数と想像したから。大体、脳味噌が低レベルモードになってる。しょうがない ので、脳を高レベルモードに切り替えてmoduleの中を探したらヒットした。

system/vm/debug.scm

(define-record-type <program-debug-info>
  (make-program-debug-info context name offset size)
  program-debug-info?
  (context program-debug-info-context)
  (name program-debug-info-name)
  ;; Offset of the procedure in the text section, in bytes.
  (offset program-debug-info-offset)
  (size program-debug-info-size))

define-record-type は (use-modules (srfi srfi-9)) に元本がある。 OOPへの入口だな。9.28 gauche.record - レコード型 も参考にしよう。

An example will illustrate typical usage,

     (define-record-type <employee>
       (make-employee name age salary)
       employee?
       (name    employee-name)
       (age     employee-age    set-employee-age!)
       (salary  employee-salary set-employee-salary!))

     <employee> ⇒ #<record-type <employee>>

     (define fred (make-employee "Fred" 45 20000.00))

     (employee? fred)        ⇒ #t
     (employee-age fred)     ⇒ 45
     (set-employee-salary! fred 25000.00)  ;; pay rise

従業員と言うレコードを定義すると、それに属する関数が湧いて来るってのは、 オイラーの性に合わないな。まあ、これが世間一般で通用してるなら、慣れな ければいけないな。で、関数とばかり思っていたのは、 アクセッサーだったのね。まあ変数ちゃあ変数だわな。と言う事は、 /usr/local/lib/libguile-3.0.so.1.6.0-gdb.scmの定義ぬけだな。

#:autoload   (system vm debug) (debug-context-from-image
                                debug-context-base
				program-debug-info-name  ;; add
                                find-program-debug-info)

今度はunboundにならずに、動作を始めた。

(gdb) guile (display-vm-frames)
  name: [unknown]
  ip: 0x7917bed9dfb8
  fp: 0x7917bf7c79c8

寂れた裏街道だと誰も通る事は無く、そこかしこに地雷が埋まっていそうだな。 通報してあげようかな。

gdb script

gdbのスクリプトも用意されてるので試してみる。お題は下記。丁度mycar の納税時期なんで、無理してmycarってのを使ってみた。なんせ高い税金を取 られていますから、使わな損ってもんです。

1970年から天文年数で、今現在何年経過してるか計算するものです。暦と比べると、天文 年は、1年で6時間弱長い。それを補正する為、うるう年が有る訳です。尚 86400は、1日の秒数になります。

(define (mycar obj) (car obj))
(define (hy)
  (let ((tv (gettimeofday)))
    (/ (mycar tv) 86400 365.242189)))

scm_gettimeofday にBPを置いて実行。止まった所で確認。

(gdb) nextframe
Invalid type combination in ordering comparison.
(gdb) progstack
No symbol "stack_base" in current context.
(gdb) gbt
Exception thrown while printing backtrace:
In procedure <: Wrong type argument in position 1: #<XXX UNUSED BOOLEAN 2 -- DO NOT USE -- SHOULD NEVER BE SEEN XXX>
$1 = (struct scm_unused_struct *) 0x804

どうも、場所を弁えていないと使い道が無い雰囲気だなあ。綺麗さっぱり と忘れてしまおう。いじけて、普通に利用できる物にしておく。

scheme@(guile-user)> ,br mycar
Trap 0: Breakpoint at #<procedure mycar (obj)>.
scheme@(guile-user)> ,br hy
Trap 1: Breakpoint at #<procedure hy ()>.
scheme@(guile-user)> ,traps
  0: Breakpoint at #<procedure mycar (obj)>
  1: Breakpoint at #<procedure hy ()>
scheme@(guile-user)> (hy)
Trap 1: Breakpoint at #<procedure hy ()>
Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> ,bt
In /tmp/hy.scm:
      2:0  0 (hy)
scheme@(guile-user) [1]> ,q
Trap 0: Breakpoint at #<procedure mycar (obj)>
Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> ,bt
In /tmp/hy.scm:
      4:7  1 (hy)
      1:0  0 (mycar (1714863233 . 255694))
scheme@(guile-user) [1]> ,q
$6 = 54.34189883701564

newer than compiled

上のセッションをやっている時、易しい英語で警告が出てきた。 通常ならソース(.scm)をコンパイルすると(.go)になる。だから、.goの方が新 らしいはず。これが逆転してるって事は、コンパイルした後でソースが変更さ れたって訳だ。それを検出して再コンパイルするよって提案してきた。makeの 基本が組み込まれている。

件のファイルを確認。

[sakae@arch ~]$ stat /usr/local/share/guile/3.0/system/vm/frame.scm
  File: /usr/local/share/guile/3.0/system/vm/frame.scm
  Size: 18944           Blocks: 40         IO Block: 4096   regular file
Device: 8,3     Inode: 1354801     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2024-05-02 16:08:08.929934147 +0900
Modify: 2024-04-25 15:48:43.149954613 +0900
Change: 2024-04-25 15:48:43.149954613 +0900
 Birth: 2024-04-25 15:48:43.149954613 +0900
[sakae@arch ~]$ stat /usr/lib/guile/3.0/ccache/system/vm/frame.go
  File: /usr/lib/guile/3.0/ccache/system/vm/frame.go
  Size: 201357          Blocks: 400        IO Block: 4096   regular file
Device: 8,3     Inode: 1212781     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2024-05-02 15:28:02.103296488 +0900
Modify: 2023-01-26 07:28:16.000000000 +0900
Change: 2023-01-31 14:11:32.523326346 +0900
 Birth: 2023-01-31 14:11:32.519993013 +0900

確かに再コンパイルを促したいな。それはいいんだけど、4種ある情報のうち の何に注目してるのかねぇ。野次馬根性で調べてみるか。多分ハイオーダーな 関数で見張りをしてると思うんで、検索範囲は、moduleの中ね。

[sakae@arch module]$ fgrep 'newer than compiled' -r .
fgrep: warning: fgrep is obsolescent; using grep -F
./ice-9/boot-9.scm:                       ";;; note: source file ~a\n;;;       newer than compiled ~a\n"

古い人間は困るなあとArchLinuxに小言を言われたな。まあ、そんな事より

(define (fresh-compiled-thunk name scmstat go-file-name)
  ;; Return GO-FILE-NAME after making sure that it contains a freshly
  ;; compiled version of source file NAME with stat SCMSTAT; return #f
  ;; on failure.
  (false-if-exception
   (let ((gostat (and (not %fresh-auto-compile)
                      (stat go-file-name #f))))
     (if (and gostat (more-recent? gostat scmstat))

で、こんな風になってて、最初にinfoを使って、statを調べてみたんだ。どう も道を間違えているぞって気付きバックトレースしたよ。そして正しい道を見 付けた。

(define (more-recent? stat1 stat2)
  ;; Return #t when STAT1 has an mtime greater than that of STAT2.
  (or (> (stat:mtime stat1) (stat:mtime stat2))
      (and (= (stat:mtime stat1) (stat:mtime stat2))
           (>= (stat:mtimensec stat1)
               (stat:mtimensec stat2)))))

Modify-timeで比較してますねぇ。にしても、秒以下まで比べているよ。現代 は、せせこましいなあ。

どうでもいいけど、statで表示される、ModifyとChangeの違いって何だ? オイラーの理解では、Modifyはファイル内容の一部abcをABCに変更する事。Change の方は、abcをabcdeみたいに、サイズが変更を伴う場合と思っていた。

実験して分ったんだけど、Modifyはファイル・コンテンツを変更する事。Changeはファイ ルのメタ情報(例えば、パーミション)の変更みたいだ。

下記はメタ情報の一つである、リンクを変更してみた。

ob$ stat -x hy.scm
  File: "hy.scm"
  Size: 112          FileType: Regular File
  Mode: (0611/-rw---x--x)         Uid: ( 1000/   sakae)  Gid: (    0/   wheel)
Device: 255,0   Inode: 3    Links: 1
Access: Mon May  6 07:22:00 2024
Modify: Mon May  6 06:59:56 2024
Change: Mon May  6 06:59:56 2024
ob$ ln hy.scm lnhy.scm
ob$ stat -x lnhy.scm
  File: "lnhy.scm"
  Size: 112          FileType: Regular File
  Mode: (0611/-rw---x--x)         Uid: ( 1000/   sakae)  Gid: (    0/   wheel)
Device: 255,0   Inode: 3    Links: 2
Access: Mon May  6 07:22:00 2024
Modify: Mon May  6 06:59:56 2024
Change: Mon May  6 08:05:18 2024

長年Unixを使っているけど、トリビアだ。恥かしいぞ。そんな貴方にguileが 愛の手を!

scheme@(guile-user)> ,a stat
(guile): current-dynamic-state  #<procedure current-dynamic-state ()>
 :
(guile): stat:uid       #<procedure stat:uid (f)>
(guile): stat:blocks    #<procedure stat:blocks (f)>
 :

そうよ、大体ねぇーなヘルプと言うかサーチ。そして

scheme@(guile-user)> ,d stat
- Scheme Procedure: stat object [exception_on_error]
     Return an object containing various information about the file
     determined by OBJECT.  OBJECT can be a string containing a file
     name or a port or integer file descriptor which is open on a file
     (in which case `fstat' is used as the underlying system call).

     stat:mtime
          The last modification time for the file.

     stat:ctime
          The last modification time for the attributes of the file.

こんな感じで説明が出てくる。

error but …

コンパイルなんたら、なんてのが出てきたので試してみると、、、

scheme@(guile-user)> (hy)
$1 = 54.34461377199895
scheme@(guile-user)> (compile hy)
ice-9/boot-9.scm:1685:16: In procedure raise-exception:
unhandled constant #<procedure hy ()>

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> ,bt
In system/base/compile.scm:
   352:28 14 (compile _ #:from _ #:to _ #:env _ #:optimization-level ?)
   265:44 13 (_ _ _)
   265:44 12 (_ _ _)
   261:33 11 (_ #<intmap 0-4> #<module (#{ g213}#) 47f9a780>)
In language/cps/optimize.scm:
   130:12 10 (_ _ _)
     88:0  9 (optimize-higher-order-cps _ _)
In language/cps/dce.scm:
   430:33  8 (eliminate-dead-code _)
   114:19  7 (compute-live-code #<intmap 0-4>)
In language/cps/intmap.scm:
    519:6  6 (visit-branch #(#<cps (kfun () 0 4 1)> #<cps (kclau?> ?) ?)
In language/cps/type-checks.scm:
    41:16  5 (elide-type-checks #<intmap 0-4> 0 #<intmap 0-4>)
In language/cps/types.scm:
   1905:6  4 (worklist-fold* #<procedure visit-cont (label typev)> _ _)
  2046:26  3 (visit-cont 2 #<intmap 0-2>)
   390:31  2 (constant-type-entry _)
    385:9  1 (constant-type _)
In ice-9/boot-9.scm:
  1685:16  0 (raise-exception _ #:continuable? _)

こんなエラーになった。使い方間違ってる?

scheme@(guile-user)> (compile-file "hy.scm")
$15 = "/home/sakae/.cache/guile/ccache/3.0-LE-8-4.6/home/sakae/hy.scm.go"

scheme:CPS

それより、どんな具合にコンパイラーが構成されているか確認したい。んだけ ど、上のエラーの一端に、CPSなんてのが出てきてる。ピピピとschemeセンサー が反応。CPSの予習しとけってな。

Scheme:CPS

お気楽 Scheme プログラミング入門 入門辺に継続が出て来るって事は、あた りまえの存在なんだな。

こんなんだったかなあ?

CPS

GNU Guile 英語のWikipediaに面白い解説が出てた。

from NEWS

Changes in 3.0.3 (since 3.0.2)
 ** New baseline compiler

Guile's CPS-based compiler generates good code, but it takes time and
memory to do so.  For users that prioritize speed of compilation over
speed of generated code, Guile now has a new baseline compiler that goes
directly from the high-level Tree-IL to bytecode, skipping CPS and all
of its optimizations.  This compiler is used for `guild compile -O0',
and generally runs around ten times as fast as the CPS compiler.

system/base/compile.scm とか、language/cps.scm あたりからかな。

;;; This is the continuation-passing style (CPS) intermediate language
;;; (IL) for Guile.

なんて具合に、熱く説明されてるなあ。但し冒頭の ;;; が邪魔だな、どうやっ て外す。コメントなんだから、M-; で簡単にアンコメント出来るな。

9.4 Compiling to the Virtual Machine

make with libtool

ArchLinuxでコンパイルを普通にやると、詳細を隠してしまう。これはイカンゼヨ!

こういう場合は、恒例のChatGPTに質問してみる事だな。

Q: libtoolが利用されているMakefileを使ってコンパイルすると 下記の様にコンパイラーに渡るパラメーターの詳細が表示され ません。詳細を表示させるには、どうすれば良いですか?

[sakae@arch guile-3.0.9]$ make
  :
make[3]: Entering directory '/tmp/guile-3.0.9/libguile'
  CC       libguile_3.0_la-hash.lo
  CC       libguile_3.0_la-hashtab.lo

A: make VERBOSE=1 もしVERBOSEオプションが使えない場合、Makefile内で詳細情報を表示するように設定されていない可能性があります。その場合、Makefileの中身を直接確認し、必要な詳細情報の表示方法を見つける必要があります。

しかし、通常はVERBOSEオプションが使えることが多いです。

嘘だったので、自力で調べた。

回避策その1

[sakae@arch guile-3.0.9]$ make V=1
libtool: compile:  gcc -DHAVE_CONFIG_H -DBUILDING_LIBGUILE=1 -I.. -I.. -I../lib -I../lib -iquote. -I../libguile/lightening -I/tmp/guile-3.0.9 -Wall -Wmissing-prototypes -Wpointer-arith -fno-strict-aliasing -fwrapv -fvisibility=hidden -g -O0 -flto -MT libguile_3.0_la-ioext.lo -MD -MP -MF .deps/libguile_3.0_la-ioext.Tpo -c ioext.c -o libguile_3.0_la-ioext.o >/dev/null 2>&1

回避策その2 ドライランだな。

[sakae@arch guile-3.0.9]$ make -n
echo "  CC      " libgnu_la-full-read.lo;/bin/sh ../libtool --silent --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I. -I..   -I/tmp/guile-3.0.9  -fvisibility=hidden -Wno-cast-qual -Wno-conversion -Wno-float-equal -Wno-sign-compare -Wno-undef -Wno-unused-function -Wno-unused-parameter -Wno-float-conversion -Wimplicit-fallthrough -Wno-pedantic -Wno-sign-conversion -Wno-type-limits -Wno-unsuffixed-float-constants -g -O0 -flto -MT libgnu_la-full-read.lo -MD -MP -MF .deps/libgnu_la-full-read.Tpo -c -o libgnu_la-full-read.lo `test -f 'full-read.c' || echo './'`full-read.c

This year's Index

Home