2025-12-13 3:10:30
諏訪子
assembly
low-level

【アセンブリ言語】x86-64 vs AArch64 vs RISC-V vs Power ISA

アセンブリ言語について投稿する度に、あたしは意図的にx86-64アセンブリを選んでいます。
其れは其れが一番簡単だからではなく、殆どの人がx86-64マシンを持っているからです。
詰り、より多くの人に実践的だからです。
然し最近AArch64がどんどん人気になってきています。
特にMacユーザーやRaspberry Piユーザー、そしてCopilot+PCやChromebook(どちらも AArch64 系)を使っている人達の間でそうです。
更に、Nintendo Switch / Switch 2向けにゲームを作る人はAArch64アセンブリを知っていた方が圧倒的に役に立ちます。
一方、PlayStation 5の開発者にとってはx86-64アセンブリの方が有用です。

加えて、RISC-Vと最近ではPower ISAがオープンなISAとしてホビースト界隈で急速に広がっています。
其処で本記事では、此れらの違いについて解説します。

主な ISA の現状

歴史的には無数のISAが存在しました。
1980~1990年代は正に「ISAの西部開拓時代」で、2000~2010年代にはIntel(x86)とARMに市場が集約されました。
然し2020年代に入り、再びISA間の競争が活発化している様に見えます。

現在の主流は以下の3つです:

  • x86-64
  • AArch64
  • RISC-V

更にPower ISAも2019年に完全オープン化され、極めてニッチながらLinuxベースのSoCやSBCを自作するプロジェクトで徐々に採用され始めています。但し現時点では非常にニッチで、実際に触るにはQEMU+クロスコンパイルが保母唯一の手段です(或いはPower Mac G5が未だ燃えずに残っていれば其れを使えるか、IBMでAIXサーバーを保守している人なら)。

MIPS64も入れようか迷いましたが、今はルーターくらいでしか使われておらず、其処でも保母死に体です。LoongArch(MIPS64の精神的后継)もありますが、中国以外では入手が極めて困難です。

何故複数のISAを学ぶのか

64ビット時代に入ってからのISA間の違いは確かに存在しますが、8ビット・16ビット・32ビット時代に比べれば劇的とは言えません。
必須ではありませんが、複数のISAを知っていると明確なメリットがあります。

  • 1つでも知っていれば出来る人として目立ちますが、複数知っていると企業にとって替えのきかない人材に成ります。
  • 例:PS5とSwitch 2向けにクロスプラットフォームゲームを作るスタジオで働く場合、x86-64とAArch64の両方のアセンブリ言語が書ければ、C++しか書けない人より圧倒的に価値が高いです。
  • Microsoftで次世代Windowsを開発しているなら・・・(まあ最近はAIに置き換えられる方向らしいですが)
  • SBC(シングルボードコンピュータ)を開発する企業なら、AArch64とRISC-Vの両方が分かれば、片方しか知らない人より大きくリードできます。

勿論、6502・Z80・SuperH・68kといった超古典ISAを学び、自作8ビット/16ビットゲームを書くのも楽しいです。
但し其れらは現代のアセンブリ言語とはか成り感覚が違います(いずれ別記事で取り上げるかもしん)。

以下に、シンプルな「こんにちは、世界」プログラムを4つのISAで書いてみます。
普段はFreeBSD向けに書いていますが、今回は統一の為Linux向けにします。

x86-64

section .rodata
  msg db "こんにちは、世界!", 10, 0
  msgLen equ $-msg

section .text
  global _start

_start:
  mov rax, 1
  mov rbx, 1
  lea rsi, msg
  mov rdx, msgLen
  syscall

  mov rax, 60
  mov rbx, 0
  syscall

$ nasm -f elf64 main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!

注意:アセンブラ言語によって文法が異成ります。

  • NASM(ネットワイド・アセンブラー)→ Intel構文
  • FASM(フラット・アセンブラー)→ Intel構文(但し細部が違う)
  • GAS(GNUアセンブラー)→ AT&T構文
  • MASM(マイクロソフト・アセンブラー)→ Microsoft独自構文

チームで作業する場合は、必ず1つのアセンブラーを決めて統一しましょう。
他のアーキテクチャでは基本的にGAS中心なので、此の問題はほぼ発生しません。

AArch64

.section .rodata
msg:
  .asciz "こんにちは、世界!\n"
  msgLen = .-msg

.section .text
.global _start

_start:
  mov x8, #64
  mov x0, #1
  adr x1, msg
  mov x2, msgLen
  svc #0

  mov x8, #93
  mov x0, #0
  svc #0

$ as main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!

RISC-V

.data
msg:
  .asciz "こんにちは、世界!"
  msgLen = .-msg

.text
.global _start

_start:
  li a7, 64
  li a0, 1
  la a1, msg
  li a2, msgLen
  ecall

  li a7, 93
  li a0, 0
  ecall

$ as main.s -o main.o
$ ld main.o -o main
$ ./main
こんにちは、世界!

Power ISA

.section .rodata
msg:
  .asciz "こんにちは、世界!\n"
  msgLen = .-msg

.section .text
  .globl _start

_start:
  ld 2, 24(12)
  li 0, 4
  li 5, 1
  ld 3, msg@toc(2)
  li 4, msgLen
  sc

  li 0, 1
  li 3, 0
  sc

$ powerpc64-linux-gnu-as main.s -o main.o
$ powerpc64-linux-gnu-ld main.o -o main
$ qemu-ppc64-static ./main

残念ながら此方は動作確認が取れませんでした。
何か足りない様です・・・

然し見ての通り、4つ全てが非常に似ています。
Power ISAだけは正しいTOC(目次ポインタ)を復元するために+1サイクル必要です。
此れが1990年代にPowerを強力にした特徴ですが、今でも残っています。

其れ以外は基本的に同じ流れです:

  1. sys_writeの番号をレジスタにロード
  2. fd = 1stdout)をロード
  3. メッセージ文字列のアドレスをロード
  4. 文字列の長さをロード
  5. syscall発行
  6. sys_exitの番号をロード
  7. 終了コード0をロードしてsyscall

又、RISC系の3つでは.asciz(NULL終端ASCII文字列)を使っているのに対し、x86-64では最後に, 0を付けてNULL終端にしている点も注目して下さい。

だからこそ「1つのISAのアセンブリ言語を覚えれば、他のISAも直ぐ覚えられる」と言われるのです。
此れは正に「C言語を覚えればC++、C#、Java、Go、Zig等は直ぐ覚えられる」「Pythonが分かればシェルスクリプトやRubyも直ぐ書ける」に近い感覚です。

マルチISA対応のアセンブリ言語はどう書くのか?

短い答え:書けません。
もう少し優しい短い答え:C言語で書きましょう。

本質的に、アセンブリ言語を書くという事は「特定のISAをターゲットにする」事です。
更にOSに余っても違いが大きく、Windows用とUnix系用、ベアメタル用は全く別物です。
ベアメタルで動かしたいなら、事実上自分でOSを書く事に成ります。

以上