Claude Skill

assembly-arm

Use when reading or writing AArch64 or AArch32 Thumb assembly, inline asm in C, AAPCS64 register roles, or NEON and SVE vector code. Not for ABI detail across ISAs: use abi-and-calling-conventions.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download outlinedriven-outline-driven-development-.devin_skills_assembly-arm-b0e8ce8.zip · 5 KB
Part of outlinedriven/outline-driven-development — 145 skills

Install

skills CLI npx skills add https://github.com/OutlineDriven/outline-driven-development/tree/main/.devin/skills/assembly-arm
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install outlinedriven-outline-driven-development@llmmart
Git git clone https://github.com/OutlineDriven/outline-driven-development.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole outlinedriven/outline-driven-development collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

ARM and AArch64 assembly

AArch64 has 31 general 64-bit registers, a clean load-store instruction set, and per-core SIMD through NEON. This skill reads compiler output and writes correct inline asm for it.

Contract

Field Bound contract
Trigger The task reads GCC or Clang AArch64 output, writes inline asm or standalone assembly, decodes AAPCS64 register roles, or writes NEON intrinsics or SVE kernels.
Authority Read-only. The skill explains, drafts, and annotates assembly; edits land in the user's source through the normal coding path. No remote mutation.
Side effect None. Output is analysis and drafted code in chat.
Done The drafted assembly assembles for the named target, or the compiler output under discussion is explained register by register.

Inputs

  • The C or C++ source, compiler output, or assembly fragment: required.
  • The target: required. aarch64-linux-gnu for application cores, arm-none-eabi with -mthumb for 32-bit Cortex-M.
  • The purpose: required. Reading output, writing inline asm, or vectorizing.

Procedure

  1. Generate a baseline. Write the C version first and read its output before hand-writing anything. Done when: the compiler's own code for the same function is on screen.
aarch64-linux-gnu-gcc -O2 -S foo.c -o foo.s
aarch64-linux-gnu-objdump -d a.out
  1. Map registers by role. The table names are the ABI's, and assembly must respect them. Done when: every register in the fragment is classified.
Register Alias Role
x0 to x7 w0 for 32-bit use Arguments and return values, caller saved
x8 Indirect result location, or syscall number on Linux
x9 to x15 Temporaries, caller saved
x16, x17 ip0, ip1 Intra-procedure call scratch, do not keep values across calls
x18 Platform register, reserved on some platforms, do not use
x19 to x28 Callee saved
x29 fp Frame pointer
x30 lr Link register, return address
sp Stack pointer, must stay 16-byte aligned at public interfaces
v0 to v7 q/d/s views FP and SIMD arguments and returns, caller saved
v8 to v15 Callee saved, low 64 bits only
  1. Read the common instructions. Load and store are separate from arithmetic, which only runs on registers. Done when: each instruction in the fragment parses.
ldr  x0, [x1]          // load 64-bit
ldrb w0, [x1]          // load byte, zero-extended
strb w0, [x1]          // store byte
ldp  x0, x1, [sp]      // load pair
stp  x29, x30, [sp, #-16]!  // store pair with pre-index writeback
add  x0, x1, x2        // x0 = x1 + x2
mul  x0, x1, x2        // low 64 bits of the product
madd x0, x1, x2, x3    // x0 = x1*x2 + x3
sdiv x0, x1, x2        // signed divide
udiv x0, x1, x2        // unsigned divide
cmp  x0, x1            // set flags from x0 - x1
cbz  x0, label         // branch if zero, no flags needed
blr  x0                // branch to address in register, set x30
ret                    // return through x30
adrp x0, symbol        // page address of a symbol
add  x0, x0, :lo12:symbol
  1. Write the standard prologue and epilogue. Leaf functions that fit in a frame need none. Done when: any callee-saved register pushed is popped, and sp returns aligned.
my_func:               // non-leaf example
    stp  x29, x30, [sp, #-32]!
    mov  x29, sp
    stp  x19, x20, [sp, #16]
    // body
    ldp  x19, x20, [sp, #16]
    ldp  x29, x30, [sp], #32
    ret
  1. Write inline asm with the constraints the target needs. volatile stops reordering, memory declares side effects on memory, and hardware registers need explicit clobbers. Use the counter to read the timer. Done when: the fragment compiles and its inputs, outputs, and clobbers are each listed.
static inline uint64_t read_cntvct(void) {
    uint64_t val;
    __asm__ volatile("mrs %0, cntvct_el0" : "=r"(val));
    return val;
}
  1. Use NEON for 128-bit SIMD. Work through <arm_neon.h> intrinsics; they map one to one onto instructions. Done when: the loop processes one vector per iteration and the scalar tail stays correct.
#include <arm_neon.h>

void sum_f32(const float *a, const float *b, float *dst, int n) {
    for (int i = 0; i + 4 <= n; i += 4) {
        float32x4_t va = vld1q_f32(a + i);   // load 4 floats
        float32x4_t vb = vld1q_f32(b + i);
        float32x4_t vc = vaddq_f32(va, vb);
        vst1q_f32(dst + i, vc);              // store 4 floats
    }
}
  1. Apply the platform corrections. Apple silicon uses 16 KiB pages and a 128-byte cache line, so sysconf(_SC_PAGESIZE) replaces the 4096 assumption. AMX is internal to Apple libraries; write portable code with Accelerate or Metal instead. SVE exists only where the hardware has it; guard SVE code and keep a NEON fallback. Done when: the code states no portability assumption the target contradicts.

Failure and recovery

Failure class Behavior
sp misaligned crash in a callee A path adjusted sp by a non-16-byte amount. Audit every sub sp and pre-index offset.
Value lost across a call in hand-written asm The value sat in x0 to x17. Move it to a callee-saved register or spill it.
Inline asm result wrong at -O2 Missing volatile or a missing "memory" clobber let the compiler delete or reorder the asm.
NEON code fails on Cortex-M Cortex-M has no NEON and runs Thumb. Rewrite with scalar code or a DSP extension.
SVE build fails on a NEON-only core SVE needs supporting hardware. Use the guarded fallback from step 7.

Output

Annotated assembly or inline asm, with the register roles named, the clobbers justified, and the target stated. The condition-code table, the wider instruction list, and the NEON category reference are in references/reference.md.

Files (outline-driven-development)
  • agents
    • openai.yaml 198 B
      interface:
        display_name: "Assembly Arm"
        short_description: "Use when reading or writing AArch64 or AArch32 Thumb assembly, inline asm in C, AAPCS64 register roles, or NEON and SVE vector code."
      
  • references
    • reference.md 4.5 KB
      # AArch64 and ARM reference
      
      Sources: the ARM Architecture Reference Manual and the ARM compiler intrinsics pages on developer.arm.com.
      
      ## Condition codes
      
      | Code | Meaning | Flags |
      |------|---------|-------|
      | `EQ` | Equal | `Z=1` |
      | `NE` | Not equal | `Z=0` |
      | `CS`/`HS` | Carry set, unsigned higher or same | `C=1` |
      | `CC`/`LO` | Carry clear, unsigned lower | `C=0` |
      | `MI` | Minus, negative | `N=1` |
      | `PL` | Plus, non-negative | `N=0` |
      | `VS` | Overflow | `V=1` |
      | `VC` | No overflow | `V=0` |
      | `HI` | Unsigned higher | `C=1` and `Z=0` |
      | `LS` | Unsigned lower or same | `C=0` or `Z=1` |
      | `GE` | Signed greater or equal | `N=V` |
      | `LT` | Signed less than | `N!=V` |
      | `GT` | Signed greater | `Z=0` and `N=V` |
      | `LE` | Signed less or equal | `Z=1` or `N!=V` |
      | `AL` | Always, the default | none |
      
      ## Key instructions
      
      Loads and stores:
      
      ```asm
      ldr  x0, [x1]              // load 64-bit
      ldrb w0, [x1]              // byte, zero-extended
      ldrsh x0, [x1]             // halfword, sign-extended
      str  x0, [x1]              // store 64-bit
      strb w0, [x1]
      ldp  x0, x1, [sp]          // load pair
      stp  x0, x1, [sp, #-16]!   // pre-index writeback
      ldr  x0, [x1, #8]!         // pre-index
      ldr  x0, [x1], #8          // post-index
      ldar x0, [x1]              // load-acquire
      stlr x0, [x1]              // store-release
      ldxr x0, [x1]              // load-exclusive
      stxr w2, x0, [x1]          // store-exclusive, w2 = 0 on success
      ```
      
      Arithmetic and logic:
      
      ```asm
      add  x0, x1, x2
      adds x0, x1, x2            // set flags
      subs x0, x1, x2
      adc  x0, x1, x2            // add with carry
      mul  x0, x1, x2            // low half of the product
      madd x0, x1, x2, x3        // x1*x2 + x3
      msub x0, x1, x2, x3        // x3 - x1*x2
      smull x0, w1, w2           // 32x32 to 64 signed
      and  x0, x1, x2
      orr  x0, x1, x2
      eor  x0, x1, x2
      mvn  x0, x1                // bitwise not
      lsl  x0, x1, #3
      asr  x0, x1, #3            // arithmetic shift right
      ror  x0, x1, #3
      rev  x0, x1                // reverse bytes, endian swap
      ```
      
      Branches and system:
      
      ```asm
      b    label
      bl   func                  // call, sets x30
      blr  x0                    // call through register
      ret                        // return via x30
      cbz  x0, label
      cbnz x0, label
      tbz  x0, #3, label         // test bit, branch if zero
      tbnz x0, #3, label
      nop
      wfe                        // wait for event
      sev
      dsb  sy                    // data synchronization barrier
      dmb  ish                   // data memory barrier, inner shareable
      isb                        // instruction synchronization barrier
      mrs  x0, cntvct_el0        // read the virtual timer count
      msr  nzcv, x0              // write the flags register
      ```
      
      ## Memory ordering instructions
      
      | Instruction | Ordering |
      |-------------|----------|
      | `ldar` | Load-acquire |
      | `stlr` | Store-release |
      | `ldxr` | Exclusive load, no ordering by itself |
      | `stxr` | Exclusive store, no ordering by itself |
      | `dmb ish` | Full barrier, inner shareable domain |
      | `dmb ishld` | Load barrier |
      | `dmb ishst` | Store barrier |
      | `dsb` | Data synchronization barrier, all device and memory |
      | `isb` | Instruction synchronization barrier |
      
      ## NEON quick categories
      
      C types from `<arm_neon.h>`:
      
      | Type | Element shape |
      |------|---------------|
      | `uint8x16_t` | 16 x u8 |
      | `int32x4_t` | 4 x i32 |
      | `int64x2_t` | 2 x i64 |
      | `float32x4_t` | 4 x f32 |
      | `float64x2_t` | 2 x f64, AArch64 only |
      
      Common patterns:
      
      ```c
      float32x4_t v  = vld1q_f32(ptr);        // load
      float32x4_t s  = vaddq_f32(a, b);
      float32x4_t m  = vmulq_f32(a, b);
      float32x4_t f  = vfmaq_f32(acc, a, b);  // acc + a*b
      float32x4_t z  = vdupq_n_f32(0.0f);     // broadcast
      float32x2_t r  = vadd_f32(vget_low_f32(v), vget_high_f32(v));
      float          t  = vgetq_lane_f32(v, 3);
      vst1q_f32(ptr, v);                      // store
      ```
      
      ## Thumb-2 notes
      
      Cortex-M cores execute Thumb-2. Most 32-bit ARM instructions exist in Thumb encoding, and 16-bit encodings keep code small. Conditional execution inside `it` blocks replaces many branches, but the block is narrow; prefer real branches for long bodies. Use the `bl` and `bx lr` pair through a linker that supports `ARM`/`Thumb` interworking, and check that function pointers carry the Thumb bit. Load immediate constants through the literal pool when the value does not fit an encoding.
      
      ## SVE and SME pointers
      
      Write NEON for baseline portability. Guard SVE code on a runtime check of the SVE feature and a length-agnostic loop, because the vector length is implementation defined. On Apple silicon, AMX is reached only through system libraries, so use Accelerate or Metal for large matrix work instead of writing AMX code.
      
  • SKILL.md 6 KB
    ---
    name: assembly-arm
    description: 'Use when reading or writing AArch64 or AArch32 Thumb assembly, inline asm in C, AAPCS64 register roles, or NEON and SVE vector code. Not for ABI detail across ISAs: use abi-and-calling-conventions.'
    ---
    
    # ARM and AArch64 assembly
    
    AArch64 has 31 general 64-bit registers, a clean load-store instruction set, and per-core SIMD through NEON. This skill reads compiler output and writes correct inline asm for it.
    
    ## Contract
    
    | Field | Bound contract |
    |---|---|
    | Trigger | The task reads GCC or Clang AArch64 output, writes inline asm or standalone assembly, decodes AAPCS64 register roles, or writes NEON intrinsics or SVE kernels. |
    | Authority | Read-only. The skill explains, drafts, and annotates assembly; edits land in the user's source through the normal coding path. No remote mutation. |
    | Side effect | None. Output is analysis and drafted code in chat. |
    | Done | The drafted assembly assembles for the named target, or the compiler output under discussion is explained register by register. |
    
    ## Inputs
    
    - The C or C++ source, compiler output, or assembly fragment: required.
    - The target: required. `aarch64-linux-gnu` for application cores, `arm-none-eabi` with `-mthumb` for 32-bit Cortex-M.
    - The purpose: required. Reading output, writing inline asm, or vectorizing.
    
    ## Procedure
    
    1. Generate a baseline. Write the C version first and read its output before hand-writing anything. Done when: the compiler's own code for the same function is on screen.
    
    ```bash
    aarch64-linux-gnu-gcc -O2 -S foo.c -o foo.s
    aarch64-linux-gnu-objdump -d a.out
    ```
    
    2. Map registers by role. The table names are the ABI's, and assembly must respect them. Done when: every register in the fragment is classified.
    
    | Register | Alias | Role |
    |----------|-------|------|
    | `x0` to `x7` | `w0` for 32-bit use | Arguments and return values, caller saved |
    | `x8` | | Indirect result location, or syscall number on Linux |
    | `x9` to `x15` | | Temporaries, caller saved |
    | `x16`, `x17` | `ip0`, `ip1` | Intra-procedure call scratch, do not keep values across calls |
    | `x18` | | Platform register, reserved on some platforms, do not use |
    | `x19` to `x28` | | Callee saved |
    | `x29` | `fp` | Frame pointer |
    | `x30` | `lr` | Link register, return address |
    | `sp` | | Stack pointer, must stay 16-byte aligned at public interfaces |
    | `v0` to `v7` | `q`/`d`/`s` views | FP and SIMD arguments and returns, caller saved |
    | `v8` to `v15` | | Callee saved, low 64 bits only |
    
    3. Read the common instructions. Load and store are separate from arithmetic, which only runs on registers. Done when: each instruction in the fragment parses.
    
    ```asm
    ldr  x0, [x1]          // load 64-bit
    ldrb w0, [x1]          // load byte, zero-extended
    strb w0, [x1]          // store byte
    ldp  x0, x1, [sp]      // load pair
    stp  x29, x30, [sp, #-16]!  // store pair with pre-index writeback
    add  x0, x1, x2        // x0 = x1 + x2
    mul  x0, x1, x2        // low 64 bits of the product
    madd x0, x1, x2, x3    // x0 = x1*x2 + x3
    sdiv x0, x1, x2        // signed divide
    udiv x0, x1, x2        // unsigned divide
    cmp  x0, x1            // set flags from x0 - x1
    cbz  x0, label         // branch if zero, no flags needed
    blr  x0                // branch to address in register, set x30
    ret                    // return through x30
    adrp x0, symbol        // page address of a symbol
    add  x0, x0, :lo12:symbol
    ```
    
    4. Write the standard prologue and epilogue. Leaf functions that fit in a frame need none. Done when: any callee-saved register pushed is popped, and `sp` returns aligned.
    
    ```asm
    my_func:               // non-leaf example
        stp  x29, x30, [sp, #-32]!
        mov  x29, sp
        stp  x19, x20, [sp, #16]
        // body
        ldp  x19, x20, [sp, #16]
        ldp  x29, x30, [sp], #32
        ret
    ```
    
    5. Write inline asm with the constraints the target needs. `volatile` stops reordering, `memory` declares side effects on memory, and hardware registers need explicit clobbers. Use the counter to read the timer. Done when: the fragment compiles and its inputs, outputs, and clobbers are each listed.
    
    ```c
    static inline uint64_t read_cntvct(void) {
        uint64_t val;
        __asm__ volatile("mrs %0, cntvct_el0" : "=r"(val));
        return val;
    }
    ```
    
    6. Use NEON for 128-bit SIMD. Work through `<arm_neon.h>` intrinsics; they map one to one onto instructions. Done when: the loop processes one vector per iteration and the scalar tail stays correct.
    
    ```c
    #include <arm_neon.h>
    
    void sum_f32(const float *a, const float *b, float *dst, int n) {
        for (int i = 0; i + 4 <= n; i += 4) {
            float32x4_t va = vld1q_f32(a + i);   // load 4 floats
            float32x4_t vb = vld1q_f32(b + i);
            float32x4_t vc = vaddq_f32(va, vb);
            vst1q_f32(dst + i, vc);              // store 4 floats
        }
    }
    ```
    
    7. Apply the platform corrections. Apple silicon uses 16 KiB pages and a 128-byte cache line, so `sysconf(_SC_PAGESIZE)` replaces the 4096 assumption. AMX is internal to Apple libraries; write portable code with Accelerate or Metal instead. SVE exists only where the hardware has it; guard SVE code and keep a NEON fallback. Done when: the code states no portability assumption the target contradicts.
    
    ## Failure and recovery
    
    | Failure class | Behavior |
    |---|---|
    | `sp` misaligned crash in a callee | A path adjusted `sp` by a non-16-byte amount. Audit every `sub sp` and pre-index offset. |
    | Value lost across a call in hand-written asm | The value sat in `x0` to `x17`. Move it to a callee-saved register or spill it. |
    | Inline asm result wrong at `-O2` | Missing `volatile` or a missing `"memory"` clobber let the compiler delete or reorder the asm. |
    | NEON code fails on Cortex-M | Cortex-M has no NEON and runs Thumb. Rewrite with scalar code or a DSP extension. |
    | SVE build fails on a NEON-only core | SVE needs supporting hardware. Use the guarded fallback from step 7. |
    
    ## Output
    
    Annotated assembly or inline asm, with the register roles named, the clobbers justified, and the target stated. The condition-code table, the wider instruction list, and the NEON category reference are in `references/reference.md`.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related