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

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

This year's Index

Home