読者です 読者をやめる 読者になる 読者になる

レガシーコード生産ガイド

私に教えられることなら

ForthでC関数ワード定義とSSEレジスタメモ

forth asm

そろそろ全くわかってない浮動小数点数をどうにかしよう、と思い立って浮動小数点数スタックをRFR64に実装した。

前後するけど、その前にRFR64でもC関数をdlopen経由で使えるようにしていたので、浮動小数点数の表示をprintfに任せてみることにした。

C関数ワード定義

gforthやSwiftForthのCライブラリ・関数読み込みを意識して、C関数を楽に使えるようなワードを定義してみた。

example.fsの下の方にある。

c-library /lib/x86_64-linux-gnu/libc.so.6
  name: printf  sym: &printf
                with: 1 as: printf1
                with: 2 as: printf2
  name: fflush  with: 1 as: fflush
  name: puts    with: 1 as: cputs
end

こんな風に、name:でC関数名を、with:で引数の数を、as:でforthワード名を定義して、その関数を呼び出すワード、をコンパイルして使える。Forthは引数の数を構文で指定できないので、こういうふうに引数の数別に定義した。ちょっと苦しいところ。

ついでに、あとで浮動小数点数を使うときのために、関数アドレスを返すワードを定義するsym:も追加した。

今実験するためにこの構文を作ったけど、gforthのように引数をスタックエフェクトで書いて、それをパースして定義できるようにしたほうが便利そうだ。

なのでもう使わないと思うけど、まあForthでのメタプログラミングの一例ということで。以下のようにcurses使って遊んだりしてた。

c-library libncurses.so.5
  name: initscr   with: 0  as: initscr
  name: endwin    with: 0  as: endwin
  name: cbreak    with: 0  as: cbreak
  name: nocbreak  with: 0  as: nocbreak
  name: echo      with: 0  as: echo
  name: noecho    with: 0  as: noecho
  name: getch     with: 0  as: getch
  name: addch     with: 1  as: addch
  name: addstr    with: 1  as: addstr
  name: move      with: 2  as: move
  name: flash     with: 0  as: flash
  name: clear     with: 0  as: clear
end

: curses-test
   initscr
   noecho cbreak
   clear
   0 0 move  s" press three keys." >cstr.dict  addstr
   1 2 move  getch addch
   2 4 move  getch addch
   3 6 move  getch addch
   4 0 move  s" press any key." >cstr.dict addstr
   getch drop flash
   echo nocbreak
   endwin ;

SSEレジスタを使った場合のC関数呼び出しについて

xmmに何か入れて、raxに使用SSEレジスタ数を入れてC言語の関数を呼び出すときは、rspを16バイトにアライメントする必要があった。

stackoverflow.com

そのあとrspが変更されたまま返ってくる?ので、rbpなどに退避させておく必要がある。

xmmを使わない場合もアライメントする必要があるのかはわからない。バグ解決前に試した所問題無かったので必要無いかもしれない。

今回の実装ではrbxをTOSとして、rbpをスタックポインタとして使っているので、上記SOの回答を参考にして、以下のようなマクロで対処した。

%macro CALL_TOS 0
    ; RBP,RSPの保存とアライメント
    push rbp
    mov  rbp, rsp
    and  rsp, -16

    call rbx

    ; RSP, RBPの復帰
    mov  rsp, rbp
    pop  rbp

    ; 戻り値
    mov  rbx, rax
%endmacro

rbpは元に戻されるはずなのでそれを期待してるけど、何かおかしくなったらそれが原因かもしれない。

おわりに

浮動小数点数自体はいろんな記事読んでなんとなく掴めてきたけど、x86のFPU?周りがさっぱりわからない……意を決してインテルのマニュアルに目を通す時が来たかもしれない。

広告を非表示にする