mcpField
A
assembly-arm

assembly-arm

github.com/mohitmishra786開発269667 回インストール18 週間前に更新
リポジトリ

assembly-arm について

ARMおよびAArch64アセンブリを扱うエージェント向けのリファレンススキルです。レジスター、呼び出し規約、インラインアセンブリ、SIMDに加え、Apple Siliconの注意点やNEONからSVE2への移行も扱います。

主な機能

  1. 01AArch64とARM Thumbの基礎
  2. 02レジスターと呼び出し規約の参照
  3. 03GCC/Clangインラインアセンブリの解説
  4. 04NEON SIMD組み込み関数の例
  5. 05Apple SiliconとABIの注意点
  6. 06NEONからSVE2への移行案内

活用シーン

  1. 01ARM64アセンブリ出力の読解
  2. 02ARMインラインアセンブリの作成
  3. 03NEONコードのSVE2移行計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill assembly-arm

インストール後、Claude Code で/assembly-armを入力して読み込まれたか確認します。

同梱ファイルreferences/reference.md

詳細

ARM / AArch64 アセンブリ

目的

AArch64(64 ビット)および ARM(32 ビット Thumb)アセンブリについて、レジスター、呼び出し規約、インラインアセンブリ、NEON/SVE SIMD のパターンをエージェントに解説します。

トリガー

  • 「ARM64 アセンブリの出力を読むにはどうすればよいですか?」
  • 「AArch64 のレジスターと呼び出し規約は何ですか?」
  • 「ARM 用のインラインアセンブリを書くにはどうすればよいですか?」
  • 「AArch64 と ARM Thumb の違いは何ですか?」
  • 「NEON intrinsic を使うにはどうすればよいですか?」

ワークフロー

1. ARM アセンブリを生成する
# AArch64 (native or cross-compile)
aarch64-linux-gnu-gcc -S -O2 foo.c -o foo.s

# 32-bit ARM Thumb
arm-linux-gnueabihf-gcc -S -O2 -mthumb foo.c -o foo.s

# From objdump
aarch64-linux-gnu-objdump -d -S prog

# From GDB on target
(gdb) disassemble /s main
2. AArch64 レジスター(AAPCS64)
レジスター別名役割
x0–x7—引数 1~8 と戻り値
x8xr間接結果の格納場所(構造体の戻り値)
x9–x15—呼び出し元保存の一時レジスター
x16–x17ip0、ip1プロシージャー呼び出し間の一時レジスター(リンカーが使用)
x18prプラットフォームレジスター(一部の OS では予約済み)
x19–x28—呼び出し先保存
x29fpフレームポインター(呼び出し先保存)
x30lrリンクレジスター(戻りアドレス)
sp—スタックポインター(呼び出し時に 16 バイト境界に整列する必要あり)
pc—プログラムカウンター(直接アクセス不可)
xzrwzrゼロレジスター(読み出し値は 0、書き込みは破棄)
v0–v7q0–q7FP/SIMD の引数と戻り値
v8–v15—呼び出し先保存の SIMD レジスター(下位 64 ビットのみ)
v16–v31—呼び出し元保存の一時レジスター

幅のバリエーション:x0(64 ビット)、w0(32 ビット、64 ビットにゼロ拡張)、h0(16 ビット)、b0(8 ビット)。

3. AAPCS64 呼び出し規約

整数/ポインター引数: x0–x7 浮動小数点/SIMD 引数: v0–v7 戻り値: x0(整数)、x0+x1(128 ビット)、v0(浮動小数点/SIMD) 呼び出し先保存: x19–x28、x29(fp)、x30(lr)、v8–v15(下位 64 ビット) 呼び出し元保存: その他すべて

bl または blr 命令の実行時、スタックは 16 バイト境界に整列している必要があります。

4. よく使われる AArch64 命令
命令効果
mov x0, x1レジスターをコピー
mov x0, #42即値をロード
movz x0, #0x1234, lsl #16ゼロ拡張してシフトしながら移動
movk x0, #0xabcd値を保持しながら移動(一部を更新)
ldr x0, [x1]x1 のアドレスから 64 ビット値をロード
ldr x0, [x1, #8]x1+8 からロード
str x0, [x1, #8]x0 を x1+8 に格納
ldp x0, x1, [sp, #16]ペアをロード(2 つのレジスターを同時に)
stp x29, x30, [sp, #-16]!ペアを格納し、sp を事前に減算
add x0, x1, x2x0 = x1 + x2
add x0, x1, #8x0 = x1 + 8
sub x0, x1, x2x0 = x1 - x2
mul x0, x1, x2x0 = x1 * x2
sdiv x0, x1, x2符号付き除算
udiv x0, x1, x2符号なし除算
cmp x0, x1x0 - x1 のフラグを設定
cbz x0, labelx0 == 0 の場合に分岐
cbnz x0, labelx0 != 0 の場合に分岐
bl funcリンク付きで分岐(呼び出し)
blr x0x0 のアドレスへリンク付きで分岐
ret戻る(x30 へ分岐)
ret x0x0 のアドレスへ戻る
adrp x0, symbolPC 相対のページアドレス
add x0, x0, :lo12:symbolsymbol のオフセットの下位 12 ビット
5. 典型的な関数のプロローグ/エピローグ
// Non-leaf function
stp  x29, x30, [sp, #-32]!   // save fp, lr; allocate 32 bytes
mov  x29, sp                  // set frame pointer
stp  x19, x20, [sp, #16]     // save callee-saved registers
// ... body ...
ldp  x19, x20, [sp, #16]     // restore
ldp  x29, x30, [sp], #32     // restore fp, lr; deallocate
ret

// Leaf function (no calls, no callee-saved regs needed)
// Can use red zone (no rsp adjustment) — but AArch64 has no red zone
sub  sp, sp, #16             // allocate locals
// ... body ...
add  sp, sp, #16
ret
6. インラインアセンブリ(GCC/Clang)
// Barrier
__asm__ volatile ("dmb ish" ::: "memory");

// Load acquire
static inline int load_acquire(volatile int *p) {
    int val;
    __asm__ volatile ("ldar %w0, %1" : "=r"(val) : "Q"(*p));
    return val;
}

// Store release
static inline void store_release(volatile int *p, int val) {
    __asm__ volatile ("stlr %w1, %0" : "=Q"(*p) : "r"(val));
}

// Read system counter
static inline uint64_t read_cntvct(void) {
    uint64_t val;
    __asm__ volatile ("mrs %0, cntvct_el0" : "=r"(val));
    return val;
}

AArch64 固有の制約:

  • "Q" — 排他/取得/解放命令に適したメモリーオペランド
  • "r" — 任意の汎用レジスター
  • "w" — 任意の FP/SIMD レジスター
7. NEON SIMD intrinsic
#include <arm_neon.h>

// Add 4 floats at once
float32x4_t a = vld1q_f32(arr_a);   // load 4 floats
float32x4_t b = vld1q_f32(arr_b);
float32x4_t c = vaddq_f32(a, b);
vst1q_f32(result, c);

// Horizontal sum
float32x4_t sum = vpaddq_f32(c, c);
sum = vpaddq_f32(sum, sum);
float total = vgetq_lane_f32(sum, 0);

命名規則:v<op><q>_<type>

  • q 接尾辞:128 ビット(クアッド)ベクトル
  • _f32:float32、_s32:int32、_u8:uint8 など
8. Darwin と Linux の AArch64 ABI の違い
項目Linux(AAPCS64)Apple Darwin(arm64)
スタックの整列公開インターフェースで 16 バイト16 バイト
レッドゾーンSP の下に 128 バイトレッドゾーンなし
x18 レジスタープラットフォーム予約済み(TLS)プラットフォームレジスター(使用しないこと)
可変長引数x0–x7、続いてスタック同じ。ただし objc_msgSend の規約は異なる
名前修飾Itanium C++ ABI同じ + Apple blocks

macOS/iOS では、x18 の使用を避け、プラットフォーム型には _DARWIN_C_LEVEL ヘッダーを使用してください。

9. AMX の概要(Apple Silicon)

Apple Matrix Coprocessor(AMX)は、公開されている組み込み関数を通じて利用できません。アクセス方法:

// Practical: Accelerate/vecLib uses AMX internally
#include <Accelerate/Accelerate.h>
// cblas_sgemm, vDSP_* dispatch to AMX on M-series

// Low-level: community-documented opcodes — not portable, avoid in production

Apple Silicon で行列演算を行う場合は、Metal Performance Shaders または Accelerate を使用してください(skills/platform/apple-silicon)。

10. Apple M シリーズの 16KB ページサイズ

Apple Silicon 上の macOS では、4KB ではなく 16KB ページが使用されます:

#include <unistd.h>
long page = sysconf(_SC_PAGESIZE);  // 16384 on macOS arm64
// Align mmap and posix_memalign to page size

PAGE_SIZE == 4096 を前提とするコードは、バッファーのアラインメントがずれたり、macOS で mmap が失敗したりする可能性があります。

11. NEON から SVE2 への移行のヒント
Porting checklist
├── Replace 128-bit fixed loops with svcnt*() strides on SVE hardware
├── Use predicates (svwhilelt) for tails instead of scalar epilogues
├── Guard SVE code with #ifdef __ARM_FEATURE_SVE
└── Keep NEON path for Apple M1–M3 (no SVE); use SVE2 on Graviton/M4+

SVE の組み込み関数と自動ベクトル化フラグについては、skills/platform/arm-sve を参照してください。

レジスターのリファレンスについては、references/reference.md を参照してください。

関連スキル

  • x86-64 アセンブリーには、skills/low-level-programming/assembly-x86 を使用してください
  • クロスコンパイルツールチェーンには、skills/compilers/cross-gcc を使用してください
  • gdbserver を使用した ARM コードのデバッグには、skills/debuggers/gdb を使用してください
  • SVE/SVE2 のスケーラブルベクトルには、skills/platform/arm-sve を使用してください
  • M シリーズのユニファイドメモリーと AMX には、skills/platform/apple-silicon を使用してください

コメント