arm64 and riscv64
DEV_BSIZE
前回やったnewfsの中でDISKに書き出すルーチンが有った。ここで使われている DEV_BSIZE
が、ちと見慣れない定義になってた。で、現地調査して、512ってのが判明したんだった。でも何となく釈然としなかった。もう少し調べてみる。
wtfs(daddr_t bno, int size, void *bf) { : n = pwrite(fso, bf, size, (off_t)bno * DEV_BSIZE); }
関係者だけを抜き出した。結果はgdbで調べるので、皆さん得意のprintfも出てこない。手抜きの極みのコードだ。
#include <sys/param.h> off_t val = 1 * DEV_BSIZE; int main() {}
結果は、勿論512になった。ここまでは単純化しただけだな。もう少し知恵を働かせて、マクロの展開までって言う寸止めをした。で、出てきたな、マクローリン展開じゃなくて、普通のマクロ展開が。
ob$ cc -E z.c : off_t val = 1 * (1 << 9); int main() {}
gdbが大人の遊び場さ
(gdb) p 1 << 9 $1 = 512 (gdb) p 1 << 1 $2 = 2 (gdb) p 1 << 2 $3 = 4 (gdb) p 160 << 9 $4 = 81920 (gdb) p 3145727 << 9 $5 = 1610612224
最後の2つは、前回 Q.E.D. ってやった奴だ。
かけ算の変わりにシフトを使ってるんだね。これはオイラーみたいなアホを煙に卷くためのしかけか? いや、そんな事はないだろう。
一つ考えられるのは、かけ算よりシフトの方が速いってのがあるな。
#include <sys/param.h> off_t dmy; int i = 0x12345678; int main() { while( i ) { dmy = i << 9; i--; } }
普通は、forで回すんだろうけど、コードがより簡単になる事を期待したのさ。
qm$ cc -g -O0 z.c qm$ time ./a.out 0m05.26s real 0m02.56s user 0m00.01s system
シフトを止めて、512 を乗算するように変更してみたけど、計算時間は同じかった。おかしいなあ、予想が外れたかと思ってdissassmebleしたよ。そしたら、
0x000016ec <main+28>: mov 0xfffffff8(%ebp),%eax 0x000016ef <main+31>: cmpl $0x0,0x101c(%eax) 0x000016f6 <main+38>: je 0x172d <main+93> 0x000016fc <main+44>: mov 0xfffffff8(%ebp),%eax 0x000016ff <main+47>: mov 0x101c(%eax),%edx 0x00001705 <main+53>: shl $0x9,%edx 0x00001708 <main+56>: mov %edx,%ecx 0x0000170a <main+58>: sar $0x1f,%ecx 0x0000170d <main+61>: mov %edx,0x1050(%eax) 0x00001713 <main+67>: mov %ecx,0x1054(%eax) 0x00001719 <main+73>: mov 0x101c(%eax),%ecx 0x0000171f <main+79>: add $0xffffffff,%ecx 0x00001722 <main+82>: mov %ecx,0x101c(%eax) 0x00001728 <main+88>: jmp 0x16ec <main+28>
かけ算を指定したにも拘わらず、コンパイラーが利口過ぎて、シフト演算に置き換えていたよ。馬鹿なコンパイラー用の対策なのかな。そういう事にしておこう。
それにしても糞石のアドレッシングは、ごつごつしてるな。まあ、そんな今更みたいな事を言っていないで、コンパイラーの裏をかいてみる。大体512って余りに特殊な数だから、コンパイラーがそれを利用してるんだ。いわゆる2のべき乘だから、シフトにうってつけ。
ならばそんな数を避ければ良い。そう、これ以上分解出来無い数なら安心。分解出来る数かどうかを512の近辺で、えいやと探してみる。
ob$ factor 512 512: 2 2 2 2 2 2 2 2 2 ob$ factor 513 513: 3 3 3 19 ob$ factor 503 503: 503 ob$ openssl prime 503 503 is prime
513って合成数だった。503は分解出来無かった。ダメ出しでopensslの判定も受けてみた。この数を使って、テストプログラムを修整。
0x000016ff <main+47>: mov 0x101c(%eax),%ecx 0x00001705 <main+53>: imul $0x1f7,%ecx,%edx 0x0000170b <main+59>: mov %edx,%ecx 0x0000170d <main+61>: sar $0x1f,%ecx
今度は諦めてかけ算を使ってくれたね。
qm$ time ./a.out 0m05.27s real 0m02.61s user 0m00.02s system
結果自体はシフトとほぼ同じになったよ。そんなものなのか。
OpenBSD 7.2 の目玉
いつもはリリースノートなんて眺めないんだけど、ちょいと暇だったので流しよみしてみた。
What's New New/extended platforms:
Added support for Ampere Altra Added support for Apple M2
Ampere Altra なんて聞いた事がないな。調べてみる。
クラウドサーバとエッジサーバに新しい標準を提供するメニーコアArm 「Altra/AltraMAX®」プロセッサ へぇー、ARMな石を集合させました。敵はIntel/amd のサーバー分野。
これを多数積んで、富嶽になりたいんだな。京のsparcはいつの間にか退場してるし。
アプルのインテル止めた、ARMにしたって事で、自前の石バージョンM2とな。
んで、みんななびく訳ね。
arm64
んなら、オイラーもインテル捨ててARMになびくかななんて思っちゃう。そんな折り、 EFIの使用例がないかと探していたら、 OpenBSD 6.5 arm64 (aarch64) on QEMU なんてのに出会った。arm64って、ブルジョアなアプルの石じゃないですか。 駄目元で、もどきをやってみる。
肝はブート用のROMみたい。記事が古いので、サポートされてるかと思ったら、無事に入手出来た。次は記事に習って10Gのメモリーを用意。
ftp http://releases.linaro.org/components/kernel/uefi-linaro/latest/release/qemu64/QEMU_EFI.fd qemu-img create -f qcow2 obsd-arm64.qcow2 10G
そして下記はインストール用のスクリプト。minirooto72.imgは勿論arm用ね。
qemu-system-aarch64 \ -M virt \ -m 512 \ -cpu cortex-a57 \ -bios QEMU_EFI.fd \ -drive file=miniroot72.img,format=raw,id=drive1 \ -drive file=obsd-arm64.qcow2,if=none,id=drive0,format=qcow2 \ -device virtio-blk-device,drive=drive0 \ -nographic
qemu-gaが動いているので、インストール時からPuTTYなコンソールが使える。無事にインストールが終了したら、miniroot72.imgな行は削除。コメントでお茶を濁そうとすると、qemuのmonitorが起動してきちゃうので注意。
下記は、qemu-gaの設定、未来のオイラーのための覚書。
ob$ cat /etc/qemu/qemu-ga.conf [general] method=isa-serial path=/dev/cua00
pkg_scripts=qemu_ga
を /etc/rc.conf.local に登録して、dialerグループに加入の事。
気になるのはARMな石が糞石かどうかだ。前回のチェックプログラムを走らせてみる。gdbは入っていないので注意。
arm$ lldb a.out (lldb) target create "a.out" Current executable set to '/tmp/a.out' (aarch64). (lldb) b main Breakpoint 1: where = a.out`main at tst.c:3:13, address = 0x00000000000108b0 (lldb) r Process 83150 launched: '/tmp/a.out' (aarch64) Process 83150 stopped * thread #1, stop reason = breakpoint 1.1 frame #0: 0x0000001684d508b0 a.out`main at tst.c:3:13 1 int lo = 0x12345678; 2 int hi = 0xfedcba98; -> 3 int main() {} ^ (lldb) p/x lo (int) $0 = 0x12345678 (lldb) x/8b &lo 0x1684d70b40: 0x78 0x56 0x34 0x12 0x98 0xba 0xdc 0xfe (lldb) x/10i main -> 0x1684d508b0: 0x9000008f adrp x15, 16 0x1684d508b4: 0xf944d5ef ldr x15, [x15, #0x9a8] 0x1684d508b8: 0xca1e01ef eor x15, x15, x30 0x1684d508bc: 0x2a1f03e0 mov w0, wzr 0x1684d508c0: 0xca1e01ef eor x15, x15, x30 0x1684d508c4: 0x90000089 adrp x9, 16 0x1684d508c8: 0xf944d529 ldr x9, [x9, #0x9a8] 0x1684d508cc: 0xeb0901ef subs x15, x15, x9 0x1684d508d0: 0xb400004f cbz x15, 0x1684d508d8 ; <+40> at tst.c:3:13 0x1684d508d4: 0xd4200020 brk #0x1
やっぱり糞石に習っているな。インストラクションセットはかっこいいのに残念だ脳。
arm$ uname -a OpenBSD arm.my.domain 7.2 GENERIC#1766 arm64
一応、魚拓をば。ホストがX86なOpenBSD 7.1 なのに、ゲストが先を行っているのはどうよ。
mul or lsl
arm$ time ./a.out 0m03.46s real 0m03.39s user 0m00.05s system
503を使ってかけ算すると
arm$ time ./a.out 0m03.11s real 0m02.99s user 0m00.07s system
若干速くなった気がする。統計的に有意な差があるって事でいいかな。
lldbでコード確認するのもあれなんで、コンパイラーにアセンブルコードを出力させてみる。
arm$ cc -O0 -S z.c arm$ less z.s : mov w10, #503 mul w10, w8, w10
うん、確かに乗算命令が使われているね。
riscv64
ARMもいいけど、没個性と思ったり。。。少し先端へも行ってみたいぞと。
Setup an OpenBSD RISCV64 VM in QEMU
Let's install OpenBSD/riscv64 on QEMU (view by w3m)
setup
まずはrootさんのお仕事
ob# pkg_add u-boot-riscv64 opensbi quirks-5.5 signed on 2022-10-18T12:24:43Z u-boot-riscv64-2021.10p3: ok opensbi-0.9p0: ok
次はインストール用のDISKを用意。インストールが完了すると、中身がそこに入る。
ob$ ftp https://ftp.jaist.ac.jp/pub/OpenBSD/7.2/riscv64/miniroot72.img ob$ qemu-img create disk.raw 4G ob$ dd conv=notrunc if=miniroot72.img of=disk.raw
よって、起動スクリプトは、install時もboot時も、同じ物を使う。コンソールがシリアルに流れてくるので、qemu-gaは必須になる。
qemu-system-riscv64 \ -machine virt \ -nographic \ -m 512M \ -smp 2 \ -bios /usr/local/share/opensbi/generic/fw_jump.bin \ -kernel /usr/local/share/u-boot/qemu-riscv64_smode/u-boot.bin \ -drive file=disk.raw,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 \ -netdev user,id=net0,ipv6=off -device virtio-net-device,netdev=net0
run
riscv$ uname -a OpenBSD riscv.my.domain 7.2 GENERIC.MP#188 riscv64
まだ開発が始まったばかりで、コンパイルの実行回数も若々しいな。
走らせてから、真先に確認したよ。これって相変わず前回の流れを追悼してるな。 こういう底を眺めておくってとっても大事と思うぞ。
riscv$ fdisk sd0 Disk: sd0 geometry: 522/255/63 [8388608 Sectors] Offset: 0 Signature: 0xAA55 Starting Ending LBA Info: #: id C H S - C H S [ start: size ] ------------------------------------------------------------------------------- *0: 0C 2 10 9 - 4 20 16 [ 32768: 32768 ] Win95 FAT32L 1: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 2: 00 0 0 0 - 0 0 0 [ 0: 0 ] unused 3: A6 4 20 17 - 522 42 32 [ 65536: 8323072 ] OpenBSD
ふむ、fdiskはちゃんと設定しましょうってか、マイクロソフトの影がちらついている気がするのは、オイラーだけ? Windowsの起動を支える根底部分だな。
riscv$ disklabel sd0 # /dev/rsd0c: type: SCSI disk: SCSI disk label: Block Device duid: 781d4a4515da21ce flags: bytes/sector: 512 sectors/track: 63 tracks/cylinder: 255 sectors/cylinder: 16065 cylinders: 522 total sectors: 8388608 boundstart: 65536 boundend: 8388608 16 partitions: # size offset fstype [fsize bsize cpg] a: 1805088 65536 4.2BSD 2048 16384 12960 # / b: 497250 1870624 swap # none c: 8388608 0 unused d: 5262944 2367904 4.2BSD 2048 16384 12960 # /usr e: 757760 7630848 4.2BSD 2048 16384 5920 # /home i: 32768 32768 MSDOS
もうIDEは古くてSCSIが復権してるのね。大体IDEは、パソコンのHDDを安価に使うための物なんだな。昔のワークステーションで使われてたあのぶっといケーブルが、シリアル仕様になって復権したんか。USBがこれに加担してるとな。
mul or lsl
残念ながら、リトルエンディアンだったけど、めげないで、最初のサンプルを走らせてみた。
riscv$ time ./a.out 0m02.76s real 0m02.51s user 0m00.18s system
lldbは無い。objdumpは逆アセはまだサポートしてない。万事休すかと思われたが、pkgにgdbが用意されてた。pythonを引き連れてくるんだけど、メモリー不足で一悶着。1Gを割当てたら、起動途中でqemuがパニくるし、768Mに設定して事泣きを得た。
0x0000000000001806 <+18>: auipc a0,0x2 0x000000000000180a <+22>: addi a0,a0,642 # 0x3a88 <i> 0x000000000000180e <+26>: lw a0,0(a0) 0x0000000000001810 <+28>: li a1,0 0x0000000000001812 <+30>: beq a0,a1,0x183c <main+72> 0x0000000000001816 <+34>: j 0x181a <main+38> 0x000000000000181a <+38>: auipc a1,0x2 0x000000000000181e <+42>: addi a1,a1,622 # 0x3a88 <i> 0x0000000000001822 <+46>: lw a0,0(a1) 0x0000000000001824 <+48>: slliw a0,a0,0x9 0x0000000000001828 <+52>: auipc a2,0x2 0x000000000000182c <+56>: addi a2,a2,632 # 0x3aa0 <dmy> 0x0000000000001830 <+60>: sd a0,0(a2) 0x0000000000001832 <+62>: lw a0,0(a1) 0x0000000000001834 <+64>: addi a0,a0,-1 0x0000000000001836 <+66>: sw a0,0(a1) 0x0000000000001838 <+68>: j 0x1806 <main+18>
503を使ってかけ算を強制。
riscv$ time ./a.out 0m02.62s real 0m02.45s user 0m00.12s system
うーん、微妙な差だな。折角のgdbだけど、アセンブラソースを眺めるのもいいか。オイラーにとってgdbは篤い戦友だけど、たまには一人になりたい事も有るのさ。
.LBB0_1: # =>This Inner Loop Header: Depth=1 .LBB0_4: # in Loop: Header=BB0_1 Depth=1 # Label of block must be emitted auipc a0, %pcrel_hi(i) addi a0, a0, %pcrel_lo(.LBB0_4) lw a0, 0(a0) mv a1, zero beq a0, a1, .LBB0_3 j .LBB0_2 .LBB0_2: # in Loop: Header=BB0_1 Depth=1 .LBB0_5: # in Loop: Header=BB0_1 Depth=1 # Label of block must be emitted auipc a1, %pcrel_hi(i) addi a1, a1, %pcrel_lo(.LBB0_5) lw a0, 0(a1) addi a2, zero, 503 mulw a0, a0, a2 .LBB0_6: # in Loop: Header=BB0_1 Depth=1 # Label of block must be emitted auipc a2, %pcrel_hi(dmy) addi a2, a2, %pcrel_lo(.LBB0_6) sd a0, 0(a2) lw a0, 0(a1) addi a0, a0, -1 sw a0, 0(a1) j .LBB0_1 : i: .word 305419896 # 0x12345678 .size i, 4 dmy: .quad 0 # 0x0 .size dmy, 8