emcc

cq by scheme

前回やったQを求めるやつ。rubyで書いたけどやっぱりschemeでも書いておくかと思った。 rubyに有ったComplexは、gaucheに有るのかと調べたらmake-rectangularって名前で提供してた。

rubyの時のwhileのtest句は、schemeでそのまま変換するとごちゃごちゃするのが眼に見えているので、valって関数に括り出した。この関数はcqの中に閉じているので、外からは見えない。

後はruby語を素直にschemeに翻訳しただけ。何の気負いもない。

;; calc Q
(define (cq l c r)
  (define (val xl xc r f)
    (abs (/ (make-rectangular r
                              (- (* xl f) (/ 1 (* xc f)))))))

  (let* ([step 0.01e6]
         [fc (/ (* 6.28 (sqrt (* l c))))]
         [th (/ 0.7 r)]
         [xl (* 6.28 l)]
         [xc (* 6.28 c)]
         [fh (+ fc step)]
         [fl (- fc step)] )
    (while (> (val xl xc r fh) th) (set! fh (+ fh step)))
    (while (> (val xl xc r fl) th) (set! fl (- fl step)))
    (/ fc (- fh fl)) ) )
sakae@pen:/tmp$ gosh -l cq.scm
gosh> (cq 2e-6 100e-12 1.0)
112.59662120804897
gosh> (cq 1e-6 200e-12 1.0)
62.553678448916095

勿論結果はrubyのそれと一緒だ。喜んで今度はguileで試してみる。

sakae@pen:/tmp$ guile -l cq.scm
GNU Guile 2.2.4
 :
scheme@(guile-user)> (cq 1e-6 200e-12 1.0)
ERROR: In procedure abs:
In procedure abs: Wrong type argument in position 1: 0.9844832009064911-0.12359622987535078i

あらエラーになった。

scheme@(guile-user)> (abs 3+4i)
ERROR: In procedure abs:
In procedure abs: Wrong type argument in position 1: 3.0+4.0i

Entering a new prompt.  Type `,bt' for a backtrace or `,q' to continue.
scheme@(guile-user) [1]> (magnitude 3+4i)
$1 = 5.0

magunitudeが複素数対応なのか。

sakae@pen:/tmp$ scheme
Chez Scheme Version 9.5.3
Copyright 1984-2019 Cisco Systems, Inc.

> (abs 3+4i)
Exception in abs: 3+4i is not a real number
Type (debug) to enter the debugger.
> (magnitude 3+4i)
5

Chezもguileと同様。って事はgaucheは過保護なのかな?

-- Function: abs z
    [R7RS+] 実数のZに対しては、その絶対値を返します。 複素数のZに対して
    は、そのmagnitudeを返します。 複素数を扱うのはGaucheの拡張です。
         (abs -1)   ⇒ 1
         (abs -1.0) ⇒ 1.0
         (abs 1+i)  ⇒ 1.4142135623731

やっぱり、気配りと言うか、おもてなしの心だな。

sakae@pen:/tmp$ scheme
Chez Scheme Version 9.5.3
Copyright 1984-2019 Cisco Systems, Inc.

> (load "./cq.scm")
> (cq 1e-6 200e-12 1.0)
Exception: variable while is not bound
Type (debug) to enter the debugger.

ChezはWhileも無い、硬派なschemeなんだな。これはこれで捨てがたい処理系だ。

(define-syntax while
  (syntax-rules ()
    ((_ pred b1 ...)
     (let loop () (when pred b1 ... (loop))))))

こんなのを追加して、命令風言語に仕立て上げる。

> (load "./cq.ss")
> (cq 1e-6 200e-12 1.0)
62.553678448916095

emcc

以前から目を付けていた、 Emscripten をやってみるか。こういう新種は32Bitのリナで試すんだけど、やってみたら32Bitお断りって言われた。リナ自身も32Bitをサポートしてるやつは少なくなってしまったから、自然にそうなるのか。寂しいのう。

install

Download and install

sakae@pen:~/src$ git clone https://github.com/emscripten-core/emsdk.git
sakae@pen:~/src$ cd emsdk/
sakae@pen:~/src/emsdk$ git pull
sakae@pen:~/src/emsdk$ ./emsdk install latest
  :
sakae@pen:~/src/emsdk$ ./emsdk activate latest
Setting the following tools as active:
   node-12.18.1-64bit
   releases-upstream-e7e39da9c81faecd9ecf44065cee864d76e4e34d-64bit

Next steps:
- To conveniently access emsdk tools from the command line,
  consider adding the following directories to your PATH:
    /home/sakae/src/emsdk
    /home/sakae/src/emsdk/node/12.18.1_64bit/bin
    /home/sakae/src/emsdk/upstream/emscripten
- This can be done for the current shell by running:
    source "/home/sakae/src/emsdk/emsdk_env.sh"
- Configure emsdk in your bash profile by running:
    echo 'source "/home/sakae/src/emsdk/emsdk_env.sh"' >> $HOME/.bash_profile
sakae@pen:~/src/emsdk$ source ./emsdk_env.sh
Adding directories to PATH:
PATH += /home/sakae/src/emsdk
PATH += /home/sakae/src/emsdk/upstream/emscripten
PATH += /home/sakae/src/emsdk/node/12.18.1_64bit/bin

Setting environment variables:
EMSDK = /home/sakae/src/emsdk
EM_CONFIG = /home/sakae/src/emsdk/.emscripten
EM_CACHE = /home/sakae/src/emsdk/upstream/emscripten/cache
EMSDK_NODE = /home/sakae/src/emsdk/node/12.18.1_64bit/bin/node

説明にも有るけど、環境設定の為に .bash_profile を作成したよ。そしたら.bashrcより優先度が高いものだから、.bashrcが無視されちゃったぞ。使う時だけ、環境を作るか。

about

どんな物なのか少し探検する。

sakae@pen:~/src$ du -sh emsdk/
682M    emsdk/

大掛かりなシステムだ箏。して中身は? node clang が根底にあるな。clang一式を入れているのは、flake8とか言うまとめ役になるのか。後はこれらをアップデートするメンテナンス系。 面倒嫌いって人用にdocker環境も内蔵。

核になるemccは、これらを酷使するんで、糊としてpythonが使われている。

sakae@pen:/tmp$ echo $PATH | tr ':' '\n'
/home/sakae/src/emsdk
/home/sakae/src/emsdk/upstream/emscripten
/home/sakae/src/emsdk/node/12.18.1_64bit/bin
 :

公開されてる場所はこんな感じ。

sakae@pen:/tmp$ node
Welcome to Node.js v12.18.1.
Type ".help" for more information.
>

もったいない病が発症したんで、隠れているclangの場所を追加

sakae@pen:/tmp$ PATH=/home/sakae/src/emsdk/upstream/bin:$PATH
sakae@pen:/tmp$ clang -v
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 55fa315b0352b63454206600d6803fafacb42d5e)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/sakae/src/emsdk/upstream/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Candidate multilib: .;@m64
Selected multilib: .;@m64

こんな具合だから、図体が大きいんだな。正体みたり、、だ。

マニュアルは、emcc –help で見るのがお約束だ。古い人には馴染まんなあ。

sakae@pen:/tmp$ emcc -v
emcc (Emscripten gcc/clang-like replacement + linker emulating GNU ld) 1.39.20
clang version 12.0.0 (/b/s/w/ir/cache/git/chromium.googlesource.com-external-github.com-llvm-llvm--project 55fa315b0352b63454206600d6803fafacb42d5e)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/sakae/src/emsdk/upstream/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Candidate multilib: .;@m64
Selected multilib: .;@m64
shared:INFO: (Emscripten: Running sanity checks)

clangにpythonで一皮かぶせてemccって名前のコマンドを作っているとな。

how to use

emccのマニュアルを見ていると、WebAssembly なんて言うオイラーに取っては微妙な名前が出て来る。で、ぐぐる辞書を引いてみる。

WebAssembly から C/C++からWebAssemblyにコンパイルする を試してみるか。

try

sakae@pen:/tmp/z$ cat >hoge.c
#include <stdio.h>

int main(int argc, char ** argv) {
  printf("Hello World\n");
}
sakae@pen:/tmp/z$ emcc hoge.c -s WASM=1 -o hello.html
sakae@pen:/tmp/z$ ls -ltr
total 244
-rw-r--r-- 1 sakae sakae     84 Jul 29 07:54 hoge.c
-rw-r--r-- 1 sakae sakae  21738 Jul 29 07:54 hello.wasm
-rw-r--r-- 1 sakae sakae 112060 Jul 29 07:54 hello.js
-rw-r--r-- 1 sakae sakae 102675 Jul 29 07:54 hello.html

server

資料によるとwasmを含むやつは、Webサーバーから取得しないと機能しないらしい。 apacheサーバーを立てる? そんな面倒はしたくない。こういう時はpythonをこき使うのが正しい利用法

alias server='python3 -m http.server 8080'

こんなのを起動

akae@pen:/tmp/z$ server
Serving HTTP on 0.0.0.0 port 8080 (http://0.0.0.0:8080/) ...
127.0.0.1 - - [29/Jul/2020 07:56:17] "GET /hello.html HTTP/1.1" 200 -
127.0.0.1 - - [29/Jul/2020 07:56:19] "GET /hello.js HTTP/1.1" 200 -
127.0.0.1 - - [29/Jul/2020 07:56:19] code 404, message File not found
127.0.0.1 - - [29/Jul/2020 07:56:19] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [29/Jul/2020 07:56:19] "GET /hello.wasm HTTP/1.1" 200 -

そしてfirefoxからアクセス。黒いエリアが2つ出て来た。そして下側のエリアに結果が出て来た。生成したファイル類を余す所なく読み込んでいる。

ダメ元でw3mから読み込んでみる。

sakae@pen:/tmp$ w3m http://localhost:8080/hello.html
image/svg+xml
Downloading...
[ ]Resize canvas [*]Lock/hide mouse pointer     [Fullscreen]
[                    ]
[                    ]
:
[                    ]
[                    ]

読み込むのは当然htmlファイルだけだった。でも、上記のような結果になった。firefoxでは、 テキストエリアに結果が表示されるのね。jsだかwasmの恩恵を受けてね。

実践

そもそもemccなんてのに興味を持ったのは、KiwiSDRがらみでcsdrのMakefileを見ている時だった。emccって、C語(あるいはCフラフラ語)で書かれたコードをjavascript(or wasm)に変換して、Webに公開しましょってやつだ。

javascriptに落ちこぼれた旧世界のC語の住人を救済するためのものだな。js世界に背を向けて生きる人に最適(って、理解で宜しいか?)。3分でわかる WebAssembly を見ると、オイラーの見解と一致した。

早速csdr/Makefileの該当部分を駆動してみるか。

make emcc

sakae@pen:/tmp/csdr$ make emcc
emcc -O3 -Isdr.js/fftw-3.3.3/api -Lsdr.js/fftw-3.3.3/emscripten-lib -o sdr.js/sdrjs-compiled.js fft_fftw.c libcsdr_wrapper.c -s TOTAL_MEMORY=67108864 -DLIBCSDR_GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`"
In file included from fft_fftw.c:3:
./fft_fftw.h:7:10: error: 'fftw3.h' file not found with <angled> include; use "quotes" instead
#include <fftw3.h>
         ^~~~~~~~~
         "fftw3.h"
1 error generated.

おかしい、ヘッダーファイルが無いなんて。気を取り直して上記のヘッダーをちゃんと読むようにしてあげる。そしたら今度は、lfftw3が無いってエラーになった。

エラーばかりに注目してて、-I… とか -L… に注目出来なかったのがそもそもの敗因。それよりREADME.mdを熟読しなかったのが最大の失敗だな、と後で反省しました。

これはもう、根本的なミスがあるな。Makefileを見直し。そしたら、

emcc-get-deps:
        echo "getting and compiling fftw3 with emscripten..."
        cd sdr.js; \
        wget http://fftw.org/$(FFTW_PACKAGE).tar.gz; \
        tar -xvf $(FFTW_PACKAGE).tar.gz; \
        rm $(FFTW_PACKAGE).tar.gz; \
        cd $(FFTW_PACKAGE); \
        emconfigure ./configure --enable-float --disable-fortran --prefix=`pwd`/emscripten-install --libdir=`pwd`/emscripten-lib; \
        emmake make; \
        emmake make install

こんなのを発見! 下準備をしておけって事だな。 emcc系のツールチェーンを使ってfftw3をコンパイルしてる。そうか、emccで扱えるようにライブラリーを作るのね。今コンパイルしてるんだけど、結構fftw3って大物なのね。ああ、出来上がった。

sakae@pen:/tmp/csdr/sdr.js/fftw-3.3.3$ file a.out
a.out: a /home/sakae/src/emsdk/node/12.18.1_64bit/bin/node script, ASCII text executable, with very long lines
sakae@pen:/tmp/csdr/sdr.js/fftw-3.3.3$ ls -l emscripten-lib/
total 916
-rw-r--r-- 1 sakae sakae 933808 Jul 31 07:42 libfftw3f.a
-rwxr-xr-x 1 sakae sakae    920 Jul 31 07:42 libfftw3f.la
drwxr-xr-x 2 sakae sakae     60 Jul 31 07:42 pkgconfig

お約束でa.outが出来上がって、ライブラリーも作成されてた。これでいいはずなんで、再度コンパイルに挑戦。

sakae@pen:/tmp/csdr$ make emcc
emcc -O3 -Isdr.js/fftw-3.3.3/api -Lsdr.js/fftw-3.3.3/emscripten-lib -o sdr.js/sd
rjs-compiled.js fft_fftw.c libcsdr_wrapper.c -s TOTAL_MEMORY=67108864 -DLIBCSDR_
GPL -DUSE_IMA_ADPCM -DUSE_FFTW -lfftw3f -s EXPORTED_FUNCTIONS="`python sdr.js/exported_functions.py`"
 :
6 warnings generated.
cat sdr.js/sdrjs-header.js sdr.js/sdrjs-compiled.js sdr.js/sdrjs-footer.js > sdr.js/sdr.js

多少の警告は出たけど、コンパイル成功。sdrjs-compiled.jsが成果物なんだな。それにヘッダーファイルとフッターファイルを加えて、最終成果物のsdr.jsにするとな。

sakae@pen:/tmp/csdr/sdr.js$ ls -ltr
total 124
-rw-r--r--  1 sakae sakae  1859 Jul 21 14:48 sdrjs-test.html
-rw-r--r--  1 sakae sakae  1031 Jul 21 14:48 sdrjs-header.js
-rw-r--r--  1 sakae sakae  2611 Jul 21 14:48 exported_functions.py
-rw-r--r--  1 sakae sakae 10735 Jul 21 14:48 sdrjs-footer.js
drwxr-xr-x 20 sakae sakae  1120 Jul 31 07:42 fftw-3.3.3
-rw-r--r--  1 sakae sakae 38721 Jul 31 07:47 sdrjs-compiled.wasm
-rw-r--r--  1 sakae sakae 21703 Jul 31 07:47 sdrjs-compiled.js
-rw-r--r--  1 sakae sakae 33469 Jul 31 07:47 sdr.js

最終成果物を見ると、いやがらせの密になったjsが挟まっていた。綺麗にして見たいな。

Makefileに綺麗にするターゲットが有ったぞ。

js-beautify

高須クリニックじゃなくて、js-beautifyは何処に有るの?

debian:tmp$ apt-file search bin/js-beautify
jsbeautifier: /usr/bin/js-beautify
node-js-beautify: /usr/lib/nodejs/js-beautify/js/bin/js-beautify.js

取り合えず入れてあげた。で、整形手術を実行。

sakae@pen:/tmp/csdr$ make emcc-beautify
bash -c 'type js-beautify >/dev/null 2>&1; if [ $? -eq 0 ]; then js-beautify sdr.js/sdr.js >sdr.js/sdr.js.beautiful; mv sdr.js/sdr.js.beautiful sdr.js/sdr.js; fi'

成果物だけ整形するなら

sakae@pen:/tmp/csdr/sdr.js$ js-beautify sdrjs-compiled.js > zzz.js

disる

wasmを解釈する機械と言うか仮想マシンは、最近のブラウザーでは標準装備されてる。また、javascriptを解釈するインタプリタもブラウザー内に昔から(ネスケの頃から)ある。

大変な世の中だ。猫も杓子も仮想マシンなんて言ってるから最近のインテルは元気が無い。アプルは電気を喰わないCPUに移行するというしね。

このブラウザーに内蔵する仮想マシンの規格はM$とアプルとググルが決めているそうな。そこにはインテルは入っていない。ハードなCPUよりブラウザーの方がリッチだからね。入力、出力、そして仮想マシンで演算力があれば、これはもう、コンピュータですから。

一世を風靡したJavaもボラクル主導になって、みんな躊躇してる。落ち目になる事目に見えているな。これからは土台をブラウザーにしたアプリが主導権を握るのね。

で、仮想マシンならそれ用のコードが有るはず。逆アセンブラが用意されてるので上でコンパイルして出来たバイナリーを人目に晒してみる。

sakae@pen:/tmp$ which wasm-dis
/home/sakae/src/emsdk/upstream/bin/wasm-dis
sakae@pen:/tmp$ wasm-dis hello.wasm
(module
 (type $i32_=>_i32 (func (param i32) (result i32)))
 (type $i32_i32_i32_=>_i32 (func (param i32 i32 i32) (result i32)))
 (type $i32_=>_none (func (param i32)))
  :

こんなlisp風の言語になった。昔からlisp系をかじっていて良かったなと思う瞬間です。


This year's Index

Home