Module 5 Exercises — Multiply, Floating Point, and NEON

Back to the Course 3 syllabus. Read first: Smith 11–13 (+14–15 for the optimization mindset); H&H 5.3 for FP formats.

Work in course3/m5/ of the labs repo. This module is the DSP bridge: the kernels here are the ones Course 2 runs on the Cortex-M4F and Course 1 §3 analyzes numerically.

NoteExercises in this set

Exercises

Exercise 5.1 — Multiply-accumulate zoo. Small predict-verify set for the integer multiply family: MUL, MADD, MSUB, SMULH/UMULH (the high 64 bits), and a 64×64→128 full product assembled from MUL + UMULH. Verify the 128-bit product against __int128 arithmetic in a C harness. In notes.md: why does a fixed-point Q31 multiply need exactly the high-half instruction?

Exercise 5.2 — The dot product, four ways. The course’s central benchmark, on float vectors of length 4096:

  1. Scalar assembly, integers — Q15-style: 16-bit data, 64-bit accumulator.
  2. Scalar assembly, doublesLDR d, FMADD.
  3. Hand NEONLD1 four lanes at a time, FMLA vector accumulate, horizontal FADDP reduction at the end.
  4. clang -O2 C — write the naive C loop, disassemble, and identify what the autovectorizer emitted before running it.

Benchmark all four (Module 2 discipline), then reconcile: expected 4× from vector width — what did you actually get, and what explains the gap (memory bandwidth, dependent accumulator, unrolling)?

Version ns / 4096 elements Speedup vs. scalar double
Scalar int (Q15-style)
Scalar double 1.0×
Hand NEON
clang -O2 autovectorized

Exercise 5.3 — FMA vs. mul-then-add. Find a concrete (a, b, c) where fma(a, b, c) ≠ a*b + c in double precision (hint: near-cancellation). Demonstrate both results from assembly (FMADD vs. FMUL+FADD), print the full bit patterns, and explain the single-rounding difference in notes.md. Cross-reference Course 1 §3: this is floating-point conditioning made tangible in one instruction.

Exercise 5.4 — Saturating Q15 MAC. Implement a Q15 multiply-accumulate with saturation using SQDMULH (and compare a manual shift-and-clamp version). Feed it a signal that overflows a naive accumulator and show the saturated vs. wrapped outputs side by side. This is Exercise 1.5 grown up, and it is exactly what the Cortex-M4’s DSP extensions (Module 6) and Course 2’s fixed-point filters do per sample.

Input drive Wrapped output Saturated output
0.9 × full scale
1.5 × full scale

Exercise 5.5 — Read the master’s code. Disassemble libc strlen (or memcpy) on your Mac (otool -tv on the shared cache extract, or step into it in lldb). Annotate ~30 instructions in notes.md: identify the vector loop, the alignment prologue, the tail handling. Smith 15’s “reading and understanding code,” applied to production SIMD. Then revisit your Exercise 4.2 ratio.