关于 assembly-arm
面向 Agent 的 ARM 与 AArch64 汇编参考技能,涵盖寄存器、调用约定、内联汇编和 SIMD 用法。也介绍 Apple Silicon 上的相关注意事项及 NEON 到 SVE2 的迁移。
主要功能
- 01AArch64 与 ARM Thumb 汇编基础
- 02寄存器和调用约定参考
- 03GCC/Clang 内联汇编指导
- 04NEON SIMD 内建函数示例
- 05Apple Silicon 与 ABI 注意事项
- 06NEON 到 SVE2 迁移提示
适用场景
- 01阅读 ARM64 汇编输出
- 02编写 ARM 内联汇编
- 03为 NEON 代码规划 SVE2 迁移
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill assembly-arm安装后在 Claude Code 中输入/assembly-arm确认已加载。
附带文件references/reference.md
详细介绍
ARM / AArch64 汇编
用途
指导 Agent 学习 AArch64(64 位)和 ARM(32 位 Thumb)汇编:寄存器、调用约定、内联汇编,以及 NEON/SVE SIMD 模式。
触发场景
- “如何阅读 ARM64 汇编输出?”
- “AArch64 有哪些寄存器?调用约定是什么?”
- “如何为 ARM 编写内联汇编?”
- “AArch64 和 ARM Thumb 有什么区别?”
- “如何使用 NEON 内建函数?”
工作流
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 和返回值 |
x8 | xr | 间接结果位置(结构体返回值) |
x9–x15 | — | 调用者保存的临时寄存器 |
x16–x17 | ip0、ip1 | 过程调用内部临时寄存器(由链接器使用) |
x18 | pr | 平台寄存器(某些 OS 上保留) |
x19–x28 | — | 被调用者保存 |
x29 | fp | 帧指针(被调用者保存) |
x30 | lr | 链接寄存器(返回地址) |
sp | — | 栈指针(调用时必须按 16 字节对齐) |
pc | — | 程序计数器(不可直接访问) |
xzr | wzr | 零寄存器(读取为 0,写入会被丢弃) |
v0–v7 | q0–q7 | 浮点/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] | 加载一对寄存器(一次加载两个寄存器) |
stp x29, x30, [sp, #-16]! | 存储一对寄存器,并预先递减 sp |
add x0, x1, x2 | x0 = x1 + x2 |
add x0, x1, #8 | x0 = x1 + 8 |
sub x0, x1, x2 | x0 = x1 - x2 |
mul x0, x1, x2 | x0 = x1 * x2 |
sdiv x0, x1, x2 | 有符号除法 |
udiv x0, x1, x2 | 无符号除法 |
cmp x0, x1 | 为 x0 - x1 设置标志位 |
cbz x0, label | 如果 x0 == 0,则分支 |
cbnz x0, label | 如果 x0 != 0,则分支 |
bl func | 带链接的分支(调用) |
blr x0 | 带链接地跳转到 x0 中的地址 |
ret | 返回(跳转到 x30) |
ret x0 | 返回到 x0 中的地址 |
adrp x0, symbol | PC 相对页地址 |
add x0, x0, :lo12:symbol | 获取 symbol 偏移的低 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"— 任意浮点/SIMD 寄存器
7. NEON SIMD 内建函数
#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 矩阵协处理器(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 使用 16KB 页面(而非 4KB):
#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。
相关技能
- 使用
skills/low-level-programming/assembly-x86处理 x86-64 汇编 - 使用
skills/compilers/cross-gcc使用交叉编译工具链 - 使用
skills/debuggers/gdb通过 gdbserver 调试 ARM 代码 - 使用
skills/platform/arm-sve使用 SVE/SVE2 可伸缩向量 - 使用
skills/platform/apple-silicon了解 M 系列统一内存和 AMX